def test_6_same_nlri_same_attributes_order_multivalued(self):
        # Two routes with same NLRI but and same attributes should
        # hash to the same values and be equal, *even if* for a said
        # multivalued attributes, like extended community, the values
        # appear in a distinct order

        atts1 = exa.Attributes()
        ecoms1 = exa.ExtendedCommunities()
        ecoms1.communities.append(exa.RouteTarget(64512, 1))
        ecoms1.communities.append(
            exa.Encapsulation(exa.Encapsulation.Type.VXLAN))
        ecoms1.communities.append(exa.RouteTarget(64512, 2))
        atts1.add(ecoms1)

        atts2 = exa.Attributes()
        ecoms2 = exa.ExtendedCommunities()
        ecoms2.communities.append(exa.RouteTarget(64512, 2))
        ecoms2.communities.append(exa.RouteTarget(64512, 1))
        ecoms2.communities.append(
            exa.Encapsulation(exa.Encapsulation.Type.VXLAN))
        atts2.add(ecoms2)

        entry1 = engine.RouteEntry(base.NLRI1, None, atts1)
        entry2 = engine.RouteEntry(base.NLRI1, None, atts2)

        self.assertEqual(hash(entry1), hash(entry2))
        self.assertEqual(entry1, entry2)
    def test_4_same_nlri_distinct_attributes(self):
        # Two routes with same NLRI but distinct attributes should
        # not be equal

        atts1 = exa.Attributes()
        atts1.add(exa.LocalPreference(10))

        atts2 = exa.Attributes()
        atts2.add(exa.LocalPreference(20))

        entry1 = engine.RouteEntry(base.NLRI1, None, atts1)
        entry2 = engine.RouteEntry(base.NLRI1, None, atts2)

        self.assertNotEqual(entry1, entry2)
    def test_5_same_nlri_same_attributes(self):
        # Two routes with same NLRI but and same attributes should
        # hash to the same values and be equal.

        atts1 = exa.Attributes()
        atts1.add(exa.LocalPreference(10))

        atts2 = exa.Attributes()
        atts2.add(exa.LocalPreference(10))

        entry1 = engine.RouteEntry(base.NLRI1, None, atts1)
        entry2 = engine.RouteEntry(base.NLRI1, None, atts2)

        self.assertEqual(hash(entry1), hash(entry2))
        self.assertEqual(entry1, entry2)
Ejemplo n.º 4
0
    def __init__(self, *args, **kwargs):

        vpn_instance.VPNInstance.__init__(self, *args, **kwargs)

        self.gw_port = None

        # Advertise route to receive multi-destination traffic
        self.log.info("Generating BGP route for broadcast/multicast traffic")

        nlri = exa.EVPNMulticast(
            self.instance_rd,
            exa.EthernetTag(),
            exa.IP.create(self.bgp_manager.get_local_address()),
            None,
            exa.IP.create(self.bgp_manager.get_local_address()))

        attributes = exa.Attributes()

        attributes.add(self._gen_encap_extended_communities())

        # add PMSI Tunnel Attribute route
        attributes.add(
            exa.PMSIIngressReplication(self.dp_driver.get_local_address(),
                                       self.instance_label))

        self.multicast_route_entry = engine.RouteEntry(nlri, self.export_rts,
                                                       attributes)

        self._advertise_route(self.multicast_route_entry)
Ejemplo n.º 5
0
    def _new_flow_event(self, event_type, nlri, to_rts, attract_rts, source,
                        afi=exa.AFI(exa.AFI.ipv4),
                        safi=exa.SAFI(exa.SAFI.flow_vpn),
                        **kwargs):
        attributes = exa.Attributes()

        ecommunities = exa.ExtendedCommunities()
        ecommunities.communities.append(
            exa.TrafficRedirect(exa.ASN(int(to_rts[0].asn)),
                                int(to_rts[0].number))
        )

        attributes.add(ecommunities)

        flow_event = engine.RouteEvent(event_type,
                                       engine.RouteEntry(nlri, attract_rts,
                                                         attributes, source),
                                       source)

        self.event_target_worker.enqueue(flow_event)

        LOG.info("*** Emitting FlowSpec event to %s: %s",
                 self.event_target_worker, flow_event)

        self._wait()

        return flow_event
Ejemplo n.º 6
0
    def _route_for_readvertisement(self, route, label, rd,
                                   lb_consistent_hash_order,
                                   do_default=False):
        prefix = "0.0.0.0/0" if do_default else route.nlri.cidr.prefix()

        nlri = self._nlri_from(prefix, label, rd)

        attributes = exa.Attributes()

        # new RTRecord = original RTRecord (if any) + orig RTs converted into
        # RTRecord attributes
        orig_rtrecords = route.ecoms(exa.RTRecord)
        rts = route.ecoms(exa.RTExtCom)
        add_rtrecords = [exa.RTRecord.from_rt(rt) for rt in rts]

        final_rtrecords = list(set(orig_rtrecords) | set(add_rtrecords))

        ecoms = self._gen_encap_extended_communities()
        ecoms.communities += final_rtrecords
        ecoms.communities.append(
            exa.ConsistentHashSortOrder(lb_consistent_hash_order))
        attributes.add(ecoms)

        entry = engine.RouteEntry(nlri, self.readvertise_to_rts, attributes)
        self.log.debug("RouteEntry for (re-)advertisement: %s", entry)
        return entry
    def test_8_route_entry_set_rts(self):
        atts = exa.Attributes()
        ecoms = exa.ExtendedCommunities()
        ecoms.communities.append(exa.RouteTarget(64512, 1))
        ecoms.communities.append(exa.RouteTarget(64512, 2))
        ecoms.communities.append(
            exa.Encapsulation(exa.Encapsulation.Type.VXLAN))
        atts.add(exa.LocalPreference(20))
        atts.add(ecoms)

        entry = engine.RouteEntry(base.NLRI1, None, atts)

        # check that the route_entry object has the RTs we wanted
        self.assertIn(exa.RouteTarget(64512, 1), entry.route_targets)
        self.assertIn(exa.RouteTarget(64512, 2), entry.route_targets)

        # modify the route targets
        entry.set_route_targets(
            [exa.RouteTarget(64512, 3),
             exa.RouteTarget(64512, 1)])

        # check that the new RTs have replaced the old ones
        self.assertIn(exa.RouteTarget(64512, 1), entry.route_targets)
        self.assertIn(exa.RouteTarget(64512, 3), entry.route_targets)
        self.assertNotIn(exa.RouteTarget(64512, 2), entry.route_targets)

        # also need to check the RTs in the attributes
        ecoms = entry.attributes[
            exa.Attribute.CODE.EXTENDED_COMMUNITY].communities
        self.assertIn(exa.RouteTarget(64512, 1), ecoms)
        self.assertIn(exa.RouteTarget(64512, 3), ecoms)
        self.assertNotIn(exa.RouteTarget(64512, 2), ecoms)

        # check that other communities were preserved
        self.assertIn(exa.Encapsulation(exa.Encapsulation.Type.VXLAN), ecoms)
    def _process_received_route(self, action, nlri, attributes):
        self.log.info("Received route: %s, %s", nlri, attributes)

        route_entry = engine.RouteEntry(nlri, None, attributes)

        if action == exa_message.IN.ANNOUNCED:
            self._advertise_route(route_entry)
        elif action == exa_message.IN.WITHDRAWN:
            self._withdraw_route(route_entry)
        else:
            raise Exception("unsupported action ??? (%s)" % action)

        # TODO(tmmorin): move RTC code out-of the peer-specific code
        if (nlri.afi, nlri.safi) == (exa.AFI(exa.AFI.ipv4),
                                     exa.SAFI(exa.SAFI.rtc)):
            self.log.info("Received an RTC route")

            if nlri.rt is None:
                self.log.info("Received RTC is a wildcard")

            # the semantic of RTC routes does not distinguish between AFI/SAFIs
            # if our peer subscribed to a Route Target, it means that we needs
            # to send him all routes of any AFI/SAFI carrying this RouteTarget.
            for (afi, safi) in self._active_families:
                if (afi, safi) != (exa.AFI(exa.AFI.ipv4), exa.SAFI(
                        exa.SAFI.rtc)):
                    if action == exa_message.IN.ANNOUNCED:
                        self._subscribe(afi, safi, nlri.rt)
                    elif action == exa_message.IN.WITHDRAWN:
                        self._unsubscribe(afi, safi, nlri.rt)
                    else:
                        raise Exception("unsupported action ??? (%s)" % action)
Ejemplo n.º 9
0
    def _new_route_event(self, event_type, nlri, rts, source, nh, lp=0,
                         replaced_route_entry=None,
                         afi=exa.AFI(exa.AFI.ipv4),
                         safi=exa.SAFI(exa.SAFI.mpls_vpn),
                         **kwargs):
        attributes = exa.Attributes()
        attributes.add(exa.NextHop(nh))
        attributes.add(exa.LocalPreference(lp))

        if 'rtrecords' in kwargs:
            ecoms = exa.ExtendedCommunities()
            ecoms.communities += kwargs['rtrecords']
            attributes.add(ecoms)

        route_event = engine.RouteEvent(event_type,
                                        engine.RouteEntry(nlri, rts,
                                                          attributes, source),
                                        source)
        route_event.set_replaced_route(replaced_route_entry)

        LOG.info("*** Emitting event to %s: %s",
                 self.event_target_worker, route_event)

        self.event_target_worker._on_event(route_event)

        return route_event
Ejemplo n.º 10
0
    def _subscription_2_rtc_route_entry(self, subscription):

        nlri = exa.RTC.new(exa.AFI.ipv4, exa.SAFI.rtc, cfg.CONF.BGP.my_as,
                           subscription.route_target,
                           exa.IP.create(self.get_local_address()))

        route_entry = engine.RouteEntry(nlri)

        return route_entry
Ejemplo n.º 11
0
    def generate_vif_bgp_route(self, mac_address, ip_prefix, plen, label, rd):
        # Generate BGP route and advertise it...

        assert(plen == 32)

        # label parameter ignored, we need to use instance label
        nlri = exa.EVPNMAC(
            rd, exa.ESI(), exa.EthernetTag(), exa.MAC(mac_address), 6*8,
            exa.Labels([self.instance_label]),
            exa.IP.create(ip_prefix), None,
            exa.IP.create(self.dp_driver.get_local_address()))

        return engine.RouteEntry(nlri)
Ejemplo n.º 12
0
    def _routes_for_attract_static_dest_prefixes(self, endpoint):
        if not self.attract_static_dest_prefixes:
            return

        label, rd, _ = self._get_route_params_for_endpoint(endpoint)

        for prefix in self.attract_static_dest_prefixes:
            nlri = self._nlri_from(prefix, label, rd)

            entry = engine.RouteEntry(nlri, self.readvertise_to_rts)
            self.log.debug(
                "RouteEntry for attract static destination prefix: "
                "%s", entry)
            yield entry
Ejemplo n.º 13
0
    def update_route_targets(self, new_import_rts, new_export_rts):
        added_import_rt = set(new_import_rts) - set(self.import_rts)
        removed_import_rt = set(self.import_rts) - set(new_import_rts)

        self.log.debug("Added Import RTs: %s", added_import_rt)
        self.log.debug("Removed Import RTs: %s", removed_import_rt)

        # Unregister from BGP with these route targets
        for rt in removed_import_rt:
            self._unsubscribe(self.afi, self.safi, rt)
            self._unsubscribe(self.afi, exa.SAFI.flow_vpn, rt)

        # Update import and export route targets
        # (needs to be done before subscribe or we get a race where
        # VRF._imported rejects route that it's supposed to use)
        self.import_rts = new_import_rts

        # Register to BGP with these route targets
        for rt in added_import_rt:
            self._subscribe(self.afi, self.safi, rt)
            self._subscribe(self.afi, exa.SAFI.flow_vpn, rt)

        # Re-advertise all routes with new export RTs
        self.log.debug("Exports RTs: %s -> %s", self.export_rts,
                       new_export_rts)
        if frozenset(new_export_rts) != frozenset(self.export_rts):
            self.log.debug("Will re-export routes with new RTs")
            self.export_rts = new_export_rts
            for route_entry in self.endpoint_2_route.values():
                if new_export_rts:
                    self.log.info(
                        "Re-advertising route %s with updated RTs "
                        "(%s)", route_entry.nlri, new_export_rts)

                    updated_route_entry = engine.RouteEntry(
                        route_entry.nlri, None,
                        copy.copy(route_entry.attributes))
                    # reset the route_targets, overwriting RTs originally
                    # present in route_entry.attributes
                    updated_route_entry.set_route_targets(self.export_rts)

                    self.log.debug("   updated route: %s", updated_route_entry)

                    self._advertise_route(updated_route_entry)
                else:
                    self._withdraw_route(route_entry)
Ejemplo n.º 14
0
    def update_route_targets(self, new_import_rts, new_export_rts):
        added_import_rt = set(new_import_rts) - set(self.import_rts)
        removed_import_rt = set(self.import_rts) - set(new_import_rts)

        self.log.debug("%s %d - Added Import RTs: %s", self.instance_type,
                       self.instance_id, added_import_rt)
        self.log.debug("%s %d - Removed Import RTs: %s", self.instance_type,
                       self.instance_id, removed_import_rt)

        # Register to BGP with these route targets
        for rt in added_import_rt:
            self._subscribe(self.afi, self.safi, rt)
            self._subscribe(self.afi, exa.SAFI(exa.SAFI.flow_vpn), rt)

        # Unregister from BGP with these route targets
        for rt in removed_import_rt:
            self._unsubscribe(self.afi, self.safi, rt)
            self._unsubscribe(self.afi, exa.SAFI(exa.SAFI.flow_vpn), rt)

        # Update import and export route targets
        self.import_rts = new_import_rts

        # Re-advertise all routes with new export RTs
        self.log.debug("Exports RTs: %s -> %s", self.export_rts,
                       new_export_rts)
        if frozenset(new_export_rts) != frozenset(self.export_rts):
            self.log.debug("Will re-export routes with new RTs")
            self.export_rts = new_export_rts
            # FIXME: we should only update the routes that
            # are routes of ports plugged to the VPN instance,
            # not all routes which would wrongly include
            # routes that we re-advertise between RTs
            for route_entry in self.get_route_entries():
                self.log.info("Re-advertising route %s with updated RTs (%s)",
                              route_entry.nlri, new_export_rts)

                updated_route_entry = engine.RouteEntry(
                    route_entry.nlri, None, copy.copy(route_entry.attributes))
                # reset the route_targets
                # will RTs originally present in route_entry.attributes
                updated_route_entry.set_route_targets(self.export_rts)

                self.log.debug("   updated route: %s", updated_route_entry)

                self._advertise_route(updated_route_entry)
Ejemplo n.º 15
0
    def generate_vif_bgp_route(self, mac_address, ip_prefix, plen, label, rd):
        # Generate BGP route and advertise it...

        if ip_prefix:
            assert (plen == 32)

        if self._vxlan_dp_driver():
            mpls_label_field = exa.Labels([], raw_labels=[self.instance_label])
        else:
            mpls_label_field = exa.Labels([self.instance_label])

        # label parameter ignored, we need to use instance label
        nlri = exa.EVPNMAC(rd, exa.ESI(), exa.EthernetTag(),
                           exa.MAC(mac_address), 6 * 8, mpls_label_field,
                           exa.IP.create(ip_prefix) if ip_prefix else None,
                           None,
                           exa.IP.create(self.dp_driver.get_local_address()))

        return engine.RouteEntry(nlri)
Ejemplo n.º 16
0
    def _default_route_for_advertisement(self, endpoint):
        label, rd, lb_consistent_hash_order = (
            self._get_route_params_for_endpoint(endpoint))
        self.log.debug(
            "Default route (re-)advertisement with label %s and "
            "route distinguisher %s", label, rd)
        nlri = self._nlri_from(DEFAULT_ADDR_PREFIX, label, rd)

        attributes = exa.Attributes()

        ecoms = self._gen_encap_extended_communities()
        ecoms.communities.append(
            exa.ConsistentHashSortOrder(lb_consistent_hash_order))
        attributes.add(ecoms)

        entry = engine.RouteEntry(nlri, self.readvertise_to_rts, attributes)
        self.log.debug("RouteEntry for default prefix advertisement: %s",
                       entry)
        return entry
    def test_9_route_entry_rts_as_init_param(self):
        atts = exa.Attributes()
        ecoms = exa.ExtendedCommunities()
        ecoms.communities.append(
            exa.Encapsulation(exa.Encapsulation.Type.VXLAN))
        atts.add(exa.LocalPreference(20))
        atts.add(ecoms)

        rts = [exa.RouteTarget(64512, 1), exa.RouteTarget(64512, 2)]

        entry = engine.RouteEntry(base.NLRI1, rts, atts)

        self.assertIn(exa.RouteTarget(64512, 1), entry.route_targets)
        self.assertIn(exa.RouteTarget(64512, 2), entry.route_targets)

        ecoms = entry.attributes[
            exa.Attribute.CODE.EXTENDED_COMMUNITY].communities
        self.assertIn(exa.RouteTarget(64512, 1), ecoms)
        self.assertIn(exa.RouteTarget(64512, 2), ecoms)
        self.assertIn(exa.Encapsulation(exa.Encapsulation.Type.VXLAN), ecoms)
Ejemplo n.º 18
0
    def _redirect_route_for_readvertisement(self, route):
        # Create a FlowSpec NLRI with distinct RD and a copy of rules from
        # FlowSpec route to readvertise
        nlri = flowspec.FlowRouteFactory(self.afi, self.instance_rd)
        nlri.rules = route.nlri.rules

        attributes = exa.Attributes()

        ecoms = exa.ExtendedCommunities()
        ecoms.communities += (self._gen_rtrecords_extended_community(
            route.ecoms))
        assert len(self.attract_rts) == 1
        rt = self.attract_rts[0]
        ecoms.communities.append(
            exa.TrafficRedirect(exa.ASN(int(rt.asn)), int(rt.number)))
        attributes.add(ecoms)

        entry = engine.RouteEntry(nlri, self.readvertise_to_rts, attributes)
        self.log.debug("RouteEntry for redirect (re-)advertisement: %s", entry)
        return entry
Ejemplo n.º 19
0
    def _route_for_readvertisement(self, route, endpoint):
        label, rd, lb_consistent_hash_order = (
            self._get_route_params_for_endpoint(endpoint))
        self.log.debug(
            "Prefix %s (re-)advertisement with label %s and route "
            "distinguisher %s", route.nlri.cidr.prefix(), label, rd)
        nlri = self._nlri_from(route.nlri.cidr.prefix(), label, rd)

        attributes = exa.Attributes()

        ecoms = self._gen_encap_extended_communities()
        ecoms.communities += (self._gen_rtrecords_extended_community(
            route.ecoms))
        ecoms.communities.append(
            exa.ConsistentHashSortOrder(lb_consistent_hash_order))
        attributes.add(ecoms)

        entry = engine.RouteEntry(nlri, self.readvertise_to_rts, attributes)
        self.log.debug("RouteEntry for (re-)advertisement: %s", entry)
        return entry
Ejemplo n.º 20
0
    def __init__(self, *args, **kwargs):

        vpn_instance.VPNInstance.__init__(self, *args, **kwargs)

        self.gw_port = None

        encaps = self.dp_driver.supported_encaps()
        if (exa.Encapsulation(exa.Encapsulation.Type.VXLAN) in encaps
                and any([
                exa.Encapsulation(exa.Encapsulation.Type.MPLS) in encaps,
                exa.Encapsulation(exa.Encapsulation.Type.GRE) in encaps,
                exa.Encapsulation(exa.Encapsulation.Type.MPLS_UDP) in encaps
                ])):
            raise Exception("The dataplane can't support both an MPLS encap "
                            "and a VXLAN encapsulation")

        # Advertise route to receive multi-destination traffic
        self.log.info("Generating BGP route for broadcast/multicast traffic")

        nlri = exa.EVPNMulticast(
            self.instance_rd,
            exa.EthernetTag(),
            exa.IP.create(self.bgp_manager.get_local_address()),
            None,
            exa.IP.create(self.bgp_manager.get_local_address()))

        attributes = exa.Attributes()

        attributes.add(self._gen_encap_extended_communities())

        # add PMSI Tunnel Attribute route
        attributes.add(
            exa.PMSIIngressReplication(self.dp_driver.get_local_address(),
                                       raw_label=self.instance_label))

        self.multicast_route_entry = engine.RouteEntry(nlri, self.export_rts,
                                                       attributes)

        self._advertise_route(self.multicast_route_entry)
Ejemplo n.º 21
0
    def _new_flow_event(self, event_type, nlri, to_rts, attract_rts, source,
                        afi=exa.AFI(exa.AFI.ipv4),
                        safi=exa.SAFI(exa.SAFI.flow_vpn),
                        **kwargs):
        attributes = exa.Attributes()

        ecommunities = exa.ExtendedCommunities()
        ecommunities.communities.append(
            exa.TrafficRedirect(exa.ASN(int(to_rts[0].asn)),
                                int(to_rts[0].number))
        )

        attributes.add(ecommunities)

        flow_event = engine.RouteEvent(event_type,
                                       engine.RouteEntry(nlri, attract_rts,
                                                         attributes, source),
                                       source)

        self.event_target_worker._on_event(flow_event)

        return flow_event
Ejemplo n.º 22
0
    def test_f1_test_empty_rt(self):
        # worker advertises a route with no RT

        w1 = self._new_worker("Worker1", worker.Worker)

        subscribe = engine.Subscription(exa.AFI(exa.AFI.ipv4),
                                        exa.SAFI(exa.SAFI.mpls_vpn), None, w1)
        self.rtm.enqueue(subscribe)

        w2 = self._new_worker("Worker2", worker.Worker)

        route_event = engine.RouteEvent(
            engine.RouteEvent.ADVERTISE,
            engine.RouteEntry(t.NLRI1, None, exa.Attributes()), w2)

        self.rtm.enqueue(route_event)

        self._wait()

        self.assertEqual(
            1, w1.enqueue.call_count,
            "1 route advertised should be synthesized to Worker1")
Ejemplo n.º 23
0
    def synthesize_redirect_bgp_route(self, rules):
        self.log.info("synthesize_redirect_bgp_route called for rules %s",
                      rules)
        nlri = flowspec.FlowRouteFactory(self.afi, self.instance_rd)
        for rule in rules:
            nlri.add(rule)

        route_entry = engine.RouteEntry(nlri)

        assert isinstance(route_entry, engine.RouteEntry)

        ecommunities = exa.ExtendedCommunities()

        # checked at __init__:
        assert len(self.readvertise_to_rts) == 1
        rt = self.readvertise_to_rts[0]
        ecommunities.communities.append(
            exa.TrafficRedirect(exa.ASN(int(rt.asn)), int(rt.number)))

        route_entry.attributes.add(ecommunities)
        route_entry.set_route_targets(self.attract_rts)

        self.log.debug("Synthesized redirect route entry: %s", route_entry)
        return route_entry
Ejemplo n.º 24
0
    def generate_vif_bgp_route(self, mac_address, ip_prefix, plen, label, rd):
        # Generate BGP route and advertise it...
        nlri = self._nlri_from("%s/%s" % (ip_prefix, plen), label, rd)

        return engine.RouteEntry(nlri)