(linenum→info "unix/slp.c:2238")

glibc/2.7/inet/inet6_opt.c

    1: /* Copyright (C) 2006, 2007 Free Software Foundation, Inc.
    2:    This file is part of the GNU C Library.
    3:    Contributed by Ulrich Drepper <drepper@redhat.com>, 2006.
    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 <string.h>
   21: #include <netinet/in.h>
   22: #include <netinet/ip6.h>
   23: 
   24: 
   25: /* RFC 3542, 10.1
   26: 
   27:    This function returns the number of bytes needed for the empty
   28:    extension header i.e., without any options.  If EXTBUF is not NULL it
   29:    also initializes the extension header to have the correct length
   30:    field.  In that case if the EXTLEN value is not a positive (i.e.,
   31:    non-zero) multiple of 8 the function fails and returns -1.  */
   32: int
   33: inet6_opt_init (void *extbuf, socklen_t extlen)
   34: {
   35:   if (extbuf != NULL)
   36:     {
   37:       if (extlen <= 0 || (extlen % 8) != 0)
   38:         return -1;
   39: 
   40:       /* Fill in the length in units of 8 octets.  */
   41:       struct ip6_hbh *extp = (struct ip6_hbh *) extbuf;
   42:       extp->ip6h_len = extlen / 8;
   43:     }
   44: 
   45:   return sizeof (struct ip6_hbh);
   46: }
   47: 
   48: 
   49: static void
   50: add_padding (uint8_t *extbuf, int offset, int npad)
   51: {
   52:   if (npad == 1)
   53:     extbuf[offset] = IP6OPT_PAD1;
   54:   else if (npad > 0)
   55:     {
   56:       struct ip6_opt *pad_opt = (struct ip6_opt *) (extbuf + offset);
   57: 
   58:       pad_opt->ip6o_type = IP6OPT_PADN;
   59:       pad_opt->ip6o_len = npad - sizeof (struct ip6_opt);
   60:       /* Clear the memory used by the padding.  */
   61:       memset (pad_opt + 1, '\0', pad_opt->ip6o_len);
   62:     }
   63: }
   64: 
   65: 
   66: 
   67: /* RFC 3542, 10.2
   68: 
   69:    This function returns the updated total length taking into account
   70:    adding an option with length 'len' and alignment 'align'.  If
   71:    EXTBUF is not NULL then, in addition to returning the length, the
   72:    function inserts any needed pad option, initializes the option
   73:    (setting the type and length fields) and returns a pointer to the
   74:    location for the option content in databufp.  If the option does
   75:    not fit in the extension header buffer the function returns -1.  */
   76: int
   77: inet6_opt_append (void *extbuf, socklen_t extlen, int offset, uint8_t type,
   78:                   socklen_t len, uint8_t align, void **databufp)
   79: {
   80:   /* Check minimum offset.  */
   81:   if (offset < sizeof (struct ip6_hbh))
   82:     return -1;
   83: 
   84:   /* One cannot add padding options.  */
   85:   if (type == IP6OPT_PAD1 || type == IP6OPT_PADN)
   86:     return -1;
   87: 
   88:   /* The option length must fit in one octet.  */
   89:   if (len > 255)
   90:     return -1;
   91: 
   92:   /* The alignment can only by 1, 2, 4, or 8 and must not exceed the
   93:      option length.  */
   94:   if (align == 0 || align > 8 || (align & (align - 1)) != 0 || align > len)
   95:     return -1;
   96: 
   97:   /* Determine the needed padding for alignment.  Following the
   98:      current content of the buffer we have the is the IPv6 option type
   99:      and length, followed immediately by the data.  The data has the
  100:      alignment constraints.  Therefore padding must be inserted in the
  101:      form of padding options before the new option. */
  102:   int data_offset = offset + sizeof (struct ip6_opt);
  103:   int npad = (align - data_offset % align) & (align - 1);
  104: 
  105:   if (extbuf != NULL)
  106:     {
  107:       /* Now we can check whether the buffer is large enough.  */
  108:       if (data_offset + npad + len > extlen)
  109:         return -1;
  110: 
  111:       add_padding (extbuf, offset, npad);
  112: 
  113:       offset += npad;
  114: 
  115:       /* Now prepare the option itself.  */
  116:       struct ip6_opt *opt = (struct ip6_opt *) ((uint8_t *) extbuf + offset);
  117: 
  118:       opt->ip6o_type = type;
  119:       opt->ip6o_len = len;
  120: 
  121:       *databufp = opt + 1;
  122:     }
  123:   else
  124:     offset += npad;
  125: 
  126:   return offset + sizeof (struct ip6_opt) + len;
  127: }
  128: 
  129: 
  130: /* RFC 3542, 10.3
  131: 
  132:    This function returns the updated total length taking into account
  133:    the final padding of the extension header to make it a multiple of
  134:    8 bytes.  If EXTBUF is not NULL the function also initializes the
  135:    option by inserting a Pad1 or PadN option of the proper length.  */
  136: int
  137: inet6_opt_finish (void *extbuf, socklen_t extlen, int offset)
  138: {
  139:   /* Check minimum offset.  */
  140:   if (offset < sizeof (struct ip6_hbh))
  141:     return -1;
  142: 
  143:   /* Required padding at the end.  */
  144:   int npad = (8 - (offset & 7)) & 7;
  145: 
  146:   if (extbuf != NULL)
  147:     {
  148:       /* Make sure the buffer is large enough.  */
  149:       if (offset + npad > extlen)
  150:         return -1;
  151: 
  152:       add_padding (extbuf, offset, npad);
  153:     }
  154: 
  155:   return offset + npad;
  156: }
  157: 
  158: 
  159: /* RFC 3542, 10.4
  160: 
  161:    This function inserts data items of various sizes in the data
  162:    portion of the option.  VAL should point to the data to be
  163:    inserted.  OFFSET specifies where in the data portion of the option
  164:    the value should be inserted; the first byte after the option type
  165:    and length is accessed by specifying an offset of zero.  */
  166: int
  167: inet6_opt_set_val (void *databuf, int offset, void *val, socklen_t vallen)
  168: {
  169:   memcpy ((uint8_t *) databuf + offset, val, vallen);
  170: 
  171:   return offset + vallen;
  172: }
  173: 
  174: 
  175: /* RFC 3542, 10.5
  176: 
  177:    This function parses received option extension headers returning
  178:    the next option.  EXTBUF and EXTLEN specifies the extension header.
  179:    OFFSET should either be zero (for the first option) or the length
  180:    returned by a previous call to 'inet6_opt_next' or
  181:    'inet6_opt_find'.  It specifies the position where to continue
  182:    scanning the extension buffer.  */
  183: int
  184: inet6_opt_next (void *extbuf, socklen_t extlen, int offset, uint8_t *typep,
  185:                 socklen_t *lenp, void **databufp)
  186: {
  187:   if (offset == 0)
  188:     offset = sizeof (struct ip6_hbh);
  189:   else if (offset < sizeof (struct ip6_hbh))
  190:     return -1;
  191: 
  192:   while (offset < extlen)
  193:     {
  194:       struct ip6_opt *opt = (struct ip6_opt *) ((uint8_t *) extbuf + offset);
  195: 
  196:       if (opt->ip6o_type == IP6OPT_PAD1)
  197:         /* Single byte padding.  */
  198:         ++offset;
  199:       else if (opt->ip6o_type == IP6OPT_PADN)
  200:         offset += sizeof (struct ip6_opt) + opt->ip6o_len;
  201:       else
  202:         {
  203:           /* Check whether the option is valid.  */
  204:           offset += sizeof (struct ip6_opt) + opt->ip6o_len;
  205:           if (offset > extlen)
  206:             return -1;
  207: 
  208:           *typep = opt->ip6o_type;
  209:           *lenp = opt->ip6o_len;
  210:           *databufp = opt + 1;
  211:           return offset;
  212:         }
  213:     }
  214: 
  215:   return -1;
  216: }
  217: 
  218: 
  219: /* RFC 3542, 10.6
  220: 
  221:    This function is similar to the previously described
  222:    'inet6_opt_next' function, except this function lets the caller
  223:    specify the option type to be searched for, instead of always
  224:    returning the next option in the extension header.  */
  225: int
  226: inet6_opt_find (void *extbuf, socklen_t extlen, int offset, uint8_t type,
  227:                 socklen_t *lenp, void **databufp)
  228: {
  229:   if (offset == 0)
  230:     offset = sizeof (struct ip6_hbh);
  231:   else if (offset < sizeof (struct ip6_hbh))
  232:     return -1;
  233: 
  234:   while (offset < extlen)
  235:     {
  236:       struct ip6_opt *opt = (struct ip6_opt *) ((uint8_t *) extbuf + offset);
  237: 
  238:       if (opt->ip6o_type == IP6OPT_PAD1)
  239:         {
  240:           /* Single byte padding.  */
  241:           ++offset;
  242:           if (type == IP6OPT_PAD1)
  243:             {
  244:               *lenp = 0;
  245:               *databufp = (uint8_t *) extbuf + offset;
  246:               return offset;
  247:             }
  248:         }
  249:       else if (opt->ip6o_type != type)
  250:         offset += sizeof (struct ip6_opt) + opt->ip6o_len;
  251:       else
  252:         {
  253:           /* Check whether the option is valid.  */
  254:           offset += sizeof (struct ip6_opt) + opt->ip6o_len;
  255:           if (offset > extlen)
  256:             return -1;
  257: 
  258:           *lenp = opt->ip6o_len;
  259:           *databufp = opt + 1;
  260:           return offset;
  261:         }
  262:     }
  263: 
  264:   return -1;
  265: }
  266: 
  267: 
  268: /* RFC 3542, 10.7
  269: 
  270:    This function extracts data items of various sizes in the data
  271:    portion of the option.  */
  272: int
  273: inet6_opt_get_val (void *databuf, int offset, void *val, socklen_t vallen)
  274: {
  275:   memcpy (val, (uint8_t *) databuf + offset, vallen);
  276: 
  277:   return offset + vallen;
  278: }
Syntax (Markdown)