-
Notifications
You must be signed in to change notification settings - Fork 128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduced named network namespace for replayshell #106
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
/* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||
#include "network_namespace.hh" | ||
|
||
#include <stdio.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
#include <fcntl.h> | ||
#include <dirent.h> | ||
#include <errno.h> | ||
#include <sys/mount.h> | ||
#include <sys/stat.h> | ||
#include <sched.h> | ||
|
||
#include "config.h" | ||
#include "system_runner.hh" | ||
#include "exception.hh" | ||
#include "file_descriptor.hh" | ||
|
||
using namespace std; | ||
|
||
NetworkNamespace::NetworkNamespace( const std::string & name ) | ||
: name_(name), has_own_resolvconf_(false), has_entered_(false) | ||
{ | ||
/* create a new network namespace */ | ||
run( { IP, "netns", "add", name_ } ); | ||
} | ||
|
||
|
||
NetworkNamespace::~NetworkNamespace() | ||
{ | ||
if ( has_entered_ ) { | ||
|
||
char etc_netns_path[PATH_MAX]; | ||
char netns_name[PATH_MAX]; | ||
char etc_name[PATH_MAX]; | ||
struct dirent *entry; | ||
DIR *dir; | ||
|
||
snprintf( etc_netns_path, sizeof( etc_netns_path ), "%s/%s", NETNS_ETC_DIR.c_str(), name_.c_str() ); | ||
dir = opendir( etc_netns_path ); | ||
if (!dir ) | ||
return; | ||
|
||
while ( ( entry = readdir(dir) ) != NULL ) { | ||
|
||
if ( strcmp(entry->d_name, ".") == 0 ) | ||
continue; | ||
if ( strcmp(entry->d_name, "..") == 0 ) | ||
continue; | ||
|
||
snprintf(netns_name, sizeof(netns_name), "%s/%s", etc_netns_path, entry->d_name); | ||
snprintf(etc_name, sizeof(etc_name), "/etc/%s", entry->d_name); | ||
|
||
if ( umount( etc_name ) < 0 ) { | ||
throw runtime_error( string("Unmounting... ") + netns_name + " -> " + etc_name + " failed: " + strerror(errno) + "\n" ); | ||
} | ||
} | ||
|
||
} | ||
|
||
if ( has_own_resolvconf_ ) { | ||
const string netns_dir = NETNS_ETC_DIR + "/" + name_; | ||
const string resolvconf_path = netns_dir + "/resolv.conf"; | ||
|
||
SystemCall("unlink", unlink ( resolvconf_path.c_str() ) ); | ||
SystemCall("unlink", rmdir ( netns_dir.c_str() ) ); | ||
} | ||
|
||
/*char netns_path[PATH_MAX]; | ||
snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_DIR.c_str(), name_.c_str()); | ||
umount2(netns_path, MNT_FORCE); | ||
if (unlink(netns_path) < 0) { | ||
throw runtime_error(string("Cannot remove namespace file manually \"")+netns_path+"\":" + strerror(errno) + "\n" ); | ||
}*/ | ||
|
||
|
||
run( { IP, "netns", "delete", name_ } ); | ||
} | ||
|
||
|
||
void NetworkNamespace::create_resolvconf( const std::string & nameserver ) | ||
{ | ||
has_own_resolvconf_ = true; | ||
/* create an individual resolv.conf for this network namespace */ | ||
const string netns_dir = NETNS_ETC_DIR + "/" + name_; | ||
const string resolvconf_path = netns_dir + "/resolv.conf"; | ||
|
||
SystemCall("mkdir", mkdir( netns_dir.c_str() , S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This fails for me on a default Ubuntu 16.10 installation (there's no NETNS_ETC_DIR, aka There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
http://man7.org/linux/man-pages/man8/ip-netns.8.html My bad. It isn't there on Ubuntu 16.04 per default as well. I guess i must have created the /etc/netns folder manually during working on an earlier prototype. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a tempfile is fine -- as I said on the other thread, I don't know of any "applications that are aware of network namespaces." glibc's resolver doesn't seem to know about this, and that's what most applications use. If we're going to use a bind mount (and a mount namespace) to replace resolv.conf inside the container, then I don't think this convention matters -- we could put it anywhere that can be reliably cleaned up at exit (normal or abnormal). |
||
|
||
FileDescriptor fd( SystemCall( "open", open( resolvconf_path.c_str(), | ||
O_WRONLY | O_CREAT | O_TRUNC, | ||
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) ) ) ; | ||
|
||
fd.write( "nameserver " + nameserver + "\n" ); | ||
} | ||
|
||
|
||
void NetworkNamespace::enter() | ||
{ | ||
char net_path[PATH_MAX]; | ||
int netns; | ||
|
||
snprintf( net_path, sizeof(net_path), "%s/%s", NETNS_DIR.c_str(), name_.c_str() ); | ||
netns = open( net_path, O_RDONLY | O_CLOEXEC ); | ||
|
||
if ( netns < 0 ) { | ||
throw runtime_error( "Cannot open network namespace \"" + name_ + "\": " + strerror(errno) + "\n " ); | ||
} | ||
|
||
if ( setns(netns, CLONE_NEWNET) < 0 ) { | ||
throw runtime_error( "setting the network namespace \"" + name_ + "\" failed: " + strerror(errno) + "\n " ); | ||
} | ||
close( netns ); | ||
|
||
has_entered_ = true; | ||
|
||
|
||
if ( unshare(CLONE_NEWNS) < 0 ) { | ||
throw runtime_error( string("unshare failed: ") + strerror(errno) + "\n " ); | ||
} | ||
|
||
/* Don't let any mounts propagate back to the parent */ | ||
/* | ||
if ( mount( "", "/", "none", MS_SLAVE | MS_REC, NULL ) ) { | ||
throw runtime_error( string("\"mount --make-rslave /\" failed: ") + strerror(errno) + "\n " ); | ||
}*/ | ||
|
||
/* Mount a version of /sys that describes the network namespace */ | ||
if ( umount2( "/sys", MNT_DETACH) < 0 ) { | ||
throw runtime_error( string("umount of /sys failed: ") + strerror(errno) + "\n" ); | ||
} | ||
|
||
if ( mount( name_.c_str(), "/sys", "sysfs", 0, NULL ) < 0 ) { | ||
throw runtime_error( string("mount of /sys failed: ") + strerror(errno) + "\n" ); | ||
} | ||
|
||
|
||
/* Setup bind mounts for config files in /etc */ | ||
char etc_netns_path[PATH_MAX]; | ||
char netns_name[PATH_MAX]; | ||
char etc_name[PATH_MAX]; | ||
struct dirent *entry; | ||
DIR *dir; | ||
|
||
snprintf( etc_netns_path, sizeof( etc_netns_path ), "%s/%s", NETNS_ETC_DIR.c_str(), name_.c_str() ); | ||
dir = opendir( etc_netns_path ); | ||
if (!dir ) | ||
return; | ||
|
||
while ( ( entry = readdir(dir) ) != NULL ) { | ||
|
||
if ( strcmp(entry->d_name, ".") == 0 ) | ||
continue; | ||
if ( strcmp(entry->d_name, "..") == 0 ) | ||
continue; | ||
|
||
snprintf(netns_name, sizeof(netns_name), "%s/%s", etc_netns_path, entry->d_name); | ||
snprintf(etc_name, sizeof(etc_name), "/etc/%s", entry->d_name); | ||
|
||
if ( mount( netns_name, etc_name, "none", MS_BIND, NULL ) < 0 ) { | ||
throw runtime_error( string("Bind ") + netns_name + " -> " + etc_name + " failed: " + strerror(errno) + "\n" ); | ||
} | ||
} | ||
|
||
closedir( dir ); | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||
|
||
#ifndef NETNAMESPACE_HH | ||
#define NETNAMESPACE_HH | ||
|
||
#include <string> | ||
|
||
class NetworkNamespace | ||
{ | ||
private: | ||
std::string name_; | ||
bool has_own_resolvconf_; | ||
bool has_entered_; | ||
|
||
public: | ||
|
||
const std::string NETNS_DIR = "/var/run/netns"; | ||
const std::string NETNS_ETC_DIR = "/etc/netns"; | ||
|
||
NetworkNamespace( const std::string & name ); | ||
~NetworkNamespace(); | ||
|
||
void create_resolvconf( const std::string & nameserver ); | ||
void enter( void ); | ||
}; | ||
|
||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line causes the compile to fail on g++ 6.2. (Can't throw from a destructor.)
network_namespace.cc: In destructor ‘NetworkNamespace::~NetworkNamespace()’: network_namespace.cc:55:135: error: throw will always call terminate() [-Werror=terminate] . ") + netns_name + " -> " + etc_name + " failed: " + strerror(errno) + "\n" ); ^ network_namespace.cc:55:135: note: in C++11 destructors default to noexcept