genl: add RPC parser that dumps what sys/rpc/clnt_nl.c sends

Use a separate file for the RPC parser.  Potentially it may get bigger.
Also to avoid include RPC header pollution of the genl.c.

Reviewed by:		rmacklem
Differential Revision:	https://reviews.freebsd.org/D48551
This commit is contained in:
Gleb Smirnoff
2025-01-31 17:02:04 -08:00
parent c5d671b711
commit 88cd1e17a7
4 changed files with 216 additions and 10 deletions
+1
View File
@@ -1,3 +1,4 @@
PROG= genl
SRCS= genl.c parser_rpc.c
.include <bsd.prog.mk>
+23 -10
View File
@@ -42,11 +42,10 @@
#include <netlink/netlink_snl_generic.h>
#include <netlink/netlink_sysevent.h>
#include "genl.h"
static int monitor_mcast(int argc, char **argv);
static int list_families(int argc, char **argv);
static void parser_nlctrl_notify(struct snl_state *ss, struct nlmsghdr *hdr);
static void parser_nlsysevent(struct snl_state *ss, struct nlmsghdr *hdr);
static void parser_fallback(struct snl_state *ss, struct nlmsghdr *hdr);
static struct commands {
const char *name;
@@ -57,12 +56,16 @@ static struct commands {
{ "list", "list", list_families },
};
static monitor_parser_t parser_nlctrl_notify;
static monitor_parser_t parser_nlsysevent;
static struct mcast_parsers {
const char *family;
void (*parser)(struct snl_state *ss, struct nlmsghdr *hdr);
monitor_parser_t *parser;
} mcast_parsers [] = {
{ "nlctrl", parser_nlctrl_notify },
{ "nlsysevent", parser_nlsysevent },
{ "rpc", parser_rpc },
};
struct nlevent {
@@ -252,7 +255,7 @@ dump_family(struct genl_family *family)
dump_mcast_groups(&family->mcast_groups);
}
void
static void
parser_nlctrl_notify(struct snl_state *ss, struct nlmsghdr *hdr)
{
struct genl_family family = {};
@@ -262,7 +265,7 @@ parser_nlctrl_notify(struct snl_state *ss, struct nlmsghdr *hdr)
dump_family(&family);
}
void
static void
parser_nlsysevent(struct snl_state *ss, struct nlmsghdr *hdr)
{
struct nlevent ne = {};
@@ -276,15 +279,25 @@ parser_nlsysevent(struct snl_state *ss, struct nlmsghdr *hdr)
}
}
void
parser_fallback(struct snl_state *ss __unused, struct nlmsghdr *hdr __unused)
static void
parser_fallback(struct snl_state *ss __unused, struct nlmsghdr *hdr)
{
printf("New unknown message\n");
printf("Unknown message: type 0x%x, length %u\n",
hdr->nlmsg_type, hdr->nlmsg_len);
}
/* Populated by monitor_mcast() and may be used by protocol parser callbacks. */
static struct genl_family attrs;
const char *
group_name(uint32_t id)
{
for (u_int i = 0; i < attrs.mcast_groups.num_groups; i++)
if (attrs.mcast_groups.groups[i]->id == id)
return (attrs.mcast_groups.groups[i]->name);
return ("???");
}
static int
monitor_mcast(int argc, char **argv)
{
@@ -294,7 +307,7 @@ monitor_mcast(int argc, char **argv)
struct pollfd pfd;
bool found = false;
bool all = false;
void (*parser)(struct snl_state *ss, struct nlmsghdr *hdr);
monitor_parser_t *parser;
if (argc < 1 || argc > 2) {
usage();
+31
View File
@@ -0,0 +1,31 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright 2025 Gleb Smirnoff <glebius@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions~
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
extern const char * group_name(uint32_t id);
typedef void monitor_parser_t(struct snl_state *, struct nlmsghdr *);
extern monitor_parser_t parser_rpc;
+161
View File
@@ -0,0 +1,161 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright 2025 Gleb Smirnoff <glebius@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions~
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <err.h>
#include <netinet/in.h>
#include <netlink/netlink.h>
#include <netlink/netlink_generic.h>
#include <netlink/netlink_snl.h>
#include <netlink/netlink_snl_generic.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <rpc/auth.h>
#include <rpc/clnt.h>
#include <rpc/rpc_msg.h>
#include <rpc/clnt_nl.h>
#include "genl.h"
struct nl_request_parsed {
uint32_t group;
struct nlattr *data;
};
static const struct snl_attr_parser rpcnl_attr_parser[] = {
#define OUT(field) offsetof(struct nl_request_parsed, field)
{ .type = RPCNL_REQUEST_GROUP, .off = OUT(group),
.cb = snl_attr_get_uint32 },
{ .type = RPCNL_REQUEST_BODY, .off = OUT(data), .cb = snl_attr_get_nla },
#undef OUT
};
SNL_DECLARE_PARSER(request_parser, struct genlmsghdr, snl_f_p_empty,
rpcnl_attr_parser);
void
parser_rpc(struct snl_state *ss __unused, struct nlmsghdr *hdr)
{
struct nl_request_parsed req;
struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1);
XDR xdrs;
struct rpc_msg msg;
struct opaque_auth *oa;
int32_t *buf;
if (!snl_parse_nlmsg(NULL, hdr, &request_parser, &req))
errx(EXIT_FAILURE, "failed to parse RPC message");
printf("RPC %s: group %8s[0x%2x] length %4u XDR length %4u\n",
ghdr->cmd == RPCNL_REQUEST ? "request" : "unknown",
group_name(req.group), req.group,
hdr->nlmsg_len, NLA_DATA_LEN(req.data));
xdrmem_create(&xdrs, NLA_DATA(req.data), NLA_DATA_LEN(req.data),
XDR_DECODE);
if ((buf = XDR_INLINE(&xdrs, 8 * BYTES_PER_XDR_UNIT)) == NULL) {
printf("\trunt datagram\n");
return;
}
msg.rm_xid = IXDR_GET_U_INT32(buf);
msg.rm_direction = IXDR_GET_ENUM(buf, enum msg_type);
msg.rm_call.cb_rpcvers = IXDR_GET_U_INT32(buf);
msg.rm_call.cb_prog = IXDR_GET_U_INT32(buf);
msg.rm_call.cb_vers = IXDR_GET_U_INT32(buf);
msg.rm_call.cb_proc = IXDR_GET_U_INT32(buf);
printf(" %5s: xid 0x%-8x program 0x%08xv%u procedure %u\n",
msg.rm_direction == CALL ? "CALL" : "REPLY", msg.rm_xid,
msg.rm_call.cb_prog, msg.rm_call.cb_vers, msg.rm_call.cb_proc);
oa = &msg.rm_call.cb_cred;
oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t);
oa->oa_length = (u_int)IXDR_GET_U_INT32(buf);
if (oa->oa_length) {
printf("\tcb_cred auth flavor %u length %u\n",
oa->oa_flavor, oa->oa_length);
/*
* Excerpt from rpc_callmsg.c, if we want to parse cb_cred better.
if (oa->oa_length > MAX_AUTH_BYTES) {
return (FALSE);
}
if (oa->oa_base == NULL) {
oa->oa_base = (caddr_t)
mem_alloc(oa->oa_length);
if (oa->oa_base == NULL)
return (FALSE);
}
buf = XDR_INLINE(&xdrs, RNDUP(oa->oa_length));
if (buf == NULL) {
if (xdr_opaque(&xdrs, oa->oa_base,
oa->oa_length) == FALSE) {
return (FALSE);
}
} else {
memmove(oa->oa_base, buf,
oa->oa_length);
}
*/
}
oa = &msg.rm_call.cb_verf;
buf = XDR_INLINE(&xdrs, 2 * BYTES_PER_XDR_UNIT);
if (buf == NULL) {
if (xdr_enum(&xdrs, &oa->oa_flavor) == FALSE ||
xdr_u_int(&xdrs, &oa->oa_length) == FALSE)
return;
} else {
oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t);
oa->oa_length = (u_int)IXDR_GET_U_INT32(buf);
}
if (oa->oa_length) {
printf("\tcb_verf auth flavor %u length %u\n",
oa->oa_flavor, oa->oa_length);
/*
* Excerpt from rpc_callmsg.c, if we want to parse cb_verf better.
if (oa->oa_length > MAX_AUTH_BYTES) {
return (FALSE);
}
if (oa->oa_base == NULL) {
oa->oa_base = (caddr_t)
mem_alloc(oa->oa_length);
if (oa->oa_base == NULL)
return (FALSE);
}
buf = XDR_INLINE(&xdrs, RNDUP(oa->oa_length));
if (buf == NULL) {
if (xdr_opaque(&xdrs, oa->oa_base,
oa->oa_length) == FALSE) {
return (FALSE);
}
} else {
memmove(oa->oa_base, buf,
oa->oa_length);
}
*/
}
}