| 1 |  |  | /*	$OpenBSD: ip6opt.c,v 1.9 2017/04/27 23:52:35 millert 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 |  |  | } |