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