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