1 /* $OpenBSD: ip6opt.c,v 1.1 1999/12/11 08:09:11 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 static int ip6optlen(u_int8_t *opt, u_int8_t *lim); 43 static void inet6_insert_padopt(u_char *p, int len); 44 45 /* 46 * This function returns the number of bytes required to hold an option 47 * when it is stored as ancillary data, including the cmsghdr structure 48 * at the beginning, and any padding at the end (to make its size a 49 * multiple of 8 bytes). The argument is the size of the structure 50 * defining the option, which must include any pad bytes at the 51 * beginning (the value y in the alignment term "xn + y"), the type 52 * byte, the length byte, and the option data. 53 */ 54 int 55 inet6_option_space(nbytes) 56 int nbytes; 57 { 58 nbytes += 2; /* we need space for nxt-hdr and length fields */ 59 return(CMSG_SPACE((nbytes + 7) & ~7)); 60 } 61 62 /* 63 * This function is called once per ancillary data object that will 64 * contain either Hop-by-Hop or Destination options. It returns 0 on 65 * success or -1 on an error. 66 */ 67 int 68 inet6_option_init(bp, cmsgp, type) 69 void *bp; 70 struct cmsghdr **cmsgp; 71 int type; 72 { 73 register struct cmsghdr *ch = (struct cmsghdr *)bp; 74 75 /* argument validation */ 76 if (type != IPV6_HOPOPTS && type != IPV6_DSTOPTS) 77 return(-1); 78 79 ch->cmsg_level = IPPROTO_IPV6; 80 ch->cmsg_type = type; 81 ch->cmsg_len = CMSG_LEN(0); 82 83 *cmsgp = ch; 84 return(0); 85 } 86 87 /* 88 * This function appends a Hop-by-Hop option or a Destination option 89 * into an ancillary data object that has been initialized by 90 * inet6_option_init(). This function returns 0 if it succeeds or -1 on 91 * an error. 92 * multx is the value x in the alignment term "xn + y" described 93 * earlier. It must have a value of 1, 2, 4, or 8. 94 * plusy is the value y in the alignment term "xn + y" described 95 * earlier. It must have a value between 0 and 7, inclusive. 96 */ 97 int 98 inet6_option_append(cmsg, typep, multx, plusy) 99 struct cmsghdr *cmsg; 100 const u_int8_t *typep; 101 int multx; 102 int plusy; 103 { 104 int padlen, optlen, off; 105 register u_char *bp = (u_char *)cmsg + cmsg->cmsg_len; 106 struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg); 107 108 /* argument validation */ 109 if (multx != 1 && multx != 2 && multx != 4 && multx != 8) 110 return(-1); 111 if (plusy < 0 || plusy > 7) 112 return(-1); 113 if (typep[0] > 255) 114 return(-1); 115 116 /* 117 * If this is the first option, allocate space for the 118 * first 2 bytes(for next header and length fields) of 119 * the option header. 120 */ 121 if (bp == (u_char *)eh) { 122 bp += 2; 123 cmsg->cmsg_len += 2; 124 } 125 126 /* calculate pad length before the option. */ 127 off = bp - (u_char *)eh; 128 padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - 129 (off % multx); 130 padlen += plusy; 131 padlen %= multx; /* keep the pad as short as possible */ 132 /* insert padding */ 133 inet6_insert_padopt(bp, padlen); 134 cmsg->cmsg_len += padlen; 135 bp += padlen; 136 137 /* copy the option */ 138 if (typep[0] == IP6OPT_PAD1) 139 optlen = 1; 140 else 141 optlen = typep[1] + 2; 142 memcpy(bp, typep, optlen); 143 bp += optlen; 144 cmsg->cmsg_len += optlen; 145 146 /* calculate pad length after the option and insert the padding */ 147 off = bp - (u_char *)eh; 148 padlen = ((off + 7) & ~7) - off; 149 inet6_insert_padopt(bp, padlen); 150 bp += padlen; 151 cmsg->cmsg_len += padlen; 152 153 /* update the length field of the ip6 option header */ 154 eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; 155 156 return(0); 157 } 158 159 /* 160 * This function appends a Hop-by-Hop option or a Destination option 161 * into an ancillary data object that has been initialized by 162 * inet6_option_init(). This function returns a pointer to the 8-bit 163 * option type field that starts the option on success, or NULL on an 164 * error. 165 * The difference between this function and inet6_option_append() is 166 * that the latter copies the contents of a previously built option into 167 * the ancillary data object while the current function returns a 168 * pointer to the space in the data object where the option's TLV must 169 * then be built by the caller. 170 * 171 */ 172 u_int8_t * 173 inet6_option_alloc(cmsg, datalen, multx, plusy) 174 struct cmsghdr *cmsg; 175 int datalen; 176 int multx; 177 int plusy; 178 { 179 int padlen, off; 180 register u_int8_t *bp = (u_char *)cmsg + cmsg->cmsg_len; 181 u_int8_t *retval; 182 struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg); 183 184 /* argument validation */ 185 if (multx != 1 && multx != 2 && multx != 4 && multx != 8) 186 return(NULL); 187 if (plusy < 0 || plusy > 7) 188 return(NULL); 189 190 /* 191 * If this is the first option, allocate space for the 192 * first 2 bytes(for next header and length fields) of 193 * the option header. 194 */ 195 if (bp == (u_char *)eh) { 196 bp += 2; 197 cmsg->cmsg_len += 2; 198 } 199 200 /* calculate pad length before the option. */ 201 off = bp - (u_char *)eh; 202 padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - 203 (off % multx); 204 padlen += plusy; 205 padlen %= multx; /* keep the pad as short as possible */ 206 /* insert padding */ 207 inet6_insert_padopt(bp, padlen); 208 cmsg->cmsg_len += padlen; 209 bp += padlen; 210 211 /* keep space to store specified length of data */ 212 retval = bp; 213 bp += datalen; 214 cmsg->cmsg_len += datalen; 215 216 /* calculate pad length after the option and insert the padding */ 217 off = bp - (u_char *)eh; 218 padlen = ((off + 7) & ~7) - off; 219 inet6_insert_padopt(bp, padlen); 220 bp += padlen; 221 cmsg->cmsg_len += padlen; 222 223 /* update the length field of the ip6 option header */ 224 eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; 225 226 return(retval); 227 } 228 229 /* 230 * This function processes the next Hop-by-Hop option or Destination 231 * option in an ancillary data object. If another option remains to be 232 * processed, the return value of the function is 0 and *tptrp points to 233 * the 8-bit option type field (which is followed by the 8-bit option 234 * data length, followed by the option data). If no more options remain 235 * to be processed, the return value is -1 and *tptrp is NULL. If an 236 * error occurs, the return value is -1 and *tptrp is not NULL. 237 * (RFC 2292, 6.3.5) 238 */ 239 int 240 inet6_option_next(cmsg, tptrp) 241 const struct cmsghdr *cmsg; 242 u_int8_t **tptrp; 243 { 244 struct ip6_ext *ip6e; 245 int hdrlen, optlen; 246 u_int8_t *lim; 247 248 if (cmsg->cmsg_level != IPPROTO_IPV6 || 249 (cmsg->cmsg_type != IPV6_HOPOPTS && 250 cmsg->cmsg_type != IPV6_DSTOPTS)) 251 return(-1); 252 253 /* message length validation */ 254 if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) 255 return(-1); 256 ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); 257 hdrlen = (ip6e->ip6e_len + 1) << 3; 258 if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) 259 return(-1); 260 261 /* 262 * If the caller does not specify the starting point, 263 * simply return the 1st option. 264 * Otherwise, search the option list for the next option. 265 */ 266 lim = (u_int8_t *)ip6e + hdrlen; 267 if (*tptrp == NULL) 268 *tptrp = (u_int8_t *)(ip6e + 1); 269 else { 270 if ((optlen = ip6optlen(*tptrp, lim)) == 0) 271 return(-1); 272 273 *tptrp = *tptrp + optlen; 274 } 275 if (*tptrp >= lim) { /* there is no option */ 276 *tptrp = NULL; 277 return(-1); 278 } 279 /* 280 * Finally, checks if the next option is safely stored in the 281 * cmsg data. 282 */ 283 if (ip6optlen(*tptrp, lim) == 0) 284 return(-1); 285 else 286 return(0); 287 } 288 289 /* 290 * This function is similar to the inet6_option_next() function, 291 * except this function lets the caller specify the option type to be 292 * searched for, instead of always returning the next option in the 293 * ancillary data object. 294 * Note: RFC 2292 says the type of tptrp is u_int8_t *, but we think 295 * it's a typo. The variable should be type of u_int8_t **. 296 */ 297 int 298 inet6_option_find(cmsg, tptrp, type) 299 const struct cmsghdr *cmsg; 300 u_int8_t **tptrp; 301 int type; 302 { 303 struct ip6_ext *ip6e; 304 int hdrlen, optlen; 305 u_int8_t *optp, *lim; 306 307 if (cmsg->cmsg_level != IPPROTO_IPV6 || 308 (cmsg->cmsg_type != IPV6_HOPOPTS && 309 cmsg->cmsg_type != IPV6_DSTOPTS)) 310 return(-1); 311 312 /* message length validation */ 313 if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) 314 return(-1); 315 ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); 316 hdrlen = (ip6e->ip6e_len + 1) << 3; 317 if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) 318 return(-1); 319 320 /* 321 * If the caller does not specify the starting point, 322 * search from the beginning of the option list. 323 * Otherwise, search from *the next option* of the specified point. 324 */ 325 lim = (u_int8_t *)ip6e + hdrlen; 326 if (*tptrp == NULL) 327 *tptrp = (u_int8_t *)(ip6e + 1); 328 else { 329 if ((optlen = ip6optlen(*tptrp, lim)) == 0) 330 return(-1); 331 332 *tptrp = *tptrp + optlen; 333 } 334 for (optp = *tptrp; optp < lim; optp += optlen) { 335 if (*optp == type) { 336 *tptrp = optp; 337 return(0); 338 } 339 if ((optlen = ip6optlen(optp, lim)) == 0) 340 return(-1); 341 } 342 343 /* search failed */ 344 *tptrp = NULL; 345 return(-1); 346 } 347 348 /* 349 * Calculate the length of a given IPv6 option. Also checks 350 * if the option is safely stored in user's buffer according to the 351 * calculated length and the limitation of the buffer. 352 */ 353 static int 354 ip6optlen(opt, lim) 355 u_int8_t *opt, *lim; 356 { 357 int optlen; 358 359 if (*opt == IP6OPT_PAD1) 360 optlen = 1; 361 else { 362 /* is there enough space to store type and len? */ 363 if (opt + 2 > lim) 364 return(0); 365 optlen = *(opt + 1) + 2; 366 } 367 if (opt + optlen <= lim) 368 return(optlen); 369 370 return(0); 371 } 372 373 static void 374 inet6_insert_padopt(u_char *p, int len) 375 { 376 switch(len) { 377 case 0: 378 return; 379 case 1: 380 p[0] = IP6OPT_PAD1; 381 return; 382 default: 383 p[0] = IP6OPT_PADN; 384 p[1] = len - 2; 385 memset(&p[2], 0, len - 2); 386 return; 387 } 388 } 389