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