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