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