Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
nf_conntrack_helper.c
Go to the documentation of this file.
1 /* Helper handling for netfilter. */
2 
3 /* (C) 1999-2001 Paul `Rusty' Russell
4  * (C) 2002-2006 Netfilter Core Team <[email protected]>
5  * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11 
12 #include <linux/types.h>
13 #include <linux/netfilter.h>
14 #include <linux/module.h>
15 #include <linux/skbuff.h>
16 #include <linux/vmalloc.h>
17 #include <linux/stddef.h>
18 #include <linux/random.h>
19 #include <linux/err.h>
20 #include <linux/kernel.h>
21 #include <linux/netdevice.h>
22 #include <linux/rculist.h>
23 #include <linux/rtnetlink.h>
24 
31 
32 static DEFINE_MUTEX(nf_ct_helper_mutex);
34 EXPORT_SYMBOL_GPL(nf_ct_helper_hash);
36 EXPORT_SYMBOL_GPL(nf_ct_helper_hsize);
37 static unsigned int nf_ct_helper_count __read_mostly;
38 
39 static bool nf_ct_auto_assign_helper __read_mostly = true;
40 module_param_named(nf_conntrack_helper, nf_ct_auto_assign_helper, bool, 0644);
42  "Enable automatic conntrack helper assignment (default 1)");
43 
44 #ifdef CONFIG_SYSCTL
45 static struct ctl_table helper_sysctl_table[] = {
46  {
47  .procname = "nf_conntrack_helper",
48  .data = &init_net.ct.sysctl_auto_assign_helper,
49  .maxlen = sizeof(unsigned int),
50  .mode = 0644,
52  },
53  {}
54 };
55 
56 static int nf_conntrack_helper_init_sysctl(struct net *net)
57 {
58  struct ctl_table *table;
59 
60  table = kmemdup(helper_sysctl_table, sizeof(helper_sysctl_table),
61  GFP_KERNEL);
62  if (!table)
63  goto out;
64 
65  table[0].data = &net->ct.sysctl_auto_assign_helper;
66 
67  net->ct.helper_sysctl_header =
68  register_net_sysctl(net, "net/netfilter", table);
69 
70  if (!net->ct.helper_sysctl_header) {
71  pr_err("nf_conntrack_helper: can't register to sysctl.\n");
72  goto out_register;
73  }
74  return 0;
75 
76 out_register:
77  kfree(table);
78 out:
79  return -ENOMEM;
80 }
81 
82 static void nf_conntrack_helper_fini_sysctl(struct net *net)
83 {
84  struct ctl_table *table;
85 
86  table = net->ct.helper_sysctl_header->ctl_table_arg;
87  unregister_net_sysctl_table(net->ct.helper_sysctl_header);
88  kfree(table);
89 }
90 #else
91 static int nf_conntrack_helper_init_sysctl(struct net *net)
92 {
93  return 0;
94 }
95 
96 static void nf_conntrack_helper_fini_sysctl(struct net *net)
97 {
98 }
99 #endif /* CONFIG_SYSCTL */
100 
101 /* Stupid hash, but collision free for the default registrations of the
102  * helpers currently in the kernel. */
103 static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple)
104 {
105  return (((tuple->src.l3num << 8) | tuple->dst.protonum) ^
106  (__force __u16)tuple->src.u.all) % nf_ct_helper_hsize;
107 }
108 
109 static struct nf_conntrack_helper *
110 __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
111 {
112  struct nf_conntrack_helper *helper;
113  struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
114  struct hlist_node *n;
115  unsigned int h;
116 
117  if (!nf_ct_helper_count)
118  return NULL;
119 
120  h = helper_hash(tuple);
121  hlist_for_each_entry_rcu(helper, n, &nf_ct_helper_hash[h], hnode) {
122  if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))
123  return helper;
124  }
125  return NULL;
126 }
127 
128 struct nf_conntrack_helper *
129 __nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum)
130 {
131  struct nf_conntrack_helper *h;
132  struct hlist_node *n;
133  unsigned int i;
134 
135  for (i = 0; i < nf_ct_helper_hsize; i++) {
136  hlist_for_each_entry_rcu(h, n, &nf_ct_helper_hash[i], hnode) {
137  if (!strcmp(h->name, name) &&
138  h->tuple.src.l3num == l3num &&
139  h->tuple.dst.protonum == protonum)
140  return h;
141  }
142  }
143  return NULL;
144 }
146 
147 struct nf_conntrack_helper *
148 nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
149 {
150  struct nf_conntrack_helper *h;
151 
152  h = __nf_conntrack_helper_find(name, l3num, protonum);
153 #ifdef CONFIG_MODULES
154  if (h == NULL) {
155  if (request_module("nfct-helper-%s", name) == 0)
156  h = __nf_conntrack_helper_find(name, l3num, protonum);
157  }
158 #endif
159  if (h != NULL && !try_module_get(h->me))
160  h = NULL;
161 
162  return h;
163 }
165 
166 struct nf_conn_help *
168  struct nf_conntrack_helper *helper, gfp_t gfp)
169 {
170  struct nf_conn_help *help;
171 
173  helper->data_len, gfp);
174  if (help)
176  else
177  pr_debug("failed to add helper extension area");
178  return help;
179 }
181 
182 int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
183  gfp_t flags)
184 {
185  struct nf_conntrack_helper *helper = NULL;
186  struct nf_conn_help *help;
187  struct net *net = nf_ct_net(ct);
188  int ret = 0;
189 
190  /* We already got a helper explicitly attached. The function
191  * nf_conntrack_alter_reply - in case NAT is in use - asks for looking
192  * the helper up again. Since now the user is in full control of
193  * making consistent helper configurations, skip this automatic
194  * re-lookup, otherwise we'll lose the helper.
195  */
196  if (test_bit(IPS_HELPER_BIT, &ct->status))
197  return 0;
198 
199  if (tmpl != NULL) {
200  help = nfct_help(tmpl);
201  if (help != NULL) {
202  helper = help->helper;
204  }
205  }
206 
207  help = nfct_help(ct);
208  if (net->ct.sysctl_auto_assign_helper && helper == NULL) {
209  helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
210  if (unlikely(!net->ct.auto_assign_helper_warned && helper)) {
211  pr_info("nf_conntrack: automatic helper "
212  "assignment is deprecated and it will "
213  "be removed soon. Use the iptables CT target "
214  "to attach helpers instead.\n");
215  net->ct.auto_assign_helper_warned = true;
216  }
217  }
218 
219  if (helper == NULL) {
220  if (help)
221  RCU_INIT_POINTER(help->helper, NULL);
222  goto out;
223  }
224 
225  if (help == NULL) {
226  help = nf_ct_helper_ext_add(ct, helper, flags);
227  if (help == NULL) {
228  ret = -ENOMEM;
229  goto out;
230  }
231  } else {
232  /* We only allow helper re-assignment of the same sort since
233  * we cannot reallocate the helper extension area.
234  */
235  if (help->helper != helper) {
236  RCU_INIT_POINTER(help->helper, NULL);
237  goto out;
238  }
239  }
240 
241  rcu_assign_pointer(help->helper, helper);
242 out:
243  return ret;
244 }
246 
247 static inline int unhelp(struct nf_conntrack_tuple_hash *i,
248  const struct nf_conntrack_helper *me)
249 {
250  struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(i);
251  struct nf_conn_help *help = nfct_help(ct);
252 
253  if (help && rcu_dereference_protected(
254  help->helper,
255  lockdep_is_held(&nf_conntrack_lock)
256  ) == me) {
257  nf_conntrack_event(IPCT_HELPER, ct);
258  RCU_INIT_POINTER(help->helper, NULL);
259  }
260  return 0;
261 }
262 
264 {
265  struct nf_conn_help *help = nfct_help(ct);
266  struct nf_conntrack_helper *helper;
267 
268  if (help) {
269  rcu_read_lock();
270  helper = rcu_dereference(help->helper);
271  if (helper && helper->destroy)
272  helper->destroy(ct);
273  rcu_read_unlock();
274  }
275 }
276 
277 static LIST_HEAD(nf_ct_helper_expectfn_list);
278 
280 {
281  spin_lock_bh(&nf_conntrack_lock);
282  list_add_rcu(&n->head, &nf_ct_helper_expectfn_list);
283  spin_unlock_bh(&nf_conntrack_lock);
284 }
286 
288 {
289  spin_lock_bh(&nf_conntrack_lock);
290  list_del_rcu(&n->head);
291  spin_unlock_bh(&nf_conntrack_lock);
292 }
294 
295 struct nf_ct_helper_expectfn *
297 {
298  struct nf_ct_helper_expectfn *cur;
299  bool found = false;
300 
301  rcu_read_lock();
302  list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
303  if (!strcmp(cur->name, name)) {
304  found = true;
305  break;
306  }
307  }
308  rcu_read_unlock();
309  return found ? cur : NULL;
310 }
312 
313 struct nf_ct_helper_expectfn *
315 {
316  struct nf_ct_helper_expectfn *cur;
317  bool found = false;
318 
319  rcu_read_lock();
320  list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
321  if (cur->expectfn == symbol) {
322  found = true;
323  break;
324  }
325  }
326  rcu_read_unlock();
327  return found ? cur : NULL;
328 }
330 
332 {
333  int ret = 0;
334  struct nf_conntrack_helper *cur;
335  struct hlist_node *n;
336  unsigned int h = helper_hash(&me->tuple);
337 
338  BUG_ON(me->expect_policy == NULL);
341 
342  mutex_lock(&nf_ct_helper_mutex);
343  hlist_for_each_entry(cur, n, &nf_ct_helper_hash[h], hnode) {
344  if (strncmp(cur->name, me->name, NF_CT_HELPER_NAME_LEN) == 0 &&
345  cur->tuple.src.l3num == me->tuple.src.l3num &&
346  cur->tuple.dst.protonum == me->tuple.dst.protonum) {
347  ret = -EEXIST;
348  goto out;
349  }
350  }
351  hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
352  nf_ct_helper_count++;
353 out:
354  mutex_unlock(&nf_ct_helper_mutex);
355  return ret;
356 }
358 
359 static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me,
360  struct net *net)
361 {
362  struct nf_conntrack_tuple_hash *h;
363  struct nf_conntrack_expect *exp;
364  const struct hlist_node *n, *next;
365  const struct hlist_nulls_node *nn;
366  unsigned int i;
367 
368  /* Get rid of expectations */
369  for (i = 0; i < nf_ct_expect_hsize; i++) {
370  hlist_for_each_entry_safe(exp, n, next,
371  &net->ct.expect_hash[i], hnode) {
372  struct nf_conn_help *help = nfct_help(exp->master);
374  help->helper,
375  lockdep_is_held(&nf_conntrack_lock)
376  ) == me || exp->helper == me) &&
377  del_timer(&exp->timeout)) {
378  nf_ct_unlink_expect(exp);
379  nf_ct_expect_put(exp);
380  }
381  }
382  }
383 
384  /* Get rid of expecteds, set helpers to NULL. */
385  hlist_nulls_for_each_entry(h, nn, &net->ct.unconfirmed, hnnode)
386  unhelp(h, me);
387  for (i = 0; i < net->ct.htable_size; i++) {
388  hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode)
389  unhelp(h, me);
390  }
391 }
392 
394 {
395  struct net *net;
396 
397  mutex_lock(&nf_ct_helper_mutex);
398  hlist_del_rcu(&me->hnode);
399  nf_ct_helper_count--;
400  mutex_unlock(&nf_ct_helper_mutex);
401 
402  /* Make sure every nothing is still using the helper unless its a
403  * connection in the hash.
404  */
405  synchronize_rcu();
406 
407  rtnl_lock();
408  spin_lock_bh(&nf_conntrack_lock);
409  for_each_net(net)
410  __nf_conntrack_helper_unregister(me, net);
411  spin_unlock_bh(&nf_conntrack_lock);
412  rtnl_unlock();
413 }
415 
416 static struct nf_ct_ext_type helper_extend __read_mostly = {
417  .len = sizeof(struct nf_conn_help),
418  .align = __alignof__(struct nf_conn_help),
419  .id = NF_CT_EXT_HELPER,
420 };
421 
422 int nf_conntrack_helper_init(struct net *net)
423 {
424  int err;
425 
426  net->ct.auto_assign_helper_warned = false;
427  net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
428 
429  if (net_eq(net, &init_net)) {
430  nf_ct_helper_hsize = 1; /* gets rounded up to use one page */
433  if (!nf_ct_helper_hash)
434  return -ENOMEM;
435 
436  err = nf_ct_extend_register(&helper_extend);
437  if (err < 0)
438  goto err1;
439  }
440 
441  err = nf_conntrack_helper_init_sysctl(net);
442  if (err < 0)
443  goto out_sysctl;
444 
445  return 0;
446 
447 out_sysctl:
448  if (net_eq(net, &init_net))
449  nf_ct_extend_unregister(&helper_extend);
450 err1:
452  return err;
453 }
454 
455 void nf_conntrack_helper_fini(struct net *net)
456 {
457  nf_conntrack_helper_fini_sysctl(net);
458  if (net_eq(net, &init_net)) {
459  nf_ct_extend_unregister(&helper_extend);
461  }
462 }