Home | History | Annotate | Download | only in inet
      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