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
, u32 saddr
, u32 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
= ntohs(htons(th
->window
) >> opts
->wscale
);
227 synproxy_build_options(nth
, opts
);
229 synproxy_send_tcp(skb
, nskb
, NULL
, 0, niph
, nth
, tcp_hdr_size
);
233 synproxy_recv_client_ack(const struct synproxy_net
*snet
,
234 const struct sk_buff
*skb
, const struct tcphdr
*th
,
235 struct synproxy_options
*opts
, u32 recv_seq
)
239 mss
= __cookie_v4_check(ip_hdr(skb
), th
, ntohl(th
->ack_seq
) - 1);
241 this_cpu_inc(snet
->stats
->cookie_invalid
);
245 this_cpu_inc(snet
->stats
->cookie_valid
);
248 if (opts
->options
& XT_SYNPROXY_OPT_TIMESTAMP
)
249 synproxy_check_timestamp_cookie(opts
);
251 synproxy_send_server_syn(snet
, skb
, th
, opts
, recv_seq
);
256 synproxy_tg4(struct sk_buff
*skb
, const struct xt_action_param
*par
)
258 const struct xt_synproxy_info
*info
= par
->targinfo
;
259 struct synproxy_net
*snet
= synproxy_pernet(dev_net(par
->in
));
260 struct synproxy_options opts
= {};
261 struct tcphdr
*th
, _th
;
263 if (nf_ip_checksum(skb
, par
->hooknum
, par
->thoff
, IPPROTO_TCP
))
266 th
= skb_header_pointer(skb
, par
->thoff
, sizeof(_th
), &_th
);
270 synproxy_parse_options(skb
, par
->thoff
, th
, &opts
);
272 if (th
->syn
&& !(th
->ack
|| th
->fin
|| th
->rst
)) {
273 /* Initial SYN from client */
274 this_cpu_inc(snet
->stats
->syn_received
);
276 if (th
->ece
&& th
->cwr
)
277 opts
.options
|= XT_SYNPROXY_OPT_ECN
;
279 opts
.options
&= info
->options
;
280 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
281 synproxy_init_timestamp_cookie(info
, &opts
);
283 opts
.options
&= ~(XT_SYNPROXY_OPT_WSCALE
|
284 XT_SYNPROXY_OPT_SACK_PERM
|
285 XT_SYNPROXY_OPT_ECN
);
287 synproxy_send_client_synack(skb
, th
, &opts
);
290 } else if (th
->ack
&& !(th
->fin
|| th
->rst
|| th
->syn
)) {
291 /* ACK from client */
292 synproxy_recv_client_ack(snet
, skb
, th
, &opts
, ntohl(th
->seq
));
299 static unsigned int ipv4_synproxy_hook(unsigned int hooknum
,
301 const struct net_device
*in
,
302 const struct net_device
*out
,
303 int (*okfn
)(struct sk_buff
*))
305 struct synproxy_net
*snet
= synproxy_pernet(dev_net(in
? : out
));
306 enum ip_conntrack_info ctinfo
;
308 struct nf_conn_synproxy
*synproxy
;
309 struct synproxy_options opts
= {};
310 const struct ip_ct_tcp
*state
;
311 struct tcphdr
*th
, _th
;
314 ct
= nf_ct_get(skb
, &ctinfo
);
318 synproxy
= nfct_synproxy(ct
);
319 if (synproxy
== NULL
)
322 if (nf_is_loopback_packet(skb
))
325 thoff
= ip_hdrlen(skb
);
326 th
= skb_header_pointer(skb
, thoff
, sizeof(_th
), &_th
);
330 state
= &ct
->proto
.tcp
;
331 switch (state
->state
) {
332 case TCP_CONNTRACK_CLOSE
:
333 if (th
->rst
&& !test_bit(IPS_SEEN_REPLY_BIT
, &ct
->status
)) {
334 nf_ct_seqadj_init(ct
, ctinfo
, synproxy
->isn
-
339 if (!th
->syn
|| th
->ack
||
340 CTINFO2DIR(ctinfo
) != IP_CT_DIR_ORIGINAL
)
343 /* Reopened connection - reset the sequence number and timestamp
344 * adjustments, they will get initialized once the connection is
347 nf_ct_seqadj_init(ct
, ctinfo
, 0);
349 this_cpu_inc(snet
->stats
->conn_reopened
);
352 case TCP_CONNTRACK_SYN_SENT
:
353 synproxy_parse_options(skb
, thoff
, th
, &opts
);
355 if (!th
->syn
&& th
->ack
&&
356 CTINFO2DIR(ctinfo
) == IP_CT_DIR_ORIGINAL
) {
357 /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
358 * therefore we need to add 1 to make the SYN sequence
359 * number match the one of first SYN.
361 if (synproxy_recv_client_ack(snet
, skb
, th
, &opts
,
363 this_cpu_inc(snet
->stats
->cookie_retrans
);
368 synproxy
->isn
= ntohl(th
->ack_seq
);
369 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
370 synproxy
->its
= opts
.tsecr
;
372 case TCP_CONNTRACK_SYN_RECV
:
373 if (!th
->syn
|| !th
->ack
)
376 synproxy_parse_options(skb
, thoff
, th
, &opts
);
377 if (opts
.options
& XT_SYNPROXY_OPT_TIMESTAMP
)
378 synproxy
->tsoff
= opts
.tsval
- synproxy
->its
;
380 opts
.options
&= ~(XT_SYNPROXY_OPT_MSS
|
381 XT_SYNPROXY_OPT_WSCALE
|
382 XT_SYNPROXY_OPT_SACK_PERM
);
384 swap(opts
.tsval
, opts
.tsecr
);
385 synproxy_send_server_ack(snet
, state
, skb
, th
, &opts
);
387 nf_ct_seqadj_init(ct
, ctinfo
, synproxy
->isn
- ntohl(th
->seq
));
389 swap(opts
.tsval
, opts
.tsecr
);
390 synproxy_send_client_ack(snet
, skb
, th
, &opts
);
398 synproxy_tstamp_adjust(skb
, thoff
, th
, ct
, ctinfo
, synproxy
);
402 static int synproxy_tg4_check(const struct xt_tgchk_param
*par
)
404 const struct ipt_entry
*e
= par
->entryinfo
;
406 if (e
->ip
.proto
!= IPPROTO_TCP
||
407 e
->ip
.invflags
& XT_INV_PROTO
)
410 return nf_ct_l3proto_try_module_get(par
->family
);
413 static void synproxy_tg4_destroy(const struct xt_tgdtor_param
*par
)
415 nf_ct_l3proto_module_put(par
->family
);
418 static struct xt_target synproxy_tg4_reg __read_mostly
= {
420 .family
= NFPROTO_IPV4
,
421 .target
= synproxy_tg4
,
422 .targetsize
= sizeof(struct xt_synproxy_info
),
423 .checkentry
= synproxy_tg4_check
,
424 .destroy
= synproxy_tg4_destroy
,
428 static struct nf_hook_ops ipv4_synproxy_ops
[] __read_mostly
= {
430 .hook
= ipv4_synproxy_hook
,
431 .owner
= THIS_MODULE
,
433 .hooknum
= NF_INET_LOCAL_IN
,
434 .priority
= NF_IP_PRI_CONNTRACK_CONFIRM
- 1,
437 .hook
= ipv4_synproxy_hook
,
438 .owner
= THIS_MODULE
,
440 .hooknum
= NF_INET_POST_ROUTING
,
441 .priority
= NF_IP_PRI_CONNTRACK_CONFIRM
- 1,
445 static int __init
synproxy_tg4_init(void)
449 err
= nf_register_hooks(ipv4_synproxy_ops
,
450 ARRAY_SIZE(ipv4_synproxy_ops
));
454 err
= xt_register_target(&synproxy_tg4_reg
);
461 nf_unregister_hooks(ipv4_synproxy_ops
, ARRAY_SIZE(ipv4_synproxy_ops
));
466 static void __exit
synproxy_tg4_exit(void)
468 xt_unregister_target(&synproxy_tg4_reg
);
469 nf_unregister_hooks(ipv4_synproxy_ops
, ARRAY_SIZE(ipv4_synproxy_ops
));
472 module_init(synproxy_tg4_init
);
473 module_exit(synproxy_tg4_exit
);
475 MODULE_LICENSE("GPL");
476 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");