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