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