658e16ac659c514962da8d6ef004846c5e03fdc5
[openvswitch] / datapath / linux / compat / exthdrs_core.c
1 #include <linux/ipv6.h>
2 #include <net/ipv6.h>
3
4 /* This function is upstream but not the version which supplies the
5  * fragment offset.  We plan to propose the extended version.
6  */
7 int rpl_ipv6_skip_exthdr(const struct sk_buff *skb, int start,
8                          u8 *nexthdrp, __be16 *frag_offp)
9 {
10         u8 nexthdr = *nexthdrp;
11
12         *frag_offp = 0;
13
14         while (ipv6_ext_hdr(nexthdr)) {
15                 struct ipv6_opt_hdr _hdr, *hp;
16                 int hdrlen;
17
18                 if (nexthdr == NEXTHDR_NONE)
19                         return -1;
20                 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
21                 if (hp == NULL)
22                         return -1;
23                 if (nexthdr == NEXTHDR_FRAGMENT) {
24                         __be16 _frag_off, *fp;
25                         fp = skb_header_pointer(skb,
26                                                 start+offsetof(struct frag_hdr,
27                                                                frag_off),
28                                                 sizeof(_frag_off),
29                                                 &_frag_off);
30                         if (fp == NULL)
31                                 return -1;
32
33                         *frag_offp = *fp;
34                         if (ntohs(*frag_offp) & ~0x7)
35                                 break;
36                         hdrlen = 8;
37                 } else if (nexthdr == NEXTHDR_AUTH)
38                         hdrlen = (hp->hdrlen+2)<<2;
39                 else
40                         hdrlen = ipv6_optlen(hp);
41
42                 nexthdr = hp->nexthdr;
43                 start += hdrlen;
44         }
45
46         *nexthdrp = nexthdr;
47         return start;
48 }