mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 21:55:25 +00:00
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:
parent
537e65e7b7
commit
6ccce75240
@ -85,8 +85,13 @@
|
|||||||
</dd>
|
</dd>
|
||||||
<dt><code>forward</code></dt>
|
<dt><code>forward</code></dt>
|
||||||
<dd>Inclusion of the <code>forward</code> element indicates that
|
<dd>Inclusion of the <code>forward</code> element indicates that
|
||||||
the virtual network is to be connected to the physical LAN. If
|
the virtual network is to be connected to the physical
|
||||||
no attributes are set, NAT forwarding will be used for connectivity.
|
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
|
Firewall rules will allow forwarding to any other network device whether
|
||||||
ethernet, wireless, dialup, or VPN. If the <code>dev</code> attribute
|
ethernet, wireless, dialup, or VPN. If the <code>dev</code> attribute
|
||||||
is set, the firewall rules will restrict forwarding to the named
|
is set, the firewall rules will restrict forwarding to the named
|
||||||
@ -118,21 +123,37 @@
|
|||||||
<dl>
|
<dl>
|
||||||
<dt><code>ip</code></dt>
|
<dt><code>ip</code></dt>
|
||||||
<dd>The <code>address</code> attribute defines an IPv4 address in
|
<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
|
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,
|
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
|
</dd><dt><code>tftp</code></dt><dd>Immediately within
|
||||||
the <code>ip</code> element there is an optional <code>tftp</code>
|
the <code>ip</code> element there is an optional <code>tftp</code>
|
||||||
element. The presence of this element and of its attribute
|
element. The presence of this element and of its attribute
|
||||||
<code>root</code> enables TFTP services. The attribute specifies
|
<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>
|
<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
|
</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
|
optional <code>dhcp</code> element. The presence of this element
|
||||||
enables DHCP services on the virtual network. It will further
|
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>
|
<span class="since">Since 0.3.0</span>
|
||||||
</dd>
|
</dd>
|
||||||
<dt><code>range</code></dt>
|
<dt><code>range</code></dt>
|
||||||
|
@ -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
|
static int
|
||||||
networkAddGeneralIptablesRules(struct network_driver *driver,
|
networkAddGeneralIptablesRules(struct network_driver *driver,
|
||||||
virNetworkObjPtr network)
|
virNetworkObjPtr network)
|
||||||
@ -926,9 +985,16 @@ networkAddGeneralIptablesRules(struct network_driver *driver,
|
|||||||
goto err8;
|
goto err8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* add IPv6 general rules, if needed */
|
||||||
|
if (networkAddGeneralIp6tablesRules(driver, network) < 0) {
|
||||||
|
goto err9;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* unwind in reverse order from the point of failure */
|
/* unwind in reverse order from the point of failure */
|
||||||
|
err9:
|
||||||
|
iptablesRemoveForwardAllowCross(driver->iptables, AF_INET, network->def->bridge);
|
||||||
err8:
|
err8:
|
||||||
iptablesRemoveForwardRejectIn(driver->iptables, AF_INET, network->def->bridge);
|
iptablesRemoveForwardRejectIn(driver->iptables, AF_INET, network->def->bridge);
|
||||||
err7:
|
err7:
|
||||||
@ -956,6 +1022,8 @@ networkRemoveGeneralIptablesRules(struct network_driver *driver,
|
|||||||
int ii;
|
int ii;
|
||||||
virNetworkIpDefPtr ipv4def;
|
virNetworkIpDefPtr ipv4def;
|
||||||
|
|
||||||
|
networkRemoveGeneralIp6tablesRules(driver, network);
|
||||||
|
|
||||||
for (ii = 0;
|
for (ii = 0;
|
||||||
(ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
|
(ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
|
||||||
ii++) {
|
ii++) {
|
||||||
@ -984,13 +1052,18 @@ networkAddIpSpecificIptablesRules(struct network_driver *driver,
|
|||||||
virNetworkObjPtr network,
|
virNetworkObjPtr network,
|
||||||
virNetworkIpDefPtr ipdef)
|
virNetworkIpDefPtr ipdef)
|
||||||
{
|
{
|
||||||
if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT &&
|
/* NB: in the case of IPv6, routing rules are added when the
|
||||||
networkAddMasqueradingIptablesRules(driver, network, ipdef) < 0)
|
* forward mode is NAT. This is because IPv6 has no NAT.
|
||||||
return -1;
|
*/
|
||||||
if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE &&
|
|
||||||
networkAddRoutingIptablesRules(driver, network, ipdef) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -999,10 +1072,14 @@ networkRemoveIpSpecificIptablesRules(struct network_driver *driver,
|
|||||||
virNetworkObjPtr network,
|
virNetworkObjPtr network,
|
||||||
virNetworkIpDefPtr ipdef)
|
virNetworkIpDefPtr ipdef)
|
||||||
{
|
{
|
||||||
if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT)
|
if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT) {
|
||||||
networkRemoveMasqueradingIptablesRules(driver, network, ipdef);
|
if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET))
|
||||||
else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE)
|
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);
|
networkRemoveRoutingIptablesRules(driver, network, ipdef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add all rules for all ip addresses (and general rules) on a network */
|
/* Add all rules for all ip addresses (and general rules) on a network */
|
||||||
@ -1084,30 +1161,47 @@ networkEnableIpForwarding(void)
|
|||||||
|
|
||||||
#define SYSCTL_PATH "/proc/sys"
|
#define SYSCTL_PATH "/proc/sys"
|
||||||
|
|
||||||
static int networkDisableIPV6(virNetworkObjPtr network)
|
static int
|
||||||
|
networkSetIPv6Sysctls(virNetworkObjPtr network)
|
||||||
{
|
{
|
||||||
char *field = NULL;
|
char *field = NULL;
|
||||||
int ret = -1;
|
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)) {
|
||||||
virReportOOMError();
|
/* Only set disable_ipv6 if there are no ipv6 addresses defined for
|
||||||
goto cleanup;
|
* 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) {
|
/* The rest of the ipv6 sysctl tunables should always be set,
|
||||||
VIR_DEBUG("ipv6 appears to already be disabled on %s", network->def->bridge);
|
* whether or not we're using ipv6 on this bridge.
|
||||||
ret = 0;
|
*/
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (virFileWriteStr(field, "1", 0) < 0) {
|
/* Prevent guests from hijacking the host network by sending out
|
||||||
virReportSystemError(errno,
|
* their own router advertisements.
|
||||||
_("cannot enable %s"), field);
|
*/
|
||||||
goto cleanup;
|
if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/accept_ra",
|
||||||
}
|
network->def->bridge) < 0) {
|
||||||
VIR_FREE(field);
|
|
||||||
|
|
||||||
if (virAsprintf(&field, SYSCTL_PATH "/net/ipv6/conf/%s/accept_ra", network->def->bridge) < 0) {
|
|
||||||
virReportOOMError();
|
virReportOOMError();
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@ -1119,7 +1213,11 @@ static int networkDisableIPV6(virNetworkObjPtr network)
|
|||||||
}
|
}
|
||||||
VIR_FREE(field);
|
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();
|
virReportOOMError();
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@ -1262,7 +1360,7 @@ networkStartNetworkDaemon(struct network_driver *driver,
|
|||||||
virNetworkObjPtr network)
|
virNetworkObjPtr network)
|
||||||
{
|
{
|
||||||
int ii, err;
|
int ii, err;
|
||||||
bool v4present = false;
|
bool v4present = false, v6present = false;
|
||||||
virErrorPtr save_err = NULL;
|
virErrorPtr save_err = NULL;
|
||||||
virNetworkIpDefPtr ipdef;
|
virNetworkIpDefPtr ipdef;
|
||||||
|
|
||||||
@ -1301,8 +1399,10 @@ networkStartNetworkDaemon(struct network_driver *driver,
|
|||||||
goto err1;
|
goto err1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disable IPv6 on the bridge */
|
/* Disable IPv6 on the bridge if there are no IPv6 addresses
|
||||||
if (networkDisableIPV6(network) < 0)
|
* defined, and set other IPv6 sysctl tunables appropriately.
|
||||||
|
*/
|
||||||
|
if (networkSetIPv6Sysctls(network) < 0)
|
||||||
goto err1;
|
goto err1;
|
||||||
|
|
||||||
/* Add "once per network" rules */
|
/* Add "once per network" rules */
|
||||||
@ -1314,6 +1414,8 @@ networkStartNetworkDaemon(struct network_driver *driver,
|
|||||||
ii++) {
|
ii++) {
|
||||||
if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET))
|
if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET))
|
||||||
v4present = true;
|
v4present = true;
|
||||||
|
if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET6))
|
||||||
|
v6present = true;
|
||||||
|
|
||||||
/* Add the IP address/netmask to the bridge */
|
/* Add the IP address/netmask to the bridge */
|
||||||
if (networkAddAddrToBridge(driver, network, ipdef) < 0) {
|
if (networkAddAddrToBridge(driver, network, ipdef) < 0) {
|
||||||
@ -1708,9 +1810,7 @@ static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) {
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we only support dhcp on one IPv4 address per defined network, and currently
|
/* We only support dhcp on one IPv4 address per defined network */
|
||||||
* don't support IPv6.
|
|
||||||
*/
|
|
||||||
for (ii = 0;
|
for (ii = 0;
|
||||||
(ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
|
(ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
|
||||||
ii++) {
|
ii++) {
|
||||||
@ -1724,14 +1824,6 @@ static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) {
|
|||||||
ipv4def = ipdef;
|
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) {
|
if (ipv4def) {
|
||||||
@ -1756,7 +1848,8 @@ cleanup:
|
|||||||
static int networkUndefine(virNetworkPtr net) {
|
static int networkUndefine(virNetworkPtr net) {
|
||||||
struct network_driver *driver = net->conn->networkPrivateData;
|
struct network_driver *driver = net->conn->networkPrivateData;
|
||||||
virNetworkObjPtr network;
|
virNetworkObjPtr network;
|
||||||
virNetworkIpDefPtr ipv4def;
|
virNetworkIpDefPtr ipdef;
|
||||||
|
bool dhcp_present = false, v6present = false;
|
||||||
int ret = -1, ii;
|
int ret = -1, ii;
|
||||||
|
|
||||||
networkDriverLock(driver);
|
networkDriverLock(driver);
|
||||||
@ -1781,12 +1874,17 @@ static int networkUndefine(virNetworkPtr net) {
|
|||||||
|
|
||||||
/* we only support dhcp on one IPv4 address per defined network */
|
/* we only support dhcp on one IPv4 address per defined network */
|
||||||
for (ii = 0;
|
for (ii = 0;
|
||||||
(ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
|
(ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
|
||||||
ii++) {
|
ii++) {
|
||||||
if (ipv4def->nranges || ipv4def->nhosts)
|
if (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET)) {
|
||||||
break;
|
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);
|
dnsmasqContext *dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR);
|
||||||
if (dctx == NULL)
|
if (dctx == NULL)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
Loading…
Reference in New Issue
Block a user