Blame inet/inet6_option.c

Packit Service 82fcde
/* Copyright (C) 2003-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <assert.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <netinet/in.h>
Packit Service 82fcde
#include <netinet/ip6.h>
Packit Service 82fcde
#include <sys/param.h>
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
add_pad (struct cmsghdr *cmsg, int len)
Packit Service 82fcde
{
Packit Service 82fcde
  unsigned char *p = CMSG_DATA (cmsg) + cmsg->cmsg_len - CMSG_LEN (0);
Packit Service 82fcde
Packit Service 82fcde
  if (len == 1)
Packit Service 82fcde
    /* Special handling for 1, a one-byte solution.  */
Packit Service 82fcde
    *p++ = IP6OPT_PAD1;
Packit Service 82fcde
  else if (len != 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Multibyte padding.  */
Packit Service 82fcde
      *p++ = IP6OPT_PADN;
Packit Service 82fcde
      *p++ = len - 2;	/* Discount the two header bytes.  */
Packit Service 82fcde
      /* The rest is filled with zero.  */
Packit Service 82fcde
      memset (p, '\0', len - 2);
Packit Service 82fcde
      p += len - 2;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Account for the bytes.  */
Packit Service 82fcde
  cmsg->cmsg_len += len;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static int
Packit Service 82fcde
get_opt_end (const uint8_t **result, const uint8_t *startp,
Packit Service 82fcde
	     const uint8_t *endp)
Packit Service 82fcde
{
Packit Service 82fcde
  if (startp >= endp)
Packit Service 82fcde
    /* Out of bounds.  */
Packit Service 82fcde
    return -1;
Packit Service 82fcde
Packit Service 82fcde
  if (*startp == IP6OPT_PAD1)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Just this one byte.  */
Packit Service 82fcde
      *result = startp + 1;
Packit Service 82fcde
      return 0;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Now we know there must be at least two bytes.  */
Packit Service 82fcde
  if (startp + 2 > endp
Packit Service 82fcde
      /* Now we can get the length byte.  */
Packit Service 82fcde
      || startp + startp[1] + 2 > endp)
Packit Service 82fcde
    return -1;
Packit Service 82fcde
Packit Service 82fcde
  *result = startp + startp[1] + 2;
Packit Service 82fcde
Packit Service 82fcde
  return 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static uint8_t *option_alloc (struct cmsghdr *cmsg, int datalen, int multx,
Packit Service 82fcde
			      int plusy);
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* RFC 2292, 6.3.1
Packit Service 82fcde
Packit Service 82fcde
   This function returns the number of bytes required to hold an option
Packit Service 82fcde
   when it is stored as ancillary data, including the cmsghdr structure
Packit Service 82fcde
   at the beginning, and any padding at the end (to make its size a
Packit Service 82fcde
   multiple of 8 bytes).  The argument is the size of the structure
Packit Service 82fcde
   defining the option, which must include any pad bytes at the
Packit Service 82fcde
   beginning (the value y in the alignment term "xn + y"), the type
Packit Service 82fcde
   byte, the length byte, and the option data.  */
Packit Service 82fcde
int
Packit Service 82fcde
inet6_option_space (int nbytes)
Packit Service 82fcde
{
Packit Service 82fcde
  /* Add room for the extension header.  */
Packit Service 82fcde
  nbytes += sizeof (struct ip6_ext);
Packit Service 82fcde
Packit Service 82fcde
  return CMSG_SPACE (roundup (nbytes, 8));
Packit Service 82fcde
}
Packit Service 82fcde
link_warning (inet6_option_space,
Packit Service 82fcde
	      "inet6_option_space is obsolete, use the RFC 3542 interfaces")
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* RFC 2292, 6.3.2
Packit Service 82fcde
Packit Service 82fcde
   This function is called once per ancillary data object that will
Packit Service 82fcde
   contain either Hop-by-Hop or Destination options.  It returns 0 on
Packit Service 82fcde
   success or -1 on an error.  */
Packit Service 82fcde
int
Packit Service 82fcde
inet6_option_init (void *bp, struct cmsghdr **cmsgp, int type)
Packit Service 82fcde
{
Packit Service 82fcde
  /* Only Hop-by-Hop or Destination options allowed.  */
Packit Service 82fcde
  if (type != IPV6_HOPOPTS && type != IPV6_DSTOPTS)
Packit Service 82fcde
    return -1;
Packit Service 82fcde
Packit Service 82fcde
  /* BP is a pointer to the previously allocated space.  */
Packit Service 82fcde
  struct cmsghdr *newp = (struct cmsghdr *) bp;
Packit Service 82fcde
Packit Service 82fcde
  /* Initialize the message header.
Packit Service 82fcde
Packit Service 82fcde
     Length: No data yet, only the cmsghdr struct.  */
Packit Service 82fcde
  newp->cmsg_len = CMSG_LEN (0);
Packit Service 82fcde
  /* Originating protocol: obviously IPv6.  */
Packit Service 82fcde
  newp->cmsg_level = IPPROTO_IPV6;
Packit Service 82fcde
  /* Message type.  */
Packit Service 82fcde
  newp->cmsg_type = type;
Packit Service 82fcde
Packit Service 82fcde
  /* Pass up the result.  */
Packit Service 82fcde
  *cmsgp = newp;
Packit Service 82fcde
Packit Service 82fcde
  return 0;
Packit Service 82fcde
}
Packit Service 82fcde
link_warning (inet6_option_init,
Packit Service 82fcde
	      "inet6_option_init is obsolete, use the RFC 3542 interfaces")
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* RFC 2292, 6.3.3
Packit Service 82fcde
Packit Service 82fcde
   This function appends a Hop-by-Hop option or a Destination option
Packit Service 82fcde
   into an ancillary data object that has been initialized by
Packit Service 82fcde
   inet6_option_init().  This function returns 0 if it succeeds or -1 on
Packit Service 82fcde
   an error.  */
Packit Service 82fcde
int
Packit Service 82fcde
inet6_option_append (struct cmsghdr *cmsg, const uint8_t *typep, int multx,
Packit Service 82fcde
		     int plusy)
Packit Service 82fcde
{
Packit Service 82fcde
  /* typep is a pointer to the 8-bit option type.  It is assumed that this
Packit Service 82fcde
     field is immediately followed by the 8-bit option data length field,
Packit Service 82fcde
     which is then followed immediately by the option data.
Packit Service 82fcde
Packit Service 82fcde
     The option types IP6OPT_PAD1 and IP6OPT_PADN also must be handled.  */
Packit Service 82fcde
  int len = typep[0] == IP6OPT_PAD1 ? 1 : typep[1] + 2;
Packit Service 82fcde
Packit Service 82fcde
  /* Get the pointer to the space in the message.  */
Packit Service 82fcde
  uint8_t *ptr = option_alloc (cmsg, len, multx, plusy);
Packit Service 82fcde
  if (ptr == NULL)
Packit Service 82fcde
    /* Some problem with the parameters.  */
Packit Service 82fcde
    return -1;
Packit Service 82fcde
Packit Service 82fcde
  /* Copy the content.  */
Packit Service 82fcde
  memcpy (ptr, typep, len);
Packit Service 82fcde
Packit Service 82fcde
  return 0;
Packit Service 82fcde
}
Packit Service 82fcde
link_warning (inet6_option_append,
Packit Service 82fcde
	      "inet6_option_append is obsolete, use the RFC 3542 interfaces")
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* RFC 2292, 6.3.4
Packit Service 82fcde
Packit Service 82fcde
   This function appends a Hop-by-Hop option or a Destination option
Packit Service 82fcde
   into an ancillary data object that has been initialized by
Packit Service 82fcde
   inet6_option_init().  This function returns a pointer to the 8-bit
Packit Service 82fcde
   option type field that starts the option on success, or NULL on an
Packit Service 82fcde
   error.  */
Packit Service 82fcde
static uint8_t *
Packit Service 82fcde
option_alloc (struct cmsghdr *cmsg, int datalen, int multx, int plusy)
Packit Service 82fcde
{
Packit Service 82fcde
  /* The RFC limits the value of the alignment values.  */
Packit Service 82fcde
  if ((multx != 1 && multx != 2 && multx != 4 && multx != 8)
Packit Service 82fcde
      || ! (plusy >= 0 && plusy <= 7))
Packit Service 82fcde
    return NULL;
Packit Service 82fcde
Packit Service 82fcde
  /* Current data size.  */
Packit Service 82fcde
  int dsize = cmsg->cmsg_len - CMSG_LEN (0);
Packit Service 82fcde
Packit Service 82fcde
  /* The first two bytes of the option are for the extended header.  */
Packit Service 82fcde
  if (__glibc_unlikely (dsize == 0))
Packit Service 82fcde
    {
Packit Service 82fcde
      cmsg->cmsg_len += sizeof (struct ip6_ext);
Packit Service 82fcde
      dsize = sizeof (struct ip6_ext);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* First add padding.  */
Packit Service 82fcde
  add_pad (cmsg, ((multx - (dsize & (multx - 1))) & (multx - 1)) + plusy);
Packit Service 82fcde
Packit Service 82fcde
  /* Return the pointer to the start of the option space.  */
Packit Service 82fcde
  uint8_t *result = CMSG_DATA (cmsg) + cmsg->cmsg_len - CMSG_LEN (0);
Packit Service 82fcde
  cmsg->cmsg_len += datalen;
Packit Service 82fcde
Packit Service 82fcde
  /* The extended option header length is measured in 8-byte groups.
Packit Service 82fcde
     To represent the current length we might have to add padding.  */
Packit Service 82fcde
  dsize = cmsg->cmsg_len - CMSG_LEN (0);
Packit Service 82fcde
  add_pad (cmsg, (8 - (dsize & (8 - 1))) & (8 - 1));
Packit Service 82fcde
Packit Service 82fcde
  /* Record the new length of the option.  */
Packit Service 82fcde
  assert (((cmsg->cmsg_len - CMSG_LEN (0)) % 8) == 0);
Packit Service 82fcde
  int len8b = (cmsg->cmsg_len - CMSG_LEN (0)) / 8 - 1;
Packit Service 82fcde
  if (len8b >= 256)
Packit Service 82fcde
    /* Too long.  */
Packit Service 82fcde
    return NULL;
Packit Service 82fcde
Packit Service 82fcde
  struct ip6_ext *ie = (void *) CMSG_DATA (cmsg);
Packit Service 82fcde
  ie->ip6e_len = len8b;
Packit Service 82fcde
Packit Service 82fcde
  return result;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
uint8_t *
Packit Service 82fcde
inet6_option_alloc (struct cmsghdr *cmsg, int datalen, int multx, int plusy)
Packit Service 82fcde
{
Packit Service 82fcde
  return option_alloc (cmsg, datalen, multx, plusy);
Packit Service 82fcde
}
Packit Service 82fcde
link_warning (inet6_option_alloc,
Packit Service 82fcde
	      "inet6_option_alloc is obsolete, use the RFC 3542 interfaces")
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* RFC 2292, 6.3.5
Packit Service 82fcde
Packit Service 82fcde
   This function processes the next Hop-by-Hop option or Destination
Packit Service 82fcde
   option in an ancillary data object.  If another option remains to be
Packit Service 82fcde
   processed, the return value of the function is 0 and *tptrp points to
Packit Service 82fcde
   the 8-bit option type field (which is followed by the 8-bit option
Packit Service 82fcde
   data length, followed by the option data).  If no more options remain
Packit Service 82fcde
   to be processed, the return value is -1 and *tptrp is NULL.  If an
Packit Service 82fcde
   error occurs, the return value is -1 and *tptrp is not NULL.  */
Packit Service 82fcde
int
Packit Service 82fcde
inet6_option_next (const struct cmsghdr *cmsg, uint8_t **tptrp)
Packit Service 82fcde
{
Packit Service 82fcde
  /* Make sure it is an option of the right type.  */
Packit Service 82fcde
  if (cmsg->cmsg_level != IPPROTO_IPV6
Packit Service 82fcde
      || (cmsg->cmsg_type != IPV6_HOPOPTS && cmsg->cmsg_type != IPV6_DSTOPTS))
Packit Service 82fcde
    return -1;
Packit Service 82fcde
Packit Service 82fcde
  /* Pointer to the extension header.  We only compute the address, we
Packit Service 82fcde
     don't access anything yet.  */
Packit Service 82fcde
  const struct ip6_ext *ip6e = (const struct ip6_ext *) CMSG_DATA (cmsg);
Packit Service 82fcde
Packit Service 82fcde
  /* Make sure the message is long enough.  */
Packit Service 82fcde
  if (cmsg->cmsg_len < CMSG_LEN (sizeof (struct ip6_ext))
Packit Service 82fcde
      /* Now we can access the extension header.  */
Packit Service 82fcde
      || cmsg->cmsg_len < CMSG_LEN ((ip6e->ip6e_len + 1) * 8))
Packit Service 82fcde
    /* Too small.  */
Packit Service 82fcde
    return -1;
Packit Service 82fcde
Packit Service 82fcde
  /* Determine the address of the byte past the message.  */
Packit Service 82fcde
  const uint8_t *endp = CMSG_DATA (cmsg) + (ip6e->ip6e_len + 1) * 8;
Packit Service 82fcde
Packit Service 82fcde
  const uint8_t *result;
Packit Service 82fcde
  if (*tptrp == NULL)
Packit Service 82fcde
    /* This is the first call, return the first option if there is one.  */
Packit Service 82fcde
    result = (const uint8_t *) (ip6e + 1);
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Make sure *TPTRP points to a beginning of a new option in
Packit Service 82fcde
	 the message.  The upper limit is checked in get_opt_end.  */
Packit Service 82fcde
      if (*tptrp < (const uint8_t *) (ip6e + 1))
Packit Service 82fcde
	return -1;
Packit Service 82fcde
Packit Service 82fcde
      /* Get the beginning of the next option.  */
Packit Service 82fcde
      if (get_opt_end (&result, *tptrp, endp) != 0)
Packit Service 82fcde
	return -1;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* We know where the next option starts.  */
Packit Service 82fcde
  *tptrp = (uint8_t *) result;
Packit Service 82fcde
Packit Service 82fcde
  /* Check the option is fully represented in the message.  */
Packit Service 82fcde
  return get_opt_end (&result, result, endp);
Packit Service 82fcde
}
Packit Service 82fcde
link_warning (inet6_option_next,
Packit Service 82fcde
	      "inet6_option_next is obsolete, use the RFC 3542 interfaces")
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* RFC 2292, 6.3.6
Packit Service 82fcde
Packit Service 82fcde
   This function is similar to the previously described
Packit Service 82fcde
   inet6_option_next() function, except this function lets the caller
Packit Service 82fcde
   specify the option type to be searched for, instead of always
Packit Service 82fcde
   returning the next option in the ancillary data object.  cmsg is a
Packit Service 82fcde
   pointer to cmsghdr structure of which cmsg_level equals IPPROTO_IPV6
Packit Service 82fcde
   and cmsg_type equals either IPV6_HOPOPTS or IPV6_DSTOPTS.  */
Packit Service 82fcde
int
Packit Service 82fcde
inet6_option_find (const struct cmsghdr *cmsg, uint8_t **tptrp, int type)
Packit Service 82fcde
{
Packit Service 82fcde
  /* Make sure it is an option of the right type.  */
Packit Service 82fcde
  if (cmsg->cmsg_level != IPPROTO_IPV6
Packit Service 82fcde
      || (cmsg->cmsg_type != IPV6_HOPOPTS && cmsg->cmsg_type != IPV6_DSTOPTS))
Packit Service 82fcde
    return -1;
Packit Service 82fcde
Packit Service 82fcde
  /* Pointer to the extension header.  We only compute the address, we
Packit Service 82fcde
     don't access anything yet.  */
Packit Service 82fcde
  const struct ip6_ext *ip6e = (const struct ip6_ext *) CMSG_DATA (cmsg);
Packit Service 82fcde
Packit Service 82fcde
  /* Make sure the message is long enough.  */
Packit Service 82fcde
  if (cmsg->cmsg_len < CMSG_LEN (sizeof (struct ip6_ext))
Packit Service 82fcde
      /* Now we can access the extension header.  */
Packit Service 82fcde
      || cmsg->cmsg_len < CMSG_LEN ((ip6e->ip6e_len + 1) * 8))
Packit Service 82fcde
    /* Too small.  */
Packit Service 82fcde
    return -1;
Packit Service 82fcde
Packit Service 82fcde
  /* Determine the address of the byte past the message.  */
Packit Service 82fcde
  const uint8_t *endp = CMSG_DATA (cmsg) + (ip6e->ip6e_len + 1) * 8;
Packit Service 82fcde
Packit Service 82fcde
  const uint8_t *next;
Packit Service 82fcde
  if (*tptrp == NULL)
Packit Service 82fcde
    /* This is the first call, return the first option if there is one.  */
Packit Service 82fcde
    next = (const uint8_t *) (ip6e + 1);
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Make sure *TPTRP points to a beginning of a new option in
Packit Service 82fcde
	 the message.  The upper limit is checked in get_opt_end.  */
Packit Service 82fcde
      if (*tptrp < (const uint8_t *) (ip6e + 1))
Packit Service 82fcde
	return -1;
Packit Service 82fcde
Packit Service 82fcde
      /* Get the beginning of the next option.  */
Packit Service 82fcde
      if (get_opt_end (&next, *tptrp, endp) != 0)
Packit Service 82fcde
	return -1;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Now search for the appropriate typed entry.  */
Packit Service 82fcde
  const uint8_t *result;
Packit Service 82fcde
  do
Packit Service 82fcde
    {
Packit Service 82fcde
      result = next;
Packit Service 82fcde
Packit Service 82fcde
      /* Get the end of this entry.  */
Packit Service 82fcde
      if (get_opt_end (&next, result, endp) != 0)
Packit Service 82fcde
	return -1;
Packit Service 82fcde
    }
Packit Service 82fcde
  while (*result != type);
Packit Service 82fcde
Packit Service 82fcde
  /* We know where the next option starts.  */
Packit Service 82fcde
  *tptrp = (uint8_t *) result;
Packit Service 82fcde
Packit Service 82fcde
  /* Success.  */
Packit Service 82fcde
  return 0;
Packit Service 82fcde
}
Packit Service 82fcde
link_warning (inet6_option_find,
Packit Service 82fcde
	      "inet6_option_find is obsolete, use the RFC 3542 interfaces")