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