diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 7a245186b4..b1b04859ba 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -85,8 +85,13 @@
forward
Inclusion of the forward
element indicates that
- the virtual network is to be connected to the physical LAN. If
- no attributes are set, NAT forwarding will be used for connectivity.
+ the virtual network is to be connected to the physical
+ LAN. the mode
attribute determines the method of
+ forwarding; possible selections are 'nat' and 'route'. If mode
+ is not specified, NAT forwarding will be used for
+ connectivity. If a network has any IPv6 addresses defined,
+ even if mode
is given as 'nat', the IPv6 traffic
+ will be forwarded using routing, since IPv6 has no concept of NAT.
Firewall rules will allow forwarding to any other network device whether
ethernet, wireless, dialup, or VPN. If the dev
attribute
is set, the firewall rules will restrict forwarding to the named
@@ -118,21 +123,37 @@
ip
- The
address
attribute defines an IPv4 address in
- dotted-decimal format, that will be configured on the bridge
+ dotted-decimal format, or an IPv6 address in standard
+ colon-separated hexadecimal format, that will be configured on
+ the bridge
device associated with the virtual network. To the guests this
- address will be their default route. The netmask
+ address will be their default route. For IPv4 addresses, the netmask
attribute defines the significant bits of the network address,
- again specified in dotted-decimal format. Since 0.3.0
+ again specified in dotted-decimal format. For IPv6 addresses,
+ and as an alternate method for IPv4 addresses, you can specify
+ the significant bits of the network address with the prefix
+ attribute, which is an integer (for example, netmask='255.255.255.0'
+ could also be given as prefix='24'
. The family
+ attribute is used to specify the type of address - 'ipv4' or 'ipv6'; if no
+ family
is given, 'ipv4' is assumed. A network can have more than
+ one of each family of address defined, but only a single address can have a
+ dhcp
or tftp
element. Since 0.3.0;
+ IPv6, multiple addresses on a single network, family
, and
+ prefix
since 0.8.7
tftp
- Immediately within
the
ip
element there is an optional tftp
element. The presence of this element and of its attribute
root
enables TFTP services. The attribute specifies
- the path to the root directory served via TFTP.
+ the path to the root directory served via TFTP. tftp
is not
+ supported for IPv6 addresses, can only be specified on a single IPv4 address
+ per network.
Since 0.7.1
dhcp
- Also within the
ip
element there is an
optional dhcp
element. The presence of this element
enables DHCP services on the virtual network. It will further
- contain one or more range
elements.
+ contain one or more range
elements. The
+ dhcp
element is not supported for IPv6, and
+ is only supported on a single IP address per network for IPv4.
Since 0.3.0
range
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 0c37eb6101..07791f152a 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -824,6 +824,65 @@ networkRemoveRoutingIptablesRules(struct network_driver *driver,
}
}
+/* Add all once/network rules required for IPv6 (if any IPv6 addresses are defined) */
+static int
+networkAddGeneralIp6tablesRules(struct network_driver *driver,
+ virNetworkObjPtr network)
+{
+
+ if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0))
+ return 0;
+
+ /* Catch all rules to block forwarding to/from bridges */
+
+ if (iptablesAddForwardRejectOut(driver->iptables, AF_INET6,
+ network->def->bridge) < 0) {
+ networkReportError(VIR_ERR_SYSTEM_ERROR,
+ _("failed to add ip6tables rule to block outbound traffic from '%s'"),
+ network->def->bridge);
+ goto err1;
+ }
+
+ if (iptablesAddForwardRejectIn(driver->iptables, AF_INET6,
+ network->def->bridge) < 0) {
+ networkReportError(VIR_ERR_SYSTEM_ERROR,
+ _("failed to add ip6tables rule to block inbound traffic to '%s'"),
+ network->def->bridge);
+ goto err2;
+ }
+
+ /* Allow traffic between guests on the same bridge */
+ if (iptablesAddForwardAllowCross(driver->iptables, AF_INET6,
+ network->def->bridge) < 0) {
+ networkReportError(VIR_ERR_SYSTEM_ERROR,
+ _("failed to add ip6tables rule to allow cross bridge traffic on '%s'"),
+ network->def->bridge);
+ goto err3;
+ }
+
+ return 0;
+
+ /* unwind in reverse order from the point of failure */
+err3:
+ iptablesRemoveForwardRejectIn(driver->iptables, AF_INET6, network->def->bridge);
+err2:
+ iptablesRemoveForwardRejectOut(driver->iptables, AF_INET6, network->def->bridge);
+err1:
+ return -1;
+}
+
+static void
+networkRemoveGeneralIp6tablesRules(struct network_driver *driver,
+ virNetworkObjPtr network)
+{
+ if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0))
+ return;
+
+ iptablesRemoveForwardAllowCross(driver->iptables, AF_INET6, network->def->bridge);
+ iptablesRemoveForwardRejectIn(driver->iptables, AF_INET6, network->def->bridge);
+ iptablesRemoveForwardRejectOut(driver->iptables, AF_INET6, network->def->bridge);
+}
+
static int
networkAddGeneralIptablesRules(struct network_driver *driver,
virNetworkObjPtr network)
@@ -926,9 +985,16 @@ networkAddGeneralIptablesRules(struct network_driver *driver,
goto err8;
}
+ /* add IPv6 general rules, if needed */
+ if (networkAddGeneralIp6tablesRules(driver, network) < 0) {
+ goto err9;
+ }
+
return 0;
/* unwind in reverse order from the point of failure */
+err9:
+ iptablesRemoveForwardAllowCross(driver->iptables, AF_INET, network->def->bridge);
err8:
iptablesRemoveForwardRejectIn(driver->iptables, AF_INET, network->def->bridge);
err7:
@@ -956,6 +1022,8 @@ networkRemoveGeneralIptablesRules(struct network_driver *driver,
int ii;
virNetworkIpDefPtr ipv4def;
+ networkRemoveGeneralIp6tablesRules(driver, network);
+
for (ii = 0;
(ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
ii++) {
@@ -984,13 +1052,18 @@ networkAddIpSpecificIptablesRules(struct network_driver *driver,
virNetworkObjPtr network,
virNetworkIpDefPtr ipdef)
{
- if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT &&
- networkAddMasqueradingIptablesRules(driver, network, ipdef) < 0)
- return -1;
- if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE &&
- networkAddRoutingIptablesRules(driver, network, ipdef) < 0)
- return -1;
+ /* NB: in the case of IPv6, routing rules are added when the
+ * forward mode is NAT. This is because IPv6 has no NAT.
+ */
+ if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT) {
+ if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET))
+ return networkAddMasqueradingIptablesRules(driver, network, ipdef);
+ else if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET6))
+ return networkAddRoutingIptablesRules(driver, network, ipdef);
+ } else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE) {
+ return networkAddRoutingIptablesRules(driver, network, ipdef);
+ }
return 0;
}
@@ -999,10 +1072,14 @@ networkRemoveIpSpecificIptablesRules(struct network_driver *driver,
virNetworkObjPtr network,
virNetworkIpDefPtr ipdef)
{
- if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT)
- networkRemoveMasqueradingIptablesRules(driver, network, ipdef);
- else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE)
+ if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT) {
+ if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET))
+ networkRemoveMasqueradingIptablesRules(driver, network, ipdef);
+ else if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET6))
+ networkRemoveRoutingIptablesRules(driver, network, ipdef);
+ } else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE) {
networkRemoveRoutingIptablesRules(driver, network, ipdef);
+ }
}
/* Add all rules for all ip addresses (and general rules) on a network */
@@ -1084,30 +1161,47 @@ networkEnableIpForwarding(void)
#define SYSCTL_PATH "/proc/sys"
-static int networkDisableIPV6(virNetworkObjPtr network)
+static int
+networkSetIPv6Sysctls(virNetworkObjPtr network)
{
char *field = NULL;
int ret = -1;
- if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/disable_ipv6", network->def->bridge) < 0) {
- virReportOOMError();
- goto cleanup;
+ if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) {
+ /* Only set disable_ipv6 if there are no ipv6 addresses defined for
+ * the network.
+ */
+ if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/disable_ipv6",
+ network->def->bridge) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (access(field, W_OK) < 0 && errno == ENOENT) {
+ VIR_DEBUG("ipv6 appears to already be disabled on %s",
+ network->def->bridge);
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (virFileWriteStr(field, "1", 0) < 0) {
+ virReportSystemError(errno,
+ _("cannot write to %s to disable IPv6 on bridge %s"),
+ field, network->def->bridge);
+ goto cleanup;
+ }
+ VIR_FREE(field);
}
- if (access(field, W_OK) < 0 && errno == ENOENT) {
- VIR_DEBUG("ipv6 appears to already be disabled on %s", network->def->bridge);
- ret = 0;
- goto cleanup;
- }
+ /* The rest of the ipv6 sysctl tunables should always be set,
+ * whether or not we're using ipv6 on this bridge.
+ */
- if (virFileWriteStr(field, "1", 0) < 0) {
- virReportSystemError(errno,
- _("cannot enable %s"), field);
- goto cleanup;
- }
- VIR_FREE(field);
-
- if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/accept_ra", network->def->bridge) < 0) {
+ /* Prevent guests from hijacking the host network by sending out
+ * their own router advertisements.
+ */
+ if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/accept_ra",
+ network->def->bridge) < 0) {
virReportOOMError();
goto cleanup;
}
@@ -1119,7 +1213,11 @@ static int networkDisableIPV6(virNetworkObjPtr network)
}
VIR_FREE(field);
- if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/autoconf", network->def->bridge) < 0) {
+ /* All interfaces used as a gateway (which is what this is, by
+ * definition), must always have autoconf=0.
+ */
+ if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/autoconf",
+ network->def->bridge) < 0) {
virReportOOMError();
goto cleanup;
}
@@ -1262,7 +1360,7 @@ networkStartNetworkDaemon(struct network_driver *driver,
virNetworkObjPtr network)
{
int ii, err;
- bool v4present = false;
+ bool v4present = false, v6present = false;
virErrorPtr save_err = NULL;
virNetworkIpDefPtr ipdef;
@@ -1301,8 +1399,10 @@ networkStartNetworkDaemon(struct network_driver *driver,
goto err1;
}
- /* Disable IPv6 on the bridge */
- if (networkDisableIPV6(network) < 0)
+ /* Disable IPv6 on the bridge if there are no IPv6 addresses
+ * defined, and set other IPv6 sysctl tunables appropriately.
+ */
+ if (networkSetIPv6Sysctls(network) < 0)
goto err1;
/* Add "once per network" rules */
@@ -1314,6 +1414,8 @@ networkStartNetworkDaemon(struct network_driver *driver,
ii++) {
if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET))
v4present = true;
+ if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET6))
+ v6present = true;
/* Add the IP address/netmask to the bridge */
if (networkAddAddrToBridge(driver, network, ipdef) < 0) {
@@ -1708,9 +1810,7 @@ static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) {
goto cleanup;
}
- /* we only support dhcp on one IPv4 address per defined network, and currently
- * don't support IPv6.
- */
+ /* We only support dhcp on one IPv4 address per defined network */
for (ii = 0;
(ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
ii++) {
@@ -1724,14 +1824,6 @@ static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) {
ipv4def = ipdef;
}
}
- } else {
- /* we currently only support IPv4 */
- networkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("unsupported address family '%s' (%d) in network definition"),
- ipdef->family ? ipdef->family : "unspecified",
- VIR_SOCKET_FAMILY(&ipdef->address));
- goto cleanup;
-
}
}
if (ipv4def) {
@@ -1756,7 +1848,8 @@ cleanup:
static int networkUndefine(virNetworkPtr net) {
struct network_driver *driver = net->conn->networkPrivateData;
virNetworkObjPtr network;
- virNetworkIpDefPtr ipv4def;
+ virNetworkIpDefPtr ipdef;
+ bool dhcp_present = false, v6present = false;
int ret = -1, ii;
networkDriverLock(driver);
@@ -1781,12 +1874,17 @@ static int networkUndefine(virNetworkPtr net) {
/* we only support dhcp on one IPv4 address per defined network */
for (ii = 0;
- (ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
+ (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
ii++) {
- if (ipv4def->nranges || ipv4def->nhosts)
- break;
+ if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET)) {
+ if (ipdef->nranges || ipdef->nhosts)
+ dhcp_present = true;
+ } else if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET6)) {
+ v6present = true;
+ }
}
- if (ipv4def) {
+
+ if (dhcp_present) {
dnsmasqContext *dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR);
if (dctx == NULL)
goto cleanup;