1 /* $NetBSD: rthdr.c,v 1.12 2002/06/27 10:22:12 itojun Exp $ */ 2 3 /* 4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the project nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #if defined(LIBC_SCCS) && !defined(lint) 34 __RCSID("$NetBSD: rthdr.c,v 1.12 2002/06/27 10:22:12 itojun Exp $"); 35 #endif /* LIBC_SCCS and not lint */ 36 37 #include "namespace.h" 38 #include <sys/param.h> 39 #include <sys/types.h> 40 #include <sys/socket.h> 41 42 #include <netinet/in.h> 43 #include <netinet/ip6.h> 44 45 #include <assert.h> 46 #include <string.h> 47 #include <stdio.h> 48 49 #ifdef __weak_alias 50 __weak_alias(inet6_rthdr_add,_inet6_rthdr_add) 51 __weak_alias(inet6_rthdr_getaddr,_inet6_rthdr_getaddr) 52 __weak_alias(inet6_rthdr_getflags,_inet6_rthdr_getflags) 53 __weak_alias(inet6_rthdr_init,_inet6_rthdr_init) 54 __weak_alias(inet6_rthdr_lasthop,_inet6_rthdr_lasthop) 55 __weak_alias(inet6_rthdr_segments,_inet6_rthdr_segments) 56 __weak_alias(inet6_rthdr_space,_inet6_rthdr_space) 57 #endif 58 59 size_t 60 inet6_rthdr_space(type, seg) 61 int type, seg; 62 { 63 switch(type) { 64 case IPV6_RTHDR_TYPE_0: 65 if (seg < 1 || seg > 23) 66 return(0); 67 return(CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) 68 + sizeof(struct ip6_rthdr0))); 69 default: 70 #ifdef DEBUG 71 fprintf(stderr, "inet6_rthdr_space: unknown type(%d)\n", type); 72 #endif 73 return(0); 74 } 75 } 76 77 struct cmsghdr * 78 inet6_rthdr_init(bp, type) 79 void *bp; 80 int type; 81 { 82 struct cmsghdr *ch; 83 struct ip6_rthdr *rthdr; 84 85 _DIAGASSERT(bp != NULL); 86 87 ch = (struct cmsghdr *)bp; 88 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(ch); 89 90 ch->cmsg_level = IPPROTO_IPV6; 91 ch->cmsg_type = IPV6_RTHDR; 92 93 switch(type) { 94 case IPV6_RTHDR_TYPE_0: 95 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - sizeof(struct in6_addr)); 96 (void)memset(rthdr, 0, sizeof(struct ip6_rthdr0)); 97 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0; 98 return(ch); 99 default: 100 #ifdef DEBUG 101 fprintf(stderr, "inet6_rthdr_init: unknown type(%d)\n", type); 102 #endif 103 return(NULL); 104 } 105 } 106 107 int 108 inet6_rthdr_add(cmsg, addr, flags) 109 struct cmsghdr *cmsg; 110 const struct in6_addr *addr; 111 u_int flags; 112 { 113 struct ip6_rthdr *rthdr; 114 115 _DIAGASSERT(cmsg != NULL); 116 _DIAGASSERT(addr != NULL); 117 118 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg); 119 120 switch(rthdr->ip6r_type) { 121 case IPV6_RTHDR_TYPE_0: 122 { 123 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr; 124 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) { 125 #ifdef DEBUG 126 fprintf(stderr, "inet6_rthdr_add: unsupported flag(%u)\n", flags); 127 #endif 128 return(-1); 129 } 130 if (rt0->ip6r0_segleft == 23) { 131 #ifdef DEBUG 132 fprintf(stderr, "inet6_rthdr_add: segment overflow\n"); 133 #endif 134 return(-1); 135 } 136 if (flags == IPV6_RTHDR_STRICT) { 137 int c, b; 138 c = rt0->ip6r0_segleft / 8; 139 b = rt0->ip6r0_segleft % 8; 140 rt0->ip6r0_slmap[c] |= (1 << (7 - b)); 141 } 142 rt0->ip6r0_segleft++; 143 (void)memcpy(((caddr_t)(void *)rt0) + ((rt0->ip6r0_len + 1) << 3), addr, 144 sizeof(struct in6_addr)); 145 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3; 146 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3); 147 break; 148 } 149 default: 150 #ifdef DEBUG 151 fprintf(stderr, "inet6_rthdr_add: unknown type(%u)\n", 152 rthdr->ip6r_type); 153 #endif 154 return(-1); 155 } 156 157 return(0); 158 } 159 160 int 161 inet6_rthdr_lasthop(cmsg, flags) 162 struct cmsghdr *cmsg; 163 unsigned int flags; 164 { 165 struct ip6_rthdr *rthdr; 166 167 _DIAGASSERT(cmsg != NULL); 168 169 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg); 170 171 switch(rthdr->ip6r_type) { 172 case IPV6_RTHDR_TYPE_0: 173 { 174 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr; 175 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) { 176 #ifdef DEBUG 177 fprintf(stderr, "inet6_rthdr_lasthop: unsupported flag(%u)\n", flags); 178 #endif 179 return(-1); 180 } 181 if (rt0->ip6r0_segleft > 23) { 182 #ifdef DEBUG 183 fprintf(stderr, "inet6_rthdr_add: segment overflow\n"); 184 #endif 185 return(-1); 186 } 187 if (flags == IPV6_RTHDR_STRICT) { 188 int c, b; 189 c = rt0->ip6r0_segleft / 8; 190 b = rt0->ip6r0_segleft % 8; 191 rt0->ip6r0_slmap[c] |= (1 << (7 - b)); 192 } 193 break; 194 } 195 default: 196 #ifdef DEBUG 197 fprintf(stderr, "inet6_rthdr_lasthop: unknown type(%u)\n", 198 rthdr->ip6r_type); 199 #endif 200 return(-1); 201 } 202 203 return(0); 204 } 205 206 #if 0 207 int 208 inet6_rthdr_reverse(in, out) 209 const struct cmsghdr *in; 210 struct cmsghdr *out; 211 { 212 #ifdef DEBUG 213 fprintf(stderr, "inet6_rthdr_reverse: not implemented yet\n"); 214 #endif 215 return -1; 216 } 217 #endif 218 219 int 220 inet6_rthdr_segments(cmsg) 221 const struct cmsghdr *cmsg; 222 { 223 const struct ip6_rthdr *rthdr; 224 225 _DIAGASSERT(cmsg != NULL); 226 227 /*LINTED const castaway*/ 228 rthdr = (const struct ip6_rthdr *)(const void *)CMSG_DATA(cmsg); 229 230 switch(rthdr->ip6r_type) { 231 case IPV6_RTHDR_TYPE_0: 232 { 233 const struct ip6_rthdr0 *rt0 = 234 (const struct ip6_rthdr0 *)(const void *)rthdr; 235 236 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) { 237 #ifdef DEBUG 238 fprintf(stderr, "inet6_rthdr_segments: invalid size(%u)\n", 239 rt0->ip6r0_len); 240 #endif 241 return -1; 242 } 243 244 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 245 } 246 247 default: 248 #ifdef DEBUG 249 fprintf(stderr, "inet6_rthdr_segments: unknown type(%u)\n", 250 rthdr->ip6r_type); 251 #endif 252 return -1; 253 } 254 } 255 256 struct in6_addr * 257 inet6_rthdr_getaddr(cmsg, idx) 258 struct cmsghdr *cmsg; 259 int idx; 260 { 261 struct ip6_rthdr *rthdr; 262 263 _DIAGASSERT(cmsg != NULL); 264 265 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg); 266 267 switch(rthdr->ip6r_type) { 268 case IPV6_RTHDR_TYPE_0: 269 { 270 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr; 271 int naddr; 272 273 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) { 274 #ifdef DEBUG 275 fprintf(stderr, "inet6_rthdr_getaddr: invalid size(%u)\n", 276 rt0->ip6r0_len); 277 #endif 278 return NULL; 279 } 280 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 281 if (idx <= 0 || naddr < idx) { 282 #ifdef DEBUG 283 fprintf(stderr, "inet6_rthdr_getaddr: invalid index(%d)\n", idx); 284 #endif 285 return NULL; 286 } 287 return &rt0->ip6r0_addr[idx - 1]; 288 } 289 290 default: 291 #ifdef DEBUG 292 fprintf(stderr, "inet6_rthdr_getaddr: unknown type(%u)\n", 293 rthdr->ip6r_type); 294 #endif 295 return NULL; 296 } 297 } 298 299 int 300 inet6_rthdr_getflags(cmsg, idx) 301 const struct cmsghdr *cmsg; 302 int idx; 303 { 304 const struct ip6_rthdr *rthdr; 305 306 _DIAGASSERT(cmsg != NULL); 307 308 /*LINTED const castaway*/ 309 rthdr = (const struct ip6_rthdr *)(const void *)CMSG_DATA(cmsg); 310 311 switch(rthdr->ip6r_type) { 312 case IPV6_RTHDR_TYPE_0: 313 { 314 const struct ip6_rthdr0 *rt0 = (const struct ip6_rthdr0 *) 315 (const void *)rthdr; 316 int naddr; 317 318 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) { 319 #ifdef DEBUG 320 fprintf(stderr, "inet6_rthdr_getflags: invalid size(%u)\n", 321 rt0->ip6r0_len); 322 #endif 323 return -1; 324 } 325 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 326 if (idx < 0 || naddr < idx) { 327 #ifdef DEBUG 328 fprintf(stderr, "inet6_rthdr_getflags: invalid index(%d)\n", idx); 329 #endif 330 return -1; 331 } 332 if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8))) 333 return IPV6_RTHDR_STRICT; 334 else 335 return IPV6_RTHDR_LOOSE; 336 } 337 338 default: 339 #ifdef DEBUG 340 fprintf(stderr, "inet6_rthdr_getflags: unknown type(%u)\n", 341 rthdr->ip6r_type); 342 #endif 343 return -1; 344 } 345 } 346