ClatdController - create tun file descriptor and pass it to clatd
Test: in progress
Bug: 65674744
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: I77d4636fb7aca46f4964208a6c44c46dd7eabb48
diff --git a/server/ClatdController.cpp b/server/ClatdController.cpp
index 77eaf5a..551b947 100644
--- a/server/ClatdController.cpp
+++ b/server/ClatdController.cpp
@@ -20,6 +20,8 @@
#include <arpa/inet.h>
#include <errno.h>
#include <linux/if_arp.h>
+#include <linux/if_tun.h>
+#include <linux/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <spawn.h>
@@ -33,6 +35,7 @@
#include "ClatdController.h"
#include "android-base/properties.h"
+#include "android-base/scopeguard.h"
#include "android-base/unique_fd.h"
#include "bpf/BpfMap.h"
#include "netdbpf/bpf_shared.h"
@@ -353,8 +356,10 @@
snprintf(fwmarkString, sizeof(fwmarkString), "0x%x", fwmark.intValue);
snprintf(netIdString, sizeof(netIdString), "%u", netId);
- ifIndex = if_nametoindex(interface.c_str());
strlcpy(iface, interface.c_str(), sizeof(iface));
+ ifIndex = if_nametoindex(iface);
+ snprintf(v4iface, sizeof(v4iface), "v4-%s", iface);
+ v4ifIndex = if_nametoindex(v4iface);
// Pass in everything that clatd needs: interface, a netid to use for DNS lookups, a fwmark for
// outgoing packets, the NAT64 prefix, and the IPv4 and IPv6 addresses.
@@ -394,46 +399,115 @@
int ClatdController::startClatd(const std::string& interface, const std::string& nat64Prefix,
std::string* v6Str) {
std::lock_guard guard(mutex);
+
+ // 1. fail if pre-existing tracker already exists
ClatdTracker* existing = getClatdTracker(interface);
if (existing != nullptr) {
ALOGE("clatd pid=%d already started on %s", existing->pid, interface.c_str());
- errno = EBUSY;
- return -errno;
+ return -EBUSY;
}
+ // 2. get network id associated with this external interface
unsigned networkId = mNetCtrl->getNetworkForInterface(interface.c_str());
if (networkId == NETID_UNSET) {
ALOGE("Interface %s not assigned to any netId", interface.c_str());
- errno = ENODEV;
- return -errno;
+ return -ENODEV;
}
+ // 3. open the tun device in non blocking mode as required by clatd
+ int res = open("/dev/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC);
+ if (res == -1) {
+ res = errno;
+ ALOGE("open of tun device failed (%s)", strerror(res));
+ return -res;
+ }
+ unique_fd tmpTunFd(res);
+
+ // 4. create the v4-... tun interface
+ struct ifreq ifr = {
+ .ifr_flags = IFF_TUN,
+ };
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "v4-%s", interface.c_str());
+
+ res = ioctl(tmpTunFd, TUNSETIFF, &ifr, sizeof(ifr));
+ if (res == -1) {
+ res = errno;
+ ALOGE("ioctl(TUNSETIFF) failed (%s)", strerror(res));
+ return -res;
+ }
+
+ // 5. initialize tracker object
ClatdTracker tracker;
int ret = tracker.init(networkId, interface, nat64Prefix);
if (ret) return ret;
+ // 6. create a throwaway socket to reserve a file descriptor number
+ res = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (res == -1) {
+ res = errno;
+ ALOGE("socket(ipv6/udp) failed (%s)", strerror(res));
+ return -res;
+ }
+ unique_fd passedTunFd(res);
+
+ // 7. this is the FD we'll pass to clatd on the cli, so need it as a string
+ char passedTunFdStr[INT32_STRLEN];
+ snprintf(passedTunFdStr, sizeof(passedTunFdStr), "%d", passedTunFd.get());
+
+ // 8. we're going to use this as argv[0] to clatd to make ps output more useful
std::string progname("clatd-");
progname += tracker.iface;
// clang-format off
- char* args[] = {(char*) progname.c_str(),
- (char*) "-i", tracker.iface,
- (char*) "-n", tracker.netIdString,
- (char*) "-m", tracker.fwmarkString,
- (char*) "-p", tracker.pfx96String,
- (char*) "-4", tracker.v4Str,
- (char*) "-6", tracker.v6Str,
- nullptr};
+ const char* args[] = {progname.c_str(),
+ "-i", tracker.iface,
+ "-n", tracker.netIdString,
+ "-m", tracker.fwmarkString,
+ "-p", tracker.pfx96String,
+ "-4", tracker.v4Str,
+ "-6", tracker.v6Str,
+ "-t", passedTunFdStr,
+ nullptr};
// clang-format on
- // Specify no flags and no actions, posix_spawn will use vfork and is
- // guaranteed to return only once exec has been called.
- int res = posix_spawn(&tracker.pid, kClatdPath, nullptr, nullptr, args, nullptr);
+ // 9. register vfork requirement
+ posix_spawnattr_t attr;
+ res = posix_spawnattr_init(&attr);
+ if (res) {
+ ALOGE("posix_spawnattr_init failed (%s)", strerror(res));
+ return -res;
+ }
+ const android::base::ScopeGuard attrGuard = [&] { posix_spawnattr_destroy(&attr); };
+ res = posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK);
+ if (res) {
+ ALOGE("posix_spawnattr_setflags failed (%s)", strerror(res));
+ return -res;
+ }
+
+ // 10. register dup2() action: this is what 'clears' the CLOEXEC flag
+ // on the tun fd that we want the child clatd process to inherit
+ // (this will happen after the vfork, and before the execve)
+ posix_spawn_file_actions_t fa;
+ res = posix_spawn_file_actions_init(&fa);
+ if (res) {
+ ALOGE("posix_spawn_file_actions_init failed (%s)", strerror(res));
+ return -res;
+ }
+ const android::base::ScopeGuard faGuard = [&] { posix_spawn_file_actions_destroy(&fa); };
+ res = posix_spawn_file_actions_adddup2(&fa, tmpTunFd, passedTunFd);
+ if (res) {
+ ALOGE("posix_spawn_file_actions_adddup2 failed (%s)", strerror(res));
+ return -res;
+ }
+
+ // 11. actually perform vfork/dup2/execve
+ res = posix_spawn(&tracker.pid, kClatdPath, &fa, &attr, (char* const*)args, nullptr);
if (res) {
ALOGE("posix_spawn failed (%s)", strerror(res));
return -res;
}
+ // 12. configure eBPF offload - if possible
maybeStartBpf(tracker);
mClatdTrackers[interface] = tracker;
@@ -473,13 +547,14 @@
{
ScopedIndent trackerIndent(dw);
- dw.println("Trackers: iif[iface] nat64Prefix v6Addr -> v4Addr [netId]");
+ dw.println("Trackers: iif[iface] nat64Prefix v6Addr -> v4Addr v4iif[v4iface] [netId]");
ScopedIndent trackerDetailIndent(dw);
for (const auto& pair : mClatdTrackers) {
const ClatdTracker& tracker = pair.second;
- dw.println("%u[%s] %s/96 %s -> %s [%u]", tracker.ifIndex, tracker.iface,
- tracker.pfx96String, tracker.v6Str, tracker.v4Str, tracker.netId);
+ dw.println("%u[%s] %s/96 %s -> %s %u[%s] [%u]", tracker.ifIndex, tracker.iface,
+ tracker.pfx96String, tracker.v6Str, tracker.v4Str, tracker.v4ifIndex,
+ tracker.v4iface, tracker.netId);
}
}