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 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 net *net = nf_ct_net((struct nf_conn *)nfct);
  46        struct dst_entry *dst;
  47        struct flowi6 fl6;
  48
  49        nth->check = ~tcp_v6_check(tcp_hdr_size, &niph->saddr, &niph->daddr, 0);
  50        nskb->ip_summed   = CHECKSUM_PARTIAL;
  51        nskb->csum_start  = (unsigned char *)nth - nskb->head;
  52        nskb->csum_offset = offsetof(struct tcphdr, check);
  53
  54        memset(&fl6, 0, sizeof(fl6));
  55        fl6.flowi6_proto = IPPROTO_TCP;
  56        fl6.saddr = niph->saddr;
  57        fl6.daddr = niph->daddr;
  58        fl6.fl6_sport = nth->source;
  59        fl6.fl6_dport = nth->dest;
  60        security_skb_classify_flow((struct sk_buff *)skb, flowi6_to_flowi(&fl6));
  61        dst = ip6_route_output(net, NULL, &fl6);
  62        if (dst == NULL || dst->error) {
  63                dst_release(dst);
  64                goto free_nskb;
  65        }
  66        dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
  67        if (IS_ERR(dst))
  68                goto free_nskb;
  69
  70        skb_dst_set(nskb, dst);
  71
  72        if (nfct) {
  73                nskb->nfct = nfct;
  74                nskb->nfctinfo = ctinfo;
  75                nf_conntrack_get(nfct);
  76        }
  77
  78        ip6_local_out(nskb);
  79        return;
  80
  81free_nskb:
  82        kfree_skb(nskb);
  83}
  84
  85static void
  86synproxy_send_client_synack(const struct sk_buff *skb, const struct tcphdr *th,
  87                            const struct synproxy_options *opts)
  88{
  89        struct sk_buff *nskb;
  90        struct ipv6hdr *iph, *niph;
  91        struct tcphdr *nth;
  92        unsigned int tcp_hdr_size;
  93        u16 mss = opts->mss;
  94
  95        iph = ipv6_hdr(skb);
  96
  97        tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
  98        nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
  99                         GFP_ATOMIC);
 100        if (nskb == NULL)
 101                return;
 102        skb_reserve(nskb, MAX_TCP_HEADER);
 103
 104        niph = synproxy_build_ip(nskb, &iph->daddr, &iph->saddr);
 105
 106        skb_reset_transport_header(nskb);
 107        nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size);
 108        nth->source     = th->dest;
 109        nth->dest       = th->source;
 110        nth->seq        = htonl(__cookie_v6_init_sequence(iph, th, &mss));
 111        nth->ack_seq    = htonl(ntohl(th->seq) + 1);
 112        tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK;
 113        if (opts->options & XT_SYNPROXY_OPT_ECN)
 114                tcp_flag_word(nth) |= TCP_FLAG_ECE;
 115        nth->doff       = tcp_hdr_size / 4;
 116        nth->window     = 0;
 117        nth->check      = 0;
 118        nth->urg_ptr    = 0;
 119
 120        synproxy_build_options(nth, opts);
 121
 122        synproxy_send_tcp(skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY,
 123                          niph, nth, tcp_hdr_size);
 124}
 125
 126static void
 127synproxy_send_server_syn(const struct synproxy_net *snet,
 128                         const struct sk_buff *skb, const struct tcphdr *th,
 129                         const struct synproxy_options *opts, u32 recv_seq)
 130{
 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(nskb, &iph->saddr, &iph->daddr);
 146
 147        skb_reset_transport_header(nskb);
 148        nth = (struct tcphdr *)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(skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW,
 167                          niph, nth, tcp_hdr_size);
 168}
 169
 170static void
 171synproxy_send_server_ack(const struct synproxy_net *snet,
 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(nskb, &iph->daddr, &iph->saddr);
 191
 192        skb_reset_transport_header(nskb);
 193        nth = (struct tcphdr *)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(skb, nskb, NULL, 0, niph, nth, tcp_hdr_size);
 207}
 208
 209static void
 210synproxy_send_client_ack(const struct synproxy_net *snet,
 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(nskb, &iph->saddr, &iph->daddr);
 229
 230        skb_reset_transport_header(nskb);
 231        nth = (struct tcphdr *)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     = ntohs(htons(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(skb, nskb, NULL, 0, niph, nth, tcp_hdr_size);
 245}
 246
 247static bool
 248synproxy_recv_client_ack(const struct synproxy_net *snet,
 249                         const struct sk_buff *skb, const struct tcphdr *th,
 250                         struct synproxy_options *opts, u32 recv_seq)
 251{
 252        int mss;
 253
 254        mss = __cookie_v6_check(ipv6_hdr(skb), th, ntohl(th->ack_seq) - 1);
 255        if (mss == 0) {
 256                this_cpu_inc(snet->stats->cookie_invalid);
 257                return false;
 258        }
 259
 260        this_cpu_inc(snet->stats->cookie_valid);
 261        opts->mss = mss;
 262        opts->options |= XT_SYNPROXY_OPT_MSS;
 263
 264        if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP)
 265                synproxy_check_timestamp_cookie(opts);
 266
 267        synproxy_send_server_syn(snet, skb, th, opts, recv_seq);
 268        return true;
 269}
 270
 271static unsigned int
 272synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par)
 273{
 274        const struct xt_synproxy_info *info = par->targinfo;
 275        struct synproxy_net *snet = synproxy_pernet(dev_net(par->in));
 276        struct synproxy_options opts = {};
 277        struct tcphdr *th, _th;
 278
 279        if (nf_ip6_checksum(skb, par->hooknum, par->thoff, IPPROTO_TCP))
 280                return NF_DROP;
 281
 282        th = skb_header_pointer(skb, par->thoff, sizeof(_th), &_th);
 283        if (th == NULL)
 284                return NF_DROP;
 285
 286        if (!synproxy_parse_options(skb, par->thoff, th, &opts))
 287                return NF_DROP;
 288
 289        if (th->syn && !(th->ack || th->fin || th->rst)) {
 290                /* Initial SYN from client */
 291                this_cpu_inc(snet->stats->syn_received);
 292
 293                if (th->ece && th->cwr)
 294                        opts.options |= XT_SYNPROXY_OPT_ECN;
 295
 296                opts.options &= info->options;
 297                if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
 298                        synproxy_init_timestamp_cookie(info, &opts);
 299                else
 300                        opts.options &= ~(XT_SYNPROXY_OPT_WSCALE |
 301                                          XT_SYNPROXY_OPT_SACK_PERM |
 302                                          XT_SYNPROXY_OPT_ECN);
 303
 304                synproxy_send_client_synack(skb, th, &opts);
 305                return NF_DROP;
 306
 307        } else if (th->ack && !(th->fin || th->rst || th->syn)) {
 308                /* ACK from client */
 309                synproxy_recv_client_ack(snet, skb, th, &opts, ntohl(th->seq));
 310                return NF_DROP;
 311        }
 312
 313        return XT_CONTINUE;
 314}
 315
 316static unsigned int ipv6_synproxy_hook(const struct nf_hook_ops *ops,
 317                                       struct sk_buff *skb,
 318                                       const struct net_device *in,
 319                                       const struct net_device *out,
 320                                       int (*okfn)(struct sk_buff *))
 321{
 322        struct synproxy_net *snet = synproxy_pernet(dev_net(in ? : out));
 323        enum ip_conntrack_info ctinfo;
 324        struct nf_conn *ct;
 325        struct nf_conn_synproxy *synproxy;
 326        struct synproxy_options opts = {};
 327        const struct ip_ct_tcp *state;
 328        struct tcphdr *th, _th;
 329        __be16 frag_off;
 330        u8 nexthdr;
 331        int thoff;
 332
 333        ct = nf_ct_get(skb, &ctinfo);
 334        if (ct == NULL)
 335                return NF_ACCEPT;
 336
 337        synproxy = nfct_synproxy(ct);
 338        if (synproxy == NULL)
 339                return NF_ACCEPT;
 340
 341        if (nf_is_loopback_packet(skb))
 342                return NF_ACCEPT;
 343
 344        nexthdr = ipv6_hdr(skb)->nexthdr;
 345        thoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
 346                                 &frag_off);
 347        if (thoff < 0)
 348                return NF_ACCEPT;
 349
 350        th = skb_header_pointer(skb, thoff, sizeof(_th), &_th);
 351        if (th == NULL)
 352                return NF_DROP;
 353
 354        state = &ct->proto.tcp;
 355        switch (state->state) {
 356        case TCP_CONNTRACK_CLOSE:
 357                if (th->rst && !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
 358                        nf_ct_seqadj_init(ct, ctinfo, synproxy->isn -
 359                                                      ntohl(th->seq) + 1);
 360                        break;
 361                }
 362
 363                if (!th->syn || th->ack ||
 364                    CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
 365                        break;
 366
 367                /* Reopened connection - reset the sequence number and timestamp
 368                 * adjustments, they will get initialized once the connection is
 369                 * reestablished.
 370                 */
 371                nf_ct_seqadj_init(ct, ctinfo, 0);
 372                synproxy->tsoff = 0;
 373                this_cpu_inc(snet->stats->conn_reopened);
 374
 375                /* fall through */
 376        case TCP_CONNTRACK_SYN_SENT:
 377                if (!synproxy_parse_options(skb, thoff, th, &opts))
 378                        return NF_DROP;
 379
 380                if (!th->syn && th->ack &&
 381                    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
 382                        /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
 383                         * therefore we need to add 1 to make the SYN sequence
 384                         * number match the one of first SYN.
 385                         */
 386                        if (synproxy_recv_client_ack(snet, skb, th, &opts,
 387                                                     ntohl(th->seq) + 1))
 388                                this_cpu_inc(snet->stats->cookie_retrans);
 389
 390                        return NF_DROP;
 391                }
 392
 393                synproxy->isn = ntohl(th->ack_seq);
 394                if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
 395                        synproxy->its = opts.tsecr;
 396                break;
 397        case TCP_CONNTRACK_SYN_RECV:
 398                if (!th->syn || !th->ack)
 399                        break;
 400
 401                if (!synproxy_parse_options(skb, thoff, th, &opts))
 402                        return NF_DROP;
 403
 404                if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
 405                        synproxy->tsoff = opts.tsval - synproxy->its;
 406
 407                opts.options &= ~(XT_SYNPROXY_OPT_MSS |
 408                                  XT_SYNPROXY_OPT_WSCALE |
 409                                  XT_SYNPROXY_OPT_SACK_PERM);
 410
 411                swap(opts.tsval, opts.tsecr);
 412                synproxy_send_server_ack(snet, state, skb, th, &opts);
 413
 414                nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq));
 415
 416                swap(opts.tsval, opts.tsecr);
 417                synproxy_send_client_ack(snet, skb, th, &opts);
 418
 419                consume_skb(skb);
 420                return NF_STOLEN;
 421        default:
 422                break;
 423        }
 424
 425        synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy);
 426        return NF_ACCEPT;
 427}
 428
 429static int synproxy_tg6_check(const struct xt_tgchk_param *par)
 430{
 431        const struct ip6t_entry *e = par->entryinfo;
 432
 433        if (!(e->ipv6.flags & IP6T_F_PROTO) ||
 434            e->ipv6.proto != IPPROTO_TCP ||
 435            e->ipv6.invflags & XT_INV_PROTO)
 436                return -EINVAL;
 437
 438        return nf_ct_l3proto_try_module_get(par->family);
 439}
 440
 441static void synproxy_tg6_destroy(const struct xt_tgdtor_param *par)
 442{
 443        nf_ct_l3proto_module_put(par->family);
 444}
 445
 446static struct xt_target synproxy_tg6_reg __read_mostly = {
 447        .name           = "SYNPROXY",
 448        .family         = NFPROTO_IPV6,
 449        .hooks          = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD),
 450        .target         = synproxy_tg6,
 451        .targetsize     = sizeof(struct xt_synproxy_info),
 452        .checkentry     = synproxy_tg6_check,
 453        .destroy        = synproxy_tg6_destroy,
 454        .me             = THIS_MODULE,
 455};
 456
 457static struct nf_hook_ops ipv6_synproxy_ops[] __read_mostly = {
 458        {
 459                .hook           = ipv6_synproxy_hook,
 460                .owner          = THIS_MODULE,
 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                .owner          = THIS_MODULE,
 468                .pf             = NFPROTO_IPV6,
 469                .hooknum        = NF_INET_POST_ROUTING,
 470                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM - 1,
 471        },
 472};
 473
 474static int __init synproxy_tg6_init(void)
 475{
 476        int err;
 477
 478        err = nf_register_hooks(ipv6_synproxy_ops,
 479                                ARRAY_SIZE(ipv6_synproxy_ops));
 480        if (err < 0)
 481                goto err1;
 482
 483        err = xt_register_target(&synproxy_tg6_reg);
 484        if (err < 0)
 485                goto err2;
 486
 487        return 0;
 488
 489err2:
 490        nf_unregister_hooks(ipv6_synproxy_ops, ARRAY_SIZE(ipv6_synproxy_ops));
 491err1:
 492        return err;
 493}
 494
 495static void __exit synproxy_tg6_exit(void)
 496{
 497        xt_unregister_target(&synproxy_tg6_reg);
 498        nf_unregister_hooks(ipv6_synproxy_ops, ARRAY_SIZE(ipv6_synproxy_ops));
 499}
 500
 501module_init(synproxy_tg6_init);
 502module_exit(synproxy_tg6_exit);
 503
 504MODULE_LICENSE("GPL");
 505MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 506