nf_conntrack_helper.c revision a9006892643a8f4e885b692de0708bcb35a7d530
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 * 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 25#include <net/netfilter/nf_conntrack.h> 26#include <net/netfilter/nf_conntrack_l3proto.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 32static DEFINE_MUTEX(nf_ct_helper_mutex); 33static struct hlist_head *nf_ct_helper_hash __read_mostly; 34static unsigned int nf_ct_helper_hsize __read_mostly; 35static unsigned int nf_ct_helper_count __read_mostly; 36 37static bool nf_ct_auto_assign_helper __read_mostly = true; 38module_param_named(nf_conntrack_helper, nf_ct_auto_assign_helper, bool, 0644); 39MODULE_PARM_DESC(nf_conntrack_helper, 40 "Enable automatic conntrack helper assignment (default 1)"); 41 42#ifdef CONFIG_SYSCTL 43static struct ctl_table helper_sysctl_table[] = { 44 { 45 .procname = "nf_conntrack_helper", 46 .data = &init_net.ct.sysctl_auto_assign_helper, 47 .maxlen = sizeof(unsigned int), 48 .mode = 0644, 49 .proc_handler = proc_dointvec, 50 }, 51 {} 52}; 53 54static int nf_conntrack_helper_init_sysctl(struct net *net) 55{ 56 struct ctl_table *table; 57 58 table = kmemdup(helper_sysctl_table, sizeof(helper_sysctl_table), 59 GFP_KERNEL); 60 if (!table) 61 goto out; 62 63 table[0].data = &net->ct.sysctl_auto_assign_helper; 64 65 net->ct.helper_sysctl_header = 66 register_net_sysctl(net, "net/netfilter", table); 67 68 if (!net->ct.helper_sysctl_header) { 69 pr_err("nf_conntrack_helper: can't register to sysctl.\n"); 70 goto out_register; 71 } 72 return 0; 73 74out_register: 75 kfree(table); 76out: 77 return -ENOMEM; 78} 79 80static void nf_conntrack_helper_fini_sysctl(struct net *net) 81{ 82 struct ctl_table *table; 83 84 table = net->ct.helper_sysctl_header->ctl_table_arg; 85 unregister_net_sysctl_table(net->ct.helper_sysctl_header); 86 kfree(table); 87} 88#else 89static int nf_conntrack_helper_init_sysctl(struct net *net) 90{ 91 return 0; 92} 93 94static void nf_conntrack_helper_fini_sysctl(struct net *net) 95{ 96} 97#endif /* CONFIG_SYSCTL */ 98 99/* Stupid hash, but collision free for the default registrations of the 100 * helpers currently in the kernel. */ 101static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple) 102{ 103 return (((tuple->src.l3num << 8) | tuple->dst.protonum) ^ 104 (__force __u16)tuple->src.u.all) % nf_ct_helper_hsize; 105} 106 107static struct nf_conntrack_helper * 108__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple) 109{ 110 struct nf_conntrack_helper *helper; 111 struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) }; 112 struct hlist_node *n; 113 unsigned int h; 114 115 if (!nf_ct_helper_count) 116 return NULL; 117 118 h = helper_hash(tuple); 119 hlist_for_each_entry_rcu(helper, n, &nf_ct_helper_hash[h], hnode) { 120 if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask)) 121 return helper; 122 } 123 return NULL; 124} 125 126struct nf_conntrack_helper * 127__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum) 128{ 129 struct nf_conntrack_helper *h; 130 struct hlist_node *n; 131 unsigned int i; 132 133 for (i = 0; i < nf_ct_helper_hsize; i++) { 134 hlist_for_each_entry_rcu(h, n, &nf_ct_helper_hash[i], hnode) { 135 if (!strcmp(h->name, name) && 136 h->tuple.src.l3num == l3num && 137 h->tuple.dst.protonum == protonum) 138 return h; 139 } 140 } 141 return NULL; 142} 143EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find); 144 145struct nf_conntrack_helper * 146nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum) 147{ 148 struct nf_conntrack_helper *h; 149 150 h = __nf_conntrack_helper_find(name, l3num, protonum); 151#ifdef CONFIG_MODULES 152 if (h == NULL) { 153 if (request_module("nfct-helper-%s", name) == 0) 154 h = __nf_conntrack_helper_find(name, l3num, protonum); 155 } 156#endif 157 if (h != NULL && !try_module_get(h->me)) 158 h = NULL; 159 160 return h; 161} 162EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get); 163 164struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp) 165{ 166 struct nf_conn_help *help; 167 168 help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp); 169 if (help) 170 INIT_HLIST_HEAD(&help->expectations); 171 else 172 pr_debug("failed to add helper extension area"); 173 return help; 174} 175EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); 176 177int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, 178 gfp_t flags) 179{ 180 struct nf_conntrack_helper *helper = NULL; 181 struct nf_conn_help *help; 182 struct net *net = nf_ct_net(ct); 183 int ret = 0; 184 185 if (tmpl != NULL) { 186 help = nfct_help(tmpl); 187 if (help != NULL) 188 helper = help->helper; 189 } 190 191 help = nfct_help(ct); 192 if (net->ct.sysctl_auto_assign_helper && helper == NULL) { 193 helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); 194 if (unlikely(!net->ct.auto_assign_helper_warned && helper)) { 195 pr_info("nf_conntrack: automatic helper " 196 "assignment is deprecated and it will " 197 "be removed soon. Use the iptables CT target " 198 "to attach helpers instead.\n"); 199 net->ct.auto_assign_helper_warned = true; 200 } 201 } 202 203 if (helper == NULL) { 204 if (help) 205 RCU_INIT_POINTER(help->helper, NULL); 206 goto out; 207 } 208 209 if (help == NULL) { 210 help = nf_ct_helper_ext_add(ct, flags); 211 if (help == NULL) { 212 ret = -ENOMEM; 213 goto out; 214 } 215 } else { 216 memset(&help->help, 0, sizeof(help->help)); 217 } 218 219 rcu_assign_pointer(help->helper, helper); 220out: 221 return ret; 222} 223EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper); 224 225static inline int unhelp(struct nf_conntrack_tuple_hash *i, 226 const struct nf_conntrack_helper *me) 227{ 228 struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(i); 229 struct nf_conn_help *help = nfct_help(ct); 230 231 if (help && rcu_dereference_protected( 232 help->helper, 233 lockdep_is_held(&nf_conntrack_lock) 234 ) == me) { 235 nf_conntrack_event(IPCT_HELPER, ct); 236 RCU_INIT_POINTER(help->helper, NULL); 237 } 238 return 0; 239} 240 241void nf_ct_helper_destroy(struct nf_conn *ct) 242{ 243 struct nf_conn_help *help = nfct_help(ct); 244 struct nf_conntrack_helper *helper; 245 246 if (help) { 247 rcu_read_lock(); 248 helper = rcu_dereference(help->helper); 249 if (helper && helper->destroy) 250 helper->destroy(ct); 251 rcu_read_unlock(); 252 } 253} 254 255static LIST_HEAD(nf_ct_helper_expectfn_list); 256 257void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n) 258{ 259 spin_lock_bh(&nf_conntrack_lock); 260 list_add_rcu(&n->head, &nf_ct_helper_expectfn_list); 261 spin_unlock_bh(&nf_conntrack_lock); 262} 263EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_register); 264 265void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n) 266{ 267 spin_lock_bh(&nf_conntrack_lock); 268 list_del_rcu(&n->head); 269 spin_unlock_bh(&nf_conntrack_lock); 270} 271EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister); 272 273struct nf_ct_helper_expectfn * 274nf_ct_helper_expectfn_find_by_name(const char *name) 275{ 276 struct nf_ct_helper_expectfn *cur; 277 bool found = false; 278 279 rcu_read_lock(); 280 list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) { 281 if (!strcmp(cur->name, name)) { 282 found = true; 283 break; 284 } 285 } 286 rcu_read_unlock(); 287 return found ? cur : NULL; 288} 289EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_name); 290 291struct nf_ct_helper_expectfn * 292nf_ct_helper_expectfn_find_by_symbol(const void *symbol) 293{ 294 struct nf_ct_helper_expectfn *cur; 295 bool found = false; 296 297 rcu_read_lock(); 298 list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) { 299 if (cur->expectfn == symbol) { 300 found = true; 301 break; 302 } 303 } 304 rcu_read_unlock(); 305 return found ? cur : NULL; 306} 307EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol); 308 309int nf_conntrack_helper_register(struct nf_conntrack_helper *me) 310{ 311 unsigned int h = helper_hash(&me->tuple); 312 313 BUG_ON(me->expect_policy == NULL); 314 BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES); 315 BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1); 316 317 mutex_lock(&nf_ct_helper_mutex); 318 hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]); 319 nf_ct_helper_count++; 320 mutex_unlock(&nf_ct_helper_mutex); 321 322 return 0; 323} 324EXPORT_SYMBOL_GPL(nf_conntrack_helper_register); 325 326static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me, 327 struct net *net) 328{ 329 struct nf_conntrack_tuple_hash *h; 330 struct nf_conntrack_expect *exp; 331 const struct hlist_node *n, *next; 332 const struct hlist_nulls_node *nn; 333 unsigned int i; 334 335 /* Get rid of expectations */ 336 for (i = 0; i < nf_ct_expect_hsize; i++) { 337 hlist_for_each_entry_safe(exp, n, next, 338 &net->ct.expect_hash[i], hnode) { 339 struct nf_conn_help *help = nfct_help(exp->master); 340 if ((rcu_dereference_protected( 341 help->helper, 342 lockdep_is_held(&nf_conntrack_lock) 343 ) == me || exp->helper == me) && 344 del_timer(&exp->timeout)) { 345 nf_ct_unlink_expect(exp); 346 nf_ct_expect_put(exp); 347 } 348 } 349 } 350 351 /* Get rid of expecteds, set helpers to NULL. */ 352 hlist_nulls_for_each_entry(h, nn, &net->ct.unconfirmed, hnnode) 353 unhelp(h, me); 354 for (i = 0; i < net->ct.htable_size; i++) { 355 hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode) 356 unhelp(h, me); 357 } 358} 359 360void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) 361{ 362 struct net *net; 363 364 mutex_lock(&nf_ct_helper_mutex); 365 hlist_del_rcu(&me->hnode); 366 nf_ct_helper_count--; 367 mutex_unlock(&nf_ct_helper_mutex); 368 369 /* Make sure every nothing is still using the helper unless its a 370 * connection in the hash. 371 */ 372 synchronize_rcu(); 373 374 rtnl_lock(); 375 spin_lock_bh(&nf_conntrack_lock); 376 for_each_net(net) 377 __nf_conntrack_helper_unregister(me, net); 378 spin_unlock_bh(&nf_conntrack_lock); 379 rtnl_unlock(); 380} 381EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister); 382 383static struct nf_ct_ext_type helper_extend __read_mostly = { 384 .len = sizeof(struct nf_conn_help), 385 .align = __alignof__(struct nf_conn_help), 386 .id = NF_CT_EXT_HELPER, 387}; 388 389int nf_conntrack_helper_init(struct net *net) 390{ 391 int err; 392 393 net->ct.auto_assign_helper_warned = false; 394 net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper; 395 396 if (net_eq(net, &init_net)) { 397 nf_ct_helper_hsize = 1; /* gets rounded up to use one page */ 398 nf_ct_helper_hash = 399 nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0); 400 if (!nf_ct_helper_hash) 401 return -ENOMEM; 402 403 err = nf_ct_extend_register(&helper_extend); 404 if (err < 0) 405 goto err1; 406 } 407 408 err = nf_conntrack_helper_init_sysctl(net); 409 if (err < 0) 410 goto out_sysctl; 411 412 return 0; 413 414out_sysctl: 415 if (net_eq(net, &init_net)) 416 nf_ct_extend_unregister(&helper_extend); 417err1: 418 nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize); 419 return err; 420} 421 422void nf_conntrack_helper_fini(struct net *net) 423{ 424 nf_conntrack_helper_fini_sysctl(net); 425 if (net_eq(net, &init_net)) { 426 nf_ct_extend_unregister(&helper_extend); 427 nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize); 428 } 429} 430