(root)/
glibc-2.38/
inet/
inet6_rth.c
       1  /* Copyright (C) 2006-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <string.h>
      19  #include <netinet/in.h>
      20  #include <netinet/ip6.h>
      21  
      22  
      23  /* RFC 3542, 7.1
      24  
      25     This function returns the number of bytes required to hold a
      26     Routing header of the specified type containing the specified
      27     number of segments (addresses).  For an IPv6 Type 0 Routing header,
      28     the number of segments must be between 0 and 127, inclusive.  */
      29  socklen_t
      30  inet6_rth_space (int type, int segments)
      31  {
      32    switch (type)
      33      {
      34      case IPV6_RTHDR_TYPE_0:
      35        if (segments < 0 || segments > 127)
      36  	return 0;
      37  
      38        return sizeof (struct ip6_rthdr0) + segments * sizeof (struct in6_addr);
      39      }
      40  
      41    return 0;
      42  }
      43  
      44  
      45  /* RFC 3542, 7.2
      46  
      47     This function initializes the buffer pointed to by BP to contain a
      48     Routing header of the specified type and sets ip6r_len based on the
      49     segments parameter.  */
      50  void *
      51  inet6_rth_init (void *bp, socklen_t bp_len, int type, int segments)
      52  {
      53    struct ip6_rthdr *rthdr = (struct ip6_rthdr *) bp;
      54  
      55    switch (type)
      56      {
      57      case IPV6_RTHDR_TYPE_0:
      58        /* Make sure the parameters are valid and the buffer is large enough.  */
      59        if (segments < 0 || segments > 127)
      60  	break;
      61  
      62        socklen_t len = (sizeof (struct ip6_rthdr0)
      63  		       + segments * sizeof (struct in6_addr));
      64        if (len > bp_len)
      65  	break;
      66  
      67        /* Some implementations seem to initialize the whole memory area.  */
      68        memset (bp, '\0', len);
      69  
      70        /* Length in units of 8 octets.  */
      71        rthdr->ip6r_len = segments * sizeof (struct in6_addr) / 8;
      72        rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
      73        return bp;
      74      }
      75  
      76    return NULL;
      77  }
      78  
      79  
      80  /* RFC 3542, 7.3
      81  
      82     This function adds the IPv6 address pointed to by addr to the end of
      83     the Routing header being constructed.  */
      84  int
      85  inet6_rth_add (void *bp, const struct in6_addr *addr)
      86  {
      87    struct ip6_rthdr *rthdr = (struct ip6_rthdr *) bp;
      88  
      89    switch (rthdr->ip6r_type)
      90      {
      91        struct ip6_rthdr0 *rthdr0;
      92      case IPV6_RTHDR_TYPE_0:
      93        rthdr0 = (struct ip6_rthdr0 *) rthdr;
      94        if (rthdr0->ip6r0_len * 8 / sizeof (struct in6_addr)
      95  	  - rthdr0->ip6r0_segleft < 1)
      96          return -1;
      97  
      98        memcpy (&rthdr0->ip6r0_addr[rthdr0->ip6r0_segleft++],
      99  	      addr, sizeof (struct in6_addr));
     100  
     101        return 0;
     102      }
     103  
     104    return -1;
     105  }
     106  
     107  
     108  /* RFC 3542, 7.4
     109  
     110     This function takes a Routing header extension header (pointed to by
     111     the first argument) and writes a new Routing header that sends
     112     datagrams along the reverse of that route.  The function reverses the
     113     order of the addresses and sets the segleft member in the new Routing
     114     header to the number of segments.  */
     115  int
     116  inet6_rth_reverse (const void *in, void *out)
     117  {
     118    struct ip6_rthdr *in_rthdr = (struct ip6_rthdr *) in;
     119  
     120    switch (in_rthdr->ip6r_type)
     121      {
     122        struct ip6_rthdr0 *in_rthdr0;
     123        struct ip6_rthdr0 *out_rthdr0;
     124      case IPV6_RTHDR_TYPE_0:
     125        in_rthdr0 = (struct ip6_rthdr0 *) in;
     126        out_rthdr0 = (struct ip6_rthdr0 *) out;
     127  
     128        /* Copy header, not the addresses.  The memory regions can overlap.  */
     129        memmove (out_rthdr0, in_rthdr0, sizeof (struct ip6_rthdr0));
     130  
     131        int total = in_rthdr0->ip6r0_len * 8 / sizeof (struct in6_addr);
     132        for (int i = 0; i < total / 2; ++i)
     133  	{
     134  	  /* Remember, IN_RTHDR0 and OUT_RTHDR0 might overlap.  */
     135  	  struct in6_addr temp = in_rthdr0->ip6r0_addr[i];
     136  	  out_rthdr0->ip6r0_addr[i] = in_rthdr0->ip6r0_addr[total - 1 - i];
     137  	  out_rthdr0->ip6r0_addr[total - 1 - i] = temp;
     138  	}
     139        if (total % 2 != 0 && in != out)
     140  	out_rthdr0->ip6r0_addr[total / 2] = in_rthdr0->ip6r0_addr[total / 2];
     141  
     142        out_rthdr0->ip6r0_segleft = total;
     143  
     144        return 0;
     145      }
     146  
     147    return -1;
     148  }
     149  
     150  
     151  /* RFC 3542, 7.5
     152  
     153     This function returns the number of segments (addresses) contained in
     154     the Routing header described by BP.  */
     155  int
     156  inet6_rth_segments (const void *bp)
     157  {
     158    struct ip6_rthdr *rthdr = (struct ip6_rthdr *) bp;
     159  
     160    switch (rthdr->ip6r_type)
     161      {
     162      case IPV6_RTHDR_TYPE_0:
     163  
     164        return rthdr->ip6r_len * 8 / sizeof (struct in6_addr);
     165      }
     166  
     167    return -1;
     168  }
     169  
     170  
     171  /* RFC 3542, 7.6
     172  
     173     This function returns a pointer to the IPv6 address specified by
     174     index (which must have a value between 0 and one less than the
     175     value returned by 'inet6_rth_segments') in the Routing header
     176     described by BP.  */
     177  struct in6_addr *
     178  inet6_rth_getaddr (const void *bp, int index)
     179  {
     180    struct ip6_rthdr *rthdr = (struct ip6_rthdr *) bp;
     181  
     182    switch (rthdr->ip6r_type)
     183      {
     184         struct ip6_rthdr0 *rthdr0;
     185      case IPV6_RTHDR_TYPE_0:
     186        rthdr0 = (struct ip6_rthdr0 *) rthdr;
     187  
     188        if (index >= rthdr0->ip6r0_len * 8 / sizeof (struct in6_addr))
     189  	break;
     190  
     191        return &rthdr0->ip6r0_addr[index];
     192      }
     193  
     194    return NULL;
     195  }