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