diff options
author | Patrick McHardy <kaber@trash.net> | 2013-08-27 02:50:16 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-08-27 18:28:13 -0400 |
commit | 4ad362282cb45bbc831a182e45637da8c5bd7aa1 (patch) | |
tree | 66d91d0ac5947985d41ff8c12cbed39fa2548e9d /net/ipv6 | |
parent | 81eb6a1487718a89621a0e0be7fafd0cd7c429a4 (diff) |
netfilter: add IPv6 SYNPROXY target
Add an IPv6 version of the SYNPROXY target. The main differences to the
IPv4 version is routing and IP header construction.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Tested-by: Martin Topholm <mph@one.com>
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/netfilter/Kconfig | 13 | ||||
-rw-r--r-- | net/ipv6/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_SYNPROXY.c | 495 |
3 files changed, 509 insertions, 0 deletions
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 4433ab40e7de..a7f842b29b67 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig | |||
@@ -153,6 +153,19 @@ config IP6_NF_TARGET_REJECT | |||
153 | 153 | ||
154 | To compile it as a module, choose M here. If unsure, say N. | 154 | To compile it as a module, choose M here. If unsure, say N. |
155 | 155 | ||
156 | config IP6_NF_TARGET_SYNPROXY | ||
157 | tristate "SYNPROXY target support" | ||
158 | depends on NF_CONNTRACK && NETFILTER_ADVANCED | ||
159 | select NETFILTER_SYNPROXY | ||
160 | select SYN_COOKIES | ||
161 | help | ||
162 | The SYNPROXY target allows you to intercept TCP connections and | ||
163 | establish them using syncookies before they are passed on to the | ||
164 | server. This allows to avoid conntrack and server resource usage | ||
165 | during SYN-flood attacks. | ||
166 | |||
167 | To compile it as a module, choose M here. If unsure, say N. | ||
168 | |||
156 | config IP6_NF_MANGLE | 169 | config IP6_NF_MANGLE |
157 | tristate "Packet mangling" | 170 | tristate "Packet mangling" |
158 | default m if NETFILTER_ADVANCED=n | 171 | default m if NETFILTER_ADVANCED=n |
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index ad13bf0d25f0..2b53738f798c 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile | |||
@@ -37,3 +37,4 @@ obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o | |||
37 | obj-$(CONFIG_IP6_NF_TARGET_MASQUERADE) += ip6t_MASQUERADE.o | 37 | obj-$(CONFIG_IP6_NF_TARGET_MASQUERADE) += ip6t_MASQUERADE.o |
38 | obj-$(CONFIG_IP6_NF_TARGET_NPT) += ip6t_NPT.o | 38 | obj-$(CONFIG_IP6_NF_TARGET_NPT) += ip6t_NPT.o |
39 | obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o | 39 | obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o |
40 | obj-$(CONFIG_IP6_NF_TARGET_SYNPROXY) += ip6t_SYNPROXY.o | ||
diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c new file mode 100644 index 000000000000..4270a9b145e5 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c | |||
@@ -0,0 +1,495 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013 Patrick McHardy <kaber@trash.net> | ||
3 | * | ||
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. | ||
7 | */ | ||
8 | |||
9 | #include <linux/module.h> | ||
10 | #include <linux/skbuff.h> | ||
11 | #include <net/ip6_checksum.h> | ||
12 | #include <net/ip6_route.h> | ||
13 | #include <net/tcp.h> | ||
14 | |||
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 | |||
22 | static struct ipv6hdr * | ||
23 | synproxy_build_ip(struct sk_buff *skb, const struct in6_addr *saddr, | ||
24 | const struct in6_addr *daddr) | ||
25 | { | ||
26 | struct ipv6hdr *iph; | ||
27 | |||
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; | ||
33 | iph->saddr = *saddr; | ||
34 | iph->daddr = *daddr; | ||
35 | |||
36 | return iph; | ||
37 | } | ||
38 | |||
39 | static void | ||
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) | ||
44 | { | ||
45 | struct net *net = nf_ct_net((struct nf_conn *)nfct); | ||
46 | struct dst_entry *dst; | ||
47 | struct flowi6 fl6; | ||
48 | |||
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); | ||
53 | |||
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) { | ||
63 | dst_release(dst); | ||
64 | goto free_nskb; | ||
65 | } | ||
66 | dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); | ||
67 | if (IS_ERR(dst)) | ||
68 | goto free_nskb; | ||
69 | |||
70 | skb_dst_set(nskb, dst); | ||
71 | |||
72 | if (nfct) { | ||
73 | nskb->nfct = nfct; | ||
74 | nskb->nfctinfo = ctinfo; | ||
75 | nf_conntrack_get(nfct); | ||
76 | } | ||
77 | |||
78 | ip6_local_out(nskb); | ||
79 | return; | ||
80 | |||
81 | free_nskb: | ||
82 | kfree_skb(nskb); | ||
83 | } | ||
84 | |||
85 | static void | ||
86 | synproxy_send_client_synack(const struct sk_buff *skb, const struct tcphdr *th, | ||
87 | const struct synproxy_options *opts) | ||
88 | { | ||
89 | struct sk_buff *nskb; | ||
90 | struct ipv6hdr *iph, *niph; | ||
91 | struct tcphdr *nth; | ||
92 | unsigned int tcp_hdr_size; | ||
93 | u16 mss = opts->mss; | ||
94 | |||
95 | iph = ipv6_hdr(skb); | ||
96 | |||
97 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
98 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
99 | GFP_ATOMIC); | ||
100 | if (nskb == NULL) | ||
101 | return; | ||
102 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
103 | |||
104 | niph = synproxy_build_ip(nskb, &iph->daddr, &iph->saddr); | ||
105 | |||
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; | ||
116 | nth->window = 0; | ||
117 | nth->check = 0; | ||
118 | nth->urg_ptr = 0; | ||
119 | |||
120 | synproxy_build_options(nth, opts); | ||
121 | |||
122 | synproxy_send_tcp(skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY, | ||
123 | niph, nth, tcp_hdr_size); | ||
124 | } | ||
125 | |||
126 | static void | ||
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) | ||
130 | { | ||
131 | struct sk_buff *nskb; | ||
132 | struct ipv6hdr *iph, *niph; | ||
133 | struct tcphdr *nth; | ||
134 | unsigned int tcp_hdr_size; | ||
135 | |||
136 | iph = ipv6_hdr(skb); | ||
137 | |||
138 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
139 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
140 | GFP_ATOMIC); | ||
141 | if (nskb == NULL) | ||
142 | return; | ||
143 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
144 | |||
145 | niph = synproxy_build_ip(nskb, &iph->saddr, &iph->daddr); | ||
146 | |||
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. | ||
154 | */ | ||
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; | ||
161 | nth->check = 0; | ||
162 | nth->urg_ptr = 0; | ||
163 | |||
164 | synproxy_build_options(nth, opts); | ||
165 | |||
166 | synproxy_send_tcp(skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW, | ||
167 | niph, nth, tcp_hdr_size); | ||
168 | } | ||
169 | |||
170 | static void | ||
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) | ||
175 | { | ||
176 | struct sk_buff *nskb; | ||
177 | struct ipv6hdr *iph, *niph; | ||
178 | struct tcphdr *nth; | ||
179 | unsigned int tcp_hdr_size; | ||
180 | |||
181 | iph = ipv6_hdr(skb); | ||
182 | |||
183 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
184 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
185 | GFP_ATOMIC); | ||
186 | if (nskb == NULL) | ||
187 | return; | ||
188 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
189 | |||
190 | niph = synproxy_build_ip(nskb, &iph->daddr, &iph->saddr); | ||
191 | |||
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); | ||
201 | nth->check = 0; | ||
202 | nth->urg_ptr = 0; | ||
203 | |||
204 | synproxy_build_options(nth, opts); | ||
205 | |||
206 | synproxy_send_tcp(skb, nskb, NULL, 0, niph, nth, tcp_hdr_size); | ||
207 | } | ||
208 | |||
209 | static void | ||
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) | ||
213 | { | ||
214 | struct sk_buff *nskb; | ||
215 | struct ipv6hdr *iph, *niph; | ||
216 | struct tcphdr *nth; | ||
217 | unsigned int tcp_hdr_size; | ||
218 | |||
219 | iph = ipv6_hdr(skb); | ||
220 | |||
221 | tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); | ||
222 | nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, | ||
223 | GFP_ATOMIC); | ||
224 | if (nskb == NULL) | ||
225 | return; | ||
226 | skb_reserve(nskb, MAX_TCP_HEADER); | ||
227 | |||
228 | niph = synproxy_build_ip(nskb, &iph->saddr, &iph->daddr); | ||
229 | |||
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); | ||
239 | nth->check = 0; | ||
240 | nth->urg_ptr = 0; | ||
241 | |||
242 | synproxy_build_options(nth, opts); | ||
243 | |||
244 | synproxy_send_tcp(skb, nskb, NULL, 0, niph, nth, tcp_hdr_size); | ||
245 | } | ||
246 | |||
247 | static bool | ||
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) | ||
251 | { | ||
252 | int mss; | ||
253 | |||
254 | mss = __cookie_v6_check(ipv6_hdr(skb), th, ntohl(th->ack_seq) - 1); | ||
255 | if (mss == 0) { | ||
256 | this_cpu_inc(snet->stats->cookie_invalid); | ||
257 | return false; | ||
258 | } | ||
259 | |||
260 | this_cpu_inc(snet->stats->cookie_valid); | ||
261 | opts->mss = mss; | ||
262 | |||
263 | if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP) | ||
264 | synproxy_check_timestamp_cookie(opts); | ||
265 | |||
266 | synproxy_send_server_syn(snet, skb, th, opts, recv_seq); | ||
267 | return true; | ||
268 | } | ||
269 | |||
270 | static unsigned int | ||
271 | synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par) | ||
272 | { | ||
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; | ||
277 | |||
278 | if (nf_ip6_checksum(skb, par->hooknum, par->thoff, IPPROTO_TCP)) | ||
279 | return NF_DROP; | ||
280 | |||
281 | th = skb_header_pointer(skb, par->thoff, sizeof(_th), &_th); | ||
282 | if (th == NULL) | ||
283 | return NF_DROP; | ||
284 | |||
285 | synproxy_parse_options(skb, par->thoff, th, &opts); | ||
286 | |||
287 | if (th->syn) { | ||
288 | /* Initial SYN from client */ | ||
289 | this_cpu_inc(snet->stats->syn_received); | ||
290 | |||
291 | if (th->ece && th->cwr) | ||
292 | opts.options |= XT_SYNPROXY_OPT_ECN; | ||
293 | |||
294 | opts.options &= info->options; | ||
295 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) | ||
296 | synproxy_init_timestamp_cookie(info, &opts); | ||
297 | else | ||
298 | opts.options &= ~(XT_SYNPROXY_OPT_WSCALE | | ||
299 | XT_SYNPROXY_OPT_SACK_PERM | | ||
300 | XT_SYNPROXY_OPT_ECN); | ||
301 | |||
302 | synproxy_send_client_synack(skb, th, &opts); | ||
303 | } else if (th->ack && !(th->fin || th->rst)) | ||
304 | /* ACK from client */ | ||
305 | synproxy_recv_client_ack(snet, skb, th, &opts, ntohl(th->seq)); | ||
306 | |||
307 | return NF_DROP; | ||
308 | } | ||
309 | |||
310 | static unsigned int ipv6_synproxy_hook(unsigned int hooknum, | ||
311 | struct sk_buff *skb, | ||
312 | const struct net_device *in, | ||
313 | const struct net_device *out, | ||
314 | int (*okfn)(struct sk_buff *)) | ||
315 | { | ||
316 | struct synproxy_net *snet = synproxy_pernet(dev_net(in ? : out)); | ||
317 | enum ip_conntrack_info ctinfo; | ||
318 | struct nf_conn *ct; | ||
319 | struct nf_conn_synproxy *synproxy; | ||
320 | struct synproxy_options opts = {}; | ||
321 | const struct ip_ct_tcp *state; | ||
322 | struct tcphdr *th, _th; | ||
323 | __be16 frag_off; | ||
324 | u8 nexthdr; | ||
325 | int thoff; | ||
326 | |||
327 | ct = nf_ct_get(skb, &ctinfo); | ||
328 | if (ct == NULL) | ||
329 | return NF_ACCEPT; | ||
330 | |||
331 | synproxy = nfct_synproxy(ct); | ||
332 | if (synproxy == NULL) | ||
333 | return NF_ACCEPT; | ||
334 | |||
335 | if (nf_is_loopback_packet(skb)) | ||
336 | return NF_ACCEPT; | ||
337 | |||
338 | nexthdr = ipv6_hdr(skb)->nexthdr; | ||
339 | thoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, | ||
340 | &frag_off); | ||
341 | if (thoff < 0) | ||
342 | return NF_ACCEPT; | ||
343 | |||
344 | th = skb_header_pointer(skb, thoff, sizeof(_th), &_th); | ||
345 | if (th == NULL) | ||
346 | return NF_DROP; | ||
347 | |||
348 | state = &ct->proto.tcp; | ||
349 | switch (state->state) { | ||
350 | case TCP_CONNTRACK_CLOSE: | ||
351 | if (th->rst && !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { | ||
352 | nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - | ||
353 | ntohl(th->seq) + 1); | ||
354 | break; | ||
355 | } | ||
356 | |||
357 | if (!th->syn || th->ack || | ||
358 | CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) | ||
359 | break; | ||
360 | |||
361 | /* Reopened connection - reset the sequence number and timestamp | ||
362 | * adjustments, they will get initialized once the connection is | ||
363 | * reestablished. | ||
364 | */ | ||
365 | nf_ct_seqadj_init(ct, ctinfo, 0); | ||
366 | synproxy->tsoff = 0; | ||
367 | this_cpu_inc(snet->stats->conn_reopened); | ||
368 | |||
369 | /* fall through */ | ||
370 | case TCP_CONNTRACK_SYN_SENT: | ||
371 | synproxy_parse_options(skb, thoff, th, &opts); | ||
372 | |||
373 | if (!th->syn && th->ack && | ||
374 | CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { | ||
375 | /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1, | ||
376 | * therefore we need to add 1 to make the SYN sequence | ||
377 | * number match the one of first SYN. | ||
378 | */ | ||
379 | if (synproxy_recv_client_ack(snet, skb, th, &opts, | ||
380 | ntohl(th->seq) + 1)) | ||
381 | this_cpu_inc(snet->stats->cookie_retrans); | ||
382 | |||
383 | return NF_DROP; | ||
384 | } | ||
385 | |||
386 | synproxy->isn = ntohl(th->ack_seq); | ||
387 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) | ||
388 | synproxy->its = opts.tsecr; | ||
389 | break; | ||
390 | case TCP_CONNTRACK_SYN_RECV: | ||
391 | if (!th->syn || !th->ack) | ||
392 | break; | ||
393 | |||
394 | synproxy_parse_options(skb, thoff, th, &opts); | ||
395 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) | ||
396 | synproxy->tsoff = opts.tsval - synproxy->its; | ||
397 | |||
398 | opts.options &= ~(XT_SYNPROXY_OPT_MSS | | ||
399 | XT_SYNPROXY_OPT_WSCALE | | ||
400 | XT_SYNPROXY_OPT_SACK_PERM); | ||
401 | |||
402 | swap(opts.tsval, opts.tsecr); | ||
403 | synproxy_send_server_ack(snet, state, skb, th, &opts); | ||
404 | |||
405 | nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq)); | ||
406 | |||
407 | swap(opts.tsval, opts.tsecr); | ||
408 | synproxy_send_client_ack(snet, skb, th, &opts); | ||
409 | |||
410 | consume_skb(skb); | ||
411 | return NF_STOLEN; | ||
412 | default: | ||
413 | break; | ||
414 | } | ||
415 | |||
416 | synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); | ||
417 | return NF_ACCEPT; | ||
418 | } | ||
419 | |||
420 | static int synproxy_tg6_check(const struct xt_tgchk_param *par) | ||
421 | { | ||
422 | const struct ip6t_entry *e = par->entryinfo; | ||
423 | |||
424 | if (!(e->ipv6.flags & IP6T_F_PROTO) || | ||
425 | e->ipv6.proto != IPPROTO_TCP || | ||
426 | e->ipv6.invflags & XT_INV_PROTO) | ||
427 | return -EINVAL; | ||
428 | |||
429 | return nf_ct_l3proto_try_module_get(par->family); | ||
430 | } | ||
431 | |||
432 | static void synproxy_tg6_destroy(const struct xt_tgdtor_param *par) | ||
433 | { | ||
434 | nf_ct_l3proto_module_put(par->family); | ||
435 | } | ||
436 | |||
437 | static struct xt_target synproxy_tg6_reg __read_mostly = { | ||
438 | .name = "SYNPROXY", | ||
439 | .family = NFPROTO_IPV6, | ||
440 | .target = synproxy_tg6, | ||
441 | .targetsize = sizeof(struct xt_synproxy_info), | ||
442 | .checkentry = synproxy_tg6_check, | ||
443 | .destroy = synproxy_tg6_destroy, | ||
444 | .me = THIS_MODULE, | ||
445 | }; | ||
446 | |||
447 | static struct nf_hook_ops ipv6_synproxy_ops[] __read_mostly = { | ||
448 | { | ||
449 | .hook = ipv6_synproxy_hook, | ||
450 | .owner = THIS_MODULE, | ||
451 | .pf = NFPROTO_IPV6, | ||
452 | .hooknum = NF_INET_LOCAL_IN, | ||
453 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, | ||
454 | }, | ||
455 | { | ||
456 | .hook = ipv6_synproxy_hook, | ||
457 | .owner = THIS_MODULE, | ||
458 | .pf = NFPROTO_IPV6, | ||
459 | .hooknum = NF_INET_POST_ROUTING, | ||
460 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, | ||
461 | }, | ||
462 | }; | ||
463 | |||
464 | static int __init synproxy_tg6_init(void) | ||
465 | { | ||
466 | int err; | ||
467 | |||
468 | err = nf_register_hooks(ipv6_synproxy_ops, | ||
469 | ARRAY_SIZE(ipv6_synproxy_ops)); | ||
470 | if (err < 0) | ||
471 | goto err1; | ||
472 | |||
473 | err = xt_register_target(&synproxy_tg6_reg); | ||
474 | if (err < 0) | ||
475 | goto err2; | ||
476 | |||
477 | return 0; | ||
478 | |||
479 | err2: | ||
480 | nf_unregister_hooks(ipv6_synproxy_ops, ARRAY_SIZE(ipv6_synproxy_ops)); | ||
481 | err1: | ||
482 | return err; | ||
483 | } | ||
484 | |||
485 | static void __exit synproxy_tg6_exit(void) | ||
486 | { | ||
487 | xt_unregister_target(&synproxy_tg6_reg); | ||
488 | nf_unregister_hooks(ipv6_synproxy_ops, ARRAY_SIZE(ipv6_synproxy_ops)); | ||
489 | } | ||
490 | |||
491 | module_init(synproxy_tg6_init); | ||
492 | module_exit(synproxy_tg6_exit); | ||
493 | |||
494 | MODULE_LICENSE("GPL"); | ||
495 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | ||