Mapping between Neutron and OVN data models ======================================================== The primary job of the Neutron OVN ML2 driver is to translate requests for resources into OVN's data model. Resources are created in OVN by updating the appropriate tables in the OVN northbound database (an ovsdb database). This document looks at the mappings between the data that exists in Neutron and what the resulting entries in the OVN northbound DB would look like. Network ---------- :: Neutron Network: id name subnets admin_state_up status tenant_id Once a network is created, we should create an entry in the Logical Switch table. :: OVN northbound DB Logical Switch: external_ids: { 'neutron:network_name': network.name } Subnet --------- :: Neutron Subnet: id name ip_version network_id cidr gateway_ip allocation_pools dns_nameservers host_routers tenant_id enable_dhcp ipv6_ra_mode ipv6_address_mode Once a subnet is created, we should create an entry in the DHCP Options table with the DHCPv4 or DHCPv6 options. :: OVN northbound DB DHCP_Options: cidr options external_ids: { 'subnet_id': subnet.id } Port ------- :: Neutron Port: id name network_id admin_state_up mac_address fixed_ips device_id device_owner tenant_id status When a port is created, we should create an entry in the Logical Switch Ports table in the OVN northbound DB. :: OVN Northbound DB Logical Switch Port: switch: reference to OVN Logical Switch router_port: (empty) name: port.id up: (read-only) macs: [port.mac_address] port_security: external_ids: {'neutron:port_name': port.name} If the port has extra DHCP options defined, we should create an entry in the DHCP Options table in the OVN northbound DB. :: OVN northbound DB DHCP_Options: cidr options external_ids: { 'subnet_id': subnet.id, 'port_id': port.id } Router ---------- :: Neutron Router: id name admin_state_up status tenant_id external_gw_info: network_id external_fixed_ips: list of dicts ip_address subnet_id ... :: OVN Northbound DB Logical Router: ip: default_gw: external_ids: Router Port -------------- ... :: OVN Northbound DB Logical Router Port: router: (reference to Logical Router) network: (reference to network this port is connected to) mac: external_ids: Security Groups ---------------- :: Neutron Port: id security_group: id network_id Neutron Security Group id name tenant_id security_group_rules Neutron Security Group Rule id tenant_id security_group_id direction remote_group_id ethertype protocol port_range_min port_range_max remote_ip_prefix ... :: OVN Northbound DB ACL Rule: lswitch: (reference to Logical Switch - port.network_id) priority: (0..65535) match: boolean expressions according to security rule Translation map (sg_rule ==> match expression) ----------------------------------------------- sg_rule.direction="Ingress" => "inport=port.id" sg_rule.direction="Egress" => "outport=port.id" sg_rule.ethertype => "eth.type" sg_rule.protocol => "ip.proto" sg_rule.port_range_min/port_range_max => "port_range_min <= tcp.src <= port_range_max" "port_range_min <= udp.src <= port_range_max" sg_rule.remote_ip_prefix => "ip4.src/mask, ip4.dst/mask, ipv6.src/mask, ipv6.dst/mask" (all match options for ACL can be found here: https://github.com/openvswitch/ovs/blob/ovn/ovn/ovn-sb.xml) action: "allow-related" log: true/false external_ids: {'neutron:port_id': port.id} {'neutron:security_rule_id': security_rule.id} Security groups maps between three neutron objects to one OVN-NB object, this enable us to do the mapping in various ways, depending on OVN capabilities The current implementation will use the first option in this list for simplicity, but all options are kept here for future reference 1) For every pair, define an ACL entry:: Leads to many ACL entries. acl.match = sg_rule converted example: ((inport==port.id) && (ip.proto == "tcp") && (1024 <= tcp.src <= 4095) && (ip.src==192.168.0.1/16)) external_ids: {'neutron:port_id': port.id} {'neutron:security_rule_id': security_rule.id} 2) For every pair, define an ACL entry:: Reduce the number of ACL entries. Means we have to manage the match field in case specific rule changes example: (((inport==port.id) && (ip.proto == "tcp") && (1024 <= tcp.src <= 4095) && (ip.src==192.168.0.1/16)) || ((outport==port.id) && (ip.proto == "udp") && (1024 <= tcp.src <= 4095)) || ((inport==port.id) && (ip.proto == 6) ) || ((inport==port.id) && (eth.type == 0x86dd))) (This example is a security group with four security rules) external_ids: {'neutron:port_id': port.id} {'neutron:security_group_id': security_group.id} 3) For every pair, define an ACL entry:: Reduce even more the number of ACL entries. Manage complexity increase example: (((inport==port.id) && (ip.proto == "tcp") && (1024 <= tcp.src <= 4095) && (ip.src==192.168.0.1/16)) || ((outport==port.id) && (ip.proto == "udp") && (1024 <= tcp.src <= 4095)) || ((inport==port.id) && (ip.proto == 6) ) || ((inport==port.id) && (eth.type == 0x86dd))) || (((inport==port2.id) && (ip.proto == "tcp") && (1024 <= tcp.src <= 4095) && (ip.src==192.168.0.1/16)) || ((outport==port2.id) && (ip.proto == "udp") && (1024 <= tcp.src <= 4095)) || ((inport==port2.id) && (ip.proto == 6) ) || ((inport==port2.id) && (eth.type == 0x86dd))) external_ids: {'neutron:security_group': security_group.id} Which option to pick depends on OVN match field length capabilities, and the trade off between better performance due to less ACL entries compared to the complexity to manage them. If the default behaviour is not "drop" for unmatched entries, a rule with lowest priority must be added to drop all traffic ("match==1") Spoofing protection rules are being added by OVN internally and we need to ignore the automatically added rules in Neutron