blob: b26e488f389eb4069a66e82633916c57cd25e79a [file] [log] [blame]
// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "patchpanel/system.h"
#include <fcntl.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <brillo/files/file_util.h>
#include "patchpanel/bpf/constants.h"
namespace patchpanel {
namespace {
// /proc/sys/ paths used for System::SysNetSet() and System::SysNetGet().
// Defines the local port range that is used by TCP and UDP traffic to choose
// the local port (IPv4 and IPv6).
constexpr char kSysNetIPLocalPortRangePath[] =
"/proc/sys/net/ipv4/ip_local_port_range";
// Enables/Disables IPv4 forwarding between interfaces.
constexpr char kSysNetIPv4ForwardingPath[] = "/proc/sys/net/ipv4/ip_forward";
// /proc/sys path for controlling connection tracking helper modules
constexpr char kSysNetConntrackHelperPath[] =
"/proc/sys/net/netfilter/nf_conntrack_helper";
// Enables/Disables IPv6.
constexpr char kSysNetDisableIPv6Path[] =
"/proc/sys/net/ipv6/conf/all/disable_ipv6";
// Allow localhost as a source or destination when routing IPv4.
constexpr char kSysNetIPv4RouteLocalnetPath[] =
"/proc/sys/net/ipv4/conf/%s/route_localnet";
// Enables/Disables IPv6 forwarding between interfaces.
constexpr char kSysNetIPv6ForwardingPath[] =
"/proc/sys/net/ipv6/conf/all/forwarding";
// Accept Router Advertisements on an interface and autoconfiguring it with IPv6
// parameters.
constexpr char kSysNetIPv6AcceptRaPath[] =
"/proc/sys/net/ipv6/conf/%s/accept_ra";
// Enables/Disables IPv6 cross-inteface NDP response.
constexpr char kSysNetIPv6ProxyNDPPath[] =
"/proc/sys/net/ipv6/conf/all/proxy_ndp";
constexpr char kSysNetIPv6HopLimitPath[] =
"/proc/sys/net/ipv6/conf/%s/hop_limit";
constexpr char kTunDev[] = "/dev/net/tun";
} // namespace
int System::Ioctl(int fd, ioctl_req_t request, const char* argp) {
return ioctl(fd, request, argp);
}
int System::Ioctl(int fd, ioctl_req_t request, uint64_t arg) {
return ioctl(fd, request, arg);
}
int System::Ioctl(int fd, ioctl_req_t request, struct ifreq* ifr) {
return ioctl(fd, request, ifr);
}
int System::Ioctl(int fd, ioctl_req_t request, struct rtentry* route) {
return ioctl(fd, request, route);
}
int System::Ioctl(int fd, ioctl_req_t request, struct in6_rtmsg* route) {
return ioctl(fd, request, route);
}
int System::SocketPair(int domain, int type, int protocol, int sv[2]) {
return socketpair(domain, type, protocol, sv);
}
pid_t System::WaitPid(pid_t pid, int* wstatus, int options) {
return waitpid(pid, wstatus, options);
}
base::ScopedFD System::OpenTunDev() {
return base::ScopedFD(open(kTunDev, O_RDWR | O_NONBLOCK));
}
bool System::SysNetSet(SysNet target,
std::string_view content,
std::string_view iface) {
const std::string path = SysNetPath(target, iface);
if (path.empty()) {
return false;
}
return Write(path, content);
}
std::string System::SysNetGet(SysNet target, std::string_view iface) const {
const base::FilePath path(SysNetPath(target, iface));
if (path.empty()) {
return "";
}
std::string content;
if (!base::ReadFileToString(path, &content)) {
LOG(ERROR) << "Failed to read the content from " << path;
return "";
}
// The content may contain '\n' character at the end. Remove it.
content =
std::string(base::TrimWhitespaceASCII(content, base::TRIM_TRAILING));
return content;
}
std::string System::SysNetPath(SysNet target, std::string_view iface) const {
switch (target) {
case SysNet::kIPv4Forward:
return kSysNetIPv4ForwardingPath;
case SysNet::kIPLocalPortRange:
return kSysNetIPLocalPortRangePath;
case SysNet::kIPv4RouteLocalnet:
if (iface.empty()) {
LOG(ERROR) << "IPv4LocalPortRange requires a valid interface";
return "";
}
return base::StringPrintf(kSysNetIPv4RouteLocalnetPath, iface.data());
case SysNet::kIPv6Forward:
return kSysNetIPv6ForwardingPath;
case SysNet::kIPv6AcceptRA:
if (iface.empty()) {
LOG(ERROR) << "IPv6AcceptRA requires a valid interface";
return "";
}
return base::StringPrintf(kSysNetIPv6AcceptRaPath, iface.data());
case SysNet::kConntrackHelper:
return kSysNetConntrackHelperPath;
case SysNet::kIPv6Disable:
return kSysNetDisableIPv6Path;
case SysNet::kIPv6ProxyNDP:
return kSysNetIPv6ProxyNDPPath;
case SysNet::kIPv6HopLimit:
if (iface.empty()) {
LOG(ERROR) << "IPv6HopLimit requires a valid interface";
return "";
}
return base::StringPrintf(kSysNetIPv6HopLimitPath, iface.data());
}
}
int System::IfNametoindex(const char* ifname) {
uint32_t ifindex = if_nametoindex(ifname);
if (ifindex > INT_MAX) {
errno = EINVAL;
return 0;
}
return static_cast<int>(ifindex);
}
int System::IfNametoindex(std::string_view ifname) {
return IfNametoindex(ifname.data());
}
char* System::IfIndextoname(int ifindex, char* ifname) {
if (ifindex < 0) {
errno = EINVAL;
return nullptr;
}
return if_indextoname(static_cast<uint32_t>(ifindex), ifname);
}
std::string System::IfIndextoname(int ifindex) {
char ifname[IFNAMSIZ] = {};
IfIndextoname(ifindex, ifname);
return ifname;
}
// static
bool System::Write(std::string_view path, std::string_view content) {
base::ScopedFD fd(open(path.data(), O_WRONLY | O_TRUNC | O_CLOEXEC));
if (!fd.is_valid()) {
PLOG(ERROR) << "Failed to open " << path;
return false;
}
if (write(fd.get(), content.data(), content.size()) != content.size()) {
PLOG(ERROR) << "Failed to write \"" << content << "\" to " << path;
return false;
}
return true;
}
bool System::WriteConfigFile(base::FilePath path, std::string_view contents) {
if (!base::WriteFile(path, contents)) {
PLOG(ERROR) << "Failed to write config file to " << path;
return false;
}
if (chmod(path.value().c_str(), S_IRUSR | S_IRGRP | S_IWUSR)) {
PLOG(ERROR) << "Failed to set permissions on " << path;
brillo::DeletePathRecursively(path);
return false;
}
if (chown(path.value().c_str(), kPatchpaneldUid, kPatchpaneldGid) != 0) {
PLOG(ERROR) << "Failed to change owner group of " << path;
brillo::DeletePathRecursively(path);
return false;
}
return true;
}
bool System::IsEbpfEnabled() const {
return base::PathExists(base::FilePath(kBPFMountPath));
}
} // namespace patchpanel