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