diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libsocket/inet/inet6_opt.c | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libsocket/inet/inet6_opt.c')
| -rw-r--r-- | usr/src/lib/libsocket/inet/inet6_opt.c | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/usr/src/lib/libsocket/inet/inet6_opt.c b/usr/src/lib/libsocket/inet/inet6_opt.c new file mode 100644 index 0000000000..42b923c5b3 --- /dev/null +++ b/usr/src/lib/libsocket/inet/inet6_opt.c @@ -0,0 +1,300 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This code is conformant to RFC 3542. + */ + +#include <assert.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysmacros.h> +#include <netinet/in.h> +#include <netinet/ip6.h> + +#include <stdio.h> + +#define bufpos(p) ((p) - (uint8_t *)extbuf) + +/* + * Section 10.1 RFC3542. This function returns the size of the empty + * extension header. If extbuf is not NULL then it initializes its length + * field. If extlen is invalid then -1 is returned. + */ +int +inet6_opt_init(void *extbuf, socklen_t extlen) +{ + if (extbuf && ((extlen < 0) || (extlen % 8))) { + return (-1); + } + + if (extbuf) { + *(uint8_t *)extbuf = 0; + *((uint8_t *)extbuf + 1) = extlen/8 - 1; + } + + return (2); +} + +/* + * Section 10.2 RFC3542. This function appends an option to an already + * initialized option buffer. inet6_opt_append() returns the total length + * after adding the option. + */ +int +inet6_opt_append(void *extbuf, socklen_t extlen, int offset, uint8_t type, + socklen_t len, uint_t align, void **databufp) +{ + uint8_t *p; + socklen_t endlen; + int remainder, padbytes; + + if (align > len || + (align != 1 && align != 2 && align != 4 && align != 8) || + len < 0 || len > 255 || type < 2) { + return (-1); + } + + if (extbuf) { + /* + * The length of the buffer is the minimum of the length + * passed in and the length stamped onto the buffer. The + * length stamped onto the buffer is the number of 8 byte + * octets in the buffer minus 1. + */ + extlen = MIN(extlen, (*((uint8_t *)extbuf + 1) + 1) * 8); + } + + remainder = (offset + 2 + len) % align; + if (remainder == 0) { + padbytes = 0; + } else { + padbytes = align - remainder; + } + + endlen = offset + padbytes + 2 + len; + if ((endlen > extlen) || !extbuf) { + if (extbuf) { + return (-1); + } else { + return (endlen); + } + } + + p = (uint8_t *)extbuf + offset; + if (padbytes != 0) { + /* + * Pad out the buffer here with pad options. If its only + * one byte then there is a special TLV with no L or V, just + * a zero to say skip this byte. For two bytes or more + * we have a special TLV with type 0 and length the number of + * padbytes. + */ + if (padbytes == 1) { + *p = IP6OPT_PAD1; + } else { + *p = IP6OPT_PADN; + *(p + 1) = padbytes - 2; + memset(p + 2, 0, padbytes - 2); + } + p += padbytes; + } + + *p++ = type; + *p++ = len; + if (databufp) { + *databufp = p; + } + return (endlen); +} + +/* + * Section 10.3 RFC3542. This function returns the updated total length. + * This functions inserts pad options to complete the option header as + * needed. + */ +int +inet6_opt_finish(void *extbuf, socklen_t extlen, int offset) +{ + uint8_t *p; + int padbytes; + + if (extbuf) { + /* + * The length of the buffer is the minimum of the length + * passed in and the length stamped onto the buffer. The + * length stamped onto the buffer is the number of 8 byte + * octets in the buffer minus 1. + */ + extlen = MIN(extlen, (*((uint8_t *)extbuf + 1) + 1) * 8); + } + + padbytes = 8 - (offset % 8); + if (padbytes == 8) + padbytes = 0; + + if ((offset + padbytes > extlen) || !extbuf) { + if (extbuf) { + return (-1); + } else { + return (offset + padbytes); + } + } + + p = (uint8_t *)extbuf + offset; + if (padbytes != 0) { + /* + * Pad out the buffer here with pad options. If its only + * one byte then there is a special TLV with no L or V, just + * a zero to say skip this byte. For two bytes or more + * we have a special TLV with type 0 and length the number of + * padbytes. + */ + if (padbytes == 1) { + *p = IP6OPT_PAD1; + } else { + *p = IP6OPT_PADN; + *(p + 1) = padbytes - 2; + memset(p + 2, 0, padbytes - 2); + } + p += padbytes; + } + + return (offset + padbytes); +} + +/* + * Section 10.4 RFC3542. Ths function takes a pointer to the data as + * returned by inet6_opt_append and inserts the data. + */ +int +inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen) +{ + memcpy((uint8_t *)databuf + offset, val, vallen); + return (offset + vallen); +} + +/* + * Section 10.5 RFC 3542. Starting walking the option header offset into the + * header. Returns where we left off. You take the output of this function + * and pass it back in as offset to iterate. -1 is returned on error. + * + * We use the fact that the first unsigned 8 bit quantity in the option + * header is the type and the second is the length. + */ +int +inet6_opt_next(void *extbuf, socklen_t extlen, int offset, uint8_t *typep, + socklen_t *lenp, void **databufp) +{ + uint8_t *p; + uint8_t *end; + + /* + * The length of the buffer is the minimum of the length + * passed in and the length stamped onto the buffer. The + * length stamped onto the buffer is the number of 8 byte + * octets in the buffer minus 1. + */ + extlen = MIN(extlen, (*((uint8_t *)extbuf + 1) + 1) * 8); + end = (uint8_t *)extbuf + extlen; + if (offset == 0) { + offset = 2; + } + + /* assumption: IP6OPT_PAD1 == 0 and IP6OPT_PADN == 1 */ + p = (uint8_t *)extbuf + offset; + while (*p == IP6OPT_PAD1 || *p == IP6OPT_PADN) { + switch (*p) { + case IP6OPT_PAD1: + p++; + break; + case IP6OPT_PADN: + /* *(p + 1) is the length of the option. */ + if (p + 2 + *(p + 1) >= end) + return (-1); + p += *(p + 1) + 2; + break; + } + } + + /* type, len, and data must fit... */ + if ((p + 2 >= end) || (p + 2 + *(p + 1) > end)) { + return (-1); + } + + if (typep) { + *typep = *p; + } + if (lenp) { + *lenp = *(p + 1); + } + if (databufp) { + *databufp = p + 2; + } + + return ((p - (uint8_t *)extbuf) + 2 + *lenp); +} + +/* + * Section 10.6 RFC 3542. Starting walking the option header offset into the + * header. Returns where we left off. You take the output of this function + * and pass it back in as offset to iterate. -1 is returned on error. + * + * We use the fact that the first unsigned 8 bit quantity in the option + * header is the type and the second is the length. + */ +int +inet6_opt_find(void *extbuf, socklen_t extlen, int offset, uint8_t type, + socklen_t *lenp, void **databufp) +{ + uint8_t newtype; + + do { + offset = inet6_opt_next(extbuf, extlen, offset, &newtype, lenp, + databufp); + + if (offset == -1) + return (-1); + } while (newtype != type); + + /* value to feed back into inet6_opt_find() as offset */ + return (offset); +} + +/* + * Section 10.7 RFC 3542. databuf should be a pointer as returned by + * inet6_opt_next or inet6_opt_find. The data is extracted from the option + * at that point. + */ +int +inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen) +{ + memcpy(val, (uint8_t *)databuf + offset, vallen); + return (offset + vallen); +} |
