* Re: [RFC PATCH 1/4] PHY Abstraction Layer III (now with more splitiness)
From: Andy Fleming @ 2005-07-27 18:46 UTC (permalink / raw)
To: Randy Dunlap; +Cc: Netdev, linuxppc-embedded, Francois Romieu
In-Reply-To: <1122487708.20267@shark.he.net>
On Jul 27, 2005, at 13:08, Randy Dunlap wrote:
>
>
>> On Jul 25, 2005, at 16:06, Francois Romieu wrote:
>>
>>
>>
>>>> +int mdiobus_register(struct mii_bus *bus)
>>>> +{
>>>> + int i;
>>>> + int err = 0;
>>>> +
>>>> + spin_lock_init(&bus->mdio_lock);
>>>> +
>>>> + if (NULL == bus || NULL == bus->name ||
>>>> + NULL == bus->read ||
>>>> + NULL == bus->write)
>>>>
>>>>
>>>
>>> Be spartan:
>>> if (!bus || !bus->name || !bus->read || !bus->write)
>>>
>>
>>
>> I think we have to agree to disagree here. I could be convinced, but
>> I'm partial to using NULL explicitly.
>>
>
> But there are 2 issues here (at least). One is to use NULL or
> not. The other is using (constant == var) or (var == constant).
>
> It's not described in CodingStlye afaik, but most recent email
> on the subject strongly prefers (var == constant) [in my
> unscientific survey -- of bits in my head].
>
> So using the suggested style will fix both of these. :)
Ok, here I won't agree to disagree with you. !foo as a check for
NULL is a reasonable idea, but not my style. If that's the preferred
style for the kernel, I will do that.
But (var == constant) is a style that asks for errors. By putting
the constant first in these checks, you never run the risk of leaving
a bug like this:
if (dev = NULL)
...
This kind of error is quite frustrating to detect, and the eye will
often miss it when scanning for errors. If you follow constant ==
var, though, then the bug looks like this:
if (NULL = dev)
which is instantly caught by the compiler.
Just my 32 cents
>
>
>>>> + /* Otherwise, we allocate the device, and initialize the
>>>> + * default values */
>>>> + dev = kmalloc(sizeof(*dev), GFP_KERNEL);
>>>> +
>>>> + if (NULL == dev) {
>>>> + errno = -ENOMEM;
>>>> + return NULL;
>>>> + }
>>>> +
>>>> + memset(dev, 0, sizeof(*dev));
>>>>
>>>>
>>>
>>> The kernel provides kcalloc.
>>>
>>
>>
>> I went looking for it, and found it in fs/cifs/misc.c. I'm hesitant
>> to link to a function defined in the filesystem code just to save 1
>> line of code
>>
>
> It's more global than that.
Should we move the function, then, to include/linux/slab.h? Or
somewhere else?
^ permalink raw reply
* Re: [RFC PATCH 1/4] PHY Abstraction Layer III (now with more splitiness)
From: Randy Dunlap @ 2005-07-27 18:08 UTC (permalink / raw)
To: Andy Fleming, Francois Romieu, Netdev, linuxppc-embedded
> On Jul 25, 2005, at 16:06, Francois Romieu wrote:
>
>
> >> +int mdiobus_register(struct mii_bus *bus)
> >> +{
> >> + int i;
> >> + int err = 0;
> >> +
> >> + spin_lock_init(&bus->mdio_lock);
> >> +
> >> + if (NULL == bus || NULL == bus->name ||
> >> + NULL == bus->read ||
> >> + NULL == bus->write)
> >>
> >
> > Be spartan:
> > if (!bus || !bus->name || !bus->read || !bus->write)
>
>
> I think we have to agree to disagree here. I could be convinced, but
> I'm partial to using NULL explicitly.
But there are 2 issues here (at least). One is to use NULL or
not. The other is using (constant == var) or (var == constant).
It's not described in CodingStlye afaik, but most recent email
on the subject strongly prefers (var == constant) [in my
unscientific survey -- of bits in my head].
So using the suggested style will fix both of these. :)
> >> + /* Otherwise, we allocate the device, and initialize the
> >> + * default values */
> >> + dev = kmalloc(sizeof(*dev), GFP_KERNEL);
> >> +
> >> + if (NULL == dev) {
> >> + errno = -ENOMEM;
> >> + return NULL;
> >> + }
> >> +
> >> + memset(dev, 0, sizeof(*dev));
> >>
> >
> > The kernel provides kcalloc.
>
>
> I went looking for it, and found it in fs/cifs/misc.c. I'm hesitant
> to link to a function defined in the filesystem code just to save 1
> line of code
It's more global than that.
~Randy
^ permalink raw reply
* Re: [RFC PATCH 1/4] PHY Abstraction Layer III (now with more splitiness)
From: Andy Fleming @ 2005-07-27 18:01 UTC (permalink / raw)
To: Francois Romieu; +Cc: Netdev, linuxppc-embedded
In-Reply-To: <20050725210646.GA17828@electric-eye.fr.zoreil.com>
On Jul 25, 2005, at 16:06, Francois Romieu wrote:
[snip]
>
>> +config DAVICOM_PHY
>> + bool "Drivers for Davicom PHYs"
>> + depends on PHYLIB
>> + ---help---
>> + Currently supports dm9161e and dm9131
>>
> [snip]
Yeah, I resisted splitting the patch up for this reason. Suffice it
to say, you have to apply patch #2 to not break everything.
Splitting the PHY driver code from the PHY layer is just for
"convenience"
>> +int mdiobus_register(struct mii_bus *bus)
>> +{
>> + int i;
>> + int err = 0;
>> +
>> + spin_lock_init(&bus->mdio_lock);
>> +
>> + if (NULL == bus || NULL == bus->name ||
>> + NULL == bus->read ||
>> + NULL == bus->write)
>>
>
> Be spartan:
> if (!bus || !bus->name || !bus->read || !bus->write)
I think we have to agree to disagree here. I could be convinced, but
I'm partial to using NULL explicitly.
>> +
>> +/* Convenience function to print out the current phy status
>> + */
>> +void phy_print_status(struct phy_device *phydev)
>> +{
>> + pr_info("%s: Link is %s", phydev->dev.bus_id,
>> + phydev->link ? "Up" : "Down");
>> + if (phydev->link)
>> + printk(" - %d/%s", phydev->speed,
>>
>
> Missing KERN_SOMETHING in the printk.
Actually, KERN_SOMETHING would muck up the line, and make it look
like this:
phy0:0: Link is Up<3> - 1000/Full
That's why it's like that.
>> +/* A mapping of all SUPPORTED settings to speed/duplex */
>> +static struct phy_setting settings[] = {
>> + { .speed = 10000, .duplex = DUPLEX_FULL,
>> + .setting = SUPPORTED_10000baseT_Full,
>> + },
>> + { .speed = SPEED_1000, .duplex = DUPLEX_FULL,
>> + .setting = SUPPORTED_1000baseT_Full,
>> + },
>> + { .speed = SPEED_1000, .duplex = DUPLEX_HALF,
>> + .setting = SUPPORTED_1000baseT_Half,
>> + },
>> + { .speed = SPEED_100, .duplex = DUPLEX_FULL,
>> + .setting = SUPPORTED_100baseT_Full,
>> + },
>> + { .speed = SPEED_100, .duplex = DUPLEX_HALF,
>> + .setting = SUPPORTED_100baseT_Half,
>> + },
>> + { .speed = SPEED_10, .duplex = DUPLEX_FULL,
>> + .setting = SUPPORTED_10baseT_Full,
>> + },
>> + { .speed = SPEED_10, .duplex = DUPLEX_HALF,
>> + .setting = SUPPORTED_10baseT_Half,
>> + },
>> +};
>>
>
> Would you veto some macro to initialise this array ?
Depends on the macro. :) I'm not keen on writing it, but I would
support one that:
a) works
b) Isn't uglier than the current solution. :)
>> +static inline int phy_find_setting(int speed, int duplex)
>> +{
>> + int idx = 0;
>> +
>> + while (idx < MAX_NUM_SETTINGS &&
>> + (settings[idx].speed != speed ||
>> + settings[idx].duplex != duplex))
>> + idx++;
>>
>
> "for" loop in disguise ?
Well.... I think it falls into the gray area. It's searching until
it finds something, which implies "while" to me. Really it's more of
a while...until. Of course, a for loop could be used, but I often
worry about using a for loop's iterator variable outside of the
loop. I will change to ARRAY_SIZE, though.
>
>
>> +
>> + return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
>>
>
> Ok (dunno if "idx % MAX_NUM_SETTINGS" is more idiomatic or not).
That would be completely different. The current code makes sure
that, if no valid match was found, the last value in the array is
returned. Using % would result in the first value being returned. I
was defaulting to the lowest setting.
>> +int phy_start_interrupts(struct phy_device *phydev)
>> +{
>> + int err = 0;
>> +
>> + INIT_WORK(&phydev->phy_queue, phy_change, phydev);
>> +
>> + if (request_irq(phydev->irq, phy_interrupt,
>> + SA_SHIRQ,
>> + "phy_interrupt",
>> + phydev) < 0) {
>>
>
> Please, don't do that :o(
>
> err = request_irq(phydev->irq, phy_interrupt, SA_SHIRQ,
> "phy_interrupt", phydev);
> if (err < 0)
> ...
I did a cursory search, and didn't find any other drivers which use
this method. Which is the method preferred in Linux?
>> + printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
>> + phydev->bus->name,
>> + phydev->irq);
>> + phydev->irq = PHY_POLL;
>> + return 0;
>>
>
> The description of the function says "Returns 0 on success".
Failing to request the IRQ does not result in failure of the
function. It falls back to polling, instead.
However, it can fail if phy_enable_interrupts() fails, which would
happen if a hardware issue occurred.
>> + /* Otherwise, we allocate the device, and initialize the
>> + * default values */
>> + dev = kmalloc(sizeof(*dev), GFP_KERNEL);
>> +
>> + if (NULL == dev) {
>> + errno = -ENOMEM;
>> + return NULL;
>> + }
>> +
>> + memset(dev, 0, sizeof(*dev));
>>
>
> The kernel provides kcalloc.
I went looking for it, and found it in fs/cifs/misc.c. I'm hesitant
to link to a function defined in the filesystem code just to save 1
line of code
I agree with all the other suggestions, and will implement them.
^ permalink raw reply
* [PATCH] emac: add bamboo support
From: Matt Porter @ 2005-07-27 17:42 UTC (permalink / raw)
To: jgarzik, netdev; +Cc: wfarnsworth
Adds support for the Bamboo board phys in the EMAC driver.
Please apply.
Signed-off-by: Wade Farnsworth <wfarnsworth@mvista.com>
Signed-off-by: Matt Porter <mporter@kernel.crashing.org>
diff -uprN linux-2.6.12/drivers/net/ibm_emac/ibm_emac_phy.c linux-2.6.12-440ep/drivers/net/ibm_emac/ibm_emac_phy.c
--- linux-2.6.12/drivers/net/ibm_emac/ibm_emac_phy.c 2005-06-17 12:48:29.000000000 -0700
+++ linux-2.6.12-440ep/drivers/net/ibm_emac/ibm_emac_phy.c 2005-07-25 11:32:38.000000000 -0700
@@ -24,6 +24,7 @@
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/delay.h>
+#include <linux/vmalloc.h>
#include "ibm_emac_phy.h"
@@ -78,6 +79,45 @@ static int cis8201_init(struct mii_phy *
return 0;
}
+#ifdef CONFIG_BAMBOO
+static int ac104_init(struct mii_phy *phy)
+{
+ /*
+ * SW2 on the Bamboo is used for ethernet configuration and is accessed
+ * via the CONFIG2 register in the FPGA. If the ANEG pin is set,
+ * overwrite the supported features with the settings in SW2.
+ */
+ u8 *config2_addr, config2_val;
+ config2_addr = ioremap64(BAMBOO_FPGA_CONFIG2_REG_ADDR, 0x8);
+ config2_val = * config2_addr;
+ iounmap(config2_addr);
+ if (BAMBOO_AUTONEGOTIATE(config2_val))
+ return 0;
+ phy->def->features = SUPPORTED_TP | SUPPORTED_MII;
+ if (BAMBOO_FORCE_100Mbps(config2_val)) {
+ phy->speed = SPEED_100;
+ if (BAMBOO_FULL_DUPLEX_EN(config2_val)) {
+ phy->def->features |= SUPPORTED_100baseT_Full;
+ phy->duplex = DUPLEX_FULL;
+ } else {
+ phy->def->features |= SUPPORTED_100baseT_Half;
+ phy->duplex = DUPLEX_HALF;
+ }
+ } else {
+ phy->speed = SPEED_10;
+ if (BAMBOO_FULL_DUPLEX_EN(config2_val)) {
+ phy->def->features |= SUPPORTED_10baseT_Full;
+ phy->duplex = DUPLEX_FULL;
+ } else {
+ phy->def->features |= SUPPORTED_10baseT_Half;
+ phy->duplex = DUPLEX_HALF;
+ }
+ }
+
+ return 0;
+}
+#endif
+
static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
{
u16 ctl, adv;
@@ -226,6 +266,17 @@ static struct mii_phy_ops cis8201_phy_op
read_link:cis8201_read_link
};
+/* AC104 phy ops */
+static struct mii_phy_ops ac104_phy_ops = {
+#ifdef CONFIG_BAMBOO
+ init:ac104_init,
+#endif
+ setup_aneg:genmii_setup_aneg,
+ setup_forced:genmii_setup_forced,
+ poll_link:genmii_poll_link,
+ read_link:genmii_read_link
+};
+
/* Generic implementation for most 10/100 PHYs */
static struct mii_phy_ops generic_phy_ops = {
setup_aneg:genmii_setup_aneg,
@@ -234,6 +285,15 @@ static struct mii_phy_ops generic_phy_op
read_link:genmii_read_link
};
+static struct mii_phy_def ac104_phy_def = {
+ phy_id:0x00225540,
+ phy_id_mask:0x00fffff0,
+ name:"AC104 Ethernet",
+ features:MII_BASIC_FEATURES,
+ magic_aneg:0,
+ ops:&ac104_phy_ops
+};
+
static struct mii_phy_def cis8201_phy_def = {
phy_id:0x000fc410,
phy_id_mask:0x000ffff0,
@@ -254,6 +314,7 @@ static struct mii_phy_def genmii_phy_def
static struct mii_phy_def *mii_phy_table[] = {
&cis8201_phy_def,
+ &ac104_phy_def,
&genmii_phy_def,
NULL
};
^ permalink raw reply
* 令您销魂的艳(图)
From: ȬȬ @ 2005-07-27 8:04 UTC (permalink / raw)
To: Reader
令您销魂的艳(图):详情进入...
http://sskkm.kuw.cn
花花
2005年07月27日
^ permalink raw reply
* Re: [2.6 patch] NETCONSOLE must depend on INET
From: David S. Miller @ 2005-07-27 2:53 UTC (permalink / raw)
To: mpm; +Cc: bunk, jgarzik, netdev, linux-kernel, shemminger
In-Reply-To: <20050727023636.GP12006@waste.org>
From: Matt Mackall <mpm@selenic.com>
Date: Tue, 26 Jul 2005 19:36:37 -0700
> [sch added to cc: as I think he's the effective pktgen maintainer]
No, that would be Robert Olsson.
> Move in_aton from net/ipv4/utils.c to net/core/utils.c
Fair enough.
^ permalink raw reply
* Re: [2.6 patch] NETCONSOLE must depend on INET
From: Matt Mackall @ 2005-07-27 2:36 UTC (permalink / raw)
To: David S. Miller; +Cc: bunk, jgarzik, netdev, linux-kernel, Stephen Hemminger
In-Reply-To: <20050726.170349.10935659.davem@davemloft.net>
[sch added to cc: as I think he's the effective pktgen maintainer]
On Tue, Jul 26, 2005 at 05:03:49PM -0700, David S. Miller wrote:
> From: Matt Mackall <mpm@selenic.com>
> Date: Tue, 26 Jul 2005 16:58:24 -0700
>
> > On Tue, Jul 26, 2005 at 04:32:02PM -0700, David S. Miller wrote:
> > > More seriously, please submit a version of whatever you
> > > believe to be the more correct fix so it can be reviewed
> > > and integrated.
> >
> > Do you have a preferred location to put this function or
> > shall I invent one?
>
> I actually can't wait to see where you're going to
> to put a function that transforms "IPV4 addresses"
> from ascii other than some place protected by CONFIG_INET.
> :-)
# HG changeset patch
# User mpm@selenic.com
# Node ID 6cdd6f36d53678a016cfbf5ce667cbd91504d538
# Parent 75716ae25f9d87ee2a5ef7c4df2d8f86e0f3f762
Move in_aton from net/ipv4/utils.c to net/core/utils.c
Move in_aton to allow netpoll and pktgen to work without the rest of
the IPv4 stack. Fix whitespace and add comment for the odd placement.
Delete now-empty net/ipv4/utils.c
Re-enable netpoll/netconsole without CONFIG_INET
Signed-off-by: Matt Mackall <mpm@selenic.com>
diff -r 75716ae25f9d -r 6cdd6f36d536 drivers/net/Kconfig
--- a/drivers/net/Kconfig Tue Jul 26 23:21:24 2005
+++ b/drivers/net/Kconfig Wed Jul 27 02:31:24 2005
@@ -2544,7 +2544,7 @@
config NETCONSOLE
tristate "Network console logging support (EXPERIMENTAL)"
- depends on NETDEVICES && INET && EXPERIMENTAL
+ depends on NETDEVICES && EXPERIMENTAL
---help---
If you want to log kernel messages over the network, enable this.
See <file:Documentation/networking/netconsole.txt> for details.
diff -r 75716ae25f9d -r 6cdd6f36d536 net/core/utils.c
--- a/net/core/utils.c Tue Jul 26 23:21:24 2005
+++ b/net/core/utils.c Wed Jul 27 02:31:24 2005
@@ -23,9 +23,9 @@
#include <linux/percpu.h>
#include <linux/init.h>
+#include <asm/byteorder.h>
#include <asm/system.h>
#include <asm/uaccess.h>
-
/*
This is a maximally equidistributed combined Tausworthe generator
@@ -153,3 +153,38 @@
EXPORT_SYMBOL(net_random);
EXPORT_SYMBOL(net_ratelimit);
EXPORT_SYMBOL(net_srandom);
+
+/*
+ * Convert an ASCII string to binary IP.
+ * This is outside of net/ipv4/ because various code that uses IP addresses
+ * is otherwise not dependent on the TCP/IP stack.
+ */
+
+__u32 in_aton(const char *str)
+{
+ unsigned long l;
+ unsigned int val;
+ int i;
+
+ l = 0;
+ for (i = 0; i < 4; i++)
+ {
+ l <<= 8;
+ if (*str != '\0')
+ {
+ val = 0;
+ while (*str != '\0' && *str != '.')
+ {
+ val *= 10;
+ val += *str - '0';
+ str++;
+ }
+ l |= val;
+ if (*str != '\0')
+ str++;
+ }
+ }
+ return(htonl(l));
+}
+
+EXPORT_SYMBOL(in_aton);
diff -r 75716ae25f9d -r 6cdd6f36d536 net/ipv4/Makefile
--- a/net/ipv4/Makefile Tue Jul 26 23:21:24 2005
+++ b/net/ipv4/Makefile Wed Jul 27 02:31:24 2005
@@ -2,7 +2,7 @@
# Makefile for the Linux TCP/IP (INET) layer.
#
-obj-y := utils.o route.o inetpeer.o protocol.o \
+obj-y := route.o inetpeer.o protocol.o \
ip_input.o ip_fragment.o ip_forward.o ip_options.o \
ip_output.o ip_sockglue.o \
tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \
diff -r 75716ae25f9d -r 6cdd6f36d536 net/ipv4/utils.c
--- a/net/ipv4/utils.c Tue Jul 26 23:21:24 2005
+++ /dev/null Wed Jul 27 02:31:24 2005
@@ -1,59 +0,0 @@
-/*
- * INET An implementation of the TCP/IP protocol suite for the LINUX
- * operating system. INET is implemented using the BSD Socket
- * interface as the means of communication with the user level.
- *
- * Various kernel-resident INET utility functions; mainly
- * for format conversion and debugging output.
- *
- * Version: $Id: utils.c,v 1.8 2000/10/03 07:29:01 anton Exp $
- *
- * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *
- * Fixes:
- * Alan Cox : verify_area check.
- * Alan Cox : removed old debugging.
- * Andi Kleen : add net_ratelimit()
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <asm/byteorder.h>
-
-/*
- * Convert an ASCII string to binary IP.
- */
-
-__u32 in_aton(const char *str)
-{
- unsigned long l;
- unsigned int val;
- int i;
-
- l = 0;
- for (i = 0; i < 4; i++)
- {
- l <<= 8;
- if (*str != '\0')
- {
- val = 0;
- while (*str != '\0' && *str != '.')
- {
- val *= 10;
- val += *str - '0';
- str++;
- }
- l |= val;
- if (*str != '\0')
- str++;
- }
- }
- return(htonl(l));
-}
-
-EXPORT_SYMBOL(in_aton);
--
Mathematics is the supreme nostalgia of our time.
^ permalink raw reply
* Re: [PATCH] move ip[6]_queue rerouting up to nf_queue
From: David S. Miller @ 2005-07-27 0:08 UTC (permalink / raw)
To: laforge; +Cc: netdev, netfilter-devel
In-Reply-To: <20050726235223.GD4673@rama.de.gnumonks.org>
From: Harald Welte <laforge@netfilter.org>
Date: Wed, 27 Jul 2005 01:52:23 +0200
> See attached file "12-queue-reroute.patch.2".
All patches applied to net-2.6.14, thanks Harald.
^ permalink raw reply
* Re: [2.6 patch] NETCONSOLE must depend on INET
From: David S. Miller @ 2005-07-27 0:03 UTC (permalink / raw)
To: mpm; +Cc: bunk, jgarzik, netdev, linux-kernel
In-Reply-To: <20050726235824.GN12006@waste.org>
From: Matt Mackall <mpm@selenic.com>
Date: Tue, 26 Jul 2005 16:58:24 -0700
> On Tue, Jul 26, 2005 at 04:32:02PM -0700, David S. Miller wrote:
> > More seriously, please submit a version of whatever you
> > believe to be the more correct fix so it can be reviewed
> > and integrated.
>
> Do you have a preferred location to put this function or
> shall I invent one?
I actually can't wait to see where you're going to
to put a function that transforms "IPV4 addresses"
from ascii other than some place protected by CONFIG_INET.
:-)
^ permalink raw reply
* Re: [2.6 patch] NETCONSOLE must depend on INET
From: Matt Mackall @ 2005-07-26 23:58 UTC (permalink / raw)
To: David S. Miller; +Cc: bunk, jgarzik, netdev, linux-kernel
In-Reply-To: <20050726.163202.119887768.davem@davemloft.net>
On Tue, Jul 26, 2005 at 04:32:02PM -0700, David S. Miller wrote:
> From: Matt Mackall <mpm@selenic.com>
> Date: Tue, 26 Jul 2005 16:20:43 -0700
>
> > This problem also exists in PKTGEN. And this fix is incorrect as
> > neither is dependent on the IP part of the networking stack in any
> > substantive way. The right fix is to make inet_aton available outside
> > of CONFIG_INET (preferred) or making private copies of inet_aton.
>
> Adrian posted a fix, you whine, that's why Adrian's fix
> went in :-)
Adrian's fix is the moral equivalent of throwing in a cast to shut up
a compiler warning for a legitimate bug.
> More seriously, please submit a version of whatever you
> believe to be the more correct fix so it can be reviewed
> and integrated.
Do you have a preferred location to put this function or
shall I invent one?
--
Mathematics is the supreme nostalgia of our time.
^ permalink raw reply
* Re: [PATCH] move ip[6]_queue rerouting up to nf_queue
From: David S. Miller @ 2005-07-26 23:56 UTC (permalink / raw)
To: laforge; +Cc: netdev, netfilter-devel
In-Reply-To: <20050726235223.GD4673@rama.de.gnumonks.org>
From: Harald Welte <laforge@netfilter.org>
Date: Wed, 27 Jul 2005 01:52:23 +0200
> I'll also prepare a modified version of the nfnetlink module
> refcounting patch, but I'm not submitting it until you've applied ll
> of the other pending netfilter patches since they would conflict
> (re-attached the other two pending patches from earlier today.
No need, see my other email, I took care of this for you.
^ permalink raw reply
* Re: [PATCH] move ip[6]_queue rerouting up to nf_queue
From: Harald Welte @ 2005-07-26 23:52 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev, netfilter-devel
In-Reply-To: <20050726.161300.12130036.davem@davemloft.net>
[-- Attachment #1.1: Type: text/plain, Size: 1155 bytes --]
On Tue, Jul 26, 2005 at 04:13:00PM -0700, David S. Miller wrote:
> > please apply. If you dislike the yet-another-NPROTO nature of it, I'll
> > prepare one with kmalloc-at-startup.
>
> Please do, thanks.
See attached file "12-queue-reroute.patch.2". I'll also prepare a
modified version of the nfnetlink module refcounting patch, but I'm not
submitting it until you've applied ll of the other pending netfilter
patches since they would conflict (re-attached the other two pending
patches from earlier today. they apply cleanly on top of the
"12-queue-reroute.patch.2". "15-nf_info_reroute-fix.patch" is already
included and no longer needed.
Please apply, thanks.
futher BSS-NPROTO-sized-array clanups of old code are following in the
next couple of days.
--
- Harald Welte <laforge@netfilter.org> http://netfilter.org/
============================================================================
"Fragmentation is like classful addressing -- an interesting early
architectural error that shows how much experimentation was going
on while IP was being designed." -- Paul Vixie
[-- Attachment #1.2: 12-queue-reroute.patch.2 --]
[-- Type: text/plain, Size: 13803 bytes --]
Move reroute-after-queue code from ip_queue and ip6_queue up to the
nf_queue layer. The rerouting functionality is required by the core,
therefore it has to be implemented by the core and not in individual queue
handlers.
Signed-off-by: Harald Welte <laforge@netfilter.org>
---
commit 1cf7694f46449b2602133e0668e53dc4219f73d7
tree 226a89eeb4cb2cd45cfede8106602aa77d95bee1
parent 2b8f1184ff934f6395ca86fd23fc0c2b316532db
author laforge <laforge@netfilter.org> Mi, 27 Jul 2005 01:42:02 +0200
committer laforge <laforge@netfilter.org> Mi, 27 Jul 2005 01:42:02 +0200
include/linux/netfilter.h | 11 +++++++
net/core/netfilter.c | 66 +++++++++++++++++++++++++++++++++-------
net/ipv4/netfilter.c | 64 ++++++++++++++++++++++++++++++++++++++-
net/ipv4/netfilter/ip_queue.c | 27 ----------------
net/ipv6/af_inet6.c | 6 ++++
net/ipv6/netfilter.c | 62 ++++++++++++++++++++++++++++++++++++++
net/ipv6/netfilter/ip6_queue.c | 24 ---------------
7 files changed, 195 insertions(+), 65 deletions(-)
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -198,6 +198,17 @@ extern void nf_invalidate_cache(int pf);
Returns true or false. */
extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len);
+struct nf_queue_rerouter {
+ void (*save)(const struct sk_buff *skb, struct nf_info *info);
+ int (*reroute)(struct sk_buff **skb, const struct nf_info *info);
+ int rer_size;
+};
+
+#define nf_info_reroute(x) ((void *)x + sizeof(struct nf_info))
+
+extern int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer);
+extern int nf_unregister_queue_rerouter(int pf);
+
#else /* !CONFIG_NETFILTER */
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
diff --git a/net/core/netfilter.c b/net/core/netfilter.c
--- a/net/core/netfilter.c
+++ b/net/core/netfilter.c
@@ -53,6 +53,9 @@ static struct nf_queue_handler_t {
nf_queue_outfn_t outfn;
void *data;
} queue_handler[NPROTO];
+
+static struct nf_queue_rerouter *queue_rerouter;
+
static DEFINE_RWLOCK(queue_handler_lock);
int nf_register_hook(struct nf_hook_ops *reg)
@@ -260,11 +263,34 @@ int nf_unregister_queue_handler(int pf)
return 0;
}
+int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer)
+{
+ if (pf >= NPROTO)
+ return -EINVAL;
+
+ write_lock_bh(&queue_handler_lock);
+ memcpy(&queue_rerouter[pf], rer, sizeof(queue_rerouter[pf]));
+ write_unlock_bh(&queue_handler_lock);
+
+ return 0;
+}
+
+int nf_unregister_queue_rerouter(int pf)
+{
+ if (pf >= NPROTO)
+ return -EINVAL;
+
+ write_lock_bh(&queue_handler_lock);
+ memset(&queue_rerouter[pf], 0, sizeof(queue_rerouter[pf]));
+ write_unlock_bh(&queue_handler_lock);
+ return 0;
+}
+
/*
* Any packet that leaves via this function must come back
* through nf_reinject().
*/
-static int nf_queue(struct sk_buff *skb,
+static int nf_queue(struct sk_buff **skb,
struct list_head *elem,
int pf, unsigned int hook,
struct net_device *indev,
@@ -282,17 +308,17 @@ static int nf_queue(struct sk_buff *skb,
read_lock(&queue_handler_lock);
if (!queue_handler[pf].outfn) {
read_unlock(&queue_handler_lock);
- kfree_skb(skb);
+ kfree_skb(*skb);
return 1;
}
- info = kmalloc(sizeof(*info), GFP_ATOMIC);
+ info = kmalloc(sizeof(*info)+queue_rerouter[pf].rer_size, GFP_ATOMIC);
if (!info) {
if (net_ratelimit())
printk(KERN_ERR "OOM queueing packet %p\n",
- skb);
+ *skb);
read_unlock(&queue_handler_lock);
- kfree_skb(skb);
+ kfree_skb(*skb);
return 1;
}
@@ -311,15 +337,21 @@ static int nf_queue(struct sk_buff *skb,
if (outdev) dev_hold(outdev);
#ifdef CONFIG_BRIDGE_NETFILTER
- if (skb->nf_bridge) {
- physindev = skb->nf_bridge->physindev;
+ if ((*skb)->nf_bridge) {
+ physindev = (*skb)->nf_bridge->physindev;
if (physindev) dev_hold(physindev);
- physoutdev = skb->nf_bridge->physoutdev;
+ physoutdev = (*skb)->nf_bridge->physoutdev;
if (physoutdev) dev_hold(physoutdev);
}
#endif
+ if (queue_rerouter[pf].save)
+ queue_rerouter[pf].save(*skb, info);
+
+ status = queue_handler[pf].outfn(*skb, info, queue_handler[pf].data);
+
+ if (status >= 0 && queue_rerouter[pf].reroute)
+ status = queue_rerouter[pf].reroute(skb, info);
- status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
read_unlock(&queue_handler_lock);
if (status < 0) {
@@ -332,9 +364,11 @@ static int nf_queue(struct sk_buff *skb,
#endif
module_put(info->elem->owner);
kfree(info);
- kfree_skb(skb);
+ kfree_skb(*skb);
+
return 1;
}
+
return 1;
}
@@ -365,7 +399,7 @@ next_hook:
ret = -EPERM;
} else if (verdict == NF_QUEUE) {
NFDEBUG("nf_hook: Verdict = QUEUE.\n");
- if (!nf_queue(*pskb, elem, pf, hook, indev, outdev, okfn))
+ if (!nf_queue(pskb, elem, pf, hook, indev, outdev, okfn))
goto next_hook;
}
unlock:
@@ -428,7 +462,7 @@ void nf_reinject(struct sk_buff *skb, st
break;
case NF_QUEUE:
- if (!nf_queue(skb, elem, info->pf, info->hook,
+ if (!nf_queue(&skb, elem, info->pf, info->hook,
info->indev, info->outdev, info->okfn))
goto next_hook;
break;
@@ -555,6 +589,12 @@ void __init netfilter_init(void)
{
int i, h;
+ queue_rerouter = kmalloc(NPROTO * sizeof(struct nf_queue_rerouter),
+ GFP_KERNEL);
+ if (!queue_rerouter)
+ panic("netfilter: cannot allocate queue rerouter array\n");
+ memset(queue_rerouter, 0, NPROTO * sizeof(struct nf_queue_rerouter));
+
for (i = 0; i < NPROTO; i++) {
for (h = 0; h < NF_MAX_HOOKS; h++)
INIT_LIST_HEAD(&nf_hooks[i][h]);
@@ -573,4 +613,6 @@ EXPORT_SYMBOL(nf_reinject);
EXPORT_SYMBOL(nf_setsockopt);
EXPORT_SYMBOL(nf_unregister_hook);
EXPORT_SYMBOL(nf_unregister_queue_handler);
+EXPORT_SYMBOL_GPL(nf_register_queue_rerouter);
+EXPORT_SYMBOL_GPL(nf_unregister_queue_rerouter);
EXPORT_SYMBOL(nf_unregister_sockopt);
diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c
--- a/net/ipv4/netfilter.c
+++ b/net/ipv4/netfilter.c
@@ -1,10 +1,11 @@
-#include <linux/config.h>
+/* IPv4 specific functions of netfilter core */
+#include <linux/config.h>
#ifdef CONFIG_NETFILTER
-/* IPv4 specific functions of netfilter core */
#include <linux/kernel.h>
#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
#include <linux/tcp.h>
#include <linux/udp.h>
@@ -76,4 +77,63 @@ int ip_route_me_harder(struct sk_buff **
return 0;
}
EXPORT_SYMBOL(ip_route_me_harder);
+
+/*
+ * Extra routing may needed on local out, as the QUEUE target never
+ * returns control to the table.
+ */
+
+struct ip_rt_info {
+ u_int32_t daddr;
+ u_int32_t saddr;
+ u_int8_t tos;
+};
+
+static void queue_save(const struct sk_buff *skb, struct nf_info *info)
+{
+ struct ip_rt_info *rt_info = nf_info_reroute(info);
+
+ if (info->hook == NF_IP_LOCAL_OUT) {
+ const struct iphdr *iph = skb->nh.iph;
+
+ rt_info->tos = iph->tos;
+ rt_info->daddr = iph->daddr;
+ rt_info->saddr = iph->saddr;
+ }
+}
+
+static int queue_reroute(struct sk_buff **pskb, const struct nf_info *info)
+{
+ const struct ip_rt_info *rt_info = nf_info_reroute(info);
+
+ if (info->hook == NF_IP_LOCAL_OUT) {
+ struct iphdr *iph = (*pskb)->nh.iph;
+
+ if (!(iph->tos == rt_info->tos
+ && iph->daddr == rt_info->daddr
+ && iph->saddr == rt_info->saddr))
+ return ip_route_me_harder(pskb);
+ }
+ return 0;
+}
+
+static struct nf_queue_rerouter ip_reroute = {
+ .rer_size = sizeof(struct ip_rt_info),
+ .save = queue_save,
+ .reroute = queue_reroute,
+};
+
+static int init(void)
+{
+ return nf_register_queue_rerouter(PF_INET, &ip_reroute);
+}
+
+static void fini(void)
+{
+ nf_unregister_queue_rerouter(PF_INET);
+}
+
+module_init(init);
+module_exit(fini);
+
#endif /* CONFIG_NETFILTER */
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c
--- a/net/ipv4/netfilter/ip_queue.c
+++ b/net/ipv4/netfilter/ip_queue.c
@@ -43,17 +43,10 @@
#define NET_IPQ_QMAX 2088
#define NET_IPQ_QMAX_NAME "ip_queue_maxlen"
-struct ipq_rt_info {
- __u8 tos;
- __u32 daddr;
- __u32 saddr;
-};
-
struct ipq_queue_entry {
struct list_head list;
struct nf_info *info;
struct sk_buff *skb;
- struct ipq_rt_info rt_info;
};
typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long);
@@ -299,14 +292,6 @@ ipq_enqueue_packet(struct sk_buff *skb,
entry->info = info;
entry->skb = skb;
- if (entry->info->hook == NF_IP_LOCAL_OUT) {
- struct iphdr *iph = skb->nh.iph;
-
- entry->rt_info.tos = iph->tos;
- entry->rt_info.daddr = iph->daddr;
- entry->rt_info.saddr = iph->saddr;
- }
-
nskb = ipq_build_packet_message(entry, &status);
if (nskb == NULL)
goto err_out_free;
@@ -386,18 +371,6 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, st
return -ENOMEM;
memcpy(e->skb->data, v->payload, v->data_len);
- /*
- * Extra routing may needed on local out, as the QUEUE target never
- * returns control to the table.
- */
- if (e->info->hook == NF_IP_LOCAL_OUT) {
- struct iphdr *iph = e->skb->nh.iph;
-
- if (!(iph->tos == e->rt_info.tos
- && iph->daddr == e->rt_info.daddr
- && iph->saddr == e->rt_info.saddr))
- return ip_route_me_harder(&e->skb);
- }
return 0;
}
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -757,6 +757,9 @@ static int __init inet6_init(void)
err = igmp6_init(&inet6_family_ops);
if (err)
goto igmp_fail;
+ err = ipv6_netfilter_init();
+ if (err)
+ goto netfilter_fail;
/* Create /proc/foo6 entries. */
#ifdef CONFIG_PROC_FS
err = -ENOMEM;
@@ -813,6 +816,8 @@ proc_tcp6_fail:
raw6_proc_exit();
proc_raw6_fail:
#endif
+ ipv6_netfilter_fini();
+netfilter_fail:
igmp6_cleanup();
igmp_fail:
ndisc_cleanup();
@@ -852,6 +857,7 @@ static void __exit inet6_exit(void)
ip6_route_cleanup();
ipv6_packet_cleanup();
igmp6_cleanup();
+ ipv6_netfilter_fini();
ndisc_cleanup();
icmpv6_cleanup();
#ifdef CONFIG_SYSCTL
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -4,6 +4,8 @@
#include <linux/kernel.h>
#include <linux/ipv6.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6.h>
#include <net/dst.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
@@ -39,4 +41,64 @@ int ip6_route_me_harder(struct sk_buff *
}
EXPORT_SYMBOL(ip6_route_me_harder);
+/*
+ * Extra routing may needed on local out, as the QUEUE target never
+ * returns control to the table.
+ */
+
+struct ip6_rt_info {
+ struct in6_addr daddr;
+ struct in6_addr saddr;
+};
+
+static void save(const struct sk_buff *skb, struct nf_info *info)
+{
+ struct ip6_rt_info *rt_info = nf_info_reroute(info);
+
+ if (info->hook == NF_IP6_LOCAL_OUT) {
+ struct ipv6hdr *iph = skb->nh.ipv6h;
+
+ rt_info->daddr = iph->daddr;
+ rt_info->saddr = iph->saddr;
+ }
+}
+
+static int reroute(struct sk_buff **pskb, const struct nf_info *info)
+{
+ struct ip6_rt_info *rt_info = nf_info_reroute(info);
+
+ if (info->hook == NF_IP6_LOCAL_OUT) {
+ struct ipv6hdr *iph = (*pskb)->nh.ipv6h;
+ if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) ||
+ !ipv6_addr_equal(&iph->saddr, &rt_info->saddr))
+ return ip6_route_me_harder(*pskb);
+ }
+ return 0;
+}
+
+static struct nf_queue_rerouter ip6_reroute = {
+ .rer_size = sizeof(struct ip6_rt_info),
+ .save = &save,
+ .reroute = &reroute,
+};
+
+int __init ipv6_netfilter_init(void)
+{
+ return nf_register_queue_rerouter(PF_INET6, &ip6_reroute);
+}
+
+void __exit ipv6_netfilter_fini(void)
+{
+ nf_unregister_queue_rerouter(PF_INET6);
+}
+
+#else /* CONFIG_NETFILTER */
+int __init ipv6_netfilter_init(void)
+{
+ return 0;
+}
+
+void __exit ipv6_netfilter_fini(void)
+{
+}
#endif /* CONFIG_NETFILTER */
diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c
--- a/net/ipv6/netfilter/ip6_queue.c
+++ b/net/ipv6/netfilter/ip6_queue.c
@@ -47,16 +47,10 @@
#define NET_IPQ_QMAX 2088
#define NET_IPQ_QMAX_NAME "ip6_queue_maxlen"
-struct ipq_rt_info {
- struct in6_addr daddr;
- struct in6_addr saddr;
-};
-
struct ipq_queue_entry {
struct list_head list;
struct nf_info *info;
struct sk_buff *skb;
- struct ipq_rt_info rt_info;
};
typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long);
@@ -296,13 +290,6 @@ ipq_enqueue_packet(struct sk_buff *skb,
entry->info = info;
entry->skb = skb;
- if (entry->info->hook == NF_IP_LOCAL_OUT) {
- struct ipv6hdr *iph = skb->nh.ipv6h;
-
- entry->rt_info.daddr = iph->daddr;
- entry->rt_info.saddr = iph->saddr;
- }
-
nskb = ipq_build_packet_message(entry, &status);
if (nskb == NULL)
goto err_out_free;
@@ -382,17 +369,6 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, st
return -ENOMEM;
memcpy(e->skb->data, v->payload, v->data_len);
- /*
- * Extra routing may needed on local out, as the QUEUE target never
- * returns control to the table.
- * Not a nice way to cmp, but works
- */
- if (e->info->hook == NF_IP_LOCAL_OUT) {
- struct ipv6hdr *iph = e->skb->nh.ipv6h;
- if (!ipv6_addr_equal(&iph->daddr, &e->rt_info.daddr) ||
- !ipv6_addr_equal(&iph->saddr, &e->rt_info.saddr))
- return ip6_route_me_harder(e->skb);
- }
return 0;
}
[-- Attachment #1.3: 13-multiqueue.patch --]
[-- Type: text/plain, Size: 10270 bytes --]
netfiler core changes required by upcoming nfnetlink_queue code:
- split netfiler verdict in 16bit verdict and 16bit queue number
- add 'queuenum' argument to nf_queue_outfn_t and its users ip[6]_queue
- move NFNL_SUBSYS_ definitions from enum to #define
- introduce autoloading for nfnetlink subsystem modules
- add MODULE_ALIAS_NFNL_SUBSYS macro
- add nf_unregister_queue_handlers() to register all handlers for a given
nf_queue_outfn_t
- add more verbose DEBUGP macro definition to nfnetlink.c
- make nfnetlink_subsys_register fail if subsys already exists
- add some more comments and debug statements to nfnetlink.c
Signed-off-by: Harald Welte <laforge@netfilter.org>
---
commit 4557bbef246922e030176404bcf5aa05f9a81d21
tree af907043ed27a6f0ea87326aec9a473c4226be47
parent f61a93c9b4b59f82ef781a50ec955c50fe32e5db
author laforge <laforge@netfilter.org> Di, 26 Jul 2005 18:53:19 +0200
committer laforge <laforge@netfilter.org> Di, 26 Jul 2005 18:53:19 +0200
include/linux/netfilter.h | 14 +++++++++++-
include/linux/netfilter/nfnetlink.h | 20 ++++++++++--------
net/core/netfilter.c | 40 +++++++++++++++++++++++++++++------
net/ipv4/netfilter/ip_queue.c | 3 ++-
net/ipv6/netfilter/ip6_queue.c | 3 ++-
net/netfilter/nfnetlink.c | 28 +++++++++++++++++++------
6 files changed, 83 insertions(+), 25 deletions(-)
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -21,6 +21,16 @@
#define NF_STOP 5
#define NF_MAX_VERDICT NF_STOP
+/* we overload the higher bits for encoding auxiliary data such as the queue
+ * number. Not nice, but better than additional function arguments. */
+#define NF_VERDICT_MASK 0x0000ffff
+#define NF_VERDICT_BITS 16
+
+#define NF_VERDICT_QMASK 0xffff0000
+#define NF_VERDICT_QBITS 16
+
+#define NF_QUEUE_NR(x) ((x << NF_VERDICT_QBITS) & NF_VERDICT_QMASK || NF_QUEUE)
+
/* only for userspace compatibility */
#ifndef __KERNEL__
/* Generic cache responses from hook functions.
@@ -179,10 +189,12 @@ int nf_getsockopt(struct sock *sk, int p
/* Packet queuing */
typedef int (*nf_queue_outfn_t)(struct sk_buff *skb,
- struct nf_info *info, void *data);
+ struct nf_info *info,
+ unsigned int queuenum, void *data);
extern int nf_register_queue_handler(int pf,
nf_queue_outfn_t outfn, void *data);
extern int nf_unregister_queue_handler(int pf);
+extern void nf_unregister_queue_handlers(nf_queue_outfn_t outfn);
extern void nf_reinject(struct sk_buff *skb,
struct nf_info *info,
unsigned int verdict);
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -69,15 +69,14 @@ struct nfgenmsg {
#define NFNL_SUBSYS_ID(x) ((x & 0xff00) >> 8)
#define NFNL_MSG_TYPE(x) (x & 0x00ff)
-enum nfnl_subsys_id {
- NFNL_SUBSYS_NONE = 0,
- NFNL_SUBSYS_CTNETLINK,
- NFNL_SUBSYS_CTNETLINK_EXP,
- NFNL_SUBSYS_IPTNETLINK,
- NFNL_SUBSYS_QUEUE,
- NFNL_SUBSYS_ULOG,
- NFNL_SUBSYS_COUNT,
-};
+/* No enum here, otherwise __stringify() trick of MODULE_ALIAS_NFNL_SUBSYS()
+ * won't work anymore */
+#define NFNL_SUBSYS_NONE 0
+#define NFNL_SUBSYS_CTNETLINK 1
+#define NFNL_SUBSYS_CTNETLINK_EXP 2
+#define NFNL_SUBSYS_QUEUE 3
+#define NFNL_SUBSYS_ULOG 4
+#define NFNL_SUBSYS_COUNT 5
#ifdef __KERNEL__
@@ -142,5 +141,8 @@ extern int nfnetlink_send(struct sk_buff
int echo);
extern int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags);
+#define MODULE_ALIAS_NFNL_SUBSYS(subsys) \
+ MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys))
+
#endif /* __KERNEL__ */
#endif /* _NFNETLINK_H */
diff --git a/net/core/netfilter.c b/net/core/netfilter.c
--- a/net/core/netfilter.c
+++ b/net/core/netfilter.c
@@ -221,7 +221,8 @@ static unsigned int nf_iterate(struct li
verdict = elem->hook(hook, skb, indev, outdev, okfn);
if (verdict != NF_ACCEPT) {
#ifdef CONFIG_NETFILTER_DEBUG
- if (unlikely(verdict > NF_MAX_VERDICT)) {
+ if (unlikely((verdict & NF_VERDICT_MASK)
+ > NF_MAX_VERDICT)) {
NFDEBUG("Evil return from %p(%u).\n",
elem->hook, hook);
continue;
@@ -239,6 +240,9 @@ int nf_register_queue_handler(int pf, nf
{
int ret;
+ if (pf >= NPROTO)
+ return -EINVAL;
+
write_lock_bh(&queue_handler_lock);
if (queue_handler[pf].outfn)
ret = -EBUSY;
@@ -255,6 +259,9 @@ int nf_register_queue_handler(int pf, nf
/* The caller must flush their queue before this */
int nf_unregister_queue_handler(int pf)
{
+ if (pf >= NPROTO)
+ return -EINVAL;
+
write_lock_bh(&queue_handler_lock);
queue_handler[pf].outfn = NULL;
queue_handler[pf].data = NULL;
@@ -286,6 +293,20 @@ int nf_unregister_queue_rerouter(int pf)
return 0;
}
+void nf_unregister_queue_handlers(nf_queue_outfn_t outfn)
+{
+ int pf;
+
+ write_lock_bh(&queue_handler_lock);
+ for (pf = 0; pf < NPROTO; pf++) {
+ if (queue_handler[pf].outfn == outfn) {
+ queue_handler[pf].outfn = NULL;
+ queue_handler[pf].data = NULL;
+ }
+ }
+ write_unlock_bh(&queue_handler_lock);
+}
+
/*
* Any packet that leaves via this function must come back
* through nf_reinject().
@@ -295,7 +316,8 @@ static int nf_queue(struct sk_buff **skb
int pf, unsigned int hook,
struct net_device *indev,
struct net_device *outdev,
- int (*okfn)(struct sk_buff *))
+ int (*okfn)(struct sk_buff *),
+ unsigned int queuenum)
{
int status;
struct nf_info *info;
@@ -347,7 +369,8 @@ static int nf_queue(struct sk_buff **skb
if (queue_rerouter[pf].save)
queue_rerouter[pf].save(*skb, info);
- status = queue_handler[pf].outfn(*skb, info, queue_handler[pf].data);
+ status = queue_handler[pf].outfn(*skb, info, queuenum,
+ queue_handler[pf].data);
if (status >= 0 && queue_rerouter[pf].reroute)
status = queue_rerouter[pf].reroute(skb, info);
@@ -397,9 +420,10 @@ next_hook:
} else if (verdict == NF_DROP) {
kfree_skb(*pskb);
ret = -EPERM;
- } else if (verdict == NF_QUEUE) {
+ } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
NFDEBUG("nf_hook: Verdict = QUEUE.\n");
- if (!nf_queue(pskb, elem, pf, hook, indev, outdev, okfn))
+ if (!nf_queue(pskb, elem, pf, hook, indev, outdev, okfn,
+ verdict >> NF_VERDICT_BITS))
goto next_hook;
}
unlock:
@@ -456,14 +480,15 @@ void nf_reinject(struct sk_buff *skb, st
info->okfn, INT_MIN);
}
- switch (verdict) {
+ switch (verdict & NF_VERDICT_MASK) {
case NF_ACCEPT:
info->okfn(skb);
break;
case NF_QUEUE:
if (!nf_queue(&skb, elem, info->pf, info->hook,
- info->indev, info->outdev, info->okfn))
+ info->indev, info->outdev, info->okfn,
+ verdict >> NF_VERDICT_BITS))
goto next_hook;
break;
}
@@ -607,6 +632,7 @@ EXPORT_SYMBOL(nf_reinject);
EXPORT_SYMBOL(nf_setsockopt);
EXPORT_SYMBOL(nf_unregister_hook);
EXPORT_SYMBOL(nf_unregister_queue_handler);
+EXPORT_SYMBOL_GPL(nf_unregister_queue_handlers);
EXPORT_SYMBOL_GPL(nf_register_queue_rerouter);
EXPORT_SYMBOL_GPL(nf_unregister_queue_rerouter);
EXPORT_SYMBOL(nf_unregister_sockopt);
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c
--- a/net/ipv4/netfilter/ip_queue.c
+++ b/net/ipv4/netfilter/ip_queue.c
@@ -274,7 +274,8 @@ nlmsg_failure:
}
static int
-ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info, void *data)
+ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info,
+ unsigned int queuenum, void *data)
{
int status = -EINVAL;
struct sk_buff *nskb;
diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c
--- a/net/ipv6/netfilter/ip6_queue.c
+++ b/net/ipv6/netfilter/ip6_queue.c
@@ -272,7 +272,8 @@ nlmsg_failure:
}
static int
-ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info, void *data)
+ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info,
+ unsigned int queuenum, void *data)
{
int status = -EINVAL;
struct sk_buff *nskb;
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -42,7 +42,9 @@ MODULE_LICENSE("GPL");
static char __initdata nfversion[] = "0.30";
#if 0
-#define DEBUGP printk
+#define DEBUGP(format, args...) \
+ printk(KERN_DEBUG "%s(%d):%s(): " format, __FILE__, \
+ __LINE__, __FUNCTION__, ## args)
#else
#define DEBUGP(format, args...)
#endif
@@ -65,11 +67,11 @@ int nfnetlink_subsys_register(struct nfn
{
DEBUGP("registering subsystem ID %u\n", n->subsys_id);
- /* If the netlink socket wasn't created, then fail */
- if (!nfnl)
- return -1;
-
nfnl_lock();
+ if (subsys_table[n->subsys_id]) {
+ nfnl_unlock();
+ return -EBUSY;
+ }
subsys_table[n->subsys_id] = n;
nfnl_unlock();
@@ -225,8 +227,18 @@ static inline int nfnetlink_rcv_msg(stru
type = nlh->nlmsg_type;
ss = nfnetlink_get_subsys(type);
- if (!ss)
+ if (!ss) {
+#ifdef CONFIG_KMOD
+ /* don't call nfnl_shunlock, since it would reenter
+ * with further packet processing */
+ up(&nfnl_sem);
+ request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
+ nfnl_shlock();
+ ss = nfnetlink_get_subsys(type);
+ if (!ss)
+#endif
goto err_inval;
+ }
nc = nfnetlink_find_client(type, ss);
if (!nc) {
@@ -250,12 +262,14 @@ static inline int nfnetlink_rcv_msg(stru
if (err < 0)
goto err_inval;
+ DEBUGP("calling handler\n");
err = nc->call(nfnl, skb, nlh, cda, errp);
*errp = err;
return err;
}
err_inval:
+ DEBUGP("returning -EINVAL\n");
*errp = -EINVAL;
return -1;
}
@@ -309,6 +323,8 @@ static void nfnetlink_rcv(struct sock *s
kfree_skb(skb);
}
+ /* don't call nfnl_shunlock, since it would reenter
+ * with further packet processing */
up(&nfnl_sem);
} while(nfnl && nfnl->sk_receive_queue.qlen);
}
[-- Attachment #1.4: 14-nfnetlink_queue.patch --]
[-- Type: text/plain, Size: 34813 bytes --]
Add "nfnetlink_queue" netfilter queue handler over nfnetlink:
- Add new nfnetlink_queue module
- Add new ipt_NFQUEUE and ip6t_NFQUEUE modules to access queue numbers 1-65535
- Mark ip_queue and ip6_queue Kconfig options as OBSOLETE
- Update feature-removal-schedule to remove ip[6]_queue in December
Signed-off-by: Harald Welte <laforge@netfilter.org>
---
commit 7237f235b6bd25773b566d88ec846245c852b1ee
tree d8bf4d0e82e9f0bdd570d1678f162f6c59d2d361
parent 4557bbef246922e030176404bcf5aa05f9a81d21
author laforge <laforge@netfilter.org> Di, 26 Jul 2005 23:52:26 +0200
committer laforge <laforge@netfilter.org> Di, 26 Jul 2005 23:52:26 +0200
Documentation/feature-removal-schedule.txt | 12
include/linux/netfilter/nfnetlink_queue.h | 85 +++
include/linux/netfilter_ipv4/ipt_NFQUEUE.h | 16 +
net/ipv4/netfilter/Kconfig | 6
net/ipv4/netfilter/Makefile | 1
net/ipv4/netfilter/ipt_NFQUEUE.c | 70 ++
net/ipv6/netfilter/Kconfig | 11
net/ipv6/netfilter/Makefile | 1
net/ipv6/netfilter/ip6t_NFQUEUE.c | 70 ++
net/netfilter/Kconfig | 8
net/netfilter/Makefile | 1
net/netfilter/nfnetlink_queue.c | 877 ++++++++++++++++++++++++++++
12 files changed, 1153 insertions(+), 5 deletions(-)
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -135,3 +135,15 @@ Why: With the 16-bit PCMCIA subsystem no
pcmciautils package available at
http://kernel.org/pub/linux/utils/kernel/pcmcia/
Who: Dominik Brodowski <linux@brodo.de>
+
+---------------------------
+
+What: ip_queue and ip6_queue (old ipv4-only and ipv6-only netfilter queue)
+When: December 2005
+Why: This interface has been obsoleted by the new layer3-independent
+ "nfnetlink_queue". The Kernel interface is compatible, so the old
+ ip[6]tables "QUEUE" targets still work and will transparently handle
+ all packets into nfnetlink queue number 0. Userspace users will have
+ to link against API-compatible library on top of libnfnetlink_queue
+ instead of the current 'libipq'.
+Who: Harald Welte <laforge@netfilter.org>
diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h
new file mode 100644
--- /dev/null
+++ b/include/linux/netfilter/nfnetlink_queue.h
@@ -0,0 +1,85 @@
+#ifndef _NFNETLINK_QUEUE_H
+#define _NFNETLINK_QUEUE_H
+
+#include <linux/netfilter/nfnetlink.h>
+
+enum nfqnl_msg_types {
+ NFQNL_MSG_PACKET, /* packet from kernel to userspace */
+ NFQNL_MSG_VERDICT, /* verdict from userspace to kernel */
+ NFQNL_MSG_CONFIG, /* connect to a particular queue */
+
+ NFQNL_MSG_MAX
+};
+
+struct nfqnl_msg_packet_hdr {
+ u_int32_t packet_id; /* unique ID of packet in queue */
+ u_int16_t hw_protocol; /* hw protocol (network order) */
+ u_int8_t hook; /* netfilter hook */
+} __attribute__ ((packed));
+
+struct nfqnl_msg_packet_hw {
+ u_int16_t hw_addrlen;
+ u_int16_t _pad;
+ u_int8_t hw_addr[8];
+} __attribute__ ((packed));
+
+struct nfqnl_msg_packet_timestamp {
+ u_int64_t sec;
+ u_int64_t usec;
+} __attribute__ ((packed));
+
+enum nfqnl_attr_type {
+ NFQA_UNSPEC,
+ NFQA_PACKET_HDR,
+ NFQA_VERDICT_HDR, /* nfqnl_msg_verdict_hrd */
+ NFQA_MARK, /* u_int32_t nfmark */
+ NFQA_TIMESTAMP, /* nfqnl_msg_packet_timestamp */
+ NFQA_IFINDEX_INDEV, /* u_int32_t ifindex */
+ NFQA_IFINDEX_OUTDEV, /* u_int32_t ifindex */
+ NFQA_HWADDR, /* nfqnl_msg_packet_hw */
+ NFQA_PAYLOAD, /* opaque data payload */
+
+ __NFQA_MAX
+};
+#define NFQA_MAX (__NFQA_MAX - 1)
+
+struct nfqnl_msg_verdict_hdr {
+ u_int32_t verdict;
+ u_int32_t id;
+} __attribute__ ((packed));
+
+
+enum nfqnl_msg_config_cmds {
+ NFQNL_CFG_CMD_NONE,
+ NFQNL_CFG_CMD_BIND,
+ NFQNL_CFG_CMD_UNBIND,
+ NFQNL_CFG_CMD_PF_BIND,
+ NFQNL_CFG_CMD_PF_UNBIND,
+};
+
+struct nfqnl_msg_config_cmd {
+ u_int8_t command; /* nfqnl_msg_config_cmds */
+ u_int8_t _pad;
+ u_int16_t pf; /* AF_xxx for PF_[UN]BIND */
+} __attribute__ ((packed));
+
+enum nfqnl_config_mode {
+ NFQNL_COPY_NONE,
+ NFQNL_COPY_META,
+ NFQNL_COPY_PACKET,
+};
+
+struct nfqnl_msg_config_params {
+ u_int32_t copy_range;
+ u_int8_t copy_mode; /* enum nfqnl_config_mode */
+} __attribute__ ((packed));
+
+
+enum nfqnl_attr_config {
+ NFQA_CFG_UNSPEC,
+ NFQA_CFG_CMD, /* nfqnl_msg_config_cmd */
+ NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */
+ __NFQA_CFG_MAX
+};
+
+#endif /* _NFNETLINK_QUEUE_H */
diff --git a/include/linux/netfilter_ipv4/ipt_NFQUEUE.h b/include/linux/netfilter_ipv4/ipt_NFQUEUE.h
new file mode 100644
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_NFQUEUE.h
@@ -0,0 +1,16 @@
+/* iptables module for using NFQUEUE mechanism
+ *
+ * (C) 2005 Harald Welte <laforge@netfilter.org>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+ *
+*/
+#ifndef _IPT_NFQ_TARGET_H
+#define _IPT_NFQ_TARGET_H
+
+/* target info */
+struct ipt_NFQ_info {
+ u_int16_t queuenum;
+};
+
+#endif /* _IPT_DSCP_TARGET_H */
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -110,11 +110,15 @@ config IP_NF_AMANDA
To compile it as a module, choose M here. If unsure, say Y.
config IP_NF_QUEUE
- tristate "Userspace queueing via NETLINK"
+ tristate "IP Userspace queueing via NETLINK (OBSOLETE)"
help
Netfilter has the ability to queue packets to user space: the
netlink device can be used to access them using this driver.
+ This option enables the old IPv4-only "ip_queue" implementation
+ which has been obsoleted by the new "nfnetlink_queue" code (see
+ CONFIG_NETFILTER_NETLINK_QUEUE).
+
To compile it as a module, choose M here. If unsure, say N.
config IP_NF_IPTABLES
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -91,3 +91,4 @@ obj-$(CONFIG_IP_NF_ARP_MANGLE) += arpt_m
obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o
obj-$(CONFIG_IP_NF_QUEUE) += ip_queue.o
+obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += ipt_NFQUEUE.o
diff --git a/net/ipv4/netfilter/ipt_NFQUEUE.c b/net/ipv4/netfilter/ipt_NFQUEUE.c
new file mode 100644
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_NFQUEUE.c
@@ -0,0 +1,70 @@
+/* iptables module for using new netfilter netlink queue
+ *
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_NFQUEUE.h>
+
+MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
+MODULE_DESCRIPTION("iptables NFQUEUE target");
+MODULE_LICENSE("GPL");
+
+static unsigned int
+target(struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ unsigned int hooknum,
+ const void *targinfo,
+ void *userinfo)
+{
+ const struct ipt_NFQ_info *tinfo = targinfo;
+
+ return NF_QUEUE_NR(tinfo->queuenum);
+}
+
+static int
+checkentry(const char *tablename,
+ const struct ipt_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ if (targinfosize != IPT_ALIGN(sizeof(struct ipt_NFQ_info))) {
+ printk(KERN_WARNING "NFQUEUE: targinfosize %u != %Zu\n",
+ targinfosize,
+ IPT_ALIGN(sizeof(struct ipt_NFQ_info)));
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct ipt_target ipt_NFQ_reg = {
+ .name = "NFQUEUE",
+ .target = target,
+ .checkentry = checkentry,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ipt_register_target(&ipt_NFQ_reg);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_target(&ipt_NFQ_reg);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -10,13 +10,16 @@ menu "IPv6: Netfilter Configuration (EXP
# dep_tristate ' FTP protocol support' CONFIG_IP6_NF_FTP $CONFIG_IP6_NF_CONNTRACK
#fi
config IP6_NF_QUEUE
- tristate "Userspace queueing via NETLINK"
+ tristate "IP6 Userspace queueing via NETLINK (OBSOLETE)"
---help---
This option adds a queue handler to the kernel for IPv6
- packets which lets us to receive the filtered packets
- with QUEUE target using libiptc as we can do with
- the IPv4 now.
+ packets which enables users to receive the filtered packets
+ with QUEUE target using libipq.
+
+ THis option enables the old IPv6-only "ip6_queue" implementation
+ which has been obsoleted by the new "nfnetlink_queue" code (see
+ CONFIG_NETFILTER_NETLINK_QUEUE).
(C) Fernando Anton 2001
IPv64 Project - Work based in IPv64 draft by Arturo Azcorra.
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile
--- a/net/ipv6/netfilter/Makefile
+++ b/net/ipv6/netfilter/Makefile
@@ -24,3 +24,4 @@ obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.
obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o
obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o
obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o
+obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += ip6t_NFQUEUE.o
diff --git a/net/ipv6/netfilter/ip6t_NFQUEUE.c b/net/ipv6/netfilter/ip6t_NFQUEUE.c
new file mode 100644
--- /dev/null
+++ b/net/ipv6/netfilter/ip6t_NFQUEUE.c
@@ -0,0 +1,70 @@
+/* ip6tables module for using new netfilter netlink queue
+ *
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv4/ipt_NFQUEUE.h>
+
+MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
+MODULE_DESCRIPTION("ip6tables NFQUEUE target");
+MODULE_LICENSE("GPL");
+
+static unsigned int
+target(struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ unsigned int hooknum,
+ const void *targinfo,
+ void *userinfo)
+{
+ const struct ipt_NFQ_info *tinfo = targinfo;
+
+ return NF_QUEUE_NR(tinfo->queuenum);
+}
+
+static int
+checkentry(const char *tablename,
+ const struct ip6t_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ if (targinfosize != IP6T_ALIGN(sizeof(struct ipt_NFQ_info))) {
+ printk(KERN_WARNING "NFQUEUE: targinfosize %u != %Zu\n",
+ targinfosize,
+ IP6T_ALIGN(sizeof(struct ipt_NFQ_info)));
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct ip6t_target ipt_NFQ_reg = {
+ .name = "NFQUEUE",
+ .target = target,
+ .checkentry = checkentry,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ip6t_register_target(&ipt_NFQ_reg);
+}
+
+static void __exit fini(void)
+{
+ ip6t_unregister_target(&ipt_NFQ_reg);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -3,3 +3,11 @@ config NETFILTER_NETLINK
help
If this option is enabled, the kernel will include support
for the new netfilter netlink interface.
+
+config NETFILTER_NETLINK_QUEUE
+ tristate "Netfilter NFQUEUE over NFNETLINK interface"
+ depends on NETFILTER_NETLINK
+ help
+ If this option isenabled, the kernel will include support
+ for queueing packets via NFNETLINK.
+
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o
+obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
new file mode 100644
--- /dev/null
+++ b/net/netfilter/nfnetlink_queue.c
@@ -0,0 +1,877 @@
+/*
+ * This is a module which is used for queueing packets and communicating with
+ * userspace via nfetlink.
+ *
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ *
+ * Based on the old ipv4-only ip_queue.c:
+ * (C) 2000-2002 James Morris <jmorris@intercode.com.au>
+ * (C) 2003-2005 Netfilter Core Team <coreteam@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <linux/list.h>
+#include <net/sock.h>
+
+#include <asm/atomic.h>
+
+#define NFQNL_QMAX_DEFAULT 1024
+
+#if 0
+#define QDEBUG(x, args ...) printk(KERN_DEBUG "%s(%d):%s(): " x, \
+ __FILE__, __LINE__, __FUNCTION__, \
+ ## args)
+#else
+#define QDEBUG(x, ...)
+#endif
+
+struct nfqnl_queue_entry {
+ struct list_head list;
+ struct nf_info *info;
+ struct sk_buff *skb;
+ unsigned int id;
+};
+
+struct nfqnl_instance {
+ struct hlist_node hlist; /* global list of queues */
+
+ int peer_pid;
+ unsigned int queue_maxlen;
+ unsigned int copy_range;
+ unsigned int queue_total;
+ unsigned int queue_dropped;
+ unsigned int queue_user_dropped;
+
+ atomic_t id_sequence; /* 'sequence' of pkt ids */
+
+ u_int16_t queue_num; /* number of this queue */
+ u_int8_t copy_mode;
+
+ spinlock_t lock;
+
+ struct list_head queue_list; /* packets in queue */
+};
+
+typedef int (*nfqnl_cmpfn)(struct nfqnl_queue_entry *, unsigned long);
+
+static DEFINE_RWLOCK(instances_lock);
+
+u_int64_t htonll(u_int64_t in)
+{
+ u_int64_t out;
+ int i;
+
+ for (i = 0; i < sizeof(u_int64_t); i++)
+ ((u_int8_t *)&out)[sizeof(u_int64_t)-1] = ((u_int8_t *)&in)[i];
+
+ return out;
+}
+
+#define INSTANCE_BUCKETS 16
+static struct hlist_head instance_table[INSTANCE_BUCKETS];
+
+static inline u_int8_t instance_hashfn(u_int16_t queue_num)
+{
+ return ((queue_num >> 8) | queue_num) % INSTANCE_BUCKETS;
+}
+
+static struct nfqnl_instance *
+__instance_lookup(u_int16_t queue_num)
+{
+ struct hlist_head *head;
+ struct hlist_node *pos;
+ struct nfqnl_instance *inst;
+
+ head = &instance_table[instance_hashfn(queue_num)];
+ hlist_for_each_entry(inst, pos, head, hlist) {
+ if (inst->queue_num == queue_num)
+ return inst;
+ }
+ return NULL;
+}
+
+static struct nfqnl_instance *
+instance_lookup(u_int16_t queue_num)
+{
+ struct nfqnl_instance *inst;
+
+ read_lock_bh(&instances_lock);
+ inst = __instance_lookup(queue_num);
+ read_unlock_bh(&instances_lock);
+
+ return inst;
+}
+
+static struct nfqnl_instance *
+instance_create(u_int16_t queue_num, int pid)
+{
+ struct nfqnl_instance *inst;
+
+ QDEBUG("entering for queue_num=%u, pid=%d\n", queue_num, pid);
+
+ write_lock_bh(&instances_lock);
+ if (__instance_lookup(queue_num)) {
+ inst = NULL;
+ QDEBUG("aborting, instance already exists\n");
+ goto out_unlock;
+ }
+
+ inst = kmalloc(sizeof(*inst), GFP_ATOMIC);
+ if (!inst)
+ goto out_unlock;
+
+ memset(inst, 0, sizeof(*inst));
+ inst->queue_num = queue_num;
+ inst->peer_pid = pid;
+ inst->queue_maxlen = NFQNL_QMAX_DEFAULT;
+ inst->copy_range = 0xfffff;
+ inst->copy_mode = NFQNL_COPY_NONE;
+ atomic_set(&inst->id_sequence, 0);
+ inst->lock = SPIN_LOCK_UNLOCKED;
+ INIT_LIST_HEAD(&inst->queue_list);
+
+ if (!try_module_get(THIS_MODULE))
+ goto out_free;
+
+ hlist_add_head(&inst->hlist,
+ &instance_table[instance_hashfn(queue_num)]);
+
+ write_unlock_bh(&instances_lock);
+
+ QDEBUG("successfully created new instance\n");
+
+ return inst;
+
+out_free:
+ kfree(inst);
+out_unlock:
+ write_unlock_bh(&instances_lock);
+ return NULL;
+}
+
+static void nfqnl_flush(struct nfqnl_instance *queue, int verdict);
+
+static void
+_instance_destroy2(struct nfqnl_instance *inst, int lock)
+{
+ /* first pull it out of the global list */
+ if (lock)
+ write_lock_bh(&instances_lock);
+
+ QDEBUG("removing instance %p (queuenum=%u) from hash\n",
+ inst, inst->queue_num);
+ hlist_del(&inst->hlist);
+
+ if (lock)
+ write_unlock_bh(&instances_lock);
+
+ /* then flush all pending skbs from the queue */
+ nfqnl_flush(inst, NF_DROP);
+
+ /* and finally free the data structure */
+ kfree(inst);
+
+ module_put(THIS_MODULE);
+}
+
+static inline void
+__instance_destroy(struct nfqnl_instance *inst)
+{
+ _instance_destroy2(inst, 0);
+}
+
+static inline void
+instance_destroy(struct nfqnl_instance *inst)
+{
+ _instance_destroy2(inst, 1);
+}
+
+
+
+static void
+issue_verdict(struct nfqnl_queue_entry *entry, int verdict)
+{
+ QDEBUG("entering for entry %p, verdict %u\n", entry, verdict);
+
+ /* TCP input path (and probably other bits) assume to be called
+ * from softirq context, not from syscall, like issue_verdict is
+ * called. TCP input path deadlocks with locks taken from timer
+ * softirq, e.g. We therefore emulate this by local_bh_disable() */
+
+ local_bh_disable();
+ nf_reinject(entry->skb, entry->info, verdict);
+ local_bh_enable();
+
+ kfree(entry);
+}
+
+static inline void
+__enqueue_entry(struct nfqnl_instance *queue,
+ struct nfqnl_queue_entry *entry)
+{
+ list_add(&entry->list, &queue->queue_list);
+ queue->queue_total++;
+}
+
+/*
+ * Find and return a queued entry matched by cmpfn, or return the last
+ * entry if cmpfn is NULL.
+ */
+static inline struct nfqnl_queue_entry *
+__find_entry(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn,
+ unsigned long data)
+{
+ struct list_head *p;
+
+ list_for_each_prev(p, &queue->queue_list) {
+ struct nfqnl_queue_entry *entry = (struct nfqnl_queue_entry *)p;
+
+ if (!cmpfn || cmpfn(entry, data))
+ return entry;
+ }
+ return NULL;
+}
+
+static inline void
+__dequeue_entry(struct nfqnl_instance *q, struct nfqnl_queue_entry *entry)
+{
+ list_del(&entry->list);
+ q->queue_total--;
+}
+
+static inline struct nfqnl_queue_entry *
+__find_dequeue_entry(struct nfqnl_instance *queue,
+ nfqnl_cmpfn cmpfn, unsigned long data)
+{
+ struct nfqnl_queue_entry *entry;
+
+ entry = __find_entry(queue, cmpfn, data);
+ if (entry == NULL)
+ return NULL;
+
+ __dequeue_entry(queue, entry);
+ return entry;
+}
+
+
+static inline void
+__nfqnl_flush(struct nfqnl_instance *queue, int verdict)
+{
+ struct nfqnl_queue_entry *entry;
+
+ while ((entry = __find_dequeue_entry(queue, NULL, 0)))
+ issue_verdict(entry, verdict);
+}
+
+static inline int
+__nfqnl_set_mode(struct nfqnl_instance *queue,
+ unsigned char mode, unsigned int range)
+{
+ int status = 0;
+
+ switch (mode) {
+ case NFQNL_COPY_NONE:
+ case NFQNL_COPY_META:
+ queue->copy_mode = mode;
+ queue->copy_range = 0;
+ break;
+
+ case NFQNL_COPY_PACKET:
+ queue->copy_mode = mode;
+ /* we're using struct nfattr which has 16bit nfa_len */
+ if (range > 0xffff)
+ queue->copy_range = 0xffff;
+ else
+ queue->copy_range = range;
+ break;
+
+ default:
+ status = -EINVAL;
+
+ }
+ return status;
+}
+
+static struct nfqnl_queue_entry *
+find_dequeue_entry(struct nfqnl_instance *queue,
+ nfqnl_cmpfn cmpfn, unsigned long data)
+{
+ struct nfqnl_queue_entry *entry;
+
+ spin_lock_bh(&queue->lock);
+ entry = __find_dequeue_entry(queue, cmpfn, data);
+ spin_unlock_bh(&queue->lock);
+
+ return entry;
+}
+
+static void
+nfqnl_flush(struct nfqnl_instance *queue, int verdict)
+{
+ spin_lock_bh(&queue->lock);
+ __nfqnl_flush(queue, verdict);
+ spin_unlock_bh(&queue->lock);
+}
+
+static struct sk_buff *
+nfqnl_build_packet_message(struct nfqnl_instance *queue,
+ struct nfqnl_queue_entry *entry, int *errp)
+{
+ unsigned char *old_tail;
+ size_t size;
+ size_t data_len = 0;
+ struct sk_buff *skb;
+ struct nfqnl_msg_packet_hdr pmsg;
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfmsg;
+ unsigned int tmp_uint;
+
+ QDEBUG("entered\n");
+
+ /* all macros expand to constant values at compile time */
+ size = NLMSG_SPACE(sizeof(struct nfqnl_msg_packet_hdr))
+ + NLMSG_SPACE(sizeof(u_int32_t)) /* ifindex */
+ + NLMSG_SPACE(sizeof(u_int32_t)) /* ifindex */
+ + NLMSG_SPACE(sizeof(u_int32_t)) /* mark */
+ + NLMSG_SPACE(sizeof(struct nfqnl_msg_packet_hw))
+ + NLMSG_SPACE(sizeof(struct nfqnl_msg_packet_timestamp));
+
+ spin_lock_bh(&queue->lock);
+
+ switch (queue->copy_mode) {
+ case NFQNL_COPY_META:
+ case NFQNL_COPY_NONE:
+ data_len = 0;
+ break;
+
+ case NFQNL_COPY_PACKET:
+ if (queue->copy_range == 0
+ || queue->copy_range > entry->skb->len)
+ data_len = entry->skb->len;
+ else
+ data_len = queue->copy_range;
+
+ size += NLMSG_SPACE(data_len);
+ break;
+
+ default:
+ *errp = -EINVAL;
+ spin_unlock_bh(&queue->lock);
+ return NULL;
+ }
+
+ spin_unlock_bh(&queue->lock);
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb)
+ goto nlmsg_failure;
+
+ old_tail= skb->tail;
+ nlh = NLMSG_PUT(skb, 0, 0,
+ NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET,
+ sizeof(struct nfgenmsg));
+ nfmsg = NLMSG_DATA(nlh);
+ nfmsg->nfgen_family = entry->info->pf;
+ nfmsg->version = NFNETLINK_V0;
+ nfmsg->res_id = htons(queue->queue_num);
+
+ pmsg.packet_id = htonl(entry->id);
+ pmsg.hw_protocol = htons(entry->skb->protocol);
+ pmsg.hook = entry->info->hook;
+
+ NFA_PUT(skb, NFQA_PACKET_HDR, sizeof(pmsg), &pmsg);
+
+ if (entry->info->indev) {
+ tmp_uint = htonl(entry->info->indev->ifindex);
+ NFA_PUT(skb, NFQA_IFINDEX_INDEV, sizeof(tmp_uint), &tmp_uint);
+ }
+
+ if (entry->info->outdev) {
+ tmp_uint = htonl(entry->info->outdev->ifindex);
+ NFA_PUT(skb, NFQA_IFINDEX_OUTDEV, sizeof(tmp_uint), &tmp_uint);
+ }
+
+ if (entry->skb->nfmark) {
+ tmp_uint = htonl(entry->skb->nfmark);
+ NFA_PUT(skb, NFQA_MARK, sizeof(u_int32_t), &tmp_uint);
+ }
+
+ if (entry->info->indev && entry->skb->dev
+ && entry->skb->dev->hard_header_parse) {
+ struct nfqnl_msg_packet_hw phw;
+
+ phw.hw_addrlen =
+ entry->skb->dev->hard_header_parse(entry->skb,
+ phw.hw_addr);
+ phw.hw_addrlen = htons(phw.hw_addrlen);
+ NFA_PUT(skb, NFQA_HWADDR, sizeof(phw), &phw);
+ }
+
+ if (entry->skb->stamp.tv_sec) {
+ struct nfqnl_msg_packet_timestamp ts;
+
+ ts.sec = htonll(entry->skb->stamp.tv_sec);
+ ts.usec = htonll(entry->skb->stamp.tv_usec);
+
+ NFA_PUT(skb, NFQA_TIMESTAMP, sizeof(ts), &ts);
+ }
+
+ if (data_len) {
+ struct nfattr *nfa;
+ int size = NFA_LENGTH(data_len);
+
+ if (skb_tailroom(skb) < (int)NFA_SPACE(data_len)) {
+ printk(KERN_WARNING "nf_queue: no tailroom!\n");
+ goto nlmsg_failure;
+ }
+
+ nfa = (struct nfattr *)skb_put(skb, NFA_ALIGN(size));
+ nfa->nfa_type = NFQA_PAYLOAD;
+ nfa->nfa_len = size;
+
+ if (skb_copy_bits(entry->skb, 0, NFA_DATA(nfa), data_len))
+ BUG();
+ }
+
+ nlh->nlmsg_len = skb->tail - old_tail;
+ return skb;
+
+nlmsg_failure:
+nfattr_failure:
+ if (skb)
+ kfree_skb(skb);
+ *errp = -EINVAL;
+ if (net_ratelimit())
+ printk(KERN_ERR "nf_queue: error creating packet message\n");
+ return NULL;
+}
+
+static int
+nfqnl_enqueue_packet(struct sk_buff *skb, struct nf_info *info,
+ unsigned int queuenum, void *data)
+{
+ int status = -EINVAL;
+ struct sk_buff *nskb;
+ struct nfqnl_instance *queue;
+ struct nfqnl_queue_entry *entry;
+
+ QDEBUG("entered\n");
+
+ queue = instance_lookup(queuenum);
+ if (!queue) {
+ QDEBUG("no queue instance matching\n");
+ return -EINVAL;
+ }
+
+ if (queue->copy_mode == NFQNL_COPY_NONE) {
+ QDEBUG("mode COPY_NONE, aborting\n");
+ return -EAGAIN;
+ }
+
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (entry == NULL) {
+ if (net_ratelimit())
+ printk(KERN_ERR
+ "nf_queue: OOM in nfqnl_enqueue_packet()\n");
+ return -ENOMEM;
+ }
+
+ entry->info = info;
+ entry->skb = skb;
+ entry->id = atomic_inc_return(&queue->id_sequence);
+
+ nskb = nfqnl_build_packet_message(queue, entry, &status);
+ if (nskb == NULL)
+ goto err_out_free;
+
+ spin_lock_bh(&queue->lock);
+
+ if (!queue->peer_pid)
+ goto err_out_free_nskb;
+
+ if (queue->queue_total >= queue->queue_maxlen) {
+ queue->queue_dropped++;
+ status = -ENOSPC;
+ if (net_ratelimit())
+ printk(KERN_WARNING "ip_queue: full at %d entries, "
+ "dropping packets(s). Dropped: %d\n",
+ queue->queue_total, queue->queue_dropped);
+ goto err_out_free_nskb;
+ }
+
+ /* nfnetlink_unicast will either free the nskb or add it to a socket */
+ status = nfnetlink_unicast(nskb, queue->peer_pid, MSG_DONTWAIT);
+ if (status < 0) {
+ queue->queue_user_dropped++;
+ goto err_out_unlock;
+ }
+
+ __enqueue_entry(queue, entry);
+
+ spin_unlock_bh(&queue->lock);
+ return status;
+
+err_out_free_nskb:
+ kfree_skb(nskb);
+
+err_out_unlock:
+ spin_unlock_bh(&queue->lock);
+
+err_out_free:
+ kfree(entry);
+ return status;
+}
+
+static int
+nfqnl_mangle(void *data, int data_len, struct nfqnl_queue_entry *e)
+{
+ int diff;
+
+ diff = data_len - e->skb->len;
+ if (diff < 0)
+ skb_trim(e->skb, data_len);
+ else if (diff > 0) {
+ if (data_len > 0xFFFF)
+ return -EINVAL;
+ if (diff > skb_tailroom(e->skb)) {
+ struct sk_buff *newskb;
+
+ newskb = skb_copy_expand(e->skb,
+ skb_headroom(e->skb),
+ diff,
+ GFP_ATOMIC);
+ if (newskb == NULL) {
+ printk(KERN_WARNING "ip_queue: OOM "
+ "in mangle, dropping packet\n");
+ return -ENOMEM;
+ }
+ if (e->skb->sk)
+ skb_set_owner_w(newskb, e->skb->sk);
+ kfree_skb(e->skb);
+ e->skb = newskb;
+ }
+ skb_put(e->skb, diff);
+ }
+ if (!skb_make_writable(&e->skb, data_len))
+ return -ENOMEM;
+ memcpy(e->skb->data, data, data_len);
+
+ return 0;
+}
+
+static inline int
+id_cmp(struct nfqnl_queue_entry *e, unsigned long id)
+{
+ return (id == e->id);
+}
+
+static int
+nfqnl_set_mode(struct nfqnl_instance *queue,
+ unsigned char mode, unsigned int range)
+{
+ int status;
+
+ spin_lock_bh(&queue->lock);
+ status = __nfqnl_set_mode(queue, mode, range);
+ spin_unlock_bh(&queue->lock);
+
+ return status;
+}
+
+static int
+dev_cmp(struct nfqnl_queue_entry *entry, unsigned long ifindex)
+{
+ if (entry->info->indev)
+ if (entry->info->indev->ifindex == ifindex)
+ return 1;
+
+ if (entry->info->outdev)
+ if (entry->info->outdev->ifindex == ifindex)
+ return 1;
+
+ return 0;
+}
+
+/* drop all packets with either indev or outdev == ifindex from all queue
+ * instances */
+static void
+nfqnl_dev_drop(int ifindex)
+{
+ int i;
+
+ QDEBUG("entering for ifindex %u\n", ifindex);
+
+ /* this only looks like we have to hold the readlock for a way too long
+ * time, issue_verdict(), nf_reinject(), ... - but we always only
+ * issue NF_DROP, which is processed directly in nf_reinject() */
+ read_lock_bh(&instances_lock);
+
+ for (i = 0; i < INSTANCE_BUCKETS; i++) {
+ struct hlist_node *tmp;
+ struct nfqnl_instance *inst;
+ struct hlist_head *head = &instance_table[i];
+
+ hlist_for_each_entry(inst, tmp, head, hlist) {
+ struct nfqnl_queue_entry *entry;
+ while ((entry = find_dequeue_entry(inst, dev_cmp,
+ ifindex)) != NULL)
+ issue_verdict(entry, NF_DROP);
+ }
+ }
+
+ read_unlock_bh(&instances_lock);
+}
+
+#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0)
+
+static int
+nfqnl_rcv_dev_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = ptr;
+
+ /* Drop any packets associated with the downed device */
+ if (event == NETDEV_DOWN)
+ nfqnl_dev_drop(dev->ifindex);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block nfqnl_dev_notifier = {
+ .notifier_call = nfqnl_rcv_dev_event,
+};
+
+static int
+nfqnl_rcv_nl_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct netlink_notify *n = ptr;
+
+ if (event == NETLINK_URELEASE &&
+ n->protocol == NETLINK_NETFILTER && n->pid) {
+ int i;
+
+ /* destroy all instances for this pid */
+ write_lock_bh(&instances_lock);
+ for (i = 0; i < INSTANCE_BUCKETS; i++) {
+ struct hlist_node *tmp, *t2;
+ struct nfqnl_instance *inst;
+ struct hlist_head *head = &instance_table[i];
+
+ hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) {
+ if (n->pid == inst->peer_pid)
+ __instance_destroy(inst);
+ }
+ }
+ write_unlock_bh(&instances_lock);
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block nfqnl_rtnl_notifier = {
+ .notifier_call = nfqnl_rcv_nl_event,
+};
+
+static int
+nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
+ struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp)
+{
+ struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
+ u_int16_t queue_num = ntohs(nfmsg->res_id);
+
+ struct nfqnl_msg_verdict_hdr *vhdr;
+ struct nfqnl_instance *queue;
+ unsigned int verdict;
+ struct nfqnl_queue_entry *entry;
+
+ queue = instance_lookup(queue_num);
+ if (!queue)
+ return -ENODEV;
+
+ if (queue->peer_pid != NETLINK_CB(skb).pid)
+ return -EPERM;
+
+ if (!nfqa[NFQA_VERDICT_HDR-1])
+ return -EINVAL;
+
+ vhdr = NFA_DATA(nfqa[NFQA_VERDICT_HDR-1]);
+ verdict = ntohl(vhdr->verdict);
+
+ if ((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT)
+ return -EINVAL;
+
+ entry = find_dequeue_entry(queue, id_cmp, ntohl(vhdr->id));
+ if (entry == NULL)
+ return -ENOENT;
+
+ if (nfqa[NFQA_PAYLOAD-1]) {
+ if (nfqnl_mangle(NFA_DATA(nfqa[NFQA_PAYLOAD-1]),
+ NFA_PAYLOAD(nfqa[NFQA_PAYLOAD-1]), entry) < 0)
+ verdict = NF_DROP;
+ }
+
+ if (nfqa[NFQA_MARK-1])
+ skb->nfmark = ntohl(*(u_int32_t *)NFA_DATA(nfqa[NFQA_MARK-1]));
+
+ issue_verdict(entry, verdict);
+ return 0;
+}
+
+static int
+nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb,
+ struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp)
+{
+ return -ENOTSUPP;
+}
+
+static int
+nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
+ struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp)
+{
+ struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
+ u_int16_t queue_num = ntohs(nfmsg->res_id);
+ struct nfqnl_instance *queue;
+
+ QDEBUG("entering for msg %u\n", NFNL_MSG_TYPE(nlh->nlmsg_type));
+
+ queue = instance_lookup(queue_num);
+ if (nfqa[NFQA_CFG_CMD-1]) {
+ struct nfqnl_msg_config_cmd *cmd;
+ cmd = NFA_DATA(nfqa[NFQA_CFG_CMD-1]);
+ QDEBUG("found CFG_CMD\n");
+
+ switch (cmd->command) {
+ case NFQNL_CFG_CMD_BIND:
+ if (queue)
+ return -EBUSY;
+
+ queue = instance_create(queue_num, NETLINK_CB(skb).pid);
+ if (!queue)
+ return -EINVAL;
+ break;
+ case NFQNL_CFG_CMD_UNBIND:
+ if (!queue)
+ return -ENODEV;
+
+ if (queue->peer_pid != NETLINK_CB(skb).pid)
+ return -EPERM;
+
+ instance_destroy(queue);
+ break;
+ case NFQNL_CFG_CMD_PF_BIND:
+ QDEBUG("registering queue handler for pf=%u\n",
+ ntohs(cmd->pf));
+ return nf_register_queue_handler(ntohs(cmd->pf),
+ nfqnl_enqueue_packet,
+ NULL);
+
+ break;
+ case NFQNL_CFG_CMD_PF_UNBIND:
+ QDEBUG("unregistering queue handler for pf=%u\n",
+ ntohs(cmd->pf));
+ /* This is a bug and a feature. We can unregister
+ * other handlers(!) */
+ return nf_unregister_queue_handler(ntohs(cmd->pf));
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ if (!queue) {
+ QDEBUG("no config command, and no instance ENOENT\n");
+ return -ENOENT;
+ }
+
+ if (queue->peer_pid != NETLINK_CB(skb).pid) {
+ QDEBUG("no config command, and wrong pid\n");
+ return -EPERM;
+ }
+ }
+
+ if (nfqa[NFQA_CFG_PARAMS-1]) {
+ struct nfqnl_msg_config_params *params;
+ params = NFA_DATA(nfqa[NFQA_CFG_PARAMS-1]);
+
+ nfqnl_set_mode(queue, params->copy_mode,
+ ntohl(params->copy_range));
+ }
+
+ return 0;
+}
+
+static struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = {
+ [NFQNL_MSG_PACKET] = { .call = nfqnl_recv_unsupp,
+ .cap_required = CAP_NET_ADMIN },
+ [NFQNL_MSG_VERDICT] = { .call = nfqnl_recv_verdict,
+ .cap_required = CAP_NET_ADMIN },
+ [NFQNL_MSG_CONFIG] = { .call = nfqnl_recv_config,
+ .cap_required = CAP_NET_ADMIN },
+};
+
+static struct nfnetlink_subsystem nfqnl_subsys = {
+ .name = "nf_queue",
+ .subsys_id = NFNL_SUBSYS_QUEUE,
+ .cb_count = NFQNL_MSG_MAX,
+ .attr_count = NFQA_MAX,
+ .cb = nfqnl_cb,
+};
+
+static int
+init_or_cleanup(int init)
+{
+ int status = -ENOMEM;
+
+ if (!init)
+ goto cleanup;
+
+ netlink_register_notifier(&nfqnl_rtnl_notifier);
+ status = nfnetlink_subsys_register(&nfqnl_subsys);
+ if (status < 0) {
+ printk(KERN_ERR "nf_queue: failed to create netlink socket\n");
+ goto cleanup_netlink_notifier;
+ }
+
+ register_netdevice_notifier(&nfqnl_dev_notifier);
+ return status;
+
+cleanup:
+ nf_unregister_queue_handlers(nfqnl_enqueue_packet);
+ unregister_netdevice_notifier(&nfqnl_dev_notifier);
+ nfnetlink_subsys_unregister(&nfqnl_subsys);
+
+cleanup_netlink_notifier:
+ netlink_unregister_notifier(&nfqnl_rtnl_notifier);
+ return status;
+}
+
+static int __init init(void)
+{
+
+ return init_or_cleanup(1);
+}
+
+static void __exit fini(void)
+{
+ init_or_cleanup(0);
+}
+
+MODULE_DESCRIPTION("netfilter packet queue handler");
+MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_QUEUE);
+
+module_init(init);
+module_exit(fini);
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: [2.6 patch] NETCONSOLE must depend on INET
From: David S. Miller @ 2005-07-26 23:32 UTC (permalink / raw)
To: mpm; +Cc: bunk, jgarzik, netdev, linux-kernel
In-Reply-To: <20050726232043.GB7425@waste.org>
From: Matt Mackall <mpm@selenic.com>
Date: Tue, 26 Jul 2005 16:20:43 -0700
> This problem also exists in PKTGEN. And this fix is incorrect as
> neither is dependent on the IP part of the networking stack in any
> substantive way. The right fix is to make inet_aton available outside
> of CONFIG_INET (preferred) or making private copies of inet_aton.
Adrian posted a fix, you whine, that's why Adrian's fix
went in :-)
More seriously, please submit a version of whatever you
believe to be the more correct fix so it can be reviewed
and integrated.
^ permalink raw reply
* Re: [2.6 patch] NETCONSOLE must depend on INET
From: Matt Mackall @ 2005-07-26 23:20 UTC (permalink / raw)
To: David S. Miller; +Cc: bunk, jgarzik, netdev, linux-kernel
In-Reply-To: <20050719.140104.68160812.davem@davemloft.net>
On Tue, Jul 19, 2005 at 02:01:04PM -0700, David S. Miller wrote:
> From: Adrian Bunk <bunk@stusta.de>
> Date: Tue, 19 Jul 2005 20:29:19 +0200
>
> > NETCONSOLE=y and INET=n results in the following compile error:
>
> Also applied, thanks Adrian.
I should have been cc:ed on this.
This problem also exists in PKTGEN. And this fix is incorrect as
neither is dependent on the IP part of the networking stack in any
substantive way. The right fix is to make inet_aton available outside
of CONFIG_INET (preferred) or making private copies of inet_aton.
--
Mathematics is the supreme nostalgia of our time.
^ permalink raw reply
* Re: [PATCH] move ip[6]_queue rerouting up to nf_queue
From: David S. Miller @ 2005-07-26 23:13 UTC (permalink / raw)
To: laforge; +Cc: netdev, netfilter-devel
In-Reply-To: <20050726164505.GD4279@rama.de.gnumonks.org>
From: Harald Welte <laforge@netfilter.org>
Date: Tue, 26 Jul 2005 18:45:05 +0200
> please apply. If you dislike the yet-another-NPROTO nature of it, I'll
> prepare one with kmalloc-at-startup.
Please do, thanks.
^ permalink raw reply
* Re: [2.6 patch] fix ip_conntrack_put prototype
From: David S. Miller @ 2005-07-26 22:39 UTC (permalink / raw)
To: bunk; +Cc: netfilter-devel, netdev, linux-kernel
In-Reply-To: <20050726145659.GR3160@stusta.de>
From: Adrian Bunk <bunk@stusta.de>
Date: Tue, 26 Jul 2005 16:56:59 +0200
> The function is not inline.
>
> Signed-off-by: Adrian Bunk <bunk@stusta.de>
Applied, thanks Adrian.
^ permalink raw reply
* Re: [PATCH] rename skb_ip_make_writable() to skb_make_writable()
From: David S. Miller @ 2005-07-26 22:35 UTC (permalink / raw)
To: laforge; +Cc: netdev, netfilter-devel
In-Reply-To: <20050726152053.GA4279@rama.de.gnumonks.org>
From: Harald Welte <laforge@netfilter.org>
Date: Tue, 26 Jul 2005 17:20:53 +0200
> Rename skb_ip_make_writable() to skb_make_writable() since there is
> nothing IPv4-specific in it. In fact, it was already used by IPv6, too...
> Upcoming nfnetlink_queue code will use it for any kind of packet.
>
> Signed-off-by: Harald Welte <laforge@netfilter.org>
Applied.
^ permalink raw reply
* [PATCH] fix nf_info_reroute() macro
From: Harald Welte @ 2005-07-26 22:01 UTC (permalink / raw)
To: David Miller; +Cc: Linux Netdev List, Netfilter Development Mailinglist
[-- Attachment #1.1: Type: text/plain, Size: 471 bytes --]
Hi Dave!
This patch fixes the pointer calculation made by the nf_info_reroute() macro.
Please apply on top of my other patches for net-2.6.14, thanks.
--
- Harald Welte <laforge@gnumonks.org> http://gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
(ETSI EN 300 175-7 Ch. A6)
[-- Attachment #1.2: 15-nf_info_reroute-fix.patch --]
[-- Type: text/plain, Size: 968 bytes --]
Fix calculation of structure end by nf_info_reroute()
Signed-off-by: Harald Welte <laforge@netfilter.org>
---
commit 08fcbede51cf315a0e7aa9b2d6bbdd0aad05cd54
tree 3c7ea5a64f856f2acff65a0b3bd2c40dfac5cf77
parent 7237f235b6bd25773b566d88ec846245c852b1ee
author laforge <laforge@netfilter.org> Di, 26 Jul 2005 23:53:58 +0200
committer laforge <laforge@netfilter.org> Di, 26 Jul 2005 23:53:58 +0200
include/linux/netfilter.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -216,7 +216,7 @@ struct nf_queue_rerouter {
int rer_size;
};
-#define nf_info_reroute(x) ((void *)x + sizeof(typeof(x)))
+#define nf_info_reroute(x) ((void *)x + sizeof(struct nf_info))
extern int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer);
extern int nf_unregister_queue_rerouter(int pf);
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* [PATCH] add new nfnetlink_queue subsystem
From: Harald Welte @ 2005-07-26 21:58 UTC (permalink / raw)
To: David Miller; +Cc: Linux Netdev List, Netfilter Development Mailinglist
[-- Attachment #1.1: Type: text/plain, Size: 572 bytes --]
Hi Dave!
This is the layer3 independent rewrite of ip[6]_queue on top of
nfnetlink. The patch is incremental to my latest 'move queue rerouting
to nf_queue level' and 'multiqueue' patches I sent earlier today.
Please apply to net-2.6.14,
Thanks.
--
- Harald Welte <laforge@gnumonks.org> http://gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
(ETSI EN 300 175-7 Ch. A6)
[-- Attachment #1.2: 13-nfnetlink_queue.patch --]
[-- Type: text/plain, Size: 34813 bytes --]
Add "nfnetlink_queue" netfilter queue handler over nfnetlink:
- Add new nfnetlink_queue module
- Add new ipt_NFQUEUE and ip6t_NFQUEUE modules to access queue numbers 1-65535
- Mark ip_queue and ip6_queue Kconfig options as OBSOLETE
- Update feature-removal-schedule to remove ip[6]_queue in December
Signed-off-by: Harald Welte <laforge@netfilter.org>
---
commit 7237f235b6bd25773b566d88ec846245c852b1ee
tree d8bf4d0e82e9f0bdd570d1678f162f6c59d2d361
parent 4557bbef246922e030176404bcf5aa05f9a81d21
author laforge <laforge@netfilter.org> Di, 26 Jul 2005 23:52:26 +0200
committer laforge <laforge@netfilter.org> Di, 26 Jul 2005 23:52:26 +0200
Documentation/feature-removal-schedule.txt | 12
include/linux/netfilter/nfnetlink_queue.h | 85 +++
include/linux/netfilter_ipv4/ipt_NFQUEUE.h | 16 +
net/ipv4/netfilter/Kconfig | 6
net/ipv4/netfilter/Makefile | 1
net/ipv4/netfilter/ipt_NFQUEUE.c | 70 ++
net/ipv6/netfilter/Kconfig | 11
net/ipv6/netfilter/Makefile | 1
net/ipv6/netfilter/ip6t_NFQUEUE.c | 70 ++
net/netfilter/Kconfig | 8
net/netfilter/Makefile | 1
net/netfilter/nfnetlink_queue.c | 877 ++++++++++++++++++++++++++++
12 files changed, 1153 insertions(+), 5 deletions(-)
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -135,3 +135,15 @@ Why: With the 16-bit PCMCIA subsystem no
pcmciautils package available at
http://kernel.org/pub/linux/utils/kernel/pcmcia/
Who: Dominik Brodowski <linux@brodo.de>
+
+---------------------------
+
+What: ip_queue and ip6_queue (old ipv4-only and ipv6-only netfilter queue)
+When: December 2005
+Why: This interface has been obsoleted by the new layer3-independent
+ "nfnetlink_queue". The Kernel interface is compatible, so the old
+ ip[6]tables "QUEUE" targets still work and will transparently handle
+ all packets into nfnetlink queue number 0. Userspace users will have
+ to link against API-compatible library on top of libnfnetlink_queue
+ instead of the current 'libipq'.
+Who: Harald Welte <laforge@netfilter.org>
diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h
new file mode 100644
--- /dev/null
+++ b/include/linux/netfilter/nfnetlink_queue.h
@@ -0,0 +1,85 @@
+#ifndef _NFNETLINK_QUEUE_H
+#define _NFNETLINK_QUEUE_H
+
+#include <linux/netfilter/nfnetlink.h>
+
+enum nfqnl_msg_types {
+ NFQNL_MSG_PACKET, /* packet from kernel to userspace */
+ NFQNL_MSG_VERDICT, /* verdict from userspace to kernel */
+ NFQNL_MSG_CONFIG, /* connect to a particular queue */
+
+ NFQNL_MSG_MAX
+};
+
+struct nfqnl_msg_packet_hdr {
+ u_int32_t packet_id; /* unique ID of packet in queue */
+ u_int16_t hw_protocol; /* hw protocol (network order) */
+ u_int8_t hook; /* netfilter hook */
+} __attribute__ ((packed));
+
+struct nfqnl_msg_packet_hw {
+ u_int16_t hw_addrlen;
+ u_int16_t _pad;
+ u_int8_t hw_addr[8];
+} __attribute__ ((packed));
+
+struct nfqnl_msg_packet_timestamp {
+ u_int64_t sec;
+ u_int64_t usec;
+} __attribute__ ((packed));
+
+enum nfqnl_attr_type {
+ NFQA_UNSPEC,
+ NFQA_PACKET_HDR,
+ NFQA_VERDICT_HDR, /* nfqnl_msg_verdict_hrd */
+ NFQA_MARK, /* u_int32_t nfmark */
+ NFQA_TIMESTAMP, /* nfqnl_msg_packet_timestamp */
+ NFQA_IFINDEX_INDEV, /* u_int32_t ifindex */
+ NFQA_IFINDEX_OUTDEV, /* u_int32_t ifindex */
+ NFQA_HWADDR, /* nfqnl_msg_packet_hw */
+ NFQA_PAYLOAD, /* opaque data payload */
+
+ __NFQA_MAX
+};
+#define NFQA_MAX (__NFQA_MAX - 1)
+
+struct nfqnl_msg_verdict_hdr {
+ u_int32_t verdict;
+ u_int32_t id;
+} __attribute__ ((packed));
+
+
+enum nfqnl_msg_config_cmds {
+ NFQNL_CFG_CMD_NONE,
+ NFQNL_CFG_CMD_BIND,
+ NFQNL_CFG_CMD_UNBIND,
+ NFQNL_CFG_CMD_PF_BIND,
+ NFQNL_CFG_CMD_PF_UNBIND,
+};
+
+struct nfqnl_msg_config_cmd {
+ u_int8_t command; /* nfqnl_msg_config_cmds */
+ u_int8_t _pad;
+ u_int16_t pf; /* AF_xxx for PF_[UN]BIND */
+} __attribute__ ((packed));
+
+enum nfqnl_config_mode {
+ NFQNL_COPY_NONE,
+ NFQNL_COPY_META,
+ NFQNL_COPY_PACKET,
+};
+
+struct nfqnl_msg_config_params {
+ u_int32_t copy_range;
+ u_int8_t copy_mode; /* enum nfqnl_config_mode */
+} __attribute__ ((packed));
+
+
+enum nfqnl_attr_config {
+ NFQA_CFG_UNSPEC,
+ NFQA_CFG_CMD, /* nfqnl_msg_config_cmd */
+ NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */
+ __NFQA_CFG_MAX
+};
+
+#endif /* _NFNETLINK_QUEUE_H */
diff --git a/include/linux/netfilter_ipv4/ipt_NFQUEUE.h b/include/linux/netfilter_ipv4/ipt_NFQUEUE.h
new file mode 100644
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_NFQUEUE.h
@@ -0,0 +1,16 @@
+/* iptables module for using NFQUEUE mechanism
+ *
+ * (C) 2005 Harald Welte <laforge@netfilter.org>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+ *
+*/
+#ifndef _IPT_NFQ_TARGET_H
+#define _IPT_NFQ_TARGET_H
+
+/* target info */
+struct ipt_NFQ_info {
+ u_int16_t queuenum;
+};
+
+#endif /* _IPT_DSCP_TARGET_H */
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -110,11 +110,15 @@ config IP_NF_AMANDA
To compile it as a module, choose M here. If unsure, say Y.
config IP_NF_QUEUE
- tristate "Userspace queueing via NETLINK"
+ tristate "IP Userspace queueing via NETLINK (OBSOLETE)"
help
Netfilter has the ability to queue packets to user space: the
netlink device can be used to access them using this driver.
+ This option enables the old IPv4-only "ip_queue" implementation
+ which has been obsoleted by the new "nfnetlink_queue" code (see
+ CONFIG_NETFILTER_NETLINK_QUEUE).
+
To compile it as a module, choose M here. If unsure, say N.
config IP_NF_IPTABLES
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -91,3 +91,4 @@ obj-$(CONFIG_IP_NF_ARP_MANGLE) += arpt_m
obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o
obj-$(CONFIG_IP_NF_QUEUE) += ip_queue.o
+obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += ipt_NFQUEUE.o
diff --git a/net/ipv4/netfilter/ipt_NFQUEUE.c b/net/ipv4/netfilter/ipt_NFQUEUE.c
new file mode 100644
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_NFQUEUE.c
@@ -0,0 +1,70 @@
+/* iptables module for using new netfilter netlink queue
+ *
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_NFQUEUE.h>
+
+MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
+MODULE_DESCRIPTION("iptables NFQUEUE target");
+MODULE_LICENSE("GPL");
+
+static unsigned int
+target(struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ unsigned int hooknum,
+ const void *targinfo,
+ void *userinfo)
+{
+ const struct ipt_NFQ_info *tinfo = targinfo;
+
+ return NF_QUEUE_NR(tinfo->queuenum);
+}
+
+static int
+checkentry(const char *tablename,
+ const struct ipt_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ if (targinfosize != IPT_ALIGN(sizeof(struct ipt_NFQ_info))) {
+ printk(KERN_WARNING "NFQUEUE: targinfosize %u != %Zu\n",
+ targinfosize,
+ IPT_ALIGN(sizeof(struct ipt_NFQ_info)));
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct ipt_target ipt_NFQ_reg = {
+ .name = "NFQUEUE",
+ .target = target,
+ .checkentry = checkentry,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ipt_register_target(&ipt_NFQ_reg);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_target(&ipt_NFQ_reg);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -10,13 +10,16 @@ menu "IPv6: Netfilter Configuration (EXP
# dep_tristate ' FTP protocol support' CONFIG_IP6_NF_FTP $CONFIG_IP6_NF_CONNTRACK
#fi
config IP6_NF_QUEUE
- tristate "Userspace queueing via NETLINK"
+ tristate "IP6 Userspace queueing via NETLINK (OBSOLETE)"
---help---
This option adds a queue handler to the kernel for IPv6
- packets which lets us to receive the filtered packets
- with QUEUE target using libiptc as we can do with
- the IPv4 now.
+ packets which enables users to receive the filtered packets
+ with QUEUE target using libipq.
+
+ THis option enables the old IPv6-only "ip6_queue" implementation
+ which has been obsoleted by the new "nfnetlink_queue" code (see
+ CONFIG_NETFILTER_NETLINK_QUEUE).
(C) Fernando Anton 2001
IPv64 Project - Work based in IPv64 draft by Arturo Azcorra.
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile
--- a/net/ipv6/netfilter/Makefile
+++ b/net/ipv6/netfilter/Makefile
@@ -24,3 +24,4 @@ obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.
obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o
obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o
obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o
+obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += ip6t_NFQUEUE.o
diff --git a/net/ipv6/netfilter/ip6t_NFQUEUE.c b/net/ipv6/netfilter/ip6t_NFQUEUE.c
new file mode 100644
--- /dev/null
+++ b/net/ipv6/netfilter/ip6t_NFQUEUE.c
@@ -0,0 +1,70 @@
+/* ip6tables module for using new netfilter netlink queue
+ *
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv4/ipt_NFQUEUE.h>
+
+MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
+MODULE_DESCRIPTION("ip6tables NFQUEUE target");
+MODULE_LICENSE("GPL");
+
+static unsigned int
+target(struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ unsigned int hooknum,
+ const void *targinfo,
+ void *userinfo)
+{
+ const struct ipt_NFQ_info *tinfo = targinfo;
+
+ return NF_QUEUE_NR(tinfo->queuenum);
+}
+
+static int
+checkentry(const char *tablename,
+ const struct ip6t_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ if (targinfosize != IP6T_ALIGN(sizeof(struct ipt_NFQ_info))) {
+ printk(KERN_WARNING "NFQUEUE: targinfosize %u != %Zu\n",
+ targinfosize,
+ IP6T_ALIGN(sizeof(struct ipt_NFQ_info)));
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct ip6t_target ipt_NFQ_reg = {
+ .name = "NFQUEUE",
+ .target = target,
+ .checkentry = checkentry,
+ .me = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+ return ip6t_register_target(&ipt_NFQ_reg);
+}
+
+static void __exit fini(void)
+{
+ ip6t_unregister_target(&ipt_NFQ_reg);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -3,3 +3,11 @@ config NETFILTER_NETLINK
help
If this option is enabled, the kernel will include support
for the new netfilter netlink interface.
+
+config NETFILTER_NETLINK_QUEUE
+ tristate "Netfilter NFQUEUE over NFNETLINK interface"
+ depends on NETFILTER_NETLINK
+ help
+ If this option isenabled, the kernel will include support
+ for queueing packets via NFNETLINK.
+
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o
+obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
new file mode 100644
--- /dev/null
+++ b/net/netfilter/nfnetlink_queue.c
@@ -0,0 +1,877 @@
+/*
+ * This is a module which is used for queueing packets and communicating with
+ * userspace via nfetlink.
+ *
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ *
+ * Based on the old ipv4-only ip_queue.c:
+ * (C) 2000-2002 James Morris <jmorris@intercode.com.au>
+ * (C) 2003-2005 Netfilter Core Team <coreteam@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <linux/list.h>
+#include <net/sock.h>
+
+#include <asm/atomic.h>
+
+#define NFQNL_QMAX_DEFAULT 1024
+
+#if 0
+#define QDEBUG(x, args ...) printk(KERN_DEBUG "%s(%d):%s(): " x, \
+ __FILE__, __LINE__, __FUNCTION__, \
+ ## args)
+#else
+#define QDEBUG(x, ...)
+#endif
+
+struct nfqnl_queue_entry {
+ struct list_head list;
+ struct nf_info *info;
+ struct sk_buff *skb;
+ unsigned int id;
+};
+
+struct nfqnl_instance {
+ struct hlist_node hlist; /* global list of queues */
+
+ int peer_pid;
+ unsigned int queue_maxlen;
+ unsigned int copy_range;
+ unsigned int queue_total;
+ unsigned int queue_dropped;
+ unsigned int queue_user_dropped;
+
+ atomic_t id_sequence; /* 'sequence' of pkt ids */
+
+ u_int16_t queue_num; /* number of this queue */
+ u_int8_t copy_mode;
+
+ spinlock_t lock;
+
+ struct list_head queue_list; /* packets in queue */
+};
+
+typedef int (*nfqnl_cmpfn)(struct nfqnl_queue_entry *, unsigned long);
+
+static DEFINE_RWLOCK(instances_lock);
+
+u_int64_t htonll(u_int64_t in)
+{
+ u_int64_t out;
+ int i;
+
+ for (i = 0; i < sizeof(u_int64_t); i++)
+ ((u_int8_t *)&out)[sizeof(u_int64_t)-1] = ((u_int8_t *)&in)[i];
+
+ return out;
+}
+
+#define INSTANCE_BUCKETS 16
+static struct hlist_head instance_table[INSTANCE_BUCKETS];
+
+static inline u_int8_t instance_hashfn(u_int16_t queue_num)
+{
+ return ((queue_num >> 8) | queue_num) % INSTANCE_BUCKETS;
+}
+
+static struct nfqnl_instance *
+__instance_lookup(u_int16_t queue_num)
+{
+ struct hlist_head *head;
+ struct hlist_node *pos;
+ struct nfqnl_instance *inst;
+
+ head = &instance_table[instance_hashfn(queue_num)];
+ hlist_for_each_entry(inst, pos, head, hlist) {
+ if (inst->queue_num == queue_num)
+ return inst;
+ }
+ return NULL;
+}
+
+static struct nfqnl_instance *
+instance_lookup(u_int16_t queue_num)
+{
+ struct nfqnl_instance *inst;
+
+ read_lock_bh(&instances_lock);
+ inst = __instance_lookup(queue_num);
+ read_unlock_bh(&instances_lock);
+
+ return inst;
+}
+
+static struct nfqnl_instance *
+instance_create(u_int16_t queue_num, int pid)
+{
+ struct nfqnl_instance *inst;
+
+ QDEBUG("entering for queue_num=%u, pid=%d\n", queue_num, pid);
+
+ write_lock_bh(&instances_lock);
+ if (__instance_lookup(queue_num)) {
+ inst = NULL;
+ QDEBUG("aborting, instance already exists\n");
+ goto out_unlock;
+ }
+
+ inst = kmalloc(sizeof(*inst), GFP_ATOMIC);
+ if (!inst)
+ goto out_unlock;
+
+ memset(inst, 0, sizeof(*inst));
+ inst->queue_num = queue_num;
+ inst->peer_pid = pid;
+ inst->queue_maxlen = NFQNL_QMAX_DEFAULT;
+ inst->copy_range = 0xfffff;
+ inst->copy_mode = NFQNL_COPY_NONE;
+ atomic_set(&inst->id_sequence, 0);
+ inst->lock = SPIN_LOCK_UNLOCKED;
+ INIT_LIST_HEAD(&inst->queue_list);
+
+ if (!try_module_get(THIS_MODULE))
+ goto out_free;
+
+ hlist_add_head(&inst->hlist,
+ &instance_table[instance_hashfn(queue_num)]);
+
+ write_unlock_bh(&instances_lock);
+
+ QDEBUG("successfully created new instance\n");
+
+ return inst;
+
+out_free:
+ kfree(inst);
+out_unlock:
+ write_unlock_bh(&instances_lock);
+ return NULL;
+}
+
+static void nfqnl_flush(struct nfqnl_instance *queue, int verdict);
+
+static void
+_instance_destroy2(struct nfqnl_instance *inst, int lock)
+{
+ /* first pull it out of the global list */
+ if (lock)
+ write_lock_bh(&instances_lock);
+
+ QDEBUG("removing instance %p (queuenum=%u) from hash\n",
+ inst, inst->queue_num);
+ hlist_del(&inst->hlist);
+
+ if (lock)
+ write_unlock_bh(&instances_lock);
+
+ /* then flush all pending skbs from the queue */
+ nfqnl_flush(inst, NF_DROP);
+
+ /* and finally free the data structure */
+ kfree(inst);
+
+ module_put(THIS_MODULE);
+}
+
+static inline void
+__instance_destroy(struct nfqnl_instance *inst)
+{
+ _instance_destroy2(inst, 0);
+}
+
+static inline void
+instance_destroy(struct nfqnl_instance *inst)
+{
+ _instance_destroy2(inst, 1);
+}
+
+
+
+static void
+issue_verdict(struct nfqnl_queue_entry *entry, int verdict)
+{
+ QDEBUG("entering for entry %p, verdict %u\n", entry, verdict);
+
+ /* TCP input path (and probably other bits) assume to be called
+ * from softirq context, not from syscall, like issue_verdict is
+ * called. TCP input path deadlocks with locks taken from timer
+ * softirq, e.g. We therefore emulate this by local_bh_disable() */
+
+ local_bh_disable();
+ nf_reinject(entry->skb, entry->info, verdict);
+ local_bh_enable();
+
+ kfree(entry);
+}
+
+static inline void
+__enqueue_entry(struct nfqnl_instance *queue,
+ struct nfqnl_queue_entry *entry)
+{
+ list_add(&entry->list, &queue->queue_list);
+ queue->queue_total++;
+}
+
+/*
+ * Find and return a queued entry matched by cmpfn, or return the last
+ * entry if cmpfn is NULL.
+ */
+static inline struct nfqnl_queue_entry *
+__find_entry(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn,
+ unsigned long data)
+{
+ struct list_head *p;
+
+ list_for_each_prev(p, &queue->queue_list) {
+ struct nfqnl_queue_entry *entry = (struct nfqnl_queue_entry *)p;
+
+ if (!cmpfn || cmpfn(entry, data))
+ return entry;
+ }
+ return NULL;
+}
+
+static inline void
+__dequeue_entry(struct nfqnl_instance *q, struct nfqnl_queue_entry *entry)
+{
+ list_del(&entry->list);
+ q->queue_total--;
+}
+
+static inline struct nfqnl_queue_entry *
+__find_dequeue_entry(struct nfqnl_instance *queue,
+ nfqnl_cmpfn cmpfn, unsigned long data)
+{
+ struct nfqnl_queue_entry *entry;
+
+ entry = __find_entry(queue, cmpfn, data);
+ if (entry == NULL)
+ return NULL;
+
+ __dequeue_entry(queue, entry);
+ return entry;
+}
+
+
+static inline void
+__nfqnl_flush(struct nfqnl_instance *queue, int verdict)
+{
+ struct nfqnl_queue_entry *entry;
+
+ while ((entry = __find_dequeue_entry(queue, NULL, 0)))
+ issue_verdict(entry, verdict);
+}
+
+static inline int
+__nfqnl_set_mode(struct nfqnl_instance *queue,
+ unsigned char mode, unsigned int range)
+{
+ int status = 0;
+
+ switch (mode) {
+ case NFQNL_COPY_NONE:
+ case NFQNL_COPY_META:
+ queue->copy_mode = mode;
+ queue->copy_range = 0;
+ break;
+
+ case NFQNL_COPY_PACKET:
+ queue->copy_mode = mode;
+ /* we're using struct nfattr which has 16bit nfa_len */
+ if (range > 0xffff)
+ queue->copy_range = 0xffff;
+ else
+ queue->copy_range = range;
+ break;
+
+ default:
+ status = -EINVAL;
+
+ }
+ return status;
+}
+
+static struct nfqnl_queue_entry *
+find_dequeue_entry(struct nfqnl_instance *queue,
+ nfqnl_cmpfn cmpfn, unsigned long data)
+{
+ struct nfqnl_queue_entry *entry;
+
+ spin_lock_bh(&queue->lock);
+ entry = __find_dequeue_entry(queue, cmpfn, data);
+ spin_unlock_bh(&queue->lock);
+
+ return entry;
+}
+
+static void
+nfqnl_flush(struct nfqnl_instance *queue, int verdict)
+{
+ spin_lock_bh(&queue->lock);
+ __nfqnl_flush(queue, verdict);
+ spin_unlock_bh(&queue->lock);
+}
+
+static struct sk_buff *
+nfqnl_build_packet_message(struct nfqnl_instance *queue,
+ struct nfqnl_queue_entry *entry, int *errp)
+{
+ unsigned char *old_tail;
+ size_t size;
+ size_t data_len = 0;
+ struct sk_buff *skb;
+ struct nfqnl_msg_packet_hdr pmsg;
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfmsg;
+ unsigned int tmp_uint;
+
+ QDEBUG("entered\n");
+
+ /* all macros expand to constant values at compile time */
+ size = NLMSG_SPACE(sizeof(struct nfqnl_msg_packet_hdr))
+ + NLMSG_SPACE(sizeof(u_int32_t)) /* ifindex */
+ + NLMSG_SPACE(sizeof(u_int32_t)) /* ifindex */
+ + NLMSG_SPACE(sizeof(u_int32_t)) /* mark */
+ + NLMSG_SPACE(sizeof(struct nfqnl_msg_packet_hw))
+ + NLMSG_SPACE(sizeof(struct nfqnl_msg_packet_timestamp));
+
+ spin_lock_bh(&queue->lock);
+
+ switch (queue->copy_mode) {
+ case NFQNL_COPY_META:
+ case NFQNL_COPY_NONE:
+ data_len = 0;
+ break;
+
+ case NFQNL_COPY_PACKET:
+ if (queue->copy_range == 0
+ || queue->copy_range > entry->skb->len)
+ data_len = entry->skb->len;
+ else
+ data_len = queue->copy_range;
+
+ size += NLMSG_SPACE(data_len);
+ break;
+
+ default:
+ *errp = -EINVAL;
+ spin_unlock_bh(&queue->lock);
+ return NULL;
+ }
+
+ spin_unlock_bh(&queue->lock);
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb)
+ goto nlmsg_failure;
+
+ old_tail= skb->tail;
+ nlh = NLMSG_PUT(skb, 0, 0,
+ NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET,
+ sizeof(struct nfgenmsg));
+ nfmsg = NLMSG_DATA(nlh);
+ nfmsg->nfgen_family = entry->info->pf;
+ nfmsg->version = NFNETLINK_V0;
+ nfmsg->res_id = htons(queue->queue_num);
+
+ pmsg.packet_id = htonl(entry->id);
+ pmsg.hw_protocol = htons(entry->skb->protocol);
+ pmsg.hook = entry->info->hook;
+
+ NFA_PUT(skb, NFQA_PACKET_HDR, sizeof(pmsg), &pmsg);
+
+ if (entry->info->indev) {
+ tmp_uint = htonl(entry->info->indev->ifindex);
+ NFA_PUT(skb, NFQA_IFINDEX_INDEV, sizeof(tmp_uint), &tmp_uint);
+ }
+
+ if (entry->info->outdev) {
+ tmp_uint = htonl(entry->info->outdev->ifindex);
+ NFA_PUT(skb, NFQA_IFINDEX_OUTDEV, sizeof(tmp_uint), &tmp_uint);
+ }
+
+ if (entry->skb->nfmark) {
+ tmp_uint = htonl(entry->skb->nfmark);
+ NFA_PUT(skb, NFQA_MARK, sizeof(u_int32_t), &tmp_uint);
+ }
+
+ if (entry->info->indev && entry->skb->dev
+ && entry->skb->dev->hard_header_parse) {
+ struct nfqnl_msg_packet_hw phw;
+
+ phw.hw_addrlen =
+ entry->skb->dev->hard_header_parse(entry->skb,
+ phw.hw_addr);
+ phw.hw_addrlen = htons(phw.hw_addrlen);
+ NFA_PUT(skb, NFQA_HWADDR, sizeof(phw), &phw);
+ }
+
+ if (entry->skb->stamp.tv_sec) {
+ struct nfqnl_msg_packet_timestamp ts;
+
+ ts.sec = htonll(entry->skb->stamp.tv_sec);
+ ts.usec = htonll(entry->skb->stamp.tv_usec);
+
+ NFA_PUT(skb, NFQA_TIMESTAMP, sizeof(ts), &ts);
+ }
+
+ if (data_len) {
+ struct nfattr *nfa;
+ int size = NFA_LENGTH(data_len);
+
+ if (skb_tailroom(skb) < (int)NFA_SPACE(data_len)) {
+ printk(KERN_WARNING "nf_queue: no tailroom!\n");
+ goto nlmsg_failure;
+ }
+
+ nfa = (struct nfattr *)skb_put(skb, NFA_ALIGN(size));
+ nfa->nfa_type = NFQA_PAYLOAD;
+ nfa->nfa_len = size;
+
+ if (skb_copy_bits(entry->skb, 0, NFA_DATA(nfa), data_len))
+ BUG();
+ }
+
+ nlh->nlmsg_len = skb->tail - old_tail;
+ return skb;
+
+nlmsg_failure:
+nfattr_failure:
+ if (skb)
+ kfree_skb(skb);
+ *errp = -EINVAL;
+ if (net_ratelimit())
+ printk(KERN_ERR "nf_queue: error creating packet message\n");
+ return NULL;
+}
+
+static int
+nfqnl_enqueue_packet(struct sk_buff *skb, struct nf_info *info,
+ unsigned int queuenum, void *data)
+{
+ int status = -EINVAL;
+ struct sk_buff *nskb;
+ struct nfqnl_instance *queue;
+ struct nfqnl_queue_entry *entry;
+
+ QDEBUG("entered\n");
+
+ queue = instance_lookup(queuenum);
+ if (!queue) {
+ QDEBUG("no queue instance matching\n");
+ return -EINVAL;
+ }
+
+ if (queue->copy_mode == NFQNL_COPY_NONE) {
+ QDEBUG("mode COPY_NONE, aborting\n");
+ return -EAGAIN;
+ }
+
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (entry == NULL) {
+ if (net_ratelimit())
+ printk(KERN_ERR
+ "nf_queue: OOM in nfqnl_enqueue_packet()\n");
+ return -ENOMEM;
+ }
+
+ entry->info = info;
+ entry->skb = skb;
+ entry->id = atomic_inc_return(&queue->id_sequence);
+
+ nskb = nfqnl_build_packet_message(queue, entry, &status);
+ if (nskb == NULL)
+ goto err_out_free;
+
+ spin_lock_bh(&queue->lock);
+
+ if (!queue->peer_pid)
+ goto err_out_free_nskb;
+
+ if (queue->queue_total >= queue->queue_maxlen) {
+ queue->queue_dropped++;
+ status = -ENOSPC;
+ if (net_ratelimit())
+ printk(KERN_WARNING "ip_queue: full at %d entries, "
+ "dropping packets(s). Dropped: %d\n",
+ queue->queue_total, queue->queue_dropped);
+ goto err_out_free_nskb;
+ }
+
+ /* nfnetlink_unicast will either free the nskb or add it to a socket */
+ status = nfnetlink_unicast(nskb, queue->peer_pid, MSG_DONTWAIT);
+ if (status < 0) {
+ queue->queue_user_dropped++;
+ goto err_out_unlock;
+ }
+
+ __enqueue_entry(queue, entry);
+
+ spin_unlock_bh(&queue->lock);
+ return status;
+
+err_out_free_nskb:
+ kfree_skb(nskb);
+
+err_out_unlock:
+ spin_unlock_bh(&queue->lock);
+
+err_out_free:
+ kfree(entry);
+ return status;
+}
+
+static int
+nfqnl_mangle(void *data, int data_len, struct nfqnl_queue_entry *e)
+{
+ int diff;
+
+ diff = data_len - e->skb->len;
+ if (diff < 0)
+ skb_trim(e->skb, data_len);
+ else if (diff > 0) {
+ if (data_len > 0xFFFF)
+ return -EINVAL;
+ if (diff > skb_tailroom(e->skb)) {
+ struct sk_buff *newskb;
+
+ newskb = skb_copy_expand(e->skb,
+ skb_headroom(e->skb),
+ diff,
+ GFP_ATOMIC);
+ if (newskb == NULL) {
+ printk(KERN_WARNING "ip_queue: OOM "
+ "in mangle, dropping packet\n");
+ return -ENOMEM;
+ }
+ if (e->skb->sk)
+ skb_set_owner_w(newskb, e->skb->sk);
+ kfree_skb(e->skb);
+ e->skb = newskb;
+ }
+ skb_put(e->skb, diff);
+ }
+ if (!skb_make_writable(&e->skb, data_len))
+ return -ENOMEM;
+ memcpy(e->skb->data, data, data_len);
+
+ return 0;
+}
+
+static inline int
+id_cmp(struct nfqnl_queue_entry *e, unsigned long id)
+{
+ return (id == e->id);
+}
+
+static int
+nfqnl_set_mode(struct nfqnl_instance *queue,
+ unsigned char mode, unsigned int range)
+{
+ int status;
+
+ spin_lock_bh(&queue->lock);
+ status = __nfqnl_set_mode(queue, mode, range);
+ spin_unlock_bh(&queue->lock);
+
+ return status;
+}
+
+static int
+dev_cmp(struct nfqnl_queue_entry *entry, unsigned long ifindex)
+{
+ if (entry->info->indev)
+ if (entry->info->indev->ifindex == ifindex)
+ return 1;
+
+ if (entry->info->outdev)
+ if (entry->info->outdev->ifindex == ifindex)
+ return 1;
+
+ return 0;
+}
+
+/* drop all packets with either indev or outdev == ifindex from all queue
+ * instances */
+static void
+nfqnl_dev_drop(int ifindex)
+{
+ int i;
+
+ QDEBUG("entering for ifindex %u\n", ifindex);
+
+ /* this only looks like we have to hold the readlock for a way too long
+ * time, issue_verdict(), nf_reinject(), ... - but we always only
+ * issue NF_DROP, which is processed directly in nf_reinject() */
+ read_lock_bh(&instances_lock);
+
+ for (i = 0; i < INSTANCE_BUCKETS; i++) {
+ struct hlist_node *tmp;
+ struct nfqnl_instance *inst;
+ struct hlist_head *head = &instance_table[i];
+
+ hlist_for_each_entry(inst, tmp, head, hlist) {
+ struct nfqnl_queue_entry *entry;
+ while ((entry = find_dequeue_entry(inst, dev_cmp,
+ ifindex)) != NULL)
+ issue_verdict(entry, NF_DROP);
+ }
+ }
+
+ read_unlock_bh(&instances_lock);
+}
+
+#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0)
+
+static int
+nfqnl_rcv_dev_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = ptr;
+
+ /* Drop any packets associated with the downed device */
+ if (event == NETDEV_DOWN)
+ nfqnl_dev_drop(dev->ifindex);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block nfqnl_dev_notifier = {
+ .notifier_call = nfqnl_rcv_dev_event,
+};
+
+static int
+nfqnl_rcv_nl_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct netlink_notify *n = ptr;
+
+ if (event == NETLINK_URELEASE &&
+ n->protocol == NETLINK_NETFILTER && n->pid) {
+ int i;
+
+ /* destroy all instances for this pid */
+ write_lock_bh(&instances_lock);
+ for (i = 0; i < INSTANCE_BUCKETS; i++) {
+ struct hlist_node *tmp, *t2;
+ struct nfqnl_instance *inst;
+ struct hlist_head *head = &instance_table[i];
+
+ hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) {
+ if (n->pid == inst->peer_pid)
+ __instance_destroy(inst);
+ }
+ }
+ write_unlock_bh(&instances_lock);
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block nfqnl_rtnl_notifier = {
+ .notifier_call = nfqnl_rcv_nl_event,
+};
+
+static int
+nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
+ struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp)
+{
+ struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
+ u_int16_t queue_num = ntohs(nfmsg->res_id);
+
+ struct nfqnl_msg_verdict_hdr *vhdr;
+ struct nfqnl_instance *queue;
+ unsigned int verdict;
+ struct nfqnl_queue_entry *entry;
+
+ queue = instance_lookup(queue_num);
+ if (!queue)
+ return -ENODEV;
+
+ if (queue->peer_pid != NETLINK_CB(skb).pid)
+ return -EPERM;
+
+ if (!nfqa[NFQA_VERDICT_HDR-1])
+ return -EINVAL;
+
+ vhdr = NFA_DATA(nfqa[NFQA_VERDICT_HDR-1]);
+ verdict = ntohl(vhdr->verdict);
+
+ if ((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT)
+ return -EINVAL;
+
+ entry = find_dequeue_entry(queue, id_cmp, ntohl(vhdr->id));
+ if (entry == NULL)
+ return -ENOENT;
+
+ if (nfqa[NFQA_PAYLOAD-1]) {
+ if (nfqnl_mangle(NFA_DATA(nfqa[NFQA_PAYLOAD-1]),
+ NFA_PAYLOAD(nfqa[NFQA_PAYLOAD-1]), entry) < 0)
+ verdict = NF_DROP;
+ }
+
+ if (nfqa[NFQA_MARK-1])
+ skb->nfmark = ntohl(*(u_int32_t *)NFA_DATA(nfqa[NFQA_MARK-1]));
+
+ issue_verdict(entry, verdict);
+ return 0;
+}
+
+static int
+nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb,
+ struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp)
+{
+ return -ENOTSUPP;
+}
+
+static int
+nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
+ struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp)
+{
+ struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
+ u_int16_t queue_num = ntohs(nfmsg->res_id);
+ struct nfqnl_instance *queue;
+
+ QDEBUG("entering for msg %u\n", NFNL_MSG_TYPE(nlh->nlmsg_type));
+
+ queue = instance_lookup(queue_num);
+ if (nfqa[NFQA_CFG_CMD-1]) {
+ struct nfqnl_msg_config_cmd *cmd;
+ cmd = NFA_DATA(nfqa[NFQA_CFG_CMD-1]);
+ QDEBUG("found CFG_CMD\n");
+
+ switch (cmd->command) {
+ case NFQNL_CFG_CMD_BIND:
+ if (queue)
+ return -EBUSY;
+
+ queue = instance_create(queue_num, NETLINK_CB(skb).pid);
+ if (!queue)
+ return -EINVAL;
+ break;
+ case NFQNL_CFG_CMD_UNBIND:
+ if (!queue)
+ return -ENODEV;
+
+ if (queue->peer_pid != NETLINK_CB(skb).pid)
+ return -EPERM;
+
+ instance_destroy(queue);
+ break;
+ case NFQNL_CFG_CMD_PF_BIND:
+ QDEBUG("registering queue handler for pf=%u\n",
+ ntohs(cmd->pf));
+ return nf_register_queue_handler(ntohs(cmd->pf),
+ nfqnl_enqueue_packet,
+ NULL);
+
+ break;
+ case NFQNL_CFG_CMD_PF_UNBIND:
+ QDEBUG("unregistering queue handler for pf=%u\n",
+ ntohs(cmd->pf));
+ /* This is a bug and a feature. We can unregister
+ * other handlers(!) */
+ return nf_unregister_queue_handler(ntohs(cmd->pf));
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ if (!queue) {
+ QDEBUG("no config command, and no instance ENOENT\n");
+ return -ENOENT;
+ }
+
+ if (queue->peer_pid != NETLINK_CB(skb).pid) {
+ QDEBUG("no config command, and wrong pid\n");
+ return -EPERM;
+ }
+ }
+
+ if (nfqa[NFQA_CFG_PARAMS-1]) {
+ struct nfqnl_msg_config_params *params;
+ params = NFA_DATA(nfqa[NFQA_CFG_PARAMS-1]);
+
+ nfqnl_set_mode(queue, params->copy_mode,
+ ntohl(params->copy_range));
+ }
+
+ return 0;
+}
+
+static struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = {
+ [NFQNL_MSG_PACKET] = { .call = nfqnl_recv_unsupp,
+ .cap_required = CAP_NET_ADMIN },
+ [NFQNL_MSG_VERDICT] = { .call = nfqnl_recv_verdict,
+ .cap_required = CAP_NET_ADMIN },
+ [NFQNL_MSG_CONFIG] = { .call = nfqnl_recv_config,
+ .cap_required = CAP_NET_ADMIN },
+};
+
+static struct nfnetlink_subsystem nfqnl_subsys = {
+ .name = "nf_queue",
+ .subsys_id = NFNL_SUBSYS_QUEUE,
+ .cb_count = NFQNL_MSG_MAX,
+ .attr_count = NFQA_MAX,
+ .cb = nfqnl_cb,
+};
+
+static int
+init_or_cleanup(int init)
+{
+ int status = -ENOMEM;
+
+ if (!init)
+ goto cleanup;
+
+ netlink_register_notifier(&nfqnl_rtnl_notifier);
+ status = nfnetlink_subsys_register(&nfqnl_subsys);
+ if (status < 0) {
+ printk(KERN_ERR "nf_queue: failed to create netlink socket\n");
+ goto cleanup_netlink_notifier;
+ }
+
+ register_netdevice_notifier(&nfqnl_dev_notifier);
+ return status;
+
+cleanup:
+ nf_unregister_queue_handlers(nfqnl_enqueue_packet);
+ unregister_netdevice_notifier(&nfqnl_dev_notifier);
+ nfnetlink_subsys_unregister(&nfqnl_subsys);
+
+cleanup_netlink_notifier:
+ netlink_unregister_notifier(&nfqnl_rtnl_notifier);
+ return status;
+}
+
+static int __init init(void)
+{
+
+ return init_or_cleanup(1);
+}
+
+static void __exit fini(void)
+{
+ init_or_cleanup(0);
+}
+
+MODULE_DESCRIPTION("netfilter packet queue handler");
+MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_QUEUE);
+
+module_init(init);
+module_exit(fini);
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* RE: Why is packet socket checked before bridge in netif_receive_skb?
From: Simon Barber @ 2005-07-26 21:22 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev
Ok - and I'm guessing that the behavior is different for the protocol
specific case (i.e. packet socket bound to a specific protocol type)
because no application has needed it to be the same? (IE sniffers
normally bind to all protocols, and protocol specific apps like DHCP
servers don't need to see frames before the bridge hook - they would
normally see them on the brige itself).
Simon
-----Original Message-----
From: David S. Miller [mailto:davem@davemloft.net]
Sent: Tuesday, July 26, 2005 12:59 PM
To: Simon Barber
Cc: netdev@oss.sgi.com
Subject: Re: Why is packet socket checked before bridge in
netif_receive_skb?
From: "Simon Barber" <simon@devicescape.com>
Subject: Why is packet socket checked before bridge in
netif_receive_skb?
Date: Tue, 26 Jul 2005 11:03:17 -0700
> The protocol handlers are also used to implement packet sockets. - Why
> is the all handler checked before the bridge hook?
Because we want packet sniffers to see the packet before the bridging
layer decapsulates it.
^ permalink raw reply
* Re: Why is packet socket checked before bridge in netif_receive_skb?
From: David S. Miller @ 2005-07-26 19:58 UTC (permalink / raw)
To: simon; +Cc: netdev
In-Reply-To: <C86180A8C204554D8A3323D8F6B0A29F6AF010@dhost002-46.dex002.intermedia.net>
From: "Simon Barber" <simon@devicescape.com>
Subject: Why is packet socket checked before bridge in netif_receive_skb?
Date: Tue, 26 Jul 2005 11:03:17 -0700
> The protocol handlers are also used to implement packet sockets. - Why
> is the all handler checked before the bridge hook?
Because we want packet sniffers to see the packet before the
bridging layer decapsulates it.
^ permalink raw reply
* RE: Why is packet socket checked before bridge in netif_receive_skb?
From: Simon Barber @ 2005-07-26 18:25 UTC (permalink / raw)
To: John W. Linville; +Cc: netdev
The odd thing is that the 'all protocols' handlers are checked before
the bridge hook, but the protocol type specific handlers are checked
after. Hence if you bind your packet socket to 'all' protocols you get
packets before bridging, but if you bind to a specific protocol you get
packets after the bridge hook.
Simon
-----Original Message-----
From: John W. Linville [mailto:linville@tuxdriver.com]
Sent: Tuesday, July 26, 2005 11:19 AM
To: Simon Barber
Cc: netdev@oss.sgi.com
Subject: Re: Why is packet socket checked before bridge in
netif_receive_skb?
On Tue, Jul 26, 2005 at 11:03:17AM -0700, Simon Barber wrote:
> The protocol handlers are also used to implement packet sockets. - Why
> is the all handler checked before the bridge hook?
Perhaps so that one can look at frames entering on a specific interface
rather than the bridge as a whole?
John
--
John W. Linville
linville@tuxdriver.com
^ permalink raw reply
* Re: Why is packet socket checked before bridge in netif_receive_skb?
From: John W. Linville @ 2005-07-26 18:18 UTC (permalink / raw)
To: Simon Barber; +Cc: netdev
In-Reply-To: <C86180A8C204554D8A3323D8F6B0A29F6AF010@dhost002-46.dex002.intermedia.net>
On Tue, Jul 26, 2005 at 11:03:17AM -0700, Simon Barber wrote:
> The protocol handlers are also used to implement packet sockets. - Why
> is the all handler checked before the bridge hook?
Perhaps so that one can look at frames entering on a specific interface
rather than the bridge as a whole?
John
--
John W. Linville
linville@tuxdriver.com
^ permalink raw reply
* Why is packet socket checked before bridge in netif_receive_skb?
From: Simon Barber @ 2005-07-26 18:03 UTC (permalink / raw)
To: netdev
[-- Attachment #1: Type: text/plain, Size: 405 bytes --]
In netif_receive_skb there is code that checks the ptype_all handlers
(protocol handlers that want to handle all protocol types) before the
bridge hook, and code to check the specific protocol handlers after the
bridge hook.
The protocol handlers are also used to implement packet sockets. - Why
is the all handler checked before the bridge hook?
(I'm reading code from 2.4.26).
Simon
[-- Attachment #2: Type: text/html, Size: 1315 bytes --]
^ permalink raw reply
* [PATCH] move ip[6]_queue rerouting up to nf_queue
From: Harald Welte @ 2005-07-26 16:45 UTC (permalink / raw)
To: David Miller; +Cc: Linux Netdev List, Netfilter Development Mailinglist
[-- Attachment #1.1: Type: text/plain, Size: 496 bytes --]
Hi Dave,
please apply. If you dislike the yet-another-NPROTO nature of it, I'll
prepare one with kmalloc-at-startup.
--
- Harald Welte <laforge@netfilter.org> http://netfilter.org/
============================================================================
"Fragmentation is like classful addressing -- an interesting early
architectural error that shows how much experimentation was going
on while IP was being designed." -- Paul Vixie
[-- Attachment #1.2: 12-queue-reroute.patch --]
[-- Type: text/plain, Size: 13365 bytes --]
Move reroute-after-queue code from ip_queue and ip6_queue up to the
nf_queue layer. The rerouting functionality is required by the core,
therefore it has to be implemented by the core and not in individual queue
handlers.
Signed-off-by: Harald Welte <laforge@netfilter.org>
---
commit f61a93c9b4b59f82ef781a50ec955c50fe32e5db
tree 588cbbf9f57d64c4be73314b9ddbbfdc6932ec55
parent 93bd54e21ddb8f5553fd8a8beaae7803ff4e3852
author laforge <laforge@netfilter.org> Di, 26 Jul 2005 18:42:37 +0200
committer laforge <laforge@netfilter.org> Di, 26 Jul 2005 18:42:37 +0200
include/linux/netfilter.h | 11 +++++++
net/core/netfilter.c | 60 ++++++++++++++++++++++++++++++--------
net/ipv4/netfilter.c | 64 +++++++++++++++++++++++++++++++++++++++-
net/ipv4/netfilter/ip_queue.c | 27 -----------------
net/ipv6/af_inet6.c | 6 ++++
net/ipv6/netfilter.c | 62 +++++++++++++++++++++++++++++++++++++++
net/ipv6/netfilter/ip6_queue.c | 24 ---------------
7 files changed, 189 insertions(+), 65 deletions(-)
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -198,6 +198,17 @@ extern void nf_invalidate_cache(int pf);
Returns true or false. */
extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len);
+struct nf_queue_rerouter {
+ void (*save)(const struct sk_buff *skb, struct nf_info *info);
+ int (*reroute)(struct sk_buff **skb, const struct nf_info *info);
+ int rer_size;
+};
+
+#define nf_info_reroute(x) ((void *)x + sizeof(typeof(x)))
+
+extern int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer);
+extern int nf_unregister_queue_rerouter(int pf);
+
#else /* !CONFIG_NETFILTER */
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
diff --git a/net/core/netfilter.c b/net/core/netfilter.c
--- a/net/core/netfilter.c
+++ b/net/core/netfilter.c
@@ -53,6 +53,9 @@ static struct nf_queue_handler_t {
nf_queue_outfn_t outfn;
void *data;
} queue_handler[NPROTO];
+
+static struct nf_queue_rerouter queue_rerouter[NPROTO];
+
static DEFINE_RWLOCK(queue_handler_lock);
int nf_register_hook(struct nf_hook_ops *reg)
@@ -260,11 +263,34 @@ int nf_unregister_queue_handler(int pf)
return 0;
}
+int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer)
+{
+ if (pf >= NPROTO)
+ return -EINVAL;
+
+ write_lock_bh(&queue_handler_lock);
+ memcpy(&queue_rerouter[pf], rer, sizeof(queue_rerouter[pf]));
+ write_unlock_bh(&queue_handler_lock);
+
+ return 0;
+}
+
+int nf_unregister_queue_rerouter(int pf)
+{
+ if (pf >= NPROTO)
+ return -EINVAL;
+
+ write_lock_bh(&queue_handler_lock);
+ memset(&queue_rerouter[pf], 0, sizeof(queue_rerouter[pf]));
+ write_unlock_bh(&queue_handler_lock);
+ return 0;
+}
+
/*
* Any packet that leaves via this function must come back
* through nf_reinject().
*/
-static int nf_queue(struct sk_buff *skb,
+static int nf_queue(struct sk_buff **skb,
struct list_head *elem,
int pf, unsigned int hook,
struct net_device *indev,
@@ -282,17 +308,17 @@ static int nf_queue(struct sk_buff *skb,
read_lock(&queue_handler_lock);
if (!queue_handler[pf].outfn) {
read_unlock(&queue_handler_lock);
- kfree_skb(skb);
+ kfree_skb(*skb);
return 1;
}
- info = kmalloc(sizeof(*info), GFP_ATOMIC);
+ info = kmalloc(sizeof(*info)+queue_rerouter[pf].rer_size, GFP_ATOMIC);
if (!info) {
if (net_ratelimit())
printk(KERN_ERR "OOM queueing packet %p\n",
- skb);
+ *skb);
read_unlock(&queue_handler_lock);
- kfree_skb(skb);
+ kfree_skb(*skb);
return 1;
}
@@ -311,15 +337,21 @@ static int nf_queue(struct sk_buff *skb,
if (outdev) dev_hold(outdev);
#ifdef CONFIG_BRIDGE_NETFILTER
- if (skb->nf_bridge) {
- physindev = skb->nf_bridge->physindev;
+ if ((*skb)->nf_bridge) {
+ physindev = (*skb)->nf_bridge->physindev;
if (physindev) dev_hold(physindev);
- physoutdev = skb->nf_bridge->physoutdev;
+ physoutdev = (*skb)->nf_bridge->physoutdev;
if (physoutdev) dev_hold(physoutdev);
}
#endif
+ if (queue_rerouter[pf].save)
+ queue_rerouter[pf].save(*skb, info);
+
+ status = queue_handler[pf].outfn(*skb, info, queue_handler[pf].data);
+
+ if (status >= 0 && queue_rerouter[pf].reroute)
+ status = queue_rerouter[pf].reroute(skb, info);
- status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
read_unlock(&queue_handler_lock);
if (status < 0) {
@@ -332,9 +364,11 @@ static int nf_queue(struct sk_buff *skb,
#endif
module_put(info->elem->owner);
kfree(info);
- kfree_skb(skb);
+ kfree_skb(*skb);
+
return 1;
}
+
return 1;
}
@@ -365,7 +399,7 @@ next_hook:
ret = -EPERM;
} else if (verdict == NF_QUEUE) {
NFDEBUG("nf_hook: Verdict = QUEUE.\n");
- if (!nf_queue(*pskb, elem, pf, hook, indev, outdev, okfn))
+ if (!nf_queue(pskb, elem, pf, hook, indev, outdev, okfn))
goto next_hook;
}
unlock:
@@ -428,7 +462,7 @@ void nf_reinject(struct sk_buff *skb, st
break;
case NF_QUEUE:
- if (!nf_queue(skb, elem, info->pf, info->hook,
+ if (!nf_queue(&skb, elem, info->pf, info->hook,
info->indev, info->outdev, info->okfn))
goto next_hook;
break;
@@ -573,4 +607,6 @@ EXPORT_SYMBOL(nf_reinject);
EXPORT_SYMBOL(nf_setsockopt);
EXPORT_SYMBOL(nf_unregister_hook);
EXPORT_SYMBOL(nf_unregister_queue_handler);
+EXPORT_SYMBOL_GPL(nf_register_queue_rerouter);
+EXPORT_SYMBOL_GPL(nf_unregister_queue_rerouter);
EXPORT_SYMBOL(nf_unregister_sockopt);
diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c
--- a/net/ipv4/netfilter.c
+++ b/net/ipv4/netfilter.c
@@ -1,10 +1,11 @@
-#include <linux/config.h>
+/* IPv4 specific functions of netfilter core */
+#include <linux/config.h>
#ifdef CONFIG_NETFILTER
-/* IPv4 specific functions of netfilter core */
#include <linux/kernel.h>
#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
#include <linux/tcp.h>
#include <linux/udp.h>
@@ -76,4 +77,63 @@ int ip_route_me_harder(struct sk_buff **
return 0;
}
EXPORT_SYMBOL(ip_route_me_harder);
+
+/*
+ * Extra routing may needed on local out, as the QUEUE target never
+ * returns control to the table.
+ */
+
+struct ip_rt_info {
+ u_int32_t daddr;
+ u_int32_t saddr;
+ u_int8_t tos;
+};
+
+static void queue_save(const struct sk_buff *skb, struct nf_info *info)
+{
+ struct ip_rt_info *rt_info = nf_info_reroute(info);
+
+ if (info->hook == NF_IP_LOCAL_OUT) {
+ const struct iphdr *iph = skb->nh.iph;
+
+ rt_info->tos = iph->tos;
+ rt_info->daddr = iph->daddr;
+ rt_info->saddr = iph->saddr;
+ }
+}
+
+static int queue_reroute(struct sk_buff **pskb, const struct nf_info *info)
+{
+ const struct ip_rt_info *rt_info = nf_info_reroute(info);
+
+ if (info->hook == NF_IP_LOCAL_OUT) {
+ struct iphdr *iph = (*pskb)->nh.iph;
+
+ if (!(iph->tos == rt_info->tos
+ && iph->daddr == rt_info->daddr
+ && iph->saddr == rt_info->saddr))
+ return ip_route_me_harder(pskb);
+ }
+ return 0;
+}
+
+static struct nf_queue_rerouter ip_reroute = {
+ .rer_size = sizeof(struct ip_rt_info),
+ .save = queue_save,
+ .reroute = queue_reroute,
+};
+
+static int init(void)
+{
+ return nf_register_queue_rerouter(PF_INET, &ip_reroute);
+}
+
+static void fini(void)
+{
+ nf_unregister_queue_rerouter(PF_INET);
+}
+
+module_init(init);
+module_exit(fini);
+
#endif /* CONFIG_NETFILTER */
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c
--- a/net/ipv4/netfilter/ip_queue.c
+++ b/net/ipv4/netfilter/ip_queue.c
@@ -43,17 +43,10 @@
#define NET_IPQ_QMAX 2088
#define NET_IPQ_QMAX_NAME "ip_queue_maxlen"
-struct ipq_rt_info {
- __u8 tos;
- __u32 daddr;
- __u32 saddr;
-};
-
struct ipq_queue_entry {
struct list_head list;
struct nf_info *info;
struct sk_buff *skb;
- struct ipq_rt_info rt_info;
};
typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long);
@@ -299,14 +292,6 @@ ipq_enqueue_packet(struct sk_buff *skb,
entry->info = info;
entry->skb = skb;
- if (entry->info->hook == NF_IP_LOCAL_OUT) {
- struct iphdr *iph = skb->nh.iph;
-
- entry->rt_info.tos = iph->tos;
- entry->rt_info.daddr = iph->daddr;
- entry->rt_info.saddr = iph->saddr;
- }
-
nskb = ipq_build_packet_message(entry, &status);
if (nskb == NULL)
goto err_out_free;
@@ -386,18 +371,6 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, st
return -ENOMEM;
memcpy(e->skb->data, v->payload, v->data_len);
- /*
- * Extra routing may needed on local out, as the QUEUE target never
- * returns control to the table.
- */
- if (e->info->hook == NF_IP_LOCAL_OUT) {
- struct iphdr *iph = e->skb->nh.iph;
-
- if (!(iph->tos == e->rt_info.tos
- && iph->daddr == e->rt_info.daddr
- && iph->saddr == e->rt_info.saddr))
- return ip_route_me_harder(&e->skb);
- }
return 0;
}
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -757,6 +757,9 @@ static int __init inet6_init(void)
err = igmp6_init(&inet6_family_ops);
if (err)
goto igmp_fail;
+ err = ipv6_netfilter_init();
+ if (err)
+ goto netfilter_fail;
/* Create /proc/foo6 entries. */
#ifdef CONFIG_PROC_FS
err = -ENOMEM;
@@ -813,6 +816,8 @@ proc_tcp6_fail:
raw6_proc_exit();
proc_raw6_fail:
#endif
+ ipv6_netfilter_fini();
+netfilter_fail:
igmp6_cleanup();
igmp_fail:
ndisc_cleanup();
@@ -852,6 +857,7 @@ static void __exit inet6_exit(void)
ip6_route_cleanup();
ipv6_packet_cleanup();
igmp6_cleanup();
+ ipv6_netfilter_fini();
ndisc_cleanup();
icmpv6_cleanup();
#ifdef CONFIG_SYSCTL
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -4,6 +4,8 @@
#include <linux/kernel.h>
#include <linux/ipv6.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6.h>
#include <net/dst.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
@@ -39,4 +41,64 @@ int ip6_route_me_harder(struct sk_buff *
}
EXPORT_SYMBOL(ip6_route_me_harder);
+/*
+ * Extra routing may needed on local out, as the QUEUE target never
+ * returns control to the table.
+ */
+
+struct ip6_rt_info {
+ struct in6_addr daddr;
+ struct in6_addr saddr;
+};
+
+static void save(const struct sk_buff *skb, struct nf_info *info)
+{
+ struct ip6_rt_info *rt_info = nf_info_reroute(info);
+
+ if (info->hook == NF_IP6_LOCAL_OUT) {
+ struct ipv6hdr *iph = skb->nh.ipv6h;
+
+ rt_info->daddr = iph->daddr;
+ rt_info->saddr = iph->saddr;
+ }
+}
+
+static int reroute(struct sk_buff **pskb, const struct nf_info *info)
+{
+ struct ip6_rt_info *rt_info = nf_info_reroute(info);
+
+ if (info->hook == NF_IP6_LOCAL_OUT) {
+ struct ipv6hdr *iph = (*pskb)->nh.ipv6h;
+ if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) ||
+ !ipv6_addr_equal(&iph->saddr, &rt_info->saddr))
+ return ip6_route_me_harder(*pskb);
+ }
+ return 0;
+}
+
+static struct nf_queue_rerouter ip6_reroute = {
+ .rer_size = sizeof(struct ip6_rt_info),
+ .save = &save,
+ .reroute = &reroute,
+};
+
+int __init ipv6_netfilter_init(void)
+{
+ return nf_register_queue_rerouter(PF_INET6, &ip6_reroute);
+}
+
+void __exit ipv6_netfilter_fini(void)
+{
+ nf_unregister_queue_rerouter(PF_INET6);
+}
+
+#else /* CONFIG_NETFILTER */
+int __init ipv6_netfilter_init(void)
+{
+ return 0;
+}
+
+void __exit ipv6_netfilter_fini(void)
+{
+}
#endif /* CONFIG_NETFILTER */
diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c
--- a/net/ipv6/netfilter/ip6_queue.c
+++ b/net/ipv6/netfilter/ip6_queue.c
@@ -47,16 +47,10 @@
#define NET_IPQ_QMAX 2088
#define NET_IPQ_QMAX_NAME "ip6_queue_maxlen"
-struct ipq_rt_info {
- struct in6_addr daddr;
- struct in6_addr saddr;
-};
-
struct ipq_queue_entry {
struct list_head list;
struct nf_info *info;
struct sk_buff *skb;
- struct ipq_rt_info rt_info;
};
typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long);
@@ -296,13 +290,6 @@ ipq_enqueue_packet(struct sk_buff *skb,
entry->info = info;
entry->skb = skb;
- if (entry->info->hook == NF_IP_LOCAL_OUT) {
- struct ipv6hdr *iph = skb->nh.ipv6h;
-
- entry->rt_info.daddr = iph->daddr;
- entry->rt_info.saddr = iph->saddr;
- }
-
nskb = ipq_build_packet_message(entry, &status);
if (nskb == NULL)
goto err_out_free;
@@ -382,17 +369,6 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, st
return -ENOMEM;
memcpy(e->skb->data, v->payload, v->data_len);
- /*
- * Extra routing may needed on local out, as the QUEUE target never
- * returns control to the table.
- * Not a nice way to cmp, but works
- */
- if (e->info->hook == NF_IP_LOCAL_OUT) {
- struct ipv6hdr *iph = e->skb->nh.ipv6h;
- if (!ipv6_addr_equal(&iph->daddr, &e->rt_info.daddr) ||
- !ipv6_addr_equal(&iph->saddr, &e->rt_info.saddr))
- return ip6_route_me_harder(e->skb);
- }
return 0;
}
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ 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