[libvirt] [PATCH] network: Allow <vlan trunk='yes'/> without explicit <tag> subelements

Matthias Bolte matthias.bolte at googlemail.com
Sun Oct 7 17:08:50 UTC 2012


Until now at least one <tag> subelement was required for a <vlan> element.
An optional trunk='yes' attribute can be used to indicate trunking of the
given tags.

This is not how VLAN tagging works with ESX. One can either specify a
single VLAN tag per virtual network or enable trunking for all possible
VLAN tags. It is not possible to limit trunking to a subset of tags.

This cannot be represented properly with the current network config format.
This patch removes the requirement for at least on tag per <vlan> element
and adapts all related code to deal with the new situation.
---
 docs/formatnetwork.html.in          |    8 +++-
 docs/schemas/networkcommon.rng      |    4 +-
 src/conf/domain_conf.c              |    6 ++-
 src/conf/netdev_vlan_conf.c         |   58 ++++++++++++++++++++---------------
 src/network/bridge_driver.c         |   28 +++++++++++++++++
 src/util/virnetdevopenvswitch.c     |    9 ++++-
 src/util/virnetdevvlan.c            |    8 ++++-
 tests/networkxml2xmlin/esx-net.xml  |   15 +++++++++
 tests/networkxml2xmlout/esx-net.xml |   15 +++++++++
 tests/networkxml2xmltest.c          |    1 +
 10 files changed, 118 insertions(+), 34 deletions(-)
 create mode 100644 tests/networkxml2xmlin/esx-net.xml
 create mode 100644 tests/networkxml2xmlout/esx-net.xml

diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 49206dd..5d601e1 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -398,7 +398,7 @@
       transparent to the guest, an optional <code><vlan></code>
       element can specify one or more vlan tags to apply to the
       traffic of all guests using this
-      network <span class="since">Since 0.10.0</span>. (openvswitch
+      network <span class="since">since 0.10.0</span>. (openvswitch
       and type='hostdev' SR-IOV networks do support transparent vlan
       tagging of guest traffic; everything else, including standard
       linux bridges and libvirt's own virtual networks, <b>do not</b>
@@ -410,7 +410,11 @@
       assumed that the user wants to do VLAN trunking using all the
       specified tags. In the case that vlan trunking with a single tag
       is desired, the optional attribute <code>trunk='yes'</code> can
-      be added to the vlan element.
+      be added to the vlan element. <span class="since">Since 0.10.3</span>
+      <code>trunk='yes'</code> can be set while specifing no
+      <code><vlan></code> elements at all to indicate trunking of all
+      available vlan tags. This is supported by ESX only and is the only
+      trunking mode that ESX supports.
     </p>
     <p>
       <code><vlan></code> elements can also be specified in
diff --git a/docs/schemas/networkcommon.rng b/docs/schemas/networkcommon.rng
index c7749e7..70cdcbb 100644
--- a/docs/schemas/networkcommon.rng
+++ b/docs/schemas/networkcommon.rng
@@ -192,7 +192,7 @@
           <value>yes</value>
         </attribute>
       </optional>
-      <oneOrMore>
+      <zeroOrMore>
         <element name="tag">
           <attribute name="id">
             <data type="unsignedInt">
@@ -201,7 +201,7 @@
           </attribute>
           <empty/>
         </element>
-      </oneOrMore>
+      </zeroOrMore>
     </element>
   </define>
 </grammar>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 33e1e7f..65a201c 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -14897,9 +14897,11 @@ virDomainNetGetActualVlan(virDomainNetDefPtr iface)
 {
     if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
         iface->data.network.actual &&
-        iface->data.network.actual->vlan.nTags > 0)
+        (iface->data.network.actual->vlan.nTags > 0 ||
+         iface->data.network.actual->vlan.trunk))
         return &iface->data.network.actual->vlan;
-    if (iface->vlan.nTags > 0)
+    if (iface->vlan.nTags > 0 ||
+        iface->vlan.trunk)
         return &iface->vlan;
     return 0;
 }
diff --git a/src/conf/netdev_vlan_conf.c b/src/conf/netdev_vlan_conf.c
index 6a0511a..8f9aa04 100644
--- a/src/conf/netdev_vlan_conf.c
+++ b/src/conf/netdev_vlan_conf.c
@@ -42,33 +42,28 @@ virNetDevVlanParse(xmlNodePtr node, xmlXPathContextPtr ctxt, virNetDevVlanPtr de
     if (nTags < 0)
         goto error;
 
-    if (nTags == 0) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("missing tag id - each <vlan> must have "
-                         "at least one <tag id='n'/> subelement"));
-        goto error;
-    }
-
-    if (VIR_ALLOC_N(def->tag, nTags) < 0) {
-        virReportOOMError();
-        goto error;
-    }
-
-    for (ii = 0; ii < nTags; ii++) {
-        unsigned long id;
-
-        ctxt->node = tagNodes[ii];
-        if (virXPathULong("string(./@id)", ctxt, &id) < 0) {
-            virReportError(VIR_ERR_XML_ERROR, "%s",
-                           _("missing or invalid vlan tag id attribute"));
+    if (nTags > 0) {
+        if (VIR_ALLOC_N(def->tag, nTags) < 0) {
+            virReportOOMError();
             goto error;
         }
-        if (id > 4095) {
-            virReportError(VIR_ERR_XML_ERROR,
-                           _("vlan tag id %lu too large (maximum 4095)"), id);
-            goto error;
+
+        for (ii = 0; ii < nTags; ii++) {
+            unsigned long id;
+
+            ctxt->node = tagNodes[ii];
+            if (virXPathULong("string(./@id)", ctxt, &id) < 0) {
+                virReportError(VIR_ERR_XML_ERROR, "%s",
+                               _("missing or invalid vlan tag id attribute"));
+                goto error;
+            }
+            if (id > 4095) {
+                virReportError(VIR_ERR_XML_ERROR,
+                               _("vlan tag id %lu too large (maximum 4095)"), id);
+                goto error;
+            }
+            def->tag[ii] = id;
         }
-        def->tag[ii] = id;
     }
 
     def->nTags = nTags;
@@ -99,6 +94,14 @@ virNetDevVlanParse(xmlNodePtr node, xmlXPathContextPtr ctxt, virNetDevVlanPtr de
         }
     }
 
+    /* only in trunk mode the tag subelements can be omitted */
+    if (!def->trunk && nTags == 0) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("invalid element - each <vlan> must have at least "
+                         "one <tag id='n'/> subelement and/or \"trunk='yes'\""));
+        goto error;
+    }
+
     ret = 0;
 error:
     ctxt->node = save;
@@ -113,8 +116,13 @@ virNetDevVlanFormat(virNetDevVlanPtr def, virBufferPtr buf)
 {
     int ii;
 
-    if (def->nTags == 0)
+    if (def->nTags == 0) {
+        if (def->trunk) {
+            virBufferAddLit(buf, "<vlan trunk='yes'/>\n");
+        }
+
         return 0;
+    }
 
     if (!def->tag) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index e1846ee..37ddb07 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -2616,6 +2616,14 @@ networkValidate(virNetworkDefPtr def)
     int ii;
     bool vlanUsed, vlanAllowed;
 
+    if (def->vlan.nTags == 0 && def->vlan.trunk) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("<vlan> element specified for network %s is missing at "
+                         "least one <tag> subelement required for trunk mode"),
+                       def->name);
+        return -1;
+    }
+
     /* The only type of networks that currently support transparent
      * vlan configuration are those using hostdev sr-iov devices from
      * a pool, and those using an Open vSwitch bridge.
@@ -2627,6 +2635,13 @@ networkValidate(virNetworkDefPtr def)
 
     vlanUsed = def->vlan.nTags > 0;
     for (ii = 0; ii < def->nPortGroups && !(vlanUsed && vlanAllowed); ii++) {
+        if (def->portGroups[ii].vlan.nTags == 0 && def->portGroups[ii].vlan.trunk) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("<vlan> element specified for portgroup %s of network %s "
+                             "is missing at least one <tag> subelement required for trunk mode"),
+                           def->portGroups[ii].name, def->name);
+            return -1;
+        }
         if (def->portGroups[ii].vlan.nTags > 0)
             vlanUsed = true;
         if (def->forwardType == VIR_NETWORK_FORWARD_BRIDGE &&
@@ -3352,6 +3367,8 @@ networkAllocateActualDevice(virDomainNetDefPtr iface)
     /* it's handy to have this initialized if we skip directly to validate */
     if (iface->vlan.nTags > 0)
         vlan = &iface->vlan;
+    else if (iface->vlan.trunk)
+        goto trunk_error;
 
     if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK)
         goto validate;
@@ -3627,10 +3644,16 @@ networkAllocateActualDevice(virDomainNetDefPtr iface)
     /* copy appropriate vlan info to actualNet */
     if (iface->vlan.nTags > 0)
         vlan = &iface->vlan;
+    else if (iface->vlan.trunk)
+        goto trunk_error;
     else if (portgroup && portgroup->vlan.nTags > 0)
         vlan = &portgroup->vlan;
+    else if (portgroup && portgroup->vlan.trunk)
+        goto trunk_error;
     else if (netdef && netdef->vlan.nTags > 0)
         vlan = &netdef->vlan;
+    else if (netdef && netdef->vlan.trunk)
+        goto trunk_error;
 
     if (virNetDevVlanCopy(&iface->data.network.actual->vlan, vlan) < 0)
         goto error;
@@ -3699,6 +3722,11 @@ error:
         iface->data.network.actual = NULL;
     }
     goto cleanup;
+
+trunk_error:
+    virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                   _("vlan trunk mode without a tag is not supported"));
+    goto cleanup;
 }
 
 /* networkNotifyActualDevice:
diff --git a/src/util/virnetdevopenvswitch.c b/src/util/virnetdevopenvswitch.c
index a6993b6..3b29dcc 100644
--- a/src/util/virnetdevopenvswitch.c
+++ b/src/util/virnetdevopenvswitch.c
@@ -80,14 +80,19 @@ int virNetDevOpenvswitchAddPort(const char *brname, const char *ifname,
             goto out_of_memory;
     }
 
-    if (virtVlan && virtVlan->nTags > 0) {
+    if (virtVlan && (virtVlan->nTags > 0 || virtVlan->trunk)) {
+        if (virtVlan->nTags == 0 && virtVlan->trunk) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("vlan trunk mode without a tag is not supported"));
+            goto cleanup;
+        }
 
         /* Trunk port first */
         if (virtVlan->trunk) {
             virBufferAddLit(&buf, "trunk=");
 
             /*
-             * Trunk ports have at least one VLAN. Do the first one
+             * Trunk ports must have at least one VLAN. Do the first one
              * outside the "for" loop so we can put a "," at the
              * start of the for loop if there are more than one VLANs
              * on this trunk port.
diff --git a/src/util/virnetdevvlan.c b/src/util/virnetdevvlan.c
index 7a6ff9b..4cf95dc 100644
--- a/src/util/virnetdevvlan.c
+++ b/src/util/virnetdevvlan.c
@@ -31,6 +31,7 @@
 void
 virNetDevVlanClear(virNetDevVlanPtr vlan)
 {
+    vlan->trunk = false;
     VIR_FREE(vlan->tag);
     vlan->nTags = 0;
 }
@@ -79,9 +80,14 @@ virNetDevVlanEqual(const virNetDevVlanPtr a, const virNetDevVlanPtr b)
 int
 virNetDevVlanCopy(virNetDevVlanPtr dst, const virNetDevVlanPtr src)
 {
-    if (!src || src->nTags == 0)
+    if (!src)
         return 0;
 
+    if (src->nTags == 0) {
+        dst->trunk = src->trunk;
+        return 0;
+    }
+
     if (VIR_ALLOC_N(dst->tag, src->nTags) < 0) {
         virReportOOMError();
         return -1;
diff --git a/tests/networkxml2xmlin/esx-net.xml b/tests/networkxml2xmlin/esx-net.xml
new file mode 100644
index 0000000..546c634
--- /dev/null
+++ b/tests/networkxml2xmlin/esx-net.xml
@@ -0,0 +1,15 @@
+<network>
+  <name>vSwitch0</name>
+  <uuid>2f467347-8b4e-8655-e7d6-c4c3fb968009</uuid>
+  <forward dev='vmnic0' mode='bridge'>
+    <interface dev='vmnic0'/>
+  </forward>
+  <portgroup name='Network1'>
+    <vlan trunk='yes'/>
+  </portgroup>
+  <portgroup name='Network2'>
+    <vlan>
+      <tag id='536'/>
+    </vlan>
+  </portgroup>
+</network>
diff --git a/tests/networkxml2xmlout/esx-net.xml b/tests/networkxml2xmlout/esx-net.xml
new file mode 100644
index 0000000..546c634
--- /dev/null
+++ b/tests/networkxml2xmlout/esx-net.xml
@@ -0,0 +1,15 @@
+<network>
+  <name>vSwitch0</name>
+  <uuid>2f467347-8b4e-8655-e7d6-c4c3fb968009</uuid>
+  <forward dev='vmnic0' mode='bridge'>
+    <interface dev='vmnic0'/>
+  </forward>
+  <portgroup name='Network1'>
+    <vlan trunk='yes'/>
+  </portgroup>
+  <portgroup name='Network2'>
+    <vlan>
+      <tag id='536'/>
+    </vlan>
+  </portgroup>
+</network>
diff --git a/tests/networkxml2xmltest.c b/tests/networkxml2xmltest.c
index e57d190..426c541 100644
--- a/tests/networkxml2xmltest.c
+++ b/tests/networkxml2xmltest.c
@@ -108,6 +108,7 @@ mymain(void)
     DO_TEST_FULL("passthrough-pf", VIR_NETWORK_XML_INACTIVE);
     DO_TEST("hostdev");
     DO_TEST_FULL("hostdev-pf", VIR_NETWORK_XML_INACTIVE);
+    DO_TEST("esx-net");
 
     return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }
-- 
1.7.4.1




More information about the libvir-list mailing list