1
2
3
4
5
6
7
8
9
10#include <linux/types.h>
11#include <linux/netfilter.h>
12#include <linux/module.h>
13#include <linux/skbuff.h>
14#include <linux/vmalloc.h>
15#include <linux/stddef.h>
16#include <linux/random.h>
17#include <linux/err.h>
18#include <linux/kernel.h>
19#include <linux/netdevice.h>
20#include <linux/rculist.h>
21#include <linux/rtnetlink.h>
22
23#include <net/netfilter/nf_conntrack.h>
24#include <net/netfilter/nf_conntrack_core.h>
25#include <net/netfilter/nf_conntrack_ecache.h>
26#include <net/netfilter/nf_conntrack_extend.h>
27#include <net/netfilter/nf_conntrack_helper.h>
28#include <net/netfilter/nf_conntrack_l4proto.h>
29#include <net/netfilter/nf_log.h>
30
31static DEFINE_MUTEX(nf_ct_helper_mutex);
32struct hlist_head *nf_ct_helper_hash __read_mostly;
33EXPORT_SYMBOL_GPL(nf_ct_helper_hash);
34unsigned int nf_ct_helper_hsize __read_mostly;
35EXPORT_SYMBOL_GPL(nf_ct_helper_hsize);
36static unsigned int nf_ct_helper_count __read_mostly;
37
38static bool nf_ct_auto_assign_helper __read_mostly = false;
39module_param_named(nf_conntrack_helper, nf_ct_auto_assign_helper, bool, 0644);
40MODULE_PARM_DESC(nf_conntrack_helper,
41 "Enable automatic conntrack helper assignment (default 0)");
42
43static DEFINE_MUTEX(nf_ct_nat_helpers_mutex);
44static struct list_head nf_ct_nat_helpers __read_mostly;
45
46extern unsigned int nf_conntrack_net_id;
47
48
49
50static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple)
51{
52 return (((tuple->src.l3num << 8) | tuple->dst.protonum) ^
53 (__force __u16)tuple->src.u.all) % nf_ct_helper_hsize;
54}
55
56static struct nf_conntrack_helper *
57__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
58{
59 struct nf_conntrack_helper *helper;
60 struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
61 unsigned int h;
62
63 if (!nf_ct_helper_count)
64 return NULL;
65
66 h = helper_hash(tuple);
67 hlist_for_each_entry_rcu(helper, &nf_ct_helper_hash[h], hnode) {
68 if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))
69 return helper;
70 }
71 return NULL;
72}
73
74struct nf_conntrack_helper *
75__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum)
76{
77 struct nf_conntrack_helper *h;
78 unsigned int i;
79
80 for (i = 0; i < nf_ct_helper_hsize; i++) {
81 hlist_for_each_entry_rcu(h, &nf_ct_helper_hash[i], hnode) {
82 if (strcmp(h->name, name))
83 continue;
84
85 if (h->tuple.src.l3num != NFPROTO_UNSPEC &&
86 h->tuple.src.l3num != l3num)
87 continue;
88
89 if (h->tuple.dst.protonum == protonum)
90 return h;
91 }
92 }
93 return NULL;
94}
95EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find);
96
97struct nf_conntrack_helper *
98nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
99{
100 struct nf_conntrack_helper *h;
101
102 rcu_read_lock();
103
104 h = __nf_conntrack_helper_find(name, l3num, protonum);
105#ifdef CONFIG_MODULES
106 if (h == NULL) {
107 rcu_read_unlock();
108 if (request_module("nfct-helper-%s", name) == 0) {
109 rcu_read_lock();
110 h = __nf_conntrack_helper_find(name, l3num, protonum);
111 } else {
112 return h;
113 }
114 }
115#endif
116 if (h != NULL && !try_module_get(h->me))
117 h = NULL;
118 if (h != NULL && !refcount_inc_not_zero(&h->refcnt)) {
119 module_put(h->me);
120 h = NULL;
121 }
122
123 rcu_read_unlock();
124
125 return h;
126}
127EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get);
128
129void nf_conntrack_helper_put(struct nf_conntrack_helper *helper)
130{
131 refcount_dec(&helper->refcnt);
132 module_put(helper->me);
133}
134EXPORT_SYMBOL_GPL(nf_conntrack_helper_put);
135
136static struct nf_conntrack_nat_helper *
137nf_conntrack_nat_helper_find(const char *mod_name)
138{
139 struct nf_conntrack_nat_helper *cur;
140 bool found = false;
141
142 list_for_each_entry_rcu(cur, &nf_ct_nat_helpers, list) {
143 if (!strcmp(cur->mod_name, mod_name)) {
144 found = true;
145 break;
146 }
147 }
148 return found ? cur : NULL;
149}
150
151int
152nf_nat_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
153{
154 struct nf_conntrack_helper *h;
155 struct nf_conntrack_nat_helper *nat;
156 char mod_name[NF_CT_HELPER_NAME_LEN];
157 int ret = 0;
158
159 rcu_read_lock();
160 h = __nf_conntrack_helper_find(name, l3num, protonum);
161 if (!h) {
162 rcu_read_unlock();
163 return -ENOENT;
164 }
165
166 nat = nf_conntrack_nat_helper_find(h->nat_mod_name);
167 if (!nat) {
168 snprintf(mod_name, sizeof(mod_name), "%s", h->nat_mod_name);
169 rcu_read_unlock();
170 request_module(mod_name);
171
172 rcu_read_lock();
173 nat = nf_conntrack_nat_helper_find(mod_name);
174 if (!nat) {
175 rcu_read_unlock();
176 return -ENOENT;
177 }
178 }
179
180 if (!try_module_get(nat->module))
181 ret = -ENOENT;
182
183 rcu_read_unlock();
184 return ret;
185}
186EXPORT_SYMBOL_GPL(nf_nat_helper_try_module_get);
187
188void nf_nat_helper_put(struct nf_conntrack_helper *helper)
189{
190 struct nf_conntrack_nat_helper *nat;
191
192 nat = nf_conntrack_nat_helper_find(helper->nat_mod_name);
193 if (WARN_ON_ONCE(!nat))
194 return;
195
196 module_put(nat->module);
197}
198EXPORT_SYMBOL_GPL(nf_nat_helper_put);
199
200struct nf_conn_help *
201nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp)
202{
203 struct nf_conn_help *help;
204
205 help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp);
206 if (help)
207 INIT_HLIST_HEAD(&help->expectations);
208 else
209 pr_debug("failed to add helper extension area");
210 return help;
211}
212EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);
213
214static struct nf_conntrack_helper *
215nf_ct_lookup_helper(struct nf_conn *ct, struct net *net)
216{
217 struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
218
219 if (!cnet->sysctl_auto_assign_helper) {
220 if (cnet->auto_assign_helper_warned)
221 return NULL;
222 if (!__nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple))
223 return NULL;
224 pr_info("nf_conntrack: default automatic helper assignment "
225 "has been turned off for security reasons and CT-based "
226 "firewall rule not found. Use the iptables CT target "
227 "to attach helpers instead.\n");
228 cnet->auto_assign_helper_warned = true;
229 return NULL;
230 }
231
232 return __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
233}
234
235int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
236 gfp_t flags)
237{
238 struct nf_conntrack_helper *helper = NULL;
239 struct nf_conn_help *help;
240 struct net *net = nf_ct_net(ct);
241
242
243
244
245
246
247
248 if (test_bit(IPS_HELPER_BIT, &ct->status))
249 return 0;
250
251 if (tmpl != NULL) {
252 help = nfct_help(tmpl);
253 if (help != NULL) {
254 helper = help->helper;
255 set_bit(IPS_HELPER_BIT, &ct->status);
256 }
257 }
258
259 help = nfct_help(ct);
260
261 if (helper == NULL) {
262 helper = nf_ct_lookup_helper(ct, net);
263 if (helper == NULL) {
264 if (help)
265 RCU_INIT_POINTER(help->helper, NULL);
266 return 0;
267 }
268 }
269
270 if (help == NULL) {
271 help = nf_ct_helper_ext_add(ct, flags);
272 if (help == NULL)
273 return -ENOMEM;
274 } else {
275
276
277
278 struct nf_conntrack_helper *tmp = rcu_dereference(help->helper);
279
280 if (tmp && tmp->help != helper->help) {
281 RCU_INIT_POINTER(help->helper, NULL);
282 return 0;
283 }
284 }
285
286 rcu_assign_pointer(help->helper, helper);
287
288 return 0;
289}
290EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);
291
292
293static int unhelp(struct nf_conn *ct, void *me)
294{
295 struct nf_conn_help *help = nfct_help(ct);
296
297 if (help && rcu_dereference_raw(help->helper) == me) {
298 nf_conntrack_event(IPCT_HELPER, ct);
299 RCU_INIT_POINTER(help->helper, NULL);
300 }
301
302
303 return 0;
304}
305
306void nf_ct_helper_destroy(struct nf_conn *ct)
307{
308 struct nf_conn_help *help = nfct_help(ct);
309 struct nf_conntrack_helper *helper;
310
311 if (help) {
312 rcu_read_lock();
313 helper = rcu_dereference(help->helper);
314 if (helper && helper->destroy)
315 helper->destroy(ct);
316 rcu_read_unlock();
317 }
318}
319
320static LIST_HEAD(nf_ct_helper_expectfn_list);
321
322void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n)
323{
324 spin_lock_bh(&nf_conntrack_expect_lock);
325 list_add_rcu(&n->head, &nf_ct_helper_expectfn_list);
326 spin_unlock_bh(&nf_conntrack_expect_lock);
327}
328EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_register);
329
330void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n)
331{
332 spin_lock_bh(&nf_conntrack_expect_lock);
333 list_del_rcu(&n->head);
334 spin_unlock_bh(&nf_conntrack_expect_lock);
335}
336EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister);
337
338
339struct nf_ct_helper_expectfn *
340nf_ct_helper_expectfn_find_by_name(const char *name)
341{
342 struct nf_ct_helper_expectfn *cur;
343 bool found = false;
344
345 list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
346 if (!strcmp(cur->name, name)) {
347 found = true;
348 break;
349 }
350 }
351 return found ? cur : NULL;
352}
353EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_name);
354
355
356struct nf_ct_helper_expectfn *
357nf_ct_helper_expectfn_find_by_symbol(const void *symbol)
358{
359 struct nf_ct_helper_expectfn *cur;
360 bool found = false;
361
362 list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
363 if (cur->expectfn == symbol) {
364 found = true;
365 break;
366 }
367 }
368 return found ? cur : NULL;
369}
370EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol);
371
372__printf(3, 4)
373void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
374 const char *fmt, ...)
375{
376 const struct nf_conn_help *help;
377 const struct nf_conntrack_helper *helper;
378 struct va_format vaf;
379 va_list args;
380
381 va_start(args, fmt);
382
383 vaf.fmt = fmt;
384 vaf.va = &args;
385
386
387 help = nfct_help(ct);
388
389
390 helper = rcu_dereference(help->helper);
391
392 nf_log_packet(nf_ct_net(ct), nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL,
393 "nf_ct_%s: dropping packet: %pV ", helper->name, &vaf);
394
395 va_end(args);
396}
397EXPORT_SYMBOL_GPL(nf_ct_helper_log);
398
399int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
400{
401 struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
402 unsigned int h = helper_hash(&me->tuple);
403 struct nf_conntrack_helper *cur;
404 int ret = 0, i;
405
406 BUG_ON(me->expect_policy == NULL);
407 BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
408 BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
409
410 if (me->expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
411 return -EINVAL;
412
413 mutex_lock(&nf_ct_helper_mutex);
414 for (i = 0; i < nf_ct_helper_hsize; i++) {
415 hlist_for_each_entry(cur, &nf_ct_helper_hash[i], hnode) {
416 if (!strcmp(cur->name, me->name) &&
417 (cur->tuple.src.l3num == NFPROTO_UNSPEC ||
418 cur->tuple.src.l3num == me->tuple.src.l3num) &&
419 cur->tuple.dst.protonum == me->tuple.dst.protonum) {
420 ret = -EEXIST;
421 goto out;
422 }
423 }
424 }
425
426
427 if (!(me->flags & NF_CT_HELPER_F_USERSPACE)) {
428 hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
429 if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple,
430 &mask)) {
431 ret = -EEXIST;
432 goto out;
433 }
434 }
435 }
436 refcount_set(&me->refcnt, 1);
437 hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
438 nf_ct_helper_count++;
439out:
440 mutex_unlock(&nf_ct_helper_mutex);
441 return ret;
442}
443EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
444
445static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data)
446{
447 struct nf_conn_help *help = nfct_help(exp->master);
448 const struct nf_conntrack_helper *me = data;
449 const struct nf_conntrack_helper *this;
450
451 if (exp->helper == me)
452 return true;
453
454 this = rcu_dereference_protected(help->helper,
455 lockdep_is_held(&nf_conntrack_expect_lock));
456 return this == me;
457}
458
459void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
460{
461 mutex_lock(&nf_ct_helper_mutex);
462 hlist_del_rcu(&me->hnode);
463 nf_ct_helper_count--;
464 mutex_unlock(&nf_ct_helper_mutex);
465
466
467
468
469 synchronize_rcu();
470
471 nf_ct_expect_iterate_destroy(expect_iter_me, NULL);
472 nf_ct_iterate_destroy(unhelp, me);
473
474
475
476
477 synchronize_rcu();
478}
479EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
480
481void nf_ct_helper_init(struct nf_conntrack_helper *helper,
482 u16 l3num, u16 protonum, const char *name,
483 u16 default_port, u16 spec_port, u32 id,
484 const struct nf_conntrack_expect_policy *exp_pol,
485 u32 expect_class_max,
486 int (*help)(struct sk_buff *skb, unsigned int protoff,
487 struct nf_conn *ct,
488 enum ip_conntrack_info ctinfo),
489 int (*from_nlattr)(struct nlattr *attr,
490 struct nf_conn *ct),
491 struct module *module)
492{
493 helper->tuple.src.l3num = l3num;
494 helper->tuple.dst.protonum = protonum;
495 helper->tuple.src.u.all = htons(spec_port);
496 helper->expect_policy = exp_pol;
497 helper->expect_class_max = expect_class_max;
498 helper->help = help;
499 helper->from_nlattr = from_nlattr;
500 helper->me = module;
501 snprintf(helper->nat_mod_name, sizeof(helper->nat_mod_name),
502 NF_NAT_HELPER_PREFIX "%s", name);
503
504 if (spec_port == default_port)
505 snprintf(helper->name, sizeof(helper->name), "%s", name);
506 else
507 snprintf(helper->name, sizeof(helper->name), "%s-%u", name, id);
508}
509EXPORT_SYMBOL_GPL(nf_ct_helper_init);
510
511int nf_conntrack_helpers_register(struct nf_conntrack_helper *helper,
512 unsigned int n)
513{
514 unsigned int i;
515 int err = 0;
516
517 for (i = 0; i < n; i++) {
518 err = nf_conntrack_helper_register(&helper[i]);
519 if (err < 0)
520 goto err;
521 }
522
523 return err;
524err:
525 if (i > 0)
526 nf_conntrack_helpers_unregister(helper, i);
527 return err;
528}
529EXPORT_SYMBOL_GPL(nf_conntrack_helpers_register);
530
531void nf_conntrack_helpers_unregister(struct nf_conntrack_helper *helper,
532 unsigned int n)
533{
534 while (n-- > 0)
535 nf_conntrack_helper_unregister(&helper[n]);
536}
537EXPORT_SYMBOL_GPL(nf_conntrack_helpers_unregister);
538
539void nf_nat_helper_register(struct nf_conntrack_nat_helper *nat)
540{
541 mutex_lock(&nf_ct_nat_helpers_mutex);
542 list_add_rcu(&nat->list, &nf_ct_nat_helpers);
543 mutex_unlock(&nf_ct_nat_helpers_mutex);
544}
545EXPORT_SYMBOL_GPL(nf_nat_helper_register);
546
547void nf_nat_helper_unregister(struct nf_conntrack_nat_helper *nat)
548{
549 mutex_lock(&nf_ct_nat_helpers_mutex);
550 list_del_rcu(&nat->list);
551 mutex_unlock(&nf_ct_nat_helpers_mutex);
552}
553EXPORT_SYMBOL_GPL(nf_nat_helper_unregister);
554
555static const struct nf_ct_ext_type helper_extend = {
556 .len = sizeof(struct nf_conn_help),
557 .align = __alignof__(struct nf_conn_help),
558 .id = NF_CT_EXT_HELPER,
559};
560
561void nf_conntrack_helper_pernet_init(struct net *net)
562{
563 struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
564
565 cnet->sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
566}
567
568int nf_conntrack_helper_init(void)
569{
570 int ret;
571 nf_ct_helper_hsize = 1;
572 nf_ct_helper_hash =
573 nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0);
574 if (!nf_ct_helper_hash)
575 return -ENOMEM;
576
577 ret = nf_ct_extend_register(&helper_extend);
578 if (ret < 0) {
579 pr_err("nf_ct_helper: Unable to register helper extension.\n");
580 goto out_extend;
581 }
582
583 INIT_LIST_HEAD(&nf_ct_nat_helpers);
584 return 0;
585out_extend:
586 kvfree(nf_ct_helper_hash);
587 return ret;
588}
589
590void nf_conntrack_helper_fini(void)
591{
592 nf_ct_extend_unregister(&helper_extend);
593 kvfree(nf_ct_helper_hash);
594}
595