git: b32cf15d8617 - main - netlink: add support for dumping kernel nexthops.

From: Alexander V. Chernikov <melifaro_at_FreeBSD.org>
Date: Tue, 25 Apr 2023 11:15:34 UTC
The branch main has been updated by melifaro:

URL: https://cgit.FreeBSD.org/src/commit/?id=b32cf15d861794fc91d0f2666a71865ccd0fcb3e

commit b32cf15d861794fc91d0f2666a71865ccd0fcb3e
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2023-04-25 11:12:18 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2023-04-25 11:12:18 +0000

    netlink: add support for dumping kernel nexthops.
    
    MFC after:      2 weeks
---
 sys/netlink/route/nexthop.c | 107 +++++++++++++++++++++++++++++++++++---------
 sys/netlink/route/nexthop.h |  11 +++++
 2 files changed, 98 insertions(+), 20 deletions(-)

diff --git a/sys/netlink/route/nexthop.c b/sys/netlink/route/nexthop.c
index 1b468ca18e73..94cc6caf7d82 100644
--- a/sys/netlink/route/nexthop.c
+++ b/sys/netlink/route/nexthop.c
@@ -436,11 +436,9 @@ enomem:
 }
 
 static bool
-dump_nhop(const struct user_nhop *unhop, struct nlmsghdr *hdr,
+dump_nhop(const struct nhop_object *nh, uint32_t uidx, struct nlmsghdr *hdr,
     struct nl_writer *nw)
 {
-	struct nhop_object *nh = unhop->un_nhop_src;
-
 	if (!nlmsg_reply(nw, hdr, sizeof(struct nhmsg)))
 		goto enomem;
 
@@ -448,10 +446,11 @@ dump_nhop(const struct user_nhop *unhop, struct nlmsghdr *hdr,
 	ENOMEM_IF_NULL(nhm);
 	nhm->nh_family = nhop_get_neigh_family(nh);
 	nhm->nh_scope = 0; // XXX: what's that?
-	nhm->nh_protocol = unhop->un_protocol;
+	nhm->nh_protocol = nhop_get_origin(nh);
 	nhm->nh_flags = 0;
 
-	nlattr_add_u32(nw, NHA_ID, unhop->un_idx);
+	if (uidx != 0)
+		nlattr_add_u32(nw, NHA_ID, uidx);
 	if (nh->nh_flags & NHF_BLACKHOLE) {
 		nlattr_add_flag(nw, NHA_BLACKHOLE);
 		goto done;
@@ -475,6 +474,19 @@ dump_nhop(const struct user_nhop *unhop, struct nlmsghdr *hdr,
 #endif
 	}
 
+	int off = nlattr_add_nested(nw, NHA_FREEBSD);
+	if (off != 0) {
+		nlattr_add_u32(nw, NHAF_AIF, nh->nh_aifp->if_index);
+
+		if (uidx == 0) {
+			nlattr_add_u32(nw, NHAF_KID, nhop_get_idx(nh));
+			nlattr_add_u32(nw, NHAF_FAMILY, nhop_get_upper_family(nh));
+			nlattr_add_u32(nw, NHAF_TABLE, nhop_get_fibnum(nh));
+		}
+
+		nlattr_set_len(nw, off);
+	}
+
 done:
         if (nlmsg_end(nw))
 		return (true);
@@ -488,7 +500,7 @@ dump_unhop(const struct user_nhop *unhop, struct nlmsghdr *hdr,
     struct nl_writer *nw)
 {
 	if (unhop->un_nhop_src != NULL)
-		dump_nhop(unhop, hdr, nw);
+		dump_nhop(unhop->un_nhop_src, unhop->un_idx, hdr, nw);
 	else
 		dump_nhgrp(unhop, hdr, nw);
 }
@@ -670,15 +682,29 @@ struct nl_parsed_nhop {
 	uint32_t	nha_id;
 	uint8_t		nha_blackhole;
 	uint8_t		nha_groups;
+	uint8_t		nhaf_knhops;
+	uint8_t		nhaf_family;
 	struct ifnet	*nha_oif;
 	struct sockaddr	*nha_gw;
 	struct nlattr	*nha_group;
 	uint8_t		nh_family;
 	uint8_t		nh_protocol;
+	uint32_t	nhaf_table;
+	uint32_t	nhaf_kid;
+	uint32_t	nhaf_aif;
 };
 
 #define	_IN(_field)	offsetof(struct nhmsg, _field)
 #define	_OUT(_field)	offsetof(struct nl_parsed_nhop, _field)
+static struct nlattr_parser nla_p_nh_fbsd[] = {
+	{ .type = NHAF_KNHOPS, .off = _OUT(nhaf_knhops), .cb = nlattr_get_flag },
+	{ .type = NHAF_TABLE, .off = _OUT(nhaf_table), .cb = nlattr_get_uint32 },
+	{ .type = NHAF_FAMILY, .off = _OUT(nhaf_family), .cb = nlattr_get_uint8 },
+	{ .type = NHAF_KID, .off = _OUT(nhaf_kid), .cb = nlattr_get_uint32 },
+	{ .type = NHAF_AIF, .off = _OUT(nhaf_aif), .cb = nlattr_get_uint32 },
+};
+NL_DECLARE_ATTR_PARSER(nh_fbsd_parser, nla_p_nh_fbsd);
+
 static const struct nlfield_parser nlf_p_nh[] = {
 	{ .off_in = _IN(nh_family), .off_out = _OUT(nh_family), .cb = nlf_get_u8 },
 	{ .off_in = _IN(nh_protocol), .off_out = _OUT(nh_protocol), .cb = nlf_get_u8 },
@@ -691,6 +717,7 @@ static const struct nlattr_parser nla_p_nh[] = {
 	{ .type = NHA_OIF, .off = _OUT(nha_oif), .cb = nlattr_get_ifp },
 	{ .type = NHA_GATEWAY, .off = _OUT(nha_gw), .cb = nlattr_get_ip },
 	{ .type = NHA_GROUPS, .off = _OUT(nha_groups), .cb = nlattr_get_flag },
+	{ .type = NHA_FREEBSD, .arg = &nh_fbsd_parser, .cb = nlattr_get_nested },
 };
 #undef _IN
 #undef _OUT
@@ -801,6 +828,7 @@ newnhop(struct nl_parsed_nhop *attrs, struct user_nhop *unhop, struct nl_pstate
 		return (ENOMEM);
 	}
 	nhop_set_uidx(nh, attrs->nha_id);
+	nhop_set_origin(nh, attrs->nh_protocol);
 
 	if (attrs->nha_blackhole)
 		nhop_set_blackhole(nh, NHF_BLACKHOLE);
@@ -957,14 +985,10 @@ static int
 rtnl_handle_getnhop(struct nlmsghdr *hdr, struct nlpcb *nlp,
     struct nl_pstate *npt)
 {
-	struct unhop_ctl *ctl = atomic_load_ptr(&V_un_ctl);
 	struct user_nhop *unhop;
 	UN_TRACKER;
 	int error;
 
-	if (__predict_false(ctl == NULL))
-		return (ESRCH);
-
 	struct nl_parsed_nhop attrs = {};
 	error = nl_parse_nlmsg(hdr, &nhmsg_parser, npt, &attrs);
 	if (error != 0)
@@ -979,8 +1003,13 @@ rtnl_handle_getnhop(struct nlmsghdr *hdr, struct nlpcb *nlp,
 	};
 
 	if (attrs.nha_id != 0) {
+		struct unhop_ctl *ctl = atomic_load_ptr(&V_un_ctl);
+		struct user_nhop key = { .un_idx = attrs.nha_id };
+
+		if (__predict_false(ctl == NULL))
+			return (ESRCH);
+
 		NL_LOG(LOG_DEBUG2, "searching for uidx %u", attrs.nha_id);
-		struct user_nhop key= { .un_idx = attrs.nha_id };
 		UN_RLOCK(ctl);
 		CHT_SLIST_FIND_BYOBJ(&ctl->un_head, unhop, &key, unhop);
 		UN_RUNLOCK(ctl);
@@ -989,15 +1018,53 @@ rtnl_handle_getnhop(struct nlmsghdr *hdr, struct nlpcb *nlp,
 			return (ESRCH);
 		dump_unhop(unhop, &wa.hdr, wa.nw);
 		return (0);
-	}
+	} else if (attrs.nhaf_kid != 0) {
+		struct nhop_iter iter = {
+			.fibnum = attrs.nhaf_table,
+			.family = attrs.nhaf_family,
+		};
+		int error = ESRCH;
+
+		NL_LOG(LOG_DEBUG2, "START table %u family %d", attrs.nhaf_table, attrs.nhaf_family);
+		for (struct nhop_object *nh = nhops_iter_start(&iter); nh;
+		    nh = nhops_iter_next(&iter)) {
+			NL_LOG(LOG_DEBUG3, "get %u", nhop_get_idx(nh));
+			if (nhop_get_idx(nh) == attrs.nhaf_kid) {
+				dump_nhop(nh, 0, &wa.hdr, wa.nw);
+				error = 0;
+				break;
+			}
+		}
+		nhops_iter_stop(&iter);
+		return (error);
+	} else if (attrs.nhaf_knhops) {
+		struct nhop_iter iter = {
+			.fibnum = attrs.nhaf_table,
+			.family = attrs.nhaf_family,
+		};
+
+		NL_LOG(LOG_DEBUG2, "DUMP table %u family %d", attrs.nhaf_table, attrs.nhaf_family);
+		wa.hdr.nlmsg_flags |= NLM_F_MULTI;
+		for (struct nhop_object *nh = nhops_iter_start(&iter); nh;
+		    nh = nhops_iter_next(&iter)) {
+			dump_nhop(nh, 0, &wa.hdr, wa.nw);
+		}
+		nhops_iter_stop(&iter);
+	} else {
+		struct unhop_ctl *ctl = atomic_load_ptr(&V_un_ctl);
 
-	UN_RLOCK(ctl);
-	wa.hdr.nlmsg_flags |= NLM_F_MULTI;
-	CHT_SLIST_FOREACH(&ctl->un_head, unhop, unhop) {
-		if (UNHOP_IS_MASTER(unhop) && match_unhop(&attrs, unhop))
-			dump_unhop(unhop, &wa.hdr, wa.nw);
-	} CHT_SLIST_FOREACH_END;
-	UN_RUNLOCK(ctl);
+		if (__predict_false(ctl == NULL))
+			return (ESRCH);
+
+		NL_LOG(LOG_DEBUG2, "DUMP unhops");
+		UN_RLOCK(ctl);
+		wa.hdr.nlmsg_flags |= NLM_F_MULTI;
+		CHT_SLIST_FOREACH(&ctl->un_head, unhop, unhop) {
+			if (UNHOP_IS_MASTER(unhop) && match_unhop(&attrs, unhop))
+				dump_unhop(unhop, &wa.hdr, wa.nw);
+		} CHT_SLIST_FOREACH_END;
+		UN_RUNLOCK(ctl);
+	}
 
 	if (wa.error == 0) {
 		if (!nlmsg_end_dump(wa.nw, wa.error, &wa.hdr))
@@ -1026,7 +1093,7 @@ static const struct rtnl_cmd_handler cmd_handlers[] = {
 	}
 };
 
-static const struct nlhdr_parser *all_parsers[] = { &nhmsg_parser };
+static const struct nlhdr_parser *all_parsers[] = { &nhmsg_parser, &nh_fbsd_parser };
 
 void
 rtnl_nexthops_init(void)
diff --git a/sys/netlink/route/nexthop.h b/sys/netlink/route/nexthop.h
index 310c3e08fc4b..4128e014a2d0 100644
--- a/sys/netlink/route/nexthop.h
+++ b/sys/netlink/route/nexthop.h
@@ -56,10 +56,21 @@ enum {
 	NHA_FDB,	/* not supported */
 	NHA_RES_GROUP,	/* not supported */
 	NHA_RES_BUCKET,	/* not supported */
+	NHA_FREEBSD,	/* nested: FreeBSD-specific attributes */
 	__NHA_MAX,
 };
 #define NHA_MAX	(__NHA_MAX - 1)
 
+enum {
+	NHAF_UNSPEC,
+	NHAF_KNHOPS,	/* flag: dump kernel nexthops */
+	NHAF_KGOUPS,	/* flag: dump kernel nexthop groups */
+	NHAF_TABLE,	/* u32: rtable id */
+	NHAF_FAMILY,	/* u32: upper family */
+	NHAF_KID,	/* u32: kernel nexthop index */
+	NHAF_AIF,	/* u32: source interface address */
+};
+
 /*
  * Attributes that can be used as filters:
  * NHA_ID (nexhop or group), NHA_OIF, NHA_GROUPS,