LCOV - code coverage report
Current view: top level - waltz/mib - fd_netdev_netlink.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 207 0.0 %
Date: 2025-08-05 05:04:49 Functions: 0 3 0.0 %

          Line data    Source code
       1             : #include "fd_netdev_netlink.h"
       2             : #include "../../util/fd_util.h"
       3             : #include "../../util/net/fd_ip4.h"
       4             : #include "fd_netdev_tbl.h"
       5             : #include <linux/if_link.h>
       6             : 
       7             : #if !defined(__linux__)
       8             : #error "fd_fib4_netlink.c requires a Linux system with kernel headers"
       9             : #endif
      10             : 
      11             : #include <errno.h>
      12             : #include <linux/if.h> /* IFNAMSIZ */
      13             : #include <linux/if_arp.h> /* ARPHRD_NETROM */
      14             : #include <linux/rtnetlink.h> /* RTM_{...}, NLM_{...} */
      15             : #include <linux/if_tunnel.h>
      16             : 
      17             : static fd_netdev_t *
      18           0 : fd_netdev_init( fd_netdev_t * netdev ) {
      19           0 :   *netdev = (fd_netdev_t) {
      20           0 :     .mtu           = 1500,
      21           0 :     .if_idx        = 0,
      22           0 :     .slave_tbl_idx = -1,
      23           0 :     .master_idx    = -1,
      24           0 :     .oper_status   = FD_OPER_STATUS_INVALID,
      25           0 :     .dev_type      = ARPHRD_NONE,
      26           0 :     .gre_dst_ip    = 0,
      27           0 :     .gre_src_ip    = 0
      28           0 :   };
      29           0 :   return netdev;
      30           0 : }
      31             : 
      32             : FD_FN_CONST static uchar
      33           0 : ifoper_to_oper_status( uint if_oper ) {
      34             :   /* Linux uses different enum values than RFC 2863 */
      35           0 :   switch( if_oper ) {
      36           0 :         case IF_OPER_UNKNOWN:
      37           0 :     return FD_OPER_STATUS_UNKNOWN;
      38           0 :         case IF_OPER_NOTPRESENT:
      39           0 :     return FD_OPER_STATUS_NOT_PRESENT;
      40           0 :         case IF_OPER_DOWN:
      41           0 :     return FD_OPER_STATUS_DOWN;
      42           0 :         case IF_OPER_LOWERLAYERDOWN:
      43           0 :     return FD_OPER_STATUS_LOWER_LAYER_DOWN;
      44           0 :         case IF_OPER_TESTING:
      45           0 :     return FD_OPER_STATUS_TESTING;
      46           0 :         case IF_OPER_DORMANT:
      47           0 :     return FD_OPER_STATUS_DORMANT;
      48           0 :         case IF_OPER_UP:
      49           0 :     return FD_OPER_STATUS_UP;
      50           0 :   default:
      51           0 :     return FD_OPER_STATUS_INVALID;
      52           0 :   }
      53           0 : }
      54             : 
      55             : int
      56             : fd_netdev_netlink_load_table( fd_netdev_tbl_join_t * tbl,
      57           0 :                               fd_netlink_t *         netlink ) {
      58           0 :   fd_netdev_tbl_reset( tbl );
      59             : 
      60           0 :   uint seq = netlink->seq++;
      61             : 
      62           0 :   struct {
      63           0 :     struct nlmsghdr  nlh;
      64           0 :     struct ifinfomsg ifi;
      65           0 :   } request;
      66           0 :   request.nlh = (struct nlmsghdr) {
      67           0 :     .nlmsg_type  = RTM_GETLINK,
      68           0 :     .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
      69           0 :     .nlmsg_len   = sizeof(request),
      70           0 :     .nlmsg_seq   = seq
      71           0 :   };
      72           0 :   request.ifi = (struct ifinfomsg) {
      73           0 :     .ifi_family = AF_PACKET,
      74           0 :     .ifi_type   = ARPHRD_NETROM,
      75           0 :   };
      76             : 
      77           0 :   long send_res = sendto( netlink->fd, &request, sizeof(request), 0, NULL, 0 );
      78           0 :   if( FD_UNLIKELY( send_res<0 ) ) {
      79           0 :     FD_LOG_WARNING(( "netlink send(%d,RTM_GETLINK,NLM_F_REQUEST|NLM_F_DUMP) failed (%i-%s)", netlink->fd, errno, fd_io_strerror( errno ) ));
      80           0 :     return errno;
      81           0 :   }
      82           0 :   if( FD_UNLIKELY( send_res!=sizeof(request) ) ) {
      83           0 :     FD_LOG_WARNING(( "netlink send(%d,RTM_GETLINK,NLM_F_REQUEST|NLM_F_DUMP) failed (short write)", netlink->fd ));
      84           0 :     return EPIPE;
      85           0 :   }
      86             : 
      87           0 :   int err = 0;
      88             : 
      89           0 :   uchar buf[ 4096 ];
      90           0 :   fd_netlink_iter_t iter[1];
      91           0 :   for( fd_netlink_iter_init( iter, netlink, buf, sizeof(buf) );
      92           0 :        !fd_netlink_iter_done( iter );
      93           0 :        fd_netlink_iter_next( iter, netlink ) ) {
      94           0 :     struct nlmsghdr const * nlh = fd_netlink_iter_msg( iter );
      95           0 :     if( FD_UNLIKELY( nlh->nlmsg_type==NLMSG_ERROR ) ) {
      96           0 :       struct nlmsgerr * err = NLMSG_DATA( nlh );
      97           0 :       int nl_err = -err->error;
      98           0 :       FD_LOG_WARNING(( "netlink RTM_GETLINK,NLM_F_REQUEST|NLM_F_DUMP failed (%d-%s)", nl_err, fd_io_strerror( nl_err ) ));
      99           0 :       return nl_err;
     100           0 :     }
     101           0 :     if( FD_UNLIKELY( nlh->nlmsg_type!=RTM_NEWLINK ) ) {
     102           0 :       FD_LOG_DEBUG(( "unexpected nlmsg_type %u", nlh->nlmsg_type ));
     103           0 :       continue;
     104           0 :     }
     105           0 :     struct ifinfomsg const * ifi = NLMSG_DATA( nlh );
     106             : 
     107           0 :     if( FD_UNLIKELY( ifi->ifi_index<0 || ifi->ifi_index>=tbl->hdr->dev_max ) ) {
     108           0 :       FD_LOG_WARNING(( "Error reading interface table: interface %d is beyond max of %u", ifi->ifi_index, tbl->hdr->dev_max ));
     109           0 :       err = ENOSPC;
     110           0 :       break;
     111           0 :     }
     112             : 
     113           0 :     ushort ifi_type = ifi->ifi_type;
     114           0 :     if( ifi_type!=ARPHRD_ETHER && ifi_type!=ARPHRD_LOOPBACK && ifi_type!=ARPHRD_IPGRE ) continue;
     115             : 
     116           0 :     struct ifinfomsg * msg    = NLMSG_DATA( nlh );
     117           0 :     struct rtattr *    rat    = (void *)( (ulong)msg + NLMSG_ALIGN( sizeof(struct ifinfomsg) ) );
     118           0 :     long               rat_sz = (long)NLMSG_PAYLOAD( nlh, sizeof(struct ifinfomsg) );
     119             : 
     120           0 :     fd_netdev_t netdev[1];
     121           0 :     fd_netdev_init( netdev );
     122             : 
     123           0 :     for( ; RTA_OK( rat, rat_sz ); rat=RTA_NEXT( rat, rat_sz ) ) {
     124           0 :       void * rta    = RTA_DATA( rat );
     125           0 :       ulong  rta_sz = RTA_PAYLOAD( rat );
     126             : 
     127           0 :       switch( rat->rta_type ) {
     128             : 
     129           0 :       case IFLA_IFNAME:
     130             :         /* Includes trailing zero */
     131           0 :         if( FD_UNLIKELY( rta_sz==0 || rta_sz>IFNAMSIZ ) ) {
     132           0 :           FD_LOG_WARNING(( "Error reading interface table: IFLA_IFNAME has unsupported size %lu", rta_sz ));
     133           0 :           err = EPROTO;
     134           0 :           goto fail;
     135           0 :         }
     136           0 :         memcpy( netdev->name, rta, rta_sz );
     137           0 :         netdev->name[ rta_sz-1 ] = '\0';
     138           0 :         break;
     139             : 
     140           0 :       case IFLA_ADDRESS:
     141           0 :         if( FD_UNLIKELY( rta_sz==6UL ) ) {
     142           0 :           memcpy( netdev->mac_addr, rta, 6 );
     143           0 :         }
     144           0 :         break;
     145             : 
     146           0 :       case IFLA_OPERSTATE:
     147           0 :         if( FD_UNLIKELY( rta_sz!=1UL ) ) {
     148           0 :           FD_LOG_WARNING(( "Error reading interface table: IFLA_OPERSTATE has unexpected size %lu", rta_sz ));
     149           0 :           err = EPROTO;
     150           0 :           goto fail;
     151           0 :         }
     152           0 :         netdev->oper_status = (uchar)ifoper_to_oper_status( FD_LOAD( uchar, rta ) );
     153           0 :         break;
     154             : 
     155           0 :       case IFLA_MTU:
     156           0 :         if( FD_UNLIKELY( rta_sz!=4UL ) ) {
     157           0 :           FD_LOG_WARNING(( "Error reading interface table: IFLA_MTU has unexpected size %lu", rta_sz ));
     158           0 :           err = EPROTO;
     159           0 :           goto fail;
     160           0 :         }
     161           0 :         netdev->mtu = (ushort)fd_uint_min( FD_LOAD( uint, rta ), USHORT_MAX );
     162           0 :         break;
     163             : 
     164           0 :       case IFLA_MASTER: {
     165           0 :         if( FD_UNLIKELY( rta_sz!=4UL ) ) {
     166           0 :           FD_LOG_WARNING(( "Error reading interface table: IFLA_MASTER has unexpected size %lu", rta_sz ));
     167           0 :           err = EPROTO;
     168           0 :           goto fail;
     169           0 :         }
     170           0 :         int master_idx = FD_LOAD( int, rta );
     171           0 :         if( FD_UNLIKELY( master_idx<0 || master_idx>=tbl->hdr->dev_max ) ) {
     172           0 :           FD_LOG_WARNING(( "Error reading interface table: IFLA_MASTER has invalid index %d", master_idx ));
     173           0 :           err = EPROTO;
     174           0 :           goto fail;
     175           0 :         }
     176           0 :         netdev->master_idx = (short)master_idx;
     177           0 :         break;
     178           0 :       } /* IFLA_MASTER */
     179             : 
     180           0 :       case IFLA_LINKINFO : {
     181           0 :         if( ifi->ifi_type!=ARPHRD_IPGRE ) continue;
     182             : 
     183           0 :         struct rtattr * info_rat    = rta;
     184           0 :         long            info_rat_sz = (long)rta_sz;
     185             : 
     186           0 :         for( ; RTA_OK( info_rat, info_rat_sz ); info_rat = RTA_NEXT( info_rat, info_rat_sz ) ) {
     187           0 :           void * info_rta    = RTA_DATA( info_rat );
     188           0 :           ulong  info_rta_sz = RTA_PAYLOAD( info_rat );
     189             : 
     190           0 :           switch( info_rat->rta_type ) {
     191           0 :           case IFLA_INFO_DATA: {
     192           0 :             struct rtattr * gre_rat    = (struct rtattr *)info_rta;
     193           0 :             long            gre_rat_sz = (long)info_rta_sz;
     194             : 
     195           0 :             for( ; RTA_OK( gre_rat, gre_rat_sz ); gre_rat = RTA_NEXT( gre_rat, gre_rat_sz ) ) {
     196           0 :               void * gre_rta    = RTA_DATA( gre_rat );
     197           0 :               uint gre_dst_ip;
     198           0 :               uint gre_src_ip;
     199             : 
     200           0 :               switch( gre_rat->rta_type ) {
     201           0 :               case IFLA_GRE_REMOTE: {
     202           0 :                 gre_dst_ip = FD_LOAD( uint, gre_rta );
     203           0 :                 if( gre_dst_ip != 0 ) netdev->gre_dst_ip = gre_dst_ip;
     204           0 :                 break;
     205           0 :               } /* IFLA_GRE_REMOTE */
     206           0 :               case IFLA_GRE_LOCAL: {
     207           0 :                 gre_src_ip = FD_LOAD( uint, gre_rta );
     208           0 :                 if( gre_src_ip != 0 ) netdev->gre_src_ip = gre_src_ip;
     209           0 :                 break;
     210           0 :               } /* IFLA_GRE_LOCAL */
     211           0 :               }
     212           0 :             }
     213           0 :             break;
     214           0 :           } /* IFLA_INFO_DATA */
     215           0 :           } /* info_rat->rta_type */
     216           0 :         } /* for each info_rta */
     217           0 :       } /* IFLA_LINKINFO */
     218             : 
     219           0 :       } /* switch( rat->rta_type ) */
     220           0 :     } /* for each RTA */
     221             : 
     222           0 :     if( ifi->ifi_type==ARPHRD_LOOPBACK ) {
     223           0 :       netdev->oper_status = FD_OPER_STATUS_UP;
     224           0 :     }
     225             : 
     226           0 :     netdev->dev_type = ifi_type;
     227           0 :     tbl->dev_tbl[ ifi->ifi_index ] = *netdev;
     228           0 :     tbl->hdr->dev_cnt = (ushort)fd_uint_max( tbl->hdr->dev_cnt, (uint)ifi->ifi_index+1U );
     229           0 :   }
     230             : 
     231             :   /* Walk the table again to index the bond master => slave mapping */
     232             : 
     233           0 :   for( ulong j=0UL; j<(tbl->hdr->dev_cnt); j++ ) {
     234             :     /* Only consider UP slaves */
     235           0 :     if( tbl->dev_tbl[ j ].oper_status!=FD_OPER_STATUS_UP ) continue;
     236             : 
     237             :     /* Find master */
     238           0 :     int master_idx = tbl->dev_tbl[ j ].master_idx;
     239           0 :     if( master_idx<0 ) continue;
     240           0 :     if( FD_UNLIKELY( master_idx>=tbl->hdr->dev_max ) ) continue; /* unreachable */
     241           0 :     fd_netdev_t * master = &tbl->dev_tbl[ master_idx ];
     242             : 
     243             :     /* Allocate a new bond slave table if needed */
     244           0 :     if( master->slave_tbl_idx<0 ) {
     245           0 :       if( FD_UNLIKELY( tbl->hdr->bond_cnt>=tbl->hdr->bond_max ) ) {
     246           0 :         FD_LOG_WARNING(( "Error reading interface table: Found %u bond devices but max is %u", tbl->hdr->bond_cnt, tbl->hdr->bond_max ));
     247           0 :         continue;
     248           0 :       }
     249             : 
     250           0 :       master->slave_tbl_idx = (short)tbl->hdr->bond_cnt;
     251           0 :       tbl->hdr->bond_cnt = (ushort)( tbl->hdr->bond_cnt+1U );
     252             :       /* Assume that this table is empty */
     253           0 :     }
     254             : 
     255           0 :     fd_netdev_bond_t * bond = &tbl->bond_tbl[ master->slave_tbl_idx ];
     256           0 :     if( FD_UNLIKELY( bond->slave_cnt>=FD_NETDEV_BOND_SLAVE_MAX ) ) {
     257           0 :       FD_LOG_WARNING(( "Error reading interface table: Bond device %d has %u slaves but max is %d", master_idx, bond->slave_cnt, FD_NETDEV_BOND_SLAVE_MAX ));
     258           0 :       continue;
     259           0 :     }
     260           0 :     bond->slave_idx[ bond->slave_cnt ] = (ushort)j;
     261           0 :     bond->slave_cnt = (uchar)( bond->slave_cnt+1U );
     262           0 :   }
     263             : 
     264           0 :   return 0;
     265             : 
     266           0 : fail:
     267           0 :   fd_netlink_iter_drain( iter, netlink );
     268           0 :   return err;
     269           0 : }

Generated by: LCOV version 1.14