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