From 20e2873a48496c42832e1e9c8f1ea8024491b17b Mon Sep 17 00:00:00 2001 From: Vlad Ki Date: Sat, 16 Jul 2016 13:29:47 -0700 Subject: [PATCH 1/2] Node: support setHostname to run a node in its own UTS namespace This is important for applications that rely on having hostnames within the same network. Using this feature still requires manual editing for /etc/hosts.x --- mininet/node.py | 18 ++++++++++++------ mininet/topolib.py | 1 + mnexec.c | 28 +++++++++++++++++++++++++--- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/mininet/node.py b/mininet/node.py index 1e50b512..943bc290 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -74,9 +74,10 @@ class Node( object ): portBase = 0 # Nodes always start with eth0/port0, even in OF 1.0 - def __init__( self, name, inNamespace=True, **params ): + def __init__( self, name, inNamespace=True, setHostname=False, **params ): """name: name of node inNamespace: in network namespace? + setHostname: in uts namespace? privateDirs: list of private directory strings or tuples params: Node parameters (see config() for details)""" @@ -86,6 +87,7 @@ def __init__( self, name, inNamespace=True, **params ): self.name = params.get( 'name', name ) self.privateDirs = params.get( 'privateDirs', [] ) self.inNamespace = params.get( 'inNamespace', inNamespace ) + self.setHostname = params.get( 'setHostname', setHostname ) # Stash configuration parameters for future reference self.params = params @@ -127,15 +129,19 @@ def startShell( self, mnopts=None ): error( "%s: shell is already running\n" % self.name ) return # mnexec: (c)lose descriptors, (d)etach from tty, - # (p)rint pid, and run in (n)amespace - opts = '-cd' if mnopts is None else mnopts + # (p)rint pid, run in (n)amespace, unshare (u)ts with a hostname + mnexec = ['mnexec'] + mnexec.append('-cd' if not mnopts else mnopts) if self.inNamespace: - opts += 'n' + mnexec.append('-n') + if self.setHostname: + mnexec.extend(['-u', self.name]) # bash -i: force interactive # -s: pass $* to shell, and make process easy to find in ps # prompt is set to sentinel chr( 127 ) - cmd = [ 'mnexec', opts, 'env', 'PS1=' + chr( 127 ), - 'bash', '--norc', '-is', 'mininet:' + self.name ] + bash = [ 'env', 'PS1=' + chr( 127 ), + 'bash', '--norc', '-is', 'mininet:' + self.name ] + cmd = mnexec + bash # Spawn a shell subprocess in a pseudo-tty, to disable buffering # in the subprocess and insulate it from signals (e.g. SIGINT) # received by the parent diff --git a/mininet/topolib.py b/mininet/topolib.py index 9a056179..27736534 100644 --- a/mininet/topolib.py +++ b/mininet/topolib.py @@ -34,6 +34,7 @@ def addTree( self, depth, fanout ): def TreeNet( depth=1, fanout=2, **kwargs ): "Convenience function for creating tree networks." + # topo = TreeTopo( depth, fanout, hopts={'setHostname':True}) topo = TreeTopo( depth, fanout ) return Mininet( topo, **kwargs ) diff --git a/mnexec.c b/mnexec.c index d3f173d7..6657322f 100644 --- a/mnexec.c +++ b/mnexec.c @@ -32,15 +32,16 @@ void usage(char *name) { printf("Execution utility for Mininet\n\n" - "Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n" + "Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] [-u hostname] cmd args...\n\n" "Options:\n" " -c: close all file descriptors except stdin/out/error\n" " -d: detach from tty by calling setsid()\n" " -n: run in new network and mount namespaces\n" " -p: print ^A + pid\n" - " -a pid: attach to pid's network and mount namespaces\n" + " -a pid: attach to pid's network, mount and UTS namespaces\n" " -g group: add to cgroup\n" " -r rtprio: run with SCHED_RR (usually requires -g)\n" + " -u hostname: run in new uts namespace\n" " -v: print version\n", name); } @@ -102,7 +103,7 @@ int main(int argc, char *argv[]) char *cwd = get_current_dir_name(); static struct sched_param sp; - while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1) + while ((c = getopt(argc, argv, "+cdnpa:g:r:u:vh")) != -1) switch(c) { case 'c': /* close file descriptors except stdin/out/error */ @@ -180,6 +181,17 @@ int main(int argc, char *argv[]) perror(cwd); return 1; } + /* Attach to pid's UTS namespace */ + sprintf(path, "/proc/%d/ns/uts", pid); + nsid = open(path, O_RDONLY); + if (nsid < 0) { + perror(path); + return 1; + } + if (setns(nsid, 0) != 0) { + perror("setns"); + return 1; + } break; case 'g': /* Attach to cgroup */ @@ -193,6 +205,16 @@ int main(int argc, char *argv[]) return 1; } break; + case 'u': + if (unshare(CLONE_NEWUTS) == -1) { + perror("unshare NEWUTS"); + return 1; + } + if (sethostname(optarg, strlen(optarg)) == -1) { + perror("sethostname"); + return 1; + } + break; case 'v': printf("%s\n", VERSION); exit(0); From 72f81ca53ba9e546f24b5bf05da985c040eddbde Mon Sep 17 00:00:00 2001 From: Vlad Ki Date: Sat, 16 Jul 2016 13:37:10 -0700 Subject: [PATCH 2/2] mnexec: include string.h, get rid of sprintf --- mnexec.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mnexec.c b/mnexec.c index 6657322f..32d7a8db 100644 --- a/mnexec.c +++ b/mnexec.c @@ -15,6 +15,7 @@ #define _GNU_SOURCE #include +#include #include #include #include @@ -155,7 +156,7 @@ int main(int argc, char *argv[]) case 'a': /* Attach to pid's network namespace and mount namespace */ pid = atoi(optarg); - sprintf(path, "/proc/%d/ns/net", pid); + snprintf(path, PATH_MAX, "/proc/%d/ns/net", pid); nsid = open(path, O_RDONLY); if (nsid < 0) { perror(path); @@ -166,11 +167,11 @@ int main(int argc, char *argv[]) return 1; } /* Plan A: call setns() to attach to mount namespace */ - sprintf(path, "/proc/%d/ns/mnt", pid); + snprintf(path, PATH_MAX, "/proc/%d/ns/mnt", pid); nsid = open(path, O_RDONLY); if (nsid < 0 || setns(nsid, 0) != 0) { /* Plan B: chroot/chdir into pid's root file system */ - sprintf(path, "/proc/%d/root", pid); + snprintf(path, PATH_MAX, "/proc/%d/root", pid); if (chroot(path) < 0) { perror(path); return 1; @@ -182,7 +183,7 @@ int main(int argc, char *argv[]) return 1; } /* Attach to pid's UTS namespace */ - sprintf(path, "/proc/%d/ns/uts", pid); + snprintf(path, PATH_MAX, "/proc/%d/ns/uts", pid); nsid = open(path, O_RDONLY); if (nsid < 0) { perror(path);