[libvirt] [PATCH libvirt 1/2] domain: add <forward> element for user mode networking

Marc-André Lureau marcandre.lureau at gmail.com
Thu May 10 00:10:44 UTC 2012


Add element <forward> to add TCP or UDP port redirection from host to
guest in user mode networking.
---
 docs/formatdomain.html.in                        |   21 ++++
 docs/schemas/domaincommon.rng                    |   29 +++++
 src/conf/domain_conf.c                           |  140 ++++++++++++++++++++++
 src/conf/domain_conf.h                           |   23 ++++
 tests/qemuargv2xmltest.c                         |    3 +-
 tests/qemuxml2argvdata/qemuxml2argv-net-user.xml |    2 +
 6 files changed, 217 insertions(+), 1 deletion(-)

diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index d6e90f1..9c9bbf5 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -2268,6 +2268,27 @@
       VMs to have outgoing access.
     </p>
 
+    <p>
+      Port redirections from host to guest can be added by
+      providing <code>forward</code> elements that takes the
+      following attributes:
+    </p>
+
+    <dl>
+      <dt><code>type</code></dt>
+      <dd>Either <code>tcp</code> (default) or <code>udp</code>.</dd>
+
+      <dt><code>host_port</code></dt>
+      <dd>Host port to redirect.</dd>
+
+      <dt><code>guest_port</code></dt>
+      <dd>Guest port to redirect to.</dd>
+
+      <dt><code>host</code></dt>
+      <dd>IPv4 address to bound the redirection to a specific host
+      interface.</dd>
+    </dl>
+
 <pre>
   ...
   <devices>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 34f63c3..740f5af 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -1408,6 +1408,35 @@
             <value>user</value>
           </attribute>
           <interleave>
+            <zeroOrMore>
+              <element name="forward">
+                <attribute name="host_port">
+                  <ref name="PortNumber"/>
+                </attribute>
+                <attribute name="guest_port">
+                  <ref name="PortNumber"/>
+                </attribute>
+                <optional>
+                  <attribute name="type">
+                    <choice>
+                      <value>tcp</value>
+                      <value>udp</value>
+                    </choice>
+                  </attribute>
+                </optional>
+                <optional>
+                  <attribute name="host">
+                    <ref name="ipv4Addr"/>
+                  </attribute>
+                </optional>
+                <optional>
+                  <attribute name="guest">
+                    <ref name="ipv4Addr"/>
+                  </attribute>
+                </optional>
+                <empty/>
+              </element>
+            </zeroOrMore>
             <ref name="interface-options"/>
           </interleave>
         </group>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 51d6cb9..4b9b644 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -650,6 +650,10 @@ VIR_ENUM_IMPL(virDomainNumatuneMemPlacementMode,
               "static",
               "auto");
 
+VIR_ENUM_IMPL(virDomainNetForward, VIR_DOMAIN_NET_FORWARD_TYPE_LAST,
+              "tcp",
+              "udp")
+
 #define virDomainReportError(code, ...)                              \
     virReportErrorHelper(VIR_FROM_DOMAIN, code, __FILE__,            \
                          __FUNCTION__, __LINE__, __VA_ARGS__)
@@ -1019,8 +1023,22 @@ virDomainActualNetDefFree(virDomainActualNetDefPtr def)
     VIR_FREE(def);
 }
 
+void
+virDomainNetForwardDefFree(virDomainNetForwardDefPtr def)
+{
+    if (!def)
+        return;
+
+    VIR_FREE(def->host_addr);
+    VIR_FREE(def->guest_addr);
+
+    VIR_FREE(def);
+}
+
 void virDomainNetDefFree(virDomainNetDefPtr def)
 {
+    int i;
+
     if (!def)
         return;
 
@@ -1066,6 +1084,11 @@ void virDomainNetDefFree(virDomainNetDefPtr def)
         break;
 
     case VIR_DOMAIN_NET_TYPE_USER:
+        for (i = 0; i < def->data.user.nforward; i++)
+            virDomainNetForwardDefFree(def->data.user.forwards[i]);
+        VIR_FREE(def->data.user.forwards);
+        break;
+
     case VIR_DOMAIN_NET_TYPE_LAST:
         break;
     }
@@ -4351,6 +4374,60 @@ error:
     return ret;
 }
 
+static virDomainNetForwardDefPtr
+virDomainNetForwardDefParseXML(const xmlNodePtr node)
+{
+    char *type = NULL;
+    char *host_port = NULL;
+    char *guest_port = NULL;
+    virDomainNetForwardDefPtr def;
+
+    if (VIR_ALLOC(def) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    type = virXMLPropString(node, "type");
+    if (type == NULL) {
+        def->type = VIR_DOMAIN_NET_FORWARD_TYPE_TCP;
+    } else if ((def->type = virDomainNetForwardTypeFromString(type)) < 0) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+                             _("unknown forward type '%s'"), type);
+        goto error;
+    }
+
+    host_port = virXMLPropString(node, "host_port");
+    if (!host_port ||
+        virStrToLong_i(host_port, NULL, 10, &def->host_port) < 0) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                             _("Cannot parse <forward> 'host_port' attribute"));
+        goto error;
+    }
+
+    guest_port = virXMLPropString(node, "guest_port");
+    if (!guest_port ||
+        virStrToLong_i(guest_port, NULL, 10, &def->guest_port) < 0) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                             _("Cannot parse <forward> 'guest_port' attribute"));
+        goto error;
+    }
+
+    def->host_addr = virXMLPropString(node, "host");
+    def->guest_addr = virXMLPropString(node, "guest");
+
+cleanup:
+    VIR_FREE(type);
+    VIR_FREE(host_port);
+    VIR_FREE(guest_port);
+
+    return def;
+
+error:
+    virDomainNetForwardDefFree(def);
+    def = NULL;
+    goto cleanup;
+}
+
 #define NET_MODEL_CHARS \
     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ091234567890_-"
 
@@ -4394,6 +4471,8 @@ virDomainNetDefParseXML(virCapsPtr caps,
     virDomainActualNetDefPtr actual = NULL;
     xmlNodePtr oldnode = ctxt->node;
     int ret;
+    int nforwards;
+    xmlNodePtr *forwardNodes = NULL;
 
     if (VIR_ALLOC(def) < 0) {
         virReportOOMError();
@@ -4683,6 +4762,28 @@ virDomainNetDefParseXML(virCapsPtr caps,
         break;
 
     case VIR_DOMAIN_NET_TYPE_USER:
+        /* parse the <forward> elements */
+        nforwards = virXPathNodeSet("./forward", ctxt, &forwardNodes);
+        if (nforwards < 0)
+            goto error;
+
+        if (nforwards > 0) {
+            int i;
+            if (VIR_ALLOC_N(def->data.user.forwards, nforwards) < 0) {
+                virReportOOMError();
+                goto error;
+            }
+            for (i = 0; i < nforwards; i++) {
+                virDomainNetForwardDefPtr fwd =
+                    virDomainNetForwardDefParseXML(forwardNodes[i]);
+                if (fwd == NULL)
+                    goto error;
+                def->data.user.forwards[def->data.user.nforward++] = fwd;
+            }
+            VIR_FREE(forwardNodes);
+        }
+        break;
+
     case VIR_DOMAIN_NET_TYPE_LAST:
         break;
     }
@@ -11413,11 +11514,42 @@ error:
 }
 
 static int
+virDomainNetForwardDefFormat(virBufferPtr buf,
+                             virDomainNetForwardDefPtr def)
+{
+    const char *type;
+    if (!def)
+        return 0;
+
+    type = virDomainNetForwardTypeToString(def->type);
+    if (!type) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected net type %d"), def->type);
+        return -1;
+    }
+    virBufferAsprintf(buf, "<forward type='%s'", type);
+
+    if (def->host_addr)
+        virBufferAsprintf(buf, " host='%s'", def->host_addr);
+
+    virBufferAsprintf(buf, " host_port='%d'", def->host_port);
+
+    if (def->guest_addr)
+        virBufferAsprintf(buf, " guest='%s'", def->guest_addr);
+
+    virBufferAsprintf(buf, " guest_port='%d'", def->guest_port);
+
+    virBufferAddLit(buf, "/>\n");
+    return 0;
+}
+
+static int
 virDomainNetDefFormat(virBufferPtr buf,
                       virDomainNetDefPtr def,
                       unsigned int flags)
 {
     const char *type = virDomainNetTypeToString(def->type);
+    int i;
 
     if (!type) {
         virDomainReportError(VIR_ERR_INTERNAL_ERROR,
@@ -11517,6 +11649,14 @@ virDomainNetDefFormat(virBufferPtr buf,
         break;
 
     case VIR_DOMAIN_NET_TYPE_USER:
+        virBufferAdjustIndent(buf, 6);
+        for (i = 0; i < def->data.user.nforward; i++) {
+            if (virDomainNetForwardDefFormat(buf, def->data.user.forwards[i]) < 0)
+                return -1;
+        }
+        virBufferAdjustIndent(buf, -6);
+        break;
+
     case VIR_DOMAIN_NET_TYPE_LAST:
         break;
     }
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 70129fe..7fde05e 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -768,6 +768,23 @@ struct _virDomainActualNetDef {
     virNetDevBandwidthPtr bandwidth;
 };
 
+enum virDomainNetForwardType {
+    VIR_DOMAIN_NET_FORWARD_TYPE_TCP,
+    VIR_DOMAIN_NET_FORWARD_TYPE_UDP,
+
+    VIR_DOMAIN_NET_FORWARD_TYPE_LAST,
+};
+
+typedef struct _virDomainNetForwardDef virDomainNetForwardDef;
+typedef virDomainNetForwardDef *virDomainNetForwardDefPtr;
+struct _virDomainNetForwardDef {
+    int type; /* enum virDomainNetForwardType */
+    char *host_addr;
+    int host_port;
+    char *guest_addr;
+    int guest_port;
+};
+
 /* Stores the virtual network interface configuration */
 struct _virDomainNetDef {
     enum virDomainNetType type;
@@ -821,6 +838,10 @@ struct _virDomainNetDef {
             virDomainHostdevDef def;
             virNetDevVPortProfilePtr virtPortProfile;
         } hostdev;
+        struct {
+            int nforward;
+            virDomainNetForwardDefPtr *forwards;
+        } user;
     } data;
     struct {
         bool sndbuf_specified;
@@ -1856,6 +1877,7 @@ int virDomainDiskFindControllerModel(virDomainDefPtr def,
 void virDomainControllerDefFree(virDomainControllerDefPtr def);
 void virDomainFSDefFree(virDomainFSDefPtr def);
 void virDomainActualNetDefFree(virDomainActualNetDefPtr def);
+void virDomainNetForwardDefFree(virDomainNetForwardDefPtr def);
 void virDomainNetDefFree(virDomainNetDefPtr def);
 void virDomainSmartcardDefFree(virDomainSmartcardDefPtr def);
 void virDomainChrDefFree(virDomainChrDefPtr def);
@@ -2170,6 +2192,7 @@ VIR_ENUM_DECL(virDomainFSAccessMode)
 VIR_ENUM_DECL(virDomainFSWrpolicy)
 VIR_ENUM_DECL(virDomainNet)
 VIR_ENUM_DECL(virDomainNetBackend)
+VIR_ENUM_DECL(virDomainNetForward)
 VIR_ENUM_DECL(virDomainNetVirtioTxMode)
 VIR_ENUM_DECL(virDomainNetInterfaceLinkState)
 VIR_ENUM_DECL(virDomainChrDevice)
diff --git a/tests/qemuargv2xmltest.c b/tests/qemuargv2xmltest.c
index 439218e..cf2862b 100644
--- a/tests/qemuargv2xmltest.c
+++ b/tests/qemuargv2xmltest.c
@@ -207,7 +207,8 @@ mymain(void)
     DO_TEST("misc-acpi");
     DO_TEST("misc-no-reboot");
     DO_TEST("misc-uuid");
-    DO_TEST("net-user");
+    /* Fixed in following commit */
+    /* 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.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-user.xml
index 37e5edf..ecefdeb 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-net-user.xml
+++ b/tests/qemuxml2argvdata/qemuxml2argv-net-user.xml
@@ -23,6 +23,8 @@
     <controller type='ide' index='0'/>
     <interface type='user'>
       <mac address='00:11:22:33:44:55'/>
+      <forward type='tcp' host_port='2222' guest_port='22'/>
+      <forward type='udp' host='127.0.0.1' host_port='2242' guest='10.0.2.15' guest_port='42'/>
     </interface>
     <memballoon model='virtio'/>
   </devices>
-- 
1.7.10.1




More information about the libvir-list mailing list