Back to index

glibc  2.9
inet6_option.c
Go to the documentation of this file.
00001 /* Copyright (C) 2003, 2006 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <assert.h>
00021 #include <string.h>
00022 #include <netinet/in.h>
00023 #include <netinet/ip6.h>
00024 #include <sys/param.h>
00025 
00026 
00027 static void
00028 internal_function
00029 add_pad (struct cmsghdr *cmsg, int len)
00030 {
00031   unsigned char *p = CMSG_DATA (cmsg) + cmsg->cmsg_len - CMSG_LEN (0);
00032 
00033   if (len == 1)
00034     /* Special handling for 1, a one-byte solution.  */
00035     *p++ = IP6OPT_PAD1;
00036   else if (len != 0)
00037     {
00038       /* Multibyte padding.  */
00039       *p++ = IP6OPT_PADN;
00040       *p++ = len - 2;       /* Discount the two header bytes.  */
00041       /* The rest is filled with zero.  */
00042       memset (p, '\0', len - 2);
00043       p += len - 2;
00044     }
00045 
00046   /* Account for the bytes.  */
00047   cmsg->cmsg_len += len;
00048 }
00049 
00050 
00051 static int
00052 get_opt_end (const uint8_t **result, const uint8_t *startp,
00053             const uint8_t *endp)
00054 {
00055   if (startp >= endp)
00056     /* Out of bounds.  */
00057     return -1;
00058 
00059   if (*startp == IP6OPT_PAD1)
00060     {
00061       /* Just this one byte.  */
00062       *result = startp + 1;
00063       return 0;
00064     }
00065 
00066   /* Now we know there must be at least two bytes.  */
00067   if (startp + 2 > endp
00068       /* Now we can get the length byte.  */
00069       || startp + startp[1] + 2 > endp)
00070     return -1;
00071 
00072   *result = startp + startp[1] + 2;
00073 
00074   return 0;
00075 }
00076 
00077 
00078 static uint8_t *option_alloc (struct cmsghdr *cmsg, int datalen, int multx,
00079                            int plusy);
00080 
00081 
00082 /* RFC 2292, 6.3.1
00083 
00084    This function returns the number of bytes required to hold an option
00085    when it is stored as ancillary data, including the cmsghdr structure
00086    at the beginning, and any padding at the end (to make its size a
00087    multiple of 8 bytes).  The argument is the size of the structure
00088    defining the option, which must include any pad bytes at the
00089    beginning (the value y in the alignment term "xn + y"), the type
00090    byte, the length byte, and the option data.  */
00091 int
00092 inet6_option_space (nbytes)
00093      int nbytes;
00094 {
00095   /* Add room for the extension header.  */
00096   nbytes += sizeof (struct ip6_ext);
00097 
00098   return CMSG_SPACE (roundup (nbytes, 8));
00099 }
00100 link_warning (inet6_option_space,
00101              "inet6_option_space is obsolete, use the RFC 3542 interfaces")
00102 
00103 
00104 /* RFC 2292, 6.3.2
00105 
00106    This function is called once per ancillary data object that will
00107    contain either Hop-by-Hop or Destination options.  It returns 0 on
00108    success or -1 on an error.  */
00109 int
00110 inet6_option_init (bp, cmsgp, type)
00111      void *bp;
00112      struct cmsghdr **cmsgp;
00113      int type;
00114 {
00115   /* Only Hop-by-Hop or Destination options allowed.  */
00116   if (type != IPV6_HOPOPTS && type != IPV6_DSTOPTS)
00117     return -1;
00118 
00119   /* BP is a pointer to the previously allocated space.  */
00120   struct cmsghdr *newp = (struct cmsghdr *) bp;
00121 
00122   /* Initialize the message header.
00123 
00124      Length: No data yet, only the cmsghdr struct.  */
00125   newp->cmsg_len = CMSG_LEN (0);
00126   /* Originating protocol: obviously IPv6.  */
00127   newp->cmsg_level = IPPROTO_IPV6;
00128   /* Message type.  */
00129   newp->cmsg_type = type;
00130 
00131   /* Pass up the result.  */
00132   *cmsgp = newp;
00133 
00134   return 0;
00135 }
00136 link_warning (inet6_option_init,
00137              "inet6_option_init is obsolete, use the RFC 3542 interfaces")
00138 
00139 
00140 /* RFC 2292, 6.3.3
00141 
00142    This function appends a Hop-by-Hop option or a Destination option
00143    into an ancillary data object that has been initialized by
00144    inet6_option_init().  This function returns 0 if it succeeds or -1 on
00145    an error.  */
00146 int
00147 inet6_option_append (cmsg, typep, multx, plusy)
00148      struct cmsghdr *cmsg;
00149      const uint8_t *typep;
00150      int multx;
00151      int plusy;
00152 {
00153   /* typep is a pointer to the 8-bit option type.  It is assumed that this
00154      field is immediately followed by the 8-bit option data length field,
00155      which is then followed immediately by the option data.
00156 
00157      The option types IP6OPT_PAD1 and IP6OPT_PADN also must be handled.  */
00158   int len = typep[0] == IP6OPT_PAD1 ? 1 : typep[1] + 2;
00159 
00160   /* Get the pointer to the space in the message.  */
00161   uint8_t *ptr = option_alloc (cmsg, len, multx, plusy);
00162   if (ptr == NULL)
00163     /* Some problem with the parameters.  */
00164     return -1;
00165 
00166   /* Copy the content.  */
00167   memcpy (ptr, typep, len);
00168 
00169   return 0;
00170 }
00171 link_warning (inet6_option_append,
00172              "inet6_option_append is obsolete, use the RFC 3542 interfaces")
00173 
00174 
00175 /* RFC 2292, 6.3.4
00176 
00177    This function appends a Hop-by-Hop option or a Destination option
00178    into an ancillary data object that has been initialized by
00179    inet6_option_init().  This function returns a pointer to the 8-bit
00180    option type field that starts the option on success, or NULL on an
00181    error.  */
00182 static uint8_t *
00183 option_alloc (struct cmsghdr *cmsg, int datalen, int multx, int plusy)
00184 {
00185   /* The RFC limits the value of the alignment values.  */
00186   if ((multx != 1 && multx != 2 && multx != 4 && multx != 8)
00187       || ! (plusy >= 0 && plusy <= 7))
00188     return NULL;
00189 
00190   /* Current data size.  */
00191   int dsize = cmsg->cmsg_len - CMSG_LEN (0);
00192 
00193   /* The first two bytes of the option are for the extended header.  */
00194   if (__builtin_expect (dsize == 0, 0))
00195     {
00196       cmsg->cmsg_len += sizeof (struct ip6_ext);
00197       dsize = sizeof (struct ip6_ext);
00198     }
00199 
00200   /* First add padding.  */
00201   add_pad (cmsg, ((multx - (dsize & (multx - 1))) & (multx - 1)) + plusy);
00202 
00203   /* Return the pointer to the start of the option space.  */
00204   uint8_t *result = CMSG_DATA (cmsg) + cmsg->cmsg_len - CMSG_LEN (0);
00205   cmsg->cmsg_len += datalen;
00206 
00207   /* The extended option header length is measured in 8-byte groups.
00208      To represent the current length we might have to add padding.  */
00209   dsize = cmsg->cmsg_len - CMSG_LEN (0);
00210   add_pad (cmsg, (8 - (dsize & (8 - 1))) & (8 - 1));
00211 
00212   /* Record the new length of the option.  */
00213   assert (((cmsg->cmsg_len - CMSG_LEN (0)) % 8) == 0);
00214   int len8b = (cmsg->cmsg_len - CMSG_LEN (0)) / 8 - 1;
00215   if (len8b >= 256)
00216     /* Too long.  */
00217     return NULL;
00218 
00219   ((struct ip6_ext *) CMSG_DATA (cmsg))->ip6e_len = len8b;
00220 
00221   return result;
00222 }
00223 
00224 
00225 uint8_t *
00226 inet6_option_alloc (cmsg, datalen, multx, plusy)
00227      struct cmsghdr *cmsg;
00228      int datalen;
00229      int multx;
00230      int plusy;
00231 {
00232   return option_alloc (cmsg, datalen, multx, plusy);
00233 }
00234 link_warning (inet6_option_alloc,
00235              "inet6_option_alloc is obsolete, use the RFC 3542 interfaces")
00236 
00237 
00238 /* RFC 2292, 6.3.5
00239 
00240    This function processes the next Hop-by-Hop option or Destination
00241    option in an ancillary data object.  If another option remains to be
00242    processed, the return value of the function is 0 and *tptrp points to
00243    the 8-bit option type field (which is followed by the 8-bit option
00244    data length, followed by the option data).  If no more options remain
00245    to be processed, the return value is -1 and *tptrp is NULL.  If an
00246    error occurs, the return value is -1 and *tptrp is not NULL.  */
00247 int
00248 inet6_option_next (cmsg, tptrp)
00249      const struct cmsghdr *cmsg;
00250      uint8_t **tptrp;
00251 {
00252   /* Make sure it is an option of the right type.  */
00253   if (cmsg->cmsg_level != IPPROTO_IPV6
00254       || (cmsg->cmsg_type != IPV6_HOPOPTS && cmsg->cmsg_type != IPV6_DSTOPTS))
00255     return -1;
00256 
00257   /* Pointer to the extension header.  We only compute the address, we
00258      don't access anything yet.  */
00259   const struct ip6_ext *ip6e = (const struct ip6_ext *) CMSG_DATA (cmsg);
00260 
00261   /* Make sure the message is long enough.  */
00262   if (cmsg->cmsg_len < CMSG_LEN (sizeof (struct ip6_ext))
00263       /* Now we can access the extension header.  */
00264       || cmsg->cmsg_len < CMSG_LEN ((ip6e->ip6e_len + 1) * 8))
00265     /* Too small.  */
00266     return -1;
00267 
00268   /* Determine the address of the byte past the message.  */
00269   const uint8_t *endp = CMSG_DATA (cmsg) + (ip6e->ip6e_len + 1) * 8;
00270 
00271   const uint8_t *result;
00272   if (*tptrp == NULL)
00273     /* This is the first call, return the first option if there is one.  */
00274     result = (const uint8_t *) (ip6e + 1);
00275   else
00276     {
00277       /* Make sure *TPTRP points to a beginning of a new option in
00278         the message.  The upper limit is checked in get_opt_end.  */
00279       if (*tptrp < (const uint8_t *) (ip6e + 1))
00280        return -1;
00281 
00282       /* Get the beginning of the next option.  */
00283       if (get_opt_end (&result, *tptrp, endp) != 0)
00284        return -1;
00285     }
00286 
00287   /* We know where the next option starts.  */
00288   *tptrp = (uint8_t *) result;
00289 
00290   /* Check the option is fully represented in the message.  */
00291   return get_opt_end (&result, result, endp);
00292 }
00293 link_warning (inet6_option_next,
00294              "inet6_option_next is obsolete, use the RFC 3542 interfaces")
00295 
00296 
00297 /* RFC 2292, 6.3.6
00298 
00299    This function is similar to the previously described
00300    inet6_option_next() function, except this function lets the caller
00301    specify the option type to be searched for, instead of always
00302    returning the next option in the ancillary data object.  cmsg is a
00303    pointer to cmsghdr structure of which cmsg_level equals IPPROTO_IPV6
00304    and cmsg_type equals either IPV6_HOPOPTS or IPV6_DSTOPTS.  */
00305 int
00306 inet6_option_find (cmsg, tptrp, type)
00307      const struct cmsghdr *cmsg;
00308      uint8_t **tptrp;
00309      int type;
00310 {
00311   /* Make sure it is an option of the right type.  */
00312   if (cmsg->cmsg_level != IPPROTO_IPV6
00313       || (cmsg->cmsg_type != IPV6_HOPOPTS && cmsg->cmsg_type != IPV6_DSTOPTS))
00314     return -1;
00315 
00316   /* Pointer to the extension header.  We only compute the address, we
00317      don't access anything yet.  */
00318   const struct ip6_ext *ip6e = (const struct ip6_ext *) CMSG_DATA (cmsg);
00319 
00320   /* Make sure the message is long enough.  */
00321   if (cmsg->cmsg_len < CMSG_LEN (sizeof (struct ip6_ext))
00322       /* Now we can access the extension header.  */
00323       || cmsg->cmsg_len < CMSG_LEN ((ip6e->ip6e_len + 1) * 8))
00324     /* Too small.  */
00325     return -1;
00326 
00327   /* Determine the address of the byte past the message.  */
00328   const uint8_t *endp = CMSG_DATA (cmsg) + (ip6e->ip6e_len + 1) * 8;
00329 
00330   const uint8_t *next;
00331   if (*tptrp == NULL)
00332     /* This is the first call, return the first option if there is one.  */
00333     next = (const uint8_t *) (ip6e + 1);
00334   else
00335     {
00336       /* Make sure *TPTRP points to a beginning of a new option in
00337         the message.  The upper limit is checked in get_opt_end.  */
00338       if (*tptrp < (const uint8_t *) (ip6e + 1))
00339        return -1;
00340 
00341       /* Get the beginning of the next option.  */
00342       if (get_opt_end (&next, *tptrp, endp) != 0)
00343        return -1;
00344     }
00345 
00346   /* Now search for the appropriate typed entry.  */
00347   const uint8_t *result;
00348   do
00349     {
00350       result = next;
00351 
00352       /* Get the end of this entry.  */
00353       if (get_opt_end (&next, result, endp) != 0)
00354        return -1;
00355     }
00356   while (*result != type);
00357 
00358   /* We know where the next option starts.  */
00359   *tptrp = (uint8_t *) result;
00360 
00361   /* Success.  */
00362   return 0;
00363 }
00364 link_warning (inet6_option_find,
00365              "inet6_option_find is obsolete, use the RFC 3542 interfaces")