Back to index

glibc  2.9
inet6_opt.c
Go to the documentation of this file.
00001 /* Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Ulrich Drepper <drepper@redhat.com>, 2006.
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 <string.h>
00021 #include <netinet/in.h>
00022 #include <netinet/ip6.h>
00023 
00024 
00025 /* RFC 3542, 10.1
00026 
00027    This function returns the number of bytes needed for the empty
00028    extension header i.e., without any options.  If EXTBUF is not NULL it
00029    also initializes the extension header to have the correct length
00030    field.  In that case if the EXTLEN value is not a positive (i.e.,
00031    non-zero) multiple of 8 the function fails and returns -1.  */
00032 int
00033 inet6_opt_init (void *extbuf, socklen_t extlen)
00034 {
00035   if (extbuf != NULL)
00036     {
00037       if (extlen <= 0 || (extlen % 8) != 0 || extlen > 256 * 8)
00038        return -1;
00039 
00040       /* Fill in the length in units of 8 octets.  */
00041       struct ip6_hbh *extp = (struct ip6_hbh *) extbuf;
00042 
00043       /* RFC 2460 requires that the header extension length is the
00044         length of the option header in 8-byte units, not including
00045         the first 8 bytes.  Hence we have to subtract one.  */
00046       extp->ip6h_len = extlen / 8 - 1;
00047     }
00048 
00049   return sizeof (struct ip6_hbh);
00050 }
00051 
00052 
00053 static void
00054 add_padding (uint8_t *extbuf, int offset, int npad)
00055 {
00056   if (npad == 1)
00057     extbuf[offset] = IP6OPT_PAD1;
00058   else if (npad > 0)
00059     {
00060       struct ip6_opt *pad_opt = (struct ip6_opt *) (extbuf + offset);
00061 
00062       pad_opt->ip6o_type = IP6OPT_PADN;
00063       pad_opt->ip6o_len = npad - sizeof (struct ip6_opt);
00064       /* Clear the memory used by the padding.  */
00065       memset (pad_opt + 1, '\0', pad_opt->ip6o_len);
00066     }
00067 }
00068 
00069 
00070 
00071 /* RFC 3542, 10.2
00072 
00073    This function returns the updated total length taking into account
00074    adding an option with length 'len' and alignment 'align'.  If
00075    EXTBUF is not NULL then, in addition to returning the length, the
00076    function inserts any needed pad option, initializes the option
00077    (setting the type and length fields) and returns a pointer to the
00078    location for the option content in databufp.  If the option does
00079    not fit in the extension header buffer the function returns -1.  */
00080 int
00081 inet6_opt_append (void *extbuf, socklen_t extlen, int offset, uint8_t type,
00082                 socklen_t len, uint8_t align, void **databufp)
00083 {
00084   /* Check minimum offset.  */
00085   if (offset < sizeof (struct ip6_hbh))
00086     return -1;
00087 
00088   /* One cannot add padding options.  */
00089   if (type == IP6OPT_PAD1 || type == IP6OPT_PADN)
00090     return -1;
00091 
00092   /* The option length must fit in one octet.  */
00093   if (len > 255)
00094     return -1;
00095 
00096   /* The alignment can only by 1, 2, 4, or 8 and must not exceed the
00097      option length.  */
00098   if (align == 0 || align > 8 || (align & (align - 1)) != 0 || align > len)
00099     return -1;
00100 
00101   /* Determine the needed padding for alignment.  Following the
00102      current content of the buffer we have the is the IPv6 option type
00103      and length, followed immediately by the data.  The data has the
00104      alignment constraints.  Therefore padding must be inserted in the
00105      form of padding options before the new option. */
00106   int data_offset = offset + sizeof (struct ip6_opt);
00107   int npad = (align - data_offset % align) & (align - 1);
00108 
00109   if (extbuf != NULL)
00110     {
00111       /* Now we can check whether the buffer is large enough.  */
00112       if (data_offset + npad + len > extlen)
00113        return -1;
00114 
00115       add_padding (extbuf, offset, npad);
00116 
00117       offset += npad;
00118 
00119       /* Now prepare the option itself.  */
00120       struct ip6_opt *opt = (struct ip6_opt *) ((uint8_t *) extbuf + offset);
00121 
00122       opt->ip6o_type = type;
00123       opt->ip6o_len = len;
00124 
00125       *databufp = opt + 1;
00126     }
00127   else
00128     offset += npad;
00129 
00130   return offset + sizeof (struct ip6_opt) + len;
00131 }
00132 
00133 
00134 /* RFC 3542, 10.3
00135 
00136    This function returns the updated total length taking into account
00137    the final padding of the extension header to make it a multiple of
00138    8 bytes.  If EXTBUF is not NULL the function also initializes the
00139    option by inserting a Pad1 or PadN option of the proper length.  */
00140 int
00141 inet6_opt_finish (void *extbuf, socklen_t extlen, int offset)
00142 {
00143   /* Check minimum offset.  */
00144   if (offset < sizeof (struct ip6_hbh))
00145     return -1;
00146 
00147   /* Required padding at the end.  */
00148   int npad = (8 - (offset & 7)) & 7;
00149 
00150   if (extbuf != NULL)
00151     {
00152       /* Make sure the buffer is large enough.  */
00153       if (offset + npad > extlen)
00154        return -1;
00155 
00156       add_padding (extbuf, offset, npad);
00157     }
00158 
00159   return offset + npad;
00160 }
00161 
00162 
00163 /* RFC 3542, 10.4
00164 
00165    This function inserts data items of various sizes in the data
00166    portion of the option.  VAL should point to the data to be
00167    inserted.  OFFSET specifies where in the data portion of the option
00168    the value should be inserted; the first byte after the option type
00169    and length is accessed by specifying an offset of zero.  */
00170 int
00171 inet6_opt_set_val (void *databuf, int offset, void *val, socklen_t vallen)
00172 {
00173   memcpy ((uint8_t *) databuf + offset, val, vallen);
00174 
00175   return offset + vallen;
00176 }
00177 
00178 
00179 /* RFC 3542, 10.5
00180 
00181    This function parses received option extension headers returning
00182    the next option.  EXTBUF and EXTLEN specifies the extension header.
00183    OFFSET should either be zero (for the first option) or the length
00184    returned by a previous call to 'inet6_opt_next' or
00185    'inet6_opt_find'.  It specifies the position where to continue
00186    scanning the extension buffer.  */
00187 int
00188 inet6_opt_next (void *extbuf, socklen_t extlen, int offset, uint8_t *typep,
00189               socklen_t *lenp, void **databufp)
00190 {
00191   if (offset == 0)
00192     offset = sizeof (struct ip6_hbh);
00193   else if (offset < sizeof (struct ip6_hbh))
00194     return -1;
00195 
00196   while (offset < extlen)
00197     {
00198       struct ip6_opt *opt = (struct ip6_opt *) ((uint8_t *) extbuf + offset);
00199 
00200       if (opt->ip6o_type == IP6OPT_PAD1)
00201        /* Single byte padding.  */
00202        ++offset;
00203       else if (opt->ip6o_type == IP6OPT_PADN)
00204        offset += sizeof (struct ip6_opt) + opt->ip6o_len;
00205       else
00206        {
00207          /* Check whether the option is valid.  */
00208          offset += sizeof (struct ip6_opt) + opt->ip6o_len;
00209          if (offset > extlen)
00210            return -1;
00211 
00212          *typep = opt->ip6o_type;
00213          *lenp = opt->ip6o_len;
00214          *databufp = opt + 1;
00215          return offset;
00216        }
00217     }
00218 
00219   return -1;
00220 }
00221 
00222 
00223 /* RFC 3542, 10.6
00224 
00225    This function is similar to the previously described
00226    'inet6_opt_next' function, except this function lets the caller
00227    specify the option type to be searched for, instead of always
00228    returning the next option in the extension header.  */
00229 int
00230 inet6_opt_find (void *extbuf, socklen_t extlen, int offset, uint8_t type,
00231               socklen_t *lenp, void **databufp)
00232 {
00233   if (offset == 0)
00234     offset = sizeof (struct ip6_hbh);
00235   else if (offset < sizeof (struct ip6_hbh))
00236     return -1;
00237 
00238   while (offset < extlen)
00239     {
00240       struct ip6_opt *opt = (struct ip6_opt *) ((uint8_t *) extbuf + offset);
00241 
00242       if (opt->ip6o_type == IP6OPT_PAD1)
00243        {
00244          /* Single byte padding.  */
00245          ++offset;
00246          if (type == IP6OPT_PAD1)
00247            {
00248              *lenp = 0;
00249              *databufp = (uint8_t *) extbuf + offset;
00250              return offset;
00251            }
00252        }
00253       else if (opt->ip6o_type != type)
00254        offset += sizeof (struct ip6_opt) + opt->ip6o_len;
00255       else
00256        {
00257          /* Check whether the option is valid.  */
00258          offset += sizeof (struct ip6_opt) + opt->ip6o_len;
00259          if (offset > extlen)
00260            return -1;
00261 
00262          *lenp = opt->ip6o_len;
00263          *databufp = opt + 1;
00264          return offset;
00265        }
00266     }
00267 
00268   return -1;
00269 }
00270 
00271 
00272 /* RFC 3542, 10.7
00273 
00274    This function extracts data items of various sizes in the data
00275    portion of the option.  */
00276 int
00277 inet6_opt_get_val (void *databuf, int offset, void *val, socklen_t vallen)
00278 {
00279   memcpy (val, (uint8_t *) databuf + offset, vallen);
00280 
00281   return offset + vallen;
00282 }