Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.10-rc2 672 lines 17 kB view raw
1/* 2 * (C) 2012 Pablo Neira Ayuso <pablo@netfilter.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation (or any later at your option). 7 * 8 * This software has been sponsored by Vyatta Inc. <http://www.vyatta.com> 9 */ 10#include <linux/init.h> 11#include <linux/module.h> 12#include <linux/kernel.h> 13#include <linux/skbuff.h> 14#include <linux/netlink.h> 15#include <linux/rculist.h> 16#include <linux/slab.h> 17#include <linux/types.h> 18#include <linux/list.h> 19#include <linux/errno.h> 20#include <net/netlink.h> 21#include <net/sock.h> 22 23#include <net/netfilter/nf_conntrack_helper.h> 24#include <net/netfilter/nf_conntrack_expect.h> 25#include <net/netfilter/nf_conntrack_ecache.h> 26 27#include <linux/netfilter/nfnetlink.h> 28#include <linux/netfilter/nfnetlink_conntrack.h> 29#include <linux/netfilter/nfnetlink_cthelper.h> 30 31MODULE_LICENSE("GPL"); 32MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); 33MODULE_DESCRIPTION("nfnl_cthelper: User-space connection tracking helpers"); 34 35static int 36nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff, 37 struct nf_conn *ct, enum ip_conntrack_info ctinfo) 38{ 39 const struct nf_conn_help *help; 40 struct nf_conntrack_helper *helper; 41 42 help = nfct_help(ct); 43 if (help == NULL) 44 return NF_DROP; 45 46 /* rcu_read_lock()ed by nf_hook_slow */ 47 helper = rcu_dereference(help->helper); 48 if (helper == NULL) 49 return NF_DROP; 50 51 /* This is an user-space helper not yet configured, skip. */ 52 if ((helper->flags & 53 (NF_CT_HELPER_F_USERSPACE | NF_CT_HELPER_F_CONFIGURED)) == 54 NF_CT_HELPER_F_USERSPACE) 55 return NF_ACCEPT; 56 57 /* If the user-space helper is not available, don't block traffic. */ 58 return NF_QUEUE_NR(helper->queue_num) | NF_VERDICT_FLAG_QUEUE_BYPASS; 59} 60 61static const struct nla_policy nfnl_cthelper_tuple_pol[NFCTH_TUPLE_MAX+1] = { 62 [NFCTH_TUPLE_L3PROTONUM] = { .type = NLA_U16, }, 63 [NFCTH_TUPLE_L4PROTONUM] = { .type = NLA_U8, }, 64}; 65 66static int 67nfnl_cthelper_parse_tuple(struct nf_conntrack_tuple *tuple, 68 const struct nlattr *attr) 69{ 70 struct nlattr *tb[NFCTH_TUPLE_MAX+1]; 71 72 nla_parse_nested(tb, NFCTH_TUPLE_MAX, attr, nfnl_cthelper_tuple_pol); 73 74 if (!tb[NFCTH_TUPLE_L3PROTONUM] || !tb[NFCTH_TUPLE_L4PROTONUM]) 75 return -EINVAL; 76 77 tuple->src.l3num = ntohs(nla_get_be16(tb[NFCTH_TUPLE_L3PROTONUM])); 78 tuple->dst.protonum = nla_get_u8(tb[NFCTH_TUPLE_L4PROTONUM]); 79 80 return 0; 81} 82 83static int 84nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct) 85{ 86 const struct nf_conn_help *help = nfct_help(ct); 87 88 if (attr == NULL) 89 return -EINVAL; 90 91 if (help->helper->data_len == 0) 92 return -EINVAL; 93 94 memcpy(&help->data, nla_data(attr), help->helper->data_len); 95 return 0; 96} 97 98static int 99nfnl_cthelper_to_nlattr(struct sk_buff *skb, const struct nf_conn *ct) 100{ 101 const struct nf_conn_help *help = nfct_help(ct); 102 103 if (help->helper->data_len && 104 nla_put(skb, CTA_HELP_INFO, help->helper->data_len, &help->data)) 105 goto nla_put_failure; 106 107 return 0; 108 109nla_put_failure: 110 return -ENOSPC; 111} 112 113static const struct nla_policy nfnl_cthelper_expect_pol[NFCTH_POLICY_MAX+1] = { 114 [NFCTH_POLICY_NAME] = { .type = NLA_NUL_STRING, 115 .len = NF_CT_HELPER_NAME_LEN-1 }, 116 [NFCTH_POLICY_EXPECT_MAX] = { .type = NLA_U32, }, 117 [NFCTH_POLICY_EXPECT_TIMEOUT] = { .type = NLA_U32, }, 118}; 119 120static int 121nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy, 122 const struct nlattr *attr) 123{ 124 struct nlattr *tb[NFCTH_POLICY_MAX+1]; 125 126 nla_parse_nested(tb, NFCTH_POLICY_MAX, attr, nfnl_cthelper_expect_pol); 127 128 if (!tb[NFCTH_POLICY_NAME] || 129 !tb[NFCTH_POLICY_EXPECT_MAX] || 130 !tb[NFCTH_POLICY_EXPECT_TIMEOUT]) 131 return -EINVAL; 132 133 strncpy(expect_policy->name, 134 nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN); 135 expect_policy->max_expected = 136 ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX])); 137 expect_policy->timeout = 138 ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_TIMEOUT])); 139 140 return 0; 141} 142 143static const struct nla_policy 144nfnl_cthelper_expect_policy_set[NFCTH_POLICY_SET_MAX+1] = { 145 [NFCTH_POLICY_SET_NUM] = { .type = NLA_U32, }, 146}; 147 148static int 149nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper, 150 const struct nlattr *attr) 151{ 152 int i, ret; 153 struct nf_conntrack_expect_policy *expect_policy; 154 struct nlattr *tb[NFCTH_POLICY_SET_MAX+1]; 155 156 nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr, 157 nfnl_cthelper_expect_policy_set); 158 159 if (!tb[NFCTH_POLICY_SET_NUM]) 160 return -EINVAL; 161 162 helper->expect_class_max = 163 ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM])); 164 165 if (helper->expect_class_max != 0 && 166 helper->expect_class_max > NF_CT_MAX_EXPECT_CLASSES) 167 return -EOVERFLOW; 168 169 expect_policy = kzalloc(sizeof(struct nf_conntrack_expect_policy) * 170 helper->expect_class_max, GFP_KERNEL); 171 if (expect_policy == NULL) 172 return -ENOMEM; 173 174 for (i=0; i<helper->expect_class_max; i++) { 175 if (!tb[NFCTH_POLICY_SET+i]) 176 goto err; 177 178 ret = nfnl_cthelper_expect_policy(&expect_policy[i], 179 tb[NFCTH_POLICY_SET+i]); 180 if (ret < 0) 181 goto err; 182 } 183 helper->expect_policy = expect_policy; 184 return 0; 185err: 186 kfree(expect_policy); 187 return -EINVAL; 188} 189 190static int 191nfnl_cthelper_create(const struct nlattr * const tb[], 192 struct nf_conntrack_tuple *tuple) 193{ 194 struct nf_conntrack_helper *helper; 195 int ret; 196 197 if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN]) 198 return -EINVAL; 199 200 helper = kzalloc(sizeof(struct nf_conntrack_helper), GFP_KERNEL); 201 if (helper == NULL) 202 return -ENOMEM; 203 204 ret = nfnl_cthelper_parse_expect_policy(helper, tb[NFCTH_POLICY]); 205 if (ret < 0) 206 goto err; 207 208 strncpy(helper->name, nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN); 209 helper->data_len = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN])); 210 helper->flags |= NF_CT_HELPER_F_USERSPACE; 211 memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple)); 212 213 helper->me = THIS_MODULE; 214 helper->help = nfnl_userspace_cthelper; 215 helper->from_nlattr = nfnl_cthelper_from_nlattr; 216 helper->to_nlattr = nfnl_cthelper_to_nlattr; 217 218 /* Default to queue number zero, this can be updated at any time. */ 219 if (tb[NFCTH_QUEUE_NUM]) 220 helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM])); 221 222 if (tb[NFCTH_STATUS]) { 223 int status = ntohl(nla_get_be32(tb[NFCTH_STATUS])); 224 225 switch(status) { 226 case NFCT_HELPER_STATUS_ENABLED: 227 helper->flags |= NF_CT_HELPER_F_CONFIGURED; 228 break; 229 case NFCT_HELPER_STATUS_DISABLED: 230 helper->flags &= ~NF_CT_HELPER_F_CONFIGURED; 231 break; 232 } 233 } 234 235 ret = nf_conntrack_helper_register(helper); 236 if (ret < 0) 237 goto err; 238 239 return 0; 240err: 241 kfree(helper); 242 return ret; 243} 244 245static int 246nfnl_cthelper_update(const struct nlattr * const tb[], 247 struct nf_conntrack_helper *helper) 248{ 249 int ret; 250 251 if (tb[NFCTH_PRIV_DATA_LEN]) 252 return -EBUSY; 253 254 if (tb[NFCTH_POLICY]) { 255 ret = nfnl_cthelper_parse_expect_policy(helper, 256 tb[NFCTH_POLICY]); 257 if (ret < 0) 258 return ret; 259 } 260 if (tb[NFCTH_QUEUE_NUM]) 261 helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM])); 262 263 if (tb[NFCTH_STATUS]) { 264 int status = ntohl(nla_get_be32(tb[NFCTH_STATUS])); 265 266 switch(status) { 267 case NFCT_HELPER_STATUS_ENABLED: 268 helper->flags |= NF_CT_HELPER_F_CONFIGURED; 269 break; 270 case NFCT_HELPER_STATUS_DISABLED: 271 helper->flags &= ~NF_CT_HELPER_F_CONFIGURED; 272 break; 273 } 274 } 275 return 0; 276} 277 278static int 279nfnl_cthelper_new(struct sock *nfnl, struct sk_buff *skb, 280 const struct nlmsghdr *nlh, const struct nlattr * const tb[]) 281{ 282 const char *helper_name; 283 struct nf_conntrack_helper *cur, *helper = NULL; 284 struct nf_conntrack_tuple tuple; 285 int ret = 0, i; 286 287 if (!tb[NFCTH_NAME] || !tb[NFCTH_TUPLE]) 288 return -EINVAL; 289 290 helper_name = nla_data(tb[NFCTH_NAME]); 291 292 ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]); 293 if (ret < 0) 294 return ret; 295 296 rcu_read_lock(); 297 for (i = 0; i < nf_ct_helper_hsize && !helper; i++) { 298 hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[i], hnode) { 299 300 /* skip non-userspace conntrack helpers. */ 301 if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) 302 continue; 303 304 if (strncmp(cur->name, helper_name, 305 NF_CT_HELPER_NAME_LEN) != 0) 306 continue; 307 308 if ((tuple.src.l3num != cur->tuple.src.l3num || 309 tuple.dst.protonum != cur->tuple.dst.protonum)) 310 continue; 311 312 if (nlh->nlmsg_flags & NLM_F_EXCL) { 313 ret = -EEXIST; 314 goto err; 315 } 316 helper = cur; 317 break; 318 } 319 } 320 rcu_read_unlock(); 321 322 if (helper == NULL) 323 ret = nfnl_cthelper_create(tb, &tuple); 324 else 325 ret = nfnl_cthelper_update(tb, helper); 326 327 return ret; 328err: 329 rcu_read_unlock(); 330 return ret; 331} 332 333static int 334nfnl_cthelper_dump_tuple(struct sk_buff *skb, 335 struct nf_conntrack_helper *helper) 336{ 337 struct nlattr *nest_parms; 338 339 nest_parms = nla_nest_start(skb, NFCTH_TUPLE | NLA_F_NESTED); 340 if (nest_parms == NULL) 341 goto nla_put_failure; 342 343 if (nla_put_be16(skb, NFCTH_TUPLE_L3PROTONUM, 344 htons(helper->tuple.src.l3num))) 345 goto nla_put_failure; 346 347 if (nla_put_u8(skb, NFCTH_TUPLE_L4PROTONUM, helper->tuple.dst.protonum)) 348 goto nla_put_failure; 349 350 nla_nest_end(skb, nest_parms); 351 return 0; 352 353nla_put_failure: 354 return -1; 355} 356 357static int 358nfnl_cthelper_dump_policy(struct sk_buff *skb, 359 struct nf_conntrack_helper *helper) 360{ 361 int i; 362 struct nlattr *nest_parms1, *nest_parms2; 363 364 nest_parms1 = nla_nest_start(skb, NFCTH_POLICY | NLA_F_NESTED); 365 if (nest_parms1 == NULL) 366 goto nla_put_failure; 367 368 if (nla_put_be32(skb, NFCTH_POLICY_SET_NUM, 369 htonl(helper->expect_class_max))) 370 goto nla_put_failure; 371 372 for (i=0; i<helper->expect_class_max; i++) { 373 nest_parms2 = nla_nest_start(skb, 374 (NFCTH_POLICY_SET+i) | NLA_F_NESTED); 375 if (nest_parms2 == NULL) 376 goto nla_put_failure; 377 378 if (nla_put_string(skb, NFCTH_POLICY_NAME, 379 helper->expect_policy[i].name)) 380 goto nla_put_failure; 381 382 if (nla_put_be32(skb, NFCTH_POLICY_EXPECT_MAX, 383 htonl(helper->expect_policy[i].max_expected))) 384 goto nla_put_failure; 385 386 if (nla_put_be32(skb, NFCTH_POLICY_EXPECT_TIMEOUT, 387 htonl(helper->expect_policy[i].timeout))) 388 goto nla_put_failure; 389 390 nla_nest_end(skb, nest_parms2); 391 } 392 nla_nest_end(skb, nest_parms1); 393 return 0; 394 395nla_put_failure: 396 return -1; 397} 398 399static int 400nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, 401 int event, struct nf_conntrack_helper *helper) 402{ 403 struct nlmsghdr *nlh; 404 struct nfgenmsg *nfmsg; 405 unsigned int flags = portid ? NLM_F_MULTI : 0; 406 int status; 407 408 event |= NFNL_SUBSYS_CTHELPER << 8; 409 nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); 410 if (nlh == NULL) 411 goto nlmsg_failure; 412 413 nfmsg = nlmsg_data(nlh); 414 nfmsg->nfgen_family = AF_UNSPEC; 415 nfmsg->version = NFNETLINK_V0; 416 nfmsg->res_id = 0; 417 418 if (nla_put_string(skb, NFCTH_NAME, helper->name)) 419 goto nla_put_failure; 420 421 if (nla_put_be32(skb, NFCTH_QUEUE_NUM, htonl(helper->queue_num))) 422 goto nla_put_failure; 423 424 if (nfnl_cthelper_dump_tuple(skb, helper) < 0) 425 goto nla_put_failure; 426 427 if (nfnl_cthelper_dump_policy(skb, helper) < 0) 428 goto nla_put_failure; 429 430 if (nla_put_be32(skb, NFCTH_PRIV_DATA_LEN, htonl(helper->data_len))) 431 goto nla_put_failure; 432 433 if (helper->flags & NF_CT_HELPER_F_CONFIGURED) 434 status = NFCT_HELPER_STATUS_ENABLED; 435 else 436 status = NFCT_HELPER_STATUS_DISABLED; 437 438 if (nla_put_be32(skb, NFCTH_STATUS, htonl(status))) 439 goto nla_put_failure; 440 441 nlmsg_end(skb, nlh); 442 return skb->len; 443 444nlmsg_failure: 445nla_put_failure: 446 nlmsg_cancel(skb, nlh); 447 return -1; 448} 449 450static int 451nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb) 452{ 453 struct nf_conntrack_helper *cur, *last; 454 455 rcu_read_lock(); 456 last = (struct nf_conntrack_helper *)cb->args[1]; 457 for (; cb->args[0] < nf_ct_helper_hsize; cb->args[0]++) { 458restart: 459 hlist_for_each_entry_rcu(cur, 460 &nf_ct_helper_hash[cb->args[0]], hnode) { 461 462 /* skip non-userspace conntrack helpers. */ 463 if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) 464 continue; 465 466 if (cb->args[1]) { 467 if (cur != last) 468 continue; 469 cb->args[1] = 0; 470 } 471 if (nfnl_cthelper_fill_info(skb, 472 NETLINK_CB(cb->skb).portid, 473 cb->nlh->nlmsg_seq, 474 NFNL_MSG_TYPE(cb->nlh->nlmsg_type), 475 NFNL_MSG_CTHELPER_NEW, cur) < 0) { 476 cb->args[1] = (unsigned long)cur; 477 goto out; 478 } 479 } 480 } 481 if (cb->args[1]) { 482 cb->args[1] = 0; 483 goto restart; 484 } 485out: 486 rcu_read_unlock(); 487 return skb->len; 488} 489 490static int 491nfnl_cthelper_get(struct sock *nfnl, struct sk_buff *skb, 492 const struct nlmsghdr *nlh, const struct nlattr * const tb[]) 493{ 494 int ret = -ENOENT, i; 495 struct nf_conntrack_helper *cur; 496 struct sk_buff *skb2; 497 char *helper_name = NULL; 498 struct nf_conntrack_tuple tuple; 499 bool tuple_set = false; 500 501 if (nlh->nlmsg_flags & NLM_F_DUMP) { 502 struct netlink_dump_control c = { 503 .dump = nfnl_cthelper_dump_table, 504 }; 505 return netlink_dump_start(nfnl, skb, nlh, &c); 506 } 507 508 if (tb[NFCTH_NAME]) 509 helper_name = nla_data(tb[NFCTH_NAME]); 510 511 if (tb[NFCTH_TUPLE]) { 512 ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]); 513 if (ret < 0) 514 return ret; 515 516 tuple_set = true; 517 } 518 519 for (i = 0; i < nf_ct_helper_hsize; i++) { 520 hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[i], hnode) { 521 522 /* skip non-userspace conntrack helpers. */ 523 if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) 524 continue; 525 526 if (helper_name && strncmp(cur->name, helper_name, 527 NF_CT_HELPER_NAME_LEN) != 0) { 528 continue; 529 } 530 if (tuple_set && 531 (tuple.src.l3num != cur->tuple.src.l3num || 532 tuple.dst.protonum != cur->tuple.dst.protonum)) 533 continue; 534 535 skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 536 if (skb2 == NULL) { 537 ret = -ENOMEM; 538 break; 539 } 540 541 ret = nfnl_cthelper_fill_info(skb2, NETLINK_CB(skb).portid, 542 nlh->nlmsg_seq, 543 NFNL_MSG_TYPE(nlh->nlmsg_type), 544 NFNL_MSG_CTHELPER_NEW, cur); 545 if (ret <= 0) { 546 kfree_skb(skb2); 547 break; 548 } 549 550 ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, 551 MSG_DONTWAIT); 552 if (ret > 0) 553 ret = 0; 554 555 /* this avoids a loop in nfnetlink. */ 556 return ret == -EAGAIN ? -ENOBUFS : ret; 557 } 558 } 559 return ret; 560} 561 562static int 563nfnl_cthelper_del(struct sock *nfnl, struct sk_buff *skb, 564 const struct nlmsghdr *nlh, const struct nlattr * const tb[]) 565{ 566 char *helper_name = NULL; 567 struct nf_conntrack_helper *cur; 568 struct hlist_node *tmp; 569 struct nf_conntrack_tuple tuple; 570 bool tuple_set = false, found = false; 571 int i, j = 0, ret; 572 573 if (tb[NFCTH_NAME]) 574 helper_name = nla_data(tb[NFCTH_NAME]); 575 576 if (tb[NFCTH_TUPLE]) { 577 ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]); 578 if (ret < 0) 579 return ret; 580 581 tuple_set = true; 582 } 583 584 for (i = 0; i < nf_ct_helper_hsize; i++) { 585 hlist_for_each_entry_safe(cur, tmp, &nf_ct_helper_hash[i], 586 hnode) { 587 /* skip non-userspace conntrack helpers. */ 588 if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) 589 continue; 590 591 j++; 592 593 if (helper_name && strncmp(cur->name, helper_name, 594 NF_CT_HELPER_NAME_LEN) != 0) { 595 continue; 596 } 597 if (tuple_set && 598 (tuple.src.l3num != cur->tuple.src.l3num || 599 tuple.dst.protonum != cur->tuple.dst.protonum)) 600 continue; 601 602 found = true; 603 nf_conntrack_helper_unregister(cur); 604 } 605 } 606 /* Make sure we return success if we flush and there is no helpers */ 607 return (found || j == 0) ? 0 : -ENOENT; 608} 609 610static const struct nla_policy nfnl_cthelper_policy[NFCTH_MAX+1] = { 611 [NFCTH_NAME] = { .type = NLA_NUL_STRING, 612 .len = NF_CT_HELPER_NAME_LEN-1 }, 613 [NFCTH_QUEUE_NUM] = { .type = NLA_U32, }, 614}; 615 616static const struct nfnl_callback nfnl_cthelper_cb[NFNL_MSG_CTHELPER_MAX] = { 617 [NFNL_MSG_CTHELPER_NEW] = { .call = nfnl_cthelper_new, 618 .attr_count = NFCTH_MAX, 619 .policy = nfnl_cthelper_policy }, 620 [NFNL_MSG_CTHELPER_GET] = { .call = nfnl_cthelper_get, 621 .attr_count = NFCTH_MAX, 622 .policy = nfnl_cthelper_policy }, 623 [NFNL_MSG_CTHELPER_DEL] = { .call = nfnl_cthelper_del, 624 .attr_count = NFCTH_MAX, 625 .policy = nfnl_cthelper_policy }, 626}; 627 628static const struct nfnetlink_subsystem nfnl_cthelper_subsys = { 629 .name = "cthelper", 630 .subsys_id = NFNL_SUBSYS_CTHELPER, 631 .cb_count = NFNL_MSG_CTHELPER_MAX, 632 .cb = nfnl_cthelper_cb, 633}; 634 635MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTHELPER); 636 637static int __init nfnl_cthelper_init(void) 638{ 639 int ret; 640 641 ret = nfnetlink_subsys_register(&nfnl_cthelper_subsys); 642 if (ret < 0) { 643 pr_err("nfnl_cthelper: cannot register with nfnetlink.\n"); 644 goto err_out; 645 } 646 return 0; 647err_out: 648 return ret; 649} 650 651static void __exit nfnl_cthelper_exit(void) 652{ 653 struct nf_conntrack_helper *cur; 654 struct hlist_node *tmp; 655 int i; 656 657 nfnetlink_subsys_unregister(&nfnl_cthelper_subsys); 658 659 for (i=0; i<nf_ct_helper_hsize; i++) { 660 hlist_for_each_entry_safe(cur, tmp, &nf_ct_helper_hash[i], 661 hnode) { 662 /* skip non-userspace conntrack helpers. */ 663 if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) 664 continue; 665 666 nf_conntrack_helper_unregister(cur); 667 } 668 } 669} 670 671module_init(nfnl_cthelper_init); 672module_exit(nfnl_cthelper_exit);