Files
doos/src/network.cpp
T

135 lines
4.6 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 == "pasta") return NetMode::Pasta;
if (s == "host") return NetMode::Host;
if (s == "none") return NetMode::None;
throw std::runtime_error("unknown network mode '" + s + "' (use slirp, pasta, host or none)");
}
std::string Network::name(NetMode m) {
switch (m) {
case NetMode::Slirp: return "slirp";
case NetMode::Pasta: return "pasta";
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::Pasta:
case NetMode::Host: {
// pasta forwards the namespace's loopback to the host and host mode shares it, so the host resolver
// config is valid as is, including the 127.0.0.53 stub.
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
namespace doos {
void PastaSession::start(pid_t targetPid) {
pid_ = ::fork();
if (pid_ < 0) throw SysError("fork pasta");
if (pid_ == 0) {
::prctl(PR_SET_PDEATHSIG, SIGKILL);
int nul = ::open("/dev/null", O_WRONLY);
if (nul >= 0) { ::dup2(nul, 1); ::dup2(nul, 2); }
std::string target = std::to_string(targetPid);
::execlp("pasta", "pasta", "--foreground", "--quiet", "--config-net", target.c_str(), static_cast<char*>(nullptr));
::_exit(127);
}
// pasta has no ready fd: wait until the target namespace has an interface besides lo.
std::string dev = "/proc/" + std::to_string(targetPid) + "/net/dev";
for (int waited = 0; waited < spec::kNetReadyTimeoutMs; waited += 20) {
int st = 0;
if (::waitpid(pid_, &st, WNOHANG) == pid_) {
pid_ = -1;
throw std::runtime_error("pasta exited early (is it installed and permitted?)");
}
std::ifstream in(dev);
int lines = 0;
for (std::string line; std::getline(in, line);) ++lines;
if (lines > 3) return; // two header lines + lo + at least one more interface
::poll(nullptr, 0, 20);
}
stop();
throw std::runtime_error("pasta not ready in time");
}
void PastaSession::stop() {
if (pid_ <= 0) return;
// pasta drops to another uid after start, so we cannot signal it, and it leaves by itself shortly after its
// namespace is gone. Reap it if it is already done; otherwise let init adopt it instead of blocking the exit.
for (int waited = 0; waited < 20; waited += 5) {
if (::waitpid(pid_, nullptr, WNOHANG) == pid_) break;
::poll(nullptr, 0, 5);
}
pid_ = -1;
}
} // namespace doos