Skip to content

Commit

Permalink
bridge: disable snooping if there is no querier
Browse files Browse the repository at this point in the history
If there is no querier on a link then we won't get periodic reports and
therefore won't be able to learn about multicast listeners behind ports,
potentially leading to lost multicast packets, especially for multicast
listeners that joined before the creation of the bridge.

These lost multicast packets can appear since c5c2326
("bridge: Add multicast_querier toggle and disable queries by default")
in particular.

With this patch we are flooding multicast packets if our querier is
disabled and if we didn't detect any other querier.

A grace period of the Maximum Response Delay of the querier is added to
give multicast responses enough time to arrive and to be learned from
before disabling the flooding behaviour again.

Signed-off-by: Linus Lüssing <linus.luessing@web.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Linus Lüssing authored and David S. Miller committed Aug 1, 2013
1 parent cf3c4c0 commit b00589a
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 11 deletions.
3 changes: 2 additions & 1 deletion net/bridge/br_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
}

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))
br_multicast_deliver(mdst, skb);
else
br_flood_deliver(br, skb, false);
Expand Down
3 changes: 2 additions & 1 deletion net/bridge/br_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ int br_handle_frame_finish(struct sk_buff *skb)
unicast = false;
} 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)) {
if ((mdst && mdst->mglist) ||
br_multicast_is_router(br))
skb2 = skb;
Expand Down
39 changes: 30 additions & 9 deletions net/bridge/br_multicast.c
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,16 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
}
#endif

static void br_multicast_update_querier_timer(struct net_bridge *br,
unsigned long max_delay)
{
if (!timer_pending(&br->multicast_querier_timer))
br->multicast_querier_delay_time = jiffies + max_delay;

mod_timer(&br->multicast_querier_timer,
jiffies + br->multicast_querier_interval);
}

/*
* Add port to router_list
* list is maintained ordered by pointer value
Expand Down Expand Up @@ -1064,11 +1074,11 @@ static void br_multicast_mark_router(struct net_bridge *br,

static void br_multicast_query_received(struct net_bridge *br,
struct net_bridge_port *port,
int saddr)
int saddr,
unsigned long max_delay)
{
if (saddr)
mod_timer(&br->multicast_querier_timer,
jiffies + br->multicast_querier_interval);
br_multicast_update_querier_timer(br, max_delay);
else if (timer_pending(&br->multicast_querier_timer))
return;

Expand Down Expand Up @@ -1096,8 +1106,6 @@ static int br_ip4_multicast_query(struct net_bridge *br,
(port && port->state == BR_STATE_DISABLED))
goto out;

br_multicast_query_received(br, port, !!iph->saddr);

group = ih->group;

if (skb->len == sizeof(*ih)) {
Expand All @@ -1121,6 +1129,8 @@ static int br_ip4_multicast_query(struct net_bridge *br,
IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
}

br_multicast_query_received(br, port, !!iph->saddr, max_delay);

if (!group)
goto out;

Expand Down Expand Up @@ -1176,8 +1186,6 @@ static int br_ip6_multicast_query(struct net_bridge *br,
(port && port->state == BR_STATE_DISABLED))
goto out;

br_multicast_query_received(br, port, !ipv6_addr_any(&ip6h->saddr));

if (skb->len == sizeof(*mld)) {
if (!pskb_may_pull(skb, sizeof(*mld))) {
err = -EINVAL;
Expand All @@ -1198,6 +1206,9 @@ static int br_ip6_multicast_query(struct net_bridge *br,
max_delay = mld2q->mld2q_mrc ? MLDV2_MRC(ntohs(mld2q->mld2q_mrc)) : 1;
}

br_multicast_query_received(br, port, !ipv6_addr_any(&ip6h->saddr),
max_delay);

if (!group)
goto out;

Expand Down Expand Up @@ -1643,6 +1654,8 @@ void br_multicast_init(struct net_bridge *br)
br->multicast_querier_interval = 255 * HZ;
br->multicast_membership_interval = 260 * HZ;

br->multicast_querier_delay_time = 0;

spin_lock_init(&br->multicast_lock);
setup_timer(&br->multicast_router_timer,
br_multicast_local_router_expired, 0);
Expand Down Expand Up @@ -1831,15 +1844,23 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val)

int br_multicast_set_querier(struct net_bridge *br, unsigned long val)
{
unsigned long max_delay;

val = !!val;

spin_lock_bh(&br->multicast_lock);
if (br->multicast_querier == val)
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->multicast_querier_timer))
br->multicast_querier_delay_time = jiffies + max_delay;

br_multicast_start_querier(br);

unlock:
spin_unlock_bh(&br->multicast_lock);
Expand Down
12 changes: 12 additions & 0 deletions net/bridge/br_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ struct net_bridge
unsigned long multicast_query_interval;
unsigned long multicast_query_response_interval;
unsigned long multicast_startup_query_interval;
unsigned long multicast_querier_delay_time;

spinlock_t multicast_lock;
struct net_bridge_mdb_htable __rcu *mdb;
Expand Down Expand Up @@ -501,6 +502,13 @@ static inline bool br_multicast_is_router(struct net_bridge *br)
(br->multicast_router == 1 &&
timer_pending(&br->multicast_router_timer));
}

static inline bool br_multicast_querier_exists(struct net_bridge *br)
{
return time_is_before_jiffies(br->multicast_querier_delay_time) &&
(br->multicast_querier ||
timer_pending(&br->multicast_querier_timer));
}
#else
static inline int br_multicast_rcv(struct net_bridge *br,
struct net_bridge_port *port,
Expand Down Expand Up @@ -557,6 +565,10 @@ static inline bool br_multicast_is_router(struct net_bridge *br)
{
return 0;
}
static inline bool br_multicast_querier_exists(struct net_bridge *br)
{
return false;
}
static inline void br_mdb_init(void)
{
}
Expand Down

0 comments on commit b00589a

Please sign in to comment.