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