[ovs fw] Restrict IPv6 NA and DHCP(v6) IP and MAC source addresses
Neighbor Advertisments are used to inform other machines of the MAC address to use to reach an IPv6. This commits prevents VMs from pretending they are assigned IPv6 they should not use. It also prevents sending UDP packets with spoofed IP or MAC even using DHCP(v6) request ports. Co-authored-by: David Sinquin <david.sinquin@gandi.net> Closes-bug: #1902917 Change-Id: Iffb6643359562487414460f5a7e19a7fae9f935c
This commit is contained in:
parent
377d8d5c83
commit
ca7822e210
@ -34,8 +34,11 @@ ICMPV6_ALLOWED_INGRESS_TYPES = (n_const.ICMPV6_TYPE_MLD_QUERY,
|
||||
# List of ICMPv6 types that should be permitted (egress) by default.
|
||||
ICMPV6_ALLOWED_EGRESS_TYPES = (n_const.ICMPV6_TYPE_MLD_QUERY,
|
||||
n_const.ICMPV6_TYPE_RS,
|
||||
n_const.ICMPV6_TYPE_NS,
|
||||
n_const.ICMPV6_TYPE_NA)
|
||||
n_const.ICMPV6_TYPE_NS)
|
||||
|
||||
# List of ICMPv6 types that should be permitted depending on payload content
|
||||
# to avoid spoofing (egress) by default.
|
||||
ICMPV6_RESTRICTED_EGRESS_TYPES = (n_const.ICMPV6_TYPE_NA, )
|
||||
|
||||
|
||||
def port_sec_enabled(port):
|
||||
|
@ -934,8 +934,7 @@ class OVSFirewallDriver(firewall.FirewallDriver):
|
||||
self._initialize_ingress(port)
|
||||
|
||||
def _initialize_egress_ipv6_icmp(self, port, allowed_pairs):
|
||||
# NOTE(slaweq): should we include also fe80::/64 (link-local) subnet
|
||||
# in the allowed pairs here?
|
||||
allowed_pairs = allowed_pairs.union({(port.mac, port.lla_address)})
|
||||
for mac_addr, ip_addr in allowed_pairs:
|
||||
for icmp_type in firewall.ICMPV6_ALLOWED_EGRESS_TYPES:
|
||||
self._add_flow(
|
||||
@ -951,6 +950,19 @@ class OVSFirewallDriver(firewall.FirewallDriver):
|
||||
actions='resubmit(,%d)' % (
|
||||
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)
|
||||
)
|
||||
for icmp_type in firewall.ICMPV6_RESTRICTED_EGRESS_TYPES:
|
||||
self._add_flow(
|
||||
table=ovs_consts.BASE_EGRESS_TABLE,
|
||||
priority=95,
|
||||
in_port=port.ofport,
|
||||
reg_port=port.ofport,
|
||||
dl_type=lib_const.ETHERTYPE_IPV6,
|
||||
nw_proto=lib_const.PROTO_NUM_IPV6_ICMP,
|
||||
icmp_type=icmp_type,
|
||||
nd_target=ip_addr,
|
||||
actions='resubmit(,%d)' % (
|
||||
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)
|
||||
)
|
||||
|
||||
def _initialize_egress_no_port_security(self, port_id, ovs_ports=None):
|
||||
try:
|
||||
@ -1025,9 +1037,9 @@ class OVSFirewallDriver(firewall.FirewallDriver):
|
||||
"""Identify egress traffic and send it to egress base"""
|
||||
|
||||
# Apply mac/ip pairs for IPv4
|
||||
allowed_pairs = port.allowed_pairs_v4.union(
|
||||
allowed_mac_ipv4_pairs = port.allowed_pairs_v4.union(
|
||||
{(port.mac, ip_addr) for ip_addr in port.ipv4_addresses})
|
||||
for mac_addr, ip_addr in allowed_pairs:
|
||||
for mac_addr, ip_addr in allowed_mac_ipv4_pairs:
|
||||
self._add_flow(
|
||||
table=ovs_consts.BASE_EGRESS_TABLE,
|
||||
priority=95,
|
||||
@ -1053,10 +1065,10 @@ class OVSFirewallDriver(firewall.FirewallDriver):
|
||||
)
|
||||
|
||||
# Apply mac/ip pairs for IPv6
|
||||
allowed_pairs = port.allowed_pairs_v6.union(
|
||||
allowed_mac_ipv6_pairs = port.allowed_pairs_v6.union(
|
||||
{(port.mac, ip_addr) for ip_addr in port.ipv6_addresses})
|
||||
self._initialize_egress_ipv6_icmp(port, allowed_pairs)
|
||||
for mac_addr, ip_addr in allowed_pairs:
|
||||
self._initialize_egress_ipv6_icmp(port, allowed_mac_ipv6_pairs)
|
||||
for mac_addr, ip_addr in allowed_mac_ipv6_pairs:
|
||||
self._add_flow(
|
||||
table=ovs_consts.BASE_EGRESS_TABLE,
|
||||
priority=65,
|
||||
@ -1071,21 +1083,30 @@ class OVSFirewallDriver(firewall.FirewallDriver):
|
||||
)
|
||||
|
||||
# DHCP discovery
|
||||
for dl_type, src_port, dst_port in (
|
||||
(lib_const.ETHERTYPE_IP, 68, 67),
|
||||
(lib_const.ETHERTYPE_IPV6, 546, 547)):
|
||||
self._add_flow(
|
||||
table=ovs_consts.BASE_EGRESS_TABLE,
|
||||
priority=80,
|
||||
reg_port=port.ofport,
|
||||
in_port=port.ofport,
|
||||
dl_type=dl_type,
|
||||
nw_proto=lib_const.PROTO_NUM_UDP,
|
||||
tp_src=src_port,
|
||||
tp_dst=dst_port,
|
||||
actions='resubmit(,{:d})'.format(
|
||||
ovs_consts.ACCEPT_OR_INGRESS_TABLE)
|
||||
)
|
||||
additional_ipv4_filters = [
|
||||
{"dl_src": mac, "nw_src": ip}
|
||||
for mac, ip in (*allowed_mac_ipv4_pairs,
|
||||
(port.mac, '0.0.0.0'),)]
|
||||
additional_ipv6_filters = [
|
||||
{"dl_src": mac, "ipv6_src": ip}
|
||||
for mac, ip in allowed_mac_ipv6_pairs]
|
||||
for dl_type, src_port, dst_port, filters_list in (
|
||||
(lib_const.ETHERTYPE_IP, 68, 67, additional_ipv4_filters),
|
||||
(lib_const.ETHERTYPE_IPV6, 546, 547, additional_ipv6_filters)):
|
||||
for additional_filters in filters_list:
|
||||
self._add_flow(
|
||||
table=ovs_consts.BASE_EGRESS_TABLE,
|
||||
priority=80,
|
||||
reg_port=port.ofport,
|
||||
in_port=port.ofport,
|
||||
dl_type=dl_type,
|
||||
**additional_filters,
|
||||
nw_proto=lib_const.PROTO_NUM_UDP,
|
||||
tp_src=src_port,
|
||||
tp_dst=dst_port,
|
||||
actions='resubmit(,{:d})'.format(
|
||||
ovs_consts.ACCEPT_OR_INGRESS_TABLE)
|
||||
)
|
||||
# Ban dhcp service running on an instance
|
||||
for dl_type, src_port, dst_port in (
|
||||
(lib_const.ETHERTYPE_IP, 67, 68),
|
||||
|
@ -1057,6 +1057,19 @@ class TestOVSFirewallDriver(base.BaseTestCase):
|
||||
ipv6_src='2003::1',
|
||||
actions='resubmit(,%d)' % (
|
||||
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)))
|
||||
for icmp_type in agent_firewall.ICMPV6_RESTRICTED_EGRESS_TYPES:
|
||||
expected_calls.append(
|
||||
mock.call(
|
||||
table=ovs_consts.BASE_EGRESS_TABLE,
|
||||
priority=95,
|
||||
in_port=TESTING_VLAN_TAG,
|
||||
reg5=TESTING_VLAN_TAG,
|
||||
dl_type='0x86dd',
|
||||
nw_proto=constants.PROTO_NUM_IPV6_ICMP,
|
||||
icmp_type=icmp_type,
|
||||
nd_target='2003::1',
|
||||
actions='resubmit(,%d)' % (
|
||||
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)))
|
||||
self.mock_bridge.br.add_flow.assert_has_calls(expected_calls)
|
||||
|
||||
def test_process_trusted_ports_caches_port_id(self):
|
||||
|
Loading…
Reference in New Issue
Block a user