diff options
author | zec <zec@FreeBSD.org> | 2009-08-20 22:39:20 +0000 |
---|---|---|
committer | zec <zec@FreeBSD.org> | 2009-08-20 22:39:20 +0000 |
commit | 825d1f23be6c62e3b62e6eeaf5376e64b77e90e8 (patch) | |
tree | 5e808ca0f1b28a074d63b2d51e85f380159e4a0b /tools/tools | |
parent | bb1c942f116ca63b21e3cf4cdc47a62dc47ee664 (diff) | |
download | FreeBSD-src-825d1f23be6c62e3b62e6eeaf5376e64b77e90e8.zip FreeBSD-src-825d1f23be6c62e3b62e6eeaf5376e64b77e90e8.tar.gz |
vimage(8) is a legacy CLI interface for managing jails associated with
network stack instances, which is provided for compatibility with
older applications. This change brings it back to life in a followup
to the initial conversion of vimage to use the new jail(4)
userland-kernel API:
- when creating vimages via "vimage -c", by default turn on a few
options expected by legacy applications, such as allow operations on
raw sockets, FS mounts etc, and allow jail-related parameters to be
optionally configured.
- introduce the "-m" modifier which allows for configuring jail
parameters of existing vimages / vnet-jails.
- make "vimage name command ..." actually work.
- when reassigning ifnets to vnets using "vimage -i", attempt to rename
the ifnet as "ethXXX" on arrival in the target vnet. Several legacy
applications are known to depend heavily on such behavior.
- vimage -l lists only jails associated with vnets. The output is
sorted using vimage / jail names as keys.
- vimage -l by default searches only the current level in the jail
hierarchy. Recursive listing can be requested via -r switch.
- vimage -l by default prints only jail names on each line, making
such output suitable for pipelining to other commands. More verbose
output can be obtained via -v switch, and even more jail specific
information will be displayed if -j switch is turned on.
- there's no need to build vimage as statically linked, so update the
Makefile accordingly.
- update the vimage.8 man page.
Approved by: re (rwatson), julian (mentor)
MFC after: immediately
Diffstat (limited to 'tools/tools')
-rw-r--r-- | tools/tools/vimage/Makefile | 1 | ||||
-rw-r--r-- | tools/tools/vimage/vimage.8 | 153 | ||||
-rw-r--r-- | tools/tools/vimage/vimage.c | 420 |
3 files changed, 399 insertions, 175 deletions
diff --git a/tools/tools/vimage/Makefile b/tools/tools/vimage/Makefile index 82f4f47..76c7617 100644 --- a/tools/tools/vimage/Makefile +++ b/tools/tools/vimage/Makefile @@ -10,6 +10,5 @@ CFLAGS+= -I../../../sys MAN= vimage.8 BINDIR?= /usr/sbin -NO_SHARED?= YES .include <bsd.prog.mk> diff --git a/tools/tools/vimage/vimage.8 b/tools/tools/vimage/vimage.8 index f012af8..517a788 100644 --- a/tools/tools/vimage/vimage.8 +++ b/tools/tools/vimage/vimage.8 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2002, 2003 Marko Zec <zec@tel.fer.hr> +.\" Copyright (c) 2002, 2003 Marko Zec <zec@fer.hr> .\" Copyright (c) 2009 University of Zagreb .\" Copyright (c) 2009 FreeBSD Foundation .\" @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 6, 2009 +.Dd August 25, 2009 .Dt VIMAGE 8 .Os .Sh NAME @@ -35,35 +35,46 @@ .Nd manage virtual network stacks .Sh SYNOPSIS .Nm -.Ar vi_name -.Op command -.Nm -.Fl c -.Ar vi_name +.Op Fl c | m +.Ar vname +.Op Ar param=value ... .Nm .Fl d -.Ar vi_name +.Ar vname .Nm .Fl l -.Op Ar vi_name +.Op Fl rvj +.Op Ar vname .Nm .Fl i -.Ar vi_name interface +.Ar vname ifname +.Op Ar newifname +.Nm +.Ar vi_name +.Op command ... .Sh DESCRIPTION +The .Nm -command is an interm user interface for controlling the virtual network -stacks in FreeBSD. +utility is an alternative user interface for controlling virtual network +stacks in FreeBSD, aimed primarily at supporting legacy applications +which are not yet converted to using +.Xr jail 8 , +.Xr jexec 8 , +and +.Xr jls 8 . +. .Ss Overview -A virtual image reprepresents an isolated operating environment with its -own independent network stack instance. Every process, socket and network -interface present in the system is always attached to one, and only one, -virtual image i.e. virtual network stack instance. -During the system bootup sequence default virtual image is created to -which all the configured interfaces and user processes are initially -assigned. -Assuming that enough system resources and per virtual image privileges -are provided, the super-user can create and manage a hierarchy of -subordinated virtual images. The +A virtual image or vimage is a jail with its own independent network +stack instance. Every process, socket and network interface present +in the system is always attached to one, and only one, virtual network +stack instance (vnet). +During system bootup sequence a default vnet +is created to which all the configured interfaces and user processes +are initially attached. +Assuming that enough system resources are +are available, a user with sufficient privileges can create and manage +a hierarchy of subordinated virtual images. +The .Nm command allows for creation, deletion and monitoring of virtual images, as well as for execution of arbitrary processes in a targeted virtual @@ -71,59 +82,72 @@ image. .Ss Invocation If invoked with no modifiers, the .Nm -command spawns a new shell process in virtual image -.Ar vi_name . -If provided, the optional arguments following the virtual image name -.Ar vi_name -are interpreted as a standard command line issued at a shell, -otherwise an interactive shell is started in the target virtual image. +command spawns a new interactive shell in virtual image +.Ar vname . +If optional additional arguments following +.Ar vname +are provided, the first of those will be executed in place of the +interactive shell, and the rest of the arguments will be passed as +arguments to the executed command. .Pp -The following parameters are available: +The following modifiers are available: .Bl -tag -width indent .It Fl c Create a new virtual image named -.So -.Ar vi_name -.Sc . +.Ar vname . +Additional arguments, if provided, may be used to specify operating +parameters different from defaults, in format +.Ar param=value . +See +.Xr jail 8 +for an extensive list of available parameters. +.It Fl m +Modify the parameters of a virtual image named +.Ar vname , +using the same syntax as with the -c form of the command. .It Fl d Delete the virtual image -.Ar vi_name . +.Ar vname . No processes and/or sockets should exist in the target virtual image -in order for the delete request to succeed. Non-loopback interfaces +in order for the delete request to succeed. Non-loopback interfaces residing in the target virtual image will be reassigned to the virtual image's parent. .It Fl l List the properties and statistics for virtual images one level below the current one in the hierarchy. If an optional argument -.Ar vi_name +.Ar vname is provided, only the information regarding the target virtual image -.Ar vi_name +.Ar vname is displayed. -.It Fl lr -List the properties and statistics for all virtual images in -the hierarchy of subordinated vimages. If an optional argument -.Ar vi_name -is provided, the hierarchy will be traversed at and below the -.Ar vi_name -level. +With the optional +.Op Ar -r +switch enabled the list will include all virtual images below the +current level in the vimage hierarchy. +Enabling the optional +.Op Ar -v +or +.Op Ar -j +switches results in a more detailed output. .It Fl i -Move the interface -.Ar interface +Move interface +.Ar ifname to the target virtual image -.Ar vi_name . -If the value of -.Ar vi_name -argument is -.So .. +.Ar vname . +Interfaces will be automatically renamed to +.So +ethXX .Sc , -the interface is returned to the parent of the current virtual image. +unless an optional argument specifying the desired interface name +.Op Ar newifname +is provided. .El .Sh EXAMPLES Create a new virtual image named .So v1 -.Sc : +.Sc , +which is allowed to create and manage an own subhierarchy of vimages: .Pp -.Dl vimage -c v1 +.Dl vimage -c v1 children.max=100 .Pp Execute the .So ifconfig @@ -137,28 +161,35 @@ Move the interface .So vlan0 .Sc to the virtual image .So v1 +.Sc while renaming the interface as +.So +ve0 .Sc : .Pp -.Dl vimage -i v1 vlan0 +.Dl vimage -i v1 vlan0 ve0 .Pp Show the status information for virtual image .So v1 .Sc : .Pp -.Dl vimage -l v1 +.Dl vimage -lv v1 .Sh DIAGNOSTICS The .Nm command exits 0 on success, and >0 if an error occurs. .Sh SEE ALSO .Xr jail 8 +.Xr jexec 8 +.Xr jls 8 .Sh BUGS -If memory allocation failure occurs during the vimage creation, it will remain -undetected/ignored in the current implementation, thus latently scheduling -an almost imminent system crash in the future. +Deletion of vimages / vnets is known to leak kernel memory and fail at +stopping various timers, hence may lead to system crashes. .Sh AUTHOR .An "Marko Zec" Aq zec@fer.hr .Sh HISTORY -The -.Nm -facility first appeared as a patch against FreeBSD 4.7-RELEASE in 2002. +Network stack virtualization framework first appeared as a patchset +against the FreeBSD 4.7 kernel in 2002, and was maintained outside +of the main FreeBSD tree. +As a result of a project sponsored by the FreeBSD Foundation and +Stiching NLNet, integrated virtualized network stack first appeared +in FreeBSD 8.0. diff --git a/tools/tools/vimage/vimage.c b/tools/tools/vimage/vimage.c index e4ab584..b54f9d6 100644 --- a/tools/tools/vimage/vimage.c +++ b/tools/tools/vimage/vimage.c @@ -28,142 +28,294 @@ */ #include <sys/param.h> -#include <sys/types.h> #include <sys/ioctl.h> #include <sys/jail.h> #include <sys/socket.h> #include <net/if.h> -#include <errno.h> +#include <ctype.h> #include <jail.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#define VI_CREATE 0x00000001 -#define VI_DESTROY 0x00000002 -#define VI_SWITCHTO 0x00000008 -#define VI_IFACE 0x00000010 -#define VI_GET 0x00000100 -#define VI_GETNEXT 0x00000200 +typedef enum { + VI_SWITCHTO, + VI_CREATE, + VI_MODIFY, + VI_DESTROY, + VI_IFMOVE, + VI_GET +} vi_cmd_t; -static int getjail(char *name, int lastjid, int *vnet); +typedef struct vimage_status { + char name[MAXPATHLEN]; /* Must be first field for strcmp(). */ + char path[MAXPATHLEN]; + char hostname[MAXPATHLEN]; + char domainname[MAXPATHLEN]; + int jid; + int parentjid; + int vnet; + int childcnt; + int childmax; + int cpuset; + int rawsock; + int socket_af; + int mount; +} vstat_t; + +#define VST_SIZE_STEP 1024 +#define MAXPARAMS 32 + +static int getjail(vstat_t *, int, int); + +static char *invocname; + +static void +usage(void) +{ + + fprintf(stderr, + "usage: %s [-c | -m] vname [param=value ...]\n" + " %s -d vname\n" + " %s -l[rvj] [vname]\n" + " %s -i vname ifname [newifname]\n" + " %s vname [command ...]\n", + invocname, invocname, invocname, invocname, invocname); + exit(1); +} int main(int argc, char **argv) { - int s; - char *shell; - int cmd; - int jid, vnet; + struct jailparam params[MAXPARAMS]; + char ifname[IFNAMSIZ]; struct ifreq ifreq; - char name[MAXHOSTNAMELEN]; - - switch (argc) { + vi_cmd_t newcmd, cmd; + int recurse = 0; + int verbose = 0; + int jid, i, s, namelen; + int vst_size, vst_last; + vstat_t *vst; + char *str; + char ch; - case 1: - cmd = 0; - break; + invocname = argv[0]; - case 2: - if (strcmp(argv[1], "-l") == 0) - cmd = VI_GETNEXT; - else if (strcmp(argv[1], "-lr") == 0) - cmd = VI_GETNEXT; - else { - strcpy(name, argv[1]); - cmd = VI_SWITCHTO; + newcmd = cmd = VI_SWITCHTO; /* Default if no modifiers specified. */ + while ((ch = getopt(argc, argv, "cdijlmrv")) != -1) { + switch (ch) { + case 'c': + newcmd = VI_CREATE; + break; + case 'm': + newcmd = VI_MODIFY; + break; + case 'd': + newcmd = VI_DESTROY; + break; + case 'l': + newcmd = VI_GET; + break; + case 'i': + newcmd = VI_IFMOVE; + break; + case 'r': + recurse = 1; + break; + case 'v': + verbose++; + break; + case 'j': + verbose = 2; + break; + default: + usage(); } - break; - - case 3: - strcpy(name, argv[2]); - if (strcmp(argv[1], "-l") == 0) - cmd = VI_GET; - if (strcmp(argv[1], "-c") == 0) - cmd = VI_CREATE; - if (strcmp(argv[1], "-d") == 0) - cmd = VI_DESTROY; - break; - - default: - strcpy(name, argv[2]); - if (strcmp(argv[1], "-c") == 0) - cmd = VI_CREATE; - if (strcmp(argv[1], "-i") == 0) - cmd = VI_IFACE; + if (cmd == VI_SWITCHTO || cmd == newcmd) + cmd = newcmd; + else + usage(); } + argc -= optind; + argv += optind; - switch (cmd) { + if ((cmd != VI_GET && (argc == 0 || recurse != 0 || verbose != 0)) || + (cmd == VI_IFMOVE && (argc < 2 || argc > 3)) || + (cmd == VI_MODIFY && argc < 2) || argc >= MAXPARAMS) + usage(); + switch (cmd) { case VI_GET: - jid = getjail(name, -1, &vnet); - if (jid < 0) - goto abort; - printf("%d: %s%s\n", jid, name, vnet ? "" : " (no vnet)"); - exit(0); - - case VI_GETNEXT: + vst_last = 0; + vst_size = VST_SIZE_STEP; + if ((vst = malloc(vst_size * sizeof(*vst))) == NULL) + break; + if (argc == 1) + namelen = strlen(argv[0]); + else + namelen = 0; jid = 0; - while ((jid = getjail(name, jid, &vnet)) > 0) - printf("%d: %s%s\n", jid, name, - vnet ? "" : " (no vnet)"); + while ((jid = getjail(&vst[vst_last], jid, verbose)) > 0) { + /* Skip jails which do not own vnets. */ + if (vst[vst_last].vnet != 1) + continue; + /* Skip non-matching vnames / hierarchies. */ + if (namelen && + ((strlen(vst[vst_last].name) < namelen || + strncmp(vst[vst_last].name, argv[0], namelen) != 0) + || (strlen(vst[vst_last].name) > namelen && + vst[vst_last].name[namelen] != '.'))) + continue; + /* Skip any sub-trees if -r not requested. */ + if (!recurse && + (strlen(vst[vst_last].name) < namelen || + strchr(&vst[vst_last].name[namelen], '.') != NULL)) + continue; + /* Grow vst table if necessary. */ + if (++vst_last == vst_size) { + vst_size += VST_SIZE_STEP; + vst = realloc(vst, vst_size * sizeof(*vst)); + if (vst == NULL) + break; + } + } + if (vst == NULL) + break; + /* Sort: the key is the 1st field in *vst, i.e. vimage name. */ + qsort(vst, vst_last, sizeof(*vst), (void *) strcmp); + for (i = 0; i < vst_last; i++) { + if (!verbose) { + printf("%s\n", vst[i].name); + continue; + } + + printf("%s:\n", vst[i].name); + printf(" Path: %s\n", vst[i].path); + printf(" Hostname: %s\n", vst[i].hostname); + printf(" Domainname: %s\n", vst[i].domainname); + printf(" Children: %d\n", vst[i].childcnt); + + if (verbose < 2) + continue; + + printf(" Children limit: %d\n", vst[i].childmax); + printf(" CPUsetID: %d\n", vst[i].cpuset); + printf(" JID: %d\n", vst[i].jid); + printf(" PJID: %d\n", vst[i].parentjid); + printf(" Raw sockets allowed: %d\n", vst[i].rawsock); + printf(" All AF allowed: %d\n", vst[i].socket_af); + printf(" Mount allowed: %d\n", vst[i].mount); + } + free(vst); exit(0); - case VI_IFACE: - s = socket(AF_INET, SOCK_DGRAM, 0); - if (s == -1) - goto abort; - jid = jail_getid(name); - if (jid < 0) - goto abort; + case VI_IFMOVE: + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + break; + if ((jid = jail_getid(argv[0])) < 0) + break; ifreq.ifr_jid = jid; - strncpy(ifreq.ifr_name, argv[3], sizeof(ifreq.ifr_name)); + strncpy(ifreq.ifr_name, argv[1], sizeof(ifreq.ifr_name)); if (ioctl(s, SIOCSIFVNET, (caddr_t)&ifreq) < 0) - goto abort; - printf("%s@%s\n", ifreq.ifr_name, name); + break; + close(s); + if (argc == 3) + snprintf(ifname, sizeof(ifname), "%s", argv[2]); + else + snprintf(ifname, sizeof(ifname), "eth0"); + ifreq.ifr_data = ifname; + /* Do we need to rename the ifnet? */ + if (strcmp(ifreq.ifr_name, ifname) != 0) { + /* Switch to the context of the target vimage. */ + if (jail_attach(jid) < 0) + break; + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + break; + for (namelen = 0; isalpha(ifname[namelen]); namelen++); + i = 0; + /* Search for a free ifunit in target vnet. Unsafe. */ + while (ioctl(s, SIOCSIFNAME, (caddr_t)&ifreq) < 0) { + snprintf(&ifname[namelen], + sizeof(ifname) - namelen, "%d", i); + /* Emergency brake. */ + if (i++ == IF_MAXUNIT) + break; + } + } + if (i < IF_MAXUNIT) + printf("%s@%s\n", ifname, argv[0]); + else + printf("%s@%s\n", ifreq.ifr_name, argv[0]); exit(0); case VI_CREATE: - if (jail_setv(JAIL_CREATE, "name", name, "vnet", NULL, - "host", NULL, "persist", NULL, NULL) < 0) - goto abort; + if ((jid = jail_setv(JAIL_CREATE, + "name", argv[0], + "vnet", NULL, + "host", NULL, + "persist", NULL, + "allow.raw_sockets", "true", + "allow.socket_af", "true", + "allow.mount", "true", + NULL)) >= 0) + break; + if (jid < 0) + break; + if (argc == 1) + exit(0); + /* Not done yet, proceed to apply non-default parameters. */ + + case VI_MODIFY: + jailparam_init(¶ms[0], "name"); + jailparam_import(¶ms[0], argv[0]); + for (i = 1; i < argc; i++) { + for (str = argv[i]; *str != '=' && *str != 0; str++) { + /* Do nothing - search for '=' delimeter. */ + } + if (*str == 0) + break; + *str++ = 0; + if (*str == 0) + break; + jailparam_init(¶ms[i], argv[i]); + jailparam_import(¶ms[i], str); + } + if (i != argc) + break; + if (jailparam_set(params, i, JAIL_UPDATE) < 0) + break; + exit(0); + + case VI_DESTROY: + if ((jid = jail_getid(argv[0])) < 0) + break; + if (jail_remove(jid) < 0) + break; exit(0); case VI_SWITCHTO: - jid = jail_getid(name); - if (jid < 0) - goto abort; + if ((jid = jail_getid(argv[0])) < 0) + break; if (jail_attach(jid) < 0) - goto abort; - - if (argc == 2) { - printf("Switched to jail %s\n", argv[1]); - if ((shell = getenv("SHELL")) == NULL) - execlp("/bin/sh", argv[0], NULL); + break; + if (argc == 1) { + printf("Switched to vimage %s\n", argv[0]); + if ((str = getenv("SHELL")) == NULL) + execlp("/bin/sh", invocname, NULL); else - execlp(shell, argv[0], NULL); + execlp(str, invocname, NULL); } else - execvp(argv[2], &argv[2]); + execvp(argv[1], &argv[1]); break; - case VI_DESTROY: - jid = jail_getid(name); - if (jid < 0) - goto abort; - if (jail_remove(jid) < 0) - goto abort; - exit(0); - default: - fprintf(stderr, "usage: %s [-cdilr] vi_name [args]\n", - argv[0]); - exit(1); + /* Should be unreachable. */ + break; } -abort: if (jail_errmsg[0]) fprintf(stderr, "Error: %s\n", jail_errmsg); else @@ -172,27 +324,69 @@ abort: } static int -getjail(char *name, int lastjid, int *vnet) +getjail(vstat_t *vs, int lastjid, int verbose) { - struct jailparam params[3]; - int jid; + struct jailparam params[32]; /* Must be > max(psize). */ + int psize = 0; - if (lastjid < 0) { - jid = jail_getid(name); - if (jid < 0) - return (jid); - jailparam_init(¶ms[0], "jid"); - jailparam_import_raw(¶ms[0], &jid, sizeof jid); - } else { - jailparam_init(¶ms[0], "lastjid"); - jailparam_import_raw(¶ms[0], &lastjid, sizeof lastjid); - } - jailparam_init(¶ms[1], "name"); - jailparam_import_raw(¶ms[1], name, MAXHOSTNAMELEN); - name[0] = 0; - jailparam_init(¶ms[2], "vnet"); - jailparam_import_raw(¶ms[2], vnet, sizeof(*vnet)); - jid = jailparam_get(params, 3, 0); - jailparam_free(params, 3); - return (jid); + bzero(params, sizeof(params)); + bzero(vs, sizeof(*vs)); + + jailparam_init(¶ms[psize], "lastjid"); + jailparam_import_raw(¶ms[psize++], &lastjid, sizeof lastjid); + + jailparam_init(¶ms[psize], "vnet"); + jailparam_import_raw(¶ms[psize++], &vs->vnet, sizeof(vs->vnet)); + + jailparam_init(¶ms[psize], "name"); + jailparam_import_raw(¶ms[psize++], &vs->name, sizeof(vs->name)); + + if (verbose == 0) + goto done; + + jailparam_init(¶ms[psize], "path"); + jailparam_import_raw(¶ms[psize++], &vs->path, sizeof(vs->path)); + + jailparam_init(¶ms[psize], "host.hostname"); + jailparam_import_raw(¶ms[psize++], &vs->hostname, + sizeof(vs->hostname)); + + jailparam_init(¶ms[psize], "host.domainname"); + jailparam_import_raw(¶ms[psize++], &vs->domainname, + sizeof(vs->domainname)); + + jailparam_init(¶ms[psize], "children.cur"); + jailparam_import_raw(¶ms[psize++], &vs->childcnt, + sizeof(vs->childcnt)); + + if (verbose == 1) + goto done; + + jailparam_init(¶ms[psize], "children.max"); + jailparam_import_raw(¶ms[psize++], &vs->childmax, + sizeof(vs->childmax)); + + jailparam_init(¶ms[psize], "cpuset.id"); + jailparam_import_raw(¶ms[psize++], &vs->cpuset, + sizeof(vs->cpuset)); + + jailparam_init(¶ms[psize], "parent"); + jailparam_import_raw(¶ms[psize++], &vs->parentjid, + sizeof(vs->parentjid)); + + jailparam_init(¶ms[psize], "allow.raw_sockets"); + jailparam_import_raw(¶ms[psize++], &vs->rawsock, + sizeof(vs->rawsock)); + + jailparam_init(¶ms[psize], "allow.socket_af"); + jailparam_import_raw(¶ms[psize++], &vs->socket_af, + sizeof(vs->socket_af)); + + jailparam_init(¶ms[psize], "allow.mount"); + jailparam_import_raw(¶ms[psize++], &vs->mount, sizeof(vs->mount)); + +done: + vs->jid = jailparam_get(params, psize, 0); + jailparam_free(params, psize); + return (vs->jid); } |