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 +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 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); } diff --git a/src/network/network_iptables.c b/src/network/network_iptables.c index 362c0202a0..7338237c21 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. * @@ -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 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,