diff options
| author | Linus Lüssing <linus.luessing@web.de> | 2013-08-30 11:28:17 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2013-08-30 15:24:37 -0400 |
| commit | cc0fdd802859eaeb00e1c87dbb655594bed2844c (patch) | |
| tree | eb5643ad4e5c783b28a6d936a1a91bb40e905447 | |
| parent | 79f9ab7e0a8ec452e58b5a5267b9eb019ff493d0 (diff) | |
bridge: separate querier and query timer into IGMP/IPv4 and MLD/IPv6 ones
Currently we would still potentially suffer multicast packet loss if there
is just either an IGMP or an MLD querier: For the former case, we would
possibly drop IPv6 multicast packets, for the latter IPv4 ones. This is
because we are currently assuming that if either an IGMP or MLD querier
is present that the other one is present, too.
This patch makes the behaviour and fix added in
"bridge: disable snooping if there is no querier" (b00589af3b04)
to also work if there is either just an IGMP or an MLD querier on the
link: It refines the deactivation of the snooping to be protocol
specific by using separate timers for the snooped IGMP and MLD queries
as well as separate timers for our internal IGMP and MLD queriers.
Signed-off-by: Linus Lüssing <linus.luessing@web.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | net/bridge/br_device.c | 2 | ||||
| -rw-r--r-- | net/bridge/br_input.c | 2 | ||||
| -rw-r--r-- | net/bridge/br_mdb.c | 14 | ||||
| -rw-r--r-- | net/bridge/br_multicast.c | 258 | ||||
| -rw-r--r-- | net/bridge/br_private.h | 57 |
5 files changed, 240 insertions, 93 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 69363bd37f64..89659d4ed1f9 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c | |||
| @@ -71,7 +71,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) | |||
| 71 | 71 | ||
| 72 | mdst = br_mdb_get(br, skb, vid); | 72 | mdst = br_mdb_get(br, skb, vid); |
| 73 | if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && | 73 | if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && |
| 74 | br_multicast_querier_exists(br)) | 74 | br_multicast_querier_exists(br, eth_hdr(skb))) |
| 75 | br_multicast_deliver(mdst, skb); | 75 | br_multicast_deliver(mdst, skb); |
| 76 | else | 76 | else |
| 77 | br_flood_deliver(br, skb, false); | 77 | br_flood_deliver(br, skb, false); |
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 8c561c0aa636..a2fd37ec35f7 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c | |||
| @@ -102,7 +102,7 @@ int br_handle_frame_finish(struct sk_buff *skb) | |||
| 102 | } else if (is_multicast_ether_addr(dest)) { | 102 | } else if (is_multicast_ether_addr(dest)) { |
| 103 | mdst = br_mdb_get(br, skb, vid); | 103 | mdst = br_mdb_get(br, skb, vid); |
| 104 | if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && | 104 | if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && |
| 105 | br_multicast_querier_exists(br)) { | 105 | br_multicast_querier_exists(br, eth_hdr(skb))) { |
| 106 | if ((mdst && mdst->mglist) || | 106 | if ((mdst && mdst->mglist) || |
| 107 | br_multicast_is_router(br)) | 107 | br_multicast_is_router(br)) |
| 108 | skb2 = skb; | 108 | skb2 = skb; |
diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 0daae3ec2355..6319c4333c39 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c | |||
| @@ -414,16 +414,20 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry) | |||
| 414 | if (!netif_running(br->dev) || br->multicast_disabled) | 414 | if (!netif_running(br->dev) || br->multicast_disabled) |
| 415 | return -EINVAL; | 415 | return -EINVAL; |
| 416 | 416 | ||
| 417 | if (timer_pending(&br->multicast_querier_timer)) | ||
| 418 | return -EBUSY; | ||
| 419 | |||
| 420 | ip.proto = entry->addr.proto; | 417 | ip.proto = entry->addr.proto; |
| 421 | if (ip.proto == htons(ETH_P_IP)) | 418 | if (ip.proto == htons(ETH_P_IP)) { |
| 419 | if (timer_pending(&br->ip4_querier.timer)) | ||
| 420 | return -EBUSY; | ||
| 421 | |||
| 422 | ip.u.ip4 = entry->addr.u.ip4; | 422 | ip.u.ip4 = entry->addr.u.ip4; |
| 423 | #if IS_ENABLED(CONFIG_IPV6) | 423 | #if IS_ENABLED(CONFIG_IPV6) |
| 424 | else | 424 | } else { |
| 425 | if (timer_pending(&br->ip6_querier.timer)) | ||
| 426 | return -EBUSY; | ||
| 427 | |||
| 425 | ip.u.ip6 = entry->addr.u.ip6; | 428 | ip.u.ip6 = entry->addr.u.ip6; |
| 426 | #endif | 429 | #endif |
| 430 | } | ||
| 427 | 431 | ||
| 428 | spin_lock_bh(&br->multicast_lock); | 432 | spin_lock_bh(&br->multicast_lock); |
| 429 | mdb = mlock_dereference(br->mdb, br); | 433 | mdb = mlock_dereference(br->mdb, br); |
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 08e576ada0b2..9d1d0e66c357 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c | |||
| @@ -33,7 +33,8 @@ | |||
| 33 | 33 | ||
| 34 | #include "br_private.h" | 34 | #include "br_private.h" |
| 35 | 35 | ||
| 36 | static void br_multicast_start_querier(struct net_bridge *br); | 36 | static void br_multicast_start_querier(struct net_bridge *br, |
| 37 | struct bridge_mcast_query *query); | ||
| 37 | unsigned int br_mdb_rehash_seq; | 38 | unsigned int br_mdb_rehash_seq; |
| 38 | 39 | ||
| 39 | static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b) | 40 | static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b) |
| @@ -755,20 +756,35 @@ static void br_multicast_local_router_expired(unsigned long data) | |||
| 755 | { | 756 | { |
| 756 | } | 757 | } |
| 757 | 758 | ||
| 758 | static void br_multicast_querier_expired(unsigned long data) | 759 | static void br_multicast_querier_expired(struct net_bridge *br, |
| 760 | struct bridge_mcast_query *query) | ||
| 759 | { | 761 | { |
| 760 | struct net_bridge *br = (void *)data; | ||
| 761 | |||
| 762 | spin_lock(&br->multicast_lock); | 762 | spin_lock(&br->multicast_lock); |
| 763 | if (!netif_running(br->dev) || br->multicast_disabled) | 763 | if (!netif_running(br->dev) || br->multicast_disabled) |
| 764 | goto out; | 764 | goto out; |
| 765 | 765 | ||
| 766 | br_multicast_start_querier(br); | 766 | br_multicast_start_querier(br, query); |
| 767 | 767 | ||
| 768 | out: | 768 | out: |
| 769 | spin_unlock(&br->multicast_lock); | 769 | spin_unlock(&br->multicast_lock); |
| 770 | } | 770 | } |
| 771 | 771 | ||
| 772 | static void br_ip4_multicast_querier_expired(unsigned long data) | ||
| 773 | { | ||
| 774 | struct net_bridge *br = (void *)data; | ||
| 775 | |||
| 776 | br_multicast_querier_expired(br, &br->ip4_query); | ||
| 777 | } | ||
| 778 | |||
| 779 | #if IS_ENABLED(CONFIG_IPV6) | ||
| 780 | static void br_ip6_multicast_querier_expired(unsigned long data) | ||
| 781 | { | ||
| 782 | struct net_bridge *br = (void *)data; | ||
| 783 | |||
| 784 | br_multicast_querier_expired(br, &br->ip6_query); | ||
| 785 | } | ||
| 786 | #endif | ||
| 787 | |||
| 772 | static void __br_multicast_send_query(struct net_bridge *br, | 788 | static void __br_multicast_send_query(struct net_bridge *br, |
| 773 | struct net_bridge_port *port, | 789 | struct net_bridge_port *port, |
| 774 | struct br_ip *ip) | 790 | struct br_ip *ip) |
| @@ -789,37 +805,45 @@ static void __br_multicast_send_query(struct net_bridge *br, | |||
| 789 | } | 805 | } |
| 790 | 806 | ||
| 791 | static void br_multicast_send_query(struct net_bridge *br, | 807 | static void br_multicast_send_query(struct net_bridge *br, |
| 792 | struct net_bridge_port *port, u32 sent) | 808 | struct net_bridge_port *port, |
| 809 | struct bridge_mcast_query *query) | ||
| 793 | { | 810 | { |
| 794 | unsigned long time; | 811 | unsigned long time; |
| 795 | struct br_ip br_group; | 812 | struct br_ip br_group; |
| 813 | struct bridge_mcast_querier *querier = NULL; | ||
| 796 | 814 | ||
| 797 | if (!netif_running(br->dev) || br->multicast_disabled || | 815 | if (!netif_running(br->dev) || br->multicast_disabled || |
| 798 | !br->multicast_querier || | 816 | !br->multicast_querier) |
| 799 | timer_pending(&br->multicast_querier_timer)) | ||
| 800 | return; | 817 | return; |
| 801 | 818 | ||
| 802 | memset(&br_group.u, 0, sizeof(br_group.u)); | 819 | memset(&br_group.u, 0, sizeof(br_group.u)); |
| 803 | 820 | ||
| 804 | br_group.proto = htons(ETH_P_IP); | 821 | if (port ? (query == &port->ip4_query) : |
| 805 | __br_multicast_send_query(br, port, &br_group); | 822 | (query == &br->ip4_query)) { |
| 806 | 823 | querier = &br->ip4_querier; | |
| 824 | br_group.proto = htons(ETH_P_IP); | ||
| 807 | #if IS_ENABLED(CONFIG_IPV6) | 825 | #if IS_ENABLED(CONFIG_IPV6) |
| 808 | br_group.proto = htons(ETH_P_IPV6); | 826 | } else { |
| 809 | __br_multicast_send_query(br, port, &br_group); | 827 | querier = &br->ip6_querier; |
| 828 | br_group.proto = htons(ETH_P_IPV6); | ||
| 810 | #endif | 829 | #endif |
| 830 | } | ||
| 831 | |||
| 832 | if (!querier || timer_pending(&querier->timer)) | ||
| 833 | return; | ||
| 834 | |||
| 835 | __br_multicast_send_query(br, port, &br_group); | ||
| 811 | 836 | ||
| 812 | time = jiffies; | 837 | time = jiffies; |
| 813 | time += sent < br->multicast_startup_query_count ? | 838 | time += query->startup_sent < br->multicast_startup_query_count ? |
| 814 | br->multicast_startup_query_interval : | 839 | br->multicast_startup_query_interval : |
| 815 | br->multicast_query_interval; | 840 | br->multicast_query_interval; |
| 816 | mod_timer(port ? &port->multicast_query_timer : | 841 | mod_timer(&query->timer, time); |
| 817 | &br->multicast_query_timer, time); | ||
| 818 | } | 842 | } |
| 819 | 843 | ||
| 820 | static void br_multicast_port_query_expired(unsigned long data) | 844 | static void br_multicast_port_query_expired(struct net_bridge_port *port, |
| 845 | struct bridge_mcast_query *query) | ||
| 821 | { | 846 | { |
| 822 | struct net_bridge_port *port = (void *)data; | ||
| 823 | struct net_bridge *br = port->br; | 847 | struct net_bridge *br = port->br; |
| 824 | 848 | ||
| 825 | spin_lock(&br->multicast_lock); | 849 | spin_lock(&br->multicast_lock); |
| @@ -827,25 +851,43 | |||
