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 sk_buff *skb, const struct in6_addr *saddr,
  24                                       const struct in6_addr *daddr)
  25{
  26        struct ipv6hdr *iph;
  27
  28        skb_reset_network_header(skb);
  29        iph = (struct ipv6hdr *)skb_put(skb, sizeof(*iph));
  30        ip6_flow_hdr(iph, 0, 0);
  31        iph->hop_limit  = 64;   //XXX
  32        iph->nexthdr    = IPPROTO_TCP;
  33        iph->saddr      = *saddr;
  34        iph->daddr      = *daddr;
  35
  36        return iph;
  37}
  38
  39static void
  40synproxy_send_tcp(const struct synproxy_net *snet,
  41                  const struct sk_buff *skb, struct sk_buff *nskb,
  42                  struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo,
  43                  struct ipv6hdr *niph, struct tcphdr *nth,
  44                  unsigned int tcp_hdr_size)
  45{
  46        struct net *net = nf_ct_net(snet->tmpl);
  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 == NULL || 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                nskb->nfct = nfct;
  75                nskb->nfctinfo = 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(const struct synproxy_net *snet,
  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(nskb, &iph->daddr, &iph->saddr);
 107
 108        skb_reset_transport_header(nskb);
 109        nth = (struct tcphdr *)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(snet, skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY,
 125                          niph, nth, tcp_hdr_size);
 126}
 127
 128static void
 129synproxy_send_server_syn(const struct synproxy_net *snet,
 130                         const struct sk_buff *skb, const struct tcphdr *th,
 131                         const struct synproxy_options *opts, u32 recv_seq)
 132{
 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(nskb, &iph->saddr, &iph->daddr);
 148
 149        skb_reset_transport_header(nskb);
 150        nth = (struct tcphdr *)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(snet, skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW,
 169                          niph, nth, tcp_hdr_size);
 170}
 171
 172static void
 173synproxy_send_server_ack(const struct synproxy_net *snet,
 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(nskb, &iph->daddr, &iph->saddr);
 193
 194        skb_reset_transport_header(nskb);
 195        nth = (struct tcphdr *)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(snet, skb, nskb, NULL, 0, niph, nth, tcp_hdr_size);
 209}
 210
 211static void
 212synproxy_send_client_ack(const struct synproxy_net *snet,
 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(nskb, &iph->saddr, &iph->daddr);
 231
 232        skb_reset_transport_header(nskb);
 233        nth = (struct tcphdr *)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(snet, skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY,
 247                          niph, nth, tcp_hdr_size);
 248}
 249
 250static bool
 251synproxy_recv_client_ack(const struct synproxy_net *snet,
 252                         const struct sk_buff *skb, const struct tcphdr *th,
 253                         struct synproxy_options *opts, u32 recv_seq)
 254{
 255        int mss;
 256
 257        mss = __cookie_v6_check(ipv6_hdr(skb), th, ntohl(th->ack_seq) - 1);
 258        if (mss == 0) {
 259                this_cpu_inc(snet->stats->cookie_invalid);
 260                return false;
 261        }
 262
 263        this_cpu_inc(snet->stats->cookie_valid);
 264        opts->mss = mss;
 265        opts->options |= XT_SYNPROXY_OPT_MSS;
 266
 267        if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP)
 268                synproxy_check_timestamp_cookie(opts);
 269
 270        synproxy_send_server_syn(snet, skb, th, opts, recv_seq);
 271        return true;
 272}
 273
 274static unsigned int
 275synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par)
 276{
 277        const struct xt_synproxy_info *info = par->targinfo;
 278        struct synproxy_net *snet = synproxy_pernet(par->net);
 279        struct synproxy_options opts = {};
 280        struct tcphdr *th, _th;
 281
 282        if (nf_ip6_checksum(skb, par->hooknum, 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(snet, skb, th, &opts);
 308                return NF_DROP;
 309
 310        } else if (th->ack && !(th->fin || th->rst || th->syn)) {
 311                /* ACK from client */
 312                synproxy_recv_client_ack(snet, skb, th, &opts, ntohl(th->seq));
 313                return NF_DROP;
 314        }
 315
 316        return XT_CONTINUE;
 317}
 318
 319static unsigned int ipv6_synproxy_hook(void *priv,
 320                                       struct sk_buff *skb,
 321                                       const struct nf_hook_state *nhs)
 322{
 323        struct synproxy_net *snet = synproxy_pernet(nhs->net);
 324        enum ip_conntrack_info ctinfo;
 325        struct nf_conn *ct;
 326        struct nf_conn_synproxy *synproxy;
 327        struct synproxy_options opts = {};
 328        const struct ip_ct_tcp *state;
 329        struct tcphdr *th, _th;
 330        __be16 frag_off;
 331        u8 nexthdr;
 332        int thoff;
 333
 334        ct = nf_ct_get(skb, &ctinfo);
 335        if (ct == NULL)
 336                return NF_ACCEPT;
 337
 338        synproxy = nfct_synproxy(ct);
 339        if (synproxy == NULL)
 340                return NF_ACCEPT;
 341
 342        if (nf_is_loopback_packet(skb))
 343                return NF_ACCEPT;
 344
 345        nexthdr = ipv6_hdr(skb)->nexthdr;
 346        thoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
 347                                 &frag_off);
 348        if (thoff < 0)
 349                return NF_ACCEPT;
 350
 351        th = skb_header_pointer(skb, thoff, sizeof(_th), &_th);
 352        if (th == NULL)
 353                return NF_DROP;
 354
 355        state = &ct->proto.tcp;
 356        switch (state->state) {
 357        case TCP_CONNTRACK_CLOSE:
 358                if (th->rst && !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
 359                        nf_ct_seqadj_init(ct, ctinfo, synproxy->isn -
 360                                                      ntohl(th->seq) + 1);
 361                        break;
 362                }
 363
 364                if (!th->syn || th->ack ||
 365                    CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
 366                        break;
 367
 368                /* Reopened connection - reset the sequence number and timestamp
 369                 * adjustments, they will get initialized once the connection is
 370                 * reestablished.
 371                 */
 372                nf_ct_seqadj_init(ct, ctinfo, 0);
 373                synproxy->tsoff = 0;
 374                this_cpu_inc(snet->stats->conn_reopened);
 375
 376                /* fall through */
 377        case TCP_CONNTRACK_SYN_SENT:
 378                if (!synproxy_parse_options(skb, thoff, th, &opts))
 379                        return NF_DROP;
 380
 381                if (!th->syn && th->ack &&
 382                    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
 383                        /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
 384                         * therefore we need to add 1 to make the SYN sequence
 385                         * number match the one of first SYN.
 386                         */
 387                        if (synproxy_recv_client_ack(snet, skb, th, &opts,
 388                                                     ntohl(th->seq) + 1))
 389                                this_cpu_inc(snet->stats->cookie_retrans);
 390
 391                        return NF_DROP;
 392                }
 393
 394                synproxy->isn = ntohl(th->ack_seq);
 395                if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
 396                        synproxy->its = opts.tsecr;
 397                break;
 398        case TCP_CONNTRACK_SYN_RECV:
 399                if (!th->syn || !th->ack)
 400                        break;
 401
 402                if (!synproxy_parse_options(skb, thoff, th, &opts))
 403                        return NF_DROP;
 404
 405                if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
 406                        synproxy->tsoff = opts.tsval - synproxy->its;
 407
 408                opts.options &= ~(XT_SYNPROXY_OPT_MSS |
 409                                  XT_SYNPROXY_OPT_WSCALE |
 410                                  XT_SYNPROXY_OPT_SACK_PERM);
 411
 412                swap(opts.tsval, opts.tsecr);
 413                synproxy_send_server_ack(snet, state, skb, th, &opts);
 414
 415                nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq));
 416
 417                swap(opts.tsval, opts.tsecr);
 418                synproxy_send_client_ack(snet, skb, th, &opts);
 419
 420                consume_skb(skb);
 421                return NF_STOLEN;
 422        default:
 423                break;
 424        }
 425
 426        synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy);
 427        return NF_ACCEPT;
 428}
 429
 430static int synproxy_tg6_check(const struct xt_tgchk_param *par)
 431{
 432        const struct ip6t_entry *e = par->entryinfo;
 433
 434        if (!(e->ipv6.flags & IP6T_F_PROTO) ||
 435            e->ipv6.proto != IPPROTO_TCP ||
 436            e->ipv6.invflags & XT_INV_PROTO)
 437                return -EINVAL;
 438
 439        return nf_ct_l3proto_try_module_get(par->family);
 440}
 441
 442static void synproxy_tg6_destroy(const struct xt_tgdtor_param *par)
 443{
 444        nf_ct_l3proto_module_put(par->family);
 445}
 446
 447static struct xt_target synproxy_tg6_reg __read_mostly = {
 448        .name           = "SYNPROXY",
 449        .family         = NFPROTO_IPV6,
 450        .hooks          = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD),
 451        .target         = synproxy_tg6,
 452        .targetsize     = sizeof(struct xt_synproxy_info),
 453        .checkentry     = synproxy_tg6_check,
 454        .destroy        = synproxy_tg6_destroy,
 455        .me             = THIS_MODULE,
 456};
 457
 458static struct nf_hook_ops ipv6_synproxy_ops[] __read_mostly = {
 459        {
 460                .hook           = ipv6_synproxy_hook,
 461                .pf             = NFPROTO_IPV6,
 462                .hooknum        = NF_INET_LOCAL_IN,
 463                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM - 1,
 464        },
 465        {
 466                .hook           = ipv6_synproxy_hook,
 467                .pf             = NFPROTO_IPV6,
 468                .hooknum        = NF_INET_POST_ROUTING,
 469                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM - 1,
 470        },
 471};
 472
 473static int __init synproxy_tg6_init(void)
 474{
 475        int err;
 476
 477        err = nf_register_hooks(ipv6_synproxy_ops,
 478                                ARRAY_SIZE(ipv6_synproxy_ops));
 479        if (err < 0)
 480                goto err1;
 481
 482        err = xt_register_target(&synproxy_tg6_reg);
 483        if (err < 0)
 484                goto err2;
 485
 486        return 0;
 487
 488err2:
 489        nf_unregister_hooks(ipv6_synproxy_ops, ARRAY_SIZE(ipv6_synproxy_ops));
 490err1:
 491        return err;
 492}
 493
 494static void __exit synproxy_tg6_exit(void)
 495{
 496        xt_unregister_target(&synproxy_tg6_reg);
 497        nf_unregister_hooks(ipv6_synproxy_ops, ARRAY_SIZE(ipv6_synproxy_ops));
 498}
 499
 500module_init(synproxy_tg6_init);
 501module_exit(synproxy_tg6_exit);
 502
 503MODULE_LICENSE("GPL");
 504MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 505