LCOV - code coverage report
Current view: top level - waltz/ip - fd_ip.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 126 222 56.8 %
Date: 2024-11-13 11:58:15 Functions: 11 17 64.7 %

          Line data    Source code
       1             : #include "fd_ip.h"
       2             : 
       3             : #include <arpa/inet.h>
       4             : 
       5             : ulong
       6           6 : fd_ip_align( void ) {
       7           6 :   return FD_IP_ALIGN;
       8           6 : }
       9             : 
      10             : 
      11             : ulong
      12             : fd_ip_footprint( ulong arp_entries,
      13           6 :                  ulong route_entries ) {
      14             : 
      15             :   /* use 256 as a default */
      16           6 :   if( arp_entries   == 0 ) arp_entries   = 256;
      17           6 :   if( route_entries == 0 ) route_entries = 256;
      18             : 
      19           6 :   ulong l;
      20             : 
      21           6 :   l = FD_LAYOUT_INIT;
      22           6 :   l = FD_LAYOUT_APPEND( l, FD_IP_ALIGN, sizeof(fd_ip_t)                                 );
      23           6 :   l = FD_LAYOUT_APPEND( l, FD_IP_ALIGN, sizeof(fd_nl_t)                                 );
      24             :   /* allocate enough space for two of each kind of table */
      25           6 :   l = FD_LAYOUT_APPEND( l, FD_IP_ALIGN, 2 * arp_entries   * sizeof(fd_nl_arp_entry_t)   );
      26           6 :   l = FD_LAYOUT_APPEND( l, FD_IP_ALIGN, 2 * route_entries * sizeof(fd_nl_route_entry_t) );
      27             : 
      28           6 :   return FD_LAYOUT_FINI( l, FD_IP_ALIGN );
      29           6 : }
      30             : 
      31             : 
      32             : void *
      33             : fd_ip_new( void * shmem,
      34             :            ulong  arp_entries,
      35           3 :            ulong  route_entries ) {
      36           3 :   if( !fd_ulong_is_aligned( (ulong)shmem, FD_IP_ALIGN ) ) {
      37           0 :     FD_LOG_ERR(( "Attempt to fd_ip_new with unaligned memory" ));
      38           0 :     return NULL;
      39           0 :   }
      40             : 
      41             :   /* use 256 as a default */
      42           3 :   if( arp_entries   == 0 ) arp_entries   = 256;
      43           3 :   if( route_entries == 0 ) route_entries = 256;
      44             : 
      45           3 :   ulong l;
      46           3 :   uchar * mem = (uchar*)shmem;
      47             : 
      48           3 :   l = FD_LAYOUT_INIT;
      49             : 
      50           3 :   fd_ip_t * ip = (fd_ip_t*)mem;
      51           3 :   l = FD_LAYOUT_APPEND( l, FD_IP_ALIGN, sizeof(fd_ip_t)                             );
      52             : 
      53           3 :   ulong ofs_netlink = l;
      54           3 :   l = FD_LAYOUT_APPEND( l, FD_IP_ALIGN, sizeof(fd_nl_t)                             );
      55             : 
      56           3 :   ulong ofs_arp_table   = FD_ULONG_ALIGN_UP( l, FD_IP_ALIGN );
      57           3 :   l = FD_LAYOUT_APPEND( l, FD_IP_ALIGN, 2 * arp_entries   * sizeof(fd_nl_arp_entry_t)   );
      58             : 
      59           3 :   ulong ofs_route_table = FD_ULONG_ALIGN_UP( l, FD_IP_ALIGN );
      60           3 :   l = FD_LAYOUT_APPEND( l, FD_IP_ALIGN, 2 * route_entries * sizeof(fd_nl_route_entry_t) );
      61             : 
      62           3 :   ulong mem_sz = FD_LAYOUT_FINI( l, FD_IP_ALIGN );
      63             : 
      64             :   /* clear all to zero */
      65           3 :   fd_memset( ip, 0, mem_sz );
      66             : 
      67             :   /* set values in ip */
      68           3 :   ip->num_arp_entries      = arp_entries;
      69           3 :   ip->num_route_entries    = route_entries;
      70           3 :   ip->ofs_netlink          = ofs_netlink;
      71           3 :   ip->ofs_arp_table        = ofs_arp_table;
      72           3 :   ip->ofs_route_table      = ofs_route_table;
      73           3 :   ip->arp_table_idx        = 0;
      74           3 :   ip->route_table_idx      = 0;
      75             : 
      76             :   /* set magic last, after a fence */
      77           3 :   FD_COMPILER_MFENCE();
      78           3 :   ip->magic                = FD_IP_MAGIC;
      79             : 
      80           3 :   return (void*)ip;
      81           3 : }
      82             : 
      83             : 
      84             : fd_ip_t *
      85           3 : fd_ip_join( void * mem ) {
      86           3 :   if( !mem ) {
      87           0 :     FD_LOG_ERR(( "Attempt to fd_ip_join a NULL" ));
      88           0 :     return NULL;
      89           0 :   }
      90             : 
      91           3 :   if( !fd_ulong_is_aligned( (ulong)mem, FD_IP_ALIGN ) ) {
      92           0 :     FD_LOG_ERR(( "Attempt to fd_ip_join with unaligned memory" ));
      93           0 :     return NULL;
      94           0 :   }
      95             : 
      96           3 :   fd_ip_t * ip = (fd_ip_t*)mem;
      97             : 
      98           3 :   if( ip->magic != FD_IP_MAGIC ) {
      99           0 :     FD_LOG_ERR(( "Failed to fd_ip_join. Possibly memory corrupt" ));
     100           0 :     return NULL;
     101           0 :   }
     102             : 
     103             :   /* initialize netlink */
     104           3 :   fd_nl_t * netlink = fd_ip_netlink_get( ip );
     105           3 :   if( fd_nl_init( netlink, 0 ) ) {
     106           0 :     FD_LOG_ERR(( "Failed to initialize fd_netlink." ));
     107           0 :     return NULL;
     108           0 :   }
     109             : 
     110           3 :   return ip;
     111           3 : }
     112             : 
     113             : 
     114             : void *
     115           3 : fd_ip_leave( fd_ip_t * ip ) {
     116           3 :   if( !ip ) {
     117           0 :     return NULL;
     118           0 :   }
     119             : 
     120             :   /* clear out the magic first */
     121           3 :   ip->magic = 0;
     122             : 
     123             :   /* then fence */
     124           3 :   FD_COMPILER_MFENCE();
     125             : 
     126             :   /* finalize the netlink */
     127           3 :   fd_nl_t * netlink = fd_ip_netlink_get( ip );
     128           3 :   fd_nl_fini( netlink );
     129             : 
     130           3 :   fd_memset( ip, 0, sizeof( *ip ) );
     131             : 
     132           3 :   return (void*)ip;
     133           3 : }
     134             : 
     135             : 
     136             : /* get pointer to fd_nl_t */
     137             : fd_nl_t *
     138           6 : fd_ip_netlink_get( fd_ip_t * ip ) {
     139           6 :   ulong mem = (ulong)ip;
     140             : 
     141           6 :   return (fd_nl_t*)( mem + ip->ofs_netlink );
     142           6 : }
     143             : 
     144             : 
     145             : /* get pointer to start of current routing table */
     146             : fd_ip_route_entry_t *
     147         129 : fd_ip_route_table_get( fd_ip_t * ip ) {
     148         129 :   ulong mem = (ulong)ip;
     149             : 
     150             :   /* find the first table from the offset */
     151         129 :   fd_ip_route_entry_t * first_table = (fd_ip_route_entry_t*)( mem + ip->ofs_route_table );
     152             : 
     153             :   /* find the table index */
     154         129 :   uint idx = ip->route_table_idx;
     155             : 
     156             :   /* offset to the current table */
     157         129 :   return first_table + ( idx * ip->num_route_entries );
     158         129 : }
     159             : 
     160             : 
     161             : /* get pointer to start of alternate routing table */
     162             : fd_ip_route_entry_t *
     163           0 : fd_ip_route_table_get_alt( fd_ip_t * ip ) {
     164           0 :   ulong mem = (ulong)ip;
     165             : 
     166             :   /* find the first table from the offset */
     167           0 :   fd_ip_route_entry_t * first_table = (fd_ip_route_entry_t*)( mem + ip->ofs_route_table );
     168             : 
     169             :   /* find the table index of the alternate table */
     170           0 :   uint idx = !ip->route_table_idx;
     171             : 
     172             :   /* offset to the alternate table */
     173           0 :   return first_table + ( idx * ip->num_route_entries );
     174           0 : }
     175             : 
     176             : 
     177             : /* get pointer to start of current arp table */
     178             : fd_ip_arp_entry_t *
     179         117 : fd_ip_arp_table_get( fd_ip_t * ip ) {
     180         117 :   ulong mem = (ulong)ip;
     181             : 
     182             :   /* find the table from the offset */
     183         117 :   fd_ip_arp_entry_t * first_table = (fd_ip_arp_entry_t*)( mem + ip->ofs_arp_table );
     184             : 
     185             :   /* find the table index */
     186         117 :   uint idx = ip->arp_table_idx;
     187             : 
     188             :   /* offset to the current table */
     189         117 :   return first_table + ( idx * ip->num_arp_entries );
     190         117 : }
     191             : 
     192             : 
     193             : /* get pointer to start of alternate arp table */
     194             : fd_ip_arp_entry_t *
     195           0 : fd_ip_arp_table_get_alt( fd_ip_t * ip ) {
     196           0 :   ulong mem = (ulong)ip;
     197             : 
     198             :   /* find the table from the offset */
     199           0 :   fd_ip_arp_entry_t * first_table = (fd_ip_arp_entry_t*)( mem + ip->ofs_arp_table );
     200             : 
     201             :   /* find the table index */
     202           0 :   uint idx = !ip->arp_table_idx;
     203             : 
     204             :   /* offset to the alternate table */
     205           0 :   return first_table + ( idx * ip->num_arp_entries );
     206           0 : }
     207             : 
     208             : 
     209             : void
     210           0 : fd_ip_arp_fetch( fd_ip_t * ip ) {
     211             :   /* we fetch into a temp space. If it is successful we switch temp and current */
     212             : 
     213             :   /* get pointer to alt table */
     214           0 :   fd_ip_arp_entry_t * alt_arp_table = fd_ip_arp_table_get_alt( ip );
     215           0 :   ulong               arp_table_cap = ip->num_arp_entries;
     216           0 :   fd_nl_t *           netlink       = fd_ip_netlink_get( ip );
     217             : 
     218           0 :   long num_entries = fd_nl_load_arp_table( netlink, alt_arp_table, arp_table_cap );
     219             : 
     220           0 :   if( num_entries <= 0L ) {
     221             :     /* don't switch */
     222           0 :     return;
     223           0 :   }
     224             : 
     225             :   /* success - switch to other table */
     226           0 :   ip->arp_table_idx      ^= 1;
     227           0 :   ip->cur_num_arp_entries = (ulong)num_entries;
     228           0 : }
     229             : 
     230             : 
     231             : /* query an arp entry
     232             : 
     233             :    searches for an IP address in the table
     234             : 
     235             :    if found, the resulting data is written into the destination and the function
     236             :        returns FD_IP_SUCCESS
     237             : 
     238             :    otherwise, the function returns FD_IP_ERROR */
     239             : 
     240             : int
     241             : fd_ip_arp_query( fd_ip_t *            ip,
     242             :                  fd_ip_arp_entry_t ** arp,
     243         114 :                  uint                 ip_addr ) {
     244         114 :   fd_ip_arp_entry_t * arp_table     = fd_ip_arp_table_get( ip );
     245         114 :   ulong               arp_table_sz  = ip->cur_num_arp_entries;
     246             : 
     247         114 :   fd_ip_arp_entry_t * entry = fd_nl_arp_query( arp_table, arp_table_sz, ip_addr );
     248         114 :   if( FD_UNLIKELY( !entry ) ) return FD_IP_ERROR;
     249             : 
     250          96 :   *arp = entry;
     251             : 
     252          96 :   return FD_IP_SUCCESS;
     253         114 : }
     254             : 
     255             : 
     256             : /* generate a raw ARP packet
     257             : 
     258             :    used for caller to generate an ARP packet to send in the event
     259             :      we don't have an existing ARP entry
     260             : 
     261             :    writes ARP packet into dest
     262             : 
     263             :    if successful, returns FD_IP_SUCCESS
     264             : 
     265             :    if unable to generate ARP, if the dest capacity (dest_cap) is not enough space
     266             :      then the function returns FD_IP_ERROR */
     267             : 
     268             : int
     269             : fd_ip_arp_gen_arp_probe( uchar *         buf,
     270             :                          ulong           buf_cap,
     271             :                          ulong *         arp_len,
     272             :                          uint            dst_ip_addr,
     273             :                          uint            src_ip_addr,
     274           0 :                          uchar const *   src_mac_addr ) {
     275           0 :   if( buf_cap < sizeof( fd_ip_arp_t ) ) {
     276           0 :     return FD_IP_ERROR;
     277           0 :   }
     278             : 
     279             :   /* convert ip_addr */
     280           0 :   uint net_dst_ip_addr = htonl( dst_ip_addr );
     281           0 :   uint net_src_ip_addr = htonl( src_ip_addr );
     282             : 
     283           0 :   fd_ip_arp_t * arp = (fd_ip_arp_t*)buf;
     284             : 
     285           0 :   fd_memset( arp->dst_mac_addr, 0xff, 6 );         /* set broadcast */
     286           0 :   fd_memcpy( arp->src_mac_addr, src_mac_addr, 6 ); /* source mac address */
     287             : 
     288           0 :   arp->ethtype        = htons( 0x0806 );       /* Ethertype - ARP is 0x0806 */
     289           0 :   arp->hw_type        = htons( 1 );            /* Ethernet is 1 */
     290           0 :   arp->proto_type     = htons( 0x0800 );       /* IP is 0x0800 */
     291           0 :   arp->hw_addr_len    = 6;                     /* hardware address length - ethernet is 6 */
     292           0 :   arp->proto_addr_len = 4;                     /* protocol address length - IPv4 is 4 */
     293           0 :   arp->op             = htons( 1 );            /* operation - request is 1 */
     294             : 
     295           0 :   fd_memcpy( arp->sender_hw_addr,    src_mac_addr,     6 ); /* sender hardware address */
     296           0 :   fd_memcpy( arp->sender_proto_addr, &net_src_ip_addr, 4 ); /* sender protocol (IPv4) address */
     297             : 
     298           0 :   fd_memset( arp->target_hw_addr,    0,                6 ); /* target hardware address - ignored for request */
     299           0 :   fd_memcpy( arp->target_proto_addr, &net_dst_ip_addr, 4 ); /* target protocol (IPv4) address - ignored for request */
     300             : 
     301           0 :   if( arp_len ) *arp_len = sizeof( *arp );
     302             : 
     303           0 :   return FD_IP_SUCCESS;
     304           0 : }
     305             : 
     306             : 
     307             : /* fetch the routing table from the kernel
     308             : 
     309             :    the routing table will be written into the workspace, completely replacing
     310             :    any existing routing entries */
     311             : 
     312             : void
     313           0 : fd_ip_route_fetch( fd_ip_t * ip ) {
     314           0 :   fd_ip_route_entry_t * alt_route_table = fd_ip_route_table_get_alt( ip );
     315           0 :   ulong                 route_table_cap = ip->num_route_entries;
     316           0 :   fd_nl_t *             netlink         = fd_ip_netlink_get( ip );
     317             : 
     318           0 :   long num_entries = fd_nl_load_route_table( netlink, alt_route_table, route_table_cap );
     319             : 
     320           0 :   if( FD_UNLIKELY( num_entries < 0 ) ) {
     321             :     /* as a workaround for some systems that return EBUSY, but succeed
     322             :      * immediately after, we will retry once */
     323           0 :     num_entries = fd_nl_load_route_table( netlink, alt_route_table, route_table_cap );
     324           0 :   }
     325             : 
     326           0 :   if( FD_UNLIKELY( num_entries <= 0L ) ) {
     327             :     /* don't switch */
     328           0 :     return;
     329           0 :   }
     330             : 
     331             :   /* switch to new table */
     332           0 :   ip->route_table_idx      ^= 1U;
     333           0 :   ip->cur_num_route_entries = (ulong)num_entries;
     334           0 : }
     335             : 
     336             : /* query the routing table
     337             : 
     338             :    the provided IP address is looked up in the routing table
     339             : 
     340             :    if an appropriate entry is found, the details are written into
     341             :      the destination and FD_IP_SUCCESS is returned
     342             : 
     343             :    otherwise, FD_IP_ERROR is returned */
     344             : 
     345             : int
     346             : fd_ip_route_query( fd_ip_t *              ip,
     347             :                    fd_ip_route_entry_t ** route,
     348         126 :                    uint                   ip_addr ) {
     349         126 :   fd_ip_route_entry_t * route_table     = fd_ip_route_table_get( ip );
     350         126 :   ulong                 route_table_cap = ip->num_route_entries;
     351             : 
     352         126 :   if( FD_UNLIKELY( route_table_cap == 0 ) ) return FD_IP_RETRY;
     353             : 
     354         126 :   fd_ip_route_entry_t * entry = fd_nl_route_query( route_table, route_table_cap, ip_addr );
     355             : 
     356         126 :   if( FD_UNLIKELY( !entry ) ) return FD_IP_NO_ROUTE;
     357             : 
     358         114 :   *route = entry;
     359             : 
     360         114 :   return FD_IP_SUCCESS;
     361         126 : }
     362             : 
     363             : 
     364             : int
     365             : fd_ip_route_ip_addr( uchar *   out_dst_mac,
     366             :                      uint *    out_next_ip_addr,
     367             :                      uint *    out_ifindex,
     368             :                      fd_ip_t * ip,
     369         132 :                      uint      ip_addr ) {
     370             :   /* handle broadcasts and multicast */
     371             : 
     372             :   /* multicast is 224.0.0.0/4 */
     373         132 :   if( ( ip_addr & 0xf0000000 ) == 0xe0000000 ) {
     374             :     /* multicast */
     375             : 
     376             :     /* map to ethernet space */
     377           6 :     out_dst_mac[0] = 0x01;
     378           6 :     out_dst_mac[1] = 0x00;
     379           6 :     out_dst_mac[2] = 0x5e;
     380           6 :     out_dst_mac[3] = (uchar)( ( ip_addr >> 020 ) & 0x7fU );
     381           6 :     out_dst_mac[4] = (uchar)( ( ip_addr >> 010 ) & 0xffU );
     382           6 :     out_dst_mac[5] = (uchar)( ( ip_addr >> 000 ) & 0xffU );
     383             : 
     384           6 :     return FD_IP_MULTICAST;
     385           6 :   }
     386             : 
     387         126 :   if( ip_addr == 0xffffffff ) {
     388             :     /* broadcast */
     389           0 :     out_dst_mac[0] = 0xffU;
     390           0 :     out_dst_mac[1] = 0xffU;
     391           0 :     out_dst_mac[2] = 0xffU;
     392           0 :     out_dst_mac[3] = 0xffU;
     393           0 :     out_dst_mac[4] = 0xffU;
     394           0 :     out_dst_mac[5] = 0xffU;
     395             : 
     396           0 :     return FD_IP_BROADCAST;
     397           0 :   }
     398             : 
     399             :   /* query routing table */
     400         126 :   fd_ip_route_entry_t * route_entry = NULL;
     401         126 :   int route_rtn = fd_ip_route_query( ip, &route_entry, ip_addr );
     402         126 :   if( route_rtn != FD_IP_SUCCESS ) {
     403          12 :     return route_rtn; /* no routing entry */
     404          12 :   }
     405             : 
     406             :   /* routing entry found */
     407             : 
     408         114 :   uint next_ip_addr = ip_addr; /* assume local */
     409             : 
     410             :   /* which ip address to use?
     411             :        if the routing entry has a gateway, use that */
     412         114 :   if( route_entry->nh_ip_addr ) {
     413          78 :     next_ip_addr = route_entry->nh_ip_addr; /* use next hop */
     414          78 :   } else {
     415          36 :     next_ip_addr = ip_addr;
     416          36 :   }
     417             : 
     418             :   /* have a next IP address, so look up ARP table */
     419             : 
     420             :   /* set out_next_ip_addr and out_ifindex */
     421         114 :   *out_next_ip_addr = next_ip_addr;
     422         114 :   *out_ifindex      = route_entry->oif;
     423             : 
     424             :   /* query ARP table */
     425         114 :   fd_ip_arp_entry_t * arp_entry = NULL;
     426         114 :   int arp_rtn = fd_ip_arp_query( ip, &arp_entry, next_ip_addr );
     427         114 :   if( arp_rtn != FD_IP_SUCCESS ) {
     428          18 :     return FD_IP_PROBE_RQD; /* no entry, so send probe */
     429          18 :   }
     430             : 
     431             :   /* arp entry found - store mac addr in out_dst_mac */
     432          96 :   fd_memcpy( out_dst_mac, arp_entry->mac_addr, 6 );
     433             : 
     434             :   /* check the status */
     435             :   /* both NUD_REACHABLE and NUD_PERMANENT are acceptable */
     436          96 :   uint accept_states = (uint)NUD_REACHABLE | (uint)NUD_PERMANENT;
     437          96 :   if( FD_LIKELY( (uint)arp_entry->state & accept_states ) ) return FD_IP_SUCCESS;
     438             : 
     439             :   /* all other statutes, try probing */
     440           0 :   return FD_IP_PROBE_RQD;
     441          96 : }
     442             : 
     443             : 
     444             : int
     445             : fd_ip_update_arp_table( fd_ip_t * ip,
     446             :                         uint      ip_addr,
     447           0 :                         uint      ifindex ) {
     448           0 :   fd_nl_t * netlink = fd_ip_netlink_get( ip );
     449             : 
     450             :   /* ensure the table is up-to-date */
     451           0 :   fd_ip_arp_fetch( ip );
     452             : 
     453             :   /* query the table */
     454           0 :   fd_ip_arp_entry_t * arp = NULL;
     455             : 
     456             :   /* if entry already exists, return success */
     457           0 :   if( fd_ip_arp_query( ip, &arp, ip_addr ) == FD_IP_SUCCESS ) return FD_IP_SUCCESS;
     458             : 
     459           0 :   return fd_nl_update_arp_table( netlink,
     460           0 :                                  fd_ip_arp_table_get(ip),
     461           0 :                                  ip->num_arp_entries,
     462           0 :                                  ip_addr,
     463           0 :                                  ifindex );
     464           0 : }

Generated by: LCOV version 1.14