]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
net: support set public ip range for forward mode nat
authorNatanael Copa <ncopa@alpinelinux.org>
Tue, 19 Feb 2013 10:44:14 +0000 (11:44 +0100)
committerLaine Stump <laine@laine.org>
Tue, 19 Feb 2013 19:42:18 +0000 (14:42 -0500)
Support setting which public ip to use for NAT via attribute
address in subelement <nat> in <forward>:

...
  <forward mode='nat'>
      <address start='1.2.3.4' end='1.2.3.10'/>
  </forward>
...

This will construct an iptables line using:

  '-j SNAT --to-source <start>-<end>'

instead of:

  '-j MASQUERADE'

Signed-off-by: Natanael Copa <ncopa@alpinelinux.org>
Signed-off-by: Laine Stump <laine@laine.org>
docs/formatnetwork.html.in
src/conf/network_conf.c
src/conf/network_conf.h
src/network/bridge_driver.c
src/util/viriptables.c
src/util/viriptables.h

index 7b4252977346717de8553e7e2a9fd343224ff773..5fbd0a9b4882be138ddc427d1472a9ca9530da04 100644 (file)
             network, and to/from the host to the guests, are
             unrestricted and not NATed.<span class="since">Since
             0.4.2</span>
+
+            <p><span class="since">Since 1.0.3</span> it is possible to
+            specify a public IPv4 address range to be used for the NAT by
+            using the <code>&lt;nat&gt;</code> and
+            <code>&lt;address&gt;</code> subelements.
+            <pre>
+...
+  &lt;forward mode='nat'&gt;
+    &lt;nat&gt;
+      &lt;address start='1.2.3.4' end='1.2.3.10'/&gt;
+    &lt;/nat&gt;
+  &lt;/forward&gt;
+...
+            </pre>
+            An singe IPv4 address can be set by setting
+            <code>start</code> and <code>end</code> attributes to
+            the same value.
+            </p>
           </dd>
 
           <dt><code>route</code></dt>
index 3604ff7d592e4c639f14595f8fd78e0e75ba5b8f..c7df2a520a66db0daa8eefb524febb2c6c1c31bc 100644 (file)
@@ -1324,6 +1324,81 @@ cleanup:
     return result;
 }
 
+static int
+virNetworkForwardNatDefParseXML(const char *networkName,
+                                xmlNodePtr node,
+                                xmlXPathContextPtr ctxt,
+                                virNetworkForwardDefPtr def)
+{
+    int ret = -1;
+    xmlNodePtr *natAddrNodes = NULL;
+    int nNatAddrs;
+    char *addrStart = NULL;
+    char *addrEnd = NULL;
+    xmlNodePtr save = ctxt->node;
+
+    ctxt->node = node;
+
+    if (def->type != VIR_NETWORK_FORWARD_NAT) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("The <nat> element can only be used when <forward> 'mode' is 'nat' in network %s"),
+                       networkName);
+        goto cleanup;
+    }
+
+    /* addresses for SNAT */
+    nNatAddrs = virXPathNodeSet("./address", ctxt, &natAddrNodes);
+    if (nNatAddrs < 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("invalid <address> element found in <forward> of "
+                         "network %s"), networkName);
+        goto cleanup;
+    } else if (nNatAddrs > 1) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Only one <address> element is allowed in <nat> in "
+                         "<forward> in network %s"), networkName);
+        goto cleanup;
+    } else if (nNatAddrs == 1) {
+        addrStart = virXMLPropString(*natAddrNodes, "start");
+        if (addrStart == NULL) {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("missing 'start' attribute in <address> element in <nat> in "
+                             "<forward> in network %s"), networkName);
+            goto cleanup;
+        }
+        addrEnd = virXMLPropString(*natAddrNodes, "end");
+        if (addrEnd == NULL) {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("missing 'end' attribute in <address> element in <nat> in "
+                             "<forward> in network %s"), networkName);
+            goto cleanup;
+        }
+    }
+
+    if (addrStart && virSocketAddrParse(&def->addrStart, addrStart, AF_INET) < 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Bad ipv4 start address '%s' in <nat> in <forward> in "
+                         "network '%s'"), addrStart, networkName);
+        goto cleanup;
+    }
+
+    if (addrEnd && virSocketAddrParse(&def->addrEnd, addrEnd, AF_INET) < 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Bad ipv4 end address '%s' in <nat> in <forward> in "
+                         "network '%s'"), addrEnd, networkName);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(addrStart);
+    VIR_FREE(addrEnd);
+    VIR_FREE(natAddrNodes);
+    ctxt->node = save;
+    return ret;
+}
+
 static int
 virNetworkForwardDefParseXML(const char *networkName,
                              xmlNodePtr node,
@@ -1334,7 +1409,8 @@ virNetworkForwardDefParseXML(const char *networkName,
     xmlNodePtr *forwardIfNodes = NULL;
     xmlNodePtr *forwardPfNodes = NULL;
     xmlNodePtr *forwardAddrNodes = NULL;
-    int nForwardIfs, nForwardAddrs, nForwardPfs;
+    xmlNodePtr *forwardNatNodes = NULL;
+    int nForwardIfs, nForwardAddrs, nForwardPfs, nForwardNats;
     char *forwardDev = NULL;
     char *forwardManaged = NULL;
     char *type = NULL;
@@ -1384,6 +1460,24 @@ virNetworkForwardDefParseXML(const char *networkName,
         goto cleanup;
     }
 
+    nForwardNats = virXPathNodeSet("./nat", ctxt, &forwardNatNodes);
+    if (nForwardNats < 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("invalid <nat> element found in <forward> of network %s"),
+                       networkName);
+        goto cleanup;
+    } else if (nForwardNats > 1) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Only one <nat> element is allowed in <forward> of network %s"),
+                       networkName);
+        goto cleanup;
+    } else if (nForwardNats == 1) {
+        if (virNetworkForwardNatDefParseXML(networkName,
+                                            *forwardNatNodes,
+                                            ctxt, def) < 0)
+            goto cleanup;
+    }
+
     if (((nForwardIfs > 0) + (nForwardAddrs > 0) + (nForwardPfs > 0)) > 1) {
         virReportError(VIR_ERR_XML_ERROR,
                        _("<address>, <interface>, and <pf> elements in <forward> "
@@ -1525,6 +1619,7 @@ cleanup:
     VIR_FREE(forwardPfNodes);
     VIR_FREE(forwardIfNodes);
     VIR_FREE(forwardAddrNodes);
+    VIR_FREE(forwardNatNodes);
     ctxt->node = save;
     return ret;
 }
@@ -2078,6 +2173,47 @@ virPortGroupDefFormat(virBufferPtr buf,
     return 0;
 }
 
+static int
+virNetworkForwardNatDefFormat(virBufferPtr buf,
+                              const virNetworkForwardDefPtr fwd)
+{
+    char *addrStart = NULL;
+    char *addrEnd = NULL;
+    int ret = -1;
+
+    if (VIR_SOCKET_ADDR_VALID(&fwd->addrStart)) {
+        addrStart = virSocketAddrFormat(&fwd->addrStart);
+        if (!addrStart)
+            goto cleanup;
+    }
+
+    if (VIR_SOCKET_ADDR_VALID(&fwd->addrEnd)) {
+        addrEnd = virSocketAddrFormat(&fwd->addrEnd);
+        if (!addrEnd)
+            goto cleanup;
+    }
+
+    if (!addrEnd && !addrStart)
+        return 0;
+
+    virBufferAddLit(buf, "<nat>\n");
+    virBufferAdjustIndent(buf, 2);
+
+    virBufferAsprintf(buf, "<address start='%s'", addrStart);
+    if (addrEnd)
+        virBufferAsprintf(buf, " end='%s'", addrEnd);
+    virBufferAddLit(buf, "/>\n");
+
+    virBufferAdjustIndent(buf, -2);
+    virBufferAddLit(buf, "</nat>\n");
+    ret = 0;
+
+cleanup:
+    VIR_FREE(addrStart);
+    VIR_FREE(addrEnd);
+    return ret;
+}
+
 static int
 virNetworkDefFormatInternal(virBufferPtr buf,
                             const virNetworkDefPtr def,
@@ -2085,7 +2221,7 @@ virNetworkDefFormatInternal(virBufferPtr buf,
 {
     unsigned char *uuid;
     char uuidstr[VIR_UUID_STRING_BUFLEN];
-    int ii;
+    int ii, shortforward;
 
     virBufferAddLit(buf, "<network");
     if (!(flags & VIR_NETWORK_XML_INACTIVE) && (def->connections > 0)) {
@@ -2122,10 +2258,17 @@ virNetworkDefFormatInternal(virBufferPtr buf,
             else
                 virBufferAddLit(buf, " managed='no'");
         }
-        virBufferAsprintf(buf, "%s>\n",
-                          (def->forward.nifs || def->forward.npfs) ? "" : "/");
+        shortforward = !(def->forward.nifs || def->forward.npfs
+                         || VIR_SOCKET_ADDR_VALID(&def->forward.addrStart)
+                         || VIR_SOCKET_ADDR_VALID(&def->forward.addrEnd));
+        virBufferAsprintf(buf, "%s>\n", shortforward ? "/" : "");
         virBufferAdjustIndent(buf, 2);
 
+        if (def->forward.type == VIR_NETWORK_FORWARD_NAT) {
+            if (virNetworkForwardNatDefFormat(buf, &def->forward) < 0)
+                goto error;
+        }
+
         /* For now, hard-coded to at most 1 forward.pfs */
         if (def->forward.npfs)
             virBufferEscapeString(buf, "<pf dev='%s'/>\n",
@@ -2155,7 +2298,7 @@ virNetworkDefFormatInternal(virBufferPtr buf,
             }
         }
         virBufferAdjustIndent(buf, -2);
-        if (def->forward.npfs || def->forward.nifs)
+        if (!shortforward)
             virBufferAddLit(buf, "</forward>\n");
     }
 
index 4c634ed47fa1ed1d573278c086ea0e12f34308d8..11d6c9ca18438ee18c6fc44a53f5ddb7ddfdd8a4 100644 (file)
@@ -174,6 +174,9 @@ struct _virNetworkForwardDef {
 
     size_t nifs;
     virNetworkForwardIfDefPtr ifs;
+
+    /* adresses for SNAT */
+    virSocketAddr addrStart, addrEnd;
 };
 
 typedef struct _virPortGroupDef virPortGroupDef;
index c834f8362654fce79d5708fa12c3314386d02efb..9f502a5ca6b266978f52731909d3e70b9e28a2fd 100644 (file)
@@ -1587,6 +1587,8 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
                                      &ipdef->address,
                                      prefix,
                                      forwardIf,
+                                     &network->def->forward.addrStart,
+                                     &network->def->forward.addrEnd,
                                      NULL) < 0) {
         virReportError(VIR_ERR_SYSTEM_ERROR,
                        forwardIf ?
@@ -1601,6 +1603,8 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
                                      &ipdef->address,
                                      prefix,
                                      forwardIf,
+                                     &network->def->forward.addrStart,
+                                     &network->def->forward.addrEnd,
                                      "udp") < 0) {
         virReportError(VIR_ERR_SYSTEM_ERROR,
                        forwardIf ?
@@ -1615,6 +1619,8 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
                                      &ipdef->address,
                                      prefix,
                                      forwardIf,
+                                     &network->def->forward.addrStart,
+                                     &network->def->forward.addrEnd,
                                      "tcp") < 0) {
         virReportError(VIR_ERR_SYSTEM_ERROR,
                        forwardIf ?
@@ -1631,12 +1637,16 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
                                     &ipdef->address,
                                     prefix,
                                     forwardIf,
+                                    &network->def->forward.addrStart,
+                                    &network->def->forward.addrEnd,
                                     "udp");
  masqerr4:
     iptablesRemoveForwardMasquerade(driver->iptables,
                                     &ipdef->address,
                                     prefix,
                                     forwardIf,
+                                    &network->def->forward.addrStart,
+                                    &network->def->forward.addrEnd,
                                     NULL);
  masqerr3:
     iptablesRemoveForwardAllowRelatedIn(driver->iptables,
@@ -1667,16 +1677,22 @@ networkRemoveMasqueradingIptablesRules(struct network_driver *driver,
                                         &ipdef->address,
                                         prefix,
                                         forwardIf,
+                                        &network->def->forward.addrStart,
+                                        &network->def->forward.addrEnd,
                                         "tcp");
         iptablesRemoveForwardMasquerade(driver->iptables,
                                         &ipdef->address,
                                         prefix,
                                         forwardIf,
+                                        &network->def->forward.addrStart,
+                                        &network->def->forward.addrEnd,
                                         "udp");
         iptablesRemoveForwardMasquerade(driver->iptables,
                                         &ipdef->address,
                                         prefix,
                                         forwardIf,
+                                        &network->def->forward.addrStart,
+                                        &network->def->forward.addrEnd,
                                         NULL);
 
         iptablesRemoveForwardAllowRelatedIn(driver->iptables,
index 41fe780afc0b724841a848f28fe2193a896ac013..954ce8b5a993687b88f49be77c9ecbe2daabb4ff 100644 (file)
@@ -805,11 +805,16 @@ iptablesForwardMasquerade(iptablesContext *ctx,
                           virSocketAddr *netaddr,
                           unsigned int prefix,
                           const char *physdev,
+                          virSocketAddr *addrStart,
+                          virSocketAddr *addrEnd,
                           const char *protocol,
                           int action)
 {
-    int ret;
-    char *networkstr;
+    int ret = -1;
+    char *networkstr = NULL;
+    char *addrStartStr = NULL;
+    char *addrEndStr = NULL;
+    char *natRangeStr = NULL;
     virCommandPtr cmd = NULL;
 
     if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
@@ -820,8 +825,16 @@ iptablesForwardMasquerade(iptablesContext *ctx,
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("Attempted to NAT '%s'. NAT is only supported for IPv4."),
                        networkstr);
-        VIR_FREE(networkstr);
-        return -1;
+        goto cleanup;
+    }
+
+    if (VIR_SOCKET_ADDR_IS_FAMILY(addrStart, AF_INET)) {
+        if (!(addrStartStr = virSocketAddrFormat(addrStart)))
+            goto cleanup;
+        if (VIR_SOCKET_ADDR_IS_FAMILY(addrEnd, AF_INET)) {
+            if (!(addrEndStr = virSocketAddrFormat(addrEnd)))
+                goto cleanup;
+        }
     }
 
     cmd = iptablesCommandNew(ctx->nat_postrouting, AF_INET, action);
@@ -835,13 +848,42 @@ iptablesForwardMasquerade(iptablesContext *ctx,
     if (physdev && physdev[0])
         virCommandAddArgList(cmd, "--out-interface", physdev, NULL);
 
-    virCommandAddArgList(cmd, "--jump", "MASQUERADE", NULL);
+    /* Use --jump SNAT if public addr is specified */
+    if (addrStartStr && addrStartStr[0]) {
+        const char *portStr = "";
+        int r = 0;
 
-    if (protocol && protocol[0])
-        virCommandAddArgList(cmd, "--to-ports", "1024-65535", NULL);
+        if (protocol && protocol[0])
+            portStr = ":1024-65535";
 
-    ret = iptablesCommandRunAndFree(cmd);
+        if (addrEndStr && addrEndStr[0]) {
+            r = virAsprintf(&natRangeStr, "%s-%s%s", addrStartStr, addrEndStr,
+                            portStr);
+        } else {
+            r = virAsprintf(&natRangeStr, "%s%s", addrStartStr, portStr);
+        }
+
+        if (r < 0) {
+            virReportOOMError();
+            goto cleanup;
+        }
+
+        virCommandAddArgList(cmd, "--jump", "SNAT",
+                                  "--to-source", natRangeStr, NULL);
+     } else {
+         virCommandAddArgList(cmd, "--jump", "MASQUERADE", NULL);
+
+         if (protocol && protocol[0])
+             virCommandAddArgList(cmd, "--to-ports", "1024-65535", NULL);
+     }
+
+    ret = virCommandRun(cmd, NULL);
+cleanup:
+    virCommandFree(cmd);
     VIR_FREE(networkstr);
+    VIR_FREE(addrStartStr);
+    VIR_FREE(addrEndStr);
+    VIR_FREE(natRangeStr);
     return ret;
 }
 
@@ -863,9 +905,11 @@ iptablesAddForwardMasquerade(iptablesContext *ctx,
                              virSocketAddr *netaddr,
                              unsigned int prefix,
                              const char *physdev,
+                             virSocketAddr *addrStart,
+                             virSocketAddr *addrEnd,
                              const char *protocol)
 {
-    return iptablesForwardMasquerade(ctx, netaddr, prefix, physdev, protocol, ADD);
+    return iptablesForwardMasquerade(ctx, netaddr, prefix, physdev, addrStart, addrEnd, protocol, ADD);
 }
 
 /**
@@ -886,9 +930,11 @@ iptablesRemoveForwardMasquerade(iptablesContext *ctx,
                                 virSocketAddr *netaddr,
                                 unsigned int prefix,
                                 const char *physdev,
+                                virSocketAddr *addrStart,
+                                virSocketAddr *addrEnd,
                                 const char *protocol)
 {
-    return iptablesForwardMasquerade(ctx, netaddr, prefix, physdev, protocol, REMOVE);
+    return iptablesForwardMasquerade(ctx, netaddr, prefix, physdev, addrStart, addrEnd, protocol, REMOVE);
 }
 
 
index d7fa7311aaac8ef9e6dbd2228bf2e7310c07b5d6..05362da7f809d0d5daaf35157745456060ae3d92 100644 (file)
@@ -107,11 +107,15 @@ int              iptablesAddForwardMasquerade    (iptablesContext *ctx,
                                                   virSocketAddr *netaddr,
                                                   unsigned int prefix,
                                                   const char *physdev,
+                                                  virSocketAddr *addrStart,
+                                                  virSocketAddr *addrEnd,
                                                   const char *protocol);
 int              iptablesRemoveForwardMasquerade (iptablesContext *ctx,
                                                   virSocketAddr *netaddr,
                                                   unsigned int prefix,
                                                   const char *physdev,
+                                                  virSocketAddr *addrStart,
+                                                  virSocketAddr *addrEnd,
                                                   const char *protocol);
 int              iptablesAddOutputFixUdpChecksum (iptablesContext *ctx,
                                                   const char *iface,