LCOV - code coverage report
Current view: top level - waltz/xdp - fd_xdp1.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 128 0.0 %
Date: 2025-07-01 05:00:49 Functions: 0 2 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "fd_xdp1.h"
       3             : 
       4             : #include "fd_xdp_license.h"
       5             : #include "../ebpf/fd_linux_bpf.h"
       6             : #include "../ebpf/fd_ebpf_asm.h"
       7             : 
       8             : #include <errno.h>
       9             : #include <unistd.h>
      10             : #include <net/if.h>
      11             : #include <sys/syscall.h>
      12             : #include <linux/bpf.h>
      13             : #include <linux/if_link.h>
      14             : 
      15             : /* Define some kernel uapi constants in case the user is compiling
      16             :    with older kernel headers.  This is especially a problem on Ubuntu
      17             :    20.04 which supports these functions, but doesn't have them in
      18             :    the default headers. */
      19             : 
      20             : #ifndef BPF_LINK_CREATE
      21           0 : #define BPF_LINK_CREATE (28)
      22             : #endif
      23             : 
      24             : #ifndef BPF_XDP
      25           0 : #define BPF_XDP (37)
      26             : #endif
      27             : 
      28             : struct __attribute__((aligned(8))) bpf_link_create {
      29             :   uint prog_fd;
      30             :   uint target_ifindex;
      31             :   uint attach_type;
      32             :   uint flags;
      33             : };
      34             : 
      35             : ulong
      36             : fd_xdp_gen_program( ulong          code_buf[ 512 ],
      37             :                     int            xsks_fd,
      38             :                     uint           listen_ip4_addr,
      39             :                     ushort const * ports,
      40           0 :                     ulong          ports_cnt ) {
      41           0 :   if( FD_UNLIKELY( ports_cnt>16UL ) ) {
      42           0 :     FD_LOG_ERR(( "Too many XDP UDP ports (%lu)", ports_cnt ));
      43           0 :   }
      44             : 
      45           0 :   ulong * code = code_buf;
      46           0 :   *(code++) = FD_EBPF( ldxw, r2, r1, 0         );  // r2 = xdp_md->data
      47           0 :   *(code++) = FD_EBPF( ldxw, r3, r1, 4         );  // r3 = xdp_md->data_end
      48           0 :   *(code++) = FD_EBPF( mov64_reg, r4, r2       );
      49           0 :   *(code++) = FD_EBPF( add64_imm, r4, 42       );
      50           0 :   *(code++) = FD_EBPF( jgt_reg, r4, r3, +1     );  // if r2+42 > r3 goto lbl_pass
      51           0 :   *(code++) = FD_EBPF( ldxh, r5, r2, 12        );
      52           0 :   *(code++) = FD_EBPF( jne_imm, r5, 0x0008, +1 );  // if eth_hdr->net_type != IP4 goto lbl_pass
      53           0 :   if( listen_ip4_addr!=0 ) {
      54           0 :     *(code++) = FD_EBPF( ldxw, r5, r2, 30 );
      55           0 :     *(code++) = FD_EBPF( jne_imm, r5, listen_ip4_addr, +1 );  // if eth_hdr->daddr != listen_ip4_addr goto lbl_pass
      56           0 :   }
      57           0 :   *(code++) = FD_EBPF( ldxb, r5, r2, 23        );
      58           0 :   *(code++) = FD_EBPF( jne_imm, r5, 17, +1     );  // if ip4_hdr->protocol != UDP goto lbl_pass
      59           0 :   *(code++) = FD_EBPF( ldxb, r5, r2, 14        );
      60           0 :   *(code++) = FD_EBPF( and64_imm, r5, 0x0f     );
      61           0 :   *(code++) = FD_EBPF( lsh64_imm, r5, 2        );  // r5 = ip4_hdr->ihl*4
      62           0 :   *(code++) = FD_EBPF( add64_reg, r2, r5       );
      63           0 :   *(code++) = FD_EBPF( mov64_reg, r4, r2       );
      64           0 :   *(code++) = FD_EBPF( add64_imm, r4, 22       );
      65           0 :   *(code++) = FD_EBPF( jgt_reg, r4, r3, +1     );  // if udp_hdr+1 > r3 goto lbl_pass
      66           0 :   *(code++) = FD_EBPF( ldxh, r4, r2, 16        );  // r4 = udp_hdr->dst_port
      67           0 :   for( ulong i=0UL; i<ports_cnt; i++ ) {
      68           0 :     ushort port = (ushort)fd_ushort_bswap( ports[ i ] );
      69           0 :     if( !port ) continue;
      70           0 :     *(code++) = FD_EBPF( jeq_imm, r4, port, +2 ); // if dst_port == ports[i] goto lbl_redirect
      71           0 :   }
      72           0 :   ulong * lbl_pass = code;
      73           0 :   *(code++) = FD_EBPF( mov64_imm, r0, XDP_PASS );
      74           0 :   *(code++) = FD_EBPF_exit;                       // return XDP_PASS
      75           0 :   ulong * lbl_redirect = code;
      76           0 :   *(code++) = FD_EBPF( ldxw, r2, r1, 16        ); // r2 = xdp_md->rx_queue_index
      77           0 :   *(code++) = FD_EBPF( lddw, r1, xsks_fd       ); // r1 = xsk_map_fd ll
      78           0 :   *(code++) = 0;
      79           0 :   *(code++) = FD_EBPF( mov64_imm, r3, 0        ); // r3 = 0
      80           0 :   *(code++) = FD_EBPF( call, 0x33              );
      81           0 :   *(code++) = FD_EBPF_exit;                       // return bpf_redirect_map(r1,r2,r3)
      82             :   // ulong * lbl_drop = code;
      83             :   // *(code++) = FD_EBPF( mov64_imm, r0, XDP_DROP );
      84             :   // *(code++) = FD_EBPF_exit;
      85           0 :   ulong * code_end = code;
      86           0 :   ulong   code_cnt = (ulong)( code_end-code_buf );
      87             : 
      88           0 :   FD_LOG_HEXDUMP_DEBUG(( "XDP program", code_buf, code_cnt*sizeof(ulong) ));
      89             : 
      90             :   /* Fill in jump labels */
      91             : 
      92           0 :   for( ulong i=0UL; i<code_cnt; i++ ) {
      93           0 :     if( (code_buf[ i ] & 0x05)==0x05 ) {
      94           0 :       ulong * jmp_target;
      95           0 :       uint    jmp_label = (code_buf[ i ]>>16) & 0xFFFF;
      96           0 :       switch( jmp_label ) {
      97           0 :       case 0: continue;
      98           0 :       case 1: jmp_target = lbl_pass;     break;
      99           0 :       case 2: jmp_target = lbl_redirect; break;
     100             :       // case 3: jmp_target = lbl_drop;     break;
     101           0 :       default: FD_LOG_ERR(( "Invalid jump instruction (%016lx)", fd_ulong_bswap( code_buf[ i ] ) ));
     102           0 :       }
     103           0 :       long   off   = jmp_target-code_buf-(long)i-1;
     104           0 :       ushort off_u = (ushort)(short)off;
     105           0 :       code_buf[ i ] = (code_buf[ i ] & 0xFFFFFFFF0000FFFF) | ((ulong)off_u<<16UL);
     106           0 :     }
     107           0 :   }
     108             : 
     109           0 :   return code_cnt;
     110           0 : }
     111             : 
     112             : fd_xdp_fds_t
     113             : fd_xdp_install( uint           if_idx,
     114             :                 uint           listen_ip4_addr,
     115             :                 ulong          ports_cnt,
     116             :                 ushort const * ports,
     117           0 :                 char const *   xdp_mode ) {
     118             :   /* Check args */
     119             : 
     120           0 :   uint uxdp_mode = 0;
     121           0 :   if(      !strcmp( xdp_mode, "skb"     ) ) uxdp_mode = XDP_FLAGS_SKB_MODE;
     122           0 :   else if( !strcmp( xdp_mode, "drv"     ) ) uxdp_mode = XDP_FLAGS_DRV_MODE;
     123           0 :   else if( !strcmp( xdp_mode, "hw"      ) ) uxdp_mode = XDP_FLAGS_HW_MODE;
     124           0 :   else if( !strcmp( xdp_mode, "generic" ) ) uxdp_mode = 0U;
     125           0 :   else FD_LOG_ERR(( "unknown XDP mode `%s`", xdp_mode ));
     126             : 
     127           0 :   uint true_port_cnt = 0U;
     128           0 :   for( ulong i=0UL; i<ports_cnt; i++ ) true_port_cnt += !!ports[ i ];
     129           0 :   if( FD_UNLIKELY( !true_port_cnt ) ) FD_LOG_ERR(( "XDP program is not listening on any UDP ports" ));
     130             : 
     131             :   /* Create XSK map */
     132             : 
     133           0 :   union bpf_attr attr2 = {
     134           0 :     .map_type    = BPF_MAP_TYPE_XSKMAP,
     135           0 :     .key_size    = 4U,
     136           0 :     .value_size  = 4U,
     137           0 :     .max_entries = 256U,
     138           0 :     .map_name    = "fd_xdp_xsks"
     139           0 :   };
     140           0 :   int xsk_map_fd = (int)bpf( BPF_MAP_CREATE, &attr2, sizeof(union bpf_attr) );
     141           0 :   if( FD_UNLIKELY( -1==xsk_map_fd ) ) FD_LOG_ERR(( "Failed to create XSKMAP (%i-%s)", errno, fd_io_strerror( errno ) ));
     142             : 
     143             :   /* Load eBPF program into kernel */
     144             : 
     145           0 :   ulong code_buf[ 512 ];
     146           0 :   ulong code_cnt = fd_xdp_gen_program( code_buf, xsk_map_fd, listen_ip4_addr, ports, ports_cnt );
     147             : 
     148           0 :   char ebpf_kern_log[ 32768UL ];
     149           0 :   union bpf_attr attr = {
     150           0 :     .prog_type = BPF_PROG_TYPE_XDP,
     151           0 :     .insn_cnt  = (uint)code_cnt,
     152           0 :     .insns     = (ulong)code_buf,
     153           0 :     .license   = (ulong)FD_LICENSE,
     154             :     /* Verifier logs */
     155           0 :     .log_level = 6,
     156           0 :     .log_size  = 32768UL,
     157           0 :     .log_buf   = (ulong)ebpf_kern_log
     158           0 :   };
     159           0 :   int prog_fd = (int)bpf( BPF_PROG_LOAD, &attr, sizeof(union bpf_attr) );
     160           0 :   if( FD_UNLIKELY( -1==prog_fd ) ) {
     161           0 :     FD_LOG_WARNING(( "bpf(BPF_PROG_LOAD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     162           0 :     FD_LOG_ERR(( "eBPF verifier log:\n%s", ebpf_kern_log ));
     163           0 :   }
     164             : 
     165             :   /* Install program to device */
     166             : 
     167           0 :   struct bpf_link_create link_create = {
     168           0 :     .prog_fd        = (uint)prog_fd,
     169           0 :     .target_ifindex = if_idx,
     170           0 :     .attach_type    = BPF_XDP,
     171           0 :     .flags          = uxdp_mode
     172           0 :   };
     173             : 
     174           0 :   int prog_link_fd = (int)bpf( BPF_LINK_CREATE, fd_type_pun( &link_create ), sizeof(struct bpf_link_create) );
     175           0 :   if( FD_UNLIKELY( -1==prog_link_fd ) ) {
     176           0 :     if( FD_LIKELY( errno==ENOSYS ) ) {
     177           0 :       FD_LOG_ERR(( "BPF_LINK_CREATE is not supported by your kernel (%i-%s). Firedancer requires a Linux "
     178           0 :                    "kernel version of v5.7 or newer to support fast XDP networking.  Please upgrade to a newer "
     179           0 :                    "kernel version.", errno, fd_io_strerror( errno ) ));
     180           0 :     } else if( FD_LIKELY( errno==EINVAL ) ) {
     181           0 :       char if_name[ IF_NAMESIZE ] = {0};
     182           0 :       FD_LOG_ERR(( "BPF_LINK_CREATE failed on interface %s (%i-%s).  This likely means the network device "
     183           0 :                    "does not have support for XDP.  If the device is a bonding device, you will need "
     184           0 :                    "a kernel version of v5.15 or newer.  For other devices, see the list of kernel "
     185           0 :                    "support at https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#xdp",
     186           0 :                    if_indextoname( if_idx, if_name ), errno, fd_io_strerror( errno ) ));
     187           0 :     } else {
     188           0 :       FD_LOG_ERR(( "BPF_LINK_CREATE failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     189           0 :     }
     190           0 :   }
     191             : 
     192           0 :   if( FD_UNLIKELY( -1==close( prog_fd ) ) ) FD_LOG_ERR(( "close(%d) failed (%i-%s)", xsk_map_fd, errno, fd_io_strerror( errno ) ));
     193             : 
     194           0 :   return (fd_xdp_fds_t){
     195           0 :     .xsk_map_fd   = xsk_map_fd,
     196           0 :     .prog_link_fd = prog_link_fd,
     197           0 :   };
     198           0 : }

Generated by: LCOV version 1.14