Example #1
0
    def validate_convert_params(cls, params, also_mandatory=()):

        for param in ('vpn_instance_id', 'mac_address',
                      'local_port') + also_mandatory:
            if param not in params:
                raise exc.APIMissingParameterException(param)

        # if local_port is not a dict, then assume it designates a linux
        # interface
        if (isinstance(params['local_port'], six.string_types)
                or isinstance(params['local_port'], six.text_type)):
            params['local_port'] = {'linuxif': params['local_port']}

        for param in ('import_rt', 'export_rt'):
            if param not in params:
                continue

            # if import_rt or export_rt are strings, convert them into lists
            if (isinstance(params[param], six.string_types)
                    or isinstance(params[param], six.text_type)):
                try:
                    params[param] = re.split(',+ *', params[param])
                except Exception:
                    raise exc.APIException("Unable to parse RT string into "
                                           " a list: '%s'" % params[param])
            # remove duplicates
            params[param] = list(set(params[param]))

        if not ('linuxif' in params['local_port']
                or 'evpn' in params['local_port']):
            raise exc.APIException("Mandatory key is missing in local_port "
                                   "parameter (linuxif, or evpn)")

        # validate mac_address
        try:
            netaddr.EUI(params['mac_address'])
        except netaddr.core.AddrFormatError:
            raise exc.MalformedMACAddress(params['mac_address'])

        # validate ip_address if present
        if 'ip_address' in params:
            try:
                net = netaddr.IPNetwork(params['ip_address'])
                params['ip_address_prefix'] = params['ip_address']
                params['ip_address_plen'] = net.prefixlen
            except netaddr.core.AddrFormatError:
                # Try again assuming ip_address is an IP without a prefix
                # (implicitly using /32)
                try:
                    netaddr.IPAddress(params['ip_address'])
                    params['ip_address_prefix'] = params['ip_address'] + "/32"
                    params['ip_address_plen'] = 32
                except netaddr.core.AddrFormatError:
                    raise exc.MalformedIPAddress(params['ip_address'])
        else:
            params['ip_address_prefix'] = None
            params['ip_address_plen'] = None

        if not isinstance(params.get('advertise_subnet', False), bool):
            raise exc.APIException("'advertise_subnet' must be a boolean")
Example #2
0
    def _check_instance_type(self, params):
        if 'vpn_type' not in params:
            raise exc.APIException("missing instance_type")

        instance_type = params['vpn_type']
        if instance_type not in self.type2class:
            raise exc.APIException("unknown vpn_type: %s" % instance_type)

        if instance_type not in self.dataplane_drivers:
            LOG.error("No dataplane driver for VPN type %s", instance_type)
            raise exc.APIException("No dataplane driver for VPN type %s" %
                                   instance_type)

        return instance_type
Example #3
0
    def _raise_if_mac2ip_inconsistency(self, mac_address, ip_address_prefix):
        if not ip_address_prefix:
            return

        if mac_address not in self.ip_address_2_mac[ip_address_prefix]:
            raise exc.APIException(
                "Inconsistent endpoint info: IP %s already bound to a MAC "
                "address different from %s (%s)" %
                (ip_address_prefix, mac_address,
                 self.ip_address_2_mac[ip_address_prefix]))
    def _parse_ipaddress_prefix(self, ip_address_prefix):
        ip_address = ""
        mask = 0
        try:
            net = netaddr.IPNetwork(ip_address_prefix)
            (ip_address, mask) = (str(net.ip), net.prefixlen)
        except netaddr.core.AddrFormatError:
            raise exc.APIException("Bogus IP prefix: %s" % ip_address_prefix)

        return (ip_address, mask)
Example #5
0
 def validate_directions(self, direction):
     # by default, assume a driver only supports plugging in both directions
     # if a driver does not support forwarding only the traffic to the port,
     # (and hence omit forwarding traffic from the port), it should raise an
     # exception if directions is TO_PORT
     # if a driver does not support forwarding only the traffic from the
     # port (and hence omit forwarding traffic to the port), it should raise
     # an exception if directions is FROM_PORT
     if (direction is not None) and (direction != constants.BOTH):
         self.log.warning("Unsupported direction: %s", direction)
         raise exc.APIException("Unsupported direction: %s" % direction)
    def validate_convert_params(cls, params, also_mandatory=()):

        for param in ('vpn_instance_id', 'mac_address', 'ip_address',
                      'local_port') + also_mandatory:
            if param not in params:
                raise exc.APIMissingParameterException(param)

        # if local_port is not a dict, then assume it designates a linux
        # interface
        if (isinstance(params['local_port'], six.string_types)
                or isinstance(params['local_port'], six.text_type)):
            params['local_port'] = {'linuxif': params['local_port']}

        # if import_rt or export_rt are strings, convert them into lists
        for param in ('import_rt', 'export_rt'):
            if (isinstance(params[param], six.string_types)
                    or isinstance(params[param], six.text_type)):
                try:
                    params[param] = re.split(',+ *', params[param])
                except Exception:
                    raise exc.APIException("Unable to parse RT string into "
                                           " a list: '%s'" % params[param])

        if not ('linuxif' in params['local_port']
                or 'evpn' in params['local_port']):
            raise exc.APIException("Mandatory key is missing in local_port "
                                   "parameter (linuxif, or evpn)")

        # Verify and format IP address with prefix if necessary
        if re.match(r'([12]?\d?\d\.){3}[12]?\d?\d\/[123]?\d',
                    params['ip_address']):
            params['ip_address_prefix'] = params['ip_address']
        elif re.match(r'([12]?\d?\d\.){3}[12]?\d?\d', params['ip_address']):
            params['ip_address_prefix'] = params['ip_address'] + "/32"
        else:
            raise exc.MalformedIPAddress()

        if not isinstance(params.get('advertise_subnet', False), bool):
            raise exc.APIException("'advertise_subnet' must be a boolean")
Example #7
0
    def validate_convert_attach_params(cls, params):
        cls.validate_convert_params(params,
                                    also_mandatory=('import_rt', 'export_rt'))

        params['advertise_subnet'] = params.get('advertise_subnet', False)
        params['lb_consistent_hash_order'] = params.get(
            'lb_consistent_hash_order', 0)
        params['vni'] = params.get('vni', 0)

        params['direction'] = params.get('direction') or constants.BOTH
        if params['direction'] not in constants.ALL_DIRECTIONS:
            raise exc.APIException("direction should be one of: %s" %
                                   ', '.join(constants.ALL_DIRECTIONS))

        cls.translate_api_internal(params)
Example #8
0
    def vif_plugged(self,
                    mac_address,
                    ip_address_prefix,
                    localport,
                    advertise_subnet=False,
                    lb_consistent_hash_order=0,
                    local_pref=None,
                    **kwargs):
        linuxif = localport['linuxif']
        endpoint = (mac_address, ip_address_prefix)
        # Check if this port has already been plugged
        # - Verify port informations consistency
        if mac_address in self.mac_2_localport_data:
            self.log.debug("MAC address already plugged, checking port "
                           "consistency")
            pdata = self.mac_2_localport_data[mac_address]

            if pdata.get("port_info") != localport:
                raise exc.APIException(
                    "Port information is not consistent. "
                    "MAC address cannot be bound to two "
                    "different ports. Previous plug for "
                    "port %s (%s != %s)" %
                    (linuxif, pdata.get("port_info"), localport))

        try:
            self.endpoint_2_desc[endpoint] = kwargs.get('description')

            (ip_prefix, plen,
             refresh_only) = self._check_ip_mac(mac_address, ip_address_prefix,
                                                advertise_subnet)

            self.log.debug("Plugging port (%s)", ip_prefix)

            pdata = self.mac_2_localport_data.get(mac_address, dict())
            if not pdata:
                pdata['label'] = self.manager.label_allocator.get_new_label(
                    "Incoming traffic for %s, interface %s, endpoint %s" %
                    (self, linuxif, endpoint))
                pdata["port_info"] = localport
                pdata["lb_consistent_hash_order"] = lb_consistent_hash_order

            direction = kwargs.get('direction')

            if not refresh_only:
                self.dp_driver.validate_directions(direction)
                # Call driver to setup the dataplane for incoming traffic
                self.dataplane.vif_plugged(mac_address, ip_prefix, localport,
                                           pdata['label'], direction)

            if forward_to_port(direction):
                endpoint_rd = self._rd_for_endpoint(
                    endpoint,
                    "Route distinguisher for %s, interface %s, endpoint %s" %
                    (self, linuxif, endpoint))

                rd = self.instance_rd if plen == 32 else endpoint_rd

                self.log.info(
                    "Synthesizing and advertising BGP route for VIF "
                    "%s endpoint %s", linuxif, endpoint)
                route_entry = self.synthesize_vif_bgp_route(
                    mac_address, ip_prefix, plen, pdata['label'],
                    lb_consistent_hash_order, rd, local_pref)

                self._advertise_route(route_entry)
                self.endpoint_2_route[endpoint] = route_entry
                self.endpoint_2_rd[endpoint] = endpoint_rd

            self.localport_2_endpoints[linuxif].add(endpoint)
            self.endpoint_2_lp[endpoint] = local_pref
            self.endpoint_2_direction[endpoint] = direction
            self.mac_2_localport_data[mac_address] = pdata

            if ip_address_prefix:
                self.ip_address_2_mac[ip_address_prefix].add(mac_address)
                self.mac_2_ips[mac_address].add(ip_address_prefix)

                # we've a non-wildcard plug, so if a wildcard IP was plugged
                # for this MAC, consider it replaced:
                # - withdraw the corresponding route
                # - cleanup the wildcard endpoint records
                wildcard_endpoint = (mac_address, None)
                if wildcard_endpoint in self.localport_2_endpoints[linuxif]:
                    self._withdraw_route(
                        self.endpoint_2_route.pop(wildcard_endpoint))
                    self.localport_2_endpoints[linuxif].remove(
                        wildcard_endpoint)
                    del self.endpoint_2_desc[wildcard_endpoint]

        except Exception as e:
            self.log.error("Error in vif_plugged: %s", e)
            if linuxif in self.localport_2_endpoints:
                self.localport_2_endpoints[linuxif].discard(endpoint)
                if not self.localport_2_endpoints[linuxif]:
                    del self.localport_2_endpoints[linuxif]
            if mac_address in self.mac_2_localport_data:
                del self.mac_2_localport_data[mac_address]
            if ip_address_prefix in self.ip_address_2_mac:
                self.ip_address_2_mac[ip_address_prefix].discard(mac_address)

            raise

        self.log.debug("localport_2_endpoints: %s", self.localport_2_endpoints)
        self.log.debug("endpoint_2_rd: %s", self.endpoint_2_rd)
        self.log.debug("mac_2_localport_data: %s", self.mac_2_localport_data)
        self.log.debug("ip_address_2_mac: %s", self.ip_address_2_mac)
Example #9
0
    def __init__(self,
                 vpn_manager,
                 dataplane_driver,
                 external_instance_id,
                 instance_id,
                 import_rts,
                 export_rts,
                 gateway_ip,
                 ip_address_plen,
                 readvertise,
                 attract_traffic,
                 fallback=None,
                 **kwargs):

        self.manager = vpn_manager

        self.instance_type = self.__class__.__name__
        self.instance_id = instance_id

        self.description = None

        threading.Thread.__init__(self)
        self.setDaemon(True)

        if dataplane_driver.ecmp_support:
            compare_routes = tracker_worker.compare_ecmp
        else:
            compare_routes = tracker_worker.compare_no_ecmp

        tracker_worker.TrackerWorker.__init__(self, self.manager.bgp_manager,
                                              repr(self), compare_routes)

        lg.LookingGlassLocalLogger.__init__(self, repr(self))
        self.lock = threading.RLock()

        self.import_rts = import_rts
        self.export_rts = export_rts
        self.external_instance_id = external_instance_id
        self.gateway_ip = gateway_ip
        self.network_plen = ip_address_plen

        self.fallback = None

        self.afi = self.__class__.afi
        self.safi = self.__class__.safi

        self.dp_driver = dataplane_driver

        if 'vni' in kwargs:
            self.instance_label = kwargs.pop('vni')
            self.forced_vni = True
        else:
            self.instance_label = self.manager.label_allocator.get_new_label(
                "Incoming traffic for %s" % self)
            self.forced_vni = False

        self.instance_rd = self.manager.rd_allocator.get_new_rd(
            "Default route distinguisher for %s" % self)

        self.localport_data = dict()

        # One local port -> set of endpoints (MAC and IP addresses tuple)
        self.localport_2_endpoints = collections.defaultdict(set)
        # One endpoint (MAC and IP addresses tuple) -> One route distinguisher
        self.endpoint_2_rd = dict()
        # endpoint (MAC and IP addresses tuple) -> route entry
        self.endpoint_2_route = dict()
        # One MAC address -> One local port
        self.mac_2_localport_data = dict()
        # One IP address prefix ->  Multiple MAC address
        self.ip_address_2_mac = collections.defaultdict(set)

        # One endpoint (MAC and IP addresses tuple) -> BGP local_pref
        self.endpoint_2_lp = dict()

        # endpoint (MAC and IP addresses tuple) -> description
        self.endpoint_2_desc = dict()

        # endpoint (MAC and IP addresses tuple) -> direction
        self.endpoint_2_direction = dict()

        # MAC -> set of IP addresses
        self.mac_2_ips = collections.defaultdict(set)

        # Redirected instances list from which traffic is attracted (based on
        # FlowSpec 5-tuple classification)
        self.redirected_instances = set()

        self.dataplane = self.dp_driver.initialize_dataplane_instance(
            self.instance_id, self.external_instance_id, self.gateway_ip,
            self.network_plen, self.instance_label, **kwargs)

        for rt in self.import_rts:
            self._subscribe(self.afi, self.safi, rt)
            # Subscribe to FlowSpec routes
            # FIXME(tmorin): this maybe isn't applicable yet to E-VPN yet
            self._subscribe(self.afi, exa.SAFI.flow_vpn, rt)

        if readvertise:
            self.readvertise = True
            try:
                self.readvertise_to_rts = readvertise['to_rt']
            except KeyError:
                raise exc.APIException("'readvertise' specified with no "
                                       "'to_rt'")
            self.readvertise_from_rts = readvertise.get('from_rt', [])
            self.log.debug("readvertise enabled, from RT:%s, to %s",
                           self.readvertise_from_rts, self.readvertise_to_rts)
            for rt in self.readvertise_from_rts:
                self._subscribe(self.afi, self.safi, rt)
        else:
            self.log.debug("readvertise not enabled")
            self.readvertise = False

        self.attract_static_dest_prefixes = None

        if (attract_traffic
                and (self.readvertise or attract_traffic.get('to_rt'))):
            # Convert route target string to RouteTarget dictionary
            attract_to_rts = attract_traffic.get('to_rt')

            if attract_to_rts:
                converted_to_rts = (
                    utils.convert_route_targets(attract_to_rts))
                if self.readvertise:
                    if converted_to_rts != self.readvertise_to_rts:
                        raise exc.APIException("if both are set, then "
                                               "'attract_traffic/to_rt' and "
                                               "'readvertise/to_rt' have to "
                                               "be equal")
                else:
                    self.readvertise_to_rts = converted_to_rts

                try:
                    self.attract_static_dest_prefixes = (
                        attract_traffic['static_destination_prefixes'])
                except KeyError:
                    raise exc.APIException("'attract_traffic/to_rt' specified "
                                           "without "
                                           "'static_destination_prefixes'")

            if len(self.readvertise_to_rts) != 1:
                raise exc.APIException("attract_traffic requires exactly one "
                                       "RT to be provided in "
                                       "'readvertise/to_rt' and "
                                       "'attract_traffic/to_rt'")
            self.attract_traffic = True
            self.attract_rts = attract_traffic['redirect_rts']
            try:
                self.attract_classifier = attract_traffic['classifier']
            except KeyError:
                raise exc.APIException("'attract_traffic' specified with no "
                                       "'classifier'")
            self.log.debug(
                "Attract traffic enabled with RT: %s and "
                "classifier: %s", self.attract_rts, self.attract_classifier)
        else:
            self.log.debug("attract traffic not enabled")
            self.attract_traffic = False

        self.dataplane.update_fallback(fallback)
Example #10
0
    def vif_unplugged(self,
                      mac_address,
                      ip_address_prefix,
                      advertise_subnet=False,
                      lb_consistent_hash_order=0):
        # NOTE(tmorin): move this as a vif_unplugged_precheck, so that
        # in ipvpn.VRF this is done before readvertised route withdrawal

        # Verify port and endpoint (MAC address, IP address) tuple consistency
        pdata = self.mac_2_localport_data.get(mac_address)
        if (not pdata or
            (ip_address_prefix in self.ip_address_2_mac
             and mac_address not in self.ip_address_2_mac[ip_address_prefix])
                or (mac_address, ip_address_prefix) not in self.endpoint_2_rd):
            self.log.error(
                "vif_unplugged called for endpoint (%s, %s), but "
                "no consistent informations or was not plugged yet",
                mac_address, ip_address_prefix)
            raise exc.APIException("Inconsistent endpoint (%s, %s) info "
                                   "or endpoint wasn't plugged yet, "
                                   "cannot unplug" %
                                   (mac_address, ip_address_prefix))

        # Finding label and local port informations
        label = pdata.get('label')
        endpoint_rd = self.endpoint_2_rd[(mac_address, ip_address_prefix)]
        localport = pdata.get('port_info')
        linuxif = localport['linuxif']
        if not label or not localport:
            self.log.error(
                "vif_unplugged called for endpoint (%s, %s), but "
                "port data (%s, %s) is incomplete", mac_address,
                ip_address_prefix, label, localport)
            raise Exception("Inconsistent informations for port, bug ?")

        if linuxif in self.localport_2_endpoints:
            # Parse address/mask
            (ip_prefix, plen) = self._parse_ipaddress_prefix(ip_address_prefix)

            last_endpoint = len(self.localport_2_endpoints[linuxif]) <= 1

            if not advertise_subnet and plen != 32:
                self.log.debug("Using /32 instead of /%d", plen)
                plen = 32

            rd = self.instance_rd if plen == 32 else endpoint_rd

            self.log.info(
                "Synthesizing and withdrawing BGP route for VIF %s "
                "endpoint (%s, %s/%d)", linuxif, mac_address, ip_prefix, plen)

            route_entry = self.synthesize_vif_bgp_route(
                mac_address, ip_prefix, plen, label, lb_consistent_hash_order,
                rd)

            self._withdraw_route(route_entry)

            # Unplug endpoint from data plane
            self.dataplane.vif_unplugged(mac_address, ip_prefix, localport,
                                         label, last_endpoint)

            # Forget data for this port if last endpoint
            if last_endpoint:
                self.log.info("Last endpoint, freeing label %s", label)
                # Free label to the allocator
                self.manager.label_allocator.release(label)

                del self.localport_2_endpoints[linuxif]
            else:
                self.localport_2_endpoints[linuxif].remove({
                    'mac':
                    mac_address,
                    'ip':
                    ip_address_prefix
                })

            del self.endpoint_2_rd[(mac_address, ip_address_prefix)]
            # Free route distinguisher to the allocator
            self.manager.rd_allocator.release(endpoint_rd)

            if not last_endpoint:
                if not any([
                        endpoint['mac'] == mac_address
                        for endpoint in self.localport_2_endpoints[linuxif]
                ]):
                    del self.mac_2_localport_data[mac_address]
            else:
                del self.mac_2_localport_data[mac_address]

            self.ip_address_2_mac[ip_address_prefix].remove(mac_address)

            if not self.ip_address_2_mac[ip_address_prefix]:
                del self.ip_address_2_mac[ip_address_prefix]
        else:
            self.log.error(
                "vif_unplugged called for endpoint {%s, %s}, but"
                " port data is incomplete", mac_address, ip_address_prefix)
            raise Exception("bagpipe-bgp bug, check its logs")

        self.log.info("localport_2_endpoints: %s", self.localport_2_endpoints)
        self.log.info("mac_2_localport_data: %s", self.mac_2_localport_data)
        self.log.info("ip_address_2_mac: %s", self.ip_address_2_mac)
Example #11
0
    def vif_plugged(self,
                    mac_address,
                    ip_address_prefix,
                    localport,
                    advertise_subnet=False,
                    lb_consistent_hash_order=0):
        linuxif = localport['linuxif']
        # Check if this port has already been plugged
        # - Verify port informations consistency
        if mac_address in self.mac_2_localport_data:
            self.log.debug("MAC address already plugged, checking port "
                           "consistency")
            pdata = self.mac_2_localport_data[mac_address]

            if pdata.get("port_info") != localport:
                raise exc.APIException(
                    "Port information is not consistent. "
                    "MAC address cannot be bound to two "
                    "different ports. Previous plug for "
                    "port %s (%s != %s)" %
                    (linuxif, pdata.get("port_info"), localport))

        try:
            # Parse address/mask
            (ip_prefix, plen) = self._parse_ipaddress_prefix(ip_address_prefix)

            if not advertise_subnet and plen != 32:
                self.log.debug("Using /32 instead of /%d", plen)
                plen = 32

            # - Verify (MAC address, IP address) tuple consistency
            if ip_address_prefix in self.ip_address_2_mac and plen == 32:
                if mac_address not in self.ip_address_2_mac[ip_address_prefix]:
                    raise exc.APIException("Inconsistent endpoint info: %s "
                                           "already bound to a MAC address "
                                           "different from %s" %
                                           (ip_address_prefix, mac_address))
                else:
                    return

            self.log.debug("Plugging port (%s)", ip_prefix)

            pdata = self.mac_2_localport_data.get(mac_address, dict())
            if not pdata:
                pdata['label'] = self.manager.label_allocator.get_new_label(
                    "Incoming traffic for %s %d, interface %s"
                    ", endpoint %s/%s" %
                    (self.instance_type, self.instance_id, linuxif,
                     mac_address, ip_address_prefix))
                pdata["port_info"] = localport
                pdata["lb_consistent_hash_order"] = lb_consistent_hash_order

            endpoint_rd = self.manager.rd_allocator.get_new_rd(
                "Route distinguisher for %s %d, interface %s, "
                "endpoint %s/%s" % (self.instance_type, self.instance_id,
                                    linuxif, mac_address, ip_address_prefix))

            rd = self.instance_rd if plen == 32 else endpoint_rd

            # Call driver to setup the dataplane for incoming traffic
            self.dataplane.vif_plugged(mac_address, ip_prefix, localport,
                                       pdata['label'])

            self.log.info(
                "Synthesizing and advertising BGP route for VIF %s "
                "endpoint (%s, %s/%d)", linuxif, mac_address, ip_prefix, plen)
            route_entry = self.synthesize_vif_bgp_route(
                mac_address, ip_prefix, plen, pdata['label'],
                lb_consistent_hash_order, rd)

            self._advertise_route(route_entry)

            if linuxif not in self.localport_2_endpoints:
                self.localport_2_endpoints[linuxif] = list()

            self.localport_2_endpoints[linuxif].append({
                'mac': mac_address,
                'ip': ip_address_prefix
            })
            self.endpoint_2_rd[(mac_address, ip_address_prefix)] = endpoint_rd
            self.mac_2_localport_data[mac_address] = pdata

            if ip_address_prefix not in self.ip_address_2_mac:
                self.ip_address_2_mac[ip_address_prefix] = list()

            self.ip_address_2_mac[ip_address_prefix].append(mac_address)

        except Exception as e:
            self.log.error("Error in vif_plugged: %s", e)
            if linuxif in self.localport_2_endpoints:
                endpoint = {'mac': mac_address, 'ip': ip_address_prefix}
                if endpoint in self.localport_2_endpoints[linuxif]:
                    self.localport_2_endpoints[linuxif].remove(endpoint)
                if not self.localport_2_endpoints[linuxif]:
                    del self.localport_2_endpoints[linuxif]
            if mac_address in self.mac_2_localport_data:
                del self.mac_2_localport_data[mac_address]
            if ip_address_prefix in self.ip_address_2_mac:
                try:
                    self.ip_address_2_mac[ip_address_prefix].remove(
                        mac_address)
                except ValueError:
                    # ok, value just had not been added
                    pass

            raise e

        self.log.info("localport_2_endpoints: %s", self.localport_2_endpoints)
        self.log.info("endpoint_2_rd: %s", self.endpoint_2_rd)
        self.log.info("mac_2_localport_data: %s", self.mac_2_localport_data)
        self.log.info("ip_address_2_mac: %s", self.ip_address_2_mac)
Example #12
0
    def __init__(self,
                 vpn_manager,
                 dataplane_driver,
                 external_instance_id,
                 instance_id,
                 import_rts,
                 export_rts,
                 gateway_ip,
                 mask,
                 readvertise,
                 attract_traffic,
                 fallback=None,
                 **kwargs):

        self.manager = vpn_manager

        self.instance_type = self.__class__.__name__
        self.instance_id = instance_id

        threading.Thread.__init__(self)
        self.setDaemon(True)

        if dataplane_driver.ecmp_support:
            compare_routes = tracker_worker.compare_ecmp
        else:
            compare_routes = tracker_worker.compare_no_ecmp

        tracker_worker.TrackerWorker.__init__(
            self, self.manager.bgp_manager,
            "%s-%d" % (self.instance_type, self.instance_id), compare_routes)

        lg.LookingGlassLocalLogger.__init__(
            self, "%s-%d" % (self.instance_type, self.instance_id))
        self.lock = threading.Lock()

        self.import_rts = import_rts
        self.export_rts = export_rts
        self.external_instance_id = external_instance_id
        self.gateway_ip = gateway_ip
        self.mask = mask

        self.fallback = None

        self.afi = self.__class__.afi
        self.safi = self.__class__.safi
        assert isinstance(self.afi, exa.AFI)
        assert isinstance(self.safi, exa.SAFI)

        self.dp_driver = dataplane_driver

        if 'vni' in kwargs:
            self.instance_label = kwargs.pop('vni')
            self.forced_vni = True
        else:
            self.instance_label = self.manager.label_allocator.get_new_label(
                "Incoming traffic for %s %d" %
                (self.instance_type, self.instance_id))
            self.forced_vni = False

        self.instance_rd = self.manager.rd_allocator.get_new_rd(
            "Default route distinguisher for %s %d" %
            (self.instance_type, self.instance_id))

        self.localport_data = dict()

        # One local port -> List of endpoints (MAC and IP addresses tuple)
        self.localport_2_endpoints = dict()
        # One endpoint (MAC and IP addresses tuple) -> One route distinguisher
        self.endpoint_2_rd = dict()
        # One MAC address -> One local port
        self.mac_2_localport_data = dict()
        # One IP address ->  Multiple MAC address
        self.ip_address_2_mac = dict()

        # Redirected instances list from which traffic is attracted (based on
        # FlowSpec 5-tuple classification)
        self.redirected_instances = set()

        self.dataplane = self.dp_driver.initialize_dataplane_instance(
            self.instance_id, self.external_instance_id, self.gateway_ip,
            self.mask, self.instance_label, **kwargs)

        for rt in self.import_rts:
            self._subscribe(self.afi, self.safi, rt)
            # Subscribe to FlowSpec routes
            # FIXME(tmorin): this maybe isn't applicable yet to E-VPN yet
            self._subscribe(self.afi, exa.SAFI(exa.SAFI.flow_vpn), rt)

        if readvertise:
            self.readvertise = True
            try:
                self.readvertise_to_rts = readvertise['to_rt']
            except KeyError:
                raise exc.APIException("'readvertise' specified with no "
                                       "'to_rt'")
            self.readvertise_from_rts = readvertise.get('from_rt', [])
            self.log.debug("readvertise enabled, from RT:%s, to %s",
                           self.readvertise_from_rts, self.readvertise_to_rts)
            for rt in self.readvertise_from_rts:
                self._subscribe(self.afi, self.safi, rt)
        else:
            self.log.debug("readvertise not enabled")
            self.readvertise = False

        if self.readvertise and attract_traffic:
            if len(self.readvertise_to_rts) != 1:
                raise exc.APIException("attract_traffic requires exactly one "
                                       "RT to be provided in "
                                       "readvertise/to_rt")
            self.attract_traffic = True
            self.attract_rts = attract_traffic['redirect_rts']
            try:
                self.attract_classifier = attract_traffic['classifier']
            except KeyError:
                raise exc.APIException("'attract_traffic' specified with no "
                                       "'classifier'")
            self.log.debug(
                "Attract traffic enabled with RT: %s and "
                "classifier: %s", self.attract_rts, self.attract_classifier)
        else:
            self.log.debug("attract traffic not enabled")
            self.attract_traffic = False

        self.dataplane.update_fallback(fallback)