linux/net/netfilter/nf_synproxy_core.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 <asm/unaligned.h>
  12#include <net/tcp.h>
  13#include <net/netns/generic.h>
  14
  15#include <linux/netfilter_ipv4/ip_tables.h>
  16#include <linux/netfilter/x_tables.h>
  17#include <linux/netfilter/xt_tcpudp.h>
  18#include <linux/netfilter/xt_SYNPROXY.h>
  19#include <net/netfilter/nf_conntrack.h>
  20#include <net/netfilter/nf_conntrack_extend.h>
  21#include <net/netfilter/nf_conntrack_seqadj.h>
  22#include <net/netfilter/nf_conntrack_synproxy.h>
  23
  24int synproxy_net_id;
  25EXPORT_SYMBOL_GPL(synproxy_net_id);
  26
  27bool
  28synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
  29                       const struct tcphdr *th, struct synproxy_options *opts)
  30{
  31        int length = (th->doff * 4) - sizeof(*th);
  32        u8 buf[40], *ptr;
  33
  34        ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf);
  35        if (ptr == NULL)
  36                return false;
  37
  38        opts->options = 0;
  39        while (length > 0) {
  40                int opcode = *ptr++;
  41                int opsize;
  42
  43                switch (opcode) {
  44                case TCPOPT_EOL:
  45                        return true;
  46                case TCPOPT_NOP:
  47                        length--;
  48                        continue;
  49                default:
  50                        opsize = *ptr++;
  51                        if (opsize < 2)
  52                                return true;
  53                        if (opsize > length)
  54                                return true;
  55
  56                        switch (opcode) {
  57                        case TCPOPT_MSS:
  58                                if (opsize == TCPOLEN_MSS) {
  59                                        opts->mss = get_unaligned_be16(ptr);
  60                                        opts->options |= XT_SYNPROXY_OPT_MSS;
  61                                }
  62                                break;
  63                        case TCPOPT_WINDOW:
  64                                if (opsize == TCPOLEN_WINDOW) {
  65                                        opts->wscale = *ptr;
  66                                        if (opts->wscale > 14)
  67                                                opts->wscale = 14;
  68                                        opts->options |= XT_SYNPROXY_OPT_WSCALE;
  69                                }
  70                                break;
  71                        case TCPOPT_TIMESTAMP:
  72                                if (opsize == TCPOLEN_TIMESTAMP) {
  73                                        opts->tsval = get_unaligned_be32(ptr);
  74                                        opts->tsecr = get_unaligned_be32(ptr + 4);
  75                                        opts->options |= XT_SYNPROXY_OPT_TIMESTAMP;
  76                                }
  77                                break;
  78                        case TCPOPT_SACK_PERM:
  79                                if (opsize == TCPOLEN_SACK_PERM)
  80                                        opts->options |= XT_SYNPROXY_OPT_SACK_PERM;
  81                                break;
  82                        }
  83
  84                        ptr += opsize - 2;
  85                        length -= opsize;
  86                }
  87        }
  88        return true;
  89}
  90EXPORT_SYMBOL_GPL(synproxy_parse_options);
  91
  92unsigned int synproxy_options_size(const struct synproxy_options *opts)
  93{
  94        unsigned int size = 0;
  95
  96        if (opts->options & XT_SYNPROXY_OPT_MSS)
  97                size += TCPOLEN_MSS_ALIGNED;
  98        if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP)
  99                size += TCPOLEN_TSTAMP_ALIGNED;
 100        else if (opts->options & XT_SYNPROXY_OPT_SACK_PERM)
 101                size += TCPOLEN_SACKPERM_ALIGNED;
 102        if (opts->options & XT_SYNPROXY_OPT_WSCALE)
 103                size += TCPOLEN_WSCALE_ALIGNED;
 104
 105        return size;
 106}
 107EXPORT_SYMBOL_GPL(synproxy_options_size);
 108
 109void
 110synproxy_build_options(struct tcphdr *th, const struct synproxy_options *opts)
 111{
 112        __be32 *ptr = (__be32 *)(th + 1);
 113        u8 options = opts->options;
 114
 115        if (options & XT_SYNPROXY_OPT_MSS)
 116                *ptr++ = htonl((TCPOPT_MSS << 24) |
 117                               (TCPOLEN_MSS << 16) |
 118                               opts->mss);
 119
 120        if (options & XT_SYNPROXY_OPT_TIMESTAMP) {
 121                if (options & XT_SYNPROXY_OPT_SACK_PERM)
 122                        *ptr++ = htonl((TCPOPT_SACK_PERM << 24) |
 123                                       (TCPOLEN_SACK_PERM << 16) |
 124                                       (TCPOPT_TIMESTAMP << 8) |
 125                                       TCPOLEN_TIMESTAMP);
 126                else
 127                        *ptr++ = htonl((TCPOPT_NOP << 24) |
 128                                       (TCPOPT_NOP << 16) |
 129                                       (TCPOPT_TIMESTAMP << 8) |
 130                                       TCPOLEN_TIMESTAMP);
 131
 132                *ptr++ = htonl(opts->tsval);
 133                *ptr++ = htonl(opts->tsecr);
 134        } else if (options & XT_SYNPROXY_OPT_SACK_PERM)
 135                *ptr++ = htonl((TCPOPT_NOP << 24) |
 136                               (TCPOPT_NOP << 16) |
 137                               (TCPOPT_SACK_PERM << 8) |
 138                               TCPOLEN_SACK_PERM);
 139
 140        if (options & XT_SYNPROXY_OPT_WSCALE)
 141                *ptr++ = htonl((TCPOPT_NOP << 24) |
 142                               (TCPOPT_WINDOW << 16) |
 143                               (TCPOLEN_WINDOW << 8) |
 144                               opts->wscale);
 145}
 146EXPORT_SYMBOL_GPL(synproxy_build_options);
 147
 148void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info,
 149                                    struct synproxy_options *opts)
 150{
 151        opts->tsecr = opts->tsval;
 152        opts->tsval = tcp_time_stamp & ~0x3f;
 153
 154        if (opts->options & XT_SYNPROXY_OPT_WSCALE) {
 155                opts->tsval |= opts->wscale;
 156                opts->wscale = info->wscale;
 157        } else
 158                opts->tsval |= 0xf;
 159
 160        if (opts->options & XT_SYNPROXY_OPT_SACK_PERM)
 161                opts->tsval |= 1 << 4;
 162
 163        if (opts->options & XT_SYNPROXY_OPT_ECN)
 164                opts->tsval |= 1 << 5;
 165}
 166EXPORT_SYMBOL_GPL(synproxy_init_timestamp_cookie);
 167
 168void synproxy_check_timestamp_cookie(struct synproxy_options *opts)
 169{
 170        opts->wscale = opts->tsecr & 0xf;
 171        if (opts->wscale != 0xf)
 172                opts->options |= XT_SYNPROXY_OPT_WSCALE;
 173
 174        opts->options |= opts->tsecr & (1 << 4) ? XT_SYNPROXY_OPT_SACK_PERM : 0;
 175
 176        opts->options |= opts->tsecr & (1 << 5) ? XT_SYNPROXY_OPT_ECN : 0;
 177}
 178EXPORT_SYMBOL_GPL(synproxy_check_timestamp_cookie);
 179
 180unsigned int synproxy_tstamp_adjust(struct sk_buff *skb,
 181                                    unsigned int protoff,
 182                                    struct tcphdr *th,
 183                                    struct nf_conn *ct,
 184                                    enum ip_conntrack_info ctinfo,
 185                                    const struct nf_conn_synproxy *synproxy)
 186{
 187        unsigned int optoff, optend;
 188        u32 *ptr, old;
 189
 190        if (synproxy->tsoff == 0)
 191                return 1;
 192
 193        optoff = protoff + sizeof(struct tcphdr);
 194        optend = protoff + th->doff * 4;
 195
 196        if (!skb_make_writable(skb, optend))
 197                return 0;
 198
 199        while (optoff < optend) {
 200                unsigned char *op = skb->data + optoff;
 201
 202                switch (op[0]) {
 203                case TCPOPT_EOL:
 204                        return 1;
 205                case TCPOPT_NOP:
 206                        optoff++;
 207                        continue;
 208                default:
 209                        if (optoff + 1 == optend ||
 210                            optoff + op[1] > optend ||
 211                            op[1] < 2)
 212                                return 0;
 213                        if (op[0] == TCPOPT_TIMESTAMP &&
 214                            op[1] == TCPOLEN_TIMESTAMP) {
 215                                if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
 216                                        ptr = (u32 *)&op[2];
 217                                        old = *ptr;
 218                                        *ptr = htonl(ntohl(*ptr) -
 219                                                     synproxy->tsoff);
 220                                } else {
 221                                        ptr = (u32 *)&op[6];
 222                                        old = *ptr;
 223                                        *ptr = htonl(ntohl(*ptr) +
 224                                                     synproxy->tsoff);
 225                                }
 226                                inet_proto_csum_replace4(&th->check, skb,
 227                                                         old, *ptr, 0);
 228                                return 1;
 229                        }
 230                        optoff += op[1];
 231                }
 232        }
 233        return 1;
 234}
 235EXPORT_SYMBOL_GPL(synproxy_tstamp_adjust);
 236
 237static struct nf_ct_ext_type nf_ct_synproxy_extend __read_mostly = {
 238        .len            = sizeof(struct nf_conn_synproxy),
 239        .align          = __alignof__(struct nf_conn_synproxy),
 240        .id             = NF_CT_EXT_SYNPROXY,
 241};
 242
 243#ifdef CONFIG_PROC_FS
 244static void *synproxy_cpu_seq_start(struct seq_file *seq, loff_t *pos)
 245{
 246        struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
 247        int cpu;
 248
 249        if (*pos == 0)
 250                return SEQ_START_TOKEN;
 251
 252        for (cpu = *pos - 1; cpu < nr_cpu_ids; cpu++) {
 253                if (!cpu_possible(cpu))
 254                        continue;
 255                *pos = cpu + 1;
 256                return per_cpu_ptr(snet->stats, cpu);
 257        }
 258
 259        return NULL;
 260}
 261
 262static void *synproxy_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 263{
 264        struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
 265        int cpu;
 266
 267        for (cpu = *pos; cpu < nr_cpu_ids; cpu++) {
 268                if (!cpu_possible(cpu))
 269                        continue;
 270                *pos = cpu + 1;
 271                return per_cpu_ptr(snet->stats, cpu);
 272        }
 273
 274        return NULL;
 275}
 276
 277static void synproxy_cpu_seq_stop(struct seq_file *seq, void *v)
 278{
 279        return;
 280}
 281
 282static int synproxy_cpu_seq_show(struct seq_file *seq, void *v)
 283{
 284        struct synproxy_stats *stats = v;
 285
 286        if (v == SEQ_START_TOKEN) {
 287                seq_printf(seq, "entries\t\tsyn_received\t"
 288                                "cookie_invalid\tcookie_valid\t"
 289                                "cookie_retrans\tconn_reopened\n");
 290                return 0;
 291        }
 292
 293        seq_printf(seq, "%08x\t%08x\t%08x\t%08x\t%08x\t%08x\n", 0,
 294                   stats->syn_received,
 295                   stats->cookie_invalid,
 296                   stats->cookie_valid,
 297                   stats->cookie_retrans,
 298                   stats->conn_reopened);
 299
 300        return 0;
 301}
 302
 303static const struct seq_operations synproxy_cpu_seq_ops = {
 304        .start          = synproxy_cpu_seq_start,
 305        .next           = synproxy_cpu_seq_next,
 306        .stop           = synproxy_cpu_seq_stop,
 307        .show           = synproxy_cpu_seq_show,
 308};
 309
 310static int synproxy_cpu_seq_open(struct inode *inode, struct file *file)
 311{
 312        return seq_open_net(inode, file, &synproxy_cpu_seq_ops,
 313                            sizeof(struct seq_net_private));
 314}
 315
 316static const struct file_operations synproxy_cpu_seq_fops = {
 317        .owner          = THIS_MODULE,
 318        .open           = synproxy_cpu_seq_open,
 319        .read           = seq_read,
 320        .llseek         = seq_lseek,
 321        .release        = seq_release_net,
 322};
 323
 324static int __net_init synproxy_proc_init(struct net *net)
 325{
 326        if (!proc_create("synproxy", S_IRUGO, net->proc_net_stat,
 327                         &synproxy_cpu_seq_fops))
 328                return -ENOMEM;
 329        return 0;
 330}
 331
 332static void __net_exit synproxy_proc_exit(struct net *net)
 333{
 334        remove_proc_entry("synproxy", net->proc_net_stat);
 335}
 336#else
 337static int __net_init synproxy_proc_init(struct net *net)
 338{
 339        return 0;
 340}
 341
 342static void __net_exit synproxy_proc_exit(struct net *net)
 343{
 344        return;
 345}
 346#endif /* CONFIG_PROC_FS */
 347
 348static int __net_init synproxy_net_init(struct net *net)
 349{
 350        struct synproxy_net *snet = synproxy_pernet(net);
 351        struct nf_conntrack_tuple t;
 352        struct nf_conn *ct;
 353        int err = -ENOMEM;
 354
 355        memset(&t, 0, sizeof(t));
 356        ct = nf_conntrack_alloc(net, 0, &t, &t, GFP_KERNEL);
 357        if (IS_ERR(ct)) {
 358                err = PTR_ERR(ct);
 359                goto err1;
 360        }
 361
 362        if (!nfct_seqadj_ext_add(ct))
 363                goto err2;
 364        if (!nfct_synproxy_ext_add(ct))
 365                goto err2;
 366
 367        nf_conntrack_tmpl_insert(net, ct);
 368        snet->tmpl = ct;
 369
 370        snet->stats = alloc_percpu(struct synproxy_stats);
 371        if (snet->stats == NULL)
 372                goto err2;
 373
 374        err = synproxy_proc_init(net);
 375        if (err < 0)
 376                goto err3;
 377
 378        return 0;
 379
 380err3:
 381        free_percpu(snet->stats);
 382err2:
 383        nf_conntrack_free(ct);
 384err1:
 385        return err;
 386}
 387
 388static void __net_exit synproxy_net_exit(struct net *net)
 389{
 390        struct synproxy_net *snet = synproxy_pernet(net);
 391
 392        nf_ct_put(snet->tmpl);
 393        synproxy_proc_exit(net);
 394        free_percpu(snet->stats);
 395}
 396
 397static struct pernet_operations synproxy_net_ops = {
 398        .init           = synproxy_net_init,
 399        .exit           = synproxy_net_exit,
 400        .id             = &synproxy_net_id,
 401        .size           = sizeof(struct synproxy_net),
 402};
 403
 404static int __init synproxy_core_init(void)
 405{
 406        int err;
 407
 408        err = nf_ct_extend_register(&nf_ct_synproxy_extend);
 409        if (err < 0)
 410                goto err1;
 411
 412        err = register_pernet_subsys(&synproxy_net_ops);
 413        if (err < 0)
 414                goto err2;
 415
 416        return 0;
 417
 418err2:
 419        nf_ct_extend_unregister(&nf_ct_synproxy_extend);
 420err1:
 421        return err;
 422}
 423
 424static void __exit synproxy_core_exit(void)
 425{
 426        unregister_pernet_subsys(&synproxy_net_ops);
 427        nf_ct_extend_unregister(&nf_ct_synproxy_extend);
 428}
 429
 430module_init(synproxy_core_init);
 431module_exit(synproxy_core_exit);
 432
 433MODULE_LICENSE("GPL");
 434MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 435