aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2012-11-30 12:01:30 -0500
committerDavid S. Miller <davem@davemloft.net>2012-11-30 12:01:30 -0500
commite7165030db8e932a9a968f7015cd3b2e984f8e7c (patch)
treeab46a0baf25f72b7001bb4673ba47534b81a0d2d /net/ipv6
parentbb728820fe7c42fdb838ab2745fb5fe6b18b5ffa (diff)
parent92eb1d477145b2e7780b5002e856f70b8c3d74da (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.c124
-rw-r--r--net/ipv6/netfilter/ip6_tables.c103
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}
157EXPORT_SYMBOL_GPL(ipv6_find_tlv); 157EXPORT_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 */
185int 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}
280EXPORT_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 */
2298int 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
2376EXPORT_SYMBOL(ip6t_register_table); 2274EXPORT_SYMBOL(ip6t_register_table);
2377EXPORT_SYMBOL(ip6t_unregister_table); 2275EXPORT_SYMBOL(ip6t_unregister_table);
2378EXPORT_SYMBOL(ip6t_do_table); 2276EXPORT_SYMBOL(ip6t_do_table);
2379EXPORT_SYMBOL(ipv6_find_hdr);
2380 2277
2381module_init(ip6_tables_init); 2278module_init(ip6_tables_init);
2382module_exit(ip6_tables_fini); 2279module_exit(ip6_tables_fini);