git: 86a6393a7d67 - main - ng_bridge: allow to automatically assign numbers to new hooks

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Mon, 08 Apr 2024 17:48:45 UTC
The branch main has been updated by glebius:

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

commit 86a6393a7d6766875a9e03daa0273a2e55faacdd
Author:     David Marker <dave@freedave.net>
AuthorDate: 2024-04-08 17:48:22 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2024-04-08 17:48:22 +0000

    ng_bridge: allow to automatically assign numbers to new hooks
    
    This will allow a userland machinery that orchestrates a bridge (e.g. a
    jail or vm manager) to not double the number allocation logic.  See bug
    278130 for longer description and examples.
    
    Reviewed by:            glebius, afedorov
    Differential Revision:  https://reviews.freebsd.org/D44615
    PR:                     278130
---
 share/man/man4/ng_bridge.4 |  13 +++++-
 sys/netgraph/ng_bridge.c   | 104 +++++++++++++++++++++++++++++++++++----------
 2 files changed, 93 insertions(+), 24 deletions(-)

diff --git a/share/man/man4/ng_bridge.4 b/share/man/man4/ng_bridge.4
index 45bc6b3cb6a7..7e5b4e0bd0ca 100644
--- a/share/man/man4/ng_bridge.4
+++ b/share/man/man4/ng_bridge.4
@@ -32,7 +32,7 @@
 .\"
 .\" Author: Archie Cobbs <archie@FreeBSD.org>
 .\"
-.Dd May 13, 2021
+.Dd April 8, 2024
 .Dt NG_BRIDGE 4
 .Os
 .Sh NAME
@@ -108,6 +108,17 @@ Frames with unknown MACs are always sent out to
 .Ar uplink
 hooks, so no functionality is lost.
 .Pp
+The
+.Ar linkX
+and
+.Ar uplinkX
+hook numbers can be autoassigned.
+If a new hook name was specified as
+.Ar link
+or
+.Ar uplink
+the node will append lowest available valid number to the name of the new hook.
+.Pp
 Frames with unknown destination MAC addresses are replicated to any
 available hook, unless the first connected hook is an
 .Ar uplink
diff --git a/sys/netgraph/ng_bridge.c b/sys/netgraph/ng_bridge.c
index ebe811acc5b5..70bc581f8570 100644
--- a/sys/netgraph/ng_bridge.c
+++ b/sys/netgraph/ng_bridge.c
@@ -124,6 +124,8 @@ struct ng_bridge_private {
 	unsigned int		persistent : 1,	/* can exist w/o hooks */
 				sendUnknown : 1;/* links receive unknowns by default */
 	struct callout		timer;		/* one second periodic timer */
+	struct unrhdr 		*linkUnit;	/* link unit number allocator */
+	struct unrhdr 		*uplinkUnit;	/* uplink unit number allocator */
 };
 typedef struct ng_bridge_private *priv_p;
 typedef struct ng_bridge_private const *priv_cp;	/* read only access */
@@ -140,6 +142,21 @@ struct ng_bridge_host {
 /* Hash table bucket declaration */
 SLIST_HEAD(ng_bridge_bucket, ng_bridge_host);
 
+/* [up]link prefix matching */
+struct ng_link_prefix {
+	const char * const	prefix;
+	size_t			len;
+};
+
+static const struct ng_link_prefix link_pfx = {
+       .prefix = NG_BRIDGE_HOOK_LINK_PREFIX,
+       .len = sizeof(NG_BRIDGE_HOOK_LINK_PREFIX) - 1,
+};
+static const struct ng_link_prefix uplink_pfx = {
+        .prefix = NG_BRIDGE_HOOK_UPLINK_PREFIX,
+        .len = sizeof(NG_BRIDGE_HOOK_UPLINK_PREFIX) - 1,
+};
+
 /* Netgraph node methods */
 static ng_constructor_t	ng_bridge_constructor;
 static ng_rcvmsg_t	ng_bridge_rcvmsg;
@@ -149,6 +166,7 @@ static ng_rcvdata_t	ng_bridge_rcvdata;
 static ng_disconnect_t	ng_bridge_disconnect;
 
 /* Other internal functions */
+static const struct	ng_link_prefix *ng_get_link_prefix(const char *name);
 static void	ng_bridge_free_link(link_p link);
 static struct	ng_bridge_host *ng_bridge_get(priv_cp priv, const u_char *addr);
 static int	ng_bridge_put(priv_p priv, const u_char *addr, link_p link);
@@ -350,6 +368,10 @@ ng_bridge_constructor(node_p node)
 	NG_NODE_SET_PRIVATE(node, priv);
 	priv->node = node;
 
+	/* Allocators for links. Historically "uplink0" is not allowed. */
+	priv->linkUnit = new_unrhdr(0, INT_MAX, NULL);
+	priv->uplinkUnit = new_unrhdr(1, INT_MAX, NULL);
+
 	/* Start timer; timer is always running while node is alive */
 	ng_callout(&priv->timer, node, NULL, hz, ng_bridge_timeout, NULL, 0);
 
@@ -364,36 +386,50 @@ static	int
 ng_bridge_newhook(node_p node, hook_p hook, const char *name)
 {
 	const priv_p priv = NG_NODE_PRIVATE(node);
-	char linkName[NG_HOOKSIZ];
-	u_int32_t linkNum;
 	link_p link;
-	const char *prefix = NG_BRIDGE_HOOK_LINK_PREFIX;
 	bool isUplink;
+	uint32_t linkNum;
+	struct unrhdr *unit;
+
+	const struct ng_link_prefix *pfx = ng_get_link_prefix(name);
+	if (pfx == NULL)
+		return (EINVAL);  /* not a valid prefix */
+
+	isUplink = (pfx == &uplink_pfx);
+	unit = isUplink ? priv->uplinkUnit : priv->linkUnit;
+
+	if (strlen(name) > pfx->len) { /* given number */
+		char linkName[NG_HOOKSIZ];
+		int rvnum __diagused;
+
+		linkNum = strtoul(name + pfx->len, NULL, 10);
+		/* Validate by comparing against the reconstucted name. */
+		snprintf(linkName, sizeof(linkName), "%s%u", pfx->prefix,
+		    linkNum);
+		if (strcmp(linkName, name) != 0)
+			return (EINVAL);
+		if (linkNum == 0 && isUplink)
+			return (EINVAL);
+		rvnum = alloc_unr_specific(unit, linkNum);
+		MPASS(rvnum == linkNum);
+	} else {
+		/* auto-assign and update hook name */
+		linkNum = alloc_unr(unit);
+		MPASS(linkNum != -1);
+		snprintf(NG_HOOK_NAME(hook), NG_HOOKSIZ, "%s%u", pfx->prefix,
+		    linkNum);
+	}
 
-	/* Check for a link hook */
-	if (strlen(name) <= strlen(prefix))
-		return (EINVAL);       /* Unknown hook name */
-
-	isUplink = (name[0] == 'u');
-	if (isUplink)
-		prefix = NG_BRIDGE_HOOK_UPLINK_PREFIX;
-
-	/* primitive parsing */
-	linkNum = strtoul(name + strlen(prefix), NULL, 10);
-	/* validation by comparing against the reconstucted name  */
-	snprintf(linkName, sizeof(linkName), "%s%u", prefix, linkNum);
-	if (strcmp(linkName, name) != 0)
-		return (EINVAL);
-
-	if (linkNum == 0 && isUplink)
-		return (EINVAL);
-
-	if(NG_PEER_NODE(hook) == node)
+	if (NG_PEER_NODE(hook) == node) {
+		free_unr(unit, linkNum);
 	        return (ELOOP);
+	}
 
 	link = malloc(sizeof(*link), M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
-	if (link == NULL)
+	if (link == NULL) {
+		free_unr(unit, linkNum);
 		return (ENOMEM);
+	}
 
 #define	NG_BRIDGE_COUNTER_ALLOC(f) do {			\
 	link->stats.f = counter_u64_alloc(M_NOWAIT);	\
@@ -431,6 +467,7 @@ ng_bridge_newhook(node_p node, hook_p hook, const char *name)
 	return (0);
 
 nomem:
+	free_unr(unit, linkNum);
 	ng_bridge_free_link(link);
 	return (ENOMEM);
 }
@@ -914,6 +951,8 @@ ng_bridge_shutdown(node_p node)
 	    ("%s: numLinks=%d numHosts=%d",
 	    __func__, priv->numLinks, priv->numHosts));
 	ng_uncallout(&priv->timer, node);
+	delete_unrhdr(priv->linkUnit);
+	delete_unrhdr(priv->uplinkUnit);
 	NG_NODE_SET_PRIVATE(node, NULL);
 	NG_NODE_UNREF(node);
 	free(priv->tab, M_NETGRAPH_BRIDGE);
@@ -927,8 +966,11 @@ ng_bridge_shutdown(node_p node)
 static int
 ng_bridge_disconnect(hook_p hook)
 {
+	char *name = NG_HOOK_NAME(hook);
 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
 	link_p link = NG_HOOK_PRIVATE(hook);
+	const struct ng_link_prefix *pfx = ng_get_link_prefix(name);
+	uint32_t linkNum;
 
 	/* Remove all hosts associated with this link */
 	ng_bridge_remove_hosts(priv, link);
@@ -937,6 +979,9 @@ ng_bridge_disconnect(hook_p hook)
 	ng_bridge_free_link(link);
 	priv->numLinks--;
 
+	linkNum = strtoul(name + pfx->len, NULL, 10);
+	free_unr(pfx == &link_pfx ? priv->linkUnit: priv->uplinkUnit, linkNum);
+
 	/* If no more hooks, go away */
 	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
 	    && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
@@ -1095,6 +1140,19 @@ ng_bridge_rehash(priv_p priv)
 		    MISC FUNCTIONS
 ******************************************************************/
 
+static const struct ng_link_prefix *
+ng_get_link_prefix(const char *name)
+{
+	static const struct ng_link_prefix *pfxs[] =
+	    { &link_pfx, &uplink_pfx, };
+
+	for (u_int i = 0; i < nitems(pfxs); i++)
+		if (strncmp(pfxs[i]->prefix, name, pfxs[i]->len) == 0)
+			return (pfxs[i]);
+
+	return (NULL);
+}
+
 /*
  * Remove all hosts associated with a specific link from the hashtable.
  * If linkNum == -1, then remove all hosts in the table.