diff --git a/share/examples/jails/jei b/share/examples/jails/jei new file mode 100755 --- /dev/null +++ b/share/examples/jails/jei @@ -0,0 +1,291 @@ +#!/bin/sh +#- +# Copyright (c) 2025 David Marker +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# +############################################################ IDENT(1) +# +# $Title: ng_eiface(4) management script for vnet jails $ +# +############################################################ INFORMATION +# +# Use this tool manually or with jail.conf(5) to add `net' interfaces to jails. +# Unlike jng this is only for ng_eiface(4) and it only creates them and sends +# them to jails. It waits until the interface is moved to rename it. This +# means you can create a "jail0" interface for all your jails and configure +# it the same in all of them. No numbering scheme required. +# +# This will also send "setvnethome" to the ng_eiface(4), this means the network +# interface is not returned when the jail is shutdown (or indeed ever). Rather +# the netgraph(4) node will be destroyed automatically when the jail is torn +# down. It does *NOT* return. This is meant for creating ephemeral interfaces +# with well known names for jails. +# +# This will not create netgraph(4) bridges or other nodes for you at all. +# It is assumed you already have a means to do that. The example config assumes +# you have two ng_bridge(4) nodes: br0 and br1 +# +# In jail.conf(5) format: +# +# ### BEGIN EXCERPT ### +# +# xxx { +# host.hostname = "xxx.yyy"; +# path = "/vm/xxx"; +# +# # +# # NB: Below line required +# # +# vnet; +# +# exec.clean; +# exec.system_user = "root"; +# exec.jail_user = "root"; +# +# # +# # NB: Below 2-lines or similar required (any number of interface) +# # NB: You can connect the ng_eiface(4) to any netgraph(4) node +# # these examples show connecting to a bridge. +# # NB: You can assign a MAC address here too for DHCP. +# # +# exec.created += "jei $name lan0 br0:link 00:0C:29:39:B4:4D"; +# exec.created += "jei $name jail0 br1:link"; +# +# # Standard recipe +# exec.start += "/bin/sh /etc/rc"; +# exec.stop = "/bin/sh /etc/rc.shutdown jail"; +# +# exec.consolelog = "/var/log/jail_xxx_console.log"; +# mount.devfs; +# +# # Optional (default off) +# #allow.mount; +# #allow.set_hostname = 1; +# #allow.sysvipc = 1; +# #devfs_ruleset = "11"; # rule to unhide bpf for DHCP +# } +# +# ### END EXCERPT ### +# +# ASIDE: dhclient(8) inside a vnet jail... +# +# To allow dhclient(8) to work inside a vnet jail, make sure the following +# appears in /etc/devfs.rules (which should be created if it doesn't exist): +# +# [devfsrules_jail=11] +# add include $devfsrules_hide_all +# add include $devfsrules_unhide_basic +# add include $devfsrules_unhide_login +# add path 'bpf*' unhide +# +# And set ether devfs.ruleset="11" (jail.conf(5)) or +# jail_{name}_devfs_ruleset="11" (rc.conf(5)). +# +############################################################ GLOBALS + +pgm="${0##*/}" # Program basename + +# +# Global exit status +# +SUCCESS=0 +FAILURE=1 + +############################################################ FUNCTIONS + +usage() +{ + exec >&2 + echo "Usage: $pgm [MAC]" + echo "" + echo " must be a valid argument for jexec(8)." + echo " must be a valid name for an interface for both" + echo "netgraph(4) and ifconfig(8)." + echo " is any valid node:hook path for netgraph(4)." + echo "" + echo "Example:" + echo " jei myjail lan0 br0:link" + echo "" + + exit $FAILURE +} + + +mustberoot_to_continue() +{ + if [ "$( id -u )" -ne 0 ]; then + echo "Must run as root!" >&2 + exit $FAILURE + fi +} + + +# Don't assume you can use the attach point to find the eiface. +# A bridge can be connected to by `[up]linkX` where X is known but also by +# `[up]link` which uses next available number. +# +# Also don't rename until the ng_eiface(4) has been moved to the jail. +# +# Create the ng_eiface(4) leave unconnected, they are persistent. Return the +# node id-name. This won't change even if there is a clash moving to jail. If +# that happens we have to clean up and this name will let us do that. +eiface_create() +{ + nid=`ngctl -f- <<- EOF 2>/dev/null | awk '{print $6}' + mkpeer . eiface e ether + show -n .:e + disconnect .: e + EOF` + + echo "[${nid}]:" +} + + +# call this to try to shutdown the eiface. +eiface_xerr() +{ + local node=$1 + local msg=$2 + local jail=${3-""} + + if [ -z $jail ]; then + ngctl shutdown $node 2>/dev/null + else + jexec $jail ngctl shutdown $node 2>/dev/null + fi + echo "$msg" >&2 + exit $FAILURE +} + + +# This has no idea what you are trying to connect to. probably a bridge, but +# it could be a vlan connected to a bridge or any other configuration allowed by +# netgraph(4). We know it was valid if it works. If you didn't want it connected +# you could just `ngctl mkpeer ...` in the jail. +eiface_connect() +{ + local node=$1 + local hookpath=$2 + + # There should be just one ':' in hookpath so use that to pivot. + nd2=${hookpath%%:*} # delete everything after and include first ':' + ph=${hookpath#*:} # delete everything before and inc first ':' + + ngctl connect $node ${nd2}: ether $ph 2>/dev/null || + eiface_xerr $node "$node unable to connect to \"${hookpath}\"" +} + + +eiface_getifname() +{ + local node=$1 + local jail=$2 + + if [ -z $jail ]; then + IFS='"' read _ ifname <<- EOF + $(ngctl msg $node getifname | sed '1d') + EOF + else + IFS='"' read _ ifname <<- EOF + $(jexec $jail ngctl msg $node getifname | sed '1d') + EOF + fi + + if [ $? -ne 0 ]; then + eiface_xerr $node "$node unable to getifname" $jail + fi + + echo $ifname +} + + +eiface_vmove() +{ + local node=$1 + local jail=$2 + local ifname=$(eiface_getifname $node) + + ifconfig $ifname vnet $jail || + eiface_xerr $node "uname to move \"$ifname\" to \"$jail\"" + + # also change ownership of netgraph node to the jail + jexec $jail ngctl msg $node setvnethome 2>/dev/null || + eiface_xerr $node "$node unable to setvnethome" $jail +} + +eiface_set_name() +{ + local node=$1 + local jail=$2 + local name=$3 + local ifname=$(eiface_getifname $node $jail) + + jexec $jail ngctl name $node $name || + eiface_xerr $node "$node unable to set name to \"$name\"" $jail + jexec $jail ifconfig $ifname name $name || + eiface_xerr $node "\"$ifname\" couldn't be changed to \"$name\"" $jail +} + + +eiface_set_mac() +{ + local node=$1 + local jail=$2 + local mac=$3 + + jexec $jail ngctl msg $node set $mac 2>/dev/null || + eiface_xerr $node "$node unable to set mac" $jail + +} + + +############################################################ MAIN + +# +# Command-line arguments +# +jail="$1" +iface="$2" +hookpath="$3" +mac="${4}" + +if [ $# -lt 3 -o $# -gt 4 ]; then + usage +fi + +mustberoot_to_continue + +node=$(eiface_create) +eiface_connect $node $hookpath +eiface_vmove $node $jail +eiface_set_name $node $jail $iface + +if [ ! -z $mac ]; then + eiface_set_mac $node $jail $mac +fi + +################################################################################ +# END +################################################################################ diff --git a/share/man/man4/ng_eiface.4 b/share/man/man4/ng_eiface.4 --- a/share/man/man4/ng_eiface.4 +++ b/share/man/man4/ng_eiface.4 @@ -81,6 +81,19 @@ string. .It Dv NGM_EIFACE_GET_IFADDRS Return the list of link-level addresses associated with the node. +.It Dv NGM_EIFACE_SET_VNETHOME Pq Ic setvnethome +Changes the +.Xr vnet 9 +home of the iterface to its current +.Xr vnet 9 . +This has the effect of shutting down the interface with the jail it is in +rather than returning it to its parent. The parent can still use +.Xr ifconfig 8 +with -vnet to retreive the +.Nm iface . +This message should be sent again to reset the +.Nm iface +home as well. .El .Sh SHUTDOWN This node shuts down upon receipt of a diff --git a/share/man/man4/ng_iface.4 b/share/man/man4/ng_iface.4 --- a/share/man/man4/ng_iface.4 +++ b/share/man/man4/ng_iface.4 @@ -103,6 +103,19 @@ .It Dv NGM_IFACE_BROADCAST Pq Ic broadcast Set the interface to broadcast mode. The interface must not currently be up. +.It Dv NGM_IFACE_SET_VNETHOME Pq Ic setvnethome +Changes the +.Xr vnet 9 +home of the iterface to its current +.Xr vnet 9 . +This has the effect of shutting down the interface with the jail it is in +rather than returning it to its parent. The parent can still use +.Xr ifconfig 8 +with -vnet to retreive the +.Nm iface . +This message should be sent again to reset the +.Nm iface +home as well. .El .Sh SHUTDOWN This node shuts down upon receipt of a diff --git a/sys/netgraph/netgraph.h b/sys/netgraph/netgraph.h --- a/sys/netgraph/netgraph.h +++ b/sys/netgraph/netgraph.h @@ -1167,6 +1167,9 @@ int ng_callout(struct callout *c, node_p node, hook_p hook, int ticks, ng_item_fn *fn, void * arg1, int arg2); #define ng_callout_init(c) callout_init(c, 1) +#ifdef VIMAGE +void ng_node_vmove(node_p node, struct vnet *new_vnet); +#endif /* Flags for netgraph functions. */ #define NG_NOFLAGS 0x00000000 /* no special options */ diff --git a/sys/netgraph/ng_base.c b/sys/netgraph/ng_base.c --- a/sys/netgraph/ng_base.c +++ b/sys/netgraph/ng_base.c @@ -378,8 +378,9 @@ #define TRAP_ERROR() #endif -VNET_DEFINE_STATIC(ng_ID_t, nextID) = 1; -#define V_nextID VNET(nextID) +/* node IDs must be unique even when node moves to a different vnet. */ +static struct mtx id_unit_lock; +static struct unrhdr *id_unit; #ifdef INVARIANTS #define CHECK_DATA_MBUF(m) do { \ @@ -670,17 +671,8 @@ LIST_INIT(&node->nd_hooks); /* Get an ID and put us in the hash chain. */ + node->nd_ID = alloc_unr(id_unit); IDHASH_WLOCK(); - for (;;) { /* wrap protection, even if silly */ - node_p node2 = NULL; - node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */ - - /* Is there a problem with the new number? */ - NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */ - if ((node->nd_ID != 0) && (node2 == NULL)) { - break; - } - } V_ng_nodes++; if (V_ng_nodes * 2 > V_ng_ID_hmask) ng_ID_rehash(); @@ -785,6 +777,74 @@ NG_NODE_UNREF(node); } +/* + * If your node "owns" a struct ifnet you probably need to implement if_reassign + * and have it call this function. You also need to add another special case in + * ng_ether_attach to skip attaching if your node is the driver. + */ +#ifdef VIMAGE +void +ng_node_vmove(node_p node, struct vnet *new_vnet) +{ + /* pull node off hashes (its moving) */ + NAMEHASH_WLOCK(); + if (NG_NODE_HAS_NAME(node)) { + V_ng_named_nodes--; + LIST_REMOVE(node, nd_nodes); + } + NAMEHASH_WUNLOCK(); + + IDHASH_WLOCK(); + V_ng_nodes--; + LIST_REMOVE(node, nd_idnodes); + IDHASH_WUNLOCK(); + + /* switch current vnet and add back everywhere. */ + CURVNET_SET(new_vnet); + if (NG_NODE_HAS_NAME(node)) { + node_p node2; + char *name = NG_NODE_NAME(node); + uint32_t hash; + bool name_used = false; + + NAMEHASH_WLOCK(); + if (V_ng_named_nodes * 2 > V_ng_name_hmask) + ng_name_rehash(); + + hash = hash32_str(name, HASHINIT) & V_ng_name_hmask; + LIST_FOREACH(node2, &V_ng_name_hash[hash], nd_nodes) + if (NG_NODE_IS_VALID(node2) && + (strcmp(NG_NODE_NAME(node2), name) == 0)) { + name_used = true; + } + + /* + * This should be a rare event and requires that user is setting + * names as all numbering is global (e.g. ngethX or ngX). + * But its not a tragedy if the user has to access by ID. + */ + if (name_used) { + name[0] = '\0'; + } else { + LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes); + V_ng_named_nodes++; + } + NAMEHASH_WUNLOCK(); + } + + IDHASH_WLOCK(); + V_ng_nodes++; + if (V_ng_nodes * 2 > V_ng_ID_hmask) + ng_ID_rehash(); + LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], node, + nd_idnodes); + IDHASH_WUNLOCK(); + + node->nd_vnet = new_vnet; + CURVNET_RESTORE(); +} +#endif + /* * Remove a reference to the node, possibly the last. * deadnode always acts as it were the last. @@ -799,7 +859,7 @@ CURVNET_SET(node->nd_vnet); if (refcount_release(&node->nd_refs)) { /* we were the last */ - + ng_ID_t id = node->nd_ID; node->nd_type->refs--; /* XXX maybe should get types lock? */ NAMEHASH_WLOCK(); if (NG_NODE_HAS_NAME(node)) { @@ -815,6 +875,7 @@ mtx_destroy(&node->nd_input_queue.q_mtx); NG_FREE_NODE(node); + free_unr(id_unit, id); } CURVNET_RESTORE(); } @@ -1772,6 +1833,10 @@ if (path == NULL) { if (lasthook != NULL) *lasthook = NULL; +#ifdef VIMAGE + if (node->nd_vnet != curvnet) + return EACCES; /* node not in current vnet */ +#endif *destp = node; return (0); } @@ -1841,6 +1906,10 @@ *lasthook = NULL; } TOPOLOGY_WUNLOCK(); +#ifdef VIMAGE + if (node->nd_vnet != curvnet) + return EACCES; /* node not in current vnet */ +#endif *destp = node; return (0); } @@ -3217,6 +3286,11 @@ mtx_init(&ngq_mtx, "netgraph item list mutex", NULL, MTX_DEF); #endif + /* ID 0 indicates not found, disallow that value */ + mtx_init(&id_unit_lock, "netgraph node id unr(9) mutex", NULL, + MTX_DEF); + id_unit = new_unrhdr(1, INT_MAX, &id_unit_lock); + ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item), NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0); uma_zone_set_max(ng_qzone, maxalloc); @@ -3240,6 +3314,7 @@ case MOD_UNLOAD: /* You can't unload it because an interface may be using it. */ error = EBUSY; + /* delete_unrhdr(id_unit); */ break; default: error = EOPNOTSUPP; diff --git a/sys/netgraph/ng_eiface.h b/sys/netgraph/ng_eiface.h --- a/sys/netgraph/ng_eiface.h +++ b/sys/netgraph/ng_eiface.h @@ -54,6 +54,9 @@ NGM_EIFACE_GET_IFNAME = 1, /* get the interface name */ NGM_EIFACE_GET_IFADDRS, /* returns list of addresses */ NGM_EIFACE_SET, /* set ethernet address */ +#ifdef VIMAGE + NGM_EIFACE_SET_VNETHOME, /* set current vnet as home */ +#endif }; #endif /* _NETGRAPH_NG_EIFACE_H_ */ diff --git a/sys/netgraph/ng_eiface.c b/sys/netgraph/ng_eiface.c --- a/sys/netgraph/ng_eiface.c +++ b/sys/netgraph/ng_eiface.c @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -74,6 +75,13 @@ &ng_parse_enaddr_type, NULL }, + { + NGM_EIFACE_COOKIE, + NGM_EIFACE_SET_VNETHOME, + "setvnethome", + NULL, + NULL + }, { 0 } }; @@ -92,6 +100,9 @@ static void ng_eiface_init(void *xsc); static void ng_eiface_start(struct ifnet *ifp); static int ng_eiface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); +#ifdef VIMAGE +static void ng_if_reassign(struct ifnet *, struct vnet *, char *); +#endif #ifdef DEBUG static void ng_eiface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data); #endif @@ -120,8 +131,7 @@ }; NETGRAPH_INIT(eiface, &typestruct); -VNET_DEFINE_STATIC(struct unrhdr *, ng_eiface_unit); -#define V_ng_eiface_unit VNET(ng_eiface_unit) +static struct unrhdr *ng_eiface_unit; /************************************************************************ INTERFACE STUFF @@ -197,6 +207,15 @@ return (error); } +#ifdef VIMAGE +static void +ng_if_reassign(struct ifnet *ifp, struct vnet *vn, char *arg) +{ + ng_node_vmove(((priv_p)(ifp->if_softc))->node, vn); + ether_reassign(ifp, vn, arg); +} +#endif + static void ng_eiface_init(void *xsc) { @@ -394,7 +413,7 @@ ifp->if_softc = priv; /* Get an interface unit number */ - priv->unit = alloc_unr(V_ng_eiface_unit); + priv->unit = alloc_unr(ng_eiface_unit); /* Link together node and private info */ NG_NODE_SET_PRIVATE(node, priv); @@ -431,6 +450,9 @@ /* Attach the interface */ ether_gen_addr(ifp, &eaddr); ether_ifattach(ifp, eaddr.octet); +#ifdef VIMAGE + if_setreassignfn(ifp, ng_if_reassign); +#endif ifp->if_baudrate = ifmedia_baudrate(IFM_ETHER | IFM_1000_T); /* Done */ @@ -534,7 +556,20 @@ NET_EPOCH_EXIT(et); break; } - +#ifdef VIMAGE + case NGM_EIFACE_SET_VNETHOME: + /* + * By setting ifp->if_home_vnet to the node->nd_vnet it + * will not be returned by vnet_if_return. It is simply + * destroyed with the jail at VNET_SYSUNINIT time. + * + * This does not "stick". If a sub-jail is gifted an + * interface it needs this called again. Even true if + * system `ifconfig ifname -nvet jname` to retrieve it. + */ + ifp->if_home_vnet = node->nd_vnet; + break; +#endif default: error = EINVAL; break; @@ -620,7 +655,7 @@ ifmedia_removeall(&priv->media); if_free(ifp); CURVNET_RESTORE(); - free_unr(V_ng_eiface_unit, priv->unit); + free_unr(ng_eiface_unit, priv->unit); free(priv, M_NETGRAPH); NG_NODE_SET_PRIVATE(node, NULL); NG_NODE_UNREF(node); @@ -653,7 +688,10 @@ switch (event) { case MOD_LOAD: + ng_eiface_unit = new_unrhdr(0, 0xffff, NULL); + break; case MOD_UNLOAD: + delete_unrhdr(ng_eiface_unit); break; default: error = EOPNOTSUPP; @@ -661,21 +699,3 @@ } return (error); } - -static void -vnet_ng_eiface_init(const void *unused) -{ - - V_ng_eiface_unit = new_unrhdr(0, 0xffff, NULL); -} -VNET_SYSINIT(vnet_ng_eiface_init, SI_SUB_PSEUDO, SI_ORDER_ANY, - vnet_ng_eiface_init, NULL); - -static void -vnet_ng_eiface_uninit(const void *unused) -{ - - delete_unrhdr(V_ng_eiface_unit); -} -VNET_SYSUNINIT(vnet_ng_eiface_uninit, SI_SUB_INIT_IF, SI_ORDER_ANY, - vnet_ng_eiface_uninit, NULL); diff --git a/sys/netgraph/ng_ether.c b/sys/netgraph/ng_ether.c --- a/sys/netgraph/ng_ether.c +++ b/sys/netgraph/ng_ether.c @@ -70,6 +70,8 @@ #include #include #include +#include +#include MODULE_VERSION(ng_ether, 1); @@ -302,22 +304,28 @@ static void ng_ether_attach(struct ifnet *ifp) { + const char *dname; char name[IFNAMSIZ]; priv_p priv; node_p node; /* - * Do not create / attach an ether node to this ifnet if - * a netgraph node with the same name already exists. - * This should prevent ether nodes to become attached to - * eiface nodes, which may be problematic due to naming - * clashes. + * Do not create / attach an ether node to this ifnet the driver name + * indicates this is a netgraph node. This should prevent ether nodes + * becoming attached to eiface nodes, which may be problematic due to + * naming clashes. + * + * All netgraph nodes that contain a `struct ifnet` need a check here. + * + * ether_reassign handles actual interfaces with ng_ether as it detaches + * and re-attaches. */ - ng_ether_sanitize_ifname(ifp->if_xname, name); - if ((node = ng_name2noderef(NULL, name)) != NULL) { - NG_NODE_UNREF(node); + dname = if_getdname(ifp); /* more reliable for eiface/iface */ + if (strcmp(NG_EIFACE_EIFACE_NAME, dname) == 0 || + strcmp(NG_IFACE_IFACE_NAME, dname) == 0) { return; } + ng_ether_sanitize_ifname(ifp->if_xname, name); /* Create node */ KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); diff --git a/sys/netgraph/ng_iface.h b/sys/netgraph/ng_iface.h --- a/sys/netgraph/ng_iface.h +++ b/sys/netgraph/ng_iface.h @@ -64,6 +64,9 @@ NGM_IFACE_POINT2POINT, NGM_IFACE_BROADCAST, NGM_IFACE_GET_IFINDEX, +#ifdef VIMAGE + NGM_IFACE_SET_VNETHOME, +#endif }; #endif /* _NETGRAPH_NG_IFACE_H_ */ diff --git a/sys/netgraph/ng_iface.c b/sys/netgraph/ng_iface.c --- a/sys/netgraph/ng_iface.c +++ b/sys/netgraph/ng_iface.c @@ -70,6 +70,7 @@ #include #include +#include #include #include #include @@ -137,6 +138,9 @@ struct mbuf *m, sa_family_t family); static int ng_iface_send(struct ifnet *ifp, struct mbuf *m, sa_family_t sa); +#ifdef VIMAGE +static void ng_if_reassign(struct ifnet *, struct vnet *, char *); +#endif #ifdef DEBUG static void ng_iface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data); #endif @@ -186,6 +190,13 @@ NULL, &ng_parse_uint32_type }, + { + NGM_IFACE_COOKIE, + NGM_IFACE_SET_VNETHOME, + "setvnethome", + NULL, + NULL + }, { 0 } }; @@ -204,8 +215,7 @@ }; NETGRAPH_INIT(iface, &typestruct); -VNET_DEFINE_STATIC(struct unrhdr *, ng_iface_unit); -#define V_ng_iface_unit VNET(ng_iface_unit) +static struct unrhdr *ng_iface_unit; /************************************************************************ HELPER STUFF @@ -338,6 +348,15 @@ return (error); } +#ifdef VIMAGE +static void +ng_if_reassign(struct ifnet *ifp, struct vnet *vn, char *arg) +{ + ng_node_vmove(((priv_p)(ifp->if_softc))->node, vn); + ether_reassign(ifp, vn, arg); +} +#endif + /* * This routine is called to deliver a packet out the interface. * We simply look at the address family and relay the packet to @@ -532,7 +551,7 @@ priv->ifp = ifp; /* Get an interface unit number */ - priv->unit = alloc_unr(V_ng_iface_unit); + priv->unit = alloc_unr(ng_iface_unit); /* Link together node and private info */ NG_NODE_SET_PRIVATE(node, priv); @@ -560,6 +579,9 @@ /* Attach the interface */ if_attach(ifp); +#ifdef VIMAGE + if_setreassignfn(ifp, ng_if_reassign); +#endif bpfattach(ifp, DLT_NULL, sizeof(u_int32_t)); /* Done */ @@ -645,7 +667,20 @@ } *((uint32_t *)resp->data) = priv->ifp->if_index; break; - +#ifdef VIMAGE + case NGM_IFACE_SET_VNETHOME: + /* + * By setting ifp->if_home_vnet to the node->nd_vnet it + * will not be returned by vnet_if_return. It is simply + * destroyed with the jail at VNET_SYSUNINIT time. + * + * This does not "stick". If a sub-jail is gifted an + * interface it needs this called again. Even true if + * system `ifconfig ifname -nvet jname` to retrieve it. + */ + ifp->if_home_vnet = node->nd_vnet; + break; +#endif default: error = EINVAL; break; @@ -749,7 +784,7 @@ if_free(priv->ifp); CURVNET_RESTORE(); priv->ifp = NULL; - free_unr(V_ng_iface_unit, priv->unit); + free_unr(ng_iface_unit, priv->unit); rm_destroy(&priv->lock); free(priv, M_NETGRAPH_IFACE); NG_NODE_SET_PRIVATE(node, NULL); @@ -785,7 +820,11 @@ switch (event) { case MOD_LOAD: + ng_iface_unit = new_unrhdr(0, 0xffff, NULL); + break; + case MOD_UNLOAD: + delete_unrhdr(ng_iface_unit); break; default: error = EOPNOTSUPP; @@ -793,21 +832,3 @@ } return (error); } - -static void -vnet_ng_iface_init(const void *unused) -{ - - V_ng_iface_unit = new_unrhdr(0, 0xffff, NULL); -} -VNET_SYSINIT(vnet_ng_iface_init, SI_SUB_PSEUDO, SI_ORDER_ANY, - vnet_ng_iface_init, NULL); - -static void -vnet_ng_iface_uninit(const void *unused) -{ - - delete_unrhdr(V_ng_iface_unit); -} -VNET_SYSUNINIT(vnet_ng_iface_uninit, SI_SUB_INIT_IF, SI_ORDER_ANY, - vnet_ng_iface_uninit, NULL);