libvirt/src/qemu/qemu_passt.c

293 lines
8.3 KiB
C
Raw Normal View History

/*
* 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");
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;
}
/* a narrow range of QEMU releases support -netdev stream, but
* don't support its "reconnect" option
*/
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NETDEV_STREAM_RECONNECT) &&
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);
char macaddr[VIR_MAC_STRING_BUFLEN];
size_t i;
cmd = virCommandNew(PASST);
virCommandClearCaps(cmd);
virCommandAddArgList(cmd,
"--one-off",
"--socket", passtSocketName,
"--mac-addr", virMacAddrFormat(&net->mac, macaddr),
"--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;
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 %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);
if (pf->dev)
virBufferAsprintf(&buf, "%%%s", pf->dev);
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;
security: make it possible to set SELinux label of child process from its binary Normally when a child process is started by libvirt, the SELinux label of that process is set to virtd_t (plus an MCS range). In at least one case (passt) we need for the SELinux label of a child process label to match the label that the binary would have transitioned to automatically if it had been run standalone (in the case of passt, that label is passt_t). This patch modifies virSecuritySELinuxSetChildProcessLabel() (and all the functions above it in the call chain) so that the toplevel function can set a new argument "useBinarySpecificLabel" to true. If it is true, then virSecuritySELinuxSetChildProcessLabel() will call the new function virSecuritySELinuxContextSetFromFile(), which uses the selinux library function security_compute_create() to determine what would be the label of the new process if it had been run standalone (rather than being run by libvirt) - the MCS range from the normally-used label is added to this newly derived label, and that is what is used for the new process rather than whatever is in the domain's security label (which will usually be virtd_t). In order to easily verify that nothing was broken by these changes to the call chain, all callers currently set useBinarySpecificPath = false, so all behavior should be completely unchanged. (The next patch will set it to true only for the case of running passt.) https://bugzilla.redhat.com/2172267 Signed-off-by: Laine Stump <laine@redhat.com> Reviewed-by: Andrea Bolognani <abologna@redhat.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2023-03-01 15:34:32 -05:00
if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1, false, NULL) < 0)
goto error;
return 0;
error:
qemuPasstKill(pidfile, passtSocketName);
return -1;
}