def withdraw_subnet(self, row): lrp_logical_port = 'lrp-' + row.logical_port lrp_datapath = self.ovn_local_lrps.get(lrp_logical_port, {}).get('datapath') ip = self.ovn_local_lrps.get(lrp_logical_port, {}).get('ip') if not lrp_datapath: return cr_lrp = self.sb_idl.is_router_gateway_on_chassis( lrp_datapath, self.chassis) if not cr_lrp: return LOG.info("Delete IP Routes for network {} on chassis {}".format( ip, self.chassis)) cr_lrp_info = self.ovn_local_cr_lrps.get(cr_lrp, {}) cr_lrp_datapath = cr_lrp_info.get('provider_datapath') if not cr_lrp_datapath: LOG.info("Subnet not connected to the provider network. " "No need to withdraw it from EVPN") return cr_lrp_ips = [ ip_address.split('/')[0] for ip_address in cr_lrp_info.get('ips', []) ] datapath_bridge, vlan_tag = self._get_bridge_for_datapath( cr_lrp_datapath) ip_version = utils.get_ip_version(ip) for cr_lrp_ip in cr_lrp_ips: if utils.get_ip_version(cr_lrp_ip) == ip_version: linux_net.del_ip_route(self._ovn_routing_tables_routes, ip.split("/")[0], cr_lrp_info['vni'], datapath_bridge, vlan=vlan_tag, mask=ip.split("/")[1], via=cr_lrp_ip) if utils.get_ip_version(cr_lrp_ip) == constants.IP_VERSION_6: net = ipaddress.IPv6Network(ip, strict=False) else: net = ipaddress.IPv4Network(ip, strict=False) break ovs.remove_evpn_network_ovs_flow(datapath_bridge, constants.OVS_VRF_RULE_COOKIE, cr_lrp_info['mac'], '{}'.format(net)) # Check if there are VMs on the network # and if so withdraw the routes vms_on_net = linux_net.get_exposed_ips_on_network( cr_lrp_info['lo'], net) linux_net.delete_exposed_ips(vms_on_net, cr_lrp_info['lo']) try: del self.ovn_local_lrps[lrp_logical_port] except KeyError: LOG.debug("Router Interface port already cleanup from the agent")
def expose_subnet(self, ip, row): if not self._expose_tenant_networks: return cr_lrp = self.sb_idl.is_router_gateway_on_chassis(row.datapath, self.chassis) if cr_lrp: LOG.info("Add IP Rules for network {} on chassis {}".format( ip, self.chassis)) self.ovn_local_lrps.add(row.logical_port) cr_lrp_info = self.ovn_local_cr_lrps.get(cr_lrp, {}) cr_lrp_datapath = cr_lrp_info.get('provider_datapath') if cr_lrp_datapath: cr_lrp_ips = [ip_address.split('/')[0] for ip_address in cr_lrp_info.get('ips', [])] rule_bridge, vlan_tag = self._get_bridge_for_datapath( cr_lrp_datapath) linux_net.add_ip_rule(ip, self.ovn_routing_tables[rule_bridge], rule_bridge) ip_version = utils.get_ip_version(ip) for cr_lrp_ip in cr_lrp_ips: if utils.get_ip_version(cr_lrp_ip) == ip_version: linux_net.add_ip_route( self.ovn_routing_tables_routes, ip.split("/")[0], self.ovn_routing_tables[rule_bridge], rule_bridge, vlan=vlan_tag, mask=ip.split("/")[1], via=cr_lrp_ip) break # Check if there are VMs on the network # and if so expose the route network_port_datapath = self.sb_idl.get_port_datapath( row.options['peer']) if network_port_datapath: ports = self.sb_idl.get_ports_on_datapath( network_port_datapath) for port in ports: if port.type != "" and port.type != "virtual": continue try: port_ips = [port.mac[0].split(' ')[1]] except IndexError: continue if len(port.mac[0].split(' ')) == 3: port_ips.append(port.mac[0].split(' ')[2]) for port_ip in port_ips: # Only adding the port ips that match the lrp # IP version port_ip_version = utils.get_ip_version(port_ip) if port_ip_version == ip_version: linux_net.add_ips_to_dev( constants.OVN_BGP_NIC, [port_ip])
def add_ips_to_dev(nic, ips, clear_local_route_at_table=False): with pyroute2.NDB() as ndb: try: with ndb.interfaces[nic] as iface: for ip in ips: address = '{}/32'.format(ip) if utils.get_ip_version(ip) == constants.IP_VERSION_6: address = '{}/128'.format(ip) iface.add_ip(address) except KeyError: # NDB raises KeyError: 'object exists' # if the ip is already added pass if clear_local_route_at_table: with pyroute2.NDB() as ndb: for ip in ips: route = {'table': clear_local_route_at_table, 'proto': 2, 'scope': 254, 'dst': ip} try: with ndb.routes[route] as r: r.remove() except (KeyError, ValueError): LOG.debug("Local route already deleted: {}".format(route))
def _ensure_port_exposed(self, port, exposed_ips, ovn_ip_rules): if port.type not in constants.OVN_VIF_PORT_TYPES: return if (len(port.mac[0].split(' ')) != 2 and len(port.mac[0].split(' ')) != 3): return port_ips = [port.mac[0].split(' ')[1]] if len(port.mac[0].split(' ')) == 3: port_ips.append(port.mac[0].split(' ')[2]) fip = self._expose_IP(port_ips, port) if fip: if fip in exposed_ips: exposed_ips.remove(fip) fip_dst = "{}/32".format(fip) if fip_dst in ovn_ip_rules.keys(): del ovn_ip_rules[fip_dst] for port_ip in port_ips: ip_address = port_ip.split("/")[0] ip_version = utils.get_ip_version(port_ip) if ip_version == constants.IP_VERSION_6: ip_dst = "{}/128".format(ip_address) else: ip_dst = "{}/32".format(ip_address) if ip_address in exposed_ips: # remove each ip to add from the list of current ips on dev OVN exposed_ips.remove(ip_address) if ip_dst in ovn_ip_rules.keys(): del ovn_ip_rules[ip_dst]
def withdraw_subnet(self, ip, row): if not self._expose_tenant_networks: return cr_lrp = self.sb_idl.is_router_gateway_on_chassis(row.datapath, self.chassis) if cr_lrp: LOG.info("Delete IP Rules for network {} on chassis {}".format( ip, self.chassis)) if row.logical_port in self.ovn_local_lrps: self.ovn_local_lrps.remove(row.logical_port) cr_lrp_info = self.ovn_local_cr_lrps.get(cr_lrp, {}) cr_lrp_datapath = cr_lrp_info.get('provider_datapath') if cr_lrp_datapath: cr_lrp_ips = [ip_address.split('/')[0] for ip_address in cr_lrp_info.get('ips', [])] rule_bridge, vlan_tag = self._get_bridge_for_datapath( cr_lrp_datapath) linux_net.del_ip_rule(ip, self.ovn_routing_tables[rule_bridge], rule_bridge) ip_version = utils.get_ip_version(ip) for cr_lrp_ip in cr_lrp_ips: if utils.get_ip_version(cr_lrp_ip) == ip_version: linux_net.del_ip_route( self.ovn_routing_tables_routes, ip.split("/")[0], self.ovn_routing_tables[rule_bridge], rule_bridge, vlan=vlan_tag, mask=ip.split("/")[1], via=cr_lrp_ip) if (utils.get_ip_version(cr_lrp_ip) == constants.IP_VERSION_6): net = ipaddress.IPv6Network(ip, strict=False) else: net = ipaddress.IPv4Network(ip, strict=False) break # Check if there are VMs on the network # and if so withdraw the routes vms_on_net = linux_net.get_exposed_ips_on_network( constants.OVN_BGP_NIC, net) linux_net.delete_exposed_ips(vms_on_net, constants.OVN_BGP_NIC)
def del_ips_from_dev(nic, ips): with pyroute2.NDB() as ndb: with ndb.interfaces[nic] as iface: for ip in ips: address = '{}/32'.format(ip) if utils.get_ip_version(ip) == constants.IP_VERSION_6: address = '{}/128'.format(ip) iface.del_ip(address)
def del_ip_rule(ip, table, dev=None, lladdr=None): ip_version = utils.get_ip_version(ip) ip_info = ip.split("/") if len(ip_info) == 1: rule = {'dst': ip_info[0], 'table': table, 'dst_len': 32} if ip_version == constants.IP_VERSION_6: rule['dst_len'] = 128 rule['family'] = AF_INET6 elif len(ip_info) == 2: rule = {'dst': ip_info[0], 'table': table, 'dst_len': int(ip_info[1])} if ip_version == constants.IP_VERSION_6: rule['family'] = AF_INET6 else: LOG.error("Invalid ip: {}".format(ip)) return with pyroute2.NDB() as ndb: try: ndb.rules[rule].remove().commit() LOG.debug("Deleting ip rule with: {}".format(rule)) except KeyError: LOG.debug("Rule already deleted: {}".format(rule)) # FIXME: There is no support for deleting neighbours in NDB # So we are using iproute here if lladdr: ip_version = utils.get_ip_version(ip) with pyroute2.IPRoute() as iproute: # This is doing something like: # sudo ip nei del 172.24.4.69 # lladdr fa:16:3e:d3:5d:7b dev br-ex nud permanent network_bridge_if = iproute.link_lookup( ifname=dev)[0] if ip_version == constants.IP_VERSION_6: iproute.neigh('del', dst=ip.split("/")[0], lladdr=lladdr, family=AF_INET6, ifindex=network_bridge_if, state=ndmsg.states['permanent']) else: iproute.neigh('del', dst=ip.split("/")[0], lladdr=lladdr, ifindex=network_bridge_if, state=ndmsg.states['permanent'])
def add_ip_route(ovn_routing_tables_routes, ip_address, route_table, dev, vlan=None, mask=None, via=None): net_ip = ip_address if not mask: # default /32 or /128 if utils.get_ip_version(ip_address) == constants.IP_VERSION_6: mask = 128 else: mask = 32 else: ip = '{}/{}'.format(ip_address, mask) if utils.get_ip_version(ip_address) == constants.IP_VERSION_6: net_ip = '{}'.format(ipaddress.IPv6Network( ip, strict=False).network_address) else: net_ip = '{}'.format(ipaddress.IPv4Network( ip, strict=False).network_address) with pyroute2.NDB() as ndb: if vlan: oif_name = '{}.{}'.format(dev, vlan) oif = ndb.interfaces[oif_name]['index'] else: oif = ndb.interfaces[dev]['index'] route = {'dst': net_ip, 'dst_len': int(mask), 'oif': oif, 'table': int(route_table), 'proto': 3} if via: route['gateway'] = via route['scope'] = 0 else: route['scope'] = 253 if utils.get_ip_version(net_ip) == constants.IP_VERSION_6: route['family'] = AF_INET6 del route['scope'] with pyroute2.NDB() as ndb: try: with ndb.routes[route] as r: LOG.debug("Route already existing: {}".format(r)) except KeyError: ndb.routes.create(route).commit() LOG.debug("Route created at table {}: {}".format(route_table, route)) route_info = {'vlan': vlan, 'route': route} ovn_routing_tables_routes.setdefault(dev, []).append(route_info)
def delete_exposed_ips(ips, nic): with pyroute2.NDB() as ndb: for ip in ips: address = '{}/32'.format(ip) if utils.get_ip_version(ip) == constants.IP_VERSION_6: address = '{}/128'.format(ip) try: ndb.interfaces[nic].ipaddr[address].remove().commit() except KeyError: LOG.debug("IP address {} already removed from nic {}.".format( ip, nic))
def _remove_network_exposed(self, router_port, gateway): gateway_ips = [ip.split('/')[0] for ip in gateway['ips']] try: router_port_ip = router_port.mac[0].split(' ')[1] except IndexError: return router_ip = router_port_ip.split('/')[0] if router_ip in gateway_ips: return if router_port.logical_port in self.ovn_local_lrps: self.ovn_local_lrps.remove(router_port.logical_port) rule_bridge, vlan_tag = self._get_bridge_for_datapath( gateway['provider_datapath']) linux_net.del_ip_rule(router_port_ip, self.ovn_routing_tables[rule_bridge], rule_bridge) router_port_ip_version = utils.get_ip_version(router_port_ip) for gateway_ip in gateway_ips: if utils.get_ip_version(gateway_ip) == router_port_ip_version: linux_net.del_ip_route( self.ovn_routing_tables_routes, router_ip, self.ovn_routing_tables[rule_bridge], rule_bridge, vlan=vlan_tag, mask=router_port_ip.split("/")[1], via=gateway_ip) if utils.get_ip_version(gateway_ip) == constants.IP_VERSION_6: net = ipaddress.IPv6Network(router_port_ip, strict=False) else: net = ipaddress.IPv4Network(router_port_ip, strict=False) break # Check if there are VMs on the network # and if so withdraw the routes vms_on_net = linux_net.get_exposed_ips_on_network( constants.OVN_BGP_NIC, net) linux_net.delete_exposed_ips(vms_on_net, constants.OVN_BGP_NIC)
def ensure_evpn_ovs_flow(bridge, cookie, mac, port, net, strip_vlan=False): ovs_port = None ovs_ports = ovs_cmd('ovs-vsctl', ['list-ports', bridge])[0].rstrip() for p in ovs_ports.split('\n'): if p.startswith('patch-provnet-'): ovs_port = p if not ovs_port: return ovs_ofport = ovs_cmd( 'ovs-vsctl', ['get', 'Interface', ovs_port, 'ofport'] )[0].rstrip() vrf_ofport = ovs_cmd( 'ovs-vsctl', ['get', 'Interface', port, 'ofport'] )[0].rstrip() ip_version = utils.get_ip_version(net) if ip_version == constants.IP_VERSION_6: with pyroute2.NDB() as ndb: if strip_vlan: flow = ( "cookie={},priority=1000,ipv6,in_port={},dl_src:{},ipv6_src={}" "actions=mod_dl_dst:{},strip_vlan,output={}".format( cookie, ovs_ofport, mac, net, ndb.interfaces[bridge]['address'], vrf_ofport)) else: flow = ( "cookie={},priority=1000,ipv6,in_port={},dl_src:{},ipv6_src={}" "actions=mod_dl_dst:{},output={}".format( cookie, ovs_ofport, mac, net, ndb.interfaces[bridge]['address'], vrf_ofport)) else: with pyroute2.NDB() as ndb: if strip_vlan: flow = ( "cookie={},priority=1000,ip,in_port={},dl_src:{},nw_src={}" "actions=mod_dl_dst:{},strip_vlan,output={}".format( cookie, ovs_ofport, mac, net, ndb.interfaces[bridge]['address'], vrf_ofport)) else: flow = ( "cookie={},priority=1000,ip,in_port={},dl_src:{},nw_src={}" "actions=mod_dl_dst:{},output={}".format( cookie, ovs_ofport, mac, net, ndb.interfaces[bridge]['address'], vrf_ofport)) ovs_cmd('ovs-ofctl', ['add-flow', bridge, flow])
def _disconnect_evpn_to_ovn(self, vni, datapath_bridge, ips, vlan_tag=None, cleanup_ndp_proxy=True): vrf = constants.OVN_EVPN_VRF_PREFIX + str(vni) # remove vrf from ovs bridge ovs.del_device_from_ovs_bridge(vrf, datapath_bridge) linux_net.delete_routes_from_table(vni) if vlan_tag: linux_net.delete_vlan_device_for_network(datapath_bridge, vlan_tag) elif cleanup_ndp_proxy: for ip in ips: if utils.get_ip_version(ip) == constants.IP_VERSION_6: linux_net.del_ndp_proxy(ip, datapath_bridge)
def _connect_evpn_to_ovn(self, vrf, ips, datapath_bridge, vni, vlan_tag): # add vrf to ovs bridge ovs.add_device_to_ovs_bridge(vrf, datapath_bridge, vlan_tag) if vlan_tag: linux_net.ensure_vlan_device_for_network(datapath_bridge, vlan_tag) # add route for ip to ovs provider bridge (at the vrf routing table) for ip in ips: ip_without_mask = ip.split("/")[0] linux_net.add_ip_route(self._ovn_routing_tables_routes, ip_without_mask, vni, datapath_bridge, vlan=vlan_tag) # add proxy ndp config for ipv6 if (utils.get_ip_version(ip_without_mask) == constants.IP_VERSION_6 ): linux_net.add_ndp_proxy(ip, datapath_bridge, vlan=vlan_tag) # add unreachable route to vrf linux_net.add_unreachable_route(vrf)
def remove_evpn_network_ovs_flow(bridge, cookie, mac, net): cookie_id = ("cookie={}/-1").format(cookie) ovs_port = None ovs_ports = ovs_cmd('ovs-vsctl', ['list-ports', bridge])[0].rstrip() for p in ovs_ports.split('\n'): if p.startswith('patch-provnet-'): ovs_port = p if not ovs_port: return ovs_ofport = ovs_cmd( 'ovs-vsctl', ['get', 'Interface', ovs_port, 'ofport'] )[0].rstrip() ip_version = utils.get_ip_version(net) if ip_version == constants.IP_VERSION_6: flow = ("{},ipv6,in_port={},dl_src:{},ipv6_src={}".format( cookie_id, ovs_ofport, mac, net)) else: flow = ("{},ip,in_port={},dl_src:{},nw_src={}".format( cookie_id, ovs_ofport, mac, net)) ovs_cmd('ovs-ofctl', ['del-flows', bridge, flow])
def _ensure_network_exposed(self, router_port, gateway): evpn_info = self.sb_idl.get_evpn_info_from_lrp_port_name( router_port.logical_port) if not evpn_info: LOG.debug("No EVPN information for LRP Port {}. " "Not exposing it.".format(router_port)) return gateway_ips = [ip.split('/')[0] for ip in gateway['ips']] try: router_port_ip = router_port.mac[0].split(' ')[1] except IndexError: return router_ip = router_port_ip.split('/')[0] if router_ip in gateway_ips: return self.ovn_local_lrps[router_port.logical_port] = { 'datapath': router_port.datapath, 'ip': router_port_ip } datapath_bridge, vlan_tag = self._get_bridge_for_datapath( gateway['provider_datapath']) router_port_ip_version = utils.get_ip_version(router_port_ip) for gateway_ip in gateway_ips: if utils.get_ip_version(gateway_ip) == router_port_ip_version: linux_net.add_ip_route(self._ovn_routing_tables_routes, router_ip, gateway['vni'], datapath_bridge, vlan=vlan_tag, mask=router_port_ip.split("/")[1], via=gateway_ip) break if router_port_ip_version == constants.IP_VERSION_6: net_ip = '{}'.format( ipaddress.IPv6Network(router_port_ip, strict=False)) else: net_ip = '{}'.format( ipaddress.IPv4Network(router_port_ip, strict=False)) strip_vlan = False if vlan_tag: strip_vlan = True ovs.ensure_evpn_ovs_flow(datapath_bridge, constants.OVS_VRF_RULE_COOKIE, gateway['mac'], gateway['vrf'], net_ip, strip_vlan) network_port_datapath = self.sb_idl.get_port_datapath( router_port.options['peer']) if not network_port_datapath: return ports = self.sb_idl.get_ports_on_datapath(network_port_datapath) for port in ports: if ((port.type != "" and port.type != "virtual") or (port.type == "" and not port.chassis)): continue try: port_ips = [port.mac[0].split(' ')[1]] except IndexError: continue if len(port.mac[0].split(' ')) == 3: port_ips.append(port.mac[0].split(' ')[2]) for port_ip in port_ips: # Only adding the port ips that match the lrp # IP version port_ip_version = utils.get_ip_version(port_ip) if port_ip_version == router_port_ip_version: linux_net.add_ips_to_dev( gateway['lo'], [port_ip], clear_local_route_at_table=gateway['vni']) self._ovn_exposed_evpn_ips.setdefault(gateway['lo'], []).extend([port_ip])
def _expose_IP(self, ips, row, associated_port=None): # VM on provider Network if ((row.type == "" or row.type == "virtual") and self.sb_idl.is_provider_network(row.datapath)): LOG.info("Add BGP route for logical port with ip {}".format(ips)) linux_net.add_ips_to_dev(constants.OVN_BGP_NIC, ips) rule_bridge, vlan_tag = self._get_bridge_for_datapath(row.datapath) for ip in ips: linux_net.add_ip_rule(ip, self.ovn_routing_tables[rule_bridge], rule_bridge) linux_net.add_ip_route( self.ovn_routing_tables_routes, ip, self.ovn_routing_tables[rule_bridge], rule_bridge, vlan=vlan_tag) # VM with FIP elif row.type == "" or row.type == "virtual": # FIPs are only supported with IPv4 fip_address, fip_datapath = self.sb_idl.get_fip_associated( row.logical_port) if fip_address: LOG.info("Add BGP route for FIP with ip {}".format( fip_address)) linux_net.add_ips_to_dev(constants.OVN_BGP_NIC, [fip_address]) rule_bridge, vlan_tag = self._get_bridge_for_datapath( fip_datapath) linux_net.add_ip_rule(fip_address, self.ovn_routing_tables[rule_bridge], rule_bridge) linux_net.add_ip_route( self.ovn_routing_tables_routes, fip_address, self.ovn_routing_tables[rule_bridge], rule_bridge, vlan=vlan_tag) return fip_address else: ovs.ensure_default_ovs_flows(self.ovn_bridge_mappings.values(), constants.OVS_RULE_COOKIE) # FIP association to VM elif row.type == "patch": if (associated_port and self.sb_idl.is_port_on_chassis( associated_port, self.chassis)): LOG.info("Add BGP route for FIP with ip {}".format(ips)) linux_net.add_ips_to_dev(constants.OVN_BGP_NIC, ips) rule_bridge, vlan_tag = self._get_bridge_for_datapath( row.datapath) for ip in ips: linux_net.add_ip_rule(ip, self.ovn_routing_tables[rule_bridge], rule_bridge) linux_net.add_ip_route( self.ovn_routing_tables_routes, ip, self.ovn_routing_tables[rule_bridge], rule_bridge, vlan=vlan_tag) # CR-LRP Port elif (row.type == "chassisredirect" and row.logical_port.startswith('cr-')): _, cr_lrp_datapath = self.sb_idl.get_fip_associated( row.logical_port) if cr_lrp_datapath: LOG.info("Add BGP route for CR-LRP Port {}".format(ips)) # Keeping information about the associated network for # tenant network advertisement self.ovn_local_cr_lrps[row.logical_port] = { 'router_datapath': row.datapath, 'provider_datapath': cr_lrp_datapath, 'ips': ips } ips_without_mask = [ip.split("/")[0] for ip in ips] linux_net.add_ips_to_dev(constants.OVN_BGP_NIC, ips_without_mask) rule_bridge, vlan_tag = self._get_bridge_for_datapath( cr_lrp_datapath) for ip in ips: ip_without_mask = ip.split("/")[0] linux_net.add_ip_rule( ip_without_mask, self.ovn_routing_tables[rule_bridge], rule_bridge, lladdr=row.mac[0].split(' ')[0]) linux_net.add_ip_route( self.ovn_routing_tables_routes, ip_without_mask, self.ovn_routing_tables[rule_bridge], rule_bridge, vlan=vlan_tag) # add proxy ndp config for ipv6 if (utils.get_ip_version(ip_without_mask) == constants.IP_VERSION_6): linux_net.add_ndp_proxy(ip, rule_bridge, vlan_tag) # Check if there are networks attached to the router, # and if so, add the needed routes/rules if not self._expose_tenant_networks: return lrp_ports = self.sb_idl.get_lrp_ports_for_router( row.datapath) for lrp in lrp_ports: if lrp.chassis: continue self._ensure_network_exposed( lrp, self.ovn_local_cr_lrps[row.logical_port])
def withdraw_IP(self, ips, row, associated_port=None): '''Withdraw BGP route by removing IP from device. This methods ensures BGP withdraw an advertised IP of a VM, either in the provider network, or the FIP associated to a VM in a tenant networks. It relies on Zebra, which withdraws the advertisement as soon as the IP is deleted from the local interface. This method assumes a device named self.ovn_decice exists (inside a VRF), and removes the IP of either: - VM IP on the provider network, - VM FIP, or - CR-LRP OVN port ''' # VM on provider Network if ((row.type == "" or row.type == "virtual") and self.sb_idl.is_provider_network(row.datapath)): LOG.info("Delete BGP route for logical port with ip {}".format( ips)) linux_net.del_ips_from_dev(constants.OVN_BGP_NIC, ips) rule_bridge, vlan_tag = self._get_bridge_for_datapath(row.datapath) for ip in ips: linux_net.del_ip_rule(ip, self.ovn_routing_tables[rule_bridge], rule_bridge) linux_net.del_ip_route( self.ovn_routing_tables_routes, ip, self.ovn_routing_tables[rule_bridge], rule_bridge, vlan=vlan_tag) # VM with FIP elif row.type == "" or row.type == "virtual": # FIPs are only supported with IPv4 fip_address, fip_datapath = self.sb_idl.get_fip_associated( row.logical_port) if fip_address: LOG.info("Delete BGP route for FIP with ip {}".format( fip_address)) linux_net.del_ips_from_dev(constants.OVN_BGP_NIC, [fip_address]) rule_bridge, vlan_tag = self._get_bridge_for_datapath( fip_datapath) linux_net.del_ip_rule(fip_address, self.ovn_routing_tables[rule_bridge], rule_bridge) linux_net.del_ip_route( self.ovn_routing_tables_routes, fip_address, self.ovn_routing_tables[rule_bridge], rule_bridge, vlan=vlan_tag) # FIP association to VM elif row.type == "patch": if (associated_port and ( self.sb_idl.is_port_on_chassis( associated_port, self.chassis) or self.sb_idl.is_port_deleted(associated_port))): LOG.info("Delete BGP route for FIP with ip {}".format(ips)) linux_net.del_ips_from_dev(constants.OVN_BGP_NIC, ips) rule_bridge, vlan_tag = self._get_bridge_for_datapath( row.datapath) for ip in ips: linux_net.del_ip_rule(ip, self.ovn_routing_tables[rule_bridge], rule_bridge) linux_net.del_ip_route( self.ovn_routing_tables_routes, ip, self.ovn_routing_tables[rule_bridge], rule_bridge, vlan=vlan_tag) # CR-LRP Port elif (row.type == "chassisredirect" and row.logical_port.startswith('cr-')): cr_lrp_datapath = self.ovn_local_cr_lrps.get( row.logical_port, {}).get('provider_datapath') if cr_lrp_datapath: LOG.info("Delete BGP route for CR-LRP Port {}".format(ips)) # Removing information about the associated network for # tenant network advertisement ips_without_mask = [ip.split("/")[0] for ip in ips] linux_net.del_ips_from_dev(constants.OVN_BGP_NIC, ips_without_mask) rule_bridge, vlan_tag = self._get_bridge_for_datapath( cr_lrp_datapath) for ip in ips_without_mask: if utils.get_ip_version(ip) == constants.IP_VERSION_6: cr_lrp_ip = '{}/128'.format(ip) else: cr_lrp_ip = '{}/32'.format(ip) linux_net.del_ip_rule( cr_lrp_ip, self.ovn_routing_tables[rule_bridge], rule_bridge, lladdr=row.mac[0].split(' ')[0]) linux_net.del_ip_route( self.ovn_routing_tables_routes, ip, self.ovn_routing_tables[rule_bridge], rule_bridge, vlan=vlan_tag) # del proxy ndp config for ipv6 if utils.get_ip_version(ip) == constants.IP_VERSION_6: cr_lrps_on_same_provider = [ p for p in self.ovn_local_cr_lrps.values() if p['provider_datapath'] == cr_lrp_datapath] if (len(cr_lrps_on_same_provider) > 1): linux_net.del_ndp_proxy(ip, rule_bridge, vlan_tag) # Check if there are networks attached to the router, # and if so, delete the needed routes/rules lrp_ports = self.sb_idl.get_lrp_ports_for_router( row.datapath) for lrp in lrp_ports: if lrp.chassis: continue local_cr_lrp_info = self.ovn_local_cr_lrps.get( row.logical_port) if local_cr_lrp_info: self._remove_network_exposed(lrp, local_cr_lrp_info) try: del self.ovn_local_cr_lrps[row.logical_port] except KeyError: LOG.debug("Gateway port already cleanup from the agent")
def expose_subnet(self, row): evpn_info = self.sb_idl.get_evpn_info_from_port(row) ip = self.sb_idl.get_ip_from_port_peer(row) if not evpn_info: LOG.debug("No EVPN information for LRP Port {}. " "Not exposing IPs: {}.".format(row.logical_port, ip)) return lrp_logical_port = 'lrp-' + row.logical_port lrp_datapath = self.sb_idl.get_port_datapath(lrp_logical_port) cr_lrp = self.sb_idl.is_router_gateway_on_chassis( lrp_datapath, self.chassis) if not cr_lrp: return LOG.info("Add IP Routes for network {} on chassis {}".format( ip, self.chassis)) self.ovn_local_lrps[lrp_logical_port] = { 'datapath': lrp_datapath, 'ip': ip } cr_lrp_info = self.ovn_local_cr_lrps.get(cr_lrp, {}) cr_lrp_datapath = cr_lrp_info.get('provider_datapath') if not cr_lrp_datapath: LOG.info("Subnet not connected to the provider network. " "No need to expose it through EVPN") return if (evpn_info['bgp_as'] != cr_lrp_info.get('bgp_as') or evpn_info['vni'] != cr_lrp_info.get('vni')): LOG.error("EVPN information at router port (vni: {}, as: {}) does" " not match with information at subnet gateway port:" " {}".format(cr_lrp_info.get('vni'), cr_lrp_info.get('bgp_as'), evpn_info)) return cr_lrp_ips = [ ip_address.split('/')[0] for ip_address in cr_lrp_info.get('ips', []) ] datapath_bridge, vlan_tag = self._get_bridge_for_datapath( cr_lrp_datapath) ip_version = utils.get_ip_version(ip) for cr_lrp_ip in cr_lrp_ips: if utils.get_ip_version(cr_lrp_ip) == ip_version: linux_net.add_ip_route(self._ovn_routing_tables_routes, ip.split("/")[0], evpn_info['vni'], datapath_bridge, vlan=vlan_tag, mask=ip.split("/")[1], via=cr_lrp_ip) break if ip_version == constants.IP_VERSION_6: net_ip = '{}'.format(ipaddress.IPv6Network(ip, strict=False)) else: net_ip = '{}'.format(ipaddress.IPv4Network(ip, strict=False)) strip_vlan = False if vlan_tag: strip_vlan = True ovs.ensure_evpn_ovs_flow(datapath_bridge, constants.OVS_VRF_RULE_COOKIE, cr_lrp_info['mac'], cr_lrp_info['vrf'], net_ip, strip_vlan) # Check if there are VMs on the network # and if so expose the route network_port_datapath = row.datapath if not network_port_datapath: return ports = self.sb_idl.get_ports_on_datapath(network_port_datapath) for port in ports: if port.type != "" and port.type != "virtual": continue try: port_ips = [port.mac[0].split(' ')[1]] except IndexError: continue if len(port.mac[0].split(' ')) == 3: port_ips.append(port.mac[0].split(' ')[2]) for port_ip in port_ips: # Only adding the port ips that match the lrp # IP version port_ip_version = utils.get_ip_version(port_ip) if port_ip_version == ip_version: linux_net.add_ips_to_dev( cr_lrp_info['lo'], [port_ip], clear_local_route_at_table=evpn_info['vni']) self._ovn_exposed_evpn_ips.setdefault( cr_lrp_info['lo'], []).extend([port_ip])
def _ensure_network_exposed(self, router_port, gateway, exposed_ips=[], ovn_ip_rules={}): gateway_ips = [ip.split('/')[0] for ip in gateway['ips']] try: router_port_ip = router_port.mac[0].split(' ')[1] except IndexError: return router_ip = router_port_ip.split('/')[0] if router_ip in gateway_ips: return self.ovn_local_lrps.add(router_port.logical_port) rule_bridge, vlan_tag = self._get_bridge_for_datapath( gateway['provider_datapath']) linux_net.add_ip_rule(router_port_ip, self.ovn_routing_tables[rule_bridge], rule_bridge) if router_port_ip in ovn_ip_rules.keys(): del ovn_ip_rules[router_port_ip] router_port_ip_version = utils.get_ip_version(router_port_ip) for gateway_ip in gateway_ips: if utils.get_ip_version(gateway_ip) == router_port_ip_version: linux_net.add_ip_route( self.ovn_routing_tables_routes, router_ip, self.ovn_routing_tables[rule_bridge], rule_bridge, vlan=vlan_tag, mask=router_port_ip.split("/")[1], via=gateway_ip) break network_port_datapath = self.sb_idl.get_port_datapath( router_port.options['peer']) if network_port_datapath: ports = self.sb_idl.get_ports_on_datapath( network_port_datapath) for port in ports: if ((port.type != "" and port.type != "virtual") or (port.type == "" and not port.chassis)): continue try: port_ips = [port.mac[0].split(' ')[1]] except IndexError: continue if len(port.mac[0].split(' ')) == 3: port_ips.append(port.mac[0].split(' ')[2]) for port_ip in port_ips: # Only adding the port ips that match the lrp # IP version port_ip_version = utils.get_ip_version(port_ip) if port_ip_version == router_port_ip_version: linux_net.add_ips_to_dev( constants.OVN_BGP_NIC, [port_ip]) if port_ip in exposed_ips: exposed_ips.remove(port_ip) if router_port_ip_version == constants.IP_VERSION_6: ip_dst = "{}/128".format(port_ip) else: ip_dst = "{}/32".format(port_ip) if ip_dst in ovn_ip_rules.keys(): del ovn_ip_rules[ip_dst]