1 /* $KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 itojun Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-3-Clause 5 * 6 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the project nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <sys/param.h> 38 #include <sys/socket.h> 39 40 #include <netinet/in.h> 41 #include <netinet/ip6.h> 42 43 #include <string.h> 44 #include <stdio.h> 45 46 /* 47 * RFC2292 API 48 */ 49 50 size_t 51 inet6_rthdr_space(int type, int 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(void *bp, int type) 71 { 72 struct cmsghdr *ch = (struct cmsghdr *)bp; 73 struct ip6_rthdr *rthdr; 74 75 rthdr = (struct ip6_rthdr *)CMSG_DATA(ch); 76 77 ch->cmsg_level = IPPROTO_IPV6; 78 ch->cmsg_type = IPV6_RTHDR; 79 80 switch (type) { 81 case IPV6_RTHDR_TYPE_0: 82 #ifdef COMPAT_RFC2292 83 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - 84 sizeof(struct in6_addr)); 85 #else 86 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0)); 87 #endif 88 89 bzero(rthdr, sizeof(struct ip6_rthdr0)); 90 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0; 91 return (ch); 92 default: 93 return (NULL); 94 } 95 } 96 97 /* ARGSUSED */ 98 int 99 inet6_rthdr_add(struct cmsghdr *cmsg, const struct in6_addr *addr, u_int flags) 100 { 101 struct ip6_rthdr *rthdr; 102 103 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 104 105 switch (rthdr->ip6r_type) { 106 case IPV6_RTHDR_TYPE_0: 107 { 108 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 109 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) 110 return (-1); 111 if (rt0->ip6r0_segleft == 23) 112 return (-1); 113 114 #ifdef COMPAT_RFC1883 /* XXX */ 115 if (flags == IPV6_RTHDR_STRICT) { 116 int c, b; 117 c = rt0->ip6r0_segleft / 8; 118 b = rt0->ip6r0_segleft % 8; 119 rt0->ip6r0_slmap[c] |= (1 << (7 - b)); 120 } 121 #else 122 if (flags != IPV6_RTHDR_LOOSE) 123 return (-1); 124 #endif 125 rt0->ip6r0_segleft++; 126 bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3), 127 sizeof(struct in6_addr)); 128 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3; 129 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3); 130 break; 131 } 132 default: 133 return (-1); 134 } 135 136 return (0); 137 } 138 139 /* ARGSUSED */ 140 int 141 inet6_rthdr_lasthop(struct cmsghdr *cmsg, unsigned int flags) 142 { 143 struct ip6_rthdr *rthdr; 144 145 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 146 147 switch (rthdr->ip6r_type) { 148 case IPV6_RTHDR_TYPE_0: 149 { 150 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 151 #ifdef COMPAT_RFC1883 /* XXX */ 152 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) 153 return (-1); 154 #endif /* COMPAT_RFC1883 */ 155 if (rt0->ip6r0_segleft > 23) 156 return (-1); 157 #ifdef COMPAT_RFC1883 /* XXX */ 158 if (flags == IPV6_RTHDR_STRICT) { 159 int c, b; 160 c = rt0->ip6r0_segleft / 8; 161 b = rt0->ip6r0_segleft % 8; 162 rt0->ip6r0_slmap[c] |= (1 << (7 - b)); 163 } 164 #else 165 if (flags != IPV6_RTHDR_LOOSE) 166 return (-1); 167 #endif /* COMPAT_RFC1883 */ 168 break; 169 } 170 default: 171 return (-1); 172 } 173 174 return (0); 175 } 176 177 #if 0 178 int 179 inet6_rthdr_reverse(const struct cmsghdr *in, struct cmsghdr *out) 180 { 181 182 return (-1); 183 } 184 #endif 185 186 int 187 inet6_rthdr_segments(const struct cmsghdr *cmsg) 188 { 189 struct ip6_rthdr *rthdr; 190 191 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 192 193 switch (rthdr->ip6r_type) { 194 case IPV6_RTHDR_TYPE_0: 195 { 196 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 197 198 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) 199 return (-1); 200 201 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 202 } 203 204 default: 205 return (-1); 206 } 207 } 208 209 struct in6_addr * 210 inet6_rthdr_getaddr(struct cmsghdr *cmsg, int idx) 211 { 212 struct ip6_rthdr *rthdr; 213 214 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 215 216 switch (rthdr->ip6r_type) { 217 case IPV6_RTHDR_TYPE_0: 218 { 219 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 220 int naddr; 221 222 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) 223 return NULL; 224 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 225 if (idx <= 0 || naddr < idx) 226 return NULL; 227 #ifdef COMPAT_RFC2292 228 return (((struct in6_addr *)(rt0 + 1)) + idx - 1); 229 #else 230 return (((struct in6_addr *)(rt0 + 1)) + idx); 231 #endif 232 } 233 234 default: 235 return NULL; 236 } 237 } 238 239 int 240 inet6_rthdr_getflags(const struct cmsghdr *cmsg, int idx) 241 { 242 struct ip6_rthdr *rthdr; 243 244 rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 245 246 switch (rthdr->ip6r_type) { 247 case IPV6_RTHDR_TYPE_0: 248 { 249 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 250 int naddr; 251 252 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) 253 return (-1); 254 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 255 if (idx < 0 || naddr < idx) 256 return (-1); 257 #ifdef COMPAT_RFC1883 /* XXX */ 258 if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8))) 259 return IPV6_RTHDR_STRICT; 260 else 261 return IPV6_RTHDR_LOOSE; 262 #else 263 return IPV6_RTHDR_LOOSE; 264 #endif /* COMPAT_RFC1883 */ 265 } 266 267 default: 268 return (-1); 269 } 270 } 271 272 /* 273 * RFC3542 API 274 */ 275 276 socklen_t 277 inet6_rth_space(int type, int segments) 278 { 279 switch (type) { 280 case IPV6_RTHDR_TYPE_0: 281 if ((segments >= 0) && (segments <= 127)) 282 return (((segments * 2) + 1) << 3); 283 /* FALLTHROUGH */ 284 default: 285 return (0); /* type not suppported */ 286 } 287 } 288 289 void * 290 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments) 291 { 292 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; 293 struct ip6_rthdr0 *rth0; 294 295 switch (type) { 296 case IPV6_RTHDR_TYPE_0: 297 /* length validation */ 298 if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments)) 299 return (NULL); 300 /* segment validation */ 301 if ((segments < 0) || (segments > 127)) 302 return (NULL); 303 304 memset(bp, 0, bp_len); 305 rth0 = (struct ip6_rthdr0 *)rth; 306 rth0->ip6r0_len = segments * 2; 307 rth0->ip6r0_type = IPV6_RTHDR_TYPE_0; 308 rth0->ip6r0_segleft = 0; 309 rth0->ip6r0_reserved = 0; 310 break; 311 default: 312 return (NULL); /* type not supported */ 313 } 314 315 return (bp); 316 } 317 318 int 319 inet6_rth_add(void *bp, const struct in6_addr *addr) 320 { 321 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; 322 struct ip6_rthdr0 *rth0; 323 struct in6_addr *nextaddr; 324 325 switch (rth->ip6r_type) { 326 case IPV6_RTHDR_TYPE_0: 327 rth0 = (struct ip6_rthdr0 *)rth; 328 /* Don't exceed the number of stated segments */ 329 if (rth0->ip6r0_segleft == (rth0->ip6r0_len / 2)) 330 return (-1); 331 nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft; 332 *nextaddr = *addr; 333 rth0->ip6r0_segleft++; 334 break; 335 default: 336 return (-1); /* type not supported */ 337 } 338 339 return (0); 340 } 341 342 int 343 inet6_rth_reverse(const void *in, void *out) 344 { 345 struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in; 346 struct ip6_rthdr0 *rth0_in, *rth0_out; 347 int i, segments; 348 349 switch (rth_in->ip6r_type) { 350 case IPV6_RTHDR_TYPE_0: 351 rth0_in = (struct ip6_rthdr0 *)in; 352 rth0_out = (struct ip6_rthdr0 *)out; 353 354 /* parameter validation XXX too paranoid? */ 355 if (rth0_in->ip6r0_len % 2) 356 return (-1); 357 segments = rth0_in->ip6r0_len / 2; 358 359 /* we can't use memcpy here, since in and out may overlap */ 360 memmove((void *)rth0_out, (void *)rth0_in, 361 ((rth0_in->ip6r0_len) + 1) << 3); 362 rth0_out->ip6r0_segleft = segments; 363 364 /* reverse the addresses */ 365 for (i = 0; i < segments / 2; i++) { 366 struct in6_addr addr_tmp, *addr1, *addr2; 367 368 addr1 = (struct in6_addr *)(rth0_out + 1) + i; 369 addr2 = (struct in6_addr *)(rth0_out + 1) + 370 (segments - i - 1); 371 addr_tmp = *addr1; 372 *addr1 = *addr2; 373 *addr2 = addr_tmp; 374 } 375 376 break; 377 default: 378 return (-1); /* type not supported */ 379 } 380 381 return (0); 382 } 383 384 int 385 inet6_rth_segments(const void *bp) 386 { 387 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; 388 struct ip6_rthdr0 *rh0; 389 int addrs; 390 391 switch (rh->ip6r_type) { 392 case IPV6_RTHDR_TYPE_0: 393 rh0 = (struct ip6_rthdr0 *)bp; 394 395 /* 396 * Validation for a type-0 routing header. 397 * Is this too strict? 398 */ 399 if ((rh0->ip6r0_len % 2) != 0 || 400 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) 401 return (-1); 402 403 return (addrs); 404 default: 405 return (-1); /* unknown type */ 406 } 407 } 408 409 struct in6_addr * 410 inet6_rth_getaddr(const void *bp, int idx) 411 { 412 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; 413 struct ip6_rthdr0 *rh0; 414 int addrs; 415 416 switch (rh->ip6r_type) { 417 case IPV6_RTHDR_TYPE_0: 418 rh0 = (struct ip6_rthdr0 *)bp; 419 420 /* 421 * Validation for a type-0 routing header. 422 * Is this too strict? 423 */ 424 if ((rh0->ip6r0_len % 2) != 0 || 425 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) 426 return (NULL); 427 428 if (idx < 0 || addrs <= idx) 429 return (NULL); 430 431 return (((struct in6_addr *)(rh0 + 1)) + idx); 432 default: 433 return (NULL); /* unknown type */ 434 break; 435 } 436 } 437