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->sk, 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 		fallthrough;
708 	case TCP_CONNTRACK_SYN_SENT:
709 		if (!synproxy_parse_options(skb, thoff, th, &opts))
710 			return NF_DROP;
711 
712 		if (!th->syn && th->ack &&
713 		    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
714 			/* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
715 			 * therefore we need to add 1 to make the SYN sequence
716 			 * number match the one of first SYN.
717 			 */
718 			if (synproxy_recv_client_ack(net, skb, th, &opts,
719 						     ntohl(th->seq) + 1)) {
720 				this_cpu_inc(snet->stats->cookie_retrans);
721 				consume_skb(skb);
722 				return NF_STOLEN;
723 			} else {
724 				return NF_DROP;
725 			}
726 		}
727 
728 		synproxy->isn = ntohl(th->ack_seq);
729 		if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP)
730 			synproxy->its = opts.tsecr;
731 
732 		nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
733 		break;
734 	case TCP_CONNTRACK_SYN_RECV:
735 		if (!th->syn || !th->ack)
736 			break;
737 
738 		if (!synproxy_parse_options(skb, thoff, th, &opts))
739 			return NF_DROP;
740 
741 		if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) {
742 			synproxy->tsoff = opts.tsval - synproxy->its;
743 			nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
744 		}
745 
746 		opts.options &= ~(NF_SYNPROXY_OPT_MSS |
747 				  NF_SYNPROXY_OPT_WSCALE |
748 				  NF_SYNPROXY_OPT_SACK_PERM);
749 
750 		swap(opts.tsval, opts.tsecr);
751 		synproxy_send_server_ack(net, state, skb, th, &opts);
752 
753 		nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq));
754 		nf_conntrack_event_cache(IPCT_SEQADJ, ct);
755 
756 		swap(opts.tsval, opts.tsecr);
757 		synproxy_send_client_ack(net, skb, th, &opts);
758 
759 		consume_skb(skb);
760 		return NF_STOLEN;
761 	default:
762 		break;
763 	}
764 
765 	synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy);
766 	return NF_ACCEPT;
767 }
768 EXPORT_SYMBOL_GPL(ipv4_synproxy_hook);
769 
770 static const struct nf_hook_ops ipv4_synproxy_ops[] = {
771 	{
772 		.hook		= ipv4_synproxy_hook,
773 		.pf		= NFPROTO_IPV4,
774 		.hooknum	= NF_INET_LOCAL_IN,
775 		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM - 1,
776 	},
777 	{
778 		.hook		= ipv4_synproxy_hook,
779 		.pf		= NFPROTO_IPV4,
780 		.hooknum	= NF_INET_POST_ROUTING,
781 		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM - 1,
782 	},
783 };
784 
785 int nf_synproxy_ipv4_init(struct synproxy_net *snet, struct net *net)
786 {
787 	int err;
788 
789 	if (snet->hook_ref4 == 0) {
790 		err = nf_register_net_hooks(net, ipv4_synproxy_ops,
791 					    ARRAY_SIZE(ipv4_synproxy_ops));
792 		if (err)
793 			return err;
794 	}
795 
796 	snet->hook_ref4++;
797 	return 0;
798 }
799 EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_init);
800 
801 void nf_synproxy_ipv4_fini(struct synproxy_net *snet, struct net *net)
802 {
803 	snet->hook_ref4--;
804 	if (snet->hook_ref4 == 0)
805 		nf_unregister_net_hooks(net, ipv4_synproxy_ops,
806 					ARRAY_SIZE(ipv4_synproxy_ops));
807 }
808 EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_fini);
809 
810 #if IS_ENABLED(CONFIG_IPV6)
811 static struct ipv6hdr *
812 synproxy_build_ip_ipv6(struct net *net, struct sk_buff *skb,
813 		       const struct in6_addr *saddr,
814 		       const struct in6_addr *daddr)
815 {
816 	struct ipv6hdr *iph;
817 
818 	skb_reset_network_header(skb);
819 	iph = skb_put(skb, sizeof(*iph));
820 	ip6_flow_hdr(iph, 0, 0);
821 	iph->hop_limit	= net->ipv6.devconf_all->hop_limit;
822 	iph->nexthdr	= IPPROTO_TCP;
823 	iph->saddr	= *saddr;
824 	iph->daddr	= *daddr;
825 
826 	return iph;
827 }
828 
829 static void
830 synproxy_send_tcp_ipv6(struct net *net,
831 		       const struct sk_buff *skb, struct sk_buff *nskb,
832 		       struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo,
833 		       struct ipv6hdr *niph, struct tcphdr *nth,
834 		       unsigned int tcp_hdr_size)
835 {
836 	struct dst_entry *dst;
837 	struct flowi6 fl6;
838 	int err;
839 
840 	nth->check = ~tcp_v6_check(tcp_hdr_size, &niph->saddr, &niph->daddr, 0);
841 	nskb->ip_summed   = CHECKSUM_PARTIAL;
842 	nskb->csum_start  = (unsigned char *)nth - nskb->head;
843 	nskb->csum_offset = offsetof(struct tcphdr, check);
844 
845 	memset(&fl6, 0, sizeof(fl6));
846 	fl6.flowi6_proto = IPPROTO_TCP;
847 	fl6.saddr = niph->saddr;
848 	fl6.daddr = niph->daddr;
849 	fl6.fl6_sport = nth->source;
850 	fl6.fl6_dport = nth->dest;
851 	security_skb_classify_flow((struct sk_buff *)skb,
852 				   flowi6_to_flowi_common(&fl6));
853 	err = nf_ip6_route(net, &dst, flowi6_to_flowi(&fl6), false);
854 	if (err) {
855 		goto free_nskb;
856 	}
857 
858 	dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
859 	if (IS_ERR(dst))
860 		goto free_nskb;
861 
862 	skb_dst_set(nskb, dst);
863 
864 	if (nfct) {
865 		nf_ct_set(nskb, (struct nf_conn *)nfct, ctinfo);
866 		nf_conntrack_get(nfct);
867 	}
868 
869 	ip6_local_out(net, nskb->sk, nskb);
870 	return;
871 
872 free_nskb:
873 	kfree_skb(nskb);
874 }
875 
876 void
877 synproxy_send_client_synack_ipv6(struct net *net,
878 				 const struct sk_buff *skb,
879 				 const struct tcphdr *th,
880 				 const struct synproxy_options *opts)
881 {
882 	struct sk_buff *nskb;
883 	struct ipv6hdr *iph, *niph;
884 	struct tcphdr *nth;
885 	unsigned int tcp_hdr_size;
886 	u16 mss = opts->mss_encode;
887 
888 	iph = ipv6_hdr(skb);
889 
890 	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
891 	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
892 			 GFP_ATOMIC);
893 	if (!nskb)
894 		return;
895 	skb_reserve(nskb, MAX_TCP_HEADER);
896 
897 	niph = synproxy_build_ip_ipv6(net, nskb, &iph->daddr, &iph->saddr);
898 
899 	skb_reset_transport_header(nskb);
900 	nth = skb_put(nskb, tcp_hdr_size);
901 	nth->source	= th->dest;
902 	nth->dest	= th->source;
903 	nth->seq	= htonl(nf_ipv6_cookie_init_sequence(iph, th, &mss));
904 	nth->ack_seq	= htonl(ntohl(th->seq) + 1);
905 	tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK;
906 	if (opts->options & NF_SYNPROXY_OPT_ECN)
907 		tcp_flag_word(nth) |= TCP_FLAG_ECE;
908 	nth->doff	= tcp_hdr_size / 4;
909 	nth->window	= 0;
910 	nth->check	= 0;
911 	nth->urg_ptr	= 0;
912 
913 	synproxy_build_options(nth, opts);
914 
915 	synproxy_send_tcp_ipv6(net, skb, nskb, skb_nfct(skb),
916 			       IP_CT_ESTABLISHED_REPLY, niph, nth,
917 			       tcp_hdr_size);
918 }
919 EXPORT_SYMBOL_GPL(synproxy_send_client_synack_ipv6);
920 
921 static void
922 synproxy_send_server_syn_ipv6(struct net *net, const struct sk_buff *skb,
923 			      const struct tcphdr *th,
924 			      const struct synproxy_options *opts, u32 recv_seq)
925 {
926 	struct synproxy_net *snet = synproxy_pernet(net);
927 	struct sk_buff *nskb;
928 	struct ipv6hdr *iph, *niph;
929 	struct tcphdr *nth;
930 	unsigned int tcp_hdr_size;
931 
932 	iph = ipv6_hdr(skb);
933 
934 	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
935 	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
936 			 GFP_ATOMIC);
937 	if (!nskb)
938 		return;
939 	skb_reserve(nskb, MAX_TCP_HEADER);
940 
941 	niph = synproxy_build_ip_ipv6(net, nskb, &iph->saddr, &iph->daddr);
942 
943 	skb_reset_transport_header(nskb);
944 	nth = skb_put(nskb, tcp_hdr_size);
945 	nth->source	= th->source;
946 	nth->dest	= th->dest;
947 	nth->seq	= htonl(recv_seq - 1);
948 	/* ack_seq is used to relay our ISN to the synproxy hook to initialize
949 	 * sequence number translation once a connection tracking entry exists.
950 	 */
951 	nth->ack_seq	= htonl(ntohl(th->ack_seq) - 1);
952 	tcp_flag_word(nth) = TCP_FLAG_SYN;
953 	if (opts->options & NF_SYNPROXY_OPT_ECN)
954 		tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR;
955 	nth->doff	= tcp_hdr_size / 4;
956 	nth->window	= th->window;
957 	nth->check	= 0;
958 	nth->urg_ptr	= 0;
959 
960 	synproxy_build_options(nth, opts);
961 
962 	synproxy_send_tcp_ipv6(net, skb, nskb, &snet->tmpl->ct_general,
963 			       IP_CT_NEW, niph, nth, tcp_hdr_size);
964 }
965 
966 static void
967 synproxy_send_server_ack_ipv6(struct net *net, const struct ip_ct_tcp *state,
968 			      const struct sk_buff *skb,
969 			      const struct tcphdr *th,
970 			      const struct synproxy_options *opts)
971 {
972 	struct sk_buff *nskb;
973 	struct ipv6hdr *iph, *niph;
974 	struct tcphdr *nth;
975 	unsigned int tcp_hdr_size;
976 
977 	iph = ipv6_hdr(skb);
978 
979 	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
980 	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
981 			 GFP_ATOMIC);
982 	if (!nskb)
983 		return;
984 	skb_reserve(nskb, MAX_TCP_HEADER);
985 
986 	niph = synproxy_build_ip_ipv6(net, nskb, &iph->daddr, &iph->saddr);
987 
988 	skb_reset_transport_header(nskb);
989 	nth = skb_put(nskb, tcp_hdr_size);
990 	nth->source	= th->dest;
991 	nth->dest	= th->source;
992 	nth->seq	= htonl(ntohl(th->ack_seq));
993 	nth->ack_seq	= htonl(ntohl(th->seq) + 1);
994 	tcp_flag_word(nth) = TCP_FLAG_ACK;
995 	nth->doff	= tcp_hdr_size / 4;
996 	nth->window	= htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin);
997 	nth->check	= 0;
998 	nth->urg_ptr	= 0;
999 
1000 	synproxy_build_options(nth, opts);
1001 
1002 	synproxy_send_tcp_ipv6(net, skb, nskb, NULL, 0, niph, nth,
1003 			       tcp_hdr_size);
1004 }
1005 
1006 static void
1007 synproxy_send_client_ack_ipv6(struct net *net, const struct sk_buff *skb,
1008 			      const struct tcphdr *th,
1009 			      const struct synproxy_options *opts)
1010 {
1011 	struct sk_buff *nskb;
1012 	struct ipv6hdr *iph, *niph;
1013 	struct tcphdr *nth;
1014 	unsigned int tcp_hdr_size;
1015 
1016 	iph = ipv6_hdr(skb);
1017 
1018 	tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
1019 	nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
1020 			 GFP_ATOMIC);
1021 	if (!nskb)
1022 		return;
1023 	skb_reserve(nskb, MAX_TCP_HEADER);
1024 
1025 	niph = synproxy_build_ip_ipv6(net, nskb, &iph->saddr, &iph->daddr);
1026 
1027 	skb_reset_transport_header(nskb);
1028 	nth = skb_put(nskb, tcp_hdr_size);
1029 	nth->source	= th->source;
1030 	nth->dest	= th->dest;
1031 	nth->seq	= htonl(ntohl(th->seq) + 1);
1032 	nth->ack_seq	= th->ack_seq;
1033 	tcp_flag_word(nth) = TCP_FLAG_ACK;
1034 	nth->doff	= tcp_hdr_size / 4;
1035 	nth->window	= htons(ntohs(th->window) >> opts->wscale);
1036 	nth->check	= 0;
1037 	nth->urg_ptr	= 0;
1038 
1039 	synproxy_build_options(nth, opts);
1040 
1041 	synproxy_send_tcp_ipv6(net, skb, nskb, skb_nfct(skb),
1042 			       IP_CT_ESTABLISHED_REPLY, niph, nth,
1043 			       tcp_hdr_size);
1044 }
1045 
1046 bool
1047 synproxy_recv_client_ack_ipv6(struct net *net,
1048 			      const struct sk_buff *skb,
1049 			      const struct tcphdr *th,
1050 			      struct synproxy_options *opts, u32 recv_seq)
1051 {
1052 	struct synproxy_net *snet = synproxy_pernet(net);
1053 	int mss;
1054 
1055 	mss = nf_cookie_v6_check(ipv6_hdr(skb), th, ntohl(th->ack_seq) - 1);
1056 	if (mss == 0) {
1057 		this_cpu_inc(snet->stats->cookie_invalid);
1058 		return false;
1059 	}
1060 
1061 	this_cpu_inc(snet->stats->cookie_valid);
1062 	opts->mss_option = mss;
1063 	opts->options |= NF_SYNPROXY_OPT_MSS;
1064 
1065 	if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP)
1066 		synproxy_check_timestamp_cookie(opts);
1067 
1068 	synproxy_send_server_syn_ipv6(net, skb, th, opts, recv_seq);
1069 	return true;
1070 }
1071 EXPORT_SYMBOL_GPL(synproxy_recv_client_ack_ipv6);
1072 
1073 unsigned int
1074 ipv6_synproxy_hook(void *priv, struct sk_buff *skb,
1075 		   const struct nf_hook_state *nhs)
1076 {
1077 	struct net *net = nhs->net;
1078 	struct synproxy_net *snet = synproxy_pernet(net);
1079 	enum ip_conntrack_info ctinfo;
1080 	struct nf_conn *ct;
1081 	struct nf_conn_synproxy *synproxy;
1082 	struct synproxy_options opts = {};
1083 	const struct ip_ct_tcp *state;
1084 	struct tcphdr *th, _th;
1085 	__be16 frag_off;
1086 	u8 nexthdr;
1087 	int thoff;
1088 
1089 	ct = nf_ct_get(skb, &ctinfo);
1090 	if (!ct)
1091 		return NF_ACCEPT;
1092 
1093 	synproxy = nfct_synproxy(ct);
1094 	if (!synproxy)
1095 		return NF_ACCEPT;
1096 
1097 	if (nf_is_loopback_packet(skb))
1098 		return NF_ACCEPT;
1099 
1100 	nexthdr = ipv6_hdr(skb)->nexthdr;
1101 	thoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
1102 				 &frag_off);
1103 	if (thoff < 0 || nexthdr != IPPROTO_TCP)
1104 		return NF_ACCEPT;
1105 
1106 	th = skb_header_pointer(skb, thoff, sizeof(_th), &_th);
1107 	if (!th)
1108 		return NF_DROP;
1109 
1110 	state = &ct->proto.tcp;
1111 	switch (state->state) {
1112 	case TCP_CONNTRACK_CLOSE:
1113 		if (th->rst && CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) {
1114 			nf_ct_seqadj_init(ct, ctinfo, synproxy->isn -
1115 						      ntohl(th->seq) + 1);
1116 			break;
1117 		}
1118 
1119 		if (!th->syn || th->ack ||
1120 		    CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
1121 			break;
1122 
1123 		/* Reopened connection - reset the sequence number and timestamp
1124 		 * adjustments, they will get initialized once the connection is
1125 		 * reestablished.
1126 		 */
1127 		nf_ct_seqadj_init(ct, ctinfo, 0);
1128 		synproxy->tsoff = 0;
1129 		this_cpu_inc(snet->stats->conn_reopened);
1130 		fallthrough;
1131 	case TCP_CONNTRACK_SYN_SENT:
1132 		if (!synproxy_parse_options(skb, thoff, th, &opts))
1133 			return NF_DROP;
1134 
1135 		if (!th->syn && th->ack &&
1136 		    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
1137 			/* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
1138 			 * therefore we need to add 1 to make the SYN sequence
1139 			 * number match the one of first SYN.
1140 			 */
1141 			if (synproxy_recv_client_ack_ipv6(net, skb, th, &opts,
1142 							  ntohl(th->seq) + 1)) {
1143 				this_cpu_inc(snet->stats->cookie_retrans);
1144 				consume_skb(skb);
1145 				return NF_STOLEN;
1146 			} else {
1147 				return NF_DROP;
1148 			}
1149 		}
1150 
1151 		synproxy->isn = ntohl(th->ack_seq);
1152 		if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP)
1153 			synproxy->its = opts.tsecr;
1154 
1155 		nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
1156 		break;
1157 	case TCP_CONNTRACK_SYN_RECV:
1158 		if (!th->syn || !th->ack)
1159 			break;
1160 
1161 		if (!synproxy_parse_options(skb, thoff, th, &opts))
1162 			return NF_DROP;
1163 
1164 		if (opts.options & NF_SYNPROXY_OPT_TIMESTAMP) {
1165 			synproxy->tsoff = opts.tsval - synproxy->its;
1166 			nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
1167 		}
1168 
1169 		opts.options &= ~(NF_SYNPROXY_OPT_MSS |
1170 				  NF_SYNPROXY_OPT_WSCALE |
1171 				  NF_SYNPROXY_OPT_SACK_PERM);
1172 
1173 		swap(opts.tsval, opts.tsecr);
1174 		synproxy_send_server_ack_ipv6(net, state, skb, th, &opts);
1175 
1176 		nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq));
1177 		nf_conntrack_event_cache(IPCT_SEQADJ, ct);
1178 
1179 		swap(opts.tsval, opts.tsecr);
1180 		synproxy_send_client_ack_ipv6(net, skb, th, &opts);
1181 
1182 		consume_skb(skb);
1183 		return NF_STOLEN;
1184 	default:
1185 		break;
1186 	}
1187 
1188 	synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy);
1189 	return NF_ACCEPT;
1190 }
1191 EXPORT_SYMBOL_GPL(ipv6_synproxy_hook);
1192 
1193 static const struct nf_hook_ops ipv6_synproxy_ops[] = {
1194 	{
1195 		.hook		= ipv6_synproxy_hook,
1196 		.pf		= NFPROTO_IPV6,
1197 		.hooknum	= NF_INET_LOCAL_IN,
1198 		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM - 1,
1199 	},
1200 	{
1201 		.hook		= ipv6_synproxy_hook,
1202 		.pf		= NFPROTO_IPV6,
1203 		.hooknum	= NF_INET_POST_ROUTING,
1204 		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM - 1,
1205 	},
1206 };
1207 
1208 int
1209 nf_synproxy_ipv6_init(struct synproxy_net *snet, struct net *net)
1210 {
1211 	int err;
1212 
1213 	if (snet->hook_ref6 == 0) {
1214 		err = nf_register_net_hooks(net, ipv6_synproxy_ops,
1215 					    ARRAY_SIZE(ipv6_synproxy_ops));
1216 		if (err)
1217 			return err;
1218 	}
1219 
1220 	snet->hook_ref6++;
1221 	return 0;
1222 }
1223 EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_init);
1224 
1225 void
1226 nf_synproxy_ipv6_fini(struct synproxy_net *snet, struct net *net)
1227 {
1228 	snet->hook_ref6--;
1229 	if (snet->hook_ref6 == 0)
1230 		nf_unregister_net_hooks(net, ipv6_synproxy_ops,
1231 					ARRAY_SIZE(ipv6_synproxy_ops));
1232 }
1233 EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_fini);
1234 #endif /* CONFIG_IPV6 */
1235 
1236 MODULE_LICENSE("GPL");
1237 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
1238 MODULE_DESCRIPTION("nftables SYNPROXY expression support");
1239