1
0
mirror of https://gitlab.com/libvirt/libvirt.git synced 2025-03-07 17:28:15 +00:00

network: move all functions manipulating iptables rules into network_iptables.c

Although initially we will add exactly the same rules for the nftables
backend, the two may (hopefully) soon diverge as we take advantage of
nftables features that weren't available in iptables. When we do that,
there will need to be a different version of these functions (currently in
bridge_driver_linux.c) for each backend:

  networkAddFirewallRules()
  networkRemoveFirewallRules()
  networkSetupPrivateChains()

Although it will mean duplicating some amount of code (with just the
function names changed) for the nftables backend, this patch moves all
of the rule-related code in the above three functions into iptables*()
functions in network_iptables.c, and changes the functions in
bridge_driver_linux.c to call the iptables*() functions. When we make
a different backend, it will only need to make equivalents of those 3
functions publicly available to the upper layer.

Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Laine Stump 2024-04-19 22:19:42 -04:00
parent e1f6d2c205
commit 01fd85fed9
3 changed files with 574 additions and 551 deletions

View File

@ -301,512 +301,10 @@ int networkCheckRouteCollision(virNetworkDef *def)
return 0;
}
static const char networkLocalMulticastIPv4[] = "224.0.0.0/24";
static const char networkLocalMulticastIPv6[] = "ff02::/16";
static const char networkLocalBroadcast[] = "255.255.255.255/32";
static int
networkAddMasqueradingFirewallRules(virFirewall *fw,
virNetworkDef *def,
virNetworkIPDef *ipdef)
int
networkAddFirewallRules(virNetworkDef *def)
{
int prefix = virNetworkIPDefPrefix(ipdef);
const char *forwardIf = virNetworkDefForwardIf(def, 0);
bool isIPv4 = VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET);
if (prefix < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid prefix or netmask for '%1$s'"),
def->bridge);
return -1;
}
/* allow forwarding packets from the bridge interface */
if (iptablesAddForwardAllowOut(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
/* allow forwarding packets to the bridge interface if they are
* part of an existing connection
*/
if (iptablesAddForwardAllowRelatedIn(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
/*
* Enable masquerading.
*
* We need to end up with 5 rules in the table in this order
*
* 1. do not masquerade packets targeting 224.0.0.0/24
* 2. do not masquerade packets targeting 255.255.255.255/32
* 3. masquerade protocol=tcp with sport mapping restriction
* 4. masquerade protocol=udp with sport mapping restriction
* 5. generic, masquerade any protocol
*
* 224.0.0.0/24 is the local network multicast range. Packets are not
* forwarded outside.
*
* 255.255.255.255/32 is the broadcast address of any local network. Again,
* such packets are never forwarded, but strict DHCP clients don't accept
* DHCP replies with changed source ports.
*
* The sport mappings are required, because default IPtables
* MASQUERADE maintain port numbers unchanged where possible.
*
* NFS can be configured to only "trust" port numbers < 1023.
*
* Guests using NAT thus need to be prevented from having port
* numbers < 1023, otherwise they can bypass the NFS "security"
* check on the source port number.
*
* Since we use '--insert' to add rules to the header of the
* chain, we actually need to add them in the reverse of the
* order just mentioned !
*/
/* First the generic masquerade rule for other protocols */
if (iptablesAddForwardMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
&def->forward.addr,
&def->forward.port,
NULL) < 0)
return -1;
/* UDP with a source port restriction */
if (iptablesAddForwardMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
&def->forward.addr,
&def->forward.port,
"udp") < 0)
return -1;
/* TCP with a source port restriction */
if (iptablesAddForwardMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
&def->forward.addr,
&def->forward.port,
"tcp") < 0)
return -1;
/* exempt local network broadcast address as destination */
if (isIPv4 &&
iptablesAddDontMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
networkLocalBroadcast) < 0)
return -1;
/* exempt local multicast range as destination */
if (iptablesAddDontMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
isIPv4 ? networkLocalMulticastIPv4 :
networkLocalMulticastIPv6) < 0)
return -1;
return 0;
}
static int
networkRemoveMasqueradingFirewallRules(virFirewall *fw,
virNetworkDef *def,
virNetworkIPDef *ipdef)
{
int prefix = virNetworkIPDefPrefix(ipdef);
const char *forwardIf = virNetworkDefForwardIf(def, 0);
bool isIPv4 = VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET);
if (prefix < 0)
return 0;
if (iptablesRemoveDontMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
isIPv4 ? networkLocalMulticastIPv4 :
networkLocalMulticastIPv6) < 0)
return -1;
if (isIPv4 &&
iptablesRemoveDontMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
networkLocalBroadcast) < 0)
return -1;
if (iptablesRemoveForwardMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
&def->forward.addr,
&def->forward.port,
"tcp") < 0)
return -1;
if (iptablesRemoveForwardMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
&def->forward.addr,
&def->forward.port,
"udp") < 0)
return -1;
if (iptablesRemoveForwardMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
&def->forward.addr,
&def->forward.port,
NULL) < 0)
return -1;
if (iptablesRemoveForwardAllowRelatedIn(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
if (iptablesRemoveForwardAllowOut(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
return 0;
}
static int
networkAddRoutingFirewallRules(virFirewall *fw,
virNetworkDef *def,
virNetworkIPDef *ipdef)
{
int prefix = virNetworkIPDefPrefix(ipdef);
const char *forwardIf = virNetworkDefForwardIf(def, 0);
if (prefix < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid prefix or netmask for '%1$s'"),
def->bridge);
return -1;
}
/* allow routing packets from the bridge interface */
if (iptablesAddForwardAllowOut(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
/* allow routing packets to the bridge interface */
if (iptablesAddForwardAllowIn(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
return 0;
}
static int
networkRemoveRoutingFirewallRules(virFirewall *fw,
virNetworkDef *def,
virNetworkIPDef *ipdef)
{
int prefix = virNetworkIPDefPrefix(ipdef);
const char *forwardIf = virNetworkDefForwardIf(def, 0);
if (prefix < 0)
return 0;
if (iptablesRemoveForwardAllowIn(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
if (iptablesRemoveForwardAllowOut(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
return 0;
}
static void
networkAddGeneralIPv4FirewallRules(virFirewall *fw,
virNetworkDef *def)
{
size_t i;
virNetworkIPDef *ipv4def;
/* First look for first IPv4 address that has dhcp or tftpboot defined. */
/* We support dhcp config on 1 IPv4 interface only. */
for (i = 0;
(ipv4def = virNetworkDefGetIPByIndex(def, AF_INET, i));
i++) {
if (ipv4def->nranges || ipv4def->nhosts || ipv4def->tftproot)
break;
}
/* allow DHCP requests through to dnsmasq & back out */
iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
iptablesAddTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
/* allow DNS requests through to dnsmasq & back out */
iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesAddTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
/* allow TFTP requests through to dnsmasq if necessary & back out */
if (ipv4def && ipv4def->tftproot) {
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
}
/* Catch all rules to block forwarding to/from bridges */
iptablesAddForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
iptablesAddForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
/* Allow traffic between guests on the same bridge */
iptablesAddForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
}
static void
networkRemoveGeneralIPv4FirewallRules(virFirewall *fw,
virNetworkDef *def)
{
size_t i;
virNetworkIPDef *ipv4def;
for (i = 0;
(ipv4def = virNetworkDefGetIPByIndex(def, AF_INET, i));
i++) {
if (ipv4def->nranges || ipv4def->nhosts || ipv4def->tftproot)
break;
}
iptablesRemoveForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
iptablesRemoveForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
iptablesRemoveForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
if (ipv4def && ipv4def->tftproot) {
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
}
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesRemoveTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
iptablesRemoveTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
}
/* Add all once/network rules required for IPv6.
* If no IPv6 addresses are defined and <network ipv6='yes'> is
* specified, then allow IPv6 communications between virtual systems.
* If any IPv6 addresses are defined, then add the rules for regular operation.
*/
static void
networkAddGeneralIPv6FirewallRules(virFirewall *fw,
virNetworkDef *def)
{
if (!virNetworkDefGetIPByIndex(def, AF_INET6, 0) &&
!def->ipv6nogw) {
return;
}
/* Catch all rules to block forwarding to/from bridges */
iptablesAddForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
iptablesAddForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
/* Allow traffic between guests on the same bridge */
iptablesAddForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
if (virNetworkDefGetIPByIndex(def, AF_INET6, 0)) {
/* allow DNS over IPv6 & back out */
iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
iptablesAddTcpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
/* allow DHCPv6 & back out */
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 547);
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 546);
}
}
static void
networkRemoveGeneralIPv6FirewallRules(virFirewall *fw,
virNetworkDef *def)
{
if (!virNetworkDefGetIPByIndex(def, AF_INET6, 0) &&
!def->ipv6nogw) {
return;
}
if (virNetworkDefGetIPByIndex(def, AF_INET6, 0)) {
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 546);
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 547);
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
iptablesRemoveTcpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
}
/* the following rules are there if no IPv6 address has been defined
* but def->ipv6nogw == true
*/
iptablesRemoveForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
iptablesRemoveForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
iptablesRemoveForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
}
static void
networkAddGeneralFirewallRules(virFirewall *fw,
virNetworkDef *def)
{
networkAddGeneralIPv4FirewallRules(fw, def);
networkAddGeneralIPv6FirewallRules(fw, def);
}
static void
networkRemoveGeneralFirewallRules(virFirewall *fw,
virNetworkDef *def)
{
networkRemoveGeneralIPv4FirewallRules(fw, def);
networkRemoveGeneralIPv6FirewallRules(fw, def);
}
static void
networkAddChecksumFirewallRules(virFirewall *fw,
virNetworkDef *def)
{
size_t i;
virNetworkIPDef *ipv4def;
/* First look for first IPv4 address that has dhcp or tftpboot defined. */
/* We support dhcp config on 1 IPv4 interface only. */
for (i = 0;
(ipv4def = virNetworkDefGetIPByIndex(def, AF_INET, i));
i++) {
if (ipv4def->nranges || ipv4def->nhosts)
break;
}
/* If we are doing local DHCP service on this network, attempt to
* add a rule that will fixup the checksum of DHCP response
* packets back to the guests (but report failure without
* aborting, since not all iptables implementations support it).
*/
if (ipv4def)
iptablesAddOutputFixUdpChecksum(fw, def->bridge, 68);
}
static void
networkRemoveChecksumFirewallRules(virFirewall *fw,
virNetworkDef *def)
{
size_t i;
virNetworkIPDef *ipv4def;
/* First look for first IPv4 address that has dhcp or tftpboot defined. */
/* We support dhcp config on 1 IPv4 interface only. */
for (i = 0;
(ipv4def = virNetworkDefGetIPByIndex(def, AF_INET, i));
i++) {
if (ipv4def->nranges || ipv4def->nhosts)
break;
}
if (ipv4def)
iptablesRemoveOutputFixUdpChecksum(fw, def->bridge, 68);
}
static int
networkAddIPSpecificFirewallRules(virFirewall *fw,
virNetworkDef *def,
virNetworkIPDef *ipdef)
{
/* NB: in the case of IPv6, routing rules are added when the
* forward mode is NAT. This is because IPv6 has no NAT.
*/
if (def->forward.type == VIR_NETWORK_FORWARD_NAT) {
if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) ||
def->forward.natIPv6 == VIR_TRISTATE_BOOL_YES)
return networkAddMasqueradingFirewallRules(fw, def, ipdef);
else if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
return networkAddRoutingFirewallRules(fw, def, ipdef);
} else if (def->forward.type == VIR_NETWORK_FORWARD_ROUTE) {
return networkAddRoutingFirewallRules(fw, def, ipdef);
}
return 0;
}
static int
networkRemoveIPSpecificFirewallRules(virFirewall *fw,
virNetworkDef *def,
virNetworkIPDef *ipdef)
{
if (def->forward.type == VIR_NETWORK_FORWARD_NAT) {
if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) ||
def->forward.natIPv6 == VIR_TRISTATE_BOOL_YES)
return networkRemoveMasqueradingFirewallRules(fw, def, ipdef);
else if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
return networkRemoveRoutingFirewallRules(fw, def, ipdef);
} else if (def->forward.type == VIR_NETWORK_FORWARD_ROUTE) {
return networkRemoveRoutingFirewallRules(fw, def, ipdef);
}
return 0;
}
/* Add all rules for all ip addresses (and general rules) on a network */
int networkAddFirewallRules(virNetworkDef *def)
{
size_t i;
virNetworkIPDef *ipdef;
g_autoptr(virFirewall) fw = virFirewallNew();
if (virOnce(&createdOnce, networkSetupPrivateChains) < 0)
return -1;
@ -891,52 +389,12 @@ int networkAddFirewallRules(virNetworkDef *def)
}
}
virFirewallStartTransaction(fw, 0);
networkAddGeneralFirewallRules(fw, def);
for (i = 0;
(ipdef = virNetworkDefGetIPByIndex(def, AF_UNSPEC, i));
i++) {
if (networkAddIPSpecificFirewallRules(fw, def, ipdef) < 0)
return -1;
}
virFirewallStartRollback(fw, 0);
for (i = 0;
(ipdef = virNetworkDefGetIPByIndex(def, AF_UNSPEC, i));
i++) {
if (networkRemoveIPSpecificFirewallRules(fw, def, ipdef) < 0)
return -1;
}
networkRemoveGeneralFirewallRules(fw, def);
virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
networkAddChecksumFirewallRules(fw, def);
return virFirewallApply(fw);
return iptablesAddFirewallRules(def);
}
/* Remove all rules for all ip addresses (and general rules) on a network */
void networkRemoveFirewallRules(virNetworkDef *def)
void
networkRemoveFirewallRules(virNetworkDef *def)
{
size_t i;
virNetworkIPDef *ipdef;
g_autoptr(virFirewall) fw = virFirewallNew();
virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
networkRemoveChecksumFirewallRules(fw, def);
virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
for (i = 0;
(ipdef = virNetworkDefGetIPByIndex(def, AF_UNSPEC, i));
i++) {
if (networkRemoveIPSpecificFirewallRules(fw, def, ipdef) < 0)
return;
}
networkRemoveGeneralFirewallRules(fw, def);
virFirewallApply(fw);
iptablesRemoveFirewallRules(def);
}

View File

@ -1,5 +1,6 @@
/*
* network_iptables.c: helper APIs for managing iptables in network driver
* network_iptables.c: iptables-based firewall implementation for
* virtual networks.
*
* Copyright (C) 2007-2014 Red Hat, Inc.
*
@ -1070,3 +1071,562 @@ iptablesRemoveOutputFixUdpChecksum(virFirewall *fw,
{
iptablesOutputFixUdpChecksum(fw, iface, port, VIR_NETFILTER_DELETE);
}
static const char networkLocalMulticastIPv4[] = "224.0.0.0/24";
static const char networkLocalMulticastIPv6[] = "ff02::/16";
static const char networkLocalBroadcast[] = "255.255.255.255/32";
static int
iptablesAddMasqueradingFirewallRules(virFirewall *fw,
virNetworkDef *def,
virNetworkIPDef *ipdef)
{
int prefix = virNetworkIPDefPrefix(ipdef);
const char *forwardIf = virNetworkDefForwardIf(def, 0);
bool isIPv4 = VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET);
if (prefix < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid prefix or netmask for '%1$s'"),
def->bridge);
return -1;
}
/* allow forwarding packets from the bridge interface */
if (iptablesAddForwardAllowOut(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
/* allow forwarding packets to the bridge interface if they are
* part of an existing connection
*/
if (iptablesAddForwardAllowRelatedIn(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
/*
* Enable masquerading.
*
* We need to end up with 5 rules in the table in this order
*
* 1. do not masquerade packets targeting 224.0.0.0/24
* 2. do not masquerade packets targeting 255.255.255.255/32
* 3. masquerade protocol=tcp with sport mapping restriction
* 4. masquerade protocol=udp with sport mapping restriction
* 5. generic, masquerade any protocol
*
* 224.0.0.0/24 is the local network multicast range. Packets are not
* forwarded outside.
*
* 255.255.255.255/32 is the broadcast address of any local network. Again,
* such packets are never forwarded, but strict DHCP clients don't accept
* DHCP replies with changed source ports.
*
* The sport mappings are required, because default IPtables
* MASQUERADE maintain port numbers unchanged where possible.
*
* NFS can be configured to only "trust" port numbers < 1023.
*
* Guests using NAT thus need to be prevented from having port
* numbers < 1023, otherwise they can bypass the NFS "security"
* check on the source port number.
*
* Since we use '--insert' to add rules to the header of the
* chain, we actually need to add them in the reverse of the
* order just mentioned !
*/
/* First the generic masquerade rule for other protocols */
if (iptablesAddForwardMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
&def->forward.addr,
&def->forward.port,
NULL) < 0)
return -1;
/* UDP with a source port restriction */
if (iptablesAddForwardMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
&def->forward.addr,
&def->forward.port,
"udp") < 0)
return -1;
/* TCP with a source port restriction */
if (iptablesAddForwardMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
&def->forward.addr,
&def->forward.port,
"tcp") < 0)
return -1;
/* exempt local network broadcast address as destination */
if (isIPv4 &&
iptablesAddDontMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
networkLocalBroadcast) < 0)
return -1;
/* exempt local multicast range as destination */
if (iptablesAddDontMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
isIPv4 ? networkLocalMulticastIPv4 :
networkLocalMulticastIPv6) < 0)
return -1;
return 0;
}
static int
iptablesRemoveMasqueradingFirewallRules(virFirewall *fw,
virNetworkDef *def,
virNetworkIPDef *ipdef)
{
int prefix = virNetworkIPDefPrefix(ipdef);
const char *forwardIf = virNetworkDefForwardIf(def, 0);
bool isIPv4 = VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET);
if (prefix < 0)
return 0;
if (iptablesRemoveDontMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
isIPv4 ? networkLocalMulticastIPv4 :
networkLocalMulticastIPv6) < 0)
return -1;
if (isIPv4 &&
iptablesRemoveDontMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
networkLocalBroadcast) < 0)
return -1;
if (iptablesRemoveForwardMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
&def->forward.addr,
&def->forward.port,
"tcp") < 0)
return -1;
if (iptablesRemoveForwardMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
&def->forward.addr,
&def->forward.port,
"udp") < 0)
return -1;
if (iptablesRemoveForwardMasquerade(fw,
&ipdef->address,
prefix,
forwardIf,
&def->forward.addr,
&def->forward.port,
NULL) < 0)
return -1;
if (iptablesRemoveForwardAllowRelatedIn(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
if (iptablesRemoveForwardAllowOut(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
return 0;
}
static int
iptablesAddRoutingFirewallRules(virFirewall *fw,
virNetworkDef *def,
virNetworkIPDef *ipdef)
{
int prefix = virNetworkIPDefPrefix(ipdef);
const char *forwardIf = virNetworkDefForwardIf(def, 0);
if (prefix < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid prefix or netmask for '%1$s'"),
def->bridge);
return -1;
}
/* allow routing packets from the bridge interface */
if (iptablesAddForwardAllowOut(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
/* allow routing packets to the bridge interface */
if (iptablesAddForwardAllowIn(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
return 0;
}
static int
iptablesRemoveRoutingFirewallRules(virFirewall *fw,
virNetworkDef *def,
virNetworkIPDef *ipdef)
{
int prefix = virNetworkIPDefPrefix(ipdef);
const char *forwardIf = virNetworkDefForwardIf(def, 0);
if (prefix < 0)
return 0;
if (iptablesRemoveForwardAllowIn(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
if (iptablesRemoveForwardAllowOut(fw,
&ipdef->address,
prefix,
def->bridge,
forwardIf) < 0)
return -1;
return 0;
}
static void
iptablesAddGeneralIPv4FirewallRules(virFirewall *fw,
virNetworkDef *def)
{
size_t i;
virNetworkIPDef *ipv4def;
/* First look for first IPv4 address that has dhcp or tftpboot defined. */
/* We support dhcp config on 1 IPv4 interface only. */
for (i = 0;
(ipv4def = virNetworkDefGetIPByIndex(def, AF_INET, i));
i++) {
if (ipv4def->nranges || ipv4def->nhosts || ipv4def->tftproot)
break;
}
/* allow DHCP requests through to dnsmasq & back out */
iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
iptablesAddTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
/* allow DNS requests through to dnsmasq & back out */
iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesAddTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
/* allow TFTP requests through to dnsmasq if necessary & back out */
if (ipv4def && ipv4def->tftproot) {
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
}
/* Catch all rules to block forwarding to/from bridges */
iptablesAddForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
iptablesAddForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
/* Allow traffic between guests on the same bridge */
iptablesAddForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
}
static void
iptablesRemoveGeneralIPv4FirewallRules(virFirewall *fw,
virNetworkDef *def)
{
size_t i;
virNetworkIPDef *ipv4def;
for (i = 0;
(ipv4def = virNetworkDefGetIPByIndex(def, AF_INET, i));
i++) {
if (ipv4def->nranges || ipv4def->nhosts || ipv4def->tftproot)
break;
}
iptablesRemoveForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
iptablesRemoveForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
iptablesRemoveForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
if (ipv4def && ipv4def->tftproot) {
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
}
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesRemoveTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
iptablesRemoveTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
}
/* Add all once/network rules required for IPv6.
* If no IPv6 addresses are defined and <network ipv6='yes'> is
* specified, then allow IPv6 communications between virtual systems.
* If any IPv6 addresses are defined, then add the rules for regular operation.
*/
static void
iptablesAddGeneralIPv6FirewallRules(virFirewall *fw,
virNetworkDef *def)
{
if (!virNetworkDefGetIPByIndex(def, AF_INET6, 0) &&
!def->ipv6nogw) {
return;
}
/* Catch all rules to block forwarding to/from bridges */
iptablesAddForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
iptablesAddForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
/* Allow traffic between guests on the same bridge */
iptablesAddForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
if (virNetworkDefGetIPByIndex(def, AF_INET6, 0)) {
/* allow DNS over IPv6 & back out */
iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
iptablesAddTcpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
/* allow DHCPv6 & back out */
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 547);
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 546);
}
}
static void
iptablesRemoveGeneralIPv6FirewallRules(virFirewall *fw,
virNetworkDef *def)
{
if (!virNetworkDefGetIPByIndex(def, AF_INET6, 0) &&
!def->ipv6nogw) {
return;
}
if (virNetworkDefGetIPByIndex(def, AF_INET6, 0)) {
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 546);
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 547);
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
iptablesRemoveTcpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
}
/* the following rules are there if no IPv6 address has been defined
* but def->ipv6nogw == true
*/
iptablesRemoveForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
iptablesRemoveForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
iptablesRemoveForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
}
static void
iptablesAddGeneralFirewallRules(virFirewall *fw,
virNetworkDef *def)
{
iptablesAddGeneralIPv4FirewallRules(fw, def);
iptablesAddGeneralIPv6FirewallRules(fw, def);
}
static void
iptablesRemoveGeneralFirewallRules(virFirewall *fw,
virNetworkDef *def)
{
iptablesRemoveGeneralIPv4FirewallRules(fw, def);
iptablesRemoveGeneralIPv6FirewallRules(fw, def);
}
static void
iptablesAddChecksumFirewallRules(virFirewall *fw,
virNetworkDef *def)
{
size_t i;
virNetworkIPDef *ipv4def;
/* First look for first IPv4 address that has dhcp or tftpboot defined. */
/* We support dhcp config on 1 IPv4 interface only. */
for (i = 0;
(ipv4def = virNetworkDefGetIPByIndex(def, AF_INET, i));
i++) {
if (ipv4def->nranges || ipv4def->nhosts)
break;
}
/* If we are doing local DHCP service on this network, attempt to
* add a rule that will fixup the checksum of DHCP response
* packets back to the guests (but report failure without
* aborting, since not all iptables implementations support it).
*/
if (ipv4def)
iptablesAddOutputFixUdpChecksum(fw, def->bridge, 68);
}
static void
iptablesRemoveChecksumFirewallRules(virFirewall *fw,
virNetworkDef *def)
{
size_t i;
virNetworkIPDef *ipv4def;
/* First look for first IPv4 address that has dhcp or tftpboot defined. */
/* We support dhcp config on 1 IPv4 interface only. */
for (i = 0;
(ipv4def = virNetworkDefGetIPByIndex(def, AF_INET, i));
i++) {
if (ipv4def->nranges || ipv4def->nhosts)
break;
}
if (ipv4def)
iptablesRemoveOutputFixUdpChecksum(fw, def->bridge, 68);
}
static int
iptablesAddIPSpecificFirewallRules(virFirewall *fw,
virNetworkDef *def,
virNetworkIPDef *ipdef)
{
/* NB: in the case of IPv6, routing rules are added when the
* forward mode is NAT. This is because IPv6 has no NAT.
*/
if (def->forward.type == VIR_NETWORK_FORWARD_NAT) {
if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) ||
def->forward.natIPv6 == VIR_TRISTATE_BOOL_YES)
return iptablesAddMasqueradingFirewallRules(fw, def, ipdef);
else if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
return iptablesAddRoutingFirewallRules(fw, def, ipdef);
} else if (def->forward.type == VIR_NETWORK_FORWARD_ROUTE) {
return iptablesAddRoutingFirewallRules(fw, def, ipdef);
}
return 0;
}
static int
iptablesRemoveIPSpecificFirewallRules(virFirewall *fw,
virNetworkDef *def,
virNetworkIPDef *ipdef)
{
if (def->forward.type == VIR_NETWORK_FORWARD_NAT) {
if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) ||
def->forward.natIPv6 == VIR_TRISTATE_BOOL_YES)
return iptablesRemoveMasqueradingFirewallRules(fw, def, ipdef);
else if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
return iptablesRemoveRoutingFirewallRules(fw, def, ipdef);
} else if (def->forward.type == VIR_NETWORK_FORWARD_ROUTE) {
return iptablesRemoveRoutingFirewallRules(fw, def, ipdef);
}
return 0;
}
/* Add all rules for all ip addresses (and general rules) on a network */
int
iptablesAddFirewallRules(virNetworkDef *def)
{
size_t i;
virNetworkIPDef *ipdef;
g_autoptr(virFirewall) fw = virFirewallNew();
virFirewallStartTransaction(fw, 0);
iptablesAddGeneralFirewallRules(fw, def);
for (i = 0;
(ipdef = virNetworkDefGetIPByIndex(def, AF_UNSPEC, i));
i++) {
if (iptablesAddIPSpecificFirewallRules(fw, def, ipdef) < 0)
return -1;
}
virFirewallStartRollback(fw, 0);
for (i = 0;
(ipdef = virNetworkDefGetIPByIndex(def, AF_UNSPEC, i));
i++) {
if (iptablesRemoveIPSpecificFirewallRules(fw, def, ipdef) < 0)
return -1;
}
iptablesRemoveGeneralFirewallRules(fw, def);
virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
iptablesAddChecksumFirewallRules(fw, def);
return virFirewallApply(fw);
}
/* Remove all rules for all ip addresses (and general rules) on a network */
void
iptablesRemoveFirewallRules(virNetworkDef *def)
{
size_t i;
virNetworkIPDef *ipdef;
g_autoptr(virFirewall) fw = virFirewallNew();
virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
iptablesRemoveChecksumFirewallRules(fw, def);
virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
for (i = 0;
(ipdef = virNetworkDefGetIPByIndex(def, AF_UNSPEC, i));
i++) {
if (iptablesRemoveIPSpecificFirewallRules(fw, def, ipdef) < 0)
return;
}
iptablesRemoveGeneralFirewallRules(fw, def);
virFirewallApply(fw);
}

View File

@ -22,8 +22,13 @@
#include "virsocketaddr.h"
#include "virfirewall.h"
#include "network_conf.h"
int iptablesSetupPrivateChains (virFirewallLayer layer);
int iptablesAddFirewallRules(virNetworkDef *def);
void iptablesRemoveFirewallRules(virNetworkDef *def);
int iptablesSetupPrivateChains(virFirewallLayer layer);
void iptablesAddTcpInput (virFirewall *fw,
virFirewallLayer layer,