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 ", 313 l3proto_name(l3proto->l3proto), nf_ct_l3num(ct), 314 l4proto_name(l4proto->l4proto), nf_ct_protonum(ct)); 315 316 if (!test_bit(IPS_OFFLOAD_BIT, &ct->status)) 317 seq_printf(s, "%ld ", nf_ct_expires(ct) / HZ); 318 319 if (l4proto->print_conntrack) 320 l4proto->print_conntrack(s, ct); 321 322 print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, 323 l3proto, l4proto); 324 325 ct_show_zone(s, ct, NF_CT_ZONE_DIR_ORIG); 326 327 if (seq_has_overflowed(s)) 328 goto release; 329 330 if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL)) 331 goto release; 332 333 if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) 334 seq_puts(s, "[UNREPLIED] "); 335 336 print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, 337 l3proto, l4proto); 338 339 ct_show_zone(s, ct, NF_CT_ZONE_DIR_REPL); 340 341 if (seq_print_acct(s, ct, IP_CT_DIR_REPLY)) 342 goto release; 343 344 if (test_bit(IPS_OFFLOAD_BIT, &ct->status)) 345 seq_puts(s, "[OFFLOAD] "); 346 else if (test_bit(IPS_ASSURED_BIT, &ct->status)) 347 seq_puts(s, "[ASSURED] "); 348 349 if (seq_has_overflowed(s)) 350 goto release; 351 352 #if defined(CONFIG_NF_CONNTRACK_MARK) 353 seq_printf(s, "mark=%u ", ct->mark); 354 #endif 355 356 ct_show_secctx(s, ct); 357 ct_show_zone(s, ct, NF_CT_DEFAULT_ZONE_DIR); 358 ct_show_delta_time(s, ct); 359 360 seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use)); 361 362 if (seq_has_overflowed(s)) 363 goto release; 364 365 ret = 0; 366 release: 367 nf_ct_put(ct); 368 return ret; 369 } 370 371 static const struct seq_operations ct_seq_ops = { 372 .start = ct_seq_start, 373 .next = ct_seq_next, 374 .stop = ct_seq_stop, 375 .show = ct_seq_show 376 }; 377 378 static int ct_open(struct inode *inode, struct file *file) 379 { 380 return seq_open_net(inode, file, &ct_seq_ops, 381 sizeof(struct ct_iter_state)); 382 } 383 384 static const struct file_operations ct_file_ops = { 385 .open = ct_open, 386 .read = seq_read, 387 .llseek = seq_lseek, 388 .release = seq_release_net, 389 }; 390 391 static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos) 392 { 393 struct net *net = seq_file_net(seq); 394 int cpu; 395 396 if (*pos == 0) 397 return SEQ_START_TOKEN; 398 399 for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) { 400 if (!cpu_possible(cpu)) 401 continue; 402 *pos = cpu + 1; 403 return per_cpu_ptr(net->ct.stat, cpu); 404 } 405 406 return NULL; 407 } 408 409 static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos) 410 { 411 struct net *net = seq_file_net(seq); 412 int cpu; 413 414 for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) { 415 if (!cpu_possible(cpu)) 416 continue; 417 *pos = cpu + 1; 418 return per_cpu_ptr(net->ct.stat, cpu); 419 } 420 421 return NULL; 422 } 423 424 static void ct_cpu_seq_stop(struct seq_file *seq, void *v) 425 { 426 } 427 428 static int ct_cpu_seq_show(struct seq_file *seq, void *v) 429 { 430 struct net *net = seq_file_net(seq); 431 unsigned int nr_conntracks = atomic_read(&net->ct.count); 432 const struct ip_conntrack_stat *st = v; 433 434 if (v == SEQ_START_TOKEN) { 435 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"); 436 return 0; 437 } 438 439 seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x " 440 "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n", 441 nr_conntracks, 442 0, 443 st->found, 444 0, 445 st->invalid, 446 st->ignore, 447 0, 448 0, 449 st->insert, 450 st->insert_failed, 451 st->drop, 452 st->early_drop, 453 st->error, 454 455 st->expect_new, 456 st->expect_create, 457 st->expect_delete, 458 st->search_restart 459 ); 460 return 0; 461 } 462 463 static const struct seq_operations ct_cpu_seq_ops = { 464 .start = ct_cpu_seq_start, 465 .next = ct_cpu_seq_next, 466 .stop = ct_cpu_seq_stop, 467 .show = ct_cpu_seq_show, 468 }; 469 470 static int ct_cpu_seq_open(struct inode *inode, struct file *file) 471 { 472 return seq_open_net(inode, file, &ct_cpu_seq_ops, 473 sizeof(struct seq_net_private)); 474 } 475 476 static const struct file_operations ct_cpu_seq_fops = { 477 .open = ct_cpu_seq_open, 478 .read = seq_read, 479 .llseek = seq_lseek, 480 .release = seq_release_net, 481 }; 482 483 static int nf_conntrack_standalone_init_proc(struct net *net) 484 { 485 struct proc_dir_entry *pde; 486 kuid_t root_uid; 487 kgid_t root_gid; 488 489 pde = proc_create("nf_conntrack", 0440, net->proc_net, &ct_file_ops); 490 if (!pde) 491 goto out_nf_conntrack; 492 493 root_uid = make_kuid(net->user_ns, 0); 494 root_gid = make_kgid(net->user_ns, 0); 495 if (uid_valid(root_uid) && gid_valid(root_gid)) 496 proc_set_user(pde, root_uid, root_gid); 497 498 pde = proc_create("nf_conntrack", S_IRUGO, net->proc_net_stat, 499 &ct_cpu_seq_fops); 500 if (!pde) 501 goto out_stat_nf_conntrack; 502 return 0; 503 504 out_stat_nf_conntrack: 505 remove_proc_entry("nf_conntrack", net->proc_net); 506 out_nf_conntrack: 507 return -ENOMEM; 508 } 509 510 static void nf_conntrack_standalone_fini_proc(struct net *net) 511 { 512 remove_proc_entry("nf_conntrack", net->proc_net_stat); 513 remove_proc_entry("nf_conntrack", net->proc_net); 514 } 515 #else 516 static int nf_conntrack_standalone_init_proc(struct net *net) 517 { 518 return 0; 519 } 520 521 static void nf_conntrack_standalone_fini_proc(struct net *net) 522 { 523 } 524 #endif /* CONFIG_NF_CONNTRACK_PROCFS */ 525 526 /* Sysctl support */ 527 528 #ifdef CONFIG_SYSCTL 529 /* Log invalid packets of a given protocol */ 530 static int log_invalid_proto_min __read_mostly; 531 static int log_invalid_proto_max __read_mostly = 255; 532 533 /* size the user *wants to set */ 534 static unsigned int nf_conntrack_htable_size_user __read_mostly; 535 536 static int 537 nf_conntrack_hash_sysctl(struct ctl_table *table, int write, 538 void __user *buffer, size_t *lenp, loff_t *ppos) 539 { 540 int ret; 541 542 ret = proc_dointvec(table, write, buffer, lenp, ppos); 543 if (ret < 0 || !write) 544 return ret; 545 546 /* update ret, we might not be able to satisfy request */ 547 ret = nf_conntrack_hash_resize(nf_conntrack_htable_size_user); 548 549 /* update it to the actual value used by conntrack */ 550 nf_conntrack_htable_size_user = nf_conntrack_htable_size; 551 return ret; 552 } 553 554 static struct ctl_table_header *nf_ct_netfilter_header; 555 556 static struct ctl_table nf_ct_sysctl_table[] = { 557 { 558 .procname = "nf_conntrack_max", 559 .data = &nf_conntrack_max, 560 .maxlen = sizeof(int), 561 .mode = 0644, 562 .proc_handler = proc_dointvec, 563 }, 564 { 565 .procname = "nf_conntrack_count", 566 .data = &init_net.ct.count, 567 .maxlen = sizeof(int), 568 .mode = 0444, 569 .proc_handler = proc_dointvec, 570 }, 571 { 572 .procname = "nf_conntrack_buckets", 573 .data = &nf_conntrack_htable_size_user, 574 .maxlen = sizeof(unsigned int), 575 .mode = 0644, 576 .proc_handler = nf_conntrack_hash_sysctl, 577 }, 578 { 579 .procname = "nf_conntrack_checksum", 580 .data = &init_net.ct.sysctl_checksum, 581 .maxlen = sizeof(unsigned int), 582 .mode = 0644, 583 .proc_handler = proc_dointvec, 584 }, 585 { 586 .procname = "nf_conntrack_log_invalid", 587 .data = &init_net.ct.sysctl_log_invalid, 588 .maxlen = sizeof(unsigned int), 589 .mode = 0644, 590 .proc_handler = proc_dointvec_minmax, 591 .extra1 = &log_invalid_proto_min, 592 .extra2 = &log_invalid_proto_max, 593 }, 594 { 595 .procname = "nf_conntrack_expect_max", 596 .data = &nf_ct_expect_max, 597 .maxlen = sizeof(int), 598 .mode = 0644, 599 .proc_handler = proc_dointvec, 600 }, 601 { } 602 }; 603 604 static struct ctl_table nf_ct_netfilter_table[] = { 605 { 606 .procname = "nf_conntrack_max", 607 .data = &nf_conntrack_max, 608 .maxlen = sizeof(int), 609 .mode = 0644, 610 .proc_handler = proc_dointvec, 611 }, 612 { } 613 }; 614 615 static int nf_conntrack_standalone_init_sysctl(struct net *net) 616 { 617 struct ctl_table *table; 618 619 table = kmemdup(nf_ct_sysctl_table, sizeof(nf_ct_sysctl_table), 620 GFP_KERNEL); 621 if (!table) 622 goto out_kmemdup; 623 624 table[1].data = &net->ct.count; 625 table[3].data = &net->ct.sysctl_checksum; 626 table[4].data = &net->ct.sysctl_log_invalid; 627 628 /* Don't export sysctls to unprivileged users */ 629 if (net->user_ns != &init_user_ns) 630 table[0].procname = NULL; 631 632 if (!net_eq(&init_net, net)) 633 table[2].mode = 0444; 634 635 net->ct.sysctl_header = register_net_sysctl(net, "net/netfilter", table); 636 if (!net->ct.sysctl_header) 637 goto out_unregister_netfilter; 638 639 return 0; 640 641 out_unregister_netfilter: 642 kfree(table); 643 out_kmemdup: 644 return -ENOMEM; 645 } 646 647 static void nf_conntrack_standalone_fini_sysctl(struct net *net) 648 { 649 struct ctl_table *table; 650 651 table = net->ct.sysctl_header->ctl_table_arg; 652 unregister_net_sysctl_table(net->ct.sysctl_header); 653 kfree(table); 654 } 655 #else 656 static int nf_conntrack_standalone_init_sysctl(struct net *net) 657 { 658 return 0; 659 } 660 661 static void nf_conntrack_standalone_fini_sysctl(struct net *net) 662 { 663 } 664 #endif /* CONFIG_SYSCTL */ 665 666 static int nf_conntrack_pernet_init(struct net *net) 667 { 668 int ret; 669 670 ret = nf_conntrack_init_net(net); 671 if (ret < 0) 672 goto out_init; 673 674 ret = nf_conntrack_standalone_init_proc(net); 675 if (ret < 0) 676 goto out_proc; 677 678 net->ct.sysctl_checksum = 1; 679 net->ct.sysctl_log_invalid = 0; 680 ret = nf_conntrack_standalone_init_sysctl(net); 681 if (ret < 0) 682 goto out_sysctl; 683 684 return 0; 685 686 out_sysctl: 687 nf_conntrack_standalone_fini_proc(net); 688 out_proc: 689 nf_conntrack_cleanup_net(net); 690 out_init: 691 return ret; 692 } 693 694 static void nf_conntrack_pernet_exit(struct list_head *net_exit_list) 695 { 696 struct net *net; 697 698 list_for_each_entry(net, net_exit_list, exit_list) { 699 nf_conntrack_standalone_fini_sysctl(net); 700 nf_conntrack_standalone_fini_proc(net); 701 } 702 nf_conntrack_cleanup_net_list(net_exit_list); 703 } 704 705 static struct pernet_operations nf_conntrack_net_ops = { 706 .init = nf_conntrack_pernet_init, 707 .exit_batch = nf_conntrack_pernet_exit, 708 }; 709 710 static int __init nf_conntrack_standalone_init(void) 711 { 712 int ret = nf_conntrack_init_start(); 713 if (ret < 0) 714 goto out_start; 715 716 BUILD_BUG_ON(SKB_NFCT_PTRMASK != NFCT_PTRMASK); 717 BUILD_BUG_ON(NFCT_INFOMASK <= IP_CT_NUMBER); 718 719 #ifdef CONFIG_SYSCTL 720 nf_ct_netfilter_header = 721 register_net_sysctl(&init_net, "net", nf_ct_netfilter_table); 722 if (!nf_ct_netfilter_header) { 723 pr_err("nf_conntrack: can't register to sysctl.\n"); 724 ret = -ENOMEM; 725 goto out_sysctl; 726 } 727 728 nf_conntrack_htable_size_user = nf_conntrack_htable_size; 729 #endif 730 731 ret = register_pernet_subsys(&nf_conntrack_net_ops); 732 if (ret < 0) 733 goto out_pernet; 734 735 nf_conntrack_init_end(); 736 return 0; 737 738 out_pernet: 739 #ifdef CONFIG_SYSCTL 740 unregister_net_sysctl_table(nf_ct_netfilter_header); 741 out_sysctl: 742 #endif 743 nf_conntrack_cleanup_end(); 744 out_start: 745 return ret; 746 } 747 748 static void __exit nf_conntrack_standalone_fini(void) 749 { 750 nf_conntrack_cleanup_start(); 751 unregister_pernet_subsys(&nf_conntrack_net_ops); 752 #ifdef CONFIG_SYSCTL 753 unregister_net_sysctl_table(nf_ct_netfilter_header); 754 #endif 755 nf_conntrack_cleanup_end(); 756 } 757 758 module_init(nf_conntrack_standalone_init); 759 module_exit(nf_conntrack_standalone_fini); 760 761 /* Some modules need us, but don't depend directly on any symbol. 762 They should call this. */ 763 void need_conntrack(void) 764 { 765 } 766 EXPORT_SYMBOL_GPL(need_conntrack); 767