git: 3c76623ad553 - main - ipfw: add 'internal monitor' subcommand to capture rtsock messages.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 18 Apr 2025 12:34:20 UTC
The branch main has been updated by ae: URL: https://cgit.FreeBSD.org/src/commit/?id=3c76623ad55369c3998b2b00d7322f8aeedc98fd commit 3c76623ad55369c3998b2b00d7322f8aeedc98fd Author: Andrey V. Elsukov <ae@FreeBSD.org> AuthorDate: 2025-04-18 12:22:56 +0000 Commit: Andrey V. Elsukov <ae@FreeBSD.org> CommitDate: 2025-04-18 12:22:56 +0000 ipfw: add 'internal monitor' subcommand to capture rtsock messages. This command is similar to route(8) monitor subcommand. It can be used for debugging rules in run-time. Also add __pritflike() to bprintf function and fix some related bugs. Obtained from: Yandex LLC Sponsored by: Yandex LLC --- sbin/ipfw/ipfw.8 | 11 +++++ sbin/ipfw/ipfw2.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++- sbin/ipfw/ipfw2.h | 3 +- 3 files changed, 148 insertions(+), 3 deletions(-) diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index 719f92e96e68..ddfdc35ce651 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -171,6 +171,8 @@ in-kernel NAT.\& .Nm .Cm internal iflist .Nm +.Cm internal monitor Op Ar filter-comment +.Nm .Cm internal talist .Nm .Cm internal vlist @@ -4332,6 +4334,15 @@ sub-options: Lists all interface which are currently tracked by .Nm with their in-kernel status. +.It Cm monitor Op Ar filter-comment +Capture messages from +.Xr route 4 +socket, that were logged using rules with +.Cm log Cm logdst Ar rtsock +opcode. Optional +.Ar filter-comment +can be specified to show only those messages, that were logged +by rules with specific rule comment. .It Cm talist List all table lookup algorithms currently available. .El diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index be796808380f..8eeff72463b5 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -47,6 +47,8 @@ #include <net/ethernet.h> #include <net/if.h> /* only IFNAMSIZ */ +#include <net/if_dl.h> +#include <net/route.h> #include <netinet/in.h> #include <netinet/in_systm.h> /* only n_short, n_long */ #include <netinet/ip.h> @@ -2009,7 +2011,7 @@ print_logdst(struct buf_pr *bp, uint16_t arg1) { char const *comma = ""; - bprintf(bp, " logdst ", arg1); + bprintf(bp, " logdst "); if (arg1 & IPFW_LOG_SYSLOG) { bprintf(bp, "%ssyslog", comma); comma = ","; @@ -2179,7 +2181,7 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, if (cmd->len == F_INSN_SIZE(ipfw_insn)) bprintf(bp, " %u", cmd->arg1); else - bprintf(bp, " %ubytes", + bprintf(bp, " %lubytes", cmd->len * sizeof(uint32_t)); break; case O_SETDSCP: @@ -5956,6 +5958,7 @@ static struct _s_x intcmds[] = { { "iflist", TOK_IFLIST }, { "olist", TOK_OLIST }, { "vlist", TOK_VLIST }, + { "monitor", TOK_MONITOR }, { NULL, 0 } }; @@ -6030,6 +6033,132 @@ ipfw_list_objects(int ac __unused, char *av[] __unused) free(olh); } +static void +bprint_sa(struct buf_pr *bp, const struct sockaddr *sa) +{ + struct sockaddr_storage ss; + char buf[INET6_ADDRSTRLEN]; + + memset(&ss, 0, sizeof(ss)); + if (sa->sa_len == 0) + ss.ss_family = sa->sa_family; + else + memcpy(&ss, sa, sa->sa_len); + + /* set ss_len in case it was shortened */ + switch (sa->sa_family) { + case AF_INET: + ss.ss_len = sizeof(struct sockaddr_in); + break; + default: + ss.ss_len = sizeof(struct sockaddr_in6); + } + if (getnameinfo((const struct sockaddr *)&ss, ss.ss_len, + buf, sizeof(buf), NULL, 0, NI_NUMERICHOST) != 0) { + bprintf(bp, "bad-addr"); + return; + } + bprintf(bp, "%s", buf); +} + +static void +ipfw_rtsock_monitor(const char *filter) +{ + char msg[2048], buf[32]; + struct timespec tp; + struct tm tm; + struct buf_pr bp; + struct rt_msghdr *hdr; + struct sockaddr *sa; + struct sockaddr_dl *sdl; + ipfwlog_rtsock_hdr_v2 *loghdr; + ssize_t msglen; + int rtsock; + + rtsock = socket(PF_ROUTE, SOCK_RAW, AF_IPFWLOG); + if (rtsock < 0) + err(EX_UNAVAILABLE, "socket(AF_IPFWLOG)"); + bp_alloc(&bp, 4096); + for (;;) { + msglen = read(rtsock, msg, sizeof(msg)); + if (msglen < 0) { + warn("read()"); + continue; + } + if (sizeof(*hdr) - msglen < 0) + continue; + + hdr = (struct rt_msghdr *)msg; + if (hdr->rtm_version != RTM_VERSION || + hdr->rtm_type != RTM_IPFWLOG || + (hdr->rtm_addrs & (1 << RTAX_DST)) == 0 || + (hdr->rtm_addrs & (1 << RTAX_GATEWAY)) == 0 || + (hdr->rtm_addrs & (1 << RTAX_NETMASK)) == 0) + continue; + + msglen -= sizeof(*hdr); + sdl = (struct sockaddr_dl *)(hdr + 1); + if (msglen - sizeof(*sdl) < 0 || msglen - SA_SIZE(sdl) < 0 || + sdl->sdl_family != AF_IPFWLOG || + sdl->sdl_type != 2 /* version */ || + sdl->sdl_alen != sizeof(*loghdr)) + continue; + + msglen -= SA_SIZE(sdl); + loghdr = (ipfwlog_rtsock_hdr_v2 *)sdl->sdl_data; + /* filter by rule comment. MAX_COMMENT_LEN = 80 */ + if (filter != NULL && + strncmp(filter, loghdr->comment, 80) != 0) + continue; + + sa = (struct sockaddr *)((char *)sdl + SA_SIZE(sdl)); + if (msglen - SA_SIZE(sa) < 0) + continue; + + msglen -= SA_SIZE(sa); + bp_flush(&bp); + + clock_gettime(CLOCK_REALTIME, &tp); + localtime_r(&tp.tv_sec, &tm); + strftime(buf, sizeof(buf), "%T", &tm); + bprintf(&bp, "%s.%03ld AF %s", buf, tp.tv_nsec / 1000000, + sa->sa_family == AF_INET ? "IPv4" : "IPv6"); + + bprintf(&bp, " %s >", + ether_ntoa((const struct ether_addr *)loghdr->ether_shost)); + bprintf(&bp, " %s, ", + ether_ntoa((const struct ether_addr *)loghdr->ether_dhost)); + bprint_sa(&bp, sa); + + sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); + if (msglen - SA_SIZE(sa) < 0) + continue; + + msglen -= SA_SIZE(sa); + bprintf(&bp, " > "); + bprint_sa(&bp, sa); + bprintf(&bp, ", set %u, rulenum %u, targ 0x%08x, " + "%scmd[op %d, len %d, arg1 0x%04x], mark 0x%08x", + sdl->sdl_index, loghdr->rulenum, loghdr->tablearg, + (loghdr->cmd.len & F_NOT) ? "!": "", + loghdr->cmd.opcode, F_LEN(&loghdr->cmd), + loghdr->cmd.arg1, loghdr->mark); + + sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); + if ((hdr->rtm_addrs & (1 << RTAX_GENMASK)) != 0 && + msglen - SA_SIZE(sa) >= 0) { + msglen -= SA_SIZE(sa); + bprintf(&bp, ", nh "); + bprint_sa(&bp, sa); + } + if (sdl->sdl_nlen > 0) + bprintf(&bp, " // %s", loghdr->comment); + printf("%s\n", bp.buf); + } + bp_free(&bp); + close(rtsock); +} + void ipfw_internal_handler(int ac, char *av[]) { @@ -6054,6 +6183,10 @@ ipfw_internal_handler(int ac, char *av[]) case TOK_VLIST: ipfw_list_values(ac, av); break; + case TOK_MONITOR: + av++; + ipfw_rtsock_monitor(*av); + break; } } diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h index 788e9fe365f3..f19ea59c1bb4 100644 --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -275,6 +275,7 @@ enum tokens { TOK_UNLOCK, TOK_VLIST, TOK_OLIST, + TOK_MONITOR, TOK_MISSING, TOK_ORFLUSH, @@ -347,7 +348,7 @@ struct buf_pr { int pr_u64(struct buf_pr *bp, void *pd, int width); int bp_alloc(struct buf_pr *b, size_t size); void bp_free(struct buf_pr *b); -int bprintf(struct buf_pr *b, const char *format, ...); +int bprintf(struct buf_pr *b, const char *format, ...) __printflike(2, 3); /* memory allocation support */