87 lines
2.7 KiB
C++
87 lines
2.7 KiB
C++
#include "network.hpp"
|
|||
|
|
|
||
|
|
#include <fcntl.h>
|
||
|
|
#include <poll.h>
|
||
|
|
#include <sys/prctl.h>
|
||
|
|
#include <signal.h>
|
||
|
|
#include <sys/wait.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
|
||
|
|
#include <cstring>
|
||
|
|
#include <fstream>
|
||
|
|
#include <stdexcept>
|
||
|
|
|
||
|
|
#include "spec.hpp"
|
||
|
|
#include "util.hpp"
|
||
|
|
|
||
|
|
namespace doos {
|
||
|
|
|
||
|
|
NetMode Network::parse(const std::string& s) {
|
||
|
|
if (s == "slirp") return NetMode::Slirp;
|
||
|
|
if (s == "host") return NetMode::Host;
|
||
|
|
if (s == "none") return NetMode::None;
|
||
|
|
throw std::runtime_error("unknown network mode '" + s + "' (use slirp, host or none)");
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string Network::name(NetMode m) {
|
||
|
|
switch (m) {
|
||
|
|
case NetMode::Slirp: return "slirp";
|
||
|
|
case NetMode::Host: return "host";
|
||
|
|
case NetMode::None: return "none";
|
||
|
|
}
|
||
|
|
return "?";
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string Network::resolvConf(NetMode m) {
|
||
|
|
switch (m) {
|
||
|
|
case NetMode::Slirp: return "nameserver " + std::string(spec::kSlirpDns) + "\n";
|
||
|
|
case NetMode::None: return "";
|
||
|
|
case NetMode::Host: {
|
||
|
|
// Host netns is shared, so the host resolver config is valid. Stub on 127.0.0.53 works too.
|
||
|
|
std::ifstream in("/etc/resolv.conf");
|
||
|
|
return std::string(std::istreambuf_iterator<char>(in), {});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
|
||
|
|
void SlirpSession::start(pid_t targetPid) {
|
||
|
|
int ready[2];
|
||
|
|
if (::pipe2(ready, O_CLOEXEC) != 0) throw SysError("pipe");
|
||
|
|
Fd readEnd(ready[0]), writeEnd(ready[1]);
|
||
|
|
|
||
|
|
pid_ = ::fork();
|
||
|
|
if (pid_ < 0) throw SysError("fork slirp4netns");
|
||
|
|
if (pid_ == 0) {
|
||
|
|
::prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||
|
|
int nul = ::open("/dev/null", O_WRONLY);
|
||
|
|
if (nul >= 0) { ::dup2(nul, 1); ::dup2(nul, 2); }
|
||
|
|
int wfd = ::dup(writeEnd.get()); // dup clears CLOEXEC
|
||
|
|
std::string readyArg = "--ready-fd=" + std::to_string(wfd);
|
||
|
|
std::string target = std::to_string(targetPid);
|
||
|
|
std::string bin(spec::kSlirpBinary), tap(spec::kSlirpTapName);
|
||
|
|
::execlp(bin.c_str(), bin.c_str(), "--configure", "--mtu=65520", "--disable-host-loopback",
|
||
|
|
readyArg.c_str(), target.c_str(), tap.c_str(), static_cast<char*>(nullptr));
|
||
|
|
::dprintf(2, "doos: cannot exec %s: %s\n", bin.c_str(), std::strerror(errno));
|
||
|
|
::_exit(127);
|
||
|
|
}
|
||
|
|
writeEnd.reset();
|
||
|
|
|
||
|
|
pollfd p{readEnd.get(), POLLIN, 0};
|
||
|
|
int rc = ::poll(&p, 1, spec::kNetReadyTimeoutMs);
|
||
|
|
char c = 0;
|
||
|
|
if (rc > 0 && ::read(readEnd.get(), &c, 1) == 1) return;
|
||
|
|
stop();
|
||
|
|
throw std::runtime_error(rc == 0 ? "slirp4netns not ready in time"
|
||
|
|
: "slirp4netns failed to start (is it installed and permitted?)");
|
||
|
|
}
|
||
|
|
|
||
|
|
void SlirpSession::stop() {
|
||
|
|
if (pid_ <= 0) return;
|
||
|
|
::kill(pid_, SIGTERM);
|
||
|
|
::waitpid(pid_, nullptr, 0);
|
||
|
|
pid_ = -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace doos
|