From 6299a89371235496fcdd13c94c95250b30009599 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 10 Apr 2025 19:17:54 -0400 Subject: [PATCH] zebra: Add ability to dump routes received from fpm_listener The fpm_listener currently has no ability to store the list of prefixes that it has received. Modify the code to store the prefixes in a typesafe RB Tree. Additionally modify the code such that when a SIGUSR1 is received to dump the routes out. If the operator specifies a -z then write the routes to that file. It will overwrite the last version of the file written. Signed-off-by: Donald Sharp --- zebra/fpm_listener.c | 141 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 1 deletion(-) diff --git a/zebra/fpm_listener.c b/zebra/fpm_listener.c index 70dcaf91a1..1cf4b308cd 100644 --- a/zebra/fpm_listener.c +++ b/zebra/fpm_listener.c @@ -19,6 +19,7 @@ #include #include #include +#include #ifdef GNU_LINUX #include @@ -40,6 +41,35 @@ XREF_SETUP(); +PREDECL_RBTREE_UNIQ(fpm_route); + +/* Route structure to store in RB tree */ +struct fpm_route { + struct prefix prefix; + uint32_t table_id; + uint32_t nhg_id; + struct fpm_route_item rb_item; +}; + +/* Comparison function for routes */ +static int fpm_route_cmp(const struct fpm_route *a, const struct fpm_route *b) +{ + int ret; + + /* First compare table IDs */ + if (a->table_id < b->table_id) + return -1; + if (a->table_id > b->table_id) + return 1; + + /* Then compare prefixes */ + ret = prefix_cmp(&a->prefix, &b->prefix); + return ret; +} + +/* RB tree for storing routes */ +DECLARE_RBTREE_UNIQ(fpm_route, struct fpm_route, rb_item, fpm_route_cmp); + struct glob { int server_sock; int sock; @@ -47,6 +77,8 @@ struct glob { bool reflect_fail_all; bool dump_hex; FILE *output_file; + const char *dump_file; + struct fpm_route_head route_tree; }; struct glob glob_space; @@ -694,6 +726,64 @@ static void fpm_listener_hexdump(const void *mem, size_t len) } } +/* + * handle_route_update + * Handles adding or removing a route from the route tree + */ +static void handle_route_update(struct netlink_msg_ctx *ctx, bool is_add) +{ + struct fpm_route *route; + struct fpm_route *existing; + struct fpm_route lookup = { 0 }; + + if (!ctx->dest || !ctx->rtmsg) + return; + + /* Set up lookup key */ + lookup.prefix.family = ctx->rtmsg->rtm_family; + lookup.prefix.prefixlen = ctx->rtmsg->rtm_dst_len; + memcpy(&lookup.prefix.u.prefix, RTA_DATA(ctx->dest), + (ctx->rtmsg->rtm_family == AF_INET) ? 4 : 16); + lookup.table_id = ctx->rtmsg->rtm_table; + lookup.nhg_id = ctx->nhgid ? *ctx->nhgid : 0; + /* Look up existing route */ + existing = fpm_route_find(&glob->route_tree, &lookup); + + if (is_add) { + if (existing) { + /* Route exists, update it */ + existing->prefix = lookup.prefix; + existing->table_id = lookup.table_id; + existing->nhg_id = lookup.nhg_id; + } else { + /* Create new route structure */ + route = calloc(1, sizeof(struct fpm_route)); + if (!route) { + fprintf(stderr, "Failed to allocate route structure\n"); + return; + } + + /* Copy prefix information */ + route->prefix = lookup.prefix; + route->table_id = lookup.table_id; + route->nhg_id = lookup.nhg_id; + + /* Add route to tree */ + if (fpm_route_add(&glob->route_tree, route)) { + fprintf(stderr, "Failed to add route to tree\n"); + free(route); + } + } + } else { + /* Remove route from tree */ + if (existing) { + existing = fpm_route_del(&glob->route_tree, existing); + if (existing) + free(existing); + } + } +} + /* * parse_netlink_msg */ @@ -726,6 +816,7 @@ static void parse_netlink_msg(char *buf, size_t buf_len, fpm_msg_hdr_t *fpm) } print_netlink_msg_ctx(ctx); + handle_route_update(ctx, hdr->nlmsg_type == RTM_NEWROUTE); if (glob->reflect && hdr->nlmsg_type == RTM_NEWROUTE && ctx->rtmsg->rtm_protocol > RTPROT_STATIC) { @@ -786,17 +877,62 @@ static void fpm_serve(void) } } +/* Signal handler for SIGUSR1 */ +static void sigusr1_handler(int signum) +{ + struct fpm_route *route; + char buf[PREFIX_STRLEN]; + FILE *out = glob->output_file; + FILE *dump_fp = NULL; + + if (glob->dump_file) { + dump_fp = fopen(glob->dump_file, "w"); + if (dump_fp) { + out = dump_fp; + setbuf(dump_fp, NULL); + } else + out = glob->output_file; + } + + fprintf(out, "\n=== Route Tree Dump ===\n"); + fprintf(out, "Timestamp: %s\n", get_timestamp()); + fprintf(out, "Total routes: %zu\n", fpm_route_count(&glob->route_tree)); + fprintf(out, "Routes:\n"); + + frr_each (fpm_route, &glob->route_tree, route) { + prefix2str(&route->prefix, buf, sizeof(buf)); + fprintf(out, " Table %u, NHG %u: %s\n", route->table_id, route->nhg_id, buf); + } + fprintf(out, "=====================\n\n"); + fflush(out); + + if (dump_fp) + fclose(dump_fp); +} + int main(int argc, char **argv) { pid_t daemon; int r; bool fork_daemon = false; const char *output_file = NULL; + struct sigaction sa; memset(glob, 0, sizeof(*glob)); glob->output_file = stdout; + fpm_route_init(&glob->route_tree); + + /* Set up signal handler for SIGUSR1 */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigusr1_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + if (sigaction(SIGUSR1, &sa, NULL) < 0) { + fprintf(stderr, "Failed to set up SIGUSR1 handler: %s\n", strerror(errno)); + exit(1); + } - while ((r = getopt(argc, argv, "rfdvo:")) != -1) { + while ((r = getopt(argc, argv, "rfdvo:z:")) != -1) { switch (r) { case 'r': glob->reflect = true; @@ -813,6 +949,9 @@ int main(int argc, char **argv) case 'o': output_file = optarg; break; + case 'z': + glob->dump_file = optarg; + break; } } -- 2.39.5