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