Support setting which public ip to use for NAT via attribute
address in subelement <nat> in <forward>:
...
<forward mode='nat'>
<nat address='1.2.3.4'/>
</forward>
...
This will construct an iptables line using:
'-j SNAT --to-source <address>'
instead of:
'-j MASQUERADE'
Signed-off-by: Natanael Copa <ncopa(a)alpinelinux.org>
---
docs/formatnetwork.html.in | 13 ++++++
src/conf/network_conf.c | 100 +++++++++++++++++++++++++++++++++++++++++---
src/conf/network_conf.h | 3 ++
src/network/bridge_driver.c | 8 ++++
src/util/viriptables.c | 45 +++++++++++++++-----
src/util/viriptables.h | 2 +
6 files changed, 156 insertions(+), 15 deletions(-)
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 7b42529..4ab1a75 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -136,6 +136,19 @@
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 the public IPv4 address to be used for the NAT by using
+ the <code><nat></code> subelement and attribute
+ <code>address</code>:
+ <pre>
+...
+ <forward mode='nat'>
+ <nat address='1.2.3.4'/>
+ </forward>
+...
+ </pre>
+ </p>
</dd>
<dt><code>route</code></dt>
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index c93916d..a9aa139 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -1325,6 +1325,43 @@ cleanup:
}
static int
+virNetworkForwardNatDefParseXML(const char *networkName,
+ xmlNodePtr node,
+ xmlXPathContextPtr ctxt,
+ virNetworkForwardDefPtr def)
+{
+ int ret = -1;
+ char *addr_start = 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 */
+ addr_start = virXPathString("string(./@address)", ctxt);
+
+ if (addr_start && virSocketAddrParse(&def->addr_start, addr_start,
AF_INET) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Bad ipv4 address '%s' in <nat> in
<forward> in "
+ "network '%s'"), addr_start, networkName);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(addr_start);
+ ctxt->node = save;
+ return ret;
+}
+
+static int
virNetworkForwardDefParseXML(const char *networkName,
xmlNodePtr node,
xmlXPathContextPtr ctxt,
@@ -1334,7 +1371,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 +1422,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 +1581,7 @@ cleanup:
VIR_FREE(forwardPfNodes);
VIR_FREE(forwardIfNodes);
VIR_FREE(forwardAddrNodes);
+ VIR_FREE(forwardNatNodes);
ctxt->node = save;
return ret;
}
@@ -2078,13 +2135,40 @@ virPortGroupDefFormat(virBufferPtr buf,
}
static int
+virNatDefFormat(virBufferPtr buf,
+ const virNetworkForwardDefPtr fwd)
+{
+ char *addr_start = NULL;
+ int ret = -1;
+
+ if (VIR_SOCKET_ADDR_VALID(&fwd->addr_start)) {
+ addr_start = virSocketAddrFormat(&fwd->addr_start);
+ if (!addr_start)
+ goto cleanup;
+ }
+
+ if (!addr_start)
+ return 0;
+
+ virBufferAddLit(buf, "<nat");
+ virBufferAsprintf(buf, " address='%s'", addr_start);
+ virBufferAsprintf(buf, "/>\n");
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(addr_start);
+ return ret;
+}
+
+static int
virNetworkDefFormatInternal(virBufferPtr buf,
const virNetworkDefPtr def,
unsigned int flags)
{
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))
{
@@ -2121,10 +2205,16 @@ 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.addr_start));
+ virBufferAsprintf(buf, "%s>\n", shortforward ? "/" :
"");
virBufferAdjustIndent(buf, 2);
+ if (def->forward.type == VIR_NETWORK_FORWARD_NAT) {
+ if (virNatDefFormat(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",
@@ -2154,7 +2244,7 @@ virNetworkDefFormatInternal(virBufferPtr buf,
}
}
virBufferAdjustIndent(buf, -2);
- if (def->forward.npfs || def->forward.nifs)
+ if (!shortforward)
virBufferAddLit(buf, "</forward>\n");
}
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 4c634ed..4a5ce92 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -174,6 +174,9 @@ struct _virNetworkForwardDef {
size_t nifs;
virNetworkForwardIfDefPtr ifs;
+
+ /* adress for SNAT */
+ virSocketAddr addr_start;
};
typedef struct _virPortGroupDef virPortGroupDef;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 21255f0..05ef19c 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -1590,6 +1590,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
&ipdef->address,
prefix,
forwardIf,
+ &network->def->forward.addr_start,
NULL) < 0) {
virReportError(VIR_ERR_SYSTEM_ERROR,
forwardIf ?
@@ -1604,6 +1605,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
&ipdef->address,
prefix,
forwardIf,
+ &network->def->forward.addr_start,
"udp") < 0) {
virReportError(VIR_ERR_SYSTEM_ERROR,
forwardIf ?
@@ -1618,6 +1620,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver,
&ipdef->address,
prefix,
forwardIf,
+ &network->def->forward.addr_start,
"tcp") < 0) {
virReportError(VIR_ERR_SYSTEM_ERROR,
forwardIf ?
@@ -1634,12 +1637,14 @@ networkAddMasqueradingIptablesRules(struct network_driver
*driver,
&ipdef->address,
prefix,
forwardIf,
+ &network->def->forward.addr_start,
"udp");
masqerr4:
iptablesRemoveForwardMasquerade(driver->iptables,
&ipdef->address,
prefix,
forwardIf,
+ &network->def->forward.addr_start,
NULL);
masqerr3:
iptablesRemoveForwardAllowRelatedIn(driver->iptables,
@@ -1670,16 +1675,19 @@ networkRemoveMasqueradingIptablesRules(struct network_driver
*driver,
&ipdef->address,
prefix,
forwardIf,
+ &network->def->forward.addr_start,
"tcp");
iptablesRemoveForwardMasquerade(driver->iptables,
&ipdef->address,
prefix,
forwardIf,
+ &network->def->forward.addr_start,
"udp");
iptablesRemoveForwardMasquerade(driver->iptables,
&ipdef->address,
prefix,
forwardIf,
+ &network->def->forward.addr_start,
NULL);
iptablesRemoveForwardAllowRelatedIn(driver->iptables,
diff --git a/src/util/viriptables.c b/src/util/viriptables.c
index b03b0e9..12d1b2e 100644
--- a/src/util/viriptables.c
+++ b/src/util/viriptables.c
@@ -397,7 +397,7 @@ iptablesForwardAllowOut(iptablesContext *ctx,
action);
virCommandAddArgList(cmd,
"--source", networkstr,
- "--in-interface", iface, NULL);
+ "--in-interface", iface, NULL);
if (physdev && physdev[0])
virCommandAddArgList(cmd, "--out-interface", physdev, NULL);
@@ -805,11 +805,13 @@ iptablesForwardMasquerade(iptablesContext *ctx,
virSocketAddr *netaddr,
unsigned int prefix,
const char *physdev,
+ virSocketAddr *addr_start,
const char *protocol,
int action)
{
- int ret;
- char *networkstr;
+ int ret = -1;
+ char *networkstr = NULL;
+ char *addr_start_str = NULL;
virCommandPtr cmd = NULL;
if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
@@ -820,8 +822,13 @@ 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(addr_start, AF_INET)) {
+ addr_start_str = virSocketAddrFormat(addr_start);
+ if (!addr_start_str)
+ goto cleanup;
}
cmd = iptablesCommandNew(ctx->nat_postrouting, AF_INET, action);
@@ -835,12 +842,28 @@ 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 (addr_start_str && addr_start_str[0]) {
+ char tmpstr[sizeof("123.123.123.123-123.123.123.123:65535-65535")];
+ const char *portstr = "";
- if (protocol && protocol[0])
- virCommandAddArgList(cmd, "--to-ports", "1024-65535", NULL);
+ memset(tmpstr, 0, sizeof(tmpstr));
+ if (protocol && protocol[0])
+ portstr = ":1024-65535";
+
+ snprintf(tmpstr, sizeof(tmpstr), "%s%s", addr_start_str, portstr);
+
+ virCommandAddArgList(cmd, "--jump", "SNAT",
+ "--to-source", tmpstr, NULL);
+ } else {
+ virCommandAddArgList(cmd, "--jump", "MASQUERADE", NULL);
+
+ if (protocol && protocol[0])
+ virCommandAddArgList(cmd, "--to-ports", "1024-65535",
NULL);
+ }
ret = iptablesCommandRunAndFree(cmd);
+cleanup:
VIR_FREE(networkstr);
return ret;
}
@@ -863,9 +886,10 @@ iptablesAddForwardMasquerade(iptablesContext *ctx,
virSocketAddr *netaddr,
unsigned int prefix,
const char *physdev,
+ virSocketAddr *addr_start,
const char *protocol)
{
- return iptablesForwardMasquerade(ctx, netaddr, prefix, physdev, protocol, ADD);
+ return iptablesForwardMasquerade(ctx, netaddr, prefix, physdev, addr_start, protocol,
ADD);
}
/**
@@ -886,9 +910,10 @@ iptablesRemoveForwardMasquerade(iptablesContext *ctx,
virSocketAddr *netaddr,
unsigned int prefix,
const char *physdev,
+ virSocketAddr *addr_start,
const char *protocol)
{
- return iptablesForwardMasquerade(ctx, netaddr, prefix, physdev, protocol, REMOVE);
+ return iptablesForwardMasquerade(ctx, netaddr, prefix, physdev, addr_start, protocol,
REMOVE);
}
diff --git a/src/util/viriptables.h b/src/util/viriptables.h
index d7fa731..b75a5ea 100644
--- a/src/util/viriptables.h
+++ b/src/util/viriptables.h
@@ -107,11 +107,13 @@ int iptablesAddForwardMasquerade (iptablesContext
*ctx,
virSocketAddr *netaddr,
unsigned int prefix,
const char *physdev,
+ virSocketAddr *addr_start,
const char *protocol);
int iptablesRemoveForwardMasquerade (iptablesContext *ctx,
virSocketAddr *netaddr,
unsigned int prefix,
const char *physdev,
+ virSocketAddr *addr_start,
const char *protocol);
int iptablesAddOutputFixUdpChecksum (iptablesContext *ctx,
const char *iface,
--
1.8.1.2