Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: allow NFQUEUE bypass if no listener is available

If an skb is to be NF_QUEUE'd, but no program has opened the queue, the
packet is dropped.

This adds a v2 target revision of xt_NFQUEUE that allows packets to
continue through the ruleset instead.

Because the actual queueing happens outside of the target context, the
'bypass' flag has to be communicated back to the netfilter core.

Unfortunately the only choice to do this without adding a new function
argument is to use the target function return value (i.e. the verdict).

In the NF_QUEUE case, the upper 16bit already contain the queue number
to use. The previous patch reduced NF_VERDICT_MASK to 0xff, i.e.
we now have extra room for a new flag.

If a hook issued a NF_QUEUE verdict, then the netfilter core will
continue packet processing if the queueing hook
returns -ESRCH (== "this queue does not exist") and the new
NF_VERDICT_FLAG_QUEUE_BYPASS flag is set in the verdict value.

Note: If the queue exists, but userspace does not consume packets fast
enough, the skb will still be dropped.

Signed-off-by: Florian Westphal <fwestphal@astaro.com>
Signed-off-by: Patrick McHardy <kaber@trash.net>

authored by

Florian Westphal and committed by
Patrick McHardy
94b27cc3 f615df76

+41 -4
+1
include/linux/netfilter.h
··· 29 29 #define NF_VERDICT_MASK 0x000000ff 30 30 31 31 /* extra verdict flags have mask 0x0000ff00 */ 32 + #define NF_VERDICT_FLAG_QUEUE_BYPASS 0x00008000 32 33 33 34 /* queue number (NF_QUEUE) or errno (NF_DROP) */ 34 35 #define NF_VERDICT_QMASK 0xffff0000
+6
include/linux/netfilter/xt_NFQUEUE.h
··· 20 20 __u16 queues_total; 21 21 }; 22 22 23 + struct xt_NFQ_info_v2 { 24 + __u16 queuenum; 25 + __u16 queues_total; 26 + __u16 bypass; 27 + }; 28 + 23 29 #endif /* _XT_NFQ_TARGET_H */
+3
net/netfilter/core.c
··· 184 184 if (ret < 0) { 185 185 if (ret == -ECANCELED) 186 186 goto next_hook; 187 + if (ret == -ESRCH && 188 + (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) 189 + goto next_hook; 187 190 kfree_skb(skb); 188 191 } 189 192 ret = 0;
+6 -1
net/netfilter/nf_queue.c
··· 138 138 rcu_read_lock(); 139 139 140 140 qh = rcu_dereference(queue_handler[pf]); 141 - if (!qh) 141 + if (!qh) { 142 + status = -ESRCH; 142 143 goto err_unlock; 144 + } 143 145 144 146 afinfo = nf_get_afinfo(pf); 145 147 if (!afinfo) ··· 304 302 verdict >> NF_VERDICT_QBITS); 305 303 if (err < 0) { 306 304 if (err == -ECANCELED) 305 + goto next_hook; 306 + if (err == -ESRCH && 307 + (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) 307 308 goto next_hook; 308 309 kfree_skb(skb); 309 310 }
+25 -3
net/netfilter/xt_NFQUEUE.c
··· 83 83 return NF_QUEUE_NR(queue); 84 84 } 85 85 86 - static int nfqueue_tg_v1_check(const struct xt_tgchk_param *par) 86 + static unsigned int 87 + nfqueue_tg_v2(struct sk_buff *skb, const struct xt_action_param *par) 87 88 { 88 - const struct xt_NFQ_info_v1 *info = par->targinfo; 89 + const struct xt_NFQ_info_v2 *info = par->targinfo; 90 + unsigned int ret = nfqueue_tg_v1(skb, par); 91 + 92 + if (info->bypass) 93 + ret |= NF_VERDICT_FLAG_QUEUE_BYPASS; 94 + return ret; 95 + } 96 + 97 + static int nfqueue_tg_check(const struct xt_tgchk_param *par) 98 + { 99 + const struct xt_NFQ_info_v2 *info = par->targinfo; 89 100 u32 maxid; 90 101 91 102 if (unlikely(!rnd_inited)) { ··· 113 102 info->queues_total, maxid); 114 103 return -ERANGE; 115 104 } 105 + if (par->target->revision == 2 && info->bypass > 1) 106 + return -EINVAL; 116 107 return 0; 117 108 } 118 109 ··· 130 117 .name = "NFQUEUE", 131 118 .revision = 1, 132 119 .family = NFPROTO_UNSPEC, 133 - .checkentry = nfqueue_tg_v1_check, 120 + .checkentry = nfqueue_tg_check, 134 121 .target = nfqueue_tg_v1, 135 122 .targetsize = sizeof(struct xt_NFQ_info_v1), 123 + .me = THIS_MODULE, 124 + }, 125 + { 126 + .name = "NFQUEUE", 127 + .revision = 2, 128 + .family = NFPROTO_UNSPEC, 129 + .checkentry = nfqueue_tg_check, 130 + .target = nfqueue_tg_v2, 131 + .targetsize = sizeof(struct xt_NFQ_info_v2), 136 132 .me = THIS_MODULE, 137 133 }, 138 134 };