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>
21 #include <net/netfilter/nf_conntrack_ecache.h>
23 static struct ipv6hdr
*
24 synproxy_build_ip(struct net
*net
, struct sk_buff
*skb
,
25 const struct in6_addr
*saddr
,
26 const struct in6_addr
*daddr
)
30 skb_reset_network_header(skb
);
31 iph
= skb_put(skb
, sizeof(*iph
));
32 ip6_flow_hdr(iph
, 0, 0);
33 iph
->hop_limit
= net
->ipv6
.devconf_all
->hop_limit
;
34 iph
->nexthdr
= IPPROTO_TCP
;
42 synproxy_send_tcp(struct net
*net
,
43 const struct sk_buff
*skb
, struct sk_buff
*nskb
,
44 struct nf_conntrack
*nfct
, enum ip_conntrack_info ctinfo
,
45 struct ipv6hdr
*niph
, struct tcphdr
*nth
,
46 unsigned int tcp_hdr_size
)
48 struct dst_entry
*dst
;
51 nth
->check
= ~tcp_v6_check(tcp_hdr_size
, &niph
->saddr
, &niph
->daddr
, 0);
52 nskb
->ip_summed
= CHECKSUM_PARTIAL
;
53 nskb
->csum_start
= (unsigned char *)nth
- nskb
->head
;
54 nskb
->csum_offset
= offsetof(struct tcphdr
, check
);
56 memset(&fl6
, 0, sizeof(fl6
));
57 fl6
.flowi6_proto
= IPPROTO_TCP
;
58 fl6
.saddr
= niph
->saddr
;
59 fl6
.daddr
= niph
->daddr
;
60 fl6
.fl6_sport
= nth
->source
;
61 fl6
.fl6_dport
= nth
->dest
;
62 security_skb_classify_flow((struct sk_buff
*)skb
, flowi6_to_flowi(&fl6
));
63 dst
= ip6_route_output(net
, NULL
, &fl6
);
68 dst
= xfrm_lookup(net
, dst
, flowi6_to_flowi(&fl6
), NULL
, 0);
72 skb_dst_set(nskb
, dst
);
75 nf_ct_set(nskb
, (struct nf_conn
*)nfct
, ctinfo
);
76 nf_conntrack_get(nfct
);
79 ip6_local_out(net
, nskb
->sk
, nskb
);
87 synproxy_send_client_synack(struct net
*net
,
88 const struct sk_buff
*skb
, const struct tcphdr
*th
,
89 const struct synproxy_options
*opts
)
92 struct ipv6hdr
*iph
, *niph
;
94 unsigned int tcp_hdr_size
;
99 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
100 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
104 skb_reserve(nskb
, MAX_TCP_HEADER
);
106 niph
= synproxy_build_ip(net
, nskb
, &iph
->daddr
, &iph
->saddr
);
108 skb_reset_transport_header(nskb
);
109 nth
= skb_put(nskb
, tcp_hdr_size
);
110 nth
->source
= th
->dest
;
111 nth
->dest
= th
->source
;
112 nth
->seq
= htonl(__cookie_v6_init_sequence(iph
, th
, &mss
));
113 nth
->ack_seq
= htonl(ntohl(th
->seq
) + 1);
114 tcp_flag_word(nth
) = TCP_FLAG_SYN
| TCP_FLAG_ACK
;
115 if (opts
->options
& XT_SYNPROXY_OPT_ECN
)
116 tcp_flag_word(nth
) |= TCP_FLAG_ECE
;
117 nth
->doff
= tcp_hdr_size
/ 4;
122 synproxy_build_options(nth
, opts
);
124 synproxy_send_tcp(net
, skb
, nskb
, skb_nfct(skb
),
125 IP_CT_ESTABLISHED_REPLY
, niph
, nth
, tcp_hdr_size
);
129 synproxy_send_server_syn(struct net
*net
,
130 const struct sk_buff
*skb
, const struct tcphdr
*th
,
131 const struct synproxy_options
*opts
, u32 recv_seq
)
133 struct synproxy_net
*snet
= synproxy_pernet(net
);
134 struct sk_buff
*nskb
;
135 struct ipv6hdr
*iph
, *niph
;
137 unsigned int tcp_hdr_size
;
141 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
142 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
146 skb_reserve(nskb
, MAX_TCP_HEADER
);
148 niph
= synproxy_build_ip(net
, nskb
, &iph
->saddr
, &iph
->daddr
);
150 skb_reset_transport_header(nskb
);
151 nth
= skb_put(nskb
, tcp_hdr_size
);
152 nth
->source
= th
->source
;
153 nth
->dest
= th
->dest
;
154 nth
->seq
= htonl(recv_seq
- 1);
155 /* ack_seq is used to relay our ISN to the synproxy hook to initialize
156 * sequence number translation once a connection tracking entry exists.
158 nth
->ack_seq
= htonl(ntohl(th
->ack_seq
) - 1);
159 tcp_flag_word(nth
) = TCP_FLAG_SYN
;
160 if (opts
->options
& XT_SYNPROXY_OPT_ECN
)
161 tcp_flag_word(nth
) |= TCP_FLAG_ECE
| TCP_FLAG_CWR
;
162 nth
->doff
= tcp_hdr_size
/ 4;
163 nth
->window
= th
->window
;
167 synproxy_build_options(nth
, opts
);
169 synproxy_send_tcp(net
, skb
, nskb
, &snet
->tmpl
->ct_general
, IP_CT_NEW
,
170 niph
, nth
, tcp_hdr_size
);
174 synproxy_send_server_ack(struct net
*net
,
175 const struct ip_ct_tcp
*state
,
176 const struct sk_buff
*skb
, const struct tcphdr
*th
,
177 const struct synproxy_options
*opts
)
179 struct sk_buff
*nskb
;
180 struct ipv6hdr
*iph
, *niph
;
182 unsigned int tcp_hdr_size
;
186 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
187 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
191 skb_reserve(nskb
, MAX_TCP_HEADER
);
193 niph
= synproxy_build_ip(net
, nskb
, &iph
->daddr
, &iph
->saddr
);
195 skb_reset_transport_header(nskb
);
196 nth
= skb_put(nskb
, tcp_hdr_size
);
197 nth
->source
= th
->dest
;
198 nth
->dest
= th
->source
;
199 nth
->seq
= htonl(ntohl(th
->ack_seq
));
200 nth
->ack_seq
= htonl(ntohl(th
->seq
) + 1);
201 tcp_flag_word(nth
) = TCP_FLAG_ACK
;
202 nth
->doff
= tcp_hdr_size
/ 4;
203 nth
->window
= htons(state
->seen
[IP_CT_DIR_ORIGINAL
].td_maxwin
);
207 synproxy_build_options(nth
, opts
);
209 synproxy_send_tcp(net
, skb
, nskb
, NULL
, 0, niph
, nth
, tcp_hdr_size
);
213 synproxy_send_client_ack(struct net
*net
,
214 const struct sk_buff
*skb
, const struct tcphdr
*th
,
215 const struct synproxy_options
*opts
)
217 struct sk_buff
*nskb
;
218 struct ipv6hdr
*iph
, *niph
;
220 unsigned int tcp_hdr_size
;
224 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
225 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
229 skb_reserve(nskb
, MAX_TCP_HEADER
);
231 niph
= synproxy_build_ip(net
, nskb
, &iph
->saddr
, &iph
->daddr
);
233 skb_reset_transport_header(nskb
);
234 nth
= skb_put(nskb
, tcp_hdr_size
);
235 nth
->source
= th
->source
;
236 nth
->dest
= th
->dest
;
237 nth
->seq
= htonl(ntohl(th
->seq
) + 1);
238 nth
->ack_seq
= th
->ack_seq
;
239 tcp_flag_word(nth
) = TCP_FLAG_ACK
;
240 nth
->doff
= tcp_hdr_size
/ 4;
241 nth
->window
= htons(ntohs(th
->window
) >> opts
->wscale
);
245 synproxy_build_options(nth
, opts
);
247 synproxy_send_tcp(net
, skb
, nskb
, skb_nfct(skb
),
248 IP_CT_ESTABLISHED_REPLY
, niph
, nth
, tcp_hdr_size
);
252 synproxy_recv_client_ack(struct net
*net
,
253 const struct sk_buff
*skb
, const struct tcphdr
*th
,
254 struct synproxy_options
*opts
, u32 recv_seq
)
256 struct synproxy_net
*snet
= synproxy_pernet(net
);
259 mss
= __cookie_v6_check(ipv6_hdr(skb
), th
, ntohl(th
->ack_seq
) - 1);
261 this_cpu_inc(snet
->stats
->cookie_invalid
);
265 this_cpu_inc(snet
->stats
->cookie_valid
);
267 opts
->options
|= XT_SYNPROXY_OPT_MSS
;
269 if (opts
->options
& XT_SYNPROXY_OPT_TIMESTAMP
)
270 synproxy_check_timestamp_cookie(opts
);
272 synproxy_send_server_syn(net
, skb
, th
, opts
, recv_seq
);
277 synproxy_tg6(struct sk_buff
*skb
, const struct xt_action_param
*par
)
279 const struct xt_synproxy_info
*info
= par
->targinfo
;
280 struct net
*net
= xt_net(par
);
281 struct synproxy_net
*snet
= synproxy_pernet(net
);
282 struct synproxy_options opts
= {};
283 struct tcphdr
*th
, _th
;
285 if (nf_ip6_checksum(skb
, xt_hooknum(par
), par
->thoff
, IPPROTO_TCP
))
288 th
= skb_header_pointer(skb
, par
->thoff
, sizeof(_th
), &_th
);
292 if (!synproxy_parse_options(skb
, par
->thoff
, th
, &opts
))
295 if (th
->syn
&& !(th
->ack
|| th
->fin
|| th
->rst
)) {
296 /* Initial SYN from client */
297 this_cpu_inc(snet
->stats
->syn_received
);
299 if (th
->ece
&& th
->cwr
)
300 opts
.options
|= XT_SYNPROXY_OPT_ECN
;
302 opts
.options
&= info
->options
;
303 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
304 synproxy_init_timestamp_cookie(info
, &opts
);
306 opts
.options
&= ~(XT_SYNPROXY_OPT_WSCALE
|
307 XT_SYNPROXY_OPT_SACK_PERM
|
308 XT_SYNPROXY_OPT_ECN
);
310 synproxy_send_client_synack(net
, skb
, th
, &opts
);
314 } else if (th
->ack
&& !(th
->fin
|| th
->rst
|| th
->syn
)) {
315 /* ACK from client */
316 if (synproxy_recv_client_ack(net
, skb
, th
, &opts
, ntohl(th
->seq
))) {
327 static unsigned int ipv6_synproxy_hook(void *priv
,
329 const struct nf_hook_state
*nhs
)
331 struct net
*net
= nhs
->net
;
332 struct synproxy_net
*snet
= synproxy_pernet(net
);
333 enum ip_conntrack_info ctinfo
;
335 struct nf_conn_synproxy
*synproxy
;
336 struct synproxy_options opts
= {};
337 const struct ip_ct_tcp
*state
;
338 struct tcphdr
*th
, _th
;
343 ct
= nf_ct_get(skb
, &ctinfo
);
347 synproxy
= nfct_synproxy(ct
);
348 if (synproxy
== NULL
)
351 if (nf_is_loopback_packet(skb
))
354 nexthdr
= ipv6_hdr(skb
)->nexthdr
;
355 thoff
= ipv6_skip_exthdr(skb
, sizeof(struct ipv6hdr
), &nexthdr
,
357 if (thoff
< 0 || nexthdr
!= IPPROTO_TCP
)
360 th
= skb_header_pointer(skb
, thoff
, sizeof(_th
), &_th
);
364 state
= &ct
->proto
.tcp
;
365 switch (state
->state
) {
366 case TCP_CONNTRACK_CLOSE
:
367 if (th
->rst
&& !test_bit(IPS_SEEN_REPLY_BIT
, &ct
->status
)) {
368 nf_ct_seqadj_init(ct
, ctinfo
, synproxy
->isn
-
373 if (!th
->syn
|| th
->ack
||
374 CTINFO2DIR(ctinfo
) != IP_CT_DIR_ORIGINAL
)
377 /* Reopened connection - reset the sequence number and timestamp
378 * adjustments, they will get initialized once the connection is
381 nf_ct_seqadj_init(ct
, ctinfo
, 0);
383 this_cpu_inc(snet
->stats
->conn_reopened
);
386 case TCP_CONNTRACK_SYN_SENT
:
387 if (!synproxy_parse_options(skb
, thoff
, th
, &opts
))
390 if (!th
->syn
&& th
->ack
&&
391 CTINFO2DIR(ctinfo
) == IP_CT_DIR_ORIGINAL
) {
392 /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
393 * therefore we need to add 1 to make the SYN sequence
394 * number match the one of first SYN.
396 if (synproxy_recv_client_ack(net
, skb
, th
, &opts
,
397 ntohl(th
->seq
) + 1)) {
398 this_cpu_inc(snet
->stats
->cookie_retrans
);
406 synproxy
->isn
= ntohl(th
->ack_seq
);
407 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
408 synproxy
->its
= opts
.tsecr
;
410 nf_conntrack_event_cache(IPCT_SYNPROXY
, ct
);
412 case TCP_CONNTRACK_SYN_RECV
:
413 if (!th
->syn
|| !th
->ack
)
416 if (!synproxy_parse_options(skb
, thoff
, th
, &opts
))
419 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
) {
420 synproxy
->tsoff
= opts
.tsval
- synproxy
->its
;
421 nf_conntrack_event_cache(IPCT_SYNPROXY
, ct
);
424 opts
.options
&= ~(XT_SYNPROXY_OPT_MSS
|
425 XT_SYNPROXY_OPT_WSCALE
|
426 XT_SYNPROXY_OPT_SACK_PERM
);
428 swap(opts
.tsval
, opts
.tsecr
);
429 synproxy_send_server_ack(net
, state
, skb
, th
, &opts
);
431 nf_ct_seqadj_init(ct
, ctinfo
, synproxy
->isn
- ntohl(th
->seq
));
432 nf_conntrack_event_cache(IPCT_SEQADJ
, ct
);
434 swap(opts
.tsval
, opts
.tsecr
);
435 synproxy_send_client_ack(net
, skb
, th
, &opts
);
443 synproxy_tstamp_adjust(skb
, thoff
, th
, ct
, ctinfo
, synproxy
);
447 static const struct nf_hook_ops ipv6_synproxy_ops
[] = {
449 .hook
= ipv6_synproxy_hook
,
451 .hooknum
= NF_INET_LOCAL_IN
,
452 .priority
= NF_IP_PRI_CONNTRACK_CONFIRM
- 1,
455 .hook
= ipv6_synproxy_hook
,
457 .hooknum
= NF_INET_POST_ROUTING
,
458 .priority
= NF_IP_PRI_CONNTRACK_CONFIRM
- 1,
462 static int synproxy_tg6_check(const struct xt_tgchk_param
*par
)
464 struct synproxy_net
*snet
= synproxy_pernet(par
->net
);
465 const struct ip6t_entry
*e
= par
->entryinfo
;
468 if (!(e
->ipv6
.flags
& IP6T_F_PROTO
) ||
469 e
->ipv6
.proto
!= IPPROTO_TCP
||
470 e
->ipv6
.invflags
& XT_INV_PROTO
)
473 err
= nf_ct_netns_get(par
->net
, par
->family
);
477 if (snet
->hook_ref6
== 0) {
478 err
= nf_register_net_hooks(par
->net
, ipv6_synproxy_ops
,
479 ARRAY_SIZE(ipv6_synproxy_ops
));
481 nf_ct_netns_put(par
->net
, par
->family
);
490 static void synproxy_tg6_destroy(const struct xt_tgdtor_param
*par
)
492 struct synproxy_net
*snet
= synproxy_pernet(par
->net
);
495 if (snet
->hook_ref6
== 0)
496 nf_unregister_net_hooks(par
->net
, ipv6_synproxy_ops
,
497 ARRAY_SIZE(ipv6_synproxy_ops
));
498 nf_ct_netns_put(par
->net
, par
->family
);
501 static struct xt_target synproxy_tg6_reg __read_mostly
= {
503 .family
= NFPROTO_IPV6
,
504 .hooks
= (1 << NF_INET_LOCAL_IN
) | (1 << NF_INET_FORWARD
),
505 .target
= synproxy_tg6
,
506 .targetsize
= sizeof(struct xt_synproxy_info
),
507 .checkentry
= synproxy_tg6_check
,
508 .destroy
= synproxy_tg6_destroy
,
512 static int __init
synproxy_tg6_init(void)
514 return xt_register_target(&synproxy_tg6_reg
);
517 static void __exit
synproxy_tg6_exit(void)
519 xt_unregister_target(&synproxy_tg6_reg
);
522 module_init(synproxy_tg6_init
);
523 module_exit(synproxy_tg6_exit
);
525 MODULE_LICENSE("GPL");
526 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");