[NETFILTER]: Core changes required by upcoming nfnetlink_queue code
authorHarald Welte <laforge@netfilter.org>
Wed, 10 Aug 2005 02:43:44 +0000 (19:43 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Mon, 29 Aug 2005 22:36:49 +0000 (15:36 -0700)
- 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>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netfilter.h
include/linux/netfilter/nfnetlink.h
net/core/netfilter.c
net/ipv4/netfilter/ip_queue.c
net/ipv6/netfilter/ip6_queue.c
net/netfilter/nfnetlink.c

index d163e20ca8d9677d234f2091fce636764b4a45e5..711e05f33d686bb2616a4185750a2952789e406e 100644 (file)
 #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);
index ace7a7be0742da5a5d1be3aeaf06cb8873c77b8a..561f9df288088d32299c58be31fad8a115f203b9 100644 (file)
@@ -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 */
index 1ed4f311042126e8473099a2438c47a893e5d3a6..3e38084ac2bd6a4a2d829b778a81f31102e3b6a3 100644 (file)
@@ -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);
index 78892980f42c5320c70eb46cb18e6395e09f94bd..cfc886f382ac819490e355884d85688b94a24923 100644 (file)
@@ -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;
index c45d8f8815de27aafbeabd47b4b7e429fb58bf60..5af4cee93d9bd20eeb07a5cadca30233eaf3829a 100644 (file)
@@ -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;
index 6210ca42166c60d5917c4cb179da7966c5d4c3ff..30b25f47f7cc149ca9f74a7686c8ec97f9585715 100644 (file)
@@ -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);
 }