Index: share/man/man4/ng_ether.4 =================================================================== --- share/man/man4/ng_ether.4 +++ share/man/man4/ng_ether.4 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 23, 2011 +.Dd December 27, 2021 .Dt NG_ETHER 4 .Os .Sh NAME @@ -122,6 +122,16 @@ .Va lower , but only receives unrecognized packets. .El +.Pp +If a node was detached without the associated interface also going down, +it is possible to reattach a +.Nm +node to an interface again by sending a +.Dv mkpeer +request connecting to the initial hook with the same name as the +interface. +This temporary helper hook is removed directly after a successful +creation of the node. .Sh CONTROL MESSAGES This node type supports the generic control messages, plus the following: .Bl -tag -width foo Index: sys/netgraph/ng_ether.c =================================================================== --- sys/netgraph/ng_ether.c +++ sys/netgraph/ng_ether.c @@ -437,14 +437,65 @@ ******************************************************************/ /* - * It is not possible or allowable to create a node of this type. - * Nodes get created when the interface is attached (or, when - * this node type's KLD is loaded). + * Usually all ng_ether nodes are automatically created by loading + * the module or creating a new interface. If a ng_ether node was + * manually destroyed while the interface persists, the ng_ether + * node can be recreated by calling: + * # ngctl mkpeer . ether + * + * This will implicitly create the new node first and then try to + * connect the hook. So let's create an empty node and fill the + * remaining data later. If the second step fails, the node will be + * removed automatically. So either the whole process will succeed + * or no node will be created. */ static int ng_ether_constructor(node_p node) { - return (EINVAL); + priv_p priv; + + /* Initialize private descriptor. */ + priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); + NG_NODE_SET_PRIVATE(node, priv); + + /* Initialization is delayed to the first newhook call */ + return (0); +} + +/* second half of initialization for a new node */ +static int +ng_ether_constructor2(node_p node, struct ifnet *ifp) { + const priv_p priv = NG_NODE_PRIVATE(node); + char name[IFNAMSIZ]; + + if (ifp == NULL) + return (ENXIO); + + if (IFP2NG(ifp) != NULL) + return (EISCONN); + + switch (ifp->if_type) { + case IFT_ETHER: + case IFT_L2VLAN: + case IFT_BRIDGE: + break; + default: + return (EOPNOTSUPP); + } + + ng_ether_sanitize_ifname(ifp->if_xname, name); + if (ng_name2noderef(NULL, name) != NULL) + return (EEXIST); + + priv->ifp = ifp; + IFP2NG(ifp) = node; + priv->hwassist = ifp->if_hwassist; + + /* Try to give the node the same name as the interface */ + if (ng_name_node(node, name) != 0) + log(LOG_WARNING, "%s: can't name node %s\n", __func__, name); + + return (0); } /* @@ -456,6 +507,18 @@ const priv_p priv = NG_NODE_PRIVATE(node); hook_p *hookptr; + /* first hook on an uninitialized node */ + if (priv->ifp == NULL) { + int error = ng_ether_constructor2(node, ifunit(name)); + /* + * TODO: Remove helper hook asap, if no error occured! + * + * Calling ng_rmhook_self(hook) right now fails, + * because the hook does not exist yet (we are creating it) + */ + return (error); + } + /* Divert hook is an alias for lower */ if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) name = NG_ETHER_HOOK_LOWER;