diff options
author | David S. Miller <davem@davemloft.net> | 2012-11-30 12:01:30 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-11-30 12:01:30 -0500 |
commit | e7165030db8e932a9a968f7015cd3b2e984f8e7c (patch) | |
tree | ab46a0baf25f72b7001bb4673ba47534b81a0d2d /net/ipv6 | |
parent | bb728820fe7c42fdb838ab2745fb5fe6b18b5ffa (diff) | |
parent | 92eb1d477145b2e7780b5002e856f70b8c3d74da (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/jesse/openvswitch
Conflicts:
net/ipv6/exthdrs_core.c
Jesse Gross says:
====================
This series of improvements for 3.8/net-next contains four components:
* Support for modifying IPv6 headers
* Support for matching and setting skb->mark for better integration with
things like iptables
* Ability to recognize the EtherType for RARP packets
* Two small performance enhancements
The movement of ipv6_find_hdr() into exthdrs_core.c causes two small merge
conflicts. I left it as is but can do the merge if you want. The conflicts
are:
* ipv6_find_hdr() and ipv6_find_tlv() were both moved to the bottom of
exthdrs_core.c. Both should stay.
* A new use of ipv6_find_hdr() was added to net/netfilter/ipvs/ip_vs_core.c
after this patch. The IPVS user has two instances of the old constant
name IP6T_FH_F_FRAG which has been renamed to IP6_FH_F_FRAG.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/exthdrs_core.c | 124 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_tables.c | 103 |
2 files changed, 124 insertions, 103 deletions
diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index e7d756e19d1d..c5e83fae4df4 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c | |||
@@ -155,3 +155,127 @@ int ipv6_find_tlv(struct sk_buff *skb, int offset, int type) | |||
155 | return -1; | 155 | return -1; |
156 | } | 156 | } |
157 | EXPORT_SYMBOL_GPL(ipv6_find_tlv); | 157 | EXPORT_SYMBOL_GPL(ipv6_find_tlv); |
158 | |||
159 | /* | ||
160 | * find the offset to specified header or the protocol number of last header | ||
161 | * if target < 0. "last header" is transport protocol header, ESP, or | ||
162 | * "No next header". | ||
163 | * | ||
164 | * Note that *offset is used as input/output parameter. an if it is not zero, | ||
165 | * then it must be a valid offset to an inner IPv6 header. This can be used | ||
166 | * to explore inner IPv6 header, eg. ICMPv6 error messages. | ||
167 | * | ||
168 | * If target header is found, its offset is set in *offset and return protocol | ||
169 | * number. Otherwise, return -1. | ||
170 | * | ||
171 | * If the first fragment doesn't contain the final protocol header or | ||
172 | * NEXTHDR_NONE it is considered invalid. | ||
173 | * | ||
174 | * Note that non-1st fragment is special case that "the protocol number | ||
175 | * of last header" is "next header" field in Fragment header. In this case, | ||
176 | * *offset is meaningless and fragment offset is stored in *fragoff if fragoff | ||
177 | * isn't NULL. | ||
178 | * | ||
179 | * if flags is not NULL and it's a fragment, then the frag flag | ||
180 | * IP6_FH_F_FRAG will be set. If it's an AH header, the | ||
181 | * IP6_FH_F_AUTH flag is set and target < 0, then this function will | ||
182 | * stop at the AH header. If IP6_FH_F_SKIP_RH flag was passed, then this | ||
183 | * function will skip all those routing headers, where segements_left was 0. | ||
184 | */ | ||
185 | int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | ||
186 | int target, unsigned short *fragoff, int *flags) | ||
187 | { | ||
188 | unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); | ||
189 | u8 nexthdr = ipv6_hdr(skb)->nexthdr; | ||
190 | unsigned int len; | ||
191 | bool found; | ||
192 | |||
193 | if (fragoff) | ||
194 | *fragoff = 0; | ||
195 | |||
196 | if (*offset) { | ||
197 | struct ipv6hdr _ip6, *ip6; | ||
198 | |||
199 | ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6); | ||
200 | if (!ip6 || (ip6->version != 6)) { | ||
201 | printk(KERN_ERR "IPv6 header not found\n"); | ||
202 | return -EBADMSG; | ||
203 | } | ||
204 | start = *offset + sizeof(struct ipv6hdr); | ||
205 | nexthdr = ip6->nexthdr; | ||
206 | } | ||
207 | len = skb->len - start; | ||
208 | |||
209 | do { | ||
210 | struct ipv6_opt_hdr _hdr, *hp; | ||
211 | unsigned int hdrlen; | ||
212 | found = (nexthdr == target); | ||
213 | |||
214 | if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { | ||
215 | if (target < 0) | ||
216 | break; | ||
217 | return -ENOENT; | ||
218 | } | ||
219 | |||
220 | hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); | ||
221 | if (hp == NULL) | ||
222 | return -EBADMSG; | ||
223 | |||
224 | if (nexthdr == NEXTHDR_ROUTING) { | ||
225 | struct ipv6_rt_hdr _rh, *rh; | ||
226 | |||
227 | rh = skb_header_pointer(skb, start, sizeof(_rh), | ||
228 | &_rh); | ||
229 | if (rh == NULL) | ||
230 | return -EBADMSG; | ||
231 | |||
232 | if (flags && (*flags & IP6_FH_F_SKIP_RH) && | ||
233 | rh->segments_left == 0) | ||
234 | found = false; | ||
235 | } | ||
236 | |||
237 | if (nexthdr == NEXTHDR_FRAGMENT) { | ||
238 | unsigned short _frag_off; | ||
239 | __be16 *fp; | ||
240 | |||
241 | if (flags) /* Indicate that this is a fragment */ | ||
242 | *flags |= IP6_FH_F_FRAG; | ||
243 | fp = skb_header_pointer(skb, | ||
244 | start+offsetof(struct frag_hdr, | ||
245 | frag_off), | ||
246 | sizeof(_frag_off), | ||
247 | &_frag_off); | ||
248 | if (fp == NULL) | ||
249 | return -EBADMSG; | ||
250 | |||
251 | _frag_off = ntohs(*fp) & ~0x7; | ||
252 | if (_frag_off) { | ||
253 | if (target < 0 && | ||
254 | ((!ipv6_ext_hdr(hp->nexthdr)) || | ||
255 | hp->nexthdr == NEXTHDR_NONE)) { | ||
256 | if (fragoff) | ||
257 | *fragoff = _frag_off; | ||
258 | return hp->nexthdr; | ||
259 | } | ||
260 | return -ENOENT; | ||
261 | } | ||
262 | hdrlen = 8; | ||
263 | } else if (nexthdr == NEXTHDR_AUTH) { | ||
264 | if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0)) | ||
265 | break; | ||
266 | hdrlen = (hp->hdrlen + 2) << 2; | ||
267 | } else | ||
268 | hdrlen = ipv6_optlen(hp); | ||
269 | |||
270 | if (!found) { | ||
271 | nexthdr = hp->nexthdr; | ||
272 | len -= hdrlen; | ||
273 | start += hdrlen; | ||
274 | } | ||
275 | } while (!found); | ||
276 | |||
277 | *offset = start; | ||
278 | return nexthdr; | ||
279 | } | ||
280 | EXPORT_SYMBOL(ipv6_find_hdr); | ||
281 | |||
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 74cadd0719a5..125a90d6a795 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c | |||
@@ -2271,112 +2271,9 @@ static void __exit ip6_tables_fini(void) | |||
2271 | unregister_pernet_subsys(&ip6_tables_net_ops); | 2271 | unregister_pernet_subsys(&ip6_tables_net_ops); |
2272 | } | 2272 | } |
2273 | 2273 | ||
2274 | /* | ||
2275 | * find the offset to specified header or the protocol number of last header | ||
2276 | * if target < 0. "last header" is transport protocol header, ESP, or | ||
2277 | * "No next header". | ||
2278 | * | ||
2279 | * Note that *offset is used as input/output parameter. an if it is not zero, | ||
2280 | * then it must be a valid offset to an inner IPv6 header. This can be used | ||
2281 | * to explore inner IPv6 header, eg. ICMPv6 error messages. | ||
2282 | * | ||
2283 | * If target header is found, its offset is set in *offset and return protocol | ||
2284 | * number. Otherwise, return -1. | ||
2285 | * | ||
2286 | * If the first fragment doesn't contain the final protocol header or | ||
2287 | * NEXTHDR_NONE it is considered invalid. | ||
2288 | * | ||
2289 | * Note that non-1st fragment is special case that "the protocol number | ||
2290 | * of last header" is "next header" field in Fragment header. In this case, | ||
2291 | * *offset is meaningless and fragment offset is stored in *fragoff if fragoff | ||
2292 | * isn't NULL. | ||
2293 | * | ||
2294 | * if flags is not NULL and it's a fragment, then the frag flag IP6T_FH_F_FRAG | ||
2295 | * will be set. If it's an AH header, the IP6T_FH_F_AUTH flag is set and | ||
2296 | * target < 0, then this function will stop at the AH header. | ||
2297 | */ | ||
2298 | int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | ||
2299 | int target, unsigned short *fragoff, int *flags) | ||
2300 | { | ||
2301 | unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); | ||
2302 | u8 nexthdr = ipv6_hdr(skb)->nexthdr; | ||
2303 | unsigned int len; | ||
2304 | |||
2305 | if (fragoff) | ||
2306 | *fragoff = 0; | ||
2307 | |||
2308 | if (*offset) { | ||
2309 | struct ipv6hdr _ip6, *ip6; | ||
2310 | |||
2311 | ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6); | ||
2312 | if (!ip6 || (ip6->version != 6)) { | ||
2313 | printk(KERN_ERR "IPv6 header not found\n"); | ||
2314 | return -EBADMSG; | ||
2315 | } | ||
2316 | start = *offset + sizeof(struct ipv6hdr); | ||
2317 | nexthdr = ip6->nexthdr; | ||
2318 | } | ||
2319 | len = skb->len - start; | ||
2320 | |||
2321 | while (nexthdr != target) { | ||
2322 | struct ipv6_opt_hdr _hdr, *hp; | ||
2323 | unsigned int hdrlen; | ||
2324 | |||
2325 | if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { | ||
2326 | if (target < 0) | ||
2327 | break; | ||
2328 | return -ENOENT; | ||
2329 | } | ||
2330 | |||
2331 | hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); | ||
2332 | if (hp == NULL) | ||
2333 | return -EBADMSG; | ||
2334 | if (nexthdr == NEXTHDR_FRAGMENT) { | ||
2335 | unsigned short _frag_off; | ||
2336 | __be16 *fp; | ||
2337 | |||
2338 | if (flags) /* Indicate that this is a fragment */ | ||
2339 | *flags |= IP6T_FH_F_FRAG; | ||
2340 | fp = skb_header_pointer(skb, | ||
2341 | start+offsetof(struct frag_hdr, | ||
2342 | frag_off), | ||
2343 | sizeof(_frag_off), | ||
2344 | &_frag_off); | ||
2345 | if (fp == NULL) | ||
2346 | return -EBADMSG; | ||
2347 | |||
2348 | _frag_off = ntohs(*fp) & ~0x7; | ||
2349 | if (_frag_off) { | ||
2350 | if (target < 0 && | ||
2351 | ((!ipv6_ext_hdr(hp->nexthdr)) || | ||
2352 | hp->nexthdr == NEXTHDR_NONE)) { | ||
2353 | if (fragoff) | ||
2354 | *fragoff = _frag_off; | ||
2355 | return hp->nexthdr; | ||
2356 | } | ||
2357 | return -ENOENT; | ||
2358 | } | ||
2359 | hdrlen = 8; | ||
2360 | } else if (nexthdr == NEXTHDR_AUTH) { | ||
2361 | if (flags && (*flags & IP6T_FH_F_AUTH) && (target < 0)) | ||
2362 | break; | ||
2363 | hdrlen = (hp->hdrlen + 2) << 2; | ||
2364 | } else | ||
2365 | hdrlen = ipv6_optlen(hp); | ||
2366 | |||
2367 | nexthdr = hp->nexthdr; | ||
2368 | len -= hdrlen; | ||
2369 | start += hdrlen; | ||
2370 | } | ||
2371 | |||
2372 | *offset = start; | ||
2373 | return nexthdr; | ||
2374 | } | ||
2375 | |||
2376 | EXPORT_SYMBOL(ip6t_register_table); | 2274 | EXPORT_SYMBOL(ip6t_register_table); |
2377 | EXPORT_SYMBOL(ip6t_unregister_table); | 2275 | EXPORT_SYMBOL(ip6t_unregister_table); |
2378 | EXPORT_SYMBOL(ip6t_do_table); | 2276 | EXPORT_SYMBOL(ip6t_do_table); |
2379 | EXPORT_SYMBOL(ipv6_find_hdr); | ||
2380 | 2277 | ||
2381 | module_init(ip6_tables_init); | 2278 | module_init(ip6_tables_init); |
2382 | module_exit(ip6_tables_fini); | 2279 | module_exit(ip6_tables_fini); |