LCOV - code coverage report
Current view: top level - util/net - fd_ip6.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 189 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 8 0.0 %

          Line data    Source code
       1             : #include "fd_ip6.h"
       2             : #include "fd_ip4.h"
       3             : #include "../fd_util.h"
       4             : 
       5             : #include <net/if.h> /* if_nametoindex */
       6             : 
       7             : /* hex_val returns the value of a hex digit, or -1 if c is not a hex
       8             :    digit. */
       9             : 
      10             : static int
      11           0 : hex_val( int c ) {
      12           0 :   if( c>='0' && c<='9' ) return c-'0';
      13           0 :   if( c>='a' && c<='f' ) return c-'a'+10;
      14           0 :   if( c>='A' && c<='F' ) return c-'A'+10;
      15           0 :   return -1;
      16           0 : }
      17             : 
      18             : /* ip4_parse parses a dotted quad in [s,end).  Returns 1 on success and
      19             :    stores the address in network byte order to out, otherwise 0. */
      20             : 
      21             : static int
      22             : ip4_parse( char const * s,
      23             :            char const * end,
      24           0 :            uint *       out ) {
      25           0 :   uchar octet[ 4 ];
      26           0 :   for( ulong i=0UL; i<4UL; i++ ) {
      27           0 :     if( i ) {
      28           0 :       if( s>=end || *s!='.' ) return 0;
      29           0 :       s++;
      30           0 :     }
      31           0 :     uint val = 0U;
      32           0 :     int  dig = 0;
      33           0 :     int  lead_zero = ( s<end && *s=='0' );
      34           0 :     while( s<end && *s>='0' && *s<='9' ) {
      35           0 :       val = val*10U + (uint)( *s-'0' );
      36           0 :       s++;
      37           0 :       if( ++dig>3 ) return 0;
      38           0 :     }
      39             :     /* Reject leading zeroes, which are ambiguous (octal or decimal?) */
      40           0 :     if( !dig || val>255U || ( lead_zero && dig>1 ) ) return 0;
      41           0 :     octet[ i ] = (uchar)val;
      42           0 :   }
      43           0 :   if( s!=end ) return 0;
      44           0 :   memcpy( out, octet, 4UL );
      45           0 :   return 1;
      46           0 : }
      47             : 
      48             : /* ip6_parse parses an IPv6 address literal (RFC 4291 section 2.2) in
      49             :    [s,end), including the zero compression ("::") and embedded IPv4
      50             :    ("::ffff:1.2.3.4") forms.  Returns 1 on success and stores the
      51             :    address in network byte order to out, otherwise 0. */
      52             : 
      53             : static int
      54             : ip6_parse( char const * s,
      55             :            char const * end,
      56           0 :            uchar        out[ 16 ] ) {
      57           0 :   uchar addr[ 16 ] = {0};
      58           0 :   int   cnt        = 0;  /* number of 16-bit groups parsed */
      59           0 :   int   gap        = -1; /* group index at which "::" appeared, or -1 */
      60             : 
      61           0 :   if( s>=end ) return 0;
      62             : 
      63           0 :   if( *s==':' ) { /* leading "::" (the only way an address may start with ':') */
      64           0 :     s++;
      65           0 :     if( s>=end || *s!=':' ) return 0;
      66           0 :     s++;
      67           0 :     gap = 0;
      68           0 :   }
      69             : 
      70           0 :   int expect_sep = 0;
      71           0 :   while( s<end ) {
      72             : 
      73           0 :     if( expect_sep ) {
      74           0 :       if( *s!=':' ) return 0;
      75           0 :       s++;
      76           0 :       if( s<end && *s==':' ) { /* "::" */
      77           0 :         if( gap>=0 ) return 0; /* at most one gap */
      78           0 :         s++;
      79           0 :         gap        = cnt;
      80           0 :         expect_sep = 0;
      81           0 :         continue;
      82           0 :       }
      83           0 :       if( s>=end ) return 0; /* trailing ':' */
      84           0 :     }
      85             : 
      86             :     /* Find the end of this group and check whether it is an embedded
      87             :        IPv4 address */
      88             : 
      89           0 :     char const * tok_end = s;
      90           0 :     int          is_ip4  = 0;
      91           0 :     while( tok_end<end && *tok_end!=':' ) {
      92           0 :       is_ip4 |= ( *tok_end=='.' );
      93           0 :       tok_end++;
      94           0 :     }
      95             : 
      96           0 :     if( is_ip4 ) {
      97           0 :       uint ip4;
      98           0 :       if( cnt>6 || tok_end!=end ) return 0; /* must be the last two groups */
      99           0 :       if( !ip4_parse( s, tok_end, &ip4 ) ) return 0;
     100           0 :       memcpy( addr+2*cnt, &ip4, 4UL );
     101           0 :       cnt += 2;
     102           0 :       s    = tok_end;
     103           0 :       break;
     104           0 :     }
     105             : 
     106           0 :     uint val = 0U;
     107           0 :     int  dig = 0;
     108           0 :     while( s<end && hex_val( *s )>=0 ) {
     109           0 :       val = ( val<<4 ) | (uint)hex_val( *s );
     110           0 :       s++;
     111           0 :       if( ++dig>4 ) return 0;
     112           0 :     }
     113           0 :     if( !dig || cnt>7 ) return 0;
     114           0 :     addr[ 2*cnt   ] = (uchar)( val>>8 );
     115           0 :     addr[ 2*cnt+1 ] = (uchar)( val    );
     116           0 :     cnt++;
     117           0 :     expect_sep = 1;
     118             : 
     119           0 :   }
     120             : 
     121           0 :   if( gap<0 ) {
     122           0 :     if( cnt!=8 ) return 0;
     123           0 :   } else {
     124           0 :     if( cnt>=8 ) return 0; /* "::" must cover at least one group */
     125             :     /* Move the groups that follow the gap to the end of the address */
     126           0 :     int tail = cnt-gap;
     127           0 :     if( tail ) memmove( addr+16-2*tail, addr+2*gap, (ulong)( 2*tail ) );
     128           0 :     memset( addr+2*gap, 0, (ulong)( 16-2*gap-2*tail ) );
     129           0 :   }
     130             : 
     131           0 :   memcpy( out, addr, 16UL );
     132           0 :   return 1;
     133           0 : }
     134             : 
     135             : /* zone_parse resolves a zone ID (interface name or numeric interface
     136             :    index) in [s,end) to an interface index.  Returns 0 on failure. */
     137             : 
     138             : static uint
     139             : zone_parse( char const * s,
     140           0 :             char const * end ) {
     141           0 :   if( s>=end ) return 0U;
     142             : 
     143           0 :   int numeric = 1;
     144           0 :   for( char const * p=s; p<end; p++ ) numeric &= ( *p>='0' && *p<='9' );
     145             : 
     146           0 :   if( numeric ) {
     147           0 :     ulong idx = 0UL;
     148           0 :     for( char const * p=s; p<end; p++ ) {
     149           0 :       idx = idx*10UL + (ulong)( *p-'0' );
     150           0 :       if( idx>UINT_MAX ) return 0U;
     151           0 :     }
     152           0 :     return (uint)idx;
     153           0 :   }
     154             : 
     155           0 :   if( (ulong)( end-s )>=IF_NAMESIZE ) return 0U;
     156           0 :   char name[ IF_NAMESIZE ];
     157           0 :   memcpy( name, s, (ulong)( end-s ) );
     158           0 :   name[ end-s ] = '\0';
     159           0 :   return if_nametoindex( name ); /* 0 if no such interface */
     160           0 : }
     161             : 
     162             : int
     163             : fd_cstr_to_ip6_addr( char const *    s,
     164           0 :                      fd_ip6_addr_t * out ) {
     165             : 
     166             :   /* Split off the zone ID, if any */
     167             : 
     168           0 :   char const * end  = s;
     169           0 :   char const * zone = NULL;
     170           0 :   while( *end ) {
     171           0 :     if( *end=='%' ) { zone = end+1; break; }
     172           0 :     end++;
     173           0 :   }
     174             : 
     175           0 :   fd_ip6_addr_t addr = {0};
     176           0 :   if( FD_UNLIKELY( !ip6_parse( s, end, addr.addr ) ) ) return 0;
     177             : 
     178           0 :   if( zone ) {
     179             :     /* A zone ID only identifies an interface for addresses that are not
     180             :        globally scoped.  Reject it elsewhere, as the kernel would
     181             :        silently ignore it. */
     182           0 :     if( FD_UNLIKELY( !fd_ip6_addr_is_scoped( addr.addr ) ) ) return 0;
     183           0 :     char const * zone_end = zone;
     184           0 :     while( *zone_end ) zone_end++;
     185           0 :     addr.scope_id = zone_parse( zone, zone_end );
     186           0 :     if( FD_UNLIKELY( !addr.scope_id ) ) return 0;
     187           0 :   }
     188             : 
     189           0 :   *out = addr;
     190           0 :   return 1;
     191           0 : }
     192             : 
     193             : int
     194             : fd_cstr_to_ip46_addr( char const *    s,
     195           0 :                       fd_ip6_addr_t * out ) {
     196           0 :   uint ip4_addr;
     197           0 :   if( fd_cstr_to_ip4_addr( s, &ip4_addr ) ) {
     198           0 :     fd_ip6_addr_t addr = {0};
     199           0 :     fd_ip6_addr_ip4_mapped( addr.addr, ip4_addr );
     200           0 :     *out = addr;
     201           0 :     return 1;
     202           0 :   }
     203           0 :   return fd_cstr_to_ip6_addr( s, out );
     204           0 : }
     205             : 
     206             : /* ip4_cstr appends a dotted quad to p */
     207             : 
     208             : static char *
     209             : ip4_cstr( char *        p,
     210           0 :           uchar const * octet ) {
     211           0 :   for( ulong i=0UL; i<4UL; i++ ) {
     212           0 :     if( i ) p = fd_cstr_append_char( p, '.' );
     213           0 :     p = fd_cstr_append_printf( p, "%u", (uint)octet[ i ] );
     214           0 :   }
     215           0 :   return p;
     216           0 : }
     217             : 
     218             : char *
     219             : fd_ip6_addr_cstr( char                  buf[ static FD_IP6_ADDR_CSTR_MAX ],
     220           0 :                   fd_ip6_addr_t const * addr ) {
     221           0 :   uchar const * a = addr->addr;
     222           0 :   int           ip4 = fd_ip6_addr_is_ip4_mapped( a );
     223             : 
     224             :   /* An IPv4 address prints as a dotted quad */
     225             : 
     226           0 :   if( ip4 && !addr->scope_id ) {
     227           0 :     char * p = fd_cstr_init( buf );
     228           0 :     p = ip4_cstr( p, a+12 );
     229           0 :     fd_cstr_fini( p );
     230           0 :     return buf;
     231           0 :   }
     232             : 
     233           0 :   ushort group[ 8 ];
     234           0 :   for( ulong i=0UL; i<8UL; i++ ) group[ i ] = (ushort)( ( (uint)a[ 2*i ]<<8 ) | a[ 2*i+1 ] );
     235             : 
     236             :   /* Find the longest run of zero groups to compress, leftmost run
     237             :      winning ties.  Runs of a single group are not compressed
     238             :      (RFC 5952 section 4.2). */
     239             : 
     240           0 :   int gap = -1, gap_len = 1;
     241           0 :   for( int i=0; i<8; ) {
     242           0 :     if( group[ i ] ) { i++; continue; }
     243           0 :     int j = i;
     244           0 :     while( j<8 && !group[ j ] ) j++;
     245           0 :     if( j-i>gap_len ) { gap = i; gap_len = j-i; }
     246           0 :     i = j;
     247           0 :   }
     248             : 
     249           0 :   char * p     = fd_cstr_init( buf );
     250           0 :   int    colon = 0;
     251           0 :   p = fd_cstr_append_char( p, '[' );
     252           0 :   for( int i=0; i<8; ) {
     253           0 :     if( i==gap ) {
     254           0 :       p     = fd_cstr_append_text( p, "::", 2UL );
     255           0 :       i    += gap_len;
     256           0 :       colon = 0;
     257           0 :       continue;
     258           0 :     }
     259           0 :     if( colon ) p = fd_cstr_append_char( p, ':' );
     260           0 :     if( ip4 && i==6 ) { /* IPv4-mapped addresses use the mixed notation (RFC 5952 section 5) */
     261           0 :       p = ip4_cstr( p, a+12 );
     262           0 :       break;
     263           0 :     }
     264           0 :     p     = fd_cstr_append_printf( p, "%x", (uint)group[ i ] );
     265           0 :     colon = 1;
     266           0 :     i++;
     267           0 :   }
     268             : 
     269           0 :   if( addr->scope_id ) p = fd_cstr_append_printf( p, "%%%u", addr->scope_id );
     270           0 :   p = fd_cstr_append_char( p, ']' );
     271           0 :   fd_cstr_fini( p );
     272             : 
     273           0 :   return buf;
     274           0 : }

Generated by: LCOV version 1.14