linux/net/netfilter/nf_synproxy_core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2013 Patrick McHardy <kaber@trash.net>
   4 */
   5
   6#include <linux/module.h>
   7#include <linux/skbuff.h>
   8#include <asm/unaligned.h>
   9#include <net/tcp.h>
  10#include <net/netns/generic.h>
  11#include <linux/proc_fs.h>
  12
  13#include <linux/netfilter_ipv6.h>
  14#include <linux/netfilter/nf_synproxy.h>
  15
  16#include <net/netfilter/nf_conntrack.h>
  17#include <net/netfilter/nf_conntrack_ecache.h>
  18#include <net/netfilter/nf_conntrack_extend.h>
  19#include <net/netfilter/nf_conntrack_seqadj.h>
  20#include <net/netfilter/nf_conntrack_synproxy.h>
  21#include <net/netfilter/nf_conntrack_zones.h>
  22#include <net/netfilter/nf_synproxy.h>
  23
  24unsigned int synproxy_net_id;
  25EXPORT_SYMBOL_GPL(synproxy_net_id);
  26
  27bool
  28synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
  29                       const struct tcphdr *th, struct synproxy_options *opts)
  30{
  31        int length = (th->doff * 4) - sizeof(*th);
  32        u8 buf[40], *ptr;
  33
  34        ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf);
  35        if (ptr == NULL)
  36                return false;
  37
  38        opts->options = 0;
  39        while (length > 0) {
  40                int opcode = *ptr++;
  41                int opsize;
  42
  43                switch (opcode) {
  44                case TCPOPT_EOL:
  45                        return true;
  46                case TCPOPT_NOP:
  47                        length--;
  48                        continue;
  49                default:
  50                        opsize = *ptr++;
  51                        if (opsize < 2)
  52                                return true;
  53                        if (opsize > length)
  54                                return true;
  55
  56                        switch (opcode) {
  57                        case TCPOPT_MSS:
  58                                if (opsize == TCPOLEN_MSS) {
  59                                        opts->mss_option = get_unaligned_be16(ptr);
  60                                        opts->options |= NF_SYNPROXY_OPT_MSS;
  61                                }
  62                                break;
  63                        case TCPOPT_WINDOW:
  64                                if (opsize == TCPOLEN_WINDOW) {
  65                                        opts->wscale = *ptr;
  66                                        if (opts->wscale > TCP_MAX_WSCALE)
  67                                                opts->wscale = TCP_MAX_WSCALE;
  68                                        opts->options |= NF_SYNPROXY_OPT_WSCALE;
  69                                }
  70                                break;
  71                        case TCPOPT_TIMESTAMP:
  72                                if (opsize == TCPOLEN_TIMESTAMP) {
  73                                        opts->tsval = get_unaligned_be32(ptr);
  74                                        opts->tsecr = get_unaligned_be32(ptr + 4);
  75                                        opts->options |= NF_SYNPROXY_OPT_TIMESTAMP;
  76                                }
  77                                break;
  78                        case TCPOPT_SACK_PERM:
  79                                if (opsize == TCPOLEN_SACK_PERM)
  80                                        opts->options |= NF_SYNPROXY_OPT_SACK_PERM;
  81                                break;
  82                        }
  83
  84                        ptr += opsize - 2;
  85                        length -= opsize;
  86                }
  87        }
  88        return true;
  89}
  90EXPORT_SYMBOL_GPL(synproxy_parse_options);
  91
  92static unsigned int
  93synproxy_options_size(const struct synproxy_options *opts)
  94{
  95        unsigned int size = 0;
  96
  97        if (opts->options & NF_SYNPROXY_OPT_MSS)
  98                size += TCPOLEN_MSS_ALIGNED;
  99        if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP)
 100                size += TCPOLEN_TSTAMP_ALIGNED;
 101        else if (opts->options & NF_SYNPROXY_OPT_SACK_PERM)
 102                size += TCPOLEN_SACKPERM_ALIGNED;
 103        if (opts->options & NF_SYNPROXY_OPT_WSCALE)
 104                size += TCPOLEN_WSCALE_ALIGNED;
 105
 106        return size;
 107}
 108
 109static void
 110synproxy_build_options(struct tcphdr *th, const struct synproxy_options *opts)
 111{
 112        __be32 *ptr = (__be32 *)(th + 1);
 113        u8 options = opts->options;
 114
 115        if (options & NF_SYNPROXY_OPT_MSS)
 116                *ptr++ = htonl((TCPOPT_MSS << 24) |
 117                               (TCPOLEN_MSS << 16) |
 118                               opts->mss_option);
 119
 120        if (options & NF_SYNPROXY_OPT_TIMESTAMP) {
 121                if (options & NF_SYNPROXY_OPT_SACK_PERM)
 122                        *ptr++ = htonl((TCPOPT_SACK_PERM << 24) |
 123                                       (TCPOLEN_SACK_PERM << 16) |
 124                                       (TCPOPT_TIMESTAMP << 8) |
 125                                       TCPOLEN_TIMESTAMP);
 126                else
 127                        *ptr++ = htonl((TCPOPT_NOP << 24) |
 128                                       (TCPOPT_NOP << 16) |
 129                                       (TCPOPT_TIMESTAMP << 8) |
 130                                       TCPOLEN_TIMESTAMP);
 131
 132                *ptr++ = htonl(opts->tsval);
 133                *ptr++ = htonl(opts->tsecr);
 134        } else if (options & NF_SYNPROXY_OPT_SACK_PERM)
 135                *ptr++ = htonl((TCPOPT_NOP << 24) |
 136                               (TCPOPT_NOP << 16) |
 137                               (TCPOPT_SACK_PERM << 8) |
 138                               TCPOLEN_SACK_PERM);
 139
 140        if (options & NF_SYNPROXY_OPT_WSCALE)
 141                *ptr++ = htonl((TCPOPT_NOP << 24) |
 142                               (TCPOPT_WINDOW << 16) |
 143                               (TCPOLEN_WINDOW << 8) |
 144                               opts->wscale);
 145}
 146
 147void synproxy_init_timestamp_cookie(const struct nf_synproxy_info *info,
 148                                    struct synproxy_options *opts)
 149{
 150        opts->tsecr = opts->tsval;
 151        opts->tsval = tcp_time_stamp_raw() & ~0x3f;
 152
 153        if (opts->options & NF_SYNPROXY_OPT_WSCALE) {
 154                opts->tsval |= opts->wscale;
 155                opts->wscale = info->wscale;
 156        } else
 157                opts->tsval |= 0xf;
 158
 159        if (opts->options & NF_SYNPROXY_OPT_SACK_PERM)
 160                opts->tsval |= 1 << 4;
 161
 162        if (opts->options & NF_SYNPROXY_OPT_ECN)
 163                opts->tsval |= 1 << 5;
 164}
 165EXPORT_SYMBOL_GPL(synproxy_init_timestamp_cookie);
 166
 167static void
 168synproxy_check_timestamp_cookie(struct synproxy_options *opts)
 169{
 170        opts->wscale = opts->tsecr & 0xf;
 171        if (opts->wscale != 0xf)
 172                opts->options |= NF_SYNPROXY_OPT_WSCALE;
 173
 174        opts->options |= opts->tsecr & (1 << 4) ? NF_SYNPROXY_OPT_SACK_PERM : 0;
 175
 176        opts->options |= opts->tsecr & (1 << 5) ? NF_SYNPROXY_OPT_ECN : 0;
 177}
 178
 179static unsigned int
 180synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff,
 181                       struct tcphdr *th, struct nf_conn *ct,
 182                       enum ip_conntrack_info ctinfo,
 183                       const struct nf_conn_synproxy *synproxy)
 184{
 185        unsigned int optoff, optend;
 186        __be32 *ptr, old;
 187
 188        if (synproxy->tsoff == 0)
 189                return 1;
 190
 191        optoff = protoff + sizeof(struct tcphdr);
 192        optend = protoff + th->doff * 4;
 193
 194        if (skb_ensure_writable(skb, optend))
 195                return 0;
 196
 197        while (optoff < optend) {
 198                unsigned char *op = skb->data + optoff;
 199
 200                switch (op[0]) {
 201                case TCPOPT_EOL:
 202                        return 1;
 203                case TCPOPT_NOP:
 204                        optoff++;
 205                        continue;
 206                default:
 207                        if (optoff + 1 == optend ||
 208                            optoff + op[1] > optend ||
 209                            op[1] < 2)
 210                                return 0;
 211                        if (op[0] == TCPOPT_TIMESTAMP &&
 212                            op[1] == TCPOLEN_TIMESTAMP) {
 213                                if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
 214                                        ptr = (__be32 *)&op[2];
 215                                        old = *ptr;
 216                                        *ptr = htonl(ntohl(*ptr) -
 217                                                     synproxy->tsoff);
 218                                } else {
 219                                        ptr = (__be32 *)&op[6];
 220                                        old = *ptr;
 221                                        *ptr = htonl(ntohl(*ptr) +
 222                                                     synproxy->tsoff);
 223                                }
 224                                inet_proto_csum_replace4(&th->check, skb,
 225                                                         old, *ptr, false);
 226                                return 1;
 227                        }
 228                        optoff += op[1];
 229                }
 230        }
 231        return 1;
 232}
 233
 234static struct nf_ct_ext_type nf_ct_synproxy_extend __read_mostly = {
 235        .len            = sizeof(struct nf_conn_synproxy),
 236        .align          = __alignof__(struct nf_conn_synproxy),
 237        .id             = NF_CT_EXT_SYNPROXY,
 238};
 239
 240#ifdef CONFIG_PROC_FS
 241static void *synproxy_cpu_seq_start(struct seq_file *seq, loff_t *pos)
 242{
 243        struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
 244        int cpu;
 245
 246        if (*pos == 0)
 247                return SEQ_START_TOKEN;
 248
 249        for (cpu = *pos - 1; cpu < nr_cpu_ids; cpu++) {
 250                if (!cpu_possible(cpu))
 251                        continue;
 252                *pos = cpu + 1;
 253                return per_cpu_ptr(snet->stats, cpu);
 254        }
 255
 256        return NULL;
 257}
 258
 259static void *synproxy_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 260{
 261        struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
 262        int cpu;
 263
 264        for (cpu = *pos; cpu < nr_cpu_ids; cpu++) {
 265                if (!cpu_possible(cpu))
 266                        continue;
 267                *pos = cpu + 1;
 268                return per_cpu_ptr(snet->stats, cpu);
 269        }
 270        (*pos)++;
 271        return NULL;
 272}
 273
 274static void synproxy_cpu_seq_stop(struct seq_file *seq, void *v)
 275{
 276        return;
 277}
 278
 279static int synproxy_cpu_seq_show(struct seq_file *seq, void *v)
 280{
 281        struct synproxy_stats *stats = v;
 282
 283        if (v == SEQ_START_TOKEN) {
 284                seq_puts(seq, "entries\t\tsyn_received\t"
 285                              "cookie_invalid\tcookie_valid\t"
 286                              "cookie_retrans\tconn_reopened\n");
 287                return 0;
 288        }
 289
 290        seq_printf(seq, "%08x\t%08x\t%08x\t%08x\t%08x\t%08x\n", 0,
 291                   stats->syn_received,
 292                   stats->cookie_invalid,
 293                   stats->cookie_valid,
 294                   stats->cookie_retrans,
 295                   stats->conn_reopened);
 296
 297        return 0;
 298}
 299
 300static const struct seq_operations synproxy_cpu_seq_ops = {
 301        .start          = synproxy_cpu_seq_start,
 302        .next           = synproxy_cpu_seq_next,
 303        .stop           = synproxy_cpu_seq_stop,
 304        .show           = synproxy_cpu_seq_show,
 305};
 306
 307static int __net_init synproxy_proc_init(struct net *net)
 308{
 309        if (!proc_create_net("synproxy", 0444, net->proc_net_stat,
 310                        &synproxy_cpu_seq_ops, sizeof(struct seq_net_private)))
 311                return -ENOMEM;
 312        return 0;
 313}
 314
 315static void __net_exit synproxy_proc_exit(struct net *net)
 316{
 317        remove_proc_entry("synproxy", net->proc_net_stat);
 318}
 319#else
 320static int __net_init synproxy_proc_init(struct net *net)
 321{
 322        return 0;
 323}
 324
 325static void __net_exit synproxy_proc_exit(struct net *net)
 326{
 327        return;
 328}
 329#endif /* CONFIG_PROC_FS */
 330
 331static int __net_init synproxy_net_init(struct net *net)
 332{
 333        struct synproxy_net *snet = synproxy_pernet(net);
 334        struct nf_conn *ct;
 335        int err = -ENOMEM;
 336
 337        ct = nf_ct_tmpl_alloc(net, &nf_ct_zone_dflt, GFP_KERNEL);
 338        if (!ct)
 339                goto err1;
 340
 341        if (!nfct_seqadj_ext_add(ct))
 342                goto err2;
 343        if (!nfct_synproxy_ext_add(ct))
 344                goto err2;
 345
 346        __set_bit(IPS_CONFIRMED_BIT, &ct->status);
 347        nf_conntrack_get(&ct->ct_general);
 348        snet->tmpl = ct;
 349
 350        snet->stats = alloc_percpu(struct synproxy_stats);
 351        if (snet->stats == NULL)
 352                goto err2;
 353
 354        err = synproxy_proc_init(net);
 355        if (err < 0)
 356                goto err3;
 357
 358        return 0;
 359
 360err3:
 361        free_percpu(snet->stats);
 362err2:
 363        nf_ct_tmpl_free(ct);
 364err1:
 365        return err;
 366}
 367
 368static void __net_exit synproxy_net_exit(struct net *net)
 369{
 370        struct synproxy_net *snet = synproxy_pernet(net);
 371
 372        nf_ct_put(snet->tmpl);
 373        synproxy_proc_exit(net);
 374        free_percpu(snet->stats);
 375}
 376
 377static struct pernet_operations synproxy_net_ops = {
 378        .init           = synproxy_net_init,
 379        .exit           = synproxy_net_exit,
 380        .id             = &synproxy_net_id,
 381        .size           = sizeof(struct synproxy_net),
 382};
 383
 384static int __init synproxy_core_init(void)
 385{
 386        int err;
 387
 388        err = nf_ct_extend_register(&nf_ct_synproxy_extend);
 389        if (err < 0)
 390                goto err1;
 391
 392        err = register_pernet_subsys(&synproxy_net_ops);
 393        if (err < 0)
 394                goto err2;
 395
 396        return 0;
 397
 398err2:
 399        nf_ct_extend_unregister(&nf_ct_synproxy_extend);
 400err1:
 401        return err;
 402}
 403
 404static void __exit synproxy_core_exit(void)
 405{
 406        unregister_pernet_subsys(&synproxy_net_ops);
 407        nf_ct_extend_unregister(&nf_ct_synproxy_extend);
 408}
 409
 410module_init(synproxy_core_init);
 411module_exit(synproxy_core_exit);
 412
 413static struct iphdr *
 414synproxy_build_ip(struct net *net, struct sk_buff *skb, __be32 saddr,
 415                  __be32 daddr)
 416{
 417        struct iphdr *iph;
 418
 419        skb_reset_network_header(skb);
 420        iph = skb_put(skb, sizeof(*iph));
 421        iph->version    = 4;
 422        iph->ihl        = sizeof(*iph) / 4;
 423        iph->tos        = 0;
 424        iph->id         = 0;
 425        iph->frag_off   = htons(IP_DF);
 426        iph->ttl        = net->ipv4.sysctl_ip_default_ttl;
 427        iph->protocol   = IPPROTO_TCP;
 428        iph->check      = 0;
 429        iph->saddr      = saddr;
 430        iph->daddr      = daddr;
 431
 432        return iph;
 433}
 434
 435static void
 436synproxy_send_tcp(struct net *net,
 437                  const struct sk_buff *skb, struct sk_buff *nskb,
 438                  struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo,
 439                  struct iphdr *niph, struct tcphdr *nth,
 440                  unsigned int tcp_hdr_size)
 441{
 442        nth->check = ~tcp_v4_check(tcp_hdr_size, niph->saddr, niph->daddr, 0);
 443        nskb->ip_summed   = CHECKSUM_PARTIAL;
 444        nskb->csum_start  = (unsigned char *)nth - nskb->head;
 445        nskb->csum_offset = offsetof(struct tcphdr, check);
 446
 447        skb_dst_set_noref(nskb, skb_dst(skb));
 448        nskb->protocol = htons(ETH_P_IP);
 449        if (ip_route_me_harder(net, nskb, RTN_UNSPEC))
 450                goto free_nskb;
 451
 452        if (nfct) {
 453                nf_ct_set(nskb, (struct nf_conn *)nfct, ctinfo);
 454                nf_conntrack_get(nfct);
 455        }
 456
 457        ip_local_out(net, nskb->sk, nskb);
 458        return;
 459
 460free_nskb:
 461        kfree_skb(nskb);
 462}
 463
 464void
 465synproxy_send_client_synack(struct net *net,
 466                            const struct sk_buff *skb, const struct tcphdr *th,
 467                            const struct synproxy_options *opts)
 468{
 469        struct sk_buff *nskb;
 470        struct iphdr *iph, *niph;
 471        struct tcphdr *nth;
 472        unsigned int tcp_hdr_size;
 473        u16 mss = opts->mss_encode;
 474
 475        iph = ip_hdr(skb);
 476
 477        tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
 478        nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
 479                         GFP_ATOMIC);
 480        if (!nskb)
 481                return;
 482        skb_reserve(nskb, MAX_TCP_HEADER);
 483
 484        niph = synproxy_build_ip(net, nskb, iph->daddr, iph->saddr);
 485
 486        skb_reset_transport_header(nskb);
 487        nth = skb_put(nskb, tcp_hdr_size);
 488        nth->source     = th->dest;
 489        nth->dest       = th->source;
 490        nth->seq        = htonl(__cookie_v4_init_sequence(iph, th, &mss));
 491        nth->ack_seq    = htonl(ntohl(th->seq) + 1);
 492        tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK;
 493        if (opts->options & NF_SYNPROXY_OPT_ECN)
 494                tcp_flag_word(nth) |= TCP_FLAG_ECE;
 495        nth->doff       = tcp_hdr_size / 4;
 496        nth->window     = 0;
 497        nth->check      = 0;
 498        nth->urg_ptr    = 0;
 499
 500        synproxy_build_options(nth, opts);
 501
 502        synproxy_send_tcp(net, skb, nskb, skb_nfct(skb),
 503                          IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size);
 504}
 505EXPORT_SYMBOL_GPL(synproxy_send_client_synack);
 506
 507static void
 508synproxy_send_server_syn(struct net *net,
 509                         const struct sk_buff *skb, const struct tcphdr *th,
 510                         const struct synproxy_options *opts, u32 recv_seq)
 511{
 512        struct synproxy_net *snet = synproxy_pernet(net);
 513        struct sk_buff *nskb;
 514        struct iphdr *iph, *niph;
 515        struct tcphdr *nth;
 516        unsigned int tcp_hdr_size;
 517
 518        iph = ip_hdr(skb);
 519
 520        tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
 521        nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
 522                         GFP_ATOMIC);
 523        if (!nskb)
 524                return;
 525        skb_reserve(nskb, MAX_TCP_HEADER);
 526
 527        niph = synproxy_build_ip(net, nskb, iph->saddr, iph->daddr);
 528
 529        skb_reset_transport_header(nskb);
 530        nth = skb_put(nskb, tcp_hdr_size);
 531        nth->source     = th->source;
 532        nth->dest       = th->dest;
 533        nth->seq        = htonl(recv_seq - 1);
 534        /* ack_seq is used to relay our ISN to the synproxy hook to initialize
 535         * sequence number translation once a connection tracking entry exists.
 536         */
 537        nth->ack_seq    = htonl(ntohl(th->ack_seq) - 1);
 538        tcp_flag_word(nth) = TCP_FLAG_SYN;
 539        if (opts->options & NF_SYNPROXY_OPT_ECN)
 540                tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR;
 541        nth->doff       = tcp_hdr_size / 4;
 542        nth->window     = th->window;
 543        nth->check      = 0;
 544        nth->urg_ptr    = 0;
 545
 546        synproxy_build_options(nth, opts);
 547
 548        synproxy_send_tcp(net, skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW,
 549                          niph, nth, tcp_hdr_size);
 550}
 551
 552static void
 553synproxy_send_server_ack(struct net *net,
 554                         const struct ip_ct_tcp *state,
 555                         const struct sk_buff *skb, const struct tcphdr *th,
 556                         const struct synproxy_options *opts)
 557{
 558        struct sk_buff *nskb;
 559        struct iphdr *iph, *niph;
 560        struct tcphdr *nth;
 561        unsigned int tcp_hdr_size;
 562
 563        iph = ip_hdr(skb);
 564
 565        tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
 566        nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
 567                         GFP_ATOMIC);
 568        if (!nskb)
 569                return;
 570        skb_reserve(nskb, MAX_TCP_HEADER);
 571
 572        niph = synproxy_build_ip(net, nskb, iph->daddr, iph->saddr);
 573
 574        skb_reset_transport_header(nskb);
 575        nth = skb_put(nskb, tcp_hdr_size);
 576        nth->source     = th->dest;
 577        nth->dest       = th->source;
 578        nth->seq        = htonl(ntohl(th->ack_seq));
 579        nth->ack_seq    = htonl(ntohl(th->seq) + 1);
 580        tcp_flag_word(nth) = TCP_FLAG_ACK;
 581        nth->doff       = tcp_hdr_size / 4;
 582        nth->window     = htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin);
 583        nth->check      = 0;
 584        nth->urg_ptr    = 0;
 585
 586        synproxy_build_options(nth, opts);
 587
 588        synproxy_send_tcp(net, skb, nskb, NULL, 0, niph, nth, tcp_hdr_size);
 589}
 590
 591static void
 592synproxy_send_client_ack(struct net *net,
 593                         const struct sk_buff *skb, const struct tcphdr *th,
 594                         const struct synproxy_options *opts)
 595{
 596        struct sk_buff *nskb;
 597        struct iphdr *iph, *niph;
 598        struct tcphdr *nth;
 599        unsigned int tcp_hdr_size;
 600
 601        iph = ip_hdr(skb);
 602
 603        tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
 604        nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
 605                         GFP_ATOMIC);
 606        if (!nskb)
 607                return;
 608        skb_reserve(nskb, MAX_TCP_HEADER);
 609
 610        niph = synproxy_build_ip(net, nskb, iph->saddr, iph->daddr);
 611
 612        skb_reset_transport_header(nskb);
 613        nth = skb_put(nskb, tcp_hdr_size);
 614        nth->source     = th->source;
 615        nth->dest       = th->dest;
 616        nth->seq        = htonl(ntohl(th->seq) + 1);
 617        nth->ack_seq    = th->ack_seq;
 618        tcp_flag_word(nth) = TCP_FLAG_ACK;
 619        nth->doff       = tcp_hdr_size / 4;
 620        nth->window     = htons(ntohs(th->window) >> opts->wscale);
 621        nth->check      = 0;
 622        nth->urg_ptr    = 0;
 623
 624        synproxy_build_options(nth, opts);
 625
 626        synproxy_send_tcp(net, skb, nskb, skb_nfct(skb),
 627                          IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size);
 628}
 629
 630bool
 631synproxy_recv_client_ack(struct net *net,
 632                         const struct sk_buff *skb, const struct tcphdr *th,
 633                         struct synproxy_options *opts, u32 recv_seq)
 634{
 635        struct synproxy_net *snet = synproxy_pernet(net);
 636        int mss;
 637
 638        mss = __cookie_v4_check(ip_hdr(skb), th, ntohl(th->ack_seq) - 1);
 639        if (mss == 0) {
 640                this_cpu_inc(snet->stats->cookie_invalid);
 641                return false;
 642        }
 643
 644        this_cpu_inc(snet->stats->cookie_valid);
 645        opts->mss_option = mss;
 646        opts->options |= NF_SYNPROXY_OPT_MSS;
 647
 648        if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP)
 649                synproxy_check_timestamp_cookie(opts);
 650
 651        synproxy_send_server_syn(net, skb, th, opts, recv_seq);
 652        return true;
 653}
 654EXPORT_SYMBOL_GPL(synproxy_recv_client_ack);
 655
 656unsigned int
 657ipv4_synproxy_hook(void *priv, struct sk_buff *skb,
 658                   const struct nf_hook_state *nhs)
 659{
 660        struct net *net = nhs->net;
 661        struct synproxy_net *snet = synproxy_pernet(net);
 662        enum ip_conntrack_info ctinfo;
 663        struct nf_conn *ct;
 664        struct nf_conn_synproxy *synproxy;
 665        struct synproxy_options opts = {};
 666        const struct ip_ct_tcp *state;
 667        struct tcphdr *th, _th;
 668        unsigned int thoff;
 669
 670        ct = nf_ct_get(skb, &ctinfo);
 671        if (!ct)
 672                return NF_ACCEPT;
 673
 674        synproxy = nfct_synproxy(ct);
 675        if (!synproxy)
 676                return NF_ACCEPT;
 677
 678        if (nf_is_loopback_packet(skb) ||
 679            ip_hdr(skb)->protocol != IPPROTO_TCP)
 680                return NF_ACCEPT;
 681
 682        thoff = ip_hdrlen(skb);
 683        th = skb_header_pointer(skb, thoff, sizeof(_th), &_th);
 684        if (!th)
 685                return NF_DROP;
 686
 687        state = &ct->proto.tcp;
 688        switch (state->state) {
 689        case TCP_CONNTRACK_CLOSE:
 690                if (th->rst && CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) {
 691                        nf_ct_seqadj_init(ct, ctinfo, synproxy->isn -
 692                                                      ntohl(th->seq) + 1);
 693                        break;
 694                }
 695
 696                if (!th->syn || th->ack ||
 697                    CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
 698                        break;
 699
 700                /* Reopened connection - reset the sequence number and timestamp
 701                 * adjustments, they will get initialized once the connection is
 702                 * reestablished.
 703                 */
 704                nf_ct_seqadj_init(ct, ctinfo, 0);
 705                synproxy->tsoff = 0;
 706                this_cpu_inc(snet->stats->conn_reopened);
 707
 708                /* fall through */
 709        case TCP_CONNTRACK_SYN_SENT:
 710                if (!synproxy_parse_options(skb, thoff, th, &opts))
 711                        return NF_DROP;
 712
 713                if (!th->syn && th->ack &&
 714                    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
 715                        /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
 716                         * therefore we need to add 1 to make the SYN sequence
 717                         * number match the one of first SYN.
 718                         */
 719                        if (synproxy_recv_client_ack(net, skb, th, &opts,
 720                                                     ntohl(th->seq) + 1)) {
 721                                this_cpu_inc(snet->stats->cookie_retrans);
 722                                consume_skb(skb);
 723                                return NF_STOLEN;
 724                        } else {
 725                                return NF_DROP;
 726                        }
 727                }
 728
 729                synproxy->isn = ntohl(th->ack_seq);
 730                if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP)
 731                        synproxy->its = opts.tsecr;
 732
 733                nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
 734                break;
 735        case TCP_CONNTRACK_SYN_RECV:
 736                if (!th->syn || !th->ack)
 737                        break;
 738
 739                if (!synproxy_parse_options(skb, thoff, th, &opts))
 740                        return NF_DROP;
 741
 742                if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) {
 743                        synproxy->tsoff = opts.tsval - synproxy->its;
 744                        nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
 745                }
 746
 747                opts.options &= ~(NF_SYNPROXY_OPT_MSS |
 748                                  NF_SYNPROXY_OPT_WSCALE |
 749                                  NF_SYNPROXY_OPT_SACK_PERM);
 750
 751                swap(opts.tsval, opts.tsecr);
 752                synproxy_send_server_ack(net, state, skb, th, &opts);
 753
 754                nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq));
 755                nf_conntrack_event_cache(IPCT_SEQADJ, ct);
 756
 757                swap(opts.tsval, opts.tsecr);
 758                synproxy_send_client_ack(net, skb, th, &opts);
 759
 760                consume_skb(skb);
 761                return NF_STOLEN;
 762        default:
 763                break;
 764        }
 765
 766        synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy);
 767        return NF_ACCEPT;
 768}
 769EXPORT_SYMBOL_GPL(ipv4_synproxy_hook);
 770
 771static const struct nf_hook_ops ipv4_synproxy_ops[] = {
 772        {
 773                .hook           = ipv4_synproxy_hook,
 774                .pf             = NFPROTO_IPV4,
 775                .hooknum        = NF_INET_LOCAL_IN,
 776                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM - 1,
 777        },
 778        {
 779                .hook           = ipv4_synproxy_hook,
 780                .pf             = NFPROTO_IPV4,
 781                .hooknum        = NF_INET_POST_ROUTING,
 782                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM - 1,
 783        },
 784};
 785
 786int nf_synproxy_ipv4_init(struct synproxy_net *snet, struct net *net)
 787{
 788        int err;
 789
 790        if (snet->hook_ref4 == 0) {
 791                err = nf_register_net_hooks(net, ipv4_synproxy_ops,
 792                                            ARRAY_SIZE(ipv4_synproxy_ops));
 793                if (err)
 794                        return err;
 795        }
 796
 797        snet->hook_ref4++;
 798        return 0;
 799}
 800EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_init);
 801
 802void nf_synproxy_ipv4_fini(struct synproxy_net *snet, struct net *net)
 803{
 804        snet->hook_ref4--;
 805        if (snet->hook_ref4 == 0)
 806                nf_unregister_net_hooks(net, ipv4_synproxy_ops,
 807                                        ARRAY_SIZE(ipv4_synproxy_ops));
 808}
 809EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_fini);
 810
 811#if IS_ENABLED(CONFIG_IPV6)
 812static struct ipv6hdr *
 813synproxy_build_ip_ipv6(struct net *net, struct sk_buff *skb,
 814                       const struct in6_addr *saddr,
 815                       const struct in6_addr *daddr)
 816{
 817        struct ipv6hdr *iph;
 818
 819        skb_reset_network_header(skb);
 820        iph = skb_put(skb, sizeof(*iph));
 821        ip6_flow_hdr(iph, 0, 0);
 822        iph->hop_limit  = net->ipv6.devconf_all->hop_limit;
 823        iph->nexthdr    = IPPROTO_TCP;
 824        iph->saddr      = *saddr;
 825        iph->daddr      = *daddr;
 826
 827        return iph;
 828}
 829
 830static void
 831synproxy_send_tcp_ipv6(struct net *net,
 832                       const struct sk_buff *skb, struct sk_buff *nskb,
 833                       struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo,
 834                       struct ipv6hdr *niph, struct tcphdr *nth,
 835                       unsigned int tcp_hdr_size)
 836{
 837        struct dst_entry *dst;
 838        struct flowi6 fl6;
 839        int err;
 840
 841        nth->check = ~tcp_v6_check(tcp_hdr_size, &niph->saddr, &niph->daddr, 0);
 842        nskb->ip_summed   = CHECKSUM_PARTIAL;
 843        nskb->csum_start  = (unsigned char *)nth - nskb->head;
 844        nskb->csum_offset = offsetof(struct tcphdr, check);
 845
 846        memset(&fl6, 0, sizeof(fl6));
 847        fl6.flowi6_proto = IPPROTO_TCP;
 848        fl6.saddr = niph->saddr;
 849        fl6.daddr = niph->daddr;
 850        fl6.fl6_sport = nth->source;
 851        fl6.fl6_dport = nth->dest;
 852        security_skb_classify_flow((struct sk_buff *)skb,
 853                                   flowi6_to_flowi(&fl6));
 854        err = nf_ip6_route(net, &dst, flowi6_to_flowi(&fl6), false);
 855        if (err) {
 856                goto free_nskb;
 857        }
 858
 859        dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
 860        if (IS_ERR(dst))
 861                goto free_nskb;
 862
 863        skb_dst_set(nskb, dst);
 864
 865        if (nfct) {
 866                nf_ct_set(nskb, (struct nf_conn *)nfct, ctinfo);
 867                nf_conntrack_get(nfct);
 868        }
 869
 870        ip6_local_out(net, nskb->sk, nskb);
 871        return;
 872
 873free_nskb:
 874        kfree_skb(nskb);
 875}
 876
 877void
 878synproxy_send_client_synack_ipv6(struct net *net,
 879                                 const struct sk_buff *skb,
 880                                 const struct tcphdr *th,
 881                                 const struct synproxy_options *opts)
 882{
 883        struct sk_buff *nskb;
 884        struct ipv6hdr *iph, *niph;
 885        struct tcphdr *nth;
 886        unsigned int tcp_hdr_size;
 887        u16 mss = opts->mss_encode;
 888
 889        iph = ipv6_hdr(skb);
 890
 891        tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
 892        nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
 893                         GFP_ATOMIC);
 894        if (!nskb)
 895                return;
 896        skb_reserve(nskb, MAX_TCP_HEADER);
 897
 898        niph = synproxy_build_ip_ipv6(net, nskb, &iph->daddr, &iph->saddr);
 899
 900        skb_reset_transport_header(nskb);
 901        nth = skb_put(nskb, tcp_hdr_size);
 902        nth->source     = th->dest;
 903        nth->dest       = th->source;
 904        nth->seq        = htonl(nf_ipv6_cookie_init_sequence(iph, th, &mss));
 905        nth->ack_seq    = htonl(ntohl(th->seq) + 1);
 906        tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK;
 907        if (opts->options & NF_SYNPROXY_OPT_ECN)
 908                tcp_flag_word(nth) |= TCP_FLAG_ECE;
 909        nth->doff       = tcp_hdr_size / 4;
 910        nth->window     = 0;
 911        nth->check      = 0;
 912        nth->urg_ptr    = 0;
 913
 914        synproxy_build_options(nth, opts);
 915
 916        synproxy_send_tcp_ipv6(net, skb, nskb, skb_nfct(skb),
 917                               IP_CT_ESTABLISHED_REPLY, niph, nth,
 918                               tcp_hdr_size);
 919}
 920EXPORT_SYMBOL_GPL(synproxy_send_client_synack_ipv6);
 921
 922static void
 923synproxy_send_server_syn_ipv6(struct net *net, const struct sk_buff *skb,
 924                              const struct tcphdr *th,
 925                              const struct synproxy_options *opts, u32 recv_seq)
 926{
 927        struct synproxy_net *snet = synproxy_pernet(net);
 928        struct sk_buff *nskb;
 929        struct ipv6hdr *iph, *niph;
 930        struct tcphdr *nth;
 931        unsigned int tcp_hdr_size;
 932
 933        iph = ipv6_hdr(skb);
 934
 935        tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
 936        nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
 937                         GFP_ATOMIC);
 938        if (!nskb)
 939                return;
 940        skb_reserve(nskb, MAX_TCP_HEADER);
 941
 942        niph = synproxy_build_ip_ipv6(net, nskb, &iph->saddr, &iph->daddr);
 943
 944        skb_reset_transport_header(nskb);
 945        nth = skb_put(nskb, tcp_hdr_size);
 946        nth->source     = th->source;
 947        nth->dest       = th->dest;
 948        nth->seq        = htonl(recv_seq - 1);
 949        /* ack_seq is used to relay our ISN to the synproxy hook to initialize
 950         * sequence number translation once a connection tracking entry exists.
 951         */
 952        nth->ack_seq    = htonl(ntohl(th->ack_seq) - 1);
 953        tcp_flag_word(nth) = TCP_FLAG_SYN;
 954        if (opts->options & NF_SYNPROXY_OPT_ECN)
 955                tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR;
 956        nth->doff       = tcp_hdr_size / 4;
 957        nth->window     = th->window;
 958        nth->check      = 0;
 959        nth->urg_ptr    = 0;
 960
 961        synproxy_build_options(nth, opts);
 962
 963        synproxy_send_tcp_ipv6(net, skb, nskb, &snet->tmpl->ct_general,
 964                               IP_CT_NEW, niph, nth, tcp_hdr_size);
 965}
 966
 967static void
 968synproxy_send_server_ack_ipv6(struct net *net, const struct ip_ct_tcp *state,
 969                              const struct sk_buff *skb,
 970                              const struct tcphdr *th,
 971                              const struct synproxy_options *opts)
 972{
 973        struct sk_buff *nskb;
 974        struct ipv6hdr *iph, *niph;
 975        struct tcphdr *nth;
 976        unsigned int tcp_hdr_size;
 977
 978        iph = ipv6_hdr(skb);
 979
 980        tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
 981        nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
 982                         GFP_ATOMIC);
 983        if (!nskb)
 984                return;
 985        skb_reserve(nskb, MAX_TCP_HEADER);
 986
 987        niph = synproxy_build_ip_ipv6(net, nskb, &iph->daddr, &iph->saddr);
 988
 989        skb_reset_transport_header(nskb);
 990        nth = skb_put(nskb, tcp_hdr_size);
 991        nth->source     = th->dest;
 992        nth->dest       = th->source;
 993        nth->seq        = htonl(ntohl(th->ack_seq));
 994        nth->ack_seq    = htonl(ntohl(th->seq) + 1);
 995        tcp_flag_word(nth) = TCP_FLAG_ACK;
 996        nth->doff       = tcp_hdr_size / 4;
 997        nth->window     = htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin);
 998        nth->check      = 0;
 999        nth->urg_ptr    = 0;
1000
1001        synproxy_build_options(nth, opts);
1002
1003        synproxy_send_tcp_ipv6(net, skb, nskb, NULL, 0, niph, nth,
1004                               tcp_hdr_size);
1005}
1006
1007static void
1008synproxy_send_client_ack_ipv6(struct net *net, const struct sk_buff *skb,
1009                              const struct tcphdr *th,
1010                              const struct synproxy_options *opts)
1011{
1012        struct sk_buff *nskb;
1013        struct ipv6hdr *iph, *niph;
1014        struct tcphdr *nth;
1015        unsigned int tcp_hdr_size;
1016
1017        iph = ipv6_hdr(skb);
1018
1019        tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
1020        nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
1021                         GFP_ATOMIC);
1022        if (!nskb)
1023                return;
1024        skb_reserve(nskb, MAX_TCP_HEADER);
1025
1026        niph = synproxy_build_ip_ipv6(net, nskb, &iph->saddr, &iph->daddr);
1027
1028        skb_reset_transport_header(nskb);
1029        nth = skb_put(nskb, tcp_hdr_size);
1030        nth->source     = th->source;
1031        nth->dest       = th->dest;
1032        nth->seq        = htonl(ntohl(th->seq) + 1);
1033        nth->ack_seq    = th->ack_seq;
1034        tcp_flag_word(nth) = TCP_FLAG_ACK;
1035        nth->doff       = tcp_hdr_size / 4;
1036        nth->window     = htons(ntohs(th->window) >> opts->wscale);
1037        nth->check      = 0;
1038        nth->urg_ptr    = 0;
1039
1040        synproxy_build_options(nth, opts);
1041
1042        synproxy_send_tcp_ipv6(net, skb, nskb, skb_nfct(skb),
1043                               IP_CT_ESTABLISHED_REPLY, niph, nth,
1044                               tcp_hdr_size);
1045}
1046
1047bool
1048synproxy_recv_client_ack_ipv6(struct net *net,
1049                              const struct sk_buff *skb,
1050                              const struct tcphdr *th,
1051                              struct synproxy_options *opts, u32 recv_seq)
1052{
1053        struct synproxy_net *snet = synproxy_pernet(net);
1054        int mss;
1055
1056        mss = nf_cookie_v6_check(ipv6_hdr(skb), th, ntohl(th->ack_seq) - 1);
1057        if (mss == 0) {
1058                this_cpu_inc(snet->stats->cookie_invalid);
1059                return false;
1060        }
1061
1062        this_cpu_inc(snet->stats->cookie_valid);
1063        opts->mss_option = mss;
1064        opts->options |= NF_SYNPROXY_OPT_MSS;
1065
1066        if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP)
1067                synproxy_check_timestamp_cookie(opts);
1068
1069        synproxy_send_server_syn_ipv6(net, skb, th, opts, recv_seq);
1070        return true;
1071}
1072EXPORT_SYMBOL_GPL(synproxy_recv_client_ack_ipv6);
1073
1074unsigned int
1075ipv6_synproxy_hook(void *priv, struct sk_buff *skb,
1076                   const struct nf_hook_state *nhs)
1077{
1078        struct net *net = nhs->net;
1079        struct synproxy_net *snet = synproxy_pernet(net);
1080        enum ip_conntrack_info ctinfo;
1081        struct nf_conn *ct;
1082        struct nf_conn_synproxy *synproxy;
1083        struct synproxy_options opts = {};
1084        const struct ip_ct_tcp *state;
1085        struct tcphdr *th, _th;
1086        __be16 frag_off;
1087        u8 nexthdr;
1088        int thoff;
1089
1090        ct = nf_ct_get(skb, &ctinfo);
1091        if (!ct)
1092                return NF_ACCEPT;
1093
1094        synproxy = nfct_synproxy(ct);
1095        if (!synproxy)
1096                return NF_ACCEPT;
1097
1098        if (nf_is_loopback_packet(skb))
1099                return NF_ACCEPT;
1100
1101        nexthdr = ipv6_hdr(skb)->nexthdr;
1102        thoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
1103                                 &frag_off);
1104        if (thoff < 0 || nexthdr != IPPROTO_TCP)
1105                return NF_ACCEPT;
1106
1107        th = skb_header_pointer(skb, thoff, sizeof(_th), &_th);
1108        if (!th)
1109                return NF_DROP;
1110
1111        state = &ct->proto.tcp;
1112        switch (state->state) {
1113        case TCP_CONNTRACK_CLOSE:
1114                if (th->rst && CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) {
1115                        nf_ct_seqadj_init(ct, ctinfo, synproxy->isn -
1116                                                      ntohl(th->seq) + 1);
1117                        break;
1118                }
1119
1120                if (!th->syn || th->ack ||
1121                    CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
1122                        break;
1123
1124                /* Reopened connection - reset the sequence number and timestamp
1125                 * adjustments, they will get initialized once the connection is
1126                 * reestablished.
1127                 */
1128                nf_ct_seqadj_init(ct, ctinfo, 0);
1129                synproxy->tsoff = 0;
1130                this_cpu_inc(snet->stats->conn_reopened);
1131
1132                /* fall through */
1133        case TCP_CONNTRACK_SYN_SENT:
1134                if (!synproxy_parse_options(skb, thoff, th, &opts))
1135                        return NF_DROP;
1136
1137                if (!th->syn && th->ack &&
1138                    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
1139                        /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
1140                         * therefore we need to add 1 to make the SYN sequence
1141                         * number match the one of first SYN.
1142                         */
1143                        if (synproxy_recv_client_ack_ipv6(net, skb, th, &opts,
1144                                                          ntohl(th->seq) + 1)) {
1145                                this_cpu_inc(snet->stats->cookie_retrans);
1146                                consume_skb(skb);
1147                                return NF_STOLEN;
1148                        } else {
1149                                return NF_DROP;
1150                        }
1151                }
1152
1153                synproxy->isn = ntohl(th->ack_seq);
1154                if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP)
1155                        synproxy->its = opts.tsecr;
1156
1157                nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
1158                break;
1159        case TCP_CONNTRACK_SYN_RECV:
1160                if (!th->syn || !th->ack)
1161                        break;
1162
1163                if (!synproxy_parse_options(skb, thoff, th, &opts))
1164                        return NF_DROP;
1165
1166                if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) {
1167                        synproxy->tsoff = opts.tsval - synproxy->its;
1168                        nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
1169                }
1170
1171                opts.options &= ~(NF_SYNPROXY_OPT_MSS |
1172                                  NF_SYNPROXY_OPT_WSCALE |
1173                                  NF_SYNPROXY_OPT_SACK_PERM);
1174
1175                swap(opts.tsval, opts.tsecr);
1176                synproxy_send_server_ack_ipv6(net, state, skb, th, &opts);
1177
1178                nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq));
1179                nf_conntrack_event_cache(IPCT_SEQADJ, ct);
1180
1181                swap(opts.tsval, opts.tsecr);
1182                synproxy_send_client_ack_ipv6(net, skb, th, &opts);
1183
1184                consume_skb(skb);
1185                return NF_STOLEN;
1186        default:
1187                break;
1188        }
1189
1190        synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy);
1191        return NF_ACCEPT;
1192}
1193EXPORT_SYMBOL_GPL(ipv6_synproxy_hook);
1194
1195static const struct nf_hook_ops ipv6_synproxy_ops[] = {
1196        {
1197                .hook           = ipv6_synproxy_hook,
1198                .pf             = NFPROTO_IPV6,
1199                .hooknum        = NF_INET_LOCAL_IN,
1200                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM - 1,
1201        },
1202        {
1203                .hook           = ipv6_synproxy_hook,
1204                .pf             = NFPROTO_IPV6,
1205                .hooknum        = NF_INET_POST_ROUTING,
1206                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM - 1,
1207        },
1208};
1209
1210int
1211nf_synproxy_ipv6_init(struct synproxy_net *snet, struct net *net)
1212{
1213        int err;
1214
1215        if (snet->hook_ref6 == 0) {
1216                err = nf_register_net_hooks(net, ipv6_synproxy_ops,
1217                                            ARRAY_SIZE(ipv6_synproxy_ops));
1218                if (err)
1219                        return err;
1220        }
1221
1222        snet->hook_ref6++;
1223        return 0;
1224}
1225EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_init);
1226
1227void
1228nf_synproxy_ipv6_fini(struct synproxy_net *snet, struct net *net)
1229{
1230        snet->hook_ref6--;
1231        if (snet->hook_ref6 == 0)
1232                nf_unregister_net_hooks(net, ipv6_synproxy_ops,
1233                                        ARRAY_SIZE(ipv6_synproxy_ops));
1234}
1235EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_fini);
1236#endif /* CONFIG_IPV6 */
1237
1238MODULE_LICENSE("GPL");
1239MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
1240