1 /* $KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 itojun Exp $ */
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <netinet/ip6.h>
50 inet6_rthdr_space(type, seg)
54 case IPV6_RTHDR_TYPE_0:
55 if (seg < 1 || seg > 23)
58 return (CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) +
59 sizeof(struct ip6_rthdr0)));
61 return (CMSG_SPACE(sizeof(struct in6_addr) * seg +
62 sizeof(struct ip6_rthdr0)));
70 inet6_rthdr_init(bp, type)
74 struct cmsghdr *ch = (struct cmsghdr *)bp;
75 struct ip6_rthdr *rthdr;
77 rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
79 ch->cmsg_level = IPPROTO_IPV6;
80 ch->cmsg_type = IPV6_RTHDR;
83 case IPV6_RTHDR_TYPE_0:
85 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) -
86 sizeof(struct in6_addr));
88 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0));
91 bzero(rthdr, sizeof(struct ip6_rthdr0));
92 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
101 inet6_rthdr_add(cmsg, addr, flags)
102 struct cmsghdr *cmsg;
103 const struct in6_addr *addr;
106 struct ip6_rthdr *rthdr;
108 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
110 switch (rthdr->ip6r_type) {
111 case IPV6_RTHDR_TYPE_0:
113 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
114 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
116 if (rt0->ip6r0_segleft == 23)
119 #ifdef COMPAT_RFC1883 /* XXX */
120 if (flags == IPV6_RTHDR_STRICT) {
122 c = rt0->ip6r0_segleft / 8;
123 b = rt0->ip6r0_segleft % 8;
124 rt0->ip6r0_slmap[c] |= (1 << (7 - b));
127 if (flags != IPV6_RTHDR_LOOSE)
130 rt0->ip6r0_segleft++;
131 bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3),
132 sizeof(struct in6_addr));
133 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
134 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
146 inet6_rthdr_lasthop(cmsg, flags)
147 struct cmsghdr *cmsg;
150 struct ip6_rthdr *rthdr;
152 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
154 switch (rthdr->ip6r_type) {
155 case IPV6_RTHDR_TYPE_0:
157 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
158 #ifdef COMPAT_RFC1883 /* XXX */
159 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
161 #endif /* COMPAT_RFC1883 */
162 if (rt0->ip6r0_segleft > 23)
164 #ifdef COMPAT_RFC1883 /* XXX */
165 if (flags == IPV6_RTHDR_STRICT) {
167 c = rt0->ip6r0_segleft / 8;
168 b = rt0->ip6r0_segleft % 8;
169 rt0->ip6r0_slmap[c] |= (1 << (7 - b));
172 if (flags != IPV6_RTHDR_LOOSE)
174 #endif /* COMPAT_RFC1883 */
186 inet6_rthdr_reverse(in, out)
187 const struct cmsghdr *in;
196 inet6_rthdr_segments(cmsg)
197 const struct cmsghdr *cmsg;
199 struct ip6_rthdr *rthdr;
201 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
203 switch (rthdr->ip6r_type) {
204 case IPV6_RTHDR_TYPE_0:
206 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
208 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
211 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
220 inet6_rthdr_getaddr(cmsg, idx)
221 struct cmsghdr *cmsg;
224 struct ip6_rthdr *rthdr;
226 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
228 switch (rthdr->ip6r_type) {
229 case IPV6_RTHDR_TYPE_0:
231 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
234 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
236 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
237 if (idx <= 0 || naddr < idx)
239 #ifdef COMPAT_RFC2292
240 return (((struct in6_addr *)(rt0 + 1)) + idx - 1);
242 return (((struct in6_addr *)(rt0 + 1)) + idx);
252 inet6_rthdr_getflags(cmsg, idx)
253 const struct cmsghdr *cmsg;
256 struct ip6_rthdr *rthdr;
258 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
260 switch (rthdr->ip6r_type) {
261 case IPV6_RTHDR_TYPE_0:
263 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
266 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
268 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
269 if (idx < 0 || naddr < idx)
271 #ifdef COMPAT_RFC1883 /* XXX */
272 if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8)))
273 return IPV6_RTHDR_STRICT;
275 return IPV6_RTHDR_LOOSE;
277 return IPV6_RTHDR_LOOSE;
278 #endif /* COMPAT_RFC1883 */
291 inet6_rth_space(int type, int segments)
294 case IPV6_RTHDR_TYPE_0:
295 return (((segments * 2) + 1) << 3);
297 return (0); /* type not suppported */
302 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
304 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
305 struct ip6_rthdr0 *rth0;
308 case IPV6_RTHDR_TYPE_0:
309 /* length validation */
310 if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments))
313 memset(bp, 0, bp_len);
314 rth0 = (struct ip6_rthdr0 *)rth;
315 rth0->ip6r0_len = segments * 2;
316 rth0->ip6r0_type = IPV6_RTHDR_TYPE_0;
317 rth0->ip6r0_segleft = 0;
318 rth0->ip6r0_reserved = 0;
321 return (NULL); /* type not supported */
328 inet6_rth_add(void *bp, const struct in6_addr *addr)
330 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
331 struct ip6_rthdr0 *rth0;
332 struct in6_addr *nextaddr;
334 switch (rth->ip6r_type) {
335 case IPV6_RTHDR_TYPE_0:
336 rth0 = (struct ip6_rthdr0 *)rth;
337 nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft;
339 rth0->ip6r0_segleft++;
342 return (-1); /* type not supported */
349 inet6_rth_reverse(const void *in, void *out)
351 struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in;
352 struct ip6_rthdr0 *rth0_in, *rth0_out;
355 switch (rth_in->ip6r_type) {
356 case IPV6_RTHDR_TYPE_0:
357 rth0_in = (struct ip6_rthdr0 *)in;
358 rth0_out = (struct ip6_rthdr0 *)out;
360 /* parameter validation XXX too paranoid? */
361 if (rth0_in->ip6r0_len % 2)
363 segments = rth0_in->ip6r0_len / 2;
365 /* we can't use memcpy here, since in and out may overlap */
366 memmove((void *)rth0_out, (void *)rth0_in,
367 ((rth0_in->ip6r0_len) + 1) << 3);
368 rth0_out->ip6r0_segleft = segments;
370 /* reverse the addresses */
371 for (i = 0; i < segments / 2; i++) {
372 struct in6_addr addr_tmp, *addr1, *addr2;
374 addr1 = (struct in6_addr *)(rth0_out + 1) + i;
375 addr2 = (struct in6_addr *)(rth0_out + 1) +
384 return (-1); /* type not supported */
391 inet6_rth_segments(const void *bp)
393 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
394 struct ip6_rthdr0 *rh0;
397 switch (rh->ip6r_type) {
398 case IPV6_RTHDR_TYPE_0:
399 rh0 = (struct ip6_rthdr0 *)bp;
402 * Validation for a type-0 routing header.
403 * Is this too strict?
405 if ((rh0->ip6r0_len % 2) != 0 ||
406 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
411 return (-1); /* unknown type */
416 inet6_rth_getaddr(const void *bp, int idx)
418 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
419 struct ip6_rthdr0 *rh0;
422 switch (rh->ip6r_type) {
423 case IPV6_RTHDR_TYPE_0:
424 rh0 = (struct ip6_rthdr0 *)bp;
427 * Validation for a type-0 routing header.
428 * Is this too strict?
430 if ((rh0->ip6r0_len % 2) != 0 ||
431 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
434 if (idx < 0 || addrs <= idx)
437 return (((struct in6_addr *)(rh0 + 1)) + idx);
439 return (NULL); /* unknown type */