Implement port forwarding for user mode networking with QEMU. --- src/libvirt_private.syms | 3 + src/qemu/qemu_command.c | 115 +++++++++++++++++++++ tests/qemuargv2xmltest.c | 3 +- tests/qemuxml2argvdata/qemuxml2argv-net-user.args | 5 +- 4 files changed, 122 insertions(+), 4 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index afb308d..333284a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -396,6 +396,9 @@ virDomainNetInsert; virDomainNetRemove; virDomainNetRemoveByMac; virDomainNetTypeToString; +virDomainNetForwardDefFree; +virDomainNetForwardTypeFromString; +virDomainNetForwardTypeToString; virDomainNostateReasonTypeFromString; virDomainNostateReasonTypeToString; virDomainNumatuneMemModeTypeFromString; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 720f5b4..82c041c 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2837,6 +2837,22 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, virBufferAsprintf(&buf, ",sndbuf=%lu", net->tune.sndbuf); } + if (netType == VIR_DOMAIN_NET_TYPE_USER) { + int i; + for (i = 0; i < net->data.user.nforward; ++i) { + virDomainNetForwardDefPtr fwd = net->data.user.forwards[i]; + const char *type = virDomainNetForwardTypeToString(fwd->type); + if (!type) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid forward type")); + return NULL; + } + virBufferAsprintf(&buf, ",hostfwd=%s:%s:%d-%s:%d", type, + fwd->host_addr ? fwd->host_addr : "", fwd->host_port, + fwd->guest_addr ? fwd->guest_addr : "", fwd->guest_port); + } + } + if (virBufferError(&buf)) { virBufferFreeAndReset(&buf); virReportOOMError(); @@ -6704,6 +6720,82 @@ qemuFindNICForVLAN(int nnics, } +static virDomainNetForwardDefPtr +qemuParseNetForward(char *val) +{ + char *type, *host, *guest, *port; + virDomainNetForwardDefPtr def; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + return NULL; + } + + type = val; + host = strchr(type, ':'); + if (!host) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse hostfwd host '%s'"), val); + goto error; + } + *host = '\0'; + host++; + guest = strchr(host, '-'); + if (!guest) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse hostfwd guest '%s'"), val); + goto error; + } + *guest = '\0'; + guest++; + + if (STREQ(type, "")) { + def->type = VIR_DOMAIN_NET_FORWARD_TYPE_TCP; + } else if ((def->type = virDomainNetForwardTypeFromString(type)) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown forward type '%s'"), type); + goto error; + } + + port = strchr(host, ':'); + if (!port) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing host port")); + goto error; + } + *port = '\0'; + port++; + if (!STREQ(host, "")) + def->host_addr = strdup(host); + if (virStrToLong_i(port, NULL, 10, &def->host_port) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot parse host port")); + goto error; + } + + port = strchr(guest, ':'); + if (!port) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing guest port")); + goto error; + } + *port = '\0'; + port++; + if (!STREQ(guest, "")) + def->guest_addr = strdup(guest); + if (virStrToLong_i(port, NULL, 10, &def->guest_port) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot parse guest port")); + goto error; + } + + return def; + +error: + virDomainNetForwardDefFree(def); + return NULL; +} + /* * Tries to parse a QEMU -net backend argument. Gets given * a list of all known -net frontend arguments to try and @@ -6723,6 +6815,7 @@ qemuParseCommandLineNet(virCapsPtr caps, int wantvlan = 0; const char *tmp; int genmac = 1; + int nforward = 0; int i; tmp = strchr(val, ','); @@ -6769,9 +6862,31 @@ qemuParseCommandLineNet(virCapsPtr caps, STREQ(keywords[i], "ifname")) { def->ifname = values[i]; values[i] = NULL; + } else if (def->type == VIR_DOMAIN_NET_TYPE_USER && + STREQ(keywords[i], "hostfwd")) { + nforward++; } } + if (nforward > 0) { + if (VIR_ALLOC_N(def->data.user.forwards, nforward) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0 ; i < nkeywords ; i++) { + virDomainNetForwardDefPtr fwd; + if (def->type != VIR_DOMAIN_NET_TYPE_USER || + !STREQ(keywords[i], "hostfwd")) + continue; + + if ((fwd = qemuParseNetForward(values[i])) == NULL) + goto cleanup; + + def->data.user.forwards[def->data.user.nforward] = fwd; + def->data.user.nforward++; + } + } /* Done parsing the nic backend. Now to try and find corresponding * frontend, based off vlan number. NB this assumes a 1-1 mapping diff --git a/tests/qemuargv2xmltest.c b/tests/qemuargv2xmltest.c index cf2862b..439218e 100644 --- a/tests/qemuargv2xmltest.c +++ b/tests/qemuargv2xmltest.c @@ -207,8 +207,7 @@ mymain(void) DO_TEST("misc-acpi"); DO_TEST("misc-no-reboot"); DO_TEST("misc-uuid"); - /* Fixed in following commit */ - /* DO_TEST("net-user"); */ + DO_TEST("net-user"); DO_TEST("net-virtio"); DO_TEST("net-eth"); DO_TEST("net-eth-ifname"); diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-user.args b/tests/qemuxml2argvdata/qemuxml2argv-net-user.args index 093ff01..db31e95 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-net-user.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-user.args @@ -1,5 +1,6 @@ LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M \ pc -m 214 -smp 1 -nographic -monitor unix:/tmp/test-monitor,server,nowait \ -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net nic,\ -macaddr=00:11:22:33:44:55,vlan=0 -net user,vlan=0 -serial none -parallel none \ --usb +macaddr=00:11:22:33:44:55,vlan=0 \ +-net user,vlan=0,hostfwd=tcp::2222-:22,hostfwd=udp:127.0.0.1:2242-10.0.2.15:42 \ +-serial none -parallel none -usb -- 1.7.10.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list