LCOV - code coverage report
Current view: top level - util/net - fd_ip6.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 189 189 100.0 %
Date: 2026-08-14 04:54:57 Functions: 8 8 100.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        2994 : hex_val( int c ) {
      12        2994 :   if( c>='0' && c<='9' ) return c-'0';
      13        1134 :   if( c>='a' && c<='f' ) return c-'a'+10;
      14         582 :   if( c>='A' && c<='F' ) return c-'A'+10;
      15         558 :   return -1;
      16         582 : }
      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          66 :            uint *       out ) {
      25          66 :   uchar octet[ 4 ];
      26         306 :   for( ulong i=0UL; i<4UL; i++ ) {
      27         261 :     if( i ) {
      28         195 :       if( s>=end || *s!='.' ) return 0;
      29         186 :       s++;
      30         186 :     }
      31         252 :     uint val = 0U;
      32         252 :     int  dig = 0;
      33         252 :     int  lead_zero = ( s<end && *s=='0' );
      34         579 :     while( s<end && *s>='0' && *s<='9' ) {
      35         327 :       val = val*10U + (uint)( *s-'0' );
      36         327 :       s++;
      37         327 :       if( ++dig>3 ) return 0;
      38         327 :     }
      39             :     /* Reject leading zeroes, which are ambiguous (octal or decimal?) */
      40         252 :     if( !dig || val>255U || ( lead_zero && dig>1 ) ) return 0;
      41         240 :     octet[ i ] = (uchar)val;
      42         240 :   }
      43          45 :   if( s!=end ) return 0;
      44          39 :   memcpy( out, octet, 4UL );
      45          39 :   return 1;
      46          45 : }
      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         261 :            uchar        out[ 16 ] ) {
      57         261 :   uchar addr[ 16 ] = {0};
      58         261 :   int   cnt        = 0;  /* number of 16-bit groups parsed */
      59         261 :   int   gap        = -1; /* group index at which "::" appeared, or -1 */
      60             : 
      61         261 :   if( s>=end ) return 0;
      62             : 
      63         249 :   if( *s==':' ) { /* leading "::" (the only way an address may start with ':') */
      64          90 :     s++;
      65          90 :     if( s>=end || *s!=':' ) return 0;
      66          84 :     s++;
      67          84 :     gap = 0;
      68          84 :   }
      69             : 
      70         243 :   int expect_sep = 0;
      71         957 :   while( s<end ) {
      72             : 
      73         837 :     if( expect_sep ) {
      74         534 :       if( *s!=':' ) return 0;
      75         531 :       s++;
      76         531 :       if( s<end && *s==':' ) { /* "::" */
      77          84 :         if( gap>=0 ) return 0; /* at most one gap */
      78          78 :         s++;
      79          78 :         gap        = cnt;
      80          78 :         expect_sep = 0;
      81          78 :         continue;
      82          84 :       }
      83         447 :       if( s>=end ) return 0; /* trailing ':' */
      84         447 :     }
      85             : 
      86             :     /* Find the end of this group and check whether it is an embedded
      87             :        IPv4 address */
      88             : 
      89         744 :     char const * tok_end = s;
      90         744 :     int          is_ip4  = 0;
      91        2619 :     while( tok_end<end && *tok_end!=':' ) {
      92        1875 :       is_ip4 |= ( *tok_end=='.' );
      93        1875 :       tok_end++;
      94        1875 :     }
      95             : 
      96         744 :     if( is_ip4 ) {
      97          75 :       uint ip4;
      98          75 :       if( cnt>6 || tok_end!=end ) return 0; /* must be the last two groups */
      99          66 :       if( !ip4_parse( s, tok_end, &ip4 ) ) return 0;
     100          39 :       memcpy( addr+2*cnt, &ip4, 4UL );
     101          39 :       cnt += 2;
     102          39 :       s    = tok_end;
     103          39 :       break;
     104          66 :     }
     105             : 
     106         669 :     uint val = 0U;
     107         669 :     int  dig = 0;
     108        1881 :     while( s<end && hex_val( *s )>=0 ) {
     109        1218 :       val = ( val<<4 ) | (uint)hex_val( *s );
     110        1218 :       s++;
     111        1218 :       if( ++dig>4 ) return 0;
     112        1218 :     }
     113         663 :     if( !dig || cnt>7 ) return 0;
     114         636 :     addr[ 2*cnt   ] = (uchar)( val>>8 );
     115         636 :     addr[ 2*cnt+1 ] = (uchar)( val    );
     116         636 :     cnt++;
     117         636 :     expect_sep = 1;
     118             : 
     119         636 :   }
     120             : 
     121         159 :   if( gap<0 ) {
     122          42 :     if( cnt!=8 ) return 0;
     123         117 :   } else {
     124         117 :     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         111 :     int tail = cnt-gap;
     127         111 :     if( tail ) memmove( addr+16-2*tail, addr+2*gap, (ulong)( 2*tail ) );
     128         111 :     memset( addr+2*gap, 0, (ulong)( 16-2*gap-2*tail ) );
     129         111 :   }
     130             : 
     131         138 :   memcpy( out, addr, 16UL );
     132         138 :   return 1;
     133         159 : }
     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          30 :             char const * end ) {
     141          30 :   if( s>=end ) return 0U;
     142             : 
     143          27 :   int numeric = 1;
     144         105 :   for( char const * p=s; p<end; p++ ) numeric &= ( *p>='0' && *p<='9' );
     145             : 
     146          27 :   if( numeric ) {
     147          12 :     ulong idx = 0UL;
     148          48 :     for( char const * p=s; p<end; p++ ) {
     149          39 :       idx = idx*10UL + (ulong)( *p-'0' );
     150          39 :       if( idx>UINT_MAX ) return 0U;
     151          39 :     }
     152           9 :     return (uint)idx;
     153          12 :   }
     154             : 
     155          15 :   if( (ulong)( end-s )>=IF_NAMESIZE ) return 0U;
     156          15 :   char name[ IF_NAMESIZE ];
     157          15 :   memcpy( name, s, (ulong)( end-s ) );
     158          15 :   name[ end-s ] = '\0';
     159          15 :   return if_nametoindex( name ); /* 0 if no such interface */
     160          15 : }
     161             : 
     162             : int
     163             : fd_cstr_to_ip6_addr( char const *    s,
     164         261 :                      fd_ip6_addr_t * out ) {
     165             : 
     166             :   /* Split off the zone ID, if any */
     167             : 
     168         261 :   char const * end  = s;
     169         261 :   char const * zone = NULL;
     170        3051 :   while( *end ) {
     171        2835 :     if( *end=='%' ) { zone = end+1; break; }
     172        2790 :     end++;
     173        2790 :   }
     174             : 
     175         261 :   fd_ip6_addr_t addr = {0};
     176         261 :   if( FD_UNLIKELY( !ip6_parse( s, end, addr.addr ) ) ) return 0;
     177             : 
     178         138 :   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          36 :     if( FD_UNLIKELY( !fd_ip6_addr_is_scoped( addr.addr ) ) ) return 0;
     183          30 :     char const * zone_end = zone;
     184         108 :     while( *zone_end ) zone_end++;
     185          30 :     addr.scope_id = zone_parse( zone, zone_end );
     186          30 :     if( FD_UNLIKELY( !addr.scope_id ) ) return 0;
     187          30 :   }
     188             : 
     189         117 :   *out = addr;
     190         117 :   return 1;
     191         138 : }
     192             : 
     193             : int
     194             : fd_cstr_to_ip46_addr( char const *    s,
     195          18 :                       fd_ip6_addr_t * out ) {
     196          18 :   uint ip4_addr;
     197          18 :   if( fd_cstr_to_ip4_addr( s, &ip4_addr ) ) {
     198           9 :     fd_ip6_addr_t addr = {0};
     199           9 :     fd_ip6_addr_ip4_mapped( addr.addr, ip4_addr );
     200           9 :     *out = addr;
     201           9 :     return 1;
     202           9 :   }
     203           9 :   return fd_cstr_to_ip6_addr( s, out );
     204          18 : }
     205             : 
     206             : /* ip4_cstr appends a dotted quad to p */
     207             : 
     208             : static char *
     209             : ip4_cstr( char *        p,
     210          12 :           uchar const * octet ) {
     211          60 :   for( ulong i=0UL; i<4UL; i++ ) {
     212          48 :     if( i ) p = fd_cstr_append_char( p, '.' );
     213          48 :     p = fd_cstr_append_printf( p, "%u", (uint)octet[ i ] );
     214          48 :   }
     215          12 :   return p;
     216          12 : }
     217             : 
     218             : char *
     219             : fd_ip6_addr_cstr( char                  buf[ static FD_IP6_ADDR_CSTR_MAX ],
     220          51 :                   fd_ip6_addr_t const * addr ) {
     221          51 :   uchar const * a = addr->addr;
     222          51 :   int           ip4 = fd_ip6_addr_is_ip4_mapped( a );
     223             : 
     224             :   /* An IPv4 address prints as a dotted quad */
     225             : 
     226          51 :   if( ip4 && !addr->scope_id ) {
     227           9 :     char * p = fd_cstr_init( buf );
     228           9 :     p = ip4_cstr( p, a+12 );
     229           9 :     fd_cstr_fini( p );
     230           9 :     return buf;
     231           9 :   }
     232             : 
     233          42 :   ushort group[ 8 ];
     234         378 :   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          42 :   int gap = -1, gap_len = 1;
     241         213 :   for( int i=0; i<8; ) {
     242         171 :     if( group[ i ] ) { i++; continue; }
     243          45 :     int j = i;
     244         255 :     while( j<8 && !group[ j ] ) j++;
     245          45 :     if( j-i>gap_len ) { gap = i; gap_len = j-i; }
     246          45 :     i = j;
     247          45 :   }
     248             : 
     249          42 :   char * p     = fd_cstr_init( buf );
     250          42 :   int    colon = 0;
     251          42 :   p = fd_cstr_append_char( p, '[' );
     252         213 :   for( int i=0; i<8; ) {
     253         174 :     if( i==gap ) {
     254          36 :       p     = fd_cstr_append_text( p, "::", 2UL );
     255          36 :       i    += gap_len;
     256          36 :       colon = 0;
     257          36 :       continue;
     258          36 :     }
     259         138 :     if( colon ) p = fd_cstr_append_char( p, ':' );
     260         138 :     if( ip4 && i==6 ) { /* IPv4-mapped addresses use the mixed notation (RFC 5952 section 5) */
     261           3 :       p = ip4_cstr( p, a+12 );
     262           3 :       break;
     263           3 :     }
     264         135 :     p     = fd_cstr_append_printf( p, "%x", (uint)group[ i ] );
     265         135 :     colon = 1;
     266         135 :     i++;
     267         135 :   }
     268             : 
     269          42 :   if( addr->scope_id ) p = fd_cstr_append_printf( p, "%%%u", addr->scope_id );
     270          42 :   p = fd_cstr_append_char( p, ']' );
     271          42 :   fd_cstr_fini( p );
     272             : 
     273          42 :   return buf;
     274          51 : }

Generated by: LCOV version 1.14