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