]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Fix IP spoofing filter blocking all packets.
authorAnton Frolov <anfrolov@mirantis.com>
Fri, 14 Jun 2013 17:01:50 +0000 (21:01 +0400)
committerAnton Frolov <anfrolov@mirantis.com>
Mon, 17 Jun 2013 06:51:32 +0000 (10:51 +0400)
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

quantum/agent/linux/iptables_firewall.py
quantum/tests/unit/test_iptables_firewall.py

index 8205b52ba2737f141c60a9ac0e0d210342c59721..6077bd28252d24db31842091b38e0df565b90873 100644 (file)
@@ -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
index aea7e6da4ed4757f08cd10a894b666b70c233854..d870999442f894aec89aea039fd6bc618a75090b 100644 (file)
@@ -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)