These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / bridge / br_multicast.c
index 9ba383f..03661d9 100644 (file)
 
 static void br_multicast_start_querier(struct net_bridge *br,
                                       struct bridge_mcast_own_query *query);
+static void br_multicast_add_router(struct net_bridge *br,
+                                   struct net_bridge_port *port);
+static void br_ip4_multicast_leave_group(struct net_bridge *br,
+                                        struct net_bridge_port *port,
+                                        __be32 group,
+                                        __u16 vid);
+#if IS_ENABLED(CONFIG_IPV6)
+static void br_ip6_multicast_leave_group(struct net_bridge *br,
+                                        struct net_bridge_port *port,
+                                        const struct in6_addr *group,
+                                        __u16 vid);
+#endif
 unsigned int br_mdb_rehash_seq;
 
 static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
@@ -271,6 +283,8 @@ static void br_multicast_del_pg(struct net_bridge *br,
                rcu_assign_pointer(*pp, p->next);
                hlist_del_init(&p->mglist);
                del_timer(&p->timer);
+               br_mdb_notify(br->dev, p->port, &pg->addr, RTM_DELMDB,
+                             p->state);
                call_rcu_bh(&p->rcu, br_multicast_free_pg);
 
                if (!mp->ports && !mp->mglist &&
@@ -692,7 +706,7 @@ static int br_multicast_add_group(struct net_bridge *br,
        if (unlikely(!p))
                goto err;
        rcu_assign_pointer(*pp, p);
-       br_mdb_notify(br->dev, port, group, RTM_NEWMDB);
+       br_mdb_notify(br->dev, port, group, RTM_NEWMDB, MDB_TEMPORARY);
 
 found:
        mod_timer(&p->timer, now + br->multicast_membership_interval);
@@ -752,6 +766,7 @@ static void br_multicast_router_expired(unsigned long data)
                goto out;
 
        hlist_del_init_rcu(&port->rlist);
+       br_rtr_notify(br->dev, port, RTM_DELMDB);
 
 out:
        spin_unlock(&br->multicast_lock);
@@ -814,8 +829,8 @@ static void __br_multicast_send_query(struct net_bridge *br,
 
        if (port) {
                skb->dev = port->dev;
-               NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NULL, skb,
-                       NULL, skb->dev,
+               NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
+                       dev_net(port->dev), NULL, skb, NULL, skb->dev,
                        br_dev_queue_push_xmit);
        } else {
                br_multicast_select_own_querier(br, ip, skb);
@@ -912,6 +927,15 @@ void br_multicast_add_port(struct net_bridge_port *port)
 
 void br_multicast_del_port(struct net_bridge_port *port)
 {
+       struct net_bridge *br = port->br;
+       struct net_bridge_port_group *pg;
+       struct hlist_node *n;
+
+       /* Take care of the remaining groups, only perm ones should be left */
+       spin_lock_bh(&br->multicast_lock);
+       hlist_for_each_entry_safe(pg, n, &port->mglist, mglist)
+               br_multicast_del_pg(br, pg);
+       spin_unlock_bh(&br->multicast_lock);
        del_timer_sync(&port->multicast_router_timer);
 }
 
@@ -936,6 +960,8 @@ void br_multicast_enable_port(struct net_bridge_port *port)
 #if IS_ENABLED(CONFIG_IPV6)
        br_multicast_enable(&port->ip6_own_query);
 #endif
+       if (port->multicast_router == 2 && hlist_unhashed(&port->rlist))
+               br_multicast_add_router(br, port);
 
 out:
        spin_unlock(&br->multicast_lock);
@@ -949,10 +975,13 @@ void br_multicast_disable_port(struct net_bridge_port *port)
 
        spin_lock(&br->multicast_lock);
        hlist_for_each_entry_safe(pg, n, &port->mglist, mglist)
-               br_multicast_del_pg(br, pg);
+               if (pg->state == MDB_TEMPORARY)
+                       br_multicast_del_pg(br, pg);
 
-       if (!hlist_unhashed(&port->rlist))
+       if (!hlist_unhashed(&port->rlist)) {
                hlist_del_init_rcu(&port->rlist);
+               br_rtr_notify(br->dev, port, RTM_DELMDB);
+       }
        del_timer(&port->multicast_router_timer);
        del_timer(&port->ip4_own_query.timer);
 #if IS_ENABLED(CONFIG_IPV6)
@@ -975,9 +1004,6 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
        int err = 0;
        __be32 group;
 
-       if (!pskb_may_pull(skb, sizeof(*ih)))
-               return -EINVAL;
-
        ih = igmpv3_report_hdr(skb);
        num = ntohs(ih->ngrec);
        len = skb_transport_offset(skb) + sizeof(*ih);
@@ -1009,9 +1035,15 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
                        continue;
                }
 
-               err = br_ip4_multicast_add_group(br, port, group, vid);
-               if (err)
-                       break;
+               if ((type == IGMPV3_CHANGE_TO_INCLUDE ||
+                    type == IGMPV3_MODE_IS_INCLUDE) &&
+                   ntohs(grec->grec_nsrcs) == 0) {
+                       br_ip4_multicast_leave_group(br, port, group, vid);
+               } else {
+                       err = br_ip4_multicast_add_group(br, port, group, vid);
+                       if (err)
+                               break;
+               }
        }
 
        return err;
@@ -1070,10 +1102,17 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
                        continue;
                }
 
-               err = br_ip6_multicast_add_group(br, port, &grec->grec_mca,
-                                                vid);
-               if (err)
-                       break;
+               if ((grec->grec_type == MLD2_CHANGE_TO_INCLUDE ||
+                    grec->grec_type == MLD2_MODE_IS_INCLUDE) &&
+                   ntohs(*nsrcs) == 0) {
+                       br_ip6_multicast_leave_group(br, port, &grec->grec_mca,
+                                                    vid);
+               } else {
+                       err = br_ip6_multicast_add_group(br, port,
+                                                        &grec->grec_mca, vid);
+                       if (!err)
+                               break;
+               }
        }
 
        return err;
@@ -1180,6 +1219,7 @@ static void br_multicast_add_router(struct net_bridge *br,
                hlist_add_behind_rcu(&port->rlist, slot);
        else
                hlist_add_head_rcu(&port->rlist, &br->router_list);
+       br_rtr_notify(br->dev, port, RTM_NEWMDB);
 }
 
 static void br_multicast_mark_router(struct net_bridge *br,
@@ -1247,25 +1287,14 @@ static int br_ip4_multicast_query(struct net_bridge *br,
                        max_delay = 10 * HZ;
                        group = 0;
                }
-       } else {
-               if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
+       } else if (skb->len >= sizeof(*ih3)) {
                ih3 = igmpv3_query_hdr(skb);
                if (ih3->nsrcs)
                        goto out;
 
                max_delay = ih3->code ?
                            IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
-       }
-
-       /* RFC2236+RFC3376 (IGMPv2+IGMPv3) require the multicast link layer
-        * all-systems destination addresses (224.0.0.1) for general queries
-        */
-       if (!group && iph->daddr != htonl(INADDR_ALLHOSTS_GROUP)) {
-               err = -EINVAL;
+       } else {
                goto out;
        }
 
@@ -1328,12 +1357,6 @@ static int br_ip6_multicast_query(struct net_bridge *br,
            (port && port->state == BR_STATE_DISABLED))
                goto out;
 
-       /* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */
-       if (!(ipv6_addr_type(&ip6h->saddr) & IPV6_ADDR_LINKLOCAL)) {
-               err = -EINVAL;
-               goto out;
-       }
-
        if (skb->len == sizeof(*mld)) {
                if (!pskb_may_pull(skb, sizeof(*mld))) {
                        err = -EINVAL;
@@ -1357,14 +1380,6 @@ static int br_ip6_multicast_query(struct net_bridge *br,
 
        is_general_query = group && ipv6_addr_any(group);
 
-       /* RFC2710+RFC3810 (MLDv1+MLDv2) require the multicast link layer
-        * all-nodes destination address (ff02::1) for general queries
-        */
-       if (is_general_query && !ipv6_addr_is_ll_all_nodes(&ip6h->daddr)) {
-               err = -EINVAL;
-               goto out;
-       }
-
        if (is_general_query) {
                saddr.proto = htons(ETH_P_IPV6);
                saddr.u.ip6 = ip6h->saddr;
@@ -1417,8 +1432,7 @@ br_multicast_leave_group(struct net_bridge *br,
 
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) ||
-           (port && port->state == BR_STATE_DISABLED) ||
-           timer_pending(&other_query->timer))
+           (port && port->state == BR_STATE_DISABLED))
                goto out;
 
        mdb = mlock_dereference(br->mdb, br);
@@ -1426,6 +1440,32 @@ br_multicast_leave_group(struct net_bridge *br,
        if (!mp)
                goto out;
 
+       if (port && (port->flags & BR_MULTICAST_FAST_LEAVE)) {
+               struct net_bridge_port_group __rcu **pp;
+
+               for (pp = &mp->ports;
+                    (p = mlock_dereference(*pp, br)) != NULL;
+                    pp = &p->next) {
+                       if (p->port != port)
+                               continue;
+
+                       rcu_assign_pointer(*pp, p->next);
+                       hlist_del_init(&p->mglist);
+                       del_timer(&p->timer);
+                       call_rcu_bh(&p->rcu, br_multicast_free_pg);
+                       br_mdb_notify(br->dev, port, group, RTM_DELMDB,
+                                     p->state);
+
+                       if (!mp->ports && !mp->mglist &&
+                           netif_running(br->dev))
+                               mod_timer(&mp->timer, jiffies);
+               }
+               goto out;
+       }
+
+       if (timer_pending(&other_query->timer))
+               goto out;
+
        if (br->multicast_querier) {
                __br_multicast_send_query(br, port, &mp->addr);
 
@@ -1451,28 +1491,6 @@ br_multicast_leave_group(struct net_bridge *br,
                }
        }
 
-       if (port && (port->flags & BR_MULTICAST_FAST_LEAVE)) {
-               struct net_bridge_port_group __rcu **pp;
-
-               for (pp = &mp->ports;
-                    (p = mlock_dereference(*pp, br)) != NULL;
-                    pp = &p->next) {
-                       if (p->port != port)
-                               continue;
-
-                       rcu_assign_pointer(*pp, p->next);
-                       hlist_del_init(&p->mglist);
-                       del_timer(&p->timer);
-                       call_rcu_bh(&p->rcu, br_multicast_free_pg);
-                       br_mdb_notify(br->dev, port, group, RTM_DELMDB);
-
-                       if (!mp->ports && !mp->mglist &&
-                           netif_running(br->dev))
-                               mod_timer(&mp->timer, jiffies);
-               }
-               goto out;
-       }
-
        now = jiffies;
        time = now + br->multicast_last_member_count *
                     br->multicast_last_member_interval;
@@ -1556,74 +1574,22 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
                                 struct sk_buff *skb,
                                 u16 vid)
 {
-       struct sk_buff *skb2 = skb;
-       const struct iphdr *iph;
+       struct sk_buff *skb_trimmed = NULL;
        struct igmphdr *ih;
-       unsigned int len;
-       unsigned int offset;
        int err;
 
-       /* We treat OOM as packet loss for now. */
-       if (!pskb_may_pull(skb, sizeof(*iph)))
-               return -EINVAL;
-
-       iph = ip_hdr(skb);
-
-       if (iph->ihl < 5 || iph->version != 4)
-               return -EINVAL;
-
-       if (!pskb_may_pull(skb, ip_hdrlen(skb)))
-               return -EINVAL;
-
-       iph = ip_hdr(skb);
+       err = ip_mc_check_igmp(skb, &skb_trimmed);
 
-       if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
-               return -EINVAL;
-
-       if (iph->protocol != IPPROTO_IGMP) {
-               if (!ipv4_is_local_multicast(iph->daddr))
+       if (err == -ENOMSG) {
+               if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr))
                        BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
                return 0;
+       } else if (err < 0) {
+               return err;
        }
 
-       len = ntohs(iph->tot_len);
-       if (skb->len < len || len < ip_hdrlen(skb))
-               return -EINVAL;
-
-       if (skb->len > len) {
-               skb2 = skb_clone(skb, GFP_ATOMIC);
-               if (!skb2)
-                       return -ENOMEM;
-
-               err = pskb_trim_rcsum(skb2, len);
-               if (err)
-                       goto err_out;
-       }
-
-       len -= ip_hdrlen(skb2);
-       offset = skb_network_offset(skb2) + ip_hdrlen(skb2);
-       __skb_pull(skb2, offset);
-       skb_reset_transport_header(skb2);
-
-       err = -EINVAL;
-       if (!pskb_may_pull(skb2, sizeof(*ih)))
-               goto out;
-
-       switch (skb2->ip_summed) {
-       case CHECKSUM_COMPLETE:
-               if (!csum_fold(skb2->csum))
-                       break;
-               /* fall through */
-       case CHECKSUM_NONE:
-               skb2->csum = 0;
-               if (skb_checksum_complete(skb2))
-                       goto out;
-       }
-
-       err = 0;
-
        BR_INPUT_SKB_CB(skb)->igmp = 1;
-       ih = igmp_hdr(skb2);
+       ih = igmp_hdr(skb);
 
        switch (ih->type) {
        case IGMP_HOST_MEMBERSHIP_REPORT:
@@ -1632,21 +1598,19 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
                err = br_ip4_multicast_add_group(br, port, ih->group, vid);
                break;
        case IGMPV3_HOST_MEMBERSHIP_REPORT:
-               err = br_ip4_multicast_igmp3_report(br, port, skb2, vid);
+               err = br_ip4_multicast_igmp3_report(br, port, skb_trimmed, vid);
                break;
        case IGMP_HOST_MEMBERSHIP_QUERY:
-               err = br_ip4_multicast_query(br, port, skb2, vid);
+               err = br_ip4_multicast_query(br, port, skb_trimmed, vid);
                break;
        case IGMP_HOST_LEAVE_MESSAGE:
                br_ip4_multicast_leave_group(br, port, ih->group, vid);
                break;
        }
 
-out:
-       __skb_push(skb2, offset);
-err_out:
-       if (skb2 != skb)
-               kfree_skb(skb2);
+       if (skb_trimmed && skb_trimmed != skb)
+               kfree_skb(skb_trimmed);
+
        return err;
 }
 
@@ -1656,138 +1620,42 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
                                 struct sk_buff *skb,
                                 u16 vid)
 {
-       struct sk_buff *skb2;
-       const struct ipv6hdr *ip6h;
-       u8 icmp6_type;
-       u8 nexthdr;
-       __be16 frag_off;
-       unsigned int len;
-       int offset;
+       struct sk_buff *skb_trimmed = NULL;
+       struct mld_msg *mld;
        int err;
 
-       if (!pskb_may_pull(skb, sizeof(*ip6h)))
-               return -EINVAL;
-
-       ip6h = ipv6_hdr(skb);
-
-       /*
-        * We're interested in MLD messages only.
-        *  - Version is 6
-        *  - MLD has always Router Alert hop-by-hop option
-        *  - But we do not support jumbrograms.
-        */
-       if (ip6h->version != 6)
-               return 0;
-
-       /* Prevent flooding this packet if there is no listener present */
-       if (!ipv6_addr_is_ll_all_nodes(&ip6h->daddr))
-               BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
-
-       if (ip6h->nexthdr != IPPROTO_HOPOPTS ||
-           ip6h->payload_len == 0)
-               return 0;
-
-       len = ntohs(ip6h->payload_len) + sizeof(*ip6h);
-       if (skb->len < len)
-               return -EINVAL;
-
-       nexthdr = ip6h->nexthdr;
-       offset = ipv6_skip_exthdr(skb, sizeof(*ip6h), &nexthdr, &frag_off);
+       err = ipv6_mc_check_mld(skb, &skb_trimmed);
 
-       if (offset < 0 || nexthdr != IPPROTO_ICMPV6)
+       if (err == -ENOMSG) {
+               if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr))
+                       BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
                return 0;
-
-       /* Okay, we found ICMPv6 header */
-       skb2 = skb_clone(skb, GFP_ATOMIC);
-       if (!skb2)
-               return -ENOMEM;
-
-       err = -EINVAL;
-       if (!pskb_may_pull(skb2, offset + sizeof(struct icmp6hdr)))
-               goto out;
-
-       len -= offset - skb_network_offset(skb2);
-
-       __skb_pull(skb2, offset);
-       skb_reset_transport_header(skb2);
-       skb_postpull_rcsum(skb2, skb_network_header(skb2),
-                          skb_network_header_len(skb2));
-
-       icmp6_type = icmp6_hdr(skb2)->icmp6_type;
-
-       switch (icmp6_type) {
-       case ICMPV6_MGM_QUERY:
-       case ICMPV6_MGM_REPORT:
-       case ICMPV6_MGM_REDUCTION:
-       case ICMPV6_MLD2_REPORT:
-               break;
-       default:
-               err = 0;
-               goto out;
-       }
-
-       /* Okay, we found MLD message. Check further. */
-       if (skb2->len > len) {
-               err = pskb_trim_rcsum(skb2, len);
-               if (err)
-                       goto out;
-               err = -EINVAL;
-       }
-
-       ip6h = ipv6_hdr(skb2);
-
-       switch (skb2->ip_summed) {
-       case CHECKSUM_COMPLETE:
-               if (!csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, skb2->len,
-                                       IPPROTO_ICMPV6, skb2->csum))
-                       break;
-               /*FALLTHROUGH*/
-       case CHECKSUM_NONE:
-               skb2->csum = ~csum_unfold(csum_ipv6_magic(&ip6h->saddr,
-                                                       &ip6h->daddr,
-                                                       skb2->len,
-                                                       IPPROTO_ICMPV6, 0));
-               if (__skb_checksum_complete(skb2))
-                       goto out;
+       } else if (err < 0) {
+               return err;
        }
 
-       err = 0;
-
        BR_INPUT_SKB_CB(skb)->igmp = 1;
+       mld = (struct mld_msg *)skb_transport_header(skb);
 
-       switch (icmp6_type) {
+       switch (mld->mld_type) {
        case ICMPV6_MGM_REPORT:
-           {
-               struct mld_msg *mld;
-               if (!pskb_may_pull(skb2, sizeof(*mld))) {
-                       err = -EINVAL;
-                       goto out;
-               }
-               mld = (struct mld_msg *)skb_transport_header(skb2);
                BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
                err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid);
                break;
-           }
        case ICMPV6_MLD2_REPORT:
-               err = br_ip6_multicast_mld2_report(br, port, skb2, vid);
+               err = br_ip6_multicast_mld2_report(br, port, skb_trimmed, vid);
                break;
        case ICMPV6_MGM_QUERY:
-               err = br_ip6_multicast_query(br, port, skb2, vid);
+               err = br_ip6_multicast_query(br, port, skb_trimmed, vid);
                break;
        case ICMPV6_MGM_REDUCTION:
-           {
-               struct mld_msg *mld;
-               if (!pskb_may_pull(skb2, sizeof(*mld))) {
-                       err = -EINVAL;
-                       goto out;
-               }
-               mld = (struct mld_msg *)skb_transport_header(skb2);
                br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid);
-           }
+               break;
        }
 
-out:
-       kfree_skb(skb2);
+       if (skb_trimmed && skb_trimmed != skb)
+               kfree_skb(skb_trimmed);
+
        return err;
 }
 #endif
@@ -1903,12 +1771,6 @@ void br_multicast_open(struct net_bridge *br)
 
 void br_multicast_stop(struct net_bridge *br)
 {
-       struct net_bridge_mdb_htable *mdb;
-       struct net_bridge_mdb_entry *mp;
-       struct hlist_node *n;
-       u32 ver;
-       int i;
-
        del_timer_sync(&br->multicast_router_timer);
        del_timer_sync(&br->ip4_other_query.timer);
        del_timer_sync(&br->ip4_own_query.timer);
@@ -1916,6 +1778,15 @@ void br_multicast_stop(struct net_bridge *br)
        del_timer_sync(&br->ip6_other_query.timer);
        del_timer_sync(&br->ip6_own_query.timer);
 #endif
+}
+
+void br_multicast_dev_del(struct net_bridge *br)
+{
+       struct net_bridge_mdb_htable *mdb;
+       struct net_bridge_mdb_entry *mp;
+       struct hlist_node *n;
+       u32 ver;
+       int i;
 
        spin_lock_bh(&br->multicast_lock);
        mdb = mlock_dereference(br->mdb, br);
@@ -1949,11 +1820,9 @@ out:
 
 int br_multicast_set_router(struct net_bridge *br, unsigned long val)
 {
-       int err = -ENOENT;
+       int err = -EINVAL;
 
        spin_lock_bh(&br->multicast_lock);
-       if (!netif_running(br->dev))
-               goto unlock;
 
        switch (val) {
        case 0:
@@ -1964,13 +1833,8 @@ int br_multicast_set_router(struct net_bridge *br, unsigned long val)
                br->multicast_router = val;
                err = 0;
                break;
-
-       default:
-               err = -EINVAL;
-               break;
        }
 
-unlock:
        spin_unlock_bh(&br->multicast_lock);
 
        return err;
@@ -1979,11 +1843,9 @@ unlock:
 int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val)
 {
        struct net_bridge *br = p->br;
-       int err = -ENOENT;
+       int err = -EINVAL;
 
        spin_lock(&br->multicast_lock);
-       if (!netif_running(br->dev) || p->state == BR_STATE_DISABLED)
-               goto unlock;
 
        switch (val) {
        case 0:
@@ -1992,8 +1854,10 @@ int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val)
                p->multicast_router = val;
                err = 0;
 
-               if (val < 2 && !hlist_unhashed(&p->rlist))
+               if (val < 2 && !hlist_unhashed(&p->rlist)) {
                        hlist_del_init_rcu(&p->rlist);
+                       br_rtr_notify(br->dev, p, RTM_DELMDB);
+               }
 
                if (val == 1)
                        break;
@@ -2005,13 +1869,8 @@ int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val)
 
                br_multicast_add_router(br, p);
                break;
-
-       default:
-               err = -EINVAL;
-               break;
        }
 
-unlock:
        spin_unlock(&br->multicast_lock);
 
        return err;
@@ -2116,15 +1975,11 @@ unlock:
 
 int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val)
 {
-       int err = -ENOENT;
+       int err = -EINVAL;
        u32 old;
        struct net_bridge_mdb_htable *mdb;
 
        spin_lock_bh(&br->multicast_lock);
-       if (!netif_running(br->dev))
-               goto unlock;
-
-       err = -EINVAL;
        if (!is_power_of_2(val))
                goto unlock;