xref: /openbsd/lib/libc/net/ip6opt.c (revision ab8e3451)
1 /*	$OpenBSD: ip6opt.c,v 1.10 2020/01/22 07:52:37 deraadt 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/types.h>
34 #include <sys/socket.h>
35 
36 #include <netinet/in.h>
37 #include <netinet/ip6.h>
38 
39 #include <string.h>
40 #include <stdio.h>
41 
42 static int ip6optlen(u_int8_t *opt, u_int8_t *lim);
43 
44 /*
45  * Calculate the length of a given IPv6 option. Also checks
46  * if the option is safely stored in user's buffer according to the
47  * calculated length and the limitation of the buffer.
48  */
49 static int
ip6optlen(u_int8_t * opt,u_int8_t * lim)50 ip6optlen(u_int8_t *opt, u_int8_t *lim)
51 {
52 	int optlen;
53 
54 	if (*opt == IP6OPT_PAD1)
55 		optlen = 1;
56 	else {
57 		/* is there enough space to store type and len? */
58 		if (opt + 2 > lim)
59 			return (0);
60 		optlen = *(opt + 1) + 2;
61 	}
62 	if (opt + optlen <= lim)
63 		return (optlen);
64 
65 	return (0);
66 }
67 
68 /*
69  * The following functions are defined in RFC3542, which is a successor
70  * of RFC2292.
71  */
72 
73 int
inet6_opt_init(void * extbuf,socklen_t extlen)74 inet6_opt_init(void *extbuf, socklen_t extlen)
75 {
76 	struct ip6_ext *ext = (struct ip6_ext *)extbuf;
77 
78 	if (ext) {
79 		if (extlen <= 0 || (extlen % 8))
80 			return (-1);
81 		ext->ip6e_len = (extlen >> 3) - 1;
82 	}
83 
84 	return (2);		/* sizeof the next and the length fields */
85 }
86 
87 int
inet6_opt_append(void * extbuf,socklen_t extlen,int offset,u_int8_t type,socklen_t len,u_int8_t align,void ** databufp)88 inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
89 		 socklen_t len, u_int8_t align, void **databufp)
90 {
91 	int currentlen = offset, padlen = 0;
92 
93 	/*
94 	 * The option type must have a value from 2 to 255, inclusive.
95 	 * (0 and 1 are reserved for the Pad1 and PadN options, respectively.)
96 	 */
97 #if 0 /* always false */
98 	if (type < 2 || type > 255)
99 #else
100 	if (type < 2)
101 #endif
102 		return (-1);
103 
104 	/*
105 	 * The option data length must have a value between 0 and 255,
106 	 * inclusive, and is the length of the option data that follows.
107 	 */
108 	if (len > 255)
109 		return (-1);
110 
111 	/*
112 	 * The align parameter must have a value of 1, 2, 4, or 8.
113 	 * The align value can not exceed the value of len.
114 	 */
115 	if (align != 1 && align != 2 && align != 4 && align != 8)
116 		return (-1);
117 	if (align > len)
118 		return (-1);
119 
120 	/* Calculate the padding length. */
121 	currentlen += 2 + len;	/* 2 means "type + len" */
122 	if (currentlen % align)
123 		padlen = align - (currentlen % align);
124 
125 	/* The option must fit in the extension header buffer. */
126 	currentlen += padlen;
127 	if (extlen &&		/* XXX: right? */
128 	    currentlen > extlen)
129 		return (-1);
130 
131 	if (extbuf) {
132 		u_int8_t *optp = (u_int8_t *)extbuf + offset;
133 
134 		if (padlen == 1) {
135 			/* insert a Pad1 option */
136 			*optp = IP6OPT_PAD1;
137 			optp++;
138 		} else if (padlen > 0) {
139 			/* insert a PadN option for alignment */
140 			*optp++ = IP6OPT_PADN;
141 			*optp++ = padlen - 2;
142 			memset(optp, 0, padlen - 2);
143 			optp += (padlen - 2);
144 		}
145 
146 		*optp++ = type;
147 		*optp++ = len;
148 
149 		*databufp = optp;
150 	}
151 
152 	return (currentlen);
153 }
154 
155 int
inet6_opt_finish(void * extbuf,socklen_t extlen,int offset)156 inet6_opt_finish(void *extbuf, socklen_t extlen, int offset)
157 {
158 	int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;
159 
160 	if (extbuf) {
161 		u_int8_t *padp;
162 		int padlen = updatelen - offset;
163 
164 		if (updatelen > extlen)
165 			return (-1);
166 
167 		padp = (u_int8_t *)extbuf + offset;
168 		if (padlen == 1)
169 			*padp = IP6OPT_PAD1;
170 		else if (padlen > 0) {
171 			*padp++ = IP6OPT_PADN;
172 			*padp++ = (padlen - 2);
173 			memset(padp, 0, padlen - 2);
174 		}
175 	}
176 
177 	return (updatelen);
178 }
179 
180 int
inet6_opt_set_val(void * databuf,int offset,void * val,socklen_t vallen)181 inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen)
182 {
183 
184 	memcpy((u_int8_t *)databuf + offset, val, vallen);
185 	return (offset + vallen);
186 }
187 
188 int
inet6_opt_next(void * extbuf,socklen_t extlen,int offset,u_int8_t * typep,socklen_t * lenp,void ** databufp)189 inet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep,
190 	       socklen_t *lenp, void **databufp)
191 {
192 	u_int8_t *optp, *lim;
193 	int optlen;
194 
195 	/* Validate extlen. XXX: is the variable really necessary?? */
196 	if (extlen == 0 || (extlen % 8))
197 		return (-1);
198 	lim = (u_int8_t *)extbuf + extlen;
199 
200 	/*
201 	 * If this is the first time this function called for this options
202 	 * header, simply return the 1st option.
203 	 * Otherwise, search the option list for the next option.
204 	 */
205 	if (offset == 0)
206 		optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
207 	else
208 		optp = (u_int8_t *)extbuf + offset;
209 
210 	/* Find the next option skipping any padding options. */
211 	while (optp < lim) {
212 		switch(*optp) {
213 		case IP6OPT_PAD1:
214 			optp++;
215 			break;
216 		case IP6OPT_PADN:
217 			if ((optlen = ip6optlen(optp, lim)) == 0)
218 				goto optend;
219 			optp += optlen;
220 			break;
221 		default:	/* found */
222 			if ((optlen = ip6optlen(optp, lim)) == 0)
223 				goto optend;
224 			*typep = *optp;
225 			*lenp = optlen - 2;
226 			*databufp = optp + 2;
227 			return (optp + optlen - (u_int8_t *)extbuf);
228 		}
229 	}
230 
231   optend:
232 	*databufp = NULL; /* for safety */
233 	return (-1);
234 }
235 
236 int
inet6_opt_find(void * extbuf,socklen_t extlen,int offset,u_int8_t type,socklen_t * lenp,void ** databufp)237 inet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
238 	       socklen_t *lenp, void **databufp)
239 {
240 	u_int8_t *optp, *lim;
241 	int optlen;
242 
243 	/* Validate extlen. XXX: is the variable really necessary?? */
244 	if (extlen == 0 || (extlen % 8))
245 		return (-1);
246 	lim = (u_int8_t *)extbuf + extlen;
247 
248 	/*
249 	 * If this is the first time this function called for this options
250 	 * header, simply return the 1st option.
251 	 * Otherwise, search the option list for the next option.
252 	 */
253 	if (offset == 0)
254 		optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
255 	else
256 		optp = (u_int8_t *)extbuf + offset;
257 
258 	/* Find the specified option */
259 	while (optp < lim) {
260 		if ((optlen = ip6optlen(optp, lim)) == 0)
261 			goto optend;
262 
263 		if (*optp == type) { /* found */
264 			*lenp = optlen - 2;
265 			*databufp = optp + 2;
266 			return (optp + optlen - (u_int8_t *)extbuf);
267 		}
268 
269 		optp += optlen;
270 	}
271 
272   optend:
273 	*databufp = NULL; /* for safety */
274 	return (-1);
275 }
276 
277 int
inet6_opt_get_val(void * databuf,int offset,void * val,socklen_t vallen)278 inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen)
279 {
280 
281 	/* we can't assume alignment here */
282 	memcpy(val, (u_int8_t *)databuf + offset, vallen);
283 
284 	return (offset + vallen);
285 }
286