1 /* $OpenBSD: ip6opt.c,v 1.7 2014/06/13 15:41:06 chrisz Exp $ */ 2 /* $KAME: ip6opt.c,v 1.18 2005/06/15 07:11:35 keiichi Exp $ */ 3 4 /* 5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 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 static int ip6optlen(u_int8_t *opt, u_int8_t *lim); 44 45 /* 46 * Calculate the length of a given IPv6 option. Also checks 47 * if the option is safely stored in user's buffer according to the 48 * calculated length and the limitation of the buffer. 49 */ 50 static int 51 ip6optlen(u_int8_t *opt, u_int8_t *lim) 52 { 53 int optlen; 54 55 if (*opt == IP6OPT_PAD1) 56 optlen = 1; 57 else { 58 /* is there enough space to store type and len? */ 59 if (opt + 2 > lim) 60 return (0); 61 optlen = *(opt + 1) + 2; 62 } 63 if (opt + optlen <= lim) 64 return (optlen); 65 66 return (0); 67 } 68 69 /* 70 * The following functions are defined in RFC3542, which is a successor 71 * of RFC2292. 72 */ 73 74 int 75 inet6_opt_init(void *extbuf, socklen_t extlen) 76 { 77 struct ip6_ext *ext = (struct ip6_ext *)extbuf; 78 79 if (ext) { 80 if (extlen <= 0 || (extlen % 8)) 81 return (-1); 82 ext->ip6e_len = (extlen >> 3) - 1; 83 } 84 85 return (2); /* sizeof the next and the length fields */ 86 } 87 88 int 89 inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type, 90 socklen_t len, u_int8_t align, void **databufp) 91 { 92 int currentlen = offset, padlen = 0; 93 94 /* 95 * The option type must have a value from 2 to 255, inclusive. 96 * (0 and 1 are reserved for the Pad1 and PadN options, respectively.) 97 */ 98 #if 0 /* always false */ 99 if (type < 2 || type > 255) 100 #else 101 if (type < 2) 102 #endif 103 return (-1); 104 105 /* 106 * The option data length must have a value between 0 and 255, 107 * inclusive, and is the length of the option data that follows. 108 */ 109 if (len < 0 || len > 255) 110 return (-1); 111 112 /* 113 * The align parameter must have a value of 1, 2, 4, or 8. 114 * The align value can not exceed the value of len. 115 */ 116 if (align != 1 && align != 2 && align != 4 && align != 8) 117 return (-1); 118 if (align > len) 119 return (-1); 120 121 /* Calculate the padding length. */ 122 currentlen += 2 + len; /* 2 means "type + len" */ 123 if (currentlen % align) 124 padlen = align - (currentlen % align); 125 126 /* The option must fit in the extension header buffer. */ 127 currentlen += padlen; 128 if (extlen && /* XXX: right? */ 129 currentlen > extlen) 130 return (-1); 131 132 if (extbuf) { 133 u_int8_t *optp = (u_int8_t *)extbuf + offset; 134 135 if (padlen == 1) { 136 /* insert a Pad1 option */ 137 *optp = IP6OPT_PAD1; 138 optp++; 139 } else if (padlen > 0) { 140 /* insert a PadN option for alignment */ 141 *optp++ = IP6OPT_PADN; 142 *optp++ = padlen - 2; 143 memset(optp, 0, padlen - 2); 144 optp += (padlen - 2); 145 } 146 147 *optp++ = type; 148 *optp++ = len; 149 150 *databufp = optp; 151 } 152 153 return (currentlen); 154 } 155 156 int 157 inet6_opt_finish(void *extbuf, socklen_t extlen, int offset) 158 { 159 int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;; 160 161 if (extbuf) { 162 u_int8_t *padp; 163 int padlen = updatelen - offset; 164 165 if (updatelen > extlen) 166 return (-1); 167 168 padp = (u_int8_t *)extbuf + offset; 169 if (padlen == 1) 170 *padp = IP6OPT_PAD1; 171 else if (padlen > 0) { 172 *padp++ = IP6OPT_PADN; 173 *padp++ = (padlen - 2); 174 memset(padp, 0, padlen - 2); 175 } 176 } 177 178 return (updatelen); 179 } 180 181 int 182 inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen) 183 { 184 185 memcpy((u_int8_t *)databuf + offset, val, vallen); 186 return (offset + vallen); 187 } 188 189 int 190 inet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep, 191 socklen_t *lenp, void **databufp) 192 { 193 u_int8_t *optp, *lim; 194 int optlen; 195 196 /* Validate extlen. XXX: is the variable really necessary?? */ 197 if (extlen == 0 || (extlen % 8)) 198 return (-1); 199 lim = (u_int8_t *)extbuf + extlen; 200 201 /* 202 * If this is the first time this function called for this options 203 * header, simply return the 1st option. 204 * Otherwise, search the option list for the next option. 205 */ 206 if (offset == 0) 207 optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); 208 else 209 optp = (u_int8_t *)extbuf + offset; 210 211 /* Find the next option skipping any padding options. */ 212 while (optp < lim) { 213 switch(*optp) { 214 case IP6OPT_PAD1: 215 optp++; 216 break; 217 case IP6OPT_PADN: 218 if ((optlen = ip6optlen(optp, lim)) == 0) 219 goto optend; 220 optp += optlen; 221 break; 222 default: /* found */ 223 if ((optlen = ip6optlen(optp, lim)) == 0) 224 goto optend; 225 *typep = *optp; 226 *lenp = optlen - 2; 227 *databufp = optp + 2; 228 return (optp + optlen - (u_int8_t *)extbuf); 229 } 230 } 231 232 optend: 233 *databufp = NULL; /* for safety */ 234 return (-1); 235 } 236 237 int 238 inet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type, 239 socklen_t *lenp, void **databufp) 240 { 241 u_int8_t *optp, *lim; 242 int optlen; 243 244 /* Validate extlen. XXX: is the variable really necessary?? */ 245 if (extlen == 0 || (extlen % 8)) 246 return (-1); 247 lim = (u_int8_t *)extbuf + extlen; 248 249 /* 250 * If this is the first time this function called for this options 251 * header, simply return the 1st option. 252 * Otherwise, search the option list for the next option. 253 */ 254 if (offset == 0) 255 optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); 256 else 257 optp = (u_int8_t *)extbuf + offset; 258 259 /* Find the specified option */ 260 while (optp < lim) { 261 if ((optlen = ip6optlen(optp, lim)) == 0) 262 goto optend; 263 264 if (*optp == type) { /* found */ 265 *lenp = optlen - 2; 266 *databufp = optp + 2; 267 return (optp + optlen - (u_int8_t *)extbuf); 268 } 269 270 optp += optlen; 271 } 272 273 optend: 274 *databufp = NULL; /* for safety */ 275 return (-1); 276 } 277 278 int 279 inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen) 280 { 281 282 /* we can't assume alignment here */ 283 memcpy(val, (u_int8_t *)databuf + offset, vallen); 284 285 return (offset + vallen); 286 } 287