LCOV - code coverage report
Current view: top level - waltz/neigh - fd_neigh4_probe.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 12 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 2 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_waltz_neigh_fd_neigh4_probe_h
       2             : #define HEADER_fd_src_waltz_neigh_fd_neigh4_probe_h
       3             : 
       4             : /* fd_neigh4_probe.h is a hack to indirectly trigger ARP requests in
       5             :    Linux.
       6             : 
       7             :    ### Background
       8             : 
       9             :    When sending an IP packet via the Firedancer network stack, it is
      10             :    the net tile's responsibility to pick the network interface to send
      11             :    the packet out on, as well as the destination MAC address.
      12             : 
      13             :    The dst MAC address is taken from a neighbor table entry given the
      14             :    "next hop" (an output of a previously done route table lookiup).
      15             :    The neighbor table is directly mirrored from the Linux kernel.
      16             : 
      17             :    If no matching neighbor table entry exists, the system should send
      18             :    broadcast an ARP request (e.g. "who is 192.168.12.13? tell
      19             :    192.168.12.4").  ARP replies to this request will then go to the
      20             :    kernel.  The kernel also needs to be told that it should expect an
      21             :    ARP reply to avoid drops.
      22             : 
      23             :    ### Possible Solutions
      24             : 
      25             :    1. Add a neighbor table entry, send out the ARP request via XDP:
      26             :       `ip neigh add IP_ADDR nud incomplete`
      27             :       Requires CAP_NET_ADMIN (to send RTM_NEWNEIGH)
      28             : 
      29             :    2. Add a neighbor table entry, make the kernel issue the ARP
      30             :       request: `ip neigh add IP_ADDR nud incomplete use`
      31             :       Requires CAP_NET_ADMIN (to send RTM_NEWNEIGH)
      32             : 
      33             :    3. Send a UDP datagram which indirectly makes the kernel do an ARP
      34             :       request: `echo "hello" | nc -u IP_ADDR:65535`
      35             :       Does not require privileges
      36             : 
      37             :    4. Send an IP packet (ICMP echo, invalid ICMP, invalid next proto...)
      38             :       which indirectly makes the kernel do an ARP request
      39             :       `ping IP_ADDR -c 1`
      40             :       Requires CAP_NET_RAW to create a SOCK_RAW socket
      41             : 
      42             :    Solution 2 is theoretically ideal.  Unfortunately, it requires the
      43             :    netlink API caller to be in the root user namespace, which would
      44             :    break assumptions made in fd_sandbox.
      45             : 
      46             :    fd_neigh4_probe implements solution 3 because it requires the least
      47             :    amount of privileges. */
      48             : 
      49             : #include "fd_neigh4_map.h"
      50             : #include "../fd_token_bucket.h"
      51             : 
      52             : /* The fd_neigh4_prober_t class provides "neighbor probing"
      53             :    functionality as described above using empty UDP/IP packets. */
      54             : 
      55             : struct fd_neigh4_prober {
      56             :   int sock_fd;  /* UDP socket with IP_TTL 0 */
      57             : 
      58             :   /* probe_delay specifies the delay in ticks for successive ARP
      59             :      requests to the same IP address (see fd_tickcount()) */
      60             :   long probe_delay;
      61             : 
      62             :   /* Token bucket rate limiter on any outgoing ARP probes */
      63             :   fd_token_bucket_t rate_limit;
      64             : 
      65             :   /* Metric counter for probes suppressed by local rate limit */
      66             :   ulong local_rate_limited_cnt;
      67             : 
      68             :   /* Metric counter for probes suppressed by global rate limit */
      69             :   ulong global_rate_limited_cnt;
      70             : };
      71             : 
      72             : typedef struct fd_neigh4_prober fd_neigh4_prober_t;
      73             : 
      74             : FD_PROTOTYPES_BEGIN
      75             : 
      76             : /* fd_neigh4_prober_init initializes a neigh4_prober object.  Creates a
      77             :    new unbound UDP socket (socket(2)) with an IPv4 TTL of zero
      78             :    (setsockopt(2)).  max_probes_per_second and max_probe_burst configure
      79             :    token bucket rate limit parameters for outgoing probe packets.
      80             :    probe_delay_seconds sets the min wait time between two probe packet
      81             :    sends for the same dst IP. */
      82             : 
      83             : void
      84             : fd_neigh4_prober_init( fd_neigh4_prober_t * prober,
      85             :                        float                max_probes_per_second,
      86             :                        ulong                max_probe_burst,
      87             :                        float                probe_delay_seconds );
      88             : 
      89             : /* fd_neigh4_prober_fini closes the neigh4_prober socket. */
      90             : 
      91             : void
      92             : fd_neigh4_prober_fini( fd_neigh4_prober_t * prober );
      93             : 
      94             : /* fd_neigh4_probe sends out an empty UDP packet to port 65535 with the
      95             :    IP time-to-live field set to 0.  ip4_addr is an IP address on a
      96             :    neighboring subnet for which the neighbor discovery process should
      97             :    be started.  ip4_addr is big endian.  now is a recent fd_tickcount()
      98             :    value.  Returns the errno value produced by sendto(2) or 0 on success. */
      99             : 
     100             : int
     101             : fd_neigh4_probe( fd_neigh4_prober_t * prober,
     102             :                  fd_neigh4_entry_t *  entry,
     103             :                  uint                 ip4_addr,
     104             :                  long                 now );
     105             : 
     106             : /* fd_neigh4_probe_rate_limited calls fd_neigh4_probe unless that would
     107             :    violate rate limits.  Returns 0 if a probe was sent out.  Returns
     108             :    positive errno on probe failure.  Returns -1 if rate limit was hit. */
     109             : 
     110             : static inline int
     111             : fd_neigh4_probe_rate_limited(
     112             :     fd_neigh4_prober_t * prober,
     113             :     fd_neigh4_entry_t *  entry,
     114             :     uint                 ip4_addr,
     115             :     long                 now
     116           0 : ) {
     117             :   /* Local rate limit */
     118           0 :   if( now < entry->probe_suppress_until ) {
     119           0 :     prober->local_rate_limited_cnt++;
     120           0 :     return -1;
     121           0 :   }
     122           0 :   entry->probe_suppress_until = now + prober->probe_delay;
     123             : 
     124             :   /* Global rate limit */
     125           0 :   if( !fd_token_bucket_consume( &prober->rate_limit, 1.0f, now ) ) {
     126           0 :     prober->global_rate_limited_cnt++;
     127           0 :     return -1;
     128           0 :   }
     129             : 
     130           0 :   return fd_neigh4_probe( prober, entry, ip4_addr, now );
     131           0 : }
     132             : 
     133             : FD_PROTOTYPES_END
     134             : 
     135             : #endif /* HEADER_fd_src_waltz_neigh_fd_neigh4_probe_h */

Generated by: LCOV version 1.14