Blame view
net/netfilter/nf_queue.c
8.58 KB
f6ebe77f9 [NETFILTER]: spli... |
1 |
#include <linux/kernel.h> |
5a0e3ad6a include cleanup: ... |
2 |
#include <linux/slab.h> |
f6ebe77f9 [NETFILTER]: spli... |
3 4 5 6 7 |
#include <linux/init.h> #include <linux/module.h> #include <linux/proc_fs.h> #include <linux/skbuff.h> #include <linux/netfilter.h> |
bbd86b9fc [NETFILTER]: add ... |
8 |
#include <linux/seq_file.h> |
7a11b9848 [NETFILTER]: nf_q... |
9 |
#include <linux/rcupdate.h> |
f6ebe77f9 [NETFILTER]: spli... |
10 |
#include <net/protocol.h> |
c01cd429f [NETFILTER]: nf_q... |
11 |
#include <net/netfilter/nf_queue.h> |
7fee226ad net: add a noref ... |
12 |
#include <net/dst.h> |
f6ebe77f9 [NETFILTER]: spli... |
13 14 |
#include "nf_internals.h" |
601e68e10 [NETFILTER]: Fix ... |
15 |
/* |
f6ebe77f9 [NETFILTER]: spli... |
16 17 18 19 |
* A queue handler may be registered for each protocol. Each is protected by * long term mutex. The handler must provide an an outfn() to accept packets * for queueing and must reinject all packets it receives, no matter what. */ |
0906a372f net/netfilter: __... |
20 |
static const struct nf_queue_handler __rcu *queue_handler[NFPROTO_NUMPROTO] __read_mostly; |
f6ebe77f9 [NETFILTER]: spli... |
21 |
|
585426fdc [NETFILTER]: nf_q... |
22 |
static DEFINE_MUTEX(queue_handler_mutex); |
f6ebe77f9 [NETFILTER]: spli... |
23 |
|
d72367b6f [NETFILTER]: more... |
24 25 |
/* return EBUSY when somebody else is registered, return EEXIST if the * same handler is registered, return 0 in case of success. */ |
76108cea0 netfilter: Use un... |
26 |
int nf_register_queue_handler(u_int8_t pf, const struct nf_queue_handler *qh) |
601e68e10 [NETFILTER]: Fix ... |
27 |
{ |
f6ebe77f9 [NETFILTER]: spli... |
28 |
int ret; |
0e60ebe04 netfilter: add __... |
29 |
const struct nf_queue_handler *old; |
f6ebe77f9 [NETFILTER]: spli... |
30 |
|
7e9c6eeb1 netfilter: Introd... |
31 |
if (pf >= ARRAY_SIZE(queue_handler)) |
f6ebe77f9 [NETFILTER]: spli... |
32 |
return -EINVAL; |
585426fdc [NETFILTER]: nf_q... |
33 |
mutex_lock(&queue_handler_mutex); |
0e60ebe04 netfilter: add __... |
34 35 36 |
old = rcu_dereference_protected(queue_handler[pf], lockdep_is_held(&queue_handler_mutex)); if (old == qh) |
d72367b6f [NETFILTER]: more... |
37 |
ret = -EEXIST; |
0e60ebe04 netfilter: add __... |
38 |
else if (old) |
f6ebe77f9 [NETFILTER]: spli... |
39 40 |
ret = -EBUSY; else { |
cf778b00e net: reintroduce ... |
41 |
rcu_assign_pointer(queue_handler[pf], qh); |
f6ebe77f9 [NETFILTER]: spli... |
42 43 |
ret = 0; } |
585426fdc [NETFILTER]: nf_q... |
44 |
mutex_unlock(&queue_handler_mutex); |
f6ebe77f9 [NETFILTER]: spli... |
45 46 47 48 49 50 |
return ret; } EXPORT_SYMBOL(nf_register_queue_handler); /* The caller must flush their queue before this */ |
76108cea0 netfilter: Use un... |
51 |
int nf_unregister_queue_handler(u_int8_t pf, const struct nf_queue_handler *qh) |
f6ebe77f9 [NETFILTER]: spli... |
52 |
{ |
0e60ebe04 netfilter: add __... |
53 |
const struct nf_queue_handler *old; |
7e9c6eeb1 netfilter: Introd... |
54 |
if (pf >= ARRAY_SIZE(queue_handler)) |
f6ebe77f9 [NETFILTER]: spli... |
55 |
return -EINVAL; |
585426fdc [NETFILTER]: nf_q... |
56 |
mutex_lock(&queue_handler_mutex); |
0e60ebe04 netfilter: add __... |
57 58 59 |
old = rcu_dereference_protected(queue_handler[pf], lockdep_is_held(&queue_handler_mutex)); if (old && old != qh) { |
585426fdc [NETFILTER]: nf_q... |
60 |
mutex_unlock(&queue_handler_mutex); |
ce7663d84 [NETFILTER]: nfne... |
61 62 |
return -EINVAL; } |
a9b3cd7f3 rcu: convert uses... |
63 |
RCU_INIT_POINTER(queue_handler[pf], NULL); |
585426fdc [NETFILTER]: nf_q... |
64 65 66 |
mutex_unlock(&queue_handler_mutex); synchronize_rcu(); |
601e68e10 [NETFILTER]: Fix ... |
67 |
|
f6ebe77f9 [NETFILTER]: spli... |
68 69 70 |
return 0; } EXPORT_SYMBOL(nf_unregister_queue_handler); |
e3ac52981 [NETFILTER]: nf_q... |
71 |
void nf_unregister_queue_handlers(const struct nf_queue_handler *qh) |
f6ebe77f9 [NETFILTER]: spli... |
72 |
{ |
76108cea0 netfilter: Use un... |
73 |
u_int8_t pf; |
f6ebe77f9 [NETFILTER]: spli... |
74 |
|
585426fdc [NETFILTER]: nf_q... |
75 |
mutex_lock(&queue_handler_mutex); |
7e9c6eeb1 netfilter: Introd... |
76 |
for (pf = 0; pf < ARRAY_SIZE(queue_handler); pf++) { |
0e60ebe04 netfilter: add __... |
77 78 79 80 |
if (rcu_dereference_protected( queue_handler[pf], lockdep_is_held(&queue_handler_mutex) ) == qh) |
a9b3cd7f3 rcu: convert uses... |
81 |
RCU_INIT_POINTER(queue_handler[pf], NULL); |
f6ebe77f9 [NETFILTER]: spli... |
82 |
} |
585426fdc [NETFILTER]: nf_q... |
83 84 85 |
mutex_unlock(&queue_handler_mutex); synchronize_rcu(); |
f6ebe77f9 [NETFILTER]: spli... |
86 87 |
} EXPORT_SYMBOL_GPL(nf_unregister_queue_handlers); |
daaa8be2e [NETFILTER]: nf_q... |
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
static void nf_queue_entry_release_refs(struct nf_queue_entry *entry) { /* Release those devices we held, or Alexey will kill me. */ if (entry->indev) dev_put(entry->indev); if (entry->outdev) dev_put(entry->outdev); #ifdef CONFIG_BRIDGE_NETFILTER if (entry->skb->nf_bridge) { struct nf_bridge_info *nf_bridge = entry->skb->nf_bridge; if (nf_bridge->physindev) dev_put(nf_bridge->physindev); if (nf_bridge->physoutdev) dev_put(nf_bridge->physoutdev); } #endif /* Drop reference to owner of hook which queued us. */ module_put(entry->elem->owner); } |
601e68e10 [NETFILTER]: Fix ... |
108 109 |
/* * Any packet that leaves via this function must come back |
f6ebe77f9 [NETFILTER]: spli... |
110 111 |
* through nf_reinject(). */ |
394f545db [NETFILTER]: nf_q... |
112 113 |
static int __nf_queue(struct sk_buff *skb, struct list_head *elem, |
76108cea0 netfilter: Use un... |
114 |
u_int8_t pf, unsigned int hook, |
394f545db [NETFILTER]: nf_q... |
115 116 117 118 |
struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *), unsigned int queuenum) |
f6ebe77f9 [NETFILTER]: spli... |
119 |
{ |
f15850861 netfilter: nfnetl... |
120 |
int status = -ENOENT; |
daaa8be2e [NETFILTER]: nf_q... |
121 |
struct nf_queue_entry *entry = NULL; |
f6ebe77f9 [NETFILTER]: spli... |
122 |
#ifdef CONFIG_BRIDGE_NETFILTER |
daaa8be2e [NETFILTER]: nf_q... |
123 124 |
struct net_device *physindev; struct net_device *physoutdev; |
f6ebe77f9 [NETFILTER]: spli... |
125 |
#endif |
1e796fda0 [NETFILTER]: cons... |
126 |
const struct nf_afinfo *afinfo; |
e3ac52981 [NETFILTER]: nf_q... |
127 |
const struct nf_queue_handler *qh; |
f6ebe77f9 [NETFILTER]: spli... |
128 |
|
25985edce Fix common misspe... |
129 |
/* QUEUE == DROP if no one is waiting, to be safe. */ |
585426fdc [NETFILTER]: nf_q... |
130 131 132 |
rcu_read_lock(); qh = rcu_dereference(queue_handler[pf]); |
94b27cc36 netfilter: allow ... |
133 134 |
if (!qh) { status = -ESRCH; |
daaa8be2e [NETFILTER]: nf_q... |
135 |
goto err_unlock; |
94b27cc36 netfilter: allow ... |
136 |
} |
f6ebe77f9 [NETFILTER]: spli... |
137 |
|
bce8032ef [NETFILTER]: Intr... |
138 |
afinfo = nf_get_afinfo(pf); |
daaa8be2e [NETFILTER]: nf_q... |
139 140 |
if (!afinfo) goto err_unlock; |
bce8032ef [NETFILTER]: Intr... |
141 |
|
02f014d88 [NETFILTER]: nf_q... |
142 |
entry = kmalloc(sizeof(*entry) + afinfo->route_key_size, GFP_ATOMIC); |
f15850861 netfilter: nfnetl... |
143 144 |
if (!entry) { status = -ENOMEM; |
daaa8be2e [NETFILTER]: nf_q... |
145 |
goto err_unlock; |
f15850861 netfilter: nfnetl... |
146 |
} |
f6ebe77f9 [NETFILTER]: spli... |
147 |
|
02f014d88 [NETFILTER]: nf_q... |
148 149 150 151 152 153 154 155 156 |
*entry = (struct nf_queue_entry) { .skb = skb, .elem = list_entry(elem, struct nf_hook_ops, list), .pf = pf, .hook = hook, .indev = indev, .outdev = outdev, .okfn = okfn, }; |
f6ebe77f9 [NETFILTER]: spli... |
157 158 |
/* If it's going away, ignore hook. */ |
02f014d88 [NETFILTER]: nf_q... |
159 |
if (!try_module_get(entry->elem->owner)) { |
06cdb6349 netfilter: nfnetl... |
160 161 |
status = -ECANCELED; goto err_unlock; |
f6ebe77f9 [NETFILTER]: spli... |
162 |
} |
f6ebe77f9 [NETFILTER]: spli... |
163 |
/* Bump dev refs so they don't vanish while packet is out */ |
8b1cf0db2 [NETFILTER]: nf_q... |
164 165 166 167 |
if (indev) dev_hold(indev); if (outdev) dev_hold(outdev); |
f6ebe77f9 [NETFILTER]: spli... |
168 |
#ifdef CONFIG_BRIDGE_NETFILTER |
394f545db [NETFILTER]: nf_q... |
169 170 |
if (skb->nf_bridge) { physindev = skb->nf_bridge->physindev; |
8b1cf0db2 [NETFILTER]: nf_q... |
171 172 |
if (physindev) dev_hold(physindev); |
394f545db [NETFILTER]: nf_q... |
173 |
physoutdev = skb->nf_bridge->physoutdev; |
8b1cf0db2 [NETFILTER]: nf_q... |
174 175 |
if (physoutdev) dev_hold(physoutdev); |
f6ebe77f9 [NETFILTER]: spli... |
176 177 |
} #endif |
7fee226ad net: add a noref ... |
178 |
skb_dst_force(skb); |
02f014d88 [NETFILTER]: nf_q... |
179 180 |
afinfo->saveroute(skb, entry); status = qh->outfn(entry, queuenum); |
f6ebe77f9 [NETFILTER]: spli... |
181 |
|
585426fdc [NETFILTER]: nf_q... |
182 |
rcu_read_unlock(); |
f6ebe77f9 [NETFILTER]: spli... |
183 184 |
if (status < 0) { |
daaa8be2e [NETFILTER]: nf_q... |
185 186 |
nf_queue_entry_release_refs(entry); goto err; |
f6ebe77f9 [NETFILTER]: spli... |
187 |
} |
f15850861 netfilter: nfnetl... |
188 |
return 0; |
daaa8be2e [NETFILTER]: nf_q... |
189 190 191 192 |
err_unlock: rcu_read_unlock(); err: |
daaa8be2e [NETFILTER]: nf_q... |
193 |
kfree(entry); |
f15850861 netfilter: nfnetl... |
194 |
return status; |
f6ebe77f9 [NETFILTER]: spli... |
195 |
} |
394f545db [NETFILTER]: nf_q... |
196 197 |
int nf_queue(struct sk_buff *skb, struct list_head *elem, |
76108cea0 netfilter: Use un... |
198 |
u_int8_t pf, unsigned int hook, |
394f545db [NETFILTER]: nf_q... |
199 200 201 202 203 204 |
struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *), unsigned int queuenum) { struct sk_buff *segs; |
f15850861 netfilter: nfnetl... |
205 206 |
int err; unsigned int queued; |
394f545db [NETFILTER]: nf_q... |
207 208 209 210 211 212 |
if (!skb_is_gso(skb)) return __nf_queue(skb, elem, pf, hook, indev, outdev, okfn, queuenum); switch (pf) { |
4b1e27e99 netfilter: queue:... |
213 |
case NFPROTO_IPV4: |
394f545db [NETFILTER]: nf_q... |
214 215 |
skb->protocol = htons(ETH_P_IP); break; |
4b1e27e99 netfilter: queue:... |
216 |
case NFPROTO_IPV6: |
394f545db [NETFILTER]: nf_q... |
217 218 219 220 221 |
skb->protocol = htons(ETH_P_IPV6); break; } segs = skb_gso_segment(skb, 0); |
f15850861 netfilter: nfnetl... |
222 223 224 225 |
/* Does not use PTR_ERR to limit the number of error codes that can be * returned by nf_queue. For instance, callers rely on -ECANCELED to mean * 'ignore this hook'. */ |
801678c5a Remove duplicated... |
226 |
if (IS_ERR(segs)) |
f15850861 netfilter: nfnetl... |
227 |
return -EINVAL; |
394f545db [NETFILTER]: nf_q... |
228 |
|
f15850861 netfilter: nfnetl... |
229 230 |
queued = 0; err = 0; |
394f545db [NETFILTER]: nf_q... |
231 232 233 234 |
do { struct sk_buff *nskb = segs->next; segs->next = NULL; |
f15850861 netfilter: nfnetl... |
235 236 237 238 239 240 |
if (err == 0) err = __nf_queue(segs, elem, pf, hook, indev, outdev, okfn, queuenum); if (err == 0) queued++; else |
394f545db [NETFILTER]: nf_q... |
241 242 243 |
kfree_skb(segs); segs = nskb; } while (segs); |
f15850861 netfilter: nfnetl... |
244 |
|
06cdb6349 netfilter: nfnetl... |
245 |
/* also free orig skb if only some segments were queued */ |
f15850861 netfilter: nfnetl... |
246 247 |
if (unlikely(err && queued)) err = 0; |
06cdb6349 netfilter: nfnetl... |
248 249 |
if (err == 0) kfree_skb(skb); |
f15850861 netfilter: nfnetl... |
250 |
return err; |
394f545db [NETFILTER]: nf_q... |
251 |
} |
02f014d88 [NETFILTER]: nf_q... |
252 |
void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) |
f6ebe77f9 [NETFILTER]: spli... |
253 |
{ |
02f014d88 [NETFILTER]: nf_q... |
254 255 |
struct sk_buff *skb = entry->skb; struct list_head *elem = &entry->elem->list; |
1e796fda0 [NETFILTER]: cons... |
256 |
const struct nf_afinfo *afinfo; |
f15850861 netfilter: nfnetl... |
257 |
int err; |
f6ebe77f9 [NETFILTER]: spli... |
258 259 |
rcu_read_lock(); |
daaa8be2e [NETFILTER]: nf_q... |
260 |
nf_queue_entry_release_refs(entry); |
f6ebe77f9 [NETFILTER]: spli... |
261 |
|
f6ebe77f9 [NETFILTER]: spli... |
262 263 264 265 266 267 268 |
/* Continue traversal iff userspace said ok... */ if (verdict == NF_REPEAT) { elem = elem->prev; verdict = NF_ACCEPT; } if (verdict == NF_ACCEPT) { |
02f014d88 [NETFILTER]: nf_q... |
269 270 |
afinfo = nf_get_afinfo(entry->pf); if (!afinfo || afinfo->reroute(skb, entry) < 0) |
7a11b9848 [NETFILTER]: nf_q... |
271 272 273 274 |
verdict = NF_DROP; } if (verdict == NF_ACCEPT) { |
f6ebe77f9 [NETFILTER]: spli... |
275 |
next_hook: |
02f014d88 [NETFILTER]: nf_q... |
276 277 278 279 |
verdict = nf_iterate(&nf_hooks[entry->pf][entry->hook], skb, entry->hook, entry->indev, entry->outdev, &elem, entry->okfn, INT_MIN); |
f6ebe77f9 [NETFILTER]: spli... |
280 281 282 283 |
} switch (verdict & NF_VERDICT_MASK) { case NF_ACCEPT: |
3bc38712e [NETFILTER]: nf_q... |
284 |
case NF_STOP: |
4b3d15ef4 [NETFILTER]: {nfn... |
285 |
local_bh_disable(); |
02f014d88 [NETFILTER]: nf_q... |
286 |
entry->okfn(skb); |
4b3d15ef4 [NETFILTER]: {nfn... |
287 |
local_bh_enable(); |
f6ebe77f9 [NETFILTER]: spli... |
288 |
break; |
f6ebe77f9 [NETFILTER]: spli... |
289 |
case NF_QUEUE: |
f15850861 netfilter: nfnetl... |
290 291 |
err = __nf_queue(skb, elem, entry->pf, entry->hook, entry->indev, entry->outdev, entry->okfn, |
f615df76e netfilter: reduce... |
292 |
verdict >> NF_VERDICT_QBITS); |
06cdb6349 netfilter: nfnetl... |
293 294 295 |
if (err < 0) { if (err == -ECANCELED) goto next_hook; |
94b27cc36 netfilter: allow ... |
296 297 298 |
if (err == -ESRCH && (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) goto next_hook; |
06cdb6349 netfilter: nfnetl... |
299 300 |
kfree_skb(skb); } |
f6ebe77f9 [NETFILTER]: spli... |
301 |
break; |
64507fdbc netfilter: nf_que... |
302 |
case NF_STOLEN: |
fad544404 netfilter: avoid ... |
303 |
break; |
3bc38712e [NETFILTER]: nf_q... |
304 305 |
default: kfree_skb(skb); |
f6ebe77f9 [NETFILTER]: spli... |
306 307 |
} rcu_read_unlock(); |
02f014d88 [NETFILTER]: nf_q... |
308 |
kfree(entry); |
f6ebe77f9 [NETFILTER]: spli... |
309 310 |
} EXPORT_SYMBOL(nf_reinject); |
bbd86b9fc [NETFILTER]: add ... |
311 312 313 |
#ifdef CONFIG_PROC_FS static void *seq_start(struct seq_file *seq, loff_t *pos) { |
7e9c6eeb1 netfilter: Introd... |
314 |
if (*pos >= ARRAY_SIZE(queue_handler)) |
bbd86b9fc [NETFILTER]: add ... |
315 316 317 318 319 320 321 322 |
return NULL; return pos; } static void *seq_next(struct seq_file *s, void *v, loff_t *pos) { (*pos)++; |
7e9c6eeb1 netfilter: Introd... |
323 |
if (*pos >= ARRAY_SIZE(queue_handler)) |
bbd86b9fc [NETFILTER]: add ... |
324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
return NULL; return pos; } static void seq_stop(struct seq_file *s, void *v) { } static int seq_show(struct seq_file *s, void *v) { int ret; loff_t *pos = v; |
e3ac52981 [NETFILTER]: nf_q... |
338 |
const struct nf_queue_handler *qh; |
bbd86b9fc [NETFILTER]: add ... |
339 |
|
585426fdc [NETFILTER]: nf_q... |
340 341 |
rcu_read_lock(); qh = rcu_dereference(queue_handler[*pos]); |
bbd86b9fc [NETFILTER]: add ... |
342 343 344 345 346 347 |
if (!qh) ret = seq_printf(s, "%2lld NONE ", *pos); else ret = seq_printf(s, "%2lld %s ", *pos, qh->name); |
585426fdc [NETFILTER]: nf_q... |
348 |
rcu_read_unlock(); |
bbd86b9fc [NETFILTER]: add ... |
349 350 351 |
return ret; } |
56b3d975b [NET]: Make all i... |
352 |
static const struct seq_operations nfqueue_seq_ops = { |
bbd86b9fc [NETFILTER]: add ... |
353 354 355 356 357 358 359 360 361 362 |
.start = seq_start, .next = seq_next, .stop = seq_stop, .show = seq_show, }; static int nfqueue_open(struct inode *inode, struct file *file) { return seq_open(file, &nfqueue_seq_ops); } |
da7071d7e [PATCH] mark stru... |
363 |
static const struct file_operations nfqueue_file_ops = { |
bbd86b9fc [NETFILTER]: add ... |
364 365 366 367 368 369 370 |
.owner = THIS_MODULE, .open = nfqueue_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; #endif /* PROC_FS */ |
f6ebe77f9 [NETFILTER]: spli... |
371 372 |
int __init netfilter_queue_init(void) { |
bbd86b9fc [NETFILTER]: add ... |
373 |
#ifdef CONFIG_PROC_FS |
8eeee8b15 [NETFILTER]: Repl... |
374 375 |
if (!proc_create("nf_queue", S_IRUGO, proc_net_netfilter, &nfqueue_file_ops)) |
bbd86b9fc [NETFILTER]: add ... |
376 |
return -1; |
bbd86b9fc [NETFILTER]: add ... |
377 |
#endif |
f6ebe77f9 [NETFILTER]: spli... |
378 379 |
return 0; } |