~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/netfilter/nf_conntrack_helper.c

Version: ~ [ linux-5.5-rc6 ] ~ [ linux-5.4.11 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.95 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.164 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.209 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.209 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.81 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /* Helper handling for netfilter. */
  2 
  3 /* (C) 1999-2001 Paul `Rusty' Russell
  4  * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
  5  * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
  6  * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  7  *
  8  * This program is free software; you can redistribute it and/or modify
  9  * it under the terms of the GNU General Public License version 2 as
 10  * published by the Free Software Foundation.
 11  */
 12 
 13 #include <linux/types.h>
 14 #include <linux/netfilter.h>
 15 #include <linux/module.h>
 16 #include <linux/skbuff.h>
 17 #include <linux/vmalloc.h>
 18 #include <linux/stddef.h>
 19 #include <linux/random.h>
 20 #include <linux/err.h>
 21 #include <linux/kernel.h>
 22 #include <linux/netdevice.h>
 23 #include <linux/rculist.h>
 24 #include <linux/rtnetlink.h>
 25 
 26 #include <net/netfilter/nf_conntrack.h>
 27 #include <net/netfilter/nf_conntrack_l4proto.h>
 28 #include <net/netfilter/nf_conntrack_helper.h>
 29 #include <net/netfilter/nf_conntrack_core.h>
 30 #include <net/netfilter/nf_conntrack_extend.h>
 31 #include <net/netfilter/nf_log.h>
 32 
 33 static DEFINE_MUTEX(nf_ct_helper_mutex);
 34 struct hlist_head *nf_ct_helper_hash __read_mostly;
 35 EXPORT_SYMBOL_GPL(nf_ct_helper_hash);
 36 unsigned int nf_ct_helper_hsize __read_mostly;
 37 EXPORT_SYMBOL_GPL(nf_ct_helper_hsize);
 38 static unsigned int nf_ct_helper_count __read_mostly;
 39 
 40 static bool nf_ct_auto_assign_helper __read_mostly = false;
 41 module_param_named(nf_conntrack_helper, nf_ct_auto_assign_helper, bool, 0644);
 42 MODULE_PARM_DESC(nf_conntrack_helper,
 43                  "Enable automatic conntrack helper assignment (default 0)");
 44 
 45 #ifdef CONFIG_SYSCTL
 46 static struct ctl_table helper_sysctl_table[] = {
 47         {
 48                 .procname       = "nf_conntrack_helper",
 49                 .data           = &init_net.ct.sysctl_auto_assign_helper,
 50                 .maxlen         = sizeof(unsigned int),
 51                 .mode           = 0644,
 52                 .proc_handler   = proc_dointvec,
 53         },
 54         {}
 55 };
 56 
 57 static int nf_conntrack_helper_init_sysctl(struct net *net)
 58 {
 59         struct ctl_table *table;
 60 
 61         table = kmemdup(helper_sysctl_table, sizeof(helper_sysctl_table),
 62                         GFP_KERNEL);
 63         if (!table)
 64                 goto out;
 65 
 66         table[0].data = &net->ct.sysctl_auto_assign_helper;
 67 
 68         /* Don't export sysctls to unprivileged users */
 69         if (net->user_ns != &init_user_ns)
 70                 table[0].procname = NULL;
 71 
 72         net->ct.helper_sysctl_header =
 73                 register_net_sysctl(net, "net/netfilter", table);
 74 
 75         if (!net->ct.helper_sysctl_header) {
 76                 pr_err("nf_conntrack_helper: can't register to sysctl.\n");
 77                 goto out_register;
 78         }
 79         return 0;
 80 
 81 out_register:
 82         kfree(table);
 83 out:
 84         return -ENOMEM;
 85 }
 86 
 87 static void nf_conntrack_helper_fini_sysctl(struct net *net)
 88 {
 89         struct ctl_table *table;
 90 
 91         table = net->ct.helper_sysctl_header->ctl_table_arg;
 92         unregister_net_sysctl_table(net->ct.helper_sysctl_header);
 93         kfree(table);
 94 }
 95 #else
 96 static int nf_conntrack_helper_init_sysctl(struct net *net)
 97 {
 98         return 0;
 99 }
100 
101 static void nf_conntrack_helper_fini_sysctl(struct net *net)
102 {
103 }
104 #endif /* CONFIG_SYSCTL */
105 
106 /* Stupid hash, but collision free for the default registrations of the
107  * helpers currently in the kernel. */
108 static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple)
109 {
110         return (((tuple->src.l3num << 8) | tuple->dst.protonum) ^
111                 (__force __u16)tuple->src.u.all) % nf_ct_helper_hsize;
112 }
113 
114 static struct nf_conntrack_helper *
115 __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
116 {
117         struct nf_conntrack_helper *helper;
118         struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
119         unsigned int h;
120 
121         if (!nf_ct_helper_count)
122                 return NULL;
123 
124         h = helper_hash(tuple);
125         hlist_for_each_entry_rcu(helper, &nf_ct_helper_hash[h], hnode) {
126                 if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))
127                         return helper;
128         }
129         return NULL;
130 }
131 
132 struct nf_conntrack_helper *
133 __nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum)
134 {
135         struct nf_conntrack_helper *h;
136         unsigned int i;
137 
138         for (i = 0; i < nf_ct_helper_hsize; i++) {
139                 hlist_for_each_entry_rcu(h, &nf_ct_helper_hash[i], hnode) {
140                         if (strcmp(h->name, name))
141                                 continue;
142 
143                         if (h->tuple.src.l3num != NFPROTO_UNSPEC &&
144                             h->tuple.src.l3num != l3num)
145                                 continue;
146 
147                         if (h->tuple.dst.protonum == protonum)
148                                 return h;
149                 }
150         }
151         return NULL;
152 }
153 EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find);
154 
155 struct nf_conntrack_helper *
156 nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
157 {
158         struct nf_conntrack_helper *h;
159 
160         rcu_read_lock();
161 
162         h = __nf_conntrack_helper_find(name, l3num, protonum);
163 #ifdef CONFIG_MODULES
164         if (h == NULL) {
165                 rcu_read_unlock();
166                 if (request_module("nfct-helper-%s", name) == 0) {
167                         rcu_read_lock();
168                         h = __nf_conntrack_helper_find(name, l3num, protonum);
169                 } else {
170                         return h;
171                 }
172         }
173 #endif
174         if (h != NULL && !try_module_get(h->me))
175                 h = NULL;
176         if (h != NULL && !refcount_inc_not_zero(&h->refcnt)) {
177                 module_put(h->me);
178                 h = NULL;
179         }
180 
181         rcu_read_unlock();
182 
183         return h;
184 }
185 EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get);
186 
187 void nf_conntrack_helper_put(struct nf_conntrack_helper *helper)
188 {
189         refcount_dec(&helper->refcnt);
190         module_put(helper->me);
191 }
192 EXPORT_SYMBOL_GPL(nf_conntrack_helper_put);
193 
194 struct nf_conn_help *
195 nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp)
196 {
197         struct nf_conn_help *help;
198 
199         help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp);
200         if (help)
201                 INIT_HLIST_HEAD(&help->expectations);
202         else
203                 pr_debug("failed to add helper extension area");
204         return help;
205 }
206 EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);
207 
208 static struct nf_conntrack_helper *
209 nf_ct_lookup_helper(struct nf_conn *ct, struct net *net)
210 {
211         if (!net->ct.sysctl_auto_assign_helper) {
212                 if (net->ct.auto_assign_helper_warned)
213                         return NULL;
214                 if (!__nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple))
215                         return NULL;
216                 pr_info("nf_conntrack: default automatic helper assignment "
217                         "has been turned off for security reasons and CT-based "
218                         " firewall rule not found. Use the iptables CT target "
219                         "to attach helpers instead.\n");
220                 net->ct.auto_assign_helper_warned = 1;
221                 return NULL;
222         }
223 
224         return __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
225 }
226 
227 
228 int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
229                               gfp_t flags)
230 {
231         struct nf_conntrack_helper *helper = NULL;
232         struct nf_conn_help *help;
233         struct net *net = nf_ct_net(ct);
234 
235         /* We already got a helper explicitly attached. The function
236          * nf_conntrack_alter_reply - in case NAT is in use - asks for looking
237          * the helper up again. Since now the user is in full control of
238          * making consistent helper configurations, skip this automatic
239          * re-lookup, otherwise we'll lose the helper.
240          */
241         if (test_bit(IPS_HELPER_BIT, &ct->status))
242                 return 0;
243 
244         if (tmpl != NULL) {
245                 help = nfct_help(tmpl);
246                 if (help != NULL) {
247                         helper = help->helper;
248                         set_bit(IPS_HELPER_BIT, &ct->status);
249                 }
250         }
251 
252         help = nfct_help(ct);
253 
254         if (helper == NULL) {
255                 helper = nf_ct_lookup_helper(ct, net);
256                 if (helper == NULL) {
257                         if (help)
258                                 RCU_INIT_POINTER(help->helper, NULL);
259                         return 0;
260                 }
261         }
262 
263         if (help == NULL) {
264                 help = nf_ct_helper_ext_add(ct, flags);
265                 if (help == NULL)
266                         return -ENOMEM;
267         } else {
268                 /* We only allow helper re-assignment of the same sort since
269                  * we cannot reallocate the helper extension area.
270                  */
271                 struct nf_conntrack_helper *tmp = rcu_dereference(help->helper);
272 
273                 if (tmp && tmp->help != helper->help) {
274                         RCU_INIT_POINTER(help->helper, NULL);
275                         return 0;
276                 }
277         }
278 
279         rcu_assign_pointer(help->helper, helper);
280 
281         return 0;
282 }
283 EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);
284 
285 /* appropriate ct lock protecting must be taken by caller */
286 static int unhelp(struct nf_conn *ct, void *me)
287 {
288         struct nf_conn_help *help = nfct_help(ct);
289 
290         if (help && rcu_dereference_raw(help->helper) == me) {
291                 nf_conntrack_event(IPCT_HELPER, ct);
292                 RCU_INIT_POINTER(help->helper, NULL);
293         }
294 
295         /* We are not intended to delete this conntrack. */
296         return 0;
297 }
298 
299 void nf_ct_helper_destroy(struct nf_conn *ct)
300 {
301         struct nf_conn_help *help = nfct_help(ct);
302         struct nf_conntrack_helper *helper;
303 
304         if (help) {
305                 rcu_read_lock();
306                 helper = rcu_dereference(help->helper);
307                 if (helper && helper->destroy)
308                         helper->destroy(ct);
309                 rcu_read_unlock();
310         }
311 }
312 
313 static LIST_HEAD(nf_ct_helper_expectfn_list);
314 
315 void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n)
316 {
317         spin_lock_bh(&nf_conntrack_expect_lock);
318         list_add_rcu(&n->head, &nf_ct_helper_expectfn_list);
319         spin_unlock_bh(&nf_conntrack_expect_lock);
320 }
321 EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_register);
322 
323 void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n)
324 {
325         spin_lock_bh(&nf_conntrack_expect_lock);
326         list_del_rcu(&n->head);
327         spin_unlock_bh(&nf_conntrack_expect_lock);
328 }
329 EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister);
330 
331 /* Caller should hold the rcu lock */
332 struct nf_ct_helper_expectfn *
333 nf_ct_helper_expectfn_find_by_name(const char *name)
334 {
335         struct nf_ct_helper_expectfn *cur;
336         bool found = false;
337 
338         list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
339                 if (!strcmp(cur->name, name)) {
340                         found = true;
341                         break;
342                 }
343         }
344         return found ? cur : NULL;
345 }
346 EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_name);
347 
348 /* Caller should hold the rcu lock */
349 struct nf_ct_helper_expectfn *
350 nf_ct_helper_expectfn_find_by_symbol(const void *symbol)
351 {
352         struct nf_ct_helper_expectfn *cur;
353         bool found = false;
354 
355         list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
356                 if (cur->expectfn == symbol) {
357                         found = true;
358                         break;
359                 }
360         }
361         return found ? cur : NULL;
362 }
363 EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol);
364 
365 __printf(3, 4)
366 void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
367                       const char *fmt, ...)
368 {
369         const struct nf_conn_help *help;
370         const struct nf_conntrack_helper *helper;
371         struct va_format vaf;
372         va_list args;
373 
374         va_start(args, fmt);
375 
376         vaf.fmt = fmt;
377         vaf.va = &args;
378 
379         /* Called from the helper function, this call never fails */
380         help = nfct_help(ct);
381 
382         /* rcu_read_lock()ed by nf_hook_thresh */
383         helper = rcu_dereference(help->helper);
384 
385         nf_log_packet(nf_ct_net(ct), nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL,
386                       "nf_ct_%s: dropping packet: %pV ", helper->name, &vaf);
387 
388         va_end(args);
389 }
390 EXPORT_SYMBOL_GPL(nf_ct_helper_log);
391 
392 int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
393 {
394         struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
395         unsigned int h = helper_hash(&me->tuple);
396         struct nf_conntrack_helper *cur;
397         int ret = 0, i;
398 
399         BUG_ON(me->expect_policy == NULL);
400         BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
401         BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
402 
403         if (me->expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
404                 return -EINVAL;
405 
406         mutex_lock(&nf_ct_helper_mutex);
407         for (i = 0; i < nf_ct_helper_hsize; i++) {
408                 hlist_for_each_entry(cur, &nf_ct_helper_hash[i], hnode) {
409                         if (!strcmp(cur->name, me->name) &&
410                             (cur->tuple.src.l3num == NFPROTO_UNSPEC ||
411                              cur->tuple.src.l3num == me->tuple.src.l3num) &&
412                             cur->tuple.dst.protonum == me->tuple.dst.protonum) {
413                                 ret = -EEXIST;
414                                 goto out;
415                         }
416                 }
417         }
418 
419         /* avoid unpredictable behaviour for auto_assign_helper */
420         if (!(me->flags & NF_CT_HELPER_F_USERSPACE)) {
421                 hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
422                         if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple,
423                                                      &mask)) {
424                                 ret = -EEXIST;
425                                 goto out;
426                         }
427                 }
428         }
429         refcount_set(&me->refcnt, 1);
430         hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
431         nf_ct_helper_count++;
432 out:
433         mutex_unlock(&nf_ct_helper_mutex);
434         return ret;
435 }
436 EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
437 
438 static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data)
439 {
440         struct nf_conn_help *help = nfct_help(exp->master);
441         const struct nf_conntrack_helper *me = data;
442         const struct nf_conntrack_helper *this;
443 
444         if (exp->helper == me)
445                 return true;
446 
447         this = rcu_dereference_protected(help->helper,
448                                          lockdep_is_held(&nf_conntrack_expect_lock));
449         return this == me;
450 }
451 
452 void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
453 {
454         mutex_lock(&nf_ct_helper_mutex);
455         hlist_del_rcu(&me->hnode);
456         nf_ct_helper_count--;
457         mutex_unlock(&nf_ct_helper_mutex);
458 
459         /* Make sure every nothing is still using the helper unless its a
460          * connection in the hash.
461          */
462         synchronize_rcu();
463 
464         nf_ct_expect_iterate_destroy(expect_iter_me, NULL);
465         nf_ct_iterate_destroy(unhelp, me);
466 
467         /* Maybe someone has gotten the helper already when unhelp above.
468          * So need to wait it.
469          */
470         synchronize_rcu();
471 }
472 EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
473 
474 void nf_ct_helper_init(struct nf_conntrack_helper *helper,
475                        u16 l3num, u16 protonum, const char *name,
476                        u16 default_port, u16 spec_port, u32 id,
477                        const struct nf_conntrack_expect_policy *exp_pol,
478                        u32 expect_class_max,
479                        int (*help)(struct sk_buff *skb, unsigned int protoff,
480                                    struct nf_conn *ct,
481                                    enum ip_conntrack_info ctinfo),
482                        int (*from_nlattr)(struct nlattr *attr,
483                                           struct nf_conn *ct),
484                        struct module *module)
485 {
486         helper->tuple.src.l3num = l3num;
487         helper->tuple.dst.protonum = protonum;
488         helper->tuple.src.u.all = htons(spec_port);
489         helper->expect_policy = exp_pol;
490         helper->expect_class_max = expect_class_max;
491         helper->help = help;
492         helper->from_nlattr = from_nlattr;
493         helper->me = module;
494 
495         if (spec_port == default_port)
496                 snprintf(helper->name, sizeof(helper->name), "%s", name);
497         else
498                 snprintf(helper->name, sizeof(helper->name), "%s-%u", name, id);
499 }
500 EXPORT_SYMBOL_GPL(nf_ct_helper_init);
501 
502 int nf_conntrack_helpers_register(struct nf_conntrack_helper *helper,
503                                   unsigned int n)
504 {
505         unsigned int i;
506         int err = 0;
507 
508         for (i = 0; i < n; i++) {
509                 err = nf_conntrack_helper_register(&helper[i]);
510                 if (err < 0)
511                         goto err;
512         }
513 
514         return err;
515 err:
516         if (i > 0)
517                 nf_conntrack_helpers_unregister(helper, i);
518         return err;
519 }
520 EXPORT_SYMBOL_GPL(nf_conntrack_helpers_register);
521 
522 void nf_conntrack_helpers_unregister(struct nf_conntrack_helper *helper,
523                                 unsigned int n)
524 {
525         while (n-- > 0)
526                 nf_conntrack_helper_unregister(&helper[n]);
527 }
528 EXPORT_SYMBOL_GPL(nf_conntrack_helpers_unregister);
529 
530 static const struct nf_ct_ext_type helper_extend = {
531         .len    = sizeof(struct nf_conn_help),
532         .align  = __alignof__(struct nf_conn_help),
533         .id     = NF_CT_EXT_HELPER,
534 };
535 
536 int nf_conntrack_helper_pernet_init(struct net *net)
537 {
538         net->ct.auto_assign_helper_warned = false;
539         net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
540         return nf_conntrack_helper_init_sysctl(net);
541 }
542 
543 void nf_conntrack_helper_pernet_fini(struct net *net)
544 {
545         nf_conntrack_helper_fini_sysctl(net);
546 }
547 
548 int nf_conntrack_helper_init(void)
549 {
550         int ret;
551         nf_ct_helper_hsize = 1; /* gets rounded up to use one page */
552         nf_ct_helper_hash =
553                 nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0);
554         if (!nf_ct_helper_hash)
555                 return -ENOMEM;
556 
557         ret = nf_ct_extend_register(&helper_extend);
558         if (ret < 0) {
559                 pr_err("nf_ct_helper: Unable to register helper extension.\n");
560                 goto out_extend;
561         }
562 
563         return 0;
564 out_extend:
565         kvfree(nf_ct_helper_hash);
566         return ret;
567 }
568 
569 void nf_conntrack_helper_fini(void)
570 {
571         nf_ct_extend_unregister(&helper_extend);
572         kvfree(nf_ct_helper_hash);
573 }
574 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp