LCOV - code coverage report
Current view: top level - waltz/ip - fd_netlink.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 250 630 39.7 %
Date: 2024-11-13 11:58:15 Functions: 9 17 52.9 %

          Line data    Source code
       1             : #include <sys/types.h>
       2             : #include <sys/socket.h>
       3             : #include <sys/ioctl.h>
       4             : #include <linux/netlink.h>
       5             : #include <linux/rtnetlink.h>
       6             : #include <linux/neighbour.h>
       7             : #include <arpa/inet.h>
       8             : #include <errno.h>
       9             : #include <unistd.h>
      10             : 
      11             : #include "fd_netlink.h"
      12             : #include "../../util/cstr/fd_cstr.h"
      13             : 
      14             : void
      15             : fd_dump_nla_err( struct nlmsghdr * nlh, uint ip_addr, uint ifindex );
      16             : 
      17             : #define FD_NL_DATATYPE(X,...) \
      18             :   X( FD_NL_IGN   , 1 , ignore , __VA_ARGS__ ) \
      19             :   X( FD_NL_CHAR  , 0 , int    , __VA_ARGS__ ) \
      20             :   X( FD_NL_SHORT , 0 , int    , __VA_ARGS__ ) \
      21             :   X( FD_NL_INT   , 0 , int    , __VA_ARGS__ ) \
      22             :   X( FD_NL_ADDR  , 0 , addr   , __VA_ARGS__ )
      23             : 
      24             : #define FD_NL_RTA_TYPE(X,...) \
      25           0 :   X( RTA_UNSPEC      , FD_NL_IGN   , "ignored"                            , __VA_ARGS__ ) \
      26           0 :   X( RTA_DST         , FD_NL_ADDR  , "Route destination address"          , __VA_ARGS__ ) \
      27           0 :   X( RTA_SRC         , FD_NL_ADDR  , "Route source address"               , __VA_ARGS__ ) \
      28           0 :   X( RTA_IIF         , FD_NL_INT   , "Input interface index"              , __VA_ARGS__ ) \
      29           0 :   X( RTA_OIF         , FD_NL_INT   , "Output interface index"             , __VA_ARGS__ ) \
      30           0 :   X( RTA_GATEWAY     , FD_NL_ADDR  , "The gateway of the route"           , __VA_ARGS__ ) \
      31           0 :   X( RTA_PRIORITY    , FD_NL_INT   , "Priority of route"                  , __VA_ARGS__ ) \
      32           0 :   X( RTA_PREFSRC     , FD_NL_ADDR  , "Preferred source address"           , __VA_ARGS__ ) \
      33           0 :   X( RTA_METRICS     , FD_NL_INT   , "Route metric"                       , __VA_ARGS__ ) \
      34           0 :   X( RTA_MULTIPATH   , FD_NL_IGN   , "Multipath nexthop data br"          , __VA_ARGS__ ) \
      35           0 :   X( RTA_PROTOINFO   , FD_NL_IGN   , "RTA_PROTOINFO No longer used"       , __VA_ARGS__ ) \
      36           0 :   X( RTA_FLOW        , FD_NL_INT   , "Route realm"                        , __VA_ARGS__ ) \
      37           0 :   X( RTA_CACHEINFO   , FD_NL_IGN   , "Cache info"                         , __VA_ARGS__ ) \
      38           0 :   X( RTA_SESSION     , FD_NL_IGN   , "RTA_SESSION No longer used"         , __VA_ARGS__ ) \
      39           0 :   X( RTA_MP_ALGO     , FD_NL_IGN   , "RTA_MP_ALGO No longer used"         , __VA_ARGS__ ) \
      40           0 :   X( RTA_TABLE       , FD_NL_INT   , "Routing table ID; if set,"          , __VA_ARGS__ ) \
      41           0 :   X( RTA_MARK        , FD_NL_INT   , "RTA_MARK"                           , __VA_ARGS__ ) \
      42           0 :   X( RTA_MFC_STATS   , FD_NL_IGN   , "RTA_MFC_STATS"                      , __VA_ARGS__ ) \
      43           0 :   X( RTA_VIA         , FD_NL_IGN   , "Gateway in different AF"            , __VA_ARGS__ ) \
      44           0 :   X( RTA_NEWDST      , FD_NL_ADDR  , "Change packet destination"          , __VA_ARGS__ ) \
      45           0 :   X( RTA_PREF        , FD_NL_CHAR  , "RFC4191 IPv6 router"                , __VA_ARGS__ ) \
      46           0 :   X( RTA_ENCAP_TYPE  , FD_NL_SHORT , "Encapsulation type"                 , __VA_ARGS__ ) \
      47           0 :   X( RTA_ENCAP       , FD_NL_IGN   , "Defined by RTA_ENCAP_TYPE"          , __VA_ARGS__ ) \
      48           0 :   X( RTA_EXPIRES     , FD_NL_INT   , "Expire time for IPv6"               , __VA_ARGS__ )
      49             : 
      50             : char const *
      51           0 : fd_rta_type_to_label( uint rta_type ) {
      52           0 : # define FD_RTA_MATCH( LABEL, CLASS, DESC, ... ) \
      53           0 :   if( rta_type == LABEL ) return #LABEL;
      54           0 :   FD_NL_RTA_TYPE(FD_RTA_MATCH,x)
      55           0 :   return "RTA_UNKNOWN";
      56           0 : # undef FD_RTA_MATCH
      57           0 : }
      58             : 
      59             : char const *
      60           0 : fd_rta_type_to_class( uint rta_type ) {
      61           0 : # define FD_RTA_MATCH( LABEL, CLASS, DESC, ... ) \
      62           0 :   if( rta_type == LABEL ) return #CLASS;
      63           0 :   FD_NL_RTA_TYPE(FD_RTA_MATCH,x)
      64           0 :   return "RTA_UNKNOWN";
      65           0 : # undef FD_RTA_MATCH
      66           0 : }
      67             : 
      68             : #define FD_NL_RTM_TYPE(X,...) \
      69           0 :   X( RTN_UNSPEC        , "unknown route"                                   , __VA_ARGS__ ) \
      70           0 :   X( RTN_UNICAST       , "a gateway or direct route"                       , __VA_ARGS__ ) \
      71           0 :   X( RTN_LOCAL         , "a local interface route"                         , __VA_ARGS__ ) \
      72           0 :   X( RTN_BROADCAST     , "a local broadcast route (sent as a broadcast)"   , __VA_ARGS__ ) \
      73           0 :   X( RTN_ANYCAST       , "a local broadcast route (sent as a unicast)"     , __VA_ARGS__ ) \
      74           0 :   X( RTN_MULTICAST     , "a multicast route"                               , __VA_ARGS__ ) \
      75           0 :   X( RTN_BLACKHOLE     , "a packet dropping route"                         , __VA_ARGS__ ) \
      76           0 :   X( RTN_UNREACHABLE   , "an unreachable destination"                      , __VA_ARGS__ ) \
      77           0 :   X( RTN_PROHIBIT      , "a packet rejection route"                        , __VA_ARGS__ ) \
      78           0 :   X( RTN_THROW         , "continue routing lookup in another table"        , __VA_ARGS__ ) \
      79           0 :   X( RTN_NAT           , "a network address translation rule"              , __VA_ARGS__ ) \
      80           0 :   X( RTN_XRESOLVE      , "refer to an external resolver (not implemented)" , __VA_ARGS__ )
      81             : 
      82             : char const *
      83           0 : fd_rtm_type_to_label( uint rtm_type ) {
      84           0 : # define FD_RTN_MATCH( LABEL, ... ) \
      85           0 :   if( rtm_type == LABEL ) return #LABEL;
      86           0 :   FD_NL_RTM_TYPE(FD_RTN_MATCH,y)
      87           0 :   return "RTN_UNKNOWN";
      88           0 : # undef FD_RTN_MATCH
      89           0 : }
      90             : 
      91             : #ifndef NDA_FDB_EXT_ATTRS
      92             : /* Some older kernel headers might not have this defined */
      93             : #define NDA_FDB_EXT_ATTRS (14)
      94             : #endif
      95             : 
      96             : #define FD_NL_NDA_TYPE(X,...) \
      97           0 :   X( NDA_UNSPEC             , __VA_ARGS__ ) \
      98           0 :   X( NDA_DST                , __VA_ARGS__ ) \
      99           0 :   X( NDA_LLADDR             , __VA_ARGS__ ) \
     100           0 :   X( NDA_CACHEINFO          , __VA_ARGS__ ) \
     101           0 :   X( NDA_PROBES             , __VA_ARGS__ ) \
     102           0 :   X( NDA_VLAN               , __VA_ARGS__ ) \
     103           0 :   X( NDA_PORT               , __VA_ARGS__ ) \
     104           0 :   X( NDA_VNI                , __VA_ARGS__ ) \
     105           0 :   X( NDA_IFINDEX            , __VA_ARGS__ ) \
     106           0 :   X( NDA_MASTER             , __VA_ARGS__ ) \
     107           0 :   X( NDA_LINK_NETNSID       , __VA_ARGS__ ) \
     108           0 :   X( NDA_SRC_VNI            , __VA_ARGS__ ) \
     109           0 :   X( NDA_PROTOCOL           , __VA_ARGS__ ) \
     110           0 :   X( NDA_FDB_EXT_ATTRS      , __VA_ARGS__ )
     111             : 
     112             : char const *
     113           0 : fd_nda_type_to_label( uint nda_type ) {
     114           0 : # define FD_NDA_MATCH( LABEL, ... ) \
     115           0 :   if( nda_type == LABEL ) return #LABEL;
     116           0 :   FD_NL_NDA_TYPE(FD_NDA_MATCH,)
     117           0 :   return "NDA_UNKNOWN";
     118           0 : # undef FD_NDA_MATCH
     119           0 : }
     120             : 
     121             : /* writes hex from data, length data_sz, into buffer buf, capacity buf_sz.
     122             :    returns length written in out_len
     123             :    always leaves a valid nul-terminated string at buf, unless buf_sz == 0,
     124             :    in which case there isn't space to put a '\0', and the function simply
     125             :    returns without doing anything
     126             :    This function is intended for small strings, and so arbitrarily limits
     127             :    the data to 20 bytes of input, or 40 bytes of hex output characters
     128             :    plus overhead */
     129             : void
     130             : fd_nl_write_hex( char *  buf,
     131             :                  ulong   buf_sz,
     132             :                  ulong * out_len,
     133             :                  uchar * data,
     134           0 :                  ulong   data_sz ) {
     135           0 :   if( FD_UNLIKELY( buf_sz == 0 ) ) {
     136           0 :     if( FD_LIKELY( out_len ) ) *out_len = 0;
     137           0 :     return;
     138           0 :   }
     139             : 
     140           0 :   char * p    = buf;
     141           0 :   ulong  p_sz = buf_sz;
     142             : 
     143           0 :   ulong lcl_out_len = 0;
     144           0 :   fd_cstr_printf( p, p_sz, &lcl_out_len, "0x" );
     145             : 
     146           0 :   p_sz -= lcl_out_len;
     147           0 :   p    += lcl_out_len;
     148             : 
     149           0 :   for( ulong j = 0; j < data_sz && p_sz > 0; ++j ) {
     150           0 :     if( j > 20 ) {
     151           0 :       fd_cstr_printf( p, p_sz, &lcl_out_len, "..." );
     152           0 :       p_sz -= lcl_out_len;
     153           0 :       p    += lcl_out_len;
     154             : 
     155           0 :       if( FD_LIKELY( out_len ) ) *out_len = (ulong)( p - buf );
     156           0 :       return;
     157           0 :     }
     158             : 
     159           0 :     fd_cstr_printf( p, p_sz, &lcl_out_len, "%02x", data[j] );
     160           0 :     p_sz -= lcl_out_len;
     161           0 :     p    += lcl_out_len;
     162           0 :   }
     163             : 
     164           0 :   if( FD_LIKELY( out_len ) ) *out_len = (ulong)( p - buf );
     165           0 :   return;
     166           0 : }
     167             : 
     168             : void
     169           0 : fd_nl_dump_rat( struct rtattr * rat, long ratmsglen ) {
     170           0 :   char  buf[2048] = {0};
     171           0 :   ulong buf_sz    = sizeof(buf);
     172             : 
     173             :   /* write attributes into a string, and then output */
     174             : 
     175             :   /* current pointer and remaining length */
     176           0 :   char * p    = buf;
     177           0 :   ulong  p_sz = buf_sz;
     178             : 
     179           0 :   ulong out_len = 0;
     180           0 :   fd_cstr_printf( buf, buf_sz, &out_len, "netlink diagnostic rtattr: " );
     181             : 
     182           0 :   ulong skip = fd_ulong_min( p_sz, out_len );
     183             : 
     184           0 :   p_sz -= skip;
     185           0 :   p    += skip;
     186             : 
     187           0 :   while( RTA_OK( rat, ratmsglen ) ) {
     188           0 :     uchar * rta_data    = RTA_DATA( rat );
     189           0 :     ulong   rta_data_sz = RTA_PAYLOAD( rat );
     190           0 :     uint    rta_type    = (uint)rat->rta_type;
     191             : 
     192             :     /* write attribute name */
     193           0 :     fd_cstr_printf( p, p_sz, &out_len, " %s(%u): ", fd_nda_type_to_label( rta_type ),
     194           0 :         rta_type );
     195             : 
     196           0 :     p    += out_len;
     197           0 :     p_sz -= out_len;
     198             : 
     199             :     /* write value in hex */
     200           0 :     fd_nl_write_hex( p, p_sz, &out_len, rta_data, rta_data_sz );
     201             : 
     202           0 :     p    += out_len;
     203           0 :     p_sz -= out_len;
     204             : 
     205           0 :     rat = RTA_NEXT( rat, ratmsglen );
     206           0 :   }
     207             : 
     208           0 :   FD_LOG_NOTICE(( "%s", buf ));
     209           0 : }
     210             : 
     211             : 
     212             : int
     213           6 : fd_nl_create_socket( void ) {
     214           6 :   int fd = socket( AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, NETLINK_ROUTE );
     215             : 
     216           6 :   if( fd < 0 ) {
     217           0 :     FD_LOG_WARNING(( "Unable to create netlink socket. Error: %d %s", errno,
     218           0 :           strerror( errno ) ));
     219           0 :     return -1;
     220           0 :   }
     221             : 
     222           6 :   int one = 1;
     223           6 :   if( setsockopt( fd, SOL_NETLINK, NETLINK_EXT_ACK,
     224           6 :         &one, sizeof(one) ) < 0 ) {
     225           0 :     FD_LOG_WARNING(( "Netlink error reporting not supported" ));
     226             :     /* continue regardless */
     227           0 :   }
     228             : 
     229           6 :   struct sockaddr_nl sa;
     230           6 :   fd_memset( &sa, 0, sizeof(sa) );
     231           6 :   sa.nl_family = AF_NETLINK;
     232           6 :   if( bind( fd, (void*)&sa, sizeof(sa) ) < 0 ) {
     233           0 :     FD_LOG_WARNING(( "Unable to bind netlink socket. Error: %d %s",
     234           0 :           errno, strerror( errno ) ));
     235           0 :     close( fd );
     236           0 :     return -1;
     237           0 :   }
     238             : 
     239           6 :   return fd;
     240           6 : }
     241             : 
     242             : 
     243             : void
     244           3 : fd_nl_close_socket( int fd ) {
     245           3 :   if( fd >= 0 ) {
     246           3 :     close( fd );
     247           3 :   }
     248           3 : }
     249             : 
     250             : long
     251          12 : fd_nl_read_socket( int fd, uchar * buf, ulong buf_sz ) {
     252             :   /* netlink is datagram based
     253             :      once a recv succeeds, any un-received bytes are lost
     254             :      and the next datagram will be properly aligned in the buffer */
     255          12 :   long len = -1;
     256          12 :   do {
     257          12 :     len = recv( fd, buf, buf_sz, 0 );
     258          12 :   } while( FD_UNLIKELY( ( len < 0 && errno == EINTR ) || len == 0 ) );
     259             : 
     260          12 :   if( FD_UNLIKELY( len < 0 ) ) {
     261           0 :     if( errno == EAGAIN ) {
     262             :       /* EAGAIN means no data. We can simply try again later */
     263           0 :       return -1;
     264           0 :     }
     265             : 
     266           0 :     FD_LOG_WARNING(( "netlink recv failed with %d %s", errno, strerror( errno ) ));
     267           0 :     return -1;
     268           0 :   }
     269             : 
     270          12 :   return len;
     271          12 : }
     272             : 
     273             : int
     274           6 : fd_nl_init( fd_nl_t * nl, uint seq ) {
     275           6 :   nl->seq  = seq;
     276           6 :   nl->fd   = fd_nl_create_socket();
     277           6 :   nl->init = 1;
     278             : 
     279             :   /* returns 1 for failure, 0 for success */
     280           6 :   return nl->fd < 0 ? 1 : 0;
     281           6 : }
     282             : 
     283             : void
     284           3 : fd_nl_fini( fd_nl_t * nl ) {
     285           3 :   fd_nl_close_socket( nl->fd );
     286           3 : }
     287             : 
     288             : 
     289             : long
     290             : fd_nl_load_route_table( fd_nl_t *             nl,
     291             :                         fd_nl_route_entry_t * route_table,
     292           3 :                         ulong                 route_table_cap ) {
     293           3 :   int fd = nl->fd;
     294           3 :   if( fd < 0 ) {
     295           0 :     FD_LOG_ERR(( "fd_nl_load_route_table called without valid file descriptor" ));
     296           0 :     return -1;
     297           0 :   }
     298             : 
     299             :   /* format the request */
     300             : 
     301             :   /* Request struct */
     302           3 :   struct {
     303           3 :     struct nlmsghdr nlh;  /* Netlink header */
     304           3 :     struct rtmsg    rtm;  /* Payload - route message */
     305           3 :   } nl_request;
     306             : 
     307           3 :   fd_memset( &nl_request, 0, sizeof( nl_request ) );
     308             : 
     309           3 :   uint seq = nl->seq++;
     310             : 
     311           3 :   nl_request.nlh.nlmsg_type  = RTM_GETROUTE;  /* We wish to get routes */
     312             : #if 0
     313             :   /* CAP_NET_ADMIN required for NLM_F_ATOMIC */
     314             :   nl_request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_ATOMIC;
     315             : #else
     316           3 :   nl_request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
     317           3 : #endif
     318           3 :   nl_request.nlh.nlmsg_len   = sizeof(nl_request);
     319           3 :   nl_request.nlh.nlmsg_pid   = 0;
     320           3 :   nl_request.nlh.nlmsg_seq   = seq;
     321             : 
     322             :   /* request only IPv4 routes */
     323           3 :   nl_request.rtm.rtm_family  = AF_INET;
     324             : 
     325           3 :   ulong send_sz = sizeof(nl_request);
     326           3 :   long  sent    = send( fd, &nl_request, send_sz, 0 );
     327           3 :   if( sent == -1 ) {
     328           0 :     FD_LOG_WARNING(( "Unable to make netlink request. Error: %d %s", errno, strerror( errno ) ));
     329           0 :     return -1;
     330           0 :   }
     331             : 
     332           3 :   if( sent != (long)send_sz ) {
     333           0 :     FD_LOG_WARNING(( "netlink send returned unexpected size: %ld expected: %lu", sent, send_sz ));
     334           0 :     return -1;
     335           0 :   }
     336             : 
     337             :   /* receiving */
     338           3 :   long len;
     339             : 
     340             :   /* set alignment such that the returned data is aligned */
     341           3 :   uchar __attribute__(( aligned(16) )) ibuf[FD_NL_BUF_SZ] = {0};
     342             : 
     343             :   /* Pointer to the messages head */
     344           3 :   struct nlmsghdr *h = (struct nlmsghdr *)ibuf;
     345             : 
     346           3 :   while(1) {
     347           3 :     len = fd_nl_read_socket( fd, ibuf, sizeof(ibuf) );
     348           3 :     if( len <= 0 ) {
     349           0 :       return -1;
     350           0 :     }
     351             : 
     352           3 :     if( h->nlmsg_seq == seq ) break;
     353           3 :   }
     354             : 
     355             :   /* index into route_entries */
     356           3 :   long route_entry_idx = 0L;
     357             : 
     358             :   /* interpret the response */
     359             : 
     360           3 :   long msglen = len;
     361             : 
     362             :   /* Iterate through all messages in buffer */
     363             : 
     364          33 :   for( ; NLMSG_OK( h, msglen ); h = NLMSG_NEXT( h, msglen ) ) {
     365             :     /* are we still within the bounds of the table? */
     366          33 :     if( route_entry_idx >= (long)route_table_cap ) {
     367             :       /* we filled the table */
     368           0 :       FD_LOG_ERR(( "fd_nl_load_route_table, but table larger than reserved storage" ));
     369           0 :       return -1; /* return failure */
     370           0 :     }
     371             : 
     372             :     /* current route entry */
     373          33 :     fd_nl_route_entry_t * entry = route_table + route_entry_idx;
     374             : 
     375             :     /* clear current entry */
     376          33 :     fd_memset( entry, 0, sizeof( *entry ) );
     377             : 
     378          33 :     if( h->nlmsg_flags & NLM_F_DUMP_INTR ) {
     379             :       /* function was interrupted */
     380             : 
     381           0 :       return -1; /* return failure */
     382           0 :     }
     383             : 
     384          33 :     if( FD_UNLIKELY( h->nlmsg_type == NLMSG_ERROR ) ) {
     385           0 :       struct nlmsgerr * err = (struct nlmsgerr*)NLMSG_DATA(h);
     386             : 
     387             :       /* acknowledgements have no error */
     388           0 :       if( FD_LIKELY( !err->error ) ) {
     389           0 :         continue;
     390           0 :       }
     391             : 
     392           0 :       if( FD_LIKELY( err->error == -EBUSY ) ) {
     393             :         /* a workaround for some systems which return EBUSY, we will
     394             :          * not log, but instead retry one more time */
     395           0 :         return -1;
     396           0 :       }
     397             : 
     398           0 :       FD_LOG_WARNING(( "netlink returned data with error: %d %s", -err->error, strerror( -err->error) ));
     399             : 
     400             :       /* error occurred */
     401             : 
     402           0 :       return -1; /* return failure */
     403           0 :     }
     404             : 
     405          33 :     struct rtmsg *  msg       = NLMSG_DATA( h );
     406          33 :     struct rtattr * rat       = RTM_RTA(msg);
     407          33 :     long            ratmsglen = (long)RTM_PAYLOAD(h);
     408             : 
     409             :     /* only care about RTN_UNICAST */
     410          33 :     if( msg->rtm_type == RTN_UNICAST ) {
     411             : 
     412          42 :       while( RTA_OK( rat, ratmsglen ) ) {
     413          42 :         uchar * rta_data    = RTA_DATA( rat );
     414          42 :         ulong   rta_data_sz = RTA_PAYLOAD( rat );
     415             : 
     416          42 :         switch( rat->rta_type ) {
     417           3 :           case RTA_GATEWAY:
     418           3 :             if( rta_data_sz != 4 ) {
     419           0 :               FD_LOG_WARNING(( "Routing entry has gateway address with other than"
     420           0 :                     " 4 byte address" ));
     421           3 :             } else {
     422           3 :               uint nh_ip_addr;
     423           3 :               memcpy( &nh_ip_addr, rta_data, 4 );
     424             : 
     425           3 :               entry->nh_ip_addr     = ntohl( nh_ip_addr );
     426             : 
     427           3 :               entry->flags |= FD_NL_RT_FLAGS_NH_IP_ADDR;
     428           3 :             }
     429           3 :             break;
     430             : 
     431           6 :           case RTA_DST:
     432           6 :             if( rta_data_sz != 4 ) {
     433           0 :               FD_LOG_WARNING(( "Routing entry has destination address with other than"
     434           0 :                     " 4 byte destination address" ));
     435           6 :             } else {
     436           6 :               uint nh_ip_addr;
     437           6 :               memcpy( &nh_ip_addr, rta_data, 4 );
     438             : 
     439           6 :               entry->dst_netmask    = (uint)( 0xffffffff00000000LU >> (ulong)msg->rtm_dst_len );
     440           6 :               entry->dst_netmask_sz = (uint)msg->rtm_dst_len;
     441           6 :               entry->dst_ip_addr    = ntohl( nh_ip_addr ) & entry->dst_netmask;
     442             : 
     443           6 :               entry->flags |= FD_NL_RT_FLAGS_DST_IP_ADDR | FD_NL_RT_FLAGS_DST_NETMASK;
     444           6 :             }
     445           6 :             break;
     446             : 
     447           9 :           case RTA_OIF:
     448           9 :             if( rta_data_sz != 4 ) {
     449           0 :               FD_LOG_WARNING(( "Routing entry has output interface with other than"
     450           0 :                     " 4 byte index" ));
     451           9 :             } else {
     452           9 :               memcpy( &entry->oif, rta_data, 4 );
     453             : 
     454           9 :               entry->flags |= FD_NL_RT_FLAGS_OIF;
     455           9 :             }
     456           9 :             break;
     457             : 
     458           9 :           case RTA_PREFSRC:
     459           9 :             if( rta_data_sz != 4 ) {
     460           0 :               FD_LOG_WARNING(( "Routing entry has destination address with other than"
     461           0 :                     " 4 byte destination address" ));
     462           9 :             } else {
     463           9 :               uint src_ip_addr;
     464           9 :               memcpy( &src_ip_addr, rta_data, 4 );
     465             : 
     466           9 :               entry->src_ip_addr = ntohl( src_ip_addr );
     467             : 
     468           9 :               entry->flags |= FD_NL_RT_FLAGS_SRC_IP_ADDR;
     469           9 :             }
     470           9 :             break;
     471             : 
     472           0 :           case RTA_MULTIPATH:
     473           0 :           case RTA_VIA:
     474             :             /* not currently supported */
     475           0 :             FD_LOG_WARNING(( "Routing entry contains an unsupported feature: %s",
     476           0 :                   fd_rta_type_to_label( rat->rta_type ) ));
     477           0 :             entry->flags |= FD_NL_RT_FLAGS_UNSUPPORTED;
     478           0 :             break;
     479          42 :         }
     480             : 
     481          42 :         if( entry->flags & FD_NL_RT_FLAGS_UNSUPPORTED ) {
     482           0 :           break;
     483           0 :         }
     484             : 
     485          42 :         rat = RTA_NEXT( rat, ratmsglen );
     486          42 :       }
     487           9 :     }
     488             : 
     489             :     /* the FD_NL_RT_FLAGS_UNSUPPORTED flags must not be present */
     490          33 :     if( ( entry->flags & FD_NL_RT_FLAGS_UNSUPPORTED ) == 0 ) {
     491             :       /* supported combinations of flags */
     492          33 :       uint rqd0 = FD_NL_RT_FLAGS_DST_IP_ADDR |
     493          33 :                   FD_NL_RT_FLAGS_DST_NETMASK |
     494          33 :                   FD_NL_RT_FLAGS_OIF;
     495          33 :       uint rqd1 = FD_NL_RT_FLAGS_NH_IP_ADDR  |
     496          33 :                   FD_NL_RT_FLAGS_OIF;
     497          33 :       uint flags = entry->flags;
     498          33 :       if( ( flags & rqd0 ) == rqd0 || ( flags & rqd1 ) == rqd1 ) {
     499           9 :         entry->flags |= FD_NL_RT_FLAGS_USED;
     500           9 :         route_entry_idx++;
     501           9 :       }
     502          33 :     }
     503             : 
     504          33 :   }
     505             : 
     506             :   /* clear remaining entries */
     507          90 :   for( long j = route_entry_idx; j < (long)route_table_cap; ++j ) {
     508          87 :     fd_memset( route_table + j, 0, sizeof( route_table[0] ) );
     509          87 :   }
     510             : 
     511           3 :   return route_entry_idx;
     512           3 : }
     513             : 
     514             : 
     515             : fd_nl_route_entry_t *
     516         210 : fd_nl_route_query( fd_nl_route_entry_t * route_table, ulong route_table_sz, uint ip_addr ) {
     517         210 :   long best_idx   = -1;
     518         210 :   uint best_class = -1U;
     519             : 
     520        1572 :   for( long j = 0L; j < (long)route_table_sz; ++j ) {
     521        1488 :     fd_nl_route_entry_t * entry = route_table + j;
     522             : 
     523             :     /* the used entries are always contiguous */
     524        1488 :     if( ( entry->flags & FD_NL_RT_FLAGS_USED ) == 0 ) break;
     525             : 
     526        1362 :     uint netmask = entry->dst_netmask;
     527        1362 :     uint clazz   = entry->dst_netmask_sz;
     528        1362 :     uint nh_net  = entry->dst_ip_addr;
     529        1362 :     uint dst_net = ip_addr & netmask;
     530             : 
     531        1362 :     if( nh_net == dst_net && ( best_idx == -1 || clazz > best_class ) ) {
     532         195 :       best_idx   = j;
     533         195 :       best_class = clazz;
     534         195 :     }
     535        1362 :   }
     536             : 
     537         210 :   if( best_idx < 0L ) {
     538          15 :     return NULL;
     539         195 :   } else {
     540         195 :     return route_table + best_idx;
     541         195 :   }
     542         210 : }
     543             : 
     544             : 
     545             : long
     546             : fd_nl_load_arp_table( fd_nl_t *           nl,
     547             :                       fd_nl_arp_entry_t * arp_table,
     548           3 :                       ulong               arp_table_cap ) {
     549           3 :   int fd = nl->fd;
     550           3 :   if( fd < 0 ) return FD_IP_ERROR;
     551             : 
     552             :   /* format the request */
     553             : 
     554             :   /* Request struct */
     555           3 :   struct {
     556           3 :     struct nlmsghdr nlh;  /* Netlink header */
     557           3 :     struct ndmsg    ndm;  /* Payload - neighbor message */
     558           3 :   } nl_request;
     559             : 
     560           3 :   fd_memset( &nl_request, 0, sizeof( nl_request ) );
     561             : 
     562           3 :   uint seq = nl->seq++;
     563             : 
     564           3 :   nl_request.nlh.nlmsg_type  = RTM_GETNEIGH;  /* We wish to get neighbors */
     565           3 :   nl_request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
     566           3 :   nl_request.nlh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct rtmsg));
     567           3 :   nl_request.nlh.nlmsg_pid   = 0;
     568           3 :   nl_request.nlh.nlmsg_seq   = seq;
     569             : 
     570             :   /* request IPv4 entries */
     571           3 :   nl_request.ndm.ndm_family  = AF_INET;
     572             : 
     573           3 :   ulong send_sz = sizeof(nl_request);
     574           3 :   long  sent    = send( fd, &nl_request, send_sz, 0 );
     575           3 :   if( sent == -1 ) {
     576           0 :     FD_LOG_WARNING(( "Unable to make netlink request. Error: %d %s", errno, strerror( errno ) ));
     577           0 :     return FD_IP_ERROR;
     578           0 :   }
     579             : 
     580           3 :   if( sent != (long)send_sz ) {
     581           0 :     FD_LOG_WARNING(( "netlink send returned unexpected size: %ld expected: %lu", sent, send_sz ));
     582           0 :     return FD_IP_ERROR;
     583           0 :   }
     584             : 
     585             :   /* receiving */
     586           3 :   long len;
     587             : 
     588             :   /* set alignment such that the returned data is aligned */
     589           3 :   uchar __attribute__(( aligned(16) )) ibuf[FD_NL_BUF_SZ] = {0};
     590             : 
     591             :   /* index into arp_table */
     592           3 :   long arp_entry_idx = 0L;
     593             : 
     594           3 :   int done = 0;
     595             : 
     596           9 :   while( !done ) {
     597             : 
     598             :     /* Pointer to the messages head */
     599           6 :     struct nlmsghdr *h = (struct nlmsghdr *)ibuf;
     600             : 
     601           9 :     while(1) {
     602           9 :       len = fd_nl_read_socket( fd, ibuf, sizeof(ibuf) );
     603           9 :       if( len <= 0 ) {
     604           0 :         return FD_IP_ERROR;
     605           0 :       }
     606             : 
     607           9 :       if( h->nlmsg_seq == seq ) break;
     608           9 :     }
     609             : 
     610             :     /* interpret the response */
     611             : 
     612           6 :     long msglen = len;
     613             : 
     614             :     /* Iterate through all messages in buffer */
     615             : 
     616          12 :     while( NLMSG_OK(h, msglen) ) {
     617             :       /* are we still within the bounds of the table? */
     618          12 :       if( arp_entry_idx >= (long)arp_table_cap ) {
     619             :         /* we filled the table */
     620           0 :         FD_LOG_ERR(( "fd_nl_load_arp_table, but table larger than reserved storage" ));
     621           0 :         return FD_IP_ERROR; /* return failure */
     622           0 :       }
     623             : 
     624             :       /* current arp entry */
     625          12 :       fd_nl_arp_entry_t * entry = arp_table + arp_entry_idx;
     626             : 
     627             :       /* clear current entry */
     628          12 :       fd_memset( entry, 0, sizeof( *entry ) );
     629             : 
     630          12 :       if( h->nlmsg_flags & NLM_F_DUMP_INTR ) {
     631             :         /* function was interrupted - don't switch to new routing table */
     632             : 
     633           0 :         return FD_IP_RETRY; /* return transient failure */
     634           0 :       }
     635             : 
     636          12 :       if( h->nlmsg_type == NLMSG_ERROR ) {
     637           0 :         struct nlmsgerr * err = NLMSG_DATA(h);
     638             : 
     639           0 :         FD_LOG_WARNING(( "netlink returned data with error: %d %s", -err->error, strerror( -err->error) ));
     640             : 
     641             :         /* error occurred - don't switch to new routing table */
     642             : 
     643           0 :         return FD_IP_ERROR; /* return failure */
     644           0 :       }
     645             : 
     646             :       /* we're done if:
     647             :        *        type is NLMSG_DONE
     648             :        *    OR  nlmsg_flags does not contain NLM_F_MULTI */
     649          12 :       done |= ( h->nlmsg_type == NLMSG_DONE ) | !( h->nlmsg_flags & NLM_F_MULTI );
     650             : 
     651          12 :       struct ndmsg *  msg       = NLMSG_DATA( h );
     652          12 :       struct rtattr * rat       = RTM_RTA(msg);
     653          12 :       long            ratmsglen = (long)RTM_PAYLOAD( h );
     654             : 
     655             :       /* store the interface */
     656          12 :       entry->ifindex = (uint)msg->ndm_ifindex;
     657          12 :       entry->flags  |= FD_NL_ARP_FLAGS_IFINDEX;
     658             : 
     659             :       /* we want to skip some entries based on state
     660             :          here are the states:
     661             :          NUD_INCOMPLETE   a currently resolving cache entry
     662             :          NUD_REACHABLE    a confirmed working cache entry
     663             :          NUD_STALE        an expired cache entry
     664             :          NUD_DELAY        an entry waiting for a timer
     665             :          NUD_PROBE        a cache entry that is currently reprobed
     666             :          NUD_FAILED       an invalid cache entry
     667             :          NUD_NOARP        a device with no destination cache
     668             :          NUD_PERMANENT    a static entry
     669             : 
     670             : Keeping:
     671             : NUD_REACHABLE    valid, so use it
     672             : NUD_STALE        probably better to use the existing address
     673             : than wait or discard packet
     674             : NUD_DELAY        an entry waiting for a timer
     675             : NUD_PROBE        being reprobed, so it's probably valid
     676             : NUD_PERMANENT    a static entry, so use it */
     677             : 
     678          36 :       while( RTA_OK( rat, ratmsglen ) ) {
     679          36 :         uchar * rta_data    = RTA_DATA( rat );
     680          36 :         ulong   rta_data_sz = RTA_PAYLOAD( rat );
     681             : 
     682          36 :         switch( rat->rta_type ) {
     683           9 :           case NDA_DST:
     684           9 :             if( rta_data_sz != 4 ) {
     685             :               /* we only support IPv4 */
     686           0 :               entry->flags |= FD_NL_ARP_FLAGS_UNSUPPORTED;
     687           9 :             } else {
     688           9 :               uint dst_ip_addr;
     689           9 :               memcpy( &dst_ip_addr, rta_data, 4 );
     690           9 :               entry->dst_ip_addr = ntohl( dst_ip_addr );
     691             : 
     692           9 :               entry->flags |= FD_NL_ARP_FLAGS_IP_ADDR;
     693           9 :             }
     694           9 :             break;
     695             : 
     696           9 :           case NDA_LLADDR:
     697           9 :             if( FD_UNLIKELY( rta_data_sz != 6 ) ) {
     698             :               /* we only support ethernet, but these entries are found */
     699             :               /* on some machines, so silently skip them */
     700           0 :               entry->flags |= FD_NL_ARP_FLAGS_UNSUPPORTED;
     701           9 :             } else {
     702           9 :               memcpy( &entry->mac_addr[0], rta_data, 6 );
     703             : 
     704           9 :               entry->flags |= FD_NL_ARP_FLAGS_MAC_ADDR;
     705           9 :             }
     706           9 :             break;
     707             : 
     708          36 :         }
     709             : 
     710          36 :         if( entry->flags & FD_NL_ARP_FLAGS_UNSUPPORTED ) {
     711           0 :           break;
     712           0 :         }
     713             : 
     714          36 :         rat = RTA_NEXT( rat, ratmsglen );
     715          36 :       }
     716             : 
     717             :       /* at present, each entry must have at least the following:
     718             :          FD_NL_ARP_FLAGS_IP_ADDR
     719             :          FD_NL_ARP_FLAGS_MAC_ADDR
     720             :          FD_NL_ARP_FLAGS_IFINDEX */
     721             : 
     722          12 :       uint required_flags = FD_NL_ARP_FLAGS_IP_ADDR | FD_NL_ARP_FLAGS_MAC_ADDR | FD_NL_ARP_FLAGS_IFINDEX;
     723             : 
     724             :       /* the FD_NL_ARP_FLAGS_UNSUPPORTED flags must not be present */
     725          12 :       if(    ( entry->flags & FD_NL_ARP_FLAGS_UNSUPPORTED ) == 0
     726          12 :           && ( entry->flags & required_flags )              == required_flags ) {
     727           9 :         entry->flags |= FD_NL_ARP_FLAGS_USED;
     728           9 :         entry->state  = msg->ndm_state;
     729           9 :         arp_entry_idx++;
     730           9 :       }
     731             : 
     732          12 :       h = NLMSG_NEXT( h, msglen );
     733          12 :     }
     734           6 :   }
     735             : 
     736             :   /* clear remaining entries */
     737          90 :   for( long j = arp_entry_idx; j < (long)arp_table_cap; ++j ) {
     738          87 :     fd_memset( arp_table + j, 0, sizeof( arp_table[0] ) );
     739          87 :   }
     740             : 
     741           3 :   return arp_entry_idx;
     742           3 : }
     743             : 
     744             : 
     745             : fd_nl_arp_entry_t *
     746             : fd_nl_arp_query( fd_nl_arp_entry_t * arp_table,
     747             :                  ulong               arp_table_sz,
     748         138 :                  uint                ip_addr ) {
     749         630 :   for( long j = 0L; j < (long)arp_table_sz; ++j ) {
     750         600 :     fd_nl_arp_entry_t * entry = arp_table + j;
     751         600 :     if( ( entry->flags & FD_NL_ARP_FLAGS_USED ) == 0 ) break;
     752             : 
     753         600 :     if( entry->dst_ip_addr == ip_addr ) {
     754         108 :       return entry;
     755         108 :     }
     756         600 :   }
     757             : 
     758          30 :   return NULL;
     759         138 : }
     760             : 
     761             : 
     762             : int
     763             : fd_nl_update_arp_table( fd_nl_t *           nl,
     764             :                         fd_nl_arp_entry_t * arp_table,
     765             :                         ulong               arp_table_cap,
     766             :                         uint                ip_addr,
     767           0 :                         uint                ifindex ) {
     768           0 :   int rtn = FD_IP_ERROR;
     769             : 
     770           0 :   int fd = nl->fd;
     771           0 :   if( fd < 0 ) {
     772           0 :     FD_LOG_ERR(( "fd_nl_update_arp_table called with invalid file descriptor" ));
     773           0 :     return FD_IP_ERROR;
     774           0 :   }
     775             : 
     776             :   /* find the entry, if one exists */
     777           0 :   int  cur_state = -1; /* -1 indicates no existing entry */
     778           0 :   for( ulong j = 0; j < arp_table_cap; ++j ) {
     779           0 :     fd_nl_arp_entry_t * entry = &arp_table[j];
     780             : 
     781             :     /* indicates the end */
     782           0 :     if( ( entry->flags & FD_NL_RT_FLAGS_USED ) == 0 ) break;
     783             : 
     784           0 :     if( entry->dst_ip_addr == ip_addr ) {
     785           0 :       cur_state = (int)entry->state;
     786           0 :       break;
     787           0 :     }
     788           0 :   }
     789             : 
     790             :   /* ARP state transitions
     791             : 
     792             :      NONE  --->  INCOMPLETE ---+
     793             :                                |
     794             :      DELAY --->  PROBE         |
     795             :        ^           |           |
     796             :        |           V           |
     797             :      STALE <---  REACHABLE <---+
     798             : 
     799             :      PERMANENT and NOARP do not participate */
     800             : 
     801             :   /* nothing to do - either in final state, or kernel handles the transition */
     802           0 :   int rtn_states = (int)( NUD_NOARP     |
     803           0 :                           NUD_PERMANENT |
     804           0 :                           NUD_DELAY     |
     805           0 :                           NUD_STALE     |
     806           0 :                           NUD_REACHABLE );
     807           0 :   if( cur_state > 0 && ( cur_state & rtn_states ) ) return FD_IP_SUCCESS;
     808             : 
     809             :   /* determine next state */
     810           0 :   int next_state = 0;
     811           0 :   switch( cur_state ) {
     812           0 :     case -1:             next_state = NUD_NONE;       rtn = FD_IP_RETRY;     break;
     813           0 :     case NUD_NONE:       next_state = NUD_INCOMPLETE; rtn = FD_IP_PROBE_RQD; break;
     814           0 :     case NUD_INCOMPLETE: return FD_IP_PROBE_RQD;
     815           0 :     case NUD_PROBE:      return FD_IP_PROBE_RQD;
     816           0 :     default:
     817           0 :       FD_LOG_WARNING(( "Unexpected state: %d", cur_state ));
     818           0 :       return FD_IP_ERROR;
     819           0 :   }
     820             : 
     821             :   /* TODO For NUD_STALE, the kernel may never see UDP packets from the IP
     822             :      (We steal them)
     823             :      In this case, the kernel will push to DELAY and then PROBE resulting
     824             :      in unnecessary ARP probes to be sent.
     825             :      What we should do is update the stale ARP entry to NUD_REACHABLE when
     826             :      we receive a packet from local IPs */
     827             : 
     828           0 :   uint net_ip_addr = htonl( ip_addr );
     829             : 
     830             :   /* format the request */
     831             : 
     832           0 : # define IP_ADDR_LEN 4
     833           0 : # define NLMSG_LEN   NLMSG_LENGTH( sizeof(struct rtmsg) )
     834           0 : # define TOT_SZ      ( NLMSG_LEN + RTA_LENGTH(IP_ADDR_LEN) )
     835           0 : # define BUF_OFFS    ( sizeof( struct nlmsghdr ) + sizeof( struct ndmsg ) )
     836           0 : # define BUF_SZ      ( TOT_SZ - BUF_OFFS )
     837             : 
     838             :   /* Request struct */
     839           0 :   struct {
     840           0 :     struct nlmsghdr nlh;         /* Netlink header */
     841           0 :     struct ndmsg    ndm;         /* Payload - neighbor message */
     842           0 :     uchar           buf[BUF_SZ]; /* sized to match the request exactly */
     843           0 :   } nl_request;
     844             : 
     845           0 :   fd_memset( &nl_request, 0, sizeof( nl_request ) );
     846             : 
     847           0 :   uint seq = nl->seq++;
     848             : 
     849           0 :   nl_request.nlh.nlmsg_type  = RTM_NEWNEIGH;  /* We wish to get neighbors */
     850           0 :   nl_request.nlh.nlmsg_flags = cur_state == -1 ? ( NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL )
     851           0 :                                                : ( NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE );
     852           0 :   nl_request.nlh.nlmsg_len   = (uint)NLMSG_LEN;
     853           0 :   nl_request.nlh.nlmsg_pid   = 0;
     854           0 :   nl_request.nlh.nlmsg_seq   = seq;
     855             : 
     856             :   /* request IPv4 entries */
     857           0 :   nl_request.ndm.ndm_family  = AF_INET;
     858           0 :   nl_request.ndm.ndm_ifindex = (int)ifindex;
     859           0 :   nl_request.ndm.ndm_state   = (ushort)next_state;
     860           0 :   nl_request.ndm.ndm_flags   = 0;
     861           0 :   nl_request.ndm.ndm_type    = RTN_UNICAST;
     862             : 
     863             :   /* not necessarily defined! */
     864           0 : #ifndef NLMSG_TAIL
     865           0 : #define NLMSG_TAIL(nmsg) ((struct rtattr *)(((uchar *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
     866           0 : #endif
     867             : 
     868             :   /* write required attributes */
     869           0 :   struct rtattr * rqs_rat = NLMSG_TAIL(&nl_request.nlh);
     870             : 
     871           0 :   rqs_rat->rta_type = NDA_DST;
     872           0 :   rqs_rat->rta_len =  RTA_LENGTH(4);
     873             : 
     874           0 :   memcpy( RTA_DATA(rqs_rat), &net_ip_addr, 4 );
     875           0 :   nl_request.nlh.nlmsg_len += (int)sizeof(struct rtattr) + 4;
     876             : 
     877           0 :   ulong nl_request_sz = (ulong)nl_request.nlh.nlmsg_len;
     878             : 
     879           0 :   if( nl_request_sz != TOT_SZ ) {
     880           0 :     FD_LOG_ERR(( "request size does not match expected size" ));
     881           0 :     return FD_IP_ERROR;
     882           0 :   }
     883             : 
     884           0 :   ulong send_sz = nl_request_sz;
     885           0 :   long  sent    = send( fd, &nl_request, send_sz, 0 );
     886           0 :   if( sent == -1 ) {
     887           0 :     FD_LOG_WARNING(( "Unable to make netlink request. Error: %d %s", errno, strerror( errno ) ));
     888           0 :     return FD_IP_ERROR;
     889           0 :   }
     890             : 
     891           0 :   if( sent != (long)send_sz ) {
     892           0 :     FD_LOG_WARNING(( "netlink send returned unexpected size: %ld expected: %lu", sent, send_sz ));
     893           0 :     return FD_IP_ERROR;
     894           0 :   }
     895             : 
     896             :   /* receiving */
     897           0 :   long len;
     898             : 
     899             :   /* set alignment such that the returned data is aligned */
     900           0 :   uchar __attribute__(( aligned(16) )) ibuf[FD_NL_BUF_SZ] = {0};
     901             : 
     902             :   /* Pointer to the messages head */
     903           0 :   struct nlmsghdr *h = (struct nlmsghdr *)ibuf;
     904             : 
     905           0 :   while(1) {
     906           0 :     len = fd_nl_read_socket( fd, ibuf, sizeof(ibuf) );
     907           0 :     if( len <= 0 ) {
     908           0 :       return FD_IP_ERROR;
     909           0 :     }
     910             : 
     911           0 :     if( h->nlmsg_seq == seq ) break;
     912           0 :   }
     913             : 
     914             :   /* interpret the response */
     915             : 
     916           0 :   long msglen = len;
     917             : 
     918             :   /* check for errors */
     919             : 
     920           0 :   while( NLMSG_OK(h, msglen) ) {
     921           0 :     if( h->nlmsg_type == NLMSG_ERROR ) {
     922           0 :       struct nlmsgerr * err = NLMSG_DATA(h);
     923             : 
     924             :       /* we are expecting the ARP entry to sometimes exist
     925             :          so simply return SUCCESS here */
     926           0 :       if( err->error != -EEXIST && err->error != 0 ) {
     927             :         /* all other errors are reported */
     928             : 
     929           0 :         fd_dump_nla_err( h, ip_addr, ifindex );
     930             : 
     931             :         /* error occurred - don't switch to new routing table */
     932             : 
     933           0 :         return FD_IP_ERROR; /* return failure */
     934           0 :       }
     935           0 :     }
     936             : 
     937           0 :     h = NLMSG_NEXT( h, msglen );
     938           0 :   }
     939             : 
     940             :   /* state has changed, reload table */
     941           0 :   fd_nl_load_arp_table( nl, arp_table, arp_table_cap );
     942             : 
     943           0 :   return rtn;
     944           0 : }
     945             : 
     946             : /* dump netlink extended ack error message */
     947             : void
     948           0 : fd_dump_nla_err( struct nlmsghdr * nlh, uint ip_addr, uint ifindex ) {
     949             : 
     950           0 : #define FD_NLA_ERR_DEFS(X,...) \
     951           0 :   X( msg  , NLMSGERR_ATTR_MSG  , CSTRING , __VA_ARGS__ ) \
     952           0 :   X( offs , NLMSGERR_ATTR_OFFS , UINT    , __VA_ARGS__ )
     953             : 
     954           0 : #   define FD_NLA_MBR(MBR,ATTR,TYPE,...) FD_NLA_MBR_TYPE_##TYPE MBR;
     955           0 : #   define FD_NLA_MBR_TYPE_CSTRING char const *
     956           0 : #   define FD_NLA_MBR_TYPE_UINT    uint
     957             : 
     958           0 : #   define FD_NLA_CPY(MBR,TYPE,DATA) FD_NLA_CPY_##TYPE(MBR,DATA)
     959           0 : #   define FD_NLA_CPY_CSTRING(MBR,DATA) (char const *)data
     960           0 : #   define FD_NLA_CPY_UINT(MBR,DATA)    *(uint *)data
     961             : 
     962           0 :   struct fd_err_attr {
     963           0 :     FD_NLA_ERR_DEFS(FD_NLA_MBR,x,y)
     964           0 :   } err_attr = {0};
     965             : 
     966           0 :   struct nlmsgerr * err = NLMSG_DATA( nlh );
     967             : 
     968             :   /* no extended info, just report what we have */
     969           0 :   if( ( nlh->nlmsg_flags & NLM_F_ACK_TLVS ) == 0 ) {
     970           0 : #   define EXPAND_IP4(ip) (((ip)>>24u)&0xffU), (((ip)>>16u)&0xffU), (((ip)>>8u)&0xffU), ((ip)&0xffU)
     971           0 :     FD_LOG_WARNING(( "netlink returned data with error: %d %s"
     972           0 :           " adding ip address: %u.%u.%u.%u on ifindex: %u",
     973           0 :           -err->error, strerror( -err->error ),
     974           0 :           EXPAND_IP4( (uint)ip_addr ),
     975           0 :           (uint)ifindex ));
     976           0 : #   undef EXPAND_IP4
     977           0 :     return;
     978           0 :   }
     979             : 
     980           0 :   struct nlattr *attr = NULL;
     981             : 
     982           0 :   int hlen = sizeof(*err);
     983             : 
     984             :   /* if NLM_F_CAPPED is set then the inner err msg was capped */
     985           0 :   if( !(nlh->nlmsg_flags & NLM_F_CAPPED) ) {
     986           0 :     hlen += (int)err->msg.nlmsg_len - (int)NLMSG_HDRLEN;
     987           0 :   }
     988             : 
     989           0 :   attr = (struct nlattr *)( (uchar *)err + hlen );
     990           0 :   int alen = (int)( (uchar *)nlh + nlh->nlmsg_len - (uchar *)attr );
     991             : 
     992           0 : # ifndef NLA_OK
     993           0 : #   define NLA_OK(NLA,REM)                      \
     994           0 :      (                                          \
     995           0 :        (REM)          >= (int)sizeof(*(NLA)) && \
     996           0 :        (NLA)->nla_len >= sizeof(*(NLA))      && \
     997           0 :        (REM)          >= (NLA)->nla_len         \
     998           0 :      )
     999           0 : # endif
    1000             : 
    1001           0 : # ifndef NLA_NEXT
    1002           0 : # define NLA_NEXT_ALIGN(NLA,REM,TOTLEN) \
    1003           0 :     ( (*(REM) -= (TOTLEN) ), (struct nlattr *)((uchar *)(NLA) + (TOTLEN)) )
    1004           0 : # define NLA_NEXT(NLA,REM) NLA_NEXT_ALIGN(NLA,REM,NLA_ALIGN((NLA)->nla_len))
    1005           0 : # endif
    1006             : 
    1007             :   /* the NLA_HDRLEN macro in netlink.h causes sign conversion errors
    1008             :      so replacing with this: */
    1009           0 : #define FD_NLA_ALIGN(len) (((long)(len) +   (long)(NLA_ALIGNTO) - 1L) & \
    1010           0 :                                           ~((long)(NLA_ALIGNTO) - 1L))
    1011           0 : #define FD_NLA_HDRLEN     ((long)(FD_NLA_ALIGN(sizeof(struct nlattr))))
    1012             : 
    1013             :   /* walk thru extended error attributes and populate err_attr */
    1014           0 :   for( int rem = alen; NLA_OK( attr, rem ); attr = NLA_NEXT(attr,&rem) ) {
    1015             :     /* get type */
    1016           0 :     int type = attr->nla_type & NLA_TYPE_MASK;
    1017           0 :     void * data = (void*)( (long)attr + FD_NLA_HDRLEN );
    1018             : 
    1019             :     /* lookup type */
    1020             : 
    1021           0 : #   define FD_NLA_POPULATE( MBR, ATTR, TYPE, ATTR_VAR, ... ) \
    1022           0 :       if( type == ATTR ) {                                   \
    1023           0 :         (ATTR_VAR).MBR = FD_NLA_CPY(MBR,TYPE,data);          \
    1024           0 :       } else
    1025             : 
    1026           0 :     FD_NLA_ERR_DEFS( FD_NLA_POPULATE, err_attr, dummy ) {
    1027             :       /* ignore */
    1028           0 :     }
    1029           0 :   }
    1030             : 
    1031           0 : # define EXPAND_IP4(ip) (((ip)>>24u)&0xffu), (((ip)>>16u)&0xffu), (((ip)>>8u)&0xffu), ((ip)&0xffu)
    1032           0 :   FD_LOG_WARNING(( "netlink returned data with error: %d %s"
    1033           0 :         " adding ip address: %u.%u.%u.%u  on ifindex: %u"
    1034           0 :         " extended info: %s  offset: %u",
    1035           0 :         -err->error, strerror( -err->error ),
    1036           0 :         EXPAND_IP4( (uint)ip_addr ),
    1037           0 :         (uint)ifindex, (char const *)err_attr.msg,
    1038           0 :         (uint)err_attr.offs ));
    1039           0 : # undef EXPAND_IP4
    1040             : 
    1041           0 :   return;
    1042           0 : }

Generated by: LCOV version 1.14