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