From 6072ed9a96daee3844fc2cf43b3e2dc8c2b73041 Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Wed, 12 Feb 2025 20:53:50 +0000 Subject: [PATCH] Trigger ARP requests via sockets, not rtnetlink Improves sandbox architecture --- book/api/metrics-generated.md | 6 +- .../metrics/generated/fd_metrics_netlnk.c | 6 +- .../metrics/generated/fd_metrics_netlnk.h | 34 +++-- src/disco/metrics/metrics.xml | 6 +- src/disco/netlink/fd_netlink_tile.c | 30 ++-- src/disco/netlink/fd_netlink_tile_private.h | 4 + src/disco/netlink/generated/netlink_seccomp.h | 74 ++++++---- src/disco/netlink/netlink.seccomppolicy | 27 +++- src/waltz/fd_token_bucket.h | 39 +++++ src/waltz/neigh/Local.mk | 6 +- src/waltz/neigh/fd_neigh4_map.c | 30 ++-- src/waltz/neigh/fd_neigh4_map.h | 6 +- src/waltz/neigh/fd_neigh4_netlink.c | 72 ---------- src/waltz/neigh/fd_neigh4_netlink.h | 12 -- src/waltz/neigh/fd_neigh4_probe.c | 81 +++++++++++ src/waltz/neigh/fd_neigh4_probe.h | 134 ++++++++++++++++++ src/waltz/neigh/test_neigh4_netlink.c | 2 - 17 files changed, 405 insertions(+), 164 deletions(-) create mode 100644 src/waltz/fd_token_bucket.h create mode 100644 src/waltz/neigh/fd_neigh4_probe.c create mode 100644 src/waltz/neigh/fd_neigh4_probe.h diff --git a/book/api/metrics-generated.md b/book/api/metrics-generated.md index e5f531ff48..f03a7dbbf1 100644 --- a/book/api/metrics-generated.md +++ b/book/api/metrics-generated.md @@ -501,5 +501,7 @@ | netlnk_​interface_​count | `gauge` | Number of network interfaces | | netlnk_​route_​count_​local | `gauge` | Number of IPv4 routes (Local) | | netlnk_​route_​count_​main | `gauge` | Number of IPv4 routes (Main) | -| netlnk_​neighbor_​solicits_​sent | `counter` | Number of neighbor solicit requests sent to kernel | -| netlnk_​neighbor_​solicits_​fails | `counter` | Number of neighbor solicit requests that failed to send | +| netlnk_​neigh_​probe_​sent | `counter` | Number of neighbor solicit requests sent to kernel | +| netlnk_​neigh_​probe_​fails | `counter` | Number of neighbor solicit requests that failed to send (kernel too slow) | +| netlnk_​neigh_​probe_​rate_​limit_​host | `counter` | Number of neighbor solicit that exceeded the per-host rate limit | +| netlnk_​neigh_​probe_​rate_​limit_​global | `counter` | Number of neighbor solicit that exceeded the global rate limit | diff --git a/src/disco/metrics/generated/fd_metrics_netlnk.c b/src/disco/metrics/generated/fd_metrics_netlnk.c index a401144e9f..315e4bfbd7 100644 --- a/src/disco/metrics/generated/fd_metrics_netlnk.c +++ b/src/disco/metrics/generated/fd_metrics_netlnk.c @@ -11,6 +11,8 @@ const fd_metrics_meta_t FD_METRICS_NETLNK[FD_METRICS_NETLNK_TOTAL] = { DECLARE_METRIC( NETLNK_INTERFACE_COUNT, GAUGE ), DECLARE_METRIC_ENUM( NETLNK_ROUTE_COUNT, GAUGE, ROUTE_TABLE, LOCAL ), DECLARE_METRIC_ENUM( NETLNK_ROUTE_COUNT, GAUGE, ROUTE_TABLE, MAIN ), - DECLARE_METRIC( NETLNK_NEIGHBOR_SOLICITS_SENT, COUNTER ), - DECLARE_METRIC( NETLNK_NEIGHBOR_SOLICITS_FAILS, COUNTER ), + DECLARE_METRIC( NETLNK_NEIGH_PROBE_SENT, COUNTER ), + DECLARE_METRIC( NETLNK_NEIGH_PROBE_FAILS, COUNTER ), + DECLARE_METRIC( NETLNK_NEIGH_PROBE_RATE_LIMIT_HOST, COUNTER ), + DECLARE_METRIC( NETLNK_NEIGH_PROBE_RATE_LIMIT_GLOBAL, COUNTER ), }; diff --git a/src/disco/metrics/generated/fd_metrics_netlnk.h b/src/disco/metrics/generated/fd_metrics_netlnk.h index 6eda56d6f1..dd884acdb0 100644 --- a/src/disco/metrics/generated/fd_metrics_netlnk.h +++ b/src/disco/metrics/generated/fd_metrics_netlnk.h @@ -48,17 +48,29 @@ #define FD_METRICS_GAUGE_NETLNK_ROUTE_COUNT_LOCAL_OFF (23UL) #define FD_METRICS_GAUGE_NETLNK_ROUTE_COUNT_MAIN_OFF (24UL) -#define FD_METRICS_COUNTER_NETLNK_NEIGHBOR_SOLICITS_SENT_OFF (25UL) -#define FD_METRICS_COUNTER_NETLNK_NEIGHBOR_SOLICITS_SENT_NAME "netlnk_neighbor_solicits_sent" -#define FD_METRICS_COUNTER_NETLNK_NEIGHBOR_SOLICITS_SENT_TYPE (FD_METRICS_TYPE_COUNTER) -#define FD_METRICS_COUNTER_NETLNK_NEIGHBOR_SOLICITS_SENT_DESC "Number of neighbor solicit requests sent to kernel" -#define FD_METRICS_COUNTER_NETLNK_NEIGHBOR_SOLICITS_SENT_CVT (FD_METRICS_CONVERTER_NONE) +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_SENT_OFF (25UL) +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_SENT_NAME "netlnk_neigh_probe_sent" +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_SENT_TYPE (FD_METRICS_TYPE_COUNTER) +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_SENT_DESC "Number of neighbor solicit requests sent to kernel" +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_SENT_CVT (FD_METRICS_CONVERTER_NONE) -#define FD_METRICS_COUNTER_NETLNK_NEIGHBOR_SOLICITS_FAILS_OFF (26UL) -#define FD_METRICS_COUNTER_NETLNK_NEIGHBOR_SOLICITS_FAILS_NAME "netlnk_neighbor_solicits_fails" -#define FD_METRICS_COUNTER_NETLNK_NEIGHBOR_SOLICITS_FAILS_TYPE (FD_METRICS_TYPE_COUNTER) -#define FD_METRICS_COUNTER_NETLNK_NEIGHBOR_SOLICITS_FAILS_DESC "Number of neighbor solicit requests that failed to send" -#define FD_METRICS_COUNTER_NETLNK_NEIGHBOR_SOLICITS_FAILS_CVT (FD_METRICS_CONVERTER_NONE) +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_FAILS_OFF (26UL) +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_FAILS_NAME "netlnk_neigh_probe_fails" +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_FAILS_TYPE (FD_METRICS_TYPE_COUNTER) +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_FAILS_DESC "Number of neighbor solicit requests that failed to send (kernel too slow)" +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_FAILS_CVT (FD_METRICS_CONVERTER_NONE) -#define FD_METRICS_NETLNK_TOTAL (11UL) +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_RATE_LIMIT_HOST_OFF (27UL) +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_RATE_LIMIT_HOST_NAME "netlnk_neigh_probe_rate_limit_host" +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_RATE_LIMIT_HOST_TYPE (FD_METRICS_TYPE_COUNTER) +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_RATE_LIMIT_HOST_DESC "Number of neighbor solicit that exceeded the per-host rate limit" +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_RATE_LIMIT_HOST_CVT (FD_METRICS_CONVERTER_NONE) + +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_RATE_LIMIT_GLOBAL_OFF (28UL) +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_RATE_LIMIT_GLOBAL_NAME "netlnk_neigh_probe_rate_limit_global" +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_RATE_LIMIT_GLOBAL_TYPE (FD_METRICS_TYPE_COUNTER) +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_RATE_LIMIT_GLOBAL_DESC "Number of neighbor solicit that exceeded the global rate limit" +#define FD_METRICS_COUNTER_NETLNK_NEIGH_PROBE_RATE_LIMIT_GLOBAL_CVT (FD_METRICS_CONVERTER_NONE) + +#define FD_METRICS_NETLNK_TOTAL (13UL) extern const fd_metrics_meta_t FD_METRICS_NETLNK[FD_METRICS_NETLNK_TOTAL]; diff --git a/src/disco/metrics/metrics.xml b/src/disco/metrics/metrics.xml index 46aba35b45..2e455f6b2b 100644 --- a/src/disco/metrics/metrics.xml +++ b/src/disco/metrics/metrics.xml @@ -642,8 +642,10 @@ metric introduced. - - + + + + diff --git a/src/disco/netlink/fd_netlink_tile.c b/src/disco/netlink/fd_netlink_tile.c index 99c18e5212..4e97ca168d 100644 --- a/src/disco/netlink/fd_netlink_tile.c +++ b/src/disco/netlink/fd_netlink_tile.c @@ -11,11 +11,13 @@ #include #include +#include /* MSG_DONTWAIT */ #include /* SOL_{...} */ #include /* getrandom */ #include /* struct timeval */ #include /* RTM_{...} */ +#define FD_SOCKADDR_IN_SZ sizeof(struct sockaddr_in) #include "generated/netlink_seccomp.h" /* Hardcoded limits */ @@ -103,7 +105,7 @@ populate_allowed_seccomp( fd_topo_t const * topo, struct sock_filter * out ) { fd_netlink_tile_ctx_t * ctx = fd_topo_obj_laddr( topo, tile->tile_obj_id ); FD_TEST( ctx->magic==FD_NETLINK_TILE_CTX_MAGIC ); - populate_sock_filter_policy_netlink( out_cnt, out, (uint)fd_log_private_logfile_fd(), (uint)ctx->nl_monitor->fd, (uint)ctx->nl_req->fd ); + populate_sock_filter_policy_netlink( out_cnt, out, (uint)fd_log_private_logfile_fd(), (uint)ctx->nl_monitor->fd, (uint)ctx->nl_req->fd, (uint)ctx->prober->sock_fd ); return sock_filter_policy_netlink_instr_cnt; } @@ -115,7 +117,7 @@ populate_allowed_fds( fd_topo_t const * topo, fd_netlink_tile_ctx_t * ctx = fd_topo_obj_laddr( topo, tile->tile_obj_id ); FD_TEST( ctx->magic==FD_NETLINK_TILE_CTX_MAGIC ); - if( FD_UNLIKELY( out_fds_cnt<4UL ) ) FD_LOG_ERR(( "out_fds_cnt too low (%lu)", out_fds_cnt )); + if( FD_UNLIKELY( out_fds_cnt<5UL ) ) FD_LOG_ERR(( "out_fds_cnt too low (%lu)", out_fds_cnt )); ulong out_cnt = 0UL; out_fds[ out_cnt++ ] = 2; /* stderr */ @@ -123,6 +125,7 @@ populate_allowed_fds( fd_topo_t const * topo, out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */ out_fds[ out_cnt++ ] = ctx->nl_monitor->fd; out_fds[ out_cnt++ ] = ctx->nl_req->fd; + out_fds[ out_cnt++ ] = ctx->prober->sock_fd; return out_cnt; } @@ -160,6 +163,11 @@ privileged_init( fd_topo_t * topo, FD_LOG_ERR(( "bind(sock,RT_NETLINK,RTMGRP_{LINK,NEIGH,IPV4_ROUTE}) failed (%i-%s)", errno, fd_io_strerror( errno ) )); } + float const max_probes_per_second = 3.f; + ulong const max_probe_burst = 128UL; + float const probe_delay_seconds = 15.f; + fd_neigh4_prober_init( ctx->prober, max_probes_per_second, max_probe_burst, probe_delay_seconds ); + /* Set duration of blocking reads in before_credit */ struct timeval tv = { .tv_usec = 3753, }; /* 3.75ms */ if( FD_UNLIKELY( 0!=setsockopt( ctx->nl_monitor->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval) ) ) ) { @@ -219,8 +227,10 @@ metrics_write( fd_netlink_tile_ctx_t * ctx ) { FD_MGAUGE_SET( NETLNK, INTERFACE_COUNT, ctx->netdev_tbl->hdr->dev_cnt ); FD_MGAUGE_SET( NETLNK, ROUTE_COUNT_LOCAL, fd_fib4_cnt( ctx->fib4_local ) ); FD_MGAUGE_SET( NETLNK, ROUTE_COUNT_MAIN, fd_fib4_cnt( ctx->fib4_main ) ); - FD_MCNT_SET( NETLNK, NEIGHBOR_SOLICITS_SENT, ctx->metrics.neigh_solicits_sent ); - FD_MCNT_SET( NETLNK, NEIGHBOR_SOLICITS_FAILS, ctx->metrics.neigh_solicits_fails ); + FD_MCNT_SET( NETLNK, NEIGH_PROBE_SENT, ctx->metrics.neigh_solicits_sent ); + FD_MCNT_SET( NETLNK, NEIGH_PROBE_FAILS, ctx->metrics.neigh_solicits_fails ); + FD_MCNT_SET( NETLNK, NEIGH_PROBE_RATE_LIMIT_HOST, ctx->prober->local_rate_limited_cnt ); + FD_MCNT_SET( NETLNK, NEIGH_PROBE_RATE_LIMIT_GLOBAL, ctx->prober->global_rate_limited_cnt ); } /* netlink_monitor_read calls recvfrom to process a link, route, or @@ -344,6 +354,7 @@ after_frag( fd_netlink_tile_ctx_t * ctx, fd_stem_context_t * stem ) { (void)in_idx; (void)seq; (void)tsorig; (void)stem; + long now = fd_tickcount(); ctx->idle_cnt = -1L; /* Parse request (fully contained in sig field) */ @@ -386,16 +397,13 @@ after_frag( fd_netlink_tile_ctx_t * ctx, /* Trigger neighbor solicit via netlink */ - int netlink_res = fd_neigh4_netlink_solicit( ctx->nl_req, if_idx, ip4_addr ); - if( FD_UNLIKELY( netlink_res!=0 ) ) { - FD_LOG_WARNING(( "`ip neigh add " FD_IP4_ADDR_FMT " dev %u use nud incomplete` failed (%i-%s)", - FD_IP4_ADDR_FMT_ARGS( ip4_addr ), if_idx, netlink_res, fd_io_strerror( netlink_res ) )); + int probe_res = fd_neigh4_probe_rate_limited( ctx->prober, ele, ip4_addr, now ); + if( probe_res==0 ) { + ctx->metrics.neigh_solicits_sent++; + } else if( probe_res>0 ) { ctx->metrics.neigh_solicits_fails++; - return; } - ctx->metrics.neigh_solicits_sent++; - } #define STEM_BURST (1UL) diff --git a/src/disco/netlink/fd_netlink_tile_private.h b/src/disco/netlink/fd_netlink_tile_private.h index 9fdd3ccf7d..3addf77994 100644 --- a/src/disco/netlink/fd_netlink_tile_private.h +++ b/src/disco/netlink/fd_netlink_tile_private.h @@ -7,6 +7,7 @@ #include "../../waltz/mib/fd_dbl_buf.h" #include "../../waltz/mib/fd_netdev_tbl.h" #include "../../waltz/neigh/fd_neigh4_map.h" +#include "../../waltz/neigh/fd_neigh4_probe.h" /* FD_NETLINK_TILE_CTX_MAGIC uniquely identifies a fd_netlink_tile_ctx_t. CHange this whenever the fd_netlink_tile_ctx_t struct changes. */ @@ -45,6 +46,9 @@ struct fd_netlink_tile_ctx { uint neigh4_ifidx; long idle_cnt; + /* Neighbor table prober */ + fd_neigh4_prober_t prober[1]; + struct { ulong link_full_syncs; ulong route_full_syncs; diff --git a/src/disco/netlink/generated/netlink_seccomp.h b/src/disco/netlink/generated/netlink_seccomp.h index 99852b8522..3a2da8fce7 100644 --- a/src/disco/netlink/generated/netlink_seccomp.h +++ b/src/disco/netlink/generated/netlink_seccomp.h @@ -21,14 +21,14 @@ #else # error "Target architecture is unsupported by seccomp." #endif -static const unsigned int sock_filter_policy_netlink_instr_cnt = 36; +static const unsigned int sock_filter_policy_netlink_instr_cnt = 46; -static void populate_sock_filter_policy_netlink( ulong out_cnt, struct sock_filter * out, unsigned int logfile_fd, unsigned int nl_mon_fd, unsigned int nl_req_fd) { - FD_TEST( out_cnt >= 36 ); - struct sock_filter filter[36] = { +static void populate_sock_filter_policy_netlink( ulong out_cnt, struct sock_filter * out, uint logfile_fd, uint nl_mon_fd, uint nl_req_fd, uint arp_probe_fd) { + FD_TEST( out_cnt >= 46 ); + struct sock_filter filter[46] = { /* Check: Jump to RET_KILL_PROCESS if the script's arch != the runtime arch */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, ( offsetof( struct seccomp_data, arch ) ) ), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ARCH_NR, 0, /* RET_KILL_PROCESS */ 32 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ARCH_NR, 0, /* RET_KILL_PROCESS */ 42 ), /* loading syscall number in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, ( offsetof( struct seccomp_data, nr ) ) ), /* allow write based on expression */ @@ -38,58 +38,78 @@ static void populate_sock_filter_policy_netlink( ulong out_cnt, struct sock_filt /* allow sendto based on expression */ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_sendto, /* check_sendto */ 8, 0 ), /* allow recvfrom based on expression */ - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_recvfrom, /* check_recvfrom */ 15, 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_recvfrom, /* check_recvfrom */ 25, 0 ), /* none of the syscalls matched */ - { BPF_JMP | BPF_JA, 0, 0, /* RET_KILL_PROCESS */ 26 }, + { BPF_JMP | BPF_JA, 0, 0, /* RET_KILL_PROCESS */ 36 }, // check_write: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_ALLOW */ 25, /* lbl_1 */ 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_ALLOW */ 35, /* lbl_1 */ 0 ), // lbl_1: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_ALLOW */ 23, /* RET_KILL_PROCESS */ 22 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_ALLOW */ 33, /* RET_KILL_PROCESS */ 32 ), // check_fsync: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_ALLOW */ 21, /* RET_KILL_PROCESS */ 20 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_ALLOW */ 31, /* RET_KILL_PROCESS */ 30 ), // check_sendto: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, nl_req_fd, /* lbl_2 */ 0, /* RET_KILL_PROCESS */ 18 ), -// lbl_2: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, nl_req_fd, /* lbl_3 */ 0, /* lbl_2 */ 6 ), +// lbl_3: /* load syscall argument 3 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[3])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* lbl_3 */ 0, /* RET_KILL_PROCESS */ 16 ), -// lbl_3: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* lbl_4 */ 0, /* lbl_2 */ 4 ), +// lbl_4: /* load syscall argument 4 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[4])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* lbl_4 */ 0, /* RET_KILL_PROCESS */ 14 ), -// lbl_4: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* lbl_5 */ 0, /* lbl_2 */ 2 ), +// lbl_5: /* load syscall argument 5 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[5])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* RET_ALLOW */ 13, /* RET_KILL_PROCESS */ 12 ), -// check_recvfrom: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* RET_ALLOW */ 23, /* lbl_2 */ 0 ), +// lbl_2: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, nl_mon_fd, /* lbl_5 */ 2, /* lbl_6 */ 0 ), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, arp_probe_fd, /* lbl_6 */ 0, /* RET_KILL_PROCESS */ 20 ), // lbl_6: + /* load syscall argument 1 in accumulator */ + BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[1])), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* lbl_7 */ 0, /* RET_KILL_PROCESS */ 18 ), +// lbl_7: + /* load syscall argument 2 in accumulator */ + BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[2])), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* lbl_8 */ 0, /* RET_KILL_PROCESS */ 16 ), +// lbl_8: + /* load syscall argument 3 in accumulator */ + BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[3])), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, MSG_DONTWAIT, /* lbl_9 */ 0, /* RET_KILL_PROCESS */ 14 ), +// lbl_9: + /* load syscall argument 5 in accumulator */ + BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[5])), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, FD_SOCKADDR_IN_SZ, /* RET_ALLOW */ 13, /* RET_KILL_PROCESS */ 12 ), +// check_recvfrom: /* load syscall argument 0 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, nl_req_fd, /* lbl_5 */ 0, /* RET_KILL_PROCESS */ 8 ), -// lbl_5: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, nl_mon_fd, /* lbl_10 */ 2, /* lbl_11 */ 0 ), +// lbl_11: + /* load syscall argument 0 in accumulator */ + BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, nl_req_fd, /* lbl_10 */ 0, /* RET_KILL_PROCESS */ 8 ), +// lbl_10: /* load syscall argument 3 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[3])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* lbl_7 */ 2, /* lbl_8 */ 0 ), -// lbl_8: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* lbl_12 */ 2, /* lbl_13 */ 0 ), +// lbl_13: /* load syscall argument 3 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[3])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, MSG_DONTWAIT, /* lbl_7 */ 0, /* RET_KILL_PROCESS */ 4 ), -// lbl_7: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, MSG_DONTWAIT, /* lbl_12 */ 0, /* RET_KILL_PROCESS */ 4 ), +// lbl_12: /* load syscall argument 4 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[4])), - BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* lbl_9 */ 0, /* RET_KILL_PROCESS */ 2 ), -// lbl_9: + BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* lbl_14 */ 0, /* RET_KILL_PROCESS */ 2 ), +// lbl_14: /* load syscall argument 5 in accumulator */ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[5])), BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* RET_ALLOW */ 1, /* RET_KILL_PROCESS */ 0 ), diff --git a/src/disco/netlink/netlink.seccomppolicy b/src/disco/netlink/netlink.seccomppolicy index 589068268f..c54586087a 100644 --- a/src/disco/netlink/netlink.seccomppolicy +++ b/src/disco/netlink/netlink.seccomppolicy @@ -3,7 +3,8 @@ # # nl_mon_fd: An rtnetlink socket used to monitor updates # nl_req_fd: An rtnetlink socket used for request-reply -unsigned int logfile_fd, unsigned int nl_mon_fd, unsigned int nl_req_fd +# arp_probe_fd: A UDP socket used to indirectly generate ARP probes +uint logfile_fd, uint nl_mon_fd, uint nl_req_fd, uint arp_probe_fd # logging: all log messages are written to a file and/or pipe # @@ -21,14 +22,26 @@ write: (or (eq (arg 0) 2) # descriptor 3 is always the logfile. fsync: (eq (arg 0) logfile_fd) -# sendto(2) is used to send netlink requests to the kernel +# nl_req_fd: Periodically send read-only/unprivileged rtnetlink requests +# +# arp_probe_fd: Send UDP packets that cause the kernel to generate ARP +# requests +# # (In theory could use send(2) but that syscall doesn't exist on arm64) -sendto: (and (eq (arg 0) nl_req_fd) - (eq (arg 3) 0) - (eq (arg 4) 0) - (eq (arg 5) 0)) +sendto: (or (and (eq (arg 0) nl_req_fd) + (eq (arg 3) 0) + (eq (arg 4) 0) + (eq (arg 5) 0)) + (and (eq (arg 0) arp_probe_fd) + (eq (arg 1) 0) + (eq (arg 2) 0) + (eq (arg 3) MSG_DONTWAIT) + (eq (arg 5) FD_SOCKADDR_IN_SZ))) -# recvfrom(2) is used to receive netlink responses from the kernel +# nl_mon_fd: Monitor for asynchronous rtnetlink updates +# +# nl_req_fd: Read replies to rtnetlink requests +# # (Using recvfrom(2) instead of recv(2) for same ABI reasons as above) recvfrom: (and (or (eq (arg 0) nl_mon_fd) (eq (arg 0) nl_req_fd)) diff --git a/src/waltz/fd_token_bucket.h b/src/waltz/fd_token_bucket.h new file mode 100644 index 0000000000..d71e3613ec --- /dev/null +++ b/src/waltz/fd_token_bucket.h @@ -0,0 +1,39 @@ +#ifndef HEADER_fd_src_waltz_fd_token_bucket_h +#define HEADER_fd_src_waltz_fd_token_bucket_h + +#include "../util/fd_util_base.h" +#include + +struct fd_token_bucket { + long ts; + float rate; + float burst; + float balance; +}; + +typedef struct fd_token_bucket fd_token_bucket_t; + +FD_PROTOTYPES_BEGIN + +static inline int +fd_token_bucket_consume( fd_token_bucket_t * bucket, + float delta, + long ts ) { + /* Refill bucket */ + long elapsed = ts - bucket->ts; + float balance = bucket->balance + ((float)elapsed * bucket->rate); + balance = fminf( balance, bucket->burst ); + + /* Consume tokens */ + int ok = delta <= balance; + balance -= (float)ok * delta; + + /* Store bucket */ + bucket->balance = balance; + bucket->ts = ts; + return ok; +} + +FD_PROTOTYPES_END + +#endif /* HEADER_fd_src_waltz_fd_token_bucket_h */ diff --git a/src/waltz/neigh/Local.mk b/src/waltz/neigh/Local.mk index 0cba1dca5f..8aa1b5e01a 100644 --- a/src/waltz/neigh/Local.mk +++ b/src/waltz/neigh/Local.mk @@ -1,9 +1,7 @@ $(call add-hdrs,fd_neigh4_map.h fd_neigh4_map_defines.h) $(call add-objs,fd_neigh4_map,fd_waltz) ifdef FD_HAS_LINUX -ifdef FD_HAS_SSE -$(call add-hdrs,fd_neigh4_netlink.h) -$(call add-objs,fd_neigh4_netlink,fd_waltz) +$(call add-hdrs,fd_neigh4_netlink.h fd_neigh4_probe.h) +$(call add-objs,fd_neigh4_netlink fd_neigh4_probe,fd_waltz) $(call make-unit-test,test_neigh4_netlink,test_neigh4_netlink,fd_waltz fd_util) endif -endif diff --git a/src/waltz/neigh/fd_neigh4_map.c b/src/waltz/neigh/fd_neigh4_map.c index 7631b1dfac..a16d3ea871 100644 --- a/src/waltz/neigh/fd_neigh4_map.c +++ b/src/waltz/neigh/fd_neigh4_map.c @@ -6,11 +6,10 @@ #define MAP_IMPL_STYLE 2 #include "../../util/tmpl/fd_map_slot_para.c" -#if FD_HAS_HOSTED && FD_HAS_SSE +#if FD_HAS_HOSTED #include #include -#include #include "../../util/net/fd_ip4.h" #include "../../util/net/fd_eth.h" @@ -23,14 +22,25 @@ fd_neigh4_hmap_fprintf( fd_neigh4_hmap_t const * map, fd_neigh4_entry_t const * ele = fd_neigh4_hmap_shele_const( map ); for( ulong j=0UL; jip4_addr ) { int print_res = fprintf( file, FD_IP4_ADDR_FMT " " FD_ETH_MAC_FMT "\n", - FD_IP4_ADDR_FMT_ARGS( l.e.ip4_addr ), FD_ETH_MAC_FMT_ARGS( l.e.mac_addr ) ); + FD_IP4_ADDR_FMT_ARGS( e->ip4_addr ), FD_ETH_MAC_FMT_ARGS( e->mac_addr ) ); if( FD_UNLIKELY( print_res<0 ) ) return errno; } } @@ -38,4 +48,4 @@ fd_neigh4_hmap_fprintf( fd_neigh4_hmap_t const * map, return 0; } -#endif /* FD_HAS_HOSTED && FD_HAS_SSE */ +#endif /* FD_HAS_HOSTED */ diff --git a/src/waltz/neigh/fd_neigh4_map.h b/src/waltz/neigh/fd_neigh4_map.h index 4930ca36ae..24a2c6cd5a 100644 --- a/src/waltz/neigh/fd_neigh4_map.h +++ b/src/waltz/neigh/fd_neigh4_map.h @@ -9,6 +9,8 @@ struct __attribute__((aligned(16))) fd_neigh4_entry { uint ip4_addr; uchar mac_addr[6]; /* MAC address */ uchar state; + uchar _pad[1]; + long probe_suppress_until; }; typedef struct fd_neigh4_entry fd_neigh4_entry_t; @@ -22,7 +24,7 @@ typedef struct fd_neigh4_entry fd_neigh4_entry_t; FD_PROTOTYPES_BEGIN -#if FD_HAS_HOSTED && FD_HAS_SSE +#if FD_HAS_HOSTED /* fd_neigh4_hmap_fprintf prints the routing table to the given FILE * pointer (or target equivalent). Order of routes is undefined but @@ -34,7 +36,7 @@ int fd_neigh4_hmap_fprintf( fd_neigh4_hmap_t const * map, void * file ); -#endif /* FD_HAS_HOSTED && FD_HAS_SSE */ +#endif /* FD_HAS_HOSTED */ FD_PROTOTYPES_END diff --git a/src/waltz/neigh/fd_neigh4_netlink.c b/src/waltz/neigh/fd_neigh4_netlink.c index c5cad0fdb6..16c0a9c313 100644 --- a/src/waltz/neigh/fd_neigh4_netlink.c +++ b/src/waltz/neigh/fd_neigh4_netlink.c @@ -6,8 +6,6 @@ #include /* RTM_NEWNEIGH */ #include /* struct ndmsg */ #include "../ip/fd_netlink1.h" -#include "../../util/fd_util.h" -#include "../../util/net/fd_ip4.h" #include "fd_neigh4_map.h" int @@ -145,73 +143,3 @@ fd_neigh4_netlink_ingest_message( fd_neigh4_hmap_t * map, } } - -int -fd_neigh4_netlink_solicit( fd_netlink_t * netlink, - uint if_idx, - uint ip4_addr ) { - - uint seq = netlink->seq++; - - struct { - struct nlmsghdr nlh; - struct ndmsg ndm; - struct nlattr nla_dst; - uint dst_addr; - } request; - request.nlh = (struct nlmsghdr) { - .nlmsg_type = RTM_NEWNEIGH, - .nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE, - .nlmsg_seq = seq, - .nlmsg_len = sizeof(request) - }; - request.ndm = (struct ndmsg) { - .ndm_family = AF_INET, - .ndm_ifindex = (int)if_idx, - .ndm_state = NUD_INCOMPLETE, /* neighbor entry starts out as empty */ - .ndm_flags = NTF_USE /* mark neighbor as used which triggers ARP request */ - }; - request.nla_dst = (struct nlattr) { - .nla_type = NDA_DST, - .nla_len = (ushort)( sizeof(struct nlattr) + fd_uint_align_up( sizeof(uint), NLA_ALIGNTO ) ) - }; - request.dst_addr = ip4_addr; /* big endian */ - - /* Send request */ - - long send_res = sendto( netlink->fd, &request, sizeof(request), 0, NULL, 0 ); - if( FD_UNLIKELY( send_res<0 ) ) { - FD_LOG_WARNING(( "netlink send(RTM_NEWNEIGH,NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE," FD_IP4_ADDR_FMT ") failed (%d-%s)", - FD_IP4_ADDR_FMT_ARGS( ip4_addr ), errno, fd_io_strerror( errno ) )); - return errno; - } - if( FD_UNLIKELY( send_res!=sizeof(request) ) ) { - FD_LOG_WARNING(( "netlink send(RTM_NEWNEIGH,NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE," FD_IP4_ADDR_FMT ") failed (short write)", - FD_IP4_ADDR_FMT_ARGS( ip4_addr ) )); - return EPIPE; - } - - /* Get error code */ - - uchar buf[ 4096 ]; - long recv_res = fd_netlink_read_socket( netlink->fd, buf, sizeof(buf) ); - if( FD_UNLIKELY( recv_res<0 ) ) { - FD_LOG_WARNING(( "netlink recv failed (%d-%s)", errno, fd_io_strerror( errno ) )); - return errno; - } - - struct nlmsghdr const * nlh = fd_type_pun_const( buf ); - if( FD_UNLIKELY( nlh->nlmsg_seq!=seq ) ) { - /* Should only happen if caller misbehaves */ - FD_LOG_ERR(( "Unexpected netlink message type=%u seq=%u", nlh->nlmsg_type, nlh->nlmsg_seq )); - } - - if( FD_UNLIKELY( nlh->nlmsg_type!=NLMSG_ERROR ) ) { - /* Should never happen */ - FD_LOG_ERR(( "unexpected netlink response nlmsg_type %u for RTM_NEWNEIGH request", nlh->nlmsg_type )); - } - - struct nlmsgerr * err = NLMSG_DATA( nlh ); - int nl_err = -err->error; - return nl_err; -} diff --git a/src/waltz/neigh/fd_neigh4_netlink.h b/src/waltz/neigh/fd_neigh4_netlink.h index 5ec9be9fd3..44e20bb192 100644 --- a/src/waltz/neigh/fd_neigh4_netlink.h +++ b/src/waltz/neigh/fd_neigh4_netlink.h @@ -34,18 +34,6 @@ fd_neigh4_netlink_ingest_message( fd_neigh4_hmap_t * map, struct nlmsghdr const * msg, uint if_idx ); -/* fd_neigh4_netlink_solicit requests the kernel to create a new neighbor - table entry and start an ARP request for it. Uses sendto(2) syscall. - Immediately tries to recvfrom(2) the error code. Assumes that netlink - socket is not bound and has no buffered messages. Returns 0 on success - and netlink error code on failure. The most common reason for failure - is EEXIST (neighbor entry already exists). */ - -int -fd_neigh4_netlink_solicit( fd_netlink_t * netlink, - uint if_idx, - uint ip4_addr ); - FD_PROTOTYPES_END #endif /* defined(__linux__) */ diff --git a/src/waltz/neigh/fd_neigh4_probe.c b/src/waltz/neigh/fd_neigh4_probe.c new file mode 100644 index 0000000000..f4507c85fb --- /dev/null +++ b/src/waltz/neigh/fd_neigh4_probe.c @@ -0,0 +1,81 @@ +#include "fd_neigh4_probe.h" +#include "../../tango/tempo/fd_tempo.h" /* fd_tempo_tick_per_ns */ + +#include +#include /* socket(2) */ +#include /* IPPROTO_IP */ +#include /* close(2) */ + +void +fd_neigh4_prober_init( fd_neigh4_prober_t * prober, + float max_probes_per_second, + ulong max_probe_burst, + float probe_delay_seconds ) { + + int sock_fd = socket( AF_INET, SOCK_DGRAM, 0 ); + if( FD_UNLIKELY( sock_fd<0 ) ) { + FD_LOG_ERR(( "socket(AF_INET,SOCK_DGRAM,0) failed (%i-%s)", + errno, fd_io_strerror( errno ) )); + } + + /* IP_TTL=1 is the lowest permitted value: + https://github.com/torvalds/linux/blob/v6.13/net/ipv4/ip_sockglue.c#L300 */ + int ip_ttl = 1; + if( FD_UNLIKELY( 0!=setsockopt( sock_fd, IPPROTO_IP, IP_TTL, &ip_ttl, sizeof(int) ) ) ) { + (void)close( sock_fd ); + FD_LOG_ERR(( "setsockopt(%i,IPPROTO_IP,IP_TTL,1) failed (%i-%s)", + sock_fd, errno, fd_io_strerror( errno ) )); + } + + /* Only need to send probe packets to Ethernet neighbors */ + int dontroute = 1; + if( FD_UNLIKELY( 0!=setsockopt( sock_fd, SOL_SOCKET, SO_DONTROUTE, &dontroute, sizeof(int) ) ) ) { + (void)close( sock_fd ); + FD_LOG_ERR(( "setsockopt(%i,SOL_SOCKET,SO_DONTROUTE,1) failed (%i-%s)", + sock_fd, errno, fd_io_strerror( errno ) )); + } + + float tick_per_ns = (float)fd_tempo_tick_per_ns( NULL ); + + *prober = (fd_neigh4_prober_t) { + .sock_fd = sock_fd, + .probe_delay = (long)( tick_per_ns * probe_delay_seconds * 1e9f ), + .rate_limit = (fd_token_bucket_t) { + .ts = fd_tickcount(), + .rate = tick_per_ns * (max_probes_per_second / 1e9f), + .burst = (float)max_probe_burst, + .balance = 0.f + }, + .local_rate_limited_cnt = 0UL, + .global_rate_limited_cnt = 0UL + }; +} + +void +fd_neigh4_prober_fini( fd_neigh4_prober_t * prober ) { + if( FD_UNLIKELY( 0!=close( prober->sock_fd ) ) ) { + FD_LOG_ERR(( "close(%i) failed (%i-%s)", + prober->sock_fd, errno, fd_io_strerror( errno ) )); + } + prober->sock_fd = -1; +} + +int +fd_neigh4_probe( fd_neigh4_prober_t * prober, + fd_neigh4_entry_t * entry, + uint ip4_addr, + long now ) { + + struct sockaddr_in dst = { + .sin_family = AF_INET, + .sin_port = (ushort)0xFFFF, + .sin_addr = { .s_addr = ip4_addr } + }; + if( FD_UNLIKELY( sendto( prober->sock_fd, NULL, 0UL, MSG_DONTWAIT, fd_type_pun_const( &dst ), sizeof(struct sockaddr_in) )<0 ) ) { + return errno; + } + + entry->probe_suppress_until = now + prober->probe_delay; + + return 0; +} diff --git a/src/waltz/neigh/fd_neigh4_probe.h b/src/waltz/neigh/fd_neigh4_probe.h new file mode 100644 index 0000000000..a0633fd28d --- /dev/null +++ b/src/waltz/neigh/fd_neigh4_probe.h @@ -0,0 +1,134 @@ +#ifndef HEADER_fd_src_waltz_neigh_fd_neigh4_probe_h +#define HEADER_fd_src_waltz_neigh_fd_neigh4_probe_h + +/* fd_neigh4_probe.h is a hack to indirectly trigger ARP requests in + Linux. + + ### Background + + When sending an IP packet via the Firedancer network stack, it is + the net tile's responsibility to pick the network interface to send + the packet out on, as well as the destination MAC address. + + The dst MAC address is taken from a neighbor table entry given the + "next hop" (an output of a previously done route table lookiup). + The neighbor table is directly mirrored from the Linux kernel. + + If no matching neighbor table entry exists, the system should send + broadcast an ARP request (e.g. "who is 192.168.12.13? tell + 192.168.12.4"). ARP relies to this request will then go to the + kernel. The kernel also needs to be told that it should expect an + ARP reply to avoid drops. + + ### Possible Solutions + + 1. Add a neighbor table entry, send out the ARP request via XDP: + `ip neigh add IP_ADDR nud incomplete` + Requires CAP_NET_ADMIN (to send RTM_NEWNEIGH) + + 2. Add a neighbor table entry, make the kernel increase the ARP + request: `ip neigh add IP_ADDR nud incomplete use` + Requires CAP_NET_ADMIN (to send RTM_NEWNEIGH) + + 3. Send a UDP datagram, which indirectly makes the kernel do an ARP + request: `echo "hello" | nc -u IP_ADDR:65535` + Does not require privileges + + 4. Send an IP packet (ICMP echo, invalid ICMP, invalid next proto...) + `ping IP_ADDR -c 1` + Requires CAP_NET_RAW to create a SOCK_RAW socket + + Solution 2 is theoretically ideal. Unfortunately, it requires the + netlink API caller to be in the root user namespace, which would + break assumptions made in fd_sandbox. + + fd_neigh4_probe implements solution 3 because it requires the least + amount of privileges. */ + +#include "fd_neigh4_map.h" +#include "../fd_token_bucket.h" + +/* The fd_neigh4_prober_t class provides "neighbor probing" + functionality as described above using empty UDP/IP packets. */ + +struct fd_neigh4_prober { + int sock_fd; /* UDP socket with IP_TTL 0 */ + + /* probe_delay specifies the delay in ticks for successive ARP + requests to the same IP address (see fd_tickcount()) */ + long probe_delay; + + /* Token bucket rate limiter on any outgoing ARP probes */ + fd_token_bucket_t rate_limit; + + /* Metric counter for probes suppressed by local rate limit */ + ulong local_rate_limited_cnt; + + /* Metric counter for probes suppressed by global rate limit */ + ulong global_rate_limited_cnt; +}; + +typedef struct fd_neigh4_prober fd_neigh4_prober_t; + +FD_PROTOTYPES_BEGIN + +/* fd_neigh4_prober_init initializes a neigh4_prober object. Creates a + new unbound UDP socket (socket(2)) with an IPv4 TTL of zero + (setsockopt(2)). max_probes_per_second and max_probe_burst configure + token bucket rate limit parameters for outgoing probe packets. + probe_delay_seconds sets the min wait time between two probe packet + sends for the same dst IP. */ + +void +fd_neigh4_prober_init( fd_neigh4_prober_t * prober, + float max_probes_per_second, + ulong max_probe_burst, + float probe_delay_seconds ); + +/* fd_neigh4_prober_fini closes the neigh4_prober socket. */ + +void +fd_neigh4_prober_fini( fd_neigh4_prober_t * prober ); + +/* fd_neigh4_probe sends out an empty UDP packet to port 65535 with the + IP time-to-live field set to 0. ip4_addr is an IP address on a + neighboring subnet for which the neighbor discovery process should + be started. ip4_addr is big endian. now is a recent fd_tickcount() + value. Returns the errno value produced by sendto(2) or 0 on success. */ + +int +fd_neigh4_probe( fd_neigh4_prober_t * prober, + fd_neigh4_entry_t * entry, + uint ip4_addr, + long now ); + +/* fd_neigh4_probe_rate_limited calls fd_neigh4_probe unless that would + violate rate limits. Returns 0 if a probe was sent out. Returns + positive errno on probe failure. Returns -1 if rate limit was hit. */ + +static inline int +fd_neigh4_probe_rate_limited( + fd_neigh4_prober_t * prober, + fd_neigh4_entry_t * entry, + uint ip4_addr, + long now +) { + /* Local rate limit */ + if( now < entry->probe_suppress_until ) { + prober->local_rate_limited_cnt++; + return -1; + } + entry->probe_suppress_until = now + prober->probe_delay; + + /* Global rate limit */ + if( !fd_token_bucket_consume( &prober->rate_limit, 1.0f, now ) ) { + prober->global_rate_limited_cnt++; + return -1; + } + + return fd_neigh4_probe( prober, entry, ip4_addr, now ); +} + +FD_PROTOTYPES_END + +#endif /* HEADER_fd_src_waltz_neigh_fd_neigh4_probe_h */ diff --git a/src/waltz/neigh/test_neigh4_netlink.c b/src/waltz/neigh/test_neigh4_netlink.c index fb1a3609dd..3fa90a1aeb 100644 --- a/src/waltz/neigh/test_neigh4_netlink.c +++ b/src/waltz/neigh/test_neigh4_netlink.c @@ -8,8 +8,6 @@ #include #include "../../util/fd_util.h" -FD_STATIC_ASSERT( sizeof(fd_neigh4_entry_t)==16, layout ); - static void dump_neighbor_table( fd_neigh4_hmap_t * map, fd_netlink_t * netlink1,