Blame inet/inet6_rth.c

Packit 6c4009
/* Copyright (C) 2006-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2006.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <netinet/in.h>
Packit 6c4009
#include <netinet/ip6.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* RFC 3542, 7.1
Packit 6c4009
Packit 6c4009
   This function returns the number of bytes required to hold a
Packit 6c4009
   Routing header of the specified type containing the specified
Packit 6c4009
   number of segments (addresses).  For an IPv6 Type 0 Routing header,
Packit 6c4009
   the number of segments must be between 0 and 127, inclusive.  */
Packit 6c4009
socklen_t
Packit 6c4009
inet6_rth_space (int type, int segments)
Packit 6c4009
{
Packit 6c4009
  switch (type)
Packit 6c4009
    {
Packit 6c4009
    case IPV6_RTHDR_TYPE_0:
Packit 6c4009
      if (segments < 0 || segments > 127)
Packit 6c4009
	return 0;
Packit 6c4009
Packit 6c4009
      return sizeof (struct ip6_rthdr0) + segments * sizeof (struct in6_addr);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* RFC 3542, 7.2
Packit 6c4009
Packit 6c4009
   This function initializes the buffer pointed to by BP to contain a
Packit 6c4009
   Routing header of the specified type and sets ip6r_len based on the
Packit 6c4009
   segments parameter.  */
Packit 6c4009
void *
Packit 6c4009
inet6_rth_init (void *bp, socklen_t bp_len, int type, int segments)
Packit 6c4009
{
Packit 6c4009
  struct ip6_rthdr *rthdr = (struct ip6_rthdr *) bp;
Packit 6c4009
Packit 6c4009
  switch (type)
Packit 6c4009
    {
Packit 6c4009
    case IPV6_RTHDR_TYPE_0:
Packit 6c4009
      /* Make sure the parameters are valid and the buffer is large enough.  */
Packit 6c4009
      if (segments < 0 || segments > 127)
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      socklen_t len = (sizeof (struct ip6_rthdr0)
Packit 6c4009
		       + segments * sizeof (struct in6_addr));
Packit 6c4009
      if (len > bp_len)
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      /* Some implementations seem to initialize the whole memory area.  */
Packit 6c4009
      memset (bp, '\0', len);
Packit 6c4009
Packit 6c4009
      /* Length in units of 8 octets.  */
Packit 6c4009
      rthdr->ip6r_len = segments * sizeof (struct in6_addr) / 8;
Packit 6c4009
      rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
Packit 6c4009
      return bp;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* RFC 3542, 7.3
Packit 6c4009
Packit 6c4009
   This function adds the IPv6 address pointed to by addr to the end of
Packit 6c4009
   the Routing header being constructed.  */
Packit 6c4009
int
Packit 6c4009
inet6_rth_add (void *bp, const struct in6_addr *addr)
Packit 6c4009
{
Packit 6c4009
  struct ip6_rthdr *rthdr = (struct ip6_rthdr *) bp;
Packit 6c4009
Packit 6c4009
  switch (rthdr->ip6r_type)
Packit 6c4009
    {
Packit 6c4009
      struct ip6_rthdr0 *rthdr0;
Packit 6c4009
    case IPV6_RTHDR_TYPE_0:
Packit 6c4009
      rthdr0 = (struct ip6_rthdr0 *) rthdr;
Packit 6c4009
      if (rthdr0->ip6r0_len * 8 / sizeof (struct in6_addr)
Packit 6c4009
	  - rthdr0->ip6r0_segleft < 1)
Packit 6c4009
        return -1;
Packit 6c4009
Packit 6c4009
      memcpy (&rthdr0->ip6r0_addr[rthdr0->ip6r0_segleft++],
Packit 6c4009
	      addr, sizeof (struct in6_addr));
Packit 6c4009
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return -1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* RFC 3542, 7.4
Packit 6c4009
Packit 6c4009
   This function takes a Routing header extension header (pointed to by
Packit 6c4009
   the first argument) and writes a new Routing header that sends
Packit 6c4009
   datagrams along the reverse of that route.  The function reverses the
Packit 6c4009
   order of the addresses and sets the segleft member in the new Routing
Packit 6c4009
   header to the number of segments.  */
Packit 6c4009
int
Packit 6c4009
inet6_rth_reverse (const void *in, void *out)
Packit 6c4009
{
Packit 6c4009
  struct ip6_rthdr *in_rthdr = (struct ip6_rthdr *) in;
Packit 6c4009
Packit 6c4009
  switch (in_rthdr->ip6r_type)
Packit 6c4009
    {
Packit 6c4009
      struct ip6_rthdr0 *in_rthdr0;
Packit 6c4009
      struct ip6_rthdr0 *out_rthdr0;
Packit 6c4009
    case IPV6_RTHDR_TYPE_0:
Packit 6c4009
      in_rthdr0 = (struct ip6_rthdr0 *) in;
Packit 6c4009
      out_rthdr0 = (struct ip6_rthdr0 *) out;
Packit 6c4009
Packit 6c4009
      /* Copy header, not the addresses.  The memory regions can overlap.  */
Packit 6c4009
      memmove (out_rthdr0, in_rthdr0, sizeof (struct ip6_rthdr0));
Packit 6c4009
Packit 6c4009
      int total = in_rthdr0->ip6r0_len * 8 / sizeof (struct in6_addr);
Packit 6c4009
      for (int i = 0; i < total / 2; ++i)
Packit 6c4009
	{
Packit 6c4009
	  /* Remember, IN_RTHDR0 and OUT_RTHDR0 might overlap.  */
Packit 6c4009
	  struct in6_addr temp = in_rthdr0->ip6r0_addr[i];
Packit 6c4009
	  out_rthdr0->ip6r0_addr[i] = in_rthdr0->ip6r0_addr[total - 1 - i];
Packit 6c4009
	  out_rthdr0->ip6r0_addr[total - 1 - i] = temp;
Packit 6c4009
	}
Packit 6c4009
      if (total % 2 != 0 && in != out)
Packit 6c4009
	out_rthdr0->ip6r0_addr[total / 2] = in_rthdr0->ip6r0_addr[total / 2];
Packit 6c4009
Packit 6c4009
      out_rthdr0->ip6r0_segleft = total;
Packit 6c4009
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return -1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* RFC 3542, 7.5
Packit 6c4009
Packit 6c4009
   This function returns the number of segments (addresses) contained in
Packit 6c4009
   the Routing header described by BP.  */
Packit 6c4009
int
Packit 6c4009
inet6_rth_segments (const void *bp)
Packit 6c4009
{
Packit 6c4009
  struct ip6_rthdr *rthdr = (struct ip6_rthdr *) bp;
Packit 6c4009
Packit 6c4009
  switch (rthdr->ip6r_type)
Packit 6c4009
    {
Packit 6c4009
    case IPV6_RTHDR_TYPE_0:
Packit 6c4009
Packit 6c4009
      return rthdr->ip6r_len * 8 / sizeof (struct in6_addr);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return -1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* RFC 3542, 7.6
Packit 6c4009
Packit 6c4009
   This function returns a pointer to the IPv6 address specified by
Packit 6c4009
   index (which must have a value between 0 and one less than the
Packit 6c4009
   value returned by 'inet6_rth_segments') in the Routing header
Packit 6c4009
   described by BP.  */
Packit 6c4009
struct in6_addr *
Packit 6c4009
inet6_rth_getaddr (const void *bp, int index)
Packit 6c4009
{
Packit 6c4009
  struct ip6_rthdr *rthdr = (struct ip6_rthdr *) bp;
Packit 6c4009
Packit 6c4009
  switch (rthdr->ip6r_type)
Packit 6c4009
    {
Packit 6c4009
       struct ip6_rthdr0 *rthdr0;
Packit 6c4009
    case IPV6_RTHDR_TYPE_0:
Packit 6c4009
      rthdr0 = (struct ip6_rthdr0 *) rthdr;
Packit 6c4009
Packit 6c4009
      if (index >= rthdr0->ip6r0_len * 8 / sizeof (struct in6_addr))
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      return &rthdr0->ip6r0_addr[index];
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return NULL;
Packit 6c4009
}