Implement port forwarding for user mode networking with QEMU. --- src/libvirt_private.syms | 3 + src/qemu/qemu_command.c | 139 +++++++++++++++++++++ tests/qemuargv2xmltest.c | 3 +- tests/qemuxml2argvdata/qemuxml2argv-net-user.args | 5 +- 4 files changed, 146 insertions(+), 4 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 7d09f33..d497bce 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -396,6 +396,9 @@ virDomainNetInsert; virDomainNetRemove; virDomainNetRemoveByMac; virDomainNetTypeToString; +virDomainNetForwardDefFree; +virDomainNetForwardProtocolTypeFromString; +virDomainNetForwardProtocolTypeToString; virDomainNostateReasonTypeFromString; virDomainNostateReasonTypeToString; virDomainNumatuneMemModeTypeFromString; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 9f99dce..2c9d948 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2917,6 +2917,34 @@ 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) { + char *hostaddr = NULL; + char *guestaddr = NULL; + virDomainNetForwardDefPtr fwd = net->data.user.forwards[i]; + const char *protocol = virDomainNetForwardProtocolTypeToString(fwd->protocol); + if (!protocol) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid forward protocol")); + return NULL; + } + + if (fwd->has_hostaddr) + hostaddr = virSocketAddrFormat(&fwd->hostaddr); + if (fwd->has_guestaddr) + guestaddr = virSocketAddrFormat(&fwd->guestaddr); + + virBufferAsprintf(&buf, ",hostfwd=%s:%s:%d-%s:%d", protocol, + hostaddr ? hostaddr : "", fwd->hostport, + guestaddr ? guestaddr : "", fwd->guestport); + + VIR_FREE(hostaddr); + VIR_FREE(guestaddr); + } + } + if (virBufferError(&buf)) { virBufferFreeAndReset(&buf); virReportOOMError(); @@ -6785,6 +6813,94 @@ qemuFindNICForVLAN(int nnics, } +static virDomainNetForwardDefPtr +qemuParseNetForward(char *val) +{ + char *protocol, *host, *guest, *port; + virDomainNetForwardDefPtr def; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + return NULL; + } + + protocol = val; + host = strchr(protocol, ':'); + 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(protocol, "")) { + def->protocol = VIR_DOMAIN_NET_FORWARD_PROTOCOL_TCP; + } else if ((def->protocol = virDomainNetForwardProtocolTypeFromString(protocol)) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown forward protocol '%s'"), protocol); + goto error; + } + + port = strchr(host, ':'); + if (!port) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing host port")); + goto error; + } + *port = '\0'; + port++; + if (!STREQ(host, "")) { + if (virSocketAddrParse(&def->hostaddr, host, AF_INET) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Bad host address '%s' for redirection"), host); + goto error; + } + def->has_hostaddr = true; + } + if (virStrToLong_i(port, NULL, 10, &def->hostport) < 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, "")) { + if (virSocketAddrParse(&def->guestaddr, guest, AF_INET) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Bad guest address '%s' for redirection"), guest); + goto error; + } + def->has_guestaddr = true; + } + if (virStrToLong_i(port, NULL, 10, &def->guestport) < 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 @@ -6804,6 +6920,7 @@ qemuParseCommandLineNet(virCapsPtr caps, int wantvlan = 0; const char *tmp; int genmac = 1; + int nforward = 0; int i; tmp = strchr(val, ','); @@ -6850,9 +6967,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