1
2
3
4
5#include <linux/export.h>
6#include <net/ipv6.h>
7
8
9
10
11
12bool ipv6_ext_hdr(u8 nexthdr)
13{
14
15
16
17 return (nexthdr == NEXTHDR_HOP) ||
18 (nexthdr == NEXTHDR_ROUTING) ||
19 (nexthdr == NEXTHDR_FRAGMENT) ||
20 (nexthdr == NEXTHDR_AUTH) ||
21 (nexthdr == NEXTHDR_NONE) ||
22 (nexthdr == NEXTHDR_DEST);
23}
24EXPORT_SYMBOL(ipv6_ext_hdr);
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
72 __be16 *frag_offp)
73{
74 u8 nexthdr = *nexthdrp;
75
76 *frag_offp = 0;
77
78 while (ipv6_ext_hdr(nexthdr)) {
79 struct ipv6_opt_hdr _hdr, *hp;
80 int hdrlen;
81
82 if (nexthdr == NEXTHDR_NONE)
83 return -1;
84 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
85 if (!hp)
86 return -1;
87 if (nexthdr == NEXTHDR_FRAGMENT) {
88 __be16 _frag_off, *fp;
89 fp = skb_header_pointer(skb,
90 start+offsetof(struct frag_hdr,
91 frag_off),
92 sizeof(_frag_off),
93 &_frag_off);
94 if (!fp)
95 return -1;
96
97 *frag_offp = *fp;
98 if (ntohs(*frag_offp) & ~0x7)
99 break;
100 hdrlen = 8;
101 } else if (nexthdr == NEXTHDR_AUTH)
102 hdrlen = ipv6_authlen(hp);
103 else
104 hdrlen = ipv6_optlen(hp);
105
106 nexthdr = hp->nexthdr;
107 start += hdrlen;
108 }
109
110 *nexthdrp = nexthdr;
111 return start;
112}
113EXPORT_SYMBOL(ipv6_skip_exthdr);
114
115int ipv6_find_tlv(const struct sk_buff *skb, int offset, int type)
116{
117 const unsigned char *nh = skb_network_header(skb);
118 int packet_len = skb_tail_pointer(skb) - skb_network_header(skb);
119 struct ipv6_opt_hdr *hdr;
120 int len;
121
122 if (offset + 2 > packet_len)
123 goto bad;
124 hdr = (struct ipv6_opt_hdr *)(nh + offset);
125 len = ((hdr->hdrlen + 1) << 3);
126
127 if (offset + len > packet_len)
128 goto bad;
129
130 offset += 2;
131 len -= 2;
132
133 while (len > 0) {
134 int opttype = nh[offset];
135 int optlen;
136
137 if (opttype == type)
138 return offset;
139
140 switch (opttype) {
141 case IPV6_TLV_PAD1:
142 optlen = 1;
143 break;
144 default:
145 optlen = nh[offset + 1] + 2;
146 if (optlen > len)
147 goto bad;
148 break;
149 }
150 offset += optlen;
151 len -= optlen;
152 }
153
154 bad:
155 return -1;
156}
157EXPORT_SYMBOL_GPL(ipv6_find_tlv);
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
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 bool found;
191
192 if (fragoff)
193 *fragoff = 0;
194
195 if (*offset) {
196 struct ipv6hdr _ip6, *ip6;
197
198 ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
199 if (!ip6 || (ip6->version != 6)) {
200 printk(KERN_ERR "IPv6 header not found\n");
201 return -EBADMSG;
202 }
203 start = *offset + sizeof(struct ipv6hdr);
204 nexthdr = ip6->nexthdr;
205 }
206
207 do {
208 struct ipv6_opt_hdr _hdr, *hp;
209 unsigned int hdrlen;
210 found = (nexthdr == target);
211
212 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
213 if (target < 0 || found)
214 break;
215 return -ENOENT;
216 }
217
218 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
219 if (!hp)
220 return -EBADMSG;
221
222 if (nexthdr == NEXTHDR_ROUTING) {
223 struct ipv6_rt_hdr _rh, *rh;
224
225 rh = skb_header_pointer(skb, start, sizeof(_rh),
226 &_rh);
227 if (!rh)
228 return -EBADMSG;
229
230 if (flags && (*flags & IP6_FH_F_SKIP_RH) &&
231 rh->segments_left == 0)
232 found = false;
233 }
234
235 if (nexthdr == NEXTHDR_FRAGMENT) {
236 unsigned short _frag_off;
237 __be16 *fp;
238
239 if (flags)
240 *flags |= IP6_FH_F_FRAG;
241 fp = skb_header_pointer(skb,
242 start+offsetof(struct frag_hdr,
243 frag_off),
244 sizeof(_frag_off),
245 &_frag_off);
246 if (!fp)
247 return -EBADMSG;
248
249 _frag_off = ntohs(*fp) & ~0x7;
250 if (_frag_off) {
251 if (target < 0 &&
252 ((!ipv6_ext_hdr(hp->nexthdr)) ||
253 hp->nexthdr == NEXTHDR_NONE)) {
254 if (fragoff)
255 *fragoff = _frag_off;
256 return hp->nexthdr;
257 }
258 if (!found)
259 return -ENOENT;
260 if (fragoff)
261 *fragoff = _frag_off;
262 break;
263 }
264 hdrlen = 8;
265 } else if (nexthdr == NEXTHDR_AUTH) {
266 if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0))
267 break;
268 hdrlen = (hp->hdrlen + 2) << 2;
269 } else
270 hdrlen = ipv6_optlen(hp);
271
272 if (!found) {
273 nexthdr = hp->nexthdr;
274 start += hdrlen;
275 }
276 } while (!found);
277
278 *offset = start;
279 return nexthdr;
280}
281EXPORT_SYMBOL(ipv6_find_hdr);
282