diff options
author | Jesse Gross <jesse@nicira.com> | 2012-11-09 20:05:07 -0500 |
---|---|---|
committer | Jesse Gross <jesse@nicira.com> | 2012-11-09 20:05:07 -0500 |
commit | f8f626754ebeca613cf1af2e6f890cfde0e74d5b (patch) | |
tree | 359723681fd28b4c6c523229dcf5c6c3fadfd60c /net/ipv6 | |
parent | c0618533815d8d92b270f03c11042ea53a8045d2 (diff) |
ipv6: Move ipv6_find_hdr() out of Netfilter code.
Open vSwitch will soon also use ipv6_find_hdr() so this moves it
out of Netfilter-specific code into a more common location.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/exthdrs_core.c | 103 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_tables.c | 103 |
2 files changed, 103 insertions, 103 deletions
diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index f73d59a14131..8ea253ad35b1 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c | |||
@@ -111,3 +111,106 @@ int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp, | |||
111 | return start; | 111 | return start; |
112 | } | 112 | } |
113 | EXPORT_SYMBOL(ipv6_skip_exthdr); | 113 | EXPORT_SYMBOL(ipv6_skip_exthdr); |
114 | |||
115 | /* | ||
116 | * find the offset to specified header or the protocol number of last header | ||
117 | * if target < 0. "last header" is transport protocol header, ESP, or | ||
118 | * "No next header". | ||
119 | * | ||
120 | * Note that *offset is used as input/output parameter. an if it is not zero, | ||
121 | * then it must be a valid offset to an inner IPv6 header. This can be used | ||
122 | * to explore inner IPv6 header, eg. ICMPv6 error messages. | ||
123 | * | ||
124 | * If target header is found, its offset is set in *offset and return protocol | ||
125 | * number. Otherwise, return -1. | ||
126 | * | ||
127 | * If the first fragment doesn't contain the final protocol header or | ||
128 | * NEXTHDR_NONE it is considered invalid. | ||
129 | * | ||
130 | * Note that non-1st fragment is special case that "the protocol number | ||
131 | * of last header" is "next header" field in Fragment header. In this case, | ||
132 | * *offset is meaningless and fragment offset is stored in *fragoff if fragoff | ||
133 | * isn't NULL. | ||
134 | * | ||
135 | * if flags is not NULL and it's a fragment, then the frag flag IP6_FH_F_FRAG | ||
136 | * will be set. If it's an AH header, the IP6_FH_F_AUTH flag is set and | ||
137 | * target < 0, then this function will stop at the AH header. | ||
138 | */ | ||
139 | int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | ||
140 | int target, unsigned short *fragoff, int *flags) | ||
141 | { | ||
142 | unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); | ||
143 | u8 nexthdr = ipv6_hdr(skb)->nexthdr; | ||
144 | unsigned int len; | ||
145 | |||
146 | if (fragoff) | ||
147 | *fragoff = 0; | ||
148 | |||
149 | if (*offset) { | ||
150 | struct ipv6hdr _ip6, *ip6; | ||
151 | |||
152 | ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6); | ||
153 | if (!ip6 || (ip6->version != 6)) { | ||
154 | printk(KERN_ERR "IPv6 header not found\n"); | ||
155 | return -EBADMSG; | ||
156 | } | ||
157 | start = *offset + sizeof(struct ipv6hdr); | ||
158 | nexthdr = ip6->nexthdr; | ||
159 | } | ||
160 | len = skb->len - start; | ||
161 | |||
162 | while (nexthdr != target) { | ||
163 | struct ipv6_opt_hdr _hdr, *hp; | ||
164 | unsigned int hdrlen; | ||
165 | |||
166 | if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { | ||
167 | if (target < 0) | ||
168 | break; | ||
169 | return -ENOENT; | ||
170 | } | ||
171 | |||
172 | hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); | ||
173 | if (hp == NULL) | ||
174 | return -EBADMSG; | ||
175 | if (nexthdr == NEXTHDR_FRAGMENT) { | ||
176 | unsigned short _frag_off; | ||
177 | __be16 *fp; | ||
178 | |||
179 | if (flags) /* Indicate that this is a fragment */ | ||
180 | *flags |= IP6_FH_F_FRAG; | ||
181 | fp = skb_header_pointer(skb, | ||
182 | start+offsetof(struct frag_hdr, | ||
183 | frag_off), | ||
184 | sizeof(_frag_off), | ||
185 | &_frag_off); | ||
186 | if (fp == NULL) | ||
187 | return -EBADMSG; | ||
188 | |||
189 | _frag_off = ntohs(*fp) & ~0x7; | ||
190 | if (_frag_off) { | ||
191 | if (target < 0 && | ||
192 | ((!ipv6_ext_hdr(hp->nexthdr)) || | ||
193 | hp->nexthdr == NEXTHDR_NONE)) { | ||
194 | if (fragoff) | ||
195 | *fragoff = _frag_off; | ||
196 | return hp->nexthdr; | ||
197 | } | ||
198 | return -ENOENT; | ||
199 | } | ||
200 | hdrlen = 8; | ||
201 | } else if (nexthdr == NEXTHDR_AUTH) { | ||
202 | if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0)) | ||
203 | break; | ||
204 | hdrlen = (hp->hdrlen + 2) << 2; | ||
205 | } else | ||
206 | hdrlen = ipv6_optlen(hp); | ||
207 | |||
208 | nexthdr = hp->nexthdr; | ||
209 | len -= hdrlen; | ||
210 | start += hdrlen; | ||
211 | } | ||
212 | |||
213 | *offset = start; | ||
214 | return nexthdr; | ||
215 | } | ||
216 | EXPORT_SYMBOL(ipv6_find_hdr); | ||
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index d7cb04506c3d..1ce4f157ce4f 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c | |||
@@ -2273,112 +2273,9 @@ static void __exit ip6_tables_fini(void) | |||
2273 | unregister_pernet_subsys(&ip6_tables_net_ops); | 2273 | unregister_pernet_subsys(&ip6_tables_net_ops); |
2274 | } | 2274 | } |
2275 | 2275 | ||
2276 | /* | ||
2277 | * find the offset to specified header or the protocol number of last header | ||
2278 | * if target < 0. "last header" is transport protocol header, ESP, or | ||
2279 | * "No next header". | ||
2280 | * | ||
2281 | * Note that *offset is used as input/output parameter. an if it is not zero, | ||
2282 | * then it must be a valid offset to an inner IPv6 header. This can be used | ||
2283 | * to explore inner IPv6 header, eg. ICMPv6 error messages. | ||
2284 | * | ||
2285 | * If target header is found, its offset is set in *offset and return protocol | ||
2286 | * number. Otherwise, return -1. | ||
2287 | * | ||
2288 | * If the first fragment doesn't contain the final protocol header or | ||
2289 | * NEXTHDR_NONE it is considered invalid. | ||
2290 | * | ||
2291 | * Note that non-1st fragment is special case that "the protocol number | ||
2292 | * of last header" is "next header" field in Fragment header. In this case, | ||
2293 | * *offset is meaningless and fragment offset is stored in *fragoff if fragoff | ||
2294 | * isn't NULL. | ||
2295 | * | ||
2296 | * if flags is not NULL and it's a fragment, then the frag flag IP6T_FH_F_FRAG | ||
2297 | * will be set. If it's an AH header, the IP6T_FH_F_AUTH flag is set and | ||
2298 | * target < 0, then this function will stop at the AH header. | ||
2299 | */ | ||
2300 | int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | ||
2301 | int target, unsigned short *fragoff, int *flags) | ||
2302 | { | ||
2303 | unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); | ||
2304 | u8 nexthdr = ipv6_hdr(skb)->nexthdr; | ||
2305 | unsigned int len; | ||
2306 | |||
2307 | if (fragoff) | ||
2308 | *fragoff = 0; | ||
2309 | |||
2310 | if (*offset) { | ||
2311 | struct ipv6hdr _ip6, *ip6; | ||
2312 | |||
2313 | ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6); | ||
2314 | if (!ip6 || (ip6->version != 6)) { | ||
2315 | printk(KERN_ERR "IPv6 header not found\n"); | ||
2316 | return -EBADMSG; | ||
2317 | } | ||
2318 | start = *offset + sizeof(struct ipv6hdr); | ||
2319 | nexthdr = ip6->nexthdr; | ||
2320 | } | ||
2321 | len = skb->len - start; | ||
2322 | |||
2323 | while (nexthdr != target) { | ||
2324 | struct ipv6_opt_hdr _hdr, *hp; | ||
2325 | unsigned int hdrlen; | ||
2326 | |||
2327 | if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { | ||
2328 | if (target < 0) | ||
2329 | break; | ||
2330 | return -ENOENT; | ||
2331 | } | ||
2332 | |||
2333 | hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); | ||
2334 | if (hp == NULL) | ||
2335 | return -EBADMSG; | ||
2336 | if (nexthdr == NEXTHDR_FRAGMENT) { | ||
2337 | unsigned short _frag_off; | ||
2338 | __be16 *fp; | ||
2339 | |||
2340 | if (flags) /* Indicate that this is a fragment */ | ||
2341 | *flags |= IP6T_FH_F_FRAG; | ||
2342 | fp = skb_header_pointer(skb, | ||
2343 | start+offsetof(struct frag_hdr, | ||
2344 | frag_off), | ||
2345 | sizeof(_frag_off), | ||
2346 | &_frag_off); | ||
2347 | if (fp == NULL) | ||
2348 | return -EBADMSG; | ||
2349 | |||
2350 | _frag_off = ntohs(*fp) & ~0x7; | ||
2351 | if (_frag_off) { | ||
2352 | if (target < 0 && | ||
2353 | ((!ipv6_ext_hdr(hp->nexthdr)) || | ||
2354 | hp->nexthdr == NEXTHDR_NONE)) { | ||
2355 | if (fragoff) | ||
2356 | *fragoff = _frag_off; | ||
2357 | return hp->nexthdr; | ||
2358 | } | ||
2359 | return -ENOENT; | ||
2360 | } | ||
2361 | hdrlen = 8; | ||
2362 | } else if (nexthdr == NEXTHDR_AUTH) { | ||
2363 | if (flags && (*flags & IP6T_FH_F_AUTH) && (target < 0)) | ||
2364 | break; | ||
2365 | hdrlen = (hp->hdrlen + 2) << 2; | ||
2366 | } else | ||
2367 | hdrlen = ipv6_optlen(hp); | ||
2368 | |||
2369 | nexthdr = hp->nexthdr; | ||
2370 | len -= hdrlen; | ||
2371 | start += hdrlen; | ||
2372 | } | ||
2373 | |||
2374 | *offset = start; | ||
2375 | return nexthdr; | ||
2376 | } | ||
2377 | |||
2378 | EXPORT_SYMBOL(ip6t_register_table); | 2276 | EXPORT_SYMBOL(ip6t_register_table); |
2379 | EXPORT_SYMBOL(ip6t_unregister_table); | 2277 | EXPORT_SYMBOL(ip6t_unregister_table); |
2380 | EXPORT_SYMBOL(ip6t_do_table); | 2278 | EXPORT_SYMBOL(ip6t_do_table); |
2381 | EXPORT_SYMBOL(ipv6_find_hdr); | ||
2382 | 2279 | ||
2383 | module_init(ip6_tables_init); | 2280 | module_init(ip6_tables_init); |
2384 | module_exit(ip6_tables_fini); | 2281 | module_exit(ip6_tables_fini); |