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 (2292bis) 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 return (((segments * 2) + 1) << 3); 296 default: 297 return (0); /* type not suppported */ 298 } 299 } 300 301 void * 302 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments) 303 { 304 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; 305 struct ip6_rthdr0 *rth0; 306 307 switch (type) { 308 case IPV6_RTHDR_TYPE_0: 309 /* length validation */ 310 if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments)) 311 return (NULL); 312 313 memset(bp, 0, bp_len); 314 rth0 = (struct ip6_rthdr0 *)rth; 315 rth0->ip6r0_len = segments * 2; 316 rth0->ip6r0_type = IPV6_RTHDR_TYPE_0; 317 rth0->ip6r0_segleft = 0; 318 rth0->ip6r0_reserved = 0; 319 break; 320 default: 321 return (NULL); /* type not supported */ 322 } 323 324 return (bp); 325 } 326 327 int 328 inet6_rth_add(void *bp, const struct in6_addr *addr) 329 { 330 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; 331 struct ip6_rthdr0 *rth0; 332 struct in6_addr *nextaddr; 333 334 switch (rth->ip6r_type) { 335 case IPV6_RTHDR_TYPE_0: 336 rth0 = (struct ip6_rthdr0 *)rth; 337 nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft; 338 *nextaddr = *addr; 339 rth0->ip6r0_segleft++; 340 break; 341 default: 342 return (-1); /* type not supported */ 343 } 344 345 return (0); 346 } 347 348 int 349 inet6_rth_reverse(const void *in, void *out) 350 { 351 struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in; 352 struct ip6_rthdr0 *rth0_in, *rth0_out; 353 int i, segments; 354 355 switch (rth_in->ip6r_type) { 356 case IPV6_RTHDR_TYPE_0: 357 rth0_in = (struct ip6_rthdr0 *)in; 358 rth0_out = (struct ip6_rthdr0 *)out; 359 360 /* parameter validation XXX too paranoid? */ 361 if (rth0_in->ip6r0_len % 2) 362 return (-1); 363 segments = rth0_in->ip6r0_len / 2; 364 365 /* we can't use memcpy here, since in and out may overlap */ 366 memmove((void *)rth0_out, (void *)rth0_in, 367 ((rth0_in->ip6r0_len) + 1) << 3); 368 rth0_out->ip6r0_segleft = segments; 369 370 /* reverse the addresses */ 371 for (i = 0; i < segments / 2; i++) { 372 struct in6_addr addr_tmp, *addr1, *addr2; 373 374 addr1 = (struct in6_addr *)(rth0_out + 1) + i; 375 addr2 = (struct in6_addr *)(rth0_out + 1) + 376 (segments - i - 1); 377 addr_tmp = *addr1; 378 *addr1 = *addr2; 379 *addr2 = addr_tmp; 380 } 381 382 break; 383 default: 384 return (-1); /* type not supported */ 385 } 386 387 return (0); 388 } 389 390 int 391 inet6_rth_segments(const void *bp) 392 { 393 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; 394 struct ip6_rthdr0 *rh0; 395 int addrs; 396 397 switch (rh->ip6r_type) { 398 case IPV6_RTHDR_TYPE_0: 399 rh0 = (struct ip6_rthdr0 *)bp; 400 401 /* 402 * Validation for a type-0 routing header. 403 * Is this too strict? 404 */ 405 if ((rh0->ip6r0_len % 2) != 0 || 406 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) 407 return (-1); 408 409 return (addrs); 410 default: 411 return (-1); /* unknown type */ 412 } 413 } 414 415 struct in6_addr * 416 inet6_rth_getaddr(const void *bp, int idx) 417 { 418 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; 419 struct ip6_rthdr0 *rh0; 420 int rthlen, addrs; 421 422 switch (rh->ip6r_type) { 423 case IPV6_RTHDR_TYPE_0: 424 rh0 = (struct ip6_rthdr0 *)bp; 425 rthlen = (rh0->ip6r0_len + 1) << 3; 426 427 /* 428 * Validation for a type-0 routing header. 429 * Is this too strict? 430 */ 431 if ((rthlen % 2) != 0 || 432 (addrs = (rthlen >> 1)) < rh0->ip6r0_segleft) 433 return (NULL); 434 435 if (idx < 0 || addrs <= idx) 436 return (NULL); 437 438 return (((struct in6_addr *)(rh0 + 1)) + idx); 439 default: 440 return (NULL); /* unknown type */ 441 break; 442 } 443 } 444