]>
Commit | Line | Data |
---|---|---|
e44d8d47 A |
1 | /* $KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 itojun Exp $ */ |
2 | ||
3b7c7bd7 A |
3 | /* |
4 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * 1. Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions and the following disclaimer. | |
12 | * 2. Redistributions in binary form must reproduce the above copyright | |
13 | * notice, this list of conditions and the following disclaimer in the | |
14 | * documentation and/or other materials provided with the distribution. | |
15 | * 3. Neither the name of the project nor the names of its contributors | |
16 | * may be used to endorse or promote products derived from this software | |
17 | * without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | |
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
29 | * SUCH DAMAGE. | |
3b7c7bd7 A |
30 | */ |
31 | ||
e44d8d47 A |
32 | /* __FBSDID("$FreeBSD: src/lib/libc/net/rthdr.c,v 1.9.10.1.4.1 2010/06/14 02:09:06 kensmith Exp $"); */ |
33 | ||
34 | /* | |
35 | * These routines support RFC 2292. | |
36 | * __APPLE_USE_RFC_2292 selects the appropriate API in <netinet6/in6.h> | |
37 | */ | |
38 | #define __APPLE_USE_RFC_2292 | |
39 | ||
40 | #include <sys/cdefs.h> | |
41 | ||
3b7c7bd7 A |
42 | #include <sys/param.h> |
43 | #include <sys/types.h> | |
44 | #include <sys/socket.h> | |
45 | ||
46 | #include <netinet/in.h> | |
47 | #include <netinet/ip6.h> | |
48 | ||
49 | #include <string.h> | |
50 | #include <stdio.h> | |
51 | ||
e44d8d47 A |
52 | /* |
53 | * RFC2292 API | |
54 | */ | |
55 | ||
3b7c7bd7 A |
56 | size_t |
57 | inet6_rthdr_space(type, seg) | |
e44d8d47 | 58 | int type, seg; |
3b7c7bd7 | 59 | { |
e44d8d47 A |
60 | switch (type) { |
61 | case IPV6_RTHDR_TYPE_0: | |
62 | if (seg < 1 || seg > 23) | |
63 | return (0); | |
64 | #ifdef COMPAT_RFC2292 | |
65 | return (CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) + | |
66 | sizeof(struct ip6_rthdr0))); | |
67 | #else | |
68 | return (CMSG_SPACE(sizeof(struct in6_addr) * seg + | |
69 | sizeof(struct ip6_rthdr0))); | |
3b7c7bd7 | 70 | #endif |
e44d8d47 A |
71 | default: |
72 | return (0); | |
73 | } | |
3b7c7bd7 A |
74 | } |
75 | ||
76 | struct cmsghdr * | |
77 | inet6_rthdr_init(bp, type) | |
e44d8d47 A |
78 | void *bp; |
79 | int type; | |
3b7c7bd7 | 80 | { |
e44d8d47 A |
81 | struct cmsghdr *ch = (struct cmsghdr *)bp; |
82 | struct ip6_rthdr *rthdr; | |
83 | ||
84 | rthdr = (struct ip6_rthdr *)CMSG_DATA(ch); | |
85 | ||
86 | ch->cmsg_level = IPPROTO_IPV6; | |
87 | ch->cmsg_type = IPV6_RTHDR; | |
88 | ||
89 | switch (type) { | |
90 | case IPV6_RTHDR_TYPE_0: | |
91 | #ifdef COMPAT_RFC2292 | |
92 | ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - | |
93 | sizeof(struct in6_addr)); | |
94 | #else | |
95 | ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0)); | |
3b7c7bd7 | 96 | #endif |
e44d8d47 A |
97 | |
98 | bzero(rthdr, sizeof(struct ip6_rthdr0)); | |
99 | rthdr->ip6r_type = IPV6_RTHDR_TYPE_0; | |
100 | return (ch); | |
101 | default: | |
102 | return (NULL); | |
103 | } | |
3b7c7bd7 A |
104 | } |
105 | ||
e44d8d47 | 106 | /* ARGSUSED */ |
3b7c7bd7 A |
107 | int |
108 | inet6_rthdr_add(cmsg, addr, flags) | |
e44d8d47 A |
109 | struct cmsghdr *cmsg; |
110 | const struct in6_addr *addr; | |
111 | u_int flags; | |
3b7c7bd7 | 112 | { |
e44d8d47 A |
113 | struct ip6_rthdr *rthdr; |
114 | ||
115 | rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); | |
116 | ||
117 | switch (rthdr->ip6r_type) { | |
118 | case IPV6_RTHDR_TYPE_0: | |
119 | { | |
120 | struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; | |
121 | if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) | |
122 | return (-1); | |
123 | if (rt0->ip6r0_segleft == 23) | |
124 | return (-1); | |
125 | ||
126 | #ifdef COMPAT_RFC1883 /* XXX */ | |
127 | if (flags == IPV6_RTHDR_STRICT) { | |
128 | int c, b; | |
129 | c = rt0->ip6r0_segleft / 8; | |
130 | b = rt0->ip6r0_segleft % 8; | |
131 | rt0->ip6r0_slmap[c] |= (1 << (7 - b)); | |
132 | } | |
133 | #else | |
134 | if (flags != IPV6_RTHDR_LOOSE) | |
135 | return (-1); | |
3b7c7bd7 | 136 | #endif |
e44d8d47 A |
137 | rt0->ip6r0_segleft++; |
138 | bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3), | |
139 | sizeof(struct in6_addr)); | |
140 | rt0->ip6r0_len += sizeof(struct in6_addr) >> 3; | |
141 | cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3); | |
142 | break; | |
143 | } | |
144 | default: | |
145 | return (-1); | |
146 | } | |
3b7c7bd7 | 147 | |
e44d8d47 | 148 | return (0); |
3b7c7bd7 A |
149 | } |
150 | ||
e44d8d47 | 151 | /* ARGSUSED */ |
3b7c7bd7 A |
152 | int |
153 | inet6_rthdr_lasthop(cmsg, flags) | |
e44d8d47 A |
154 | struct cmsghdr *cmsg; |
155 | unsigned int flags; | |
3b7c7bd7 | 156 | { |
e44d8d47 A |
157 | struct ip6_rthdr *rthdr; |
158 | ||
159 | rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); | |
160 | ||
161 | switch (rthdr->ip6r_type) { | |
162 | case IPV6_RTHDR_TYPE_0: | |
163 | { | |
164 | struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; | |
165 | #ifdef COMPAT_RFC1883 /* XXX */ | |
166 | if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) | |
167 | return (-1); | |
168 | #endif /* COMPAT_RFC1883 */ | |
169 | if (rt0->ip6r0_segleft > 23) | |
170 | return (-1); | |
171 | #ifdef COMPAT_RFC1883 /* XXX */ | |
172 | if (flags == IPV6_RTHDR_STRICT) { | |
173 | int c, b; | |
174 | c = rt0->ip6r0_segleft / 8; | |
175 | b = rt0->ip6r0_segleft % 8; | |
176 | rt0->ip6r0_slmap[c] |= (1 << (7 - b)); | |
177 | } | |
178 | #else | |
179 | if (flags != IPV6_RTHDR_LOOSE) | |
180 | return (-1); | |
181 | #endif /* COMPAT_RFC1883 */ | |
182 | break; | |
183 | } | |
184 | default: | |
185 | return (-1); | |
186 | } | |
3b7c7bd7 | 187 | |
e44d8d47 | 188 | return (0); |
3b7c7bd7 A |
189 | } |
190 | ||
191 | #if 0 | |
192 | int | |
193 | inet6_rthdr_reverse(in, out) | |
e44d8d47 A |
194 | const struct cmsghdr *in; |
195 | struct cmsghdr *out; | |
3b7c7bd7 | 196 | { |
e44d8d47 A |
197 | |
198 | return (-1); | |
3b7c7bd7 A |
199 | } |
200 | #endif | |
201 | ||
202 | int | |
203 | inet6_rthdr_segments(cmsg) | |
e44d8d47 | 204 | const struct cmsghdr *cmsg; |
3b7c7bd7 | 205 | { |
e44d8d47 | 206 | struct ip6_rthdr *rthdr; |
3b7c7bd7 | 207 | |
e44d8d47 | 208 | rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); |
3b7c7bd7 | 209 | |
e44d8d47 A |
210 | switch (rthdr->ip6r_type) { |
211 | case IPV6_RTHDR_TYPE_0: | |
212 | { | |
213 | struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; | |
3b7c7bd7 | 214 | |
e44d8d47 A |
215 | if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) |
216 | return (-1); | |
3b7c7bd7 | 217 | |
e44d8d47 A |
218 | return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); |
219 | } | |
3b7c7bd7 | 220 | |
e44d8d47 A |
221 | default: |
222 | return (-1); | |
223 | } | |
3b7c7bd7 A |
224 | } |
225 | ||
226 | struct in6_addr * | |
e44d8d47 A |
227 | inet6_rthdr_getaddr(cmsg, idx) |
228 | struct cmsghdr *cmsg; | |
229 | int idx; | |
3b7c7bd7 | 230 | { |
e44d8d47 A |
231 | struct ip6_rthdr *rthdr; |
232 | ||
233 | rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); | |
234 | ||
235 | switch (rthdr->ip6r_type) { | |
236 | case IPV6_RTHDR_TYPE_0: | |
237 | { | |
238 | struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; | |
239 | int naddr; | |
240 | ||
241 | if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) | |
242 | return NULL; | |
243 | naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); | |
244 | if (idx <= 0 || naddr < idx) | |
245 | return NULL; | |
246 | #ifdef COMPAT_RFC2292 | |
247 | return (((struct in6_addr *)(rt0 + 1)) + idx - 1); | |
248 | #else | |
249 | return (((struct in6_addr *)(rt0 + 1)) + idx); | |
250 | #endif | |
251 | } | |
3b7c7bd7 | 252 | |
e44d8d47 A |
253 | default: |
254 | return NULL; | |
255 | } | |
256 | } | |
3b7c7bd7 | 257 | |
e44d8d47 A |
258 | int |
259 | inet6_rthdr_getflags(cmsg, idx) | |
260 | const struct cmsghdr *cmsg; | |
261 | int idx; | |
262 | { | |
263 | struct ip6_rthdr *rthdr; | |
264 | ||
265 | rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); | |
266 | ||
267 | switch (rthdr->ip6r_type) { | |
268 | case IPV6_RTHDR_TYPE_0: | |
269 | { | |
270 | struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; | |
271 | int naddr; | |
272 | ||
273 | if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) | |
274 | return (-1); | |
275 | naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); | |
276 | if (idx < 0 || naddr < idx) | |
277 | return (-1); | |
278 | #ifdef COMPAT_RFC1883 /* XXX */ | |
279 | if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8))) | |
280 | return IPV6_RTHDR_STRICT; | |
281 | else | |
282 | return IPV6_RTHDR_LOOSE; | |
283 | #else | |
284 | return IPV6_RTHDR_LOOSE; | |
285 | #endif /* COMPAT_RFC1883 */ | |
286 | } | |
287 | ||
288 | default: | |
289 | return (-1); | |
290 | } | |
291 | } | |
3b7c7bd7 | 292 | |
e44d8d47 A |
293 | /* |
294 | * RFC3542 API | |
295 | */ | |
296 | ||
297 | socklen_t | |
298 | inet6_rth_space(int type, int segments) | |
299 | { | |
300 | switch (type) { | |
301 | case IPV6_RTHDR_TYPE_0: | |
302 | if ((segments >= 0) && (segments <= 127)) | |
303 | return (((segments * 2) + 1) << 3); | |
304 | /* FALLTHROUGH */ | |
305 | default: | |
306 | return (0); /* type not suppported */ | |
3b7c7bd7 | 307 | } |
e44d8d47 A |
308 | } |
309 | ||
310 | void * | |
311 | inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments) | |
312 | { | |
313 | struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; | |
314 | struct ip6_rthdr0 *rth0; | |
315 | ||
316 | switch (type) { | |
317 | case IPV6_RTHDR_TYPE_0: | |
318 | /* length validation */ | |
319 | if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments)) | |
320 | return (NULL); | |
321 | /* segment validation */ | |
322 | if ((segments < 0) || (segments > 127)) | |
323 | return (NULL); | |
324 | ||
325 | memset(bp, 0, bp_len); | |
326 | rth0 = (struct ip6_rthdr0 *)rth; | |
327 | rth0->ip6r0_len = segments * 2; | |
328 | rth0->ip6r0_type = IPV6_RTHDR_TYPE_0; | |
329 | rth0->ip6r0_segleft = 0; | |
330 | rth0->ip6r0_reserved = 0; | |
331 | break; | |
332 | default: | |
333 | return (NULL); /* type not supported */ | |
3b7c7bd7 | 334 | } |
3b7c7bd7 | 335 | |
e44d8d47 | 336 | return (bp); |
3b7c7bd7 A |
337 | } |
338 | ||
339 | int | |
e44d8d47 | 340 | inet6_rth_add(void *bp, const struct in6_addr *addr) |
3b7c7bd7 | 341 | { |
e44d8d47 A |
342 | struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; |
343 | struct ip6_rthdr0 *rth0; | |
344 | struct in6_addr *nextaddr; | |
345 | ||
346 | switch (rth->ip6r_type) { | |
347 | case IPV6_RTHDR_TYPE_0: | |
348 | rth0 = (struct ip6_rthdr0 *)rth; | |
349 | /* Don't exceed the number of stated segments */ | |
350 | if (rth0->ip6r0_segleft == (rth0->ip6r0_len / 2)) | |
351 | return (-1); | |
352 | nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft; | |
353 | *nextaddr = *addr; | |
354 | rth0->ip6r0_segleft++; | |
355 | break; | |
356 | default: | |
357 | return (-1); /* type not supported */ | |
358 | } | |
359 | ||
360 | return (0); | |
361 | } | |
3b7c7bd7 | 362 | |
e44d8d47 A |
363 | int |
364 | inet6_rth_reverse(const void *in, void *out) | |
365 | { | |
366 | struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in; | |
367 | struct ip6_rthdr0 *rth0_in, *rth0_out; | |
368 | int i, segments; | |
369 | ||
370 | switch (rth_in->ip6r_type) { | |
371 | case IPV6_RTHDR_TYPE_0: | |
372 | rth0_in = (struct ip6_rthdr0 *)in; | |
373 | rth0_out = (struct ip6_rthdr0 *)out; | |
374 | ||
375 | /* parameter validation XXX too paranoid? */ | |
376 | if (rth0_in->ip6r0_len % 2) | |
377 | return (-1); | |
378 | segments = rth0_in->ip6r0_len / 2; | |
379 | ||
380 | /* we can't use memcpy here, since in and out may overlap */ | |
381 | memmove((void *)rth0_out, (void *)rth0_in, | |
382 | ((rth0_in->ip6r0_len) + 1) << 3); | |
383 | rth0_out->ip6r0_segleft = segments; | |
384 | ||
385 | /* reverse the addresses */ | |
386 | for (i = 0; i < segments / 2; i++) { | |
387 | struct in6_addr addr_tmp, *addr1, *addr2; | |
388 | ||
389 | addr1 = (struct in6_addr *)(rth0_out + 1) + i; | |
390 | addr2 = (struct in6_addr *)(rth0_out + 1) + | |
391 | (segments - i - 1); | |
392 | addr_tmp = *addr1; | |
393 | *addr1 = *addr2; | |
394 | *addr2 = addr_tmp; | |
395 | } | |
396 | ||
397 | break; | |
398 | default: | |
399 | return (-1); /* type not supported */ | |
400 | } | |
3b7c7bd7 | 401 | |
e44d8d47 A |
402 | return (0); |
403 | } | |
3b7c7bd7 | 404 | |
e44d8d47 A |
405 | int |
406 | inet6_rth_segments(const void *bp) | |
407 | { | |
408 | struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; | |
409 | struct ip6_rthdr0 *rh0; | |
410 | int addrs; | |
411 | ||
412 | switch (rh->ip6r_type) { | |
413 | case IPV6_RTHDR_TYPE_0: | |
414 | rh0 = (struct ip6_rthdr0 *)bp; | |
415 | ||
416 | /* | |
417 | * Validation for a type-0 routing header. | |
418 | * Is this too strict? | |
419 | */ | |
420 | if ((rh0->ip6r0_len % 2) != 0 || | |
421 | (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) | |
422 | return (-1); | |
423 | ||
424 | return (addrs); | |
425 | default: | |
426 | return (-1); /* unknown type */ | |
3b7c7bd7 | 427 | } |
e44d8d47 A |
428 | } |
429 | ||
430 | struct in6_addr * | |
431 | inet6_rth_getaddr(const void *bp, int idx) | |
432 | { | |
433 | struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; | |
434 | struct ip6_rthdr0 *rh0; | |
435 | int addrs; | |
436 | ||
437 | switch (rh->ip6r_type) { | |
438 | case IPV6_RTHDR_TYPE_0: | |
439 | rh0 = (struct ip6_rthdr0 *)bp; | |
440 | ||
441 | /* | |
442 | * Validation for a type-0 routing header. | |
443 | * Is this too strict? | |
444 | */ | |
445 | if ((rh0->ip6r0_len % 2) != 0 || | |
446 | (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) | |
447 | return (NULL); | |
448 | ||
449 | if (idx < 0 || addrs <= idx) | |
450 | return (NULL); | |
451 | ||
452 | return (((struct in6_addr *)(rh0 + 1)) + idx); | |
453 | default: | |
454 | return (NULL); /* unknown type */ | |
455 | break; | |
3b7c7bd7 | 456 | } |
3b7c7bd7 | 457 | } |