123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- /*
- * Copyright (c) 2013 Patrick McHardy <kaber@trash.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- #include <linux/module.h>
- #include <linux/skbuff.h>
- #include <asm/unaligned.h>
- #include <net/tcp.h>
- #include <net/netns/generic.h>
- #include <linux/proc_fs.h>
- #include <linux/netfilter_ipv4/ip_tables.h>
- #include <linux/netfilter/x_tables.h>
- #include <linux/netfilter/xt_tcpudp.h>
- #include <linux/netfilter/xt_SYNPROXY.h>
- #include <net/netfilter/nf_conntrack.h>
- #include <net/netfilter/nf_conntrack_extend.h>
- #include <net/netfilter/nf_conntrack_seqadj.h>
- #include <net/netfilter/nf_conntrack_synproxy.h>
- #include <net/netfilter/nf_conntrack_zones.h>
- int synproxy_net_id;
- EXPORT_SYMBOL_GPL(synproxy_net_id);
- bool
- synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
- const struct tcphdr *th, struct synproxy_options *opts)
- {
- int length = (th->doff * 4) - sizeof(*th);
- u8 buf[40], *ptr;
- ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf);
- if (ptr == NULL)
- return false;
- opts->options = 0;
- while (length > 0) {
- int opcode = *ptr++;
- int opsize;
- switch (opcode) {
- case TCPOPT_EOL:
- return true;
- case TCPOPT_NOP:
- length--;
- continue;
- default:
- opsize = *ptr++;
- if (opsize < 2)
- return true;
- if (opsize > length)
- return true;
- switch (opcode) {
- case TCPOPT_MSS:
- if (opsize == TCPOLEN_MSS) {
- opts->mss = get_unaligned_be16(ptr);
- opts->options |= XT_SYNPROXY_OPT_MSS;
- }
- break;
- case TCPOPT_WINDOW:
- if (opsize == TCPOLEN_WINDOW) {
- opts->wscale = *ptr;
- if (opts->wscale > 14)
- opts->wscale = 14;
- opts->options |= XT_SYNPROXY_OPT_WSCALE;
- }
- break;
- case TCPOPT_TIMESTAMP:
- if (opsize == TCPOLEN_TIMESTAMP) {
- opts->tsval = get_unaligned_be32(ptr);
- opts->tsecr = get_unaligned_be32(ptr + 4);
- opts->options |= XT_SYNPROXY_OPT_TIMESTAMP;
- }
- break;
- case TCPOPT_SACK_PERM:
- if (opsize == TCPOLEN_SACK_PERM)
- opts->options |= XT_SYNPROXY_OPT_SACK_PERM;
- break;
- }
- ptr += opsize - 2;
- length -= opsize;
- }
- }
- return true;
- }
- EXPORT_SYMBOL_GPL(synproxy_parse_options);
- unsigned int synproxy_options_size(const struct synproxy_options *opts)
- {
- unsigned int size = 0;
- if (opts->options & XT_SYNPROXY_OPT_MSS)
- size += TCPOLEN_MSS_ALIGNED;
- if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP)
- size += TCPOLEN_TSTAMP_ALIGNED;
- else if (opts->options & XT_SYNPROXY_OPT_SACK_PERM)
- size += TCPOLEN_SACKPERM_ALIGNED;
- if (opts->options & XT_SYNPROXY_OPT_WSCALE)
- size += TCPOLEN_WSCALE_ALIGNED;
- return size;
- }
- EXPORT_SYMBOL_GPL(synproxy_options_size);
- void
- synproxy_build_options(struct tcphdr *th, const struct synproxy_options *opts)
- {
- __be32 *ptr = (__be32 *)(th + 1);
- u8 options = opts->options;
- if (options & XT_SYNPROXY_OPT_MSS)
- *ptr++ = htonl((TCPOPT_MSS << 24) |
- (TCPOLEN_MSS << 16) |
- opts->mss);
- if (options & XT_SYNPROXY_OPT_TIMESTAMP) {
- if (options & XT_SYNPROXY_OPT_SACK_PERM)
- *ptr++ = htonl((TCPOPT_SACK_PERM << 24) |
- (TCPOLEN_SACK_PERM << 16) |
- (TCPOPT_TIMESTAMP << 8) |
- TCPOLEN_TIMESTAMP);
- else
- *ptr++ = htonl((TCPOPT_NOP << 24) |
- (TCPOPT_NOP << 16) |
- (TCPOPT_TIMESTAMP << 8) |
- TCPOLEN_TIMESTAMP);
- *ptr++ = htonl(opts->tsval);
- *ptr++ = htonl(opts->tsecr);
- } else if (options & XT_SYNPROXY_OPT_SACK_PERM)
- *ptr++ = htonl((TCPOPT_NOP << 24) |
- (TCPOPT_NOP << 16) |
- (TCPOPT_SACK_PERM << 8) |
- TCPOLEN_SACK_PERM);
- if (options & XT_SYNPROXY_OPT_WSCALE)
- *ptr++ = htonl((TCPOPT_NOP << 24) |
- (TCPOPT_WINDOW << 16) |
- (TCPOLEN_WINDOW << 8) |
- opts->wscale);
- }
- EXPORT_SYMBOL_GPL(synproxy_build_options);
- void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info,
- struct synproxy_options *opts)
- {
- opts->tsecr = opts->tsval;
- opts->tsval = tcp_time_stamp & ~0x3f;
- if (opts->options & XT_SYNPROXY_OPT_WSCALE) {
- opts->tsval |= opts->wscale;
- opts->wscale = info->wscale;
- } else
- opts->tsval |= 0xf;
- if (opts->options & XT_SYNPROXY_OPT_SACK_PERM)
- opts->tsval |= 1 << 4;
- if (opts->options & XT_SYNPROXY_OPT_ECN)
- opts->tsval |= 1 << 5;
- }
- EXPORT_SYMBOL_GPL(synproxy_init_timestamp_cookie);
- void synproxy_check_timestamp_cookie(struct synproxy_options *opts)
- {
- opts->wscale = opts->tsecr & 0xf;
- if (opts->wscale != 0xf)
- opts->options |= XT_SYNPROXY_OPT_WSCALE;
- opts->options |= opts->tsecr & (1 << 4) ? XT_SYNPROXY_OPT_SACK_PERM : 0;
- opts->options |= opts->tsecr & (1 << 5) ? XT_SYNPROXY_OPT_ECN : 0;
- }
- EXPORT_SYMBOL_GPL(synproxy_check_timestamp_cookie);
- unsigned int synproxy_tstamp_adjust(struct sk_buff *skb,
- unsigned int protoff,
- struct tcphdr *th,
- struct nf_conn *ct,
- enum ip_conntrack_info ctinfo,
- const struct nf_conn_synproxy *synproxy)
- {
- unsigned int optoff, optend;
- __be32 *ptr, old;
- if (synproxy->tsoff == 0)
- return 1;
- optoff = protoff + sizeof(struct tcphdr);
- optend = protoff + th->doff * 4;
- if (!skb_make_writable(skb, optend))
- return 0;
- while (optoff < optend) {
- unsigned char *op = skb->data + optoff;
- switch (op[0]) {
- case TCPOPT_EOL:
- return 1;
- case TCPOPT_NOP:
- optoff++;
- continue;
- default:
- if (optoff + 1 == optend ||
- optoff + op[1] > optend ||
- op[1] < 2)
- return 0;
- if (op[0] == TCPOPT_TIMESTAMP &&
- op[1] == TCPOLEN_TIMESTAMP) {
- if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
- ptr = (__be32 *)&op[2];
- old = *ptr;
- *ptr = htonl(ntohl(*ptr) -
- synproxy->tsoff);
- } else {
- ptr = (__be32 *)&op[6];
- old = *ptr;
- *ptr = htonl(ntohl(*ptr) +
- synproxy->tsoff);
- }
- inet_proto_csum_replace4(&th->check, skb,
- old, *ptr, false);
- return 1;
- }
- optoff += op[1];
- }
- }
- return 1;
- }
- EXPORT_SYMBOL_GPL(synproxy_tstamp_adjust);
- static struct nf_ct_ext_type nf_ct_synproxy_extend __read_mostly = {
- .len = sizeof(struct nf_conn_synproxy),
- .align = __alignof__(struct nf_conn_synproxy),
- .id = NF_CT_EXT_SYNPROXY,
- };
- #ifdef CONFIG_PROC_FS
- static void *synproxy_cpu_seq_start(struct seq_file *seq, loff_t *pos)
- {
- struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
- int cpu;
- if (*pos == 0)
- return SEQ_START_TOKEN;
- for (cpu = *pos - 1; cpu < nr_cpu_ids; cpu++) {
- if (!cpu_possible(cpu))
- continue;
- *pos = cpu + 1;
- return per_cpu_ptr(snet->stats, cpu);
- }
- return NULL;
- }
- static void *synproxy_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
- {
- struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
- int cpu;
- for (cpu = *pos; cpu < nr_cpu_ids; cpu++) {
- if (!cpu_possible(cpu))
- continue;
- *pos = cpu + 1;
- return per_cpu_ptr(snet->stats, cpu);
- }
- return NULL;
- }
- static void synproxy_cpu_seq_stop(struct seq_file *seq, void *v)
- {
- return;
- }
- static int synproxy_cpu_seq_show(struct seq_file *seq, void *v)
- {
- struct synproxy_stats *stats = v;
- if (v == SEQ_START_TOKEN) {
- seq_printf(seq, "entries\t\tsyn_received\t"
- "cookie_invalid\tcookie_valid\t"
- "cookie_retrans\tconn_reopened\n");
- return 0;
- }
- seq_printf(seq, "%08x\t%08x\t%08x\t%08x\t%08x\t%08x\n", 0,
- stats->syn_received,
- stats->cookie_invalid,
- stats->cookie_valid,
- stats->cookie_retrans,
- stats->conn_reopened);
- return 0;
- }
- static const struct seq_operations synproxy_cpu_seq_ops = {
- .start = synproxy_cpu_seq_start,
- .next = synproxy_cpu_seq_next,
- .stop = synproxy_cpu_seq_stop,
- .show = synproxy_cpu_seq_show,
- };
- static int synproxy_cpu_seq_open(struct inode *inode, struct file *file)
- {
- return seq_open_net(inode, file, &synproxy_cpu_seq_ops,
- sizeof(struct seq_net_private));
- }
- static const struct file_operations synproxy_cpu_seq_fops = {
- .owner = THIS_MODULE,
- .open = synproxy_cpu_seq_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release_net,
- };
- static int __net_init synproxy_proc_init(struct net *net)
- {
- if (!proc_create("synproxy", S_IRUGO, net->proc_net_stat,
- &synproxy_cpu_seq_fops))
- return -ENOMEM;
- return 0;
- }
- static void __net_exit synproxy_proc_exit(struct net *net)
- {
- remove_proc_entry("synproxy", net->proc_net_stat);
- }
- #else
- static int __net_init synproxy_proc_init(struct net *net)
- {
- return 0;
- }
- static void __net_exit synproxy_proc_exit(struct net *net)
- {
- return;
- }
- #endif /* CONFIG_PROC_FS */
- static int __net_init synproxy_net_init(struct net *net)
- {
- struct synproxy_net *snet = synproxy_pernet(net);
- struct nf_conn *ct;
- int err = -ENOMEM;
- ct = nf_ct_tmpl_alloc(net, &nf_ct_zone_dflt, GFP_KERNEL);
- if (!ct)
- goto err1;
- if (!nfct_seqadj_ext_add(ct))
- goto err2;
- if (!nfct_synproxy_ext_add(ct))
- goto err2;
- __set_bit(IPS_CONFIRMED_BIT, &ct->status);
- nf_conntrack_get(&ct->ct_general);
- snet->tmpl = ct;
- snet->stats = alloc_percpu(struct synproxy_stats);
- if (snet->stats == NULL)
- goto err2;
- err = synproxy_proc_init(net);
- if (err < 0)
- goto err3;
- return 0;
- err3:
- free_percpu(snet->stats);
- err2:
- nf_ct_tmpl_free(ct);
- err1:
- return err;
- }
- static void __net_exit synproxy_net_exit(struct net *net)
- {
- struct synproxy_net *snet = synproxy_pernet(net);
- nf_ct_put(snet->tmpl);
- synproxy_proc_exit(net);
- free_percpu(snet->stats);
- }
- static struct pernet_operations synproxy_net_ops = {
- .init = synproxy_net_init,
- .exit = synproxy_net_exit,
- .id = &synproxy_net_id,
- .size = sizeof(struct synproxy_net),
- };
- static int __init synproxy_core_init(void)
- {
- int err;
- err = nf_ct_extend_register(&nf_ct_synproxy_extend);
- if (err < 0)
- goto err1;
- err = register_pernet_subsys(&synproxy_net_ops);
- if (err < 0)
- goto err2;
- return 0;
- err2:
- nf_ct_extend_unregister(&nf_ct_synproxy_extend);
- err1:
- return err;
- }
- static void __exit synproxy_core_exit(void)
- {
- unregister_pernet_subsys(&synproxy_net_ops);
- nf_ct_extend_unregister(&nf_ct_synproxy_extend);
- }
- module_init(synproxy_core_init);
- module_exit(synproxy_core_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|