1 /* $NetBSD: rthdr.c,v 1.17 2009/02/05 23:22:39 lukem 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 #include <sys/cdefs.h> 33 #if defined(LIBC_SCCS) && !defined(lint) 34 __RCSID("$NetBSD: rthdr.c,v 1.17 2009/02/05 23:22:39 lukem Exp $"); 35 #endif /* LIBC_SCCS and not lint */ 36 37 #include "namespace.h" 38 #include <sys/param.h> 39 #include <sys/types.h> 40 #include <sys/socket.h> 41 42 #include <netinet/in.h> 43 #include <netinet/ip6.h> 44 45 #include <assert.h> 46 #include <string.h> 47 #include <stdio.h> 48 49 #ifdef __weak_alias 50 __weak_alias(inet6_rthdr_add,_inet6_rthdr_add) 51 __weak_alias(inet6_rthdr_getaddr,_inet6_rthdr_getaddr) 52 __weak_alias(inet6_rthdr_getflags,_inet6_rthdr_getflags) 53 __weak_alias(inet6_rthdr_init,_inet6_rthdr_init) 54 __weak_alias(inet6_rthdr_lasthop,_inet6_rthdr_lasthop) 55 __weak_alias(inet6_rthdr_segments,_inet6_rthdr_segments) 56 __weak_alias(inet6_rthdr_space,_inet6_rthdr_space) 57 __weak_alias(inet6_rth_space, _inet6_rth_space) 58 __weak_alias(inet6_rth_init, _inet6_rth_init) 59 __weak_alias(inet6_rth_add, _inet6_rth_add) 60 __weak_alias(inet6_rth_reverse, _inet6_rth_reverse) 61 __weak_alias(inet6_rth_segments, _inet6_rth_segments) 62 __weak_alias(inet6_rth_getaddr, _inet6_rth_getaddr) 63 #endif 64 65 /* 66 * RFC2292 API 67 */ 68 69 size_t 70 inet6_rthdr_space(type, seg) 71 int type, seg; 72 { 73 switch (type) { 74 case IPV6_RTHDR_TYPE_0: 75 if (seg < 1 || seg > 23) 76 return (0); 77 return (CMSG_SPACE(sizeof(struct in6_addr) * seg + 78 sizeof(struct ip6_rthdr0))); 79 default: 80 return (0); 81 } 82 } 83 84 struct cmsghdr * 85 inet6_rthdr_init(bp, type) 86 void *bp; 87 int type; 88 { 89 struct cmsghdr *ch; 90 struct ip6_rthdr *rthdr; 91 92 _DIAGASSERT(bp != NULL); 93 94 ch = (struct cmsghdr *)bp; 95 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(ch); 96 97 ch->cmsg_level = IPPROTO_IPV6; 98 ch->cmsg_type = IPV6_RTHDR; 99 100 switch (type) { 101 case IPV6_RTHDR_TYPE_0: 102 #ifdef COMPAT_RFC2292 103 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - 104 sizeof(struct in6_addr)); 105 #else 106 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0)); 107 #endif 108 (void)memset(rthdr, 0, sizeof(struct ip6_rthdr0)); 109 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0; 110 return (ch); 111 default: 112 return (NULL); 113 } 114 } 115 116 int 117 inet6_rthdr_add(cmsg, addr, flags) 118 struct cmsghdr *cmsg; 119 const struct in6_addr *addr; 120 u_int flags; 121 { 122 struct ip6_rthdr *rthdr; 123 124 _DIAGASSERT(cmsg != NULL); 125 _DIAGASSERT(addr != NULL); 126 127 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg); 128 129 switch (rthdr->ip6r_type) { 130 case IPV6_RTHDR_TYPE_0: 131 { 132 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr; 133 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) 134 return (-1); 135 if (rt0->ip6r0_segleft == 23) 136 return (-1); 137 if (flags != IPV6_RTHDR_LOOSE) 138 return (-1); 139 rt0->ip6r0_segleft++; 140 (void)memcpy(((caddr_t)(void *)rt0) + 141 ((rt0->ip6r0_len + 1) << 3), addr, sizeof(struct in6_addr)); 142 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3; 143 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3); 144 break; 145 } 146 default: 147 return (-1); 148 } 149 150 return (0); 151 } 152 153 int 154 inet6_rthdr_lasthop(cmsg, flags) 155 struct cmsghdr *cmsg; 156 unsigned int flags; 157 { 158 struct ip6_rthdr *rthdr; 159 160 _DIAGASSERT(cmsg != NULL); 161 162 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg); 163 164 switch (rthdr->ip6r_type) { 165 case IPV6_RTHDR_TYPE_0: 166 { 167 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr; 168 if (rt0->ip6r0_segleft > 23) 169 return (-1); 170 if (flags != IPV6_RTHDR_LOOSE) 171 return (-1); 172 break; 173 } 174 default: 175 return (-1); 176 } 177 178 return (0); 179 } 180 181 #if 0 182 int 183 inet6_rthdr_reverse(in, out) 184 const struct cmsghdr *in; 185 struct cmsghdr *out; 186 { 187 188 return (-1); 189 } 190 #endif 191 192 int 193 inet6_rthdr_segments(cmsg) 194 const struct cmsghdr *cmsg; 195 { 196 const struct ip6_rthdr *rthdr; 197 198 _DIAGASSERT(cmsg != NULL); 199 200 rthdr = __UNCONST(CCMSG_DATA(cmsg)); 201 202 switch (rthdr->ip6r_type) { 203 case IPV6_RTHDR_TYPE_0: 204 { 205 const struct ip6_rthdr0 *rt0 = 206 (const struct ip6_rthdr0 *)(const void *)rthdr; 207 208 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) 209 return (-1); 210 211 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 212 } 213 214 default: 215 return (-1); 216 } 217 } 218 219 struct in6_addr * 220 inet6_rthdr_getaddr(cmsg, idx) 221 struct cmsghdr *cmsg; 222 int idx; 223 { 224 struct ip6_rthdr *rthdr; 225 226 _DIAGASSERT(cmsg != NULL); 227 228 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg); 229 230 switch (rthdr->ip6r_type) { 231 case IPV6_RTHDR_TYPE_0: 232 { 233 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr; 234 int naddr; 235 236 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) 237 return NULL; 238 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 239 if (idx <= 0 || naddr < idx) 240 return NULL; 241 #ifdef COMPAT_RFC2292 242 return ((struct in6_addr *)(void *)(rt0 + 1)) + idx - 1; 243 #else 244 return ((struct in6_addr *)(void *)(rt0 + 1)) + idx; 245 #endif 246 } 247 248 default: 249 return NULL; 250 } 251 } 252 253 int 254 inet6_rthdr_getflags(cmsg, idx) 255 const struct cmsghdr *cmsg; 256 int idx; 257 { 258 const struct ip6_rthdr *rthdr; 259 260 _DIAGASSERT(cmsg != NULL); 261 262 rthdr = __UNCONST(CCMSG_DATA(cmsg)); 263 264 switch (rthdr->ip6r_type) { 265 case IPV6_RTHDR_TYPE_0: 266 { 267 const struct ip6_rthdr0 *rt0 = (const struct ip6_rthdr0 *) 268 (const void *)rthdr; 269 int naddr; 270 271 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) 272 return (-1); 273 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 274 if (idx < 0 || naddr < idx) 275 return (-1); 276 return IPV6_RTHDR_LOOSE; 277 } 278 279 default: 280 return (-1); 281 } 282 } 283 284 /* 285 * RFC3542 (2292bis) API 286 */ 287 288 socklen_t 289 inet6_rth_space(int type, int segments) 290 { 291 switch (type) { 292 case IPV6_RTHDR_TYPE_0: 293 return (((segments * 2) + 1) << 3); 294 default: 295 return (0); /* type not suppported */ 296 } 297 } 298 299 void * 300 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments) 301 { 302 struct ip6_rthdr *rth; 303 struct ip6_rthdr0 *rth0; 304 305 _DIAGASSERT(bp != NULL); 306 307 rth = (struct ip6_rthdr *)bp; 308 309 switch (type) { 310 case IPV6_RTHDR_TYPE_0: 311 /* length validation */ 312 if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments)) 313 return (NULL); 314 315 memset(bp, 0, bp_len); 316 rth0 = (struct ip6_rthdr0 *)(void *)rth; 317 rth0->ip6r0_len = segments * 2; 318 rth0->ip6r0_type = IPV6_RTHDR_TYPE_0; 319 rth0->ip6r0_segleft = 0; 320 rth0->ip6r0_reserved = 0; 321 break; 322 default: 323 return (NULL); /* type not supported */ 324 } 325 326 return (bp); 327 } 328 329 int 330 inet6_rth_add(void *bp, const struct in6_addr *addr) 331 { 332 struct ip6_rthdr *rth; 333 struct ip6_rthdr0 *rth0; 334 struct in6_addr *nextaddr; 335 336 _DIAGASSERT(bp != NULL); 337 338 rth = (struct ip6_rthdr *)bp; 339 340 switch (rth->ip6r_type) { 341 case IPV6_RTHDR_TYPE_0: 342 rth0 = (struct ip6_rthdr0 *)(void *)rth; 343 nextaddr = (struct in6_addr *)(void *)(rth0 + 1) 344 + rth0->ip6r0_segleft; 345 *nextaddr = *addr; 346 rth0->ip6r0_segleft++; 347 break; 348 default: 349 return (-1); /* type not supported */ 350 } 351 352 return (0); 353 } 354 355 int 356 inet6_rth_reverse(const void *in, void *out) 357 { 358 const struct ip6_rthdr *rth_in; 359 const struct ip6_rthdr0 *rth0_in; 360 struct ip6_rthdr0 *rth0_out; 361 int i, segments; 362 363 _DIAGASSERT(in != NULL); 364 _DIAGASSERT(out != NULL); 365 366 rth_in = (const struct ip6_rthdr *)in; 367 368 switch (rth_in->ip6r_type) { 369 case IPV6_RTHDR_TYPE_0: 370 rth0_in = (const struct ip6_rthdr0 *)in; 371 rth0_out = (struct ip6_rthdr0 *)out; 372 373 /* parameter validation XXX too paranoid? */ 374 if (rth0_in->ip6r0_len % 2) 375 return (-1); 376 segments = rth0_in->ip6r0_len / 2; 377 378 /* we can't use memcpy here, since in and out may overlap */ 379 memmove((void *)rth0_out, (const void *)rth0_in, 380 (unsigned int)(((rth0_in->ip6r0_len) + 1) << 3)); 381 rth0_out->ip6r0_segleft = segments; 382 383 /* reverse the addresses */ 384 for (i = 0; i < segments / 2; i++) { 385 struct in6_addr addr_tmp, *addr1, *addr2; 386 387 addr1 = (struct in6_addr *)(void *)(rth0_out + 1) + i; 388 addr2 = (struct in6_addr *)(void *)(rth0_out + 1) + 389 (segments - i - 1); 390 addr_tmp = *addr1; 391 *addr1 = *addr2; 392 *addr2 = addr_tmp; 393 } 394 395 break; 396 default: 397 return (-1); /* type not supported */ 398 } 399 400 return (0); 401 } 402 403 int 404 inet6_rth_segments(const void *bp) 405 { 406 const struct ip6_rthdr *rh; 407 const struct ip6_rthdr0 *rh0; 408 unsigned int addrs; 409 410 _DIAGASSERT(bp != NULL); 411 412 rh = (const struct ip6_rthdr *)bp; 413 414 switch (rh->ip6r_type) { 415 case IPV6_RTHDR_TYPE_0: 416 rh0 = (const struct ip6_rthdr0 *)bp; 417 418 /* 419 * Validation for a type-0 routing header. 420 * Is this too strict? 421 */ 422 if ((rh0->ip6r0_len % 2) != 0 || 423 (addrs = (rh0->ip6r0_len / 2)) < rh0->ip6r0_segleft) 424 return (-1); 425 426 return (addrs); 427 default: 428 return (-1); /* unknown type */ 429 } 430 } 431 432 struct in6_addr * 433 inet6_rth_getaddr(const void *bp, int idx) 434 { 435 const struct ip6_rthdr *rh; 436 const struct ip6_rthdr0 *rh0; 437 unsigned int addrs; 438 439 _DIAGASSERT(bp != NULL); 440 441 rh = (const struct ip6_rthdr *)bp; 442 443 switch (rh->ip6r_type) { 444 case IPV6_RTHDR_TYPE_0: 445 rh0 = (const struct ip6_rthdr0 *)bp; 446 447 /* 448 * Validation for a type-0 routing header. 449 * Is this too strict? 450 */ 451 if ((rh0->ip6r0_len % 2) != 0 || 452 (addrs = (rh0->ip6r0_len / 2)) < rh0->ip6r0_segleft) 453 return (NULL); 454 455 if (idx < 0 || addrs <= (unsigned int)idx) 456 return (NULL); 457 458 return (((struct in6_addr *)(void *)__UNCONST(rh0 + 1)) + idx); 459 default: 460 return (NULL); /* unknown type */ 461 } 462 } 463