Add support for unaddressed port
Neutron could create a port without the IP address when the network doesn't have a subnet. In this case, neutron will have no L3 knowledgee and we need remove the L3 filter on it but reserve the L2 filter if there is. This patch will make L2 agent verify the fixed_ips before converting the security-group-rules into firewall rules, L3 rules in it will be removed. And filter like arp-spoofing will be disabled for this port. Partially Implements: blueprint vm-without-l3-address Change-Id: I5cd1fdfa13a7e57258be7251768eaa8ba64d486e
This commit is contained in:
parent
d6742d48b0
commit
d4c52b7f5a
@ -45,6 +45,13 @@ LINUX_DEV_LEN = 14
|
|||||||
comment_rule = iptables_manager.comment_rule
|
comment_rule = iptables_manager.comment_rule
|
||||||
|
|
||||||
|
|
||||||
|
def port_needs_l3_security(port):
|
||||||
|
if port['fixed_ips'] or port.get('allowed_address_pairs'):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class IptablesFirewallDriver(firewall.FirewallDriver):
|
class IptablesFirewallDriver(firewall.FirewallDriver):
|
||||||
"""Driver which enforces security groups through iptables rules."""
|
"""Driver which enforces security groups through iptables rules."""
|
||||||
IPTABLES_DIRECTION = {firewall.INGRESS_DIRECTION: 'physdev-out',
|
IPTABLES_DIRECTION = {firewall.INGRESS_DIRECTION: 'physdev-out',
|
||||||
@ -367,6 +374,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
|
|||||||
mac_ipv6_pairs.append((mac, ip_address))
|
mac_ipv6_pairs.append((mac, ip_address))
|
||||||
|
|
||||||
def _spoofing_rule(self, port, ipv4_rules, ipv6_rules):
|
def _spoofing_rule(self, port, ipv4_rules, ipv6_rules):
|
||||||
|
if port_needs_l3_security(port):
|
||||||
# Allow dhcp client packets
|
# Allow dhcp client packets
|
||||||
ipv4_rules += [comment_rule('-p udp -m udp --sport 68 --dport 67 '
|
ipv4_rules += [comment_rule('-p udp -m udp --sport 68 --dport 67 '
|
||||||
'-j RETURN', comment=ic.DHCP_CLIENT)]
|
'-j RETURN', comment=ic.DHCP_CLIENT)]
|
||||||
@ -376,8 +384,10 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
|
|||||||
comment=ic.IPV6_RA_DROP)]
|
comment=ic.IPV6_RA_DROP)]
|
||||||
ipv6_rules += [comment_rule('-p icmpv6 -j RETURN',
|
ipv6_rules += [comment_rule('-p icmpv6 -j RETURN',
|
||||||
comment=ic.IPV6_ICMP_ALLOW)]
|
comment=ic.IPV6_ICMP_ALLOW)]
|
||||||
ipv6_rules += [comment_rule('-p udp -m udp --sport 546 --dport 547 '
|
ipv6_rules += [comment_rule('-p udp -m udp --sport 546 --dport '
|
||||||
'-j RETURN', comment=ic.DHCP_CLIENT)]
|
'547 -j RETURN',
|
||||||
|
comment=ic.DHCP_CLIENT)]
|
||||||
|
|
||||||
mac_ipv4_pairs = []
|
mac_ipv4_pairs = []
|
||||||
mac_ipv6_pairs = []
|
mac_ipv6_pairs = []
|
||||||
|
|
||||||
@ -483,11 +493,14 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
|
|||||||
ipv6_iptables_rules)
|
ipv6_iptables_rules)
|
||||||
elif direction == firewall.INGRESS_DIRECTION:
|
elif direction == firewall.INGRESS_DIRECTION:
|
||||||
ipv6_iptables_rules += self._accept_inbound_icmpv6()
|
ipv6_iptables_rules += self._accept_inbound_icmpv6()
|
||||||
|
|
||||||
|
if port_needs_l3_security(port):
|
||||||
# include IPv4 and IPv6 iptable rules from security group
|
# include IPv4 and IPv6 iptable rules from security group
|
||||||
ipv4_iptables_rules += self._convert_sgr_to_iptables_rules(
|
ipv4_iptables_rules += self._convert_sgr_to_iptables_rules(
|
||||||
ipv4_sg_rules)
|
ipv4_sg_rules)
|
||||||
ipv6_iptables_rules += self._convert_sgr_to_iptables_rules(
|
ipv6_iptables_rules += self._convert_sgr_to_iptables_rules(
|
||||||
ipv6_sg_rules)
|
ipv6_sg_rules)
|
||||||
|
|
||||||
# finally add the rules to the port chain for a given direction
|
# finally add the rules to the port chain for a given direction
|
||||||
self._add_rules_to_chain_v4v6(self._port_chain_name(port, direction),
|
self._add_rules_to_chain_v4v6(self._port_chain_name(port, direction),
|
||||||
ipv4_iptables_rules,
|
ipv4_iptables_rules,
|
||||||
@ -498,6 +511,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
|
|||||||
self._spoofing_rule(port,
|
self._spoofing_rule(port,
|
||||||
ipv4_iptables_rules,
|
ipv4_iptables_rules,
|
||||||
ipv6_iptables_rules)
|
ipv6_iptables_rules)
|
||||||
|
if port_needs_l3_security(port):
|
||||||
self._drop_dhcp_rule(ipv4_iptables_rules, ipv6_iptables_rules)
|
self._drop_dhcp_rule(ipv4_iptables_rules, ipv6_iptables_rules)
|
||||||
|
|
||||||
def _update_ipset_members(self, security_group_ids):
|
def _update_ipset_members(self, security_group_ids):
|
||||||
|
@ -30,6 +30,7 @@ from neutron.agent.common import ovs_lib
|
|||||||
from neutron.agent.common import polling
|
from neutron.agent.common import polling
|
||||||
from neutron.agent.common import utils
|
from neutron.agent.common import utils
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
|
from neutron.agent.linux.iptables_firewall import port_needs_l3_security
|
||||||
from neutron.agent import rpc as agent_rpc
|
from neutron.agent import rpc as agent_rpc
|
||||||
from neutron.agent import securitygroups_rpc as sg_rpc
|
from neutron.agent import securitygroups_rpc as sg_rpc
|
||||||
from neutron.api.rpc.handlers import dvr_rpc
|
from neutron.api.rpc.handlers import dvr_rpc
|
||||||
@ -807,6 +808,9 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
|||||||
addresses |= {p['ip_address']
|
addresses |= {p['ip_address']
|
||||||
for p in port_details['allowed_address_pairs']}
|
for p in port_details['allowed_address_pairs']}
|
||||||
|
|
||||||
|
if not port_needs_l3_security(port_details):
|
||||||
|
return
|
||||||
|
|
||||||
addresses = {ip for ip in addresses
|
addresses = {ip for ip in addresses
|
||||||
if netaddr.IPNetwork(ip).version == 4}
|
if netaddr.IPNetwork(ip).version == 4}
|
||||||
if any(netaddr.IPNetwork(ip).prefixlen == 0 for ip in addresses):
|
if any(netaddr.IPNetwork(ip).prefixlen == 0 for ip in addresses):
|
||||||
|
@ -1453,15 +1453,6 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase):
|
|||||||
'--physdev-is-bridged '
|
'--physdev-is-bridged '
|
||||||
'-j $ifake_dev',
|
'-j $ifake_dev',
|
||||||
comment=ic.SG_TO_VM_SG),
|
comment=ic.SG_TO_VM_SG),
|
||||||
mock.call.add_rule(
|
|
||||||
'ifake_dev',
|
|
||||||
'-m state --state INVALID -j DROP', comment=None),
|
|
||||||
mock.call.add_rule(
|
|
||||||
'ifake_dev',
|
|
||||||
'-m state --state RELATED,ESTABLISHED -j RETURN',
|
|
||||||
comment=None),
|
|
||||||
mock.call.add_rule('ifake_dev', '-j $sg-fallback',
|
|
||||||
comment=None),
|
|
||||||
mock.call.add_chain('ofake_dev'),
|
mock.call.add_chain('ofake_dev'),
|
||||||
mock.call.add_rule('FORWARD',
|
mock.call.add_rule('FORWARD',
|
||||||
'-m physdev --physdev-in tapfake_dev '
|
'-m physdev --physdev-in tapfake_dev '
|
||||||
@ -1483,26 +1474,8 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase):
|
|||||||
mock.call.add_rule(
|
mock.call.add_rule(
|
||||||
'sfake_dev', '-j DROP',
|
'sfake_dev', '-j DROP',
|
||||||
comment=ic.PAIR_DROP),
|
comment=ic.PAIR_DROP),
|
||||||
mock.call.add_rule(
|
|
||||||
'ofake_dev',
|
|
||||||
'-p udp -m udp --sport 68 --dport 67 -j RETURN',
|
|
||||||
comment=None),
|
|
||||||
mock.call.add_rule('ofake_dev', '-j $sfake_dev',
|
mock.call.add_rule('ofake_dev', '-j $sfake_dev',
|
||||||
comment=None),
|
comment=None),
|
||||||
mock.call.add_rule(
|
|
||||||
'ofake_dev',
|
|
||||||
'-p udp -m udp --sport 67 --dport 68 -j DROP',
|
|
||||||
comment=None),
|
|
||||||
mock.call.add_rule(
|
|
||||||
'ofake_dev',
|
|
||||||
'-m state --state INVALID -j DROP',
|
|
||||||
comment=None),
|
|
||||||
mock.call.add_rule(
|
|
||||||
'ofake_dev',
|
|
||||||
'-m state --state RELATED,ESTABLISHED -j RETURN',
|
|
||||||
comment=None),
|
|
||||||
mock.call.add_rule('ofake_dev', '-j $sg-fallback',
|
|
||||||
comment=None),
|
|
||||||
mock.call.add_rule('sg-chain', '-j ACCEPT')]
|
mock.call.add_rule('sg-chain', '-j ACCEPT')]
|
||||||
self.v4filter_inst.assert_has_calls(calls)
|
self.v4filter_inst.assert_has_calls(calls)
|
||||||
|
|
||||||
|
@ -1102,7 +1102,7 @@ class TestOvsNeutronAgent(object):
|
|||||||
self.assertTrue(int_br.delete_arp_spoofing_protection.called)
|
self.assertTrue(int_br.delete_arp_spoofing_protection.called)
|
||||||
self.assertFalse(int_br.install_arp_spoofing_protection.called)
|
self.assertFalse(int_br.install_arp_spoofing_protection.called)
|
||||||
|
|
||||||
def test_arp_spoofing_basic_rule_setup(self):
|
def test_arp_spoofing_basic_rule_setup_without_ip(self):
|
||||||
vif = FakeVif()
|
vif = FakeVif()
|
||||||
fake_details = {'fixed_ips': []}
|
fake_details = {'fixed_ips': []}
|
||||||
self.agent.prevent_arp_spoofing = True
|
self.agent.prevent_arp_spoofing = True
|
||||||
@ -1111,9 +1111,18 @@ class TestOvsNeutronAgent(object):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[mock.call(port=vif.ofport)],
|
[mock.call(port=vif.ofport)],
|
||||||
int_br.delete_arp_spoofing_protection.mock_calls)
|
int_br.delete_arp_spoofing_protection.mock_calls)
|
||||||
|
self.assertFalse(int_br.install_arp_spoofing_protection.called)
|
||||||
|
|
||||||
|
def test_arp_spoofing_basic_rule_setup_fixed_ip(self):
|
||||||
|
vif = FakeVif()
|
||||||
|
fake_details = {'fixed_ips': [{'ip_address': '192.168.44.100'}]}
|
||||||
|
self.agent.prevent_arp_spoofing = True
|
||||||
|
int_br = mock.create_autospec(self.agent.int_br)
|
||||||
|
self.agent.setup_arp_spoofing_protection(int_br, vif, fake_details)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[mock.call(ip_addresses=set(), port=vif.ofport)],
|
[mock.call(port=vif.ofport)],
|
||||||
int_br.install_arp_spoofing_protection.mock_calls)
|
int_br.delete_arp_spoofing_protection.mock_calls)
|
||||||
|
self.assertTrue(int_br.install_arp_spoofing_protection.called)
|
||||||
|
|
||||||
def test_arp_spoofing_fixed_and_allowed_addresses(self):
|
def test_arp_spoofing_fixed_and_allowed_addresses(self):
|
||||||
vif = FakeVif()
|
vif = FakeVif()
|
||||||
|
Loading…
Reference in New Issue
Block a user