2 * Copyright (c) 2013 Patrick McHardy <kaber@trash.net>
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.
9 #include <linux/module.h>
10 #include <linux/skbuff.h>
11 #include <asm/unaligned.h>
13 #include <net/netns/generic.h>
14 #include <linux/proc_fs.h>
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>
21 #include <net/netfilter/nf_conntrack.h>
22 #include <net/netfilter/nf_conntrack_extend.h>
23 #include <net/netfilter/nf_conntrack_seqadj.h>
24 #include <net/netfilter/nf_conntrack_synproxy.h>
25 #include <net/netfilter/nf_conntrack_zones.h>
27 unsigned int synproxy_net_id;
28 EXPORT_SYMBOL_GPL(synproxy_net_id);
31 synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
32 const struct tcphdr *th, struct synproxy_options *opts)
34 int length = (th->doff * 4) - sizeof(*th);
37 if (unlikely(length < 0))
40 ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf);
66 if (opsize == TCPOLEN_MSS) {
67 opts->mss = get_unaligned_be16(ptr);
68 opts->options |= XT_SYNPROXY_OPT_MSS;
72 if (opsize == TCPOLEN_WINDOW) {
74 if (opts->wscale > TCP_MAX_WSCALE)
75 opts->wscale = TCP_MAX_WSCALE;
76 opts->options |= XT_SYNPROXY_OPT_WSCALE;
79 case TCPOPT_TIMESTAMP:
80 if (opsize == TCPOLEN_TIMESTAMP) {
81 opts->tsval = get_unaligned_be32(ptr);
82 opts->tsecr = get_unaligned_be32(ptr + 4);
83 opts->options |= XT_SYNPROXY_OPT_TIMESTAMP;
86 case TCPOPT_SACK_PERM:
87 if (opsize == TCPOLEN_SACK_PERM)
88 opts->options |= XT_SYNPROXY_OPT_SACK_PERM;
98 EXPORT_SYMBOL_GPL(synproxy_parse_options);
100 unsigned int synproxy_options_size(const struct synproxy_options *opts)
102 unsigned int size = 0;
104 if (opts->options & XT_SYNPROXY_OPT_MSS)
105 size += TCPOLEN_MSS_ALIGNED;
106 if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP)
107 size += TCPOLEN_TSTAMP_ALIGNED;
108 else if (opts->options & XT_SYNPROXY_OPT_SACK_PERM)
109 size += TCPOLEN_SACKPERM_ALIGNED;
110 if (opts->options & XT_SYNPROXY_OPT_WSCALE)
111 size += TCPOLEN_WSCALE_ALIGNED;
115 EXPORT_SYMBOL_GPL(synproxy_options_size);
118 synproxy_build_options(struct tcphdr *th, const struct synproxy_options *opts)
120 __be32 *ptr = (__be32 *)(th + 1);
121 u8 options = opts->options;
123 if (options & XT_SYNPROXY_OPT_MSS)
124 *ptr++ = htonl((TCPOPT_MSS << 24) |
125 (TCPOLEN_MSS << 16) |
128 if (options & XT_SYNPROXY_OPT_TIMESTAMP) {
129 if (options & XT_SYNPROXY_OPT_SACK_PERM)
130 *ptr++ = htonl((TCPOPT_SACK_PERM << 24) |
131 (TCPOLEN_SACK_PERM << 16) |
132 (TCPOPT_TIMESTAMP << 8) |
135 *ptr++ = htonl((TCPOPT_NOP << 24) |
137 (TCPOPT_TIMESTAMP << 8) |
140 *ptr++ = htonl(opts->tsval);
141 *ptr++ = htonl(opts->tsecr);
142 } else if (options & XT_SYNPROXY_OPT_SACK_PERM)
143 *ptr++ = htonl((TCPOPT_NOP << 24) |
145 (TCPOPT_SACK_PERM << 8) |
148 if (options & XT_SYNPROXY_OPT_WSCALE)
149 *ptr++ = htonl((TCPOPT_NOP << 24) |
150 (TCPOPT_WINDOW << 16) |
151 (TCPOLEN_WINDOW << 8) |
154 EXPORT_SYMBOL_GPL(synproxy_build_options);
156 void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info,
157 struct synproxy_options *opts)
159 opts->tsecr = opts->tsval;
160 opts->tsval = tcp_time_stamp_raw() & ~0x3f;
162 if (opts->options & XT_SYNPROXY_OPT_WSCALE) {
163 opts->tsval |= opts->wscale;
164 opts->wscale = info->wscale;
168 if (opts->options & XT_SYNPROXY_OPT_SACK_PERM)
169 opts->tsval |= 1 << 4;
171 if (opts->options & XT_SYNPROXY_OPT_ECN)
172 opts->tsval |= 1 << 5;
174 EXPORT_SYMBOL_GPL(synproxy_init_timestamp_cookie);
176 void synproxy_check_timestamp_cookie(struct synproxy_options *opts)
178 opts->wscale = opts->tsecr & 0xf;
179 if (opts->wscale != 0xf)
180 opts->options |= XT_SYNPROXY_OPT_WSCALE;
182 opts->options |= opts->tsecr & (1 << 4) ? XT_SYNPROXY_OPT_SACK_PERM : 0;
184 opts->options |= opts->tsecr & (1 << 5) ? XT_SYNPROXY_OPT_ECN : 0;
186 EXPORT_SYMBOL_GPL(synproxy_check_timestamp_cookie);
188 unsigned int synproxy_tstamp_adjust(struct sk_buff *skb,
189 unsigned int protoff,
192 enum ip_conntrack_info ctinfo,
193 const struct nf_conn_synproxy *synproxy)
195 unsigned int optoff, optend;
198 if (synproxy->tsoff == 0)
201 optoff = protoff + sizeof(struct tcphdr);
202 optend = protoff + th->doff * 4;
204 if (!skb_make_writable(skb, optend))
207 while (optoff < optend) {
208 unsigned char *op = skb->data + optoff;
217 if (optoff + 1 == optend ||
218 optoff + op[1] > optend ||
221 if (op[0] == TCPOPT_TIMESTAMP &&
222 op[1] == TCPOLEN_TIMESTAMP) {
223 if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
224 ptr = (__be32 *)&op[2];
226 *ptr = htonl(ntohl(*ptr) -
229 ptr = (__be32 *)&op[6];
231 *ptr = htonl(ntohl(*ptr) +
234 inet_proto_csum_replace4(&th->check, skb,
243 EXPORT_SYMBOL_GPL(synproxy_tstamp_adjust);
245 static struct nf_ct_ext_type nf_ct_synproxy_extend __read_mostly = {
246 .len = sizeof(struct nf_conn_synproxy),
247 .align = __alignof__(struct nf_conn_synproxy),
248 .id = NF_CT_EXT_SYNPROXY,
251 #ifdef CONFIG_PROC_FS
252 static void *synproxy_cpu_seq_start(struct seq_file *seq, loff_t *pos)
254 struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
258 return SEQ_START_TOKEN;
260 for (cpu = *pos - 1; cpu < nr_cpu_ids; cpu++) {
261 if (!cpu_possible(cpu))
264 return per_cpu_ptr(snet->stats, cpu);
270 static void *synproxy_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
272 struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
275 for (cpu = *pos; cpu < nr_cpu_ids; cpu++) {
276 if (!cpu_possible(cpu))
279 return per_cpu_ptr(snet->stats, cpu);
285 static void synproxy_cpu_seq_stop(struct seq_file *seq, void *v)
290 static int synproxy_cpu_seq_show(struct seq_file *seq, void *v)
292 struct synproxy_stats *stats = v;
294 if (v == SEQ_START_TOKEN) {
295 seq_puts(seq, "entries\t\tsyn_received\t"
296 "cookie_invalid\tcookie_valid\t"
297 "cookie_retrans\tconn_reopened\n");
301 seq_printf(seq, "%08x\t%08x\t%08x\t%08x\t%08x\t%08x\n", 0,
303 stats->cookie_invalid,
305 stats->cookie_retrans,
306 stats->conn_reopened);
311 static const struct seq_operations synproxy_cpu_seq_ops = {
312 .start = synproxy_cpu_seq_start,
313 .next = synproxy_cpu_seq_next,
314 .stop = synproxy_cpu_seq_stop,
315 .show = synproxy_cpu_seq_show,
318 static int __net_init synproxy_proc_init(struct net *net)
320 if (!proc_create_net("synproxy", 0444, net->proc_net_stat,
321 &synproxy_cpu_seq_ops, sizeof(struct seq_net_private)))
326 static void __net_exit synproxy_proc_exit(struct net *net)
328 remove_proc_entry("synproxy", net->proc_net_stat);
331 static int __net_init synproxy_proc_init(struct net *net)
336 static void __net_exit synproxy_proc_exit(struct net *net)
340 #endif /* CONFIG_PROC_FS */
342 static int __net_init synproxy_net_init(struct net *net)
344 struct synproxy_net *snet = synproxy_pernet(net);
348 ct = nf_ct_tmpl_alloc(net, &nf_ct_zone_dflt, GFP_KERNEL);
352 if (!nfct_seqadj_ext_add(ct))
354 if (!nfct_synproxy_ext_add(ct))
357 __set_bit(IPS_CONFIRMED_BIT, &ct->status);
358 nf_conntrack_get(&ct->ct_general);
361 snet->stats = alloc_percpu(struct synproxy_stats);
362 if (snet->stats == NULL)
365 err = synproxy_proc_init(net);
372 free_percpu(snet->stats);
379 static void __net_exit synproxy_net_exit(struct net *net)
381 struct synproxy_net *snet = synproxy_pernet(net);
383 nf_ct_put(snet->tmpl);
384 synproxy_proc_exit(net);
385 free_percpu(snet->stats);
388 static struct pernet_operations synproxy_net_ops = {
389 .init = synproxy_net_init,
390 .exit = synproxy_net_exit,
391 .id = &synproxy_net_id,
392 .size = sizeof(struct synproxy_net),
395 static int __init synproxy_core_init(void)
399 err = nf_ct_extend_register(&nf_ct_synproxy_extend);
403 err = register_pernet_subsys(&synproxy_net_ops);
410 nf_ct_extend_unregister(&nf_ct_synproxy_extend);
415 static void __exit synproxy_core_exit(void)
417 unregister_pernet_subsys(&synproxy_net_ops);
418 nf_ct_extend_unregister(&nf_ct_synproxy_extend);
421 module_init(synproxy_core_init);
422 module_exit(synproxy_core_exit);
424 MODULE_LICENSE("GPL");
425 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");