xref: /openbsd-src/lib/libc/net/ip6opt.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: ip6opt.c,v 1.7 2014/06/13 15:41:06 chrisz Exp $	*/
2 /*	$KAME: ip6opt.c,v 1.18 2005/06/15 07:11:35 keiichi Exp $	*/
3 
4 /*
5  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the project nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 
37 #include <netinet/in.h>
38 #include <netinet/ip6.h>
39 
40 #include <string.h>
41 #include <stdio.h>
42 
43 static int ip6optlen(u_int8_t *opt, u_int8_t *lim);
44 
45 /*
46  * Calculate the length of a given IPv6 option. Also checks
47  * if the option is safely stored in user's buffer according to the
48  * calculated length and the limitation of the buffer.
49  */
50 static int
51 ip6optlen(u_int8_t *opt, u_int8_t *lim)
52 {
53 	int optlen;
54 
55 	if (*opt == IP6OPT_PAD1)
56 		optlen = 1;
57 	else {
58 		/* is there enough space to store type and len? */
59 		if (opt + 2 > lim)
60 			return (0);
61 		optlen = *(opt + 1) + 2;
62 	}
63 	if (opt + optlen <= lim)
64 		return (optlen);
65 
66 	return (0);
67 }
68 
69 /*
70  * The following functions are defined in RFC3542, which is a successor
71  * of RFC2292.
72  */
73 
74 int
75 inet6_opt_init(void *extbuf, socklen_t extlen)
76 {
77 	struct ip6_ext *ext = (struct ip6_ext *)extbuf;
78 
79 	if (ext) {
80 		if (extlen <= 0 || (extlen % 8))
81 			return (-1);
82 		ext->ip6e_len = (extlen >> 3) - 1;
83 	}
84 
85 	return (2);		/* sizeof the next and the length fields */
86 }
87 
88 int
89 inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
90 		 socklen_t len, u_int8_t align, void **databufp)
91 {
92 	int currentlen = offset, padlen = 0;
93 
94 	/*
95 	 * The option type must have a value from 2 to 255, inclusive.
96 	 * (0 and 1 are reserved for the Pad1 and PadN options, respectively.)
97 	 */
98 #if 0 /* always false */
99 	if (type < 2 || type > 255)
100 #else
101 	if (type < 2)
102 #endif
103 		return (-1);
104 
105 	/*
106 	 * The option data length must have a value between 0 and 255,
107 	 * inclusive, and is the length of the option data that follows.
108 	 */
109 	if (len < 0 || len > 255)
110 		return (-1);
111 
112 	/*
113 	 * The align parameter must have a value of 1, 2, 4, or 8.
114 	 * The align value can not exceed the value of len.
115 	 */
116 	if (align != 1 && align != 2 && align != 4 && align != 8)
117 		return (-1);
118 	if (align > len)
119 		return (-1);
120 
121 	/* Calculate the padding length. */
122 	currentlen += 2 + len;	/* 2 means "type + len" */
123 	if (currentlen % align)
124 		padlen = align - (currentlen % align);
125 
126 	/* The option must fit in the extension header buffer. */
127 	currentlen += padlen;
128 	if (extlen &&		/* XXX: right? */
129 	    currentlen > extlen)
130 		return (-1);
131 
132 	if (extbuf) {
133 		u_int8_t *optp = (u_int8_t *)extbuf + offset;
134 
135 		if (padlen == 1) {
136 			/* insert a Pad1 option */
137 			*optp = IP6OPT_PAD1;
138 			optp++;
139 		} else if (padlen > 0) {
140 			/* insert a PadN option for alignment */
141 			*optp++ = IP6OPT_PADN;
142 			*optp++ = padlen - 2;
143 			memset(optp, 0, padlen - 2);
144 			optp += (padlen - 2);
145 		}
146 
147 		*optp++ = type;
148 		*optp++ = len;
149 
150 		*databufp = optp;
151 	}
152 
153 	return (currentlen);
154 }
155 
156 int
157 inet6_opt_finish(void *extbuf, socklen_t extlen, int offset)
158 {
159 	int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;;
160 
161 	if (extbuf) {
162 		u_int8_t *padp;
163 		int padlen = updatelen - offset;
164 
165 		if (updatelen > extlen)
166 			return (-1);
167 
168 		padp = (u_int8_t *)extbuf + offset;
169 		if (padlen == 1)
170 			*padp = IP6OPT_PAD1;
171 		else if (padlen > 0) {
172 			*padp++ = IP6OPT_PADN;
173 			*padp++ = (padlen - 2);
174 			memset(padp, 0, padlen - 2);
175 		}
176 	}
177 
178 	return (updatelen);
179 }
180 
181 int
182 inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen)
183 {
184 
185 	memcpy((u_int8_t *)databuf + offset, val, vallen);
186 	return (offset + vallen);
187 }
188 
189 int
190 inet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep,
191 	       socklen_t *lenp, void **databufp)
192 {
193 	u_int8_t *optp, *lim;
194 	int optlen;
195 
196 	/* Validate extlen. XXX: is the variable really necessary?? */
197 	if (extlen == 0 || (extlen % 8))
198 		return (-1);
199 	lim = (u_int8_t *)extbuf + extlen;
200 
201 	/*
202 	 * If this is the first time this function called for this options
203 	 * header, simply return the 1st option.
204 	 * Otherwise, search the option list for the next option.
205 	 */
206 	if (offset == 0)
207 		optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
208 	else
209 		optp = (u_int8_t *)extbuf + offset;
210 
211 	/* Find the next option skipping any padding options. */
212 	while (optp < lim) {
213 		switch(*optp) {
214 		case IP6OPT_PAD1:
215 			optp++;
216 			break;
217 		case IP6OPT_PADN:
218 			if ((optlen = ip6optlen(optp, lim)) == 0)
219 				goto optend;
220 			optp += optlen;
221 			break;
222 		default:	/* found */
223 			if ((optlen = ip6optlen(optp, lim)) == 0)
224 				goto optend;
225 			*typep = *optp;
226 			*lenp = optlen - 2;
227 			*databufp = optp + 2;
228 			return (optp + optlen - (u_int8_t *)extbuf);
229 		}
230 	}
231 
232   optend:
233 	*databufp = NULL; /* for safety */
234 	return (-1);
235 }
236 
237 int
238 inet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
239 	       socklen_t *lenp, void **databufp)
240 {
241 	u_int8_t *optp, *lim;
242 	int optlen;
243 
244 	/* Validate extlen. XXX: is the variable really necessary?? */
245 	if (extlen == 0 || (extlen % 8))
246 		return (-1);
247 	lim = (u_int8_t *)extbuf + extlen;
248 
249 	/*
250 	 * If this is the first time this function called for this options
251 	 * header, simply return the 1st option.
252 	 * Otherwise, search the option list for the next option.
253 	 */
254 	if (offset == 0)
255 		optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
256 	else
257 		optp = (u_int8_t *)extbuf + offset;
258 
259 	/* Find the specified option */
260 	while (optp < lim) {
261 		if ((optlen = ip6optlen(optp, lim)) == 0)
262 			goto optend;
263 
264 		if (*optp == type) { /* found */
265 			*lenp = optlen - 2;
266 			*databufp = optp + 2;
267 			return (optp + optlen - (u_int8_t *)extbuf);
268 		}
269 
270 		optp += optlen;
271 	}
272 
273   optend:
274 	*databufp = NULL; /* for safety */
275 	return (-1);
276 }
277 
278 int
279 inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen)
280 {
281 
282 	/* we can't assume alignment here */
283 	memcpy(val, (u_int8_t *)databuf + offset, vallen);
284 
285 	return (offset + vallen);
286 }
287