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
This commit is contained in:
parent
36be84b966
commit
b1bd44732d
@ -34,7 +34,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 6, 2021
|
.Dd February 17, 2021
|
||||||
.Dt NG_BRIDGE 4
|
.Dt NG_BRIDGE 4
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -214,6 +214,25 @@ Returns the current host mapping table used to direct packets, in a
|
|||||||
.Vt "struct ng_bridge_host_ary" .
|
.Vt "struct ng_bridge_host_ary" .
|
||||||
.It Dv NGM_BRIDGE_SET_PERSISTENT Pq Ar setpersistent
|
.It Dv NGM_BRIDGE_SET_PERSISTENT Pq Ar setpersistent
|
||||||
This command sets the persistent flag on the node, and takes no arguments.
|
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
|
.El
|
||||||
.Sh SHUTDOWN
|
.Sh SHUTDOWN
|
||||||
This node shuts down upon receipt of a
|
This node shuts down upon receipt of a
|
||||||
|
@ -234,6 +234,13 @@ static const struct ng_parse_type ng_bridge_stats_type = {
|
|||||||
&ng_parse_struct_type,
|
&ng_parse_struct_type,
|
||||||
&ng_bridge_stats_type_fields
|
&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 */
|
/* List of commands and how to convert arguments to/from ASCII */
|
||||||
static const struct ng_cmdlist ng_bridge_cmdlist[] = {
|
static const struct ng_cmdlist ng_bridge_cmdlist[] = {
|
||||||
@ -293,6 +300,13 @@ static const struct ng_cmdlist ng_bridge_cmdlist[] = {
|
|||||||
NULL,
|
NULL,
|
||||||
NULL
|
NULL
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
NGM_BRIDGE_COOKIE,
|
||||||
|
NGM_BRIDGE_MOVE_HOST,
|
||||||
|
"movehost",
|
||||||
|
&ng_bridge_move_host_type,
|
||||||
|
NULL
|
||||||
|
},
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -601,6 +615,32 @@ ng_bridge_rcvmsg(node_p node, item_p item, hook_p lasthook)
|
|||||||
priv->persistent = 1;
|
priv->persistent = 1;
|
||||||
break;
|
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:
|
default:
|
||||||
error = EINVAL;
|
error = EINVAL;
|
||||||
break;
|
break;
|
||||||
@ -809,12 +849,26 @@ ng_bridge_rcvdata(hook_p hook, item_p item)
|
|||||||
host->age = 0;
|
host->age = 0;
|
||||||
}
|
}
|
||||||
} else if (ctx.incoming->learnMac) {
|
} 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);
|
counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
|
||||||
NG_FREE_ITEM(item);
|
NG_FREE_ITEM(item);
|
||||||
NG_FREE_M(ctx.m);
|
NG_FREE_M(ctx.m);
|
||||||
return (ENOMEM);
|
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 */
|
/* 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
|
* 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
|
* already exist in the table. Returns 0 on success.
|
||||||
* was a memory allocation failure.
|
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
ng_bridge_put(priv_p priv, const u_char *addr, link_p link)
|
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
|
#ifdef INVARIANTS
|
||||||
/* Assert that entry does not already exist in hashtable */
|
/* Assert that entry does not already exist in hashtable */
|
||||||
SLIST_FOREACH(host, &priv->tab[bucket], next) {
|
KASSERT(ng_bridge_get(priv, addr) == NULL,
|
||||||
KASSERT(!ETHER_EQUAL(host->addr, addr),
|
("%s: entry %6D exists in table", __func__, addr, ":"));
|
||||||
("%s: entry %6D exists in table", __func__, addr, ":"));
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Allocate and initialize new hashtable entry */
|
/* Allocate and initialize new hashtable entry */
|
||||||
host = malloc(sizeof(*host), M_NETGRAPH_BRIDGE, M_NOWAIT);
|
host = malloc(sizeof(*host), M_NETGRAPH_BRIDGE, M_NOWAIT);
|
||||||
if (host == NULL)
|
if (host == NULL)
|
||||||
return (0);
|
return (ENOMEM);
|
||||||
bcopy(addr, host->addr, ETHER_ADDR_LEN);
|
bcopy(addr, host->addr, ETHER_ADDR_LEN);
|
||||||
host->link = link;
|
host->link = link;
|
||||||
host->staleness = 0;
|
host->staleness = 0;
|
||||||
@ -991,7 +1042,7 @@ ng_bridge_put(priv_p priv, const u_char *addr, link_p link)
|
|||||||
|
|
||||||
/* Resize table if necessary */
|
/* Resize table if necessary */
|
||||||
ng_bridge_rehash(priv);
|
ng_bridge_rehash(priv);
|
||||||
return (1);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -140,6 +140,16 @@ struct ng_bridge_host_ary {
|
|||||||
{ NULL } \
|
{ 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 */
|
/* Netgraph control messages */
|
||||||
enum {
|
enum {
|
||||||
NGM_BRIDGE_SET_CONFIG = 1, /* set node configuration */
|
NGM_BRIDGE_SET_CONFIG = 1, /* set node configuration */
|
||||||
@ -150,6 +160,7 @@ enum {
|
|||||||
NGM_BRIDGE_GETCLR_STATS, /* atomically get & clear link stats */
|
NGM_BRIDGE_GETCLR_STATS, /* atomically get & clear link stats */
|
||||||
NGM_BRIDGE_GET_TABLE, /* get link table */
|
NGM_BRIDGE_GET_TABLE, /* get link table */
|
||||||
NGM_BRIDGE_SET_PERSISTENT, /* set persistent mode */
|
NGM_BRIDGE_SET_PERSISTENT, /* set persistent mode */
|
||||||
|
NGM_BRIDGE_MOVE_HOST, /* move a host to a link */
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _NETGRAPH_NG_BRIDGE_H_ */
|
#endif /* _NETGRAPH_NG_BRIDGE_H_ */
|
||||||
|
Loading…
Reference in New Issue
Block a user