* Re: [patch 2/5] atm: use for_each_set_bit()
From: David Miller @ 2010-03-15 23:05 UTC (permalink / raw)
To: akpm; +Cc: netdev, akinobu.mita, chas
In-Reply-To: <201003112207.o2BM7o2e013489@imap1.linux-foundation.org>
From: akpm@linux-foundation.org
Date: Thu, 11 Mar 2010 14:07:50 -0800
> From: Akinobu Mita <akinobu.mita@gmail.com>
>
> Replace open-coded loop with for_each_set_bit().
>
> Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
> Cc: Chas Williams <chas@cmf.nrl.navy.mil>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Applied.
^ permalink raw reply
* Re: [patch 3/5] LL TEMAC driver: add non-Virtex 5 support
From: David Miller @ 2010-03-15 23:05 UTC (permalink / raw)
To: akpm; +Cc: netdev, jtyner, afleming, grant.likely
In-Reply-To: <201003112207.o2BM7qhv013492@imap1.linux-foundation.org>
From: akpm@linux-foundation.org
Date: Thu, 11 Mar 2010 14:07:51 -0800
> From: John Tyner <jtyner@cs.ucr.edu>
>
> Add support for using the LL TEMAC Ethernet driver on non-Virtex 5
> platforms by adding support for accessing the Soft DMA registers as if
> they were memory mapped instead of solely through the DCR's (available on
> the Virtex 5).
>
> Signed-off-by: John Tyner <jtyner@cs.ucr.edu>
> Cc: Andy Fleming <afleming@freescale.com>
> Cc: Grant Likely <grant.likely@secretlab.ca>
> Cc: David S. Miller <davem@davemloft.net>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This is still going through revision, so I'm holding off
on this.
^ permalink raw reply
* Re: [patch 4/5] obsolete config in kernel source: USE_INTERNAL_TIMER
From: David Miller @ 2010-03-15 23:05 UTC (permalink / raw)
To: akpm; +Cc: netdev, siccegge, roel.kluin
In-Reply-To: <201003112207.o2BM7rlU013495@imap1.linux-foundation.org>
From: akpm@linux-foundation.org
Date: Thu, 11 Mar 2010 14:07:52 -0800
> From: Christoph Egger <siccegge@stud.informatik.uni-erlangen.de>
>
> CONFIG_USE_INTERNAL_TIMER seems to be the remainings of some experiment.
> It is explicitely #undef-ed as not working, only referenced from one
> source file and rather aged.
>
> Hereby cleaning it from the kernel tree.
>
> Signed-off-by: Christoph Egger <siccegge@stud.informatik.uni-erlangen.de>
> Acked-by: Roel Kluin <roel.kluin@gmail.com>
> Cc: "David S. Miller" <davem@davemloft.net>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Applied.
^ permalink raw reply
* Re: [patch 5/5] obsolete config in kernel source: HSO_AUTOPM
From: David Miller @ 2010-03-15 23:06 UTC (permalink / raw)
To: akpm; +Cc: netdev, siccegge
In-Reply-To: <201003112207.o2BM7sbR013498@imap1.linux-foundation.org>
From: akpm@linux-foundation.org
Date: Thu, 11 Mar 2010 14:07:54 -0800
> From: Christoph Egger <siccegge@stud.informatik.uni-erlangen.de>
>
> CONFIG_HSO_AUTOPM is set by KConfig / set in the Kernel source, makefiles
> and won't be ever set this way, therefor simply removing the protected
> code.
>
> Signed-off-by: Christoph Egger <siccegge@stud.informatik.uni-erlangen.de>
> Cc: "David S. Miller" <davem@davemloft.net>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Applied.
^ permalink raw reply
* Re: [PATCH 0/3] gigaset fixes for 2.6.34-rc1
From: David Miller @ 2010-03-15 23:06 UTC (permalink / raw)
To: tilman; +Cc: isdn, hjlipp, alan, isdn4linux, i4ldeveloper, netdev,
linux-kernel
In-Reply-To: <cover.1268580322.git.tilman@imap.cc>
From: Tilman Schmidt <tilman@imap.cc>
Date: Sun, 14 Mar 2010 23:58:05 +0100 (CET)
> following are three bugfix patches to the Gigaset driver which should
> still go into 2.6.34 if possible, and also into the 2.6.33 stable branch.
All applied, thanks Tilman.
^ permalink raw reply
* Re: [patch] myri: remove dead code
From: David Miller @ 2010-03-15 23:06 UTC (permalink / raw)
To: gallatin; +Cc: error27, brice, kaber, netdev, kernel-janitors
In-Reply-To: <4B9E17BE.1080009@myri.com>
From: Andrew Gallatin <gallatin@myri.com>
Date: Mon, 15 Mar 2010 07:19:26 -0400
> Dan Carpenter wrote:
>> We can never reach the return statement.
>
> Agreed, thanks for pointing that out.
Applied, thanks everyone.
^ permalink raw reply
* Re: net-2.6 [Bug-Fix][dccp]: fix oops caused after failed initialisation
From: David Miller @ 2010-03-15 23:07 UTC (permalink / raw)
To: gerrit; +Cc: acme, netdev, dccp
In-Reply-To: <20100315061318.GA6110@gerrit.erg.abdn.ac.uk>
From: Gerrit Renker <gerrit@erg.abdn.ac.uk>
Date: Mon, 15 Mar 2010 07:13:19 +0100
> dccp: fix panic caused by failed initialisation
>
> This fixes a kernel panic reported thanks to Andre Noll:
>
> if DCCP is compiled into the kernel and any out of the initialisation
> steps in net/dccp/proto.c:dccp_init() fail, a subsequent attempt to create
> a SOCK_DCCP socket will panic, since inet{,6}_create() are not prevented
> from creating DCCP sockets.
>
> This patch fixes the problem by propagating a failure in dccp_init() to
> dccp_v{4,6}_init_net(), and from there to dccp_v{4,6}_init(), so that the
> DCCP protocol is not made available if its initialisation fails.
>
> Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk>
Applied, thanks Gerrit.
^ permalink raw reply
* net-next-2.6...
From: David Miller @ 2010-03-15 23:18 UTC (permalink / raw)
To: netdev
I intend to open up net-next-2.6 right after Linus pulls in
the next batch of bug fixes I send in via net-2.6
That submission should be either tonight or tomorrow some
time.
^ permalink raw reply
* Re: [Regression] r8169: enable 64-bit DMA by default for PCI Express devices (v2)
From: Robert Hancock @ 2010-03-15 23:28 UTC (permalink / raw)
To: David Miller; +Cc: joern, torvalds, linux-kernel, netdev, romieu
In-Reply-To: <20100315.115748.13754030.davem@davemloft.net>
On Mon, Mar 15, 2010 at 12:57 PM, David Miller <davem@davemloft.net> wrote:
> From: Jörn Engel <joern@dublin.logfs.org>
> Date: Mon, 15 Mar 2010 16:10:41 +0100
>
>> Forgot to Cc: lkml.
>
> Actually, what you really need to CC: is netdev, added.
>> On Mon, 15 March 2010 16:08:06 +0100, J�rn Engel wrote:
>>>
>>> Hello Robert!
>>>
>>> Looks like 353176888386d9025062a12dcec08d49af10cf2c broke one of my
>>> machines. Reverting that patch fixes things again. Machine is
>>> running an x86_64 kernel. Lspci gives me this:
>>>
>>> 01:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8101E/RTL8102E PCI Express Fast Ethernet controller (rev 02)
>>> Subsystem: ASRock Incorporation Device 8136
>>> Flags: bus master, fast devsel, latency 0, IRQ 26
>>> I/O ports at e800 [size=256]
>>> Memory at fdeff000 (64-bit, prefetchable) [size=4K]
>>> Memory at fdee0000 (64-bit, prefetchable) [size=64K]
>>> [virtual] Expansion ROM at fde00000 [disabled] [size=128K]
>>> Capabilities: [40] Power Management version 3
>>> Capabilities: [50] MSI: Enable+ Count=1/1 Maskable- 64bit+
>>> Capabilities: [70] Express Endpoint, MSI 01
>>> Capabilities: [ac] MSI-X: Enable- Count=2 Masked-
>>> Capabilities: [cc] Vital Product Data
>>> Capabilities: [100] Advanced Error Reporting
>>> Capabilities: [140] Virtual Channel <?>
>>> Capabilities: [160] Device Serial Number 08-00-00-00-ff-ff-00-00
>>> Kernel driver in use: r8169
>>>
>>> If you have any test patches to try, please send them my way. :)
>>>
>>> J�rn
>
What are the symptoms? Does setting use_dac=0 in the module options
for r8169 also resolve the problem?
Full dmesg and lspci -vv output might also be useful.
^ permalink raw reply
* Re: [1/3] gigaset: avoid registering CAPI driver more than once
From: Karsten Keil @ 2010-03-16 0:23 UTC (permalink / raw)
To: David Miller
Cc: tilman, netdev, linux-kernel, stable, isdn4linux, hjlipp,
i4ldeveloper
In-Reply-To: <20100315.142040.58983190.davem@davemloft.net>
On Montag, 15. März 2010 22:20:40 David Miller wrote:
> Karsten the way you reforward patches does not work.
>
> And I've told you about this last time you submitted ISDN patches.
>
> You don't add an extra "From: " line in the message body, so when the
> patch gets applied the author gets set to _you_ instead of the person
> who actually wrote the change.
>
Sorry about that.
>
> All you're doing is ACK'ing this person's work, so simply reply to the
> patch they posted and with your "Acked-by" line. This way patchwork
> and friends will figure out the rest when the patch gets integrated
> into the net-2.6 tree.
I tried to use my own patchwork which is feed by the
ISDN list to learn more about it, unfortunately it does not help for such
issues.
>
> By reposting the patches the way you are we're losing information.
>
> Add to this the fact that you never have the time to properly take
> care of ISDN patches, and when you do finally "get to it" you make all
> kinds of submission errors.
>
> This is not the first time either, it happens over and over again.
> It's incredibly frustrating, especially for me.
I can understand that.
> Please seriously consider handing ISDN maintainership over to someone
> who unlike you 1) has the time and the desire and 2) knows how to
> submit patches properly. Probably Tilman is the person who most meets
> these criteria based upon what I've seen.
>
> Meanwhile, I'm going to apply the original patches as posted by
> Tilman so that the proper authorship gets set.
Fair enough, thanks.
Karsten
- who had really a very bad day
^ permalink raw reply
* [PATCH] r8169: Fix rtl8169_rx_interrupt()
From: Eric Dumazet @ 2010-03-16 0:33 UTC (permalink / raw)
To: Oleg Nesterov, David Miller
Cc: Ingo Molnar, Sergey Senozhatsky, Francois Romieu, Peter Zijlstra,
netdev, linux-kernel
In-Reply-To: <1268686865.2824.4.camel@edumazet-laptop>
Le lundi 15 mars 2010 à 22:01 +0100, Eric Dumazet a écrit :
> Yes, this is wrong. In this context (process context, not softirq), we
> should use netif_rx() or just drop frames if we are in reset phase.
>
Sergey,
Here is a compiled but untested patch (I dont have the hardware), could
you please test it ?
Thanks
[PATCH] r8169: Fix rtl8169_rx_interrupt()
In case a reset is performed, rtl8169_rx_interrupt() is called from
process context instead of softirq context. Special care must be taken
to call appropriate network core services (netif_rx() instead of
netif_receive_skb()). VLAN handling also corrected.
Reported-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Diagnosed-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 9d3ebf3..d873639 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -1038,14 +1038,14 @@ static void rtl8169_vlan_rx_register(struct net_device *dev,
}
static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc,
- struct sk_buff *skb)
+ struct sk_buff *skb, int polling)
{
u32 opts2 = le32_to_cpu(desc->opts2);
struct vlan_group *vlgrp = tp->vlgrp;
int ret;
if (vlgrp && (opts2 & RxVlanTag)) {
- vlan_hwaccel_receive_skb(skb, vlgrp, swab16(opts2 & 0xffff));
+ __vlan_hwaccel_rx(skb, vlgrp, swab16(opts2 & 0xffff), polling);
ret = 0;
} else
ret = -1;
@@ -1062,7 +1062,7 @@ static inline u32 rtl8169_tx_vlan_tag(struct rtl8169_private *tp,
}
static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc,
- struct sk_buff *skb)
+ struct sk_buff *skb, int polling)
{
return -1;
}
@@ -4429,12 +4429,20 @@ out:
return done;
}
+/*
+ * Warning : rtl8169_rx_interrupt() might be called :
+ * 1) from NAPI (softirq) context
+ * (polling = 1 : we should call netif_receive_skb())
+ * 2) from process context (rtl8169_reset_task())
+ * (polling = 0 : we must call netif_rx() instead)
+ */
static int rtl8169_rx_interrupt(struct net_device *dev,
struct rtl8169_private *tp,
void __iomem *ioaddr, u32 budget)
{
unsigned int cur_rx, rx_left;
unsigned int delta, count;
+ int polling = (budget != ~(u32)0) ? 1 : 0;
cur_rx = tp->cur_rx;
rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx;
@@ -4496,8 +4504,12 @@ static int rtl8169_rx_interrupt(struct net_device *dev,
skb_put(skb, pkt_size);
skb->protocol = eth_type_trans(skb, dev);
- if (rtl8169_rx_vlan_skb(tp, desc, skb) < 0)
- netif_receive_skb(skb);
+ if (rtl8169_rx_vlan_skb(tp, desc, skb, polling) < 0) {
+ if (likely(polling))
+ netif_receive_skb(skb);
+ else
+ netif_rx(skb);
+ }
dev->stats.rx_bytes += pkt_size;
dev->stats.rx_packets++;
^ permalink raw reply related
* Re: [PATCH] ISDN: Add PCI ID for HFC-2S/4S Beronet Card PCIe
From: Karsten Keil @ 2010-03-16 1:08 UTC (permalink / raw)
To: David Miller
Cc: torvalds, lars.ellenberg, netdev, andreas, akpm, linux-kernel
In-Reply-To: <20100315.160133.67908104.davem@davemloft.net>
On Dienstag, 16. März 2010 00:01:33 David Miller wrote:
> From: Karsten Keil <keil@b1-systems.de>
> Date: Mon, 15 Mar 2010 14:18:41 +0100
>
> > A few subdevice IDs seem to have been dropped when hfc_multi was
> > included upstream, just compare the list at
> > http://www.openvox.cn/viewvc/misdn/trunk/hfc_multi.c?revision=75&view=ann
> >otate#l175 with the IDs in drivers/isdn/hardware/mISDN/hfcmulti.c
> >
> > Added PCIe 2 Port card and LED settings (same as PCI) /KKe
> >
> > Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
> > Signed-off-by: Karsten Keil <keil@b1-systems.de>
>
> We don't add new defines to linux/pci_ids.h, it is deprecated.
> Simply add the device ID constants to the table.
Good policy, makes sense. Will send a new version.
May a check for adding defines to <linux/pci_ids> would help to enforce it.
>
> The only exception is the case where a device ID is used in
> more than one driver, but even that scenerio is heavily
> discouraged.
>
Is here some plan to cleanup drivers for -next and remove IDs from pci_ids.h,
or should it not changed for old entries ?
Karsten
^ permalink raw reply
* Re: [PATCH] ISDN: Add PCI ID for HFC-2S/4S Beronet Card PCIe
From: David Miller @ 2010-03-16 2:06 UTC (permalink / raw)
To: keil; +Cc: torvalds, lars.ellenberg, netdev, andreas, akpm, linux-kernel
In-Reply-To: <201003160208.57232.keil@b1-systems.de>
From: Karsten Keil <keil@b1-systems.de>
Date: Tue, 16 Mar 2010 02:08:57 +0100
> Is here some plan to cleanup drivers for -next and remove IDs from pci_ids.h,
> or should it not changed for old entries ?
Leave things as they are for existing ones.
^ permalink raw reply
* Re: [PATCH V2] ISDN: Add PCI ID for HFC-2S/4S Beronet Card PCIe
From: David Miller @ 2010-03-16 2:09 UTC (permalink / raw)
To: keil; +Cc: torvalds, lars.ellenberg, netdev, andreas, akpm, linux-kernel
In-Reply-To: <1268702879.31146@pingi>
From: Karsten Keil <keil@b1-systems.de>
Date: Mon, 15 Mar 2010 14:18:41 +0100
> From: Lars Ellenberg <lars.ellenberg@linbit.com>
>
> A few subdevice IDs seem to have been dropped when hfc_multi was
> included upstream, just compare the list at
> http://www.openvox.cn/viewvc/misdn/trunk/hfc_multi.c?revision=75&view=annotate#l175
> with the IDs in drivers/isdn/hardware/mISDN/hfcmulti.c
>
> Added PCIe 2 Port card and LED settings (same as PCI)
> Do not use <linux/pci_ids.h> /KKe
>
> Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
> Signed-off-by: Karsten Keil <keil@b1-systems.de>
Applied, thanks.
^ permalink raw reply
* Re: [Bridge] [PATCH] bridge: per-cpu packet statistics
From: Herbert Xu @ 2010-03-16 2:48 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: eric.dumazet, netdev, bridge, davem
In-Reply-To: <20100302092209.048f5682@nehalam>
Stephen Hemminger <shemminger@vyatta.com> wrote:
>
> Bridge is already using lockless transmit LLTX, so tx_lock is not touched.
LLTX doesn't actually buy you anything since you're still going
through a single qdisc. To get the full benefits of the per-cpu
counters you need to implement multiqueue support in the bridge.
Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* Re: [Bridge] [PATCH] bridge: per-cpu packet statistics
From: Herbert Xu @ 2010-03-16 2:51 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: eric.dumazet, netdev, bridge, davem
In-Reply-To: <20100302092301.7ca9b368@nehalam>
Stephen Hemminger <shemminger@vyatta.com> wrote:
>
> No need, bridge has no queue!
OK, ignore my previous comment :)
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* Re: [PATCH] BUG: unable to handle kernel NULL pointer dereference at br_pass_frame_up
From: Herbert Xu @ 2010-03-16 3:00 UTC (permalink / raw)
To: michael-dev, David S. Miller; +Cc: netdev
In-Reply-To: <4B9C4A2F.8040203@fami-braun.de>
michael-dev@fami-braun.de wrote:
>
> I'm currently using linux-next and have been running into an OOPs which
> I think might be caused by a patch submitted on 2010-02-27.
Thanks for the patch. Next time please add a changelog and a
Sign-off so that it can be immediately integrated.
I've done this for you now.
From: Michael Braun <michael-dev@fami-braun.de>
bridge: Fix br_forward crash in promiscuous mode
It's a linux-next kernel from 2010-03-12 on an x86 system and it
OOPs in the bridge module in br_pass_frame_up (called by
br_handle_frame_finish) because brdev cannot be dereferenced (its set to
a non-null value).
Adding some BUG_ON statements revealed that
BR_INPUT_SKB_CB(skb)->brdev == br-dev
(as set in br_handle_frame_finish first)
only holds until br_forward is called.
The next call to br_pass_frame_up then fails.
Digging deeper it seems that br_forward either frees the skb or passes
it to NF_HOOK which will in turn take care of freeing the skb. The same
is holds for br_pass_frame_ip. So it seems as if two independent skb
allocations are required. As far as I can see, commit
b33084be192ee1e347d98bb5c9e38a53d98d35e2 removed skb duplication and so
likely causes this crash. This crash does not happen on 2.6.33.
I've therefore modified br_forward the same way br_flood has been
modified so that the skb is not freed if skb0 is going to be used
and I can confirm that the attached patch resolves the issue for me.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index d61e6f7..8347916 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -19,6 +19,10 @@
#include <linux/netfilter_bridge.h>
#include "br_private.h"
+static int deliver_clone(struct net_bridge_port *prev, struct sk_buff *skb,
+ void (*__packet_hook)(const struct net_bridge_port *p,
+ struct sk_buff *skb));
+
/* Don't forward packets to originating port or forwarding diasabled */
static inline int should_deliver(const struct net_bridge_port *p,
const struct sk_buff *skb)
@@ -94,14 +98,18 @@ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
}
/* called with rcu_read_lock */
-void br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
+void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0)
{
if (should_deliver(to, skb)) {
- __br_forward(to, skb);
+ if (skb0)
+ deliver_clone(to, skb, __br_forward);
+ else
+ __br_forward(to, skb);
return;
}
- kfree_skb(skb);
+ if (!skb0)
+ kfree_skb(skb);
}
static int deliver_clone(struct net_bridge_port *prev, struct sk_buff *skb,
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 53b3985..08a72e6 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -90,7 +90,7 @@ int br_handle_frame_finish(struct sk_buff *skb)
if (skb) {
if (dst)
- br_forward(dst->dst, skb);
+ br_forward(dst->dst, skb, skb2);
else
br_flood_forward(br, skb, skb2);
}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index fef0384..bfb8feb 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -252,7 +252,7 @@ extern void br_deliver(const struct net_bridge_port *to,
struct sk_buff *skb);
extern int br_dev_queue_push_xmit(struct sk_buff *skb);
extern void br_forward(const struct net_bridge_port *to,
- struct sk_buff *skb);
+ struct sk_buff *skb, struct sk_buff *skb0);
extern int br_forward_finish(struct sk_buff *skb);
extern void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb);
extern void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply related
* Re: [PATCH] BUG: unable to handle kernel NULL pointer dereference at br_multicast_leave_group
From: Herbert Xu @ 2010-03-16 3:10 UTC (permalink / raw)
To: michael-dev; +Cc: netdev, David S. Miller
In-Reply-To: <4B9C49E7.1080703@fami-braun.de>
michael-dev@fami-braun.de wrote:
>
> I'm currently using linux-next and have been running into an OOPs which
> I think might be caused by a patch you submitted on 2010-02-27.
>
> It's a linux-next kernel from 2010-03-12 on an x86 system and it
> OOPs in the bridge module in br_mdp_ip_get (called by
> br_multicast_leave_group) because the br->mdb is null.
Thanks, there's actually another spot (the query handler) where
the same thing can happen.
Here's a patch to fix them both.
bridge: Move NULL mdb check into br_mdb_ip_get
Since all callers of br_mdb_ip_get need to check whether the
hash table is NULL, this patch moves the check into the function.
This fixes the two callers (query/leave handler) that didn't
check it.
Reported-by: Michael Braun <michael-dev@fami-braun.de>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index fd96a8d..398221e 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -49,22 +49,23 @@ static struct net_bridge_mdb_entry *__br_mdb_ip_get(
static struct net_bridge_mdb_entry *br_mdb_ip_get(
struct net_bridge_mdb_htable *mdb, __be32 dst)
{
+ if (!mdb)
+ return NULL;
+
return __br_mdb_ip_get(mdb, dst, br_ip_hash(mdb, dst));
}
struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
struct sk_buff *skb)
{
- struct net_bridge_mdb_htable *mdb = br->mdb;
-
- if (!mdb || br->multicast_disabled)
+ if (br->multicast_disabled)
return NULL;
switch (skb->protocol) {
case htons(ETH_P_IP):
if (BR_INPUT_SKB_CB(skb)->igmp)
break;
- return br_mdb_ip_get(mdb, ip_hdr(skb)->daddr);
+ return br_mdb_ip_get(br->mdb, ip_hdr(skb)->daddr);
}
return NULL;
Thanks,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply related
* Re: [PATCH] BUG: unable to handle kernel NULL pointer dereference at br_multicast_leave_group
From: David Miller @ 2010-03-16 3:38 UTC (permalink / raw)
To: herbert; +Cc: michael-dev, netdev
In-Reply-To: <20100316031005.GA17727@gondor.apana.org.au>
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Tue, 16 Mar 2010 11:10:05 +0800
> michael-dev@fami-braun.de wrote:
>>
>> I'm currently using linux-next and have been running into an OOPs which
>> I think might be caused by a patch you submitted on 2010-02-27.
>>
>> It's a linux-next kernel from 2010-03-12 on an x86 system and it
>> OOPs in the bridge module in br_mdp_ip_get (called by
>> br_multicast_leave_group) because the br->mdb is null.
>
> Thanks, there's actually another spot (the query handler) where
> the same thing can happen.
>
> Here's a patch to fix them both.
>
> bridge: Move NULL mdb check into br_mdb_ip_get
>
> Since all callers of br_mdb_ip_get need to check whether the
> hash table is NULL, this patch moves the check into the function.
>
> This fixes the two callers (query/leave handler) that didn't
> check it.
>
> Reported-by: Michael Braun <michael-dev@fami-braun.de>
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Applied, thanks Herbert.
^ permalink raw reply
* [PATCH net-2.6] bridge br_multicast: Fix skb leakage in error path.
From: YOSHIFUJI Hideaki @ 2010-03-16 5:26 UTC (permalink / raw)
To: davem; +Cc: yoshfuji, netdev
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
net/bridge/br_multicast.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index fd96a8d..4095192 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -990,7 +990,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
err = pskb_trim_rcsum(skb2, len);
if (err)
- return err;
+ goto err_out;
}
len -= ip_hdrlen(skb2);
@@ -1012,7 +1012,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
case CHECKSUM_NONE:
skb2->csum = 0;
if (skb_checksum_complete(skb2))
- return -EINVAL;
+ goto out;
}
err = 0;
@@ -1039,6 +1039,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
out:
__skb_push(skb2, offset);
+err_out:
if (skb2 != skb)
kfree_skb(skb2);
return err;
--
1.5.6.5
^ permalink raw reply related
* [PATCH net-2.6] bridge br_multicast: Fix handling of Max Response Code in IGMPv3 message.
From: YOSHIFUJI Hideaki @ 2010-03-16 5:27 UTC (permalink / raw)
To: davem; +Cc: yoshfuji, netdev
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
net/bridge/br_multicast.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 4095192..bbbd996 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -851,8 +851,8 @@ static int br_multicast_query(struct net_bridge *br,
if (ih3->nsrcs)
goto out;
- max_delay = ih3->code ? 1 :
- IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE);
+ max_delay = ih3->code ?
+ IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
}
if (!group)
--
1.5.6.5
^ permalink raw reply related
* [RFC PATCH net-2.6 0/3] ipv6 mcast: Introduce include/net/mld.h for MLD definitions.
From: YOSHIFUJI Hideaki @ 2010-03-16 5:27 UTC (permalink / raw)
To: davem; +Cc: yoshfuji, netdev
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
include/net/mld.h | 75 +++++++++++++++++++++++++++++
net/ipv6/mcast.c | 135 ++++++++++++++++-------------------------------------
2 files changed, 115 insertions(+), 95 deletions(-)
create mode 100644 include/net/mld.h
diff --git a/include/net/mld.h b/include/net/mld.h
new file mode 100644
index 0000000..ecc7553
--- /dev/null
+++ b/include/net/mld.h
@@ -0,0 +1,75 @@
+#ifndef LINUX_MLD_H
+#define LINUX_MLD_H
+
+#include <linux/in6.h>
+#include <linux/icmpv6.h>
+
+/* MLDv1 Query/Report/Done */
+struct mld_msg {
+ struct icmp6hdr mld_hdr;
+ struct in6_addr mld_mca;
+};
+
+#define mld_type mld_hdr.icmp6_type
+#define mld_code mld_hdr.icmp6_code
+#define mld_cksum mld_hdr.icmp6_cksum
+#define mld_maxdelay mld_hdr.icmp6_maxdelay
+#define mld_reserved mld_hdr.icmp6_dataun.un_data16[1]
+
+/* Multicast Listener Discovery version 2 headers */
+/* MLDv2 Report */
+struct mld2_grec {
+ __u8 grec_type;
+ __u8 grec_auxwords;
+ __be16 grec_nsrcs;
+ struct in6_addr grec_mca;
+ struct in6_addr grec_src[0];
+};
+
+struct mld2_report {
+ struct icmp6hdr mld2r_hdr;
+ struct mld2_grec mld2r_grec[0];
+};
+
+#define mld2r_type mld2r_hdr.icmp6_type
+#define mld2r_resv1 mld2r_hdr.icmp6_code
+#define mld2r_cksum mld2r_hdr.icmp6_cksum
+#define mld2r_resv2 mld2r_hdr.icmp6_dataun.un_data16[0]
+#define mld2r_ngrec mld2r_hdr.icmp6_dataun.un_data16[1]
+
+/* MLDv2 Query */
+struct mld2_query {
+ struct icmp6hdr mld2q_hdr;
+ struct in6_addr mld2q_mca;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 mld2q_qrv:3,
+ mld2q_suppress:1,
+ mld2q_resv2:4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u8 mld2q_resv2:4,
+ mld2q_suppress:1,
+ mld2q_qrv:3;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ __u8 mld2q_qqic;
+ __be16 mld2q_nsrcs;
+ struct in6_addr mld2q_srcs[0];
+};
+
+#define mld2q_type mld2q_hdr.icmp6_type
+#define mld2q_code mld2q_hdr.icmp6_code
+#define mld2q_cksum mld2q_hdr.icmp6_cksum
+#define mld2q_mrc mld2q_hdr.icmp6_maxdelay
+#define mld2q_resv1 mld2q_hdr.icmp6_dataun.un_data16[1]
+
+/* Max Response Code */
+#define MLDV2_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))
+#define MLDV2_EXP(thresh, nbmant, nbexp, value) \
+ ((value) < (thresh) ? (value) : \
+ ((MLDV2_MASK(value, nbmant) | (1<<(nbmant))) << \
+ (MLDV2_MASK((value) >> (nbmant), nbexp) + (nbexp))))
+
+#define MLDV2_MRC(value) MLDV2_EXP(0x8000, 12, 3, value)
+
+#endif
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index bcd9719..9f62278 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -43,6 +43,7 @@
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
+#include <net/mld.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
@@ -70,54 +71,11 @@
#define MDBG(x)
#endif
-/*
- * These header formats should be in a separate include file, but icmpv6.h
- * doesn't have in6_addr defined in all cases, there is no __u128, and no
- * other files reference these.
- *
- * +-DLS 4/14/03
- */
-
-/* Multicast Listener Discovery version 2 headers */
-
-struct mld2_grec {
- __u8 grec_type;
- __u8 grec_auxwords;
- __be16 grec_nsrcs;
- struct in6_addr grec_mca;
- struct in6_addr grec_src[0];
-};
-
-struct mld2_report {
- __u8 type;
- __u8 resv1;
- __sum16 csum;
- __be16 resv2;
- __be16 ngrec;
- struct mld2_grec grec[0];
-};
-
-struct mld2_query {
- __u8 type;
- __u8 code;
- __sum16 csum;
- __be16 mrc;
- __be16 resv1;
- struct in6_addr mca;
-#if defined(__LITTLE_ENDIAN_BITFIELD)
- __u8 qrv:3,
- suppress:1,
- resv2:4;
-#elif defined(__BIG_ENDIAN_BITFIELD)
- __u8 resv2:4,
- suppress:1,
- qrv:3;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
- __u8 qqic;
- __be16 nsrcs;
- struct in6_addr srcs[0];
+/* Ensure that we have struct in6_addr aligned on 32bit word. */
+static void *__mld2_query_bugs[] __attribute__((__unused__)) = {
+ BUILD_BUG_ON_NULL(offsetof(struct mld2_query, mld2q_srcs) % 4),
+ BUILD_BUG_ON_NULL(offsetof(struct mld2_report, mld2r_grec) % 4),
+ BUILD_BUG_ON_NULL(offsetof(struct mld2_grec, grec_mca) % 4)
};
static struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT;
@@ -156,14 +114,6 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
((idev)->mc_v1_seen && \
time_before(jiffies, (idev)->mc_v1_seen)))
-#define MLDV2_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))
-#define MLDV2_EXP(thresh, nbmant, nbexp, value) \
- ((value) < (thresh) ? (value) : \
- ((MLDV2_MASK(value, nbmant) | (1<<(nbmant))) << \
- (MLDV2_MASK((value) >> (nbmant), nbexp) + (nbexp))))
-
-#define MLDV2_MRC(value) MLDV2_EXP(0x8000, 12, 3, value)
-
#define IPV6_MLD_MAX_MSF 64
int sysctl_mld_max_msf __read_mostly = IPV6_MLD_MAX_MSF;
@@ -1160,7 +1110,7 @@ int igmp6_event_query(struct sk_buff *skb)
struct in6_addr *group;
unsigned long max_delay;
struct inet6_dev *idev;
- struct icmp6hdr *hdr;
+ struct mld_msg *mld;
int group_type;
int mark = 0;
int len;
@@ -1181,8 +1131,8 @@ int igmp6_event_query(struct sk_buff *skb)
if (idev == NULL)
return 0;
- hdr = icmp6_hdr(skb);
- group = (struct in6_addr *) (hdr + 1);
+ mld = mld_msg(skb);
+ group = &mld->mld_mca;
group_type = ipv6_addr_type(group);
if (group_type != IPV6_ADDR_ANY &&
@@ -1196,7 +1146,7 @@ int igmp6_event_query(struct sk_buff *skb)
/* MLDv1 router present */
/* Translate milliseconds to jiffies */
- max_delay = (ntohs(hdr->icmp6_maxdelay)*HZ)/1000;
+ max_delay = (ntohs(mld->mld_maxdelay)*HZ)/1000;
switchback = (idev->mc_qrv + 1) * max_delay;
idev->mc_v1_seen = jiffies + switchback;
@@ -1215,14 +1165,14 @@ int igmp6_event_query(struct sk_buff *skb)
return -EINVAL;
}
mlh2 = (struct mld2_query *)skb_transport_header(skb);
- max_delay = (MLDV2_MRC(ntohs(mlh2->mrc))*HZ)/1000;
+ max_delay = (MLDV2_MRC(ntohs(mlh2->mld2q_mrc))*HZ)/1000;
if (!max_delay)
max_delay = 1;
idev->mc_maxdelay = max_delay;
- if (mlh2->qrv)
- idev->mc_qrv = mlh2->qrv;
+ if (mlh2->mld2q_qrv)
+ idev->mc_qrv = mlh2->mld2q_qrv;
if (group_type == IPV6_ADDR_ANY) { /* general query */
- if (mlh2->nsrcs) {
+ if (mlh2->mld2q_nsrcs) {
in6_dev_put(idev);
return -EINVAL; /* no sources allowed */
}
@@ -1231,9 +1181,9 @@ int igmp6_event_query(struct sk_buff *skb)
return 0;
}
/* mark sources to include, if group & source-specific */
- if (mlh2->nsrcs != 0) {
+ if (mlh2->mld2q_nsrcs != 0) {
if (!pskb_may_pull(skb, srcs_offset +
- ntohs(mlh2->nsrcs) * sizeof(struct in6_addr))) {
+ ntohs(mlh2->mld2q_nsrcs) * sizeof(struct in6_addr))) {
in6_dev_put(idev);
return -EINVAL;
}
@@ -1269,7 +1219,7 @@ int igmp6_event_query(struct sk_buff *skb)
ma->mca_flags &= ~MAF_GSQUERY;
}
if (!(ma->mca_flags & MAF_GSQUERY) ||
- mld_marksources(ma, ntohs(mlh2->nsrcs), mlh2->srcs))
+ mld_marksources(ma, ntohs(mlh2->mld2q_nsrcs), mlh2->mld2q_srcs))
igmp6_group_queried(ma, max_delay);
spin_unlock_bh(&ma->mca_lock);
break;
@@ -1285,9 +1235,8 @@ int igmp6_event_query(struct sk_buff *skb)
int igmp6_event_report(struct sk_buff *skb)
{
struct ifmcaddr6 *ma;
- struct in6_addr *addrp;
struct inet6_dev *idev;
- struct icmp6hdr *hdr;
+ struct mld_msg *mld;
int addr_type;
/* Our own report looped back. Ignore it. */
@@ -1299,10 +1248,10 @@ int igmp6_event_report(struct sk_buff *skb)
skb->pkt_type != PACKET_BROADCAST)
return 0;
- if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
+ if (!pskb_may_pull(skb, sizeof(*mld) - sizeof(struct icmp6hdr)))
return -EINVAL;
- hdr = icmp6_hdr(skb);
+ mld = mld_msg(skb);
/* Drop reports with not link local source */
addr_type = ipv6_addr_type(&ipv6_hdr(skb)->saddr);
@@ -1310,8 +1259,6 @@ int igmp6_event_report(struct sk_buff *skb)
!(addr_type&IPV6_ADDR_LINKLOCAL))
return -EINVAL;
- addrp = (struct in6_addr *) (hdr + 1);
-
idev = in6_dev_get(skb->dev);
if (idev == NULL)
return -ENODEV;
@@ -1322,7 +1269,7 @@ int igmp6_event_report(struct sk_buff *skb)
read_lock_bh(&idev->lock);
for (ma = idev->mc_list; ma; ma=ma->next) {
- if (ipv6_addr_equal(&ma->mca_addr, addrp)) {
+ if (ipv6_addr_equal(&ma->mca_addr, &mld->mld_mca)) {
spin_lock(&ma->mca_lock);
if (del_timer(&ma->mca_timer))
atomic_dec(&ma->mca_refcnt);
@@ -1431,11 +1378,11 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
skb_set_transport_header(skb, skb_tail_pointer(skb) - skb->data);
skb_put(skb, sizeof(*pmr));
pmr = (struct mld2_report *)skb_transport_header(skb);
- pmr->type = ICMPV6_MLD2_REPORT;
- pmr->resv1 = 0;
- pmr->csum = 0;
- pmr->resv2 = 0;
- pmr->ngrec = 0;
+ pmr->mld2r_type = ICMPV6_MLD2_REPORT;
+ pmr->mld2r_resv1 = 0;
+ pmr->mld2r_cksum = 0;
+ pmr->mld2r_resv2 = 0;
+ pmr->mld2r_ngrec = 0;
return skb;
}
@@ -1457,9 +1404,10 @@ static void mld_sendpack(struct sk_buff *skb)
mldlen = skb->tail - skb->transport_header;
pip6->payload_len = htons(payload_len);
- pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen,
- IPPROTO_ICMPV6, csum_partial(skb_transport_header(skb),
- mldlen, 0));
+ pmr->mld2r_cksum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen,
+ IPPROTO_ICMPV6,
+ csum_partial(skb_transport_header(skb),
+ mldlen, 0));
dst = icmp6_dst_alloc(skb->dev, NULL, &ipv6_hdr(skb)->daddr);
@@ -1520,7 +1468,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,
pgr->grec_nsrcs = 0;
pgr->grec_mca = pmc->mca_addr; /* structure copy */
pmr = (struct mld2_report *)skb_transport_header(skb);
- pmr->ngrec = htons(ntohs(pmr->ngrec)+1);
+ pmr->mld2r_ngrec = htons(ntohs(pmr->mld2r_ngrec)+1);
*ppgr = pgr;
return skb;
}
@@ -1556,7 +1504,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
/* EX and TO_EX get a fresh packet, if needed */
if (truncate) {
- if (pmr && pmr->ngrec &&
+ if (pmr && pmr->mld2r_ngrec &&
AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) {
if (skb)
mld_sendpack(skb);
@@ -1769,9 +1717,8 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
struct sock *sk = net->ipv6.igmp_sk;
struct inet6_dev *idev;
struct sk_buff *skb;
- struct icmp6hdr *hdr;
+ struct mld_msg *hdr;
const struct in6_addr *snd_addr, *saddr;
- struct in6_addr *addrp;
struct in6_addr addr_buf;
int err, len, payload_len, full_len;
u8 ra[8] = { IPPROTO_ICMPV6, 0,
@@ -1819,16 +1766,14 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
- hdr = (struct icmp6hdr *) skb_put(skb, sizeof(struct icmp6hdr));
- memset(hdr, 0, sizeof(struct icmp6hdr));
- hdr->icmp6_type = type;
+ hdr = (struct mld_msg *) skb_put(skb, sizeof(struct mld_msg));
+ memset(hdr, 0, sizeof(struct mld_msg));
+ hdr->mld_type = type;
+ ipv6_addr_copy(&hdr->mld_mca, addr);
- addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr));
- ipv6_addr_copy(addrp, addr);
-
- hdr->icmp6_cksum = csum_ipv6_magic(saddr, snd_addr, len,
- IPPROTO_ICMPV6,
- csum_partial(hdr, len, 0));
+ hdr->mld_cksum = csum_ipv6_magic(saddr, snd_addr, len,
+ IPPROTO_ICMPV6,
+ csum_partial(hdr, len, 0));
idev = in6_dev_get(skb->dev);
--
1.5.6.5
^ permalink raw reply related
* [RFC PATCH net-2.6 2/3] bridge br_multicast: Make functions less ipv4 dependent.
From: YOSHIFUJI Hideaki @ 2010-03-16 5:27 UTC (permalink / raw)
To: davem; +Cc: yoshfuji, netdev
Introduce struct br_ip{} to store ip address and protocol
and make functions more generic so that we can support
both IPv4 and IPv6 with less pain.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
net/bridge/br_multicast.c | 176 +++++++++++++++++++++++++++++++--------------
net/bridge/br_private.h | 12 +++-
2 files changed, 133 insertions(+), 55 deletions(-)
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index bbbd996..dad364b 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -27,28 +27,60 @@
#include "br_private.h"
-static inline int br_ip_hash(struct net_bridge_mdb_htable *mdb, __be32 ip)
+#define IP4_BRIP(ip) (struct br_ip) { \
+ .u.ip4 = ip, \
+ .proto = htons(ETH_P_IP), \
+ }
+
+static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
+{
+ if (a->proto != b->proto)
+ return 0;
+ switch (a->proto) {
+ case htons(ETH_P_IP):
+ return a->u.ip4 == b->u.ip4;
+ }
+ return 0;
+}
+
+static inline int __br_ip4_hash(struct net_bridge_mdb_htable *mdb, __be32 ip)
{
return jhash_1word(mdb->secret, (u32)ip) & (mdb->max - 1);
}
+static inline int br_ip_hash(struct net_bridge_mdb_htable *mdb,
+ struct br_ip *ip)
+{
+ switch (ip->proto) {
+ case htons(ETH_P_IP):
+ return __br_ip4_hash(mdb, ip->u.ip4);
+ }
+ return 0;
+}
+
static struct net_bridge_mdb_entry *__br_mdb_ip_get(
- struct net_bridge_mdb_htable *mdb, __be32 dst, int hash)
+ struct net_bridge_mdb_htable *mdb, struct br_ip *dst, int hash)
{
struct net_bridge_mdb_entry *mp;
struct hlist_node *p;
hlist_for_each_entry_rcu(mp, p, &mdb->mhash[hash], hlist[mdb->ver]) {
- if (dst == mp->addr)
+ if (br_ip_equal(&mp->addr, dst))
return mp;
}
return NULL;
}
-static struct net_bridge_mdb_entry *br_mdb_ip_get(
+static struct net_bridge_mdb_entry *br_mdb_ip4_get(
struct net_bridge_mdb_htable *mdb, __be32 dst)
{
+ return __br_mdb_ip_get(mdb, &IP4_BRIP(dst), __br_ip4_hash(mdb, dst));
+}
+
+static struct net_bridge_mdb_entry *br_mdb_ip_get(
+ struct net_bridge_mdb_htable *mdb, struct br_ip *dst)
+{
return __br_mdb_ip_get(mdb, dst, br_ip_hash(mdb, dst));
}
@@ -56,18 +88,25 @@ struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
struct sk_buff *skb)
{
struct net_bridge_mdb_htable *mdb = br->mdb;
+ struct br_ip ip;
if (!mdb || br->multicast_disabled)
return NULL;
+ if (BR_INPUT_SKB_CB(skb)->igmp)
+ return NULL;
+
+ ip.proto = skb->protocol;
+
switch (skb->protocol) {
case htons(ETH_P_IP):
- if (BR_INPUT_SKB_CB(skb)->igmp)
- break;
- return br_mdb_ip_get(mdb, ip_hdr(skb)->daddr);
+ ip.u.ip4 = ip_hdr(skb)->daddr;
+ break;
+ default:
+ return NULL;
}
- return NULL;
+ return br_mdb_ip_get(mdb, &ip);
}
static void br_mdb_free(struct rcu_head *head)
@@ -94,7 +133,7 @@ static int br_mdb_copy(struct net_bridge_mdb_htable *new,
for (i = 0; i < old->max; i++)
hlist_for_each_entry(mp, p, &old->mhash[i], hlist[old->ver])
hlist_add_head(&mp->hlist[new->ver],
- &new->mhash[br_ip_hash(new, mp->addr)]);
+ &new->mhash[br_ip_hash(new, &mp->addr)]);
if (!elasticity)
return 0;
@@ -162,7 +201,7 @@ static void br_multicast_del_pg(struct net_bridge *br,
struct net_bridge_port_group *p;
struct net_bridge_port_group **pp;
- mp = br_mdb_ip_get(mdb, pg->addr);
+ mp = br_mdb_ip_get(mdb, &pg->addr);
if (WARN_ON(!mp))
return;
@@ -248,8 +287,8 @@ out:
return 0;
}
-static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
- __be32 group)
+static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
+ __be32 group)
{
struct sk_buff *skb;
struct igmphdr *ih;
@@ -313,12 +352,22 @@ out:
return skb;
}
+static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
+ struct br_ip *addr)
+{
+ switch (addr->proto) {
+ case htons(ETH_P_IP):
+ return br_ip4_multicast_alloc_query(br, addr->u.ip4);
+ }
+ return NULL;
+}
+
static void br_multicast_send_group_query(struct net_bridge_mdb_entry *mp)
{
struct net_bridge *br = mp->br;
struct sk_buff *skb;
- skb = br_multicast_alloc_query(br, mp->addr);
+ skb = br_multicast_alloc_query(br, &mp->addr);
if (!skb)
goto timer;
@@ -352,7 +401,7 @@ static void br_multicast_send_port_group_query(struct net_bridge_port_group *pg)
struct net_bridge *br = port->br;
struct sk_buff *skb;
- skb = br_multicast_alloc_query(br, pg->addr);
+ skb = br_multicast_alloc_query(br, &pg->addr);
if (!skb)
goto timer;
@@ -382,8 +431,8 @@ out:
}
static struct net_bridge_mdb_entry *br_multicast_get_group(
- struct net_bridge *br, struct net_bridge_port *port, __be32 group,
- int hash)
+ struct net_bridge *br, struct net_bridge_port *port,
+ struct br_ip *group, int hash)
{
struct net_bridge_mdb_htable *mdb = br->mdb;
struct net_bridge_mdb_entry *mp;
@@ -395,9 +444,8 @@ static struct net_bridge_mdb_entry *br_multicast_get_group(
hlist_for_each_entry(mp, p, &mdb->mhash[hash], hlist[mdb->ver]) {
count++;
- if (unlikely(group == mp->addr)) {
+ if (unlikely(br_ip_equal(group, &mp->addr)))
return mp;
- }
}
elasticity = 0;
@@ -462,7 +510,8 @@ err:
}
static struct net_bridge_mdb_entry *br_multicast_new_group(
- struct net_bridge *br, struct net_bridge_port *port, __be32 group)
+ struct net_bridge *br, struct net_bridge_port *port,
+ struct br_ip *group)
{
struct net_bridge_mdb_htable *mdb = br->mdb;
struct net_bridge_mdb_entry *mp;
@@ -495,7 +544,7 @@ rehash:
goto out;
mp->br = br;
- mp->addr = group;
+ mp->addr = *group;
setup_timer(&mp->timer, br_multicast_group_expired,
(unsigned long)mp);
setup_timer(&mp->query_timer, br_multicast_group_query_expired,
@@ -509,7 +558,8 @@ out:
}
static int br_multicast_add_group(struct net_bridge *br,
- struct net_bridge_port *port, __be32 group)
+ struct net_bridge_port *port,
+ struct br_ip *group)
{
struct net_bridge_mdb_entry *mp;
struct net_bridge_port_group *p;
@@ -517,9 +567,6 @@ static int br_multicast_add_group(struct net_bridge *br,
unsigned long now = jiffies;
int err;
- if (ipv4_is_local_multicast(group))
- return 0;
-
spin_lock(&br->multicast_lock);
if (!netif_running(br->dev) ||
(port && port->state == BR_STATE_DISABLED))
@@ -548,7 +595,7 @@ static int br_multicast_add_group(struct net_bridge *br,
if (unlikely(!p))
goto err;
- p->addr = group;
+ p->addr = *group;
p->port = port;
p->next = *pp;
hlist_add_head(&p->mglist, &port->mglist);
@@ -569,6 +616,15 @@ err:
return err;
}
+static int br_ip4_multicast_add_group(struct net_bridge *br,
+ struct net_bridge_port *port,
+ __be32 group)
+{
+ if (ipv4_is_local_multicast(group))
+ return 0;
+ return br_multicast_add_group(br, port, &IP4_BRIP(group));
+}
+
static void br_multicast_router_expired(unsigned long data)
{
struct net_bridge_port *port = (void *)data;
@@ -590,19 +646,15 @@ static void br_multicast_local_router_expired(unsigned long data)
{
}
-static void br_multicast_send_query(struct net_bridge *br,
- struct net_bridge_port *port, u32 sent)
+static void __br_multicast_send_query(struct net_bridge *br,
+ struct net_bridge_port *port,
+ struct br_ip *ip)
{
- unsigned long time;
struct sk_buff *skb;
- if (!netif_running(br->dev) || br->multicast_disabled ||
- timer_pending(&br->multicast_querier_timer))
- return;
-
- skb = br_multicast_alloc_query(br, 0);
+ skb = br_multicast_alloc_query(br, ip);
if (!skb)
- goto timer;
+ return;
if (port) {
__skb_push(skb, sizeof(struct ethhdr));
@@ -611,8 +663,19 @@ static void br_multicast_send_query(struct net_bridge *br,
dev_queue_xmit);
} else
netif_rx(skb);
+}
+
+static void br_multicast_send_query(struct net_bridge *br,
+ struct net_bridge_port *port, u32 sent)
+{
+ unsigned long time;
+
+ if (!netif_running(br->dev) || br->multicast_disabled ||
+ timer_pending(&br->multicast_querier_timer))
+ return;
+
+ __br_multicast_send_query(br, port, &IP4_BRIP(0));
-timer:
time = jiffies;
time += sent < br->multicast_startup_query_count ?
br->multicast_startup_query_interval :
@@ -697,9 +760,9 @@ void br_multicast_disable_port(struct net_bridge_port *port)
spin_unlock(&br->multicast_lock);
}
-static int br_multicast_igmp3_report(struct net_bridge *br,
- struct net_bridge_port *port,
- struct sk_buff *skb)
+static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
+ struct net_bridge_port *port,
+ struct sk_buff *skb)
{
struct igmpv3_report *ih;
struct igmpv3_grec *grec;
@@ -744,7 +807,7 @@ static int br_multicast_igmp3_report(struct net_bridge *br,
continue;
}
- err = br_multicast_add_group(br, port, group);
+ err = br_ip4_multicast_add_group(br, port, group);
if (err)
break;
}
@@ -799,7 +862,7 @@ timer:
static void br_multicast_query_received(struct net_bridge *br,
struct net_bridge_port *port,
- __be32 saddr)
+ int saddr)
{
if (saddr)
mod_timer(&br->multicast_querier_timer,
@@ -810,9 +873,9 @@ static void br_multicast_query_received(struct net_bridge *br,
br_multicast_mark_router(br, port);
}
-static int br_multicast_query(struct net_bridge *br,
- struct net_bridge_port *port,
- struct sk_buff *skb)
+static int br_ip4_multicast_query(struct net_bridge *br,
+ struct net_bridge_port *port,
+ struct sk_buff *skb)
{
struct iphdr *iph = ip_hdr(skb);
struct igmphdr *ih = igmp_hdr(skb);
@@ -830,7 +893,7 @@ static int br_multicast_query(struct net_bridge *br,
(port && port->state == BR_STATE_DISABLED))
goto out;
- br_multicast_query_received(br, port, iph->saddr);
+ br_multicast_query_received(br, port, !!iph->saddr);
group = ih->group;
@@ -858,7 +921,7 @@ static int br_multicast_query(struct net_bridge *br,
if (!group)
goto out;
- mp = br_mdb_ip_get(br->mdb, group);
+ mp = br_mdb_ip4_get(br->mdb, group);
if (!mp)
goto out;
@@ -884,7 +947,7 @@ out:
static void br_multicast_leave_group(struct net_bridge *br,
struct net_bridge_port *port,
- __be32 group)
+ struct br_ip *group)
{
struct net_bridge_mdb_htable *mdb;
struct net_bridge_mdb_entry *mp;
@@ -892,9 +955,6 @@ static void br_multicast_leave_group(struct net_bridge *br,
unsigned long now;
unsigned long time;
- if (ipv4_is_local_multicast(group))
- return;
-
spin_lock(&br->multicast_lock);
if (!netif_running(br->dev) ||
(port && port->state == BR_STATE_DISABLED) ||
@@ -945,6 +1005,16 @@ out:
spin_unlock(&br->multicast_lock);
}
+static void br_ip4_multicast_leave_group(struct net_bridge *br,
+ struct net_bridge_port *port,
+ __be32 group)
+{
+ if (ipv4_is_local_multicast(group))
+ return;
+ br_multicast_leave_group(br, port, &IP4_BRIP(group));
+}
+
+
static int br_multicast_ipv4_rcv(struct net_bridge *br,
struct net_bridge_port *port,
struct sk_buff *skb)
@@ -1024,16 +1094,16 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
case IGMP_HOST_MEMBERSHIP_REPORT:
case IGMPV2_HOST_MEMBERSHIP_REPORT:
BR_INPUT_SKB_CB(skb2)->mrouters_only = 1;
- err = br_multicast_add_group(br, port, ih->group);
+ err = br_ip4_multicast_add_group(br, port, ih->group);
break;
case IGMPV3_HOST_MEMBERSHIP_REPORT:
- err = br_multicast_igmp3_report(br, port, skb2);
+ err = br_ip4_multicast_igmp3_report(br, port, skb2);
break;
case IGMP_HOST_MEMBERSHIP_QUERY:
- err = br_multicast_query(br, port, skb2);
+ err = br_ip4_multicast_query(br, port, skb2);
break;
case IGMP_HOST_LEAVE_MESSAGE:
- br_multicast_leave_group(br, port, ih->group);
+ br_ip4_multicast_leave_group(br, port, ih->group);
break;
}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 1cf2cef..10e93bf 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -45,6 +45,14 @@ struct mac_addr
unsigned char addr[6];
};
+struct br_ip
+{
+ union {
+ __be32 ip4;
+ } u;
+ __be16 proto;
+};
+
struct net_bridge_fdb_entry
{
struct hlist_node hlist;
@@ -64,7 +72,7 @@ struct net_bridge_port_group {
struct rcu_head rcu;
struct timer_list timer;
struct timer_list query_timer;
- __be32 addr;
+ struct br_ip addr;
u32 queries_sent;
};
@@ -77,7 +85,7 @@ struct net_bridge_mdb_entry
struct rcu_head rcu;
struct timer_list timer;
struct timer_list query_timer;
- __be32 addr;
+ struct br_ip addr;
u32 queries_sent;
};
--
1.5.6.5
^ permalink raw reply related
* [RFC PATCH net-2.6 3/3] bridge br_multicast: IPv6 MLD support.
From: YOSHIFUJI Hideaki @ 2010-03-16 5:27 UTC (permalink / raw)
To: davem; +Cc: yoshfuji, netdev
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
net/bridge/Kconfig | 6 +-
net/bridge/br_multicast.c | 401 +++++++++++++++++++++++++++++++++++++++++++++
net/bridge/br_private.h | 3 +
3 files changed, 407 insertions(+), 3 deletions(-)
diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig
index d115d5c..9190ae4 100644
--- a/net/bridge/Kconfig
+++ b/net/bridge/Kconfig
@@ -33,14 +33,14 @@ config BRIDGE
If unsure, say N.
config BRIDGE_IGMP_SNOOPING
- bool "IGMP snooping"
+ bool "IGMP/MLD snooping"
depends on BRIDGE
depends on INET
default y
---help---
If you say Y here, then the Ethernet bridge will be able selectively
- forward multicast traffic based on IGMP traffic received from each
- port.
+ forward multicast traffic based on IGMP/MLD traffic received from
+ each port.
Say N to exclude this support and reduce the binary size.
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index dad364b..067c23d 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -24,6 +24,8 @@
#include <linux/slab.h>
#include <linux/timer.h>
#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/mld.h>
#include "br_private.h"
@@ -32,6 +34,17 @@
.proto = htons(ETH_P_IP), \
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#define IP6_BRIP(ip) (struct br_ip) { \
+ .u.ip6 = *ip, \
+ .proto = htons(ETH_P_IPV6), \
+ }
+static inline int ipv6_addr_is_mc_linklocal(const struct in6_addr *addr)
+{
+ return addr->s6_addr16[0] == htons(0xff02);
+}
+#endif
+
static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
{
if (a->proto != b->proto)
@@ -39,6 +52,10 @@ static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
switch (a->proto) {
case htons(ETH_P_IP):
return a->u.ip4 == b->u.ip4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case htons(ETH_P_IPV6):
+ return ipv6_addr_equal(&a->u.ip6, &b->u.ip6);
+#endif
}
return 0;
}
@@ -48,12 +65,24 @@ static inline int __br_ip4_hash(struct net_bridge_mdb_htable *mdb, __be32 ip)
return jhash_1word(mdb->secret, (u32)ip) & (mdb->max - 1);
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static inline int __br_ip6_hash(struct net_bridge_mdb_htable *mdb,
+ const struct in6_addr *ip)
+{
+ return jhash2(ip->s6_addr32, 4, mdb->secret) & (mdb->max - 1);
+}
+#endif
+
static inline int br_ip_hash(struct net_bridge_mdb_htable *mdb,
struct br_ip *ip)
{
switch (ip->proto) {
case htons(ETH_P_IP):
return __br_ip4_hash(mdb, ip->u.ip4);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case htons(ETH_P_IPV6):
+ return __br_ip6_hash(mdb, &ip->u.ip6);
+#endif
}
return 0;
}
@@ -78,6 +107,14 @@ static struct net_bridge_mdb_entry *br_mdb_ip4_get(
return __br_mdb_ip_get(mdb, &IP4_BRIP(dst), __br_ip4_hash(mdb, dst));
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static struct net_bridge_mdb_entry *br_mdb_ip6_get(
+ struct net_bridge_mdb_htable *mdb, const struct in6_addr *dst)
+{
+ return __br_mdb_ip_get(mdb, &IP6_BRIP(dst), __br_ip6_hash(mdb, dst));
+}
+#endif
+
static struct net_bridge_mdb_entry *br_mdb_ip_get(
struct net_bridge_mdb_htable *mdb, struct br_ip *dst)
{
@@ -102,6 +139,11 @@ struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
case htons(ETH_P_IP):
ip.u.ip4 = ip_hdr(skb)->daddr;
break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case htons(ETH_P_IPV6):
+ ipv6_addr_copy(&ip.u.ip6, &ipv6_hdr(skb)->daddr);
+ break;
+#endif
default:
return NULL;
}
@@ -352,12 +394,95 @@ out:
return skb;
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
+ struct in6_addr *group)
+{
+ struct sk_buff *skb;
+ struct ipv6hdr *ip6h;
+ struct mld_msg *mldq;
+ struct ethhdr *eth;
+ u8 *hopopt;
+ unsigned long interval;
+
+ skb = netdev_alloc_skb_ip_align(br->dev, sizeof(*eth) + sizeof(*ip6h) +
+ 8 + sizeof(*mldq));
+ if (!skb)
+ goto out;
+
+ skb->protocol = htons(ETH_P_IPV6);
+
+ /* Ethernet header */
+ skb_reset_mac_header(skb);
+ eth = eth_hdr(skb);
+
+ memcpy(eth->h_source, br->dev->dev_addr, 6);
+ ipv6_eth_mc_map(group, eth->h_dest);
+ eth->h_proto = htons(ETH_P_IPV6);
+ skb_put(skb, sizeof(*eth));
+
+ /* IPv6 header + HbH option */
+ skb_set_network_header(skb, skb->len);
+ ip6h = ipv6_hdr(skb);
+
+ *(__force __be32 *)ip6h = htonl(0x60000000);
+ ip6h->payload_len = 8 + sizeof(*mldq);
+ ip6h->nexthdr = IPPROTO_HOPOPTS;
+ ip6h->hop_limit = 1;
+ ipv6_addr_copy(&ip6h->saddr, &in6addr_any);
+ ipv6_addr_copy(&ip6h->daddr, &in6addr_linklocal_allnodes);
+ ipv6_addr_copy(&ip6h->daddr, group);
+
+ hopopt = (u8 *)(ip6h + 1);
+ hopopt[0] = IPPROTO_ICMPV6; /* next hdr */
+ hopopt[1] = 0; /* length of HbH */
+ hopopt[2] = IPV6_TLV_ROUTERALERT; /* Router Alert */
+ hopopt[3] = 2; /* Length of RA Option */
+ hopopt[4] = 0; /* Type = 0x0000 (MLD) */
+ hopopt[5] = 0;
+ hopopt[6] = IPV6_TLV_PAD0; /* Pad0 */
+ hopopt[7] = IPV6_TLV_PAD0; /* Pad0 */
+
+ skb_put(skb, sizeof(*ip6h) + 8);
+
+ /* ICMPv6 */
+ skb_set_transport_header(skb, skb->len);
+ mldq = (struct mld_msg *) icmp6_hdr(skb);
+
+ interval = ipv6_addr_any(group) ? br->multicast_last_member_interval :
+ br->multicast_query_response_interval;
+
+ mldq->mld_type = ICMPV6_MGM_QUERY;
+ mldq->mld_code = 0;
+ mldq->mld_cksum = 0;
+ mldq->mld_maxdelay = htons((u16)jiffies_to_msecs(interval));
+ mldq->mld_reserved = 0;
+ ipv6_addr_copy(&mldq->mld_mca, group);
+
+ /* checksum */
+ mldq->mld_cksum = csum_ipv6_magic(&in6addr_any, group,
+ sizeof(*mldq), IPPROTO_ICMPV6,
+ csum_partial(mldq,
+ sizeof(*mldq), 0));
+ skb_put(skb, sizeof(*mldq));
+
+ __skb_pull(skb, sizeof(*eth));
+
+out:
+ return skb;
+}
+#endif
+
static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
struct br_ip *addr)
{
switch (addr->proto) {
case htons(ETH_P_IP):
return br_ip4_multicast_alloc_query(br, addr->u.ip4);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case htons(ETH_P_IPV6):
+ return br_ip6_multicast_alloc_query(br, &addr->u.ip6);
+#endif
}
return NULL;
}
@@ -625,6 +750,17 @@ static int br_ip4_multicast_add_group(struct net_bridge *br,
return br_multicast_add_group(br, port, &IP4_BRIP(group));
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static int br_ip6_multicast_add_group(struct net_bridge *br,
+ struct net_bridge_port *port,
+ const struct in6_addr *group)
+{
+ if (ipv6_addr_is_mc_linklocal(group))
+ return 0;
+ return br_multicast_add_group(br, port, &IP6_BRIP(group));
+}
+#endif
+
static void br_multicast_router_expired(unsigned long data)
{
struct net_bridge_port *port = (void *)data;
@@ -675,6 +811,9 @@ static void br_multicast_send_query(struct net_bridge *br,
return;
__br_multicast_send_query(br, port, &IP4_BRIP(0));
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ __br_multicast_send_query(br, port, &IP6_BRIP(&in6addr_any));
+#endif
time = jiffies;
time += sent < br->multicast_startup_query_count ?
@@ -815,6 +954,65 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
return err;
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static int br_ip6_multicast_mld2_report(struct net_bridge *br,
+ struct net_bridge_port *port,
+ struct sk_buff *skb)
+{
+ struct icmp6hdr *icmp6h;
+ struct mld2_grec *grec;
+ int i;
+ int len;
+ int num;
+ int err = 0;
+
+ if (!pskb_may_pull(skb, sizeof(*icmp6h)))
+ return -EINVAL;
+
+ icmp6h = icmp6_hdr(skb);
+ num = ntohs(icmp6h->icmp6_dataun.un_data16[1]);
+ len = sizeof(*icmp6h);
+
+ for (i = 0; i < num; i++) {
+ __be16 *nsrcs, _nsrcs;
+
+ nsrcs = skb_header_pointer(skb,
+ len + offsetof(struct mld2_grec,
+ grec_mca),
+ sizeof(_nsrcs), &_nsrcs);
+ if (!nsrcs)
+ return -EINVAL;
+
+ len += sizeof(*grec) + sizeof(struct in6_addr) * (*nsrcs);
+ if (!pskb_may_pull(skb, len))
+ return -EINVAL;
+
+ grec = (struct mld2_grec *)(skb->data + len);
+ len += sizeof(struct in6_addr) * (*nsrcs);
+
+ /* We treat these as MLDv1 reports for now. */
+ switch (grec->grec_type) {
+ case MLD2_MODE_IS_INCLUDE:
+ case MLD2_MODE_IS_EXCLUDE:
+ case MLD2_CHANGE_TO_INCLUDE:
+ case MLD2_CHANGE_TO_EXCLUDE:
+ case MLD2_ALLOW_NEW_SOURCES:
+ case MLD2_BLOCK_OLD_SOURCES:
+ break;
+
+ default:
+ continue;
+ }
+
+ err = br_ip6_multicast_add_group(br, port, &grec->grec_mca);
+ if (!err)
+ break;
+ }
+
+ return err;
+}
+#endif
+
static void br_multicast_add_router(struct net_bridge *br,
struct net_bridge_port *port)
{
@@ -945,6 +1143,75 @@ out:
return err;
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static int br_ip6_multicast_query(struct net_bridge *br,
+ struct net_bridge_port *port,
+ struct sk_buff *skb)
+{
+ struct ipv6hdr *ip6h = ipv6_hdr(skb);
+ struct mld_msg *mld = (struct mld_msg *) icmp6_hdr(skb);
+ struct net_bridge_mdb_entry *mp;
+ struct mld2_query *mld2q;
+ struct net_bridge_port_group *p, **pp;
+ unsigned long max_delay;
+ unsigned long now = jiffies;
+ struct in6_addr *group = NULL;
+ int err = 0;
+
+ spin_lock(&br->multicast_lock);
+ if (!netif_running(br->dev) ||
+ (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;
+ goto out;
+ }
+ mld = (struct mld_msg *) icmp6_hdr(skb);
+ max_delay = msecs_to_jiffies(htons(mld->mld_maxdelay));
+ if (max_delay)
+ group = &mld->mld_mca;
+ } else if (skb->len >= sizeof(*mld2q)) {
+ if (!pskb_may_pull(skb, sizeof(*mld2q))) {
+ err = -EINVAL;
+ goto out;
+ }
+ mld2q = (struct mld2_query *)icmp6_hdr(skb);
+ if (!mld2q->mld2q_nsrcs)
+ group = &mld2q->mld2q_mca;
+ max_delay = mld2q->mld2q_mrc ? MLDV2_MRC(mld2q->mld2q_mrc) : 1;
+ }
+
+ if (!group)
+ goto out;
+
+ mp = br_mdb_ip6_get(br->mdb, group);
+ if (!mp)
+ goto out;
+
+ max_delay *= br->multicast_last_member_count;
+ if (!hlist_unhashed(&mp->mglist) &&
+ (timer_pending(&mp->timer) ?
+ time_after(mp->timer.expires, now + max_delay) :
+ try_to_del_timer_sync(&mp->timer) >= 0))
+ mod_timer(&mp->timer, now + max_delay);
+
+ for (pp = &mp->ports; (p = *pp); pp = &p->next) {
+ if (timer_pending(&p->timer) ?
+ time_after(p->timer.expires, now + max_delay) :
+ try_to_del_timer_sync(&p->timer) >= 0)
+ mod_timer(&mp->timer, now + max_delay);
+ }
+
+out:
+ spin_unlock(&br->multicast_lock);
+ return err;
+}
+#endif
+
static void br_multicast_leave_group(struct net_bridge *br,
struct net_bridge_port *port,
struct br_ip *group)
@@ -1014,6 +1281,16 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br,
br_multicast_leave_group(br, port, &IP4_BRIP(group));
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static void br_ip6_multicast_leave_group(struct net_bridge *br,
+ struct net_bridge_port *port,
+ const struct in6_addr *group)
+{
+ if (ipv6_addr_is_mc_linklocal(group))
+ return;
+ br_multicast_leave_group(br, port, &IP6_BRIP(group));
+}
+#endif
static int br_multicast_ipv4_rcv(struct net_bridge *br,
struct net_bridge_port *port,
@@ -1115,6 +1392,126 @@ err_out:
return err;
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static int br_multicast_ipv6_rcv(struct net_bridge *br,
+ struct net_bridge_port *port,
+ struct sk_buff *skb)
+{
+ struct sk_buff *skb2 = skb;
+ struct ipv6hdr *ip6h;
+ struct icmp6hdr *icmp6h;
+ u8 nexthdr;
+ unsigned len;
+ unsigned offset;
+ int err;
+
+ BR_INPUT_SKB_CB(skb)->igmp = 0;
+ BR_INPUT_SKB_CB(skb)->mrouters_only = 0;
+
+ if (!pskb_may_pull(skb, sizeof(*ip6h)))
+ return -EINVAL;
+
+ ip6h = ipv6_hdr(skb);
+
+ /*
+ * We're interested in MLD messages only.
+ * - Version is 6
+ * - MLD has always Router Alert hop-by-hop option
+ * - But we do not support jumbrograms.
+ */
+ if (ip6h->version != 6 ||
+ ip6h->nexthdr != IPPROTO_HOPOPTS ||
+ ip6h->payload_len == 0)
+ return 0;
+
+ len = ntohs(ip6h->payload_len);
+ if (skb->len < len)
+ return -EINVAL;
+
+ nexthdr = ip6h->nexthdr;
+ offset = ipv6_skip_exthdr(skb, sizeof(*ip6h), &nexthdr);
+
+ if (offset < 0 || nexthdr != IPPROTO_ICMPV6)
+ return 0;
+
+ /* Okay, we found ICMPv6 header */
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (!skb2)
+ return -ENOMEM;
+
+ len -= offset - skb_network_offset(skb2);
+
+ __skb_pull(skb2, offset);
+ skb_reset_transport_header(skb2);
+
+ err = -EINVAL;
+ if (!pskb_may_pull(skb2, sizeof(*icmp6h)))
+ goto out;
+
+ icmp6h = icmp6_hdr(skb2);
+
+ switch (icmp6h->icmp6_type) {
+ case ICMPV6_MGM_QUERY:
+ case ICMPV6_MGM_REPORT:
+ case ICMPV6_MGM_REDUCTION:
+ case ICMPV6_MLD2_REPORT:
+ break;
+ default:
+ err = 0;
+ goto out;
+ }
+
+ /* Okay, we found MLD message. Check further. */
+ if (skb2->len > len) {
+ err = pskb_trim_rcsum(skb2, len);
+ if (err)
+ goto out;
+ }
+
+ switch (skb2->ip_summed) {
+ case CHECKSUM_COMPLETE:
+ if (!csum_fold(skb2->csum))
+ break;
+ /*FALLTHROUGH*/
+ case CHECKSUM_NONE:
+ skb2->csum = 0;
+ if (skb_checksum_complete(skb2))
+ goto out;
+ }
+
+ err = 0;
+
+ BR_INPUT_SKB_CB(skb)->igmp = 1;
+
+ switch (icmp6h->icmp6_type) {
+ case ICMPV6_MGM_REPORT:
+ {
+ struct mld_msg *mld = (struct mld_msg *)icmp6h;
+ BR_INPUT_SKB_CB(skb2)->mrouters_only = 1;
+ err = br_ip6_multicast_add_group(br, port, &mld->mld_mca);
+ break;
+ }
+ case ICMPV6_MLD2_REPORT:
+ err = br_ip6_multicast_mld2_report(br, port, skb2);
+ break;
+ case ICMPV6_MGM_QUERY:
+ err = br_ip6_multicast_query(br, port, skb2);
+ break;
+ case ICMPV6_MGM_REDUCTION:
+ {
+ struct mld_msg *mld = (struct mld_msg *)icmp6h;
+ br_ip6_multicast_leave_group(br, port, &mld->mld_mca);
+ }
+ }
+
+out:
+ __skb_push(skb2, offset);
+ if (skb2 != skb)
+ kfree_skb(skb2);
+ return err;
+}
+#endif
+
int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
struct sk_buff *skb)
{
@@ -1124,6 +1521,10 @@ int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
switch (skb->protocol) {
case htons(ETH_P_IP):
return br_multicast_ipv4_rcv(br, port, skb);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case htons(ETH_P_IPV6):
+ return br_multicast_ipv6_rcv(br, port, skb);
+#endif
}
return 0;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 10e93bf..6f65675 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -49,6 +49,9 @@ struct br_ip
{
union {
__be32 ip4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct in6_addr ip6;
+#endif
} u;
__be16 proto;
};
--
1.5.6.5
^ permalink raw reply related
* Re: [RFC PATCH net-2.6 0/3] ipv6 mcast: Introduce include/net/mld.h for MLD definitions.
From: YOSHIFUJI Hideaki @ 2010-03-16 5:31 UTC (permalink / raw)
To: YOSHIFUJI Hideaki; +Cc: davem, netdev
In-Reply-To: <201003160527.o2G5R4MQ010839@94.43.138.210.xn.2iij.net>
Oops, this should have 1/3 instead of 0/3 in subject...
(2010/03/16 14:27), YOSHIFUJI Hideaki wrote:
> Signed-off-by: YOSHIFUJI Hideaki<yoshfuji@linux-ipv6.org>
> ---
> include/net/mld.h | 75 +++++++++++++++++++++++++++++
> net/ipv6/mcast.c | 135 ++++++++++++++++-------------------------------------
> 2 files changed, 115 insertions(+), 95 deletions(-)
> create mode 100644 include/net/mld.h
--yoshfuji
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox