aboutgitcodebugslistschat
path: root/qrap.c
diff options
context:
space:
mode:
Diffstat (limited to 'qrap.c')
-rw-r--r--qrap.c429
1 files changed, 0 insertions, 429 deletions
diff --git a/qrap.c b/qrap.c
deleted file mode 100644
index d596706..0000000
--- a/qrap.c
+++ /dev/null
@@ -1,429 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/* PASST - Plug A Simple Socket Transport
- *
- * qrap.c - qemu wrapper connecting UNIX domain socket to file descriptor
- *
- * Copyright (c) 2020-2021 Red Hat GmbH
- * Author: Stefano Brivio <sbrivio@redhat.com>
- *
- * TODO: Drop this implementation once qemu commit 13c6be96618c ("net: stream:
- * add unix socket") is included in a release (7.2), and once we can reasonably
- * assume existing users switched to it.
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <errno.h>
-#include <linux/limits.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <net/if_arp.h>
-#include <netinet/in.h>
-#include <netinet/ip6.h>
-#include <netinet/if_ether.h>
-#include <time.h>
-
-#include <linux/icmpv6.h>
-
-#include "util.h"
-#include "ip.h"
-#include "passt.h"
-#include "arp.h"
-
-static char *qemu_names[] = {
- "kvm",
- "qemu-kvm",
-#ifdef ARCH
- ( "qemu-system-" ARCH ),
-#endif
- "/usr/libexec/qemu-kvm",
- NULL,
-};
-
-/**
- * struct drop_arg - Drop matching arguments on command line
- * @name: Option name
- * @val: Substring in option value, NULL matches any value
- */
-static const struct drop_arg {
- char *name;
- char *val;
-} drop_args[] = {
- { "-netdev", NULL },
- { "-net", NULL },
- { "-device", "virtio-net-pci," },
- { "-device", "{\"driver\":\"virtio-net-pci\"," },
- { "-device", "virtio-net-ccw," },
- { "-device", "{\"driver\":\"virtio-net-ccw\"," },
- { "-device", "e1000," },
- { "-device", "{\"driver\":\"e1000\"," },
- { "-device", "e1000e," },
- { "-device", "{\"driver\":\"e1000e\"," },
- { "-device", "rtl8139," },
- { "-device", "{\"driver\":\"rtl8139\"," },
- { 0 },
-};
-
-/**
- * struct pci_dev - PCI devices to add on command line depending on machine name
- * @mach: Machine name
- * @name: Device ("-device") name to insert
- * @template: Prefix for device specification (first part of address)
- * @template_post: Suffix for device specification (last part of address)
- * @template_json: Device prefix for when JSON is used
- * @template_json_post: Device suffix for when JSON is used
- * @base: Base used for PCI addresses
- * @first: First usable PCI address
- * @last: Last usable PCI address
- */
-static const struct pci_dev {
- char *mach;
- char *name;
- char *template;
- char *template_post;
- char *template_json;
- char *template_json_post;
- int base;
- int first;
- int last;
-} pci_devs[] = {
- {
- "pc-q35", "virtio-net-pci",
- "bus=pci.", ",addr=0x0",
- "\"bus\":\"pci.", ",\"addr\":\"0x0\"",
- 10, 3, /* 2: hotplug bus */ 31
- },
- {
- "pc-", "virtio-net-pci",
- "bus=pci.0,addr=0x", "",
- "\"bus\":\"pci.0\",\"addr\":\"0x", "",
- 16, 2, /* 1: ISA bridge */ 31
- },
- {
- "s390-ccw", "virtio-net-ccw",
- "devno=fe.0.", "",
- "\"devno\":\"fe.0.", "",
- 16, 1, 16
- },
- { 0 },
-};
-
-#define DEFAULT_FD 5
-
-/**
- * usage() - Print usage and exit
- * @name: Executable name
- */
-void usage(const char *name)
-{
- fprintf(stderr, "Usage: %s [FDNUM QEMU_CMD] [QEMU_ARG]...\n", name);
- fprintf(stderr, "\n");
- fprintf(stderr, "If first and second arguments aren't a socket number\n"
- "and a path, %s will try to locate a qemu binary\n"
- "and directly patch the command line\n", name);
-
- exit(EXIT_FAILURE);
-}
-
-/**
- * main() - Entry point and main loop
- * @argc: Argument count
- * @argv: File descriptor number, then qemu with arguments
- *
- * Return: 0 once interrupted, non-zero on failure
- */
-int main(int argc, char **argv)
-{
- int i, s, qemu_argc = 0, addr_map = 0, has_dev = 0, has_json = 0, retry_on_reset, rc;
- struct timeval tv = { .tv_sec = 0, .tv_usec = (long)(500 * 1000) };
- char *qemu_argv[ARG_MAX], dev_str[ARG_MAX];
- struct sockaddr_un addr = {
- .sun_family = AF_UNIX,
- };
- const struct pci_dev *dev = NULL;
- long fd;
- struct {
- uint32_t vnet_len4;
- struct ethhdr eh4;
- struct arphdr ah;
- struct arpmsg am;
-
- uint32_t vnet_len6;
- struct ethhdr eh6;
- struct ipv6hdr ip6hr;
- struct icmp6hdr ihr;
- struct in6_addr target;
- } __attribute__((__packed__)) probe = {
- .vnet_len4 = htonl(42),
- {
- .h_dest = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
- .h_source = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
- .h_proto = htons(ETH_P_ARP),
- },
- { .ar_hrd = htons(ARPHRD_ETHER),
- .ar_pro = htons(ETH_P_IP),
- .ar_hln = ETH_ALEN,
- .ar_pln = 4,
- .ar_op = htons(ARPOP_REQUEST),
- },
- {
- .sha = { 0 }, .sip = { 0 }, .tha = { 0 }, .tip = { 0 },
- },
- .vnet_len6 = htonl(78),
- {
- .h_dest = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
- .h_source = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
- .h_proto = htons(ETH_P_IPV6),
- },
- {
- .version = 6,
- .payload_len = htons(24),
- .nexthdr = IPPROTO_ICMPV6,
- .hop_limit = 255,
- .saddr = IN6ADDR_LOOPBACK_INIT,
- .daddr = IN6ADDR_ANY_INIT,
- },
- {
- .icmp6_type = 135,
- .icmp6_code = 0,
- },
- IN6ADDR_ANY_INIT,
- };
- char probe_r;
-
- if (argc >= 3) {
- const char *path = getenv("PATH");
- errno = 0;
- fd = strtol(argv[1], NULL, 0);
- if (fd >= 3 && fd < INT_MAX && !errno && path) {
- char env_path[ARG_MAX + 1], *p, command[ARG_MAX];
-
- strncpy(env_path, path, ARG_MAX);
- /* cppcheck-suppress strtokCalled */
- p = strtok(env_path, ":");
- while (p) {
- snprintf(command, ARG_MAX, "%s/%s", p, argv[2]);
- if (!access(command, X_OK))
- goto valid_args;
-
- /* cppcheck-suppress strtokCalled */
- p = strtok(NULL, ":");
- }
- }
- }
-
- fd = DEFAULT_FD;
-
- for (i = 1; i < argc - 1; i++) {
- if (strcmp(argv[i], "-machine"))
- continue;
-
- for (dev = pci_devs; dev->mach; dev++) {
- if (strstr(argv[i + 1], dev->mach) == argv[i + 1])
- break;
- }
- }
-
- if (!dev || !dev->mach)
- dev = pci_devs;
-
- for (qemu_argc = 1, i = 1; i < argc; i++) {
- const struct drop_arg *a;
-
- for (a = drop_args; a->name; a++) {
- if (!strcmp(argv[i], a->name)) {
- if (!a->val)
- break;
-
- if (i + 1 < argc &&
- strstr(argv[i + 1], a->val) == argv[i + 1])
- break;
- }
- }
- if (a->name) {
- i++;
- continue;
- }
-
- if (!strcmp(argv[i], "-device") && i + 1 < argc) {
- const char *template = NULL;
- const char *p;
-
- has_dev = 1;
-
- if ((p = strstr(argv[i + 1], dev->template))) {
- template = dev->template;
- } else if ((p = strstr(argv[i + 1], dev->template_json))) {
- template = dev->template_json;
- has_json = 1;
- }
-
- if (template) {
- long n;
-
- n = strtol(p + strlen(template), NULL, dev->base);
- if (!errno)
- addr_map |= (1 << n);
- }
- }
-
- qemu_argv[qemu_argc++] = argv[i];
- }
-
- for (i = dev->first; i < dev->last; i++) {
- if (!(addr_map & (1 << i)))
- break;
- }
- if (i == dev->last) {
- fprintf(stderr, "Couldn't find free address for device\n");
- usage(argv[0]);
- }
-
- if (has_dev) {
- qemu_argv[qemu_argc++] = "-device";
- if (!has_json) {
- if (dev->base == 16) {
- snprintf(dev_str, ARG_MAX,
- "%s,%s%x%s,netdev=hostnet0,x-txburst=4096",
- dev->name, dev->template, i, dev->template_post);
- } else if (dev->base == 10) {
- snprintf(dev_str, ARG_MAX,
- "%s,%s%d%s,netdev=hostnet0,x-txburst=4096",
- dev->name, dev->template, i, dev->template_post);
- }
- } else {
- if (dev->base == 16) {
- snprintf(dev_str, ARG_MAX,
- "{\"driver\":\"%s\",%s%x\"%s,\"netdev\":\"hostnet0\",\"x-txburst\":4096}",
- dev->name, dev->template_json, i, dev->template_json_post);
- } else if (dev->base == 10) {
- snprintf(dev_str, ARG_MAX,
- "{\"driver\":\"%s\",%s%d\"%s,\"netdev\":\"hostnet0\",\"x-txburst\":4096}",
- dev->name, dev->template_json, i, dev->template_json_post);
- }
- }
- qemu_argv[qemu_argc++] = dev_str;
- }
-
- qemu_argv[qemu_argc++] = "-netdev";
- if (!has_json) {
- qemu_argv[qemu_argc++] = "socket,fd=" STR(DEFAULT_FD) ",id=hostnet0";
- } else {
- qemu_argv[qemu_argc++] = "{\"type\":\"socket\",\"fd\":\"" STR(DEFAULT_FD) "\",\"id\":\"hostnet0\"}";
- }
- qemu_argv[qemu_argc] = NULL;
-
-valid_args:
- for (i = 1; i < UNIX_SOCK_MAX; i++) {
- retry_on_reset = 50;
-
-retry:
- s = socket(AF_UNIX, SOCK_STREAM, 0);
- if (s < 0) {
- perror("socket");
- exit(EXIT_FAILURE);
- }
-
- if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
- perror("setsockopt SO_RCVTIMEO");
- if (setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)))
- perror("setsockopt SO_SNDTIMEO");
-
- snprintf(addr.sun_path, UNIX_PATH_MAX, UNIX_SOCK_PATH, i);
-
- errno = 0;
-
- if (connect(s, (const struct sockaddr *)&addr, sizeof(addr))) {
- rc = errno;
- perror("connect");
- } else if (send(s, &probe, sizeof(probe), 0) != sizeof(probe)) {
- rc = errno;
- perror("send");
- } else if (recv(s, &probe_r, 1, MSG_PEEK) <= 0) {
- rc = errno;
- perror("recv");
- } else {
- break;
- }
-
- /* FIXME: in a KubeVirt environment, libvirtd invokes qrap three
- * times in a strict sequence when a virtual machine needs to
- * be started, namely, when:
- * - the domain XML is saved
- * - the domain is started (for "probing")
- * - the virtual machine is started for real
- * and it often happens that the qemu process is still running
- * when qrap is invoked again, so passt will refuse the new
- * connection because the previous one is still active. This
- * overlap seems to be anywhere between 0 and 3ms.
- *
- * If we get a connection reset, retry a few times, to allow for
- * the previous qemu instance to terminate and, in turn, for the
- * connection to passt to be closed.
- *
- * This should be fixed in libvirt instead. It probably makes
- * sense to check this behaviour once native libvirt support is
- * there, and this implies native qemu support too, so at that
- * point qrap will have no reason to exist anymore -- that is,
- * this FIXME will probably remain until the tool itself is
- * obsoleted.
- */
- if (retry_on_reset && rc == ECONNRESET) {
- retry_on_reset--;
- /* cppcheck-suppress usleepCalled */
- usleep(50 * 1000);
- goto retry;
- }
-
- fprintf(stderr, "Probe of %s failed\n", addr.sun_path);
-
- close(s);
- }
-
- if (i == UNIX_SOCK_MAX) {
- perror("connect");
- exit(EXIT_FAILURE);
- }
-
- tv.tv_usec = 0;
- if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
- perror("setsockopt, SO_RCVTIMEO reset");
- if (setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)))
- perror("setsockopt, SO_SNDTIMEO reset");
-
- fprintf(stderr, "Connected to %s\n", addr.sun_path);
-
- if (dup2(s, (int)fd) < 0) {
- perror("dup");
- exit(EXIT_FAILURE);
- }
-
- close(s);
-
- if (qemu_argc) {
- char **name;
-
- for (name = qemu_names; *name; name++) {
- qemu_argv[0] = *name;
- execvp(*name, qemu_argv);
- if (errno != ENOENT) {
- perror("execvp");
- usage(argv[0]);
- }
- }
- if (errno == ENOENT)
- fprintf(stderr, "Couldn't find qemu command\n");
- } else {
- execvp(argv[2], argv + 2);
- }
-
- perror("execvp");
-
- return EXIT_FAILURE;
-}