LCOV - code coverage report
Current view: top level - waltz/mib - fd_netdev_netlink.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 169 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 3 0.0 %

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

Generated by: LCOV version 1.14