1 /* $NetBSD: rthdr.c,v 1.4 1999/09/20 04:39:18 lukem 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/param.h> 33 #include <sys/types.h> 34 #include <sys/socket.h> 35 36 #include <netinet/in.h> 37 #include <netinet/ip6.h> 38 39 #include <assert.h> 40 #include <string.h> 41 #include <stdio.h> 42 43 size_t 44 inet6_rthdr_space(type, seg) 45 int type, seg; 46 { 47 switch(type) { 48 case IPV6_RTHDR_TYPE_0: 49 if (seg < 1 || seg > 23) 50 return(0); 51 return(CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) 52 + sizeof(struct ip6_rthdr0))); 53 default: 54 #ifdef DEBUG 55 fprintf(stderr, "inet6_rthdr_space: unknown type(%d)\n", type); 56 #endif 57 return(0); 58 } 59 } 60 61 struct cmsghdr * 62 inet6_rthdr_init(bp, type) 63 void *bp; 64 int type; 65 { 66 register struct cmsghdr *ch; 67 register struct ip6_rthdr *rthdr; 68 69 _DIAGASSERT(bp != NULL); 70 71 ch = (struct cmsghdr *)bp; 72 rthdr = (struct ip6_rthdr *)(ch + 1); 73 74 ch->cmsg_level = IPPROTO_IPV6; 75 ch->cmsg_type = IPV6_RTHDR; 76 77 switch(type) { 78 case IPV6_RTHDR_TYPE_0: 79 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - sizeof(struct in6_addr)); 80 bzero(rthdr, sizeof(struct ip6_rthdr0)); 81 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0; 82 return(ch); 83 default: 84 #ifdef DEBUG 85 fprintf(stderr, "inet6_rthdr_init: unknown type(%d)\n", type); 86 #endif 87 return(NULL); 88 } 89 } 90 91 int 92 inet6_rthdr_add(cmsg, addr, flags) 93 struct cmsghdr *cmsg; 94 const struct in6_addr *addr; 95 u_int flags; 96 { 97 register struct ip6_rthdr *rthdr; 98 99 _DIAGASSERT(cmsg != NULL); 100 _DIAGASSERT(addr != NULL); 101 102 rthdr = (struct ip6_rthdr *)(cmsg + 1); 103 104 switch(rthdr->ip6r_type) { 105 case IPV6_RTHDR_TYPE_0: 106 { 107 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 108 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) { 109 #ifdef DEBUG 110 fprintf(stderr, "inet6_rthdr_add: unsupported flag(%d)\n", flags); 111 #endif 112 return(-1); 113 } 114 if (rt0->ip6r0_segleft == 23) { 115 #ifdef DEBUG 116 fprintf(stderr, "inet6_rthdr_add: segment overflow\n"); 117 #endif 118 return(-1); 119 } 120 if (flags == IPV6_RTHDR_STRICT) { 121 int c, b; 122 c = rt0->ip6r0_segleft / 8; 123 b = rt0->ip6r0_segleft % 8; 124 rt0->ip6r0_slmap[c] |= (1 << (7 - b)); 125 } 126 rt0->ip6r0_segleft++; 127 bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3), 128 sizeof(struct in6_addr)); 129 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3; 130 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3); 131 break; 132 } 133 default: 134 #ifdef DEBUG 135 fprintf(stderr, "inet6_rthdr_add: unknown type(%d)\n", 136 rthdr->ip6r_type); 137 #endif 138 return(-1); 139 } 140 141 return(0); 142 } 143 144 int 145 inet6_rthdr_lasthop(cmsg, flags) 146 struct cmsghdr *cmsg; 147 unsigned int flags; 148 { 149 register struct ip6_rthdr *rthdr; 150 151 _DIAGASSERT(cmsg != NULL); 152 153 rthdr = (struct ip6_rthdr *)(cmsg + 1); 154 155 switch(rthdr->ip6r_type) { 156 case IPV6_RTHDR_TYPE_0: 157 { 158 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 159 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) { 160 #ifdef DEBUG 161 fprintf(stderr, "inet6_rthdr_lasthop: unsupported flag(%d)\n", flags); 162 #endif 163 return(-1); 164 } 165 if (rt0->ip6r0_segleft > 23) { 166 #ifdef DEBUG 167 fprintf(stderr, "inet6_rthdr_add: segment overflow\n"); 168 #endif 169 return(-1); 170 } 171 if (flags == IPV6_RTHDR_STRICT) { 172 int c, b; 173 c = rt0->ip6r0_segleft / 8; 174 b = rt0->ip6r0_segleft % 8; 175 rt0->ip6r0_slmap[c] |= (1 << (7 - b)); 176 } 177 break; 178 } 179 default: 180 #ifdef DEBUG 181 fprintf(stderr, "inet6_rthdr_lasthop: unknown type(%d)\n", 182 rthdr->ip6r_type); 183 #endif 184 return(-1); 185 } 186 187 return(0); 188 } 189 190 #if 0 191 int 192 inet6_rthdr_reverse(in, out) 193 const struct cmsghdr *in; 194 struct cmsghdr *out; 195 { 196 #ifdef DEBUG 197 fprintf(stderr, "inet6_rthdr_reverse: not implemented yet\n"); 198 #endif 199 return -1; 200 } 201 #endif 202 203 int 204 inet6_rthdr_segments(cmsg) 205 const struct cmsghdr *cmsg; 206 { 207 register struct ip6_rthdr *rthdr; 208 209 _DIAGASSERT(cmsg != NULL); 210 211 rthdr = (struct ip6_rthdr *)(cmsg + 1); 212 213 switch(rthdr->ip6r_type) { 214 case IPV6_RTHDR_TYPE_0: 215 { 216 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 217 218 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) { 219 #ifdef DEBUG 220 fprintf(stderr, "inet6_rthdr_segments: invalid size(%d)\n", 221 rt0->ip6r0_len); 222 #endif 223 return -1; 224 } 225 226 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 227 } 228 229 default: 230 #ifdef DEBUG 231 fprintf(stderr, "inet6_rthdr_segments: unknown type(%d)\n", 232 rthdr->ip6r_type); 233 #endif 234 return -1; 235 } 236 } 237 238 struct in6_addr * 239 inet6_rthdr_getaddr(cmsg, index) 240 struct cmsghdr *cmsg; 241 int index; 242 { 243 register struct ip6_rthdr *rthdr; 244 245 _DIAGASSERT(cmsg != NULL); 246 247 rthdr = (struct ip6_rthdr *)(cmsg + 1); 248 249 switch(rthdr->ip6r_type) { 250 case IPV6_RTHDR_TYPE_0: 251 { 252 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 253 int naddr; 254 255 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) { 256 #ifdef DEBUG 257 fprintf(stderr, "inet6_rthdr_getaddr: invalid size(%d)\n", 258 rt0->ip6r0_len); 259 #endif 260 return NULL; 261 } 262 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 263 if (index <= 0 || naddr < index) { 264 #ifdef DEBUG 265 fprintf(stderr, "inet6_rthdr_getaddr: invalid index(%d)\n", index); 266 #endif 267 return NULL; 268 } 269 return &rt0->ip6r0_addr[index - 1]; 270 } 271 272 default: 273 #ifdef DEBUG 274 fprintf(stderr, "inet6_rthdr_getaddr: unknown type(%d)\n", 275 rthdr->ip6r_type); 276 #endif 277 return NULL; 278 } 279 } 280 281 int 282 inet6_rthdr_getflags(cmsg, index) 283 const struct cmsghdr *cmsg; 284 int index; 285 { 286 register struct ip6_rthdr *rthdr; 287 288 _DIAGASSERT(cmsg != NULL); 289 290 rthdr = (struct ip6_rthdr *)(cmsg + 1); 291 292 switch(rthdr->ip6r_type) { 293 case IPV6_RTHDR_TYPE_0: 294 { 295 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; 296 int naddr; 297 298 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) { 299 #ifdef DEBUG 300 fprintf(stderr, "inet6_rthdr_getflags: invalid size(%d)\n", 301 rt0->ip6r0_len); 302 #endif 303 return -1; 304 } 305 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); 306 if (index < 0 || naddr < index) { 307 #ifdef DEBUG 308 fprintf(stderr, "inet6_rthdr_getflags: invalid index(%d)\n", index); 309 #endif 310 return -1; 311 } 312 if (rt0->ip6r0_slmap[index / 8] & (0x80 >> (index % 8))) 313 return IPV6_RTHDR_STRICT; 314 else 315 return IPV6_RTHDR_LOOSE; 316 } 317 318 default: 319 #ifdef DEBUG 320 fprintf(stderr, "inet6_rthdr_getflags: unknown type(%d)\n", 321 rthdr->ip6r_type); 322 #endif 323 return -1; 324 } 325 } 326