2013-10-19 22:59:41

by Linus Lüssing

[permalink] [raw]
Subject: [PATCH] Revert "bridge: only expire the mdb entry when query is received"

While this commit was a good attempt to fix issues occuring when no
multicast querier is present, this commit still has two more issues:

1) There are cases where mdb entries do not expire even if there is a
querier present. The bridge will unnecessarily continue flooding
multicast packets on the according ports.

2) Never removing an mdb entry could be exploited for a Denial of
Service by an attacker on the local link, slowly, but steadily eating up
all memory.

Actually, this commit became obsolete with
"bridge: disable snooping if there is no querier" (b00589af3b)
which included fixes for a few more cases.

Therefore reverting the following commits (the commit stated in the
commit message plus three of its follow up fixes):

---
Revert "bridge: update mdb expiration timer upon reports."
This reverts commit f144febd93d5ee534fdf23505ab091b2b9088edc.
Revert "bridge: do not call setup_timer() multiple times"
This reverts commit 1faabf2aab1fdaa1ace4e8c829d1b9cf7bfec2f1.
Revert "bridge: fix some kernel warning in multicast timer"
This reverts commit c7e8e8a8f7a70b343ca1e0f90a31e35ab2d16de1.
Revert "bridge: only expire the mdb entry when query is received"
This reverts commit 9f00b2e7cf241fa389733d41b615efdaa2cb0f5b.
---

CC: Cong Wang <[email protected]>
Signed-off-by: Linus Lüssing <[email protected]>
---
net/bridge/br_mdb.c | 2 +-
net/bridge/br_multicast.c | 47 ++++++++++++++++++++++++++-------------------
net/bridge/br_private.h | 1 -
3 files changed, 28 insertions(+), 22 deletions(-)

diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
index 85a09bb..b7b1914 100644
--- a/net/bridge/br_mdb.c
+++ b/net/bridge/br_mdb.c
@@ -453,7 +453,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
call_rcu_bh(&p->rcu, br_multicast_free_pg);
err = 0;

- if (!mp->ports && !mp->mglist && mp->timer_armed &&
+ if (!mp->ports && !mp->mglist &&
netif_running(br->dev))
mod_timer(&mp->timer, jiffies);
break;
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 1085f21..8b0b610 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -272,7 +272,7 @@ static void br_multicast_del_pg(struct net_bridge *br,
del_timer(&p->timer);
call_rcu_bh(&p->rcu, br_multicast_free_pg);

- if (!mp->ports && !mp->mglist && mp->timer_armed &&
+ if (!mp->ports && !mp->mglist &&
netif_running(br->dev))
mod_timer(&mp->timer, jiffies);

@@ -611,9 +611,6 @@ rehash:
break;

default:
- /* If we have an existing entry, update it's expire timer */
- mod_timer(&mp->timer,
- jiffies + br->multicast_membership_interval);
goto out;
}

@@ -623,7 +620,6 @@ rehash:

mp->br = br;
mp->addr = *group;
-
setup_timer(&mp->timer, br_multicast_group_expired,
(unsigned long)mp);

@@ -663,6 +659,7 @@ static int br_multicast_add_group(struct net_bridge *br,
struct net_bridge_mdb_entry *mp;
struct net_bridge_port_group *p;
struct net_bridge_port_group __rcu **pp;
+ unsigned long now = jiffies;
int err;

spin_lock(&br->multicast_lock);
@@ -677,18 +674,15 @@ static int br_multicast_add_group(struct net_bridge *br,

if (!port) {
mp->mglist = true;
+ mod_timer(&mp->timer, now + br->multicast_membership_interval);
goto out;
}

for (pp = &mp->ports;
(p = mlock_dereference(*pp, br)) != NULL;
pp = &p->next) {
- if (p->port == port) {
- /* We already have a portgroup, update the timer. */
- mod_timer(&p->timer,
- jiffies + br->multicast_membership_interval);
- goto out;
- }
+ if (p->port == port)
+ goto found;
if ((unsigned long)p->port < (unsigned long)port)
break;
}
@@ -699,6 +693,8 @@ static int br_multicast_add_group(struct net_bridge *br,
rcu_assign_pointer(*pp, p);
br_mdb_notify(br->dev, port, group, RTM_NEWMDB);

+found:
+ mod_timer(&p->timer, now + br->multicast_membership_interval);
out:
err = 0;

@@ -1198,9 +1194,6 @@ static int br_ip4_multicast_query(struct net_bridge *br,
if (!mp)
goto out;

- mod_timer(&mp->timer, now + br->multicast_membership_interval);
- mp->timer_armed = true;
-
max_delay *= br->multicast_last_member_count;

if (mp->mglist &&
@@ -1277,9 +1270,6 @@ static int br_ip6_multicast_query(struct net_bridge *br,
if (!mp)
goto out;

- mod_timer(&mp->timer, now + br->multicast_membership_interval);
- mp->timer_armed = true;
-
max_delay *= br->multicast_last_member_count;
if (mp->mglist &&
(timer_pending(&mp->timer) ?
@@ -1365,7 +1355,7 @@ static void br_multicast_leave_group(struct net_bridge *br,
call_rcu_bh(&p->rcu, br_multicast_free_pg);
br_mdb_notify(br->dev, port, group, RTM_DELMDB);

- if (!mp->ports && !mp->mglist && mp->timer_armed &&
+ if (!mp->ports && !mp->mglist &&
netif_running(br->dev))
mod_timer(&mp->timer, jiffies);
}
@@ -1377,12 +1367,30 @@ static void br_multicast_leave_group(struct net_bridge *br,
br->multicast_last_member_interval;

if (!port) {
- if (mp->mglist && mp->timer_armed &&
+ if (mp->mglist &&
(timer_pending(&mp->timer) ?
time_after(mp->timer.expires, time) :
try_to_del_timer_sync(&mp->timer) >= 0)) {
mod_timer(&mp->timer, time);
}
+
+ goto out;
+ }
+
+ 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;
}
out:
spin_unlock(&br->multicast_lock);
@@ -1805,7 +1813,6 @@ void br_multicast_stop(struct net_bridge *br)
hlist_for_each_entry_safe(mp, n, &mdb->mhash[i],
hlist[ver]) {
del_timer(&mp->timer);
- mp->timer_armed = false;
call_rcu_bh(&mp->rcu, br_multicast_free_group);
}
}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 7ca2ae4..e14c33b 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -126,7 +126,6 @@ struct net_bridge_mdb_entry
struct timer_list timer;
struct br_ip addr;
bool mglist;
- bool timer_armed;
};

struct net_bridge_mdb_htable
--
1.7.10.4


2013-10-21 22:45:15

by David Miller

[permalink] [raw]
Subject: Re: [PATCH] Revert "bridge: only expire the mdb entry when query is received"

From: Linus L?ssing <[email protected]>
Date: Sun, 20 Oct 2013 00:58:57 +0200

> While this commit was a good attempt to fix issues occuring when no
> multicast querier is present, this commit still has two more issues:
>
> 1) There are cases where mdb entries do not expire even if there is a
> querier present. The bridge will unnecessarily continue flooding
> multicast packets on the according ports.
>
> 2) Never removing an mdb entry could be exploited for a Denial of
> Service by an attacker on the local link, slowly, but steadily eating up
> all memory.
>
> Actually, this commit became obsolete with
> "bridge: disable snooping if there is no querier" (b00589af3b)
> which included fixes for a few more cases.
>
> Therefore reverting the following commits (the commit stated in the
> commit message plus three of its follow up fixes):
>
> ---
> Revert "bridge: update mdb expiration timer upon reports."
> This reverts commit f144febd93d5ee534fdf23505ab091b2b9088edc.
> Revert "bridge: do not call setup_timer() multiple times"
> This reverts commit 1faabf2aab1fdaa1ace4e8c829d1b9cf7bfec2f1.
> Revert "bridge: fix some kernel warning in multicast timer"
> This reverts commit c7e8e8a8f7a70b343ca1e0f90a31e35ab2d16de1.
> Revert "bridge: only expire the mdb entry when query is received"
> This reverts commit 9f00b2e7cf241fa389733d41b615efdaa2cb0f5b.
> ---

Cong, and other bridge folks, please review this revert.

Thanks.

2013-10-22 03:02:26

by Cong Wang

[permalink] [raw]
Subject: Re: [PATCH] Revert "bridge: only expire the mdb entry when query is received"

On Sat, Oct 19, 2013 at 3:58 PM, Linus Lüssing <[email protected]> wrote:
> While this commit was a good attempt to fix issues occuring when no
> multicast querier is present, this commit still has two more issues:
>
> 1) There are cases where mdb entries do not expire even if there is a
> querier present. The bridge will unnecessarily continue flooding
> multicast packets on the according ports.
>
> 2) Never removing an mdb entry could be exploited for a Denial of
> Service by an attacker on the local link, slowly, but steadily eating up
> all memory.
>

I raised the first issue too when I sent the patch, but Herbert said
it is not a problem. So, I will leave this to Herbert to decide.

For me, your patch makes sense.

Thanks.

2013-10-22 13:13:52

by Vlad Yasevich

[permalink] [raw]
Subject: Re: [PATCH] Revert "bridge: only expire the mdb entry when query is received"

On 10/21/2013 06:45 PM, David Miller wrote:
> From: Linus L?ssing <[email protected]>
> Date: Sun, 20 Oct 2013 00:58:57 +0200
>
>> While this commit was a good attempt to fix issues occuring when no
>> multicast querier is present, this commit still has two more issues:
>>
>> 1) There are cases where mdb entries do not expire even if there is a
>> querier present. The bridge will unnecessarily continue flooding
>> multicast packets on the according ports.
>>
>> 2) Never removing an mdb entry could be exploited for a Denial of
>> Service by an attacker on the local link, slowly, but steadily eating up
>> all memory.
>>
>> Actually, this commit became obsolete with
>> "bridge: disable snooping if there is no querier" (b00589af3b)
>> which included fixes for a few more cases.
>>
>> Therefore reverting the following commits (the commit stated in the
>> commit message plus three of its follow up fixes):
>>
>> ---
>> Revert "bridge: update mdb expiration timer upon reports."
>> This reverts commit f144febd93d5ee534fdf23505ab091b2b9088edc.
>> Revert "bridge: do not call setup_timer() multiple times"
>> This reverts commit 1faabf2aab1fdaa1ace4e8c829d1b9cf7bfec2f1.
>> Revert "bridge: fix some kernel warning in multicast timer"
>> This reverts commit c7e8e8a8f7a70b343ca1e0f90a31e35ab2d16de1.
>> Revert "bridge: only expire the mdb entry when query is received"
>> This reverts commit 9f00b2e7cf241fa389733d41b615efdaa2cb0f5b.
>> ---
>
> Cong, and other bridge folks, please review this revert.
>
t http://vger.kernel.org/majordomo-info.html
>

Makes sense and make the implementation better follow the spec.
Looks like the issues seen before are resolved by the revert.

-vlad

2013-10-22 18:41:36

by David Miller

[permalink] [raw]
Subject: Re: [PATCH] Revert "bridge: only expire the mdb entry when query is received"

From: Linus L?ssing <[email protected]>
Date: Sun, 20 Oct 2013 00:58:57 +0200

> While this commit was a good attempt to fix issues occuring when no
> multicast querier is present, this commit still has two more issues:
>
> 1) There are cases where mdb entries do not expire even if there is a
> querier present. The bridge will unnecessarily continue flooding
> multicast packets on the according ports.
>
> 2) Never removing an mdb entry could be exploited for a Denial of
> Service by an attacker on the local link, slowly, but steadily eating up
> all memory.
>
> Actually, this commit became obsolete with
> "bridge: disable snooping if there is no querier" (b00589af3b)
> which included fixes for a few more cases.
>
> Therefore reverting the following commits (the commit stated in the
> commit message plus three of its follow up fixes):
>
> ---
> Revert "bridge: update mdb expiration timer upon reports."
> This reverts commit f144febd93d5ee534fdf23505ab091b2b9088edc.
> Revert "bridge: do not call setup_timer() multiple times"
> This reverts commit 1faabf2aab1fdaa1ace4e8c829d1b9cf7bfec2f1.
> Revert "bridge: fix some kernel warning in multicast timer"
> This reverts commit c7e8e8a8f7a70b343ca1e0f90a31e35ab2d16de1.
> Revert "bridge: only expire the mdb entry when query is received"
> This reverts commit 9f00b2e7cf241fa389733d41b615efdaa2cb0f5b.
> ---
>
> CC: Cong Wang <[email protected]>
> Signed-off-by: Linus L?ssing <[email protected]>

Applied, thanks a lot.