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