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
This commit is contained in:
Anton Frolov 2013-06-14 21:01:50 +04:00
parent a36f2373d2
commit 531ff2fb34
2 changed files with 84 additions and 3 deletions

View 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

View 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)