1 /* (C) 1999-2001 Paul `Rusty' Russell 2 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> 3 * (C) 2005-2012 Patrick McHardy <kaber@trash.net> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 */ 9 10 #include <linux/types.h> 11 #include <linux/netfilter.h> 12 #include <linux/slab.h> 13 #include <linux/module.h> 14 #include <linux/skbuff.h> 15 #include <linux/proc_fs.h> 16 #include <linux/seq_file.h> 17 #include <linux/percpu.h> 18 #include <linux/netdevice.h> 19 #include <linux/security.h> 20 #include <net/net_namespace.h> 21 #ifdef CONFIG_SYSCTL 22 #include <linux/sysctl.h> 23 #endif 24 25 #include <net/netfilter/nf_conntrack.h> 26 #include <net/netfilter/nf_conntrack_core.h> 27 #include <net/netfilter/nf_conntrack_l3proto.h> 28 #include <net/netfilter/nf_conntrack_l4proto.h> 29 #include <net/netfilter/nf_conntrack_expect.h> 30 #include <net/netfilter/nf_conntrack_helper.h> 31 #include <net/netfilter/nf_conntrack_acct.h> 32 #include <net/netfilter/nf_conntrack_zones.h> 33 #include <net/netfilter/nf_conntrack_timestamp.h> 34 #include <linux/rculist_nulls.h> 35 36 MODULE_LICENSE("GPL"); 37 38 #ifdef CONFIG_NF_CONNTRACK_PROCFS 39 void 40 print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple, 41 const struct nf_conntrack_l3proto *l3proto, 42 const struct nf_conntrack_l4proto *l4proto) 43 { 44 switch (l3proto->l3proto) { 45 case NFPROTO_IPV4: 46 seq_printf(s, "src=%pI4 dst=%pI4 ", 47 &tuple->src.u3.ip, &tuple->dst.u3.ip); 48 break; 49 case NFPROTO_IPV6: 50 seq_printf(s, "src=%pI6 dst=%pI6 ", 51 tuple->src.u3.ip6, tuple->dst.u3.ip6); 52 break; 53 default: 54 break; 55 } 56 57 switch (l4proto->l4proto) { 58 case IPPROTO_ICMP: 59 seq_printf(s, "type=%u code=%u id=%u ", 60 tuple->dst.u.icmp.type, 61 tuple->dst.u.icmp.code, 62 ntohs(tuple->src.u.icmp.id)); 63 break; 64 case IPPROTO_TCP: 65 seq_printf(s, "sport=%hu dport=%hu ", 66 ntohs(tuple->src.u.tcp.port), 67 ntohs(tuple->dst.u.tcp.port)); 68 break; 69 case IPPROTO_UDPLITE: /* fallthrough */ 70 case IPPROTO_UDP: 71 seq_printf(s, "sport=%hu dport=%hu ", 72 ntohs(tuple->src.u.udp.port), 73 ntohs(tuple->dst.u.udp.port)); 74 75 break; 76 case IPPROTO_DCCP: 77 seq_printf(s, "sport=%hu dport=%hu ", 78 ntohs(tuple->src.u.dccp.port), 79 ntohs(tuple->dst.u.dccp.port)); 80 break; 81 case IPPROTO_SCTP: 82 seq_printf(s, "sport=%hu dport=%hu ", 83 ntohs(tuple->src.u.sctp.port), 84 ntohs(tuple->dst.u.sctp.port)); 85 break; 86 case IPPROTO_ICMPV6: 87 seq_printf(s, "type=%u code=%u id=%u ", 88 tuple->dst.u.icmp.type, 89 tuple->dst.u.icmp.code, 90 ntohs(tuple->src.u.icmp.id)); 91 break; 92 case IPPROTO_GRE: 93 seq_printf(s, "srckey=0x%x dstkey=0x%x ", 94 ntohs(tuple->src.u.gre.key), 95 ntohs(tuple->dst.u.gre.key)); 96 break; 97 default: 98 break; 99 } 100 } 101 EXPORT_SYMBOL_GPL(print_tuple); 102 103 struct ct_iter_state { 104 struct seq_net_private p; 105 struct hlist_nulls_head *hash; 106 unsigned int htable_size; 107 unsigned int bucket; 108 u_int64_t time_now; 109 }; 110 111 static struct hlist_nulls_node *ct_get_first(struct seq_file *seq) 112 { 113 struct ct_iter_state *st = seq->private; 114 struct hlist_nulls_node *n; 115 116 for (st->bucket = 0; 117 st->bucket < st->htable_size; 118 st->bucket++) { 119 n = rcu_dereference( 120 hlist_nulls_first_rcu(&st->hash[st->bucket])); 121 if (!is_a_nulls(n)) 122 return n; 123 } 124 return NULL; 125 } 126 127 static struct hlist_nulls_node *ct_get_next(struct seq_file *seq, 128 struct hlist_nulls_node *head) 129 { 130 struct ct_iter_state *st = seq->private; 131 132 head = rcu_dereference(hlist_nulls_next_rcu(head)); 133 while (is_a_nulls(head)) { 134 if (likely(get_nulls_value(head) == st->bucket)) { 135 if (++st->bucket >= st->htable_size) 136 return NULL; 137 } 138 head = rcu_dereference( 139 hlist_nulls_first_rcu(&st->hash[st->bucket])); 140 } 141 return head; 142 } 143 144 static struct hlist_nulls_node *ct_get_idx(struct seq_file *seq, loff_t pos) 145 { 146 struct hlist_nulls_node *head = ct_get_first(seq); 147 148 if (head) 149 while (pos && (head = ct_get_next(seq, head))) 150 pos--; 151 return pos ? NULL : head; 152 } 153 154 static void *ct_seq_start(struct seq_file *seq, loff_t *pos) 155 __acquires(RCU) 156 { 157 struct ct_iter_state *st = seq->private; 158 159 st->time_now = ktime_get_real_ns(); 160 rcu_read_lock(); 161 162 nf_conntrack_get_ht(&st->hash, &st->htable_size); 163 return ct_get_idx(seq, *pos); 164 } 165 166 static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos) 167 { 168 (*pos)++; 169 return ct_get_next(s, v); 170 } 171 172 static void ct_seq_stop(struct seq_file *s, void *v) 173 __releases(RCU) 174 { 175 rcu_read_unlock(); 176 } 177 178 #ifdef CONFIG_NF_CONNTRACK_SECMARK 179 static void ct_show_secctx(struct seq_file *s, const struct nf_conn *ct) 180 { 181 int ret; 182 u32 len; 183 char *secctx; 184 185 ret = security_secid_to_secctx(ct->secmark, &secctx, &len); 186 if (ret) 187 return; 188 189 seq_printf(s, "secctx=%s ", secctx); 190 191 security_release_secctx(secctx, len); 192 } 193 #else 194 static inline void ct_show_secctx(struct seq_file *s, const struct nf_conn *ct) 195 { 196 } 197 #endif 198 199 #ifdef CONFIG_NF_CONNTRACK_ZONES 200 static void ct_show_zone(struct seq_file *s, const struct nf_conn *ct, 201 int dir) 202 { 203 const struct nf_conntrack_zone *zone = nf_ct_zone(ct); 204 205 if (zone->dir != dir) 206 return; 207 switch (zone->dir) { 208 case NF_CT_DEFAULT_ZONE_DIR: 209 seq_printf(s, "zone=%u ", zone->id); 210 break; 211 case NF_CT_ZONE_DIR_ORIG: 212 seq_printf(s, "zone-orig=%u ", zone->id); 213 break; 214 case NF_CT_ZONE_DIR_REPL: 215 seq_printf(s, "zone-reply=%u ", zone->id); 216 break; 217 default: 218 break; 219 } 220 } 221 #else 222 static inline void ct_show_zone(struct seq_file *s, const struct nf_conn *ct, 223 int dir) 224 { 225 } 226 #endif 227 228 #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP 229 static void ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct) 230 { 231 struct ct_iter_state *st = s->private; 232 struct nf_conn_tstamp *tstamp; 233 s64 delta_time; 234 235 tstamp = nf_conn_tstamp_find(ct); 236 if (tstamp) { 237 delta_time = st->time_now - tstamp->start; 238 if (delta_time > 0) 239 delta_time = div_s64(delta_time, NSEC_PER_SEC); 240 else 241 delta_time = 0; 242 243 seq_printf(s, "delta-time=%llu ", 244 (unsigned long long)delta_time); 245 } 246 return; 247 } 248 #else 249 static inline void 250 ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct) 251 { 252 } 253 #endif 254 255 static const char* l3proto_name(u16 proto) 256 { 257 switch (proto) { 258 case AF_INET: return "ipv4"; 259 case AF_INET6: return "ipv6"; 260 } 261 262 return "unknown"; 263 } 264 265 static const char* l4proto_name(u16 proto) 266 { 267 switch (proto) { 268 case IPPROTO_ICMP: return "icmp"; 269 case IPPROTO_TCP: return "tcp"; 270 case IPPROTO_UDP: return "udp"; 271 case IPPROTO_DCCP: return "dccp"; 272 case IPPROTO_GRE: return "gre"; 273 case IPPROTO_SCTP: return "sctp"; 274 case IPPROTO_UDPLITE: return "udplite"; 275 } 276 277 return "unknown"; 278 } 279 280 /* return 0 on success, 1 in case of error */ 281 static int ct_seq_show(struct seq_file *s, void *v) 282 { 283 struct nf_conntrack_tuple_hash *hash = v; 284 struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(hash); 285 const struct nf_conntrack_l3proto *l3proto; 286 const struct nf_conntrack_l4proto *l4proto; 287 struct net *net = seq_file_net(s); 288 int ret = 0; 289 290 WARN_ON(!ct); 291 if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use))) 292 return 0; 293 294 if (nf_ct_should_gc(ct)) { 295 nf_ct_kill(ct); 296 goto release; 297 } 298 299 /* we only want to print DIR_ORIGINAL */ 300 if (NF_CT_DIRECTION(hash)) 301 goto release; 302 303 if (!net_eq(nf_ct_net(ct), net)) 304 goto release; 305 306 l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct)); 307 WARN_ON(!l3proto); 308 l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); 309 WARN_ON(!l4proto); 310 311 ret = -ENOSPC; 312 seq_printf(s, "%-8s %u %-8s %u %ld ", 313 l3proto_name(l3proto->l3proto), nf_ct_l3num(ct), 314 l4proto_name(l4proto->l4proto), nf_ct_protonum(ct), 315 nf_ct_expires(ct) / HZ); 316 317 if (l4proto->print_conntrack) 318 l4proto->print_conntrack(s, ct); 319 320 print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, 321 l3proto, l4proto); 322 323 ct_show_zone(s, ct, NF_CT_ZONE_DIR_ORIG); 324 325 if (seq_has_overflowed(s)) 326 goto release; 327 328 if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL)) 329 goto release; 330 331 if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) 332 seq_puts(s, "[UNREPLIED] "); 333 334 print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, 335 l3proto, l4proto); 336 337 ct_show_zone(s, ct, NF_CT_ZONE_DIR_REPL); 338 339 if (seq_print_acct(s, ct, IP_CT_DIR_REPLY)) 340 goto release; 341 342 if (test_bit(IPS_ASSURED_BIT, &ct->status)) 343 seq_puts(s, "[ASSURED] "); 344 345 if (seq_has_overflowed(s)) 346 goto release; 347 348 #if defined(CONFIG_NF_CONNTRACK_MARK) 349 seq_printf(s, "mark=%u ", ct->mark); 350 #endif 351 352 ct_show_secctx(s, ct); 353 ct_show_zone(s, ct, NF_CT_DEFAULT_ZONE_DIR); 354 ct_show_delta_time(s, ct); 355 356 seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use)); 357 358 if (seq_has_overflowed(s)) 359 goto release; 360 361 ret = 0; 362 release: 363 nf_ct_put(ct); 364 return ret; 365 } 366 367 static const struct seq_operations ct_seq_ops = { 368 .start = ct_seq_start, 369 .next = ct_seq_next, 370 .stop = ct_seq_stop, 371 .show = ct_seq_show 372 }; 373 374 static int ct_open(struct inode *inode, struct file *file) 375 { 376 return seq_open_net(inode, file, &ct_seq_ops, 377 sizeof(struct ct_iter_state)); 378 } 379 380 static const struct file_operations ct_file_ops = { 381 .owner = THIS_MODULE, 382 .open = ct_open, 383 .read = seq_read, 384 .llseek = seq_lseek, 385 .release = seq_release_net, 386 }; 387 388 static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos) 389 { 390 struct net *net = seq_file_net(seq); 391 int cpu; 392 393 if (*pos == 0) 394 return SEQ_START_TOKEN; 395 396 for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) { 397 if (!cpu_possible(cpu)) 398 continue; 399 *pos = cpu + 1; 400 return per_cpu_ptr(net->ct.stat, cpu); 401 } 402 403 return NULL; 404 } 405 406 static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos) 407 { 408 struct net *net = seq_file_net(seq); 409 int cpu; 410 411 for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) { 412 if (!cpu_possible(cpu)) 413 continue; 414 *pos = cpu + 1; 415 return per_cpu_ptr(net->ct.stat, cpu); 416 } 417 418 return NULL; 419 } 420 421 static void ct_cpu_seq_stop(struct seq_file *seq, void *v) 422 { 423 } 424 425 static int ct_cpu_seq_show(struct seq_file *seq, void *v) 426 { 427 struct net *net = seq_file_net(seq); 428 unsigned int nr_conntracks = atomic_read(&net->ct.count); 429 const struct ip_conntrack_stat *st = v; 430 431 if (v == SEQ_START_TOKEN) { 432 seq_puts(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete search_restart\n"); 433 return 0; 434 } 435 436 seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x " 437 "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n", 438 nr_conntracks, 439 0, 440 st->found, 441 0, 442 st->invalid, 443 st->ignore, 444 0, 445 0, 446 st->insert, 447 st->insert_failed, 448 st->drop, 449 st->early_drop, 450 st->error, 451 452 st->expect_new, 453 st->expect_create, 454 st->expect_delete, 455 st->search_restart 456 ); 457 return 0; 458 } 459 460 static const struct seq_operations ct_cpu_seq_ops = { 461 .start = ct_cpu_seq_start, 462 .next = ct_cpu_seq_next, 463 .stop = ct_cpu_seq_stop, 464 .show = ct_cpu_seq_show, 465 }; 466 467 static int ct_cpu_seq_open(struct inode *inode, struct file *file) 468 { 469 return seq_open_net(inode, file, &ct_cpu_seq_ops, 470 sizeof(struct seq_net_private)); 471 } 472 473 static const struct file_operations ct_cpu_seq_fops = { 474 .owner = THIS_MODULE, 475 .open = ct_cpu_seq_open, 476 .read = seq_read, 477 .llseek = seq_lseek, 478 .release = seq_release_net, 479 }; 480 481 static int nf_conntrack_standalone_init_proc(struct net *net) 482 { 483 struct proc_dir_entry *pde; 484 kuid_t root_uid; 485 kgid_t root_gid; 486 487 pde = proc_create("nf_conntrack", 0440, net->proc_net, &ct_file_ops); 488 if (!pde) 489 goto out_nf_conntrack; 490 491 root_uid = make_kuid(net->user_ns, 0); 492 root_gid = make_kgid(net->user_ns, 0); 493 if (uid_valid(root_uid) && gid_valid(root_gid)) 494 proc_set_user(pde, root_uid, root_gid); 495 496 pde = proc_create("nf_conntrack", S_IRUGO, net->proc_net_stat, 497 &ct_cpu_seq_fops); 498 if (!pde) 499 goto out_stat_nf_conntrack; 500 return 0; 501 502 out_stat_nf_conntrack: 503 remove_proc_entry("nf_conntrack", net->proc_net); 504 out_nf_conntrack: 505 return -ENOMEM; 506 } 507 508 static void nf_conntrack_standalone_fini_proc(struct net *net) 509 { 510 remove_proc_entry("nf_conntrack", net->proc_net_stat); 511 remove_proc_entry("nf_conntrack", net->proc_net); 512 } 513 #else 514 static int nf_conntrack_standalone_init_proc(struct net *net) 515 { 516 return 0; 517 } 518 519 static void nf_conntrack_standalone_fini_proc(struct net *net) 520 { 521 } 522 #endif /* CONFIG_NF_CONNTRACK_PROCFS */ 523 524 /* Sysctl support */ 525 526 #ifdef CONFIG_SYSCTL 527 /* Log invalid packets of a given protocol */ 528 static int log_invalid_proto_min __read_mostly; 529 static int log_invalid_proto_max __read_mostly = 255; 530 531 /* size the user *wants to set */ 532 static unsigned int nf_conntrack_htable_size_user __read_mostly; 533 534 static int 535 nf_conntrack_hash_sysctl(struct ctl_table *table, int write, 536 void __user *buffer, size_t *lenp, loff_t *ppos) 537 { 538 int ret; 539 540 ret = proc_dointvec(table, write, buffer, lenp, ppos); 541 if (ret < 0 || !write) 542 return ret; 543 544 /* update ret, we might not be able to satisfy request */ 545 ret = nf_conntrack_hash_resize(nf_conntrack_htable_size_user); 546 547 /* update it to the actual value used by conntrack */ 548 nf_conntrack_htable_size_user = nf_conntrack_htable_size; 549 return ret; 550 } 551 552 static struct ctl_table_header *nf_ct_netfilter_header; 553 554 static struct ctl_table nf_ct_sysctl_table[] = { 555 { 556 .procname = "nf_conntrack_max", 557 .data = &nf_conntrack_max, 558 .maxlen = sizeof(int), 559 .mode = 0644, 560 .proc_handler = proc_dointvec, 561 }, 562 { 563 .procname = "nf_conntrack_count", 564 .data = &init_net.ct.count, 565 .maxlen = sizeof(int), 566 .mode = 0444, 567 .proc_handler = proc_dointvec, 568 }, 569 { 570 .procname = "nf_conntrack_buckets", 571 .data = &nf_conntrack_htable_size_user, 572 .maxlen = sizeof(unsigned int), 573 .mode = 0644, 574 .proc_handler = nf_conntrack_hash_sysctl, 575 }, 576 { 577 .procname = "nf_conntrack_checksum", 578 .data = &init_net.ct.sysctl_checksum, 579 .maxlen = sizeof(unsigned int), 580 .mode = 0644, 581 .proc_handler = proc_dointvec, 582 }, 583 { 584 .procname = "nf_conntrack_log_invalid", 585 .data = &init_net.ct.sysctl_log_invalid, 586 .maxlen = sizeof(unsigned int), 587 .mode = 0644, 588 .proc_handler = proc_dointvec_minmax, 589 .extra1 = &log_invalid_proto_min, 590 .extra2 = &log_invalid_proto_max, 591 }, 592 { 593 .procname = "nf_conntrack_expect_max", 594 .data = &nf_ct_expect_max, 595 .maxlen = sizeof(int), 596 .mode = 0644, 597 .proc_handler = proc_dointvec, 598 }, 599 { } 600 }; 601 602 static struct ctl_table nf_ct_netfilter_table[] = { 603 { 604 .procname = "nf_conntrack_max", 605 .data = &nf_conntrack_max, 606 .maxlen = sizeof(int), 607 .mode = 0644, 608 .proc_handler = proc_dointvec, 609 }, 610 { } 611 }; 612 613 static int nf_conntrack_standalone_init_sysctl(struct net *net) 614 { 615 struct ctl_table *table; 616 617 table = kmemdup(nf_ct_sysctl_table, sizeof(nf_ct_sysctl_table), 618 GFP_KERNEL); 619 if (!table) 620 goto out_kmemdup; 621 622 table[1].data = &net->ct.count; 623 table[3].data = &net->ct.sysctl_checksum; 624 table[4].data = &net->ct.sysctl_log_invalid; 625 626 /* Don't export sysctls to unprivileged users */ 627 if (net->user_ns != &init_user_ns) 628 table[0].procname = NULL; 629 630 if (!net_eq(&init_net, net)) 631 table[2].mode = 0444; 632 633 net->ct.sysctl_header = register_net_sysctl(net, "net/netfilter", table); 634 if (!net->ct.sysctl_header) 635 goto out_unregister_netfilter; 636 637 return 0; 638 639 out_unregister_netfilter: 640 kfree(table); 641 out_kmemdup: 642 return -ENOMEM; 643 } 644 645 static void nf_conntrack_standalone_fini_sysctl(struct net *net) 646 { 647 struct ctl_table *table; 648 649 table = net->ct.sysctl_header->ctl_table_arg; 650 unregister_net_sysctl_table(net->ct.sysctl_header); 651 kfree(table); 652 } 653 #else 654 static int nf_conntrack_standalone_init_sysctl(struct net *net) 655 { 656 return 0; 657 } 658 659 static void nf_conntrack_standalone_fini_sysctl(struct net *net) 660 { 661 } 662 #endif /* CONFIG_SYSCTL */ 663 664 static int nf_conntrack_pernet_init(struct net *net) 665 { 666 int ret; 667 668 ret = nf_conntrack_init_net(net); 669 if (ret < 0) 670 goto out_init; 671 672 ret = nf_conntrack_standalone_init_proc(net); 673 if (ret < 0) 674 goto out_proc; 675 676 net->ct.sysctl_checksum = 1; 677 net->ct.sysctl_log_invalid = 0; 678 ret = nf_conntrack_standalone_init_sysctl(net); 679 if (ret < 0) 680 goto out_sysctl; 681 682 return 0; 683 684 out_sysctl: 685 nf_conntrack_standalone_fini_proc(net); 686 out_proc: 687 nf_conntrack_cleanup_net(net); 688 out_init: 689 return ret; 690 } 691 692 static void nf_conntrack_pernet_exit(struct list_head *net_exit_list) 693 { 694 struct net *net; 695 696 list_for_each_entry(net, net_exit_list, exit_list) { 697 nf_conntrack_standalone_fini_sysctl(net); 698 nf_conntrack_standalone_fini_proc(net); 699 } 700 nf_conntrack_cleanup_net_list(net_exit_list); 701 } 702 703 static struct pernet_operations nf_conntrack_net_ops = { 704 .init = nf_conntrack_pernet_init, 705 .exit_batch = nf_conntrack_pernet_exit, 706 }; 707 708 static int __init nf_conntrack_standalone_init(void) 709 { 710 int ret = nf_conntrack_init_start(); 711 if (ret < 0) 712 goto out_start; 713 714 BUILD_BUG_ON(SKB_NFCT_PTRMASK != NFCT_PTRMASK); 715 BUILD_BUG_ON(NFCT_INFOMASK <= IP_CT_NUMBER); 716 717 #ifdef CONFIG_SYSCTL 718 nf_ct_netfilter_header = 719 register_net_sysctl(&init_net, "net", nf_ct_netfilter_table); 720 if (!nf_ct_netfilter_header) { 721 pr_err("nf_conntrack: can't register to sysctl.\n"); 722 ret = -ENOMEM; 723 goto out_sysctl; 724 } 725 726 nf_conntrack_htable_size_user = nf_conntrack_htable_size; 727 #endif 728 729 ret = register_pernet_subsys(&nf_conntrack_net_ops); 730 if (ret < 0) 731 goto out_pernet; 732 733 nf_conntrack_init_end(); 734 return 0; 735 736 out_pernet: 737 #ifdef CONFIG_SYSCTL 738 unregister_net_sysctl_table(nf_ct_netfilter_header); 739 out_sysctl: 740 #endif 741 nf_conntrack_cleanup_end(); 742 out_start: 743 return ret; 744 } 745 746 static void __exit nf_conntrack_standalone_fini(void) 747 { 748 nf_conntrack_cleanup_start(); 749 unregister_pernet_subsys(&nf_conntrack_net_ops); 750 #ifdef CONFIG_SYSCTL 751 unregister_net_sysctl_table(nf_ct_netfilter_header); 752 #endif 753 nf_conntrack_cleanup_end(); 754 } 755 756 module_init(nf_conntrack_standalone_init); 757 module_exit(nf_conntrack_standalone_fini); 758 759 /* Some modules need us, but don't depend directly on any symbol. 760 They should call this. */ 761 void need_conntrack(void) 762 { 763 } 764 EXPORT_SYMBOL_GPL(need_conntrack); 765