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