1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2013 Patrick McHardy <kaber@trash.net>
4  */
5 
6 #include <linux/module.h>
7 #include <linux/skbuff.h>
8 #include <asm/unaligned.h>
9 #include <net/tcp.h>
10 #include <net/netns/generic.h>
11 #include <linux/proc_fs.h>
12 
13 #include <linux/netfilter_ipv6.h>
14 #include <linux/netfilter/nf_synproxy.h>
15 
16 #include <net/netfilter/nf_conntrack.h>
17 #include <net/netfilter/nf_conntrack_ecache.h>
18 #include <net/netfilter/nf_conntrack_extend.h>
19 #include <net/netfilter/nf_conntrack_seqadj.h>
20 #include <net/netfilter/nf_conntrack_synproxy.h>
21 #include <net/netfilter/nf_conntrack_zones.h>
22 #include <net/netfilter/nf_synproxy.h>
23 
24 unsigned int synproxy_net_id;
25 EXPORT_SYMBOL_GPL(synproxy_net_id);
26 
27 bool
28 synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
29 		       const struct tcphdr *th, struct synproxy_options *opts)
30 {
31 	int length = (th->doff * 4) - sizeof(*th);
32 	u8 buf[40], *ptr;
33 
34 	ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf);
35 	if (ptr == NULL)
36 		return false;
37 
38 	opts->options = 0;
39 	while (length > 0) {
40 		int opcode = *ptr++;
41 		int opsize;
42 
43 		switch (opcode) {
44 		case TCPOPT_EOL:
45 			return true;
46 		case TCPOPT_NOP:
47 			length--;
48 			continue;
49 		default:
50 			opsize = *ptr++;
51 			if (opsize < 2)
52 				return true;
53 			if (opsize > length)
54 				return true;
55 
56 			switch (opcode) {
57 			case TCPOPT_MSS:
58 				if (opsize == TCPOLEN_MSS) {
59 					opts->mss_option = get_unaligned_be16(ptr);
60 					opts->options |= NF_SYNPROXY_OPT_MSS;
61 				}
62 				break;
63 			case TCPOPT_WINDOW:
64 				if (opsize == TCPOLEN_WINDOW) {
65 					opts->wscale = *ptr;
66 					if (opts->wscale > TCP_MAX_WSCALE)
67 						opts->wscale = TCP_MAX_WSCALE;
68 					opts->options |= NF_SYNPROXY_OPT_WSCALE;
69 				}
70 				break;
71 			case TCPOPT_TIMESTAMP:
72 				if (opsize == TCPOLEN_TIMESTAMP) {
73 					opts->tsval = get_unaligned_be32(ptr);
74 					opts->tsecr = get_unaligned_be32(ptr + 4);
75 					opts->options |= NF_SYNPROXY_OPT_TIMESTAMP;
76 				}
77 				break;
78 			case TCPOPT_SACK_PERM:
79 				if (opsize == TCPOLEN_SACK_PERM)
80 					opts->options |= NF_SYNPROXY_OPT_SACK_PERM;
81 				break;
82 			}
83 
84 			ptr += opsize - 2;
85 			length -= opsize;
86 		}
87 	}
88 	return true;
89 }
90 EXPORT_SYMBOL_GPL(synproxy_parse_options);
91 
92 static unsigned int
93 synproxy_options_size(const struct synproxy_options *opts)
94 {
95 	unsigned int size = 0;
96 
97 	if (opts->options & NF_SYNPROXY_OPT_MSS)
98 		size += TCPOLEN_MSS_ALIGNED;
99 	if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP)
100 		size += TCPOLEN_TSTAMP_ALIGNED;
101 	else if (opts->options & NF_SYNPROXY_OPT_SACK_PERM)
102 		size += TCPOLEN_SACKPERM_ALIGNED;
103 	if (opts->options & NF_SYNPROXY_OPT_WSCALE)
104 		size += TCPOLEN_WSCALE_ALIGNED;
105 
106 	return size;
107 }
108 
109 static void
110 synproxy_build_options(struct tcphdr *th, const struct synproxy_options *opts)
111 {
112 	__be32 *ptr = (__be32 *)(th + 1);
113 	u8 options = opts->options;
114 
115 	if (options & NF_SYNPROXY_OPT_MSS)
116 		*ptr++ = htonl((TCPOPT_MSS << 24) |
117 			       (TCPOLEN_MSS << 16) |
118 			       opts->mss_option);
119 
120 	if (options & NF_SYNPROXY_OPT_TIMESTAMP) {
121 		if (options & NF_SYNPROXY_OPT_SACK_PERM)
122 			*ptr++ = htonl((TCPOPT_SACK_PERM << 24) |
123 				       (TCPOLEN_SACK_PERM << 16) |
124 				       (TCPOPT_TIMESTAMP << 8) |
125 				       TCPOLEN_TIMESTAMP);
126 		else
127 			*ptr++ = htonl((TCPOPT_NOP << 24) |
128 				       (TCPOPT_NOP << 16) |
129 				       (TCPOPT_TIMESTAMP << 8) |
130 				       TCPOLEN_TIMESTAMP);
131 
132 		*ptr++ = htonl(opts->tsval);
133 		*ptr++ = htonl(opts->tsecr);
134 	} else if (options & NF_SYNPROXY_OPT_SACK_PERM)
135 		*ptr++ = htonl((TCPOPT_NOP << 24) |
136 			       (TCPOPT_NOP << 16) |
137 			       (TCPOPT_SACK_PERM << 8) |
138 			       TCPOLEN_SACK_PERM);
139 
140 	if (options & NF_SYNPROXY_OPT_WSCALE)
141 		*ptr++ = htonl((TCPOPT_NOP << 24) |
142 			       (TCPOPT_WINDOW << 16) |
143 			       (TCPOLEN_WINDOW << 8) |
144 			       opts->wscale);
145 }
146 
147 void synproxy_init_timestamp_cookie(const struct nf_synproxy_info *info,
148 				    struct synproxy_options *opts)
149 {
150 	opts->tsecr = opts->tsval;
151 	opts->tsval = tcp_time_stamp_raw() & ~0x3f;
152 
153 	if (opts->options & NF_SYNPROXY_OPT_WSCALE) {
154 		opts->tsval |= opts->wscale;
155 		opts->wscale = info->wscale;
156 	} else
157 		opts->tsval |= 0xf;
158 
159 	if (opts->options & NF_SYNPROXY_OPT_SACK_PERM)
160 		opts->tsval |= 1 << 4;
161 
162 	if (opts->options & NF_SYNPROXY_OPT_ECN)
163 		opts->tsval |= 1 << 5;
164 }
165 EXPORT_SYMBOL_GPL(synproxy_init_timestamp_cookie);
166 
167 static void
168 synproxy_check_timestamp_cookie(struct synproxy_options *opts)
169 {
170 	opts->wscale = opts->tsecr & 0xf;
171 	if (opts->wscale != 0xf)
172 		opts->options |= NF_SYNPROXY_OPT_WSCALE;
173 
174 	opts->options |= opts->tsecr & (1 << 4) ? NF_SYNPROXY_OPT_SACK_PERM : 0;
175 
176 	opts->options |= opts->tsecr & (1 << 5) ? NF_SYNPROXY_OPT_ECN : 0;
177 }
178 
179 static unsigned int
180 synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff,
181 		       struct tcphdr *th, struct nf_conn *ct,
182 		       enum ip_conntrack_info ctinfo,
183 		       const struct nf_conn_synproxy *synproxy)
184 {
185 	unsigned int optoff, optend;
186 	__be32 *ptr, old;
187 
188 	if (synproxy->tsoff == 0)
189 		return 1;
190 
191 	optoff = protoff + sizeof(struct tcphdr);
192 	optend = protoff + th->doff * 4;
193 
194 	if (skb_ensure_writable(skb, optend))
195 		return 0;
196 
197 	while (optoff < optend) {
198 		unsigned char *op = skb->data + optoff;
199 
200 		switch (op[0]) {
201 		case TCPOPT_EOL:
202 			return 1;
203 		case TCPOPT_NOP:
204 			optoff++;
205 			continue;
206 		default:
207 			if (optoff + 1 == optend ||
208 			    optoff + op[1] > optend ||
209 			    op[1] < 2)
210 				return 0;
211 			if (op[0] == TCPOPT_TIMESTAMP &&
212 			    op[1] == TCPOLEN_TIMESTAMP) {
213 				if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
214 					ptr = (__be32 *)&op[2];
215 					old = *ptr;
216 					*ptr = htonl(ntohl(*ptr) -
217 						     synproxy->tsoff);
218 				} else {
219 					ptr = (__be32 *)&op[6];
220 					old = *ptr;
221 					*ptr = htonl(ntohl(*ptr) +
222 						     synproxy->tsoff);
223 				}
224 				inet_proto_csum_replace4(&th->check, skb,
225 							 old, *ptr, false);
226 				return 1;
227 			}
228 			optoff += op[1];
229 		}
230 	}
231 	return 1;
232 }
233 
234 static struct nf_ct_ext_type nf_ct_synproxy_extend __read_mostly = {
235 	.len		= sizeof(struct nf_conn_synproxy),
236 	.align		= __alignof__(struct nf_conn_synproxy),
237 	.id		= NF_CT_EXT_SYNPROXY,
238 };
239 
240 #ifdef CONFIG_PROC_FS
241 static void *synproxy_cpu_seq_start(struct seq_file *seq, loff_t *pos)
242 {
243 	struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
244 	int cpu;
245 
246 	if (*pos == 0)
247 		return SEQ_START_TOKEN;
248 
249 	for (cpu = *pos - 1; cpu < nr_cpu_ids; cpu++) {
250 		if (!cpu_possible(cpu))
251 			continue;
252 		*pos = cpu + 1;
253 		return per_cpu_ptr(snet->stats, cpu);
254 	}
255 
256 	return NULL;
257 }
258 
259 static void *synproxy_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
260 {
261 	struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
262 	int cpu;
263 
264 	for (cpu = *pos; cpu < nr_cpu_ids; cpu++) {
265 		if (!cpu_possible(cpu))
266 			continue;
267 		*pos = cpu + 1;
268 		return per_cpu_ptr(snet->stats, cpu);
269 	}
270 	(*pos)++;
271 	return NULL;
272 }
273 
274 static void synproxy_cpu_seq_stop(struct seq_file *seq, void *v)
275 {
276 	return;
277 }
278 
279 static int synproxy_cpu_seq_show(struct seq_file *seq, void *v)
280 {
281 	struct synproxy_stats *stats = v;
282 
283 	if (v == SEQ_START_TOKEN) {
284 		seq_puts(seq, "entries\t\tsyn_received\t"
285 			      "cookie_invalid\tcookie_valid\t"
286 			      "cookie_retrans\tconn_reopened\n");
287 		return 0;
288 	}
289 
290 	seq_printf(seq, "%08x\t%08x\t%08x\t%08x\t%08x\t%08x\n", 0,
291 		   stats->syn_received,
292 		   stats->cookie_invalid,
293 		   stats->cookie_valid,
294 		   stats->cookie_retrans,
295 		   stats->conn_reopened);
296 
297 	return 0;
298 }
299 
300 static const struct seq_operations synproxy_cpu_seq_ops = {
301 	.start		= synproxy_cpu_seq_start,
302 	.next		= synproxy_cpu_seq_next,
303 	.stop		= synproxy_cpu_seq_stop,
304 	.show		= synproxy_cpu_seq_show,
305 };
306 
307 static int __net_init synproxy_proc_init(struct net *net)
308 {
309 	if (!proc_create_net("synproxy", 0444, net->proc_net_stat,
310 			&synproxy_cpu_seq_ops, sizeof(struct seq_net_private)))
311 		return -ENOMEM;
312 	return 0;
313 }
314 
315 static void __net_exit synproxy_proc_exit(struct net *net)
316 {
317 	remove_proc_entry("synproxy", net->proc_net_stat);
318 }
319 #else
320 static int __net_init synproxy_proc_init(struct net *net)
321 {
322 	return 0;
323 }
324 
325 static void __net_exit synproxy_proc_exit(struct net *net)
326 {
327 	return;
328 }
329 #endif /* CONFIG_PROC_FS */
330 
331 static int __net_init synproxy_net_init(struct net *net)
332 {
333 	struct synproxy_net *snet = synproxy_pernet(net);
334 	struct nf_conn *ct;
335 	int err = -ENOMEM;
336 
337 	ct = nf_ct_tmpl_alloc(net, &nf_ct_zone_dflt, GFP_KERNEL);
338 	if (!ct)
339 		goto err1;
340 
341 	if (!nfct_seqadj_ext_add(ct))
342 		goto err2;
343 	if (!nfct_synproxy_ext_add(ct))
344 		goto err2;
345 
346 	__set_bit(IPS_CONFIRMED_BIT, &ct->status);
347 	nf_conntrack_get(&ct->ct_general);
348 	snet->tmpl = ct;
349 
350 	snet->stats = alloc_percpu(struct synproxy_stats);
351 	if (snet->stats == NULL)
352 		goto err2;
353 
354 	err = synproxy_proc_init(net);
355 	if (err < 0)
356 		goto err3;
357 
358 	return 0;
359 
360 err3:
361 	free_percpu(snet->stats);
362 err2:
363 	nf_ct_tmpl_free(ct);
364 err1:
365 	return err;
366 }
367 
368 static void __net_exit synproxy_net_exit(struct net *net)
369 {
370 	struct synproxy_net *snet = synproxy_pernet(net);
371 
372 	nf_ct_put(snet->tmpl);
373 	synproxy_proc_exit(net);
374 	free_percpu(snet->stats);
375 }
376 
377 static struct pernet_operations synproxy_net_ops = {
378 	.init		= synproxy_net_init,
379 	.exit		= synproxy_net_exit,
380 	.id		= &synproxy_net_id,
381 	.size		= sizeof(struct synproxy_net),
382 };
383 
384 static int __init synproxy_core_init(void)
385 {
386 	int err;
387 
388 	err = nf_ct_extend_register(&nf_ct_synproxy_extend);
389 	if (err < 0)
390 		goto err1;
391 
392 	err = register_pernet_subsys(&synproxy_net_ops);
393 	if (err < 0)
394 		goto err2;
395 
396 	return 0;
397 
398 err2:
399 	nf_ct_extend_unregister(&nf_ct_synproxy_extend);
400 err1:
401 	return err;
402 }
403 
404 static void __exit synproxy_core_exit(void)
405 {
406 	unregister_pernet_subsys(&synproxy_net_ops);
407 	nf_ct_extend_unregister(&nf_ct_synproxy_extend);
408 }
409 
410 module_init(synproxy_core_init);
411 module_exit(synproxy_core_exit);
412 
413 static struct iphdr *
414 synproxy_build_ip(struct net *net, struct sk_buff *skb, __be32 saddr,
415 		  __be32 daddr)
416 {
417 	struct iphdr *iph;
418 
419 	skb_reset_network_header(skb);
420 	iph = skb_put(skb, sizeof(*iph));
421 	iph->version	= 4;
422 	iph->ihl	= sizeof(*iph) / 4;
423 	iph->tos	= 0;
424 	iph->id		= 0;
425 	iph->frag_off	= htons(IP_DF);
426 	iph->ttl	= net->ipv4.sysctl_ip_default_ttl;
427 	iph->protocol	= IPPROTO_TCP;
428 	iph->check	= 0;
429 	iph->saddr	= saddr;
430 	iph->daddr	= daddr;
431 
432 	return iph;
433 }
434 
435 static void
436 synproxy_send_tcp(struct net *net,
437 		  const struct sk_buff *skb, struct sk_buff *nskb,
438 		  struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo,
439 		  struct iphdr *niph, struct tcphdr *nth,
440 		  unsigned int tcp_hdr_size)
441 {
442 	nth->check = ~tcp_v4_check(tcp_hdr_size, niph->saddr, niph->daddr, 0);
443 	nskb->ip_summed   = CHECKSUM_PARTIAL;
444 	nskb->csum_start  = (unsigned char *)nth - nskb->head;
445 	nskb->csum_offset = offsetof(struct tcphdr, check);
446 
447 	skb_dst_set_noref(nskb, skb_dst(skb));
448 	nskb->protocol = htons(ETH_P_IP);
449 	if (ip_route_me_harder(net, nskb, RTN_UNSPEC))
450 		goto free_nskb;
451 
452 	if (nfct) {
453 		nf_ct_set(nskb, (struct nf_conn *)nfct, ctinfo);
454 		nf_conntrack_get(nfct);
455 	}
456 
457 	ip_local_out(net, nskb->sk, nskb);
458 	return;
459 
460 free_nskb:
461 	kfree_skb(nskb);
462 }
463 
464 void
465 synproxy_send_client_synack(struct net *net,
466 			    const struct sk_buff *skb, const struct tcphdr *th,
467 			    const struct synproxy_options *opts)
468 {
469 	struct sk_buff *nskb;
470 	struct iphdr *iph, *niph;
471 	struct tcphdr *nth;
472 	unsigned int tcp_hdr_size;
473 	u16 mss = opts->mss_encode;
474 
475 	iph = ip_hdr(skb);
476 
477 	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
478 	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
479 			 GFP_ATOMIC);
480 	if (!nskb)
481 		return;
482 	skb_reserve(nskb, MAX_TCP_HEADER);
483 
484 	niph = synproxy_build_ip(net, nskb, iph->daddr, iph->saddr);
485 
486 	skb_reset_transport_header(nskb);
487 	nth = skb_put(nskb, tcp_hdr_size);
488 	nth->source	= th->dest;
489 	nth->dest	= th->source;
490 	nth->seq	= htonl(__cookie_v4_init_sequence(iph, th, &mss));
491 	nth->ack_seq	= htonl(ntohl(th->seq) + 1);
492 	tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK;
493 	if (opts->options & NF_SYNPROXY_OPT_ECN)
494 		tcp_flag_word(nth) |= TCP_FLAG_ECE;
495 	nth->doff	= tcp_hdr_size / 4;
496 	nth->window	= 0;
497 	nth->check	= 0;
498 	nth->urg_ptr	= 0;
499 
500 	synproxy_build_options(nth, opts);
501 
502 	synproxy_send_tcp(net, skb, nskb, skb_nfct(skb),
503 			  IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size);
504 }
505 EXPORT_SYMBOL_GPL(synproxy_send_client_synack);
506 
507 static void
508 synproxy_send_server_syn(struct net *net,
509 			 const struct sk_buff *skb, const struct tcphdr *th,
510 			 const struct synproxy_options *opts, u32 recv_seq)
511 {
512 	struct synproxy_net *snet = synproxy_pernet(net);
513 	struct sk_buff *nskb;
514 	struct iphdr *iph, *niph;
515 	struct tcphdr *nth;
516 	unsigned int tcp_hdr_size;
517 
518 	iph = ip_hdr(skb);
519 
520 	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
521 	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
522 			 GFP_ATOMIC);
523 	if (!nskb)
524 		return;
525 	skb_reserve(nskb, MAX_TCP_HEADER);
526 
527 	niph = synproxy_build_ip(net, nskb, iph->saddr, iph->daddr);
528 
529 	skb_reset_transport_header(nskb);
530 	nth = skb_put(nskb, tcp_hdr_size);
531 	nth->source	= th->source;
532 	nth->dest	= th->dest;
533 	nth->seq	= htonl(recv_seq - 1);
534 	/* ack_seq is used to relay our ISN to the synproxy hook to initialize
535 	 * sequence number translation once a connection tracking entry exists.
536 	 */
537 	nth->ack_seq	= htonl(ntohl(th->ack_seq) - 1);
538 	tcp_flag_word(nth) = TCP_FLAG_SYN;
539 	if (opts->options & NF_SYNPROXY_OPT_ECN)
540 		tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR;
541 	nth->doff	= tcp_hdr_size / 4;
542 	nth->window	= th->window;
543 	nth->check	= 0;
544 	nth->urg_ptr	= 0;
545 
546 	synproxy_build_options(nth, opts);
547 
548 	synproxy_send_tcp(net, skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW,
549 			  niph, nth, tcp_hdr_size);
550 }
551 
552 static void
553 synproxy_send_server_ack(struct net *net,
554 			 const struct ip_ct_tcp *state,
555 			 const struct sk_buff *skb, const struct tcphdr *th,
556 			 const struct synproxy_options *opts)
557 {
558 	struct sk_buff *nskb;
559 	struct iphdr *iph, *niph;
560 	struct tcphdr *nth;
561 	unsigned int tcp_hdr_size;
562 
563 	iph = ip_hdr(skb);
564 
565 	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
566 	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
567 			 GFP_ATOMIC);
568 	if (!nskb)
569 		return;
570 	skb_reserve(nskb, MAX_TCP_HEADER);
571 
572 	niph = synproxy_build_ip(net, nskb, iph->daddr, iph->saddr);
573 
574 	skb_reset_transport_header(nskb);
575 	nth = skb_put(nskb, tcp_hdr_size);
576 	nth->source	= th->dest;
577 	nth->dest	= th->source;
578 	nth->seq	= htonl(ntohl(th->ack_seq));
579 	nth->ack_seq	= htonl(ntohl(th->seq) + 1);
580 	tcp_flag_word(nth) = TCP_FLAG_ACK;
581 	nth->doff	= tcp_hdr_size / 4;
582 	nth->window	= htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin);
583 	nth->check	= 0;
584 	nth->urg_ptr	= 0;
585 
586 	synproxy_build_options(nth, opts);
587 
588 	synproxy_send_tcp(net, skb, nskb, NULL, 0, niph, nth, tcp_hdr_size);
589 }
590 
591 static void
592 synproxy_send_client_ack(struct net *net,
593 			 const struct sk_buff *skb, const struct tcphdr *th,
594 			 const struct synproxy_options *opts)
595 {
596 	struct sk_buff *nskb;
597 	struct iphdr *iph, *niph;
598 	struct tcphdr *nth;
599 	unsigned int tcp_hdr_size;
600 
601 	iph = ip_hdr(skb);
602 
603 	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
604 	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
605 			 GFP_ATOMIC);
606 	if (!nskb)
607 		return;
608 	skb_reserve(nskb, MAX_TCP_HEADER);
609 
610 	niph = synproxy_build_ip(net, nskb, iph->saddr, iph->daddr);
611 
612 	skb_reset_transport_header(nskb);
613 	nth = skb_put(nskb, tcp_hdr_size);
614 	nth->source	= th->source;
615 	nth->dest	= th->dest;
616 	nth->seq	= htonl(ntohl(th->seq) + 1);
617 	nth->ack_seq	= th->ack_seq;
618 	tcp_flag_word(nth) = TCP_FLAG_ACK;
619 	nth->doff	= tcp_hdr_size / 4;
620 	nth->window	= htons(ntohs(th->window) >> opts->wscale);
621 	nth->check	= 0;
622 	nth->urg_ptr	= 0;
623 
624 	synproxy_build_options(nth, opts);
625 
626 	synproxy_send_tcp(net, skb, nskb, skb_nfct(skb),
627 			  IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size);
628 }
629 
630 bool
631 synproxy_recv_client_ack(struct net *net,
632 			 const struct sk_buff *skb, const struct tcphdr *th,
633 			 struct synproxy_options *opts, u32 recv_seq)
634 {
635 	struct synproxy_net *snet = synproxy_pernet(net);
636 	int mss;
637 
638 	mss = __cookie_v4_check(ip_hdr(skb), th, ntohl(th->ack_seq) - 1);
639 	if (mss == 0) {
640 		this_cpu_inc(snet->stats->cookie_invalid);
641 		return false;
642 	}
643 
644 	this_cpu_inc(snet->stats->cookie_valid);
645 	opts->mss_option = mss;
646 	opts->options |= NF_SYNPROXY_OPT_MSS;
647 
648 	if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP)
649 		synproxy_check_timestamp_cookie(opts);
650 
651 	synproxy_send_server_syn(net, skb, th, opts, recv_seq);
652 	return true;
653 }
654 EXPORT_SYMBOL_GPL(synproxy_recv_client_ack);
655 
656 unsigned int
657 ipv4_synproxy_hook(void *priv, struct sk_buff *skb,
658 		   const struct nf_hook_state *nhs)
659 {
660 	struct net *net = nhs->net;
661 	struct synproxy_net *snet = synproxy_pernet(net);
662 	enum ip_conntrack_info ctinfo;
663 	struct nf_conn *ct;
664 	struct nf_conn_synproxy *synproxy;
665 	struct synproxy_options opts = {};
666 	const struct ip_ct_tcp *state;
667 	struct tcphdr *th, _th;
668 	unsigned int thoff;
669 
670 	ct = nf_ct_get(skb, &ctinfo);
671 	if (!ct)
672 		return NF_ACCEPT;
673 
674 	synproxy = nfct_synproxy(ct);
675 	if (!synproxy)
676 		return NF_ACCEPT;
677 
678 	if (nf_is_loopback_packet(skb) ||
679 	    ip_hdr(skb)->protocol != IPPROTO_TCP)
680 		return NF_ACCEPT;
681 
682 	thoff = ip_hdrlen(skb);
683 	th = skb_header_pointer(skb, thoff, sizeof(_th), &_th);
684 	if (!th)
685 		return NF_DROP;
686 
687 	state = &ct->proto.tcp;
688 	switch (state->state) {
689 	case TCP_CONNTRACK_CLOSE:
690 		if (th->rst && CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) {
691 			nf_ct_seqadj_init(ct, ctinfo, synproxy->isn -
692 						      ntohl(th->seq) + 1);
693 			break;
694 		}
695 
696 		if (!th->syn || th->ack ||
697 		    CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
698 			break;
699 
700 		/* Reopened connection - reset the sequence number and timestamp
701 		 * adjustments, they will get initialized once the connection is
702 		 * reestablished.
703 		 */
704 		nf_ct_seqadj_init(ct, ctinfo, 0);
705 		synproxy->tsoff = 0;
706 		this_cpu_inc(snet->stats->conn_reopened);
707 
708 		/* fall through */
709 	case TCP_CONNTRACK_SYN_SENT:
710 		if (!synproxy_parse_options(skb, thoff, th, &opts))
711 			return NF_DROP;
712 
713 		if (!th->syn && th->ack &&
714 		    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
715 			/* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
716 			 * therefore we need to add 1 to make the SYN sequence
717 			 * number match the one of first SYN.
718 			 */
719 			if (synproxy_recv_client_ack(net, skb, th, &opts,
720 						     ntohl(th->seq) + 1)) {
721 				this_cpu_inc(snet->stats->cookie_retrans);
722 				consume_skb(skb);
723 				return NF_STOLEN;
724 			} else {
725 				return NF_DROP;
726 			}
727 		}
728 
729 		synproxy->isn = ntohl(th->ack_seq);
730 		if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP)
731 			synproxy->its = opts.tsecr;
732 
733 		nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
734 		break;
735 	case TCP_CONNTRACK_SYN_RECV:
736 		if (!th->syn || !th->ack)
737 			break;
738 
739 		if (!synproxy_parse_options(skb, thoff, th, &opts))
740 			return NF_DROP;
741 
742 		if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) {
743 			synproxy->tsoff = opts.tsval - synproxy->its;
744 			nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
745 		}
746 
747 		opts.options &= ~(NF_SYNPROXY_OPT_MSS |
748 				  NF_SYNPROXY_OPT_WSCALE |
749 				  NF_SYNPROXY_OPT_SACK_PERM);
750 
751 		swap(opts.tsval, opts.tsecr);
752 		synproxy_send_server_ack(net, state, skb, th, &opts);
753 
754 		nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq));
755 		nf_conntrack_event_cache(IPCT_SEQADJ, ct);
756 
757 		swap(opts.tsval, opts.tsecr);
758 		synproxy_send_client_ack(net, skb, th, &opts);
759 
760 		consume_skb(skb);
761 		return NF_STOLEN;
762 	default:
763 		break;
764 	}
765 
766 	synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy);
767 	return NF_ACCEPT;
768 }
769 EXPORT_SYMBOL_GPL(ipv4_synproxy_hook);
770 
771 static const struct nf_hook_ops ipv4_synproxy_ops[] = {
772 	{
773 		.hook		= ipv4_synproxy_hook,
774 		.pf		= NFPROTO_IPV4,
775 		.hooknum	= NF_INET_LOCAL_IN,
776 		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM - 1,
777 	},
778 	{
779 		.hook		= ipv4_synproxy_hook,
780 		.pf		= NFPROTO_IPV4,
781 		.hooknum	= NF_INET_POST_ROUTING,
782 		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM - 1,
783 	},
784 };
785 
786 int nf_synproxy_ipv4_init(struct synproxy_net *snet, struct net *net)
787 {
788 	int err;
789 
790 	if (snet->hook_ref4 == 0) {
791 		err = nf_register_net_hooks(net, ipv4_synproxy_ops,
792 					    ARRAY_SIZE(ipv4_synproxy_ops));
793 		if (err)
794 			return err;
795 	}
796 
797 	snet->hook_ref4++;
798 	return 0;
799 }
800 EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_init);
801 
802 void nf_synproxy_ipv4_fini(struct synproxy_net *snet, struct net *net)
803 {
804 	snet->hook_ref4--;
805 	if (snet->hook_ref4 == 0)
806 		nf_unregister_net_hooks(net, ipv4_synproxy_ops,
807 					ARRAY_SIZE(ipv4_synproxy_ops));
808 }
809 EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_fini);
810 
811 #if IS_ENABLED(CONFIG_IPV6)
812 static struct ipv6hdr *
813 synproxy_build_ip_ipv6(struct net *net, struct sk_buff *skb,
814 		       const struct in6_addr *saddr,
815 		       const struct in6_addr *daddr)
816 {
817 	struct ipv6hdr *iph;
818 
819 	skb_reset_network_header(skb);
820 	iph = skb_put(skb, sizeof(*iph));
821 	ip6_flow_hdr(iph, 0, 0);
822 	iph->hop_limit	= net->ipv6.devconf_all->hop_limit;
823 	iph->nexthdr	= IPPROTO_TCP;
824 	iph->saddr	= *saddr;
825 	iph->daddr	= *daddr;
826 
827 	return iph;
828 }
829 
830 static void
831 synproxy_send_tcp_ipv6(struct net *net,
832 		       const struct sk_buff *skb, struct sk_buff *nskb,
833 		       struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo,
834 		       struct ipv6hdr *niph, struct tcphdr *nth,
835 		       unsigned int tcp_hdr_size)
836 {
837 	struct dst_entry *dst;
838 	struct flowi6 fl6;
839 	int err;
840 
841 	nth->check = ~tcp_v6_check(tcp_hdr_size, &niph->saddr, &niph->daddr, 0);
842 	nskb->ip_summed   = CHECKSUM_PARTIAL;
843 	nskb->csum_start  = (unsigned char *)nth - nskb->head;
844 	nskb->csum_offset = offsetof(struct tcphdr, check);
845 
846 	memset(&fl6, 0, sizeof(fl6));
847 	fl6.flowi6_proto = IPPROTO_TCP;
848 	fl6.saddr = niph->saddr;
849 	fl6.daddr = niph->daddr;
850 	fl6.fl6_sport = nth->source;
851 	fl6.fl6_dport = nth->dest;
852 	security_skb_classify_flow((struct sk_buff *)skb,
853 				   flowi6_to_flowi(&fl6));
854 	err = nf_ip6_route(net, &dst, flowi6_to_flowi(&fl6), false);
855 	if (err) {
856 		goto free_nskb;
857 	}
858 
859 	dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
860 	if (IS_ERR(dst))
861 		goto free_nskb;
862 
863 	skb_dst_set(nskb, dst);
864 
865 	if (nfct) {
866 		nf_ct_set(nskb, (struct nf_conn *)nfct, ctinfo);
867 		nf_conntrack_get(nfct);
868 	}
869 
870 	ip6_local_out(net, nskb->sk, nskb);
871 	return;
872 
873 free_nskb:
874 	kfree_skb(nskb);
875 }
876 
877 void
878 synproxy_send_client_synack_ipv6(struct net *net,
879 				 const struct sk_buff *skb,
880 				 const struct tcphdr *th,
881 				 const struct synproxy_options *opts)
882 {
883 	struct sk_buff *nskb;
884 	struct ipv6hdr *iph, *niph;
885 	struct tcphdr *nth;
886 	unsigned int tcp_hdr_size;
887 	u16 mss = opts->mss_encode;
888 
889 	iph = ipv6_hdr(skb);
890 
891 	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
892 	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
893 			 GFP_ATOMIC);
894 	if (!nskb)
895 		return;
896 	skb_reserve(nskb, MAX_TCP_HEADER);
897 
898 	niph = synproxy_build_ip_ipv6(net, nskb, &iph->daddr, &iph->saddr);
899 
900 	skb_reset_transport_header(nskb);
901 	nth = skb_put(nskb, tcp_hdr_size);
902 	nth->source	= th->dest;
903 	nth->dest	= th->source;
904 	nth->seq	= htonl(nf_ipv6_cookie_init_sequence(iph, th, &mss));
905 	nth->ack_seq	= htonl(ntohl(th->seq) + 1);
906 	tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK;
907 	if (opts->options & NF_SYNPROXY_OPT_ECN)
908 		tcp_flag_word(nth) |= TCP_FLAG_ECE;
909 	nth->doff	= tcp_hdr_size / 4;
910 	nth->window	= 0;
911 	nth->check	= 0;
912 	nth->urg_ptr	= 0;
913 
914 	synproxy_build_options(nth, opts);
915 
916 	synproxy_send_tcp_ipv6(net, skb, nskb, skb_nfct(skb),
917 			       IP_CT_ESTABLISHED_REPLY, niph, nth,
918 			       tcp_hdr_size);
919 }
920 EXPORT_SYMBOL_GPL(synproxy_send_client_synack_ipv6);
921 
922 static void
923 synproxy_send_server_syn_ipv6(struct net *net, const struct sk_buff *skb,
924 			      const struct tcphdr *th,
925 			      const struct synproxy_options *opts, u32 recv_seq)
926 {
927 	struct synproxy_net *snet = synproxy_pernet(net);
928 	struct sk_buff *nskb;
929 	struct ipv6hdr *iph, *niph;
930 	struct tcphdr *nth;
931 	unsigned int tcp_hdr_size;
932 
933 	iph = ipv6_hdr(skb);
934 
935 	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
936 	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
937 			 GFP_ATOMIC);
938 	if (!nskb)
939 		return;
940 	skb_reserve(nskb, MAX_TCP_HEADER);
941 
942 	niph = synproxy_build_ip_ipv6(net, nskb, &iph->saddr, &iph->daddr);
943 
944 	skb_reset_transport_header(nskb);
945 	nth = skb_put(nskb, tcp_hdr_size);
946 	nth->source	= th->source;
947 	nth->dest	= th->dest;
948 	nth->seq	= htonl(recv_seq - 1);
949 	/* ack_seq is used to relay our ISN to the synproxy hook to initialize
950 	 * sequence number translation once a connection tracking entry exists.
951 	 */
952 	nth->ack_seq	= htonl(ntohl(th->ack_seq) - 1);
953 	tcp_flag_word(nth) = TCP_FLAG_SYN;
954 	if (opts->options & NF_SYNPROXY_OPT_ECN)
955 		tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR;
956 	nth->doff	= tcp_hdr_size / 4;
957 	nth->window	= th->window;
958 	nth->check	= 0;
959 	nth->urg_ptr	= 0;
960 
961 	synproxy_build_options(nth, opts);
962 
963 	synproxy_send_tcp_ipv6(net, skb, nskb, &snet->tmpl->ct_general,
964 			       IP_CT_NEW, niph, nth, tcp_hdr_size);
965 }
966 
967 static void
968 synproxy_send_server_ack_ipv6(struct net *net, const struct ip_ct_tcp *state,
969 			      const struct sk_buff *skb,
970 			      const struct tcphdr *th,
971 			      const struct synproxy_options *opts)
972 {
973 	struct sk_buff *nskb;
974 	struct ipv6hdr *iph, *niph;
975 	struct tcphdr *nth;
976 	unsigned int tcp_hdr_size;
977 
978 	iph = ipv6_hdr(skb);
979 
980 	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
981 	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
982 			 GFP_ATOMIC);
983 	if (!nskb)
984 		return;
985 	skb_reserve(nskb, MAX_TCP_HEADER);
986 
987 	niph = synproxy_build_ip_ipv6(net, nskb, &iph->daddr, &iph->saddr);
988 
989 	skb_reset_transport_header(nskb);
990 	nth = skb_put(nskb, tcp_hdr_size);
991 	nth->source	= th->dest;
992 	nth->dest	= th->source;
993 	nth->seq	= htonl(ntohl(th->ack_seq));
994 	nth->ack_seq	= htonl(ntohl(th->seq) + 1);
995 	tcp_flag_word(nth) = TCP_FLAG_ACK;
996 	nth->doff	= tcp_hdr_size / 4;
997 	nth->window	= htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin);
998 	nth->check	= 0;
999 	nth->urg_ptr	= 0;
1000 
1001 	synproxy_build_options(nth, opts);
1002 
1003 	synproxy_send_tcp_ipv6(net, skb, nskb, NULL, 0, niph, nth,
1004 			       tcp_hdr_size);
1005 }
1006 
1007 static void
1008 synproxy_send_client_ack_ipv6(struct net *net, const struct sk_buff *skb,
1009 			      const struct tcphdr *th,
1010 			      const struct synproxy_options *opts)
1011 {
1012 	struct sk_buff *nskb;
1013 	struct ipv6hdr *iph, *niph;
1014 	struct tcphdr *nth;
1015 	unsigned int tcp_hdr_size;
1016 
1017 	iph = ipv6_hdr(skb);
1018 
1019 	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
1020 	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
1021 			 GFP_ATOMIC);
1022 	if (!nskb)
1023 		return;
1024 	skb_reserve(nskb, MAX_TCP_HEADER);
1025 
1026 	niph = synproxy_build_ip_ipv6(net, nskb, &iph->saddr, &iph->daddr);
1027 
1028 	skb_reset_transport_header(nskb);
1029 	nth = skb_put(nskb, tcp_hdr_size);
1030 	nth->source	= th->source;
1031 	nth->dest	= th->dest;
1032 	nth->seq	= htonl(ntohl(th->seq) + 1);
1033 	nth->ack_seq	= th->ack_seq;
1034 	tcp_flag_word(nth) = TCP_FLAG_ACK;
1035 	nth->doff	= tcp_hdr_size / 4;
1036 	nth->window	= htons(ntohs(th->window) >> opts->wscale);
1037 	nth->check	= 0;
1038 	nth->urg_ptr	= 0;
1039 
1040 	synproxy_build_options(nth, opts);
1041 
1042 	synproxy_send_tcp_ipv6(net, skb, nskb, skb_nfct(skb),
1043 			       IP_CT_ESTABLISHED_REPLY, niph, nth,
1044 			       tcp_hdr_size);
1045 }
1046 
1047 bool
1048 synproxy_recv_client_ack_ipv6(struct net *net,
1049 			      const struct sk_buff *skb,
1050 			      const struct tcphdr *th,
1051 			      struct synproxy_options *opts, u32 recv_seq)
1052 {
1053 	struct synproxy_net *snet = synproxy_pernet(net);
1054 	int mss;
1055 
1056 	mss = nf_cookie_v6_check(ipv6_hdr(skb), th, ntohl(th->ack_seq) - 1);
1057 	if (mss == 0) {
1058 		this_cpu_inc(snet->stats->cookie_invalid);
1059 		return false;
1060 	}
1061 
1062 	this_cpu_inc(snet->stats->cookie_valid);
1063 	opts->mss_option = mss;
1064 	opts->options |= NF_SYNPROXY_OPT_MSS;
1065 
1066 	if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP)
1067 		synproxy_check_timestamp_cookie(opts);
1068 
1069 	synproxy_send_server_syn_ipv6(net, skb, th, opts, recv_seq);
1070 	return true;
1071 }
1072 EXPORT_SYMBOL_GPL(synproxy_recv_client_ack_ipv6);
1073 
1074 unsigned int
1075 ipv6_synproxy_hook(void *priv, struct sk_buff *skb,
1076 		   const struct nf_hook_state *nhs)
1077 {
1078 	struct net *net = nhs->net;
1079 	struct synproxy_net *snet = synproxy_pernet(net);
1080 	enum ip_conntrack_info ctinfo;
1081 	struct nf_conn *ct;
1082 	struct nf_conn_synproxy *synproxy;
1083 	struct synproxy_options opts = {};
1084 	const struct ip_ct_tcp *state;
1085 	struct tcphdr *th, _th;
1086 	__be16 frag_off;
1087 	u8 nexthdr;
1088 	int thoff;
1089 
1090 	ct = nf_ct_get(skb, &ctinfo);
1091 	if (!ct)
1092 		return NF_ACCEPT;
1093 
1094 	synproxy = nfct_synproxy(ct);
1095 	if (!synproxy)
1096 		return NF_ACCEPT;
1097 
1098 	if (nf_is_loopback_packet(skb))
1099 		return NF_ACCEPT;
1100 
1101 	nexthdr = ipv6_hdr(skb)->nexthdr;
1102 	thoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
1103 				 &frag_off);
1104 	if (thoff < 0 || nexthdr != IPPROTO_TCP)
1105 		return NF_ACCEPT;
1106 
1107 	th = skb_header_pointer(skb, thoff, sizeof(_th), &_th);
1108 	if (!th)
1109 		return NF_DROP;
1110 
1111 	state = &ct->proto.tcp;
1112 	switch (state->state) {
1113 	case TCP_CONNTRACK_CLOSE:
1114 		if (th->rst && CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) {
1115 			nf_ct_seqadj_init(ct, ctinfo, synproxy->isn -
1116 						      ntohl(th->seq) + 1);
1117 			break;
1118 		}
1119 
1120 		if (!th->syn || th->ack ||
1121 		    CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
1122 			break;
1123 
1124 		/* Reopened connection - reset the sequence number and timestamp
1125 		 * adjustments, they will get initialized once the connection is
1126 		 * reestablished.
1127 		 */
1128 		nf_ct_seqadj_init(ct, ctinfo, 0);
1129 		synproxy->tsoff = 0;
1130 		this_cpu_inc(snet->stats->conn_reopened);
1131 
1132 		/* fall through */
1133 	case TCP_CONNTRACK_SYN_SENT:
1134 		if (!synproxy_parse_options(skb, thoff, th, &opts))
1135 			return NF_DROP;
1136 
1137 		if (!th->syn && th->ack &&
1138 		    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
1139 			/* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
1140 			 * therefore we need to add 1 to make the SYN sequence
1141 			 * number match the one of first SYN.
1142 			 */
1143 			if (synproxy_recv_client_ack_ipv6(net, skb, th, &opts,
1144 							  ntohl(th->seq) + 1)) {
1145 				this_cpu_inc(snet->stats->cookie_retrans);
1146 				consume_skb(skb);
1147 				return NF_STOLEN;
1148 			} else {
1149 				return NF_DROP;
1150 			}
1151 		}
1152 
1153 		synproxy->isn = ntohl(th->ack_seq);
1154 		if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP)
1155 			synproxy->its = opts.tsecr;
1156 
1157 		nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
1158 		break;
1159 	case TCP_CONNTRACK_SYN_RECV:
1160 		if (!th->syn || !th->ack)
1161 			break;
1162 
1163 		if (!synproxy_parse_options(skb, thoff, th, &opts))
1164 			return NF_DROP;
1165 
1166 		if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) {
1167 			synproxy->tsoff = opts.tsval - synproxy->its;
1168 			nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
1169 		}
1170 
1171 		opts.options &= ~(NF_SYNPROXY_OPT_MSS |
1172 				  NF_SYNPROXY_OPT_WSCALE |
1173 				  NF_SYNPROXY_OPT_SACK_PERM);
1174 
1175 		swap(opts.tsval, opts.tsecr);
1176 		synproxy_send_server_ack_ipv6(net, state, skb, th, &opts);
1177 
1178 		nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq));
1179 		nf_conntrack_event_cache(IPCT_SEQADJ, ct);
1180 
1181 		swap(opts.tsval, opts.tsecr);
1182 		synproxy_send_client_ack_ipv6(net, skb, th, &opts);
1183 
1184 		consume_skb(skb);
1185 		return NF_STOLEN;
1186 	default:
1187 		break;
1188 	}
1189 
1190 	synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy);
1191 	return NF_ACCEPT;
1192 }
1193 EXPORT_SYMBOL_GPL(ipv6_synproxy_hook);
1194 
1195 static const struct nf_hook_ops ipv6_synproxy_ops[] = {
1196 	{
1197 		.hook		= ipv6_synproxy_hook,
1198 		.pf		= NFPROTO_IPV6,
1199 		.hooknum	= NF_INET_LOCAL_IN,
1200 		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM - 1,
1201 	},
1202 	{
1203 		.hook		= ipv6_synproxy_hook,
1204 		.pf		= NFPROTO_IPV6,
1205 		.hooknum	= NF_INET_POST_ROUTING,
1206 		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM - 1,
1207 	},
1208 };
1209 
1210 int
1211 nf_synproxy_ipv6_init(struct synproxy_net *snet, struct net *net)
1212 {
1213 	int err;
1214 
1215 	if (snet->hook_ref6 == 0) {
1216 		err = nf_register_net_hooks(net, ipv6_synproxy_ops,
1217 					    ARRAY_SIZE(ipv6_synproxy_ops));
1218 		if (err)
1219 			return err;
1220 	}
1221 
1222 	snet->hook_ref6++;
1223 	return 0;
1224 }
1225 EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_init);
1226 
1227 void
1228 nf_synproxy_ipv6_fini(struct synproxy_net *snet, struct net *net)
1229 {
1230 	snet->hook_ref6--;
1231 	if (snet->hook_ref6 == 0)
1232 		nf_unregister_net_hooks(net, ipv6_synproxy_ops,
1233 					ARRAY_SIZE(ipv6_synproxy_ops));
1234 }
1235 EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_fini);
1236 #endif /* CONFIG_IPV6 */
1237 
1238 MODULE_LICENSE("GPL");
1239 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
1240