1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * This code is conformant to RFC 3542. 29 */ 30 31 #include <assert.h> 32 #include <string.h> 33 #include <sys/types.h> 34 #include <sys/socket.h> 35 #include <sys/sysmacros.h> 36 #include <netinet/in.h> 37 #include <netinet/ip6.h> 38 39 #include <stdio.h> 40 41 #define bufpos(p) ((p) - (uint8_t *)extbuf) 42 43 /* 44 * Section 10.1 RFC3542. This function returns the size of the empty 45 * extension header. If extbuf is not NULL then it initializes its length 46 * field. If extlen is invalid then -1 is returned. 47 */ 48 int 49 inet6_opt_init(void *extbuf, socklen_t extlen) 50 { 51 if (extbuf && ((extlen < 0) || (extlen % 8))) { 52 return (-1); 53 } 54 55 if (extbuf) { 56 *(uint8_t *)extbuf = 0; 57 *((uint8_t *)extbuf + 1) = extlen/8 - 1; 58 } 59 60 return (2); 61 } 62 63 /* 64 * Section 10.2 RFC3542. This function appends an option to an already 65 * initialized option buffer. inet6_opt_append() returns the total length 66 * after adding the option. 67 */ 68 int 69 inet6_opt_append(void *extbuf, socklen_t extlen, int offset, uint8_t type, 70 socklen_t len, uint_t align, void **databufp) 71 { 72 uint8_t *p; 73 socklen_t endlen; 74 int remainder, padbytes; 75 76 if (align > len || 77 (align != 1 && align != 2 && align != 4 && align != 8) || 78 len < 0 || len > 255 || type < 2) { 79 return (-1); 80 } 81 82 if (extbuf) { 83 /* 84 * The length of the buffer is the minimum of the length 85 * passed in and the length stamped onto the buffer. The 86 * length stamped onto the buffer is the number of 8 byte 87 * octets in the buffer minus 1. 88 */ 89 extlen = MIN(extlen, (*((uint8_t *)extbuf + 1) + 1) * 8); 90 } 91 92 remainder = (offset + 2 + len) % align; 93 if (remainder == 0) { 94 padbytes = 0; 95 } else { 96 padbytes = align - remainder; 97 } 98 99 endlen = offset + padbytes + 2 + len; 100 if ((endlen > extlen) || !extbuf) { 101 if (extbuf) { 102 return (-1); 103 } else { 104 return (endlen); 105 } 106 } 107 108 p = (uint8_t *)extbuf + offset; 109 if (padbytes != 0) { 110 /* 111 * Pad out the buffer here with pad options. If its only 112 * one byte then there is a special TLV with no L or V, just 113 * a zero to say skip this byte. For two bytes or more 114 * we have a special TLV with type 0 and length the number of 115 * padbytes. 116 */ 117 if (padbytes == 1) { 118 *p = IP6OPT_PAD1; 119 } else { 120 *p = IP6OPT_PADN; 121 *(p + 1) = padbytes - 2; 122 memset(p + 2, 0, padbytes - 2); 123 } 124 p += padbytes; 125 } 126 127 *p++ = type; 128 *p++ = len; 129 if (databufp) { 130 *databufp = p; 131 } 132 return (endlen); 133 } 134 135 /* 136 * Section 10.3 RFC3542. This function returns the updated total length. 137 * This functions inserts pad options to complete the option header as 138 * needed. 139 */ 140 int 141 inet6_opt_finish(void *extbuf, socklen_t extlen, int offset) 142 { 143 uint8_t *p; 144 int padbytes; 145 146 if (extbuf) { 147 /* 148 * The length of the buffer is the minimum of the length 149 * passed in and the length stamped onto the buffer. The 150 * length stamped onto the buffer is the number of 8 byte 151 * octets in the buffer minus 1. 152 */ 153 extlen = MIN(extlen, (*((uint8_t *)extbuf + 1) + 1) * 8); 154 } 155 156 padbytes = 8 - (offset % 8); 157 if (padbytes == 8) 158 padbytes = 0; 159 160 if ((offset + padbytes > extlen) || !extbuf) { 161 if (extbuf) { 162 return (-1); 163 } else { 164 return (offset + padbytes); 165 } 166 } 167 168 p = (uint8_t *)extbuf + offset; 169 if (padbytes != 0) { 170 /* 171 * Pad out the buffer here with pad options. If its only 172 * one byte then there is a special TLV with no L or V, just 173 * a zero to say skip this byte. For two bytes or more 174 * we have a special TLV with type 0 and length the number of 175 * padbytes. 176 */ 177 if (padbytes == 1) { 178 *p = IP6OPT_PAD1; 179 } else { 180 *p = IP6OPT_PADN; 181 *(p + 1) = padbytes - 2; 182 memset(p + 2, 0, padbytes - 2); 183 } 184 p += padbytes; 185 } 186 187 return (offset + padbytes); 188 } 189 190 /* 191 * Section 10.4 RFC3542. Ths function takes a pointer to the data as 192 * returned by inet6_opt_append and inserts the data. 193 */ 194 int 195 inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen) 196 { 197 memcpy((uint8_t *)databuf + offset, val, vallen); 198 return (offset + vallen); 199 } 200 201 /* 202 * Section 10.5 RFC 3542. Starting walking the option header offset into the 203 * header. Returns where we left off. You take the output of this function 204 * and pass it back in as offset to iterate. -1 is returned on error. 205 * 206 * We use the fact that the first unsigned 8 bit quantity in the option 207 * header is the type and the second is the length. 208 */ 209 int 210 inet6_opt_next(void *extbuf, socklen_t extlen, int offset, uint8_t *typep, 211 socklen_t *lenp, void **databufp) 212 { 213 uint8_t *p; 214 uint8_t *end; 215 216 /* 217 * The length of the buffer is the minimum of the length 218 * passed in and the length stamped onto the buffer. The 219 * length stamped onto the buffer is the number of 8 byte 220 * octets in the buffer minus 1. 221 */ 222 extlen = MIN(extlen, (*((uint8_t *)extbuf + 1) + 1) * 8); 223 end = (uint8_t *)extbuf + extlen; 224 if (offset == 0) { 225 offset = 2; 226 } 227 228 /* assumption: IP6OPT_PAD1 == 0 and IP6OPT_PADN == 1 */ 229 p = (uint8_t *)extbuf + offset; 230 while (*p == IP6OPT_PAD1 || *p == IP6OPT_PADN) { 231 switch (*p) { 232 case IP6OPT_PAD1: 233 p++; 234 break; 235 case IP6OPT_PADN: 236 /* *(p + 1) is the length of the option. */ 237 if (p + 2 + *(p + 1) >= end) 238 return (-1); 239 p += *(p + 1) + 2; 240 break; 241 } 242 } 243 244 /* type, len, and data must fit... */ 245 if ((p + 2 >= end) || (p + 2 + *(p + 1) > end)) { 246 return (-1); 247 } 248 249 if (typep) { 250 *typep = *p; 251 } 252 if (lenp) { 253 *lenp = *(p + 1); 254 } 255 if (databufp) { 256 *databufp = p + 2; 257 } 258 259 return ((p - (uint8_t *)extbuf) + 2 + *lenp); 260 } 261 262 /* 263 * Section 10.6 RFC 3542. Starting walking the option header offset into the 264 * header. Returns where we left off. You take the output of this function 265 * and pass it back in as offset to iterate. -1 is returned on error. 266 * 267 * We use the fact that the first unsigned 8 bit quantity in the option 268 * header is the type and the second is the length. 269 */ 270 int 271 inet6_opt_find(void *extbuf, socklen_t extlen, int offset, uint8_t type, 272 socklen_t *lenp, void **databufp) 273 { 274 uint8_t newtype; 275 276 do { 277 offset = inet6_opt_next(extbuf, extlen, offset, &newtype, lenp, 278 databufp); 279 280 if (offset == -1) 281 return (-1); 282 } while (newtype != type); 283 284 /* value to feed back into inet6_opt_find() as offset */ 285 return (offset); 286 } 287 288 /* 289 * Section 10.7 RFC 3542. databuf should be a pointer as returned by 290 * inet6_opt_next or inet6_opt_find. The data is extracted from the option 291 * at that point. 292 */ 293 int 294 inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen) 295 { 296 memcpy(val, (uint8_t *)databuf + offset, vallen); 297 return (offset + vallen); 298 } 299