188cd1e17SGleb Smirnoff /*
288cd1e17SGleb Smirnoff * SPDX-License-Identifier: BSD-2-Clause
388cd1e17SGleb Smirnoff *
488cd1e17SGleb Smirnoff * Copyright 2025 Gleb Smirnoff <glebius@FreeBSD.org>
588cd1e17SGleb Smirnoff *
688cd1e17SGleb Smirnoff * Redistribution and use in source and binary forms, with or without
788cd1e17SGleb Smirnoff * modification, are permitted providing that the following conditions~
888cd1e17SGleb Smirnoff * are met:
988cd1e17SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright
1088cd1e17SGleb Smirnoff * notice, this list of conditions and the following disclaimer.
1188cd1e17SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright
1288cd1e17SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the
1388cd1e17SGleb Smirnoff * documentation and/or other materials provided with the distribution.
1488cd1e17SGleb Smirnoff *
1588cd1e17SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1688cd1e17SGleb Smirnoff * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1788cd1e17SGleb Smirnoff * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1888cd1e17SGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
1988cd1e17SGleb Smirnoff * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2088cd1e17SGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2188cd1e17SGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2288cd1e17SGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2388cd1e17SGleb Smirnoff * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
2488cd1e17SGleb Smirnoff * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2588cd1e17SGleb Smirnoff * POSSIBILITY OF SUCH DAMAGE.
2688cd1e17SGleb Smirnoff */
2788cd1e17SGleb Smirnoff
2888cd1e17SGleb Smirnoff #include <stdio.h>
2988cd1e17SGleb Smirnoff #include <err.h>
3088cd1e17SGleb Smirnoff
3188cd1e17SGleb Smirnoff #include <netinet/in.h>
3288cd1e17SGleb Smirnoff
3388cd1e17SGleb Smirnoff #include <netlink/netlink.h>
3488cd1e17SGleb Smirnoff #include <netlink/netlink_generic.h>
3588cd1e17SGleb Smirnoff #include <netlink/netlink_snl.h>
3688cd1e17SGleb Smirnoff #include <netlink/netlink_snl_generic.h>
3788cd1e17SGleb Smirnoff
3888cd1e17SGleb Smirnoff #include <rpc/types.h>
3988cd1e17SGleb Smirnoff #include <rpc/xdr.h>
4088cd1e17SGleb Smirnoff #include <rpc/auth.h>
4188cd1e17SGleb Smirnoff #include <rpc/clnt.h>
4288cd1e17SGleb Smirnoff #include <rpc/rpc_msg.h>
4388cd1e17SGleb Smirnoff #include <rpc/clnt_nl.h>
4488cd1e17SGleb Smirnoff
4588cd1e17SGleb Smirnoff #include "genl.h"
4688cd1e17SGleb Smirnoff
4788cd1e17SGleb Smirnoff struct nl_request_parsed {
4888cd1e17SGleb Smirnoff uint32_t group;
4988cd1e17SGleb Smirnoff struct nlattr *data;
5088cd1e17SGleb Smirnoff };
5188cd1e17SGleb Smirnoff static const struct snl_attr_parser rpcnl_attr_parser[] = {
5288cd1e17SGleb Smirnoff #define OUT(field) offsetof(struct nl_request_parsed, field)
5388cd1e17SGleb Smirnoff { .type = RPCNL_REQUEST_GROUP, .off = OUT(group),
5488cd1e17SGleb Smirnoff .cb = snl_attr_get_uint32 },
5588cd1e17SGleb Smirnoff { .type = RPCNL_REQUEST_BODY, .off = OUT(data), .cb = snl_attr_get_nla },
5688cd1e17SGleb Smirnoff #undef OUT
5788cd1e17SGleb Smirnoff };
5888cd1e17SGleb Smirnoff SNL_DECLARE_PARSER(request_parser, struct genlmsghdr, snl_f_p_empty,
5988cd1e17SGleb Smirnoff rpcnl_attr_parser);
6088cd1e17SGleb Smirnoff
6188cd1e17SGleb Smirnoff void
parser_rpc(struct snl_state * ss __unused,struct nlmsghdr * hdr)6288cd1e17SGleb Smirnoff parser_rpc(struct snl_state *ss __unused, struct nlmsghdr *hdr)
6388cd1e17SGleb Smirnoff {
6488cd1e17SGleb Smirnoff struct nl_request_parsed req;
6588cd1e17SGleb Smirnoff struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1);
6688cd1e17SGleb Smirnoff XDR xdrs;
6788cd1e17SGleb Smirnoff struct rpc_msg msg;
6888cd1e17SGleb Smirnoff struct opaque_auth *oa;
6988cd1e17SGleb Smirnoff int32_t *buf;
7088cd1e17SGleb Smirnoff
7188cd1e17SGleb Smirnoff if (!snl_parse_nlmsg(NULL, hdr, &request_parser, &req))
7288cd1e17SGleb Smirnoff errx(EXIT_FAILURE, "failed to parse RPC message");
7388cd1e17SGleb Smirnoff
7488cd1e17SGleb Smirnoff printf("RPC %s: group %8s[0x%2x] length %4u XDR length %4u\n",
7588cd1e17SGleb Smirnoff ghdr->cmd == RPCNL_REQUEST ? "request" : "unknown",
7688cd1e17SGleb Smirnoff group_name(req.group), req.group,
7788cd1e17SGleb Smirnoff hdr->nlmsg_len, NLA_DATA_LEN(req.data));
7888cd1e17SGleb Smirnoff
7988cd1e17SGleb Smirnoff xdrmem_create(&xdrs, NLA_DATA(req.data), NLA_DATA_LEN(req.data),
8088cd1e17SGleb Smirnoff XDR_DECODE);
8188cd1e17SGleb Smirnoff if ((buf = XDR_INLINE(&xdrs, 8 * BYTES_PER_XDR_UNIT)) == NULL) {
8288cd1e17SGleb Smirnoff printf("\trunt datagram\n");
8388cd1e17SGleb Smirnoff return;
8488cd1e17SGleb Smirnoff }
8588cd1e17SGleb Smirnoff
8688cd1e17SGleb Smirnoff msg.rm_xid = IXDR_GET_U_INT32(buf);
8788cd1e17SGleb Smirnoff msg.rm_direction = IXDR_GET_ENUM(buf, enum msg_type);
8888cd1e17SGleb Smirnoff msg.rm_call.cb_rpcvers = IXDR_GET_U_INT32(buf);
8988cd1e17SGleb Smirnoff msg.rm_call.cb_prog = IXDR_GET_U_INT32(buf);
9088cd1e17SGleb Smirnoff msg.rm_call.cb_vers = IXDR_GET_U_INT32(buf);
9188cd1e17SGleb Smirnoff msg.rm_call.cb_proc = IXDR_GET_U_INT32(buf);
9288cd1e17SGleb Smirnoff printf(" %5s: xid 0x%-8x program 0x%08xv%u procedure %u\n",
9388cd1e17SGleb Smirnoff msg.rm_direction == CALL ? "CALL" : "REPLY", msg.rm_xid,
9488cd1e17SGleb Smirnoff msg.rm_call.cb_prog, msg.rm_call.cb_vers, msg.rm_call.cb_proc);
9588cd1e17SGleb Smirnoff
9688cd1e17SGleb Smirnoff oa = &msg.rm_call.cb_cred;
9788cd1e17SGleb Smirnoff oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t);
9888cd1e17SGleb Smirnoff oa->oa_length = (u_int)IXDR_GET_U_INT32(buf);
9988cd1e17SGleb Smirnoff if (oa->oa_length) {
10088cd1e17SGleb Smirnoff printf("\tcb_cred auth flavor %u length %u\n",
10188cd1e17SGleb Smirnoff oa->oa_flavor, oa->oa_length);
10288cd1e17SGleb Smirnoff /*
10388cd1e17SGleb Smirnoff * Excerpt from rpc_callmsg.c, if we want to parse cb_cred better.
10488cd1e17SGleb Smirnoff if (oa->oa_length > MAX_AUTH_BYTES) {
10588cd1e17SGleb Smirnoff return (FALSE);
10688cd1e17SGleb Smirnoff }
10788cd1e17SGleb Smirnoff if (oa->oa_base == NULL) {
10888cd1e17SGleb Smirnoff oa->oa_base = (caddr_t)
10988cd1e17SGleb Smirnoff mem_alloc(oa->oa_length);
11088cd1e17SGleb Smirnoff if (oa->oa_base == NULL)
11188cd1e17SGleb Smirnoff return (FALSE);
11288cd1e17SGleb Smirnoff }
11388cd1e17SGleb Smirnoff buf = XDR_INLINE(&xdrs, RNDUP(oa->oa_length));
11488cd1e17SGleb Smirnoff if (buf == NULL) {
11588cd1e17SGleb Smirnoff if (xdr_opaque(&xdrs, oa->oa_base,
11688cd1e17SGleb Smirnoff oa->oa_length) == FALSE) {
11788cd1e17SGleb Smirnoff return (FALSE);
11888cd1e17SGleb Smirnoff }
11988cd1e17SGleb Smirnoff } else {
12088cd1e17SGleb Smirnoff memmove(oa->oa_base, buf,
12188cd1e17SGleb Smirnoff oa->oa_length);
12288cd1e17SGleb Smirnoff }
12388cd1e17SGleb Smirnoff */
12488cd1e17SGleb Smirnoff }
12588cd1e17SGleb Smirnoff oa = &msg.rm_call.cb_verf;
12688cd1e17SGleb Smirnoff buf = XDR_INLINE(&xdrs, 2 * BYTES_PER_XDR_UNIT);
12788cd1e17SGleb Smirnoff if (buf == NULL) {
12888cd1e17SGleb Smirnoff if (xdr_enum(&xdrs, &oa->oa_flavor) == FALSE ||
12988cd1e17SGleb Smirnoff xdr_u_int(&xdrs, &oa->oa_length) == FALSE)
13088cd1e17SGleb Smirnoff return;
13188cd1e17SGleb Smirnoff } else {
13288cd1e17SGleb Smirnoff oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t);
13388cd1e17SGleb Smirnoff oa->oa_length = (u_int)IXDR_GET_U_INT32(buf);
13488cd1e17SGleb Smirnoff }
13588cd1e17SGleb Smirnoff if (oa->oa_length) {
13688cd1e17SGleb Smirnoff printf("\tcb_verf auth flavor %u length %u\n",
13788cd1e17SGleb Smirnoff oa->oa_flavor, oa->oa_length);
13888cd1e17SGleb Smirnoff /*
13988cd1e17SGleb Smirnoff * Excerpt from rpc_callmsg.c, if we want to parse cb_verf better.
14088cd1e17SGleb Smirnoff if (oa->oa_length > MAX_AUTH_BYTES) {
14188cd1e17SGleb Smirnoff return (FALSE);
14288cd1e17SGleb Smirnoff }
14388cd1e17SGleb Smirnoff if (oa->oa_base == NULL) {
14488cd1e17SGleb Smirnoff oa->oa_base = (caddr_t)
14588cd1e17SGleb Smirnoff mem_alloc(oa->oa_length);
14688cd1e17SGleb Smirnoff if (oa->oa_base == NULL)
14788cd1e17SGleb Smirnoff return (FALSE);
14888cd1e17SGleb Smirnoff }
14988cd1e17SGleb Smirnoff buf = XDR_INLINE(&xdrs, RNDUP(oa->oa_length));
15088cd1e17SGleb Smirnoff if (buf == NULL) {
15188cd1e17SGleb Smirnoff if (xdr_opaque(&xdrs, oa->oa_base,
15288cd1e17SGleb Smirnoff oa->oa_length) == FALSE) {
15388cd1e17SGleb Smirnoff return (FALSE);
15488cd1e17SGleb Smirnoff }
15588cd1e17SGleb Smirnoff } else {
15688cd1e17SGleb Smirnoff memmove(oa->oa_base, buf,
15788cd1e17SGleb Smirnoff oa->oa_length);
15888cd1e17SGleb Smirnoff }
15988cd1e17SGleb Smirnoff */
16088cd1e17SGleb Smirnoff }
16188cd1e17SGleb Smirnoff }
162