source: trunk/libs/newlib/src/newlib/libc/sys/linux/net/inet6_option.c @ 444

Last change on this file since 444 was 444, checked in by satin@…, 6 years ago

add newlib,libalmos-mkh, restructure shared_syscalls.h and mini-libc

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