예제 #1
0
    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")
예제 #2
0
    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])
예제 #3
0
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))
예제 #4
0
    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]
예제 #5
0
    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)
예제 #6
0
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)
예제 #7
0
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'])
예제 #8
0
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)
예제 #9
0
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))
예제 #10
0
    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)
예제 #11
0
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])
예제 #12
0
    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)
예제 #13
0
    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)
예제 #14
0
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])
예제 #15
0
    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])
예제 #16
0
    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])
예제 #17
0
    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")
예제 #18
0
    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])
예제 #19
0
    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]