1/*
2 * Copyright (c) 2013 Patrick McHardy <kaber@trash.net>
3 *
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.
7 */
8
9#include <linux/module.h>
10#include <linux/skbuff.h>
11#include <net/ip6_checksum.h>
12#include <net/ip6_route.h>
13#include <net/tcp.h>
14
15#include <linux/netfilter_ipv6/ip6_tables.h>
16#include <linux/netfilter/x_tables.h>
17#include <linux/netfilter/xt_SYNPROXY.h>
18#include <net/netfilter/nf_conntrack.h>
19#include <net/netfilter/nf_conntrack_seqadj.h>
20#include <net/netfilter/nf_conntrack_synproxy.h>
21
22static struct ipv6hdr *
23synproxy_build_ip(struct sk_buff *skb, const struct in6_addr *saddr,
24				       const struct in6_addr *daddr)
25{
26	struct ipv6hdr *iph;
27
28	skb_reset_network_header(skb);
29	iph = (struct ipv6hdr *)skb_put(skb, sizeof(*iph));
30	ip6_flow_hdr(iph, 0, 0);
31	iph->hop_limit	= 64;	//XXX
32	iph->nexthdr	= IPPROTO_TCP;
33	iph->saddr	= *saddr;
34	iph->daddr	= *daddr;
35
36	return iph;
37}
38
39static void
40synproxy_send_tcp(const struct sk_buff *skb, struct sk_buff *nskb,
41		  struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo,
42		  struct ipv6hdr *niph, struct tcphdr *nth,
43		  unsigned int tcp_hdr_size)
44{
45	struct net *net = nf_ct_net((struct nf_conn *)nfct);
46	struct dst_entry *dst;
47	struct flowi6 fl6;
48
49	nth->check = ~tcp_v6_check(tcp_hdr_size, &niph->saddr, &niph->daddr, 0);
50	nskb->ip_summed   = CHECKSUM_PARTIAL;
51	nskb->csum_start  = (unsigned char *)nth - nskb->head;
52	nskb->csum_offset = offsetof(struct tcphdr, check);
53
54	memset(&fl6, 0, sizeof(fl6));
55	fl6.flowi6_proto = IPPROTO_TCP;
56	fl6.saddr = niph->saddr;
57	fl6.daddr = niph->daddr;
58	fl6.fl6_sport = nth->source;
59	fl6.fl6_dport = nth->dest;
60	security_skb_classify_flow((struct sk_buff *)skb, flowi6_to_flowi(&fl6));
61	dst = ip6_route_output(net, NULL, &fl6);
62	if (dst == NULL || dst->error) {
63		dst_release(dst);
64		goto free_nskb;
65	}
66	dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
67	if (IS_ERR(dst))
68		goto free_nskb;
69
70	skb_dst_set(nskb, dst);
71
72	if (nfct) {
73		nskb->nfct = nfct;
74		nskb->nfctinfo = ctinfo;
75		nf_conntrack_get(nfct);
76	}
77
78	ip6_local_out(nskb);
79	return;
80
81free_nskb:
82	kfree_skb(nskb);
83}
84
85static void
86synproxy_send_client_synack(const struct sk_buff *skb, const struct tcphdr *th,
87			    const struct synproxy_options *opts)
88{
89	struct sk_buff *nskb;
90	struct ipv6hdr *iph, *niph;
91	struct tcphdr *nth;
92	unsigned int tcp_hdr_size;
93	u16 mss = opts->mss;
94
95	iph = ipv6_hdr(skb);
96
97	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
98	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
99			 GFP_ATOMIC);
100	if (nskb == NULL)
101		return;
102	skb_reserve(nskb, MAX_TCP_HEADER);
103
104	niph = synproxy_build_ip(nskb, &iph->daddr, &iph->saddr);
105
106	skb_reset_transport_header(nskb);
107	nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size);
108	nth->source	= th->dest;
109	nth->dest	= th->source;
110	nth->seq	= htonl(__cookie_v6_init_sequence(iph, th, &mss));
111	nth->ack_seq	= htonl(ntohl(th->seq) + 1);
112	tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK;
113	if (opts->options & XT_SYNPROXY_OPT_ECN)
114		tcp_flag_word(nth) |= TCP_FLAG_ECE;
115	nth->doff	= tcp_hdr_size / 4;
116	nth->window	= 0;
117	nth->check	= 0;
118	nth->urg_ptr	= 0;
119
120	synproxy_build_options(nth, opts);
121
122	synproxy_send_tcp(skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY,
123			  niph, nth, tcp_hdr_size);
124}
125
126static void
127synproxy_send_server_syn(const struct synproxy_net *snet,
128			 const struct sk_buff *skb, const struct tcphdr *th,
129			 const struct synproxy_options *opts, u32 recv_seq)
130{
131	struct sk_buff *nskb;
132	struct ipv6hdr *iph, *niph;
133	struct tcphdr *nth;
134	unsigned int tcp_hdr_size;
135
136	iph = ipv6_hdr(skb);
137
138	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
139	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
140			 GFP_ATOMIC);
141	if (nskb == NULL)
142		return;
143	skb_reserve(nskb, MAX_TCP_HEADER);
144
145	niph = synproxy_build_ip(nskb, &iph->saddr, &iph->daddr);
146
147	skb_reset_transport_header(nskb);
148	nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size);
149	nth->source	= th->source;
150	nth->dest	= th->dest;
151	nth->seq	= htonl(recv_seq - 1);
152	/* ack_seq is used to relay our ISN to the synproxy hook to initialize
153	 * sequence number translation once a connection tracking entry exists.
154	 */
155	nth->ack_seq	= htonl(ntohl(th->ack_seq) - 1);
156	tcp_flag_word(nth) = TCP_FLAG_SYN;
157	if (opts->options & XT_SYNPROXY_OPT_ECN)
158		tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR;
159	nth->doff	= tcp_hdr_size / 4;
160	nth->window	= th->window;
161	nth->check	= 0;
162	nth->urg_ptr	= 0;
163
164	synproxy_build_options(nth, opts);
165
166	synproxy_send_tcp(skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW,
167			  niph, nth, tcp_hdr_size);
168}
169
170static void
171synproxy_send_server_ack(const struct synproxy_net *snet,
172			 const struct ip_ct_tcp *state,
173			 const struct sk_buff *skb, const struct tcphdr *th,
174			 const struct synproxy_options *opts)
175{
176	struct sk_buff *nskb;
177	struct ipv6hdr *iph, *niph;
178	struct tcphdr *nth;
179	unsigned int tcp_hdr_size;
180
181	iph = ipv6_hdr(skb);
182
183	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
184	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
185			 GFP_ATOMIC);
186	if (nskb == NULL)
187		return;
188	skb_reserve(nskb, MAX_TCP_HEADER);
189
190	niph = synproxy_build_ip(nskb, &iph->daddr, &iph->saddr);
191
192	skb_reset_transport_header(nskb);
193	nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size);
194	nth->source	= th->dest;
195	nth->dest	= th->source;
196	nth->seq	= htonl(ntohl(th->ack_seq));
197	nth->ack_seq	= htonl(ntohl(th->seq) + 1);
198	tcp_flag_word(nth) = TCP_FLAG_ACK;
199	nth->doff	= tcp_hdr_size / 4;
200	nth->window	= htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin);
201	nth->check	= 0;
202	nth->urg_ptr	= 0;
203
204	synproxy_build_options(nth, opts);
205
206	synproxy_send_tcp(skb, nskb, NULL, 0, niph, nth, tcp_hdr_size);
207}
208
209static void
210synproxy_send_client_ack(const struct synproxy_net *snet,
211			 const struct sk_buff *skb, const struct tcphdr *th,
212			 const struct synproxy_options *opts)
213{
214	struct sk_buff *nskb;
215	struct ipv6hdr *iph, *niph;
216	struct tcphdr *nth;
217	unsigned int tcp_hdr_size;
218
219	iph = ipv6_hdr(skb);
220
221	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
222	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
223			 GFP_ATOMIC);
224	if (nskb == NULL)
225		return;
226	skb_reserve(nskb, MAX_TCP_HEADER);
227
228	niph = synproxy_build_ip(nskb, &iph->saddr, &iph->daddr);
229
230	skb_reset_transport_header(nskb);
231	nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size);
232	nth->source	= th->source;
233	nth->dest	= th->dest;
234	nth->seq	= htonl(ntohl(th->seq) + 1);
235	nth->ack_seq	= th->ack_seq;
236	tcp_flag_word(nth) = TCP_FLAG_ACK;
237	nth->doff	= tcp_hdr_size / 4;
238	nth->window	= ntohs(htons(th->window) >> opts->wscale);
239	nth->check	= 0;
240	nth->urg_ptr	= 0;
241
242	synproxy_build_options(nth, opts);
243
244	synproxy_send_tcp(skb, nskb, NULL, 0, niph, nth, tcp_hdr_size);
245}
246
247static bool
248synproxy_recv_client_ack(const struct synproxy_net *snet,
249			 const struct sk_buff *skb, const struct tcphdr *th,
250			 struct synproxy_options *opts, u32 recv_seq)
251{
252	int mss;
253
254	mss = __cookie_v6_check(ipv6_hdr(skb), th, ntohl(th->ack_seq) - 1);
255	if (mss == 0) {
256		this_cpu_inc(snet->stats->cookie_invalid);
257		return false;
258	}
259
260	this_cpu_inc(snet->stats->cookie_valid);
261	opts->mss = mss;
262	opts->options |= XT_SYNPROXY_OPT_MSS;
263
264	if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP)
265		synproxy_check_timestamp_cookie(opts);
266
267	synproxy_send_server_syn(snet, skb, th, opts, recv_seq);
268	return true;
269}
270
271static unsigned int
272synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par)
273{
274	const struct xt_synproxy_info *info = par->targinfo;
275	struct synproxy_net *snet = synproxy_pernet(dev_net(par->in));
276	struct synproxy_options opts = {};
277	struct tcphdr *th, _th;
278
279	if (nf_ip6_checksum(skb, par->hooknum, par->thoff, IPPROTO_TCP))
280		return NF_DROP;
281
282	th = skb_header_pointer(skb, par->thoff, sizeof(_th), &_th);
283	if (th == NULL)
284		return NF_DROP;
285
286	if (!synproxy_parse_options(skb, par->thoff, th, &opts))
287		return NF_DROP;
288
289	if (th->syn && !(th->ack || th->fin || th->rst)) {
290		/* Initial SYN from client */
291		this_cpu_inc(snet->stats->syn_received);
292
293		if (th->ece && th->cwr)
294			opts.options |= XT_SYNPROXY_OPT_ECN;
295
296		opts.options &= info->options;
297		if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
298			synproxy_init_timestamp_cookie(info, &opts);
299		else
300			opts.options &= ~(XT_SYNPROXY_OPT_WSCALE |
301					  XT_SYNPROXY_OPT_SACK_PERM |
302					  XT_SYNPROXY_OPT_ECN);
303
304		synproxy_send_client_synack(skb, th, &opts);
305		return NF_DROP;
306
307	} else if (th->ack && !(th->fin || th->rst || th->syn)) {
308		/* ACK from client */
309		synproxy_recv_client_ack(snet, skb, th, &opts, ntohl(th->seq));
310		return NF_DROP;
311	}
312
313	return XT_CONTINUE;
314}
315
316static unsigned int ipv6_synproxy_hook(const struct nf_hook_ops *ops,
317				       struct sk_buff *skb,
318				       const struct net_device *in,
319				       const struct net_device *out,
320				       int (*okfn)(struct sk_buff *))
321{
322	struct synproxy_net *snet = synproxy_pernet(dev_net(in ? : out));
323	enum ip_conntrack_info ctinfo;
324	struct nf_conn *ct;
325	struct nf_conn_synproxy *synproxy;
326	struct synproxy_options opts = {};
327	const struct ip_ct_tcp *state;
328	struct tcphdr *th, _th;
329	__be16 frag_off;
330	u8 nexthdr;
331	int thoff;
332
333	ct = nf_ct_get(skb, &ctinfo);
334	if (ct == NULL)
335		return NF_ACCEPT;
336
337	synproxy = nfct_synproxy(ct);
338	if (synproxy == NULL)
339		return NF_ACCEPT;
340
341	if (nf_is_loopback_packet(skb))
342		return NF_ACCEPT;
343
344	nexthdr = ipv6_hdr(skb)->nexthdr;
345	thoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
346				 &frag_off);
347	if (thoff < 0)
348		return NF_ACCEPT;
349
350	th = skb_header_pointer(skb, thoff, sizeof(_th), &_th);
351	if (th == NULL)
352		return NF_DROP;
353
354	state = &ct->proto.tcp;
355	switch (state->state) {
356	case TCP_CONNTRACK_CLOSE:
357		if (th->rst && !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
358			nf_ct_seqadj_init(ct, ctinfo, synproxy->isn -
359						      ntohl(th->seq) + 1);
360			break;
361		}
362
363		if (!th->syn || th->ack ||
364		    CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
365			break;
366
367		/* Reopened connection - reset the sequence number and timestamp
368		 * adjustments, they will get initialized once the connection is
369		 * reestablished.
370		 */
371		nf_ct_seqadj_init(ct, ctinfo, 0);
372		synproxy->tsoff = 0;
373		this_cpu_inc(snet->stats->conn_reopened);
374
375		/* fall through */
376	case TCP_CONNTRACK_SYN_SENT:
377		if (!synproxy_parse_options(skb, thoff, th, &opts))
378			return NF_DROP;
379
380		if (!th->syn && th->ack &&
381		    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
382			/* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
383			 * therefore we need to add 1 to make the SYN sequence
384			 * number match the one of first SYN.
385			 */
386			if (synproxy_recv_client_ack(snet, skb, th, &opts,
387						     ntohl(th->seq) + 1))
388				this_cpu_inc(snet->stats->cookie_retrans);
389
390			return NF_DROP;
391		}
392
393		synproxy->isn = ntohl(th->ack_seq);
394		if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
395			synproxy->its = opts.tsecr;
396		break;
397	case TCP_CONNTRACK_SYN_RECV:
398		if (!th->syn || !th->ack)
399			break;
400
401		if (!synproxy_parse_options(skb, thoff, th, &opts))
402			return NF_DROP;
403
404		if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
405			synproxy->tsoff = opts.tsval - synproxy->its;
406
407		opts.options &= ~(XT_SYNPROXY_OPT_MSS |
408				  XT_SYNPROXY_OPT_WSCALE |
409				  XT_SYNPROXY_OPT_SACK_PERM);
410
411		swap(opts.tsval, opts.tsecr);
412		synproxy_send_server_ack(snet, state, skb, th, &opts);
413
414		nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq));
415
416		swap(opts.tsval, opts.tsecr);
417		synproxy_send_client_ack(snet, skb, th, &opts);
418
419		consume_skb(skb);
420		return NF_STOLEN;
421	default:
422		break;
423	}
424
425	synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy);
426	return NF_ACCEPT;
427}
428
429static int synproxy_tg6_check(const struct xt_tgchk_param *par)
430{
431	const struct ip6t_entry *e = par->entryinfo;
432
433	if (!(e->ipv6.flags & IP6T_F_PROTO) ||
434	    e->ipv6.proto != IPPROTO_TCP ||
435	    e->ipv6.invflags & XT_INV_PROTO)
436		return -EINVAL;
437
438	return nf_ct_l3proto_try_module_get(par->family);
439}
440
441static void synproxy_tg6_destroy(const struct xt_tgdtor_param *par)
442{
443	nf_ct_l3proto_module_put(par->family);
444}
445
446static struct xt_target synproxy_tg6_reg __read_mostly = {
447	.name		= "SYNPROXY",
448	.family		= NFPROTO_IPV6,
449	.hooks		= (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD),
450	.target		= synproxy_tg6,
451	.targetsize	= sizeof(struct xt_synproxy_info),
452	.checkentry	= synproxy_tg6_check,
453	.destroy	= synproxy_tg6_destroy,
454	.me		= THIS_MODULE,
455};
456
457static struct nf_hook_ops ipv6_synproxy_ops[] __read_mostly = {
458	{
459		.hook		= ipv6_synproxy_hook,
460		.owner		= THIS_MODULE,
461		.pf		= NFPROTO_IPV6,
462		.hooknum	= NF_INET_LOCAL_IN,
463		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM - 1,
464	},
465	{
466		.hook		= ipv6_synproxy_hook,
467		.owner		= THIS_MODULE,
468		.pf		= NFPROTO_IPV6,
469		.hooknum	= NF_INET_POST_ROUTING,
470		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM - 1,
471	},
472};
473
474static int __init synproxy_tg6_init(void)
475{
476	int err;
477
478	err = nf_register_hooks(ipv6_synproxy_ops,
479				ARRAY_SIZE(ipv6_synproxy_ops));
480	if (err < 0)
481		goto err1;
482
483	err = xt_register_target(&synproxy_tg6_reg);
484	if (err < 0)
485		goto err2;
486
487	return 0;
488
489err2:
490	nf_unregister_hooks(ipv6_synproxy_ops, ARRAY_SIZE(ipv6_synproxy_ops));
491err1:
492	return err;
493}
494
495static void __exit synproxy_tg6_exit(void)
496{
497	xt_unregister_target(&synproxy_tg6_reg);
498	nf_unregister_hooks(ipv6_synproxy_ops, ARRAY_SIZE(ipv6_synproxy_ops));
499}
500
501module_init(synproxy_tg6_init);
502module_exit(synproxy_tg6_exit);
503
504MODULE_LICENSE("GPL");
505MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
506