| From foo@baz Mon May 16 11:20:33 PDT 2016 |
| From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue> |
| Date: Wed, 4 May 2016 17:25:02 +0200 |
| Subject: bridge: fix igmp / mld query parsing |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue> |
| |
| [ Upstream commit 856ce5d083e14571d051301fe3c65b32b8cbe321 ] |
| |
| With the newly introduced helper functions the skb pulling is hidden |
| in the checksumming function - and undone before returning to the |
| caller. |
| |
| The IGMP and MLD query parsing functions in the bridge still |
| assumed that the skb is pointing to the beginning of the IGMP/MLD |
| message while it is now kept at the beginning of the IPv4/6 header. |
| |
| If there is a querier somewhere else, then this either causes |
| the multicast snooping to stay disabled even though it could be |
| enabled. Or, if we have the querier enabled too, then this can |
| create unnecessary IGMP / MLD query messages on the link. |
| |
| Fixing this by taking the offset between IP and IGMP/MLD header into |
| account, too. |
| |
| Fixes: 9afd85c9e455 ("net: Export IGMP/MLD message validation code") |
| Reported-by: Simon Wunderlich <sw@simonwunderlich.de> |
| Signed-off-by: Linus LΓΌssing <linus.luessing@c0d3.blue> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/bridge/br_multicast.c | 12 +++++++----- |
| 1 file changed, 7 insertions(+), 5 deletions(-) |
| |
| --- a/net/bridge/br_multicast.c |
| +++ b/net/bridge/br_multicast.c |
| @@ -1270,6 +1270,7 @@ static int br_ip4_multicast_query(struct |
| struct br_ip saddr; |
| unsigned long max_delay; |
| unsigned long now = jiffies; |
| + unsigned int offset = skb_transport_offset(skb); |
| __be32 group; |
| int err = 0; |
| |
| @@ -1280,14 +1281,14 @@ static int br_ip4_multicast_query(struct |
| |
| group = ih->group; |
| |
| - if (skb->len == sizeof(*ih)) { |
| + if (skb->len == offset + sizeof(*ih)) { |
| max_delay = ih->code * (HZ / IGMP_TIMER_SCALE); |
| |
| if (!max_delay) { |
| max_delay = 10 * HZ; |
| group = 0; |
| } |
| - } else if (skb->len >= sizeof(*ih3)) { |
| + } else if (skb->len >= offset + sizeof(*ih3)) { |
| ih3 = igmpv3_query_hdr(skb); |
| if (ih3->nsrcs) |
| goto out; |
| @@ -1348,6 +1349,7 @@ static int br_ip6_multicast_query(struct |
| struct br_ip saddr; |
| unsigned long max_delay; |
| unsigned long now = jiffies; |
| + unsigned int offset = skb_transport_offset(skb); |
| const struct in6_addr *group = NULL; |
| bool is_general_query; |
| int err = 0; |
| @@ -1357,8 +1359,8 @@ static int br_ip6_multicast_query(struct |
| (port && port->state == BR_STATE_DISABLED)) |
| goto out; |
| |
| - if (skb->len == sizeof(*mld)) { |
| - if (!pskb_may_pull(skb, sizeof(*mld))) { |
| + if (skb->len == offset + sizeof(*mld)) { |
| + if (!pskb_may_pull(skb, offset + sizeof(*mld))) { |
| err = -EINVAL; |
| goto out; |
| } |
| @@ -1367,7 +1369,7 @@ static int br_ip6_multicast_query(struct |
| if (max_delay) |
| group = &mld->mld_mca; |
| } else { |
| - if (!pskb_may_pull(skb, sizeof(*mld2q))) { |
| + if (!pskb_may_pull(skb, offset + sizeof(*mld2q))) { |
| err = -EINVAL; |
| goto out; |
| } |