Libinfo-503.50.4.tar.gz
[apple/libinfo.git] / gen.subproj / rthdr.c
1 /* $KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 itojun Exp $ */
2
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.
30 */
31
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
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
52 /*
53 * RFC2292 API
54 */
55
56 size_t
57 inet6_rthdr_space(type, seg)
58 int type, seg;
59 {
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)));
70 #endif
71 default:
72 return (0);
73 }
74 }
75
76 struct cmsghdr *
77 inet6_rthdr_init(bp, type)
78 void *bp;
79 int type;
80 {
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));
96 #endif
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 }
104 }
105
106 /* ARGSUSED */
107 int
108 inet6_rthdr_add(cmsg, addr, flags)
109 struct cmsghdr *cmsg;
110 const struct in6_addr *addr;
111 u_int flags;
112 {
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);
136 #endif
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 }
147
148 return (0);
149 }
150
151 /* ARGSUSED */
152 int
153 inet6_rthdr_lasthop(cmsg, flags)
154 struct cmsghdr *cmsg;
155 unsigned int flags;
156 {
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 }
187
188 return (0);
189 }
190
191 #if 0
192 int
193 inet6_rthdr_reverse(in, out)
194 const struct cmsghdr *in;
195 struct cmsghdr *out;
196 {
197
198 return (-1);
199 }
200 #endif
201
202 int
203 inet6_rthdr_segments(cmsg)
204 const struct cmsghdr *cmsg;
205 {
206 struct ip6_rthdr *rthdr;
207
208 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
209
210 switch (rthdr->ip6r_type) {
211 case IPV6_RTHDR_TYPE_0:
212 {
213 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
214
215 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
216 return (-1);
217
218 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
219 }
220
221 default:
222 return (-1);
223 }
224 }
225
226 struct in6_addr *
227 inet6_rthdr_getaddr(cmsg, idx)
228 struct cmsghdr *cmsg;
229 int idx;
230 {
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 }
252
253 default:
254 return NULL;
255 }
256 }
257
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 }
292
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 */
307 }
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 */
334 }
335
336 return (bp);
337 }
338
339 int
340 inet6_rth_add(void *bp, const struct in6_addr *addr)
341 {
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 }
362
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 }
401
402 return (0);
403 }
404
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 */
427 }
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;
456 }
457 }