From 905629f47ed6eb12e149be45a10da2b61082ed88 Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Tue, 19 Feb 2013 11:44:14 +0100 Subject: [PATCH] net: support set public ip range for forward mode nat Support setting which public ip to use for NAT via attribute address in subelement in : ...
... This will construct an iptables line using: '-j SNAT --to-source -' instead of: '-j MASQUERADE' Signed-off-by: Natanael Copa Signed-off-by: Laine Stump --- docs/formatnetwork.html.in | 18 +++++ src/conf/network_conf.c | 153 ++++++++++++++++++++++++++++++++++-- src/conf/network_conf.h | 3 + src/network/bridge_driver.c | 16 ++++ src/util/viriptables.c | 66 +++++++++++++--- src/util/viriptables.h | 4 + 6 files changed, 245 insertions(+), 15 deletions(-) diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 7b42529773..5fbd0a9b48 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -136,6 +136,24 @@ network, and to/from the host to the guests, are unrestricted and not NATed.Since 0.4.2 + +

Since 1.0.3 it is possible to + specify a public IPv4 address range to be used for the NAT by + using the <nat> and + <address> subelements. +

+...
+  <forward mode='nat'>
+    <nat>
+      <address start='1.2.3.4' end='1.2.3.10'/>
+    </nat>
+  </forward>
+...
+            
+ An singe IPv4 address can be set by setting + start and end attributes to + the same value. +

route
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 3604ff7d59..c7df2a520a 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -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 element can only be used when '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
element found in of " + "network %s"), networkName); + goto cleanup; + } else if (nNatAddrs > 1) { + virReportError(VIR_ERR_XML_ERROR, + _("Only one
element is allowed in in " + " 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
element in in " + " in network %s"), networkName); + goto cleanup; + } + addrEnd = virXMLPropString(*natAddrNodes, "end"); + if (addrEnd == NULL) { + virReportError(VIR_ERR_XML_ERROR, + _("missing 'end' attribute in
element in in " + " 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 in 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 in 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 element found in of network %s"), + networkName); + goto cleanup; + } else if (nForwardNats > 1) { + virReportError(VIR_ERR_XML_ERROR, + _("Only one element is allowed in 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, _("
, , and elements in " @@ -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, "\n"); + virBufferAdjustIndent(buf, 2); + + virBufferAsprintf(buf, "
\n"); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "\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, "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, "\n", @@ -2155,7 +2298,7 @@ virNetworkDefFormatInternal(virBufferPtr buf, } } virBufferAdjustIndent(buf, -2); - if (def->forward.npfs || def->forward.nifs) + if (!shortforward) virBufferAddLit(buf, "\n"); } diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 4c634ed47f..11d6c9ca18 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -174,6 +174,9 @@ struct _virNetworkForwardDef { size_t nifs; virNetworkForwardIfDefPtr ifs; + + /* adresses for SNAT */ + virSocketAddr addrStart, addrEnd; }; typedef struct _virPortGroupDef virPortGroupDef; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index c834f83626..9f502a5ca6 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -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, diff --git a/src/util/viriptables.c b/src/util/viriptables.c index 41fe780afc..954ce8b5a9 100644 --- a/src/util/viriptables.c +++ b/src/util/viriptables.c @@ -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); } diff --git a/src/util/viriptables.h b/src/util/viriptables.h index d7fa7311aa..05362da7f8 100644 --- a/src/util/viriptables.h +++ b/src/util/viriptables.h @@ -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,