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 */