8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libsocket / inet / inet6_opt.c
blob42b923c5b39b2cc70cddf5271353b82200a36b3b
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * This code is conformant to RFC 3542.
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>
41 #include <stdio.h>
43 #define bufpos(p) ((p) - (uint8_t *)extbuf)
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.
50 int
51 inet6_opt_init(void *extbuf, socklen_t extlen)
53 if (extbuf && ((extlen < 0) || (extlen % 8))) {
54 return (-1);
57 if (extbuf) {
58 *(uint8_t *)extbuf = 0;
59 *((uint8_t *)extbuf + 1) = extlen/8 - 1;
62 return (2);
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.
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)
74 uint8_t *p;
75 socklen_t endlen;
76 int remainder, padbytes;
78 if (align > len ||
79 (align != 1 && align != 2 && align != 4 && align != 8) ||
80 len < 0 || len > 255 || type < 2) {
81 return (-1);
84 if (extbuf) {
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.
91 extlen = MIN(extlen, (*((uint8_t *)extbuf + 1) + 1) * 8);
94 remainder = (offset + 2 + len) % align;
95 if (remainder == 0) {
96 padbytes = 0;
97 } else {
98 padbytes = align - remainder;
101 endlen = offset + padbytes + 2 + len;
102 if ((endlen > extlen) || !extbuf) {
103 if (extbuf) {
104 return (-1);
105 } else {
106 return (endlen);
110 p = (uint8_t *)extbuf + offset;
111 if (padbytes != 0) {
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.
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);
126 p += padbytes;
129 *p++ = type;
130 *p++ = len;
131 if (databufp) {
132 *databufp = p;
134 return (endlen);
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.
143 inet6_opt_finish(void *extbuf, socklen_t extlen, int offset)
145 uint8_t *p;
146 int padbytes;
148 if (extbuf) {
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.
155 extlen = MIN(extlen, (*((uint8_t *)extbuf + 1) + 1) * 8);
158 padbytes = 8 - (offset % 8);
159 if (padbytes == 8)
160 padbytes = 0;
162 if ((offset + padbytes > extlen) || !extbuf) {
163 if (extbuf) {
164 return (-1);
165 } else {
166 return (offset + padbytes);
170 p = (uint8_t *)extbuf + offset;
171 if (padbytes != 0) {
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.
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);
186 p += padbytes;
189 return (offset + padbytes);
193 * Section 10.4 RFC3542. Ths function takes a pointer to the data as
194 * returned by inet6_opt_append and inserts the data.
197 inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen)
199 memcpy((uint8_t *)databuf + offset, val, vallen);
200 return (offset + vallen);
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.
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.
212 inet6_opt_next(void *extbuf, socklen_t extlen, int offset, uint8_t *typep,
213 socklen_t *lenp, void **databufp)
215 uint8_t *p;
216 uint8_t *end;
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.
224 extlen = MIN(extlen, (*((uint8_t *)extbuf + 1) + 1) * 8);
225 end = (uint8_t *)extbuf + extlen;
226 if (offset == 0) {
227 offset = 2;
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;
246 /* type, len, and data must fit... */
247 if ((p + 2 >= end) || (p + 2 + *(p + 1) > end)) {
248 return (-1);
251 if (typep) {
252 *typep = *p;
254 if (lenp) {
255 *lenp = *(p + 1);
257 if (databufp) {
258 *databufp = p + 2;
261 return ((p - (uint8_t *)extbuf) + 2 + *lenp);
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.
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.
273 inet6_opt_find(void *extbuf, socklen_t extlen, int offset, uint8_t type,
274 socklen_t *lenp, void **databufp)
276 uint8_t newtype;
278 do {
279 offset = inet6_opt_next(extbuf, extlen, offset, &newtype, lenp,
280 databufp);
282 if (offset == -1)
283 return (-1);
284 } while (newtype != type);
286 /* value to feed back into inet6_opt_find() as offset */
287 return (offset);
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.
296 inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen)
298 memcpy(val, (uint8_t *)databuf + offset, vallen);
299 return (offset + vallen);