1 /* $OpenBSD: rthdr.c,v 1.12 2016/09/21 04:38:56 guenther Exp $ */ 2 /* $KAME: rthdr.c,v 1.22 2006/02/09 08:18:58 keiichi Exp $ */ 3 4 /* 5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/socket.h> 35 36 #include <netinet/in.h> 37 #include <netinet/ip6.h> 38 39 #include <string.h> 40 #include <stdio.h> 41 42 /* 43 * RFC3542 (2292bis) API 44 */ 45 46 socklen_t 47 inet6_rth_space(int type, int segments) 48 { 49 switch (type) { 50 case IPV6_RTHDR_TYPE_0: 51 return (((segments * 2) + 1) << 3); 52 default: 53 return (0); /* type not suppported */ 54 } 55 } 56 DEF_WEAK(inet6_rth_space); 57 58 void * 59 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments) 60 { 61 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; 62 struct ip6_rthdr0 *rth0; 63 64 switch (type) { 65 case IPV6_RTHDR_TYPE_0: 66 /* length validation */ 67 if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments)) 68 return (NULL); 69 70 memset(bp, 0, bp_len); 71 rth0 = (struct ip6_rthdr0 *)rth; 72 rth0->ip6r0_len = segments * 2; 73 rth0->ip6r0_type = IPV6_RTHDR_TYPE_0; 74 rth0->ip6r0_segleft = 0; 75 rth0->ip6r0_reserved = 0; 76 break; 77 default: 78 return (NULL); /* type not supported */ 79 } 80 81 return (bp); 82 } 83 84 int 85 inet6_rth_add(void *bp, const struct in6_addr *addr) 86 { 87 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; 88 struct ip6_rthdr0 *rth0; 89 struct in6_addr *nextaddr; 90 91 switch (rth->ip6r_type) { 92 case IPV6_RTHDR_TYPE_0: 93 rth0 = (struct ip6_rthdr0 *)rth; 94 nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft; 95 *nextaddr = *addr; 96 rth0->ip6r0_segleft++; 97 break; 98 default: 99 return (-1); /* type not supported */ 100 } 101 102 return (0); 103 } 104 105 int 106 inet6_rth_reverse(const void *in, void *out) 107 { 108 struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in; 109 struct ip6_rthdr0 *rth0_in, *rth0_out; 110 int i, segments; 111 112 switch (rth_in->ip6r_type) { 113 case IPV6_RTHDR_TYPE_0: 114 rth0_in = (struct ip6_rthdr0 *)in; 115 rth0_out = (struct ip6_rthdr0 *)out; 116 117 /* parameter validation XXX too paranoid? */ 118 if (rth0_in->ip6r0_len % 2) 119 return (-1); 120 segments = rth0_in->ip6r0_len / 2; 121 122 /* we can't use memcpy here, since in and out may overlap */ 123 memmove(rth0_out, rth0_in, ((rth0_in->ip6r0_len) + 1) << 3); 124 rth0_out->ip6r0_segleft = segments; 125 126 /* reverse the addresses */ 127 for (i = 0; i < segments / 2; i++) { 128 struct in6_addr addr_tmp, *addr1, *addr2; 129 130 addr1 = (struct in6_addr *)(rth0_out + 1) + i; 131 addr2 = (struct in6_addr *)(rth0_out + 1) + 132 (segments - i - 1); 133 addr_tmp = *addr1; 134 *addr1 = *addr2; 135 *addr2 = addr_tmp; 136 } 137 138 break; 139 default: 140 return (-1); /* type not supported */ 141 } 142 143 return (0); 144 } 145 146 int 147 inet6_rth_segments(const void *bp) 148 { 149 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; 150 struct ip6_rthdr0 *rh0; 151 int addrs; 152 153 switch (rh->ip6r_type) { 154 case IPV6_RTHDR_TYPE_0: 155 rh0 = (struct ip6_rthdr0 *)bp; 156 157 /* 158 * Validation for a type-0 routing header. 159 * Is this too strict? 160 */ 161 if ((rh0->ip6r0_len % 2) != 0 || 162 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) 163 return (-1); 164 165 return (addrs); 166 default: 167 return (-1); /* unknown type */ 168 } 169 } 170 171 struct in6_addr * 172 inet6_rth_getaddr(const void *bp, int idx) 173 { 174 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; 175 struct ip6_rthdr0 *rh0; 176 int addrs; 177 178 switch (rh->ip6r_type) { 179 case IPV6_RTHDR_TYPE_0: 180 rh0 = (struct ip6_rthdr0 *)bp; 181 182 /* 183 * Validation for a type-0 routing header. 184 * Is this too strict? 185 */ 186 if ((rh0->ip6r0_len % 2) != 0 || 187 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) 188 return (NULL); 189 190 if (idx < 0 || addrs <= idx) 191 return (NULL); 192 193 return (((struct in6_addr *)(rh0 + 1)) + idx); 194 default: 195 return (NULL); /* unknown type */ 196 break; 197 } 198 } 199