LCOV - code coverage report
Current view: top level - disco/net - fd_net_router.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 108 116 93.1 %
Date: 2026-09-17 04:28:31 Functions: 5 10 50.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_disco_net_fd_net_router_h
       2             : #define HEADER_fd_src_disco_net_fd_net_router_h
       3             : 
       4             : /* fd_net_router.h provides an internal API for userland routing. */
       5             : 
       6             : #include "../../waltz/ip/fd_fib4.h"
       7             : #include "../../waltz/mib/fd_netdev_tbl.h"
       8             : #include "../../waltz/neigh/fd_neigh4_map.h"
       9             : #include "../netlink/fd_netlink_tile.h" /* neigh4_solicit */
      10             : #include "../../util/net/fd_eth.h"
      11             : #include "../../util/net/fd_ip4.h"
      12             : 
      13             : #include <linux/if_arp.h> /* ARPHRD_LOOPBACK */
      14             : 
      15          24 : #define FD_NET_ROUTE_FAIL_NO_ROUTE              (0U)
      16          24 : #define FD_NET_ROUTE_FAIL_ROUTE_TYPE            (1U)
      17          12 : #define FD_NET_ROUTE_FAIL_MISSING_INTERFACE     (2U)
      18           6 : #define FD_NET_ROUTE_FAIL_SOURCE_IP             (3U)
      19          12 : #define FD_NET_ROUTE_FAIL_UNSUPPORTED_INTERFACE (4U)
      20             : #define FD_NET_ROUTE_FAIL_CNT                   (5U)
      21             : 
      22          60 : #define FD_NET_TX_FILL_INVALID   (-1)
      23           6 : #define FD_NET_TX_FILL_NO_SOURCE ( 0)
      24         132 : #define FD_NET_TX_FILL_OK        ( 1)
      25             : 
      26             : struct fd_net_router {
      27             :   /* Route and neighbor tables */
      28             :   fd_fib4_t fib_local[1];
      29             :   fd_fib4_t fib_main[1];
      30             :   fd_neigh4_hmap_t  neigh4[1];
      31             :   fd_netlink_neigh4_solicit_link_t neigh4_solicit[1];
      32             : 
      33             :   /* Netdev table */
      34             :   fd_netdev_tbl_join_t netdev_tbl;    /* local copy in scratch */
      35             :   fd_netdev_tbl_join_t netdev_shared; /* shared seqlock-protected table */
      36             : 
      37             :   uint if_virt;
      38             :   uint bind_address;
      39             :   uint default_address;
      40             : 
      41             :   struct {
      42             :     ulong tx_route_fail_cnt[ FD_NET_ROUTE_FAIL_CNT ];
      43             :     ulong tx_neigh_fail_cnt;
      44             :   } metrics;
      45             : };
      46             : typedef struct fd_net_router fd_net_router_t;
      47             : 
      48             : struct fd_net_tx_route {
      49             :   uchar mac_addrs[12];
      50             :   ushort mtu;
      51             :   uint  src_ip;
      52             :   uint  if_idx;
      53             :   uint  use_loopback;
      54             :   uint  use_gre;
      55             :   uint  gre_outer_src_ip;
      56             :   uint  gre_outer_dst_ip;
      57             : };
      58             : typedef struct fd_net_tx_route fd_net_tx_route_t;
      59             : 
      60             : FD_PROTOTYPES_BEGIN
      61             : 
      62             : /* fd_net_tx_route resolves the destination interface index, src MAC
      63             :    address, and dst MAC address.  Returns 1 on success, 0 on failure.
      64             :    On success, writes the complete route result to out. */
      65             : 
      66             : static int
      67             : fd_net_tx_route( fd_net_router_t * ctx,
      68             :                  uint              dst_ip,
      69         144 :                  fd_net_tx_route_t * out ) {
      70             : 
      71         144 :   if( FD_UNLIKELY( !out ) ) return 0;
      72         144 :   *out = (fd_net_tx_route_t) {0};
      73             : 
      74             :   /* Route lookup */
      75             : 
      76         144 :   fd_fib4_hop_t hop[2] = {0};
      77         144 :   hop[0] = fd_fib4_lookup( ctx->fib_local, dst_ip, 0UL );
      78         144 :   hop[1] = fd_fib4_lookup( ctx->fib_main,  dst_ip, 0UL );
      79         144 :   fd_fib4_hop_t const * next_hop = fd_fib4_hop_or( hop+0, hop+1 );
      80             : 
      81         144 :   uint rtype   = next_hop->rtype;
      82         144 :   uint if_idx  = next_hop->if_idx;
      83         144 :   uint ip4_src = next_hop->ip4_src;
      84             : 
      85         144 :   if( FD_UNLIKELY( rtype==FD_FIB4_RTYPE_LOCAL ) ) {
      86           6 :     rtype  = FD_FIB4_RTYPE_UNICAST;
      87           6 :     if_idx = 1;
      88           6 :   }
      89             : 
      90         144 :   if( FD_UNLIKELY( rtype!=FD_FIB4_RTYPE_UNICAST ) ) {
      91          24 :     uint const reason = fd_uint_if( rtype==FD_FIB4_RTYPE_THROW,
      92          24 :                                     FD_NET_ROUTE_FAIL_NO_ROUTE,
      93          24 :                                     FD_NET_ROUTE_FAIL_ROUTE_TYPE );
      94          24 :     ctx->metrics.tx_route_fail_cnt[ reason ]++;
      95          24 :     return 0;
      96          24 :   }
      97             : 
      98         120 :   fd_netdev_t * netdev = fd_netdev_tbl_query( &ctx->netdev_tbl, if_idx );
      99         120 :   if( !netdev ) {
     100          12 :     ctx->metrics.tx_route_fail_cnt[ FD_NET_ROUTE_FAIL_MISSING_INTERFACE ]++;
     101          12 :     return 0;
     102          12 :   }
     103             : 
     104         108 :   ip4_src = fd_uint_if( !!ctx->bind_address, ctx->bind_address, ip4_src );
     105         108 :   out->mtu    = netdev->mtu;
     106         108 :   out->src_ip = ip4_src;
     107         108 :   out->if_idx = if_idx;
     108             : 
     109         108 :   if( netdev->dev_type==ARPHRD_LOOPBACK ) {
     110           6 :     memset( out->mac_addrs, 0, sizeof(out->mac_addrs) );
     111           6 :     out->src_ip       = fd_uint_if( !ip4_src, FD_IP4_ADDR( 127,0,0,1 ), ip4_src );
     112           6 :     out->use_loopback = 1U;
     113           6 :     return 1;
     114         102 :   } else if( netdev->dev_type==ARPHRD_IPGRE ) {
     115             :     /* skip MAC addrs lookup for GRE inner dst ip */
     116          18 :     out->gre_outer_src_ip = netdev->gre_src_ip;
     117          18 :     out->gre_outer_dst_ip = netdev->gre_dst_ip;
     118          18 :     out->use_gre = 1U;
     119          18 :     return 1;
     120          18 :   }
     121             : 
     122          84 :   if( FD_UNLIKELY( netdev->dev_type!=ARPHRD_ETHER ) ) {
     123           0 :     ctx->metrics.tx_route_fail_cnt[ FD_NET_ROUTE_FAIL_UNSUPPORTED_INTERFACE ]++;
     124           0 :     return 0;
     125           0 :   }
     126             : 
     127          84 :   if( FD_UNLIKELY( if_idx!=ctx->if_virt ) ) {
     128          12 :     ctx->metrics.tx_route_fail_cnt[ FD_NET_ROUTE_FAIL_UNSUPPORTED_INTERFACE ]++;
     129          12 :     return 0;
     130          12 :   }
     131             : 
     132             :   /* Neighbor resolve */
     133          72 :   uint neigh_ip = next_hop->ip4_gw;
     134          72 :   if( !neigh_ip ) neigh_ip = dst_ip;
     135             : 
     136          72 :   fd_neigh4_entry_t neigh[1];
     137          72 :   int neigh_res = fd_neigh4_hmap_query_entry( ctx->neigh4, neigh_ip, neigh );
     138          72 :   if( FD_UNLIKELY( neigh_res!=FD_MAP_SUCCESS ) ) {
     139             :     /* Neighbor not found */
     140          12 :     fd_netlink_neigh4_solicit( ctx->neigh4_solicit, neigh_ip, if_idx, fd_frag_meta_ts_comp( fd_tickcount() ) );
     141          12 :     ctx->metrics.tx_neigh_fail_cnt++;
     142          12 :     return 0;
     143          12 :   }
     144          60 :   if( FD_UNLIKELY( neigh->state != FD_NEIGH4_STATE_ACTIVE ) ) {
     145           0 :     ctx->metrics.tx_neigh_fail_cnt++;
     146           0 :     return 0;
     147           0 :   }
     148          60 :   ip4_src = fd_uint_if( !ip4_src, ctx->default_address, ip4_src );
     149          60 :   out->src_ip = ip4_src;
     150          60 :   memcpy( out->mac_addrs+0, neigh->mac_addr,  6 );
     151          60 :   memcpy( out->mac_addrs+6, netdev->mac_addr, 6 );
     152             : 
     153          60 :   return 1;
     154          60 : }
     155             : 
     156             : static int
     157             : fd_net_tx_validate_ip4( fd_ip4_hdr_t const * ip4_hdr,
     158         171 :                         ulong                ip4_buf_sz ) {
     159         171 :   if( FD_UNLIKELY( !ip4_hdr || ip4_buf_sz<sizeof(fd_ip4_hdr_t) ) ) return 0;
     160         171 :   ulong const ip4_hdr_sz = FD_IP4_GET_LEN( *ip4_hdr );
     161         171 :   return FD_IP4_GET_VERSION( *ip4_hdr )==4U &&
     162         171 :          ip4_hdr_sz>=sizeof(fd_ip4_hdr_t) && ip4_hdr_sz<=ip4_buf_sz;
     163         171 : }
     164             : 
     165             : static int
     166             : fd_net_tx_validate_frame( void const * frame,
     167          96 :                           ulong        frame_sz ) {
     168          96 :   if( FD_UNLIKELY( !frame ||
     169          96 :                    frame_sz<sizeof(fd_eth_hdr_t)+sizeof(fd_ip4_hdr_t) ||
     170          96 :                    frame_sz>FD_ETH_PAYLOAD_MAX ) ) return 0;
     171          96 :   fd_eth_hdr_t const * eth_hdr = (fd_eth_hdr_t const *)frame;
     172          96 :   return eth_hdr->net_type==fd_ushort_bswap( FD_ETH_HDR_TYPE_IP ) &&
     173          96 :          fd_net_tx_validate_ip4( (fd_ip4_hdr_t const *)(eth_hdr+1), frame_sz-sizeof(fd_eth_hdr_t) );
     174          96 : }
     175             : 
     176             : /* fd_net_tx_fill_ip4 validates an IPv4 packet and fills a missing source
     177             :    address from its selected route. */
     178             : static int
     179             : fd_net_tx_fill_ip4( fd_net_router_t *         ctx,
     180             :                     fd_net_tx_route_t const * route,
     181             :                     fd_ip4_hdr_t *            ip4_hdr,
     182          75 :                     ulong                     ip4_buf_sz ) {
     183          75 :   if( FD_UNLIKELY( !route || !fd_net_tx_validate_ip4( ip4_hdr, ip4_buf_sz ) ) ) {
     184           0 :     return FD_NET_TX_FILL_INVALID;
     185           0 :   }
     186             : 
     187          75 :   if( ip4_hdr->saddr==0U ) {
     188          75 :     if( FD_UNLIKELY( route->src_ip==0U ) ) {
     189           6 :       ctx->metrics.tx_route_fail_cnt[ FD_NET_ROUTE_FAIL_SOURCE_IP ]++;
     190           6 :       return FD_NET_TX_FILL_NO_SOURCE;
     191           6 :     }
     192             : 
     193          69 :     ip4_hdr->saddr = route->src_ip;
     194          69 :     ip4_hdr->check = 0U;
     195          69 :     ip4_hdr->check = fd_ip4_hdr_check( ip4_hdr );
     196          69 :   }
     197          69 :   return FD_NET_TX_FILL_OK;
     198          75 : }
     199             : 
     200             : /* fd_net_tx_fill_addrs validates the Ethernet and IPv4 headers, sets both
     201             :    Ethernet addresses, and fills a missing IPv4 source address. */
     202             : static int
     203             : fd_net_tx_fill_addrs( fd_net_router_t *         ctx,
     204             :                       fd_net_tx_route_t const * route,
     205             :                       uchar *                   packet,
     206          96 :                       ulong                     sz ) {
     207          96 :   if( FD_UNLIKELY( !route || !fd_net_tx_validate_frame( packet, sz ) ) ) {
     208          27 :     return FD_NET_TX_FILL_INVALID;
     209          27 :   }
     210             : 
     211          69 :   fd_eth_hdr_t * eth_hdr = (fd_eth_hdr_t *)packet;
     212          69 :   fd_ip4_hdr_t * ip4_hdr = (fd_ip4_hdr_t *)(eth_hdr+1);
     213          69 :   int const fill_result = fd_net_tx_fill_ip4( ctx, route, ip4_hdr, sz-sizeof(fd_eth_hdr_t) );
     214          69 :   if( FD_UNLIKELY( fill_result!=FD_NET_TX_FILL_OK ) ) return fill_result;
     215             : 
     216          63 :   memcpy( eth_hdr->dst, route->mac_addrs, 12UL );
     217          63 :   return FD_NET_TX_FILL_OK;
     218          69 : }
     219             : 
     220             : FD_PROTOTYPES_END
     221             : 
     222             : #endif /* HEADER_fd_src_disco_net_fd_net_router_h */

Generated by: LCOV version 1.14