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>
13 #include <linux/netfilter_ipv4/ip_tables.h>
14 #include <linux/netfilter/x_tables.h>
15 #include <linux/netfilter/xt_SYNPROXY.h>
16 #include <net/netfilter/nf_conntrack.h>
17 #include <net/netfilter/nf_conntrack_seqadj.h>
18 #include <net/netfilter/nf_conntrack_synproxy.h>
21 synproxy_build_ip(struct sk_buff
*skb
, __be32 saddr
, __be32 daddr
)
25 skb_reset_network_header(skb
);
26 iph
= (struct iphdr
*)skb_put(skb
, sizeof(*iph
));
28 iph
->ihl
= sizeof(*iph
) / 4;
31 iph
->frag_off
= htons(IP_DF
);
32 iph
->ttl
= sysctl_ip_default_ttl
;
33 iph
->protocol
= IPPROTO_TCP
;
42 synproxy_send_tcp(const struct sk_buff
*skb
, struct sk_buff
*nskb
,
43 struct nf_conntrack
*nfct
, enum ip_conntrack_info ctinfo
,
44 struct iphdr
*niph
, struct tcphdr
*nth
,
45 unsigned int tcp_hdr_size
)
47 nth
->check
= ~tcp_v4_check(tcp_hdr_size
, niph
->saddr
, niph
->daddr
, 0);
48 nskb
->ip_summed
= CHECKSUM_PARTIAL
;
49 nskb
->csum_start
= (unsigned char *)nth
- nskb
->head
;
50 nskb
->csum_offset
= offsetof(struct tcphdr
, check
);
52 skb_dst_set_noref(nskb
, skb_dst(skb
));
53 nskb
->protocol
= htons(ETH_P_IP
);
54 if (ip_route_me_harder(nskb
, RTN_UNSPEC
))
59 nskb
->nfctinfo
= ctinfo
;
60 nf_conntrack_get(nfct
);
71 synproxy_send_client_synack(const struct sk_buff
*skb
, const struct tcphdr
*th
,
72 const struct synproxy_options
*opts
)
75 struct iphdr
*iph
, *niph
;
77 unsigned int tcp_hdr_size
;
82 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
83 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
87 skb_reserve(nskb
, MAX_TCP_HEADER
);
89 niph
= synproxy_build_ip(nskb
, iph
->daddr
, iph
->saddr
);
91 skb_reset_transport_header(nskb
);
92 nth
= (struct tcphdr
*)skb_put(nskb
, tcp_hdr_size
);
93 nth
->source
= th
->dest
;
94 nth
->dest
= th
->source
;
95 nth
->seq
= htonl(__cookie_v4_init_sequence(iph
, th
, &mss
));
96 nth
->ack_seq
= htonl(ntohl(th
->seq
) + 1);
97 tcp_flag_word(nth
) = TCP_FLAG_SYN
| TCP_FLAG_ACK
;
98 if (opts
->options
& XT_SYNPROXY_OPT_ECN
)
99 tcp_flag_word(nth
) |= TCP_FLAG_ECE
;
100 nth
->doff
= tcp_hdr_size
/ 4;
105 synproxy_build_options(nth
, opts
);
107 synproxy_send_tcp(skb
, nskb
, skb
->nfct
, IP_CT_ESTABLISHED_REPLY
,
108 niph
, nth
, tcp_hdr_size
);
112 synproxy_send_server_syn(const struct synproxy_net
*snet
,
113 const struct sk_buff
*skb
, const struct tcphdr
*th
,
114 const struct synproxy_options
*opts
, u32 recv_seq
)
116 struct sk_buff
*nskb
;
117 struct iphdr
*iph
, *niph
;
119 unsigned int tcp_hdr_size
;
123 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
124 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
128 skb_reserve(nskb
, MAX_TCP_HEADER
);
130 niph
= synproxy_build_ip(nskb
, iph
->saddr
, iph
->daddr
);
132 skb_reset_transport_header(nskb
);
133 nth
= (struct tcphdr
*)skb_put(nskb
, tcp_hdr_size
);
134 nth
->source
= th
->source
;
135 nth
->dest
= th
->dest
;
136 nth
->seq
= htonl(recv_seq
- 1);
137 /* ack_seq is used to relay our ISN to the synproxy hook to initialize
138 * sequence number translation once a connection tracking entry exists.
140 nth
->ack_seq
= htonl(ntohl(th
->ack_seq
) - 1);
141 tcp_flag_word(nth
) = TCP_FLAG_SYN
;
142 if (opts
->options
& XT_SYNPROXY_OPT_ECN
)
143 tcp_flag_word(nth
) |= TCP_FLAG_ECE
| TCP_FLAG_CWR
;
144 nth
->doff
= tcp_hdr_size
/ 4;
145 nth
->window
= th
->window
;
149 synproxy_build_options(nth
, opts
);
151 synproxy_send_tcp(skb
, nskb
, &snet
->tmpl
->ct_general
, IP_CT_NEW
,
152 niph
, nth
, tcp_hdr_size
);
156 synproxy_send_server_ack(const struct synproxy_net
*snet
,
157 const struct ip_ct_tcp
*state
,
158 const struct sk_buff
*skb
, const struct tcphdr
*th
,
159 const struct synproxy_options
*opts
)
161 struct sk_buff
*nskb
;
162 struct iphdr
*iph
, *niph
;
164 unsigned int tcp_hdr_size
;
168 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
169 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
173 skb_reserve(nskb
, MAX_TCP_HEADER
);
175 niph
= synproxy_build_ip(nskb
, iph
->daddr
, iph
->saddr
);
177 skb_reset_transport_header(nskb
);
178 nth
= (struct tcphdr
*)skb_put(nskb
, tcp_hdr_size
);
179 nth
->source
= th
->dest
;
180 nth
->dest
= th
->source
;
181 nth
->seq
= htonl(ntohl(th
->ack_seq
));
182 nth
->ack_seq
= htonl(ntohl(th
->seq
) + 1);
183 tcp_flag_word(nth
) = TCP_FLAG_ACK
;
184 nth
->doff
= tcp_hdr_size
/ 4;
185 nth
->window
= htons(state
->seen
[IP_CT_DIR_ORIGINAL
].td_maxwin
);
189 synproxy_build_options(nth
, opts
);
191 synproxy_send_tcp(skb
, nskb
, NULL
, 0, niph
, nth
, tcp_hdr_size
);
195 synproxy_send_client_ack(const struct synproxy_net
*snet
,
196 const struct sk_buff
*skb
, const struct tcphdr
*th
,
197 const struct synproxy_options
*opts
)
199 struct sk_buff
*nskb
;
200 struct iphdr
*iph
, *niph
;
202 unsigned int tcp_hdr_size
;
206 tcp_hdr_size
= sizeof(*nth
) + synproxy_options_size(opts
);
207 nskb
= alloc_skb(sizeof(*niph
) + tcp_hdr_size
+ MAX_TCP_HEADER
,
211 skb_reserve(nskb
, MAX_TCP_HEADER
);
213 niph
= synproxy_build_ip(nskb
, iph
->saddr
, iph
->daddr
);
215 skb_reset_transport_header(nskb
);
216 nth
= (struct tcphdr
*)skb_put(nskb
, tcp_hdr_size
);
217 nth
->source
= th
->source
;
218 nth
->dest
= th
->dest
;
219 nth
->seq
= htonl(ntohl(th
->seq
) + 1);
220 nth
->ack_seq
= th
->ack_seq
;
221 tcp_flag_word(nth
) = TCP_FLAG_ACK
;
222 nth
->doff
= tcp_hdr_size
/ 4;
223 nth
->window
= htons(ntohs(th
->window
) >> opts
->wscale
);
227 synproxy_build_options(nth
, opts
);
229 synproxy_send_tcp(skb
, nskb
, skb
->nfct
, IP_CT_ESTABLISHED_REPLY
,
230 niph
, nth
, tcp_hdr_size
);
234 synproxy_recv_client_ack(const struct synproxy_net
*snet
,
235 const struct sk_buff
*skb
, const struct tcphdr
*th
,
236 struct synproxy_options
*opts
, u32 recv_seq
)
240 mss
= __cookie_v4_check(ip_hdr(skb
), th
, ntohl(th
->ack_seq
) - 1);
242 this_cpu_inc(snet
->stats
->cookie_invalid
);
246 this_cpu_inc(snet
->stats
->cookie_valid
);
248 opts
->options
|= XT_SYNPROXY_OPT_MSS
;
250 if (opts
->options
& XT_SYNPROXY_OPT_TIMESTAMP
)
251 synproxy_check_timestamp_cookie(opts
);
253 synproxy_send_server_syn(snet
, skb
, th
, opts
, recv_seq
);
258 synproxy_tg4(struct sk_buff
*skb
, const struct xt_action_param
*par
)
260 const struct xt_synproxy_info
*info
= par
->targinfo
;
261 struct synproxy_net
*snet
= synproxy_pernet(dev_net(par
->in
));
262 struct synproxy_options opts
= {};
263 struct tcphdr
*th
, _th
;
265 if (nf_ip_checksum(skb
, par
->hooknum
, par
->thoff
, IPPROTO_TCP
))
268 th
= skb_header_pointer(skb
, par
->thoff
, sizeof(_th
), &_th
);
272 if (!synproxy_parse_options(skb
, par
->thoff
, th
, &opts
))
275 if (th
->syn
&& !(th
->ack
|| th
->fin
|| th
->rst
)) {
276 /* Initial SYN from client */
277 this_cpu_inc(snet
->stats
->syn_received
);
279 if (th
->ece
&& th
->cwr
)
280 opts
.options
|= XT_SYNPROXY_OPT_ECN
;
282 opts
.options
&= info
->options
;
283 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
284 synproxy_init_timestamp_cookie(info
, &opts
);
286 opts
.options
&= ~(XT_SYNPROXY_OPT_WSCALE
|
287 XT_SYNPROXY_OPT_SACK_PERM
|
288 XT_SYNPROXY_OPT_ECN
);
290 synproxy_send_client_synack(skb
, th
, &opts
);
293 } else if (th
->ack
&& !(th
->fin
|| th
->rst
|| th
->syn
)) {
294 /* ACK from client */
295 synproxy_recv_client_ack(snet
, skb
, th
, &opts
, ntohl(th
->seq
));
302 static unsigned int ipv4_synproxy_hook(const struct nf_hook_ops
*ops
,
304 const struct nf_hook_state
*nhs
)
306 struct synproxy_net
*snet
= synproxy_pernet(dev_net(nhs
->in
? : nhs
->out
));
307 enum ip_conntrack_info ctinfo
;
309 struct nf_conn_synproxy
*synproxy
;
310 struct synproxy_options opts
= {};
311 const struct ip_ct_tcp
*state
;
312 struct tcphdr
*th
, _th
;
315 ct
= nf_ct_get(skb
, &ctinfo
);
319 synproxy
= nfct_synproxy(ct
);
320 if (synproxy
== NULL
)
323 if (nf_is_loopback_packet(skb
))
326 thoff
= ip_hdrlen(skb
);
327 th
= skb_header_pointer(skb
, thoff
, sizeof(_th
), &_th
);
331 state
= &ct
->proto
.tcp
;
332 switch (state
->state
) {
333 case TCP_CONNTRACK_CLOSE
:
334 if (th
->rst
&& !test_bit(IPS_SEEN_REPLY_BIT
, &ct
->status
)) {
335 nf_ct_seqadj_init(ct
, ctinfo
, synproxy
->isn
-
340 if (!th
->syn
|| th
->ack
||
341 CTINFO2DIR(ctinfo
) != IP_CT_DIR_ORIGINAL
)
344 /* Reopened connection - reset the sequence number and timestamp
345 * adjustments, they will get initialized once the connection is
348 nf_ct_seqadj_init(ct
, ctinfo
, 0);
350 this_cpu_inc(snet
->stats
->conn_reopened
);
353 case TCP_CONNTRACK_SYN_SENT
:
354 if (!synproxy_parse_options(skb
, thoff
, th
, &opts
))
357 if (!th
->syn
&& th
->ack
&&
358 CTINFO2DIR(ctinfo
) == IP_CT_DIR_ORIGINAL
) {
359 /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
360 * therefore we need to add 1 to make the SYN sequence
361 * number match the one of first SYN.
363 if (synproxy_recv_client_ack(snet
, skb
, th
, &opts
,
365 this_cpu_inc(snet
->stats
->cookie_retrans
);
370 synproxy
->isn
= ntohl(th
->ack_seq
);
371 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
372 synproxy
->its
= opts
.tsecr
;
374 case TCP_CONNTRACK_SYN_RECV
:
375 if (!th
->syn
|| !th
->ack
)
378 if (!synproxy_parse_options(skb
, thoff
, th
, &opts
))
381 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
382 synproxy
->tsoff
= opts
.tsval
- synproxy
->its
;
384 opts
.options
&= ~(XT_SYNPROXY_OPT_MSS
|
385 XT_SYNPROXY_OPT_WSCALE
|
386 XT_SYNPROXY_OPT_SACK_PERM
);
388 swap(opts
.tsval
, opts
.tsecr
);
389 synproxy_send_server_ack(snet
, state
, skb
, th
, &opts
);
391 nf_ct_seqadj_init(ct
, ctinfo
, synproxy
->isn
- ntohl(th
->seq
));
393 swap(opts
.tsval
, opts
.tsecr
);
394 synproxy_send_client_ack(snet
, skb
, th
, &opts
);
402 synproxy_tstamp_adjust(skb
, thoff
, th
, ct
, ctinfo
, synproxy
);
406 static int synproxy_tg4_check(const struct xt_tgchk_param
*par
)
408 const struct ipt_entry
*e
= par
->entryinfo
;
410 if (e
->ip
.proto
!= IPPROTO_TCP
||
411 e
->ip
.invflags
& XT_INV_PROTO
)
414 return nf_ct_l3proto_try_module_get(par
->family
);
417 static void synproxy_tg4_destroy(const struct xt_tgdtor_param
*par
)
419 nf_ct_l3proto_module_put(par
->family
);
422 static struct xt_target synproxy_tg4_reg __read_mostly
= {
424 .family
= NFPROTO_IPV4
,
425 .hooks
= (1 << NF_INET_LOCAL_IN
) | (1 << NF_INET_FORWARD
),
426 .target
= synproxy_tg4
,
427 .targetsize
= sizeof(struct xt_synproxy_info
),
428 .checkentry
= synproxy_tg4_check
,
429 .destroy
= synproxy_tg4_destroy
,
433 static struct nf_hook_ops ipv4_synproxy_ops
[] __read_mostly
= {
435 .hook
= ipv4_synproxy_hook
,
436 .owner
= THIS_MODULE
,
438 .hooknum
= NF_INET_LOCAL_IN
,
439 .priority
= NF_IP_PRI_CONNTRACK_CONFIRM
- 1,
442 .hook
= ipv4_synproxy_hook
,
443 .owner
= THIS_MODULE
,
445 .hooknum
= NF_INET_POST_ROUTING
,
446 .priority
= NF_IP_PRI_CONNTRACK_CONFIRM
- 1,
450 static int __init
synproxy_tg4_init(void)
454 err
= nf_register_hooks(ipv4_synproxy_ops
,
455 ARRAY_SIZE(ipv4_synproxy_ops
));
459 err
= xt_register_target(&synproxy_tg4_reg
);
466 nf_unregister_hooks(ipv4_synproxy_ops
, ARRAY_SIZE(ipv4_synproxy_ops
));
471 static void __exit
synproxy_tg4_exit(void)
473 xt_unregister_target(&synproxy_tg4_reg
);
474 nf_unregister_hooks(ipv4_synproxy_ops
, ARRAY_SIZE(ipv4_synproxy_ops
));
477 module_init(synproxy_tg4_init
);
478 module_exit(synproxy_tg4_exit
);
480 MODULE_LICENSE("GPL");
481 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");