netdev-dpdk: fix mbuf leaks
[cascardo/ovs.git] / datapath / linux / compat / exthdrs_core.c
1 #include <linux/ipv6.h>
2 #include <linux/version.h>
3 #include <net/ipv6.h>
4
5 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
6 int rpl_ipv6_skip_exthdr(const struct sk_buff *skb, int start,
7                          u8 *nexthdrp, __be16 *frag_offp)
8 {
9         u8 nexthdr = *nexthdrp;
10
11         *frag_offp = 0;
12
13         while (ipv6_ext_hdr(nexthdr)) {
14                 struct ipv6_opt_hdr _hdr, *hp;
15                 int hdrlen;
16
17                 if (nexthdr == NEXTHDR_NONE)
18                         return -1;
19                 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
20                 if (hp == NULL)
21                         return -1;
22                 if (nexthdr == NEXTHDR_FRAGMENT) {
23                         __be16 _frag_off, *fp;
24                         fp = skb_header_pointer(skb,
25                                                 start+offsetof(struct frag_hdr,
26                                                                frag_off),
27                                                 sizeof(_frag_off),
28                                                 &_frag_off);
29                         if (fp == NULL)
30                                 return -1;
31
32                         *frag_offp = *fp;
33                         if (ntohs(*frag_offp) & ~0x7)
34                                 break;
35                         hdrlen = 8;
36                 } else if (nexthdr == NEXTHDR_AUTH)
37                         hdrlen = (hp->hdrlen+2)<<2;
38                 else
39                         hdrlen = ipv6_optlen(hp);
40
41                 nexthdr = hp->nexthdr;
42                 start += hdrlen;
43         }
44
45         *nexthdrp = nexthdr;
46         return start;
47 }
48 EXPORT_SYMBOL_GPL(rpl_ipv6_skip_exthdr);
49 #endif /* Kernel version < 3.3 */
50
51 #ifndef HAVE_IP6_FH_F_SKIP_RH
52 /*
53  * find the offset to specified header or the protocol number of last header
54  * if target < 0. "last header" is transport protocol header, ESP, or
55  * "No next header".
56  *
57  * Note that *offset is used as input/output parameter. an if it is not zero,
58  * then it must be a valid offset to an inner IPv6 header. This can be used
59  * to explore inner IPv6 header, eg. ICMPv6 error messages.
60  *
61  * If target header is found, its offset is set in *offset and return protocol
62  * number. Otherwise, return -1.
63  *
64  * If the first fragment doesn't contain the final protocol header or
65  * NEXTHDR_NONE it is considered invalid.
66  *
67  * Note that non-1st fragment is special case that "the protocol number
68  * of last header" is "next header" field in Fragment header. In this case,
69  * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
70  * isn't NULL.
71  *
72  * if flags is not NULL and it's a fragment, then the frag flag
73  * IP6_FH_F_FRAG will be set. If it's an AH header, the
74  * IP6_FH_F_AUTH flag is set and target < 0, then this function will
75  * stop at the AH header. If IP6_FH_F_SKIP_RH flag was passed, then this
76  * function will skip all those routing headers, where segements_left was 0.
77  */
78 int rpl_ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
79                   int target, unsigned short *fragoff, int *flags)
80 {
81         unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
82         u8 nexthdr = ipv6_hdr(skb)->nexthdr;
83         unsigned int len;
84         bool found;
85
86         if (fragoff)
87                 *fragoff = 0;
88
89         if (*offset) {
90                 struct ipv6hdr _ip6, *ip6;
91
92                 ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
93                 if (!ip6 || (ip6->version != 6)) {
94                         printk(KERN_ERR "IPv6 header not found\n");
95                         return -EBADMSG;
96                 }
97                 start = *offset + sizeof(struct ipv6hdr);
98                 nexthdr = ip6->nexthdr;
99         }
100         len = skb->len - start;
101
102         do {
103                 struct ipv6_opt_hdr _hdr, *hp;
104                 unsigned int hdrlen;
105                 found = (nexthdr == target);
106
107                 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
108                         if (target < 0 || found)
109                                 break;
110                         return -ENOENT;
111                 }
112
113                 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
114                 if (hp == NULL)
115                         return -EBADMSG;
116
117                 if (nexthdr == NEXTHDR_ROUTING) {
118                         struct ipv6_rt_hdr _rh, *rh;
119
120                         rh = skb_header_pointer(skb, start, sizeof(_rh),
121                                                 &_rh);
122                         if (rh == NULL)
123                                 return -EBADMSG;
124
125                         if (flags && (*flags & IP6_FH_F_SKIP_RH) &&
126                             rh->segments_left == 0)
127                                 found = false;
128                 }
129
130                 if (nexthdr == NEXTHDR_FRAGMENT) {
131                         unsigned short _frag_off;
132                         __be16 *fp;
133
134                         if (flags)      /* Indicate that this is a fragment */
135                                 *flags |= IP6_FH_F_FRAG;
136                         fp = skb_header_pointer(skb,
137                                                 start+offsetof(struct frag_hdr,
138                                                                frag_off),
139                                                 sizeof(_frag_off),
140                                                 &_frag_off);
141                         if (fp == NULL)
142                                 return -EBADMSG;
143
144                         _frag_off = ntohs(*fp) & ~0x7;
145                         if (_frag_off) {
146                                 if (target < 0 &&
147                                     ((!ipv6_ext_hdr(hp->nexthdr)) ||
148                                      hp->nexthdr == NEXTHDR_NONE)) {
149                                         if (fragoff)
150                                                 *fragoff = _frag_off;
151                                         return hp->nexthdr;
152                                 }
153                                 return -ENOENT;
154                         }
155                         hdrlen = 8;
156                 } else if (nexthdr == NEXTHDR_AUTH) {
157                         if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0))
158                                 break;
159                         hdrlen = (hp->hdrlen + 2) << 2;
160                 } else
161                         hdrlen = ipv6_optlen(hp);
162
163                 if (!found) {
164                         nexthdr = hp->nexthdr;
165                         len -= hdrlen;
166                         start += hdrlen;
167                 }
168         } while (!found);
169
170         *offset = start;
171         return nexthdr;
172 }
173 EXPORT_SYMBOL_GPL(rpl_ipv6_find_hdr);
174
175 #endif