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