From: Felix Fietkau Date: Thu, 24 Jul 2014 09:12:59 +0000 (+0000) Subject: kernel: backport upstream bridge multicast snooping fixes X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fsvn-archive%2Farchive.git;a=commitdiff_plain;hb=48e42aaf617ba2a5bab89973886fa03fee9df922 kernel: backport upstream bridge multicast snooping fixes Signed-off-by: Felix Fietkau SVN-Revision: 41817 --- diff --git a/target/linux/generic/patches-3.10/070-net_bridge_backports.patch b/target/linux/generic/patches-3.10/070-net_bridge_backports.patch new file mode 100644 index 0000000000..3edc2cab5e --- /dev/null +++ b/target/linux/generic/patches-3.10/070-net_bridge_backports.patch @@ -0,0 +1,848 @@ +--- a/net/bridge/br_device.c ++++ b/net/bridge/br_device.c +@@ -67,7 +67,8 @@ netdev_tx_t br_dev_xmit(struct sk_buff * + } + + mdst = br_mdb_get(br, skb, vid); +- if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) ++ if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && ++ br_multicast_querier_exists(br, eth_hdr(skb))) + br_multicast_deliver(mdst, skb); + else + br_flood_deliver(br, skb); +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -98,7 +98,8 @@ int br_handle_frame_finish(struct sk_buf + skb2 = skb; + else if (is_multicast_ether_addr(dest)) { + mdst = br_mdb_get(br, skb, vid); +- if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) { ++ if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && ++ br_multicast_querier_exists(br, eth_hdr(skb))) { + if ((mdst && mdst->mglist) || + br_multicast_is_router(br)) + skb2 = skb; +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -23,16 +23,19 @@ + #include + #include + #include ++#include + #include + #if IS_ENABLED(CONFIG_IPV6) + #include + #include + #include ++#include + #endif + + #include "br_private.h" + +-static void br_multicast_start_querier(struct net_bridge *br); ++static void br_multicast_start_querier(struct net_bridge *br, ++ struct bridge_mcast_query *query); + unsigned int br_mdb_rehash_seq; + + static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b) +@@ -381,7 +384,8 @@ static struct sk_buff *br_ip4_multicast_ + iph->frag_off = htons(IP_DF); + iph->ttl = 1; + iph->protocol = IPPROTO_IGMP; +- iph->saddr = 0; ++ iph->saddr = br->multicast_query_use_ifaddr ? ++ inet_select_addr(br->dev, 0, RT_SCOPE_LINK) : 0; + iph->daddr = htonl(INADDR_ALLHOSTS_GROUP); + ((u8 *)&iph[1])[0] = IPOPT_RA; + ((u8 *)&iph[1])[1] = 4; +@@ -724,7 +728,7 @@ static int br_ip6_multicast_add_group(st + { + struct br_ip br_group; + +- if (!ipv6_is_transient_multicast(group)) ++ if (ipv6_addr_is_ll_all_nodes(group)) + return 0; + + br_group.u.ip6 = *group; +@@ -756,20 +760,35 @@ static void br_multicast_local_router_ex + { + } + +-static void br_multicast_querier_expired(unsigned long data) ++static void br_multicast_querier_expired(struct net_bridge *br, ++ struct bridge_mcast_query *query) + { +- struct net_bridge *br = (void *)data; +- + spin_lock(&br->multicast_lock); + if (!netif_running(br->dev) || br->multicast_disabled) + goto out; + +- br_multicast_start_querier(br); ++ br_multicast_start_querier(br, query); + + out: + spin_unlock(&br->multicast_lock); + } + ++static void br_ip4_multicast_querier_expired(unsigned long data) ++{ ++ struct net_bridge *br = (void *)data; ++ ++ br_multicast_querier_expired(br, &br->ip4_query); ++} ++ ++#if IS_ENABLED(CONFIG_IPV6) ++static void br_ip6_multicast_querier_expired(unsigned long data) ++{ ++ struct net_bridge *br = (void *)data; ++ ++ br_multicast_querier_expired(br, &br->ip6_query); ++} ++#endif ++ + static void __br_multicast_send_query(struct net_bridge *br, + struct net_bridge_port *port, + struct br_ip *ip) +@@ -790,37 +809,45 @@ static void __br_multicast_send_query(st + } + + static void br_multicast_send_query(struct net_bridge *br, +- struct net_bridge_port *port, u32 sent) ++ struct net_bridge_port *port, ++ struct bridge_mcast_query *query) + { + unsigned long time; + struct br_ip br_group; ++ struct bridge_mcast_querier *querier = NULL; + + if (!netif_running(br->dev) || br->multicast_disabled || +- !br->multicast_querier || +- timer_pending(&br->multicast_querier_timer)) ++ !br->multicast_querier) + return; + + memset(&br_group.u, 0, sizeof(br_group.u)); + +- br_group.proto = htons(ETH_P_IP); +- __br_multicast_send_query(br, port, &br_group); +- ++ if (port ? (query == &port->ip4_query) : ++ (query == &br->ip4_query)) { ++ querier = &br->ip4_querier; ++ br_group.proto = htons(ETH_P_IP); + #if IS_ENABLED(CONFIG_IPV6) +- br_group.proto = htons(ETH_P_IPV6); +- __br_multicast_send_query(br, port, &br_group); ++ } else { ++ querier = &br->ip6_querier; ++ br_group.proto = htons(ETH_P_IPV6); + #endif ++ } ++ ++ if (!querier || timer_pending(&querier->timer)) ++ return; ++ ++ __br_multicast_send_query(br, port, &br_group); + + time = jiffies; +- time += sent < br->multicast_startup_query_count ? ++ time += query->startup_sent < br->multicast_startup_query_count ? + br->multicast_startup_query_interval : + br->multicast_query_interval; +- mod_timer(port ? &port->multicast_query_timer : +- &br->multicast_query_timer, time); ++ mod_timer(&query->timer, time); + } + +-static void br_multicast_port_query_expired(unsigned long data) ++static void br_multicast_port_query_expired(struct net_bridge_port *port, ++ struct bridge_mcast_query *query) + { +- struct net_bridge_port *port = (void *)data; + struct net_bridge *br = port->br; + + spin_lock(&br->multicast_lock); +@@ -828,25 +855,43 @@ static void br_multicast_port_query_expi + port->state == BR_STATE_BLOCKING) + goto out; + +- if (port->multicast_startup_queries_sent < +- br->multicast_startup_query_count) +- port->multicast_startup_queries_sent++; ++ if (query->startup_sent < br->multicast_startup_query_count) ++ query->startup_sent++; + +- br_multicast_send_query(port->br, port, +- port->multicast_startup_queries_sent); ++ br_multicast_send_query(port->br, port, query); + + out: + spin_unlock(&br->multicast_lock); + } + ++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); ++} ++ ++#if IS_ENABLED(CONFIG_IPV6) ++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); ++} ++#endif ++ + void br_multicast_add_port(struct net_bridge_port *port) + { + port->multicast_router = 1; + + setup_timer(&port->multicast_router_timer, br_multicast_router_expired, + (unsigned long)port); +- setup_timer(&port->multicast_query_timer, +- br_multicast_port_query_expired, (unsigned long)port); ++ setup_timer(&port->ip4_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); ++#endif + } + + void br_multicast_del_port(struct net_bridge_port *port) +@@ -854,13 +899,13 @@ void br_multicast_del_port(struct net_br + del_timer_sync(&port->multicast_router_timer); + } + +-static void __br_multicast_enable_port(struct net_bridge_port *port) ++static void br_multicast_enable(struct bridge_mcast_query *query) + { +- port->multicast_startup_queries_sent = 0; ++ query->startup_sent = 0; + +- if (try_to_del_timer_sync(&port->multicast_query_timer) >= 0 || +- del_timer(&port->multicast_query_timer)) +- mod_timer(&port->multicast_query_timer, jiffies); ++ if (try_to_del_timer_sync(&query->timer) >= 0 || ++ del_timer(&query->timer)) ++ mod_timer(&query->timer, jiffies); + } + + void br_multicast_enable_port(struct net_bridge_port *port) +@@ -871,7 +916,10 @@ void br_multicast_enable_port(struct net + if (br->multicast_disabled || !netif_running(br->dev)) + goto out; + +- __br_multicast_enable_port(port); ++ br_multicast_enable(&port->ip4_query); ++#if IS_ENABLED(CONFIG_IPV6) ++ br_multicast_enable(&port->ip6_query); ++#endif + + out: + spin_unlock(&br->multicast_lock); +@@ -890,7 +938,10 @@ void br_multicast_disable_port(struct ne + if (!hlist_unhashed(&port->rlist)) + hlist_del_init_rcu(&port->rlist); + del_timer(&port->multicast_router_timer); +- del_timer(&port->multicast_query_timer); ++ del_timer(&port->ip4_query.timer); ++#if IS_ENABLED(CONFIG_IPV6) ++ del_timer(&port->ip6_query.timer); ++#endif + spin_unlock(&br->multicast_lock); + } + +@@ -1015,6 +1066,17 @@ static int br_ip6_multicast_mld2_report( + } + #endif + ++static void ++br_multicast_update_querier_timer(struct net_bridge *br, ++ struct bridge_mcast_querier *querier, ++ unsigned long max_delay) ++{ ++ if (!timer_pending(&querier->timer)) ++ querier->delay_time = jiffies + max_delay; ++ ++ mod_timer(&querier->timer, jiffies + br->multicast_querier_interval); ++} ++ + /* + * Add port to rotuer_list + * list is maintained ordered by pointer value +@@ -1065,12 +1127,13 @@ timer: + + static void br_multicast_query_received(struct net_bridge *br, + struct net_bridge_port *port, +- int saddr) ++ struct bridge_mcast_querier *querier, ++ int saddr, ++ unsigned long max_delay) + { + if (saddr) +- mod_timer(&br->multicast_querier_timer, +- jiffies + br->multicast_querier_interval); +- else if (timer_pending(&br->multicast_querier_timer)) ++ br_multicast_update_querier_timer(br, querier, max_delay); ++ else if (timer_pending(&querier->timer)) + return; + + br_multicast_mark_router(br, port); +@@ -1097,8 +1160,6 @@ static int br_ip4_multicast_query(struct + (port && port->state == BR_STATE_DISABLED)) + goto out; + +- br_multicast_query_received(br, port, !!iph->saddr); +- + group = ih->group; + + if (skb->len == sizeof(*ih)) { +@@ -1122,6 +1183,9 @@ static int br_ip4_multicast_query(struct + IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1; + } + ++ br_multicast_query_received(br, port, &br->ip4_querier, !!iph->saddr, ++ max_delay); ++ + if (!group) + goto out; + +@@ -1174,8 +1238,6 @@ static int br_ip6_multicast_query(struct + (port && port->state == BR_STATE_DISABLED)) + goto out; + +- br_multicast_query_received(br, port, !ipv6_addr_any(&ip6h->saddr)); +- + /* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */ + if (!(ipv6_addr_type(&ip6h->saddr) & IPV6_ADDR_LINKLOCAL)) { + err = -EINVAL; +@@ -1203,6 +1265,9 @@ static int br_ip6_multicast_query(struct + max_delay = max(msecs_to_jiffies(MLDV2_MRC(ntohs(mld2q->mld2q_mrc))), 1UL); + } + ++ br_multicast_query_received(br, port, &br->ip6_querier, ++ !ipv6_addr_any(&ip6h->saddr), max_delay); ++ + if (!group) + goto out; + +@@ -1235,7 +1300,9 @@ out: + + static void br_multicast_leave_group(struct net_bridge *br, + struct net_bridge_port *port, +- struct br_ip *group) ++ struct br_ip *group, ++ struct bridge_mcast_querier *querier, ++ struct bridge_mcast_query *query) + { + struct net_bridge_mdb_htable *mdb; + struct net_bridge_mdb_entry *mp; +@@ -1246,7 +1313,7 @@ static void br_multicast_leave_group(str + spin_lock(&br->multicast_lock); + if (!netif_running(br->dev) || + (port && port->state == BR_STATE_DISABLED) || +- timer_pending(&br->multicast_querier_timer)) ++ timer_pending(&querier->timer)) + goto out; + + mdb = mlock_dereference(br->mdb, br); +@@ -1254,6 +1321,31 @@ static void br_multicast_leave_group(str + if (!mp) + goto out; + ++ if (br->multicast_querier) { ++ __br_multicast_send_query(br, port, &mp->addr); ++ ++ time = jiffies + br->multicast_last_member_count * ++ br->multicast_last_member_interval; ++ ++ mod_timer(&query->timer, time); ++ ++ for (p = mlock_dereference(mp->ports, br); ++ p != NULL; ++ p = mlock_dereference(p->next, br)) { ++ if (p->port != port) ++ continue; ++ ++ if (!hlist_unhashed(&p->mglist) && ++ (timer_pending(&p->timer) ? ++ time_after(p->timer.expires, time) : ++ try_to_del_timer_sync(&p->timer) >= 0)) { ++ mod_timer(&p->timer, time); ++ } ++ ++ break; ++ } ++ } ++ + if (port && (port->flags & BR_MULTICAST_FAST_LEAVE)) { + struct net_bridge_port_group __rcu **pp; + +@@ -1306,7 +1398,6 @@ static void br_multicast_leave_group(str + + break; + } +- + out: + spin_unlock(&br->multicast_lock); + } +@@ -1317,6 +1408,8 @@ static void br_ip4_multicast_leave_group + __u16 vid) + { + struct br_ip br_group; ++ struct bridge_mcast_query *query = port ? &port->ip4_query : ++ &br->ip4_query; + + if (ipv4_is_local_multicast(group)) + return; +@@ -1325,7 +1418,7 @@ static void br_ip4_multicast_leave_group + br_group.proto = htons(ETH_P_IP); + br_group.vid = vid; + +- br_multicast_leave_group(br, port, &br_group); ++ br_multicast_leave_group(br, port, &br_group, &br->ip4_querier, query); + } + + #if IS_ENABLED(CONFIG_IPV6) +@@ -1335,15 +1428,18 @@ static void br_ip6_multicast_leave_group + __u16 vid) + { + struct br_ip br_group; ++ struct bridge_mcast_query *query = port ? &port->ip6_query : ++ &br->ip6_query; + +- if (!ipv6_is_transient_multicast(group)) ++ ++ if (ipv6_addr_is_ll_all_nodes(group)) + return; + + 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_multicast_leave_group(br, port, &br_group, &br->ip6_querier, query); + } + #endif + +@@ -1473,8 +1569,14 @@ static int br_multicast_ipv6_rcv(struct + * - MLD has always Router Alert hop-by-hop option + * - But we do not support jumbrograms. + */ +- if (ip6h->version != 6 || +- ip6h->nexthdr != IPPROTO_HOPOPTS || ++ 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; + +@@ -1605,19 +1707,32 @@ int br_multicast_rcv(struct net_bridge * + return 0; + } + +-static void br_multicast_query_expired(unsigned long data) ++static void br_multicast_query_expired(struct net_bridge *br, ++ struct bridge_mcast_query *query) ++{ ++ spin_lock(&br->multicast_lock); ++ if (query->startup_sent < br->multicast_startup_query_count) ++ query->startup_sent++; ++ ++ br_multicast_send_query(br, NULL, query); ++ spin_unlock(&br->multicast_lock); ++} ++ ++static void br_ip4_multicast_query_expired(unsigned long data) + { + struct net_bridge *br = (void *)data; + +- spin_lock(&br->multicast_lock); +- if (br->multicast_startup_queries_sent < +- br->multicast_startup_query_count) +- br->multicast_startup_queries_sent++; ++ br_multicast_query_expired(br, &br->ip4_query); ++} + +- br_multicast_send_query(br, NULL, br->multicast_startup_queries_sent); ++#if IS_ENABLED(CONFIG_IPV6) ++static void br_ip6_multicast_query_expired(unsigned long data) ++{ ++ struct net_bridge *br = (void *)data; + +- spin_unlock(&br->multicast_lock); ++ br_multicast_query_expired(br, &br->ip6_query); + } ++#endif + + void br_multicast_init(struct net_bridge *br) + { +@@ -1626,6 +1741,7 @@ void br_multicast_init(struct net_bridge + + br->multicast_router = 1; + br->multicast_querier = 0; ++ br->multicast_query_use_ifaddr = 0; + br->multicast_last_member_count = 2; + br->multicast_startup_query_count = 2; + +@@ -1636,23 +1752,43 @@ void br_multicast_init(struct net_bridge + br->multicast_querier_interval = 255 * HZ; + br->multicast_membership_interval = 260 * HZ; + ++ br->ip4_querier.delay_time = 0; ++#if IS_ENABLED(CONFIG_IPV6) ++ br->ip6_querier.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->multicast_querier_timer, +- br_multicast_querier_expired, (unsigned long)br); +- setup_timer(&br->multicast_query_timer, br_multicast_query_expired, ++ 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, ++ (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, ++ (unsigned long)br); ++#endif + } + +-void br_multicast_open(struct net_bridge *br) ++static void __br_multicast_open(struct net_bridge *br, ++ struct bridge_mcast_query *query) + { +- br->multicast_startup_queries_sent = 0; ++ query->startup_sent = 0; + + if (br->multicast_disabled) + return; + +- mod_timer(&br->multicast_query_timer, jiffies); ++ mod_timer(&query->timer, jiffies); ++} ++ ++void br_multicast_open(struct net_bridge *br) ++{ ++ __br_multicast_open(br, &br->ip4_query); ++#if IS_ENABLED(CONFIG_IPV6) ++ __br_multicast_open(br, &br->ip6_query); ++#endif + } + + void br_multicast_stop(struct net_bridge *br) +@@ -1664,8 +1800,12 @@ void br_multicast_stop(struct net_bridge + int i; + + del_timer_sync(&br->multicast_router_timer); +- del_timer_sync(&br->multicast_querier_timer); +- del_timer_sync(&br->multicast_query_timer); ++ del_timer_sync(&br->ip4_querier.timer); ++ del_timer_sync(&br->ip4_query.timer); ++#if IS_ENABLED(CONFIG_IPV6) ++ del_timer_sync(&br->ip6_querier.timer); ++ del_timer_sync(&br->ip6_query.timer); ++#endif + + spin_lock_bh(&br->multicast_lock); + mdb = mlock_dereference(br->mdb, br); +@@ -1767,18 +1907,24 @@ unlock: + return err; + } + +-static void br_multicast_start_querier(struct net_bridge *br) ++static void br_multicast_start_querier(struct net_bridge *br, ++ struct bridge_mcast_query *query) + { + struct net_bridge_port *port; + +- br_multicast_open(br); ++ __br_multicast_open(br, query); + + list_for_each_entry(port, &br->port_list, list) { + if (port->state == BR_STATE_DISABLED || + port->state == BR_STATE_BLOCKING) + continue; + +- __br_multicast_enable_port(port); ++ if (query == &br->ip4_query) ++ br_multicast_enable(&port->ip4_query); ++#if IS_ENABLED(CONFIG_IPV6) ++ else ++ br_multicast_enable(&port->ip6_query); ++#endif + } + } + +@@ -1813,7 +1959,10 @@ rollback: + goto rollback; + } + +- br_multicast_start_querier(br); ++ br_multicast_start_querier(br, &br->ip4_query); ++#if IS_ENABLED(CONFIG_IPV6) ++ br_multicast_start_querier(br, &br->ip6_query); ++#endif + + unlock: + spin_unlock_bh(&br->multicast_lock); +@@ -1823,6 +1972,8 @@ unlock: + + int br_multicast_set_querier(struct net_bridge *br, unsigned long val) + { ++ unsigned long max_delay; ++ + val = !!val; + + spin_lock_bh(&br->multicast_lock); +@@ -1830,8 +1981,22 @@ int br_multicast_set_querier(struct net_ + goto unlock; + + br->multicast_querier = val; +- if (val) +- br_multicast_start_querier(br); ++ if (!val) ++ goto unlock; ++ ++ max_delay = br->multicast_query_response_interval; ++ ++ if (!timer_pending(&br->ip4_querier.timer)) ++ br->ip4_querier.delay_time = jiffies + max_delay; ++ ++ br_multicast_start_querier(br, &br->ip4_query); ++ ++#if IS_ENABLED(CONFIG_IPV6) ++ if (!timer_pending(&br->ip6_querier.timer)) ++ br->ip6_querier.delay_time = jiffies + max_delay; ++ ++ br_multicast_start_querier(br, &br->ip6_query); ++#endif + + unlock: + spin_unlock_bh(&br->multicast_lock); +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -66,6 +66,20 @@ struct br_ip + __u16 vid; + }; + ++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING ++/* our own querier */ ++struct bridge_mcast_query { ++ struct timer_list timer; ++ u32 startup_sent; ++}; ++ ++/* other querier */ ++struct bridge_mcast_querier { ++ struct timer_list timer; ++ unsigned long delay_time; ++}; ++#endif ++ + struct net_port_vlans { + u16 port_idx; + u16 pvid; +@@ -159,10 +173,12 @@ struct net_bridge_port + #define BR_ADMIN_COST 0x00000010 + + #ifdef CONFIG_BRIDGE_IGMP_SNOOPING +- u32 multicast_startup_queries_sent; ++ struct bridge_mcast_query ip4_query; ++#if IS_ENABLED(CONFIG_IPV6) ++ struct bridge_mcast_query ip6_query; ++#endif /* IS_ENABLED(CONFIG_IPV6) */ + unsigned char multicast_router; + struct timer_list multicast_router_timer; +- struct timer_list multicast_query_timer; + struct hlist_head mglist; + struct hlist_node rlist; + #endif +@@ -246,12 +262,12 @@ struct net_bridge + + u8 multicast_disabled:1; + u8 multicast_querier:1; ++ u8 multicast_query_use_ifaddr:1; + + u32 hash_elasticity; + u32 hash_max; + + u32 multicast_last_member_count; +- u32 multicast_startup_queries_sent; + u32 multicast_startup_query_count; + + unsigned long multicast_last_member_interval; +@@ -266,8 +282,12 @@ struct net_bridge + struct hlist_head router_list; + + struct timer_list multicast_router_timer; +- struct timer_list multicast_querier_timer; +- struct timer_list multicast_query_timer; ++ struct bridge_mcast_querier ip4_querier; ++ struct bridge_mcast_query ip4_query; ++#if IS_ENABLED(CONFIG_IPV6) ++ struct bridge_mcast_querier ip6_querier; ++ struct bridge_mcast_query ip6_query; ++#endif /* IS_ENABLED(CONFIG_IPV6) */ + #endif + + struct timer_list hello_timer; +@@ -477,22 +497,35 @@ extern void br_mdb_notify(struct net_dev + #define mlock_dereference(X, br) \ + rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock)) + +-#if IS_ENABLED(CONFIG_IPV6) +-#include +-static inline int ipv6_is_transient_multicast(const struct in6_addr *addr) +-{ +- if (ipv6_addr_is_multicast(addr) && IPV6_ADDR_MC_FLAG_TRANSIENT(addr)) +- return 1; +- return 0; +-} +-#endif +- + static inline bool br_multicast_is_router(struct net_bridge *br) + { + return br->multicast_router == 2 || + (br->multicast_router == 1 && + timer_pending(&br->multicast_router_timer)); + } ++ ++static inline bool ++__br_multicast_querier_exists(struct net_bridge *br, ++ struct bridge_mcast_querier *querier) ++{ ++ return time_is_before_jiffies(querier->delay_time) && ++ (br->multicast_querier || timer_pending(&querier->timer)); ++} ++ ++static inline bool br_multicast_querier_exists(struct net_bridge *br, ++ struct ethhdr *eth) ++{ ++ switch (eth->h_proto) { ++ case (htons(ETH_P_IP)): ++ return __br_multicast_querier_exists(br, &br->ip4_querier); ++#if IS_ENABLED(CONFIG_IPV6) ++ case (htons(ETH_P_IPV6)): ++ return __br_multicast_querier_exists(br, &br->ip6_querier); ++#endif ++ default: ++ return false; ++ } ++} + #else + static inline int br_multicast_rcv(struct net_bridge *br, + struct net_bridge_port *port, +@@ -549,6 +582,11 @@ static inline bool br_multicast_is_route + { + return 0; + } ++static inline bool br_multicast_querier_exists(struct net_bridge *br, ++ struct ethhdr *eth) ++{ ++ return false; ++} + static inline void br_mdb_init(void) + { + } +--- a/net/bridge/br_sysfs_br.c ++++ b/net/bridge/br_sysfs_br.c +@@ -375,6 +375,31 @@ static ssize_t store_multicast_snooping( + static DEVICE_ATTR(multicast_snooping, S_IRUGO | S_IWUSR, + show_multicast_snooping, store_multicast_snooping); + ++static ssize_t show_multicast_query_use_ifaddr(struct device *d, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct net_bridge *br = to_bridge(d); ++ return sprintf(buf, "%d\n", br->multicast_query_use_ifaddr); ++} ++ ++static int set_query_use_ifaddr(struct net_bridge *br, unsigned long val) ++{ ++ br->multicast_query_use_ifaddr = !!val; ++ return 0; ++} ++ ++static ssize_t ++store_multicast_query_use_ifaddr(struct device *d, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ return store_bridge_parm(d, buf, len, set_query_use_ifaddr); ++} ++static DEVICE_ATTR(multicast_query_use_ifaddr, S_IRUGO | S_IWUSR, ++ show_multicast_query_use_ifaddr, ++ store_multicast_query_use_ifaddr); ++ + static ssize_t show_multicast_querier(struct device *d, + struct device_attribute *attr, + char *buf) +@@ -734,6 +759,7 @@ static struct attribute *bridge_attrs[] + &dev_attr_multicast_router.attr, + &dev_attr_multicast_snooping.attr, + &dev_attr_multicast_querier.attr, ++ &dev_attr_multicast_query_use_ifaddr.attr, + &dev_attr_hash_elasticity.attr, + &dev_attr_hash_max.attr, + &dev_attr_multicast_last_member_count.attr, +--- a/net/bridge/br_mdb.c ++++ b/net/bridge/br_mdb.c +@@ -9,6 +9,7 @@ + #include + #if IS_ENABLED(CONFIG_IPV6) + #include ++#include + #endif + + #include "br_private.h" +@@ -253,7 +254,7 @@ static bool is_valid_mdb_entry(struct br + return false; + #if IS_ENABLED(CONFIG_IPV6) + } else if (entry->addr.proto == htons(ETH_P_IPV6)) { +- if (!ipv6_is_transient_multicast(&entry->addr.u.ip6)) ++ if (ipv6_addr_is_ll_all_nodes(&entry->addr.u.ip6)) + return false; + #endif + } else +@@ -414,16 +415,20 @@ static int __br_mdb_del(struct net_bridg + if (!netif_running(br->dev) || br->multicast_disabled) + return -EINVAL; + +- if (timer_pending(&br->multicast_querier_timer)) +- return -EBUSY; +- + ip.proto = entry->addr.proto; +- if (ip.proto == htons(ETH_P_IP)) ++ if (ip.proto == htons(ETH_P_IP)) { ++ if (timer_pending(&br->ip4_querier.timer)) ++ return -EBUSY; ++ + ip.u.ip4 = entry->addr.u.ip4; + #if IS_ENABLED(CONFIG_IPV6) +- else ++ } else { ++ if (timer_pending(&br->ip6_querier.timer)) ++ return -EBUSY; ++ + ip.u.ip6 = entry->addr.u.ip6; + #endif ++ } + + spin_lock_bh(&br->multicast_lock); + mdb = mlock_dereference(br->mdb, br); diff --git a/target/linux/generic/patches-3.10/120-bridge_allow_receiption_on_disabled_port.patch b/target/linux/generic/patches-3.10/120-bridge_allow_receiption_on_disabled_port.patch index f64db0fa3c..7fe4bfd429 100644 --- a/target/linux/generic/patches-3.10/120-bridge_allow_receiption_on_disabled_port.patch +++ b/target/linux/generic/patches-3.10/120-bridge_allow_receiption_on_disabled_port.patch @@ -15,7 +15,7 @@ Signed-off-by: Felix Fietkau --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c -@@ -139,10 +139,14 @@ drop: +@@ -140,10 +140,14 @@ drop: static int br_handle_local_finish(struct sk_buff *skb) { struct net_bridge_port *p = br_port_get_rcu(skb->dev); @@ -33,7 +33,7 @@ Signed-off-by: Felix Fietkau return 0; /* process further */ } -@@ -212,6 +216,18 @@ rx_handler_result_t br_handle_frame(stru +@@ -213,6 +217,18 @@ rx_handler_result_t br_handle_frame(stru forward: switch (p->state) { diff --git a/target/linux/generic/patches-3.10/642-bridge_port_isolate.patch b/target/linux/generic/patches-3.10/642-bridge_port_isolate.patch index 7a5a074e83..e25479d2ea 100644 --- a/target/linux/generic/patches-3.10/642-bridge_port_isolate.patch +++ b/target/linux/generic/patches-3.10/642-bridge_port_isolate.patch @@ -1,13 +1,13 @@ --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h -@@ -157,6 +157,7 @@ struct net_bridge_port +@@ -171,6 +171,7 @@ struct net_bridge_port #define BR_ROOT_BLOCK 0x00000004 #define BR_MULTICAST_FAST_LEAVE 0x00000008 #define BR_ADMIN_COST 0x00000010 +#define BR_ISOLATE_MODE 0x00000020 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING - u32 multicast_startup_queries_sent; + struct bridge_mcast_query ip4_query; --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -159,6 +159,22 @@ BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPI @@ -43,7 +43,7 @@ --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c -@@ -114,8 +114,8 @@ int br_handle_frame_finish(struct sk_buf +@@ -115,8 +115,8 @@ int br_handle_frame_finish(struct sk_buf skb2 = skb; br->dev->stats.multicast++; diff --git a/target/linux/generic/patches-3.10/643-bridge_remove_ipv6_dependency.patch b/target/linux/generic/patches-3.10/643-bridge_remove_ipv6_dependency.patch index 407c980e01..e38e001689 100644 --- a/target/linux/generic/patches-3.10/643-bridge_remove_ipv6_dependency.patch +++ b/target/linux/generic/patches-3.10/643-bridge_remove_ipv6_dependency.patch @@ -25,7 +25,7 @@ Ethernet bridge, which means that the different Ethernet segments it --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile -@@ -44,3 +44,4 @@ obj-y += addrconf_core.o exthdrs_core.o +@@ -44,3 +44,4 @@ obj-y += addrconf_core.o exthdrs_core.o obj-$(CONFIG_INET) += output_core.o protocol.o $(ipv6-offload) obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o diff --git a/target/linux/generic/patches-3.10/644-bridge_optimize_netfilter_hooks.patch b/target/linux/generic/patches-3.10/644-bridge_optimize_netfilter_hooks.patch index c079b794ae..a9c5d689fe 100644 --- a/target/linux/generic/patches-3.10/644-bridge_optimize_netfilter_hooks.patch +++ b/target/linux/generic/patches-3.10/644-bridge_optimize_netfilter_hooks.patch @@ -38,7 +38,7 @@ netif_receive_skb); } -@@ -209,7 +209,7 @@ rx_handler_result_t br_handle_frame(stru +@@ -210,7 +210,7 @@ rx_handler_result_t br_handle_frame(stru } /* Deliver packet to local host only */ @@ -47,7 +47,7 @@ NULL, br_handle_local_finish)) { return RX_HANDLER_CONSUMED; /* consumed by filter */ } else { -@@ -224,7 +224,7 @@ forward: +@@ -225,7 +225,7 @@ forward: if (ether_addr_equal(p->br->dev->dev_addr, dest)) skb->pkt_type = PACKET_HOST; @@ -56,7 +56,7 @@ br_handle_local_finish)) break; -@@ -246,7 +246,7 @@ forward: +@@ -247,7 +247,7 @@ forward: if (ether_addr_equal(p->br->dev->dev_addr, dest)) skb->pkt_type = PACKET_HOST; @@ -67,7 +67,7 @@ default: --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c -@@ -783,7 +783,7 @@ static void __br_multicast_send_query(st +@@ -802,7 +802,7 @@ static void __br_multicast_send_query(st if (port) { __skb_push(skb, sizeof(struct ethhdr)); skb->dev = port->dev; @@ -96,7 +96,7 @@ if (vlan_tx_tag_present(skb)) --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h -@@ -696,15 +696,29 @@ static inline u16 br_get_pvid(const stru +@@ -734,15 +734,29 @@ static inline u16 br_get_pvid(const stru /* br_netfilter.c */ #ifdef CONFIG_BRIDGE_NETFILTER diff --git a/target/linux/generic/patches-3.10/645-bridge_multicast_to_unicast.patch b/target/linux/generic/patches-3.10/645-bridge_multicast_to_unicast.patch index f5b781bb17..9ee6b01df0 100644 --- a/target/linux/generic/patches-3.10/645-bridge_multicast_to_unicast.patch +++ b/target/linux/generic/patches-3.10/645-bridge_multicast_to_unicast.patch @@ -1,6 +1,6 @@ --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c -@@ -630,7 +630,8 @@ struct net_bridge_port_group *br_multica +@@ -634,7 +634,8 @@ struct net_bridge_port_group *br_multica struct net_bridge_port *port, struct br_ip *group, struct net_bridge_port_group __rcu *next, @@ -10,7 +10,7 @@ { struct net_bridge_port_group *p; -@@ -645,12 +646,33 @@ struct net_bridge_port_group *br_multica +@@ -649,12 +650,33 @@ struct net_bridge_port_group *br_multica hlist_add_head(&p->mglist, &port->mglist); setup_timer(&p->timer, br_multicast_port_group_expired, (unsigned long)p); @@ -45,7 +45,7 @@ { struct net_bridge_mdb_entry *mp; struct net_bridge_port_group *p; -@@ -677,13 +699,13 @@ static int br_multicast_add_group(struct +@@ -681,13 +703,13 @@ static int br_multicast_add_group(struct for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL; pp = &p->next) { @@ -61,7 +61,7 @@ if (unlikely(!p)) goto err; rcu_assign_pointer(*pp, p); -@@ -702,7 +724,7 @@ err: +@@ -706,7 +728,7 @@ err: static int br_ip4_multicast_add_group(struct net_bridge *br, struct net_bridge_port *port, __be32 group, @@ -70,7 +70,7 @@ { struct br_ip br_group; -@@ -713,14 +735,14 @@ static int br_ip4_multicast_add_group(st +@@ -717,14 +739,14 @@ static int br_ip4_multicast_add_group(st br_group.proto = htons(ETH_P_IP); br_group.vid = vid; @@ -87,7 +87,7 @@ { struct br_ip br_group; -@@ -731,7 +753,7 @@ static int br_ip6_multicast_add_group(st +@@ -735,7 +757,7 @@ static int br_ip6_multicast_add_group(st br_group.proto = htons(ETH_P_IPV6); br_group.vid = vid; @@ -96,7 +96,7 @@ } #endif -@@ -898,6 +920,7 @@ static int br_ip4_multicast_igmp3_report +@@ -949,6 +971,7 @@ static int br_ip4_multicast_igmp3_report struct net_bridge_port *port, struct sk_buff *skb) { @@ -104,7 +104,7 @@ struct igmpv3_report *ih; struct igmpv3_grec *grec; int i; -@@ -943,7 +966,7 @@ static int br_ip4_multicast_igmp3_report +@@ -994,7 +1017,7 @@ static int br_ip4_multicast_igmp3_report continue; } @@ -113,7 +113,7 @@ if (err) break; } -@@ -956,6 +979,7 @@ static int br_ip6_multicast_mld2_report( +@@ -1007,6 +1030,7 @@ static int br_ip6_multicast_mld2_report( struct net_bridge_port *port, struct sk_buff *skb) { @@ -121,7 +121,7 @@ struct icmp6hdr *icmp6h; struct mld2_grec *grec; int i; -@@ -1006,7 +1030,7 @@ static int br_ip6_multicast_mld2_report( +@@ -1057,7 +1081,7 @@ static int br_ip6_multicast_mld2_report( } err = br_ip6_multicast_add_group(br, port, &grec->grec_mca, @@ -130,17 +130,17 @@ if (!err) break; } -@@ -1235,7 +1259,8 @@ out: - - static void br_multicast_leave_group(struct net_bridge *br, +@@ -1302,7 +1326,8 @@ static void br_multicast_leave_group(str struct net_bridge_port *port, -- struct br_ip *group) -+ struct br_ip *group, + struct br_ip *group, + struct bridge_mcast_querier *querier, +- struct bridge_mcast_query *query) ++ struct bridge_mcast_query *query, + const unsigned char *src) { struct net_bridge_mdb_htable *mdb; struct net_bridge_mdb_entry *mp; -@@ -1260,7 +1285,7 @@ static void br_multicast_leave_group(str +@@ -1352,7 +1377,7 @@ static void br_multicast_leave_group(str for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL; pp = &p->next) { @@ -149,7 +149,7 @@ continue; rcu_assign_pointer(*pp, p->next); -@@ -1294,7 +1319,7 @@ static void br_multicast_leave_group(str +@@ -1386,7 +1411,7 @@ static void br_multicast_leave_group(str for (p = mlock_dereference(mp->ports, br); p != NULL; p = mlock_dereference(p->next, br)) { @@ -158,7 +158,7 @@ continue; if (!hlist_unhashed(&p->mglist) && -@@ -1313,8 +1338,8 @@ out: +@@ -1404,8 +1429,8 @@ out: static void br_ip4_multicast_leave_group(struct net_bridge *br, struct net_bridge_port *port, @@ -168,13 +168,14 @@ + const unsigned char *src) { struct br_ip br_group; - -@@ -1325,14 +1350,14 @@ static void br_ip4_multicast_leave_group + struct bridge_mcast_query *query = port ? &port->ip4_query : +@@ -1418,14 +1443,15 @@ static void br_ip4_multicast_leave_group br_group.proto = htons(ETH_P_IP); br_group.vid = vid; -- br_multicast_leave_group(br, port, &br_group); -+ br_multicast_leave_group(br, port, &br_group, src); +- br_multicast_leave_group(br, port, &br_group, &br->ip4_querier, query); ++ br_multicast_leave_group(br, port, &br_group, &br->ip4_querier, query, ++ src); } #if IS_ENABLED(CONFIG_IPV6) @@ -185,17 +186,18 @@ + __u16 vid, const unsigned char *src) { struct br_ip br_group; - -@@ -1343,7 +1368,7 @@ static void br_ip6_multicast_leave_group + struct bridge_mcast_query *query = port ? &port->ip6_query : +@@ -1439,7 +1465,8 @@ static void br_ip6_multicast_leave_group br_group.proto = htons(ETH_P_IPV6); br_group.vid = vid; -- br_multicast_leave_group(br, port, &br_group); -+ br_multicast_leave_group(br, port, &br_group, src); +- br_multicast_leave_group(br, port, &br_group, &br->ip6_querier, query); ++ br_multicast_leave_group(br, port, &br_group, &br->ip6_querier, query, ++ src); } #endif -@@ -1351,6 +1376,7 @@ static int br_multicast_ipv4_rcv(struct +@@ -1447,6 +1474,7 @@ static int br_multicast_ipv4_rcv(struct struct net_bridge_port *port, struct sk_buff *skb) { @@ -203,7 +205,7 @@ struct sk_buff *skb2 = skb; const struct iphdr *iph; struct igmphdr *ih; -@@ -1426,7 +1452,7 @@ static int br_multicast_ipv4_rcv(struct +@@ -1522,7 +1550,7 @@ static int br_multicast_ipv4_rcv(struct case IGMP_HOST_MEMBERSHIP_REPORT: case IGMPV2_HOST_MEMBERSHIP_REPORT: BR_INPUT_SKB_CB(skb)->mrouters_only = 1; @@ -212,7 +214,7 @@ break; case IGMPV3_HOST_MEMBERSHIP_REPORT: err = br_ip4_multicast_igmp3_report(br, port, skb2); -@@ -1435,7 +1461,7 @@ static int br_multicast_ipv4_rcv(struct +@@ -1531,7 +1559,7 @@ static int br_multicast_ipv4_rcv(struct err = br_ip4_multicast_query(br, port, skb2); break; case IGMP_HOST_LEAVE_MESSAGE: @@ -221,7 +223,7 @@ break; } -@@ -1452,6 +1478,7 @@ static int br_multicast_ipv6_rcv(struct +@@ -1548,6 +1576,7 @@ static int br_multicast_ipv6_rcv(struct struct net_bridge_port *port, struct sk_buff *skb) { @@ -229,7 +231,7 @@ struct sk_buff *skb2; const struct ipv6hdr *ip6h; u8 icmp6_type; -@@ -1557,7 +1584,8 @@ static int br_multicast_ipv6_rcv(struct +@@ -1659,7 +1688,8 @@ static int br_multicast_ipv6_rcv(struct } mld = (struct mld_msg *)skb_transport_header(skb2); BR_INPUT_SKB_CB(skb)->mrouters_only = 1; @@ -239,7 +241,7 @@ break; } case ICMPV6_MLD2_REPORT: -@@ -1574,7 +1602,7 @@ static int br_multicast_ipv6_rcv(struct +@@ -1676,7 +1706,7 @@ static int br_multicast_ipv6_rcv(struct goto out; } mld = (struct mld_msg *)skb_transport_header(skb2); @@ -250,7 +252,7 @@ --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h -@@ -101,6 +101,9 @@ struct net_bridge_port_group { +@@ -115,6 +115,9 @@ struct net_bridge_port_group { struct timer_list timer; struct br_ip addr; unsigned char state; @@ -260,15 +262,15 @@ }; struct net_bridge_mdb_entry -@@ -158,6 +161,7 @@ struct net_bridge_port +@@ -172,6 +175,7 @@ struct net_bridge_port #define BR_MULTICAST_FAST_LEAVE 0x00000008 #define BR_ADMIN_COST 0x00000010 #define BR_ISOLATE_MODE 0x00000020 +#define BR_MULTICAST_TO_UCAST 0x00000040 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING - u32 multicast_startup_queries_sent; -@@ -469,7 +473,8 @@ extern struct net_bridge_port_group *br_ + struct bridge_mcast_query ip4_query; +@@ -489,7 +493,8 @@ extern struct net_bridge_port_group *br_ struct net_bridge_port *port, struct br_ip *group, struct net_bridge_port_group *next, @@ -280,7 +282,7 @@ extern void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port, --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c -@@ -340,7 +340,7 @@ static int br_mdb_add_group(struct net_b +@@ -341,7 +341,7 @@ static int br_mdb_add_group(struct net_b break; } diff --git a/target/linux/generic/patches-3.10/657-qdisc_reduce_truesize.patch b/target/linux/generic/patches-3.10/657-qdisc_reduce_truesize.patch index 93ff7ba71d..e27d0a04d6 100644 --- a/target/linux/generic/patches-3.10/657-qdisc_reduce_truesize.patch +++ b/target/linux/generic/patches-3.10/657-qdisc_reduce_truesize.patch @@ -24,7 +24,7 @@ commont qdiscs. } --- a/net/sched/sch_fifo.c +++ b/net/sched/sch_fifo.c -@@ -29,17 +29,21 @@ static int bfifo_enqueue(struct sk_buff +@@ -29,17 +29,21 @@ static int bfifo_enqueue(struct sk_buff static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch) {