#if defined(RPI) || defined(ANDROID) || NETNS == 0 #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef MS_REC #define MS_REC (16384) #endif #include namespace llarp { namespace GNULinux { static const char netns_rundir[] = "/var/run/netns"; static const char netns_etcdir[] = "/etc/netns"; static bool GetCGroups2MountPoint(fs::path& cgroups2_mount) { std::string mountpoint; std::ifstream inf; inf.open("/proc/mounts"); if(!inf.is_open()) { llarp::LogError("failed to open /proc/mounts"); return false; } std::string line; while(std::getline(inf, line)) { std::string part; std::stringstream parts; parts.str(line); // discard std::getline(parts, part); // mount point std::getline(parts, part); mountpoint = part; // type std::getline(parts, part); if(part == "cgroup2") { // found cgroup2 mountpoint cgroups2_mount = mountpoint; return true; } } llarp::LogError("cannot find cgroups2 in /proc/mounts"); return false; } static bool GetNetNS(std::string& netns) { auto nfd = open("/proc/self/ns/net", O_RDONLY); if(nfd < 0) { llarp::LogError( "Failed to get our own netns, could not open /proc/self/ns/net"); return false; } struct stat netst; if(::fstat(nfd, &netst) < 0) { close(nfd); llarp::LogError("stat of netns failed: ", strerror(errno)); return false; } close(nfd); fs::path run_dir = netns_rundir; bool foundIt = false; // find corrosponding file for netns llarp::util::IterDir(run_dir, [&](const fs::path& f) -> bool { struct stat fst; if(::stat(f.string().c_str(), &fst) >= 0) { if(fst.st_dev == netst.st_dev && fst.st_ino == netst.st_ino) { // found it foundIt = true; netns = f.filename().string(); // break iteration return false; } } // continue iteration return true; }); return foundIt; } static bool GetVRFPath(std::string& path) { char p[256] = {0}; snprintf(p, sizeof(p), "/proc/%d/cgroup", getpid()); std::ifstream inf; inf.open(p); if(!inf.is_open()) { llarp::LogError("could not open '", p, "': ", strerror(errno)); return false; } path = ""; std::string line; while(std::getline(inf, line)) { auto pos = line.find("::/"); if(pos != std::string::npos) { line = line.substr(pos + 2); pos = line.find("/vrf"); if(pos != std::string::npos) { path = line.substr(pos); if(path == "/") path = ""; } break; } } return true; } static bool ResetVRF() { fs::path cgroups2_mount; if(!GetCGroups2MountPoint(cgroups2_mount)) { llarp::LogError("could not find cgroup2 mount point, is it mounted?"); return false; } std::string netns; if(!GetNetNS(netns)) { llarp::LogError("could not get our netns: ", strerror(errno)); return false; } std::string vrfpath; if(!GetVRFPath(vrfpath)) { llarp::LogError("could not determine vrf cgroup path: ", strerror(errno)); return false; } fs::path cgroup_path = cgroups2_mount / vrfpath / netns / "vrf" / "default"; std::error_code ec; if(!fs::exists(cgroup_path, ec)) { if(!fs::create_directories(cgroup_path, ec)) { llarp::LogError("could not create '", cgroup_path.string(), "': ", ec); return false; } } else if(ec) { llarp::LogError("Could not check '", cgroup_path.string(), "': ", ec); return false; } cgroup_path /= "cgroup.procs"; auto fd = open(cgroup_path.string().c_str(), O_RDWR | O_APPEND); if(fd < 0) { llarp::LogError("could not open '", cgroup_path.string(), "': ", strerror(errno)); return false; } bool success = true; std::string pid = std::to_string(getpid()); if(write(fd, pid.c_str(), pid.size()) < 0) { llarp::LogError("failed to join cgroup"); success = false; } close(fd); return success; } /// bind network namespace paths into /etc/ static bool BindNetworkNS(const char* name) { fs::path etc_dir = netns_etcdir; etc_dir /= name; std::error_code ec; if(!fs::exists(etc_dir, ec)) { errno = 0; llarp::LogInfo(etc_dir, " does not exist, skipping"); return true; } bool didFail = false; llarp::util::IterDir(etc_dir, [&](const fs::path& f) -> bool { if(fs::is_regular_file(f)) { fs::path netns_path = "/etc"; netns_path /= f.filename(); if(mount(f.string().c_str(), netns_path.string().c_str(), "none", MS_BIND, nullptr) < 0) { llarp::LogError("failed to bind '", f.string(), "' to '", netns_path.string(), "': ", strerror(errno)); didFail = true; } } // continue iteration return true; }); return !didFail; } static void DropCap() { if(getuid() != 0 && geteuid() != 0) { cap_t capabilities; cap_value_t net_admin = CAP_NET_ADMIN; cap_flag_t inheritable = CAP_INHERITABLE; cap_flag_value_t is_set; capabilities = cap_get_proc(); if(!capabilities) exit(EXIT_FAILURE); if(cap_get_flag(capabilities, net_admin, inheritable, &is_set) != 0) exit(EXIT_FAILURE); if(is_set == CAP_CLEAR) { if(cap_clear(capabilities) != 0) exit(EXIT_FAILURE); if(cap_set_proc(capabilities) != 0) exit(EXIT_FAILURE); } cap_free(capabilities); } } bool NetNSSwitch(const char* name) { fs::path netns_path = netns_rundir; netns_path /= name; auto nsfd = open(netns_path.string().c_str(), O_RDONLY | O_CLOEXEC); if(nsfd < 0) { llarp::LogError("Failed to open network namespace '", name, "': ", strerror(errno)); return false; } if(setns(nsfd, CLONE_NEWNET) < 0) { llarp::LogError("Failed to enter network namespace '", name, "': ", strerror(errno)); close(nsfd); return false; } close(nsfd); if(unshare(CLONE_NEWNS) < 0) { llarp::LogError("unshare failed: ", strerror(errno)); return false; } // dont let any mount points prop back to parent // iproute2 source does this if(mount("", "/", "none", MS_SLAVE | MS_REC, nullptr)) { llarp::LogError("mount --make-rslave failed: ", strerror(errno)); return false; } unsigned long mountflags = 0; // ensaure /sys not mounted if(umount2("/sys", MNT_DETACH) < 0) { struct statvfs fsstat; if(statvfs("/sys", &fsstat) == 0) { if(fsstat.f_flag & ST_RDONLY) mountflags = MS_RDONLY; } } // mount sysfs for our namespace if(mount(name, "/sys", "sysfs", mountflags, nullptr) < 0) { llarp::LogError("failed to mount sysfs: ", strerror(errno)); return false; } if(!BindNetworkNS(name)) { llarp::LogError("failed to bind namespace directories"); return false; } if(!ResetVRF()) { llarp::LogError("failed to reset vrf"); return false; } DropCap(); return true; } } // namespace GNULinux } // namespace llarp #endif