Page MenuHomeFreeBSD

D50245.id156091.diff
No OneTemporary

D50245.id156091.diff

diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -170,6 +170,7 @@
SUBDIR.${MK_NETGRAPH}+= flowctl
SUBDIR.${MK_NETGRAPH}+= ngctl
SUBDIR.${MK_NETGRAPH}+= nghook
+SUBDIR.${MK_NETGRAPH}+= ngportal
SUBDIR.${MK_NIS}+= rpc.yppasswdd
SUBDIR.${MK_NIS}+= rpc.ypupdated
SUBDIR.${MK_NIS}+= rpc.ypxfrd
diff --git a/usr.sbin/ngportal/Makefile b/usr.sbin/ngportal/Makefile
new file mode 100644
--- /dev/null
+++ b/usr.sbin/ngportal/Makefile
@@ -0,0 +1,8 @@
+PROG= ngportal
+MAN= ngportal.8
+SRCS= kld.c ng.c portal.c
+LIBADD= jail netgraph
+
+WARNS?=1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ngportal/kld.c b/usr.sbin/ngportal/kld.c
new file mode 100644
--- /dev/null
+++ b/usr.sbin/ngportal/kld.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2025 David Marker <dave@freedave.net>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <assert.h>
+#include <err.h>
+#include <string.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+
+#include "portal.h"
+
+void
+kld_ensure_load(const char *search)
+{
+ int fileid, modid;
+ const char *cp;
+ struct module_stat mstat;
+
+ assert(search != NULL);
+
+ /* scan files in kernel */
+ mstat.version = sizeof(struct module_stat);
+ for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) {
+ /* scan modules in file */
+ for (modid = kldfirstmod(fileid); modid > 0;
+ modid = modfnext(modid)) {
+ if (modstat(modid, &mstat) < 0)
+ continue;
+ /* strip bus name if present */
+ if ((cp = strchr(mstat.name, '/')) != NULL) {
+ cp++;
+ } else {
+ cp = mstat.name;
+ }
+
+ /* found, already loaded */
+ if (strcmp(search, cp) == 0)
+ return;
+ }
+ }
+
+ /*
+ * In theory you could use this in a jail before loading ng_wormhole or
+ * ng_socket. Only thing we can do is let you know the kernel modules
+ * can't be loaded.
+ */
+ if (kldload(search) == -1)
+ err(ERREXIT, "%s: unable to load kernel module \"%s\"",
+ __func__, search);
+}
diff --git a/usr.sbin/ngportal/ng.c b/usr.sbin/ngportal/ng.c
new file mode 100644
--- /dev/null
+++ b/usr.sbin/ngportal/ng.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2025 David Marker <dave@freedave.net>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <assert.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netgraph/ng_wormhole.h>
+
+#include "portal.h"
+
+/* in this file we always want the colon on the end */
+#define IDFMT "[%08x]:"
+
+ngctx
+ng_create_context(void)
+{
+ int rc;
+ int ngs = -1; /* won't be touched if it fails */
+ char name[NG_NODESIZ];
+
+ snprintf(name, sizeof(name), "ngctl%d", getpid());
+ name[NG_NODESIZ - 1] = '\0';
+
+ rc = NgMkSockNode(name, &ngs, NULL);
+ if (rc == -1)
+ err(ERREXIT, "%s: failed to initialize netgraph(4)", __func__);
+
+ return (ngs);
+}
+
+/*
+ * NOTE: this is remains connected to control socket otherwise it would shutdown
+ * since it doesn't persist. It is disconnected as part of ng_wormhole_open.
+ */
+ng_ID_t
+ng_wh_create(ngctx ctx)
+{
+# define OURHK "tmp"
+ int rc;
+ struct ng_mesg *resp;
+ struct ngm_mkpeer msg = {
+ .type = NG_WORMHOLE_NODE_TYPE,
+ .ourhook = OURHK,
+ .peerhook = NG_WORMHOLE_HOOK,
+ };
+ const char *pth = ".:" OURHK;
+ ng_ID_t nd;
+# undef OURHK
+
+ assert(ctx >= 0);
+
+ rc = NgSendMsg(ctx, ".", NGM_GENERIC_COOKIE, NGM_MKPEER, &msg,
+ sizeof(msg));
+ if (rc == -1)
+ err(ERREXIT, "unable to create %s", msg.type);
+
+ rc = NgSendMsg(ctx, pth, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0);
+ if (rc == -1)
+ errx(ERREXIT, "unable to request %s info, presumed dead",
+ msg.type);
+
+ rc = NgAllocRecvMsg(ctx, &resp, NULL);
+ if (rc == -1)
+ err(ERREXIT, "unable to retrieve %s info, presumed dead",
+ msg.type);
+
+ /*
+ * This warns about structure alignment but is also done in ngctl(8).
+ */
+ nd = ((struct nodeinfo *) resp->data)->id;
+ free(resp);
+
+ /* valid netgraph IDs start at 1 */
+ if (nd == 0)
+ err(ERREXIT, "invalid node id for wormhole, presumed dead");
+
+ return (nd);
+}
+
+void
+ng_wh_name(ngctx ctx, ng_ID_t wh, const char *name)
+{
+ int rc;
+ struct ngm_name msg;
+ char pth[NG_NODESIZ + 1]; /* extra for ':' */
+
+ assert(ctx >= 0);
+ assert(wh > 0);
+
+ if (name == NULL)
+ return; /* no name given */
+
+ assert(strlen(name) < NG_NODELEN);
+
+ snprintf(msg.name, sizeof(msg.name), "%s", name);
+ snprintf(pth, sizeof(pth), IDFMT, wh);
+
+ rc = NgSendMsg(ctx, pth, NGM_GENERIC_COOKIE, NGM_NAME, &msg, sizeof(msg));
+ if (rc == -1)
+ err(ERREXIT, "failed to name `%s'", pth);
+}
+
+void
+ng_wh_connect(ngctx ctx, ng_ID_t wh, const char *peer, const char *peerhook)
+{
+ int rc;
+ struct ngm_connect msg = { .ourhook = NG_WORMHOLE_HOOK };
+ char pth[NG_NODESIZ];
+
+ assert(ctx >= 0);
+ assert(wh > 0);
+
+ if (peer == NULL)
+ return; /* nothing to connect */
+
+ assert(peerhook != NULL);
+ assert(strlen(peer) < sizeof(msg.path) - 2); /* for ':' and '\0' */
+ assert(strlen(peerhook) < sizeof(msg.peerhook) - 1); /* for '\0' */
+
+ snprintf(pth, sizeof(pth), IDFMT, wh);
+
+ snprintf(msg.path, sizeof(msg.path), "%s:", peer);
+ strncpy(msg.peerhook, peerhook, sizeof(msg.peerhook));
+
+ rc = NgSendMsg(ctx, pth, NGM_GENERIC_COOKIE, NGM_CONNECT, &msg,
+ sizeof(msg));
+ if (rc == -1) {
+ /* something standard going on, like maybe node doesn't exist */
+ if (strcmp(peerhook, NG_WORMHOLE_HOOK) != 0)
+ goto generic_err;
+
+ /*
+ * You can connect wormholes together, `ngportal` does so when
+ * 2 jails specified. But there are 2 cases it will fail.
+ *
+ * EINVAL means other side not open. EDOOFUS means that if the
+ * connection were allowed (which it isn't by kernel) it would
+ * result in a connected pair of wormholes in the same vnet.
+ * Obviously pointles.
+ */
+ if (errno == EINVAL) /* we opened before connect */
+ err(EX_DATAERR,
+ "unable to connect to `%s%s', not opened",
+ msg.path, msg.peerhook);
+
+ if (errno == EDOOFUS)
+ err(EX_DATAERR,
+ "forbidden: collapse would result in connected "
+ "wormholes in the same vnet");
+
+generic_err:
+ err(EX_DATAERR, "unable to connect `%s%s' to `%s%s'", pth,
+ msg.ourhook, msg.path, msg.peerhook);
+
+ }
+}
+
+ng_ID_t
+ng_wh_open(ngctx ctx, ng_ID_t wh, const char *jail)
+{
+ int rc;
+ ng_ID_t nd;
+ char pth[NG_NODESIZ];
+ struct hooklist *hlist;
+ struct nodeinfo *ninfo;
+ struct ng_mesg *resp;
+ struct ngm_rmhook msg = { .ourhook = NG_WORMHOLE_HOOK };
+
+ assert(ctx >= 0);
+ assert(wh > 0);
+ assert(jail != NULL);
+ assert(strlen(jail) < MAXHOSTNAMELEN);
+
+ snprintf(pth, sizeof(pth), IDFMT, wh);
+
+ rc = NgSendMsg(ctx, pth, NGM_WORMHOLE_COOKIE, NGM_WORMHOLE_OPEN, jail,
+ strlen(jail) + 1);
+ if (rc == -1)
+ errx(ERREXIT, "unable to open wormhole in `%s'", jail);
+
+ /*
+ * Now the struct calls them `eh` and `warp`. But to inform users what
+ * is going on we helpfully name the warp hooks after the jail ID they
+ * are in. It can't be the jail name as that can far exceed the length
+ * of hooks.
+ *
+ * The point is we have a jail name (not id, or it could be we don't
+ * know since both are valid).
+ *
+ */
+ rc = NgSendMsg(ctx, pth, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0);
+ if (rc == -1)
+ errx(ERREXIT,
+ "unable to request wormhole node list, presumed dead");
+
+ rc = NgAllocRecvMsg(ctx, &resp, NULL);
+ if (rc == -1)
+ errx(ERREXIT, "unable to get response for wormhole node list, "
+ "presumed dead");
+
+ hlist = (struct hooklist *) resp->data;
+ ninfo = &hlist->nodeinfo;
+ assert(ninfo->hooks == 2); /* socket and our other wormhole */
+ if (strcmp(hlist->link[0].nodeinfo.type, NG_WORMHOLE_NODE_TYPE) == 0) {
+ nd = hlist->link[0].nodeinfo.id;
+ } else {
+ assert(strcmp(hlist->link[1].nodeinfo.type,
+ NG_WORMHOLE_NODE_TYPE) == 0);
+ nd = hlist->link[1].nodeinfo.id;
+ }
+ free(resp);
+
+ rc = NgSendMsg(ctx, pth, NGM_GENERIC_COOKIE, NGM_RMHOOK, &msg,
+ sizeof(msg));
+ if (rc == -1)
+ errx(ERREXIT, "unable to rmhook `%s' from `%s'",
+ msg.ourhook, pth);
+
+ return (nd);
+}
+
+/* NOTE: only called from the cleanup. already in an err function */
+void
+ng_shutdown_node(ngctx ctx, ng_ID_t nd)
+{
+ int rc;
+ char pth[NG_NODESIZ];
+
+ assert(ctx >= 0);
+ assert(nd > 0);
+
+ snprintf(pth, NG_NODESIZ, IDFMT, nd);
+
+ rc = NgSendMsg(ctx, pth, NGM_GENERIC_COOKIE, NGM_SHUTDOWN, NULL, 0);
+ if (rc == -1) {
+ (void) fprintf(
+ stderr,
+ "Failed to shutdown node.\n"
+ "try:\n"
+ "\tngctl shutdown %s\n",
+ pth
+ );
+ }
+}
diff --git a/usr.sbin/ngportal/ngportal.8 b/usr.sbin/ngportal/ngportal.8
new file mode 100644
--- /dev/null
+++ b/usr.sbin/ngportal/ngportal.8
@@ -0,0 +1,132 @@
+.\"
+.\" Copyright (c) 2025 David Marker <dave@freedave.net>
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd April 4, 2025
+.Dt NGPORTAL 8
+.Os
+.Sh NAME
+.Nm ngportal
+.Nd netgraph portal gun to create connected wormhole pairs
+.Sh SYNOPSIS
+.Nm
+.Op Fl n
+.Op Fl j Ar jail
+.Ar spec1
+.Op Ar spec2
+.Sh DESCRIPTION
+The
+.Nm
+utility creates an
+.Xr ng_wormhole 4
+and then opens it in a different jail to connect and complete the pair.
+Additionally, as a convenience,
+.Nm
+can name one or both wormholes for
+.Xr ngctl 8 ,
+as well as connect one or both sides event horizon
+to an already existing
+.Xr netgraph 4
+node.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl n
+Disable automatic loading of
+.Xr ng_socket 4
+and
+.Xr ng_wormhole 4
+kernel modules.
+.It Fl j Ar jail
+Perform the actions inside the
+.Ar jail .
+This becomes the new default jail when none is specified.
+.El
+.Pp
+Specifications are colon separated strings with the following
+components, all of which are optional:
+.Op Ar jail Ns :
+.Op Ar name Ns :
+.Op Ar node:hook
+.Bd -literal -offset indent -compact
+jail name or id of jail that has been created.
+name a name to provide the wormhole in the jail referenced
+ above if present or the default.
+node:hook a netgraph node and one of its hooks to connect to the
+ evthorizon of the wormhole in the jail referenced
+ above if present or the default.
+.Ed
+.Pp
+.Ar jail
+defaults to where you are running
+.Nm .
+But wormhole pairs can
+.Sy not
+be in the same
+.Ar jail .
+So at least one
+.Ar jail
+must be specified.
+Trailing colons are optional, but to specify a wormhole
+.Ar name
+you must have a leading colon even if you do not provide a
+.Ar jail .
+The
+.Ar node:hook
+pair really must be that.
+It is not a
+.Em path
+with
+.Em hook .
+It must simply
+identify the name or ID of a
+.Em node
+and the name of a
+.Em hook
+on that
+.Em node .
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Create a wormhole pair from the system to jail
+.Li test :
+.Dl # ngportal test
+.Pp
+Do the same but name the wormholes this time:
+.Dl # ngportal :wh0a test:wh0b
+.Pp
+Assuming you have an
+.Xr ng_bridge 4
+named
+.Li br0
+where you are running
+.Nm
+and an
+.Xr ng_eiface 4
+named
+.Li ngeth0
+in the jail
+.Li test
+they can be connected via wormhole to each other.
+The following will name the wormholes and connect them:
+.Dl # ngportal :wh0a:br0:link test:wh0b:ngeth0:ether
+.Pp
+All previous examples used a default jail.
+But to connect two
+.Xr ng_eiface 4
+in two peer
+.Li jails
+.Li (ngeth0a
+in left and
+.Li ngeth0b
+in center) use the following:
+.Dl # ngportal left::ngeth0a:ether center::ngeth0b:ether
+.Sh SEE ALSO
+.Xr netgraph 4 ,
+.Xr ng_bridge 4 ,
+.Xr ng_eiface 4 ,
+.Xr ng_wormhole 4 ,
+.Xr ngctl 8
+.Sh AUTHORS
+.An David Marker Aq Mt dave@freedave.net
diff --git a/usr.sbin/ngportal/portal.h b/usr.sbin/ngportal/portal.h
new file mode 100644
--- /dev/null
+++ b/usr.sbin/ngportal/portal.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2025 David Marker <dave@freedave.net>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <errno.h>
+#include <net/if.h>
+#include <sys/param.h>
+#include <sysexits.h>
+#include <netgraph.h>
+
+/* remove 1 for '\0' */
+#define NG_NODELEN (NG_NODESIZ - 1)
+#define NG_HOOKLEN (NG_HOOKSIZ - 1)
+
+#define LLNAMSIZ 18
+#define LLNAMLEN (LLNAMSIZ - 1)
+
+#define ERREXIT ((errno == EPERM) ? EX_NOPERM : EX_OSERR)
+#define ERRALT(alt) ((errno == EPERM) ? EX_NOPERM : alt)
+
+
+/* module loading: kld.c */
+void kld_ensure_load(const char *);
+
+/* netgraph functions: ng.c */
+typedef int ngctx;
+
+ngctx ng_create_context(void);
+void ng_shutdown_node(ngctx, ng_ID_t);
+
+ng_ID_t ng_wh_create(ngctx);
+ng_ID_t ng_wh_open(ngctx, ng_ID_t, const char *);
+void ng_wh_name(ngctx, ng_ID_t, const char *);
+void ng_wh_connect(ngctx, ng_ID_t, const char *, const char *);
diff --git a/usr.sbin/ngportal/portal.c b/usr.sbin/ngportal/portal.c
new file mode 100644
--- /dev/null
+++ b/usr.sbin/ngportal/portal.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2025 David Marker <dave@freedave.net>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <assert.h>
+#include <err.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/wait.h>
+#include <jail.h>
+
+#include <netgraph/ng_wormhole.h>
+
+#include "portal.h"
+
+/* name of our utility */
+#define ME "ngportal"
+
+/*
+ * The single purpose of this utility is to create and connect an ng_wormhole(4)
+ * that exists in the current vnet and a separate jail vnet.
+ */
+
+static void
+Usage(const char *format, ...)
+{
+ if (format != NULL) {
+ va_list ap;
+ va_start(ap, format);
+ (void) vfprintf(stderr, format, ap);
+ va_end(ap);
+ }
+
+ /*
+ * Sadly users don't need to know or care that providing both jail names
+ * means we create a 2 pairs of wormholes then collapse them into a
+ * single pair that remains in both jails. The portal gun is more
+ * mysterious than they will ever know.
+ */
+ (void) fprintf(stderr,
+ "USAGE: " ME " [-n] [-j jail] spec1 [spec2]\n"
+ "-n\tDisable automatic loading of netgraph(4) kernel modules.\n"
+ "-j jail\tSwitch to jail for all references.\n\n"
+ "You provide 2 wormhole specifications (components of which are\n"
+ "separated by colons). The wormhole spec componentes are:\n"
+ "\t[jail][:name][:node:hook]\n"
+ "For each spec the components are:\n"
+ "jail\t\tjail reference for remaining components. If not preset it\n"
+ "\t\tdefaults to where `" ME "' is run (see `-j' above).\n"
+ "name\t\tset the netgraph name of the wormhole to [name] in [jail].\n"
+ "node:hook\tconnect the wormhole in [jail] to this [node:hook] pair.\n\n"
+ "Without a jail componet the spec assumes the jail where `" ME "' was\n"
+ "run or the jail from `-j'. But the jails from spec1 and spec2 MUST be\n"
+ "different. Without a name the wormhole can only be accessed by ID.\n"
+ "Without a [node:hook] netgraph path the wormhole is left unconnected\n"
+ "but is held open by the other side. It can be connected to later with\n"
+ "ngctl(8).\n");
+
+ exit(EX_USAGE);
+}
+
+/*
+ * Module global, for err_cleanup to find everything.
+ * Start with invalid values our error cleanup can check for.
+ */
+static struct {
+ ngctx fd;
+ ng_ID_t wh1a;
+ ng_ID_t wh2a;
+} G = {
+ .fd = -1,
+ .wh1a = 0,
+ .wh2a = 0
+};
+
+static void
+err_cleanup(int _)
+{
+ if (G.fd == -1)
+ return; /* can't shutdown without this */
+
+ if (G.wh1a != 0)
+ ng_shutdown_node(G.fd, G.wh1a);
+ if (G.wh2a != 0)
+ ng_shutdown_node(G.fd, G.wh2a);
+
+ close(G.fd);
+}
+
+/*
+ * This will split a string like "jail:name:node:hook" into separate
+ * parts: "jail", "name", "node", "hook".
+ *
+ * We have an extra ':' after jail. And an extra ':' after name. That's so
+ * we can replace with '\0' and slice string up with strsep. But
+ * the last component found does not need to have a ':' after it.
+ *
+ * But you can't provide node and not provide hook.
+ *
+ * So that users don't have to play "fetch a rock" with their input we
+ * warn and return -1 after reporting as many issues as we can find.
+ *
+ */
+static int
+parse_spec(char *arg, const char **jail, const char **name,
+ const char **node, const char **hook)
+{
+ int rc = 0;
+ char **iter, *components[4];
+
+ assert(arg != NULL);
+ assert(jail != NULL);
+ assert(name != NULL);
+ assert(node != NULL);
+ assert(hook != NULL);
+
+ memset(components, 0, sizeof(components));
+
+ /* almost straight from man page :) */
+ for (iter = components; (*iter = strsep(&arg, ":")) != NULL;)
+ if (++iter >= &components[4])
+ break;
+
+ if (arg != NULL)
+ warnx("unrecognized components after wormhole spec `%s'",
+ arg), rc++;
+
+# define CHECKCOMPONENT(var, max, set) \
+ do { \
+ if (var != NULL) { \
+ if (strlen(var) > (max)) { \
+ warnx(#set " name too long: `%s'", \
+ var), rc++; \
+ } else \
+ *set = var[0] == '\0' ? NULL: var; \
+ } \
+ } while(0)
+ CHECKCOMPONENT(components[0], MAXHOSTNAMELEN, jail);
+ CHECKCOMPONENT(components[1], NG_NODELEN, name);
+ CHECKCOMPONENT(components[2], NG_NODELEN, node);
+ CHECKCOMPONENT(components[3], NG_NODELEN, hook);
+# undef CHECKCOMPONENT
+
+ /* node,hook must both be set or both be unset */
+ if (components[2] != NULL && components[3] == NULL)
+ warnx("node: `%s': set but missing hook", components[2]), rc++;
+ if (components[2] == NULL && components[3] != NULL)
+ warnx("hook: `%s': set but missing node", components[3]), rc++;
+
+ return (rc == 0) ? 0 : -1;
+}
+
+static void
+jail_name_connect(int jid, ng_ID_t wh, const char *name, const char *node,
+ const char *hook)
+{
+ int rc;
+ pid_t pid;
+ assert(jid > 0); /* system is 0 */
+ assert(wh > 0);
+
+ if (name == NULL && node == NULL)
+ return; /* nothing to do so don't fork etc */
+
+ pid = fork();
+ if (pid == -1)
+ err(ERREXIT, "%s: fork()", __func__);
+ if (pid == 0) { /* child */
+ (void) close(G.fd);
+ G.fd = -1;
+ /* child never has to close wormholes */
+ G.wh1a = 0;
+ G.wh2a = 0;
+ if (jail_attach(jid) != 0)
+ errx(ERRALT(EX_OSERR), "cannot attach to jail (jid=%d)",
+ jid);
+ /* must get new context */
+ G.fd = ng_create_context();
+ ng_wh_name(G.fd, wh, name);
+ ng_wh_connect(G.fd, wh, node, hook);
+ (void) close(G.fd);
+ exit(0);
+ } else { /* parent */
+ int status;
+ do {
+ rc = wait(&status);
+ } while (rc == -1 && errno == EINTR);
+ if (rc == -1)
+ err(EX_OSERR, "failed to wait for child in prison");
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ err(EX_OSERR, "child failed its mission");
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int ch, rc = 0, jid1 = 0, jid2 = 0, load_kmod = 1;
+ const char *jail = NULL; /* for -j not spec */
+ const char *jail1, *name1, *node1, *hook1;
+ const char *jail2, *name2, *node2, *hook2;
+ ng_ID_t wh1b, wh2b;
+
+ setvbuf(stdout, NULL, _IONBF, BUFSIZ);
+
+ /* use getopt_long so they can place options anywhere */
+ while ((ch = getopt_long(argc, argv, ":nj:", NULL, NULL)) != -1) {
+ switch (ch) {
+ case 'j':
+ jail = optarg;
+ if (strlen(jail) > MAXHOSTNAMELEN)
+ Usage(ME ": `%s' exceeds %d characters\n\n",
+ jail, MAXHOSTNAMELEN);
+ break;
+ case 'n':
+ load_kmod = 0; /* user asked not to */
+ break;
+ default:
+ Usage(ME ": unrecognized option `%s'\n\n",
+ argv[optind - 1]);
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ jail1 = name1 = node1 = hook1 = NULL;
+ jail2 = name2 = node2 = hook2 = NULL;
+ switch (argc) {
+ case 0:
+ Usage(ME ": must minimally provide `jail' of one spec\n\n");
+ case 1:
+ rc = parse_spec(argv[0], &jail1, &name1, &node1, &hook1);
+ jail2 = name2 = node2 = hook2 = NULL;
+ break;
+ case 2:
+ rc = parse_spec(argv[0], &jail1, &name1, &node1, &hook1);
+ rc += parse_spec(argv[1], &jail2, &name2, &node2, &hook2);
+ break;
+ default:
+ Usage(ME ": too many arguments provided\n\n");
+ }
+
+ if (rc != 0)
+ Usage("\n\n");
+
+ if (jail1 == NULL && jail2 == NULL)
+ Usage(ME ": duplicate (default) jail reference detected\n\n");
+
+ /*
+ * All that mattered is that we got at least one jail name.
+ * Arbitrarily lets make that jail1 if we only have 1 jail name.
+ * After this we know we have to do jail1 and may have to deal
+ * with jail2.
+ */
+ if (jail1 == NULL) {
+ const char *tmp;
+# define SWAP(cp1, cp2) \
+ do { tmp = cp1; cp1 = cp2; cp2 = tmp; } while(0)
+ SWAP(jail2, jail1);
+ SWAP(name2, name1);
+ SWAP(node2, node1);
+ SWAP(hook2, hook1);
+# undef SWAP
+ }
+
+ /*
+ * if we have a jail to switch to it must be before we look up jids
+ * for wormhole specs
+ */
+ if (jail != NULL) {
+ int jid;
+ jid = jail_getid(jail);
+ if (jid == -1)
+ errx(ERRALT(EX_NOHOST), "%s", jail_errmsg);
+ if (jail_attach(jid) != 0)
+ errx(ERRALT(EX_OSERR), "cannot attach to jail");
+ }
+
+ /* comparing jail names won't work, one could be given numerically */
+ jid1 = jail_getid(jail1);
+ if (jid1 == -1)
+ errx(ERRALT(EX_NOHOST), "%s", jail_errmsg);
+ if (jail2 != NULL) {
+ jid2 = jail_getid(jail2);
+ if (jid2 == -1)
+ errx(ERRALT(EX_NOHOST), "%s", jail_errmsg);
+ }
+
+ /*
+ * the kernel code specifically prevents this too, but this is a bettter
+ * message than just "invalid argument".
+ */
+ if (jid1 == jid2)
+ Usage( ME ": duplicate jail reference detected\n\n");
+
+ /*
+ * Unless told not to (or we definitely are in a jail) try to make sure
+ * we have modules loaded.
+ */
+ if (load_kmod != 0 && jail == NULL) {
+ kld_ensure_load("ng_socket");
+ kld_ensure_load("ng_wormhole");
+ }
+
+ /* set up error cleanup */
+ err_set_exit(err_cleanup);
+
+ /* open netgraph socket */
+ G.fd = ng_create_context();
+
+ /* this one is always created and opened etc. */
+ G.wh1a = ng_wh_create(G.fd);
+ wh1b = ng_wh_open(G.fd, G.wh1a, jail1);
+ jail_name_connect(jid1, wh1b, name1, node1, hook1);
+
+ if (jid2 != 0) {
+ /*
+ * In this case we have to make another wormhole.
+ * Then we have to collapse it with wh2a.
+ */
+ char pth[NG_NODESIZ];
+ G.wh2a = ng_wh_create(G.fd);
+ wh2b = ng_wh_open(G.fd, G.wh2a, jail2);
+ jail_name_connect(jid2, wh2b, name2, node2, hook2);
+
+ /* not IDFMT, it can't have ':' on end */
+ snprintf(pth, sizeof(pth), "[%08x]", G.wh2a);
+ ng_wh_connect(G.fd, G.wh1a, pth, NG_WORMHOLE_HOOK);
+ } else {
+ /* just deal with connecting in current vnet */
+ ng_wh_name(G.fd, G.wh1a, name2);
+ ng_wh_connect(G.fd, G.wh1a, node2, hook2);
+ }
+
+ return (0);
+}

File Metadata

Mime Type
text/plain
Expires
Mon, Feb 23, 4:46 PM (4 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28960496
Default Alt Text
D50245.id156091.diff (21 KB)

Event Timeline