summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netfilter.h14
-rw-r--r--include/linux/netfilter/nfnetlink.h20
-rw-r--r--net/core/netfilter.c40
-rw-r--r--net/ipv4/netfilter/ip_queue.c3
-rw-r--r--net/ipv6/netfilter/ip6_queue.c3
-rw-r--r--net/netfilter/nfnetlink.c28
6 files changed, 83 insertions, 25 deletions
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index d163e20ca8d9..711e05f33d68 100644
--- 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 pf, int optval, char __user *opt,
/* 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
index ace7a7be0742..561f9df28808 100644
--- 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 *skb, u32 pid, unsigned group,
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
index 1ed4f3110421..3e38084ac2bd 100644
--- a/net/core/netfilter.c
+++ b/net/core/netfilter.c
@@ -221,7 +221,8 @@ static unsigned int nf_iterate(struct list_head *head,
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_queue_outfn_t outfn, void *data)
{
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_queue_outfn_t outfn, void *data)
/* 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, struct nf_info *info,
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;
}
@@ -613,6 +638,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
index 78892980f42c..cfc886f382ac 100644
--- a/net/ipv4/netfilter/ip_queue.c
+++ b/net/ipv4/netfilter/ip_queue.c
@@ -280,7 +280,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
index c45d8f8815de..5af4cee93d9b 100644
--- a/net/ipv6/netfilter/ip6_queue.c
+++ b/net/ipv6/netfilter/ip6_queue.c
@@ -278,7 +278,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
index 6210ca42166c..30b25f47f7cc 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -44,7 +44,9 @@ MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
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
@@ -67,11 +69,11 @@ int nfnetlink_subsys_register(struct nfnetlink_subsystem *n)
{
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();
@@ -227,8 +229,18 @@ static inline int nfnetlink_rcv_msg(struct sk_buff *skb,
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) {
@@ -252,12 +264,14 @@ static inline int nfnetlink_rcv_msg(struct sk_buff *skb,
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;
}
@@ -311,6 +325,8 @@ static void nfnetlink_rcv(struct sock *sk, int len)
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);
}