mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-06 21:15:22 +00:00
e4d058866e
The 'reconnect' field of 'stream' network backend type is about to be deprecated so libvirt will need to start using 'reconnect-ms'. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
301 lines
8.5 KiB
C
301 lines
8.5 KiB
C
/*
|
|
* qemu_passt.c: QEMU passt support
|
|
*
|
|
* Copyright (C) 2022 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "qemu_dbus.h"
|
|
#include "qemu_extdevice.h"
|
|
#include "qemu_security.h"
|
|
#include "qemu_passt.h"
|
|
#include "virenum.h"
|
|
#include "virerror.h"
|
|
#include "virjson.h"
|
|
#include "virlog.h"
|
|
#include "virpidfile.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
VIR_LOG_INIT("qemu.passt");
|
|
|
|
|
|
#define PASST "passt"
|
|
|
|
|
|
static char *
|
|
qemuPasstCreatePidFilename(virDomainObj *vm,
|
|
virDomainNetDef *net)
|
|
{
|
|
qemuDomainObjPrivate *priv = vm->privateData;
|
|
virQEMUDriver *driver = priv->driver;
|
|
g_autofree char *shortName = virDomainDefGetShortName(vm->def);
|
|
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
|
|
g_autofree char *name = NULL;
|
|
|
|
name = g_strdup_printf("%s-%s-passt", shortName, net->info.alias);
|
|
|
|
return virPidFileBuildPath(cfg->passtStateDir, name);
|
|
}
|
|
|
|
|
|
static char *
|
|
qemuPasstCreateSocketPath(virDomainObj *vm,
|
|
virDomainNetDef *net)
|
|
{
|
|
qemuDomainObjPrivate *priv = vm->privateData;
|
|
virQEMUDriver *driver = priv->driver;
|
|
g_autofree char *shortName = virDomainDefGetShortName(vm->def);
|
|
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
|
|
|
|
return g_strdup_printf("%s/%s-%s.socket", cfg->passtStateDir,
|
|
shortName, net->info.alias);
|
|
}
|
|
|
|
|
|
static int
|
|
qemuPasstGetPid(virDomainObj *vm,
|
|
virDomainNetDef *net,
|
|
pid_t *pid)
|
|
{
|
|
g_autofree char *pidfile = qemuPasstCreatePidFilename(vm, net);
|
|
|
|
return virPidFileReadPath(pidfile, pid);
|
|
}
|
|
|
|
|
|
int
|
|
qemuPasstAddNetProps(virDomainObj *vm,
|
|
virDomainNetDef *net,
|
|
virJSONValue **netprops)
|
|
{
|
|
g_autofree char *passtSocketName = qemuPasstCreateSocketPath(vm, net);
|
|
g_autoptr(virJSONValue) addrprops = NULL;
|
|
qemuDomainObjPrivate *priv = vm->privateData;
|
|
virQEMUCaps *qemuCaps = priv->qemuCaps;
|
|
|
|
if (virJSONValueObjectAdd(&addrprops,
|
|
"s:type", "unix",
|
|
"s:path", passtSocketName,
|
|
NULL) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (virJSONValueObjectAdd(netprops,
|
|
"s:type", "stream",
|
|
"a:addr", &addrprops,
|
|
"b:server", false,
|
|
NULL) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NETDEV_STREAM_RECONNECT)) {
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NETDEV_STREAM_RECONNECT_MILISECONDS)) {
|
|
if (virJSONValueObjectAdd(netprops, "u:reconnect-ms", 5000, NULL) < 0)
|
|
return -1;
|
|
} else {
|
|
if (virJSONValueObjectAdd(netprops, "u:reconnect", 5, NULL) < 0)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
qemuPasstKill(const char *pidfile, const char *passtSocketName)
|
|
{
|
|
virErrorPtr orig_err;
|
|
pid_t pid = 0;
|
|
|
|
virErrorPreserveLast(&orig_err);
|
|
|
|
ignore_value(virPidFileReadPath(pidfile, &pid));
|
|
if (pid != 0)
|
|
virProcessKillPainfully(pid, true);
|
|
unlink(pidfile);
|
|
|
|
unlink(passtSocketName);
|
|
|
|
virErrorRestore(&orig_err);
|
|
}
|
|
|
|
|
|
void
|
|
qemuPasstStop(virDomainObj *vm,
|
|
virDomainNetDef *net)
|
|
{
|
|
g_autofree char *pidfile = qemuPasstCreatePidFilename(vm, net);
|
|
g_autofree char *passtSocketName = qemuPasstCreateSocketPath(vm, net);
|
|
|
|
qemuPasstKill(pidfile, passtSocketName);
|
|
}
|
|
|
|
|
|
int
|
|
qemuPasstSetupCgroup(virDomainObj *vm,
|
|
virDomainNetDef *net,
|
|
virCgroup *cgroup)
|
|
{
|
|
pid_t pid = (pid_t) -1;
|
|
|
|
if (qemuPasstGetPid(vm, net, &pid) < 0 || pid <= 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not get process ID of passt"));
|
|
return -1;
|
|
}
|
|
|
|
return virCgroupAddProcess(cgroup, pid);
|
|
}
|
|
|
|
|
|
int
|
|
qemuPasstStart(virDomainObj *vm,
|
|
virDomainNetDef *net)
|
|
{
|
|
qemuDomainObjPrivate *priv = vm->privateData;
|
|
virQEMUDriver *driver = priv->driver;
|
|
g_autofree char *passtSocketName = qemuPasstCreateSocketPath(vm, net);
|
|
g_autoptr(virCommand) cmd = NULL;
|
|
g_autofree char *pidfile = qemuPasstCreatePidFilename(vm, net);
|
|
size_t i;
|
|
|
|
cmd = virCommandNew(PASST);
|
|
|
|
virCommandClearCaps(cmd);
|
|
|
|
virCommandAddArgList(cmd,
|
|
"--one-off",
|
|
"--socket", passtSocketName,
|
|
"--pid", pidfile,
|
|
NULL);
|
|
|
|
if (net->mtu) {
|
|
virCommandAddArg(cmd, "--mtu");
|
|
virCommandAddArgFormat(cmd, "%u", net->mtu);
|
|
}
|
|
|
|
if (net->sourceDev)
|
|
virCommandAddArgList(cmd, "--interface", net->sourceDev, NULL);
|
|
|
|
if (net->backend.logFile)
|
|
virCommandAddArgList(cmd, "--log-file", net->backend.logFile, NULL);
|
|
|
|
/* Add IP address info */
|
|
for (i = 0; i < net->guestIP.nips; i++) {
|
|
const virNetDevIPAddr *ip = net->guestIP.ips[i];
|
|
g_autofree char *addr = NULL;
|
|
|
|
/* validation has already limited us to
|
|
* a single IPv4 and single IPv6 address
|
|
*/
|
|
if (!(addr = virSocketAddrFormat(&ip->address)))
|
|
return -1;
|
|
|
|
virCommandAddArgList(cmd, "--address", addr, NULL);
|
|
|
|
if (ip->prefix && VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET)) {
|
|
/* validation already made sure no prefix is
|
|
* specified for IPv6 (not allowed by passt)
|
|
*/
|
|
virCommandAddArg(cmd, "--netmask");
|
|
virCommandAddArgFormat(cmd, "%u", ip->prefix);
|
|
}
|
|
}
|
|
|
|
/* Add port forwarding info */
|
|
|
|
for (i = 0; i < net->nPortForwards; i++) {
|
|
virDomainNetPortForward *pf = net->portForwards[i];
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
|
bool emitsep = false;
|
|
|
|
if (pf->proto == VIR_DOMAIN_NET_PROTO_TCP) {
|
|
virCommandAddArg(cmd, "--tcp-ports");
|
|
} else if (pf->proto == VIR_DOMAIN_NET_PROTO_UDP) {
|
|
virCommandAddArg(cmd, "--udp-ports");
|
|
} else {
|
|
/* validation guarantees this will never happen */
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Invalid portForward proto value %1$u"), pf->proto);
|
|
return -1;
|
|
}
|
|
|
|
if (VIR_SOCKET_ADDR_VALID(&pf->address)) {
|
|
g_autofree char *addr = NULL;
|
|
|
|
if (!(addr = virSocketAddrFormat(&pf->address)))
|
|
return -1;
|
|
|
|
virBufferAddStr(&buf, addr);
|
|
emitsep = true;
|
|
}
|
|
|
|
if (pf->dev) {
|
|
virBufferAsprintf(&buf, "%%%s", pf->dev);
|
|
emitsep = true;
|
|
}
|
|
|
|
if (emitsep)
|
|
virBufferAddChar(&buf, '/');
|
|
|
|
if (!pf->nRanges) {
|
|
virBufferAddLit(&buf, "all");
|
|
} else {
|
|
size_t r;
|
|
|
|
for (r = 0; r < pf->nRanges; r++) {
|
|
virDomainNetPortForwardRange *range = pf->ranges[r];
|
|
|
|
if (r > 0)
|
|
virBufferAddChar(&buf, ',');
|
|
|
|
if (range->exclude == VIR_TRISTATE_BOOL_YES)
|
|
virBufferAddChar(&buf, '~');
|
|
|
|
virBufferAsprintf(&buf, "%u", range->start);
|
|
|
|
if (range->end)
|
|
virBufferAsprintf(&buf, "-%u", range->end);
|
|
if (range->to) {
|
|
virBufferAsprintf(&buf, ":%u", range->to);
|
|
if (range->end) {
|
|
virBufferAsprintf(&buf, "-%u",
|
|
range->end + range->to - range->start);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
virCommandAddArg(cmd, virBufferCurrentContent(&buf));
|
|
}
|
|
|
|
|
|
if (qemuExtDeviceLogCommand(driver, vm, cmd, "passt") < 0)
|
|
return -1;
|
|
|
|
if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1, true, NULL) < 0)
|
|
goto error;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
qemuPasstKill(pidfile, passtSocketName);
|
|
return -1;
|
|
}
|