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