]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - net/bridge/br_multicast.c
bridge: adhere to querier election mechanism specified by RFCs
[mirror_ubuntu-zesty-kernel.git] / net / bridge / br_multicast.c
index 93067ecdb9a212321c8780bd65153d60e8203907..b3f17c9b4d0649c6c8f41685a6cfabd4b8833ebd 100644 (file)
@@ -35,7 +35,7 @@
 #include "br_private.h"
 
 static void br_multicast_start_querier(struct net_bridge *br,
-                                      struct bridge_mcast_query *query);
+                                      struct bridge_mcast_own_query *query);
 unsigned int br_mdb_rehash_seq;
 
 static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
@@ -363,7 +363,7 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
        skb_reset_mac_header(skb);
        eth = eth_hdr(skb);
 
-       memcpy(eth->h_source, br->dev->dev_addr, ETH_ALEN);
+       ether_addr_copy(eth->h_source, br->dev->dev_addr);
        eth->h_dest[0] = 1;
        eth->h_dest[1] = 0;
        eth->h_dest[2] = 0x5e;
@@ -433,7 +433,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
        skb_reset_mac_header(skb);
        eth = eth_hdr(skb);
 
-       memcpy(eth->h_source, br->dev->dev_addr, ETH_ALEN);
+       ether_addr_copy(eth->h_source, br->dev->dev_addr);
        eth->h_proto = htons(ETH_P_IPV6);
        skb_put(skb, sizeof(*eth));
 
@@ -761,7 +761,7 @@ static void br_multicast_local_router_expired(unsigned long data)
 }
 
 static void br_multicast_querier_expired(struct net_bridge *br,
-                                        struct bridge_mcast_query *query)
+                                        struct bridge_mcast_own_query *query)
 {
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) || br->multicast_disabled)
@@ -777,7 +777,7 @@ static void br_ip4_multicast_querier_expired(unsigned long data)
 {
        struct net_bridge *br = (void *)data;
 
-       br_multicast_querier_expired(br, &br->ip4_query);
+       br_multicast_querier_expired(br, &br->ip4_own_query);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -785,10 +785,22 @@ static void br_ip6_multicast_querier_expired(unsigned long data)
 {
        struct net_bridge *br = (void *)data;
 
-       br_multicast_querier_expired(br, &br->ip6_query);
+       br_multicast_querier_expired(br, &br->ip6_own_query);
 }
 #endif
 
+static void br_multicast_select_own_querier(struct net_bridge *br,
+                                           struct br_ip *ip,
+                                           struct sk_buff *skb)
+{
+       if (ip->proto == htons(ETH_P_IP))
+               br->ip4_querier.addr.u.ip4 = ip_hdr(skb)->saddr;
+#if IS_ENABLED(CONFIG_IPV6)
+       else
+               br->ip6_querier.addr.u.ip6 = ipv6_hdr(skb)->saddr;
+#endif
+}
+
 static void __br_multicast_send_query(struct net_bridge *br,
                                      struct net_bridge_port *port,
                                      struct br_ip *ip)
@@ -804,17 +816,19 @@ static void __br_multicast_send_query(struct net_bridge *br,
                skb->dev = port->dev;
                NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
                        dev_queue_xmit);
-       } else
+       } else {
+               br_multicast_select_own_querier(br, ip, skb);
                netif_rx(skb);
+       }
 }
 
 static void br_multicast_send_query(struct net_bridge *br,
                                    struct net_bridge_port *port,
-                                   struct bridge_mcast_query *query)
+                                   struct bridge_mcast_own_query *own_query)
 {
        unsigned long time;
        struct br_ip br_group;
-       struct bridge_mcast_querier *querier = NULL;
+       struct bridge_mcast_other_query *other_query = NULL;
 
        if (!netif_running(br->dev) || br->multicast_disabled ||
            !br->multicast_querier)
@@ -822,31 +836,32 @@ static void br_multicast_send_query(struct net_bridge *br,
 
        memset(&br_group.u, 0, sizeof(br_group.u));
 
-       if (port ? (query == &port->ip4_query) :
-                  (query == &br->ip4_query)) {
-               querier = &br->ip4_querier;
+       if (port ? (own_query == &port->ip4_own_query) :
+                  (own_query == &br->ip4_own_query)) {
+               other_query = &br->ip4_other_query;
                br_group.proto = htons(ETH_P_IP);
 #if IS_ENABLED(CONFIG_IPV6)
        } else {
-               querier = &br->ip6_querier;
+               other_query = &br->ip6_other_query;
                br_group.proto = htons(ETH_P_IPV6);
 #endif
        }
 
-       if (!querier || timer_pending(&querier->timer))
+       if (!other_query || timer_pending(&other_query->timer))
                return;
 
        __br_multicast_send_query(br, port, &br_group);
 
        time = jiffies;
-       time += query->startup_sent < br->multicast_startup_query_count ?
+       time += own_query->startup_sent < br->multicast_startup_query_count ?
                br->multicast_startup_query_interval :
                br->multicast_query_interval;
-       mod_timer(&query->timer, time);
+       mod_timer(&own_query->timer, time);
 }
 
-static void br_multicast_port_query_expired(struct net_bridge_port *port,
-                                           struct bridge_mcast_query *query)
+static void
+br_multicast_port_query_expired(struct net_bridge_port *port,
+                               struct bridge_mcast_own_query *query)
 {
        struct net_bridge *br = port->br;
 
@@ -868,7 +883,7 @@ static void br_ip4_multicast_port_query_expired(unsigned long data)
 {
        struct net_bridge_port *port = (void *)data;
 
-       br_multicast_port_query_expired(port, &port->ip4_query);
+       br_multicast_port_query_expired(port, &port->ip4_own_query);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -876,7 +891,7 @@ static void br_ip6_multicast_port_query_expired(unsigned long data)
 {
        struct net_bridge_port *port = (void *)data;
 
-       br_multicast_port_query_expired(port, &port->ip6_query);
+       br_multicast_port_query_expired(port, &port->ip6_own_query);
 }
 #endif
 
@@ -886,11 +901,11 @@ void br_multicast_add_port(struct net_bridge_port *port)
 
        setup_timer(&port->multicast_router_timer, br_multicast_router_expired,
                    (unsigned long)port);
-       setup_timer(&port->ip4_query.timer, br_ip4_multicast_port_query_expired,
-                   (unsigned long)port);
+       setup_timer(&port->ip4_own_query.timer,
+                   br_ip4_multicast_port_query_expired, (unsigned long)port);
 #if IS_ENABLED(CONFIG_IPV6)
-       setup_timer(&port->ip6_query.timer, br_ip6_multicast_port_query_expired,
-                   (unsigned long)port);
+       setup_timer(&port->ip6_own_query.timer,
+                   br_ip6_multicast_port_query_expired, (unsigned long)port);
 #endif
 }
 
@@ -899,7 +914,7 @@ void br_multicast_del_port(struct net_bridge_port *port)
        del_timer_sync(&port->multicast_router_timer);
 }
 
-static void br_multicast_enable(struct bridge_mcast_query *query)
+static void br_multicast_enable(struct bridge_mcast_own_query *query)
 {
        query->startup_sent = 0;
 
@@ -916,9 +931,9 @@ void br_multicast_enable_port(struct net_bridge_port *port)
        if (br->multicast_disabled || !netif_running(br->dev))
                goto out;
 
-       br_multicast_enable(&port->ip4_query);
+       br_multicast_enable(&port->ip4_own_query);
 #if IS_ENABLED(CONFIG_IPV6)
-       br_multicast_enable(&port->ip6_query);
+       br_multicast_enable(&port->ip6_own_query);
 #endif
 
 out:
@@ -938,9 +953,9 @@ void br_multicast_disable_port(struct net_bridge_port *port)
        if (!hlist_unhashed(&port->rlist))
                hlist_del_init_rcu(&port->rlist);
        del_timer(&port->multicast_router_timer);
-       del_timer(&port->ip4_query.timer);
+       del_timer(&port->ip4_own_query.timer);
 #if IS_ENABLED(CONFIG_IPV6)
-       del_timer(&port->ip6_query.timer);
+       del_timer(&port->ip6_own_query.timer);
 #endif
        spin_unlock(&br->multicast_lock);
 }
@@ -1064,15 +1079,71 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
 }
 #endif
 
+static bool br_ip4_multicast_select_querier(struct net_bridge *br,
+                                           __be32 saddr)
+{
+       if (!timer_pending(&br->ip4_own_query.timer) &&
+           !timer_pending(&br->ip4_other_query.timer))
+               goto update;
+
+       if (!br->ip4_querier.addr.u.ip4)
+               goto update;
+
+       if (ntohl(saddr) <= ntohl(br->ip4_querier.addr.u.ip4))
+               goto update;
+
+       return false;
+
+update:
+       br->ip4_querier.addr.u.ip4 = saddr;
+
+       return true;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static bool br_ip6_multicast_select_querier(struct net_bridge *br,
+                                           struct in6_addr *saddr)
+{
+       if (!timer_pending(&br->ip6_own_query.timer) &&
+           !timer_pending(&br->ip6_other_query.timer))
+               goto update;
+
+       if (ipv6_addr_cmp(saddr, &br->ip6_querier.addr.u.ip6) <= 0)
+               goto update;
+
+       return false;
+
+update:
+       br->ip6_querier.addr.u.ip6 = *saddr;
+
+       return true;
+}
+#endif
+
+static bool br_multicast_select_querier(struct net_bridge *br,
+                                       struct br_ip *saddr)
+{
+       switch (saddr->proto) {
+       case htons(ETH_P_IP):
+               return br_ip4_multicast_select_querier(br, saddr->u.ip4);
+#if IS_ENABLED(CONFIG_IPV6)
+       case htons(ETH_P_IPV6):
+               return br_ip6_multicast_select_querier(br, &saddr->u.ip6);
+#endif
+       }
+
+       return false;
+}
+
 static void
-br_multicast_update_querier_timer(struct net_bridge *br,
-                                 struct bridge_mcast_querier *querier,
-                                 unsigned long max_delay)
+br_multicast_update_query_timer(struct net_bridge *br,
+                               struct bridge_mcast_other_query *query,
+                               unsigned long max_delay)
 {
-       if (!timer_pending(&querier->timer))
-               querier->delay_time = jiffies + max_delay;
+       if (!timer_pending(&query->timer))
+               query->delay_time = jiffies + max_delay;
 
-       mod_timer(&querier->timer, jiffies + br->multicast_querier_interval);
+       mod_timer(&query->timer, jiffies + br->multicast_querier_interval);
 }
 
 /*
@@ -1125,16 +1196,14 @@ timer:
 
 static void br_multicast_query_received(struct net_bridge *br,
                                        struct net_bridge_port *port,
-                                       struct bridge_mcast_querier *querier,
-                                       int saddr,
-                                       bool is_general_query,
+                                       struct bridge_mcast_other_query *query,
+                                       struct br_ip *saddr,
                                        unsigned long max_delay)
 {
-       if (saddr && is_general_query)
-               br_multicast_update_querier_timer(br, querier, max_delay);
-       else if (timer_pending(&querier->timer))
+       if (!br_multicast_select_querier(br, saddr))
                return;
 
+       br_multicast_update_query_timer(br, query, max_delay);
        br_multicast_mark_router(br, port);
 }
 
@@ -1149,6 +1218,7 @@ static int br_ip4_multicast_query(struct net_bridge *br,
        struct igmpv3_query *ih3;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
+       struct br_ip saddr;
        unsigned long max_delay;
        unsigned long now = jiffies;
        __be32 group;
@@ -1190,11 +1260,14 @@ static int br_ip4_multicast_query(struct net_bridge *br,
                goto out;
        }
 
-       br_multicast_query_received(br, port, &br->ip4_querier, !!iph->saddr,
-                                   !group, max_delay);
+       if (!group) {
+               saddr.proto = htons(ETH_P_IP);
+               saddr.u.ip4 = iph->saddr;
 
-       if (!group)
+               br_multicast_query_received(br, port, &br->ip4_other_query,
+                                           &saddr, max_delay);
                goto out;
+       }
 
        mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group, vid);
        if (!mp)
@@ -1234,6 +1307,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
        struct mld2_query *mld2q;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
+       struct br_ip saddr;
        unsigned long max_delay;
        unsigned long now = jiffies;
        const struct in6_addr *group = NULL;
@@ -1282,12 +1356,14 @@ static int br_ip6_multicast_query(struct net_bridge *br,
                goto out;
        }
 
-       br_multicast_query_received(br, port, &br->ip6_querier,
-                                   !ipv6_addr_any(&ip6h->saddr),
-                                   is_general_query, max_delay);
+       if (is_general_query) {
+               saddr.proto = htons(ETH_P_IPV6);
+               saddr.u.ip6 = ip6h->saddr;
 
-       if (!group)
+               br_multicast_query_received(br, port, &br->ip6_other_query,
+                                           &saddr, max_delay);
                goto out;
+       }
 
        mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group, vid);
        if (!mp)
@@ -1315,11 +1391,12 @@ out:
 }
 #endif
 
-static void br_multicast_leave_group(struct net_bridge *br,
-                                    struct net_bridge_port *port,
-                                    struct br_ip *group,
-                                    struct bridge_mcast_querier *querier,
-                                    struct bridge_mcast_query *query)
+static void
+br_multicast_leave_group(struct net_bridge *br,
+                        struct net_bridge_port *port,
+                        struct br_ip *group,
+                        struct bridge_mcast_other_query *other_query,
+                        struct bridge_mcast_own_query *own_query)
 {
        struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
@@ -1330,7 +1407,7 @@ static void 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(&querier->timer))
+           timer_pending(&other_query->timer))
                goto out;
 
        mdb = mlock_dereference(br->mdb, br);
@@ -1344,7 +1421,7 @@ static void br_multicast_leave_group(struct net_bridge *br,
                time = jiffies + br->multicast_last_member_count *
                                 br->multicast_last_member_interval;
 
-               mod_timer(&query->timer, time);
+               mod_timer(&own_query->timer, time);
 
                for (p = mlock_dereference(mp->ports, br);
                     p != NULL;
@@ -1425,17 +1502,19 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br,
                                         __u16 vid)
 {
        struct br_ip br_group;
-       struct bridge_mcast_query *query = port ? &port->ip4_query :
-                                                 &br->ip4_query;
+       struct bridge_mcast_own_query *own_query;
 
        if (ipv4_is_local_multicast(group))
                return;
 
+       own_query = port ? &port->ip4_own_query : &br->ip4_own_query;
+
        br_group.u.ip4 = group;
        br_group.proto = htons(ETH_P_IP);
        br_group.vid = vid;
 
-       br_multicast_leave_group(br, port, &br_group, &br->ip4_querier, query);
+       br_multicast_leave_group(br, port, &br_group, &br->ip4_other_query,
+                                own_query);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -1445,18 +1524,19 @@ static void br_ip6_multicast_leave_group(struct net_bridge *br,
                                         __u16 vid)
 {
        struct br_ip br_group;
-       struct bridge_mcast_query *query = port ? &port->ip6_query :
-                                                 &br->ip6_query;
-
+       struct bridge_mcast_own_query *own_query;
 
        if (ipv6_addr_is_ll_all_nodes(group))
                return;
 
+       own_query = port ? &port->ip6_own_query : &br->ip6_own_query;
+
        br_group.u.ip6 = *group;
        br_group.proto = htons(ETH_P_IPV6);
        br_group.vid = vid;
 
-       br_multicast_leave_group(br, port, &br_group, &br->ip6_querier, query);
+       br_multicast_leave_group(br, port, &br_group, &br->ip6_other_query,
+                                own_query);
 }
 #endif
 
@@ -1723,7 +1803,7 @@ int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
 }
 
 static void br_multicast_query_expired(struct net_bridge *br,
-                                      struct bridge_mcast_query *query)
+                                      struct bridge_mcast_own_query *query)
 {
        spin_lock(&br->multicast_lock);
        if (query->startup_sent < br->multicast_startup_query_count)
@@ -1737,7 +1817,7 @@ static void br_ip4_multicast_query_expired(unsigned long data)
 {
        struct net_bridge *br = (void *)data;
 
-       br_multicast_query_expired(br, &br->ip4_query);
+       br_multicast_query_expired(br, &br->ip4_own_query);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -1745,7 +1825,7 @@ static void br_ip6_multicast_query_expired(unsigned long data)
 {
        struct net_bridge *br = (void *)data;
 
-       br_multicast_query_expired(br, &br->ip6_query);
+       br_multicast_query_expired(br, &br->ip6_own_query);
 }
 #endif
 
@@ -1767,28 +1847,28 @@ void br_multicast_init(struct net_bridge *br)
        br->multicast_querier_interval = 255 * HZ;
        br->multicast_membership_interval = 260 * HZ;
 
-       br->ip4_querier.delay_time = 0;
+       br->ip4_other_query.delay_time = 0;
 #if IS_ENABLED(CONFIG_IPV6)
-       br->ip6_querier.delay_time = 0;
+       br->ip6_other_query.delay_time = 0;
 #endif
 
        spin_lock_init(&br->multicast_lock);
        setup_timer(&br->multicast_router_timer,
                    br_multicast_local_router_expired, 0);
-       setup_timer(&br->ip4_querier.timer, br_ip4_multicast_querier_expired,
-                   (unsigned long)br);
-       setup_timer(&br->ip4_query.timer, br_ip4_multicast_query_expired,
+       setup_timer(&br->ip4_other_query.timer,
+                   br_ip4_multicast_querier_expired, (unsigned long)br);
+       setup_timer(&br->ip4_own_query.timer, br_ip4_multicast_query_expired,
                    (unsigned long)br);
 #if IS_ENABLED(CONFIG_IPV6)
-       setup_timer(&br->ip6_querier.timer, br_ip6_multicast_querier_expired,
-                   (unsigned long)br);
-       setup_timer(&br->ip6_query.timer, br_ip6_multicast_query_expired,
+       setup_timer(&br->ip6_other_query.timer,
+                   br_ip6_multicast_querier_expired, (unsigned long)br);
+       setup_timer(&br->ip6_own_query.timer, br_ip6_multicast_query_expired,
                    (unsigned long)br);
 #endif
 }
 
 static void __br_multicast_open(struct net_bridge *br,
-                               struct bridge_mcast_query *query)
+                               struct bridge_mcast_own_query *query)
 {
        query->startup_sent = 0;
 
@@ -1800,9 +1880,9 @@ static void __br_multicast_open(struct net_bridge *br,
 
 void br_multicast_open(struct net_bridge *br)
 {
-       __br_multicast_open(br, &br->ip4_query);
+       __br_multicast_open(br, &br->ip4_own_query);
 #if IS_ENABLED(CONFIG_IPV6)
-       __br_multicast_open(br, &br->ip6_query);
+       __br_multicast_open(br, &br->ip6_own_query);
 #endif
 }
 
@@ -1815,11 +1895,11 @@ void br_multicast_stop(struct net_bridge *br)
        int i;
 
        del_timer_sync(&br->multicast_router_timer);
-       del_timer_sync(&br->ip4_querier.timer);
-       del_timer_sync(&br->ip4_query.timer);
+       del_timer_sync(&br->ip4_other_query.timer);
+       del_timer_sync(&br->ip4_own_query.timer);
 #if IS_ENABLED(CONFIG_IPV6)
-       del_timer_sync(&br->ip6_querier.timer);
-       del_timer_sync(&br->ip6_query.timer);
+       del_timer_sync(&br->ip6_other_query.timer);
+       del_timer_sync(&br->ip6_own_query.timer);
 #endif
 
        spin_lock_bh(&br->multicast_lock);
@@ -1923,7 +2003,7 @@ unlock:
 }
 
 static void br_multicast_start_querier(struct net_bridge *br,
-                                      struct bridge_mcast_query *query)
+                                      struct bridge_mcast_own_query *query)
 {
        struct net_bridge_port *port;
 
@@ -1934,11 +2014,11 @@ static void br_multicast_start_querier(struct net_bridge *br,
                    port->state == BR_STATE_BLOCKING)
                        continue;
 
-               if (query == &br->ip4_query)
-                       br_multicast_enable(&port->ip4_query);
+               if (query == &br->ip4_own_query)
+                       br_multicast_enable(&port->ip4_own_query);
 #if IS_ENABLED(CONFIG_IPV6)
                else
-                       br_multicast_enable(&port->ip6_query);
+                       br_multicast_enable(&port->ip6_own_query);
 #endif
        }
 }
@@ -1974,9 +2054,9 @@ rollback:
                        goto rollback;
        }
 
-       br_multicast_start_querier(br, &br->ip4_query);
+       br_multicast_start_querier(br, &br->ip4_own_query);
 #if IS_ENABLED(CONFIG_IPV6)
-       br_multicast_start_querier(br, &br->ip6_query);
+       br_multicast_start_querier(br, &br->ip6_own_query);
 #endif
 
 unlock:
@@ -2001,16 +2081,16 @@ int br_multicast_set_querier(struct net_bridge *br, unsigned long val)
 
        max_delay = br->multicast_query_response_interval;
 
-       if (!timer_pending(&br->ip4_querier.timer))
-               br->ip4_querier.delay_time = jiffies + max_delay;
+       if (!timer_pending(&br->ip4_other_query.timer))
+               br->ip4_other_query.delay_time = jiffies + max_delay;
 
-       br_multicast_start_querier(br, &br->ip4_query);
+       br_multicast_start_querier(br, &br->ip4_own_query);
 
 #if IS_ENABLED(CONFIG_IPV6)
-       if (!timer_pending(&br->ip6_querier.timer))
-               br->ip6_querier.delay_time = jiffies + max_delay;
+       if (!timer_pending(&br->ip6_other_query.timer))
+               br->ip6_other_query.delay_time = jiffies + max_delay;
 
-       br_multicast_start_querier(br, &br->ip6_query);
+       br_multicast_start_querier(br, &br->ip6_own_query);
 #endif
 
 unlock: