git: ec4ae3856656 - stable/13 - netlink: allow creation of temporary lle entries.

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Wed, 01 Nov 2023 09:06:05 UTC
The branch stable/13 has been updated by kp:

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

commit ec4ae38566569bfac4e18e1fb2ce61fec1e2361b
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2023-04-25 11:08:47 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2023-10-31 08:08:44 +0000

    netlink: allow creation of temporary lle entries.
    
    MFC after:      2 weeks
    
    (cherry picked from commit a2728a9a5b8da974e238e6413a980134dbd6297f)
---
 sys/netlink/route/neigh.c | 58 +++++++++++++++++++++++++++++++++++------------
 sys/netlink/route/neigh.h |  8 ++++++-
 2 files changed, 50 insertions(+), 16 deletions(-)

diff --git a/sys/netlink/route/neigh.c b/sys/netlink/route/neigh.c
index 097b75298745..140194b4ad32 100644
--- a/sys/netlink/route/neigh.c
+++ b/sys/netlink/route/neigh.c
@@ -119,6 +119,14 @@ lle_flags_to_nl_flags(const struct llentry *lle)
 	return (nl_flags);
 }
 
+static uint32_t
+get_lle_next_ts(const struct llentry *lle)
+{
+	if (lle->la_expire == 0)
+		return (0);
+	return (lle->la_expire + lle->lle_remtime / hz + time_second - time_uptime);
+}
+
 static int
 dump_lle_locked(struct llentry *lle, void *arg)
 {
@@ -179,6 +187,13 @@ dump_lle_locked(struct llentry *lle, void *arg)
 	/* TODO: provide confirmed/updated */
 	cache->ndm_refcnt = lle->lle_refcnt;
 
+	int off = nlattr_add_nested(nw, NDA_FREEBSD);
+	if (off != 0) {
+		nlattr_add_u32(nw, NDAF_NEXT_STATE_TS, get_lle_next_ts(lle));
+
+		nlattr_set_len(nw, off);
+	}
+
         if (nlmsg_end(nw))
 		return (0);
 enomem:
@@ -282,6 +297,7 @@ struct nl_parsed_neigh {
 	struct sockaddr	*nda_dst;
 	struct ifnet	*nda_ifp;
 	struct nlattr	*nda_lladdr;
+	uint32_t	ndaf_next_ts;
 	uint32_t	ndm_flags;
 	uint16_t	ndm_state;
 	uint8_t		ndm_family;
@@ -289,18 +305,24 @@ struct nl_parsed_neigh {
 
 #define	_IN(_field)	offsetof(struct ndmsg, _field)
 #define	_OUT(_field)	offsetof(struct nl_parsed_neigh, _field)
-static struct nlfield_parser nlf_p_neigh[] = {
+static const struct nlattr_parser nla_p_neigh_fbsd[] = {
+	{ .type = NDAF_NEXT_STATE_TS, .off = _OUT(ndaf_next_ts), .cb = nlattr_get_uint32 },
+};
+NL_DECLARE_ATTR_PARSER(neigh_fbsd_parser, nla_p_neigh_fbsd);
+
+static const struct nlfield_parser nlf_p_neigh[] = {
 	{ .off_in = _IN(ndm_family), .off_out = _OUT(ndm_family), .cb = nlf_get_u8 },
 	{ .off_in = _IN(ndm_flags), .off_out = _OUT(ndm_flags), .cb = nlf_get_u8_u32 },
 	{ .off_in = _IN(ndm_state), .off_out = _OUT(ndm_state), .cb = nlf_get_u16 },
 	{ .off_in = _IN(ndm_ifindex), .off_out = _OUT(nda_ifp), .cb = nlf_get_ifpz },
 };
 
-static struct nlattr_parser nla_p_neigh[] = {
+static const struct nlattr_parser nla_p_neigh[] = {
 	{ .type = NDA_DST, .off = _OUT(nda_dst), .cb = nlattr_get_ip },
 	{ .type = NDA_LLADDR, .off = _OUT(nda_lladdr), .cb = nlattr_get_nla },
 	{ .type = NDA_IFINDEX, .off = _OUT(nda_ifp), .cb = nlattr_get_ifp },
 	{ .type = NDA_FLAGS_EXT, .off = _OUT(ndm_flags), .cb = nlattr_get_uint32 },
+	{ .type = NDA_FREEBSD, .arg = &neigh_fbsd_parser, .cb = nlattr_get_nested },
 };
 #undef _IN
 #undef _OUT
@@ -350,11 +372,6 @@ rtnl_handle_newneigh(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *
 		return (EINVAL);
 	}
 
-	if (attrs.ndm_state != NUD_PERMANENT) {
-		NLMSG_REPORT_ERR_MSG(npt, "ndm_state %d not supported", attrs.ndm_state);
-		return (ENOTSUP);
-	}
-
 	const uint16_t supported_flags = NTF_PROXY | NTF_STICKY;
 	if ((attrs.ndm_flags & supported_flags) != attrs.ndm_flags) {
 		NLMSG_REPORT_ERR_MSG(npt, "ndm_flags %X not supported",
@@ -380,26 +397,36 @@ rtnl_handle_newneigh(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *
 		return (EINVAL);
 	}
 
-	int lle_flags = LLE_STATIC | ((attrs.ndm_flags & NTF_PROXY) ? LLE_PUB : 0);
+	int lle_flags = (attrs.ndm_flags & NTF_PROXY) ? LLE_PUB : 0;
+	if (attrs.ndm_flags & NTF_STICKY)
+		lle_flags |= LLE_STATIC;
 	struct llentry *lle = lltable_alloc_entry(llt, lle_flags, attrs.nda_dst);
 	if (lle == NULL)
 		return (ENOMEM);
 	lltable_set_entry_addr(attrs.nda_ifp, lle, linkhdr, linkhdrsize, lladdr_off);
 
-	/* llentry created, try to insert or update :*/
+	if (attrs.ndm_flags & NTF_STICKY)
+		lle->la_expire = 0;
+	else
+		lle->la_expire = attrs.ndaf_next_ts - time_second + time_uptime;
+
+	/* llentry created, try to insert or update */
 	IF_AFDATA_WLOCK(attrs.nda_ifp);
 	LLE_WLOCK(lle);
 	struct llentry *lle_tmp = lla_lookup(llt, LLE_EXCLUSIVE, attrs.nda_dst);
 	if (lle_tmp != NULL) {
+		error = EEXIST;
 		if (hdr->nlmsg_flags & NLM_F_EXCL) {
 			LLE_WUNLOCK(lle_tmp);
 			lle_tmp = NULL;
-			error = EEXIST;
 		} else if (hdr->nlmsg_flags & NLM_F_REPLACE) {
-			lltable_unlink_entry(llt, lle_tmp);
-			lltable_link_entry(llt, lle);
-		} else
-			error = EEXIST;
+			if ((lle_tmp->la_flags & LLE_IFADDR) == 0) {
+				lltable_unlink_entry(llt, lle_tmp);
+				lltable_link_entry(llt, lle);
+				error = 0;
+			} else
+				error = EPERM;
+		}
 	} else {
 		if (hdr->nlmsg_flags & NLM_F_CREATE)
 			lltable_link_entry(llt, lle);
@@ -455,6 +482,7 @@ rtnl_handle_delneigh(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *
 			LLE_WUNLOCK(lle);
 			lle = NULL;
 			error = EPERM;
+			NLMSG_REPORT_ERR_MSG(npt, "unable to delete ifaddr record");
 		} else
 			lltable_unlink_entry(llt, lle);
 	} else
@@ -554,7 +582,7 @@ rtnl_lle_event(void *arg __unused, struct llentry *lle, int evt)
 	nlmsg_flush(&nw);
 }
 
-static const struct nlhdr_parser *all_parsers[] = { &ndmsg_parser };
+static const struct nlhdr_parser *all_parsers[] = { &ndmsg_parser, &neigh_fbsd_parser };
 
 void
 rtnl_neighs_init(void)
diff --git a/sys/netlink/route/neigh.h b/sys/netlink/route/neigh.h
index d6835505ea74..eacacc09711a 100644
--- a/sys/netlink/route/neigh.h
+++ b/sys/netlink/route/neigh.h
@@ -49,7 +49,7 @@ enum {
 	NDA_DST,		/* binary: neigh l3 address */
 	NDA_LLADDR,		/* binary: neigh link-level address */
 	NDA_CACHEINFO,		/* binary, struct nda_cacheinfo */
-	NDA_PROBES,		/* XXX */
+	NDA_PROBES,		/* u32: number of probes sent */
 	NDA_VLAN,		/* upper 802.1Q tag */
 	NDA_PORT,		/* not supported */
 	NDA_VNI,		/* not supported */
@@ -63,11 +63,17 @@ enum {
 	NDA_FLAGS_EXT,		/* u32: ndm_flags */
 	NDA_NDM_STATE_MASK,	/* XXX */
 	NDA_NDM_FLAGS_MASK,	/* XXX */
+	NDA_FREEBSD,		/* nested: FreeBSD-specific */
 	__NDA_MAX
 };
 
 #define	NDA_MAX	(__NDA_MAX - 1)
 
+enum {
+	NDAF_UNSPEC,
+	NDAF_NEXT_STATE_TS,	/* (u32) seconds from time_uptime when moving to the next state */
+};
+
 
 /* ndm_flags / NDA_FLAGS_EXT */
 #define	NTF_USE			0x0001	/* XXX */