From b1bd44732d8332930dc6a17092f47a201caff1ef Mon Sep 17 00:00:00 2001 From: Lutz Donnerhacke Date: Tue, 4 May 2021 22:14:59 +0200 Subject: [PATCH] netgraph/ng_bridge: learn MACs via control message Add a new control message to move ethernet addresses to a given link in ng_bridge(4). Send this message instead of doing the work directly. This decouples the read-only activity from the modification under a more strict writer lock. Decoupling the work is a prerequisite for multithreaded operation. Approved by: manpages (bcr), kp (earlier version) MFC: 3 weeks Differential Revision: https://reviews.freebsd.org/D28516 --- share/man/man4/ng_bridge.4 | 21 +++++++++++- sys/netgraph/ng_bridge.c | 69 +++++++++++++++++++++++++++++++++----- sys/netgraph/ng_bridge.h | 11 ++++++ 3 files changed, 91 insertions(+), 10 deletions(-) diff --git a/share/man/man4/ng_bridge.4 b/share/man/man4/ng_bridge.4 index be128d62c938..216dea3c392a 100644 --- a/share/man/man4/ng_bridge.4 +++ b/share/man/man4/ng_bridge.4 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 6, 2021 +.Dd February 17, 2021 .Dt NG_BRIDGE 4 .Os .Sh NAME @@ -214,6 +214,25 @@ Returns the current host mapping table used to direct packets, in a .Vt "struct ng_bridge_host_ary" . .It Dv NGM_BRIDGE_SET_PERSISTENT Pq Ar setpersistent This command sets the persistent flag on the node, and takes no arguments. +.It Dv NGM_BRIDGE_MOVE_HOST Pq Ar movehost +This command takes a +.Vt "struct ng_bridge_move_host" +as an argument. +It assigns the MAC +.Va addr +to the +.Va hook , +which must not be assigned yet. +If the +.Va hook +is the empty string, the incoming hook of the control message is +used as fallback. +.Bd -literal -offset 0n +struct ng_bridge_move_host { + u_char addr[ETHER_ADDR_LEN]; /* ethernet address */ + char hook[NG_HOOKSIZ]; /* link where addr can be found */ +}; +.Ed .El .Sh SHUTDOWN This node shuts down upon receipt of a diff --git a/sys/netgraph/ng_bridge.c b/sys/netgraph/ng_bridge.c index 7b03b1a2599b..915a18550cba 100644 --- a/sys/netgraph/ng_bridge.c +++ b/sys/netgraph/ng_bridge.c @@ -234,6 +234,13 @@ static const struct ng_parse_type ng_bridge_stats_type = { &ng_parse_struct_type, &ng_bridge_stats_type_fields }; +/* Parse type for struct ng_bridge_move_host */ +static const struct ng_parse_struct_field ng_bridge_move_host_type_fields[] + = NG_BRIDGE_MOVE_HOST_TYPE_INFO(&ng_parse_enaddr_type); +static const struct ng_parse_type ng_bridge_move_host_type = { + &ng_parse_struct_type, + &ng_bridge_move_host_type_fields +}; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_bridge_cmdlist[] = { @@ -293,6 +300,13 @@ static const struct ng_cmdlist ng_bridge_cmdlist[] = { NULL, NULL }, + { + NGM_BRIDGE_COOKIE, + NGM_BRIDGE_MOVE_HOST, + "movehost", + &ng_bridge_move_host_type, + NULL + }, { 0 } }; @@ -601,6 +615,32 @@ ng_bridge_rcvmsg(node_p node, item_p item, hook_p lasthook) priv->persistent = 1; break; } + case NGM_BRIDGE_MOVE_HOST: + { + struct ng_bridge_move_host *mh; + hook_p hook; + struct ng_bridge_host *host; + + if (msg->header.arglen < sizeof(*mh)) { + error = EINVAL; + break; + } + mh = (struct ng_bridge_move_host *)msg->data; + hook = (mh->hook[0] == 0) + ? lasthook + : ng_findhook(node, mh->hook); + if (hook == NULL) { + error = ENOENT; + break; + } + host = ng_bridge_get(priv, mh->addr); + if (host != NULL) { + error = EADDRINUSE; + break; + } + error = ng_bridge_put(priv, mh->addr, NG_HOOK_PRIVATE(hook)); + break; + } default: error = EINVAL; break; @@ -809,12 +849,26 @@ ng_bridge_rcvdata(hook_p hook, item_p item) host->age = 0; } } else if (ctx.incoming->learnMac) { - if (!ng_bridge_put(priv, eh->ether_shost, ctx.incoming)) { + struct ng_mesg *msg; + struct ng_bridge_move_host *mh; + int error = 0; + + NG_MKMESSAGE(msg, NGM_BRIDGE_COOKIE, NGM_BRIDGE_MOVE_HOST, + sizeof(*mh), M_NOWAIT); + if (msg == NULL) { counter_u64_add(ctx.incoming->stats.memoryFailures, 1); NG_FREE_ITEM(item); NG_FREE_M(ctx.m); return (ENOMEM); } + mh = (struct ng_bridge_move_host *)msg->data; + strncpy(mh->hook, NG_HOOK_NAME(ctx.incoming->hook), + sizeof(mh->hook)); + memcpy(mh->addr, eh->ether_shost, sizeof(mh->addr)); + NG_SEND_MSG_ID(error, node, msg, NG_NODE_ID(node), + NG_NODE_ID(node)); + if (error) + counter_u64_add(ctx.incoming->stats.memoryFailures, 1); } /* Run packet through ipfw processing, if enabled */ @@ -959,8 +1013,7 @@ ng_bridge_get(priv_cp priv, const u_char *addr) /* * Add a new host entry to the table. This assumes the host doesn't - * already exist in the table. Returns 1 on success, 0 if there - * was a memory allocation failure. + * already exist in the table. Returns 0 on success. */ static int ng_bridge_put(priv_p priv, const u_char *addr, link_p link) @@ -970,16 +1023,14 @@ ng_bridge_put(priv_p priv, const u_char *addr, link_p link) #ifdef INVARIANTS /* Assert that entry does not already exist in hashtable */ - SLIST_FOREACH(host, &priv->tab[bucket], next) { - KASSERT(!ETHER_EQUAL(host->addr, addr), - ("%s: entry %6D exists in table", __func__, addr, ":")); - } + KASSERT(ng_bridge_get(priv, addr) == NULL, + ("%s: entry %6D exists in table", __func__, addr, ":")); #endif /* Allocate and initialize new hashtable entry */ host = malloc(sizeof(*host), M_NETGRAPH_BRIDGE, M_NOWAIT); if (host == NULL) - return (0); + return (ENOMEM); bcopy(addr, host->addr, ETHER_ADDR_LEN); host->link = link; host->staleness = 0; @@ -991,7 +1042,7 @@ ng_bridge_put(priv_p priv, const u_char *addr, link_p link) /* Resize table if necessary */ ng_bridge_rehash(priv); - return (1); + return (0); } /* diff --git a/sys/netgraph/ng_bridge.h b/sys/netgraph/ng_bridge.h index 03541deceacd..12717d16ac7a 100644 --- a/sys/netgraph/ng_bridge.h +++ b/sys/netgraph/ng_bridge.h @@ -140,6 +140,16 @@ struct ng_bridge_host_ary { { NULL } \ } +struct ng_bridge_move_host { + u_char addr[ETHER_ADDR_LEN]; /* ethernet address */ + char hook[NG_HOOKSIZ]; /* link where addr can be found */ +}; +/* Keep this in sync with the above structure definition */ +#define NG_BRIDGE_MOVE_HOST_TYPE_INFO(entype) { \ + { "addr", (entype) }, \ + { "hook", &ng_parse_hookbuf_type }, \ +} + /* Netgraph control messages */ enum { NGM_BRIDGE_SET_CONFIG = 1, /* set node configuration */ @@ -150,6 +160,7 @@ enum { NGM_BRIDGE_GETCLR_STATS, /* atomically get & clear link stats */ NGM_BRIDGE_GET_TABLE, /* get link table */ NGM_BRIDGE_SET_PERSISTENT, /* set persistent mode */ + NGM_BRIDGE_MOVE_HOST, /* move a host to a link */ }; #endif /* _NETGRAPH_NG_BRIDGE_H_ */