From 0e99edc219361a31974aacdfcd3eb1e45bf7741f Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Mon, 30 Mar 2026 18:55:30 +0200 Subject: [PATCH] init: Set up a dummy network interface with TSI Some applications check for network availability by looking for a network device configured for Internet access. When TSI is used, there is no such device available by default, although Internet is accessible. Then those applications behave like when the connection is not available. Let's solve this problem by setting up a dummy network interface. The dummy interface is automatically created when CONFIG_DUMMY is enabled in kernel or the corresponding kernel module is loaded. This means a sufficiently recent libkrunfw version is needed (see https://github.com/containers/libkrunfw/pull/116). The dummy interface is initially down. In order to make the applications happy, the interface must be brought up and set up for Internet connections. This is ensured by setting the IP address to 10.0.0.1/8 (an arbitrary choice without any special reason) in init.c if TSI is enabled. The netmask is selected to be sane; it doesn't cover the whole IP range and we cannot set a default route because then TSI has problems, but it's OK for the tested application. We can change it if some application has trouble with that. TSI availability is determined by checking the presence of `tsi_hijack' in the kernel command line, before `--' delimiter if present. The dummy interface simply swallows all packets. But it is effectively bypassed by TSI for practical purposes. Things like ICMP don't work in either case. When the kernel support is not available, the device is not present and init.c cannot set it up. We skip the configuration silently in such a case, to not spam users with errors if they use older libkrunfw or custom kernels. Fixes: #576 Signed-off-by: Milan Zamazal --- init/init.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/init/init.c b/init/init.c index 4b205db69..59a5c3d94 100644 --- a/init/init.c +++ b/init/init.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -1285,6 +1286,109 @@ char *clone_str(const char *str) return strdup(str); } +#if __linux__ +static bool tsi_enabled() +{ + const char *const option = "tsi_hijack"; + bool enabled = false; + char *cmdline = NULL; + size_t cmdline_length = 0; + FILE *f; + const char *const delimiters = " \n"; + char *token; + + f = fopen("/proc/cmdline", "r"); + if (f == NULL) { + perror("fopen(/proc/cmdline)"); + return false; + } + + if (getline(&cmdline, &cmdline_length, f) < 0) { + perror("getline(/proc/cmdline)"); + fclose(f); + goto cleanup; + } + fclose(f); + + token = strtok(cmdline, delimiters); + while (token != NULL) { + if (strcmp(token, "--") == 0) { + break; + } + if (strcmp(token, option) == 0) { + enabled = true; + break; + } + token = strtok(NULL, delimiters); + } + +cleanup: + free(cmdline); + + return enabled; +} + +static int enable_dummy_interface() +{ + // See https://www.man7.org/linux/man-pages/man7/netdevice.7.html + + const char *const name = "dummy0"; + struct ifreq ifr; + int sockfd; + struct sockaddr_in *addr = (struct sockaddr_in *)&ifr.ifr_addr; + struct sockaddr_in *netmask = (struct sockaddr_in *)&ifr.ifr_netmask; + int result = -1; + + if (snprintf(ifr.ifr_name, IFNAMSIZ, "%s", name) >= IFNAMSIZ) { + printf("dummy interface name too long\n"); + return -1; + } + + sockfd = socket(PF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + perror("dummy interface socket"); + return -1; + } + + ifr.ifr_flags = IFF_UP; + if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { + if (errno == ENODEV) { + // Most likely not enabled in the kernel, ignore quietly + result = 0; + goto close_socket; + } + perror("dummy interface up"); + goto close_socket; + } + + addr->sin_family = AF_INET; + if (inet_pton(AF_INET, "10.0.0.1", &addr->sin_addr) <= 0) { + printf("inet_pton address conversion failed\n"); + goto close_socket; + } + if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) { + perror("dummy interface address"); + goto close_socket; + } + + netmask->sin_family = AF_INET; + if (inet_pton(AF_INET, "255.0.0.0", &netmask->sin_addr) <= 0) { + printf("inet_pton netmask conversion failed\n"); + goto close_socket; + } + if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) { + perror("dummy interface mask"); + goto close_socket; + } + + result = 0; + +close_socket: + close(sockfd); + return result; +} +#endif + int main(int argc, char **argv) { struct ifreq ifr; @@ -1435,6 +1539,14 @@ int main(int argc, char **argv) close(sockfd); } +#if __linux__ + if (tsi_enabled()) { + if (enable_dummy_interface() < 0) { + printf("Warning: Couldn't enable dummy interface\n"); + } + } +#endif + config_argv = NULL; config_workdir = NULL; config_tmpfs = NULL;