linux/net/ipv6/netfilter/ip6t_SYNPROXY.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 <net/ip6_checksum.h>
   9#include <net/ip6_route.h>
  10#include <net/tcp.h>
  11
  12#include <linux/netfilter_ipv6/ip6_tables.h>
  13#include <linux/netfilter/x_tables.h>
  14#include <linux/netfilter/xt_SYNPROXY.h>
  15#include <net/netfilter/nf_conntrack.h>
  16#include <net/netfilter/nf_conntrack_seqadj.h>
  17#include <net/netfilter/nf_conntrack_synproxy.h>
  18#include <net/netfilter/nf_conntrack_ecache.h>
  19
  20static struct ipv6hdr *
  21synproxy_build_ip(struct net *net, struct sk_buff *skb,
  22                  const struct in6_addr *saddr,
  23                  const struct in6_addr *daddr)
  24{
  25        struct ipv6hdr *iph;
  26
  27        skb_reset_network_header(skb);
  28        iph = skb_put(skb, sizeof(*iph));
  29        ip6_flow_hdr(iph, 0, 0);
  30        iph->hop_limit  = net->ipv6.devconf_all->hop_limit;
  31        iph->nexthdr    = IPPROTO_TCP;
  32        iph->saddr      = *saddr;
  33        iph->daddr      = *daddr;
  34
  35        return iph;
  36}
  37
  38static void
  39synproxy_send_tcp(struct net *net,
  40                  const struct sk_buff *skb, struct sk_buff *nskb,
  41                  struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo,
  42                  struct ipv6hdr *niph, struct tcphdr *nth,
  43                  unsigned int tcp_hdr_size)
  44{
  45        struct dst_entry *dst;
  46        struct flowi6 fl6;
  47
  48        nth->check = ~tcp_v6_check(tcp_hdr_size, &niph->saddr, &niph->daddr, 0);
  49        nskb->ip_summed   = CHECKSUM_PARTIAL;
  50        nskb->csum_start  = (unsigned char *)nth - nskb->head;
  51        nskb->csum_offset = offsetof(struct tcphdr, check);
  52
  53        memset(&fl6, 0, sizeof(fl6));
  54        fl6.flowi6_proto = IPPROTO_TCP;
  55        fl6.saddr = niph->saddr;
  56        fl6.daddr = niph->daddr;
  57        fl6.fl6_sport = nth->source;
  58        fl6.fl6_dport = nth->dest;
  59        security_skb_classify_flow((struct sk_buff *)skb, flowi6_to_flowi(&fl6));
  60        dst = ip6_route_output(net, NULL, &fl6);
  61        if (dst->error) {
  62                dst_release(dst);
  63                goto free_nskb;
  64        }
  65        dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
  66        if (IS_ERR(dst))
  67                goto free_nskb;
  68
  69        skb_dst_set(nskb, dst);
  70
  71        if (nfct) {
  72                nf_ct_set(nskb, (struct nf_conn *)nfct, ctinfo);
  73                nf_conntrack_get(nfct);
  74        }
  75
  76        ip6_local_out(net, nskb->sk, nskb);
  77        return;
  78
  79free_nskb:
  80        kfree_skb(nskb);
  81}
  82
  83static void
  84synproxy_send_client_synack(struct net *net,
  85                            const struct sk_buff *skb, const struct tcphdr *th,
  86                            const struct synproxy_options *opts)
  87{
  88        struct sk_buff *nskb;
  89        struct ipv6hdr *iph, *niph;
  90        struct tcphdr *nth;
  91        unsigned int tcp_hdr_size;
  92        u16 mss = opts->mss;
  93
  94        iph = ipv6_hdr(skb);
  95
  96        tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
  97        nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
  98                         GFP_ATOMIC);
  99        if (nskb == NULL)
 100                return;
 101        skb_reserve(nskb, MAX_TCP_HEADER);
 102
 103        niph = synproxy_build_ip(net, nskb, &iph->daddr, &iph->saddr);
 104
 105        skb_reset_transport_header(nskb);
 106        nth = skb_put(nskb, tcp_hdr_size);
 107        nth->source     = th->dest;
 108        nth->dest       = th->source;
 109        nth->seq        = htonl(__cookie_v6_init_sequence(iph, th, &mss));
 110        nth->ack_seq    = htonl(ntohl(th->seq) + 1);
 111        tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK;
 112        if (opts->options & XT_SYNPROXY_OPT_ECN)
 113                tcp_flag_word(nth) |= TCP_FLAG_ECE;
 114        nth->doff       = tcp_hdr_size / 4;
 115        nth->window     = 0;
 116        nth->check      = 0;
 117        nth->urg_ptr    = 0;
 118
 119        synproxy_build_options(nth, opts);
 120
 121        synproxy_send_tcp(net, skb, nskb, skb_nfct(skb),
 122                          IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size);
 123}
 124
 125static void
 126synproxy_send_server_syn(struct net *net,
 127                         const struct sk_buff *skb, const struct tcphdr *th,
 128                         const struct synproxy_options *opts, u32 recv_seq)
 129{
 130        struct synproxy_net *snet = synproxy_pernet(net);
 131        struct sk_buff *nskb;
 132        struct ipv6hdr *iph, *niph;
 133        struct tcphdr *nth;
 134        unsigned int tcp_hdr_size;
 135
 136        iph = ipv6_hdr(skb);
 137
 138        tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
 139        nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
 140                         GFP_ATOMIC);
 141        if (nskb == NULL)
 142                return;
 143        skb_reserve(nskb, MAX_TCP_HEADER);
 144
 145        niph = synproxy_build_ip(net, nskb, &iph->saddr, &iph->daddr);
 146
 147        skb_reset_transport_header(nskb);
 148        nth = skb_put(nskb, tcp_hdr_size);
 149        nth->source     = th->source;
 150        nth->dest       = th->dest;
 151        nth->seq        = htonl(recv_seq - 1);
 152        /* ack_seq is used to relay our ISN to the synproxy hook to initialize
 153         * sequence number translation once a connection tracking entry exists.
 154         */
 155        nth->ack_seq    = htonl(ntohl(th->ack_seq) - 1);
 156        tcp_flag_word(nth) = TCP_FLAG_SYN;
 157        if (opts->options & XT_SYNPROXY_OPT_ECN)
 158                tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR;
 159        nth->doff       = tcp_hdr_size / 4;
 160        nth->window     = th->window;
 161        nth->check      = 0;
 162        nth->urg_ptr    = 0;
 163
 164        synproxy_build_options(nth, opts);
 165
 166        synproxy_send_tcp(net, skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW,
 167                          niph, nth, tcp_hdr_size);
 168}
 169
 170static void
 171synproxy_send_server_ack(struct net *net,
 172                         const struct ip_ct_tcp *state,
 173                         const struct sk_buff *skb, const struct tcphdr *th,
 174                         const struct synproxy_options *opts)
 175{
 176        struct sk_buff *nskb;
 177        struct ipv6hdr *iph, *niph;
 178        struct tcphdr *nth;
 179        unsigned int tcp_hdr_size;
 180
 181        iph = ipv6_hdr(skb);
 182
 183        tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
 184        nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
 185                         GFP_ATOMIC);
 186        if (nskb == NULL)
 187                return;
 188        skb_reserve(nskb, MAX_TCP_HEADER);
 189
 190        niph = synproxy_build_ip(net, nskb, &iph->daddr, &iph->saddr);
 191
 192        skb_reset_transport_header(nskb);
 193        nth = skb_put(nskb, tcp_hdr_size);
 194        nth->source     = th->dest;
 195        nth->dest       = th->source;
 196        nth->seq        = htonl(ntohl(th->ack_seq));
 197        nth->ack_seq    = htonl(ntohl(th->seq) + 1);
 198        tcp_flag_word(nth) = TCP_FLAG_ACK;
 199        nth->doff       = tcp_hdr_size / 4;
 200        nth->window     = htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin);
 201        nth->check      = 0;
 202        nth->urg_ptr    = 0;
 203
 204        synproxy_build_options(nth, opts);
 205
 206        synproxy_send_tcp(net, skb, nskb, NULL, 0, niph, nth, tcp_hdr_size);
 207}
 208
 209static void
 210synproxy_send_client_ack(struct net *net,
 211                         const struct sk_buff *skb, const struct tcphdr *th,
 212                         const struct synproxy_options *opts)
 213{
 214        struct sk_buff *nskb;
 215        struct ipv6hdr *iph, *niph;
 216        struct tcphdr *nth;
 217        unsigned int tcp_hdr_size;
 218
 219        iph = ipv6_hdr(skb);
 220
 221        tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
 222        nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
 223                         GFP_ATOMIC);
 224        if (nskb == NULL)
 225                return;
 226        skb_reserve(nskb, MAX_TCP_HEADER);
 227
 228        niph = synproxy_build_ip(net, nskb, &iph->saddr, &iph->daddr);
 229
 230        skb_reset_transport_header(nskb);
 231        nth = skb_put(nskb, tcp_hdr_size);
 232        nth->source     = th->source;
 233        nth->dest       = th->dest;
 234        nth->seq        = htonl(ntohl(th->seq) + 1);
 235        nth->ack_seq    = th->ack_seq;
 236        tcp_flag_word(nth) = TCP_FLAG_ACK;
 237        nth->doff       = tcp_hdr_size / 4;
 238        nth->window     = htons(ntohs(th->window) >> opts->wscale);
 239        nth->check      = 0;
 240        nth->urg_ptr    = 0;
 241
 242        synproxy_build_options(nth, opts);
 243
 244        synproxy_send_tcp(net, skb, nskb, skb_nfct(skb),
 245                          IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size);
 246}
 247
 248static bool
 249synproxy_recv_client_ack(struct net *net,
 250                         const struct sk_buff *skb, const struct tcphdr *th,
 251                         struct synproxy_options *opts, u32 recv_seq)
 252{
 253        struct synproxy_net *snet = synproxy_pernet(net);
 254        int mss;
 255
 256        mss = __cookie_v6_check(ipv6_hdr(skb), th, ntohl(th->ack_seq) - 1);
 257        if (mss == 0) {
 258                this_cpu_inc(snet->stats->cookie_invalid);
 259                return false;
 260        }
 261
 262        this_cpu_inc(snet->stats->cookie_valid);
 263        opts->mss = mss;
 264        opts->options |= XT_SYNPROXY_OPT_MSS;
 265
 266        if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP)
 267                synproxy_check_timestamp_cookie(opts);
 268
 269        synproxy_send_server_syn(net, skb, th, opts, recv_seq);
 270        return true;
 271}
 272
 273static unsigned int
 274synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par)
 275{
 276        const struct xt_synproxy_info *info = par->targinfo;
 277        struct net *net = xt_net(par);
 278        struct synproxy_net *snet = synproxy_pernet(net);
 279        struct synproxy_options opts = {};
 280        struct tcphdr *th, _th;
 281
 282        if (nf_ip6_checksum(skb, xt_hooknum(par), par->thoff, IPPROTO_TCP))
 283                return NF_DROP;
 284
 285        th = skb_header_pointer(skb, par->thoff, sizeof(_th), &_th);
 286        if (th == NULL)
 287                return NF_DROP;
 288
 289        if (!synproxy_parse_options(skb, par->thoff, th, &opts))
 290                return NF_DROP;
 291
 292        if (th->syn && !(th->ack || th->fin || th->rst)) {
 293                /* Initial SYN from client */
 294                this_cpu_inc(snet->stats->syn_received);
 295
 296                if (th->ece && th->cwr)
 297                        opts.options |= XT_SYNPROXY_OPT_ECN;
 298
 299                opts.options &= info->options;
 300                if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
 301                        synproxy_init_timestamp_cookie(info, &opts);
 302                else
 303                        opts.options &= ~(XT_SYNPROXY_OPT_WSCALE |
 304                                          XT_SYNPROXY_OPT_SACK_PERM |
 305                                          XT_SYNPROXY_OPT_ECN);
 306
 307                synproxy_send_client_synack(net, skb, th, &opts);
 308                consume_skb(skb);
 309                return NF_STOLEN;
 310
 311        } else if (th->ack && !(th->fin || th->rst || th->syn)) {
 312                /* ACK from client */
 313                if (synproxy_recv_client_ack(net, skb, th, &opts, ntohl(th->seq))) {
 314                        consume_skb(skb);
 315                        return NF_STOLEN;
 316                } else {
 317                        return NF_DROP;
 318                }
 319        }
 320
 321        return XT_CONTINUE;
 322}
 323
 324static unsigned int ipv6_synproxy_hook(void *priv,
 325                                       struct sk_buff *skb,
 326                                       const struct nf_hook_state *nhs)
 327{
 328        struct net *net = nhs->net;
 329        struct synproxy_net *snet = synproxy_pernet(net);
 330        enum ip_conntrack_info ctinfo;
 331        struct nf_conn *ct;
 332        struct nf_conn_synproxy *synproxy;
 333        struct synproxy_options opts = {};
 334        const struct ip_ct_tcp *state;
 335        struct tcphdr *th, _th;
 336        __be16 frag_off;
 337        u8 nexthdr;
 338        int thoff;
 339
 340        ct = nf_ct_get(skb, &ctinfo);
 341        if (ct == NULL)
 342                return NF_ACCEPT;
 343
 344        synproxy = nfct_synproxy(ct);
 345        if (synproxy == NULL)
 346                return NF_ACCEPT;
 347
 348        if (nf_is_loopback_packet(skb))
 349                return NF_ACCEPT;
 350
 351        nexthdr = ipv6_hdr(skb)->nexthdr;
 352        thoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
 353                                 &frag_off);
 354        if (thoff < 0 || nexthdr != IPPROTO_TCP)
 355                return NF_ACCEPT;
 356
 357        th = skb_header_pointer(skb, thoff, sizeof(_th), &_th);
 358        if (th == NULL)
 359                return NF_DROP;
 360
 361        state = &ct->proto.tcp;
 362        switch (state->state) {
 363        case TCP_CONNTRACK_CLOSE:
 364                if (th->rst && !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
 365                        nf_ct_seqadj_init(ct, ctinfo, synproxy->isn -
 366                                                      ntohl(th->seq) + 1);
 367                        break;
 368                }
 369
 370                if (!th->syn || th->ack ||
 371                    CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
 372                        break;
 373
 374                /* Reopened connection - reset the sequence number and timestamp
 375                 * adjustments, they will get initialized once the connection is
 376                 * reestablished.
 377                 */
 378                nf_ct_seqadj_init(ct, ctinfo, 0);
 379                synproxy->tsoff = 0;
 380                this_cpu_inc(snet->stats->conn_reopened);
 381
 382                /* fall through */
 383        case TCP_CONNTRACK_SYN_SENT:
 384                if (!synproxy_parse_options(skb, thoff, th, &opts))
 385                        return NF_DROP;
 386
 387                if (!th->syn && th->ack &&
 388                    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
 389                        /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
 390                         * therefore we need to add 1 to make the SYN sequence
 391                         * number match the one of first SYN.
 392                         */
 393                        if (synproxy_recv_client_ack(net, skb, th, &opts,
 394                                                     ntohl(th->seq) + 1)) {
 395                                this_cpu_inc(snet->stats->cookie_retrans);
 396                                consume_skb(skb);
 397                                return NF_STOLEN;
 398                        } else {
 399                                return NF_DROP;
 400                        }
 401                }
 402
 403                synproxy->isn = ntohl(th->ack_seq);
 404                if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
 405                        synproxy->its = opts.tsecr;
 406
 407                nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
 408                break;
 409        case TCP_CONNTRACK_SYN_RECV:
 410                if (!th->syn || !th->ack)
 411                        break;
 412
 413                if (!synproxy_parse_options(skb, thoff, th, &opts))
 414                        return NF_DROP;
 415
 416                if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) {
 417                        synproxy->tsoff = opts.tsval - synproxy->its;
 418                        nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
 419                }
 420
 421                opts.options &= ~(XT_SYNPROXY_OPT_MSS |
 422                                  XT_SYNPROXY_OPT_WSCALE |
 423                                  XT_SYNPROXY_OPT_SACK_PERM);
 424
 425                swap(opts.tsval, opts.tsecr);
 426                synproxy_send_server_ack(net, state, skb, th, &opts);
 427
 428                nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq));
 429                nf_conntrack_event_cache(IPCT_SEQADJ, ct);
 430
 431                swap(opts.tsval, opts.tsecr);
 432                synproxy_send_client_ack(net, skb, th, &opts);
 433
 434                consume_skb(skb);
 435                return NF_STOLEN;
 436        default:
 437                break;
 438        }
 439
 440        synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy);
 441        return NF_ACCEPT;
 442}
 443
 444static const struct nf_hook_ops ipv6_synproxy_ops[] = {
 445        {
 446                .hook           = ipv6_synproxy_hook,
 447                .pf             = NFPROTO_IPV6,
 448                .hooknum        = NF_INET_LOCAL_IN,
 449                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM - 1,
 450        },
 451        {
 452                .hook           = ipv6_synproxy_hook,
 453                .pf             = NFPROTO_IPV6,
 454                .hooknum        = NF_INET_POST_ROUTING,
 455                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM - 1,
 456        },
 457};
 458
 459static int synproxy_tg6_check(const struct xt_tgchk_param *par)
 460{
 461        struct synproxy_net *snet = synproxy_pernet(par->net);
 462        const struct ip6t_entry *e = par->entryinfo;
 463        int err;
 464
 465        if (!(e->ipv6.flags & IP6T_F_PROTO) ||
 466            e->ipv6.proto != IPPROTO_TCP ||
 467            e->ipv6.invflags & XT_INV_PROTO)
 468                return -EINVAL;
 469
 470        err = nf_ct_netns_get(par->net, par->family);
 471        if (err)
 472                return err;
 473
 474        if (snet->hook_ref6 == 0) {
 475                err = nf_register_net_hooks(par->net, ipv6_synproxy_ops,
 476                                            ARRAY_SIZE(ipv6_synproxy_ops));
 477                if (err) {
 478                        nf_ct_netns_put(par->net, par->family);
 479                        return err;
 480                }
 481        }
 482
 483        snet->hook_ref6++;
 484        return err;
 485}
 486
 487static void synproxy_tg6_destroy(const struct xt_tgdtor_param *par)
 488{
 489        struct synproxy_net *snet = synproxy_pernet(par->net);
 490
 491        snet->hook_ref6--;
 492        if (snet->hook_ref6 == 0)
 493                nf_unregister_net_hooks(par->net, ipv6_synproxy_ops,
 494                                        ARRAY_SIZE(ipv6_synproxy_ops));
 495        nf_ct_netns_put(par->net, par->family);
 496}
 497
 498static struct xt_target synproxy_tg6_reg __read_mostly = {
 499        .name           = "SYNPROXY",
 500        .family         = NFPROTO_IPV6,
 501        .hooks          = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD),
 502        .target         = synproxy_tg6,
 503        .targetsize     = sizeof(struct xt_synproxy_info),
 504        .checkentry     = synproxy_tg6_check,
 505        .destroy        = synproxy_tg6_destroy,
 506        .me             = THIS_MODULE,
 507};
 508
 509static int __init synproxy_tg6_init(void)
 510{
 511        return xt_register_target(&synproxy_tg6_reg);
 512}
 513
 514static void __exit synproxy_tg6_exit(void)
 515{
 516        xt_unregister_target(&synproxy_tg6_reg);
 517}
 518
 519module_init(synproxy_tg6_init);
 520module_exit(synproxy_tg6_exit);
 521
 522MODULE_LICENSE("GPL");
 523MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 524