2 * Copyright (c) 2013 Patrick McHardy <kaber@trash.net>
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.
9 #include <linux/module.h>
10 #include <linux/skbuff.h>
11 #include <net/ip6_checksum.h>
12 #include <net/ip6_route.h>
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>
22 static struct ipv6hdr
*
23 synproxy_build_ip(struct net
*net
, struct sk_buff
*skb
,
24 const struct in6_addr
*saddr
,
25 const struct in6_addr
*daddr
)
29 skb_reset_network_header(skb
);
30 iph
= skb_put(skb
, sizeof(*iph
));
31 ip6_flow_hdr(iph
, 0, 0);
32 iph
->hop_limit
= net
->ipv6
.devconf_all
->hop_limit
;
33 iph
->nexthdr
= IPPROTO_TCP
;
41 synproxy_send_tcp(struct net
*net
,
42 const struct sk_buff
*skb
, struct sk_buff
*nskb
,
43 struct nf_conntrack
*nfct
, enum ip_conntrack_info ctinfo
,
44 struct ipv6hdr
*niph
, struct tcphdr
*nth
,
45 unsigned int tcp_hdr_size
)
47 struct dst_entry
*dst
;
50 nth
->check
= ~tcp_v6_check(tcp_hdr_size
, &niph
->saddr
, &niph
->daddr
, 0);
51 nskb
->ip_summed
= CHECKSUM_PARTIAL
;
52 nskb
->csum_start
= (unsigned char *)nth
- nskb
->head
;
53 nskb
->csum_offset
= offsetof(struct tcphdr
, check
);
55 memset(&fl6
, 0, sizeof(fl6
));
56 fl6
.flowi6_proto
= IPPROTO_TCP
;
57 fl6
.saddr
= niph
->saddr
;
58 fl6
.daddr
= niph
->daddr
;
59 fl6
.fl6_sport
= nth
->source
;
60 fl6
.fl6_dport
= nth
->dest
;
61 security_skb_classify_flow((struct sk_buff
*)skb
, flowi6_to_flowi(&fl6
));
62 dst
= ip6_route_output(net
, NULL
, &fl6
);
67 dst
= xfrm_lookup(net
, dst
, flowi6_to_flowi(&fl6
), NULL
, 0);
71 skb_dst_set(nskb
, dst
);
74 nf_ct_set(nskb
, (struct nf_conn
*)nfct
, ctinfo
);
75 nf_conntrack_get(nfct
);
78 ip6_local_out(net
, nskb
->sk
, nskb
);
86 synproxy_send_client_synack(struct net
*net
,
87 const struct sk_buff
*skb
, const struct tcphdr
*th
,
88 const struct synproxy_options
*opts
)
91 struct ipv6hdr
*iph
, *niph
;
93 unsigned int tcp_hdr_size
;
98 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
99 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
103 skb_reserve(nskb
, MAX_TCP_HEADER
);
105 niph
= synproxy_build_ip(net
, nskb
, &iph
->daddr
, &iph
->saddr
);
107 skb_reset_transport_header(nskb
);
108 nth
= skb_put(nskb
, tcp_hdr_size
);
109 nth
->source
= th
->dest
;
110 nth
->dest
= th
->source
;
111 nth
->seq
= htonl(__cookie_v6_init_sequence(iph
, th
, &mss
));
112 nth
->ack_seq
= htonl(ntohl(th
->seq
) + 1);
113 tcp_flag_word(nth
) = TCP_FLAG_SYN
| TCP_FLAG_ACK
;
114 if (opts
->options
& XT_SYNPROXY_OPT_ECN
)
115 tcp_flag_word(nth
) |= TCP_FLAG_ECE
;
116 nth
->doff
= tcp_hdr_size
/ 4;
121 synproxy_build_options(nth
, opts
);
123 synproxy_send_tcp(net
, skb
, nskb
, skb_nfct(skb
),
124 IP_CT_ESTABLISHED_REPLY
, niph
, nth
, tcp_hdr_size
);
128 synproxy_send_server_syn(struct net
*net
,
129 const struct sk_buff
*skb
, const struct tcphdr
*th
,
130 const struct synproxy_options
*opts
, u32 recv_seq
)
132 struct synproxy_net
*snet
= synproxy_pernet(net
);
133 struct sk_buff
*nskb
;
134 struct ipv6hdr
*iph
, *niph
;
136 unsigned int tcp_hdr_size
;
140 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
141 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
145 skb_reserve(nskb
, MAX_TCP_HEADER
);
147 niph
= synproxy_build_ip(net
, nskb
, &iph
->saddr
, &iph
->daddr
);
149 skb_reset_transport_header(nskb
);
150 nth
= skb_put(nskb
, tcp_hdr_size
);
151 nth
->source
= th
->source
;
152 nth
->dest
= th
->dest
;
153 nth
->seq
= htonl(recv_seq
- 1);
154 /* ack_seq is used to relay our ISN to the synproxy hook to initialize
155 * sequence number translation once a connection tracking entry exists.
157 nth
->ack_seq
= htonl(ntohl(th
->ack_seq
) - 1);
158 tcp_flag_word(nth
) = TCP_FLAG_SYN
;
159 if (opts
->options
& XT_SYNPROXY_OPT_ECN
)
160 tcp_flag_word(nth
) |= TCP_FLAG_ECE
| TCP_FLAG_CWR
;
161 nth
->doff
= tcp_hdr_size
/ 4;
162 nth
->window
= th
->window
;
166 synproxy_build_options(nth
, opts
);
168 synproxy_send_tcp(net
, skb
, nskb
, &snet
->tmpl
->ct_general
, IP_CT_NEW
,
169 niph
, nth
, tcp_hdr_size
);
173 synproxy_send_server_ack(struct net
*net
,
174 const struct ip_ct_tcp
*state
,
175 const struct sk_buff
*skb
, const struct tcphdr
*th
,
176 const struct synproxy_options
*opts
)
178 struct sk_buff
*nskb
;
179 struct ipv6hdr
*iph
, *niph
;
181 unsigned int tcp_hdr_size
;
185 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
186 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
190 skb_reserve(nskb
, MAX_TCP_HEADER
);
192 niph
= synproxy_build_ip(net
, nskb
, &iph
->daddr
, &iph
->saddr
);
194 skb_reset_transport_header(nskb
);
195 nth
= skb_put(nskb
, tcp_hdr_size
);
196 nth
->source
= th
->dest
;
197 nth
->dest
= th
->source
;
198 nth
->seq
= htonl(ntohl(th
->ack_seq
));
199 nth
->ack_seq
= htonl(ntohl(th
->seq
) + 1);
200 tcp_flag_word(nth
) = TCP_FLAG_ACK
;
201 nth
->doff
= tcp_hdr_size
/ 4;
202 nth
->window
= htons(state
->seen
[IP_CT_DIR_ORIGINAL
].td_maxwin
);
206 synproxy_build_options(nth
, opts
);
208 synproxy_send_tcp(net
, skb
, nskb
, NULL
, 0, niph
, nth
, tcp_hdr_size
);
212 synproxy_send_client_ack(struct net
*net
,
213 const struct sk_buff
*skb
, const struct tcphdr
*th
,
214 const struct synproxy_options
*opts
)
216 struct sk_buff
*nskb
;
217 struct ipv6hdr
*iph
, *niph
;
219 unsigned int tcp_hdr_size
;
223 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
224 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
228 skb_reserve(nskb
, MAX_TCP_HEADER
);
230 niph
= synproxy_build_ip(net
, nskb
, &iph
->saddr
, &iph
->daddr
);
232 skb_reset_transport_header(nskb
);
233 nth
= skb_put(nskb
, tcp_hdr_size
);
234 nth
->source
= th
->source
;
235 nth
->dest
= th
->dest
;
236 nth
->seq
= htonl(ntohl(th
->seq
) + 1);
237 nth
->ack_seq
= th
->ack_seq
;
238 tcp_flag_word(nth
) = TCP_FLAG_ACK
;
239 nth
->doff
= tcp_hdr_size
/ 4;
240 nth
->window
= htons(ntohs(th
->window
) >> opts
->wscale
);
244 synproxy_build_options(nth
, opts
);
246 synproxy_send_tcp(net
, skb
, nskb
, skb_nfct(skb
),
247 IP_CT_ESTABLISHED_REPLY
, niph
, nth
, tcp_hdr_size
);
251 synproxy_recv_client_ack(struct net
*net
,
252 const struct sk_buff
*skb
, const struct tcphdr
*th
,
253 struct synproxy_options
*opts
, u32 recv_seq
)
255 struct synproxy_net
*snet
= synproxy_pernet(net
);
258 mss
= __cookie_v6_check(ipv6_hdr(skb
), th
, ntohl(th
->ack_seq
) - 1);
260 this_cpu_inc(snet
->stats
->cookie_invalid
);
264 this_cpu_inc(snet
->stats
->cookie_valid
);
266 opts
->options
|= XT_SYNPROXY_OPT_MSS
;
268 if (opts
->options
& XT_SYNPROXY_OPT_TIMESTAMP
)
269 synproxy_check_timestamp_cookie(opts
);
271 synproxy_send_server_syn(net
, skb
, th
, opts
, recv_seq
);
276 synproxy_tg6(struct sk_buff
*skb
, const struct xt_action_param
*par
)
278 const struct xt_synproxy_info
*info
= par
->targinfo
;
279 struct net
*net
= xt_net(par
);
280 struct synproxy_net
*snet
= synproxy_pernet(net
);
281 struct synproxy_options opts
= {};
282 struct tcphdr
*th
, _th
;
284 if (nf_ip6_checksum(skb
, xt_hooknum(par
), par
->thoff
, IPPROTO_TCP
))
287 th
= skb_header_pointer(skb
, par
->thoff
, sizeof(_th
), &_th
);
291 if (!synproxy_parse_options(skb
, par
->thoff
, th
, &opts
))
294 if (th
->syn
&& !(th
->ack
|| th
->fin
|| th
->rst
)) {
295 /* Initial SYN from client */
296 this_cpu_inc(snet
->stats
->syn_received
);
298 if (th
->ece
&& th
->cwr
)
299 opts
.options
|= XT_SYNPROXY_OPT_ECN
;
301 opts
.options
&= info
->options
;
302 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
303 synproxy_init_timestamp_cookie(info
, &opts
);
305 opts
.options
&= ~(XT_SYNPROXY_OPT_WSCALE
|
306 XT_SYNPROXY_OPT_SACK_PERM
|
307 XT_SYNPROXY_OPT_ECN
);
309 synproxy_send_client_synack(net
, skb
, th
, &opts
);
313 } else if (th
->ack
&& !(th
->fin
|| th
->rst
|| th
->syn
)) {
314 /* ACK from client */
315 if (synproxy_recv_client_ack(net
, skb
, th
, &opts
, ntohl(th
->seq
))) {
326 static unsigned int ipv6_synproxy_hook(void *priv
,
328 const struct nf_hook_state
*nhs
)
330 struct net
*net
= nhs
->net
;
331 struct synproxy_net
*snet
= synproxy_pernet(net
);
332 enum ip_conntrack_info ctinfo
;
334 struct nf_conn_synproxy
*synproxy
;
335 struct synproxy_options opts
= {};
336 const struct ip_ct_tcp
*state
;
337 struct tcphdr
*th
, _th
;
342 ct
= nf_ct_get(skb
, &ctinfo
);
346 synproxy
= nfct_synproxy(ct
);
347 if (synproxy
== NULL
)
350 if (nf_is_loopback_packet(skb
))
353 nexthdr
= ipv6_hdr(skb
)->nexthdr
;
354 thoff
= ipv6_skip_exthdr(skb
, sizeof(struct ipv6hdr
), &nexthdr
,
359 th
= skb_header_pointer(skb
, thoff
, sizeof(_th
), &_th
);
363 state
= &ct
->proto
.tcp
;
364 switch (state
->state
) {
365 case TCP_CONNTRACK_CLOSE
:
366 if (th
->rst
&& !test_bit(IPS_SEEN_REPLY_BIT
, &ct
->status
)) {
367 nf_ct_seqadj_init(ct
, ctinfo
, synproxy
->isn
-
372 if (!th
->syn
|| th
->ack
||
373 CTINFO2DIR(ctinfo
) != IP_CT_DIR_ORIGINAL
)
376 /* Reopened connection - reset the sequence number and timestamp
377 * adjustments, they will get initialized once the connection is
380 nf_ct_seqadj_init(ct
, ctinfo
, 0);
382 this_cpu_inc(snet
->stats
->conn_reopened
);
385 case TCP_CONNTRACK_SYN_SENT
:
386 if (!synproxy_parse_options(skb
, thoff
, th
, &opts
))
389 if (!th
->syn
&& th
->ack
&&
390 CTINFO2DIR(ctinfo
) == IP_CT_DIR_ORIGINAL
) {
391 /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
392 * therefore we need to add 1 to make the SYN sequence
393 * number match the one of first SYN.
395 if (synproxy_recv_client_ack(net
, skb
, th
, &opts
,
396 ntohl(th
->seq
) + 1)) {
397 this_cpu_inc(snet
->stats
->cookie_retrans
);
405 synproxy
->isn
= ntohl(th
->ack_seq
);
406 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
407 synproxy
->its
= opts
.tsecr
;
409 case TCP_CONNTRACK_SYN_RECV
:
410 if (!th
->syn
|| !th
->ack
)
413 if (!synproxy_parse_options(skb
, thoff
, th
, &opts
))
416 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
417 synproxy
->tsoff
= opts
.tsval
- synproxy
->its
;
419 opts
.options
&= ~(XT_SYNPROXY_OPT_MSS
|
420 XT_SYNPROXY_OPT_WSCALE
|
421 XT_SYNPROXY_OPT_SACK_PERM
);
423 swap(opts
.tsval
, opts
.tsecr
);
424 synproxy_send_server_ack(net
, state
, skb
, th
, &opts
);
426 nf_ct_seqadj_init(ct
, ctinfo
, synproxy
->isn
- ntohl(th
->seq
));
428 swap(opts
.tsval
, opts
.tsecr
);
429 synproxy_send_client_ack(net
, skb
, th
, &opts
);
437 synproxy_tstamp_adjust(skb
, thoff
, th
, ct
, ctinfo
, synproxy
);
441 static struct nf_hook_ops ipv6_synproxy_ops
[] __read_mostly
= {
443 .hook
= ipv6_synproxy_hook
,
445 .hooknum
= NF_INET_LOCAL_IN
,
446 .priority
= NF_IP_PRI_CONNTRACK_CONFIRM
- 1,
449 .hook
= ipv6_synproxy_hook
,
451 .hooknum
= NF_INET_POST_ROUTING
,
452 .priority
= NF_IP_PRI_CONNTRACK_CONFIRM
- 1,
456 static int synproxy_tg6_check(const struct xt_tgchk_param
*par
)
458 struct synproxy_net
*snet
= synproxy_pernet(par
->net
);
459 const struct ip6t_entry
*e
= par
->entryinfo
;
462 if (!(e
->ipv6
.flags
& IP6T_F_PROTO
) ||
463 e
->ipv6
.proto
!= IPPROTO_TCP
||
464 e
->ipv6
.invflags
& XT_INV_PROTO
)
467 err
= nf_ct_netns_get(par
->net
, par
->family
);
471 if (snet
->hook_ref6
== 0) {
472 err
= nf_register_net_hooks(par
->net
, ipv6_synproxy_ops
,
473 ARRAY_SIZE(ipv6_synproxy_ops
));
475 nf_ct_netns_put(par
->net
, par
->family
);
484 static void synproxy_tg6_destroy(const struct xt_tgdtor_param
*par
)
486 struct synproxy_net
*snet
= synproxy_pernet(par
->net
);
489 if (snet
->hook_ref6
== 0)
490 nf_unregister_net_hooks(par
->net
, ipv6_synproxy_ops
,
491 ARRAY_SIZE(ipv6_synproxy_ops
));
492 nf_ct_netns_put(par
->net
, par
->family
);
495 static struct xt_target synproxy_tg6_reg __read_mostly
= {
497 .family
= NFPROTO_IPV6
,
498 .hooks
= (1 << NF_INET_LOCAL_IN
) | (1 << NF_INET_FORWARD
),
499 .target
= synproxy_tg6
,
500 .targetsize
= sizeof(struct xt_synproxy_info
),
501 .checkentry
= synproxy_tg6_check
,
502 .destroy
= synproxy_tg6_destroy
,
506 static int __init
synproxy_tg6_init(void)
508 return xt_register_target(&synproxy_tg6_reg
);
511 static void __exit
synproxy_tg6_exit(void)
513 xt_unregister_target(&synproxy_tg6_reg
);
516 module_init(synproxy_tg6_init
);
517 module_exit(synproxy_tg6_exit
);
519 MODULE_LICENSE("GPL");
520 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");