diff --git a/src/frontend/replayshell.cc b/src/frontend/replayshell.cc index 33cb49bc..754ea74f 100644 --- a/src/frontend/replayshell.cc +++ b/src/frontend/replayshell.cc @@ -1,10 +1,9 @@ /* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -#include -#include - #include #include +#include +#include +#include #include "util.hh" #include "netdevice.hh" @@ -16,6 +15,7 @@ #include "http_response.hh" #include "dns_server.hh" #include "exception.hh" +#include "network_namespace.hh" #include "http_record.pb.h" @@ -61,22 +61,31 @@ int main( int argc, char *argv[] ) /* get working directory */ const string working_directory { get_working_directory() }; + /* chdir to result of getcwd just in case */ SystemCall( "chdir", chdir( working_directory.c_str() ) ); + const string netns_name = string("mahimahi.") + to_string( getpid() ); + + NetworkNamespace network_namespace; + + /* Setup our own resolvconf with nameserver 8.8.8.8 */ + network_namespace.create_resolvconf( "8.8.8.8" ); + + /* Switch to the newly created network namespace */ + network_namespace.enter(); + /* what command will we run inside the container? */ vector< string > command; if ( argc == 2 ) { command.push_back( shell_path() ); } else { + for ( int i = 2; i < argc; i++ ) { command.push_back( argv[ i ] ); } } - /* create a new network namespace */ - SystemCall( "unshare", unshare( CLONE_NEWNET ) ); - /* bring up localhost */ interface_ioctl( SIOCSIFFLAGS, "lo", [] ( ifreq &ifr ) { ifr.ifr_flags = IFF_UP; } ); @@ -143,7 +152,6 @@ int main( int argc, char *argv[] ) const string interface_name = "nameserver" + to_string( server_num ); add_dummy_interface( interface_name, nameservers.at( server_num ) ); } - /* start dnsmasq */ event_loop.add_child_process( start_dnsmasq( dnsmasq_args ) ); diff --git a/src/util/Makefile.am b/src/util/Makefile.am index ab48fde7..4e8a05e1 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -5,7 +5,8 @@ noinst_LIBRARIES = libutil.a libutil_a_SOURCES = exception.hh ezio.cc ezio.hh \ file_descriptor.hh file_descriptor.cc netdevice.cc netdevice.hh \ - timestamp.cc timestamp.hh \ + timestamp.cc timestamp.hh \ + network_namespace.cc network_namespace.hh \ child_process.hh child_process.cc signalfd.hh signalfd.cc \ socket.cc socket.hh address.cc address.hh \ system_runner.hh system_runner.cc nat.hh nat.cc \ diff --git a/src/util/network_namespace.cc b/src/util/network_namespace.cc new file mode 100644 index 00000000..e12d62a3 --- /dev/null +++ b/src/util/network_namespace.cc @@ -0,0 +1,92 @@ +/* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +#include "network_namespace.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "config.h" +#include "system_runner.hh" +#include "exception.hh" +#include "file_descriptor.hh" + +using namespace std; + +NetworkNamespace::NetworkNamespace( ) + : has_own_resolvconf_(false), resolvconf_file_(nullptr), has_entered_(false) +{ +} + + +NetworkNamespace::~NetworkNamespace() +{ + /* If we want to cleanup resolv.conf tempfile we need to make sure it is unmounted first */ + if ( has_entered_ && has_own_resolvconf_ ) { + + if ( umount( "/etc/resolv.conf" ) < 0 ) { + //TODO (worenga): proper RAII for mounts + std::cerr << string("Unmounting... ") << resolvconf_file_->name() << " -> " << "/etc/resolv.conf" << " failed: " << strerror(errno) << "\n" << std::endl; + } + } + + //TODO (worenga): How can we redo the (mount/bind) namespace in a RAII manner? + +} + + +void NetworkNamespace::create_resolvconf( const std::string & nameserver ) +{ + if ( has_entered_ ) { + throw runtime_error( string("Cannot create resolvconf after namespace has been entered.\n") ); + } + + resolvconf_file_.reset( std::move( new TempFile( "resolvconf" ) ) ); + has_own_resolvconf_ = true; + + char mode[] = "0644"; + if ( chmod (resolvconf_file_->name().c_str(), strtol(mode, 0, 8) ) < 0 ) { + throw runtime_error( string("chmod ") + mode + " for " + resolvconf_file_->name() +" failed: " + strerror(errno) + "\n " ); + } + + resolvconf_file_->write( "# Ephemeral resolv.conf(5) file for glibc resolver(3) generated by mahimahi\n" ); + resolvconf_file_->write( "# DO NOT EDIT THIS FILE BY HAND -- CHANGES WILL BE LOST\n" ); + resolvconf_file_->write( "nameserver " + nameserver + "\n" ); +} + + +void NetworkNamespace::enter() +{ + + 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 " ); + } + + if ( unshare(CLONE_NEWNET) < 0 ) { + throw runtime_error( string("unshare failed: ") + strerror(errno) + "\n " ); + } + + has_entered_ = true; + + if ( has_own_resolvconf_ ) { + + if ( mount( resolvconf_file_->name().c_str() , "/etc/resolv.conf" , "none", MS_BIND, NULL ) < 0 ) { + throw runtime_error( string("Bind ") + resolvconf_file_->name() + " -> " + "/etc/resolv.conf" + " failed: " + strerror(errno) + "\n" ); + } + + } + +} diff --git a/src/util/network_namespace.hh b/src/util/network_namespace.hh new file mode 100644 index 00000000..dedb652d --- /dev/null +++ b/src/util/network_namespace.hh @@ -0,0 +1,32 @@ +/* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef NETNAMESPACE_HH +#define NETNAMESPACE_HH + +#include +#include + +#include "temp_file.hh" + +class NetworkNamespace +{ + +private: + bool has_own_resolvconf_; + std::unique_ptr resolvconf_file_; + + bool has_entered_; + +public: + + const std::string NETNS_DIR = "/var/run/netns"; + const std::string NETNS_ETC_DIR = "/etc/netns"; + + NetworkNamespace(); + ~NetworkNamespace(); + + void create_resolvconf( const std::string & nameserver ); + void enter( void ); +}; + +#endif