diff options
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/ip6_input.c | 5 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 6 | ||||
-rw-r--r-- | net/ipv6/netfilter/Kconfig | 14 | ||||
-rw-r--r-- | net/ipv6/netfilter/Makefile | 6 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_MARK.c | 6 | ||||
-rw-r--r-- | net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 556 | ||||
-rw-r--r-- | net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 272 | ||||
-rw-r--r-- | net/ipv6/netfilter/nf_conntrack_reasm.c | 885 | ||||
-rw-r--r-- | net/ipv6/raw.c | 4 |
9 files changed, 1750 insertions, 4 deletions
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 6e3480426939..a6026d2787d2 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c | |||
@@ -176,6 +176,11 @@ resubmit: | |||
176 | if (ipprot->flags & INET6_PROTO_FINAL) { | 176 | if (ipprot->flags & INET6_PROTO_FINAL) { |
177 | struct ipv6hdr *hdr; | 177 | struct ipv6hdr *hdr; |
178 | 178 | ||
179 | /* Free reference early: we don't need it any more, | ||
180 | and it may hold ip_conntrack module loaded | ||
181 | indefinitely. */ | ||
182 | nf_reset(skb); | ||
183 | |||
179 | skb_postpull_rcsum(skb, skb->nh.raw, | 184 | skb_postpull_rcsum(skb, skb->nh.raw, |
180 | skb->h.raw - skb->nh.raw); | 185 | skb->h.raw - skb->nh.raw); |
181 | hdr = skb->nh.ipv6h; | 186 | hdr = skb->nh.ipv6h; |
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index dbd9767b32e4..c1fa693511a1 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -441,9 +441,15 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from) | |||
441 | #ifdef CONFIG_NETFILTER | 441 | #ifdef CONFIG_NETFILTER |
442 | to->nfmark = from->nfmark; | 442 | to->nfmark = from->nfmark; |
443 | /* Connection association is same as pre-frag packet */ | 443 | /* Connection association is same as pre-frag packet */ |
444 | nf_conntrack_put(to->nfct); | ||
444 | to->nfct = from->nfct; | 445 | to->nfct = from->nfct; |
445 | nf_conntrack_get(to->nfct); | 446 | nf_conntrack_get(to->nfct); |
446 | to->nfctinfo = from->nfctinfo; | 447 | to->nfctinfo = from->nfctinfo; |
448 | #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) | ||
449 | nf_conntrack_put_reasm(to->nfct_reasm); | ||
450 | to->nfct_reasm = from->nfct_reasm; | ||
451 | nf_conntrack_get_reasm(to->nfct_reasm); | ||
452 | #endif | ||
447 | #ifdef CONFIG_BRIDGE_NETFILTER | 453 | #ifdef CONFIG_BRIDGE_NETFILTER |
448 | nf_bridge_put(to->nf_bridge); | 454 | nf_bridge_put(to->nf_bridge); |
449 | to->nf_bridge = from->nf_bridge; | 455 | to->nf_bridge = from->nf_bridge; |
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index bb7ccfe33f23..971ba60bf6e9 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig | |||
@@ -278,5 +278,19 @@ config IP6_NF_RAW | |||
278 | If you want to compile it as a module, say M here and read | 278 | If you want to compile it as a module, say M here and read |
279 | <file:Documentation/modules.txt>. If unsure, say `N'. | 279 | <file:Documentation/modules.txt>. If unsure, say `N'. |
280 | 280 | ||
281 | config NF_CONNTRACK_IPV6 | ||
282 | tristate "IPv6 support for new connection tracking (EXPERIMENTAL)" | ||
283 | depends on EXPERIMENTAL && NF_CONNTRACK | ||
284 | ---help--- | ||
285 | Connection tracking keeps a record of what packets have passed | ||
286 | through your machine, in order to figure out how they are related | ||
287 | into connections. | ||
288 | |||
289 | This is IPv6 support on Layer 3 independent connection tracking. | ||
290 | Layer 3 independent connection tracking is experimental scheme | ||
291 | which generalize ip_conntrack to support other layer 3 protocols. | ||
292 | |||
293 | To compile it as a module, choose M here. If unsure, say N. | ||
294 | |||
281 | endmenu | 295 | endmenu |
282 | 296 | ||
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 2b2c370e8b1c..9ab5b2ca1f59 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile | |||
@@ -27,3 +27,9 @@ obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o | |||
27 | obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o | 27 | obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o |
28 | obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o | 28 | obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o |
29 | obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o | 29 | obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o |
30 | |||
31 | # objects for l3 independent conntrack | ||
32 | nf_conntrack_ipv6-objs := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o nf_conntrack_reasm.o | ||
33 | |||
34 | # l3 independent conntrack | ||
35 | obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o | ||
diff --git a/net/ipv6/netfilter/ip6t_MARK.c b/net/ipv6/netfilter/ip6t_MARK.c index 0c7584f92172..eab8fb864ee0 100644 --- a/net/ipv6/netfilter/ip6t_MARK.c +++ b/net/ipv6/netfilter/ip6t_MARK.c | |||
@@ -56,9 +56,9 @@ checkentry(const char *tablename, | |||
56 | return 1; | 56 | return 1; |
57 | } | 57 | } |
58 | 58 | ||
59 | static struct ip6t_target ip6t_mark_reg = { | 59 | static struct ip6t_target ip6t_mark_reg = { |
60 | .name = "MARK", | 60 | .name = "MARK", |
61 | .target = target, | 61 | .target = target, |
62 | .checkentry = checkentry, | 62 | .checkentry = checkentry, |
63 | .me = THIS_MODULE | 63 | .me = THIS_MODULE |
64 | }; | 64 | }; |
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c new file mode 100644 index 000000000000..e2c90b3a8074 --- /dev/null +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | |||
@@ -0,0 +1,556 @@ | |||
1 | /* | ||
2 | * Copyright (C)2004 USAGI/WIDE Project | ||
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 | * Author: | ||
9 | * Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | ||
10 | * | ||
11 | * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | ||
12 | * - support Layer 3 protocol independent connection tracking. | ||
13 | * Based on the original ip_conntrack code which had the following | ||
14 | * copyright information: | ||
15 | * (C) 1999-2001 Paul `Rusty' Russell | ||
16 | * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> | ||
17 | * | ||
18 | * 23 Mar 2004: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | ||
19 | * - add get_features() to support various size of conntrack | ||
20 | * structures. | ||
21 | */ | ||
22 | |||
23 | #include <linux/config.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <linux/ipv6.h> | ||
26 | #include <linux/in6.h> | ||
27 | #include <linux/netfilter.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/skbuff.h> | ||
30 | #include <linux/icmp.h> | ||
31 | #include <linux/sysctl.h> | ||
32 | #include <net/ipv6.h> | ||
33 | |||
34 | #include <linux/netfilter_ipv6.h> | ||
35 | #include <net/netfilter/nf_conntrack.h> | ||
36 | #include <net/netfilter/nf_conntrack_helper.h> | ||
37 | #include <net/netfilter/nf_conntrack_protocol.h> | ||
38 | #include <net/netfilter/nf_conntrack_l3proto.h> | ||
39 | #include <net/netfilter/nf_conntrack_core.h> | ||
40 | |||
41 | #if 0 | ||
42 | #define DEBUGP printk | ||
43 | #else | ||
44 | #define DEBUGP(format, args...) | ||
45 | #endif | ||
46 | |||
47 | DECLARE_PER_CPU(struct ip_conntrack_stat, nf_conntrack_stat); | ||
48 | |||
49 | static int ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, | ||
50 | struct nf_conntrack_tuple *tuple) | ||
51 | { | ||
52 | u_int32_t _addrs[8], *ap; | ||
53 | |||
54 | ap = skb_header_pointer(skb, nhoff + offsetof(struct ipv6hdr, saddr), | ||
55 | sizeof(_addrs), _addrs); | ||
56 | if (ap == NULL) | ||
57 | return 0; | ||
58 | |||
59 | memcpy(tuple->src.u3.ip6, ap, sizeof(tuple->src.u3.ip6)); | ||
60 | memcpy(tuple->dst.u3.ip6, ap + 4, sizeof(tuple->dst.u3.ip6)); | ||
61 | |||
62 | return 1; | ||
63 | } | ||
64 | |||
65 | static int ipv6_invert_tuple(struct nf_conntrack_tuple *tuple, | ||
66 | const struct nf_conntrack_tuple *orig) | ||
67 | { | ||
68 | memcpy(tuple->src.u3.ip6, orig->dst.u3.ip6, sizeof(tuple->src.u3.ip6)); | ||
69 | memcpy(tuple->dst.u3.ip6, orig->src.u3.ip6, sizeof(tuple->dst.u3.ip6)); | ||
70 | |||
71 | return 1; | ||
72 | } | ||
73 | |||
74 | static int ipv6_print_tuple(struct seq_file *s, | ||
75 | const struct nf_conntrack_tuple *tuple) | ||
76 | { | ||
77 | return seq_printf(s, "src=%x:%x:%x:%x:%x:%x:%x:%x dst=%x:%x:%x:%x:%x:%x:%x:%x ", | ||
78 | NIP6(*((struct in6_addr *)tuple->src.u3.ip6)), | ||
79 | NIP6(*((struct in6_addr *)tuple->dst.u3.ip6))); | ||
80 | } | ||
81 | |||
82 | static int ipv6_print_conntrack(struct seq_file *s, | ||
83 | const struct nf_conn *conntrack) | ||
84 | { | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | /* | ||
89 | * Based on ipv6_skip_exthdr() in net/ipv6/exthdr.c | ||
90 | * | ||
91 | * This function parses (probably truncated) exthdr set "hdr" | ||
92 | * of length "len". "nexthdrp" initially points to some place, | ||
93 | * where type of the first header can be found. | ||
94 | * | ||
95 | * It skips all well-known exthdrs, and returns pointer to the start | ||
96 | * of unparsable area i.e. the first header with unknown type. | ||
97 | * if success, *nexthdr is updated by type/protocol of this header. | ||
98 | * | ||
99 | * NOTES: - it may return pointer pointing beyond end of packet, | ||
100 | * if the last recognized header is truncated in the middle. | ||
101 | * - if packet is truncated, so that all parsed headers are skipped, | ||
102 | * it returns -1. | ||
103 | * - if packet is fragmented, return pointer of the fragment header. | ||
104 | * - ESP is unparsable for now and considered like | ||
105 | * normal payload protocol. | ||
106 | * - Note also special handling of AUTH header. Thanks to IPsec wizards. | ||
107 | */ | ||
108 | |||
109 | int nf_ct_ipv6_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp, | ||
110 | int len) | ||
111 | { | ||
112 | u8 nexthdr = *nexthdrp; | ||
113 | |||
114 | while (ipv6_ext_hdr(nexthdr)) { | ||
115 | struct ipv6_opt_hdr hdr; | ||
116 | int hdrlen; | ||
117 | |||
118 | if (len < (int)sizeof(struct ipv6_opt_hdr)) | ||
119 | return -1; | ||
120 | if (nexthdr == NEXTHDR_NONE) | ||
121 | break; | ||
122 | if (nexthdr == NEXTHDR_FRAGMENT) | ||
123 | break; | ||
124 | if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) | ||
125 | BUG(); | ||
126 | if (nexthdr == NEXTHDR_AUTH) | ||
127 | hdrlen = (hdr.hdrlen+2)<<2; | ||
128 | else | ||
129 | hdrlen = ipv6_optlen(&hdr); | ||
130 | |||
131 | nexthdr = hdr.nexthdr; | ||
132 | len -= hdrlen; | ||
133 | start += hdrlen; | ||
134 | } | ||
135 | |||
136 | *nexthdrp = nexthdr; | ||
137 | return start; | ||
138 | } | ||
139 | |||
140 | static int | ||
141 | ipv6_prepare(struct sk_buff **pskb, unsigned int hooknum, unsigned int *dataoff, | ||
142 | u_int8_t *protonum) | ||
143 | { | ||
144 | unsigned int extoff; | ||
145 | unsigned char pnum; | ||
146 | int protoff; | ||
147 | |||
148 | extoff = (u8*)((*pskb)->nh.ipv6h + 1) - (*pskb)->data; | ||
149 | pnum = (*pskb)->nh.ipv6h->nexthdr; | ||
150 | |||
151 | protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum, | ||
152 | (*pskb)->len - extoff); | ||
153 | |||
154 | /* | ||
155 | * (protoff == (*pskb)->len) mean that the packet doesn't have no data | ||
156 | * except of IPv6 & ext headers. but it's tracked anyway. - YK | ||
157 | */ | ||
158 | if ((protoff < 0) || (protoff > (*pskb)->len)) { | ||
159 | DEBUGP("ip6_conntrack_core: can't find proto in pkt\n"); | ||
160 | NF_CT_STAT_INC(error); | ||
161 | NF_CT_STAT_INC(invalid); | ||
162 | return -NF_ACCEPT; | ||
163 | } | ||
164 | |||
165 | *dataoff = protoff; | ||
166 | *protonum = pnum; | ||
167 | return NF_ACCEPT; | ||
168 | } | ||
169 | |||
170 | static u_int32_t ipv6_get_features(const struct nf_conntrack_tuple *tuple) | ||
171 | { | ||
172 | return NF_CT_F_BASIC; | ||
173 | } | ||
174 | |||
175 | static unsigned int ipv6_confirm(unsigned int hooknum, | ||
176 | struct sk_buff **pskb, | ||
177 | const struct net_device *in, | ||
178 | const struct net_device *out, | ||
179 | int (*okfn)(struct sk_buff *)) | ||
180 | { | ||
181 | struct nf_conn *ct; | ||
182 | enum ip_conntrack_info ctinfo; | ||
183 | |||
184 | /* This is where we call the helper: as the packet goes out. */ | ||
185 | ct = nf_ct_get(*pskb, &ctinfo); | ||
186 | if (ct && ct->helper) { | ||
187 | unsigned int ret, protoff; | ||
188 | unsigned int extoff = (u8*)((*pskb)->nh.ipv6h + 1) | ||
189 | - (*pskb)->data; | ||
190 | unsigned char pnum = (*pskb)->nh.ipv6h->nexthdr; | ||
191 | |||
192 | protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum, | ||
193 | (*pskb)->len - extoff); | ||
194 | if (protoff < 0 || protoff > (*pskb)->len || | ||
195 | pnum == NEXTHDR_FRAGMENT) { | ||
196 | DEBUGP("proto header not found\n"); | ||
197 | return NF_ACCEPT; | ||
198 | } | ||
199 | |||
200 | ret = ct->helper->help(pskb, protoff, ct, ctinfo); | ||
201 | if (ret != NF_ACCEPT) | ||
202 | return ret; | ||
203 | } | ||
204 | |||
205 | /* We've seen it coming out the other side: confirm it */ | ||
206 | |||
207 | return nf_conntrack_confirm(pskb); | ||
208 | } | ||
209 | |||
210 | extern struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb); | ||
211 | extern void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb, | ||
212 | struct net_device *in, | ||
213 | struct net_device *out, | ||
214 | int (*okfn)(struct sk_buff *)); | ||
215 | static unsigned int ipv6_defrag(unsigned int hooknum, | ||
216 | struct sk_buff **pskb, | ||
217 | const struct net_device *in, | ||
218 | const struct net_device *out, | ||
219 | int (*okfn)(struct sk_buff *)) | ||
220 | { | ||
221 | struct sk_buff *reasm; | ||
222 | |||
223 | /* Previously seen (loopback)? */ | ||
224 | if ((*pskb)->nfct) | ||
225 | return NF_ACCEPT; | ||
226 | |||
227 | reasm = nf_ct_frag6_gather(*pskb); | ||
228 | |||
229 | /* queued */ | ||
230 | if (reasm == NULL) | ||
231 | return NF_STOLEN; | ||
232 | |||
233 | /* error occured or not fragmented */ | ||
234 | if (reasm == *pskb) | ||
235 | return NF_ACCEPT; | ||
236 | |||
237 | nf_ct_frag6_output(hooknum, reasm, (struct net_device *)in, | ||
238 | (struct net_device *)out, okfn); | ||
239 | |||
240 | return NF_STOLEN; | ||
241 | } | ||
242 | |||
243 | static unsigned int ipv6_conntrack_in(unsigned int hooknum, | ||
244 | struct sk_buff **pskb, | ||
245 | const struct net_device *in, | ||
246 | const struct net_device *out, | ||
247 | int (*okfn)(struct sk_buff *)) | ||
248 | { | ||
249 | struct sk_buff *reasm = (*pskb)->nfct_reasm; | ||
250 | |||
251 | /* This packet is fragmented and has reassembled packet. */ | ||
252 | if (reasm) { | ||
253 | /* Reassembled packet isn't parsed yet ? */ | ||
254 | if (!reasm->nfct) { | ||
255 | unsigned int ret; | ||
256 | |||
257 | ret = nf_conntrack_in(PF_INET6, hooknum, &reasm); | ||
258 | if (ret != NF_ACCEPT) | ||
259 | return ret; | ||
260 | } | ||
261 | nf_conntrack_get(reasm->nfct); | ||
262 | (*pskb)->nfct = reasm->nfct; | ||
263 | return NF_ACCEPT; | ||
264 | } | ||
265 | |||
266 | return nf_conntrack_in(PF_INET6, hooknum, pskb); | ||
267 | } | ||
268 | |||
269 | static unsigned int ipv6_conntrack_local(unsigned int hooknum, | ||
270 | struct sk_buff **pskb, | ||
271 | const struct net_device *in, | ||
272 | const struct net_device *out, | ||
273 | int (*okfn)(struct sk_buff *)) | ||
274 | { | ||
275 | /* root is playing with raw sockets. */ | ||
276 | if ((*pskb)->len < sizeof(struct ipv6hdr)) { | ||
277 | if (net_ratelimit()) | ||
278 | printk("ipv6_conntrack_local: packet too short\n"); | ||
279 | return NF_ACCEPT; | ||
280 | } | ||
281 | return ipv6_conntrack_in(hooknum, pskb, in, out, okfn); | ||
282 | } | ||
283 | |||
284 | /* Connection tracking may drop packets, but never alters them, so | ||
285 | make it the first hook. */ | ||
286 | static struct nf_hook_ops ipv6_conntrack_defrag_ops = { | ||
287 | .hook = ipv6_defrag, | ||
288 | .owner = THIS_MODULE, | ||
289 | .pf = PF_INET6, | ||
290 | .hooknum = NF_IP6_PRE_ROUTING, | ||
291 | .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, | ||
292 | }; | ||
293 | |||
294 | static struct nf_hook_ops ipv6_conntrack_in_ops = { | ||
295 | .hook = ipv6_conntrack_in, | ||
296 | .owner = THIS_MODULE, | ||
297 | .pf = PF_INET6, | ||
298 | .hooknum = NF_IP6_PRE_ROUTING, | ||
299 | .priority = NF_IP6_PRI_CONNTRACK, | ||
300 | }; | ||
301 | |||
302 | static struct nf_hook_ops ipv6_conntrack_local_out_ops = { | ||
303 | .hook = ipv6_conntrack_local, | ||
304 | .owner = THIS_MODULE, | ||
305 | .pf = PF_INET6, | ||
306 | .hooknum = NF_IP6_LOCAL_OUT, | ||
307 | .priority = NF_IP6_PRI_CONNTRACK, | ||
308 | }; | ||
309 | |||
310 | static struct nf_hook_ops ipv6_conntrack_defrag_local_out_ops = { | ||
311 | .hook = ipv6_defrag, | ||
312 | .owner = THIS_MODULE, | ||
313 | .pf = PF_INET6, | ||
314 | .hooknum = NF_IP6_LOCAL_OUT, | ||
315 | .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, | ||
316 | }; | ||
317 | |||
318 | /* Refragmenter; last chance. */ | ||
319 | static struct nf_hook_ops ipv6_conntrack_out_ops = { | ||
320 | .hook = ipv6_confirm, | ||
321 | .owner = THIS_MODULE, | ||
322 | .pf = PF_INET6, | ||
323 | .hooknum = NF_IP6_POST_ROUTING, | ||
324 | .priority = NF_IP6_PRI_LAST, | ||
325 | }; | ||
326 | |||
327 | static struct nf_hook_ops ipv6_conntrack_local_in_ops = { | ||
328 | .hook = ipv6_confirm, | ||
329 | .owner = THIS_MODULE, | ||
330 | .pf = PF_INET6, | ||
331 | .hooknum = NF_IP6_LOCAL_IN, | ||
332 | .priority = NF_IP6_PRI_LAST-1, | ||
333 | }; | ||
334 | |||
335 | #ifdef CONFIG_SYSCTL | ||
336 | |||
337 | /* From nf_conntrack_proto_icmpv6.c */ | ||
338 | extern unsigned long nf_ct_icmpv6_timeout; | ||
339 | |||
340 | /* From nf_conntrack_frag6.c */ | ||
341 | extern unsigned long nf_ct_frag6_timeout; | ||
342 | extern unsigned long nf_ct_frag6_low_thresh; | ||
343 | extern unsigned long nf_ct_frag6_high_thresh; | ||
344 | |||
345 | static struct ctl_table_header *nf_ct_ipv6_sysctl_header; | ||
346 | |||
347 | static ctl_table nf_ct_sysctl_table[] = { | ||
348 | { | ||
349 | .ctl_name = NET_NF_CONNTRACK_ICMPV6_TIMEOUT, | ||
350 | .procname = "nf_conntrack_icmpv6_timeout", | ||
351 | .data = &nf_ct_icmpv6_timeout, | ||
352 | .maxlen = sizeof(unsigned int), | ||
353 | .mode = 0644, | ||
354 | .proc_handler = &proc_dointvec_jiffies, | ||
355 | }, | ||
356 | { | ||
357 | .ctl_name = NET_NF_CONNTRACK_FRAG6_TIMEOUT, | ||
358 | .procname = "nf_conntrack_frag6_timeout", | ||
359 | .data = &nf_ct_frag6_timeout, | ||
360 | .maxlen = sizeof(unsigned int), | ||
361 | .mode = 0644, | ||
362 | .proc_handler = &proc_dointvec_jiffies, | ||
363 | }, | ||
364 | { | ||
365 | .ctl_name = NET_NF_CONNTRACK_FRAG6_LOW_THRESH, | ||
366 | .procname = "nf_conntrack_frag6_low_thresh", | ||
367 | .data = &nf_ct_frag6_low_thresh, | ||
368 | .maxlen = sizeof(unsigned int), | ||
369 | .mode = 0644, | ||
370 | .proc_handler = &proc_dointvec_jiffies, | ||
371 | }, | ||
372 | { | ||
373 | .ctl_name = NET_NF_CONNTRACK_FRAG6_HIGH_THRESH, | ||
374 | .procname = "nf_conntrack_frag6_high_thresh", | ||
375 | .data = &nf_ct_frag6_high_thresh, | ||
376 | .maxlen = sizeof(unsigned int), | ||
377 | .mode = 0644, | ||
378 | .proc_handler = &proc_dointvec_jiffies, | ||
379 | }, | ||
380 | { .ctl_name = 0 } | ||
381 | }; | ||
382 | |||
383 | static ctl_table nf_ct_netfilter_table[] = { | ||
384 | { | ||
385 | .ctl_name = NET_NETFILTER, | ||
386 | .procname = "netfilter", | ||
387 | .mode = 0555, | ||
388 | .child = nf_ct_sysctl_table, | ||
389 | }, | ||
390 | { .ctl_name = 0 } | ||
391 | }; | ||
392 | |||
393 | static ctl_table nf_ct_net_table[] = { | ||
394 | { | ||
395 | .ctl_name = CTL_NET, | ||
396 | .procname = "net", | ||
397 | .mode = 0555, | ||
398 | .child = nf_ct_netfilter_table, | ||
399 | }, | ||
400 | { .ctl_name = 0 } | ||
401 | }; | ||
402 | #endif | ||
403 | |||
404 | struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 = { | ||
405 | .l3proto = PF_INET6, | ||
406 | .name = "ipv6", | ||
407 | .pkt_to_tuple = ipv6_pkt_to_tuple, | ||
408 | .invert_tuple = ipv6_invert_tuple, | ||
409 | .print_tuple = ipv6_print_tuple, | ||
410 | .print_conntrack = ipv6_print_conntrack, | ||
411 | .prepare = ipv6_prepare, | ||
412 | .get_features = ipv6_get_features, | ||
413 | .me = THIS_MODULE, | ||
414 | }; | ||
415 | |||
416 | extern struct nf_conntrack_protocol nf_conntrack_protocol_tcp6; | ||
417 | extern struct nf_conntrack_protocol nf_conntrack_protocol_udp6; | ||
418 | extern struct nf_conntrack_protocol nf_conntrack_protocol_icmpv6; | ||
419 | extern int nf_ct_frag6_init(void); | ||
420 | extern void nf_ct_frag6_cleanup(void); | ||
421 | static int init_or_cleanup(int init) | ||
422 | { | ||
423 | int ret = 0; | ||
424 | |||
425 | if (!init) goto cleanup; | ||
426 | |||
427 | ret = nf_ct_frag6_init(); | ||
428 | if (ret < 0) { | ||
429 | printk("nf_conntrack_ipv6: can't initialize frag6.\n"); | ||
430 | goto cleanup_nothing; | ||
431 | } | ||
432 | ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_tcp6); | ||
433 | if (ret < 0) { | ||
434 | printk("nf_conntrack_ipv6: can't register tcp.\n"); | ||
435 | goto cleanup_frag6; | ||
436 | } | ||
437 | |||
438 | ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_udp6); | ||
439 | if (ret < 0) { | ||
440 | printk("nf_conntrack_ipv6: can't register udp.\n"); | ||
441 | goto cleanup_tcp; | ||
442 | } | ||
443 | |||
444 | ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_icmpv6); | ||
445 | if (ret < 0) { | ||
446 | printk("nf_conntrack_ipv6: can't register icmpv6.\n"); | ||
447 | goto cleanup_udp; | ||
448 | } | ||
449 | |||
450 | ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv6); | ||
451 | if (ret < 0) { | ||
452 | printk("nf_conntrack_ipv6: can't register ipv6\n"); | ||
453 | goto cleanup_icmpv6; | ||
454 | } | ||
455 | |||
456 | ret = nf_register_hook(&ipv6_conntrack_defrag_ops); | ||
457 | if (ret < 0) { | ||
458 | printk("nf_conntrack_ipv6: can't register pre-routing defrag " | ||
459 | "hook.\n"); | ||
460 | goto cleanup_ipv6; | ||
461 | } | ||
462 | |||
463 | ret = nf_register_hook(&ipv6_conntrack_defrag_local_out_ops); | ||
464 | if (ret < 0) { | ||
465 | printk("nf_conntrack_ipv6: can't register local_out defrag " | ||
466 | "hook.\n"); | ||
467 | goto cleanup_defragops; | ||
468 | } | ||
469 | |||
470 | ret = nf_register_hook(&ipv6_conntrack_in_ops); | ||
471 | if (ret < 0) { | ||
472 | printk("nf_conntrack_ipv6: can't register pre-routing hook.\n"); | ||
473 | goto cleanup_defraglocalops; | ||
474 | } | ||
475 | |||
476 | ret = nf_register_hook(&ipv6_conntrack_local_out_ops); | ||
477 | if (ret < 0) { | ||
478 | printk("nf_conntrack_ipv6: can't register local out hook.\n"); | ||
479 | goto cleanup_inops; | ||
480 | } | ||
481 | |||
482 | ret = nf_register_hook(&ipv6_conntrack_out_ops); | ||
483 | if (ret < 0) { | ||
484 | printk("nf_conntrack_ipv6: can't register post-routing hook.\n"); | ||
485 | goto cleanup_inandlocalops; | ||
486 | } | ||
487 | |||
488 | ret = nf_register_hook(&ipv6_conntrack_local_in_ops); | ||
489 | if (ret < 0) { | ||
490 | printk("nf_conntrack_ipv6: can't register local in hook.\n"); | ||
491 | goto cleanup_inoutandlocalops; | ||
492 | } | ||
493 | |||
494 | #ifdef CONFIG_SYSCTL | ||
495 | nf_ct_ipv6_sysctl_header = register_sysctl_table(nf_ct_net_table, 0); | ||
496 | if (nf_ct_ipv6_sysctl_header == NULL) { | ||
497 | printk("nf_conntrack: can't register to sysctl.\n"); | ||
498 | ret = -ENOMEM; | ||
499 | goto cleanup_localinops; | ||
500 | } | ||
501 | #endif | ||
502 | return ret; | ||
503 | |||
504 | cleanup: | ||
505 | synchronize_net(); | ||
506 | #ifdef CONFIG_SYSCTL | ||
507 | unregister_sysctl_table(nf_ct_ipv6_sysctl_header); | ||
508 | cleanup_localinops: | ||
509 | #endif | ||
510 | nf_unregister_hook(&ipv6_conntrack_local_in_ops); | ||
511 | cleanup_inoutandlocalops: | ||
512 | nf_unregister_hook(&ipv6_conntrack_out_ops); | ||
513 | cleanup_inandlocalops: | ||
514 | nf_unregister_hook(&ipv6_conntrack_local_out_ops); | ||
515 | cleanup_inops: | ||
516 | nf_unregister_hook(&ipv6_conntrack_in_ops); | ||
517 | cleanup_defraglocalops: | ||
518 | nf_unregister_hook(&ipv6_conntrack_defrag_local_out_ops); | ||
519 | cleanup_defragops: | ||
520 | nf_unregister_hook(&ipv6_conntrack_defrag_ops); | ||
521 | cleanup_ipv6: | ||
522 | nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6); | ||
523 | cleanup_icmpv6: | ||
524 | nf_conntrack_protocol_unregister(&nf_conntrack_protocol_icmpv6); | ||
525 | cleanup_udp: | ||
526 | nf_conntrack_protocol_unregister(&nf_conntrack_protocol_udp6); | ||
527 | cleanup_tcp: | ||
528 | nf_conntrack_protocol_unregister(&nf_conntrack_protocol_tcp6); | ||
529 | cleanup_frag6: | ||
530 | nf_ct_frag6_cleanup(); | ||
531 | cleanup_nothing: | ||
532 | return ret; | ||
533 | } | ||
534 | |||
535 | MODULE_LICENSE("GPL"); | ||
536 | MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>"); | ||
537 | |||
538 | static int __init init(void) | ||
539 | { | ||
540 | need_nf_conntrack(); | ||
541 | return init_or_cleanup(1); | ||
542 | } | ||
543 | |||
544 | static void __exit fini(void) | ||
545 | { | ||
546 | init_or_cleanup(0); | ||
547 | } | ||
548 | |||
549 | module_init(init); | ||
550 | module_exit(fini); | ||
551 | |||
552 | void need_ip6_conntrack(void) | ||
553 | { | ||
554 | } | ||
555 | |||
556 | EXPORT_SYMBOL(need_ip6_conntrack); | ||
diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c new file mode 100644 index 000000000000..c0f1da5497a9 --- /dev/null +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | |||
@@ -0,0 +1,272 @@ | |||
1 | /* | ||
2 | * Copyright (C)2003,2004 USAGI/WIDE Project | ||
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 | * Author: | ||
9 | * Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | ||
10 | * | ||
11 | * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | ||
12 | * - ICMPv6 tracking support. Derived from the original ip_conntrack code | ||
13 | * net/ipv4/netfilter/ip_conntrack_proto_icmp.c which had the following | ||
14 | * copyright information: | ||
15 | * (C) 1999-2001 Paul `Rusty' Russell | ||
16 | * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> | ||
17 | */ | ||
18 | |||
19 | #include <linux/types.h> | ||
20 | #include <linux/sched.h> | ||
21 | #include <linux/timer.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/netfilter.h> | ||
24 | #include <linux/in6.h> | ||
25 | #include <linux/icmpv6.h> | ||
26 | #include <linux/ipv6.h> | ||
27 | #include <net/ipv6.h> | ||
28 | #include <net/ip6_checksum.h> | ||
29 | #include <linux/seq_file.h> | ||
30 | #include <linux/netfilter_ipv6.h> | ||
31 | #include <net/netfilter/nf_conntrack_tuple.h> | ||
32 | #include <net/netfilter/nf_conntrack_protocol.h> | ||
33 | #include <net/netfilter/nf_conntrack_core.h> | ||
34 | #include <net/netfilter/ipv6/nf_conntrack_icmpv6.h> | ||
35 | |||
36 | unsigned long nf_ct_icmpv6_timeout = 30*HZ; | ||
37 | |||
38 | #if 0 | ||
39 | #define DEBUGP printk | ||
40 | #else | ||
41 | #define DEBUGP(format, args...) | ||
42 | #endif | ||
43 | |||
44 | static int icmpv6_pkt_to_tuple(const struct sk_buff *skb, | ||
45 | unsigned int dataoff, | ||
46 | struct nf_conntrack_tuple *tuple) | ||
47 | { | ||
48 | struct icmp6hdr _hdr, *hp; | ||
49 | |||
50 | hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); | ||
51 | if (hp == NULL) | ||
52 | return 0; | ||
53 | tuple->dst.u.icmp.type = hp->icmp6_type; | ||
54 | tuple->src.u.icmp.id = hp->icmp6_identifier; | ||
55 | tuple->dst.u.icmp.code = hp->icmp6_code; | ||
56 | |||
57 | return 1; | ||
58 | } | ||
59 | |||
60 | static int icmpv6_invert_tuple(struct nf_conntrack_tuple *tuple, | ||
61 | const struct nf_conntrack_tuple *orig) | ||
62 | { | ||
63 | /* Add 1; spaces filled with 0. */ | ||
64 | static u_int8_t invmap[] = { | ||
65 | [ICMPV6_ECHO_REQUEST - 128] = ICMPV6_ECHO_REPLY + 1, | ||
66 | [ICMPV6_ECHO_REPLY - 128] = ICMPV6_ECHO_REQUEST + 1, | ||
67 | [ICMPV6_NI_QUERY - 128] = ICMPV6_NI_QUERY + 1, | ||
68 | [ICMPV6_NI_REPLY - 128] = ICMPV6_NI_REPLY +1 | ||
69 | }; | ||
70 | |||
71 | __u8 type = orig->dst.u.icmp.type - 128; | ||
72 | if (type >= sizeof(invmap) || !invmap[type]) | ||
73 | return 0; | ||
74 | |||
75 | tuple->src.u.icmp.id = orig->src.u.icmp.id; | ||
76 | tuple->dst.u.icmp.type = invmap[type] - 1; | ||
77 | tuple->dst.u.icmp.code = orig->dst.u.icmp.code; | ||
78 | return 1; | ||
79 | } | ||
80 | |||
81 | /* Print out the per-protocol part of the tuple. */ | ||
82 | static int icmpv6_print_tuple(struct seq_file *s, | ||
83 | const struct nf_conntrack_tuple *tuple) | ||
84 | { | ||
85 | return seq_printf(s, "type=%u code=%u id=%u ", | ||
86 | tuple->dst.u.icmp.type, | ||
87 | tuple->dst.u.icmp.code, | ||
88 | ntohs(tuple->src.u.icmp.id)); | ||
89 | } | ||
90 | |||
91 | /* Print out the private part of the conntrack. */ | ||
92 | static int icmpv6_print_conntrack(struct seq_file *s, | ||
93 | const struct nf_conn *conntrack) | ||
94 | { | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | /* Returns verdict for packet, or -1 for invalid. */ | ||
99 | static int icmpv6_packet(struct nf_conn *ct, | ||
100 | const struct sk_buff *skb, | ||
101 | unsigned int dataoff, | ||
102 | enum ip_conntrack_info ctinfo, | ||
103 | int pf, | ||
104 | unsigned int hooknum) | ||
105 | { | ||
106 | /* Try to delete connection immediately after all replies: | ||
107 | won't actually vanish as we still have skb, and del_timer | ||
108 | means this will only run once even if count hits zero twice | ||
109 | (theoretically possible with SMP) */ | ||
110 | if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { | ||
111 | if (atomic_dec_and_test(&ct->proto.icmp.count) | ||
112 | && del_timer(&ct->timeout)) | ||
113 | ct->timeout.function((unsigned long)ct); | ||
114 | } else { | ||
115 | atomic_inc(&ct->proto.icmp.count); | ||
116 | nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb); | ||
117 | nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmpv6_timeout); | ||
118 | } | ||
119 | |||
120 | return NF_ACCEPT; | ||
121 | } | ||
122 | |||
123 | /* Called when a new connection for this protocol found. */ | ||
124 | static int icmpv6_new(struct nf_conn *conntrack, | ||
125 | const struct sk_buff *skb, | ||
126 | unsigned int dataoff) | ||
127 | { | ||
128 | static u_int8_t valid_new[] = { | ||
129 | [ICMPV6_ECHO_REQUEST - 128] = 1, | ||
130 | [ICMPV6_NI_QUERY - 128] = 1 | ||
131 | }; | ||
132 | |||
133 | if (conntrack->tuplehash[0].tuple.dst.u.icmp.type - 128 >= sizeof(valid_new) | ||
134 | || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type - 128]) { | ||
135 | /* Can't create a new ICMPv6 `conn' with this. */ | ||
136 | DEBUGP("icmp: can't create new conn with type %u\n", | ||
137 | conntrack->tuplehash[0].tuple.dst.u.icmp.type); | ||
138 | NF_CT_DUMP_TUPLE(&conntrack->tuplehash[0].tuple); | ||
139 | return 0; | ||
140 | } | ||
141 | atomic_set(&conntrack->proto.icmp.count, 0); | ||
142 | return 1; | ||
143 | } | ||
144 | |||
145 | extern int | ||
146 | nf_ct_ipv6_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp, int len); | ||
147 | extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6; | ||
148 | static int | ||
149 | icmpv6_error_message(struct sk_buff *skb, | ||
150 | unsigned int icmp6off, | ||
151 | enum ip_conntrack_info *ctinfo, | ||
152 | unsigned int hooknum) | ||
153 | { | ||
154 | struct nf_conntrack_tuple intuple, origtuple; | ||
155 | struct nf_conntrack_tuple_hash *h; | ||
156 | struct icmp6hdr _hdr, *hp; | ||
157 | unsigned int inip6off; | ||
158 | struct nf_conntrack_protocol *inproto; | ||
159 | u_int8_t inprotonum; | ||
160 | unsigned int inprotoff; | ||
161 | |||
162 | NF_CT_ASSERT(skb->nfct == NULL); | ||
163 | |||
164 | hp = skb_header_pointer(skb, icmp6off, sizeof(_hdr), &_hdr); | ||
165 | if (hp == NULL) { | ||
166 | DEBUGP("icmpv6_error: Can't get ICMPv6 hdr.\n"); | ||
167 | return -NF_ACCEPT; | ||
168 | } | ||
169 | |||
170 | inip6off = icmp6off + sizeof(_hdr); | ||
171 | if (skb_copy_bits(skb, inip6off+offsetof(struct ipv6hdr, nexthdr), | ||
172 | &inprotonum, sizeof(inprotonum)) != 0) { | ||
173 | DEBUGP("icmpv6_error: Can't get nexthdr in inner IPv6 header.\n"); | ||
174 | return -NF_ACCEPT; | ||
175 | } | ||
176 | inprotoff = nf_ct_ipv6_skip_exthdr(skb, | ||
177 | inip6off + sizeof(struct ipv6hdr), | ||
178 | &inprotonum, | ||
179 | skb->len - inip6off | ||
180 | - sizeof(struct ipv6hdr)); | ||
181 | |||
182 | if ((inprotoff < 0) || (inprotoff > skb->len) || | ||
183 | (inprotonum == NEXTHDR_FRAGMENT)) { | ||
184 | DEBUGP("icmpv6_error: Can't get protocol header in ICMPv6 payload.\n"); | ||
185 | return -NF_ACCEPT; | ||
186 | } | ||
187 | |||
188 | inproto = nf_ct_find_proto(PF_INET6, inprotonum); | ||
189 | |||
190 | /* Are they talking about one of our connections? */ | ||
191 | if (!nf_ct_get_tuple(skb, inip6off, inprotoff, PF_INET6, inprotonum, | ||
192 | &origtuple, &nf_conntrack_l3proto_ipv6, inproto)) { | ||
193 | DEBUGP("icmpv6_error: Can't get tuple\n"); | ||
194 | return -NF_ACCEPT; | ||
195 | } | ||
196 | |||
197 | /* Ordinarily, we'd expect the inverted tupleproto, but it's | ||
198 | been preserved inside the ICMP. */ | ||
199 | if (!nf_ct_invert_tuple(&intuple, &origtuple, | ||
200 | &nf_conntrack_l3proto_ipv6, inproto)) { | ||
201 | DEBUGP("icmpv6_error: Can't invert tuple\n"); | ||
202 | return -NF_ACCEPT; | ||
203 | } | ||
204 | |||
205 | *ctinfo = IP_CT_RELATED; | ||
206 | |||
207 | h = nf_conntrack_find_get(&intuple, NULL); | ||
208 | if (!h) { | ||
209 | DEBUGP("icmpv6_error: no match\n"); | ||
210 | return -NF_ACCEPT; | ||
211 | } else { | ||
212 | if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) | ||
213 | *ctinfo += IP_CT_IS_REPLY; | ||
214 | } | ||
215 | |||
216 | /* Update skb to refer to this connection */ | ||
217 | skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general; | ||
218 | skb->nfctinfo = *ctinfo; | ||
219 | return -NF_ACCEPT; | ||
220 | } | ||
221 | |||
222 | static int | ||
223 | icmpv6_error(struct sk_buff *skb, unsigned int dataoff, | ||
224 | enum ip_conntrack_info *ctinfo, int pf, unsigned int hooknum) | ||
225 | { | ||
226 | struct icmp6hdr _ih, *icmp6h; | ||
227 | |||
228 | icmp6h = skb_header_pointer(skb, dataoff, sizeof(_ih), &_ih); | ||
229 | if (icmp6h == NULL) { | ||
230 | if (LOG_INVALID(IPPROTO_ICMPV6)) | ||
231 | nf_log_packet(PF_INET6, 0, skb, NULL, NULL, NULL, | ||
232 | "nf_ct_icmpv6: short packet "); | ||
233 | return -NF_ACCEPT; | ||
234 | } | ||
235 | |||
236 | if (hooknum != NF_IP6_PRE_ROUTING) | ||
237 | goto skipped; | ||
238 | |||
239 | /* Ignore it if the checksum's bogus. */ | ||
240 | if (csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr, | ||
241 | skb->len - dataoff, IPPROTO_ICMPV6, | ||
242 | skb_checksum(skb, dataoff, | ||
243 | skb->len - dataoff, 0))) { | ||
244 | nf_log_packet(PF_INET6, 0, skb, NULL, NULL, NULL, | ||
245 | "nf_ct_icmpv6: ICMPv6 checksum failed\n"); | ||
246 | return -NF_ACCEPT; | ||
247 | } | ||
248 | |||
249 | skipped: | ||
250 | |||
251 | /* is not error message ? */ | ||
252 | if (icmp6h->icmp6_type >= 128) | ||
253 | return NF_ACCEPT; | ||
254 | |||
255 | return icmpv6_error_message(skb, dataoff, ctinfo, hooknum); | ||
256 | } | ||
257 | |||
258 | struct nf_conntrack_protocol nf_conntrack_protocol_icmpv6 = | ||
259 | { | ||
260 | .l3proto = PF_INET6, | ||
261 | .proto = IPPROTO_ICMPV6, | ||
262 | .name = "icmpv6", | ||
263 | .pkt_to_tuple = icmpv6_pkt_to_tuple, | ||
264 | .invert_tuple = icmpv6_invert_tuple, | ||
265 | .print_tuple = icmpv6_print_tuple, | ||
266 | .print_conntrack = icmpv6_print_conntrack, | ||
267 | .packet = icmpv6_packet, | ||
268 | .new = icmpv6_new, | ||
269 | .error = icmpv6_error, | ||
270 | }; | ||
271 | |||
272 | EXPORT_SYMBOL(nf_conntrack_protocol_icmpv6); | ||
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c new file mode 100644 index 000000000000..7640b9bb7694 --- /dev/null +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c | |||
@@ -0,0 +1,885 @@ | |||
1 | /* | ||
2 | * IPv6 fragment reassembly for connection tracking | ||
3 | * | ||
4 | * Copyright (C)2004 USAGI/WIDE Project | ||
5 | * | ||
6 | * Author: | ||
7 | * Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | ||
8 | * | ||
9 | * Based on: net/ipv6/reassembly.c | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * as published by the Free Software Foundation; either version | ||
14 | * 2 of the License, or (at your option) any later version. | ||
15 | */ | ||
16 | |||
17 | #include <linux/config.h> | ||
18 | #include <linux/errno.h> | ||
19 | #include <linux/types.h> | ||
20 | #include <linux/string.h> | ||
21 | #include <linux/socket.h> | ||
22 | #include <linux/sockios.h> | ||
23 | #include <linux/jiffies.h> | ||
24 | #include <linux/net.h> | ||
25 | #include <linux/list.h> | ||
26 | #include <linux/netdevice.h> | ||
27 | #include <linux/in6.h> | ||
28 | #include <linux/ipv6.h> | ||
29 | #include <linux/icmpv6.h> | ||
30 | #include <linux/random.h> | ||
31 | #include <linux/jhash.h> | ||
32 | |||
33 | #include <net/sock.h> | ||
34 | #include <net/snmp.h> | ||
35 | |||
36 | #include <net/ipv6.h> | ||
37 | #include <net/protocol.h> | ||
38 | #include <net/transp_v6.h> | ||
39 | #include <net/rawv6.h> | ||
40 | #include <net/ndisc.h> | ||
41 | #include <net/addrconf.h> | ||
42 | #include <linux/sysctl.h> | ||
43 | #include <linux/netfilter.h> | ||
44 | #include <linux/netfilter_ipv6.h> | ||
45 | #include <linux/kernel.h> | ||
46 | #include <linux/module.h> | ||
47 | |||
48 | #if 0 | ||
49 | #define DEBUGP printk | ||
50 | #else | ||
51 | #define DEBUGP(format, args...) | ||
52 | #endif | ||
53 | |||
54 | #define NF_CT_FRAG6_HIGH_THRESH 262144 /* == 256*1024 */ | ||
55 | #define NF_CT_FRAG6_LOW_THRESH 196608 /* == 192*1024 */ | ||
56 | #define NF_CT_FRAG6_TIMEOUT IPV6_FRAG_TIMEOUT | ||
57 | |||
58 | int nf_ct_frag6_high_thresh = 256*1024; | ||
59 | int nf_ct_frag6_low_thresh = 192*1024; | ||
60 | int nf_ct_frag6_timeout = IPV6_FRAG_TIMEOUT; | ||
61 | |||
62 | struct nf_ct_frag6_skb_cb | ||
63 | { | ||
64 | struct inet6_skb_parm h; | ||
65 | int offset; | ||
66 | struct sk_buff *orig; | ||
67 | }; | ||
68 | |||
69 | #define NFCT_FRAG6_CB(skb) ((struct nf_ct_frag6_skb_cb*)((skb)->cb)) | ||
70 | |||
71 | struct nf_ct_frag6_queue | ||
72 | { | ||
73 | struct nf_ct_frag6_queue *next; | ||
74 | struct list_head lru_list; /* lru list member */ | ||
75 | |||
76 | __u32 id; /* fragment id */ | ||
77 | struct in6_addr saddr; | ||
78 | struct in6_addr daddr; | ||
79 | |||
80 | spinlock_t lock; | ||
81 | atomic_t refcnt; | ||
82 | struct timer_list timer; /* expire timer */ | ||
83 | struct sk_buff *fragments; | ||
84 | int len; | ||
85 | int meat; | ||
86 | struct timeval stamp; | ||
87 | unsigned int csum; | ||
88 | __u8 last_in; /* has first/last segment arrived? */ | ||
89 | #define COMPLETE 4 | ||
90 | #define FIRST_IN 2 | ||
91 | #define LAST_IN 1 | ||
92 | __u16 nhoffset; | ||
93 | struct nf_ct_frag6_queue **pprev; | ||
94 | }; | ||
95 | |||
96 | /* Hash table. */ | ||
97 | |||
98 | #define FRAG6Q_HASHSZ 64 | ||
99 | |||
100 | static struct nf_ct_frag6_queue *nf_ct_frag6_hash[FRAG6Q_HASHSZ]; | ||
101 | static rwlock_t nf_ct_frag6_lock = RW_LOCK_UNLOCKED; | ||
102 | static u32 nf_ct_frag6_hash_rnd; | ||
103 | static LIST_HEAD(nf_ct_frag6_lru_list); | ||
104 | int nf_ct_frag6_nqueues = 0; | ||
105 | |||
106 | static __inline__ void __fq_unlink(struct nf_ct_frag6_queue *fq) | ||
107 | { | ||
108 | if (fq->next) | ||
109 | fq->next->pprev = fq->pprev; | ||
110 | *fq->pprev = fq->next; | ||
111 | list_del(&fq->lru_list); | ||
112 | nf_ct_frag6_nqueues--; | ||
113 | } | ||
114 | |||
115 | static __inline__ void fq_unlink(struct nf_ct_frag6_queue *fq) | ||
116 | { | ||
117 | write_lock(&nf_ct_frag6_lock); | ||
118 | __fq_unlink(fq); | ||
119 | write_unlock(&nf_ct_frag6_lock); | ||
120 | } | ||
121 | |||
122 | static unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr, | ||
123 | struct in6_addr *daddr) | ||
124 | { | ||
125 | u32 a, b, c; | ||
126 | |||
127 | a = saddr->s6_addr32[0]; | ||
128 | b = saddr->s6_addr32[1]; | ||
129 | c = saddr->s6_addr32[2]; | ||
130 | |||
131 | a += JHASH_GOLDEN_RATIO; | ||
132 | b += JHASH_GOLDEN_RATIO; | ||
133 | c += nf_ct_frag6_hash_rnd; | ||
134 | __jhash_mix(a, b, c); | ||
135 | |||
136 | a += saddr->s6_addr32[3]; | ||
137 | b += daddr->s6_addr32[0]; | ||
138 | c += daddr->s6_addr32[1]; | ||
139 | __jhash_mix(a, b, c); | ||
140 | |||
141 | a += daddr->s6_addr32[2]; | ||
142 | b += daddr->s6_addr32[3]; | ||
143 | c += id; | ||
144 | __jhash_mix(a, b, c); | ||
145 | |||
146 | return c & (FRAG6Q_HASHSZ - 1); | ||
147 | } | ||
148 | |||
149 | static struct timer_list nf_ct_frag6_secret_timer; | ||
150 | int nf_ct_frag6_secret_interval = 10 * 60 * HZ; | ||
151 | |||
152 | static void nf_ct_frag6_secret_rebuild(unsigned long dummy) | ||
153 | { | ||
154 | unsigned long now = jiffies; | ||
155 | int i; | ||
156 | |||
157 | write_lock(&nf_ct_frag6_lock); | ||
158 | get_random_bytes(&nf_ct_frag6_hash_rnd, sizeof(u32)); | ||
159 | for (i = 0; i < FRAG6Q_HASHSZ; i++) { | ||
160 | struct nf_ct_frag6_queue *q; | ||
161 | |||
162 | q = nf_ct_frag6_hash[i]; | ||
163 | while (q) { | ||
164 | struct nf_ct_frag6_queue *next = q->next; | ||
165 | unsigned int hval = ip6qhashfn(q->id, | ||
166 | &q->saddr, | ||
167 | &q->daddr); | ||
168 | |||
169 | if (hval != i) { | ||
170 | /* Unlink. */ | ||
171 | if (q->next) | ||
172 | q->next->pprev = q->pprev; | ||
173 | *q->pprev = q->next; | ||
174 | |||
175 | /* Relink to new hash chain. */ | ||
176 | if ((q->next = nf_ct_frag6_hash[hval]) != NULL) | ||
177 | q->next->pprev = &q->next; | ||
178 | nf_ct_frag6_hash[hval] = q; | ||
179 | q->pprev = &nf_ct_frag6_hash[hval]; | ||
180 | } | ||
181 | |||
182 | q = next; | ||
183 | } | ||
184 | } | ||
185 | write_unlock(&nf_ct_frag6_lock); | ||
186 | |||
187 | mod_timer(&nf_ct_frag6_secret_timer, now + nf_ct_frag6_secret_interval); | ||
188 | } | ||
189 | |||
190 | atomic_t nf_ct_frag6_mem = ATOMIC_INIT(0); | ||
191 | |||
192 | /* Memory Tracking Functions. */ | ||
193 | static inline void frag_kfree_skb(struct sk_buff *skb) | ||
194 | { | ||
195 | atomic_sub(skb->truesize, &nf_ct_frag6_mem); | ||
196 | if (NFCT_FRAG6_CB(skb)->orig) | ||
197 | kfree_skb(NFCT_FRAG6_CB(skb)->orig); | ||
198 | |||
199 | kfree_skb(skb); | ||
200 | } | ||
201 | |||
202 | static inline void frag_free_queue(struct nf_ct_frag6_queue *fq) | ||
203 | { | ||
204 | atomic_sub(sizeof(struct nf_ct_frag6_queue), &nf_ct_frag6_mem); | ||
205 | kfree(fq); | ||
206 | } | ||
207 | |||
208 | static inline struct nf_ct_frag6_queue *frag_alloc_queue(void) | ||
209 | { | ||
210 | struct nf_ct_frag6_queue *fq = kmalloc(sizeof(struct nf_ct_frag6_queue), GFP_ATOMIC); | ||
211 | |||
212 | if (!fq) | ||
213 | return NULL; | ||
214 | atomic_add(sizeof(struct nf_ct_frag6_queue), &nf_ct_frag6_mem); | ||
215 | return fq; | ||
216 | } | ||
217 | |||
218 | /* Destruction primitives. */ | ||
219 | |||
220 | /* Complete destruction of fq. */ | ||
221 | static void nf_ct_frag6_destroy(struct nf_ct_frag6_queue *fq) | ||
222 | { | ||
223 | struct sk_buff *fp; | ||
224 | |||
225 | BUG_TRAP(fq->last_in&COMPLETE); | ||
226 | BUG_TRAP(del_timer(&fq->timer) == 0); | ||
227 | |||
228 | /* Release all fragment data. */ | ||
229 | fp = fq->fragments; | ||
230 | while (fp) { | ||
231 | struct sk_buff *xp = fp->next; | ||
232 | |||
233 | frag_kfree_skb(fp); | ||
234 | fp = xp; | ||
235 | } | ||
236 | |||
237 | frag_free_queue(fq); | ||
238 | } | ||
239 | |||
240 | static __inline__ void fq_put(struct nf_ct_frag6_queue *fq) | ||
241 | { | ||
242 | if (atomic_dec_and_test(&fq->refcnt)) | ||
243 | nf_ct_frag6_destroy(fq); | ||
244 | } | ||
245 | |||
246 | /* Kill fq entry. It is not destroyed immediately, | ||
247 | * because caller (and someone more) holds reference count. | ||
248 | */ | ||
249 | static __inline__ void fq_kill(struct nf_ct_frag6_queue *fq) | ||
250 | { | ||
251 | if (del_timer(&fq->timer)) | ||
252 | atomic_dec(&fq->refcnt); | ||
253 | |||
254 | if (!(fq->last_in & COMPLETE)) { | ||
255 | fq_unlink(fq); | ||
256 | atomic_dec(&fq->refcnt); | ||
257 | fq->last_in |= COMPLETE; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | static void nf_ct_frag6_evictor(void) | ||
262 | { | ||
263 | struct nf_ct_frag6_queue *fq; | ||
264 | struct list_head *tmp; | ||
265 | |||
266 | for (;;) { | ||
267 | if (atomic_read(&nf_ct_frag6_mem) <= nf_ct_frag6_low_thresh) | ||
268 | return; | ||
269 | read_lock(&nf_ct_frag6_lock); | ||
270 | if (list_empty(&nf_ct_frag6_lru_list)) { | ||
271 | read_unlock(&nf_ct_frag6_lock); | ||
272 | return; | ||
273 | } | ||
274 | tmp = nf_ct_frag6_lru_list.next; | ||
275 | fq = list_entry(tmp, struct nf_ct_frag6_queue, lru_list); | ||
276 | atomic_inc(&fq->refcnt); | ||
277 | read_unlock(&nf_ct_frag6_lock); | ||
278 | |||
279 | spin_lock(&fq->lock); | ||
280 | if (!(fq->last_in&COMPLETE)) | ||
281 | fq_kill(fq); | ||
282 | spin_unlock(&fq->lock); | ||
283 | |||
284 | fq_put(fq); | ||
285 | } | ||
286 | } | ||
287 | |||
288 | static void nf_ct_frag6_expire(unsigned long data) | ||
289 | { | ||
290 | struct nf_ct_frag6_queue *fq = (struct nf_ct_frag6_queue *) data; | ||
291 | |||
292 | spin_lock(&fq->lock); | ||
293 | |||
294 | if (fq->last_in & COMPLETE) | ||
295 | goto out; | ||
296 | |||
297 | fq_kill(fq); | ||
298 | |||
299 | out: | ||
300 | spin_unlock(&fq->lock); | ||
301 | fq_put(fq); | ||
302 | } | ||
303 | |||
304 | /* Creation primitives. */ | ||
305 | |||
306 | |||
307 | static struct nf_ct_frag6_queue *nf_ct_frag6_intern(unsigned int hash, | ||
308 | struct nf_ct_frag6_queue *fq_in) | ||
309 | { | ||
310 | struct nf_ct_frag6_queue *fq; | ||
311 | |||
312 | write_lock(&nf_ct_frag6_lock); | ||
313 | #ifdef CONFIG_SMP | ||
314 | for (fq = nf_ct_frag6_hash[hash]; fq; fq = fq->next) { | ||
315 | if (fq->id == fq_in->id && | ||
316 | !ipv6_addr_cmp(&fq_in->saddr, &fq->saddr) && | ||
317 | !ipv6_addr_cmp(&fq_in->daddr, &fq->daddr)) { | ||
318 | atomic_inc(&fq->refcnt); | ||
319 | write_unlock(&nf_ct_frag6_lock); | ||
320 | fq_in->last_in |= COMPLETE; | ||
321 | fq_put(fq_in); | ||
322 | return fq; | ||
323 | } | ||
324 | } | ||
325 | #endif | ||
326 | fq = fq_in; | ||
327 | |||
328 | if (!mod_timer(&fq->timer, jiffies + nf_ct_frag6_timeout)) | ||
329 | atomic_inc(&fq->refcnt); | ||
330 | |||
331 | atomic_inc(&fq->refcnt); | ||
332 | if ((fq->next = nf_ct_frag6_hash[hash]) != NULL) | ||
333 | fq->next->pprev = &fq->next; | ||
334 | nf_ct_frag6_hash[hash] = fq; | ||
335 | fq->pprev = &nf_ct_frag6_hash[hash]; | ||
336 | INIT_LIST_HEAD(&fq->lru_list); | ||
337 | list_add_tail(&fq->lru_list, &nf_ct_frag6_lru_list); | ||
338 | nf_ct_frag6_nqueues++; | ||
339 | write_unlock(&nf_ct_frag6_lock); | ||
340 | return fq; | ||
341 | } | ||
342 | |||
343 | |||
344 | static struct nf_ct_frag6_queue * | ||
345 | nf_ct_frag6_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr *dst) | ||
346 | { | ||
347 | struct nf_ct_frag6_queue *fq; | ||
348 | |||
349 | if ((fq = frag_alloc_queue()) == NULL) { | ||
350 | DEBUGP("Can't alloc new queue\n"); | ||
351 | goto oom; | ||
352 | } | ||
353 | |||
354 | memset(fq, 0, sizeof(struct nf_ct_frag6_queue)); | ||
355 | |||
356 | fq->id = id; | ||
357 | ipv6_addr_copy(&fq->saddr, src); | ||
358 | ipv6_addr_copy(&fq->daddr, dst); | ||
359 | |||
360 | init_timer(&fq->timer); | ||
361 | fq->timer.function = nf_ct_frag6_expire; | ||
362 | fq->timer.data = (long) fq; | ||
363 | fq->lock = SPIN_LOCK_UNLOCKED; | ||
364 | atomic_set(&fq->refcnt, 1); | ||
365 | |||
366 | return nf_ct_frag6_intern(hash, fq); | ||
367 | |||
368 | oom: | ||
369 | return NULL; | ||
370 | } | ||
371 | |||
372 | static __inline__ struct nf_ct_frag6_queue * | ||
373 | fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst) | ||
374 | { | ||
375 | struct nf_ct_frag6_queue *fq; | ||
376 | unsigned int hash = ip6qhashfn(id, src, dst); | ||
377 | |||
378 | read_lock(&nf_ct_frag6_lock); | ||
379 | for (fq = nf_ct_frag6_hash[hash]; fq; fq = fq->next) { | ||
380 | if (fq->id == id && | ||
381 | !ipv6_addr_cmp(src, &fq->saddr) && | ||
382 | !ipv6_addr_cmp(dst, &fq->daddr)) { | ||
383 | atomic_inc(&fq->refcnt); | ||
384 | read_unlock(&nf_ct_frag6_lock); | ||
385 | return fq; | ||
386 | } | ||
387 | } | ||
388 | read_unlock(&nf_ct_frag6_lock); | ||
389 | |||
390 | return nf_ct_frag6_create(hash, id, src, dst); | ||
391 | } | ||
392 | |||
393 | |||
394 | static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, | ||
395 | struct frag_hdr *fhdr, int nhoff) | ||
396 | { | ||
397 | struct sk_buff *prev, *next; | ||
398 | int offset, end; | ||
399 | |||
400 | if (fq->last_in & COMPLETE) { | ||
401 | DEBUGP("Allready completed\n"); | ||
402 | goto err; | ||
403 | } | ||
404 | |||
405 | offset = ntohs(fhdr->frag_off) & ~0x7; | ||
406 | end = offset + (ntohs(skb->nh.ipv6h->payload_len) - | ||
407 | ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1))); | ||
408 | |||
409 | if ((unsigned int)end > IPV6_MAXPLEN) { | ||
410 | DEBUGP("offset is too large.\n"); | ||
411 | return -1; | ||
412 | } | ||
413 | |||
414 | if (skb->ip_summed == CHECKSUM_HW) | ||
415 | skb->csum = csum_sub(skb->csum, | ||
416 | csum_partial(skb->nh.raw, | ||
417 | (u8*)(fhdr + 1) - skb->nh.raw, | ||
418 | 0)); | ||
419 | |||
420 | /* Is this the final fragment? */ | ||
421 | if (!(fhdr->frag_off & htons(IP6_MF))) { | ||
422 | /* If we already have some bits beyond end | ||
423 | * or have different end, the segment is corrupted. | ||
424 | */ | ||
425 | if (end < fq->len || | ||
426 | ((fq->last_in & LAST_IN) && end != fq->len)) { | ||
427 | DEBUGP("already received last fragment\n"); | ||
428 | goto err; | ||
429 | } | ||
430 | fq->last_in |= LAST_IN; | ||
431 | fq->len = end; | ||
432 | } else { | ||
433 | /* Check if the fragment is rounded to 8 bytes. | ||
434 | * Required by the RFC. | ||
435 | */ | ||
436 | if (end & 0x7) { | ||
437 | /* RFC2460 says always send parameter problem in | ||
438 | * this case. -DaveM | ||
439 | */ | ||
440 | DEBUGP("the end of this fragment is not rounded to 8 bytes.\n"); | ||
441 | return -1; | ||
442 | } | ||
443 | if (end > fq->len) { | ||
444 | /* Some bits beyond end -> corruption. */ | ||
445 | if (fq->last_in & LAST_IN) { | ||
446 | DEBUGP("last packet already reached.\n"); | ||
447 | goto err; | ||
448 | } | ||
449 | fq->len = end; | ||
450 | } | ||
451 | } | ||
452 | |||
453 | if (end == offset) | ||
454 | goto err; | ||
455 | |||
456 | /* Point into the IP datagram 'data' part. */ | ||
457 | if (!pskb_pull(skb, (u8 *) (fhdr + 1) - skb->data)) { | ||
458 | DEBUGP("queue: message is too short.\n"); | ||
459 | goto err; | ||
460 | } | ||
461 | if (end-offset < skb->len) { | ||
462 | if (pskb_trim(skb, end - offset)) { | ||
463 | DEBUGP("Can't trim\n"); | ||
464 | goto err; | ||
465 | } | ||
466 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) | ||
467 | skb->ip_summed = CHECKSUM_NONE; | ||
468 | } | ||
469 | |||
470 | /* Find out which fragments are in front and at the back of us | ||
471 | * in the chain of fragments so far. We must know where to put | ||
472 | * this fragment, right? | ||
473 | */ | ||
474 | prev = NULL; | ||
475 | for (next = fq->fragments; next != NULL; next = next->next) { | ||
476 | if (NFCT_FRAG6_CB(next)->offset >= offset) | ||
477 | break; /* bingo! */ | ||
478 | prev = next; | ||
479 | } | ||
480 | |||
481 | /* We found where to put this one. Check for overlap with | ||
482 | * preceding fragment, and, if needed, align things so that | ||
483 | * any overlaps are eliminated. | ||
484 | */ | ||
485 | if (prev) { | ||
486 | int i = (NFCT_FRAG6_CB(prev)->offset + prev->len) - offset; | ||
487 | |||
488 | if (i > 0) { | ||
489 | offset += i; | ||
490 | if (end <= offset) { | ||
491 | DEBUGP("overlap\n"); | ||
492 | goto err; | ||
493 | } | ||
494 | if (!pskb_pull(skb, i)) { | ||
495 | DEBUGP("Can't pull\n"); | ||
496 | goto err; | ||
497 | } | ||
498 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) | ||
499 | skb->ip_summed = CHECKSUM_NONE; | ||
500 | } | ||
501 | } | ||
502 | |||
503 | /* Look for overlap with succeeding segments. | ||
504 | * If we can merge fragments, do it. | ||
505 | */ | ||
506 | while (next && NFCT_FRAG6_CB(next)->offset < end) { | ||
507 | /* overlap is 'i' bytes */ | ||
508 | int i = end - NFCT_FRAG6_CB(next)->offset; | ||
509 | |||
510 | if (i < next->len) { | ||
511 | /* Eat head of the next overlapped fragment | ||
512 | * and leave the loop. The next ones cannot overlap. | ||
513 | */ | ||
514 | DEBUGP("Eat head of the overlapped parts.: %d", i); | ||
515 | if (!pskb_pull(next, i)) | ||
516 | goto err; | ||
517 | |||
518 | /* next fragment */ | ||
519 | NFCT_FRAG6_CB(next)->offset += i; | ||
520 | fq->meat -= i; | ||
521 | if (next->ip_summed != CHECKSUM_UNNECESSARY) | ||
522 | next->ip_summed = CHECKSUM_NONE; | ||
523 | break; | ||
524 | } else { | ||
525 | struct sk_buff *free_it = next; | ||
526 | |||
527 | /* Old fragmnet is completely overridden with | ||
528 | * new one drop it. | ||
529 | */ | ||
530 | next = next->next; | ||
531 | |||
532 | if (prev) | ||
533 | prev->next = next; | ||
534 | else | ||
535 | fq->fragments = next; | ||
536 | |||
537 | fq->meat -= free_it->len; | ||
538 | frag_kfree_skb(free_it); | ||
539 | } | ||
540 | } | ||
541 | |||
542 | NFCT_FRAG6_CB(skb)->offset = offset; | ||
543 | |||
544 | /* Insert this fragment in the chain of fragments. */ | ||
545 | skb->next = next; | ||
546 | if (prev) | ||
547 | prev->next = skb; | ||
548 | else | ||
549 | fq->fragments = skb; | ||
550 | |||
551 | skb->dev = NULL; | ||
552 | skb_get_timestamp(skb, &fq->stamp); | ||
553 | fq->meat += skb->len; | ||
554 | atomic_add(skb->truesize, &nf_ct_frag6_mem); | ||
555 | |||
556 | /* The first fragment. | ||
557 | * nhoffset is obtained from the first fragment, of course. | ||
558 | */ | ||
559 | if (offset == 0) { | ||
560 | fq->nhoffset = nhoff; | ||
561 | fq->last_in |= FIRST_IN; | ||
562 | } | ||
563 | write_lock(&nf_ct_frag6_lock); | ||
564 | list_move_tail(&fq->lru_list, &nf_ct_frag6_lru_list); | ||
565 | write_unlock(&nf_ct_frag6_lock); | ||
566 | return 0; | ||
567 | |||
568 | err: | ||
569 | return -1; | ||
570 | } | ||
571 | |||
572 | /* | ||
573 | * Check if this packet is complete. | ||
574 | * Returns NULL on failure by any reason, and pointer | ||
575 | * to current nexthdr field in reassembled frame. | ||
576 | * | ||
577 | * It is called with locked fq, and caller must check that | ||
578 | * queue is eligible for reassembly i.e. it is not COMPLETE, | ||
579 | * the last and the first frames arrived and all the bits are here. | ||
580 | */ | ||
581 | static struct sk_buff * | ||
582 | nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev) | ||
583 | { | ||
584 | struct sk_buff *fp, *op, *head = fq->fragments; | ||
585 | int payload_len; | ||
586 | |||
587 | fq_kill(fq); | ||
588 | |||
589 | BUG_TRAP(head != NULL); | ||
590 | BUG_TRAP(NFCT_FRAG6_CB(head)->offset == 0); | ||
591 | |||
592 | /* Unfragmented part is taken from the first segment. */ | ||
593 | payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len - sizeof(struct frag_hdr); | ||
594 | if (payload_len > IPV6_MAXPLEN) { | ||
595 | DEBUGP("payload len is too large.\n"); | ||
596 | goto out_oversize; | ||
597 | } | ||
598 | |||
599 | /* Head of list must not be cloned. */ | ||
600 | if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC)) { | ||
601 | DEBUGP("skb is cloned but can't expand head"); | ||
602 | goto out_oom; | ||
603 | } | ||
604 | |||
605 | /* If the first fragment is fragmented itself, we split | ||
606 | * it to two chunks: the first with data and paged part | ||
607 | * and the second, holding only fragments. */ | ||
608 | if (skb_shinfo(head)->frag_list) { | ||
609 | struct sk_buff *clone; | ||
610 | int i, plen = 0; | ||
611 | |||
612 | if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL) { | ||
613 | DEBUGP("Can't alloc skb\n"); | ||
614 | goto out_oom; | ||
615 | } | ||
616 | clone->next = head->next; | ||
617 | head->next = clone; | ||
618 | skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list; | ||
619 | skb_shinfo(head)->frag_list = NULL; | ||
620 | for (i=0; i<skb_shinfo(head)->nr_frags; i++) | ||
621 | plen += skb_shinfo(head)->frags[i].size; | ||
622 | clone->len = clone->data_len = head->data_len - plen; | ||
623 | head->data_len -= clone->len; | ||
624 | head->len -= clone->len; | ||
625 | clone->csum = 0; | ||
626 | clone->ip_summed = head->ip_summed; | ||
627 | |||
628 | NFCT_FRAG6_CB(clone)->orig = NULL; | ||
629 | atomic_add(clone->truesize, &nf_ct_frag6_mem); | ||
630 | } | ||
631 | |||
632 | /* We have to remove fragment header from datagram and to relocate | ||
633 | * header in order to calculate ICV correctly. */ | ||
634 | head->nh.raw[fq->nhoffset] = head->h.raw[0]; | ||
635 | memmove(head->head + sizeof(struct frag_hdr), head->head, | ||
636 | (head->data - head->head) - sizeof(struct frag_hdr)); | ||
637 | head->mac.raw += sizeof(struct frag_hdr); | ||
638 | head->nh.raw += sizeof(struct frag_hdr); | ||
639 | |||
640 | skb_shinfo(head)->frag_list = head->next; | ||
641 | head->h.raw = head->data; | ||
642 | skb_push(head, head->data - head->nh.raw); | ||
643 | atomic_sub(head->truesize, &nf_ct_frag6_mem); | ||
644 | |||
645 | for (fp=head->next; fp; fp = fp->next) { | ||
646 | head->data_len += fp->len; | ||
647 | head->len += fp->len; | ||
648 | if (head->ip_summed != fp->ip_summed) | ||
649 | head->ip_summed = CHECKSUM_NONE; | ||
650 | else if (head->ip_summed == CHECKSUM_HW) | ||
651 | head->csum = csum_add(head->csum, fp->csum); | ||
652 | head->truesize += fp->truesize; | ||
653 | atomic_sub(fp->truesize, &nf_ct_frag6_mem); | ||
654 | } | ||
655 | |||
656 | head->next = NULL; | ||
657 | head->dev = dev; | ||
658 | skb_set_timestamp(head, &fq->stamp); | ||
659 | head->nh.ipv6h->payload_len = htons(payload_len); | ||
660 | |||
661 | /* Yes, and fold redundant checksum back. 8) */ | ||
662 | if (head->ip_summed == CHECKSUM_HW) | ||
663 | head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum); | ||
664 | |||
665 | fq->fragments = NULL; | ||
666 | |||
667 | /* all original skbs are linked into the NFCT_FRAG6_CB(head).orig */ | ||
668 | fp = skb_shinfo(head)->frag_list; | ||
669 | if (NFCT_FRAG6_CB(fp)->orig == NULL) | ||
670 | /* at above code, head skb is divided into two skbs. */ | ||
671 | fp = fp->next; | ||
672 | |||
673 | op = NFCT_FRAG6_CB(head)->orig; | ||
674 | for (; fp; fp = fp->next) { | ||
675 | struct sk_buff *orig = NFCT_FRAG6_CB(fp)->orig; | ||
676 | |||
677 | op->next = orig; | ||
678 | op = orig; | ||
679 | NFCT_FRAG6_CB(fp)->orig = NULL; | ||
680 | } | ||
681 | |||
682 | return head; | ||
683 | |||
684 | out_oversize: | ||
685 | if (net_ratelimit()) | ||
686 | printk(KERN_DEBUG "nf_ct_frag6_reasm: payload len = %d\n", payload_len); | ||
687 | goto out_fail; | ||
688 | out_oom: | ||
689 | if (net_ratelimit()) | ||
690 | printk(KERN_DEBUG "nf_ct_frag6_reasm: no memory for reassembly\n"); | ||
691 | out_fail: | ||
692 | return NULL; | ||
693 | } | ||
694 | |||
695 | /* | ||
696 | * find the header just before Fragment Header. | ||
697 | * | ||
698 | * if success return 0 and set ... | ||
699 | * (*prevhdrp): the value of "Next Header Field" in the header | ||
700 | * just before Fragment Header. | ||
701 | * (*prevhoff): the offset of "Next Header Field" in the header | ||
702 | * just before Fragment Header. | ||
703 | * (*fhoff) : the offset of Fragment Header. | ||
704 | * | ||
705 | * Based on ipv6_skip_hdr() in net/ipv6/exthdr.c | ||
706 | * | ||
707 | */ | ||
708 | static int | ||
709 | find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) | ||
710 | { | ||
711 | u8 nexthdr = skb->nh.ipv6h->nexthdr; | ||
712 | u8 prev_nhoff = (u8 *)&skb->nh.ipv6h->nexthdr - skb->data; | ||
713 | int start = (u8 *)(skb->nh.ipv6h+1) - skb->data; | ||
714 | int len = skb->len - start; | ||
715 | u8 prevhdr = NEXTHDR_IPV6; | ||
716 | |||
717 | while (nexthdr != NEXTHDR_FRAGMENT) { | ||
718 | struct ipv6_opt_hdr hdr; | ||
719 | int hdrlen; | ||
720 | |||
721 | if (!ipv6_ext_hdr(nexthdr)) { | ||
722 | return -1; | ||
723 | } | ||
724 | if (len < (int)sizeof(struct ipv6_opt_hdr)) { | ||
725 | DEBUGP("too short\n"); | ||
726 | return -1; | ||
727 | } | ||
728 | if (nexthdr == NEXTHDR_NONE) { | ||
729 | DEBUGP("next header is none\n"); | ||
730 | return -1; | ||
731 | } | ||
732 | if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) | ||
733 | BUG(); | ||
734 | if (nexthdr == NEXTHDR_AUTH) | ||
735 | hdrlen = (hdr.hdrlen+2)<<2; | ||
736 | else | ||
737 | hdrlen = ipv6_optlen(&hdr); | ||
738 | |||
739 | prevhdr = nexthdr; | ||
740 | prev_nhoff = start; | ||
741 | |||
742 | nexthdr = hdr.nexthdr; | ||
743 | len -= hdrlen; | ||
744 | start += hdrlen; | ||
745 | } | ||
746 | |||
747 | if (len < 0) | ||
748 | return -1; | ||
749 | |||
750 | *prevhdrp = prevhdr; | ||
751 | *prevhoff = prev_nhoff; | ||
752 | *fhoff = start; | ||
753 | |||
754 | return 0; | ||
755 | } | ||
756 | |||
757 | struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb) | ||
758 | { | ||
759 | struct sk_buff *clone; | ||
760 | struct net_device *dev = skb->dev; | ||
761 | struct frag_hdr *fhdr; | ||
762 | struct nf_ct_frag6_queue *fq; | ||
763 | struct ipv6hdr *hdr; | ||
764 | int fhoff, nhoff; | ||
765 | u8 prevhdr; | ||
766 | struct sk_buff *ret_skb = NULL; | ||
767 | |||
768 | /* Jumbo payload inhibits frag. header */ | ||
769 | if (skb->nh.ipv6h->payload_len == 0) { | ||
770 | DEBUGP("payload len = 0\n"); | ||
771 | return skb; | ||
772 | } | ||
773 | |||
774 | if (find_prev_fhdr(skb, &prevhdr, &nhoff, &fhoff) < 0) | ||
775 | return skb; | ||
776 | |||
777 | clone = skb_clone(skb, GFP_ATOMIC); | ||
778 | if (clone == NULL) { | ||
779 | DEBUGP("Can't clone skb\n"); | ||
780 | return skb; | ||
781 | } | ||
782 | |||
783 | NFCT_FRAG6_CB(clone)->orig = skb; | ||
784 | |||
785 | if (!pskb_may_pull(clone, fhoff + sizeof(*fhdr))) { | ||
786 | DEBUGP("message is too short.\n"); | ||
787 | goto ret_orig; | ||
788 | } | ||
789 | |||
790 | clone->h.raw = clone->data + fhoff; | ||
791 | hdr = clone->nh.ipv6h; | ||
792 | fhdr = (struct frag_hdr *)clone->h.raw; | ||
793 | |||
794 | if (!(fhdr->frag_off & htons(0xFFF9))) { | ||
795 | DEBUGP("Invalid fragment offset\n"); | ||
796 | /* It is not a fragmented frame */ | ||
797 | goto ret_orig; | ||
798 | } | ||
799 | |||
800 | if (atomic_read(&nf_ct_frag6_mem) > nf_ct_frag6_high_thresh) | ||
801 | nf_ct_frag6_evictor(); | ||
802 | |||
803 | fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr); | ||
804 | if (fq == NULL) { | ||
805 | DEBUGP("Can't find and can't create new queue\n"); | ||
806 | goto ret_orig; | ||
807 | } | ||
808 | |||
809 | spin_lock(&fq->lock); | ||
810 | |||
811 | if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) { | ||
812 | spin_unlock(&fq->lock); | ||
813 | DEBUGP("Can't insert skb to queue\n"); | ||
814 | fq_put(fq); | ||
815 | goto ret_orig; | ||
816 | } | ||
817 | |||
818 | if (fq->last_in == (FIRST_IN|LAST_IN) && fq->meat == fq->len) { | ||
819 | ret_skb = nf_ct_frag6_reasm(fq, dev); | ||
820 | if (ret_skb == NULL) | ||
821 | DEBUGP("Can't reassemble fragmented packets\n"); | ||
822 | } | ||
823 | spin_unlock(&fq->lock); | ||
824 | |||
825 | fq_put(fq); | ||
826 | return ret_skb; | ||
827 | |||
828 | ret_orig: | ||
829 | kfree_skb(clone); | ||
830 | return skb; | ||
831 | } | ||
832 | |||
833 | void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb, | ||
834 | struct net_device *in, struct net_device *out, | ||
835 | int (*okfn)(struct sk_buff *)) | ||
836 | { | ||
837 | struct sk_buff *s, *s2; | ||
838 | |||
839 | for (s = NFCT_FRAG6_CB(skb)->orig; s;) { | ||
840 | nf_conntrack_put_reasm(s->nfct_reasm); | ||
841 | nf_conntrack_get_reasm(skb); | ||
842 | s->nfct_reasm = skb; | ||
843 | |||
844 | s2 = s->next; | ||
845 | NF_HOOK_THRESH(PF_INET6, hooknum, s, in, out, okfn, | ||
846 | NF_IP6_PRI_CONNTRACK_DEFRAG + 1); | ||
847 | s = s2; | ||
848 | } | ||
849 | nf_conntrack_put_reasm(skb); | ||
850 | } | ||
851 | |||
852 | int nf_ct_frag6_kfree_frags(struct sk_buff *skb) | ||
853 | { | ||
854 | struct sk_buff *s, *s2; | ||
855 | |||
856 | for (s = NFCT_FRAG6_CB(skb)->orig; s; s = s2) { | ||
857 | |||
858 | s2 = s->next; | ||
859 | kfree_skb(s); | ||
860 | } | ||
861 | |||
862 | kfree_skb(skb); | ||
863 | |||
864 | return 0; | ||
865 | } | ||
866 | |||
867 | int nf_ct_frag6_init(void) | ||
868 | { | ||
869 | nf_ct_frag6_hash_rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^ | ||
870 | (jiffies ^ (jiffies >> 6))); | ||
871 | |||
872 | init_timer(&nf_ct_frag6_secret_timer); | ||
873 | nf_ct_frag6_secret_timer.function = nf_ct_frag6_secret_rebuild; | ||
874 | nf_ct_frag6_secret_timer.expires = jiffies | ||
875 | + nf_ct_frag6_secret_interval; | ||
876 | add_timer(&nf_ct_frag6_secret_timer); | ||
877 | |||
878 | return 0; | ||
879 | } | ||
880 | |||
881 | void nf_ct_frag6_cleanup(void) | ||
882 | { | ||
883 | del_timer(&nf_ct_frag6_secret_timer); | ||
884 | nf_ct_frag6_evictor(); | ||
885 | } | ||
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index a1265a320b11..651c79b41eeb 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c | |||
@@ -174,8 +174,10 @@ int ipv6_raw_deliver(struct sk_buff *skb, int nexthdr) | |||
174 | struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC); | 174 | struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC); |
175 | 175 | ||
176 | /* Not releasing hash table! */ | 176 | /* Not releasing hash table! */ |
177 | if (clone) | 177 | if (clone) { |
178 | nf_reset(clone); | ||
178 | rawv6_rcv(sk, clone); | 179 | rawv6_rcv(sk, clone); |
180 | } | ||
179 | } | 181 | } |
180 | sk = __raw_v6_lookup(sk_next(sk), nexthdr, daddr, saddr, | 182 | sk = __raw_v6_lookup(sk_next(sk), nexthdr, daddr, saddr, |
181 | IP6CB(skb)->iif); | 183 | IP6CB(skb)->iif); |