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 56size_t 57inet6_rthdr_space(type, seg) 58int 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 76struct cmsghdr * 77inet6_rthdr_init(bp, type) 78void *bp; 79int 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 */ 107int 108inet6_rthdr_add(cmsg, addr, flags) 109struct cmsghdr *cmsg; 110const struct in6_addr *addr; 111u_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 */ 152int 153inet6_rthdr_lasthop(cmsg, flags) 154struct cmsghdr *cmsg; 155unsigned 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 192int 193inet6_rthdr_reverse(in, out) 194const struct cmsghdr *in; 195struct cmsghdr *out; 196{ 197 198 return (-1); 199} 200#endif 201 202int 203inet6_rthdr_segments(cmsg) 204const 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 226struct in6_addr * 227inet6_rthdr_getaddr(cmsg, idx) 228struct cmsghdr *cmsg; 229int 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 258int 259inet6_rthdr_getflags(cmsg, idx) 260const struct cmsghdr *cmsg; 261int 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 297socklen_t 298inet6_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 310void * 311inet6_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 339int 340inet6_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 363int 364inet6_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 405int 406inet6_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 430struct in6_addr * 431inet6_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} 458