1121472Sume/* $KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 itojun Exp $ */ 295023Ssuz 3331722Seadler/* 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/socket.h> 3754696Sshin 3854696Sshin#include <netinet/in.h> 3954696Sshin#include <netinet/ip6.h> 4054696Sshin 4154696Sshin#include <string.h> 4254696Sshin#include <stdio.h> 4354696Sshin 44121472Sume/* 45121472Sume * RFC2292 API 46121472Sume */ 47121472Sume 4854696Sshinsize_t 49288045Srodrigcinet6_rthdr_space(int type, int seg) 5054696Sshin{ 51121472Sume switch (type) { 52121472Sume case IPV6_RTHDR_TYPE_0: 53121472Sume if (seg < 1 || seg > 23) 54121472Sume return (0); 55121472Sume#ifdef COMPAT_RFC2292 56121472Sume return (CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) + 57121472Sume sizeof(struct ip6_rthdr0))); 58121472Sume#else 59121472Sume return (CMSG_SPACE(sizeof(struct in6_addr) * seg + 60121472Sume sizeof(struct ip6_rthdr0))); 6154696Sshin#endif 62121472Sume default: 63121472Sume return (0); 64121472Sume } 6554696Sshin} 6654696Sshin 6754696Sshinstruct cmsghdr * 68288045Srodrigcinet6_rthdr_init(void *bp, int type) 6954696Sshin{ 70121472Sume struct cmsghdr *ch = (struct cmsghdr *)bp; 71121472Sume struct ip6_rthdr *rthdr; 7254696Sshin 73121472Sume rthdr = (struct ip6_rthdr *)CMSG_DATA(ch); 7457719Sshin 75121472Sume ch->cmsg_level = IPPROTO_IPV6; 76121472Sume ch->cmsg_type = IPV6_RTHDR; 7754696Sshin 78121472Sume switch (type) { 79121472Sume case IPV6_RTHDR_TYPE_0: 80121472Sume#ifdef COMPAT_RFC2292 81121472Sume ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - 82121472Sume sizeof(struct in6_addr)); 83121472Sume#else 84121472Sume ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0)); 8554696Sshin#endif 86121472Sume 87121472Sume bzero(rthdr, sizeof(struct ip6_rthdr0)); 88121472Sume rthdr->ip6r_type = IPV6_RTHDR_TYPE_0; 89121472Sume return (ch); 90121472Sume default: 91121472Sume return (NULL); 92121472Sume } 9354696Sshin} 9454696Sshin 95121472Sume/* ARGSUSED */ 9654696Sshinint 97288045Srodrigcinet6_rthdr_add(struct cmsghdr *cmsg, const struct in6_addr *addr, u_int flags) 9854696Sshin{ 99121472Sume struct ip6_rthdr *rthdr; 10054696Sshin 101121472Sume rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 10257719Sshin 103121472Sume switch (rthdr->ip6r_type) { 104121472Sume case IPV6_RTHDR_TYPE_0: 105121472Sume { 106121472Sume struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 107121472Sume if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) 108121472Sume return (-1); 109121472Sume if (rt0->ip6r0_segleft == 23) 110121472Sume return (-1); 111121472Sume 112121472Sume#ifdef COMPAT_RFC1883 /* XXX */ 113121472Sume if (flags == IPV6_RTHDR_STRICT) { 114121472Sume int c, b; 115121472Sume c = rt0->ip6r0_segleft / 8; 116121472Sume b = rt0->ip6r0_segleft % 8; 117121472Sume rt0->ip6r0_slmap[c] |= (1 << (7 - b)); 118121472Sume } 119121472Sume#else 120121472Sume if (flags != IPV6_RTHDR_LOOSE) 121121472Sume return (-1); 12254696Sshin#endif 123121472Sume rt0->ip6r0_segleft++; 124121472Sume bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3), 125121472Sume sizeof(struct in6_addr)); 126121472Sume rt0->ip6r0_len += sizeof(struct in6_addr) >> 3; 127121472Sume cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3); 128121472Sume break; 129121472Sume } 130121472Sume default: 131121472Sume return (-1); 132121472Sume } 13354696Sshin 134121472Sume return (0); 13554696Sshin} 13654696Sshin 137121472Sume/* ARGSUSED */ 13854696Sshinint 139288045Srodrigcinet6_rthdr_lasthop(struct cmsghdr *cmsg, unsigned int flags) 14054696Sshin{ 141121472Sume struct ip6_rthdr *rthdr; 14254696Sshin 143121472Sume rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 14457719Sshin 145121472Sume switch (rthdr->ip6r_type) { 146121472Sume case IPV6_RTHDR_TYPE_0: 147121472Sume { 148121472Sume struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 149121472Sume#ifdef COMPAT_RFC1883 /* XXX */ 150121472Sume if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) 151121472Sume return (-1); 152121472Sume#endif /* COMPAT_RFC1883 */ 153121472Sume if (rt0->ip6r0_segleft > 23) 154121472Sume return (-1); 155121472Sume#ifdef COMPAT_RFC1883 /* XXX */ 156121472Sume if (flags == IPV6_RTHDR_STRICT) { 157121472Sume int c, b; 158121472Sume c = rt0->ip6r0_segleft / 8; 159121472Sume b = rt0->ip6r0_segleft % 8; 160121472Sume rt0->ip6r0_slmap[c] |= (1 << (7 - b)); 161121472Sume } 162121472Sume#else 163121472Sume if (flags != IPV6_RTHDR_LOOSE) 164121472Sume return (-1); 165121472Sume#endif /* COMPAT_RFC1883 */ 166121472Sume break; 167121472Sume } 168121472Sume default: 169121472Sume return (-1); 170121472Sume } 17154696Sshin 172121472Sume return (0); 17354696Sshin} 17454696Sshin 17554696Sshin#if 0 17654696Sshinint 177288045Srodrigcinet6_rthdr_reverse(const struct cmsghdr *in, struct cmsghdr *out) 17854696Sshin{ 179121472Sume 180121472Sume return (-1); 18154696Sshin} 18254696Sshin#endif 18354696Sshin 18454696Sshinint 185288045Srodrigcinet6_rthdr_segments(const struct cmsghdr *cmsg) 18654696Sshin{ 187121472Sume struct ip6_rthdr *rthdr; 18854696Sshin 189121472Sume rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 19057719Sshin 191121472Sume switch (rthdr->ip6r_type) { 192121472Sume case IPV6_RTHDR_TYPE_0: 193121472Sume { 194121472Sume struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 19554696Sshin 196121472Sume if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) 197121472Sume return (-1); 198121472Sume 199121472Sume return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 20054696Sshin } 20154696Sshin 202121472Sume default: 203121472Sume return (-1); 204121472Sume } 20554696Sshin} 20654696Sshin 20754696Sshinstruct in6_addr * 208288045Srodrigcinet6_rthdr_getaddr(struct cmsghdr *cmsg, int idx) 20954696Sshin{ 210121472Sume struct ip6_rthdr *rthdr; 21154696Sshin 212121472Sume rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 21357719Sshin 214121472Sume switch (rthdr->ip6r_type) { 215121472Sume case IPV6_RTHDR_TYPE_0: 216121472Sume { 217121472Sume struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 218121472Sume int naddr; 21954696Sshin 220121472Sume if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) 221121472Sume return NULL; 222121472Sume naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 223121472Sume if (idx <= 0 || naddr < idx) 224121472Sume return NULL; 225121472Sume#ifdef COMPAT_RFC2292 226121472Sume return (((struct in6_addr *)(rt0 + 1)) + idx - 1); 227121472Sume#else 228121472Sume return (((struct in6_addr *)(rt0 + 1)) + idx); 229121472Sume#endif 23054696Sshin } 231121472Sume 232121472Sume default: 233121472Sume return NULL; 23454696Sshin } 23554696Sshin} 23654696Sshin 23754696Sshinint 238288045Srodrigcinet6_rthdr_getflags(const struct cmsghdr *cmsg, int idx) 23954696Sshin{ 240121472Sume struct ip6_rthdr *rthdr; 24154696Sshin 242121472Sume rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); 24357719Sshin 244121472Sume switch (rthdr->ip6r_type) { 245121472Sume case IPV6_RTHDR_TYPE_0: 246121472Sume { 247121472Sume struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 248121472Sume int naddr; 24954696Sshin 250121472Sume if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) 251121472Sume return (-1); 252121472Sume naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 253121472Sume if (idx < 0 || naddr < idx) 254121472Sume return (-1); 255121472Sume#ifdef COMPAT_RFC1883 /* XXX */ 256121472Sume if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8))) 257121472Sume return IPV6_RTHDR_STRICT; 258121472Sume else 259121472Sume return IPV6_RTHDR_LOOSE; 260121472Sume#else 261121472Sume return IPV6_RTHDR_LOOSE; 262121472Sume#endif /* COMPAT_RFC1883 */ 26354696Sshin } 264121472Sume 265121472Sume default: 266121472Sume return (-1); 26754696Sshin } 268121472Sume} 26954696Sshin 270121472Sume/* 271148160Sume * RFC3542 API 272121472Sume */ 273121472Sume 274121472Sumesocklen_t 275121472Sumeinet6_rth_space(int type, int segments) 276121472Sume{ 277121472Sume switch (type) { 278121472Sume case IPV6_RTHDR_TYPE_0: 279168867Smtm if ((segments >= 0) && (segments <= 127)) 280168867Smtm return (((segments * 2) + 1) << 3); 281168867Smtm /* FALLTHROUGH */ 282121472Sume default: 283121472Sume return (0); /* type not suppported */ 284121472Sume } 28554696Sshin} 286121472Sume 287121472Sumevoid * 288121472Sumeinet6_rth_init(void *bp, socklen_t bp_len, int type, int segments) 289121472Sume{ 290121472Sume struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; 291121472Sume struct ip6_rthdr0 *rth0; 292121472Sume 293121472Sume switch (type) { 294121472Sume case IPV6_RTHDR_TYPE_0: 295121472Sume /* length validation */ 296121472Sume if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments)) 297121472Sume return (NULL); 298168867Smtm /* segment validation */ 299168867Smtm if ((segments < 0) || (segments > 127)) 300168867Smtm return (NULL); 301121472Sume 302121472Sume memset(bp, 0, bp_len); 303121472Sume rth0 = (struct ip6_rthdr0 *)rth; 304121472Sume rth0->ip6r0_len = segments * 2; 305121472Sume rth0->ip6r0_type = IPV6_RTHDR_TYPE_0; 306121472Sume rth0->ip6r0_segleft = 0; 307121472Sume rth0->ip6r0_reserved = 0; 308121472Sume break; 309121472Sume default: 310121472Sume return (NULL); /* type not supported */ 311121472Sume } 312121472Sume 313121472Sume return (bp); 314121472Sume} 315121472Sume 316121472Sumeint 317121472Sumeinet6_rth_add(void *bp, const struct in6_addr *addr) 318121472Sume{ 319121472Sume struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; 320121472Sume struct ip6_rthdr0 *rth0; 321121472Sume struct in6_addr *nextaddr; 322121472Sume 323121472Sume switch (rth->ip6r_type) { 324121472Sume case IPV6_RTHDR_TYPE_0: 325121472Sume rth0 = (struct ip6_rthdr0 *)rth; 326168867Smtm /* Don't exceed the number of stated segments */ 327168867Smtm if (rth0->ip6r0_segleft == (rth0->ip6r0_len / 2)) 328168867Smtm return (-1); 329121472Sume nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft; 330121472Sume *nextaddr = *addr; 331121472Sume rth0->ip6r0_segleft++; 332121472Sume break; 333121472Sume default: 334121472Sume return (-1); /* type not supported */ 335121472Sume } 336121472Sume 337121472Sume return (0); 338121472Sume} 339121472Sume 340121472Sumeint 341121472Sumeinet6_rth_reverse(const void *in, void *out) 342121472Sume{ 343121472Sume struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in; 344121472Sume struct ip6_rthdr0 *rth0_in, *rth0_out; 345121472Sume int i, segments; 346121472Sume 347121472Sume switch (rth_in->ip6r_type) { 348121472Sume case IPV6_RTHDR_TYPE_0: 349121472Sume rth0_in = (struct ip6_rthdr0 *)in; 350121472Sume rth0_out = (struct ip6_rthdr0 *)out; 351121472Sume 352121472Sume /* parameter validation XXX too paranoid? */ 353121472Sume if (rth0_in->ip6r0_len % 2) 354121472Sume return (-1); 355121472Sume segments = rth0_in->ip6r0_len / 2; 356121472Sume 357121472Sume /* we can't use memcpy here, since in and out may overlap */ 358121472Sume memmove((void *)rth0_out, (void *)rth0_in, 359121472Sume ((rth0_in->ip6r0_len) + 1) << 3); 360121472Sume rth0_out->ip6r0_segleft = segments; 361121472Sume 362121472Sume /* reverse the addresses */ 363121472Sume for (i = 0; i < segments / 2; i++) { 364121472Sume struct in6_addr addr_tmp, *addr1, *addr2; 365121472Sume 366121472Sume addr1 = (struct in6_addr *)(rth0_out + 1) + i; 367121472Sume addr2 = (struct in6_addr *)(rth0_out + 1) + 368121472Sume (segments - i - 1); 369121472Sume addr_tmp = *addr1; 370121472Sume *addr1 = *addr2; 371121472Sume *addr2 = addr_tmp; 372121472Sume } 373121472Sume 374121472Sume break; 375121472Sume default: 376121472Sume return (-1); /* type not supported */ 377121472Sume } 378121472Sume 379121472Sume return (0); 380121472Sume} 381121472Sume 382121472Sumeint 383121472Sumeinet6_rth_segments(const void *bp) 384121472Sume{ 385121472Sume struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; 386121472Sume struct ip6_rthdr0 *rh0; 387121472Sume int addrs; 388121472Sume 389121472Sume switch (rh->ip6r_type) { 390121472Sume case IPV6_RTHDR_TYPE_0: 391121472Sume rh0 = (struct ip6_rthdr0 *)bp; 392121472Sume 393121472Sume /* 394121472Sume * Validation for a type-0 routing header. 395121472Sume * Is this too strict? 396121472Sume */ 397121472Sume if ((rh0->ip6r0_len % 2) != 0 || 398121472Sume (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) 399121472Sume return (-1); 400121472Sume 401121472Sume return (addrs); 402121472Sume default: 403121472Sume return (-1); /* unknown type */ 404121472Sume } 405121472Sume} 406121472Sume 407121472Sumestruct in6_addr * 408121472Sumeinet6_rth_getaddr(const void *bp, int idx) 409121472Sume{ 410121472Sume struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; 411121472Sume struct ip6_rthdr0 *rh0; 412148159Sume int addrs; 413121472Sume 414121472Sume switch (rh->ip6r_type) { 415121472Sume case IPV6_RTHDR_TYPE_0: 416121472Sume rh0 = (struct ip6_rthdr0 *)bp; 417121472Sume 418121472Sume /* 419121472Sume * Validation for a type-0 routing header. 420121472Sume * Is this too strict? 421121472Sume */ 422148159Sume if ((rh0->ip6r0_len % 2) != 0 || 423148159Sume (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) 424121472Sume return (NULL); 425121472Sume 426121472Sume if (idx < 0 || addrs <= idx) 427121472Sume return (NULL); 428121472Sume 429121472Sume return (((struct in6_addr *)(rh0 + 1)) + idx); 430121472Sume default: 431121472Sume return (NULL); /* unknown type */ 432121472Sume break; 433121472Sume } 434121472Sume} 435