def new_open(self, restarted, asn4, config, enabledFamilies=[]):
        '''Same as exabgp.Protocol.new_open except that we advertise support
        for MPLS VPN and RTC'''

        asn = self.neighbor.local_as
        # (we don't support ASN4)

        o = Open(4, asn, self.neighbor.router_id.ip,
                 Capabilities().default(self.neighbor, restarted),
                 self.neighbor.hold_time)

        o.capabilities[Capabilities.MULTIPROTOCOL_EXTENSIONS].remove(
            (AFI(AFI.ipv4), SAFI(SAFI.flow_ipv4)))
        o.capabilities[Capabilities.MULTIPROTOCOL_EXTENSIONS].remove(
            (AFI(AFI.ipv4), SAFI(SAFI.unicast)))
        o.capabilities[Capabilities.MULTIPROTOCOL_EXTENSIONS].remove(
            (AFI(AFI.ipv6), SAFI(SAFI.unicast)))
        for afi_safi in enabledFamilies:
            o.capabilities[Capabilities.MULTIPROTOCOL_EXTENSIONS].append(
                afi_safi)

        if config['enable_rtc']:
            o.capabilities[Capabilities.MULTIPROTOCOL_EXTENSIONS].append(
                (AFI(AFI.ipv4), SAFI(SAFI.rtc)))

        if not self.connection.write(o.message()):
            raise Exception("Error while sending open")

        return o
    def __cmp__(self, other):
        assert isinstance(other, Match)

        self_afi = self.afi or AFI(0)
        self_safi = self.safi or SAFI(0)
        self_rt = self.routeTarget or RouteTarget(0, None, 0)

        other_afi = other.afi or AFI(0)
        other_safi = other.safi or SAFI(0)
        other_rt = other.routeTarget or RouteTarget(0, None, 0)

        return cmp((self_afi, self_safi, self_rt),
                   (other_afi, other_safi, other_rt))
示例#3
0
    def _subscription2RTCRouteEntry(self, subscription):

        route = Route(
            RouteTargetConstraint(AFI(AFI.ipv4), SAFI(SAFI.rtc),
                                  self.config['my_as'],
                                  subscription.routeTarget))
        nh = Inet(
            1, socket.inet_pton(socket.AF_INET, self.config['local_address']))
        route.attributes.add(NextHop(nh))

        routeEntry = RouteEntry(AFI(AFI.ipv4), SAFI(SAFI.rtc), [], route.nlri,
                                route.attributes, self)

        return routeEntry
    def getLGRoutes(self, pathPrefix):
        result = {}

        match_IPVPN = Match(AFI(AFI.ipv4), SAFI(SAFI.mpls_vpn),
                            Subscription.ANY_RT)
        match_EVPN = Match(AFI(AFI.l2vpn), SAFI(SAFI.evpn),
                           Subscription.ANY_RT)
        match_RTC = Match(AFI(AFI.ipv4), SAFI(SAFI.rtc), Subscription.ANY_RT)
        for match in [match_IPVPN, match_EVPN, match_RTC]:
            matchResult = []
            if match in self._match2workersAndEntries:
                for entry in self._match2entries(match):
                    matchResult.append(entry.getLookingGlassInfo(pathPrefix))
            result[repr(match)] = matchResult
        return result
 def _workerUnsubscriptions(self,
                            worker,
                            rts,
                            afi=AFI(AFI.ipv4),
                            safi=SAFI(SAFI.mpls_vpn)):
     for rt in rts:
         unsubscribe = Unsubscription(afi, safi, rt, worker)
         self.routeTableManager.enqueue(unsubscribe)
示例#6
0
 def __init__(self,afi,safi,prefix,rd,labelStack):
     self.afi = AFI(afi)
     self.safi = SAFI(safi)
     self.rd = rd
     self.labelStack = labelStack  # an array of LabelStackEntry's
     if type(labelStack) != list or len(labelStack)==0:
         raise Exception("Labelstack has to be a non-empty array")
     self.prefix = prefix
 def getAllRoutesButRTC(self):
     try:
         return [
             re for re in self._match2workersAndEntries[Match(
                 Subscription.ANY_AFI, Subscription.ANY_SAFI,
                 Subscription.ANY_RT)].entries
             if not (re.afi == AFI(AFI.ipv4) and re.safi == SAFI(SAFI.rtc))
         ]
     except KeyError:
         return []
    def _toEstablished(self):
        BGPPeerWorker._toEstablished(self)

        if self.rtc_active:
            # subscribe to RTC routes, to be able to propagate them from
            # internal workers to this peer
            self._subscribe(AFI(AFI.ipv4), SAFI(SAFI.rtc))
        else:
            # if we don't use RTC with our peer, then we need to see events for
            # all routes of all active families, to be able to send them to him
            for (afi, safi) in self._activeFamilies:
                self._subscribe(afi, safi)
    def _processReceivedRoute(self, route):
        self.log.info("Received route: %s", route)

        rts = []
        if AttributeID.EXTENDED_COMMUNITY in route.attributes:
            rts = [
                ecom for ecom in route.attributes[
                    AttributeID.EXTENDED_COMMUNITY].communities
                if isinstance(ecom, RouteTarget)
            ]

            if not rts:
                raise Exception("Unable to find any Route Targets"
                                "in the received route")

        routeEntry = self._newRouteEntry(route.nlri.afi, route.nlri.safi, rts,
                                         route.nlri, route.attributes)

        if route.action == "announce":
            self._pushEvent(RouteEvent(RouteEvent.ADVERTISE, routeEntry))
        else:
            self._pushEvent(RouteEvent(RouteEvent.WITHDRAW, routeEntry))

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

            if route.nlri.route_target 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._activeFamilies:
                if (afi, safi) != (AFI(AFI.ipv4), SAFI(SAFI.rtc)):
                    if route.action == "announce":
                        self._subscribe(afi, safi, route.nlri.route_target)
                    else:  # withdraw
                        self._unsubscribe(afi, safi, route.nlri.route_target)
    def testF1_testEmptyRT(self):
        # worker advertises a route with no RT

        w1 = self._newworker("Worker1", Worker)

        subscribe = Subscription(AFI(AFI.ipv4), SAFI(SAFI.mpls_vpn), None, w1)
        self.routeTableManager.enqueue(subscribe)

        w2 = self._newworker("Worker2", Worker)

        routeEvent = RouteEvent(
            RouteEvent.ADVERTISE,
            RouteEntry(AFI(AFI.ipv4), SAFI(SAFI.mpls_vpn), None, NLRI1,
                       Attributes(), w2), w2)

        self.routeTableManager.enqueue(routeEvent)

        self._wait()

        self.assertEqual(
            1, w1.enqueue.call_count,
            "1 route advertised should be synthesized to Worker1")
示例#11
0
    def _newRouteEvent(self, eventType, nlri, rts, source, nh, lp=0,
                       replacedRouteEntry=None,
                       afi=AFI(AFI.ipv4), safi=SAFI(SAFI.mpls_vpn)):
        attributes = Attributes()
        attributes.add(NextHop(nh))
        attributes.add(LocalPreference(lp))
        routeEvent = RouteEvent(eventType, RouteEntry(
            afi, safi, rts, nlri, attributes, source), source)
        routeEvent.setReplacedRoute(replacedRouteEntry)

        self.eventTargetWorker.enqueue(routeEvent)

        log.info("Emitting event to %s: %s",
                 self.eventTargetWorker, routeEvent)

        self._wait()

        return routeEvent
示例#12
0
class Subscription(_SubUnsubCommon):
    """Represents a Subscription to RouteEvents

A subscription specifies the AFI, the SAFI, and the Route Target of the
RouteEntry for which the subscriber wants to receive events.

Any of these (afi, safi or route target) can be replaced by a wildcard:

* Subscription.ANY_AFI
* Subscription.ANY_SAFI
* Subscription.ANY_RT
    """

    ANY_AFI = AFI(0)
    ANY_SAFI = SAFI(0)
    ANY_RT = None

    def __init__(self, afi, safi, routeTarget=None, worker=None):
        _SubUnsubCommon.__init__(self, afi, safi, routeTarget, worker)
示例#13
0
    def setUp(self):
        super(TestVPNInstance, self).setUp()
        self.labelAllocator = LabelAllocator()

        self.mockDataplane = mock.Mock()
        self.mockDataplane.vifPlugged = mock.Mock()
        self.mockDataplane.vifUnplugged = mock.Mock()

        self.mockDPDriver = mock.Mock()
        self.mockDPDriver.initializeDataplaneInstance.returnValue = \
            self.mockDataplane

        VPNInstance.afi = AFI(AFI.ipv4)
        VPNInstance.safi = SAFI(SAFI.mpls_vpn)
        self.vpnInstance = TestableVPNInstance(mock.Mock(name='BGPManager'),
                                               self.labelAllocator,
                                               self.mockDPDriver, 1, 1, [RT1],
                                               [RT1], '10.0.0.1', 24, None)
        self.vpnInstance.synthesizeVifBGPRoute = mock.Mock(
            return_value=RouteEntry(self.vpnInstance.afi, self.vpnInstance.
                                    safi, NLRI1, RT1, Attributes(), None))
        self.vpnInstance._pushEvent = mock.Mock()
        self.vpnInstance._postFirstPlug = mock.Mock()
        self.vpnInstance.start()
from bagpipe.bgp.engine import RouteEvent
from bagpipe.bgp.engine import RouteEntry
from bagpipe.bgp.engine import Subscription
from bagpipe.bgp.engine import Unsubscription
from bagpipe.bgp.engine.worker import Worker
from bagpipe.bgp.engine.bgp_peer_worker import BGPPeerWorker
from bagpipe.bgp.engine.route_table_manager import RouteTableManager
from bagpipe.bgp.engine.route_table_manager import Match
from bagpipe.bgp.engine.route_table_manager import WorkerCleanupEvent

from bagpipe.exabgp.message.update.attributes import Attributes
from bagpipe.exabgp.structure.address import AFI, SAFI

log = logging.getLogger()

MATCH1 = Match(AFI(AFI.ipv4), SAFI(SAFI.mpls_vpn), RT1)
MATCH2 = Match(AFI(AFI.ipv4), SAFI(SAFI.mpls_vpn), RT2)
MATCH3 = Match(AFI(AFI.ipv4), SAFI(SAFI.mpls_vpn), RT3)


class TestRouteTableManager(TestCase, BaseTestBagPipeBGP):
    def setUp(self):
        super(TestRouteTableManager, self).setUp()
        self.routeTableManager = RouteTableManager()
        self.routeTableManager.start()
        self.setEventTargetWorker(self.routeTableManager)

    def tearDown(self):
        super(TestRouteTableManager, self).tearDown()
        self.routeTableManager.stop()
        self.routeTableManager.join()
示例#15
0
class ExaBGPPeerWorker(BGPPeerWorker, LookingGlass):

    enabledFamilies = [
        (AFI(AFI.ipv4), SAFI(SAFI.mpls_vpn)),
        # (AFI(AFI.ipv6), SAFI(SAFI.mpls_vpn)),
        (AFI(AFI.l2vpn), SAFI(SAFI.evpn))
    ]

    def __init__(self, bgpManager, name, peerAddress, config):
        BGPPeerWorker.__init__(self, bgpManager, name, peerAddress)
        self.config = config
        self.localAddress = self.config['local_address']
        self.peerAddress = peerAddress

        self.connection = None

        self.rtc_active = False
        self._activeFamilies = []

    def _toIdle(self):
        self._activeFamilies = []

    def _initiateConnection(self):
        self.log.debug("Initiate ExaBGP connection to %s from %s",
                       self.peerAddress, self.localAddress)

        self.rtc_active = False

        neighbor = Neighbor()
        neighbor.router_id = RouterID(self.config['local_address'])
        neighbor.local_as = self.config['my_as']
        neighbor.peer_as = self.config['peer_as']
        neighbor.local_address = self.config['local_address']
        neighbor.peer_address = self.peerAddress
        neighbor.parse_routes = True

        # create dummy objects to fake exabgp into talking with us
        peer = FakePeer(neighbor)
        local = FakeLocal(self.localAddress)

        try:
            self.connection = Connection(peer, local, None, None)
        except Failure as e:
            raise InitiateConnectionException(repr(e))

        self.log.debug("Instantiate ExaBGP Protocol")
        self.protocol = MyBGPProtocol(peer, self.connection)
        self.protocol.connect()

        # this is highly similar to exabgp.network.peer._run

        o = self.protocol.new_open(False, False, self.config,
                                   ExaBGPPeerWorker.enabledFamilies)

        self.log.debug("Send open: [%s]", o)
        self.fsm.state = FSM.OpenSent

        count = 0
        self.log.debug("Wait for open...")
        while not self.shouldStop:
            # FIXME: we should time-out here, at some point
            message = self.protocol.read_open(o, None)

            count += 1
            if isinstance(message, NOP):
                # TODO(tmmorin): check compliance with BGP specs...
                if count > 20:
                    self.connection.close()
                    # FIXME: this should be moved to
                    # BGPPeerWorker in a more generic way
                    # (+ send Notify when needed)
                    raise OpenWaitTimeout("%ds" % int(20 * 0.5))
                sleep(0.5)
                continue

            self.log.debug("Read message: %s", message)

            if isinstance(message, Open):
                break
            else:
                self.log.error("Received unexpected message: %s", message)
                # FIXME

        if self.shouldStop:
            raise StoppedException()

        # An Open was received
        received_open = message

        self._setHoldTime(received_open.hold_time)

        # Hack to ease troubleshooting, have the real peer address appear in
        # the logs when fakerr is used
        if received_open.router_id.ip != self.peerAddress:
            self.log.info(
                "changing thread name from %s to BGP-x%s, based on"
                " the router-id advertized in Open (different from"
                " peerAddress == %s)", self.name, received_open.router_id.ip,
                self.peerAddress)
            self.name = "BGP-%s:%s" % (self.peerAddress,
                                       received_open.router_id.ip)

        try:
            mp_capabilities = received_open.capabilities[
                Capabilities.MULTIPROTOCOL_EXTENSIONS]
        except Exception:
            mp_capabilities = []

        # check that our peer advertized at least mpls_vpn and evpn
        # capabilities
        self._activeFamilies = []
        for (afi, safi) in (ExaBGPPeerWorker.enabledFamilies +
                            [(AFI(AFI.ipv4), SAFI(SAFI.rtc))]):
            if (afi, safi) not in mp_capabilities:
                self.log.warning("Peer does not advertise (%s,%s) capability",
                                 afi, safi)
            else:
                self.log.info(
                    "Family (%s,%s) successfully negotiated with peer %s", afi,
                    safi, self.peerAddress)
                self._activeFamilies.append((afi, safi))

        if len(self._activeFamilies) == 0:
            self.log.error("No family was negotiated for VPN routes")

        # proceed BGP session

        self.connection.io.setblocking(1)

        self.enqueue(SendKeepAlive)

        self.fsm.state = FSM.OpenConfirm

        self.rtc_active = False

        if self.config['enable_rtc']:
            if (AFI(AFI.ipv4), SAFI(SAFI.rtc)) in mp_capabilities:
                self.log.info("RTC successfully enabled with peer %s",
                              self.peerAddress)
                self.rtc_active = True
            else:
                self.log.warning(
                    "enable_rtc True but peer not configured for RTC")

    def _toEstablished(self):
        BGPPeerWorker._toEstablished(self)

        if self.rtc_active:
            # subscribe to RTC routes, to be able to propagate them from
            # internal workers to this peer
            self._subscribe(AFI(AFI.ipv4), SAFI(SAFI.rtc))
        else:
            # if we don't use RTC with our peer, then we need to see events for
            # all routes of all active families, to be able to send them to him
            for (afi, safi) in self._activeFamilies:
                self._subscribe(afi, safi)

    def _receiveLoopFun(self):

        select.select([self.connection.io], [], [], 5)

        if not self._queue.empty():
            if self._stopLoops.isSet():
                self.log.info("stopLoops is set -> Close connection and"
                              " Finish receive Loop")
                self.connection.close()
                return 0

        try:
            message = self.protocol.read_message()
        except Notification as e:
            self.log.error("Peer notified us about an error: %s", e)
            return 2
        except Failure as e:
            self.log.warning("Protocol failure: %s", e)
            return 2
        except socket.error as e:
            self.log.warning("Socket error: %s", e)
            return 2
        except Exception as e:
            self.log.error("Error while reading BGP message: %s", e)
            raise

        if message.TYPE in (NOP.TYPE):
            # we arrived here because select call timed-out
            return 1
        elif message.TYPE == Update.TYPE:
            if (self.fsm.state != FSM.Established):
                raise Exception("Update received but not in Established state")
            pass  # see below
        elif message.TYPE == KeepAlive.TYPE:
            if (self.fsm.state == FSM.OpenConfirm):
                self._toEstablished()
            self.enqueue(KeepAliveReceived)
            self.log.debug("Received message: %s", message)
        else:
            self.log.warning("Received unexpected message: %s", message)

        if isinstance(message, Update):
            self.log.info("Received message: UPDATE...")
            if message.routes:
                for route in message.routes:
                    self._processReceivedRoute(route)

        return 1

    def _processReceivedRoute(self, route):
        self.log.info("Received route: %s", route)

        rts = []
        if AttributeID.EXTENDED_COMMUNITY in route.attributes:
            rts = [
                ecom for ecom in route.attributes[
                    AttributeID.EXTENDED_COMMUNITY].communities
                if isinstance(ecom, RouteTarget)
            ]

            if not rts:
                raise Exception("Unable to find any Route Targets"
                                "in the received route")

        routeEntry = self._newRouteEntry(route.nlri.afi, route.nlri.safi, rts,
                                         route.nlri, route.attributes)

        if route.action == "announce":
            self._pushEvent(RouteEvent(RouteEvent.ADVERTISE, routeEntry))
        else:
            self._pushEvent(RouteEvent(RouteEvent.WITHDRAW, routeEntry))

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

            if route.nlri.route_target 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._activeFamilies:
                if (afi, safi) != (AFI(AFI.ipv4), SAFI(SAFI.rtc)):
                    if route.action == "announce":
                        self._subscribe(afi, safi, route.nlri.route_target)
                    else:  # withdraw
                        self._unsubscribe(afi, safi, route.nlri.route_target)

    def _send(self, data):
        # (error if state not the right one for sending updates)
        self.log.debug("Sending %d bytes on socket to peer %s", len(data),
                       self.peerAddress)
        try:
            self.connection.write(data)
        except Exception as e:
            self.log.error("Was not able to send data: %s", e)

    def _keepAliveMessageData(self):
        return KeepAlive().message()

    def _updateForRouteEvent(self, event):
        r = Route(event.routeEntry.nlri)
        if event.type == event.ADVERTISE:
            self.log.info("Generate UPDATE message: %s", r)
            r.attributes = event.routeEntry.attributes
            try:
                return Update([r]).update(False, self.config['my_as'],
                                          self.config['my_as'])
            except Exception as e:
                self.log.error(
                    "Exception while generating message for "
                    "route %s: %s", r, e)
                self.log.warning("%s", traceback.format_exc())
                return ''

        elif event.type == event.WITHDRAW:
            self.log.info("Generate WITHDRAW message: %s", r)
            return Update([r]).withdraw(False, self.config['my_as'],
                                        self.config['my_as'])

    def stop(self):
        if self.connection is not None:
            self.connection.close()
        BGPPeerWorker.stop(self)

    # Looking Glass ###############

    def getLookingGlassLocalInfo(self, pathPrefix):
        return {
            "peeringAddresses": {
                "peerAddress": self.peerAddress,
                "localAddress": self.localAddress
            },
            "as_info": {
                "local": self.config['my_as'],
                "peer": self.config['peer_as']
            },
            "rtc": {
                "active": self.rtc_active,
                "enabled": self.config['enable_rtc']
            },
            "active_families": [repr(f) for f in self._activeFamilies],
        }
示例#16
0
class EVPNNLRI(object):
    '''
    +-----------------------------------+
    |    Route Type (1 octet)           |
    +-----------------------------------+
    |     Length (1 octet)              |
    +-----------------------------------+
    | Route Type specific (variable)    |
    +-----------------------------------+
    '''
    afi = AFI(AFI.l2vpn)
    safi = SAFI(SAFI.evpn)

    def __init__(self, subtype, packedValue=None):
        self.subtype = subtype
        self.packedValue = packedValue

    def __str__(self):
        if self.subtype in EVPN_types_to_class:
            type_string = EVPN_types_to_class[self.subtype].nickname
            return "EVPN:%s" % type_string
        else:
            type_string = "%d" % self.subtype
            return "EVPN:%s:%s" % (type_string, "..."
                                   )  #FIXME: add binascii dump of packedValue

    def __repr__(self):
        return self.__str__()

    def pack(self):
        if self.packedValue is None:
            self._computePackedValue()
        return pack('!BB', self.subtype, len(
            self.packedValue)) + self.packedValue

    def _computePackedValue(self):
        raise Exception("Abstract class, cannot compute packedValue")

    def __len__(self):
        if self.packedValue is None:
            self._computePackedValue()
        return len(self.packedValue) + 2

    def __cmp__(self, other):
        #
        # For subtype 2, we will have to ignore a part of the route, so this method will be overridden
        #
        if self.packedValue is None:
            self._computePackedValue()
        if other.packedValue is None:
            other._computePackedValue()

        if (isinstance(other, EVPNNLRI) and self.subtype == other.subtype
                and self.packedValue == other.packedValue):
            return 0
        else:
            return -1

    def __hash__(self):  #FIXME: improve for better performance?
        #
        # same as above: need to ignore label and ESI for subtype 2
        #
        if self.packedValue is None:
            self._computePackedValue()
        return hash("%s:%s:%s:%s" %
                    (self.afi, self.safi, self.subtype, self.packedValue))

    @staticmethod
    def unpack(data):

        #subtype
        typeid = ord(data[0])
        data = data[1:]

        #length
        length = ord(data[0])
        data = data[1:length + 1]

        if typeid in EVPN_types_to_class:
            return EVPN_types_to_class[typeid].unpack(data)
        else:
            return EVPNNLRI
示例#17
0
	def __init__ (self,afi,safi):
		Address.__init__(self,AFI(afi),SAFI(safi))
示例#18
0
class EVI(VPNInstance, LookingGlass):
    '''
    Implementation an E-VPN instance (EVI) based on RFC7432 and
    draft-ietf-bess-evpn-overlay.
    '''

    type = "evpn"
    afi = AFI(AFI.l2vpn)
    safi = SAFI(SAFI.evpn)

    @logDecorator.log
    def __init__(self, *args, **kwargs):

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

        self.gwPort = None

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

        etag = None
        label = LabelStackEntry(self.instanceLabel)

        route = Route(
            EVPNMulticast(
                RouteDistinguisher(RouteDistinguisher.TYPE_IP_LOC, None,
                                   self.bgpManager.getLocalAddress(),
                                   self.instanceId), etag,
                self.bgpManager.getLocalAddress()))

        route.attributes.add(self._genExtendedCommunities())

        # add PMSI Tunnel Attribute route
        pmsi_tunnel_attribute = PMSITunnelIngressReplication(
            self.dataplaneDriver.getLocalAddress(), label)
        route.attributes.add(pmsi_tunnel_attribute)

        nh = Inet(
            1,
            socket.inet_pton(socket.AF_INET,
                             self.dataplaneDriver.getLocalAddress()))
        route.attributes.add(NextHop(nh))

        self.multicastRouteEntry = self._newRouteEntry(self.afi, self.safi,
                                                       self.exportRTs,
                                                       route.nlri,
                                                       route.attributes)

        self._pushEvent(
            RouteEvent(RouteEvent.ADVERTISE, self.multicastRouteEntry))

    def generateVifBGPRoute(self, macAddress, ipPrefix, prefixLen, label):
        # Generate BGP route and advertise it...

        assert (prefixLen == 32)

        # label parameter ignored, we want the instance label
        lse = LabelStackEntry(self.instanceLabel, True)
        etag = None

        route = Route(
            EVPNMACAdvertisement(
                RouteDistinguisher(RouteDistinguisher.TYPE_IP_LOC, None,
                                   self.dataplaneDriver.getLocalAddress(),
                                   self.instanceId),
                EthernetSegmentIdentifier(), etag, MAC(macAddress), lse,
                ipPrefix))

        return self._newRouteEntry(self.afi, self.safi, self.exportRTs,
                                   route.nlri, route.attributes)

    @logDecorator.log
    def setGatewayPort(self, linuxif, ipvpn):
        self.dataplane.setGatewayPort(linuxif)
        self.gwPort = (linuxif, ipvpn)

    @logDecorator.log
    def gatewayPortDown(self, linuxif):
        self.dataplane.gatewayPortDown(linuxif)
        self.gwPort = None

    def hasGatewayPort(self):
        return (self.gwPort is not None)

    # TrackerWorker callbacks for BGP route updates ##########################

    def _route2trackedEntry(self, route):
        if isinstance(route.nlri, EVPNMACAdvertisement):
            return (EVPNMACAdvertisement, route.nlri.mac)
        elif isinstance(route.nlri, EVPNMulticast):
            return (EVPNMulticast, (route.nlri.ip, route.nlri.rd))
        elif isinstance(route.nlri, EVPNNLRI):
            self.log.warning("Received EVPN route of unsupported subtype: %s",
                             route.nlri.subtype)
        else:
            raise Exception("EVI %d should not receive routes of type %s" %
                            (self.instanceId, type(route.nlri)))

    @utils.synchronized
    @logDecorator.log
    def _newBestRoute(self, entry, newRoute):
        (entryClass, info) = entry

        encaps = self._checkEncaps(newRoute)
        if not encaps:
            return

        if entryClass == EVPNMACAdvertisement:
            prefix = info

            nh = newRoute.attributes.get(NextHop.ID)
            remotePE = nh.next_hop
            label = newRoute.nlri.label.labelValue

            self.dataplane.setupDataplaneForRemoteEndpoint(
                prefix, remotePE, label, newRoute.nlri, encaps)

        elif entryClass == EVPNMulticast:
            remote_endpoint = info

            # check that the route is actually carrying an PMSITunnel of type
            # ingress replication
            pmsi_tunnel = newRoute.attributes.get(PMSITunnel.ID)
            if not isinstance(pmsi_tunnel, PMSITunnelIngressReplication):
                self.log.warning("Received PMSITunnel of unsupported type: %s",
                                 type(pmsi_tunnel))
            else:
                remote_endpoint = pmsi_tunnel.ip
                label = pmsi_tunnel.label.labelValue

                self.log.info(
                    "Setting up dataplane for new ingress "
                    "replication destination %s", remote_endpoint)
                self.dataplane.addDataplaneForBroadcastEndpoint(
                    remote_endpoint, label, newRoute.nlri, encaps)
        else:
            self.log.warning("unsupported entryClass: %s", entryClass.__name__)

    @utils.synchronized
    @logDecorator.log
    def _bestRouteRemoved(self, entry, oldRoute, last):
        (entryClass, info) = entry

        if entryClass == EVPNMACAdvertisement:

            if self._skipRouteRemoval(last):
                self.log.debug("Skipping removal of non-last route because "
                               "dataplane does not want it")
                return

            prefix = info

            nh = oldRoute.attributes.get(NextHop.ID)
            remotePE = nh.next_hop
            label = oldRoute.nlri.label.labelValue

            self.dataplane.removeDataplaneForRemoteEndpoint(
                prefix, remotePE, label, oldRoute.nlri)

        elif entryClass == EVPNMulticast:
            remote_endpoint = info

            # check that the route is actually carrying an PMSITunnel of type
            # ingress replication
            pmsi_tunnel = oldRoute.attributes.get(PMSITunnel.ID)
            if not isinstance(pmsi_tunnel, PMSITunnelIngressReplication):
                self.log.warning("PMSITunnel of suppressed route is of"
                                 " unsupported type")
            else:
                remote_endpoint = pmsi_tunnel.ip
                label = pmsi_tunnel.label.labelValue
                self.log.info(
                    "Cleaning up dataplane for ingress replication "
                    "destination %s", remote_endpoint)
                self.dataplane.removeDataplaneForBroadcastEndpoint(
                    remote_endpoint, label, oldRoute.nlri)
        else:
            self.log.warning("unsupported entryClass: %s", entryClass.__name__)

    # Looking Glass ####

    def getLookingGlassLocalInfo(self, pathPrefix):
        if not self.gwPort:
            return {"gateway_port": None}
        else:
            (linuxif, ipvpn) = self.gwPort
            return {
                "gateway_port": {
                    "interface": repr(linuxif),
                    "ipvpn": {
                        "href":
                        LookingGlassReferences.getAbsolutePath(
                            "VPN_INSTANCES", pathPrefix,
                            [ipvpn.externalInstanceId]),
                        "id":
                        ipvpn.name,
                        "external_instance_id":
                        ipvpn.externalInstanceId
                    },
                }
            }
示例#19
0
 def __init__(self, afi, raw):
     self.afi = AFI(afi)
     self.raw = raw
     self.__update()
示例#20
0
    def CapabilitiesFactory(self, data):
        capabilities = Capabilities()
        option_len = ord(data[0])
        if option_len:
            data = data[1:]
            while data:
                key, value, data = self._key_values('parameter', data)
                # Paramaters must only be sent once.
                if key == Parameter.AUTHENTIFICATION_INFORMATION:
                    raise Notify(2, 5)

                if key == Parameter.CAPABILITIES:
                    while value:
                        k, capv, value = self._key_values('capability', value)
                        # Multiple Capabilities can be present in a single attribute
                        #if r:
                        #	raise Notify(2,0,"Bad length for OPEN %s (size mismatch) %s" % ('capability',hexa(value)))

                        if k == Capabilities.MULTIPROTOCOL_EXTENSIONS:
                            if k not in capabilities:
                                capabilities[k] = MultiProtocol()
                            afi = AFI(unpack('!H', capv[:2])[0])
                            safi = SAFI(ord(capv[3]))
                            capabilities[k].append((afi, safi))
                            continue

                        if k == Capabilities.GRACEFUL_RESTART:
                            restart = unpack('!H', capv[:2])[0]
                            restart_flag = restart >> 12
                            restart_time = restart & Graceful.TIME_MASK
                            value_gr = capv[2:]
                            families = []
                            while value_gr:
                                afi = AFI(unpack('!H', value_gr[:2])[0])
                                safi = SAFI(ord(value_gr[2]))
                                flag_family = ord(value_gr[0])
                                families.append((afi, safi, flag_family))
                                value_gr = value_gr[4:]
                            capabilities[k] = Graceful(restart_flag,
                                                       restart_time, families)
                            continue

                        if k == Capabilities.FOUR_BYTES_ASN:
                            capabilities[k] = ASN(unpack('!L', capv[:4])[0])
                            continue

                        if k == Capabilities.ROUTE_REFRESH:
                            capabilities[k] = RouteRefresh()
                            continue

                        if k == Capabilities.CISCO_ROUTE_REFRESH:
                            capabilities[k] = CiscoRouteRefresh()
                            continue

                        if k == Capabilities.MULTISESSION_BGP:
                            capabilities[k] = MultiSession()
                            continue
                        if k == Capabilities.MULTISESSION_BGP_RFC:
                            capabilities[k] = MultiSession()
                            continue

                        if k not in capabilities:
                            capabilities[k] = Unknown(k,
                                                      [ord(_) for _ in capv])
                else:
                    raise Notify(2, 0, 'Unknown OPEN parameter %s' % hex(key))
        return capabilities
示例#21
0
 def __init__(self, afi, safi, origin_as, route_target):
     self.afi = AFI(afi)
     self.safi = SAFI(safi)
     self.origin_as = origin_as
     self.route_target = route_target
示例#22
0
class VRF(VPNInstance, LookingGlass):
    # component managing a VRF:
    # - calling a driver to instantiate the dataplane
    # - registering to receive routes for the needed route targets
    # - calling the driver to setup/update/remove routes in the dataplane
    # - cleanup: calling the driver, unregistering for BGP routes

    type = "ipvpn"
    afi = AFI(AFI.ipv4)
    safi = SAFI(SAFI.mpls_vpn)

    @logDecorator.log
    def __init__(self, *args, **kwargs):
        VPNInstance.__init__(self, *args, **kwargs)
        self.readvertised = set()

    def _routeFrom(self, prefix, label, rd):
        return Route(
            VPNLabelledPrefix(self.afi, self.safi, prefix, rd,
                              [LabelStackEntry(label, True)]))

    def generateVifBGPRoute(self, macAdress, ipPrefix, prefixLen, label):
        # Generate BGP route and advertise it...
        route = self._routeFrom(
            Prefix(self.afi, ipPrefix, prefixLen), label,
            RouteDistinguisher(RouteDistinguisher.TYPE_IP_LOC, None,
                               self.bgpManager.getLocalAddress(),
                               self.instanceId))
        self.log.debug("route attributes: %s", route.attributes)

        return self._newRouteEntry(self.afi, self.safi, self.exportRTs,
                                   route.nlri, route.attributes)

    def _getLocalLabels(self):
        for portData in self.macAddress2LocalPortData.itervalues():
            yield portData['label']

    def _getRDFromLabel(self, label):
        # FIXME: this is a crude hack that will break beyond 10000 VRFs
        return RouteDistinguisher(RouteDistinguisher.TYPE_IP_LOC, None,
                                  self.bgpManager.getLocalAddress(),
                                  10000 + label)

    def _routeForReAdvertisement(self, prefix, label):
        route = self._routeFrom(prefix, label, self._getRDFromLabel(label))

        nh = Inet(
            1,
            socket.inet_pton(socket.AF_INET,
                             self.dataplane.driver.getLocalAddress()))

        route.attributes.add(NextHop(nh))

        route.attributes.add(ECommunities(self.readvertiseToRTs))

        routeEntry = self._newRouteEntry(self.afi, self.safi,
                                         self.readvertiseToRTs, route.nlri,
                                         route.attributes)
        return routeEntry

    @logDecorator.log
    def _readvertise(self, nlri):
        self.log.debug("Start re-advertising %s from VRF", nlri.prefix)
        for label in self._getLocalLabels():
            self.log.debug("Start re-advertising %s from VRF, with label %s",
                           nlri.prefix, label)
            # need a distinct RD for each route...
            routeEntry = self._routeForReAdvertisement(nlri.prefix, label)
            self._pushEvent(RouteEvent(RouteEvent.ADVERTISE, routeEntry))

        self.readvertised.add(nlri.prefix)

    @logDecorator.log
    def _readvertiseStop(self, nlri):
        self.log.debug("Stop re-advertising %s from VRF", nlri.prefix)
        for label in self._getLocalLabels():
            self.log.debug("Stop re-advertising %s from VRF, with label %s",
                           nlri.prefix, label)
            routeEntry = self._routeForReAdvertisement(nlri.prefix, label)
            self._pushEvent(RouteEvent(RouteEvent.WITHDRAW, routeEntry))

        self.readvertised.remove(nlri.prefix)

    def vifPlugged(self, macAddress, ipAddressPrefix, localPort,
                   advertiseSubnet):
        VPNInstance.vifPlugged(self, macAddress, ipAddressPrefix, localPort,
                               advertiseSubnet)

        label = self.macAddress2LocalPortData[macAddress]['label']
        for prefix in self.readvertised:
            self.log.debug("Re-advertising %s with this port as next hop",
                           prefix)
            routeEntry = self._routeForReAdvertisement(prefix, label)
            self._pushEvent(RouteEvent(RouteEvent.ADVERTISE, routeEntry))

    def vifUnplugged(self, macAddress, ipAddressPrefix, advertiseSubnet):
        label = self.macAddress2LocalPortData[macAddress]['label']
        for prefix in self.readvertised:
            self.log.debug("Stop re-advertising %s with this port as next hop",
                           prefix)
            routeEntry = self._routeForReAdvertisement(prefix, label)
            self._pushEvent(RouteEvent(RouteEvent.WITHDRAW, routeEntry))

        VPNInstance.vifUnplugged(self, macAddress, ipAddressPrefix,
                                 advertiseSubnet)

    # Callbacks for BGP route updates (TrackerWorker) ########################

    def _route2trackedEntry(self, route):
        if isinstance(route.nlri, VPNLabelledPrefix):
            return route.nlri.prefix
        else:
            self.log.error("We should not receive routes of type %s",
                           type(route.nlri))
            return None

    def _toReadvertise(self, route):
        return (len(
            set(route.routeTargets).intersection(set(self.readvertiseFromRTs)))
                > 0)

    def _imported(self, route):
        return (len(set(route.routeTargets).intersection(set(self.importRTs)))
                > 0)

    @utils.synchronized
    @logDecorator.log
    def _newBestRoute(self, entry, newRoute):

        prefix = entry

        if self.readvertise:
            # check if this is a route we need to re-advertise
            self.log.debug("route RTs: %s", newRoute.routeTargets)
            self.log.debug("readv from RTs: %s", self.readvertiseFromRTs)
            if self._toReadvertise(newRoute):
                self.log.debug("Need to re-advertise %s", prefix)
                self._readvertise(newRoute.nlri)
                if not self._imported(newRoute):
                    self.log.debug("No need to setup dataplane for:%s", prefix)
                    return

        encaps = self._checkEncaps(newRoute)
        if not encaps:
            return

        self.dataplane.setupDataplaneForRemoteEndpoint(
            prefix,
            newRoute.attributes.get(NextHop.ID).next_hop,
            newRoute.nlri.labelStack[0].labelValue, newRoute.nlri, encaps)

    @utils.synchronized
    @logDecorator.log
    def _bestRouteRemoved(self, entry, oldRoute, last):

        prefix = entry

        if self.readvertise and last:
            # check if this is a route we were re-advertising
            if self._toReadvertise(oldRoute):
                self.log.debug("Need to stop re-advertising %s", prefix)
                self._readvertiseStop(oldRoute.nlri)
                if not self._imported(oldRoute):
                    self.log.debug("No need to setup dataplane for:%s", prefix)
                    return

        if self._skipRouteRemoval(last):
            self.log.debug("Skipping removal of non-last route because "
                           "dataplane does not want it")
            return

        self.dataplane.removeDataplaneForRemoteEndpoint(
            prefix,
            oldRoute.attributes.get(NextHop.ID).next_hop,
            oldRoute.nlri.labelStack[0].labelValue, oldRoute.nlri)

    def getLGMap(self):
        return {
            "readvertised":
            (LGMap.VALUE, [repr(prefix) for prefix in self.readvertised])
        }
示例#23
0
    def _initiateConnection(self):
        self.log.debug("Initiate ExaBGP connection to %s from %s",
                       self.peerAddress, self.localAddress)

        self.rtc_active = False

        neighbor = Neighbor()
        neighbor.router_id = RouterID(self.config['local_address'])
        neighbor.local_as = self.config['my_as']
        neighbor.peer_as = self.config['peer_as']
        neighbor.local_address = self.config['local_address']
        neighbor.peer_address = self.peerAddress
        neighbor.parse_routes = True

        # create dummy objects to fake exabgp into talking with us
        peer = FakePeer(neighbor)
        local = FakeLocal(self.localAddress)

        try:
            self.connection = Connection(peer, local, None, None)
        except Failure as e:
            raise InitiateConnectionException(repr(e))

        self.log.debug("Instantiate ExaBGP Protocol")
        self.protocol = MyBGPProtocol(peer, self.connection)
        self.protocol.connect()

        # this is highly similar to exabgp.network.peer._run

        o = self.protocol.new_open(False, False, self.config,
                                   ExaBGPPeerWorker.enabledFamilies)

        self.log.debug("Send open: [%s]", o)
        self.fsm.state = FSM.OpenSent

        count = 0
        self.log.debug("Wait for open...")
        while not self.shouldStop:
            # FIXME: we should time-out here, at some point
            message = self.protocol.read_open(o, None)

            count += 1
            if isinstance(message, NOP):
                # TODO(tmmorin): check compliance with BGP specs...
                if count > 20:
                    self.connection.close()
                    # FIXME: this should be moved to
                    # BGPPeerWorker in a more generic way
                    # (+ send Notify when needed)
                    raise OpenWaitTimeout("%ds" % int(20 * 0.5))
                sleep(0.5)
                continue

            self.log.debug("Read message: %s", message)

            if isinstance(message, Open):
                break
            else:
                self.log.error("Received unexpected message: %s", message)
                # FIXME

        if self.shouldStop:
            raise StoppedException()

        # An Open was received
        received_open = message

        self._setHoldTime(received_open.hold_time)

        # Hack to ease troubleshooting, have the real peer address appear in
        # the logs when fakerr is used
        if received_open.router_id.ip != self.peerAddress:
            self.log.info(
                "changing thread name from %s to BGP-x%s, based on"
                " the router-id advertized in Open (different from"
                " peerAddress == %s)", self.name, received_open.router_id.ip,
                self.peerAddress)
            self.name = "BGP-%s:%s" % (self.peerAddress,
                                       received_open.router_id.ip)

        try:
            mp_capabilities = received_open.capabilities[
                Capabilities.MULTIPROTOCOL_EXTENSIONS]
        except Exception:
            mp_capabilities = []

        # check that our peer advertized at least mpls_vpn and evpn
        # capabilities
        self._activeFamilies = []
        for (afi, safi) in (ExaBGPPeerWorker.enabledFamilies +
                            [(AFI(AFI.ipv4), SAFI(SAFI.rtc))]):
            if (afi, safi) not in mp_capabilities:
                self.log.warning("Peer does not advertise (%s,%s) capability",
                                 afi, safi)
            else:
                self.log.info(
                    "Family (%s,%s) successfully negotiated with peer %s", afi,
                    safi, self.peerAddress)
                self._activeFamilies.append((afi, safi))

        if len(self._activeFamilies) == 0:
            self.log.error("No family was negotiated for VPN routes")

        # proceed BGP session

        self.connection.io.setblocking(1)

        self.enqueue(SendKeepAlive)

        self.fsm.state = FSM.OpenConfirm

        self.rtc_active = False

        if self.config['enable_rtc']:
            if (AFI(AFI.ipv4), SAFI(SAFI.rtc)) in mp_capabilities:
                self.log.info("RTC successfully enabled with peer %s",
                              self.peerAddress)
                self.rtc_active = True
            else:
                self.log.warning(
                    "enable_rtc True but peer not configured for RTC")