checkpatch: make --strict the default for drivers/staging files and patches
[firefly-linux-kernel-4.4.55.git] / net / ipv4 / igmp.c
index 651cdf648ec4728bff6e709b0324b7d52ffd65ed..d38b8b61eaeef2e79faa28091dafbee715f7f059 100644 (file)
 #define IP_MAX_MEMBERSHIPS     20
 #define IP_MAX_MSF             10
 
+/* IGMP reports for link-local multicast groups are enabled by default */
+int sysctl_igmp_llm_reports __read_mostly = 1;
+
 #ifdef CONFIG_IP_MULTICAST
 /* Parameter names and values are taken from igmp-v2-06 draft */
 
@@ -437,6 +440,8 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
 
        if (pmc->multiaddr == IGMP_ALL_HOSTS)
                return skb;
+       if (ipv4_is_local_multicast(pmc->multiaddr) && !sysctl_igmp_llm_reports)
+               return skb;
 
        isquery = type == IGMPV3_MODE_IS_INCLUDE ||
                  type == IGMPV3_MODE_IS_EXCLUDE;
@@ -545,6 +550,9 @@ static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc)
                for_each_pmc_rcu(in_dev, pmc) {
                        if (pmc->multiaddr == IGMP_ALL_HOSTS)
                                continue;
+                       if (ipv4_is_local_multicast(pmc->multiaddr) &&
+                            !sysctl_igmp_llm_reports)
+                               continue;
                        spin_lock_bh(&pmc->lock);
                        if (pmc->sfcount[MCAST_EXCLUDE])
                                type = IGMPV3_MODE_IS_EXCLUDE;
@@ -678,7 +686,11 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
 
        if (type == IGMPV3_HOST_MEMBERSHIP_REPORT)
                return igmpv3_send_report(in_dev, pmc);
-       else if (type == IGMP_HOST_LEAVE_MESSAGE)
+
+       if (ipv4_is_local_multicast(group) && !sysctl_igmp_llm_reports)
+               return 0;
+
+       if (type == IGMP_HOST_LEAVE_MESSAGE)
                dst = IGMP_ALL_ROUTER;
        else
                dst = group;
@@ -851,6 +863,8 @@ static bool igmp_heard_report(struct in_device *in_dev, __be32 group)
 
        if (group == IGMP_ALL_HOSTS)
                return false;
+       if (ipv4_is_local_multicast(group) && !sysctl_igmp_llm_reports)
+               return false;
 
        rcu_read_lock();
        for_each_pmc_rcu(in_dev, im) {
@@ -957,6 +971,9 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
                        continue;
                if (im->multiaddr == IGMP_ALL_HOSTS)
                        continue;
+               if (ipv4_is_local_multicast(im->multiaddr) &&
+                   !sysctl_igmp_llm_reports)
+                       continue;
                spin_lock_bh(&im->lock);
                if (im->tm_running)
                        im->gsquery = im->gsquery && mark;
@@ -1181,6 +1198,8 @@ static void igmp_group_dropped(struct ip_mc_list *im)
 #ifdef CONFIG_IP_MULTICAST
        if (im->multiaddr == IGMP_ALL_HOSTS)
                return;
+       if (ipv4_is_local_multicast(im->multiaddr) && !sysctl_igmp_llm_reports)
+               return;
 
        reporter = im->reporter;
        igmp_stop_timer(im);
@@ -1213,6 +1232,8 @@ static void igmp_group_added(struct ip_mc_list *im)
 #ifdef CONFIG_IP_MULTICAST
        if (im->multiaddr == IGMP_ALL_HOSTS)
                return;
+       if (ipv4_is_local_multicast(im->multiaddr) && !sysctl_igmp_llm_reports)
+               return;
 
        if (in_dev->dead)
                return;
@@ -1435,33 +1456,35 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed)
        struct sk_buff *skb_chk;
        unsigned int transport_len;
        unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr);
-       int ret;
+       int ret = -EINVAL;
 
        transport_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb);
 
-       skb_get(skb);
        skb_chk = skb_checksum_trimmed(skb, transport_len,
                                       ip_mc_validate_checksum);
        if (!skb_chk)
-               return -EINVAL;
+               goto err;
 
-       if (!pskb_may_pull(skb_chk, len)) {
-               kfree_skb(skb_chk);
-               return -EINVAL;
-       }
+       if (!pskb_may_pull(skb_chk, len))
+               goto err;
 
        ret = ip_mc_check_igmp_msg(skb_chk);
-       if (ret) {
-               kfree_skb(skb_chk);
-               return ret;
-       }
+       if (ret)
+               goto err;
 
        if (skb_trimmed)
                *skb_trimmed = skb_chk;
-       else
+       /* free now unneeded clone */
+       else if (skb_chk != skb)
                kfree_skb(skb_chk);
 
-       return 0;
+       ret = 0;
+
+err:
+       if (ret && skb_chk && skb_chk != skb)
+               kfree_skb(skb_chk);
+
+       return ret;
 }
 
 /**
@@ -1470,7 +1493,7 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed)
  * @skb_trimmed: to store an skb pointer trimmed to IPv4 packet tail (optional)
  *
  * Checks whether an IPv4 packet is a valid IGMP packet. If so sets
- * skb network and transport headers accordingly and returns zero.
+ * skb transport header accordingly and returns zero.
  *
  * -EINVAL: A broken packet was detected, i.e. it violates some internet
  *  standard
@@ -1485,7 +1508,8 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed)
  * to leave the original skb and its full frame unchanged (which might be
  * desirable for layer 2 frame jugglers).
  *
- * The caller needs to release a reference count from any returned skb_trimmed.
+ * Caller needs to set the skb network header and free any returned skb if it
+ * differs from the provided skb.
  */
 int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed)
 {
@@ -1515,6 +1539,9 @@ static void ip_mc_rejoin_groups(struct in_device *in_dev)
        for_each_pmc_rtnl(in_dev, im) {
                if (im->multiaddr == IGMP_ALL_HOSTS)
                        continue;
+               if (ipv4_is_local_multicast(im->multiaddr) &&
+                   !sysctl_igmp_llm_reports)
+                       continue;
 
                /* a failover is happening and switches
                 * must be notified immediately