Turn on IPv6 support in the bridge_driver.c virtual network driver

At this point everything is already in place to make IPv6 happen, we just
need to add a few rules, remove some checks for IPv4-only, and document
the changes to the XML on the website.
This commit is contained in:
Laine Stump 2010-12-16 15:50:01 -05:00
parent 537e65e7b7
commit 6ccce75240
2 changed files with 172 additions and 53 deletions

View File

@ -85,8 +85,13 @@
</dd>
<dt><code>forward</code></dt>
<dd>Inclusion of the <code>forward</code> 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 <code>mode</code> 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 <code>mode</code> 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 <code>dev</code> attribute
is set, the firewall rules will restrict forwarding to the named
@ -118,21 +123,37 @@
<dl>
<dt><code>ip</code></dt>
<dd>The <code>address</code> 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 <code>netmask</code>
address will be their default route. For IPv4 addresses, the <code>netmask</code>
attribute defines the significant bits of the network address,
again specified in dotted-decimal format. <span class="since">Since 0.3.0</span>
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 <code>prefix</code>
attribute, which is an integer (for example, <code>netmask='255.255.255.0'</code>
could also be given as <code>prefix='24'</code>. The <code>family</code>
attribute is used to specify the type of address - 'ipv4' or 'ipv6'; if no
<code>family</code> 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
<code>dhcp</code> or <code>tftp</code> element. <span class="since">Since 0.3.0;
IPv6, multiple addresses on a single network, <code>family</code>, and
<code>prefix</code> since 0.8.7</span>
</dd><dt><code>tftp</code></dt><dd>Immediately within
the <code>ip</code> element there is an optional <code>tftp</code>
element. The presence of this element and of its attribute
<code>root</code> enables TFTP services. The attribute specifies
the path to the root directory served via TFTP.
the path to the root directory served via TFTP. <code>tftp</code> is not
supported for IPv6 addresses, can only be specified on a single IPv4 address
per network.
<span class="since">Since 0.7.1</span>
</dd><dt><code>dhcp</code></dt><dd>Also within the <code>ip</code> element there is an
optional <code>dhcp</code> element. The presence of this element
enables DHCP services on the virtual network. It will further
contain one or more <code>range</code> elements.
contain one or more <code>range</code> elements. The
<code>dhcp</code> element is not supported for IPv6, and
is only supported on a single IP address per network for IPv4.
<span class="since">Since 0.3.0</span>
</dd>
<dt><code>range</code></dt>

View File

@ -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)
if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT) {
if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET))
networkRemoveMasqueradingIptablesRules(driver, network, ipdef);
else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE)
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) {
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);
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 enable %s"), field);
_("cannot write to %s to disable IPv6 on bridge %s"),
field, network->def->bridge);
goto cleanup;
}
VIR_FREE(field);
}
if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/accept_ra", network->def->bridge) < 0) {
/* The rest of the ipv6 sysctl tunables should always be set,
* whether or not we're using ipv6 on this bridge.
*/
/* 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;