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