LCOV - code coverage report
Current view: top level - waltz/ip - fd_netlink1.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 70 144 48.6 %
Date: 2025-03-20 12:08:36 Functions: 10 12 83.3 %

          Line data    Source code
       1             : #include <sys/types.h>
       2             : #include <sys/socket.h>
       3             : #include <linux/netlink.h>
       4             : #include <linux/rtnetlink.h>
       5             : #include <errno.h>
       6             : #include <unistd.h>
       7             : 
       8             : #include "fd_netlink1.h"
       9             : #include "../../util/fd_util.h"
      10             : 
      11             : FD_TL ulong fd_netlink_enobufs_cnt;
      12             : 
      13             : static int
      14           3 : fd_nl_create_socket( void ) {
      15           3 :   int fd = socket( AF_NETLINK, SOCK_RAW, NETLINK_ROUTE );
      16             : 
      17           3 :   if( FD_UNLIKELY( fd<0 ) ) {
      18           0 :     FD_LOG_WARNING(( "socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE) failed (%i-%s)",
      19           0 :                       errno, fd_io_strerror( errno ) ));
      20           0 :     return -1;
      21           0 :   }
      22             : 
      23           3 :   int one = 1;
      24           3 :   if( setsockopt( fd, SOL_NETLINK, NETLINK_EXT_ACK, &one, sizeof(one) )<0 ) {
      25           0 :     FD_LOG_WARNING(( "setsockopt(sock,SOL_NETLINK,NETLINK_EXT_ACK) failed (%i-%s)",
      26           0 :                      errno, fd_io_strerror( errno ) ));
      27           0 :     close( fd );
      28           0 :     return -1;
      29           0 :   }
      30             : 
      31           3 :   return fd;
      32           3 : }
      33             : 
      34             : static void
      35           3 : fd_nl_close_socket( int fd ) {
      36           3 :   if( fd >= 0 ) {
      37           3 :     close( fd );
      38           3 :   }
      39           3 : }
      40             : 
      41             : long
      42             : fd_netlink_read_socket( int     fd,
      43             :                         uchar * buf,
      44          12 :                         ulong   buf_sz ) {
      45             :   /* netlink is datagram based
      46             :      once a recv succeeds, any un-received bytes are lost
      47             :      and the next datagram will be properly aligned in the buffer */
      48          12 :   for(;;) {
      49          12 :     long len = recvfrom( fd, buf, buf_sz, 0, NULL, NULL );
      50          12 :     if( FD_UNLIKELY( len<=0L ) ) {
      51           0 :       if( len==0L      ) continue;
      52           0 :       if( errno==EINTR ) continue;
      53           0 :       if( errno==ENOBUFS ) {
      54           0 :         fd_netlink_enobufs_cnt++;
      55           0 :         continue;
      56           0 :       }
      57           0 :       FD_LOG_WARNING(( "netlink recv failed (%d-%s)", errno, fd_io_strerror( errno ) ));
      58           0 :       return -(long)errno;
      59           0 :     }
      60          12 :     return len;
      61          12 :   }
      62          12 : }
      63             : 
      64             : fd_netlink_t *
      65             : fd_netlink_init( fd_netlink_t * nl,
      66           3 :                  uint           seq0 ) {
      67           3 :   nl->fd = fd_nl_create_socket();
      68           3 :   if( FD_UNLIKELY( nl->fd<0 ) ) return NULL;
      69           3 :   nl->seq = seq0;
      70           3 :   return nl;
      71           3 : }
      72             : 
      73             : void *
      74           3 : fd_netlink_fini( fd_netlink_t * nl ) {
      75           3 :   fd_nl_close_socket( nl->fd );
      76           3 :   nl->fd = -1;
      77           3 :   return nl;
      78           3 : }
      79             : 
      80             : static void
      81             : fd_netlink_iter_recvmsg( fd_netlink_iter_t * iter,
      82          12 :                          fd_netlink_t *      netlink ) {
      83          12 :   long len = fd_netlink_read_socket( netlink->fd, iter->buf, iter->buf_sz );
      84          12 :   if( len<0L ) {
      85           0 :     iter->err = (int)-len;
      86           0 :     return;
      87           0 :   }
      88          12 :   iter->msg0 = iter->buf;
      89          12 :   iter->msg1 = iter->buf+len;
      90          12 : }
      91             : 
      92             : /* fd_netlink_iter_verify_next bounds checks the next message.  If out-of-
      93             :    bounds, logs warning and sets error EPROTO.  This prevents the iterator
      94             :    from returning an out-of-bounds netlink message. */
      95             : 
      96             : static void
      97          84 : fd_netlink_iter_bounds_check( fd_netlink_iter_t * iter ) {
      98          84 :   if( fd_netlink_iter_done( iter ) ) return;
      99             : 
     100          78 :   struct nlmsghdr const * nlh = fd_type_pun_const( iter->msg0 );
     101          78 :   if( FD_UNLIKELY( iter->msg0 + sizeof(struct nlmsghdr) > iter->msg1 ) ) {
     102           0 :     FD_LOG_WARNING(( "netlink message header out-of-bounds" ));
     103           0 :     iter->err = EPROTO;
     104           0 :     return;
     105           0 :   }
     106          78 :   if( FD_UNLIKELY( nlh->nlmsg_len < sizeof(struct nlmsghdr) ) ) {
     107             :     /* prevent infinite loop */
     108           0 :     FD_LOG_WARNING(( "netlink message smaller than header" ));
     109           0 :     iter->err = EPROTO;
     110           0 :     return;
     111           0 :   }
     112          78 :   if( FD_UNLIKELY( iter->msg0 + nlh->nlmsg_len > iter->msg1 ) ) {
     113           0 :     FD_LOG_WARNING(( "netlink message out-of-bounds: cur=[%p,%p) buf=[%p,%p)",
     114           0 :                      (void *)iter->msg0, (void *)iter->msg1, (void *)iter->buf, (void *)( iter->buf+iter->buf_sz ) ));
     115           0 :     iter->err = EPROTO;
     116           0 :     return;
     117           0 :   }
     118          78 : }
     119             : 
     120             : fd_netlink_iter_t *
     121             : fd_netlink_iter_init( fd_netlink_iter_t * iter,
     122             :                       fd_netlink_t *      netlink,
     123             :                       uchar *             buf,
     124           6 :                       ulong               buf_sz ) {
     125           6 :   *iter = (fd_netlink_iter_t) {
     126           6 :     .buf    = buf,
     127           6 :     .buf_sz = buf_sz,
     128           6 :     .msg0   = buf,
     129           6 :     .msg1   = buf,
     130           6 :   };
     131             : 
     132           6 :   fd_netlink_iter_recvmsg( iter, netlink );
     133           6 :   fd_netlink_iter_bounds_check( iter );
     134             : 
     135           6 :   return iter;
     136           6 : }
     137             : 
     138             : int
     139         252 : fd_netlink_iter_done( fd_netlink_iter_t const * iter ) {
     140         252 :   if( (iter->err!=0) | ( iter->msg1 - iter->msg0 < (long)sizeof(struct nlmsghdr) ) ) {
     141           0 :     return 1;
     142           0 :   }
     143         252 :   struct nlmsghdr const * nlh = fd_type_pun_const( iter->msg0 );
     144         252 :   return nlh->nlmsg_type==NLMSG_DONE;
     145         252 : }
     146             : 
     147             : fd_netlink_iter_t *
     148             : fd_netlink_iter_next( fd_netlink_iter_t * iter,
     149          78 :                       fd_netlink_t *      netlink ) {
     150             : 
     151          78 :   if( fd_netlink_iter_done( iter ) ) return iter;
     152             : 
     153          78 :   struct nlmsghdr const * nlh = fd_type_pun_const( iter->msg0 );
     154          78 :   if( !(nlh->nlmsg_flags & NLM_F_MULTI) ) {
     155             :     /* Last message was not a multipart message */
     156           0 :     iter->err = -1; /* eof */
     157           0 :     return iter;
     158           0 :   }
     159          78 :   iter->msg0 += NLMSG_ALIGN( nlh->nlmsg_len );
     160             : 
     161          78 :   if( iter->msg0 >= iter->msg1 ) {
     162           6 :     fd_netlink_iter_recvmsg( iter, netlink );
     163           6 :   }
     164          78 :   fd_netlink_iter_bounds_check( iter );
     165             : 
     166          78 :   return iter;
     167          78 : }
     168             : 
     169             : char const *
     170           0 : fd_netlink_rtm_type_str( int rtm_type ) {
     171           0 :   switch( rtm_type ) {
     172           0 :   case RTN_UNSPEC:      return "unspec";
     173           0 :   case RTN_UNICAST:     return "unicast";
     174           0 :   case RTN_LOCAL:       return "local";
     175           0 :   case RTN_BROADCAST:   return "broadcast";
     176           0 :   case RTN_ANYCAST:     return "anycast";
     177           0 :   case RTN_MULTICAST:   return "multicast";
     178           0 :   case RTN_BLACKHOLE:   return "blackhole";
     179           0 :   case RTN_UNREACHABLE: return "unreachable";
     180           0 :   case RTN_PROHIBIT:    return "prohibit";
     181           0 :   case RTN_THROW:       return "throw";
     182           0 :   case RTN_NAT:         return "nat";
     183           0 :   case RTN_XRESOLVE:    return "xresolve";
     184           0 :   default:              return "unknown";
     185           0 :   }
     186           0 : }
     187             : 
     188             : char const *
     189           0 : fd_netlink_rtattr_str( int rta_type ) {
     190           0 :   switch( rta_type ) {
     191             :   /* These exist since at least Linux v3.7 */
     192           0 :   case RTA_DST:                return "dst";
     193           0 :   case RTA_SRC:                return "src";
     194           0 :   case RTA_IIF:                return "iif";
     195           0 :   case RTA_OIF:                return "oif";
     196           0 :   case RTA_GATEWAY:            return "gateway";
     197           0 :   case RTA_PRIORITY:           return "priority";
     198           0 :   case RTA_PREFSRC:            return "prefsrc";
     199           0 :   case RTA_METRICS:            return "metrics";
     200           0 :   case RTA_MULTIPATH:          return "multipath";
     201           0 :   case RTA_FLOW:               return "flow";
     202           0 :   case RTA_CACHEINFO:          return "cacheinfo";
     203           0 :   case RTA_TABLE:              return "table";
     204           0 :   case RTA_MARK:               return "mark";
     205             : #ifdef RTA_MFC_STATS
     206             :   case RTA_MFC_STATS:          return "mfc_stats";
     207             : #endif
     208             : #ifdef RTA_VIA
     209             :   case RTA_VIA:                return "via";
     210             : #endif
     211             : #ifdef RTA_NEWDST
     212             :   case RTA_NEWDST:             return "newdst";
     213             : #endif
     214             : #ifdef RTA_PREF
     215             :   case RTA_PREF:               return "pref";
     216             : #endif
     217             : #ifdef RTA_ENCAP_TYPE
     218             :   case RTA_ENCAP_TYPE:         return "encap_type";
     219             : #endif
     220             : #ifdef RTA_ENCAP
     221             :   case RTA_ENCAP:              return "encap";
     222             : #endif
     223             : #ifdef RTA_EXPIRES
     224             :   case RTA_EXPIRES:            return "expires";
     225             : #endif
     226             : #ifdef RTA_PAD
     227             :   case RTA_PAD:                return "pad";
     228             : #endif
     229             : #ifdef RTA_UID
     230             :   case RTA_UID:                return "uid";
     231             : #endif
     232             : #ifdef RTA_TTL_PROPAGATE
     233             :   case RTA_TTL_PROPAGATE:      return "ttl_propagate";
     234             : #endif
     235             : #ifdef RTA_IP_PROTO
     236             :   case RTA_IP_PROTO:           return "ip_proto";
     237             : #endif
     238             : #ifdef RTA_SPORT
     239             :   case RTA_SPORT:              return "sport";
     240             : #endif
     241             : #ifdef RTA_DPORT
     242             :   case RTA_DPORT:              return "dport";
     243             : #endif
     244             : #ifdef RTA_NH_ID
     245             :   case RTA_NH_ID:              return "nh_id";
     246             : #endif
     247           0 :   default:                     return "unknown";
     248           0 :   }
     249           0 : }

Generated by: LCOV version 1.14