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