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