1121472Sume/* $KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 itojun Exp $ */ 295023Ssuz 354696Sshin/* 454696Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 554696Sshin * All rights reserved. 654696Sshin * 754696Sshin * Redistribution and use in source and binary forms, with or without 854696Sshin * modification, are permitted provided that the following conditions 954696Sshin * are met: 1054696Sshin * 1. Redistributions of source code must retain the above copyright 1154696Sshin * notice, this list of conditions and the following disclaimer. 1254696Sshin * 2. Redistributions in binary form must reproduce the above copyright 1354696Sshin * notice, this list of conditions and the following disclaimer in the 1454696Sshin * documentation and/or other materials provided with the distribution. 1554696Sshin * 3. Neither the name of the project nor the names of its contributors 1654696Sshin * may be used to endorse or promote products derived from this software 1754696Sshin * without specific prior written permission. 1854696Sshin * 1954696Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2054696Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2154696Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2254696Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2354696Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2454696Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2554696Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2654696Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2754696Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2854696Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2954696Sshin * SUCH DAMAGE. 3054696Sshin */ 3154696Sshin 3292986Sobrien#include <sys/cdefs.h> 3392986Sobrien__FBSDID("$FreeBSD$"); 3492986Sobrien 3554696Sshin#include <sys/param.h> 3654696Sshin#include <sys/types.h> 3754696Sshin#include <sys/socket.h> 3854696Sshin 3954696Sshin#include <netinet/in.h> 4054696Sshin#include <netinet/ip6.h> 4154696Sshin 4254696Sshin#include <string.h> 4354696Sshin#include <stdio.h> 4454696Sshin 45121472Sume/* 46121472Sume * RFC2292 API 47121472Sume */ 48121472Sume 4954696Sshinsize_t 5054696Sshininet6_rthdr_space(type, seg) 51121472Sume int type, seg; 5254696Sshin{ 53121472Sume switch (type) { 54121472Sume case IPV6_RTHDR_TYPE_0: 55121472Sume if (seg < 1 || seg > 23) 56121472Sume return (0); 57121472Sume#ifdef COMPAT_RFC2292 58121472Sume return (CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) + 59121472Sume sizeof(struct ip6_rthdr0))); 60121472Sume#else 61121472Sume return (CMSG_SPACE(sizeof(struct in6_addr) * seg + 62121472Sume sizeof(struct ip6_rthdr0))); 6354696Sshin#endif 64121472Sume default: 65121472Sume return (0); 66121472Sume } 6754696Sshin} 6854696Sshin 6954696Sshinstruct cmsghdr * 7054696Sshininet6_rthdr_init(bp, type) 71121472Sume void *bp; 72121472Sume int type; 7354696Sshin{ 74121472Sume struct cmsghdr *ch = (struct cmsghdr *)bp; 75121472Sume struct ip6_rthdr *rthdr; 7654696Sshin 77121472Sume rthdr = (struct ip6_rthdr *)CMSG_DATA(ch); 7857719Sshin 79121472Sume ch->cmsg_level = IPPROTO_IPV6; 80121472Sume ch->cmsg_type = IPV6_RTHDR; 8154696Sshin 82121472Sume switch (type) { 83121472Sume case IPV6_RTHDR_TYPE_0: 84121472Sume#ifdef COMPAT_RFC2292 85121472Sume ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - 86121472Sume sizeof(struct in6_addr)); 87121472Sume#else 88121472Sume ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0)); 8954696Sshin#endif 90121472Sume 91121472Sume bzero(rthdr, sizeof(struct ip6_rthdr0)); 92121472Sume rthdr->ip6r_type = IPV6_RTHDR_TYPE_0; 93121472Sume return (ch); 94121472Sume default: 95121472Sume return (NULL); 96121472Sume } 9754696Sshin} 9854696Sshin 99121472Sume/* ARGSUSED */ 10054696Sshinint 10154696Sshininet6_rthdr_add(cmsg, addr, flags) 102121472Sume struct cmsghdr *cmsg; 103121472Sume const struct in6_addr *addr; 104121472Sume u_int flags; 10554696Sshin{ 106121472Sume struct ip6_rthdr *rthdr; 10754696Sshin 108121472Sume rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 10957719Sshin 110121472Sume switch (rthdr->ip6r_type) { 111121472Sume case IPV6_RTHDR_TYPE_0: 112121472Sume { 113121472Sume struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 114121472Sume if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) 115121472Sume return (-1); 116121472Sume if (rt0->ip6r0_segleft == 23) 117121472Sume return (-1); 118121472Sume 119121472Sume#ifdef COMPAT_RFC1883 /* XXX */ 120121472Sume if (flags == IPV6_RTHDR_STRICT) { 121121472Sume int c, b; 122121472Sume c = rt0->ip6r0_segleft / 8; 123121472Sume b = rt0->ip6r0_segleft % 8; 124121472Sume rt0->ip6r0_slmap[c] |= (1 << (7 - b)); 125121472Sume } 126121472Sume#else 127121472Sume if (flags != IPV6_RTHDR_LOOSE) 128121472Sume return (-1); 12954696Sshin#endif 130121472Sume rt0->ip6r0_segleft++; 131121472Sume bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3), 132121472Sume sizeof(struct in6_addr)); 133121472Sume rt0->ip6r0_len += sizeof(struct in6_addr) >> 3; 134121472Sume cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3); 135121472Sume break; 136121472Sume } 137121472Sume default: 138121472Sume return (-1); 139121472Sume } 14054696Sshin 141121472Sume return (0); 14254696Sshin} 14354696Sshin 144121472Sume/* ARGSUSED */ 14554696Sshinint 14654696Sshininet6_rthdr_lasthop(cmsg, flags) 147121472Sume struct cmsghdr *cmsg; 148121472Sume unsigned int flags; 14954696Sshin{ 150121472Sume struct ip6_rthdr *rthdr; 15154696Sshin 152121472Sume rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 15357719Sshin 154121472Sume switch (rthdr->ip6r_type) { 155121472Sume case IPV6_RTHDR_TYPE_0: 156121472Sume { 157121472Sume struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 158121472Sume#ifdef COMPAT_RFC1883 /* XXX */ 159121472Sume if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) 160121472Sume return (-1); 161121472Sume#endif /* COMPAT_RFC1883 */ 162121472Sume if (rt0->ip6r0_segleft > 23) 163121472Sume return (-1); 164121472Sume#ifdef COMPAT_RFC1883 /* XXX */ 165121472Sume if (flags == IPV6_RTHDR_STRICT) { 166121472Sume int c, b; 167121472Sume c = rt0->ip6r0_segleft / 8; 168121472Sume b = rt0->ip6r0_segleft % 8; 169121472Sume rt0->ip6r0_slmap[c] |= (1 << (7 - b)); 170121472Sume } 171121472Sume#else 172121472Sume if (flags != IPV6_RTHDR_LOOSE) 173121472Sume return (-1); 174121472Sume#endif /* COMPAT_RFC1883 */ 175121472Sume break; 176121472Sume } 177121472Sume default: 178121472Sume return (-1); 179121472Sume } 18054696Sshin 181121472Sume return (0); 18254696Sshin} 18354696Sshin 18454696Sshin#if 0 18554696Sshinint 18654696Sshininet6_rthdr_reverse(in, out) 187121472Sume const struct cmsghdr *in; 188121472Sume struct cmsghdr *out; 18954696Sshin{ 190121472Sume 191121472Sume return (-1); 19254696Sshin} 19354696Sshin#endif 19454696Sshin 19554696Sshinint 19654696Sshininet6_rthdr_segments(cmsg) 197121472Sume const struct cmsghdr *cmsg; 19854696Sshin{ 199121472Sume struct ip6_rthdr *rthdr; 20054696Sshin 201121472Sume rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 20257719Sshin 203121472Sume switch (rthdr->ip6r_type) { 204121472Sume case IPV6_RTHDR_TYPE_0: 205121472Sume { 206121472Sume struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 20754696Sshin 208121472Sume if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) 209121472Sume return (-1); 210121472Sume 211121472Sume return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 21254696Sshin } 21354696Sshin 214121472Sume default: 215121472Sume return (-1); 216121472Sume } 21754696Sshin} 21854696Sshin 21954696Sshinstruct in6_addr * 22095023Ssuzinet6_rthdr_getaddr(cmsg, idx) 221121472Sume struct cmsghdr *cmsg; 222121472Sume int idx; 22354696Sshin{ 224121472Sume struct ip6_rthdr *rthdr; 22554696Sshin 226121472Sume rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 22757719Sshin 228121472Sume switch (rthdr->ip6r_type) { 229121472Sume case IPV6_RTHDR_TYPE_0: 230121472Sume { 231121472Sume struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 232121472Sume int naddr; 23354696Sshin 234121472Sume if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) 235121472Sume return NULL; 236121472Sume naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 237121472Sume if (idx <= 0 || naddr < idx) 238121472Sume return NULL; 239121472Sume#ifdef COMPAT_RFC2292 240121472Sume return (((struct in6_addr *)(rt0 + 1)) + idx - 1); 241121472Sume#else 242121472Sume return (((struct in6_addr *)(rt0 + 1)) + idx); 243121472Sume#endif 24454696Sshin } 245121472Sume 246121472Sume default: 247121472Sume return NULL; 24854696Sshin } 24954696Sshin} 25054696Sshin 25154696Sshinint 25295023Ssuzinet6_rthdr_getflags(cmsg, idx) 253121472Sume const struct cmsghdr *cmsg; 254121472Sume int idx; 25554696Sshin{ 256121472Sume struct ip6_rthdr *rthdr; 25754696Sshin 258121472Sume rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 25957719Sshin 260121472Sume switch (rthdr->ip6r_type) { 261121472Sume case IPV6_RTHDR_TYPE_0: 262121472Sume { 263121472Sume struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 264121472Sume int naddr; 26554696Sshin 266121472Sume if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) 267121472Sume return (-1); 268121472Sume naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 269121472Sume if (idx < 0 || naddr < idx) 270121472Sume return (-1); 271121472Sume#ifdef COMPAT_RFC1883 /* XXX */ 272121472Sume if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8))) 273121472Sume return IPV6_RTHDR_STRICT; 274121472Sume else 275121472Sume return IPV6_RTHDR_LOOSE; 276121472Sume#else 277121472Sume return IPV6_RTHDR_LOOSE; 278121472Sume#endif /* COMPAT_RFC1883 */ 27954696Sshin } 280121472Sume 281121472Sume default: 282121472Sume return (-1); 28354696Sshin } 284121472Sume} 28554696Sshin 286121472Sume/* 287148160Sume * RFC3542 API 288121472Sume */ 289121472Sume 290121472Sumesocklen_t 291121472Sumeinet6_rth_space(int type, int segments) 292121472Sume{ 293121472Sume switch (type) { 294121472Sume case IPV6_RTHDR_TYPE_0: 295168867Smtm if ((segments >= 0) && (segments <= 127)) 296168867Smtm return (((segments * 2) + 1) << 3); 297168867Smtm /* FALLTHROUGH */ 298121472Sume default: 299121472Sume return (0); /* type not suppported */ 300121472Sume } 30154696Sshin} 302121472Sume 303121472Sumevoid * 304121472Sumeinet6_rth_init(void *bp, socklen_t bp_len, int type, int segments) 305121472Sume{ 306121472Sume struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; 307121472Sume struct ip6_rthdr0 *rth0; 308121472Sume 309121472Sume switch (type) { 310121472Sume case IPV6_RTHDR_TYPE_0: 311121472Sume /* length validation */ 312121472Sume if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments)) 313121472Sume return (NULL); 314168867Smtm /* segment validation */ 315168867Smtm if ((segments < 0) || (segments > 127)) 316168867Smtm return (NULL); 317121472Sume 318121472Sume memset(bp, 0, bp_len); 319121472Sume rth0 = (struct ip6_rthdr0 *)rth; 320121472Sume rth0->ip6r0_len = segments * 2; 321121472Sume rth0->ip6r0_type = IPV6_RTHDR_TYPE_0; 322121472Sume rth0->ip6r0_segleft = 0; 323121472Sume rth0->ip6r0_reserved = 0; 324121472Sume break; 325121472Sume default: 326121472Sume return (NULL); /* type not supported */ 327121472Sume } 328121472Sume 329121472Sume return (bp); 330121472Sume} 331121472Sume 332121472Sumeint 333121472Sumeinet6_rth_add(void *bp, const struct in6_addr *addr) 334121472Sume{ 335121472Sume struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; 336121472Sume struct ip6_rthdr0 *rth0; 337121472Sume struct in6_addr *nextaddr; 338121472Sume 339121472Sume switch (rth->ip6r_type) { 340121472Sume case IPV6_RTHDR_TYPE_0: 341121472Sume rth0 = (struct ip6_rthdr0 *)rth; 342168867Smtm /* Don't exceed the number of stated segments */ 343168867Smtm if (rth0->ip6r0_segleft == (rth0->ip6r0_len / 2)) 344168867Smtm return (-1); 345121472Sume nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft; 346121472Sume *nextaddr = *addr; 347121472Sume rth0->ip6r0_segleft++; 348121472Sume break; 349121472Sume default: 350121472Sume return (-1); /* type not supported */ 351121472Sume } 352121472Sume 353121472Sume return (0); 354121472Sume} 355121472Sume 356121472Sumeint 357121472Sumeinet6_rth_reverse(const void *in, void *out) 358121472Sume{ 359121472Sume struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in; 360121472Sume struct ip6_rthdr0 *rth0_in, *rth0_out; 361121472Sume int i, segments; 362121472Sume 363121472Sume switch (rth_in->ip6r_type) { 364121472Sume case IPV6_RTHDR_TYPE_0: 365121472Sume rth0_in = (struct ip6_rthdr0 *)in; 366121472Sume rth0_out = (struct ip6_rthdr0 *)out; 367121472Sume 368121472Sume /* parameter validation XXX too paranoid? */ 369121472Sume if (rth0_in->ip6r0_len % 2) 370121472Sume return (-1); 371121472Sume segments = rth0_in->ip6r0_len / 2; 372121472Sume 373121472Sume /* we can't use memcpy here, since in and out may overlap */ 374121472Sume memmove((void *)rth0_out, (void *)rth0_in, 375121472Sume ((rth0_in->ip6r0_len) + 1) << 3); 376121472Sume rth0_out->ip6r0_segleft = segments; 377121472Sume 378121472Sume /* reverse the addresses */ 379121472Sume for (i = 0; i < segments / 2; i++) { 380121472Sume struct in6_addr addr_tmp, *addr1, *addr2; 381121472Sume 382121472Sume addr1 = (struct in6_addr *)(rth0_out + 1) + i; 383121472Sume addr2 = (struct in6_addr *)(rth0_out + 1) + 384121472Sume (segments - i - 1); 385121472Sume addr_tmp = *addr1; 386121472Sume *addr1 = *addr2; 387121472Sume *addr2 = addr_tmp; 388121472Sume } 389121472Sume 390121472Sume break; 391121472Sume default: 392121472Sume return (-1); /* type not supported */ 393121472Sume } 394121472Sume 395121472Sume return (0); 396121472Sume} 397121472Sume 398121472Sumeint 399121472Sumeinet6_rth_segments(const void *bp) 400121472Sume{ 401121472Sume struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; 402121472Sume struct ip6_rthdr0 *rh0; 403121472Sume int addrs; 404121472Sume 405121472Sume switch (rh->ip6r_type) { 406121472Sume case IPV6_RTHDR_TYPE_0: 407121472Sume rh0 = (struct ip6_rthdr0 *)bp; 408121472Sume 409121472Sume /* 410121472Sume * Validation for a type-0 routing header. 411121472Sume * Is this too strict? 412121472Sume */ 413121472Sume if ((rh0->ip6r0_len % 2) != 0 || 414121472Sume (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) 415121472Sume return (-1); 416121472Sume 417121472Sume return (addrs); 418121472Sume default: 419121472Sume return (-1); /* unknown type */ 420121472Sume } 421121472Sume} 422121472Sume 423121472Sumestruct in6_addr * 424121472Sumeinet6_rth_getaddr(const void *bp, int idx) 425121472Sume{ 426121472Sume struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; 427121472Sume struct ip6_rthdr0 *rh0; 428148159Sume int addrs; 429121472Sume 430121472Sume switch (rh->ip6r_type) { 431121472Sume case IPV6_RTHDR_TYPE_0: 432121472Sume rh0 = (struct ip6_rthdr0 *)bp; 433121472Sume 434121472Sume /* 435121472Sume * Validation for a type-0 routing header. 436121472Sume * Is this too strict? 437121472Sume */ 438148159Sume if ((rh0->ip6r0_len % 2) != 0 || 439148159Sume (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) 440121472Sume return (NULL); 441121472Sume 442121472Sume if (idx < 0 || addrs <= idx) 443121472Sume return (NULL); 444121472Sume 445121472Sume return (((struct in6_addr *)(rh0 + 1)) + idx); 446121472Sume default: 447121472Sume return (NULL); /* unknown type */ 448121472Sume break; 449121472Sume } 450121472Sume} 451