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 sk_buff
*skb
, const struct in6_addr
*saddr
,
24 const struct in6_addr
*daddr
)
28 skb_reset_network_header(skb
);
29 iph
= (struct ipv6hdr
*)skb_put(skb
, sizeof(*iph
));
30 ip6_flow_hdr(iph
, 0, 0);
31 iph
->hop_limit
= 64; //XXX
32 iph
->nexthdr
= IPPROTO_TCP
;
40 synproxy_send_tcp(const struct sk_buff
*skb
, struct sk_buff
*nskb
,
41 struct nf_conntrack
*nfct
, enum ip_conntrack_info ctinfo
,
42 struct ipv6hdr
*niph
, struct tcphdr
*nth
,
43 unsigned int tcp_hdr_size
)
45 struct net
*net
= nf_ct_net((struct nf_conn
*)nfct
);
46 struct dst_entry
*dst
;
49 nth
->check
= ~tcp_v6_check(tcp_hdr_size
, &niph
->saddr
, &niph
->daddr
, 0);
50 nskb
->ip_summed
= CHECKSUM_PARTIAL
;
51 nskb
->csum_start
= (unsigned char *)nth
- nskb
->head
;
52 nskb
->csum_offset
= offsetof(struct tcphdr
, check
);
54 memset(&fl6
, 0, sizeof(fl6
));
55 fl6
.flowi6_proto
= IPPROTO_TCP
;
56 fl6
.saddr
= niph
->saddr
;
57 fl6
.daddr
= niph
->daddr
;
58 fl6
.fl6_sport
= nth
->source
;
59 fl6
.fl6_dport
= nth
->dest
;
60 security_skb_classify_flow((struct sk_buff
*)skb
, flowi6_to_flowi(&fl6
));
61 dst
= ip6_route_output(net
, NULL
, &fl6
);
62 if (dst
== NULL
|| dst
->error
) {
66 dst
= xfrm_lookup(net
, dst
, flowi6_to_flowi(&fl6
), NULL
, 0);
70 skb_dst_set(nskb
, dst
);
74 nskb
->nfctinfo
= ctinfo
;
75 nf_conntrack_get(nfct
);
86 synproxy_send_client_synack(const struct sk_buff
*skb
, const struct tcphdr
*th
,
87 const struct synproxy_options
*opts
)
90 struct ipv6hdr
*iph
, *niph
;
92 unsigned int tcp_hdr_size
;
97 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
98 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
102 skb_reserve(nskb
, MAX_TCP_HEADER
);
104 niph
= synproxy_build_ip(nskb
, &iph
->daddr
, &iph
->saddr
);
106 skb_reset_transport_header(nskb
);
107 nth
= (struct tcphdr
*)skb_put(nskb
, tcp_hdr_size
);
108 nth
->source
= th
->dest
;
109 nth
->dest
= th
->source
;
110 nth
->seq
= htonl(__cookie_v6_init_sequence(iph
, th
, &mss
));
111 nth
->ack_seq
= htonl(ntohl(th
->seq
) + 1);
112 tcp_flag_word(nth
) = TCP_FLAG_SYN
| TCP_FLAG_ACK
;
113 if (opts
->options
& XT_SYNPROXY_OPT_ECN
)
114 tcp_flag_word(nth
) |= TCP_FLAG_ECE
;
115 nth
->doff
= tcp_hdr_size
/ 4;
120 synproxy_build_options(nth
, opts
);
122 synproxy_send_tcp(skb
, nskb
, skb
->nfct
, IP_CT_ESTABLISHED_REPLY
,
123 niph
, nth
, tcp_hdr_size
);
127 synproxy_send_server_syn(const struct synproxy_net
*snet
,
128 const struct sk_buff
*skb
, const struct tcphdr
*th
,
129 const struct synproxy_options
*opts
, u32 recv_seq
)
131 struct sk_buff
*nskb
;
132 struct ipv6hdr
*iph
, *niph
;
134 unsigned int tcp_hdr_size
;
138 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
139 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
143 skb_reserve(nskb
, MAX_TCP_HEADER
);
145 niph
= synproxy_build_ip(nskb
, &iph
->saddr
, &iph
->daddr
);
147 skb_reset_transport_header(nskb
);
148 nth
= (struct tcphdr
*)skb_put(nskb
, tcp_hdr_size
);
149 nth
->source
= th
->source
;
150 nth
->dest
= th
->dest
;
151 nth
->seq
= htonl(recv_seq
- 1);
152 /* ack_seq is used to relay our ISN to the synproxy hook to initialize
153 * sequence number translation once a connection tracking entry exists.
155 nth
->ack_seq
= htonl(ntohl(th
->ack_seq
) - 1);
156 tcp_flag_word(nth
) = TCP_FLAG_SYN
;
157 if (opts
->options
& XT_SYNPROXY_OPT_ECN
)
158 tcp_flag_word(nth
) |= TCP_FLAG_ECE
| TCP_FLAG_CWR
;
159 nth
->doff
= tcp_hdr_size
/ 4;
160 nth
->window
= th
->window
;
164 synproxy_build_options(nth
, opts
);
166 synproxy_send_tcp(skb
, nskb
, &snet
->tmpl
->ct_general
, IP_CT_NEW
,
167 niph
, nth
, tcp_hdr_size
);
171 synproxy_send_server_ack(const struct synproxy_net
*snet
,
172 const struct ip_ct_tcp
*state
,
173 const struct sk_buff
*skb
, const struct tcphdr
*th
,
174 const struct synproxy_options
*opts
)
176 struct sk_buff
*nskb
;
177 struct ipv6hdr
*iph
, *niph
;
179 unsigned int tcp_hdr_size
;
183 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
184 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
188 skb_reserve(nskb
, MAX_TCP_HEADER
);
190 niph
= synproxy_build_ip(nskb
, &iph
->daddr
, &iph
->saddr
);
192 skb_reset_transport_header(nskb
);
193 nth
= (struct tcphdr
*)skb_put(nskb
, tcp_hdr_size
);
194 nth
->source
= th
->dest
;
195 nth
->dest
= th
->source
;
196 nth
->seq
= htonl(ntohl(th
->ack_seq
));
197 nth
->ack_seq
= htonl(ntohl(th
->seq
) + 1);
198 tcp_flag_word(nth
) = TCP_FLAG_ACK
;
199 nth
->doff
= tcp_hdr_size
/ 4;
200 nth
->window
= htons(state
->seen
[IP_CT_DIR_ORIGINAL
].td_maxwin
);
204 synproxy_build_options(nth
, opts
);
206 synproxy_send_tcp(skb
, nskb
, NULL
, 0, niph
, nth
, tcp_hdr_size
);
210 synproxy_send_client_ack(const struct synproxy_net
*snet
,
211 const struct sk_buff
*skb
, const struct tcphdr
*th
,
212 const struct synproxy_options
*opts
)
214 struct sk_buff
*nskb
;
215 struct ipv6hdr
*iph
, *niph
;
217 unsigned int tcp_hdr_size
;
221 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
222 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
226 skb_reserve(nskb
, MAX_TCP_HEADER
);
228 niph
= synproxy_build_ip(nskb
, &iph
->saddr
, &iph
->daddr
);
230 skb_reset_transport_header(nskb
);
231 nth
= (struct tcphdr
*)skb_put(nskb
, tcp_hdr_size
);
232 nth
->source
= th
->source
;
233 nth
->dest
= th
->dest
;
234 nth
->seq
= htonl(ntohl(th
->seq
) + 1);
235 nth
->ack_seq
= th
->ack_seq
;
236 tcp_flag_word(nth
) = TCP_FLAG_ACK
;
237 nth
->doff
= tcp_hdr_size
/ 4;
238 nth
->window
= ntohs(htons(th
->window
) >> opts
->wscale
);
242 synproxy_build_options(nth
, opts
);
244 synproxy_send_tcp(skb
, nskb
, NULL
, 0, niph
, nth
, tcp_hdr_size
);
248 synproxy_recv_client_ack(const struct synproxy_net
*snet
,
249 const struct sk_buff
*skb
, const struct tcphdr
*th
,
250 struct synproxy_options
*opts
, u32 recv_seq
)
254 mss
= __cookie_v6_check(ipv6_hdr(skb
), th
, ntohl(th
->ack_seq
) - 1);
256 this_cpu_inc(snet
->stats
->cookie_invalid
);
260 this_cpu_inc(snet
->stats
->cookie_valid
);
263 if (opts
->options
& XT_SYNPROXY_OPT_TIMESTAMP
)
264 synproxy_check_timestamp_cookie(opts
);
266 synproxy_send_server_syn(snet
, skb
, th
, opts
, recv_seq
);
271 synproxy_tg6(struct sk_buff
*skb
, const struct xt_action_param
*par
)
273 const struct xt_synproxy_info
*info
= par
->targinfo
;
274 struct synproxy_net
*snet
= synproxy_pernet(dev_net(par
->in
));
275 struct synproxy_options opts
= {};
276 struct tcphdr
*th
, _th
;
278 if (nf_ip6_checksum(skb
, par
->hooknum
, par
->thoff
, IPPROTO_TCP
))
281 th
= skb_header_pointer(skb
, par
->thoff
, sizeof(_th
), &_th
);
285 synproxy_parse_options(skb
, par
->thoff
, th
, &opts
);
287 if (th
->syn
&& !(th
->ack
|| th
->fin
|| th
->rst
)) {
288 /* Initial SYN from client */
289 this_cpu_inc(snet
->stats
->syn_received
);
291 if (th
->ece
&& th
->cwr
)
292 opts
.options
|= XT_SYNPROXY_OPT_ECN
;
294 opts
.options
&= info
->options
;
295 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
296 synproxy_init_timestamp_cookie(info
, &opts
);
298 opts
.options
&= ~(XT_SYNPROXY_OPT_WSCALE
|
299 XT_SYNPROXY_OPT_SACK_PERM
|
300 XT_SYNPROXY_OPT_ECN
);
302 synproxy_send_client_synack(skb
, th
, &opts
);
305 } else if (th
->ack
&& !(th
->fin
|| th
->rst
|| th
->syn
)) {
306 /* ACK from client */
307 synproxy_recv_client_ack(snet
, skb
, th
, &opts
, ntohl(th
->seq
));
314 static unsigned int ipv6_synproxy_hook(unsigned int hooknum
,
316 const struct net_device
*in
,
317 const struct net_device
*out
,
318 int (*okfn
)(struct sk_buff
*))
320 struct synproxy_net
*snet
= synproxy_pernet(dev_net(in
? : out
));
321 enum ip_conntrack_info ctinfo
;
323 struct nf_conn_synproxy
*synproxy
;
324 struct synproxy_options opts
= {};
325 const struct ip_ct_tcp
*state
;
326 struct tcphdr
*th
, _th
;
331 ct
= nf_ct_get(skb
, &ctinfo
);
335 synproxy
= nfct_synproxy(ct
);
336 if (synproxy
== NULL
)
339 if (nf_is_loopback_packet(skb
))
342 nexthdr
= ipv6_hdr(skb
)->nexthdr
;
343 thoff
= ipv6_skip_exthdr(skb
, sizeof(struct ipv6hdr
), &nexthdr
,
348 th
= skb_header_pointer(skb
, thoff
, sizeof(_th
), &_th
);
352 state
= &ct
->proto
.tcp
;
353 switch (state
->state
) {
354 case TCP_CONNTRACK_CLOSE
:
355 if (th
->rst
&& !test_bit(IPS_SEEN_REPLY_BIT
, &ct
->status
)) {
356 nf_ct_seqadj_init(ct
, ctinfo
, synproxy
->isn
-
361 if (!th
->syn
|| th
->ack
||
362 CTINFO2DIR(ctinfo
) != IP_CT_DIR_ORIGINAL
)
365 /* Reopened connection - reset the sequence number and timestamp
366 * adjustments, they will get initialized once the connection is
369 nf_ct_seqadj_init(ct
, ctinfo
, 0);
371 this_cpu_inc(snet
->stats
->conn_reopened
);
374 case TCP_CONNTRACK_SYN_SENT
:
375 synproxy_parse_options(skb
, thoff
, th
, &opts
);
377 if (!th
->syn
&& th
->ack
&&
378 CTINFO2DIR(ctinfo
) == IP_CT_DIR_ORIGINAL
) {
379 /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
380 * therefore we need to add 1 to make the SYN sequence
381 * number match the one of first SYN.
383 if (synproxy_recv_client_ack(snet
, skb
, th
, &opts
,
385 this_cpu_inc(snet
->stats
->cookie_retrans
);
390 synproxy
->isn
= ntohl(th
->ack_seq
);
391 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
392 synproxy
->its
= opts
.tsecr
;
394 case TCP_CONNTRACK_SYN_RECV
:
395 if (!th
->syn
|| !th
->ack
)
398 synproxy_parse_options(skb
, thoff
, th
, &opts
);
399 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
400 synproxy
->tsoff
= opts
.tsval
- synproxy
->its
;
402 opts
.options
&= ~(XT_SYNPROXY_OPT_MSS
|
403 XT_SYNPROXY_OPT_WSCALE
|
404 XT_SYNPROXY_OPT_SACK_PERM
);
406 swap(opts
.tsval
, opts
.tsecr
);
407 synproxy_send_server_ack(snet
, state
, skb
, th
, &opts
);
409 nf_ct_seqadj_init(ct
, ctinfo
, synproxy
->isn
- ntohl(th
->seq
));
411 swap(opts
.tsval
, opts
.tsecr
);
412 synproxy_send_client_ack(snet
, skb
, th
, &opts
);
420 synproxy_tstamp_adjust(skb
, thoff
, th
, ct
, ctinfo
, synproxy
);
424 static int synproxy_tg6_check(const struct xt_tgchk_param
*par
)
426 const struct ip6t_entry
*e
= par
->entryinfo
;
428 if (!(e
->ipv6
.flags
& IP6T_F_PROTO
) ||
429 e
->ipv6
.proto
!= IPPROTO_TCP
||
430 e
->ipv6
.invflags
& XT_INV_PROTO
)
433 return nf_ct_l3proto_try_module_get(par
->family
);
436 static void synproxy_tg6_destroy(const struct xt_tgdtor_param
*par
)
438 nf_ct_l3proto_module_put(par
->family
);
441 static struct xt_target synproxy_tg6_reg __read_mostly
= {
443 .family
= NFPROTO_IPV6
,
444 .target
= synproxy_tg6
,
445 .targetsize
= sizeof(struct xt_synproxy_info
),
446 .checkentry
= synproxy_tg6_check
,
447 .destroy
= synproxy_tg6_destroy
,
451 static struct nf_hook_ops ipv6_synproxy_ops
[] __read_mostly
= {
453 .hook
= ipv6_synproxy_hook
,
454 .owner
= THIS_MODULE
,
456 .hooknum
= NF_INET_LOCAL_IN
,
457 .priority
= NF_IP_PRI_CONNTRACK_CONFIRM
- 1,
460 .hook
= ipv6_synproxy_hook
,
461 .owner
= THIS_MODULE
,
463 .hooknum
= NF_INET_POST_ROUTING
,
464 .priority
= NF_IP_PRI_CONNTRACK_CONFIRM
- 1,
468 static int __init
synproxy_tg6_init(void)
472 err
= nf_register_hooks(ipv6_synproxy_ops
,
473 ARRAY_SIZE(ipv6_synproxy_ops
));
477 err
= xt_register_target(&synproxy_tg6_reg
);
484 nf_unregister_hooks(ipv6_synproxy_ops
, ARRAY_SIZE(ipv6_synproxy_ops
));
489 static void __exit
synproxy_tg6_exit(void)
491 xt_unregister_target(&synproxy_tg6_reg
);
492 nf_unregister_hooks(ipv6_synproxy_ops
, ARRAY_SIZE(ipv6_synproxy_ops
));
495 module_init(synproxy_tg6_init
);
496 module_exit(synproxy_tg6_exit
);
498 MODULE_LICENSE("GPL");
499 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");