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