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
This commit is contained in:
parent
6fe4d8395b
commit
86a6393a7d
|
@ -32,7 +32,7 @@
|
||||||
.\"
|
.\"
|
||||||
.\" Author: Archie Cobbs <archie@FreeBSD.org>
|
.\" Author: Archie Cobbs <archie@FreeBSD.org>
|
||||||
.\"
|
.\"
|
||||||
.Dd May 13, 2021
|
.Dd April 8, 2024
|
||||||
.Dt NG_BRIDGE 4
|
.Dt NG_BRIDGE 4
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
@ -108,6 +108,17 @@ Frames with unknown MACs are always sent out to
|
||||||
.Ar uplink
|
.Ar uplink
|
||||||
hooks, so no functionality is lost.
|
hooks, so no functionality is lost.
|
||||||
.Pp
|
.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
|
Frames with unknown destination MAC addresses are replicated to any
|
||||||
available hook, unless the first connected hook is an
|
available hook, unless the first connected hook is an
|
||||||
.Ar uplink
|
.Ar uplink
|
||||||
|
|
|
@ -124,6 +124,8 @@ struct ng_bridge_private {
|
||||||
unsigned int persistent : 1, /* can exist w/o hooks */
|
unsigned int persistent : 1, /* can exist w/o hooks */
|
||||||
sendUnknown : 1;/* links receive unknowns by default */
|
sendUnknown : 1;/* links receive unknowns by default */
|
||||||
struct callout timer; /* one second periodic timer */
|
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 *priv_p;
|
||||||
typedef struct ng_bridge_private const *priv_cp; /* read only access */
|
typedef struct ng_bridge_private const *priv_cp; /* read only access */
|
||||||
|
@ -140,6 +142,21 @@ struct ng_bridge_host {
|
||||||
/* Hash table bucket declaration */
|
/* Hash table bucket declaration */
|
||||||
SLIST_HEAD(ng_bridge_bucket, ng_bridge_host);
|
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 */
|
/* Netgraph node methods */
|
||||||
static ng_constructor_t ng_bridge_constructor;
|
static ng_constructor_t ng_bridge_constructor;
|
||||||
static ng_rcvmsg_t ng_bridge_rcvmsg;
|
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;
|
static ng_disconnect_t ng_bridge_disconnect;
|
||||||
|
|
||||||
/* Other internal functions */
|
/* 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 void ng_bridge_free_link(link_p link);
|
||||||
static struct ng_bridge_host *ng_bridge_get(priv_cp priv, const u_char *addr);
|
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);
|
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);
|
NG_NODE_SET_PRIVATE(node, priv);
|
||||||
priv->node = node;
|
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 */
|
/* Start timer; timer is always running while node is alive */
|
||||||
ng_callout(&priv->timer, node, NULL, hz, ng_bridge_timeout, NULL, 0);
|
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)
|
ng_bridge_newhook(node_p node, hook_p hook, const char *name)
|
||||||
{
|
{
|
||||||
const priv_p priv = NG_NODE_PRIVATE(node);
|
const priv_p priv = NG_NODE_PRIVATE(node);
|
||||||
char linkName[NG_HOOKSIZ];
|
|
||||||
u_int32_t linkNum;
|
|
||||||
link_p link;
|
link_p link;
|
||||||
const char *prefix = NG_BRIDGE_HOOK_LINK_PREFIX;
|
|
||||||
bool isUplink;
|
bool isUplink;
|
||||||
|
uint32_t linkNum;
|
||||||
|
struct unrhdr *unit;
|
||||||
|
|
||||||
/* Check for a link hook */
|
const struct ng_link_prefix *pfx = ng_get_link_prefix(name);
|
||||||
if (strlen(name) <= strlen(prefix))
|
if (pfx == NULL)
|
||||||
return (EINVAL); /* Unknown hook name */
|
return (EINVAL); /* not a valid prefix */
|
||||||
|
|
||||||
isUplink = (name[0] == 'u');
|
isUplink = (pfx == &uplink_pfx);
|
||||||
if (isUplink)
|
unit = isUplink ? priv->uplinkUnit : priv->linkUnit;
|
||||||
prefix = NG_BRIDGE_HOOK_UPLINK_PREFIX;
|
|
||||||
|
|
||||||
/* primitive parsing */
|
if (strlen(name) > pfx->len) { /* given number */
|
||||||
linkNum = strtoul(name + strlen(prefix), NULL, 10);
|
char linkName[NG_HOOKSIZ];
|
||||||
/* validation by comparing against the reconstucted name */
|
int rvnum __diagused;
|
||||||
snprintf(linkName, sizeof(linkName), "%s%u", prefix, linkNum);
|
|
||||||
if (strcmp(linkName, name) != 0)
|
|
||||||
return (EINVAL);
|
|
||||||
|
|
||||||
if (linkNum == 0 && isUplink)
|
linkNum = strtoul(name + pfx->len, NULL, 10);
|
||||||
return (EINVAL);
|
/* 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);
|
||||||
|
}
|
||||||
|
|
||||||
if(NG_PEER_NODE(hook) == node)
|
if (NG_PEER_NODE(hook) == node) {
|
||||||
|
free_unr(unit, linkNum);
|
||||||
return (ELOOP);
|
return (ELOOP);
|
||||||
|
}
|
||||||
|
|
||||||
link = malloc(sizeof(*link), M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
|
link = malloc(sizeof(*link), M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
|
||||||
if (link == NULL)
|
if (link == NULL) {
|
||||||
|
free_unr(unit, linkNum);
|
||||||
return (ENOMEM);
|
return (ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
#define NG_BRIDGE_COUNTER_ALLOC(f) do { \
|
#define NG_BRIDGE_COUNTER_ALLOC(f) do { \
|
||||||
link->stats.f = counter_u64_alloc(M_NOWAIT); \
|
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);
|
return (0);
|
||||||
|
|
||||||
nomem:
|
nomem:
|
||||||
|
free_unr(unit, linkNum);
|
||||||
ng_bridge_free_link(link);
|
ng_bridge_free_link(link);
|
||||||
return (ENOMEM);
|
return (ENOMEM);
|
||||||
}
|
}
|
||||||
|
@ -914,6 +951,8 @@ ng_bridge_shutdown(node_p node)
|
||||||
("%s: numLinks=%d numHosts=%d",
|
("%s: numLinks=%d numHosts=%d",
|
||||||
__func__, priv->numLinks, priv->numHosts));
|
__func__, priv->numLinks, priv->numHosts));
|
||||||
ng_uncallout(&priv->timer, node);
|
ng_uncallout(&priv->timer, node);
|
||||||
|
delete_unrhdr(priv->linkUnit);
|
||||||
|
delete_unrhdr(priv->uplinkUnit);
|
||||||
NG_NODE_SET_PRIVATE(node, NULL);
|
NG_NODE_SET_PRIVATE(node, NULL);
|
||||||
NG_NODE_UNREF(node);
|
NG_NODE_UNREF(node);
|
||||||
free(priv->tab, M_NETGRAPH_BRIDGE);
|
free(priv->tab, M_NETGRAPH_BRIDGE);
|
||||||
|
@ -927,8 +966,11 @@ ng_bridge_shutdown(node_p node)
|
||||||
static int
|
static int
|
||||||
ng_bridge_disconnect(hook_p hook)
|
ng_bridge_disconnect(hook_p hook)
|
||||||
{
|
{
|
||||||
|
char *name = NG_HOOK_NAME(hook);
|
||||||
const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
|
const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
|
||||||
link_p link = NG_HOOK_PRIVATE(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 */
|
/* Remove all hosts associated with this link */
|
||||||
ng_bridge_remove_hosts(priv, link);
|
ng_bridge_remove_hosts(priv, link);
|
||||||
|
@ -937,6 +979,9 @@ ng_bridge_disconnect(hook_p hook)
|
||||||
ng_bridge_free_link(link);
|
ng_bridge_free_link(link);
|
||||||
priv->numLinks--;
|
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 no more hooks, go away */
|
||||||
if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
|
if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
|
||||||
&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
|
&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
|
||||||
|
@ -1095,6 +1140,19 @@ ng_bridge_rehash(priv_p priv)
|
||||||
MISC FUNCTIONS
|
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.
|
* Remove all hosts associated with a specific link from the hashtable.
|
||||||
* If linkNum == -1, then remove all hosts in the table.
|
* If linkNum == -1, then remove all hosts in the table.
|
||||||
|
|
Loading…
Reference in New Issue