1 /* $OpenBSD: ip6opt.c,v 1.10 2020/01/22 07:52:37 deraadt 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/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 44 /* 45 * Calculate the length of a given IPv6 option. Also checks 46 * if the option is safely stored in user's buffer according to the 47 * calculated length and the limitation of the buffer. 48 */ 49 static int 50 ip6optlen(u_int8_t *opt, u_int8_t *lim) 51 { 52 int optlen; 53 54 if (*opt == IP6OPT_PAD1) 55 optlen = 1; 56 else { 57 /* is there enough space to store type and len? */ 58 if (opt + 2 > lim) 59 return (0); 60 optlen = *(opt + 1) + 2; 61 } 62 if (opt + optlen <= lim) 63 return (optlen); 64 65 return (0); 66 } 67 68 /* 69 * The following functions are defined in RFC3542, which is a successor 70 * of RFC2292. 71 */ 72 73 int 74 inet6_opt_init(void *extbuf, socklen_t extlen) 75 { 76 struct ip6_ext *ext = (struct ip6_ext *)extbuf; 77 78 if (ext) { 79 if (extlen <= 0 || (extlen % 8)) 80 return (-1); 81 ext->ip6e_len = (extlen >> 3) - 1; 82 } 83 84 return (2); /* sizeof the next and the length fields */ 85 } 86 87 int 88 inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type, 89 socklen_t len, u_int8_t align, void **databufp) 90 { 91 int currentlen = offset, padlen = 0; 92 93 /* 94 * The option type must have a value from 2 to 255, inclusive. 95 * (0 and 1 are reserved for the Pad1 and PadN options, respectively.) 96 */ 97 #if 0 /* always false */ 98 if (type < 2 || type > 255) 99 #else 100 if (type < 2) 101 #endif 102 return (-1); 103 104 /* 105 * The option data length must have a value between 0 and 255, 106 * inclusive, and is the length of the option data that follows. 107 */ 108 if (len > 255) 109 return (-1); 110 111 /* 112 * The align parameter must have a value of 1, 2, 4, or 8. 113 * The align value can not exceed the value of len. 114 */ 115 if (align != 1 && align != 2 && align != 4 && align != 8) 116 return (-1); 117 if (align > len) 118 return (-1); 119 120 /* Calculate the padding length. */ 121 currentlen += 2 + len; /* 2 means "type + len" */ 122 if (currentlen % align) 123 padlen = align - (currentlen % align); 124 125 /* The option must fit in the extension header buffer. */ 126 currentlen += padlen; 127 if (extlen && /* XXX: right? */ 128 currentlen > extlen) 129 return (-1); 130 131 if (extbuf) { 132 u_int8_t *optp = (u_int8_t *)extbuf + offset; 133 134 if (padlen == 1) { 135 /* insert a Pad1 option */ 136 *optp = IP6OPT_PAD1; 137 optp++; 138 } else if (padlen > 0) { 139 /* insert a PadN option for alignment */ 140 *optp++ = IP6OPT_PADN; 141 *optp++ = padlen - 2; 142 memset(optp, 0, padlen - 2); 143 optp += (padlen - 2); 144 } 145 146 *optp++ = type; 147 *optp++ = len; 148 149 *databufp = optp; 150 } 151 152 return (currentlen); 153 } 154 155 int 156 inet6_opt_finish(void *extbuf, socklen_t extlen, int offset) 157 { 158 int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0; 159 160 if (extbuf) { 161 u_int8_t *padp; 162 int padlen = updatelen - offset; 163 164 if (updatelen > extlen) 165 return (-1); 166 167 padp = (u_int8_t *)extbuf + offset; 168 if (padlen == 1) 169 *padp = IP6OPT_PAD1; 170 else if (padlen > 0) { 171 *padp++ = IP6OPT_PADN; 172 *padp++ = (padlen - 2); 173 memset(padp, 0, padlen - 2); 174 } 175 } 176 177 return (updatelen); 178 } 179 180 int 181 inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen) 182 { 183 184 memcpy((u_int8_t *)databuf + offset, val, vallen); 185 return (offset + vallen); 186 } 187 188 int 189 inet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep, 190 socklen_t *lenp, void **databufp) 191 { 192 u_int8_t *optp, *lim; 193 int optlen; 194 195 /* Validate extlen. XXX: is the variable really necessary?? */ 196 if (extlen == 0 || (extlen % 8)) 197 return (-1); 198 lim = (u_int8_t *)extbuf + extlen; 199 200 /* 201 * If this is the first time this function called for this options 202 * header, simply return the 1st option. 203 * Otherwise, search the option list for the next option. 204 */ 205 if (offset == 0) 206 optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); 207 else 208 optp = (u_int8_t *)extbuf + offset; 209 210 /* Find the next option skipping any padding options. */ 211 while (optp < lim) { 212 switch(*optp) { 213 case IP6OPT_PAD1: 214 optp++; 215 break; 216 case IP6OPT_PADN: 217 if ((optlen = ip6optlen(optp, lim)) == 0) 218 goto optend; 219 optp += optlen; 220 break; 221 default: /* found */ 222 if ((optlen = ip6optlen(optp, lim)) == 0) 223 goto optend; 224 *typep = *optp; 225 *lenp = optlen - 2; 226 *databufp = optp + 2; 227 return (optp + optlen - (u_int8_t *)extbuf); 228 } 229 } 230 231 optend: 232 *databufp = NULL; /* for safety */ 233 return (-1); 234 } 235 236 int 237 inet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type, 238 socklen_t *lenp, void **databufp) 239 { 240 u_int8_t *optp, *lim; 241 int optlen; 242 243 /* Validate extlen. XXX: is the variable really necessary?? */ 244 if (extlen == 0 || (extlen % 8)) 245 return (-1); 246 lim = (u_int8_t *)extbuf + extlen; 247 248 /* 249 * If this is the first time this function called for this options 250 * header, simply return the 1st option. 251 * Otherwise, search the option list for the next option. 252 */ 253 if (offset == 0) 254 optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); 255 else 256 optp = (u_int8_t *)extbuf + offset; 257 258 /* Find the specified option */ 259 while (optp < lim) { 260 if ((optlen = ip6optlen(optp, lim)) == 0) 261 goto optend; 262 263 if (*optp == type) { /* found */ 264 *lenp = optlen - 2; 265 *databufp = optp + 2; 266 return (optp + optlen - (u_int8_t *)extbuf); 267 } 268 269 optp += optlen; 270 } 271 272 optend: 273 *databufp = NULL; /* for safety */ 274 return (-1); 275 } 276 277 int 278 inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen) 279 { 280 281 /* we can't assume alignment here */ 282 memcpy(val, (u_int8_t *)databuf + offset, vallen); 283 284 return (offset + vallen); 285 } 286