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(a)redhat.com>
---
src/network/bridge_driver_linux.c | 556 +----------------------------
src/network/network_iptables.c | 562 +++++++++++++++++++++++++++++-
src/network/network_iptables.h | 7 +-
3 files changed, 574 insertions(+), 551 deletions(-)
diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c
index fd4bf7b61c..4914d5c903 100644
--- a/src/network/bridge_driver_linux.c
+++ b/src/network/bridge_driver_linux.c
@@ -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 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)
+int
+networkAddFirewallRules(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)
-{
- 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);
+void
+networkRemoveFirewallRules(virNetworkDef *def)
+{
+ iptablesRemoveFirewallRules(def);
}
diff --git a/src/network/network_iptables.c b/src/network/network_iptables.c
index bf6e3065f5..106e8bfabf 100644
--- a/src/network/network_iptables.c
+++ b/src/network/network_iptables.c
@@ -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.
*
@@ -1071,3 +1072,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);
+}
diff --git a/src/network/network_iptables.h b/src/network/network_iptables.h
index bfb6bbe0e7..d3f6b48437 100644
--- a/src/network/network_iptables.h
+++ b/src/network/network_iptables.h
@@ -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,
--
2.44.0