Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F145652530
D50245.id156091.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
21 KB
Referenced Files
None
Subscribers
None
D50245.id156091.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D50245: add utility to simplify use of ng_wormhole
Attached
Detach File
Event Timeline
Log In to Comment