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/socket.h>
38 #include <netinet/in.h>
39 #include <netinet/ip6.h>
49 inet6_rthdr_space(int type, int seg)
52 case IPV6_RTHDR_TYPE_0:
53 if (seg < 1 || seg > 23)
56 return (CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) +
57 sizeof(struct ip6_rthdr0)));
59 return (CMSG_SPACE(sizeof(struct in6_addr) * seg +
60 sizeof(struct ip6_rthdr0)));
68 inet6_rthdr_init(void *bp, int type)
70 struct cmsghdr *ch = (struct cmsghdr *)bp;
71 struct ip6_rthdr *rthdr;
73 rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
75 ch->cmsg_level = IPPROTO_IPV6;
76 ch->cmsg_type = IPV6_RTHDR;
79 case IPV6_RTHDR_TYPE_0:
81 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) -
82 sizeof(struct in6_addr));
84 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0));
87 bzero(rthdr, sizeof(struct ip6_rthdr0));
88 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
97 inet6_rthdr_add(struct cmsghdr *cmsg, const struct in6_addr *addr, u_int flags)
99 struct ip6_rthdr *rthdr;
101 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
103 switch (rthdr->ip6r_type) {
104 case IPV6_RTHDR_TYPE_0:
106 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
107 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
109 if (rt0->ip6r0_segleft == 23)
112 #ifdef COMPAT_RFC1883 /* XXX */
113 if (flags == IPV6_RTHDR_STRICT) {
115 c = rt0->ip6r0_segleft / 8;
116 b = rt0->ip6r0_segleft % 8;
117 rt0->ip6r0_slmap[c] |= (1 << (7 - b));
120 if (flags != IPV6_RTHDR_LOOSE)
123 rt0->ip6r0_segleft++;
124 bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3),
125 sizeof(struct in6_addr));
126 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
127 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
139 inet6_rthdr_lasthop(struct cmsghdr *cmsg, unsigned int flags)
141 struct ip6_rthdr *rthdr;
143 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
145 switch (rthdr->ip6r_type) {
146 case IPV6_RTHDR_TYPE_0:
148 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
149 #ifdef COMPAT_RFC1883 /* XXX */
150 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
152 #endif /* COMPAT_RFC1883 */
153 if (rt0->ip6r0_segleft > 23)
155 #ifdef COMPAT_RFC1883 /* XXX */
156 if (flags == IPV6_RTHDR_STRICT) {
158 c = rt0->ip6r0_segleft / 8;
159 b = rt0->ip6r0_segleft % 8;
160 rt0->ip6r0_slmap[c] |= (1 << (7 - b));
163 if (flags != IPV6_RTHDR_LOOSE)
165 #endif /* COMPAT_RFC1883 */
177 inet6_rthdr_reverse(const struct cmsghdr *in, struct cmsghdr *out)
185 inet6_rthdr_segments(const struct cmsghdr *cmsg)
187 struct ip6_rthdr *rthdr;
189 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
191 switch (rthdr->ip6r_type) {
192 case IPV6_RTHDR_TYPE_0:
194 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
196 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
199 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
208 inet6_rthdr_getaddr(struct cmsghdr *cmsg, int idx)
210 struct ip6_rthdr *rthdr;
212 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
214 switch (rthdr->ip6r_type) {
215 case IPV6_RTHDR_TYPE_0:
217 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
220 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
222 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
223 if (idx <= 0 || naddr < idx)
225 #ifdef COMPAT_RFC2292
226 return (((struct in6_addr *)(rt0 + 1)) + idx - 1);
228 return (((struct in6_addr *)(rt0 + 1)) + idx);
238 inet6_rthdr_getflags(const struct cmsghdr *cmsg, int idx)
240 struct ip6_rthdr *rthdr;
242 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
244 switch (rthdr->ip6r_type) {
245 case IPV6_RTHDR_TYPE_0:
247 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
250 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
252 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
253 if (idx < 0 || naddr < idx)
255 #ifdef COMPAT_RFC1883 /* XXX */
256 if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8)))
257 return IPV6_RTHDR_STRICT;
259 return IPV6_RTHDR_LOOSE;
261 return IPV6_RTHDR_LOOSE;
262 #endif /* COMPAT_RFC1883 */
275 inet6_rth_space(int type, int segments)
278 case IPV6_RTHDR_TYPE_0:
279 if ((segments >= 0) && (segments <= 127))
280 return (((segments * 2) + 1) << 3);
283 return (0); /* type not suppported */
288 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
290 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
291 struct ip6_rthdr0 *rth0;
294 case IPV6_RTHDR_TYPE_0:
295 /* length validation */
296 if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments))
298 /* segment validation */
299 if ((segments < 0) || (segments > 127))
302 memset(bp, 0, bp_len);
303 rth0 = (struct ip6_rthdr0 *)rth;
304 rth0->ip6r0_len = segments * 2;
305 rth0->ip6r0_type = IPV6_RTHDR_TYPE_0;
306 rth0->ip6r0_segleft = 0;
307 rth0->ip6r0_reserved = 0;
310 return (NULL); /* type not supported */
317 inet6_rth_add(void *bp, const struct in6_addr *addr)
319 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
320 struct ip6_rthdr0 *rth0;
321 struct in6_addr *nextaddr;
323 switch (rth->ip6r_type) {
324 case IPV6_RTHDR_TYPE_0:
325 rth0 = (struct ip6_rthdr0 *)rth;
326 /* Don't exceed the number of stated segments */
327 if (rth0->ip6r0_segleft == (rth0->ip6r0_len / 2))
329 nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft;
331 rth0->ip6r0_segleft++;
334 return (-1); /* type not supported */
341 inet6_rth_reverse(const void *in, void *out)
343 struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in;
344 struct ip6_rthdr0 *rth0_in, *rth0_out;
347 switch (rth_in->ip6r_type) {
348 case IPV6_RTHDR_TYPE_0:
349 rth0_in = (struct ip6_rthdr0 *)in;
350 rth0_out = (struct ip6_rthdr0 *)out;
352 /* parameter validation XXX too paranoid? */
353 if (rth0_in->ip6r0_len % 2)
355 segments = rth0_in->ip6r0_len / 2;
357 /* we can't use memcpy here, since in and out may overlap */
358 memmove((void *)rth0_out, (void *)rth0_in,
359 ((rth0_in->ip6r0_len) + 1) << 3);
360 rth0_out->ip6r0_segleft = segments;
362 /* reverse the addresses */
363 for (i = 0; i < segments / 2; i++) {
364 struct in6_addr addr_tmp, *addr1, *addr2;
366 addr1 = (struct in6_addr *)(rth0_out + 1) + i;
367 addr2 = (struct in6_addr *)(rth0_out + 1) +
376 return (-1); /* type not supported */
383 inet6_rth_segments(const void *bp)
385 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
386 struct ip6_rthdr0 *rh0;
389 switch (rh->ip6r_type) {
390 case IPV6_RTHDR_TYPE_0:
391 rh0 = (struct ip6_rthdr0 *)bp;
394 * Validation for a type-0 routing header.
395 * Is this too strict?
397 if ((rh0->ip6r0_len % 2) != 0 ||
398 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
403 return (-1); /* unknown type */
408 inet6_rth_getaddr(const void *bp, int idx)
410 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
411 struct ip6_rthdr0 *rh0;
414 switch (rh->ip6r_type) {
415 case IPV6_RTHDR_TYPE_0:
416 rh0 = (struct ip6_rthdr0 *)bp;
419 * Validation for a type-0 routing header.
420 * Is this too strict?
422 if ((rh0->ip6r0_len % 2) != 0 ||
423 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
426 if (idx < 0 || addrs <= idx)
429 return (((struct in6_addr *)(rh0 + 1)) + idx);
431 return (NULL); /* unknown type */