1 /* Copyright (C) 2003-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <assert.h>
19 #include <string.h>
20 #include <netinet/in.h>
21 #include <netinet/ip6.h>
22 #include <sys/param.h>
23 
24 
25 static void
add_pad(struct cmsghdr * cmsg,int len)26 add_pad (struct cmsghdr *cmsg, int len)
27 {
28   unsigned char *p = CMSG_DATA (cmsg) + cmsg->cmsg_len - CMSG_LEN (0);
29 
30   if (len == 1)
31     /* Special handling for 1, a one-byte solution.  */
32     *p++ = IP6OPT_PAD1;
33   else if (len != 0)
34     {
35       /* Multibyte padding.  */
36       *p++ = IP6OPT_PADN;
37       *p++ = len - 2;	/* Discount the two header bytes.  */
38       /* The rest is filled with zero.  */
39       memset (p, '\0', len - 2);
40       p += len - 2;
41     }
42 
43   /* Account for the bytes.  */
44   cmsg->cmsg_len += len;
45 }
46 
47 
48 static int
get_opt_end(const uint8_t ** result,const uint8_t * startp,const uint8_t * endp)49 get_opt_end (const uint8_t **result, const uint8_t *startp,
50 	     const uint8_t *endp)
51 {
52   if (startp >= endp)
53     /* Out of bounds.  */
54     return -1;
55 
56   if (*startp == IP6OPT_PAD1)
57     {
58       /* Just this one byte.  */
59       *result = startp + 1;
60       return 0;
61     }
62 
63   /* Now we know there must be at least two bytes.  */
64   if (startp + 2 > endp
65       /* Now we can get the length byte.  */
66       || startp + startp[1] + 2 > endp)
67     return -1;
68 
69   *result = startp + startp[1] + 2;
70 
71   return 0;
72 }
73 
74 
75 static uint8_t *option_alloc (struct cmsghdr *cmsg, int datalen, int multx,
76 			      int plusy);
77 
78 
79 /* RFC 2292, 6.3.1
80 
81    This function returns the number of bytes required to hold an option
82    when it is stored as ancillary data, including the cmsghdr structure
83    at the beginning, and any padding at the end (to make its size a
84    multiple of 8 bytes).  The argument is the size of the structure
85    defining the option, which must include any pad bytes at the
86    beginning (the value y in the alignment term "xn + y"), the type
87    byte, the length byte, and the option data.  */
88 int
inet6_option_space(int nbytes)89 inet6_option_space (int nbytes)
90 {
91   /* Add room for the extension header.  */
92   nbytes += sizeof (struct ip6_ext);
93 
94   return CMSG_SPACE (roundup (nbytes, 8));
95 }
96 link_warning (inet6_option_space,
97 	      "inet6_option_space is obsolete, use the RFC 3542 interfaces")
98 
99 
100 /* RFC 2292, 6.3.2
101 
102    This function is called once per ancillary data object that will
103    contain either Hop-by-Hop or Destination options.  It returns 0 on
104    success or -1 on an error.  */
105 int
inet6_option_init(void * bp,struct cmsghdr ** cmsgp,int type)106 inet6_option_init (void *bp, struct cmsghdr **cmsgp, int type)
107 {
108   /* Only Hop-by-Hop or Destination options allowed.  */
109   if (type != IPV6_HOPOPTS && type != IPV6_DSTOPTS)
110     return -1;
111 
112   /* BP is a pointer to the previously allocated space.  */
113   struct cmsghdr *newp = (struct cmsghdr *) bp;
114 
115   /* Initialize the message header.
116 
117      Length: No data yet, only the cmsghdr struct.  */
118   newp->cmsg_len = CMSG_LEN (0);
119   /* Originating protocol: obviously IPv6.  */
120   newp->cmsg_level = IPPROTO_IPV6;
121   /* Message type.  */
122   newp->cmsg_type = type;
123 
124   /* Pass up the result.  */
125   *cmsgp = newp;
126 
127   return 0;
128 }
129 link_warning (inet6_option_init,
130 	      "inet6_option_init is obsolete, use the RFC 3542 interfaces")
131 
132 
133 /* RFC 2292, 6.3.3
134 
135    This function appends a Hop-by-Hop option or a Destination option
136    into an ancillary data object that has been initialized by
137    inet6_option_init().  This function returns 0 if it succeeds or -1 on
138    an error.  */
139 int
inet6_option_append(struct cmsghdr * cmsg,const uint8_t * typep,int multx,int plusy)140 inet6_option_append (struct cmsghdr *cmsg, const uint8_t *typep, int multx,
141 		     int plusy)
142 {
143   /* typep is a pointer to the 8-bit option type.  It is assumed that this
144      field is immediately followed by the 8-bit option data length field,
145      which is then followed immediately by the option data.
146 
147      The option types IP6OPT_PAD1 and IP6OPT_PADN also must be handled.  */
148   int len = typep[0] == IP6OPT_PAD1 ? 1 : typep[1] + 2;
149 
150   /* Get the pointer to the space in the message.  */
151   uint8_t *ptr = option_alloc (cmsg, len, multx, plusy);
152   if (ptr == NULL)
153     /* Some problem with the parameters.  */
154     return -1;
155 
156   /* Copy the content.  */
157   memcpy (ptr, typep, len);
158 
159   return 0;
160 }
161 link_warning (inet6_option_append,
162 	      "inet6_option_append is obsolete, use the RFC 3542 interfaces")
163 
164 
165 /* RFC 2292, 6.3.4
166 
167    This function appends a Hop-by-Hop option or a Destination option
168    into an ancillary data object that has been initialized by
169    inet6_option_init().  This function returns a pointer to the 8-bit
170    option type field that starts the option on success, or NULL on an
171    error.  */
172 static uint8_t *
option_alloc(struct cmsghdr * cmsg,int datalen,int multx,int plusy)173 option_alloc (struct cmsghdr *cmsg, int datalen, int multx, int plusy)
174 {
175   /* The RFC limits the value of the alignment values.  */
176   if ((multx != 1 && multx != 2 && multx != 4 && multx != 8)
177       || ! (plusy >= 0 && plusy <= 7))
178     return NULL;
179 
180   /* Current data size.  */
181   int dsize = cmsg->cmsg_len - CMSG_LEN (0);
182 
183   /* The first two bytes of the option are for the extended header.  */
184   if (__glibc_unlikely (dsize == 0))
185     {
186       cmsg->cmsg_len += sizeof (struct ip6_ext);
187       dsize = sizeof (struct ip6_ext);
188     }
189 
190   /* First add padding.  */
191   add_pad (cmsg, ((multx - (dsize & (multx - 1))) & (multx - 1)) + plusy);
192 
193   /* Return the pointer to the start of the option space.  */
194   uint8_t *result = CMSG_DATA (cmsg) + cmsg->cmsg_len - CMSG_LEN (0);
195   cmsg->cmsg_len += datalen;
196 
197   /* The extended option header length is measured in 8-byte groups.
198      To represent the current length we might have to add padding.  */
199   dsize = cmsg->cmsg_len - CMSG_LEN (0);
200   add_pad (cmsg, (8 - (dsize & (8 - 1))) & (8 - 1));
201 
202   /* Record the new length of the option.  */
203   assert (((cmsg->cmsg_len - CMSG_LEN (0)) % 8) == 0);
204   int len8b = (cmsg->cmsg_len - CMSG_LEN (0)) / 8 - 1;
205   if (len8b >= 256)
206     /* Too long.  */
207     return NULL;
208 
209   struct ip6_ext *ie = (void *) CMSG_DATA (cmsg);
210   ie->ip6e_len = len8b;
211 
212   return result;
213 }
214 
215 
216 uint8_t *
inet6_option_alloc(struct cmsghdr * cmsg,int datalen,int multx,int plusy)217 inet6_option_alloc (struct cmsghdr *cmsg, int datalen, int multx, int plusy)
218 {
219   return option_alloc (cmsg, datalen, multx, plusy);
220 }
221 link_warning (inet6_option_alloc,
222 	      "inet6_option_alloc is obsolete, use the RFC 3542 interfaces")
223 
224 
225 /* RFC 2292, 6.3.5
226 
227    This function processes the next Hop-by-Hop option or Destination
228    option in an ancillary data object.  If another option remains to be
229    processed, the return value of the function is 0 and *tptrp points to
230    the 8-bit option type field (which is followed by the 8-bit option
231    data length, followed by the option data).  If no more options remain
232    to be processed, the return value is -1 and *tptrp is NULL.  If an
233    error occurs, the return value is -1 and *tptrp is not NULL.  */
234 int
inet6_option_next(const struct cmsghdr * cmsg,uint8_t ** tptrp)235 inet6_option_next (const struct cmsghdr *cmsg, uint8_t **tptrp)
236 {
237   /* Make sure it is an option of the right type.  */
238   if (cmsg->cmsg_level != IPPROTO_IPV6
239       || (cmsg->cmsg_type != IPV6_HOPOPTS && cmsg->cmsg_type != IPV6_DSTOPTS))
240     return -1;
241 
242   /* Pointer to the extension header.  We only compute the address, we
243      don't access anything yet.  */
244   const struct ip6_ext *ip6e = (const struct ip6_ext *) CMSG_DATA (cmsg);
245 
246   /* Make sure the message is long enough.  */
247   if (cmsg->cmsg_len < CMSG_LEN (sizeof (struct ip6_ext))
248       /* Now we can access the extension header.  */
249       || cmsg->cmsg_len < CMSG_LEN ((ip6e->ip6e_len + 1) * 8))
250     /* Too small.  */
251     return -1;
252 
253   /* Determine the address of the byte past the message.  */
254   const uint8_t *endp = CMSG_DATA (cmsg) + (ip6e->ip6e_len + 1) * 8;
255 
256   const uint8_t *result;
257   if (*tptrp == NULL)
258     /* This is the first call, return the first option if there is one.  */
259     result = (const uint8_t *) (ip6e + 1);
260   else
261     {
262       /* Make sure *TPTRP points to a beginning of a new option in
263 	 the message.  The upper limit is checked in get_opt_end.  */
264       if (*tptrp < (const uint8_t *) (ip6e + 1))
265 	return -1;
266 
267       /* Get the beginning of the next option.  */
268       if (get_opt_end (&result, *tptrp, endp) != 0)
269 	return -1;
270     }
271 
272   /* We know where the next option starts.  */
273   *tptrp = (uint8_t *) result;
274 
275   /* Check the option is fully represented in the message.  */
276   return get_opt_end (&result, result, endp);
277 }
278 link_warning (inet6_option_next,
279 	      "inet6_option_next is obsolete, use the RFC 3542 interfaces")
280 
281 
282 /* RFC 2292, 6.3.6
283 
284    This function is similar to the previously described
285    inet6_option_next() function, except this function lets the caller
286    specify the option type to be searched for, instead of always
287    returning the next option in the ancillary data object.  cmsg is a
288    pointer to cmsghdr structure of which cmsg_level equals IPPROTO_IPV6
289    and cmsg_type equals either IPV6_HOPOPTS or IPV6_DSTOPTS.  */
290 int
inet6_option_find(const struct cmsghdr * cmsg,uint8_t ** tptrp,int type)291 inet6_option_find (const struct cmsghdr *cmsg, uint8_t **tptrp, int type)
292 {
293   /* Make sure it is an option of the right type.  */
294   if (cmsg->cmsg_level != IPPROTO_IPV6
295       || (cmsg->cmsg_type != IPV6_HOPOPTS && cmsg->cmsg_type != IPV6_DSTOPTS))
296     return -1;
297 
298   /* Pointer to the extension header.  We only compute the address, we
299      don't access anything yet.  */
300   const struct ip6_ext *ip6e = (const struct ip6_ext *) CMSG_DATA (cmsg);
301 
302   /* Make sure the message is long enough.  */
303   if (cmsg->cmsg_len < CMSG_LEN (sizeof (struct ip6_ext))
304       /* Now we can access the extension header.  */
305       || cmsg->cmsg_len < CMSG_LEN ((ip6e->ip6e_len + 1) * 8))
306     /* Too small.  */
307     return -1;
308 
309   /* Determine the address of the byte past the message.  */
310   const uint8_t *endp = CMSG_DATA (cmsg) + (ip6e->ip6e_len + 1) * 8;
311 
312   const uint8_t *next;
313   if (*tptrp == NULL)
314     /* This is the first call, return the first option if there is one.  */
315     next = (const uint8_t *) (ip6e + 1);
316   else
317     {
318       /* Make sure *TPTRP points to a beginning of a new option in
319 	 the message.  The upper limit is checked in get_opt_end.  */
320       if (*tptrp < (const uint8_t *) (ip6e + 1))
321 	return -1;
322 
323       /* Get the beginning of the next option.  */
324       if (get_opt_end (&next, *tptrp, endp) != 0)
325 	return -1;
326     }
327 
328   /* Now search for the appropriate typed entry.  */
329   const uint8_t *result;
330   do
331     {
332       result = next;
333 
334       /* Get the end of this entry.  */
335       if (get_opt_end (&next, result, endp) != 0)
336 	return -1;
337     }
338   while (*result != type);
339 
340   /* We know where the next option starts.  */
341   *tptrp = (uint8_t *) result;
342 
343   /* Success.  */
344   return 0;
345 }
346 link_warning (inet6_option_find,
347 	      "inet6_option_find is obsolete, use the RFC 3542 interfaces")
348