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
inet6_opt_init(void * extbuf,socklen_t extlen)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
inet6_opt_append(void * extbuf,socklen_t extlen,int offset,uint8_t type,socklen_t len,uint_t align,void ** databufp)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
inet6_opt_finish(void * extbuf,socklen_t extlen,int offset)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
inet6_opt_set_val(void * databuf,int offset,void * val,socklen_t vallen)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
inet6_opt_next(void * extbuf,socklen_t extlen,int offset,uint8_t * typep,socklen_t * lenp,void ** databufp)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
inet6_opt_find(void * extbuf,socklen_t extlen,int offset,uint8_t type,socklen_t * lenp,void ** databufp)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
inet6_opt_get_val(void * databuf,int offset,void * val,socklen_t vallen)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