148b1de4cSPatrick McHardy /*
248b1de4cSPatrick McHardy  * Copyright (c) 2013 Patrick McHardy <kaber@trash.net>
348b1de4cSPatrick McHardy  *
448b1de4cSPatrick McHardy  * This program is free software; you can redistribute it and/or modify
548b1de4cSPatrick McHardy  * it under the terms of the GNU General Public License version 2 as
648b1de4cSPatrick McHardy  * published by the Free Software Foundation.
748b1de4cSPatrick McHardy  */
848b1de4cSPatrick McHardy 
948b1de4cSPatrick McHardy #include <linux/module.h>
1048b1de4cSPatrick McHardy #include <linux/skbuff.h>
1148b1de4cSPatrick McHardy #include <asm/unaligned.h>
1248b1de4cSPatrick McHardy #include <net/tcp.h>
1348b1de4cSPatrick McHardy #include <net/netns/generic.h>
1410c04a8eSPablo Neira Ayuso #include <linux/proc_fs.h>
1548b1de4cSPatrick McHardy 
1648b1de4cSPatrick McHardy #include <linux/netfilter_ipv4/ip_tables.h>
1748b1de4cSPatrick McHardy #include <linux/netfilter/x_tables.h>
1848b1de4cSPatrick McHardy #include <linux/netfilter/xt_tcpudp.h>
1948b1de4cSPatrick McHardy #include <linux/netfilter/xt_SYNPROXY.h>
2048b1de4cSPatrick McHardy #include <net/netfilter/nf_conntrack.h>
2148b1de4cSPatrick McHardy #include <net/netfilter/nf_conntrack_extend.h>
2248b1de4cSPatrick McHardy #include <net/netfilter/nf_conntrack_seqadj.h>
2348b1de4cSPatrick McHardy #include <net/netfilter/nf_conntrack_synproxy.h>
2448b1de4cSPatrick McHardy 
2548b1de4cSPatrick McHardy int synproxy_net_id;
2648b1de4cSPatrick McHardy EXPORT_SYMBOL_GPL(synproxy_net_id);
2748b1de4cSPatrick McHardy 
28f4a87e7bSPatrick McHardy bool
2948b1de4cSPatrick McHardy synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
3048b1de4cSPatrick McHardy 		       const struct tcphdr *th, struct synproxy_options *opts)
3148b1de4cSPatrick McHardy {
3248b1de4cSPatrick McHardy 	int length = (th->doff * 4) - sizeof(*th);
3348b1de4cSPatrick McHardy 	u8 buf[40], *ptr;
3448b1de4cSPatrick McHardy 
3548b1de4cSPatrick McHardy 	ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf);
36f4a87e7bSPatrick McHardy 	if (ptr == NULL)
37f4a87e7bSPatrick McHardy 		return false;
3848b1de4cSPatrick McHardy 
3948b1de4cSPatrick McHardy 	opts->options = 0;
4048b1de4cSPatrick McHardy 	while (length > 0) {
4148b1de4cSPatrick McHardy 		int opcode = *ptr++;
4248b1de4cSPatrick McHardy 		int opsize;
4348b1de4cSPatrick McHardy 
4448b1de4cSPatrick McHardy 		switch (opcode) {
4548b1de4cSPatrick McHardy 		case TCPOPT_EOL:
46f4a87e7bSPatrick McHardy 			return true;
4748b1de4cSPatrick McHardy 		case TCPOPT_NOP:
4848b1de4cSPatrick McHardy 			length--;
4948b1de4cSPatrick McHardy 			continue;
5048b1de4cSPatrick McHardy 		default:
5148b1de4cSPatrick McHardy 			opsize = *ptr++;
5248b1de4cSPatrick McHardy 			if (opsize < 2)
53f4a87e7bSPatrick McHardy 				return true;
5448b1de4cSPatrick McHardy 			if (opsize > length)
55f4a87e7bSPatrick McHardy 				return true;
5648b1de4cSPatrick McHardy 
5748b1de4cSPatrick McHardy 			switch (opcode) {
5848b1de4cSPatrick McHardy 			case TCPOPT_MSS:
5948b1de4cSPatrick McHardy 				if (opsize == TCPOLEN_MSS) {
6048b1de4cSPatrick McHardy 					opts->mss = get_unaligned_be16(ptr);
6148b1de4cSPatrick McHardy 					opts->options |= XT_SYNPROXY_OPT_MSS;
6248b1de4cSPatrick McHardy 				}
6348b1de4cSPatrick McHardy 				break;
6448b1de4cSPatrick McHardy 			case TCPOPT_WINDOW:
6548b1de4cSPatrick McHardy 				if (opsize == TCPOLEN_WINDOW) {
6648b1de4cSPatrick McHardy 					opts->wscale = *ptr;
6748b1de4cSPatrick McHardy 					if (opts->wscale > 14)
6848b1de4cSPatrick McHardy 						opts->wscale = 14;
6948b1de4cSPatrick McHardy 					opts->options |= XT_SYNPROXY_OPT_WSCALE;
7048b1de4cSPatrick McHardy 				}
7148b1de4cSPatrick McHardy 				break;
7248b1de4cSPatrick McHardy 			case TCPOPT_TIMESTAMP:
7348b1de4cSPatrick McHardy 				if (opsize == TCPOLEN_TIMESTAMP) {
7448b1de4cSPatrick McHardy 					opts->tsval = get_unaligned_be32(ptr);
7548b1de4cSPatrick McHardy 					opts->tsecr = get_unaligned_be32(ptr + 4);
7648b1de4cSPatrick McHardy 					opts->options |= XT_SYNPROXY_OPT_TIMESTAMP;
7748b1de4cSPatrick McHardy 				}
7848b1de4cSPatrick McHardy 				break;
7948b1de4cSPatrick McHardy 			case TCPOPT_SACK_PERM:
8048b1de4cSPatrick McHardy 				if (opsize == TCPOLEN_SACK_PERM)
8148b1de4cSPatrick McHardy 					opts->options |= XT_SYNPROXY_OPT_SACK_PERM;
8248b1de4cSPatrick McHardy 				break;
8348b1de4cSPatrick McHardy 			}
8448b1de4cSPatrick McHardy 
8548b1de4cSPatrick McHardy 			ptr += opsize - 2;
8648b1de4cSPatrick McHardy 			length -= opsize;
8748b1de4cSPatrick McHardy 		}
8848b1de4cSPatrick McHardy 	}
89f4a87e7bSPatrick McHardy 	return true;
9048b1de4cSPatrick McHardy }
9148b1de4cSPatrick McHardy EXPORT_SYMBOL_GPL(synproxy_parse_options);
9248b1de4cSPatrick McHardy 
9348b1de4cSPatrick McHardy unsigned int synproxy_options_size(const struct synproxy_options *opts)
9448b1de4cSPatrick McHardy {
9548b1de4cSPatrick McHardy 	unsigned int size = 0;
9648b1de4cSPatrick McHardy 
9748b1de4cSPatrick McHardy 	if (opts->options & XT_SYNPROXY_OPT_MSS)
9848b1de4cSPatrick McHardy 		size += TCPOLEN_MSS_ALIGNED;
9948b1de4cSPatrick McHardy 	if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP)
10048b1de4cSPatrick McHardy 		size += TCPOLEN_TSTAMP_ALIGNED;
10148b1de4cSPatrick McHardy 	else if (opts->options & XT_SYNPROXY_OPT_SACK_PERM)
10248b1de4cSPatrick McHardy 		size += TCPOLEN_SACKPERM_ALIGNED;
10348b1de4cSPatrick McHardy 	if (opts->options & XT_SYNPROXY_OPT_WSCALE)
10448b1de4cSPatrick McHardy 		size += TCPOLEN_WSCALE_ALIGNED;
10548b1de4cSPatrick McHardy 
10648b1de4cSPatrick McHardy 	return size;
10748b1de4cSPatrick McHardy }
10848b1de4cSPatrick McHardy EXPORT_SYMBOL_GPL(synproxy_options_size);
10948b1de4cSPatrick McHardy 
11048b1de4cSPatrick McHardy void
11148b1de4cSPatrick McHardy synproxy_build_options(struct tcphdr *th, const struct synproxy_options *opts)
11248b1de4cSPatrick McHardy {
11348b1de4cSPatrick McHardy 	__be32 *ptr = (__be32 *)(th + 1);
11448b1de4cSPatrick McHardy 	u8 options = opts->options;
11548b1de4cSPatrick McHardy 
11648b1de4cSPatrick McHardy 	if (options & XT_SYNPROXY_OPT_MSS)
11748b1de4cSPatrick McHardy 		*ptr++ = htonl((TCPOPT_MSS << 24) |
11848b1de4cSPatrick McHardy 			       (TCPOLEN_MSS << 16) |
11948b1de4cSPatrick McHardy 			       opts->mss);
12048b1de4cSPatrick McHardy 
12148b1de4cSPatrick McHardy 	if (options & XT_SYNPROXY_OPT_TIMESTAMP) {
12248b1de4cSPatrick McHardy 		if (options & XT_SYNPROXY_OPT_SACK_PERM)
12348b1de4cSPatrick McHardy 			*ptr++ = htonl((TCPOPT_SACK_PERM << 24) |
12448b1de4cSPatrick McHardy 				       (TCPOLEN_SACK_PERM << 16) |
12548b1de4cSPatrick McHardy 				       (TCPOPT_TIMESTAMP << 8) |
12648b1de4cSPatrick McHardy 				       TCPOLEN_TIMESTAMP);
12748b1de4cSPatrick McHardy 		else
12848b1de4cSPatrick McHardy 			*ptr++ = htonl((TCPOPT_NOP << 24) |
12948b1de4cSPatrick McHardy 				       (TCPOPT_NOP << 16) |
13048b1de4cSPatrick McHardy 				       (TCPOPT_TIMESTAMP << 8) |
13148b1de4cSPatrick McHardy 				       TCPOLEN_TIMESTAMP);
13248b1de4cSPatrick McHardy 
13348b1de4cSPatrick McHardy 		*ptr++ = htonl(opts->tsval);
13448b1de4cSPatrick McHardy 		*ptr++ = htonl(opts->tsecr);
13548b1de4cSPatrick McHardy 	} else if (options & XT_SYNPROXY_OPT_SACK_PERM)
13648b1de4cSPatrick McHardy 		*ptr++ = htonl((TCPOPT_NOP << 24) |
13748b1de4cSPatrick McHardy 			       (TCPOPT_NOP << 16) |
13848b1de4cSPatrick McHardy 			       (TCPOPT_SACK_PERM << 8) |
13948b1de4cSPatrick McHardy 			       TCPOLEN_SACK_PERM);
14048b1de4cSPatrick McHardy 
14148b1de4cSPatrick McHardy 	if (options & XT_SYNPROXY_OPT_WSCALE)
14248b1de4cSPatrick McHardy 		*ptr++ = htonl((TCPOPT_NOP << 24) |
14348b1de4cSPatrick McHardy 			       (TCPOPT_WINDOW << 16) |
14448b1de4cSPatrick McHardy 			       (TCPOLEN_WINDOW << 8) |
14548b1de4cSPatrick McHardy 			       opts->wscale);
14648b1de4cSPatrick McHardy }
14748b1de4cSPatrick McHardy EXPORT_SYMBOL_GPL(synproxy_build_options);
14848b1de4cSPatrick McHardy 
14948b1de4cSPatrick McHardy void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info,
15048b1de4cSPatrick McHardy 				    struct synproxy_options *opts)
15148b1de4cSPatrick McHardy {
15248b1de4cSPatrick McHardy 	opts->tsecr = opts->tsval;
15348b1de4cSPatrick McHardy 	opts->tsval = tcp_time_stamp & ~0x3f;
15448b1de4cSPatrick McHardy 
155c1898c4cSMartin Topholm 	if (opts->options & XT_SYNPROXY_OPT_WSCALE) {
156c1898c4cSMartin Topholm 		opts->tsval |= opts->wscale;
157c1898c4cSMartin Topholm 		opts->wscale = info->wscale;
158c1898c4cSMartin Topholm 	} else
15948b1de4cSPatrick McHardy 		opts->tsval |= 0xf;
16048b1de4cSPatrick McHardy 
16148b1de4cSPatrick McHardy 	if (opts->options & XT_SYNPROXY_OPT_SACK_PERM)
16248b1de4cSPatrick McHardy 		opts->tsval |= 1 << 4;
16348b1de4cSPatrick McHardy 
16448b1de4cSPatrick McHardy 	if (opts->options & XT_SYNPROXY_OPT_ECN)
16548b1de4cSPatrick McHardy 		opts->tsval |= 1 << 5;
16648b1de4cSPatrick McHardy }
16748b1de4cSPatrick McHardy EXPORT_SYMBOL_GPL(synproxy_init_timestamp_cookie);
16848b1de4cSPatrick McHardy 
16948b1de4cSPatrick McHardy void synproxy_check_timestamp_cookie(struct synproxy_options *opts)
17048b1de4cSPatrick McHardy {
17148b1de4cSPatrick McHardy 	opts->wscale = opts->tsecr & 0xf;
17248b1de4cSPatrick McHardy 	if (opts->wscale != 0xf)
17348b1de4cSPatrick McHardy 		opts->options |= XT_SYNPROXY_OPT_WSCALE;
17448b1de4cSPatrick McHardy 
17548b1de4cSPatrick McHardy 	opts->options |= opts->tsecr & (1 << 4) ? XT_SYNPROXY_OPT_SACK_PERM : 0;
17648b1de4cSPatrick McHardy 
17748b1de4cSPatrick McHardy 	opts->options |= opts->tsecr & (1 << 5) ? XT_SYNPROXY_OPT_ECN : 0;
17848b1de4cSPatrick McHardy }
17948b1de4cSPatrick McHardy EXPORT_SYMBOL_GPL(synproxy_check_timestamp_cookie);
18048b1de4cSPatrick McHardy 
18148b1de4cSPatrick McHardy unsigned int synproxy_tstamp_adjust(struct sk_buff *skb,
18248b1de4cSPatrick McHardy 				    unsigned int protoff,
18348b1de4cSPatrick McHardy 				    struct tcphdr *th,
18448b1de4cSPatrick McHardy 				    struct nf_conn *ct,
18548b1de4cSPatrick McHardy 				    enum ip_conntrack_info ctinfo,
18648b1de4cSPatrick McHardy 				    const struct nf_conn_synproxy *synproxy)
18748b1de4cSPatrick McHardy {
18848b1de4cSPatrick McHardy 	unsigned int optoff, optend;
18948b1de4cSPatrick McHardy 	u32 *ptr, old;
19048b1de4cSPatrick McHardy 
19148b1de4cSPatrick McHardy 	if (synproxy->tsoff == 0)
19248b1de4cSPatrick McHardy 		return 1;
19348b1de4cSPatrick McHardy 
19448b1de4cSPatrick McHardy 	optoff = protoff + sizeof(struct tcphdr);
19548b1de4cSPatrick McHardy 	optend = protoff + th->doff * 4;
19648b1de4cSPatrick McHardy 
19748b1de4cSPatrick McHardy 	if (!skb_make_writable(skb, optend))
19848b1de4cSPatrick McHardy 		return 0;
19948b1de4cSPatrick McHardy 
20048b1de4cSPatrick McHardy 	while (optoff < optend) {
20148b1de4cSPatrick McHardy 		unsigned char *op = skb->data + optoff;
20248b1de4cSPatrick McHardy 
20348b1de4cSPatrick McHardy 		switch (op[0]) {
20448b1de4cSPatrick McHardy 		case TCPOPT_EOL:
20548b1de4cSPatrick McHardy 			return 1;
20648b1de4cSPatrick McHardy 		case TCPOPT_NOP:
20748b1de4cSPatrick McHardy 			optoff++;
20848b1de4cSPatrick McHardy 			continue;
20948b1de4cSPatrick McHardy 		default:
21048b1de4cSPatrick McHardy 			if (optoff + 1 == optend ||
21148b1de4cSPatrick McHardy 			    optoff + op[1] > optend ||
21248b1de4cSPatrick McHardy 			    op[1] < 2)
21348b1de4cSPatrick McHardy 				return 0;
21448b1de4cSPatrick McHardy 			if (op[0] == TCPOPT_TIMESTAMP &&
21548b1de4cSPatrick McHardy 			    op[1] == TCPOLEN_TIMESTAMP) {
21648b1de4cSPatrick McHardy 				if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
21748b1de4cSPatrick McHardy 					ptr = (u32 *)&op[2];
21848b1de4cSPatrick McHardy 					old = *ptr;
21948b1de4cSPatrick McHardy 					*ptr = htonl(ntohl(*ptr) -
22048b1de4cSPatrick McHardy 						     synproxy->tsoff);
22148b1de4cSPatrick McHardy 				} else {
22248b1de4cSPatrick McHardy 					ptr = (u32 *)&op[6];
22348b1de4cSPatrick McHardy 					old = *ptr;
22448b1de4cSPatrick McHardy 					*ptr = htonl(ntohl(*ptr) +
22548b1de4cSPatrick McHardy 						     synproxy->tsoff);
22648b1de4cSPatrick McHardy 				}
22748b1de4cSPatrick McHardy 				inet_proto_csum_replace4(&th->check, skb,
22848b1de4cSPatrick McHardy 							 old, *ptr, 0);
22948b1de4cSPatrick McHardy 				return 1;
23048b1de4cSPatrick McHardy 			}
23148b1de4cSPatrick McHardy 			optoff += op[1];
23248b1de4cSPatrick McHardy 		}
23348b1de4cSPatrick McHardy 	}
23448b1de4cSPatrick McHardy 	return 1;
23548b1de4cSPatrick McHardy }
23648b1de4cSPatrick McHardy EXPORT_SYMBOL_GPL(synproxy_tstamp_adjust);
23748b1de4cSPatrick McHardy 
23848b1de4cSPatrick McHardy static struct nf_ct_ext_type nf_ct_synproxy_extend __read_mostly = {
23948b1de4cSPatrick McHardy 	.len		= sizeof(struct nf_conn_synproxy),
24048b1de4cSPatrick McHardy 	.align		= __alignof__(struct nf_conn_synproxy),
24148b1de4cSPatrick McHardy 	.id		= NF_CT_EXT_SYNPROXY,
24248b1de4cSPatrick McHardy };
24348b1de4cSPatrick McHardy 
24448b1de4cSPatrick McHardy #ifdef CONFIG_PROC_FS
24548b1de4cSPatrick McHardy static void *synproxy_cpu_seq_start(struct seq_file *seq, loff_t *pos)
24648b1de4cSPatrick McHardy {
24748b1de4cSPatrick McHardy 	struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
24848b1de4cSPatrick McHardy 	int cpu;
24948b1de4cSPatrick McHardy 
25048b1de4cSPatrick McHardy 	if (*pos == 0)
25148b1de4cSPatrick McHardy 		return SEQ_START_TOKEN;
25248b1de4cSPatrick McHardy 
25348b1de4cSPatrick McHardy 	for (cpu = *pos - 1; cpu < nr_cpu_ids; cpu++) {
25448b1de4cSPatrick McHardy 		if (!cpu_possible(cpu))
25548b1de4cSPatrick McHardy 			continue;
25648b1de4cSPatrick McHardy 		*pos = cpu + 1;
25748b1de4cSPatrick McHardy 		return per_cpu_ptr(snet->stats, cpu);
25848b1de4cSPatrick McHardy 	}
25948b1de4cSPatrick McHardy 
26048b1de4cSPatrick McHardy 	return NULL;
26148b1de4cSPatrick McHardy }
26248b1de4cSPatrick McHardy 
26348b1de4cSPatrick McHardy static void *synproxy_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
26448b1de4cSPatrick McHardy {
26548b1de4cSPatrick McHardy 	struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
26648b1de4cSPatrick McHardy 	int cpu;
26748b1de4cSPatrick McHardy 
26848b1de4cSPatrick McHardy 	for (cpu = *pos; cpu < nr_cpu_ids; cpu++) {
26948b1de4cSPatrick McHardy 		if (!cpu_possible(cpu))
27048b1de4cSPatrick McHardy 			continue;
27148b1de4cSPatrick McHardy 		*pos = cpu + 1;
27248b1de4cSPatrick McHardy 		return per_cpu_ptr(snet->stats, cpu);
27348b1de4cSPatrick McHardy 	}
27448b1de4cSPatrick McHardy 
27548b1de4cSPatrick McHardy 	return NULL;
27648b1de4cSPatrick McHardy }
27748b1de4cSPatrick McHardy 
27848b1de4cSPatrick McHardy static void synproxy_cpu_seq_stop(struct seq_file *seq, void *v)
27948b1de4cSPatrick McHardy {
28048b1de4cSPatrick McHardy 	return;
28148b1de4cSPatrick McHardy }
28248b1de4cSPatrick McHardy 
28348b1de4cSPatrick McHardy static int synproxy_cpu_seq_show(struct seq_file *seq, void *v)
28448b1de4cSPatrick McHardy {
28548b1de4cSPatrick McHardy 	struct synproxy_stats *stats = v;
28648b1de4cSPatrick McHardy 
28748b1de4cSPatrick McHardy 	if (v == SEQ_START_TOKEN) {
28848b1de4cSPatrick McHardy 		seq_printf(seq, "entries\t\tsyn_received\t"
28948b1de4cSPatrick McHardy 				"cookie_invalid\tcookie_valid\t"
29048b1de4cSPatrick McHardy 				"cookie_retrans\tconn_reopened\n");
29148b1de4cSPatrick McHardy 		return 0;
29248b1de4cSPatrick McHardy 	}
29348b1de4cSPatrick McHardy 
29448b1de4cSPatrick McHardy 	seq_printf(seq, "%08x\t%08x\t%08x\t%08x\t%08x\t%08x\n", 0,
29548b1de4cSPatrick McHardy 		   stats->syn_received,
29648b1de4cSPatrick McHardy 		   stats->cookie_invalid,
29748b1de4cSPatrick McHardy 		   stats->cookie_valid,
29848b1de4cSPatrick McHardy 		   stats->cookie_retrans,
29948b1de4cSPatrick McHardy 		   stats->conn_reopened);
30048b1de4cSPatrick McHardy 
30148b1de4cSPatrick McHardy 	return 0;
30248b1de4cSPatrick McHardy }
30348b1de4cSPatrick McHardy 
30448b1de4cSPatrick McHardy static const struct seq_operations synproxy_cpu_seq_ops = {
30548b1de4cSPatrick McHardy 	.start		= synproxy_cpu_seq_start,
30648b1de4cSPatrick McHardy 	.next		= synproxy_cpu_seq_next,
30748b1de4cSPatrick McHardy 	.stop		= synproxy_cpu_seq_stop,
30848b1de4cSPatrick McHardy 	.show		= synproxy_cpu_seq_show,
30948b1de4cSPatrick McHardy };
31048b1de4cSPatrick McHardy 
31148b1de4cSPatrick McHardy static int synproxy_cpu_seq_open(struct inode *inode, struct file *file)
31248b1de4cSPatrick McHardy {
31348b1de4cSPatrick McHardy 	return seq_open_net(inode, file, &synproxy_cpu_seq_ops,
31448b1de4cSPatrick McHardy 			    sizeof(struct seq_net_private));
31548b1de4cSPatrick McHardy }
31648b1de4cSPatrick McHardy 
31748b1de4cSPatrick McHardy static const struct file_operations synproxy_cpu_seq_fops = {
31848b1de4cSPatrick McHardy 	.owner		= THIS_MODULE,
31948b1de4cSPatrick McHardy 	.open		= synproxy_cpu_seq_open,
32048b1de4cSPatrick McHardy 	.read		= seq_read,
32148b1de4cSPatrick McHardy 	.llseek		= seq_lseek,
32248b1de4cSPatrick McHardy 	.release	= seq_release_net,
32348b1de4cSPatrick McHardy };
32448b1de4cSPatrick McHardy 
32548b1de4cSPatrick McHardy static int __net_init synproxy_proc_init(struct net *net)
32648b1de4cSPatrick McHardy {
32748b1de4cSPatrick McHardy 	if (!proc_create("synproxy", S_IRUGO, net->proc_net_stat,
32848b1de4cSPatrick McHardy 			 &synproxy_cpu_seq_fops))
32948b1de4cSPatrick McHardy 		return -ENOMEM;
33048b1de4cSPatrick McHardy 	return 0;
33148b1de4cSPatrick McHardy }
33248b1de4cSPatrick McHardy 
33348b1de4cSPatrick McHardy static void __net_exit synproxy_proc_exit(struct net *net)
33448b1de4cSPatrick McHardy {
33548b1de4cSPatrick McHardy 	remove_proc_entry("synproxy", net->proc_net_stat);
33648b1de4cSPatrick McHardy }
33748b1de4cSPatrick McHardy #else
33848b1de4cSPatrick McHardy static int __net_init synproxy_proc_init(struct net *net)
33948b1de4cSPatrick McHardy {
34048b1de4cSPatrick McHardy 	return 0;
34148b1de4cSPatrick McHardy }
34248b1de4cSPatrick McHardy 
34348b1de4cSPatrick McHardy static void __net_exit synproxy_proc_exit(struct net *net)
34448b1de4cSPatrick McHardy {
34548b1de4cSPatrick McHardy 	return;
34648b1de4cSPatrick McHardy }
34748b1de4cSPatrick McHardy #endif /* CONFIG_PROC_FS */
34848b1de4cSPatrick McHardy 
34948b1de4cSPatrick McHardy static int __net_init synproxy_net_init(struct net *net)
35048b1de4cSPatrick McHardy {
35148b1de4cSPatrick McHardy 	struct synproxy_net *snet = synproxy_pernet(net);
35248b1de4cSPatrick McHardy 	struct nf_conn *ct;
35348b1de4cSPatrick McHardy 	int err = -ENOMEM;
35448b1de4cSPatrick McHardy 
3550838aa7fSPablo Neira Ayuso 	ct = nf_ct_tmpl_alloc(net, 0, GFP_KERNEL);
3561a727c63SDan Carpenter 	if (!ct)
35748b1de4cSPatrick McHardy 		goto err1;
35848b1de4cSPatrick McHardy 
35948b1de4cSPatrick McHardy 	if (!nfct_seqadj_ext_add(ct))
36048b1de4cSPatrick McHardy 		goto err2;
36148b1de4cSPatrick McHardy 	if (!nfct_synproxy_ext_add(ct))
36248b1de4cSPatrick McHardy 		goto err2;
36348b1de4cSPatrick McHardy 
3640838aa7fSPablo Neira Ayuso 	__set_bit(IPS_CONFIRMED_BIT, &ct->status);
3650838aa7fSPablo Neira Ayuso 	nf_conntrack_get(&ct->ct_general);
36648b1de4cSPatrick McHardy 	snet->tmpl = ct;
36748b1de4cSPatrick McHardy 
36848b1de4cSPatrick McHardy 	snet->stats = alloc_percpu(struct synproxy_stats);
36948b1de4cSPatrick McHardy 	if (snet->stats == NULL)
37048b1de4cSPatrick McHardy 		goto err2;
37148b1de4cSPatrick McHardy 
37248b1de4cSPatrick McHardy 	err = synproxy_proc_init(net);
37348b1de4cSPatrick McHardy 	if (err < 0)
37448b1de4cSPatrick McHardy 		goto err3;
37548b1de4cSPatrick McHardy 
37648b1de4cSPatrick McHardy 	return 0;
37748b1de4cSPatrick McHardy 
37848b1de4cSPatrick McHardy err3:
37948b1de4cSPatrick McHardy 	free_percpu(snet->stats);
38048b1de4cSPatrick McHardy err2:
3819cf94eabSDaniel Borkmann 	nf_ct_tmpl_free(ct);
38248b1de4cSPatrick McHardy err1:
38348b1de4cSPatrick McHardy 	return err;
38448b1de4cSPatrick McHardy }
38548b1de4cSPatrick McHardy 
38648b1de4cSPatrick McHardy static void __net_exit synproxy_net_exit(struct net *net)
38748b1de4cSPatrick McHardy {
38848b1de4cSPatrick McHardy 	struct synproxy_net *snet = synproxy_pernet(net);
38948b1de4cSPatrick McHardy 
390e53376beSPablo Neira Ayuso 	nf_ct_put(snet->tmpl);
39148b1de4cSPatrick McHardy 	synproxy_proc_exit(net);
39248b1de4cSPatrick McHardy 	free_percpu(snet->stats);
39348b1de4cSPatrick McHardy }
39448b1de4cSPatrick McHardy 
39548b1de4cSPatrick McHardy static struct pernet_operations synproxy_net_ops = {
39648b1de4cSPatrick McHardy 	.init		= synproxy_net_init,
39748b1de4cSPatrick McHardy 	.exit		= synproxy_net_exit,
39848b1de4cSPatrick McHardy 	.id		= &synproxy_net_id,
39948b1de4cSPatrick McHardy 	.size		= sizeof(struct synproxy_net),
40048b1de4cSPatrick McHardy };
40148b1de4cSPatrick McHardy 
40248b1de4cSPatrick McHardy static int __init synproxy_core_init(void)
40348b1de4cSPatrick McHardy {
40448b1de4cSPatrick McHardy 	int err;
40548b1de4cSPatrick McHardy 
40648b1de4cSPatrick McHardy 	err = nf_ct_extend_register(&nf_ct_synproxy_extend);
40748b1de4cSPatrick McHardy 	if (err < 0)
40848b1de4cSPatrick McHardy 		goto err1;
40948b1de4cSPatrick McHardy 
41048b1de4cSPatrick McHardy 	err = register_pernet_subsys(&synproxy_net_ops);
41148b1de4cSPatrick McHardy 	if (err < 0)
41248b1de4cSPatrick McHardy 		goto err2;
41348b1de4cSPatrick McHardy 
41448b1de4cSPatrick McHardy 	return 0;
41548b1de4cSPatrick McHardy 
41648b1de4cSPatrick McHardy err2:
41748b1de4cSPatrick McHardy 	nf_ct_extend_unregister(&nf_ct_synproxy_extend);
41848b1de4cSPatrick McHardy err1:
41948b1de4cSPatrick McHardy 	return err;
42048b1de4cSPatrick McHardy }
42148b1de4cSPatrick McHardy 
42248b1de4cSPatrick McHardy static void __exit synproxy_core_exit(void)
42348b1de4cSPatrick McHardy {
42448b1de4cSPatrick McHardy 	unregister_pernet_subsys(&synproxy_net_ops);
42548b1de4cSPatrick McHardy 	nf_ct_extend_unregister(&nf_ct_synproxy_extend);
42648b1de4cSPatrick McHardy }
42748b1de4cSPatrick McHardy 
42848b1de4cSPatrick McHardy module_init(synproxy_core_init);
42948b1de4cSPatrick McHardy module_exit(synproxy_core_exit);
43048b1de4cSPatrick McHardy 
43148b1de4cSPatrick McHardy MODULE_LICENSE("GPL");
43248b1de4cSPatrick McHardy MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
433