nf_conntrack_helper.c revision f9caed59f801f77a2844ab04d2dea8df33ac862b
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#include <net/netfilter/nf_log.h> 32 33static DEFINE_MUTEX(nf_ct_helper_mutex); 34struct hlist_head *nf_ct_helper_hash __read_mostly; 35EXPORT_SYMBOL_GPL(nf_ct_helper_hash); 36unsigned int nf_ct_helper_hsize __read_mostly; 37EXPORT_SYMBOL_GPL(nf_ct_helper_hsize); 38static unsigned int nf_ct_helper_count __read_mostly; 39 40static bool nf_ct_auto_assign_helper __read_mostly = true; 41module_param_named(nf_conntrack_helper, nf_ct_auto_assign_helper, bool, 0644); 42MODULE_PARM_DESC(nf_conntrack_helper, 43 "Enable automatic conntrack helper assignment (default 1)"); 44 45#ifdef CONFIG_SYSCTL 46static 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 57static 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 81out_register: 82 kfree(table); 83out: 84 return -ENOMEM; 85} 86 87static 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 96static int nf_conntrack_helper_init_sysctl(struct net *net) 97{ 98 return 0; 99} 100 101static 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. */ 108static 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 114static 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 struct hlist_node *n; 120 unsigned int h; 121 122 if (!nf_ct_helper_count) 123 return NULL; 124 125 h = helper_hash(tuple); 126 hlist_for_each_entry_rcu(helper, n, &nf_ct_helper_hash[h], hnode) { 127 if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask)) 128 return helper; 129 } 130 return NULL; 131} 132 133struct nf_conntrack_helper * 134__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum) 135{ 136 struct nf_conntrack_helper *h; 137 struct hlist_node *n; 138 unsigned int i; 139 140 for (i = 0; i < nf_ct_helper_hsize; i++) { 141 hlist_for_each_entry_rcu(h, n, &nf_ct_helper_hash[i], hnode) { 142 if (!strcmp(h->name, name) && 143 h->tuple.src.l3num == l3num && 144 h->tuple.dst.protonum == protonum) 145 return h; 146 } 147 } 148 return NULL; 149} 150EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find); 151 152struct nf_conntrack_helper * 153nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum) 154{ 155 struct nf_conntrack_helper *h; 156 157 h = __nf_conntrack_helper_find(name, l3num, protonum); 158#ifdef CONFIG_MODULES 159 if (h == NULL) { 160 if (request_module("nfct-helper-%s", name) == 0) 161 h = __nf_conntrack_helper_find(name, l3num, protonum); 162 } 163#endif 164 if (h != NULL && !try_module_get(h->me)) 165 h = NULL; 166 167 return h; 168} 169EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get); 170 171struct nf_conn_help * 172nf_ct_helper_ext_add(struct nf_conn *ct, 173 struct nf_conntrack_helper *helper, gfp_t gfp) 174{ 175 struct nf_conn_help *help; 176 177 help = nf_ct_ext_add_length(ct, NF_CT_EXT_HELPER, 178 helper->data_len, gfp); 179 if (help) 180 INIT_HLIST_HEAD(&help->expectations); 181 else 182 pr_debug("failed to add helper extension area"); 183 return help; 184} 185EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); 186 187int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, 188 gfp_t flags) 189{ 190 struct nf_conntrack_helper *helper = NULL; 191 struct nf_conn_help *help; 192 struct net *net = nf_ct_net(ct); 193 int ret = 0; 194 195 /* We already got a helper explicitly attached. The function 196 * nf_conntrack_alter_reply - in case NAT is in use - asks for looking 197 * the helper up again. Since now the user is in full control of 198 * making consistent helper configurations, skip this automatic 199 * re-lookup, otherwise we'll lose the helper. 200 */ 201 if (test_bit(IPS_HELPER_BIT, &ct->status)) 202 return 0; 203 204 if (tmpl != NULL) { 205 help = nfct_help(tmpl); 206 if (help != NULL) { 207 helper = help->helper; 208 set_bit(IPS_HELPER_BIT, &ct->status); 209 } 210 } 211 212 help = nfct_help(ct); 213 if (net->ct.sysctl_auto_assign_helper && helper == NULL) { 214 helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); 215 if (unlikely(!net->ct.auto_assign_helper_warned && helper)) { 216 pr_info("nf_conntrack: automatic helper " 217 "assignment is deprecated and it will " 218 "be removed soon. Use the iptables CT target " 219 "to attach helpers instead.\n"); 220 net->ct.auto_assign_helper_warned = true; 221 } 222 } 223 224 if (helper == NULL) { 225 if (help) 226 RCU_INIT_POINTER(help->helper, NULL); 227 goto out; 228 } 229 230 if (help == NULL) { 231 help = nf_ct_helper_ext_add(ct, helper, flags); 232 if (help == NULL) { 233 ret = -ENOMEM; 234 goto out; 235 } 236 } else { 237 /* We only allow helper re-assignment of the same sort since 238 * we cannot reallocate the helper extension area. 239 */ 240 struct nf_conntrack_helper *tmp = rcu_dereference(help->helper); 241 242 if (tmp && tmp->help != helper->help) { 243 RCU_INIT_POINTER(help->helper, NULL); 244 goto out; 245 } 246 } 247 248 rcu_assign_pointer(help->helper, helper); 249out: 250 return ret; 251} 252EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper); 253 254static inline int unhelp(struct nf_conntrack_tuple_hash *i, 255 const struct nf_conntrack_helper *me) 256{ 257 struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(i); 258 struct nf_conn_help *help = nfct_help(ct); 259 260 if (help && rcu_dereference_protected( 261 help->helper, 262 lockdep_is_held(&nf_conntrack_lock) 263 ) == me) { 264 nf_conntrack_event(IPCT_HELPER, ct); 265 RCU_INIT_POINTER(help->helper, NULL); 266 } 267 return 0; 268} 269 270void nf_ct_helper_destroy(struct nf_conn *ct) 271{ 272 struct nf_conn_help *help = nfct_help(ct); 273 struct nf_conntrack_helper *helper; 274 275 if (help) { 276 rcu_read_lock(); 277 helper = rcu_dereference(help->helper); 278 if (helper && helper->destroy) 279 helper->destroy(ct); 280 rcu_read_unlock(); 281 } 282} 283 284static LIST_HEAD(nf_ct_helper_expectfn_list); 285 286void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n) 287{ 288 spin_lock_bh(&nf_conntrack_lock); 289 list_add_rcu(&n->head, &nf_ct_helper_expectfn_list); 290 spin_unlock_bh(&nf_conntrack_lock); 291} 292EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_register); 293 294void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n) 295{ 296 spin_lock_bh(&nf_conntrack_lock); 297 list_del_rcu(&n->head); 298 spin_unlock_bh(&nf_conntrack_lock); 299} 300EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister); 301 302struct nf_ct_helper_expectfn * 303nf_ct_helper_expectfn_find_by_name(const char *name) 304{ 305 struct nf_ct_helper_expectfn *cur; 306 bool found = false; 307 308 rcu_read_lock(); 309 list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) { 310 if (!strcmp(cur->name, name)) { 311 found = true; 312 break; 313 } 314 } 315 rcu_read_unlock(); 316 return found ? cur : NULL; 317} 318EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_name); 319 320struct nf_ct_helper_expectfn * 321nf_ct_helper_expectfn_find_by_symbol(const void *symbol) 322{ 323 struct nf_ct_helper_expectfn *cur; 324 bool found = false; 325 326 rcu_read_lock(); 327 list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) { 328 if (cur->expectfn == symbol) { 329 found = true; 330 break; 331 } 332 } 333 rcu_read_unlock(); 334 return found ? cur : NULL; 335} 336EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol); 337 338__printf(3, 4) 339void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct, 340 const char *fmt, ...) 341{ 342 const struct nf_conn_help *help; 343 const struct nf_conntrack_helper *helper; 344 struct va_format vaf; 345 va_list args; 346 347 va_start(args, fmt); 348 349 vaf.fmt = fmt; 350 vaf.va = &args; 351 352 /* Called from the helper function, this call never fails */ 353 help = nfct_help(ct); 354 355 /* rcu_read_lock()ed by nf_hook_slow */ 356 helper = rcu_dereference(help->helper); 357 358 nf_log_packet(nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL, 359 "nf_ct_%s: dropping packet: %pV ", helper->name, &vaf); 360 361 va_end(args); 362} 363EXPORT_SYMBOL_GPL(nf_ct_helper_log); 364 365int nf_conntrack_helper_register(struct nf_conntrack_helper *me) 366{ 367 int ret = 0; 368 struct nf_conntrack_helper *cur; 369 struct hlist_node *n; 370 unsigned int h = helper_hash(&me->tuple); 371 372 BUG_ON(me->expect_policy == NULL); 373 BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES); 374 BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1); 375 376 mutex_lock(&nf_ct_helper_mutex); 377 hlist_for_each_entry(cur, n, &nf_ct_helper_hash[h], hnode) { 378 if (strncmp(cur->name, me->name, NF_CT_HELPER_NAME_LEN) == 0 && 379 cur->tuple.src.l3num == me->tuple.src.l3num && 380 cur->tuple.dst.protonum == me->tuple.dst.protonum) { 381 ret = -EEXIST; 382 goto out; 383 } 384 } 385 hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]); 386 nf_ct_helper_count++; 387out: 388 mutex_unlock(&nf_ct_helper_mutex); 389 return ret; 390} 391EXPORT_SYMBOL_GPL(nf_conntrack_helper_register); 392 393static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me, 394 struct net *net) 395{ 396 struct nf_conntrack_tuple_hash *h; 397 struct nf_conntrack_expect *exp; 398 const struct hlist_node *n, *next; 399 const struct hlist_nulls_node *nn; 400 unsigned int i; 401 402 /* Get rid of expectations */ 403 for (i = 0; i < nf_ct_expect_hsize; i++) { 404 hlist_for_each_entry_safe(exp, n, next, 405 &net->ct.expect_hash[i], hnode) { 406 struct nf_conn_help *help = nfct_help(exp->master); 407 if ((rcu_dereference_protected( 408 help->helper, 409 lockdep_is_held(&nf_conntrack_lock) 410 ) == me || exp->helper == me) && 411 del_timer(&exp->timeout)) { 412 nf_ct_unlink_expect(exp); 413 nf_ct_expect_put(exp); 414 } 415 } 416 } 417 418 /* Get rid of expecteds, set helpers to NULL. */ 419 hlist_nulls_for_each_entry(h, nn, &net->ct.unconfirmed, hnnode) 420 unhelp(h, me); 421 for (i = 0; i < net->ct.htable_size; i++) { 422 hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode) 423 unhelp(h, me); 424 } 425} 426 427void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) 428{ 429 struct net *net; 430 431 mutex_lock(&nf_ct_helper_mutex); 432 hlist_del_rcu(&me->hnode); 433 nf_ct_helper_count--; 434 mutex_unlock(&nf_ct_helper_mutex); 435 436 /* Make sure every nothing is still using the helper unless its a 437 * connection in the hash. 438 */ 439 synchronize_rcu(); 440 441 rtnl_lock(); 442 spin_lock_bh(&nf_conntrack_lock); 443 for_each_net(net) 444 __nf_conntrack_helper_unregister(me, net); 445 spin_unlock_bh(&nf_conntrack_lock); 446 rtnl_unlock(); 447} 448EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister); 449 450static struct nf_ct_ext_type helper_extend __read_mostly = { 451 .len = sizeof(struct nf_conn_help), 452 .align = __alignof__(struct nf_conn_help), 453 .id = NF_CT_EXT_HELPER, 454}; 455 456int nf_conntrack_helper_pernet_init(struct net *net) 457{ 458 net->ct.auto_assign_helper_warned = false; 459 net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper; 460 return nf_conntrack_helper_init_sysctl(net); 461} 462 463void nf_conntrack_helper_pernet_fini(struct net *net) 464{ 465 nf_conntrack_helper_fini_sysctl(net); 466} 467 468int nf_conntrack_helper_init(void) 469{ 470 int ret; 471 nf_ct_helper_hsize = 1; /* gets rounded up to use one page */ 472 nf_ct_helper_hash = 473 nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0); 474 if (!nf_ct_helper_hash) 475 return -ENOMEM; 476 477 ret = nf_ct_extend_register(&helper_extend); 478 if (ret < 0) { 479 pr_err("nf_ct_helper: Unable to register helper extension.\n"); 480 goto out_extend; 481 } 482 483 return 0; 484out_extend: 485 nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize); 486 return ret; 487} 488 489void nf_conntrack_helper_fini(void) 490{ 491 nf_ct_extend_unregister(&helper_extend); 492 nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize); 493} 494