From: Anton Frolov Date: Fri, 14 Jun 2013 17:01:50 +0000 (+0400) Subject: Fix IP spoofing filter blocking all packets. X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=531ff2fb34df9e6b0f84ada811c29240f0f1cd48;p=openstack-build%2Fneutron-build.git Fix IP spoofing filter blocking all packets. Implement IP spoofing filter by adding yet another chain to iptables with RETURN rule for each of IP addresses assigned to port and DROP rule at the end of the chain. Fixes: bug #1190613 Change-Id: I2e7a0f8dd4c3519c57d28e87a44735a3482624b1 --- diff --git a/quantum/agent/linux/iptables_firewall.py b/quantum/agent/linux/iptables_firewall.py index 8205b52ba..6077bd282 100644 --- a/quantum/agent/linux/iptables_firewall.py +++ b/quantum/agent/linux/iptables_firewall.py @@ -28,8 +28,10 @@ LOG = logging.getLogger(__name__) SG_CHAIN = 'sg-chain' INGRESS_DIRECTION = 'ingress' EGRESS_DIRECTION = 'egress' +IP_SPOOF_FILTER = 'ip-spoof-filter' CHAIN_NAME_PREFIX = {INGRESS_DIRECTION: 'i', - EGRESS_DIRECTION: 'o'} + EGRESS_DIRECTION: 'o', + IP_SPOOF_FILTER: 's'} LINUX_DEV_LEN = 14 @@ -94,6 +96,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver): for port in self.filtered_ports.values(): self._remove_chain(port, INGRESS_DIRECTION) self._remove_chain(port, EGRESS_DIRECTION) + self._remove_chain(port, IP_SPOOF_FILTER) self._remove_chain_by_name_v4v6(SG_CHAIN) def _setup_chain(self, port, DIRECTION): @@ -175,15 +178,32 @@ class IptablesFirewallDriver(firewall.FirewallDriver): def _arp_spoofing_rule(self, port): return ['-m mac ! --mac-source %s -j DROP' % port['mac_address']] + def _setup_ip_spoof_filter_chain(self, port, table, addresses, rules): + if len(addresses) == 1: + rules.append('! -s %s -j DROP' % addresses[0]) + elif addresses: + chain_name = self._port_chain_name(port, IP_SPOOF_FILTER) + table.add_chain(chain_name) + for ip in addresses: + table.add_rule(chain_name, '-s %s -j RETURN' % ip) + table.add_rule(chain_name, '-j DROP') + rules.append('-j $%s' % chain_name) + def _ip_spoofing_rule(self, port, ipv4_rules, ipv6_rules): #Note(nati) allow dhcp or RA packet ipv4_rules += ['-p udp --sport 68 --dport 67 -j RETURN'] ipv6_rules += ['-p icmpv6 -j RETURN'] + ipv4_addresses = [] + ipv6_addresses = [] for ip in port['fixed_ips']: if netaddr.IPAddress(ip).version == 4: - ipv4_rules += ['! -s %s -j DROP' % ip] + ipv4_addresses.append(ip) else: - ipv6_rules += ['! -s %s -j DROP' % ip] + ipv6_addresses.append(ip) + self._setup_ip_spoof_filter_chain(port, self.iptables.ipv4['filter'], + ipv4_addresses, ipv4_rules) + self._setup_ip_spoof_filter_chain(port, self.iptables.ipv6['filter'], + ipv6_addresses, ipv6_rules) def _drop_dhcp_rule(self): #Note(nati) Drop dhcp packet from VM diff --git a/quantum/tests/unit/test_iptables_firewall.py b/quantum/tests/unit/test_iptables_firewall.py index aea7e6da4..d87099944 100644 --- a/quantum/tests/unit/test_iptables_firewall.py +++ b/quantum/tests/unit/test_iptables_firewall.py @@ -839,6 +839,7 @@ class IptablesFirewallTestCase(base.BaseTestCase): call.add_rule('sg-chain', '-j ACCEPT'), call.ensure_remove_chain('ifake_dev'), call.ensure_remove_chain('ofake_dev'), + call.ensure_remove_chain('sfake_dev'), call.ensure_remove_chain('sg-chain'), call.add_chain('sg-chain'), call.add_chain('ifake_dev'), @@ -889,6 +890,7 @@ class IptablesFirewallTestCase(base.BaseTestCase): call.add_rule('sg-chain', '-j ACCEPT'), call.ensure_remove_chain('ifake_dev'), call.ensure_remove_chain('ofake_dev'), + call.ensure_remove_chain('sfake_dev'), call.ensure_remove_chain('sg-chain'), call.add_chain('sg-chain')] @@ -914,3 +916,62 @@ class IptablesFirewallTestCase(base.BaseTestCase): pass self.iptables_inst.assert_has_calls([call.defer_apply_on(), call.defer_apply_off()]) + + def test_ip_spoofing_filter_with_multiple_ips(self): + port = {'device': 'tapfake_dev', + 'mac_address': 'ff:ff:ff:ff', + 'fixed_ips': ['10.0.0.1', 'fe80::1', '10.0.0.2']} + self.firewall.prepare_port_filter(port) + calls = [call.add_chain('sg-fallback'), + call.add_rule('sg-fallback', '-j DROP'), + call.ensure_remove_chain('sg-chain'), + call.add_chain('sg-chain'), + call.add_chain('ifake_dev'), + call.add_rule('FORWARD', + '-m physdev --physdev-is-bridged ' + '--physdev-out tapfake_dev ' + '-j $sg-chain'), + call.add_rule('sg-chain', + '-m physdev --physdev-is-bridged ' + '--physdev-out tapfake_dev ' + '-j $ifake_dev'), + call.add_rule( + 'ifake_dev', '-m state --state INVALID -j DROP'), + call.add_rule( + 'ifake_dev', + '-m state --state ESTABLISHED,RELATED -j RETURN'), + call.add_rule('ifake_dev', '-j $sg-fallback'), + call.add_chain('ofake_dev'), + call.add_rule('FORWARD', + '-m physdev --physdev-is-bridged ' + '--physdev-in tapfake_dev ' + '-j $sg-chain'), + call.add_rule('sg-chain', + '-m physdev --physdev-is-bridged ' + '--physdev-in tapfake_dev ' + '-j $ofake_dev'), + call.add_rule('INPUT', + '-m physdev --physdev-is-bridged ' + '--physdev-in tapfake_dev ' + '-j $ofake_dev'), + call.add_chain('sfake_dev'), + call.add_rule('sfake_dev', '-s 10.0.0.1 -j RETURN'), + call.add_rule('sfake_dev', '-s 10.0.0.2 -j RETURN'), + call.add_rule('sfake_dev', '-j DROP'), + call.add_rule( + 'ofake_dev', '-m mac ! --mac-source ff:ff:ff:ff -j DROP'), + call.add_rule( + 'ofake_dev', + '-p udp --sport 68 --dport 67 -j RETURN'), + call.add_rule('ofake_dev', '-j $sfake_dev'), + call.add_rule( + 'ofake_dev', + '-p udp --sport 67 --dport 68 -j DROP'), + call.add_rule( + 'ofake_dev', '-m state --state INVALID -j DROP'), + call.add_rule( + 'ofake_dev', + '-m state --state ESTABLISHED,RELATED -j RETURN'), + call.add_rule('ofake_dev', '-j $sg-fallback'), + call.add_rule('sg-chain', '-j ACCEPT')] + self.v4filter_inst.assert_has_calls(calls)