1 # Copyright (C) 2013,2014 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2013,2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
23 # - RFC 4364 BGP/MPLS IP Virtual Private Networks (VPNs)
40 from ryu.lib.stringify import StringifyMixin
41 from ryu.lib.packet import afi as addr_family
42 from ryu.lib.packet import safi as subaddr_family
43 from ryu.lib.packet import packet_base
44 from ryu.lib.packet import stream_parser
45 from ryu.lib.packet import vxlan
46 from ryu.lib.packet import mpls
47 from ryu.lib import addrconv
48 from ryu.lib import type_desc
49 from ryu.lib.type_desc import TypeDisp
50 from ryu.lib import ip
51 from ryu.lib.pack_utils import msg_pack_into
52 from ryu.utils import binary_str
53 from ryu.utils import import_module
55 reduce = six.moves.reduce
61 BGP_MSG_NOTIFICATION = 3
63 BGP_MSG_ROUTE_REFRESH = 5 # RFC 2918
66 _MARKER = 16 * b'\xff'
68 BGP_OPT_CAPABILITY = 2 # RFC 5492
70 BGP_CAP_MULTIPROTOCOL = 1 # RFC 4760
71 BGP_CAP_ROUTE_REFRESH = 2 # RFC 2918
72 BGP_CAP_CARRYING_LABEL_INFO = 4 # RFC 3107
73 BGP_CAP_GRACEFUL_RESTART = 64 # RFC 4724
74 BGP_CAP_FOUR_OCTET_AS_NUMBER = 65 # RFC 4893
75 BGP_CAP_ENHANCED_ROUTE_REFRESH = 70 # https://tools.ietf.org/html/\
76 # draft-ietf-idr-bgp-enhanced-route-refresh-05
77 BGP_CAP_ROUTE_REFRESH_CISCO = 128 # in cisco routers, there are two\
78 # route refresh code: one using the capability code of 128 (old),
79 # another using the capability code of 2 (new).
81 BGP_ATTR_FLAG_OPTIONAL = 1 << 7
82 BGP_ATTR_FLAG_TRANSITIVE = 1 << 6
83 BGP_ATTR_FLAG_PARTIAL = 1 << 5
84 BGP_ATTR_FLAG_EXTENDED_LENGTH = 1 << 4
86 BGP_ATTR_TYPE_ORIGIN = 1 # 0,1,2 (1 byte)
87 BGP_ATTR_TYPE_AS_PATH = 2 # a list of AS_SET/AS_SEQUENCE eg. {1 2 3} 4 5
88 BGP_ATTR_TYPE_NEXT_HOP = 3 # an IPv4 address
89 BGP_ATTR_TYPE_MULTI_EXIT_DISC = 4 # uint32 metric
90 BGP_ATTR_TYPE_LOCAL_PREF = 5 # uint32
91 BGP_ATTR_TYPE_ATOMIC_AGGREGATE = 6 # 0 bytes
92 BGP_ATTR_TYPE_AGGREGATOR = 7 # AS number and IPv4 address
93 BGP_ATTR_TYPE_COMMUNITIES = 8 # RFC 1997
94 BGP_ATTR_TYPE_ORIGINATOR_ID = 9 # RFC 4456
95 BGP_ATTR_TYPE_CLUSTER_LIST = 10 # RFC 4456
96 BGP_ATTR_TYPE_MP_REACH_NLRI = 14 # RFC 4760
97 BGP_ATTR_TYPE_MP_UNREACH_NLRI = 15 # RFC 4760
98 BGP_ATTR_TYPE_EXTENDED_COMMUNITIES = 16 # RFC 4360
99 BGP_ATTR_TYPE_AS4_PATH = 17 # RFC 4893
100 BGP_ATTR_TYPE_AS4_AGGREGATOR = 18 # RFC 4893
101 BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE = 22 # RFC 6514
103 BGP_ATTR_ORIGIN_IGP = 0x00
104 BGP_ATTR_ORIGIN_EGP = 0x01
105 BGP_ATTR_ORIGIN_INCOMPLETE = 0x02
107 AS_TRANS = 23456 # RFC 4893
109 # Well known commmunities (RFC 1997)
110 BGP_COMMUNITY_NO_EXPORT = 0xffffff01
111 BGP_COMMUNITY_NO_ADVERTISE = 0xffffff02
112 BGP_COMMUNITY_NO_EXPORT_SUBCONFED = 0xffffff03
115 # The low-order octet of Type field (subtype)
116 BGP_EXTENDED_COMMUNITY_ROUTE_TARGET = 0x02
117 BGP_EXTENDED_COMMUNITY_ROUTE_ORIGIN = 0x03
119 # NOTIFICATION Error Code and SubCode
120 # Note: 0 is a valid SubCode. (Unspecific)
122 # NOTIFICATION Error Code RFC 4271 4.5.
123 BGP_ERROR_MESSAGE_HEADER_ERROR = 1
124 BGP_ERROR_OPEN_MESSAGE_ERROR = 2
125 BGP_ERROR_UPDATE_MESSAGE_ERROR = 3
126 BGP_ERROR_HOLD_TIMER_EXPIRED = 4
127 BGP_ERROR_FSM_ERROR = 5
130 # NOTIFICATION Error Subcode for BGP_ERROR_MESSAGE_HEADER_ERROR
131 BGP_ERROR_SUB_CONNECTION_NOT_SYNCHRONIZED = 1
132 BGP_ERROR_SUB_BAD_MESSAGE_LENGTH = 2 # Data: the erroneous Length field
133 BGP_ERROR_SUB_BAD_MESSAGE_TYPE = 3 # Data: the erroneous Type field
135 # NOTIFICATION Error Subcode for BGP_ERROR_OPEN_MESSAGE_ERROR
136 BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER = 1 # Data: 2 octet version number
137 BGP_ERROR_SUB_BAD_PEER_AS = 2
138 BGP_ERROR_SUB_BAD_BGP_IDENTIFIER = 3
139 BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER = 4
140 BGP_ERROR_SUB_AUTHENTICATION_FAILURE = 5 # deprecated RFC 1771
141 BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME = 6
143 # NOTIFICATION Error Subcode for BGP_ERROR_UPDATE_MESSAGE_ERROR
144 BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST = 1
145 BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE = 2 # Data: type of the attr
146 BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE = 3 # Data: ditto
147 BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR = 4 # Data: the attr (type, len, value)
148 BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR = 5 # Data: ditto
149 BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE = 6 # Data: ditto
150 BGP_ERROR_SUB_ROUTING_LOOP = 7 # deprecated RFC 1771 AS Routing Loop
151 BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE = 8 # Data: ditto
152 BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR = 9 # Data: ditto
153 BGP_ERROR_SUB_INVALID_NETWORK_FIELD = 10
154 BGP_ERROR_SUB_MALFORMED_AS_PATH = 11
156 # NOTIFICATION Error Subcode for BGP_ERROR_HOLD_TIMER_EXPIRED
157 BGP_ERROR_SUB_HOLD_TIMER_EXPIRED = 1
159 # NOTIFICATION Error Subcode for BGP_ERROR_FSM_ERROR
160 BGP_ERROR_SUB_FSM_ERROR = 1
162 # NOTIFICATION Error Subcode for BGP_ERROR_CEASE (RFC 4486)
163 BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED = 1 # Data: optional
164 BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN = 2
165 BGP_ERROR_SUB_PEER_DECONFIGURED = 3
166 BGP_ERROR_SUB_ADMINISTRATIVE_RESET = 4
167 BGP_ERROR_SUB_CONNECTION_RESET = 5
168 BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE = 6
169 BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION = 7
170 BGP_ERROR_SUB_OUT_OF_RESOURCES = 8
173 class _Value(object):
174 _VALUE_PACK_STR = None
175 _VALUE_FIELDS = ['value']
178 def do_init(cls_type, self, kwargs, **extra_kwargs):
180 for f in cls_type._VALUE_FIELDS:
184 kwargs.update(extra_kwargs)
185 super(cls_type, self).__init__(**kwargs)
186 self.__dict__.update(ourfields)
189 def parse_value(cls, buf):
190 values = struct.unpack_from(cls._VALUE_PACK_STR, six.binary_type(buf))
191 return dict(zip(cls._VALUE_FIELDS, values))
193 def serialize_value(self):
195 for f in self._VALUE_FIELDS:
196 args.append(getattr(self, f))
197 return struct.pack(self._VALUE_PACK_STR, *args)
200 class BgpExc(Exception):
201 """Base bgp exception."""
204 """BGP error code."""
207 """BGP error sub-code."""
210 """Flag if set indicates Notification message should be sent to peer."""
212 def __init__(self, data=''):
213 super(BgpExc, self).__init__()
217 return '<%s %r>' % (self.__class__.__name__, self.data)
220 class BadNotification(BgpExc):
223 # ============================================================================
224 # Message Header Errors
225 # ============================================================================
228 class NotSync(BgpExc):
229 CODE = BGP_ERROR_MESSAGE_HEADER_ERROR
230 SUB_CODE = BGP_ERROR_SUB_CONNECTION_NOT_SYNCHRONIZED
233 class BadLen(BgpExc):
234 CODE = BGP_ERROR_MESSAGE_HEADER_ERROR
235 SUB_CODE = BGP_ERROR_SUB_BAD_MESSAGE_LENGTH
237 def __init__(self, msg_type_code, message_length):
238 super(BadLen, self).__init__()
239 self.msg_type_code = msg_type_code
240 self.length = message_length
241 self.data = struct.pack('!H', self.length)
244 return '<BadLen %d msgtype=%d>' % (self.length, self.msg_type_code)
247 class BadMsg(BgpExc):
248 """Error to indicate un-recognized message type.
250 RFC says: If the Type field of the message header is not recognized, then
251 the Error Subcode MUST be set to Bad Message Type. The Data field MUST
252 contain the erroneous Type field.
254 CODE = BGP_ERROR_MESSAGE_HEADER_ERROR
255 SUB_CODE = BGP_ERROR_SUB_BAD_MESSAGE_TYPE
257 def __init__(self, msg_type):
258 super(BadMsg, self).__init__()
259 self.msg_type = msg_type
260 self.data = struct.pack('B', msg_type)
263 return '<BadMsg %d>' % (self.msg_type,)
265 # ============================================================================
266 # OPEN Message Errors
267 # ============================================================================
270 class MalformedOptionalParam(BgpExc):
271 """If recognized optional parameters are malformed.
273 RFC says: If one of the Optional Parameters in the OPEN message is
274 recognized, but is malformed, then the Error Subcode MUST be set to 0
277 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
281 class UnsupportedVersion(BgpExc):
282 """Error to indicate unsupport bgp version number.
284 RFC says: If the version number in the Version field of the received OPEN
285 message is not supported, then the Error Subcode MUST be set to Unsupported
286 Version Number. The Data field is a 2-octet unsigned integer, which
287 indicates the largest, locally-supported version number less than the
288 version the remote BGP peer bid (as indicated in the received OPEN
289 message), or if the smallest, locally-supported version number is greater
290 than the version the remote BGP peer bid, then the smallest, locally-
291 supported version number.
293 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
294 SUB_CODE = BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER
296 def __init__(self, locally_support_version):
297 super(UnsupportedVersion, self).__init__()
298 self.data = struct.pack('H', locally_support_version)
301 class BadPeerAs(BgpExc):
302 """Error to indicate open message has incorrect AS number.
304 RFC says: If the Autonomous System field of the OPEN message is
305 unacceptable, then the Error Subcode MUST be set to Bad Peer AS. The
306 determination of acceptable Autonomous System numbers is configure peer AS.
308 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
309 SUB_CODE = BGP_ERROR_SUB_BAD_PEER_AS
312 class BadBgpId(BgpExc):
313 """Error to indicate incorrect BGP Identifier.
315 RFC says: If the BGP Identifier field of the OPEN message is syntactically
316 incorrect, then the Error Subcode MUST be set to Bad BGP Identifier.
317 Syntactic correctness means that the BGP Identifier field represents a
318 valid unicast IP host address.
320 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
321 SUB_CODE = BGP_ERROR_SUB_BAD_BGP_IDENTIFIER
324 class UnsupportedOptParam(BgpExc):
325 """Error to indicate unsupported optional parameters.
327 RFC says: If one of the Optional Parameters in the OPEN message is not
328 recognized, then the Error Subcode MUST be set to Unsupported Optional
331 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
332 SUB_CODE = BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER
335 class AuthFailure(BgpExc):
336 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
337 SUB_CODE = BGP_ERROR_SUB_AUTHENTICATION_FAILURE
340 class UnacceptableHoldTime(BgpExc):
341 """Error to indicate Unacceptable Hold Time in open message.
343 RFC says: If the Hold Time field of the OPEN message is unacceptable, then
344 the Error Subcode MUST be set to Unacceptable Hold Time.
346 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
347 SUB_CODE = BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME
349 # ============================================================================
350 # UPDATE message related errors
351 # ============================================================================
354 class MalformedAttrList(BgpExc):
355 """Error to indicate UPDATE message is malformed.
357 RFC says: Error checking of an UPDATE message begins by examining the path
358 attributes. If the Withdrawn Routes Length or Total Attribute Length is
359 too large (i.e., if Withdrawn Routes Length + Total Attribute Length + 23
360 exceeds the message Length), then the Error Subcode MUST be set to
361 Malformed Attribute List.
363 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
364 SUB_CODE = BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST
367 class UnRegWellKnowAttr(BgpExc):
368 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
369 SUB_CODE = BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE
372 class MissingWellKnown(BgpExc):
373 """Error to indicate missing well-known attribute.
375 RFC says: If any of the well-known mandatory attributes are not present,
376 then the Error Subcode MUST be set to Missing Well-known Attribute. The
377 Data field MUST contain the Attribute Type Code of the missing, well-known
380 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
381 SUB_CODE = BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE
383 def __init__(self, pattr_type_code):
384 super(MissingWellKnown, self).__init__()
385 self.pattr_type_code = pattr_type_code
386 self.data = struct.pack('B', pattr_type_code)
389 class AttrFlagError(BgpExc):
390 """Error to indicate recognized path attributes have incorrect flags.
392 RFC says: If any recognized attribute has Attribute Flags that conflict
393 with the Attribute Type Code, then the Error Subcode MUST be set to
394 Attribute Flags Error. The Data field MUST contain the erroneous attribute
395 (type, length, and value).
397 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
398 SUB_CODE = BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR
401 class AttrLenError(BgpExc):
402 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
403 SUB_CODE = BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR
406 class InvalidOriginError(BgpExc):
407 """Error indicates undefined Origin attribute value.
409 RFC says: If the ORIGIN attribute has an undefined value, then the Error
410 Sub- code MUST be set to Invalid Origin Attribute. The Data field MUST
411 contain the unrecognized attribute (type, length, and value).
413 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
414 SUB_CODE = BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE
417 class RoutingLoop(BgpExc):
418 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
419 SUB_CODE = BGP_ERROR_SUB_ROUTING_LOOP
422 class InvalidNextHop(BgpExc):
423 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
424 SUB_CODE = BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE
427 class OptAttrError(BgpExc):
428 """Error indicates Optional Attribute is malformed.
430 RFC says: If an optional attribute is recognized, then the value of this
431 attribute MUST be checked. If an error is detected, the attribute MUST be
432 discarded, and the Error Subcode MUST be set to Optional Attribute Error.
433 The Data field MUST contain the attribute (type, length, and value).
435 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
436 SUB_CODE = BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR
439 class InvalidNetworkField(BgpExc):
440 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
441 SUB_CODE = BGP_ERROR_SUB_INVALID_NETWORK_FIELD
444 class MalformedAsPath(BgpExc):
445 """Error to indicate if AP_PATH attribute is syntactically incorrect.
447 RFC says: The AS_PATH attribute is checked for syntactic correctness. If
448 the path is syntactically incorrect, then the Error Subcode MUST be set to
451 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
452 SUB_CODE = BGP_ERROR_SUB_MALFORMED_AS_PATH
455 # ============================================================================
457 # ============================================================================
460 class HoldTimerExpired(BgpExc):
461 """Error to indicate Hold Timer expired.
463 RFC says: If a system does not receive successive KEEPALIVE, UPDATE, and/or
464 NOTIFICATION messages within the period specified in the Hold Time field of
465 the OPEN message, then the NOTIFICATION message with the Hold Timer Expired
466 Error Code is sent and the BGP connection is closed.
468 CODE = BGP_ERROR_HOLD_TIMER_EXPIRED
469 SUB_CODE = BGP_ERROR_SUB_HOLD_TIMER_EXPIRED
471 # ============================================================================
472 # Finite State Machine Error
473 # ============================================================================
476 class FiniteStateMachineError(BgpExc):
477 """Error to indicate any Finite State Machine Error.
479 RFC says: Any error detected by the BGP Finite State Machine (e.g., receipt
480 of an unexpected event) is indicated by sending the NOTIFICATION message
481 with the Error Code Finite State Machine Error.
483 CODE = BGP_ERROR_FSM_ERROR
484 SUB_CODE = BGP_ERROR_SUB_FSM_ERROR
487 # ============================================================================
489 # ============================================================================
491 class MaxPrefixReached(BgpExc):
492 CODE = BGP_ERROR_CEASE
493 SUB_CODE = BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED
496 class AdminShutdown(BgpExc):
497 """Error to indicate Administrative shutdown.
499 RFC says: If a BGP speaker decides to administratively shut down its
500 peering with a neighbor, then the speaker SHOULD send a NOTIFICATION
501 message with the Error Code Cease and the Error Subcode 'Administrative
504 CODE = BGP_ERROR_CEASE
505 SUB_CODE = BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN
508 class PeerDeConfig(BgpExc):
509 CODE = BGP_ERROR_CEASE
510 SUB_CODE = BGP_ERROR_SUB_PEER_DECONFIGURED
513 class AdminReset(BgpExc):
514 CODE = BGP_ERROR_CEASE
515 SUB_CODE = BGP_ERROR_SUB_ADMINISTRATIVE_RESET
518 class ConnRejected(BgpExc):
519 """Error to indicate Connection Rejected.
521 RFC says: If a BGP speaker decides to disallow a BGP connection (e.g., the
522 peer is not configured locally) after the speaker accepts a transport
523 protocol connection, then the BGP speaker SHOULD send a NOTIFICATION
524 message with the Error Code Cease and the Error Subcode "Connection
527 CODE = BGP_ERROR_CEASE
528 SUB_CODE = BGP_ERROR_SUB_CONNECTION_RESET
531 class OtherConfChange(BgpExc):
532 CODE = BGP_ERROR_CEASE
533 SUB_CODE = BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE
536 class CollisionResolution(BgpExc):
537 """Error to indicate Connection Collision Resolution.
539 RFC says: If a BGP speaker decides to send a NOTIFICATION message with the
540 Error Code Cease as a result of the collision resolution procedure (as
541 described in [BGP-4]), then the subcode SHOULD be set to "Connection
542 Collision Resolution".
544 CODE = BGP_ERROR_CEASE
545 SUB_CODE = BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION
548 class OutOfResource(BgpExc):
549 CODE = BGP_ERROR_CEASE
550 SUB_CODE = BGP_ERROR_SUB_OUT_OF_RESOURCES
553 @functools.total_ordering
554 class RouteFamily(StringifyMixin):
555 def __init__(self, afi, safi):
559 def __lt__(self, other):
560 return (self.afi, self.safi) < (other.afi, other.safi)
562 def __eq__(self, other):
563 return (self.afi, self.safi) == (other.afi, other.safi)
566 return hash((self.afi, self.safi))
569 # Route Family Singleton
570 RF_IPv4_UC = RouteFamily(addr_family.IP, subaddr_family.UNICAST)
571 RF_IPv6_UC = RouteFamily(addr_family.IP6, subaddr_family.UNICAST)
572 RF_IPv4_VPN = RouteFamily(addr_family.IP, subaddr_family.MPLS_VPN)
573 RF_IPv6_VPN = RouteFamily(addr_family.IP6, subaddr_family.MPLS_VPN)
574 RF_IPv4_MPLS = RouteFamily(addr_family.IP, subaddr_family.MPLS_LABEL)
575 RF_IPv6_MPLS = RouteFamily(addr_family.IP6, subaddr_family.MPLS_LABEL)
576 RF_L2_EVPN = RouteFamily(addr_family.L2VPN, subaddr_family.EVPN)
577 RF_IPv4_FLOWSPEC = RouteFamily(addr_family.IP, subaddr_family.IP_FLOWSPEC)
578 RF_IPv6_FLOWSPEC = RouteFamily(addr_family.IP6, subaddr_family.IP_FLOWSPEC)
579 RF_VPNv4_FLOWSPEC = RouteFamily(addr_family.IP, subaddr_family.VPN_FLOWSPEC)
580 RF_VPNv6_FLOWSPEC = RouteFamily(addr_family.IP6, subaddr_family.VPN_FLOWSPEC)
581 RF_L2VPN_FLOWSPEC = RouteFamily(
582 addr_family.L2VPN, subaddr_family.VPN_FLOWSPEC)
583 RF_RTC_UC = RouteFamily(addr_family.IP,
584 subaddr_family.ROUTE_TARGET_CONSTRAINTS)
587 (addr_family.IP, subaddr_family.UNICAST): RF_IPv4_UC,
588 (addr_family.IP6, subaddr_family.UNICAST): RF_IPv6_UC,
589 (addr_family.IP, subaddr_family.MPLS_VPN): RF_IPv4_VPN,
590 (addr_family.IP6, subaddr_family.MPLS_VPN): RF_IPv6_VPN,
591 (addr_family.IP, subaddr_family.MPLS_LABEL): RF_IPv4_MPLS,
592 (addr_family.IP6, subaddr_family.MPLS_LABEL): RF_IPv6_MPLS,
593 (addr_family.L2VPN, subaddr_family.EVPN): RF_L2_EVPN,
594 (addr_family.IP, subaddr_family.IP_FLOWSPEC): RF_IPv4_FLOWSPEC,
595 (addr_family.IP6, subaddr_family.IP_FLOWSPEC): RF_IPv6_FLOWSPEC,
596 (addr_family.IP, subaddr_family.VPN_FLOWSPEC): RF_VPNv4_FLOWSPEC,
597 (addr_family.IP6, subaddr_family.VPN_FLOWSPEC): RF_VPNv6_FLOWSPEC,
598 (addr_family.L2VPN, subaddr_family.VPN_FLOWSPEC): RF_L2VPN_FLOWSPEC,
599 (addr_family.IP, subaddr_family.ROUTE_TARGET_CONSTRAINTS): RF_RTC_UC
603 def get_rf(afi, safi):
604 return _rf_map[(afi, safi)]
607 def pad(binary, len_):
608 assert len(binary) <= len_
609 return binary + b'\0' * (len_ - len(binary))
612 class _RouteDistinguisher(StringifyMixin, TypeDisp, _Value):
618 def __init__(self, admin=0, assigned=0, type_=None):
620 type_ = self._rev_lookup_type(self.__class__)
623 self.assigned = assigned
626 def parser(cls, buf):
628 (type_,) = struct.unpack_from(cls._PACK_STR, six.binary_type(buf))
629 rest = buf[struct.calcsize(cls._PACK_STR):]
630 subcls = cls._lookup_type(type_)
631 return subcls(**subcls.parse_value(rest))
634 def from_str(cls, str_):
635 assert isinstance(str_, str)
637 first, second = str_.split(':')
639 type_ = cls.IPV4_ADDRESS
640 elif int(first) > (1 << 16):
641 type_ = cls.FOUR_OCTET_AS
644 type_ = cls.TWO_OCTET_AS
646 subcls = cls._lookup_type(type_)
647 return subcls(admin=first, assigned=int(second))
650 value = self.serialize_value()
652 msg_pack_into(self._PACK_STR, buf, 0, self.type)
653 return six.binary_type(buf + value)
656 def formatted_str(self):
657 return "%s:%s" % (self.admin, self.assigned)
660 @_RouteDistinguisher.register_type(_RouteDistinguisher.TWO_OCTET_AS)
661 class BGPTwoOctetAsRD(_RouteDistinguisher):
662 _VALUE_PACK_STR = '!HI'
663 _VALUE_FIELDS = ['admin', 'assigned']
665 def __init__(self, **kwargs):
666 super(BGPTwoOctetAsRD, self).__init__()
667 self.do_init(BGPTwoOctetAsRD, self, kwargs)
670 @_RouteDistinguisher.register_type(_RouteDistinguisher.IPV4_ADDRESS)
671 class BGPIPv4AddressRD(_RouteDistinguisher):
672 _VALUE_PACK_STR = '!4sH'
673 _VALUE_FIELDS = ['admin', 'assigned']
680 def __init__(self, **kwargs):
681 super(BGPIPv4AddressRD, self).__init__()
682 self.do_init(BGPIPv4AddressRD, self, kwargs)
685 def parse_value(cls, buf):
686 d_ = super(BGPIPv4AddressRD, cls).parse_value(buf)
687 d_['admin'] = addrconv.ipv4.bin_to_text(d_['admin'])
690 def serialize_value(self):
692 for f in self._VALUE_FIELDS:
695 v = bytes(addrconv.ipv4.text_to_bin(v))
698 msg_pack_into(self._VALUE_PACK_STR, buf, 0, *args)
702 @_RouteDistinguisher.register_type(_RouteDistinguisher.FOUR_OCTET_AS)
703 class BGPFourOctetAsRD(_RouteDistinguisher):
704 _VALUE_PACK_STR = '!IH'
705 _VALUE_FIELDS = ['admin', 'assigned']
707 def __init__(self, **kwargs):
708 super(BGPFourOctetAsRD, self).__init__()
709 self.do_init(BGPFourOctetAsRD, self, kwargs)
712 @six.add_metaclass(abc.ABCMeta)
713 class _AddrPrefix(StringifyMixin):
714 _PACK_STR = '!B' # length
716 def __init__(self, length, addr, prefixes=None, **kwargs):
717 # length is on-wire bit length of prefixes+addr.
718 assert prefixes != ()
719 if isinstance(addr, tuple):
720 # for _AddrPrefix.parser
721 # also for _VPNAddrPrefix.__init__ etc
725 addr = prefixes + (addr,)
730 def _to_bin(cls, addr):
735 def _from_bin(cls, addr):
739 def parser(cls, buf):
740 (length, ) = struct.unpack_from(cls._PACK_STR, six.binary_type(buf))
741 rest = buf[struct.calcsize(cls._PACK_STR):]
742 byte_length = (length + 7) // 8
743 addr = cls._from_bin(rest[:byte_length])
744 rest = rest[byte_length:]
745 return cls(length=length, addr=addr), rest
749 byte_length = (self.length + 7) // 8
750 bin_addr = self._to_bin(self.addr)
751 if (self.length % 8) == 0:
752 bin_addr = bin_addr[:byte_length]
754 # clear trailing bits in the last octet.
755 # rfc doesn't require this.
756 mask = 0xff00 >> (self.length % 8)
757 last_byte = six.int2byte(
758 six.indexbytes(bin_addr, byte_length - 1) & mask)
759 bin_addr = bin_addr[:byte_length - 1] + last_byte
760 self.addr = self._from_bin(bin_addr)
763 msg_pack_into(self._PACK_STR, buf, 0, self.length)
764 return buf + bytes(bin_addr)
767 class _BinAddrPrefix(_AddrPrefix):
769 def _to_bin(cls, addr):
773 def _from_bin(cls, addr):
777 class _LabelledAddrPrefix(_AddrPrefix):
778 _LABEL_PACK_STR = '!3B'
780 # 3. Carrying Label Mapping Information
781 # The label information carried (as part of NLRI) in the Withdrawn
782 # Routes field should be set to 0x800000. (Of course, terminating the
783 # BGP session also withdraws all the previously advertised routes.)
785 _WITHDRAW_LABEL = 0x800000
787 def __init__(self, length, addr, labels=None, **kwargs):
788 labels = labels if labels else []
789 assert isinstance(labels, list)
790 is_tuple = isinstance(addr, tuple)
792 # for _AddrPrefix.parser
797 length += struct.calcsize(self._LABEL_PACK_STR) * 8 * len(labels)
798 assert length > struct.calcsize(self._LABEL_PACK_STR) * 8 * len(labels)
800 super(_LabelledAddrPrefix, self).__init__(prefixes=prefixes,
806 def _label_to_bin(cls, label):
808 msg_pack_into(cls._LABEL_PACK_STR, buf, 0,
809 (label & 0xff0000) >> 16,
810 (label & 0x00ff00) >> 8,
811 (label & 0x0000ff) >> 0)
812 return six.binary_type(buf)
815 def _label_from_bin(cls, label):
816 (b1, b2, b3) = struct.unpack_from(cls._LABEL_PACK_STR,
817 six.binary_type(label))
818 rest = label[struct.calcsize(cls._LABEL_PACK_STR):]
819 return (b1 << 16) | (b2 << 8) | b3, rest
822 def _to_bin(cls, addr):
825 labels = [x << 4 for x in labels]
826 if labels and labels[-1] != cls._WITHDRAW_LABEL:
827 labels[-1] |= 1 # bottom of stack
828 bin_labels = list(cls._label_to_bin(l) for l in labels)
829 return bytes(reduce(lambda x, y: x + y, bin_labels,
830 bytearray()) + cls._prefix_to_bin(rest))
833 def _has_no_label(cls, bin_):
838 (label, bin_) = cls._label_from_bin(bin_)
840 if label & 1 or label == cls._WITHDRAW_LABEL:
842 assert length > struct.calcsize(cls._LABEL_PACK_STR) * len(labels)
845 except AssertionError:
850 def _from_bin(cls, addr):
854 if cls._has_no_label(rest):
855 return ([],) + cls._prefix_from_bin(rest)
858 (label, rest) = cls._label_from_bin(rest)
859 labels.append(label >> 4)
860 if label & 1 or label == cls._WITHDRAW_LABEL:
862 return (labels,) + cls._prefix_from_bin(rest)
865 class _UnlabelledAddrPrefix(_AddrPrefix):
867 def _to_bin(cls, addr):
868 return cls._prefix_to_bin((addr,))
871 def _from_bin(cls, binaddr):
872 (addr,) = cls._prefix_from_bin(binaddr)
876 class _IPAddrPrefix(_AddrPrefix):
878 def _prefix_to_bin(addr):
880 return addrconv.ipv4.text_to_bin(addr)
883 def _prefix_from_bin(addr):
884 return addrconv.ipv4.bin_to_text(pad(addr, 4)),
887 class _IP6AddrPrefix(_AddrPrefix):
889 def _prefix_to_bin(addr):
891 return addrconv.ipv6.text_to_bin(addr)
894 def _prefix_from_bin(addr):
895 return addrconv.ipv6.bin_to_text(pad(addr, 16)),
898 class _VPNAddrPrefix(_AddrPrefix):
901 def __init__(self, length, addr, prefixes=(), route_dist=0):
902 if isinstance(addr, tuple):
903 # for _AddrPrefix.parser
904 assert not route_dist
905 assert length > struct.calcsize(self._RD_PACK_STR) * 8
909 length += struct.calcsize(self._RD_PACK_STR) * 8
911 if isinstance(route_dist, str):
912 route_dist = _RouteDistinguisher.from_str(route_dist)
914 prefixes = prefixes + (route_dist,)
915 super(_VPNAddrPrefix, self).__init__(prefixes=prefixes,
920 def _prefix_to_bin(cls, addr):
923 binrd = rd.serialize()
924 return binrd + super(_VPNAddrPrefix, cls)._prefix_to_bin(rest)
927 def _prefix_from_bin(cls, binaddr):
929 binrest = binaddr[8:]
930 rd = _RouteDistinguisher.parser(binrd)
931 return (rd,) + super(_VPNAddrPrefix, cls)._prefix_from_bin(binrest)
934 class IPAddrPrefix(_UnlabelledAddrPrefix, _IPAddrPrefix):
935 ROUTE_FAMILY = RF_IPv4_UC
944 return self.addr + '/{0}'.format(self.length)
947 def formatted_nlri_str(self):
951 class IP6AddrPrefix(_UnlabelledAddrPrefix, _IP6AddrPrefix):
952 ROUTE_FAMILY = RF_IPv6_UC
961 return self.addr + '/{0}'.format(self.length)
964 def formatted_nlri_str(self):
968 class LabelledIPAddrPrefix(_LabelledAddrPrefix, _IPAddrPrefix):
969 ROUTE_FAMILY = RF_IPv4_MPLS
972 class LabelledIP6AddrPrefix(_LabelledAddrPrefix, _IP6AddrPrefix):
973 ROUTE_FAMILY = RF_IPv6_MPLS
976 class LabelledVPNIPAddrPrefix(_LabelledAddrPrefix, _VPNAddrPrefix,
978 ROUTE_FAMILY = RF_IPv4_VPN
982 masklen = self.length - struct.calcsize(self._RD_PACK_STR) * 8 \
983 - struct.calcsize(self._LABEL_PACK_STR) * 8 * len(self.addr[:-2])
984 return self.addr[-1] + '/{0}'.format(masklen)
987 def route_dist(self):
988 return self.addr[-2].formatted_str
991 def label_list(self):
995 def formatted_nlri_str(self):
996 return "%s:%s" % (self.route_dist, self.prefix)
999 class LabelledVPNIP6AddrPrefix(_LabelledAddrPrefix, _VPNAddrPrefix,
1001 ROUTE_FAMILY = RF_IPv6_VPN
1005 masklen = self.length - struct.calcsize(self._RD_PACK_STR) * 8 \
1006 - struct.calcsize(self._LABEL_PACK_STR) * 8 * len(self.addr[:-2])
1007 return self.addr[-1] + '/{0}'.format(masklen)
1010 def route_dist(self):
1011 return self.addr[-2].formatted_str
1014 def label_list(self):
1018 def formatted_nlri_str(self):
1019 return "%s:%s" % (self.route_dist, self.prefix)
1022 class EvpnEsi(StringifyMixin, TypeDisp, _Value):
1024 Ethernet Segment Identifier
1026 The supported ESI Types:
1028 - ``EvpnEsi.ARBITRARY`` indicates EvpnArbitraryEsi.
1030 - ``EvpnEsi.LACP`` indicates EvpnLACPEsi.
1032 - ``EvpnEsi.L2_BRIDGE`` indicates EvpnL2BridgeEsi.
1034 - ``EvpnEsi.MAC_BASED`` indicates EvpnMacBasedEsi.
1036 - ``EvpnEsi.ROUTER_ID`` indicates EvpnRouterIDEsi.
1038 - ``EvpnEsi.AS_BASED`` indicates EvpnASBasedEsi.
1040 _PACK_STR = "!B" # ESI Type
1049 MAX = 0xff # Reserved
1051 _TYPE_NAME = None # must be defined in subclass
1053 def __init__(self, type_=None):
1055 type_ = self._rev_lookup_type(self.__class__)
1059 def parser(cls, buf):
1060 (esi_type,) = struct.unpack_from(
1061 cls._PACK_STR, six.binary_type(buf))
1062 subcls = cls._lookup_type(esi_type)
1063 return subcls(**subcls.parse_value(buf[1:cls._ESI_LEN]))
1065 def serialize(self):
1067 msg_pack_into(EvpnEsi._PACK_STR, buf, 0, self.type)
1068 return six.binary_type(buf + self.serialize_value())
1071 def formatted_str(self):
1074 ','.join(str(getattr(self, v)) for v in self._VALUE_FIELDS))
1077 @EvpnEsi.register_unknown_type()
1078 class EvpnUnknownEsi(EvpnEsi):
1080 ESI value for unknown type
1082 _TYPE_NAME = 'unknown'
1083 _VALUE_PACK_STR = '!9s'
1084 _VALUE_FIELDS = ['value']
1086 def __init__(self, value, type_=None):
1087 super(EvpnUnknownEsi, self).__init__(type_)
1091 def formatted_str(self):
1092 return '%s(%s)' % (self._TYPE_NAME, binary_str(self.value))
1095 @EvpnEsi.register_type(EvpnEsi.ARBITRARY)
1096 class EvpnArbitraryEsi(EvpnEsi):
1098 Arbitrary 9-octet ESI value
1100 This type indicates an arbitrary 9-octet ESI value,
1101 which is managed and configured by the operator.
1103 _TYPE_NAME = 'arbitrary'
1104 _VALUE_PACK_STR = '!9s'
1105 _VALUE_FIELDS = ['value']
1107 def __init__(self, value, type_=None):
1108 super(EvpnArbitraryEsi, self).__init__(type_)
1112 def formatted_str(self):
1113 return '%s(%s)' % (self._TYPE_NAME, binary_str(self.value))
1116 @EvpnEsi.register_type(EvpnEsi.LACP)
1117 class EvpnLACPEsi(EvpnEsi):
1121 When IEEE 802.1AX LACP is used between the PEs and CEs,
1122 this ESI type indicates an auto-generated ESI value
1123 determined from LACP.
1126 _VALUE_PACK_STR = '!6sHx'
1127 _VALUE_FIELDS = ['mac_addr', 'port_key']
1134 def __init__(self, mac_addr, port_key, type_=None):
1135 super(EvpnLACPEsi, self).__init__(type_)
1136 self.mac_addr = mac_addr
1137 self.port_key = port_key
1140 def parse_value(cls, buf):
1141 (mac_addr, port_key) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
1143 'mac_addr': addrconv.mac.bin_to_text(mac_addr),
1144 'port_key': port_key,
1147 def serialize_value(self):
1149 self._VALUE_PACK_STR,
1150 addrconv.mac.text_to_bin(self.mac_addr), self.port_key)
1153 @EvpnEsi.register_type(EvpnEsi.L2_BRIDGE)
1154 class EvpnL2BridgeEsi(EvpnEsi):
1156 ESI value for Layer 2 Bridge
1158 This type is used in the case of indirectly connected hosts
1159 via a bridged LAN between the CEs and the PEs.
1160 The ESI Value is auto-generated and determined based
1161 on the Layer 2 bridge protocol.
1163 _TYPE_NAME = 'l2_bridge'
1164 _VALUE_PACK_STR = '!6sHx'
1165 _VALUE_FIELDS = ['mac_addr', 'priority']
1172 def __init__(self, mac_addr, priority, type_=None):
1173 super(EvpnL2BridgeEsi, self).__init__(type_)
1174 self.mac_addr = mac_addr
1175 self.priority = priority
1178 def parse_value(cls, buf):
1179 (mac_addr, priority) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
1181 'mac_addr': addrconv.mac.bin_to_text(mac_addr),
1182 'priority': priority,
1185 def serialize_value(self):
1187 self._VALUE_PACK_STR,
1188 addrconv.mac.text_to_bin(self.mac_addr), self.priority)
1191 @EvpnEsi.register_type(EvpnEsi.MAC_BASED)
1192 class EvpnMacBasedEsi(EvpnEsi):
1196 This type indicates a MAC-based ESI Value that
1197 can be auto-generated or configured by the operator.
1199 _TYPE_NAME = 'mac_based'
1200 _VALUE_PACK_STR = '!6s3s'
1201 _VALUE_FIELDS = ['mac_addr', 'local_disc']
1208 def __init__(self, mac_addr, local_disc, type_=None):
1209 super(EvpnMacBasedEsi, self).__init__(type_)
1210 self.mac_addr = mac_addr
1211 self.local_disc = local_disc
1214 def parse_value(cls, buf):
1215 (mac_addr, local_disc) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
1217 'mac_addr': addrconv.mac.bin_to_text(mac_addr),
1218 'local_disc': type_desc.Int3.to_user(local_disc),
1221 def serialize_value(self):
1223 self._VALUE_PACK_STR,
1224 addrconv.mac.text_to_bin(self.mac_addr),
1225 type_desc.Int3.from_user(self.local_disc))
1228 @EvpnEsi.register_type(EvpnEsi.ROUTER_ID)
1229 class EvpnRouterIDEsi(EvpnEsi):
1233 This type indicates a router-ID ESI Value that
1234 can be auto-generated or configured by the operator.
1236 _TYPE_NAME = 'router_id'
1237 _VALUE_PACK_STR = '!4sIx'
1238 _VALUE_FIELDS = ['router_id', 'local_disc']
1245 def __init__(self, router_id, local_disc, type_=None):
1246 super(EvpnRouterIDEsi, self).__init__(type_)
1247 self.router_id = router_id
1248 self.local_disc = local_disc
1251 def parse_value(cls, buf):
1252 (router_id, local_disc) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
1254 'router_id': addrconv.ipv4.bin_to_text(router_id),
1255 'local_disc': local_disc,
1258 def serialize_value(self):
1260 self._VALUE_PACK_STR,
1261 addrconv.ipv4.text_to_bin(self.router_id), self.local_disc)
1264 @EvpnEsi.register_type(EvpnEsi.AS_BASED)
1265 class EvpnASBasedEsi(EvpnEsi):
1269 This type indicates an Autonomous System(AS)-based
1270 ESI Value that can be auto-generated or configured by
1273 _TYPE_NAME = 'as_based'
1274 _VALUE_PACK_STR = '!IIx'
1275 _VALUE_FIELDS = ['as_number', 'local_disc']
1277 def __init__(self, as_number, local_disc, type_=None):
1278 super(EvpnASBasedEsi, self).__init__(type_)
1279 self.as_number = as_number
1280 self.local_disc = local_disc
1283 class EvpnNLRI(StringifyMixin, TypeDisp):
1285 BGP Network Layer Reachability Information (NLRI) for EVPN
1287 ROUTE_FAMILY = RF_L2_EVPN
1290 # +-----------------------------------+
1291 # | Route Type (1 octet) |
1292 # +-----------------------------------+
1293 # | Length (1 octet) |
1294 # +-----------------------------------+
1295 # | Route Type specific (variable) |
1296 # +-----------------------------------+
1298 _PACK_STR_SIZE = struct.calcsize(_PACK_STR)
1300 ETHERNET_AUTO_DISCOVERY = 0x01
1301 MAC_IP_ADVERTISEMENT = 0x02
1302 INCLUSIVE_MULTICAST_ETHERNET_TAG = 0x03
1303 ETHERNET_SEGMENT = 0x04
1304 IP_PREFIX_ROUTE = 0x05
1306 ROUTE_TYPE_NAME = None # must be defined in subclass
1308 # Reserved value for Ethernet Tag ID.
1311 # Dictionary of ROUTE_TYPE_NAME to subclass.
1313 # _NAMES = {'eth_ad': EvpnEthernetAutoDiscoveryNLRI, ...}
1316 # List of the fields considered to be part of the prefix in the NLRI.
1317 # This list should be defined in subclasses to format NLRI string
1319 NLRI_PREFIX_FIELDS = []
1321 def __init__(self, type_=None, length=None):
1323 type_ = self._rev_lookup_type(self.__class__)
1325 self.length = length
1326 self.route_dist = None # should be initialized in subclass
1329 def register_type(cls, type_):
1330 cls._TYPES = cls._TYPES.copy()
1331 cls._NAMES = cls._NAMES.copy()
1333 def _register_type(subcls):
1334 cls._TYPES[type_] = subcls
1335 cls._NAMES[subcls.ROUTE_TYPE_NAME] = subcls
1336 cls._REV_TYPES = None
1339 return _register_type
1342 def _lookup_type_name(cls, type_name):
1344 return cls._NAMES[type_name]
1346 return EvpnUnknownNLRI
1349 def parser(cls, buf):
1350 (route_type, length) = struct.unpack_from(
1351 cls._PACK_STR, six.binary_type(buf))
1352 offset = cls._PACK_STR_SIZE + length
1353 subcls = cls._lookup_type(route_type)
1354 values = subcls.parse_value(buf[cls._PACK_STR_SIZE:offset])
1355 return subcls(type_=route_type, length=length,
1356 **values), buf[offset:]
1358 def serialize_value(self):
1359 # Overrided in subclass
1362 def serialize(self):
1363 value_bin = self.serialize_value()
1365 self.length = len(value_bin)
1366 return struct.pack(EvpnNLRI._PACK_STR,
1367 self.type, self.length) + value_bin
1370 def _rd_from_bin(buf):
1371 return _RouteDistinguisher.parser(buf[:8]), buf[8:]
1375 return six.binary_type(rd.serialize())
1378 def _esi_from_bin(buf):
1379 return EvpnEsi.parser(buf[:10]), buf[10:]
1382 def _esi_to_bin(esi):
1383 return esi.serialize()
1386 def _ethernet_tag_id_from_bin(buf):
1387 return type_desc.Int4.to_user(six.binary_type(buf[:4])), buf[4:]
1390 def _ethernet_tag_id_to_bin(tag_id):
1391 return type_desc.Int4.from_user(tag_id)
1394 def _mac_addr_len_from_bin(buf):
1395 return type_desc.Int1.to_user(six.binary_type(buf[:1])), buf[1:]
1398 def _mac_addr_len_to_bin(mac_len):
1399 return type_desc.Int1.from_user(mac_len)
1402 def _mac_addr_from_bin(buf, mac_len):
1404 return addrconv.mac.bin_to_text(buf[:mac_len]), buf[mac_len:]
1407 def _mac_addr_to_bin(mac_addr):
1408 return addrconv.mac.text_to_bin(mac_addr)
1411 def _ip_addr_len_from_bin(buf):
1412 return type_desc.Int1.to_user(six.binary_type(buf[:1])), buf[1:]
1415 def _ip_addr_len_to_bin(ip_len):
1416 return type_desc.Int1.from_user(ip_len)
1419 def _ip_addr_from_bin(buf, ip_len):
1420 return ip.bin_to_text(buf[:ip_len]), buf[ip_len:]
1423 def _ip_addr_to_bin(ip_addr):
1424 return ip.text_to_bin(ip_addr)
1427 def _mpls_label_from_bin(buf):
1428 mpls_label, is_bos = mpls.label_from_bin(buf)
1430 return mpls_label, rest, is_bos
1433 def _mpls_label_to_bin(label, is_bos=True):
1434 return mpls.label_to_bin(label, is_bos=is_bos)
1437 def _vni_from_bin(buf):
1438 return vxlan.vni_from_bin(six.binary_type(buf[:3])), buf[3:]
1441 def _vni_to_bin(vni):
1442 return vxlan.vni_to_bin(vni)
1448 for k in i.NLRI_PREFIX_FIELDS:
1451 pairs.append('%s:%s' % (k, v.formatted_str))
1453 pairs.append('%s:%s' % (k, v))
1454 return ','.join(pairs)
1456 return '%s(%s)' % (self.ROUTE_TYPE_NAME, _format(self))
1459 def formatted_nlri_str(self):
1460 return '%s:%s' % (self.route_dist, self.prefix)
1463 @EvpnNLRI.register_unknown_type()
1464 class EvpnUnknownNLRI(EvpnNLRI):
1466 Unknown route type specific EVPN NLRI
1468 ROUTE_TYPE_NAME = 'unknown'
1469 NLRI_PREFIX_FIELDS = ['value']
1471 def __init__(self, value, type_, length=None):
1472 super(EvpnUnknownNLRI, self).__init__(type_, length)
1476 def parse_value(cls, buf):
1481 def serialize_value(self):
1485 def formatted_nlri_str(self):
1486 return '%s(%s)' % (self.ROUTE_TYPE_NAME, binary_str(self.value))
1489 @EvpnNLRI.register_type(EvpnNLRI.ETHERNET_AUTO_DISCOVERY)
1490 class EvpnEthernetAutoDiscoveryNLRI(EvpnNLRI):
1492 Ethernet A-D route type specific EVPN NLRI
1494 ROUTE_TYPE_NAME = 'eth_ad'
1496 # +---------------------------------------+
1497 # | Route Distinguisher (RD) (8 octets) |
1498 # +---------------------------------------+
1499 # |Ethernet Segment Identifier (10 octets)|
1500 # +---------------------------------------+
1501 # | Ethernet Tag ID (4 octets) |
1502 # +---------------------------------------+
1503 # | MPLS Label (3 octets) |
1504 # +---------------------------------------+
1505 _PACK_STR = "!8s10sI3s"
1506 NLRI_PREFIX_FIELDS = ['esi', 'ethernet_tag_id']
1513 def __init__(self, route_dist, esi, ethernet_tag_id,
1514 mpls_label=None, vni=None, label=None,
1515 type_=None, length=None):
1516 super(EvpnEthernetAutoDiscoveryNLRI, self).__init__(type_, length)
1517 self.route_dist = route_dist
1519 self.ethernet_tag_id = ethernet_tag_id
1521 # If binary type label field value is specified, stores it
1522 # and decodes as MPLS label and VNI.
1524 self._mpls_label, _, _ = self._mpls_label_from_bin(label)
1525 self._vni, _ = self._vni_from_bin(label)
1527 # If either MPLS label or VNI is specified, stores it
1528 # and encodes into binary type label field value.
1529 self._label = self._serialize_label(mpls_label, vni)
1530 self._mpls_label = mpls_label
1533 def _serialize_label(self, mpls_label, vni):
1535 return self._mpls_label_to_bin(mpls_label, is_bos=True)
1537 return self._vni_to_bin(vni)
1542 def parse_value(cls, buf):
1543 route_dist, rest = cls._rd_from_bin(buf)
1544 esi, rest = cls._esi_from_bin(rest)
1545 ethernet_tag_id, rest = cls._ethernet_tag_id_from_bin(rest)
1548 'route_dist': route_dist.formatted_str,
1550 'ethernet_tag_id': ethernet_tag_id,
1554 def serialize_value(self):
1555 route_dist = _RouteDistinguisher.from_str(self.route_dist)
1557 self._PACK_STR, route_dist.serialize(), self.esi.serialize(),
1558 self.ethernet_tag_id, self._label)
1561 def mpls_label(self):
1562 return self._mpls_label
1565 def mpls_label(self, mpls_label):
1566 self._label = self._mpls_label_to_bin(mpls_label, is_bos=True)
1567 self._mpls_label = mpls_label
1568 self._vni = None # disables VNI
1576 self._label = self._vni_to_bin(vni)
1577 self._mpls_label = None # disables MPLS label
1581 def label_list(self):
1582 return [self.mpls_label]
1585 @EvpnNLRI.register_type(EvpnNLRI.MAC_IP_ADVERTISEMENT)
1586 class EvpnMacIPAdvertisementNLRI(EvpnNLRI):
1588 MAC/IP Advertisement route type specific EVPN NLRI
1590 ROUTE_TYPE_NAME = 'mac_ip_adv'
1592 # +---------------------------------------+
1594 # +---------------------------------------+
1595 # |Ethernet Segment Identifier (10 octets)|
1596 # +---------------------------------------+
1597 # | Ethernet Tag ID (4 octets) |
1598 # +---------------------------------------+
1599 # | MAC Address Length (1 octet) |
1600 # +---------------------------------------+
1601 # | MAC Address (6 octets) |
1602 # +---------------------------------------+
1603 # | IP Address Length (1 octet) |
1604 # +---------------------------------------+
1605 # | IP Address (0, 4, or 16 octets) |
1606 # +---------------------------------------+
1607 # | MPLS Label1 (3 octets) |
1608 # +---------------------------------------+
1609 # | MPLS Label2 (0 or 3 octets) |
1610 # +---------------------------------------+
1611 _PACK_STR = "!8s10sIB6sB%ds%ds"
1612 # Note: mac_addr_len and ip_addr_len are omitted for readability.
1613 NLRI_PREFIX_FIELDS = ['ethernet_tag_id', 'mac_addr', 'ip_addr']
1622 def __init__(self, route_dist, ethernet_tag_id, mac_addr, ip_addr,
1623 esi=None, mpls_labels=None, vni=None, labels=None,
1624 mac_addr_len=None, ip_addr_len=None,
1625 type_=None, length=None):
1626 super(EvpnMacIPAdvertisementNLRI, self).__init__(type_, length)
1627 self.route_dist = route_dist
1629 self.ethernet_tag_id = ethernet_tag_id
1630 self.mac_addr_len = mac_addr_len
1631 self.mac_addr = mac_addr
1632 self.ip_addr_len = ip_addr_len
1633 self.ip_addr = ip_addr
1635 # If binary type labels field value is specified, stores it
1636 # and decodes as MPLS labels and VNI.
1637 self._mpls_labels, self._vni = self._parse_labels(labels)
1638 self._labels = labels
1640 # If either MPLS labels or VNI is specified, stores it
1641 # and encodes into binary type labels field value.
1642 self._labels = self._serialize_labels(mpls_labels, vni)
1643 self._mpls_labels = mpls_labels
1646 def _parse_labels(self, labels):
1647 mpls_label1, rest, is_bos = self._mpls_label_from_bin(labels)
1648 mpls_labels = [mpls_label1]
1649 if rest and not is_bos:
1650 mpls_label2, rest, _ = self._mpls_label_from_bin(rest)
1651 mpls_labels.append(mpls_label2)
1652 vni, _ = self._vni_from_bin(labels)
1653 return mpls_labels, vni
1655 def _serialize_labels(self, mpls_labels, vni):
1657 return self._serialize_mpls_labels(mpls_labels)
1659 return self._vni_to_bin(vni)
1663 def _serialize_mpls_labels(self, mpls_labels):
1664 if len(mpls_labels) == 1:
1665 return self._mpls_label_to_bin(mpls_labels[0], is_bos=True)
1666 elif len(mpls_labels) == 2:
1667 return (self._mpls_label_to_bin(mpls_labels[0], is_bos=False) +
1668 self._mpls_label_to_bin(mpls_labels[1], is_bos=True))
1673 def parse_value(cls, buf):
1674 route_dist, rest = cls._rd_from_bin(buf)
1675 esi, rest = cls._esi_from_bin(rest)
1676 ethernet_tag_id, rest = cls._ethernet_tag_id_from_bin(rest)
1677 mac_addr_len, rest = cls._mac_addr_len_from_bin(rest)
1678 mac_addr, rest = cls._mac_addr_from_bin(rest, mac_addr_len)
1679 ip_addr_len, rest = cls._ip_addr_len_from_bin(rest)
1680 if ip_addr_len != 0:
1681 ip_addr, rest = cls._ip_addr_from_bin(rest, ip_addr_len // 8)
1686 'route_dist': route_dist.formatted_str,
1688 'ethernet_tag_id': ethernet_tag_id,
1689 'mac_addr_len': mac_addr_len,
1690 'mac_addr': mac_addr,
1691 'ip_addr_len': ip_addr_len,
1696 def serialize_value(self):
1697 route_dist = _RouteDistinguisher.from_str(self.route_dist)
1698 mac_addr = self._mac_addr_to_bin(self.mac_addr)
1699 self.mac_addr_len = len(mac_addr) * 8 # fixup
1701 ip_addr = self._ip_addr_to_bin(self.ip_addr)
1704 ip_addr_len = len(ip_addr)
1705 self.ip_addr_len = ip_addr_len * 8 # fixup
1708 self._PACK_STR % (ip_addr_len, len(self._labels)),
1709 route_dist.serialize(), self.esi.serialize(),
1710 self.ethernet_tag_id,
1711 self.mac_addr_len, mac_addr,
1712 self.ip_addr_len, ip_addr,
1716 def mpls_labels(self):
1717 return self._mpls_labels
1720 def mpls_labels(self, mpls_labels):
1721 self._labels = self._serialize_mpls_labels(mpls_labels)
1722 self._mpls_labels = mpls_labels
1723 self._vni = None # disables VNI
1731 self._labels = self._vni_to_bin(vni)
1732 self._mpls_labels = None # disables MPLS labels
1736 def label_list(self):
1737 return self.mpls_labels
1740 @EvpnNLRI.register_type(EvpnNLRI.INCLUSIVE_MULTICAST_ETHERNET_TAG)
1741 class EvpnInclusiveMulticastEthernetTagNLRI(EvpnNLRI):
1743 Inclusive Multicast Ethernet Tag route type specific EVPN NLRI
1745 ROUTE_TYPE_NAME = 'multicast_etag'
1747 # +---------------------------------------+
1749 # +---------------------------------------+
1750 # | Ethernet Tag ID (4 octets) |
1751 # +---------------------------------------+
1752 # | IP Address Length (1 octet) |
1753 # +---------------------------------------+
1754 # | Originating Router's IP Address |
1755 # | (4 or 16 octets) |
1756 # +---------------------------------------+
1757 _PACK_STR = '!8sIB%ds'
1758 NLRI_PREFIX_FIELDS = ['ethernet_tag_id', 'ip_addr']
1766 def __init__(self, route_dist, ethernet_tag_id, ip_addr,
1767 ip_addr_len=None, type_=None, length=None):
1768 super(EvpnInclusiveMulticastEthernetTagNLRI,
1769 self).__init__(type_, length)
1770 self.route_dist = route_dist
1771 self.ethernet_tag_id = ethernet_tag_id
1772 self.ip_addr_len = ip_addr_len
1773 self.ip_addr = ip_addr
1776 def parse_value(cls, buf):
1777 route_dist, rest = cls._rd_from_bin(buf)
1778 ethernet_tag_id, rest = cls._ethernet_tag_id_from_bin(rest)
1779 ip_addr_len, rest = cls._ip_addr_len_from_bin(rest)
1780 ip_addr, rest = cls._ip_addr_from_bin(rest, ip_addr_len // 8)
1783 'route_dist': route_dist.formatted_str,
1784 'ethernet_tag_id': ethernet_tag_id,
1785 'ip_addr_len': ip_addr_len,
1789 def serialize_value(self):
1790 route_dist = _RouteDistinguisher.from_str(self.route_dist)
1791 ip_addr = self._ip_addr_to_bin(self.ip_addr)
1792 self.ip_addr_len = len(ip_addr) * 8 # fixup
1795 self._PACK_STR % len(ip_addr),
1796 route_dist.serialize(), self.ethernet_tag_id,
1797 self.ip_addr_len, ip_addr)
1800 @EvpnNLRI.register_type(EvpnNLRI.ETHERNET_SEGMENT)
1801 class EvpnEthernetSegmentNLRI(EvpnNLRI):
1803 Ethernet Segment route type specific EVPN NLRI
1805 ROUTE_TYPE_NAME = 'eth_seg'
1807 # +---------------------------------------+
1809 # +---------------------------------------+
1810 # |Ethernet Segment Identifier (10 octets)|
1811 # +---------------------------------------+
1812 # | IP Address Length (1 octet) |
1813 # +---------------------------------------+
1814 # | Originating Router's IP Address |
1815 # | (4 or 16 octets) |
1816 # +---------------------------------------+
1817 _PACK_STR = '!8s10sB%ds'
1818 NLRI_PREFIX_FIELDS = ['esi', 'ip_addr']
1826 def __init__(self, route_dist, esi, ip_addr, ip_addr_len=None,
1827 type_=None, length=None):
1828 super(EvpnEthernetSegmentNLRI, self).__init__(type_, length)
1829 self.route_dist = route_dist
1831 self.ip_addr_len = ip_addr_len
1832 self.ip_addr = ip_addr
1835 def parse_value(cls, buf):
1836 route_dist, rest = cls._rd_from_bin(buf)
1837 esi, rest = cls._esi_from_bin(rest)
1838 ip_addr_len, rest = cls._ip_addr_len_from_bin(rest)
1839 ip_addr, rest = cls._ip_addr_from_bin(rest, ip_addr_len // 8)
1842 'route_dist': route_dist.formatted_str,
1844 'ip_addr_len': ip_addr_len,
1848 def serialize_value(self):
1849 route_dist = _RouteDistinguisher.from_str(self.route_dist)
1850 ip_addr = self._ip_addr_to_bin(self.ip_addr)
1852 self.ip_addr_len = len(ip_addr) * 8
1855 self._PACK_STR % len(ip_addr),
1856 route_dist.serialize(), self.esi.serialize(),
1857 self.ip_addr_len, ip_addr)
1860 @EvpnNLRI.register_type(EvpnNLRI.IP_PREFIX_ROUTE)
1861 class EvpnIpPrefixNLRI(EvpnNLRI):
1863 IP Prefix advertisement route NLRI
1865 ROUTE_TYPE_NAME = 'ip_prefix'
1867 # +---------------------------------------+
1869 # +---------------------------------------+
1870 # |Ethernet Segment Identifier (10 octets)|
1871 # +---------------------------------------+
1872 # | Ethernet Tag ID (4 octets) |
1873 # +---------------------------------------+
1874 # | IP Prefix Length (1 octet) |
1875 # +---------------------------------------+
1876 # | IP Prefix (4 or 16 octets) |
1877 # +---------------------------------------+
1878 # | GW IP Address (4 or 16 octets) |
1879 # +---------------------------------------+
1880 # | MPLS Label (3 octets) |
1881 # +---------------------------------------+
1882 _PACK_STR = '!8s10sIB%ds%ds3s'
1883 NLRI_PREFIX_FIELDS = ['ethernet_tag_id', 'ip_prefix']
1893 def __init__(self, route_dist, ethernet_tag_id, ip_prefix,
1894 esi=None, gw_ip_addr=None,
1895 mpls_label=None, vni=None, label=None,
1896 type_=None, length=None):
1897 super(EvpnIpPrefixNLRI, self).__init__(type_, length)
1898 self.route_dist = route_dist
1900 self.ethernet_tag_id = ethernet_tag_id
1901 self._ip_prefix = None
1902 self._ip_prefix_len = None
1903 self.ip_prefix = ip_prefix
1905 if gw_ip_addr is None:
1906 if ':' not in self._ip_prefix:
1907 self.gw_ip_addr = '0.0.0.0'
1909 self.gw_ip_addr = '::'
1911 self.gw_ip_addr = gw_ip_addr
1914 # If binary type label field value is specified, stores it
1915 # and decodes as MPLS label and VNI.
1917 self._mpls_label, _, _ = self._mpls_label_from_bin(label)
1918 self._vni, _ = self._vni_from_bin(label)
1920 # If either MPLS label or VNI is specified, stores it
1921 # and encodes into binary type label field value.
1922 self._label = self._serialize_label(mpls_label, vni)
1923 self._mpls_label = mpls_label
1926 def _serialize_label(self, mpls_label, vni):
1928 return self._mpls_label_to_bin(mpls_label, is_bos=True)
1930 return vxlan.vni_to_bin(vni)
1935 def parse_value(cls, buf):
1936 route_dist, rest = cls._rd_from_bin(buf)
1937 esi, rest = cls._esi_from_bin(rest)
1938 ethernet_tag_id, rest = cls._ethernet_tag_id_from_bin(rest)
1939 ip_prefix_len, rest = cls._ip_addr_len_from_bin(rest)
1940 _len = (len(rest) - cls._LABEL_LEN) // 2
1941 ip_prefix, rest = cls._ip_addr_from_bin(rest, _len)
1942 gw_ip_addr, rest = cls._ip_addr_from_bin(rest, _len)
1945 'route_dist': route_dist.formatted_str,
1947 'ethernet_tag_id': ethernet_tag_id,
1948 'ip_prefix': '%s/%s' % (ip_prefix, ip_prefix_len),
1949 'gw_ip_addr': gw_ip_addr,
1953 def serialize_value(self):
1954 route_dist = _RouteDistinguisher.from_str(self.route_dist)
1955 ip_prefix = self._ip_addr_to_bin(self._ip_prefix)
1956 gw_ip_addr = self._ip_addr_to_bin(self.gw_ip_addr)
1959 self._PACK_STR % (len(ip_prefix), len(gw_ip_addr)),
1960 route_dist.serialize(), self.esi.serialize(),
1961 self.ethernet_tag_id, self._ip_prefix_len, ip_prefix,
1962 gw_ip_addr, self._label)
1965 def ip_prefix(self):
1966 return '%s/%s' % (self._ip_prefix, self._ip_prefix_len)
1969 def ip_prefix(self, ip_prefix):
1970 self._ip_prefix, ip_prefix_len = ip_prefix.split('/')
1971 self._ip_prefix_len = int(ip_prefix_len)
1974 def mpls_label(self):
1975 return self._mpls_label
1978 def mpls_label(self, mpls_label):
1979 self._label = self._mpls_label_to_bin(mpls_label, is_bos=True)
1980 self._mpls_label = mpls_label
1981 self._vni = None # disables VNI
1989 self._label = self._vni_to_bin(vni)
1990 self._mpls_label = None # disables MPLS label
1994 def label_list(self):
1995 return [self.mpls_label]
1998 class _FlowSpecNLRIBase(StringifyMixin, TypeDisp):
2000 Base class for Flow Specification NLRI
2004 # +-----------------------------------+
2005 # | length (0xnn or 0xfn nn) |
2006 # +-----------------------------------+
2007 # | NLRI value (variable) |
2008 # +-----------------------------------+
2010 _LENGTH_SHORT_FMT = '!B'
2011 LENGTH_SHORT_SIZE = struct.calcsize(_LENGTH_SHORT_FMT)
2012 _LENGTH_LONG_FMT = '!H'
2013 LENGTH_LONG_SIZE = struct.calcsize(_LENGTH_LONG_FMT)
2014 _LENGTH_THRESHOLD = 0xf000
2015 FLOWSPEC_FAMILY = ''
2017 def __init__(self, length=0, rules=None):
2018 self.length = length
2021 assert isinstance(r, _FlowSpecComponentBase)
2025 def parser(cls, buf):
2026 (length,) = struct.unpack_from(
2027 cls._LENGTH_LONG_FMT, six.binary_type(buf))
2029 if length < cls._LENGTH_THRESHOLD:
2031 offset = cls.LENGTH_SHORT_SIZE
2033 offset = cls.LENGTH_LONG_SIZE
2035 kwargs = {'length': length}
2036 rest = buf[offset:offset + length]
2038 if cls.ROUTE_FAMILY.safi == subaddr_family.VPN_FLOWSPEC:
2039 route_dist = _RouteDistinguisher.parser(rest[:8])
2040 kwargs['route_dist'] = route_dist.formatted_str
2046 subcls, rest = _FlowSpecComponentBase.parse_header(
2047 rest, cls.ROUTE_FAMILY.afi)
2050 rule, rest = subcls.parse_body(rest)
2053 if (not isinstance(rule, _FlowSpecOperatorBase) or
2054 rule.operator & rule.END_OF_LIST):
2057 kwargs['rules'] = rules
2059 return cls(**kwargs), rest
2061 def serialize(self):
2064 if self.ROUTE_FAMILY.safi == subaddr_family.VPN_FLOWSPEC:
2065 route_dist = _RouteDistinguisher.from_str(self.route_dist)
2066 rules_bin += route_dist.serialize()
2068 self.rules.sort(key=lambda x: x.type)
2069 for _, rules in itertools.groupby(self.rules, key=lambda x: x.type):
2071 rules_bin += rules[0].serialize_header()
2073 if isinstance(rules[-1], _FlowSpecOperatorBase):
2074 rules[-1].operator |= rules[-1].END_OF_LIST
2077 rules_bin += r.serialize_body()
2079 self.length = len(rules_bin)
2081 if self.length < self._LENGTH_THRESHOLD:
2082 buf = struct.pack(self._LENGTH_SHORT_FMT, self.length)
2084 buf = struct.pack(self._LENGTH_LONG_FMT, self.length)
2086 return buf + rules_bin
2089 def _from_user(cls, **kwargs):
2091 for k, v in kwargs.items():
2092 subcls = _FlowSpecComponentBase.lookup_type_name(
2093 k, cls.ROUTE_FAMILY.afi)
2094 rule = subcls.from_str(str(v))
2096 rules.sort(key=lambda x: x.type)
2097 return cls(rules=rules)
2103 i.rules.sort(key=lambda x: x.type)
2104 previous_type = None
2106 if r.type == previous_type:
2107 if r.to_str()[0] != '&':
2109 pairs[-1] += r.to_str()
2111 pairs.append('%s:%s' % (r.COMPONENT_NAME, r.to_str()))
2112 previous_type = r.type
2114 return ','.join(pairs)
2116 return '%s(%s)' % (self.FLOWSPEC_FAMILY, _format(self))
2119 def formatted_nlri_str(self):
2123 class FlowSpecIPv4NLRI(_FlowSpecNLRIBase):
2125 Flow Specification NLRI class for IPv4 [RFC 5575]
2127 ROUTE_FAMILY = RF_IPv4_FLOWSPEC
2128 FLOWSPEC_FAMILY = 'ipv4fs'
2131 def from_user(cls, **kwargs):
2133 Utility method for creating a NLRI instance.
2135 This function returns a NLRI instance from human readable format value.
2137 :param kwargs: The following arguments are available.
2139 =========== ============= ========= ==============================
2140 Argument Value Operator Description
2141 =========== ============= ========= ==============================
2142 dst_prefix IPv4 Prefix Nothing Destination Prefix.
2143 src_prefix IPv4 Prefix Nothing Source Prefix.
2144 ip_proto Integer Numeric IP Protocol.
2145 port Integer Numeric Port number.
2146 dst_port Integer Numeric Destination port number.
2147 src_port Integer Numeric Source port number.
2148 icmp_type Integer Numeric ICMP type.
2149 icmp_code Integer Numeric ICMP code.
2150 tcp_flags Fixed string Bitmask TCP flags.
2151 Supported values are
2152 ``CWR``, ``ECN``, ``URGENT``,
2153 ``ACK``, ``PUSH``, ``RST``,
2154 ``SYN`` and ``FIN``.
2155 packet_len Integer Numeric Packet length.
2156 dscp Integer Numeric Differentiated Services
2158 fragment Fixed string Bitmask Fragment.
2159 Supported values are
2160 ``DF`` (Don't fragment),
2161 ``ISF`` (Is a fragment),
2162 ``FF`` (First fragment) and
2163 ``LF`` (Last fragment)
2164 =========== ============= ========= ==============================
2168 >>> msg = bgp.FlowSpecIPv4NLRI.from_user(
2169 ... dst_prefix='10.0.0.0/24',
2170 ... src_prefix='20.0.0.1/24',
2172 ... port='80 | 8000',
2173 ... dst_port='>9000 & <9050',
2174 ... src_port='>=8500 & <=9000',
2177 ... tcp_flags='SYN+ACK & !=URGENT',
2178 ... packet_len=1000,
2180 ... fragment='LF | ==FF')
2183 You can specify conditions with the following keywords.
2185 The following keywords can be used when the operator type is Numeric.
2187 ========== ============================================================
2189 ========== ============================================================
2190 < Less than comparison between data and value.
2191 <= Less than or equal to comparison between data and value.
2192 > Greater than comparison between data and value.
2193 >= Greater than or equal to comparison between data and value.
2194 == Equality between data and value.
2195 This operator can be omitted.
2196 ========== ============================================================
2198 The following keywords can be used when the operator type is Bitmask.
2200 ========== ================================================
2202 ========== ================================================
2203 != Not equal operation.
2204 == Exact match operation if specified.
2205 Otherwise partial match operation.
2206 `+` Used for the summation of bitmask values.
2208 ========== ================================================
2210 You can combine the multiple conditions with the following operators.
2212 ========== =======================================
2214 ========== =======================================
2215 `|` Logical OR operation
2216 & Logical AND operation
2217 ========== =======================================
2219 :return: A instance of FlowSpecVPNv4NLRI.
2221 return cls._from_user(**kwargs)
2224 class FlowSpecVPNv4NLRI(_FlowSpecNLRIBase):
2226 Flow Specification NLRI class for VPNv4 [RFC 5575]
2230 # +-----------------------------------+
2231 # | length (0xnn or 0xfn nn) |
2232 # +-----------------------------------+
2234 # +-----------------------------------+
2235 # | NLRI value (variable) |
2236 # +-----------------------------------+
2237 ROUTE_FAMILY = RF_VPNv4_FLOWSPEC
2238 FLOWSPEC_FAMILY = 'vpnv4fs'
2240 def __init__(self, length=0, route_dist=None, rules=None):
2241 super(FlowSpecVPNv4NLRI, self).__init__(length, rules)
2242 assert route_dist is not None
2243 self.route_dist = route_dist
2246 def _from_user(cls, route_dist, **kwargs):
2248 for k, v in kwargs.items():
2249 subcls = _FlowSpecComponentBase.lookup_type_name(
2250 k, cls.ROUTE_FAMILY.afi)
2251 rule = subcls.from_str(str(v))
2253 rules.sort(key=lambda x: x.type)
2254 return cls(route_dist=route_dist, rules=rules)
2257 def from_user(cls, route_dist, **kwargs):
2259 Utility method for creating a NLRI instance.
2261 This function returns a NLRI instance from human readable format value.
2263 :param route_dist: Route Distinguisher.
2264 :param kwargs: See :py:mod:`ryu.lib.packet.bgp.FlowSpecIPv4NLRI`
2268 >>> msg = bgp.FlowSpecIPv4NLRI.from_user(
2269 ... route_dist='65000:1000',
2270 ... dst_prefix='10.0.0.0/24',
2271 ... src_prefix='20.0.0.1/24',
2273 ... port='80 | 8000',
2274 ... dst_port='>9000 & <9050',
2275 ... src_port='>=8500 & <=9000',
2278 ... tcp_flags='SYN+ACK & !=URGENT',
2279 ... packet_len=1000,
2281 ... fragment='LF | ==FF')
2284 return cls._from_user(route_dist, **kwargs)
2287 def formatted_nlri_str(self):
2288 return '%s:%s' % (self.route_dist, self.prefix)
2291 class FlowSpecIPv6NLRI(_FlowSpecNLRIBase):
2293 Flow Specification NLRI class for IPv6 [RFC draft-ietf-idr-flow-spec-v6-08]
2295 ROUTE_FAMILY = RF_IPv6_FLOWSPEC
2296 FLOWSPEC_FAMILY = 'ipv6fs'
2299 def from_user(cls, **kwargs):
2301 Utility method for creating a NLRI instance.
2303 This function returns a NLRI instance from human readable format value.
2305 :param kwargs: The following arguments are available.
2307 =========== ============= ========= ==============================
2308 Argument Value Operator Description
2309 =========== ============= ========= ==============================
2310 dst_prefix IPv6 Prefix Nothing Destination Prefix.
2311 src_prefix IPv6 Prefix Nothing Source Prefix.
2312 next_header Integer Numeric Next Header.
2313 port Integer Numeric Port number.
2314 dst_port Integer Numeric Destination port number.
2315 src_port Integer Numeric Source port number.
2316 icmp_type Integer Numeric ICMP type.
2317 icmp_code Integer Numeric ICMP code.
2318 tcp_flags Fixed string Bitmask TCP flags.
2319 Supported values are
2320 ``CWR``, ``ECN``, ``URGENT``,
2321 ``ACK``, ``PUSH``, ``RST``,
2322 ``SYN`` and ``FIN``.
2323 packet_len Integer Numeric Packet length.
2324 dscp Integer Numeric Differentiated Services
2326 fragment Fixed string Bitmask Fragment.
2327 Supported values are
2328 ``ISF`` (Is a fragment),
2329 ``FF`` (First fragment) and
2330 ``LF`` (Last fragment)
2331 flow_label Intefer Numeric Flow Label.
2332 =========== ============= ========= ==============================
2336 For ``dst_prefix`` and ``src_prefix``, you can give "offset" value
2337 like this: ``2001::2/128/32``. At this case, ``offset`` is 32.
2338 ``offset`` can be omitted, then ``offset`` is treated as 0.
2340 return cls._from_user(**kwargs)
2343 class FlowSpecVPNv6NLRI(_FlowSpecNLRIBase):
2345 Flow Specification NLRI class for VPNv6 [draft-ietf-idr-flow-spec-v6-08]
2349 # +-----------------------------------+
2350 # | length (0xnn or 0xfn nn) |
2351 # +-----------------------------------+
2353 # +-----------------------------------+
2354 # | NLRI value (variable) |
2355 # +-----------------------------------+
2356 ROUTE_FAMILY = RF_VPNv6_FLOWSPEC
2357 FLOWSPEC_FAMILY = 'vpnv6fs'
2359 def __init__(self, length=0, route_dist=None, rules=None):
2360 super(FlowSpecVPNv6NLRI, self).__init__(length, rules)
2361 assert route_dist is not None
2362 self.route_dist = route_dist
2365 def _from_user(cls, route_dist, **kwargs):
2367 for k, v in kwargs.items():
2368 subcls = _FlowSpecComponentBase.lookup_type_name(
2369 k, cls.ROUTE_FAMILY.afi)
2370 rule = subcls.from_str(str(v))
2372 rules.sort(key=lambda x: x.type)
2373 return cls(route_dist=route_dist, rules=rules)
2376 def from_user(cls, route_dist, **kwargs):
2378 Utility method for creating a NLRI instance.
2380 This function returns a NLRI instance from human readable format value.
2382 :param route_dist: Route Distinguisher.
2383 :param kwargs: See :py:mod:`ryu.lib.packet.bgp.FlowSpecIPv6NLRI`
2385 return cls._from_user(route_dist, **kwargs)
2388 def formatted_nlri_str(self):
2389 return '%s:%s' % (self.route_dist, self.prefix)
2392 class FlowSpecL2VPNNLRI(_FlowSpecNLRIBase):
2394 Flow Specification NLRI class for L2VPN [draft-ietf-idr-flowspec-l2vpn-05]
2398 # +-----------------------------------+
2399 # | length (0xnn or 0xfn nn) |
2400 # +-----------------------------------+
2402 # +-----------------------------------+
2403 # | NLRI value (variable) |
2404 # +-----------------------------------+
2405 ROUTE_FAMILY = RF_L2VPN_FLOWSPEC
2406 FLOWSPEC_FAMILY = 'l2vpnfs'
2408 def __init__(self, length=0, route_dist=None, rules=None):
2409 super(FlowSpecL2VPNNLRI, self).__init__(length, rules)
2410 assert route_dist is not None
2411 self.route_dist = route_dist
2414 def _from_user(cls, route_dist, **kwargs):
2416 for k, v in kwargs.items():
2417 subcls = _FlowSpecComponentBase.lookup_type_name(
2418 k, cls.ROUTE_FAMILY.afi)
2419 rule = subcls.from_str(str(v))
2421 rules.sort(key=lambda x: x.type)
2422 return cls(route_dist=route_dist, rules=rules)
2425 def from_user(cls, route_dist, **kwargs):
2427 Utility method for creating a L2VPN NLRI instance.
2429 This function returns a L2VPN NLRI instance
2430 from human readable format value.
2432 :param kwargs: The following arguments are available.
2434 ============== ============= ========= ==============================
2435 Argument Value Operator Description
2436 ============== ============= ========= ==============================
2437 ether_type Integer Numeric Ethernet Type.
2438 src_mac Mac Address Nothing Source Mac address.
2439 dst_mac Mac Address Nothing Destination Mac address.
2440 llc_ssap Integer Numeric Source Service Access Point
2442 llc_dsap Integer Numeric Destination Service Access
2444 llc_control Integer Numeric Control field in LLC.
2445 snap Integer Numeric Sub-Network Access Protocol
2447 vlan_id Integer Numeric VLAN ID.
2448 vlan_cos Integer Numeric VLAN COS field.
2449 inner_vlan_id Integer Numeric Inner VLAN ID.
2450 inner_vlan_cos Integer Numeric Inner VLAN COS field.
2451 ============== ============= ========= ==============================
2453 return cls._from_user(route_dist, **kwargs)
2456 def formatted_nlri_str(self):
2457 return '%s:%s' % (self.route_dist, self.prefix)
2460 class _FlowSpecComponentBase(StringifyMixin, TypeDisp):
2462 Base class for Flow Specification NLRI component
2464 COMPONENT_NAME = None
2467 _BASE_STR_SIZE = struct.calcsize(_BASE_STR)
2469 # Dictionary of COMPONENT_NAME to subclass.
2471 # _NAMES = {'dst_prefix': FlowSpecDestPrefix, ...}
2474 def __init__(self, type_=None):
2476 type_, _ = self._rev_lookup_type(self.__class__)
2480 def register_type(cls, type_, afi):
2481 cls._TYPES = cls._TYPES.copy()
2482 cls._NAMES = cls._NAMES.copy()
2484 def _register_type(subcls):
2485 cls._TYPES[(type_, afi)] = subcls
2486 cls._NAMES[(subcls.COMPONENT_NAME, afi)] = subcls
2487 cls._REV_TYPES = None
2490 return _register_type
2493 def lookup_type_name(cls, type_name, afi):
2494 return cls._NAMES[(type_name, afi)]
2497 def _lookup_type(cls, type_, afi):
2499 return cls._TYPES[(type_, afi)]
2501 return cls._UNKNOWN_TYPE
2504 def parse_header(cls, rest, afi):
2505 (type_,) = struct.unpack_from(
2506 cls._BASE_STR, six.binary_type(rest))
2507 rest = rest[cls._BASE_STR_SIZE:]
2508 return cls._lookup_type(type_, afi), rest
2510 def serialize_header(self):
2511 return struct.pack(self._BASE_STR, self.type)
2514 class _FlowSpecIPv4Component(_FlowSpecComponentBase):
2516 Base class for Flow Specification for IPv4 NLRI component
2518 TYPE_DESTINATION_PREFIX = 0x01
2519 TYPE_SOURCE_PREFIX = 0x02
2520 TYPE_PROTOCOL = 0x03
2522 TYPE_DESTINATION_PORT = 0x05
2523 TYPE_SOURCE_PORT = 0x06
2525 TYPE_ICMP_CODE = 0x08
2526 TYPE_TCP_FLAGS = 0x09
2527 TYPE_PACKET_LENGTH = 0x0a
2528 TYPE_DIFFSERV_CODE_POINT = 0x0b
2529 TYPE_FRAGMENT = 0x0c
2532 class _FlowSpecIPv6Component(_FlowSpecComponentBase):
2534 Base class for Flow Specification for IPv6 NLRI component
2536 TYPE_DESTINATION_PREFIX = 0x01
2537 TYPE_SOURCE_PREFIX = 0x02
2538 TYPE_NEXT_HEADER = 0x03
2540 TYPE_DESTINATION_PORT = 0x05
2541 TYPE_SOURCE_PORT = 0x06
2543 TYPE_ICMP_CODE = 0x08
2544 TYPE_TCP_FLAGS = 0x09
2545 TYPE_PACKET_LENGTH = 0x0a
2546 TYPE_DIFFSERV_CODE_POINT = 0x0b
2547 TYPE_FRAGMENT = 0x0c
2548 TYPE_FLOW_LABEL = 0x0d
2551 class _FlowSpecL2VPNComponent(_FlowSpecComponentBase):
2553 Base class for Flow Specification for L2VPN NLRI component
2555 TYPE_ETHER_TYPE = 0x0e
2556 TYPE_SOURCE_MAC = 0x0f
2557 TYPE_DESTINATION_MAC = 0x10
2558 TYPE_LLC_DSAP = 0x11
2559 TYPE_LLC_SSAP = 0x12
2560 TYPE_LLC_CONTROL = 0x13
2563 TYPE_VLAN_COS = 0x16
2564 TYPE_INNER_VLAN_ID = 0x17
2565 TYPE_INNER_VLAN_COS = 0x18
2568 @_FlowSpecComponentBase.register_unknown_type()
2569 class FlowSpecComponentUnknown(_FlowSpecComponentBase):
2571 Unknown component type for Flow Specification NLRI component
2574 def __init__(self, buf, type_=None):
2575 super(FlowSpecComponentUnknown, self).__init__(type_)
2579 def parse_body(cls, buf):
2580 return cls(buf), None
2582 def serialize_body(self):
2586 class _FlowSpecPrefixBase(_FlowSpecIPv4Component, IPAddrPrefix):
2588 Prefix base class for Flow Specification NLRI component
2591 def __init__(self, length, addr, type_=None):
2592 super(_FlowSpecPrefixBase, self).__init__(type_)
2593 self.length = length
2594 prefix = "%s/%s" % (addr, length)
2595 self.addr = str(netaddr.ip.IPNetwork(prefix).network)
2598 def parse_body(cls, buf):
2599 return cls.parser(buf)
2601 def serialize_body(self):
2602 return self.serialize()
2605 def from_str(cls, value):
2607 addr, length = value.split('/')
2608 rule.append(cls(int(length), addr))
2613 return "%s/%s" % (self.addr, self.length)
2619 class _FlowSpecIPv6PrefixBase(_FlowSpecIPv6Component, IP6AddrPrefix):
2621 Prefix base class for Flow Specification NLRI component
2623 _PACK_STR = '!BB' # length, offset
2625 def __init__(self, length, addr, offset=0, type_=None):
2626 super(_FlowSpecIPv6PrefixBase, self).__init__(type_)
2627 self.length = length
2628 self.offset = offset
2629 prefix = "%s/%s" % (addr, length)
2630 self.addr = str(netaddr.ip.IPNetwork(prefix).network)
2633 def parser(cls, buf):
2634 (length, offset) = struct.unpack_from(
2635 cls._PACK_STR, six.binary_type(buf))
2636 rest = buf[struct.calcsize(cls._PACK_STR):]
2637 byte_length = (length + 7) // 8
2638 addr = cls._from_bin(rest[:byte_length])
2639 rest = rest[byte_length:]
2640 return cls(length=length, offset=offset, addr=addr), rest
2643 def parse_body(cls, buf):
2644 return cls.parser(buf)
2646 def serialize(self):
2647 byte_length = (self.length + 7) // 8
2648 bin_addr = self._to_bin(self.addr)[:byte_length]
2650 msg_pack_into(self._PACK_STR, buf, 0, self.length, self.offset)
2651 return buf + bin_addr
2653 def serialize_body(self):
2654 return self.serialize()
2657 def from_str(cls, value):
2659 values = value.split('/')
2660 if len(values) == 3:
2661 rule.append(cls(int(values[1]), values[0], offset=int(values[2])))
2663 rule.append(cls(int(values[1]), values[0]))
2668 return "%s/%s/%s" % (self.addr, self.length, self.offset)
2674 class _FlowSpecL2VPNPrefixBase(_FlowSpecL2VPNComponent):
2676 Prefix base class for Flow Specification NLRI component
2680 def __init__(self, length, addr, type_=None):
2681 super(_FlowSpecL2VPNPrefixBase, self).__init__(type_)
2682 self.length = length
2683 self.addr = addr.lower()
2686 def parse_body(cls, buf):
2687 (length, addr) = struct.unpack_from(
2688 cls._PACK_STR, six.binary_type(buf))
2689 rest = buf[struct.calcsize(cls._PACK_STR):]
2690 addr = addrconv.mac.bin_to_text(addr)
2691 return cls(length=length, addr=addr), rest
2693 def serialize(self):
2694 addr = addrconv.mac.text_to_bin(self.addr)
2695 return struct.pack(self._PACK_STR, self.length, addr)
2697 def serialize_body(self):
2698 return self.serialize()
2701 def from_str(cls, value):
2702 return [cls(len(value.split(':')), value)]
2712 @_FlowSpecComponentBase.register_type(
2713 _FlowSpecIPv4Component.TYPE_DESTINATION_PREFIX, addr_family.IP)
2714 class FlowSpecDestPrefix(_FlowSpecPrefixBase):
2716 Destination Prefix for Flow Specification NLRI component
2718 COMPONENT_NAME = 'dst_prefix'
2721 @_FlowSpecComponentBase.register_type(
2722 _FlowSpecIPv4Component.TYPE_SOURCE_PREFIX, addr_family.IP)
2723 class FlowSpecSrcPrefix(_FlowSpecPrefixBase):
2725 Source Prefix for Flow Specification NLRI component
2727 COMPONENT_NAME = 'src_prefix'
2730 @_FlowSpecComponentBase.register_type(
2731 _FlowSpecIPv6Component.TYPE_DESTINATION_PREFIX, addr_family.IP6)
2732 class FlowSpecIPv6DestPrefix(_FlowSpecIPv6PrefixBase):
2734 IPv6 destination Prefix for Flow Specification NLRI component
2736 COMPONENT_NAME = 'dst_prefix'
2739 @_FlowSpecComponentBase.register_type(
2740 _FlowSpecIPv6Component.TYPE_SOURCE_PREFIX, addr_family.IP6)
2741 class FlowSpecIPv6SrcPrefix(_FlowSpecIPv6PrefixBase):
2743 IPv6 source Prefix for Flow Specification NLRI component
2745 COMPONENT_NAME = 'src_prefix'
2748 class _FlowSpecOperatorBase(_FlowSpecComponentBase):
2749 """Operator base class for Flow Specification NLRI component
2751 ===================== ===============================================
2752 Attribute Description
2753 ===================== ===============================================
2754 operator Match conditions.
2755 value Value of component.
2756 ===================== ===============================================
2758 _OPE_PACK_STR = '!B'
2759 _OPE_PACK_STR_SIZE = struct.calcsize(_OPE_PACK_STR)
2760 _VAL_PACK_STR = '!%ds'
2762 END_OF_LIST = 1 << 7 # END OF LIST bit
2763 AND = 1 << 6 # AND bit
2765 _LENGTH_BIT_MASK = 0x30 # The mask for length of the value
2767 _logical_conditions = {
2771 _comparison_conditions = {}
2773 def __init__(self, operator, value, type_=None):
2774 super(_FlowSpecOperatorBase, self).__init__(type_)
2775 self.operator = operator
2779 def parse_body(cls, rest):
2780 (operator,) = struct.unpack_from(cls._OPE_PACK_STR,
2781 six.binary_type(rest))
2782 rest = rest[cls._OPE_PACK_STR_SIZE:]
2783 length = 1 << ((operator & cls._LENGTH_BIT_MASK) >> 4)
2784 value_type = type_desc.IntDescr(length)
2785 value = value_type.to_user(rest)
2786 rest = rest[length:]
2788 return cls(operator, value), rest
2790 def serialize_body(self):
2791 byte_length = (self.value.bit_length() + 7) // 8 or 1
2792 length = int(math.ceil(math.log(byte_length, 2)))
2793 self.operator |= length << 4
2794 buf = struct.pack(self._OPE_PACK_STR, self.operator)
2795 value_type = type_desc.IntDescr(1 << length)
2796 buf += struct.pack(self._VAL_PACK_STR % (1 << length),
2797 value_type.from_user(self.value))
2802 def from_str(cls, val):
2807 # value = '80 | ==90|>=8000&<=9000 | <100 & >110'
2808 # elements = ['80', '|', '==', '90', '|', '>=', '8000', '&',
2809 # '<=', '9000', '|', '<', '100', '&', '>', '110']
2810 elements = [v.strip() for v in re.split(
2811 r'([0-9]+)|([A-Z]+)|(\|&\+)|([!=<>]+)', val) if v and v.strip()]
2813 elms_iter = iter(elements)
2815 for elm in elms_iter:
2816 if elm in cls._logical_conditions:
2818 operator |= cls._logical_conditions[elm]
2820 elif elm in cls._comparison_conditions:
2821 # ['=', '<', '>', '<=', '>=' ] or ['=', '!=']
2822 operator |= cls._comparison_conditions[elm]
2825 # If keyword "+" is used, add the value to the previous rule.
2826 # e.g.) 'SYN+ACK' or '!=SYN+ACK'
2827 rules[-1].value |= cls._to_value(next(elms_iter))
2830 value = cls._to_value(elm)
2832 operator = cls.normalize_operator(operator)
2834 rules.append(cls(operator, value))
2840 def _to_value(cls, value):
2844 def normalize_operator(cls, operator):
2848 class _FlowSpecNumeric(_FlowSpecOperatorBase):
2850 Numeric operator class for Flow Specification NLRI component
2852 # Numeric operator format
2854 # +---+---+---+---+---+---+---+---+
2855 # | e | a | len | 0 |lt |gt |eq |
2856 # +---+---+---+---+---+---+---+---+
2858 LT = 1 << 2 # Less than comparison bit
2859 GT = 1 << 1 # Greater than comparison bit
2860 EQ = 1 << 0 # Equality bit
2862 _comparison_conditions = {
2871 def _to_value(cls, value):
2873 return int(str(value), 0)
2875 raise ValueError('Invalid params: %s="%s"' % (
2876 cls.COMPONENT_NAME, value))
2880 if self.operator & self.AND:
2883 operator = self.operator & (self.LT | self.GT | self.EQ)
2884 for k, v in self._comparison_conditions.items():
2888 string += str(self.value)
2893 def normalize_operator(cls, operator):
2894 if operator & (cls.LT | cls.GT | cls.EQ):
2897 return operator | cls.EQ
2900 class _FlowSpecBitmask(_FlowSpecOperatorBase):
2902 Bitmask operator class for Flow Specification NLRI component
2904 # Bitmask operator format
2906 # +---+---+---+---+---+---+---+---+
2907 # | e | a | len | 0 | 0 |not| m |
2908 # +---+---+---+---+---+---+---+---+
2910 NOT = 1 << 1 # NOT bit
2911 MATCH = 1 << 0 # MATCH bit
2913 _comparison_conditions = {
2921 def _to_value(cls, value):
2923 return cls.__dict__[value]
2925 raise ValueError('Invalid params: %s="%s"' % (
2926 cls.COMPONENT_NAME, value))
2930 if self.operator & self.AND:
2933 operator = self.operator & (self.NOT | self.MATCH)
2934 for k, v in self._comparison_conditions.items():
2939 for k, v in self._bitmask_flags.items():
2947 @_FlowSpecComponentBase.register_type(
2948 _FlowSpecIPv4Component.TYPE_PROTOCOL, addr_family.IP)
2949 class FlowSpecIPProtocol(_FlowSpecNumeric):
2950 """IP Protocol for Flow Specification NLRI component
2952 Set the IP protocol number at value.
2954 COMPONENT_NAME = 'ip_proto'
2957 @_FlowSpecComponentBase.register_type(
2958 _FlowSpecIPv6Component.TYPE_NEXT_HEADER, addr_family.IP6)
2959 class FlowSpecNextHeader(_FlowSpecNumeric):
2960 """Next Header value in IPv6 packets
2962 Set the IP protocol number at value
2964 COMPONENT_NAME = 'next_header'
2967 @_FlowSpecComponentBase.register_type(
2968 _FlowSpecIPv4Component.TYPE_PORT, addr_family.IP)
2969 @_FlowSpecComponentBase.register_type(
2970 _FlowSpecIPv6Component.TYPE_PORT, addr_family.IP6)
2971 class FlowSpecPort(_FlowSpecNumeric):
2972 """Port number for Flow Specification NLRI component
2974 Set the source or destination TCP/UDP ports at value.
2976 COMPONENT_NAME = 'port'
2979 @_FlowSpecComponentBase.register_type(
2980 _FlowSpecIPv4Component.TYPE_DESTINATION_PORT, addr_family.IP)
2981 @_FlowSpecComponentBase.register_type(
2982 _FlowSpecIPv6Component.TYPE_DESTINATION_PORT, addr_family.IP6)
2983 class FlowSpecDestPort(_FlowSpecNumeric):
2984 """Destination port number for Flow Specification NLRI component
2986 Set the destination port of a TCP or UDP packet at value.
2988 COMPONENT_NAME = 'dst_port'
2991 @_FlowSpecComponentBase.register_type(
2992 _FlowSpecIPv4Component.TYPE_SOURCE_PORT, addr_family.IP)
2993 @_FlowSpecComponentBase.register_type(
2994 _FlowSpecIPv6Component.TYPE_SOURCE_PORT, addr_family.IP6)
2995 class FlowSpecSrcPort(_FlowSpecNumeric):
2996 """Source port number for Flow Specification NLRI component
2998 Set the source port of a TCP or UDP packet at value.
3000 COMPONENT_NAME = 'src_port'
3003 @_FlowSpecComponentBase.register_type(
3004 _FlowSpecIPv4Component.TYPE_ICMP, addr_family.IP)
3005 @_FlowSpecComponentBase.register_type(
3006 _FlowSpecIPv6Component.TYPE_ICMP, addr_family.IP6)
3007 class FlowSpecIcmpType(_FlowSpecNumeric):
3008 """ICMP type for Flow Specification NLRI component
3010 Set the type field of an ICMP packet at value.
3012 COMPONENT_NAME = 'icmp_type'
3015 @_FlowSpecComponentBase.register_type(
3016 _FlowSpecIPv4Component.TYPE_ICMP_CODE, addr_family.IP)
3017 @_FlowSpecComponentBase.register_type(
3018 _FlowSpecIPv6Component.TYPE_ICMP_CODE, addr_family.IP6)
3019 class FlowSpecIcmpCode(_FlowSpecNumeric):
3020 """ICMP code Flow Specification NLRI component
3022 Set the code field of an ICMP packet at value.
3024 COMPONENT_NAME = 'icmp_code'
3027 @_FlowSpecComponentBase.register_type(
3028 _FlowSpecIPv4Component.TYPE_TCP_FLAGS, addr_family.IP)
3029 @_FlowSpecComponentBase.register_type(
3030 _FlowSpecIPv6Component.TYPE_TCP_FLAGS, addr_family.IP6)
3031 class FlowSpecTCPFlags(_FlowSpecBitmask):
3032 """TCP flags for Flow Specification NLRI component
3034 Supported TCP flags are CWR, ECN, URGENT, ACK, PUSH, RST, SYN and FIN.
3036 COMPONENT_NAME = 'tcp_flags'
3040 # +----+----+----+----+----+----+----+----+
3041 # |CWR |ECN |URG |ACK |PSH |RST |SYN |FIN |
3042 # +----+----+----+----+----+----+----+----+
3053 _bitmask_flags = collections.OrderedDict()
3054 _bitmask_flags[SYN] = 'SYN'
3055 _bitmask_flags[ACK] = 'ACK'
3056 _bitmask_flags[FIN] = 'FIN'
3057 _bitmask_flags[RST] = 'RST'
3058 _bitmask_flags[PUSH] = 'PUSH'
3059 _bitmask_flags[URGENT] = 'URGENT'
3060 _bitmask_flags[ECN] = 'ECN'
3061 _bitmask_flags[CWR] = 'CWR'
3064 @_FlowSpecComponentBase.register_type(
3065 _FlowSpecIPv4Component.TYPE_PACKET_LENGTH, addr_family.IP)
3066 @_FlowSpecComponentBase.register_type(
3067 _FlowSpecIPv6Component.TYPE_PACKET_LENGTH, addr_family.IP6)
3068 class FlowSpecPacketLen(_FlowSpecNumeric):
3069 """Packet length for Flow Specification NLRI component
3071 Set the total IP packet length at value.
3073 COMPONENT_NAME = 'packet_len'
3076 @_FlowSpecComponentBase.register_type(
3077 _FlowSpecIPv4Component.TYPE_DIFFSERV_CODE_POINT, addr_family.IP)
3078 @_FlowSpecComponentBase.register_type(
3079 _FlowSpecIPv6Component.TYPE_DIFFSERV_CODE_POINT, addr_family.IP6)
3080 class FlowSpecDSCP(_FlowSpecNumeric):
3081 """Diffserv Code Point for Flow Specification NLRI component
3083 Set the 6-bit DSCP field at value. [RFC2474]
3085 COMPONENT_NAME = 'dscp'
3088 @_FlowSpecComponentBase.register_type(
3089 _FlowSpecIPv4Component.TYPE_FRAGMENT, addr_family.IP)
3090 class FlowSpecFragment(_FlowSpecBitmask):
3091 """Fragment for Flow Specification NLRI component
3093 Set the bitmask for operand format at value.
3094 The following values are supported.
3096 ========== ===============================================
3097 Attribute Description
3098 ========== ===============================================
3103 ========== ===============================================
3105 COMPONENT_NAME = 'fragment'
3109 # +---+---+---+---+---+---+---+---+
3110 # | Reserved |LF |FF |IsF|DF |
3111 # +---+---+---+---+---+---+---+---+
3118 _bitmask_flags = collections.OrderedDict()
3119 _bitmask_flags[LF] = 'LF'
3120 _bitmask_flags[FF] = 'FF'
3121 _bitmask_flags[ISF] = 'ISF'
3122 _bitmask_flags[DF] = 'DF'
3125 @_FlowSpecComponentBase.register_type(
3126 _FlowSpecIPv6Component.TYPE_FRAGMENT, addr_family.IP6)
3127 class FlowSpecIPv6Fragment(_FlowSpecBitmask):
3128 """Fragment for Flow Specification for IPv6 NLRI component
3130 ========== ===============================================
3131 Attribute Description
3132 ========== ===============================================
3136 ========== ===============================================
3138 COMPONENT_NAME = 'fragment'
3142 # +---+---+---+---+---+---+---+---+
3143 # | Reserved |LF |FF |IsF| 0 |
3144 # +---+---+---+---+---+---+---+---+
3150 _bitmask_flags = collections.OrderedDict()
3151 _bitmask_flags[LF] = 'LF'
3152 _bitmask_flags[FF] = 'FF'
3153 _bitmask_flags[ISF] = 'ISF'
3156 @_FlowSpecComponentBase.register_type(
3157 _FlowSpecL2VPNComponent.TYPE_ETHER_TYPE, addr_family.L2VPN)
3158 class FlowSpecEtherType(_FlowSpecNumeric):
3159 """Ethernet Type field in an Ethernet frame.
3161 Set the 2 byte value of an Ethernet Type field at value.
3163 COMPONENT_NAME = 'ether_type'
3166 @_FlowSpecComponentBase.register_type(
3167 _FlowSpecL2VPNComponent.TYPE_SOURCE_MAC, addr_family.L2VPN)
3168 class FlowSpecSourceMac(_FlowSpecL2VPNPrefixBase):
3169 """Source Mac Address.
3171 Set the Mac Address at value.
3173 COMPONENT_NAME = 'src_mac'
3176 @_FlowSpecComponentBase.register_type(
3177 _FlowSpecL2VPNComponent.TYPE_DESTINATION_MAC, addr_family.L2VPN)
3178 class FlowSpecDestinationMac(_FlowSpecL2VPNPrefixBase):
3179 """Destination Mac Address.
3181 Set the Mac Address at value.
3183 COMPONENT_NAME = 'dst_mac'
3186 @_FlowSpecComponentBase.register_type(
3187 _FlowSpecL2VPNComponent.TYPE_LLC_DSAP, addr_family.L2VPN)
3188 class FlowSpecLLCDSAP(_FlowSpecNumeric):
3189 """Destination SAP field in LLC header in an Ethernet frame.
3191 Set the 2 byte value of an Destination SAP at value.
3193 COMPONENT_NAME = 'llc_dsap'
3196 @_FlowSpecComponentBase.register_type(
3197 _FlowSpecL2VPNComponent.TYPE_LLC_SSAP, addr_family.L2VPN)
3198 class FlowSpecLLCSSAP(_FlowSpecNumeric):
3199 """Source SAP field in LLC header in an Ethernet frame.
3201 Set the 2 byte value of an Source SAP at value.
3203 COMPONENT_NAME = 'llc_ssap'
3206 @_FlowSpecComponentBase.register_type(
3207 _FlowSpecL2VPNComponent.TYPE_LLC_CONTROL, addr_family.L2VPN)
3208 class FlowSpecLLCControl(_FlowSpecNumeric):
3209 """Control field in LLC header in an Ethernet frame.
3211 Set the Contorol field at value.
3213 COMPONENT_NAME = 'llc_control'
3216 @_FlowSpecComponentBase.register_type(
3217 _FlowSpecL2VPNComponent.TYPE_SNAP, addr_family.L2VPN)
3218 class FlowSpecSNAP(_FlowSpecNumeric):
3219 """Sub-Network Access Protocol field in an Ethernet frame.
3221 Set the 5 byte SNAP field at value.
3223 COMPONENT_NAME = 'snap'
3226 @_FlowSpecComponentBase.register_type(
3227 _FlowSpecL2VPNComponent.TYPE_VLAN_ID, addr_family.L2VPN)
3228 class FlowSpecVLANID(_FlowSpecNumeric):
3231 Set VLAN ID at value.
3233 COMPONENT_NAME = 'vlan_id'
3236 @_FlowSpecComponentBase.register_type(
3237 _FlowSpecL2VPNComponent.TYPE_VLAN_COS, addr_family.L2VPN)
3238 class FlowSpecVLANCoS(_FlowSpecNumeric):
3239 """VLAN CoS Fields in an Ethernet frame.
3241 Set the 3 bit CoS field at value.
3243 COMPONENT_NAME = 'vlan_cos'
3246 @_FlowSpecComponentBase.register_type(
3247 _FlowSpecL2VPNComponent.TYPE_INNER_VLAN_ID, addr_family.L2VPN)
3248 class FlowSpecInnerVLANID(_FlowSpecNumeric):
3251 Set VLAN ID at value.
3253 COMPONENT_NAME = 'inner_vlan_id'
3256 @_FlowSpecComponentBase.register_type(
3257 _FlowSpecL2VPNComponent.TYPE_INNER_VLAN_COS, addr_family.L2VPN)
3258 class FlowSpecInnerVLANCoS(_FlowSpecNumeric):
3259 """VLAN CoS Fields in an Inner Ethernet frame.
3261 Set the 3 bit CoS field at value..
3263 COMPONENT_NAME = 'inner_vlan_cos'
3266 @_FlowSpecComponentBase.register_type(
3267 _FlowSpecIPv6Component.TYPE_FLOW_LABEL, addr_family.IP6)
3268 class FlowSpecIPv6FlowLabel(_FlowSpecNumeric):
3269 COMPONENT_NAME = 'flow_label'
3272 @functools.total_ordering
3273 class RouteTargetMembershipNLRI(StringifyMixin):
3274 """Route Target Membership NLRI.
3276 Route Target membership NLRI is advertised in BGP UPDATE messages using
3277 the MP_REACH_NLRI and MP_UNREACH_NLRI attributes.
3280 ROUTE_FAMILY = RF_RTC_UC
3284 def __init__(self, origin_as, route_target):
3285 # If given is not default_as and default_rt
3286 if not (origin_as is self.DEFAULT_AS and
3287 route_target is self.DEFAULT_RT):
3289 if (not self._is_valid_asn(origin_as) or
3290 not self._is_valid_ext_comm_attr(route_target)):
3291 raise ValueError('Invalid params.')
3292 self.origin_as = origin_as
3293 self.route_target = route_target
3295 def _is_valid_asn(self, asn):
3296 """Returns True if the given AS number is Two or Four Octet."""
3297 if isinstance(asn, six.integer_types) and 0 <= asn <= 0xffffffff:
3302 def _is_valid_ext_comm_attr(self, attr):
3303 """Validates *attr* as string representation of RT or SOO.
3305 Returns True if *attr* is as per our convention of RT or SOO, else
3306 False. Our convention is to represent RT/SOO is a string with format:
3307 *global_admin_part:local_admin_path*
3311 if not isinstance(attr, str):
3314 first, second = attr.split(':')
3317 socket.inet_aton(first)
3321 except (ValueError, socket.error):
3327 def formatted_nlri_str(self):
3328 return "%s:%s" % (self.origin_as, self.route_target)
3330 def is_default_rtnlri(self):
3331 if (self._origin_as is self.DEFAULT_AS and
3332 self._route_target is self.DEFAULT_RT):
3336 def __lt__(self, other):
3337 return ((self.origin_as, self.route_target) <
3338 (other.origin_as, other.route_target))
3340 def __eq__(self, other):
3341 return ((self.origin_as, self.route_target) ==
3342 (other.origin_as, other.route_target))
3345 return hash((self.origin_as, self.route_target))
3348 def parser(cls, buf):
3351 # Extract origin AS.
3352 origin_as, = struct.unpack_from('!I', buf, idx)
3355 # Extract route target.
3356 route_target = _ExtendedCommunity(buf[idx:])
3357 return cls(origin_as, route_target)
3359 def serialize(self):
3361 if not self.is_default_rtnlri():
3362 rt_nlri += struct.pack('!I', self.origin_as)
3363 # Encode route target
3364 rt_nlri += self.route_target.serialize()
3366 # RT Nlri is 12 octets
3367 return struct.pack('B', (8 * 12)) + rt_nlri
3370 def _addr_class_key(route_family):
3371 return route_family.afi, route_family.safi
3375 _addr_class_key(RF_IPv4_UC): IPAddrPrefix,
3376 _addr_class_key(RF_IPv6_UC): IP6AddrPrefix,
3377 _addr_class_key(RF_IPv4_MPLS): LabelledIPAddrPrefix,
3378 _addr_class_key(RF_IPv6_MPLS): LabelledIP6AddrPrefix,
3379 _addr_class_key(RF_IPv4_VPN): LabelledVPNIPAddrPrefix,
3380 _addr_class_key(RF_IPv6_VPN): LabelledVPNIP6AddrPrefix,
3381 _addr_class_key(RF_L2_EVPN): EvpnNLRI,
3382 _addr_class_key(RF_IPv4_FLOWSPEC): FlowSpecIPv4NLRI,
3383 _addr_class_key(RF_IPv6_FLOWSPEC): FlowSpecIPv6NLRI,
3384 _addr_class_key(RF_VPNv4_FLOWSPEC): FlowSpecVPNv4NLRI,
3385 _addr_class_key(RF_VPNv6_FLOWSPEC): FlowSpecVPNv6NLRI,
3386 _addr_class_key(RF_L2VPN_FLOWSPEC): FlowSpecL2VPNNLRI,
3387 _addr_class_key(RF_RTC_UC): RouteTargetMembershipNLRI,
3391 def _get_addr_class(afi, safi):
3393 return _ADDR_CLASSES[(afi, safi)]
3395 return _BinAddrPrefix
3398 class _OptParam(StringifyMixin, TypeDisp, _Value):
3399 _PACK_STR = '!BB' # type, length
3401 def __init__(self, type_, value=None, length=None):
3403 type_ = self._rev_lookup_type(self.__class__)
3405 self.length = length
3406 if value is not None:
3410 def parser(cls, buf):
3411 (type_, length) = struct.unpack_from(cls._PACK_STR,
3412 six.binary_type(buf))
3413 rest = buf[struct.calcsize(cls._PACK_STR):]
3414 value = bytes(rest[:length])
3415 rest = rest[length:]
3416 subcls = cls._lookup_type(type_)
3417 caps = subcls.parse_value(value)
3418 if not isinstance(caps, list):
3419 caps = [subcls(type_=type_, length=length, **caps[0])]
3422 def serialize(self):
3424 value = self.serialize_value()
3425 self.length = len(value)
3428 msg_pack_into(self._PACK_STR, buf, 0, self.type, self.length)
3432 @_OptParam.register_unknown_type()
3433 class BGPOptParamUnknown(_OptParam):
3435 def parse_value(cls, buf):
3440 def serialize_value(self):
3444 @_OptParam.register_type(BGP_OPT_CAPABILITY)
3445 class _OptParamCapability(_OptParam, TypeDisp):
3446 _CAP_HDR_PACK_STR = '!BB'
3448 def __init__(self, cap_code=None, cap_value=None, cap_length=None,
3449 type_=None, length=None):
3450 super(_OptParamCapability, self).__init__(type_=BGP_OPT_CAPABILITY,
3452 if cap_code is None:
3453 cap_code = self._rev_lookup_type(self.__class__)
3454 self.cap_code = cap_code
3455 if cap_value is not None:
3456 self.cap_value = cap_value
3457 if cap_length is not None:
3458 self.cap_length = cap_length
3461 def parse_value(cls, buf):
3464 (code, length) = struct.unpack_from(cls._CAP_HDR_PACK_STR,
3465 six.binary_type(buf))
3466 value = buf[struct.calcsize(cls._CAP_HDR_PACK_STR):]
3467 buf = buf[length + 2:]
3470 'cap_length': length,
3472 subcls = cls._lookup_type(code)
3473 kwargs.update(subcls.parse_cap_value(value))
3474 caps.append(subcls(type_=BGP_OPT_CAPABILITY, length=length + 2,
3478 def serialize_value(self):
3480 cap_value = self.serialize_cap_value()
3481 self.cap_length = len(cap_value)
3484 msg_pack_into(self._CAP_HDR_PACK_STR, buf, 0, self.cap_code,
3486 return buf + cap_value
3489 class _OptParamEmptyCapability(_OptParamCapability):
3491 def parse_cap_value(cls, buf):
3494 def serialize_cap_value(self):
3498 @_OptParamCapability.register_unknown_type()
3499 class BGPOptParamCapabilityUnknown(_OptParamCapability):
3501 def parse_cap_value(cls, buf):
3502 return {'cap_value': buf}
3504 def serialize_cap_value(self):
3505 return self.cap_value
3508 @_OptParamCapability.register_type(BGP_CAP_ROUTE_REFRESH)
3509 class BGPOptParamCapabilityRouteRefresh(_OptParamEmptyCapability):
3513 @_OptParamCapability.register_type(BGP_CAP_ROUTE_REFRESH_CISCO)
3514 class BGPOptParamCapabilityCiscoRouteRefresh(_OptParamEmptyCapability):
3518 @_OptParamCapability.register_type(BGP_CAP_ENHANCED_ROUTE_REFRESH)
3519 class BGPOptParamCapabilityEnhancedRouteRefresh(_OptParamEmptyCapability):
3523 @_OptParamCapability.register_type(BGP_CAP_GRACEFUL_RESTART)
3524 class BGPOptParamCapabilityGracefulRestart(_OptParamCapability):
3525 _CAP_PACK_STR = "!H"
3527 def __init__(self, flags, time, tuples, **kwargs):
3528 super(BGPOptParamCapabilityGracefulRestart, self).__init__(**kwargs)
3531 self.tuples = tuples
3534 def parse_cap_value(cls, buf):
3535 (restart, ) = struct.unpack_from(cls._CAP_PACK_STR,
3536 six.binary_type(buf))
3539 while len(buf) >= 4:
3540 l.append(struct.unpack_from("!HBB", buf))
3542 return {'flags': restart >> 12, 'time': restart & 0xfff, 'tuples': l}
3544 def serialize_cap_value(self):
3546 msg_pack_into(self._CAP_PACK_STR, buf, 0, self.flags << 12 | self.time)
3548 for i in self.tuples:
3549 afi, safi, flags = i
3550 msg_pack_into("!HBB", buf, offset, afi, safi, flags)
3555 @_OptParamCapability.register_type(BGP_CAP_FOUR_OCTET_AS_NUMBER)
3556 class BGPOptParamCapabilityFourOctetAsNumber(_OptParamCapability):
3557 _CAP_PACK_STR = '!I'
3559 def __init__(self, as_number, **kwargs):
3560 super(BGPOptParamCapabilityFourOctetAsNumber, self).__init__(**kwargs)
3561 self.as_number = as_number
3564 def parse_cap_value(cls, buf):
3565 (as_number, ) = struct.unpack_from(cls._CAP_PACK_STR,
3566 six.binary_type(buf))
3567 return {'as_number': as_number}
3569 def serialize_cap_value(self):
3571 msg_pack_into(self._CAP_PACK_STR, buf, 0, self.as_number)
3575 @_OptParamCapability.register_type(BGP_CAP_MULTIPROTOCOL)
3576 class BGPOptParamCapabilityMultiprotocol(_OptParamCapability):
3577 _CAP_PACK_STR = '!HBB' # afi, reserved, safi
3579 def __init__(self, afi, safi, reserved=0, **kwargs):
3580 super(BGPOptParamCapabilityMultiprotocol, self).__init__(**kwargs)
3582 self.reserved = reserved
3586 def parse_cap_value(cls, buf):
3587 (afi, reserved, safi,) = struct.unpack_from(cls._CAP_PACK_STR,
3588 six.binary_type(buf))
3591 'reserved': reserved,
3595 def serialize_cap_value(self):
3600 msg_pack_into(self._CAP_PACK_STR, buf, 0,
3601 self.afi, self.reserved, self.safi)
3605 @_OptParamCapability.register_type(BGP_CAP_CARRYING_LABEL_INFO)
3606 class BGPOptParamCapabilityCarryingLabelInfo(_OptParamEmptyCapability):
3610 class BGPWithdrawnRoute(IPAddrPrefix):
3614 class _PathAttribute(StringifyMixin, TypeDisp, _Value):
3615 _PACK_STR = '!BB' # flags, type
3616 _PACK_STR_LEN = '!B' # length
3617 _PACK_STR_EXT_LEN = '!H' # length w/ BGP_ATTR_FLAG_EXTENDED_LENGTH
3620 def __init__(self, value=None, flags=0, type_=None, length=None):
3622 type_ = self._rev_lookup_type(self.__class__)
3625 self.length = length
3626 if value is not None:
3630 def parser(cls, buf):
3631 (flags, type_) = struct.unpack_from(cls._PACK_STR,
3632 six.binary_type(buf))
3633 rest = buf[struct.calcsize(cls._PACK_STR):]
3634 if (flags & BGP_ATTR_FLAG_EXTENDED_LENGTH) != 0:
3635 len_pack_str = cls._PACK_STR_EXT_LEN
3637 len_pack_str = cls._PACK_STR_LEN
3638 (length,) = struct.unpack_from(len_pack_str, six.binary_type(rest))
3639 rest = rest[struct.calcsize(len_pack_str):]
3640 value = bytes(rest[:length])
3641 rest = rest[length:]
3642 subcls = cls._lookup_type(type_)
3643 return subcls(flags=flags, type_=type_, length=length,
3644 **subcls.parse_value(value)), rest
3646 def serialize(self):
3648 if self._ATTR_FLAGS is not None:
3651 & ~(BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE)
3653 value = self.serialize_value()
3654 self.length = len(value)
3655 if self.flags & BGP_ATTR_FLAG_EXTENDED_LENGTH:
3656 len_pack_str = self._PACK_STR_EXT_LEN
3657 elif self.length > 255:
3658 self.flags |= BGP_ATTR_FLAG_EXTENDED_LENGTH
3659 len_pack_str = self._PACK_STR_EXT_LEN
3661 self.flags &= ~BGP_ATTR_FLAG_EXTENDED_LENGTH
3662 len_pack_str = self._PACK_STR_LEN
3665 msg_pack_into(self._PACK_STR, buf, 0, self.flags, self.type)
3666 msg_pack_into(len_pack_str, buf, len(buf), self.length)
3670 @_PathAttribute.register_unknown_type()
3671 class BGPPathAttributeUnknown(_PathAttribute):
3673 def parse_value(cls, buf):
3678 def serialize_value(self):
3682 class _PathAttributeUint32(_PathAttribute):
3683 _VALUE_PACK_STR = '!I'
3686 @_PathAttribute.register_type(BGP_ATTR_TYPE_ORIGIN)
3687 class BGPPathAttributeOrigin(_PathAttribute):
3688 _VALUE_PACK_STR = '!B'
3689 _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
3692 class _BGPPathAttributeAsPathCommon(_PathAttribute):
3695 _SEG_HDR_PACK_STR = '!BB'
3697 _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
3699 def __init__(self, value, as_pack_str=None, flags=0, type_=None,
3701 super(_BGPPathAttributeAsPathCommon, self).__init__(value=value,
3706 self._AS_PACK_STR = as_pack_str
3709 def path_seg_list(self):
3710 return copy.deepcopy(self.value)
3712 def get_as_path_len(self):
3714 for seg in self.value:
3715 if isinstance(seg, list):
3716 # Segment type 2 stored in list and all AS counted.
3719 # Segment type 1 stored in set and count as one.
3724 def has_local_as(self, local_as, max_count=0):
3725 """Check if *local_as* is already present on path list."""
3727 for as_path_seg in self.value:
3728 _count += list(as_path_seg).count(local_as)
3729 return _count > max_count
3731 def has_matching_leftmost(self, remote_as):
3732 """Check if leftmost AS matches *remote_as*."""
3733 if not self.value or not remote_as:
3736 leftmost_seg = self.path_seg_list[0]
3737 if leftmost_seg and leftmost_seg[0] == remote_as:
3743 def _is_valid_16bit_as_path(cls, buf):
3745 two_byte_as_size = struct.calcsize('!H')
3748 (type_, num_as) = struct.unpack_from(cls._SEG_HDR_PACK_STR,
3749 six.binary_type(buf))
3751 if type_ is not cls._AS_SET and type_ is not cls._AS_SEQUENCE:
3754 buf = buf[struct.calcsize(cls._SEG_HDR_PACK_STR):]
3756 if len(buf) < num_as * two_byte_as_size:
3759 buf = buf[num_as * two_byte_as_size:]
3764 def parse_value(cls, buf):
3767 if cls._is_valid_16bit_as_path(buf):
3773 (type_, num_as) = struct.unpack_from(cls._SEG_HDR_PACK_STR,
3774 six.binary_type(buf))
3775 buf = buf[struct.calcsize(cls._SEG_HDR_PACK_STR):]
3777 for _ in range(0, num_as):
3778 (as_number,) = struct.unpack_from(as_pack_str,
3779 six.binary_type(buf))
3780 buf = buf[struct.calcsize(as_pack_str):]
3782 if type_ == cls._AS_SET:
3783 result.append(set(l))
3784 elif type_ == cls._AS_SEQUENCE:
3788 raise struct.error('Unsupported segment type: %s' % type_)
3791 'as_pack_str': as_pack_str,
3794 def serialize_value(self):
3797 for e in self.value:
3798 if isinstance(e, set):
3799 type_ = self._AS_SET
3800 elif isinstance(e, list):
3801 type_ = self._AS_SEQUENCE
3804 'Element of %s.value must be of type set or list' %
3805 self.__class__.__name__)
3810 msg_pack_into(self._SEG_HDR_PACK_STR, buf, offset, type_, num_as)
3811 offset += struct.calcsize(self._SEG_HDR_PACK_STR)
3813 msg_pack_into(self._AS_PACK_STR, buf, offset, i)
3814 offset += struct.calcsize(self._AS_PACK_STR)
3818 @_PathAttribute.register_type(BGP_ATTR_TYPE_AS_PATH)
3819 class BGPPathAttributeAsPath(_BGPPathAttributeAsPathCommon):
3820 # XXX depends on negotiated capability, AS numbers can be 32 bit.
3821 # while wireshark seems to attempt auto-detect, it seems that
3822 # there's no way to detect it reliably. for example, the
3823 # following byte sequence can be interpreted in two ways.
3824 # 01 02 99 88 77 66 02 01 55 44
3825 # AS_SET num=2 9988 7766 AS_SEQUENCE num=1 5544
3826 # AS_SET num=2 99887766 02015544
3827 # we first check whether AS path can be parsed in 16bit format and if
3828 # it fails, we try to parse as 32bit
3832 @_PathAttribute.register_type(BGP_ATTR_TYPE_AS4_PATH)
3833 class BGPPathAttributeAs4Path(_BGPPathAttributeAsPathCommon):
3835 _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL
3838 def _is_valid_16bit_as_path(cls, buf):
3842 @_PathAttribute.register_type(BGP_ATTR_TYPE_NEXT_HOP)
3843 class BGPPathAttributeNextHop(_PathAttribute):
3844 _VALUE_PACK_STR = '!4s'
3845 _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
3853 def parse_value(cls, buf):
3854 (ip_addr,) = struct.unpack_from(cls._VALUE_PACK_STR,
3855 six.binary_type(buf))
3857 'value': addrconv.ipv4.bin_to_text(ip_addr),
3860 def serialize_value(self):
3862 msg_pack_into(self._VALUE_PACK_STR, buf, 0,
3863 addrconv.ipv4.text_to_bin(self.value))
3867 @_PathAttribute.register_type(BGP_ATTR_TYPE_MULTI_EXIT_DISC)
3868 class BGPPathAttributeMultiExitDisc(_PathAttributeUint32):
3869 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
3872 @_PathAttribute.register_type(BGP_ATTR_TYPE_LOCAL_PREF)
3873 class BGPPathAttributeLocalPref(_PathAttributeUint32):
3874 _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
3877 @_PathAttribute.register_type(BGP_ATTR_TYPE_ATOMIC_AGGREGATE)
3878 class BGPPathAttributeAtomicAggregate(_PathAttribute):
3879 _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
3882 def parse_value(cls, buf):
3885 def serialize_value(self):
3889 class _BGPPathAttributeAggregatorCommon(_PathAttribute):
3890 _VALUE_PACK_STR = None
3891 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE
3898 def __init__(self, as_number, addr, flags=0, type_=None, length=None):
3899 super(_BGPPathAttributeAggregatorCommon, self).__init__(flags=flags,
3902 self.as_number = as_number
3906 def parse_value(cls, buf):
3907 (as_number, addr) = struct.unpack_from(cls._VALUE_PACK_STR,
3908 six.binary_type(buf))
3910 'as_number': as_number,
3911 'addr': addrconv.ipv4.bin_to_text(addr),
3914 def serialize_value(self):
3916 msg_pack_into(self._VALUE_PACK_STR, buf, 0, self.as_number,
3917 addrconv.ipv4.text_to_bin(self.addr))
3921 @_PathAttribute.register_type(BGP_ATTR_TYPE_AGGREGATOR)
3922 class BGPPathAttributeAggregator(_BGPPathAttributeAggregatorCommon):
3923 # Note: AS numbers can be Two-Octet or Four-Octet.
3924 # This class would detect it by the value length field.
3926 # - if the value field length is 6 (='!H4s'), AS number should
3928 # - else if the length is 8 (='!I4s'), AS number should be Four-Octet.
3929 _TWO_OCTET_VALUE_PACK_STR = '!H4s'
3930 _FOUR_OCTET_VALUE_PACK_STR = '!I4s'
3931 _VALUE_PACK_STR = _TWO_OCTET_VALUE_PACK_STR # Two-Octet by default
3932 _FOUR_OCTET_VALUE_SIZE = struct.calcsize(_FOUR_OCTET_VALUE_PACK_STR)
3935 def parse_value(cls, buf):
3936 if len(buf) == cls._FOUR_OCTET_VALUE_SIZE:
3937 cls._VALUE_PACK_STR = cls._FOUR_OCTET_VALUE_PACK_STR
3938 return super(BGPPathAttributeAggregator, cls).parse_value(buf)
3940 def serialize_value(self):
3941 if self.as_number > 0xffff:
3942 self._VALUE_PACK_STR = self._FOUR_OCTET_VALUE_PACK_STR
3943 return super(BGPPathAttributeAggregator, self).serialize_value()
3946 @_PathAttribute.register_type(BGP_ATTR_TYPE_AS4_AGGREGATOR)
3947 class BGPPathAttributeAs4Aggregator(_BGPPathAttributeAggregatorCommon):
3948 _VALUE_PACK_STR = '!I4s'
3951 @_PathAttribute.register_type(BGP_ATTR_TYPE_COMMUNITIES)
3952 class BGPPathAttributeCommunities(_PathAttribute):
3953 _VALUE_PACK_STR = '!I'
3954 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE
3956 # String constants of well-known-communities
3957 NO_EXPORT = int('0xFFFFFF01', 16)
3958 NO_ADVERTISE = int('0xFFFFFF02', 16)
3959 NO_EXPORT_SUBCONFED = int('0xFFFFFF03', 16)
3960 WELL_KNOW_COMMUNITIES = (NO_EXPORT, NO_ADVERTISE, NO_EXPORT_SUBCONFED)
3962 def __init__(self, communities,
3963 flags=0, type_=None, length=None):
3964 super(BGPPathAttributeCommunities, self).__init__(flags=flags,
3967 self.communities = communities
3970 def parse_value(cls, buf):
3973 elem_size = struct.calcsize(cls._VALUE_PACK_STR)
3974 while len(rest) >= elem_size:
3975 (comm, ) = struct.unpack_from(cls._VALUE_PACK_STR,
3976 six.binary_type(rest))
3977 communities.append(comm)
3978 rest = rest[elem_size:]
3980 'communities': communities,
3983 def serialize_value(self):
3985 for comm in self.communities:
3986 bincomm = bytearray()
3987 msg_pack_into(self._VALUE_PACK_STR, bincomm, 0, comm)
3992 def is_no_export(comm_attr):
3993 """Returns True if given value matches well-known community NO_EXPORT
3996 return comm_attr == BGPPathAttributeCommunities.NO_EXPORT
3999 def is_no_advertise(comm_attr):
4000 """Returns True if given value matches well-known community
4001 NO_ADVERTISE attribute value.
4003 return comm_attr == BGPPathAttributeCommunities.NO_ADVERTISE
4006 def is_no_export_subconfed(comm_attr):
4007 """Returns True if given value matches well-known community
4008 NO_EXPORT_SUBCONFED attribute value.
4010 return comm_attr == BGPPathAttributeCommunities.NO_EXPORT_SUBCONFED
4012 def has_comm_attr(self, attr):
4013 """Returns True if given community attribute is present."""
4015 for comm_attr in self.communities:
4016 if comm_attr == attr:
4022 @_PathAttribute.register_type(BGP_ATTR_TYPE_ORIGINATOR_ID)
4023 class BGPPathAttributeOriginatorId(_PathAttribute):
4024 # ORIGINATOR_ID is a new optional, non-transitive BGP attribute of Type
4025 # code 9. This attribute is 4 bytes long and it will be created by an
4026 # RR in reflecting a route.
4027 _VALUE_PACK_STR = '!4s'
4028 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
4036 def parse_value(cls, buf):
4037 (originator_id,) = struct.unpack_from(cls._VALUE_PACK_STR,
4038 six.binary_type(buf))
4040 'value': addrconv.ipv4.bin_to_text(originator_id),
4043 def serialize_value(self):
4045 msg_pack_into(self._VALUE_PACK_STR, buf, 0,
4046 addrconv.ipv4.text_to_bin(self.value))
4050 @_PathAttribute.register_type(BGP_ATTR_TYPE_CLUSTER_LIST)
4051 class BGPPathAttributeClusterList(_PathAttribute):
4052 # CLUSTER_LIST is a new, optional, non-transitive BGP attribute of Type
4053 # code 10. It is a sequence of CLUSTER_ID values representing the
4054 # reflection path that the route has passed.
4055 _VALUE_PACK_STR = '!4s'
4056 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
4064 def parse_value(cls, buf):
4067 elem_size = struct.calcsize(cls._VALUE_PACK_STR)
4068 while len(rest) >= elem_size:
4069 (cluster_id, ) = struct.unpack_from(
4070 cls._VALUE_PACK_STR, six.binary_type(rest))
4071 cluster_list.append(addrconv.ipv4.bin_to_text(cluster_id))
4072 rest = rest[elem_size:]
4074 'value': cluster_list,
4077 def serialize_value(self):
4080 for cluster_id in self.value:
4082 self._VALUE_PACK_STR,
4085 addrconv.ipv4.text_to_bin(cluster_id))
4086 offset += struct.calcsize(self._VALUE_PACK_STR)
4090 # Extended Communities
4094 # https://www.iana.org/assignments/bgp-extended-communities/
4095 # bgp-extended-communities.xml
4099 # 00 sub-type Two-Octet AS Specific Extended Community (transitive)
4100 # 40 sub-type Two-Octet AS Specific Extended Community
4102 # 2 byte Global Administrator (AS number)
4103 # 4 byte Local Administrator (defined by sub-type)
4104 # 01 sub-type IPv4 Address Specific Extended Community (transitive)
4105 # 41 sub-type IPv4 Address Specific Extended Community
4107 # 4 byte Global Administrator (IPv4 address)
4108 # 2 byte Local Administrator (defined by sub-type)
4109 # 03 sub-type Opaque Extended Community (transitive)
4110 # 43 sub-type Opaque Extended Community
4112 # 6 byte opaque value (defined by sub-type)
4114 # 00 02 Route Target Community (two-octet AS specific)
4115 # 01 02 Route Target Community (IPv4 address specific)
4116 # 02 02 Route Target Community (four-octet AS specific, RFC 5668)
4117 # 00 03 Route Origin Community (two-octet AS specific)
4118 # 01 03 Route Origin Community (IPv4 address specific)
4119 # 02 03 Route Origin Community (four-octet AS specific, RFC 5668)
4120 # 06 sub-type Ethernet VPN Extended Community (RFC 7432)
4121 # 80 sub-type Flow Specification Extended Community (RFC 5575)
4123 @_PathAttribute.register_type(BGP_ATTR_TYPE_EXTENDED_COMMUNITIES)
4124 class BGPPathAttributeExtendedCommunities(_PathAttribute):
4125 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE
4126 _class_prefixes = ['BGP']
4128 def __init__(self, communities,
4129 flags=0, type_=None, length=None):
4130 super(BGPPathAttributeExtendedCommunities,
4131 self).__init__(flags=flags,
4134 self.communities = communities
4137 def parse_value(cls, buf):
4141 comm, rest = _ExtendedCommunity.parse(rest)
4142 communities.append(comm)
4144 'communities': communities,
4147 def serialize_value(self):
4149 for comm in self.communities:
4150 buf += comm.serialize()
4153 def _community_list(self, subtype):
4155 for comm in (c for c in self.communities
4156 if hasattr(c, "subtype") and c.subtype == subtype):
4157 if comm.type == 0 or comm.type == 2:
4158 _list.append('%d:%d' % (comm.as_number,
4159 comm.local_administrator))
4160 elif comm.type == 1:
4161 _list.append('%s:%d' % (comm.ipv4_address,
4162 comm.local_administrator))
4167 return self._community_list(2)
4171 return self._community_list(3)
4174 class _ExtendedCommunity(StringifyMixin, TypeDisp, _Value):
4175 _PACK_STR = '!B7s' # type high (+ type low), value
4176 _PACK_STR_SIZE = struct.calcsize(_PACK_STR)
4177 _SUBTYPE_PACK_STR = '!B' # subtype
4178 IANA_AUTHORITY = 0x80
4180 _TYPE_HIGH_MASK = ~TRANSITIVE
4182 TWO_OCTET_AS_SPECIFIC = 0x00
4183 IPV4_ADDRESS_SPECIFIC = 0x01
4184 FOUR_OCTET_AS_SPECIFIC = 0x02
4186 SUBTYPE_ENCAPSULATION = 0x0c
4187 ENCAPSULATION = (OPAQUE, SUBTYPE_ENCAPSULATION)
4189 SUBTYPE_EVPN_MAC_MOBILITY = 0x00
4190 SUBTYPE_EVPN_ESI_LABEL = 0x01
4191 SUBTYPE_EVPN_ES_IMPORT_RT = 0x02
4192 EVPN_MAC_MOBILITY = (EVPN, SUBTYPE_EVPN_MAC_MOBILITY)
4193 EVPN_ESI_LABEL = (EVPN, SUBTYPE_EVPN_ESI_LABEL)
4194 EVPN_ES_IMPORT_RT = (EVPN, SUBTYPE_EVPN_ES_IMPORT_RT)
4196 FLOWSPEC_L2VPN = 0x08
4197 SUBTYPE_FLOWSPEC_TRAFFIC_RATE = 0x06
4198 SUBTYPE_FLOWSPEC_TRAFFIC_ACTION = 0x07
4199 SUBTYPE_FLOWSPEC_REDIRECT = 0x08
4200 SUBTYPE_FLOWSPEC_TRAFFIC_REMARKING = 0x09
4201 SUBTYPE_FLOWSPEC_VLAN_ACTION = 0x0a
4202 SUBTYPE_FLOWSPEC_TPID_ACTION = 0x0b
4203 FLOWSPEC_TRAFFIC_RATE = (FLOWSPEC, SUBTYPE_FLOWSPEC_TRAFFIC_RATE)
4204 FLOWSPEC_TRAFFIC_ACTION = (FLOWSPEC, SUBTYPE_FLOWSPEC_TRAFFIC_ACTION)
4205 FLOWSPEC_REDIRECT = (FLOWSPEC, SUBTYPE_FLOWSPEC_REDIRECT)
4206 FLOWSPEC_TRAFFIC_REMARKING = (FLOWSPEC, SUBTYPE_FLOWSPEC_TRAFFIC_REMARKING)
4207 FLOWSPEC_VLAN_ACTION = (FLOWSPEC_L2VPN, SUBTYPE_FLOWSPEC_VLAN_ACTION)
4208 FLOWSPEC_TPID_ACTION = (FLOWSPEC_L2VPN, SUBTYPE_FLOWSPEC_TPID_ACTION)
4210 def __init__(self, type_=None):
4212 type_ = self._rev_lookup_type(self.__class__)
4213 if isinstance(type_, (tuple, list)):
4218 def parse_subtype(cls, buf):
4219 (subtype,) = struct.unpack_from(cls._SUBTYPE_PACK_STR, buf)
4223 def parse(cls, buf):
4224 (type_, value) = struct.unpack_from(cls._PACK_STR, buf)
4225 rest = buf[cls._PACK_STR_SIZE:]
4226 type_low = type_ & cls._TYPE_HIGH_MASK
4227 subtype = cls.parse_subtype(value)
4228 subcls = cls._lookup_type((type_low, subtype))
4229 if subcls == cls._UNKNOWN_TYPE:
4230 subcls = cls._lookup_type(type_low)
4231 return subcls(type_=type_, **subcls.parse_value(value)), rest
4233 def serialize(self):
4234 return struct.pack(self._PACK_STR, self.type,
4235 self.serialize_value())
4238 @_ExtendedCommunity.register_type(_ExtendedCommunity.TWO_OCTET_AS_SPECIFIC)
4239 class BGPTwoOctetAsSpecificExtendedCommunity(_ExtendedCommunity):
4240 _VALUE_PACK_STR = '!BHI' # sub type, as number, local adm
4241 _VALUE_FIELDS = ['subtype', 'as_number', 'local_administrator']
4243 def __init__(self, **kwargs):
4244 super(BGPTwoOctetAsSpecificExtendedCommunity, self).__init__()
4245 self.do_init(BGPTwoOctetAsSpecificExtendedCommunity, self, kwargs)
4248 @_ExtendedCommunity.register_type(_ExtendedCommunity.IPV4_ADDRESS_SPECIFIC)
4249 class BGPIPv4AddressSpecificExtendedCommunity(_ExtendedCommunity):
4250 _VALUE_PACK_STR = '!B4sH' # sub type, IPv4 address, local adm
4251 _VALUE_FIELDS = ['subtype', 'ipv4_address', 'local_administrator']
4258 def __init__(self, **kwargs):
4259 super(BGPIPv4AddressSpecificExtendedCommunity, self).__init__()
4260 self.do_init(BGPIPv4AddressSpecificExtendedCommunity, self, kwargs)
4263 def parse_value(cls, buf):
4264 d_ = super(BGPIPv4AddressSpecificExtendedCommunity,
4265 cls).parse_value(buf)
4266 d_['ipv4_address'] = addrconv.ipv4.bin_to_text(d_['ipv4_address'])
4269 def serialize_value(self):
4270 return struct.pack(self._VALUE_PACK_STR, self.subtype,
4271 addrconv.ipv4.text_to_bin(self.ipv4_address),
4272 self.local_administrator)
4275 @_ExtendedCommunity.register_type(_ExtendedCommunity.FOUR_OCTET_AS_SPECIFIC)
4276 class BGPFourOctetAsSpecificExtendedCommunity(_ExtendedCommunity):
4277 _VALUE_PACK_STR = '!BIH' # sub type, as number, local adm
4278 _VALUE_FIELDS = ['subtype', 'as_number', 'local_administrator']
4280 def __init__(self, **kwargs):
4281 super(BGPFourOctetAsSpecificExtendedCommunity, self).__init__()
4282 self.do_init(BGPFourOctetAsSpecificExtendedCommunity, self, kwargs)
4285 @_ExtendedCommunity.register_type(_ExtendedCommunity.OPAQUE)
4286 class BGPOpaqueExtendedCommunity(_ExtendedCommunity):
4287 _VALUE_PACK_STR = '!B6s'
4288 _VALUE_FIELDS = ['subtype', 'opaque']
4290 def __init__(self, **kwargs):
4291 super(BGPOpaqueExtendedCommunity, self).__init__()
4292 self.do_init(BGPOpaqueExtendedCommunity, self, kwargs)
4295 @_ExtendedCommunity.register_type(_ExtendedCommunity.ENCAPSULATION)
4296 class BGPEncapsulationExtendedCommunity(_ExtendedCommunity):
4297 _VALUE_PACK_STR = '!B4xH'
4298 _VALUE_FIELDS = ['subtype', 'tunnel_type']
4300 # BGP Tunnel Encapsulation Attribute Tunnel Types
4301 # http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#tunnel-types
4302 TUNNEL_TYPE_L2TPV3 = 1
4304 TUNNEL_TYPE_IP_IN_IP = 7
4305 TUNNEL_TYPE_VXLAN = 8
4306 TUNNEL_TYPE_NVGRE = 9
4307 TUNNEL_TYPE_MPLS = 10
4308 TUNNEL_TYPE_MPLS_IN_GRE = 11
4309 TUNNEL_TYPE_VXLAN_GRE = 12
4310 TUNNEL_TYPE_MPLS_IN_UDP = 13
4312 def __init__(self, **kwargs):
4313 super(BGPEncapsulationExtendedCommunity, self).__init__()
4314 self.do_init(BGPEncapsulationExtendedCommunity, self, kwargs)
4317 def from_str(cls, tunnel_type):
4319 Returns an instance identified with the given `tunnel_type`.
4321 `tunnel_type` should be a str type value and corresponding to
4322 BGP Tunnel Encapsulation Attribute Tunnel Type constants name
4323 omitting `TUNNEL_TYPE_` prefix.
4326 - `gre` means TUNNEL_TYPE_GRE
4327 - `vxlan` means TUNNEL_TYPE_VXLAN
4329 And raises AttributeError when the corresponding Tunnel Type
4330 is not found to the given `tunnel_type`.
4332 return cls(subtype=_ExtendedCommunity.SUBTYPE_ENCAPSULATION,
4333 tunnel_type=getattr(cls,
4334 'TUNNEL_TYPE_' + tunnel_type.upper()))
4337 @_ExtendedCommunity.register_type(_ExtendedCommunity.EVPN_MAC_MOBILITY)
4338 class BGPEvpnMacMobilityExtendedCommunity(_ExtendedCommunity):
4340 MAC Mobility Extended Community
4342 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4343 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4344 # | Type=0x06 | Sub-Type=0x00 |Flags(1 octet)| Reserved=0 |
4345 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4346 # | Sequence Number |
4347 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4348 _VALUE_PACK_STR = '!BBxI'
4349 _VALUE_FIELDS = ['subtype', 'flags', 'sequence_number']
4351 def __init__(self, **kwargs):
4352 super(BGPEvpnMacMobilityExtendedCommunity, self).__init__()
4353 self.do_init(BGPEvpnMacMobilityExtendedCommunity, self, kwargs)
4356 @_ExtendedCommunity.register_type(_ExtendedCommunity.EVPN_ESI_LABEL)
4357 class BGPEvpnEsiLabelExtendedCommunity(_ExtendedCommunity):
4359 ESI Label Extended Community
4361 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4362 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4363 # | Type=0x06 | Sub-Type=0x01 | Flags(1 octet)| Reserved=0 |
4364 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4365 # | Reserved=0 | ESI Label |
4366 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4367 _VALUE_PACK_STR = '!BB2x3s'
4368 _VALUE_FIELDS = ['subtype', 'flags']
4370 # Classification for Flags.
4371 SINGLE_ACTIVE_BIT = 1 << 0
4373 def __init__(self, label=None, mpls_label=None, vni=None, **kwargs):
4374 super(BGPEvpnEsiLabelExtendedCommunity, self).__init__()
4375 self.do_init(BGPEvpnEsiLabelExtendedCommunity, self, kwargs)
4378 # If binary type label field value is specified, stores it
4379 # and decodes as MPLS label and VNI.
4381 self._mpls_label, _ = mpls.label_from_bin(label)
4382 self._vni = vxlan.vni_from_bin(label)
4384 # If either MPLS label or VNI is specified, stores it
4385 # and encodes into binary type label field value.
4386 self._label = self._serialize_label(mpls_label, vni)
4387 self._mpls_label = mpls_label
4390 def _serialize_label(self, mpls_label, vni):
4392 return mpls.label_to_bin(mpls_label, is_bos=True)
4394 return vxlan.vni_to_bin(vni)
4399 def parse_value(cls, buf):
4401 label) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
4408 def serialize_value(self):
4409 return struct.pack(self._VALUE_PACK_STR, self.subtype, self.flags,
4413 def mpls_label(self):
4414 return self._mpls_label
4417 def mpls_label(self, mpls_label):
4418 self._label = mpls.label_to_bin(mpls_label, is_bos=True)
4419 self._mpls_label = mpls_label
4420 self._vni = None # disables VNI
4428 self._label = vxlan.vni_to_bin(vni)
4429 self._mpls_label = None # disables ESI label
4433 @_ExtendedCommunity.register_type(_ExtendedCommunity.EVPN_ES_IMPORT_RT)
4434 class BGPEvpnEsImportRTExtendedCommunity(_ExtendedCommunity):
4436 ES-Import Route Target Extended Community
4438 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4439 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4440 # | Type=0x06 | Sub-Type=0x02 | ES-Import |
4441 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4442 # | ES-Import Cont'd |
4443 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4444 _VALUE_PACK_STR = '!B6s'
4445 _VALUE_FIELDS = ['subtype', 'es_import']
4452 def __init__(self, **kwargs):
4453 super(BGPEvpnEsImportRTExtendedCommunity, self).__init__()
4454 self.do_init(BGPEvpnEsImportRTExtendedCommunity, self, kwargs)
4457 def parse_value(cls, buf):
4458 (subtype, es_import) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
4461 'es_import': addrconv.mac.bin_to_text(es_import),
4464 def serialize_value(self):
4465 return struct.pack(self._VALUE_PACK_STR, self.subtype,
4466 addrconv.mac.text_to_bin(self.es_import))
4469 @_ExtendedCommunity.register_type(_ExtendedCommunity.FLOWSPEC_TRAFFIC_RATE)
4470 class BGPFlowSpecTrafficRateCommunity(_ExtendedCommunity):
4472 Flow Specification Traffic Filtering Actions for Traffic Rate.
4474 ========================== ===============================================
4475 Attribute Description
4476 ========================== ===============================================
4477 as_number Autonomous System number.
4478 rate_info rate information.
4479 ========================== ===============================================
4481 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4482 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4483 # | Type=0x80 | Sub-Type=0x06 | AS number |
4484 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4485 # | Rate information |
4486 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4487 _VALUE_PACK_STR = '!BHf'
4488 _VALUE_FIELDS = ['subtype', 'as_number', 'rate_info']
4489 ACTION_NAME = 'traffic_rate'
4491 def __init__(self, **kwargs):
4492 super(BGPFlowSpecTrafficRateCommunity, self).__init__()
4493 kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_TRAFFIC_RATE
4494 self.do_init(BGPFlowSpecTrafficRateCommunity, self, kwargs)
4497 def parse_value(cls, buf):
4498 (subtype, as_number,
4499 rate_info) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
4502 'as_number': as_number,
4503 'rate_info': rate_info,
4506 def serialize_value(self):
4507 return struct.pack(self._VALUE_PACK_STR, self.subtype,
4508 self.as_number, self.rate_info)
4511 @_ExtendedCommunity.register_type(_ExtendedCommunity.FLOWSPEC_TRAFFIC_ACTION)
4512 class BGPFlowSpecTrafficActionCommunity(_ExtendedCommunity):
4514 Flow Specification Traffic Filtering Actions for Traffic Action.
4516 ========================== ===============================================
4517 Attribute Description
4518 ========================== ===============================================
4519 action Apply action.
4520 The supported action are
4521 ``SAMPLE`` and ``TERMINAL``.
4522 ========================== ===============================================
4524 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4525 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4526 # | Type=0x80 | Sub-Type=0x07 | Traffic-action |
4527 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4528 # | Traffic-action Cont'd |
4529 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4531 # Traffic-action format
4532 # 40 41 42 43 44 45 46 47
4533 # +---+---+---+---+---+---+---+---+
4534 # | reserved | S | T |
4535 # +---+---+---+---+---+---+---+---+
4537 _VALUE_PACK_STR = '!B5xB'
4538 _VALUE_FIELDS = ['subtype', 'action']
4539 ACTION_NAME = 'traffic_action'
4543 def __init__(self, **kwargs):
4544 super(BGPFlowSpecTrafficActionCommunity, self).__init__()
4545 kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_TRAFFIC_ACTION
4546 self.do_init(BGPFlowSpecTrafficActionCommunity, self, kwargs)
4549 @_ExtendedCommunity.register_type(_ExtendedCommunity.FLOWSPEC_REDIRECT)
4550 class BGPFlowSpecRedirectCommunity(BGPTwoOctetAsSpecificExtendedCommunity):
4552 Flow Specification Traffic Filtering Actions for Redirect.
4554 ========================== ===============================================
4555 Attribute Description
4556 ========================== ===============================================
4557 as_number Autonomous System number.
4558 local_administrator Local Administrator.
4559 ========================== ===============================================
4561 ACTION_NAME = 'redirect'
4563 def __init__(self, **kwargs):
4564 super(BGPTwoOctetAsSpecificExtendedCommunity, self).__init__()
4565 kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_REDIRECT
4566 self.do_init(BGPTwoOctetAsSpecificExtendedCommunity, self, kwargs)
4569 @_ExtendedCommunity.register_type(
4570 _ExtendedCommunity.FLOWSPEC_TRAFFIC_REMARKING)
4571 class BGPFlowSpecTrafficMarkingCommunity(_ExtendedCommunity):
4573 Flow Specification Traffic Filtering Actions for Traffic Marking.
4575 ========================== ===============================================
4576 Attribute Description
4577 ========================== ===============================================
4578 dscp Differentiated Services Code Point.
4579 ========================== ===============================================
4581 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4582 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4583 # | Type=0x80 | Sub-Type=0x09 | Reserved=0 |
4584 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4585 # | Reserved=0 | Dscp |
4586 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4587 _VALUE_PACK_STR = '!B5xB'
4588 _VALUE_FIELDS = ['subtype', 'dscp']
4589 ACTION_NAME = 'traffic_marking'
4591 def __init__(self, **kwargs):
4592 super(BGPFlowSpecTrafficMarkingCommunity, self).__init__()
4593 kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_TRAFFIC_REMARKING
4594 self.do_init(BGPFlowSpecTrafficMarkingCommunity, self, kwargs)
4597 def parse_value(cls, buf):
4598 (subtype, dscp) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
4604 def serialize_value(self):
4605 return struct.pack(self._VALUE_PACK_STR, self.subtype, self.dscp)
4609 # Implement "Redirect-IPv6" [draft-ietf-idr-flow-spec-v6-08]
4612 @_ExtendedCommunity.register_type(
4613 _ExtendedCommunity.FLOWSPEC_VLAN_ACTION)
4614 class BGPFlowSpecVlanActionCommunity(_ExtendedCommunity):
4616 Flow Specification Vlan Actions.
4618 ========= ===============================================
4619 Attribute Description
4620 ========= ===============================================
4621 actions_1 Bit representation of actions.
4622 Supported actions are
4623 ``POP``, ``PUSH``, ``SWAP``, ``REWRITE_INNER``, ``REWRITE_OUTER``.
4624 actions_2 Same as ``actions_1``.
4625 vlan_1 VLAN ID used by ``actions_1``.
4626 cos_1 Class of Service used by ``actions_1``.
4627 vlan_2 VLAN ID used by ``actions_2``.
4628 cos_2 Class of Service used by ``actions_2``.
4629 ========= ===============================================
4631 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4632 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4633 # | Type=0x08 | Sub-Type=0x0a |PO1|PU1|SW1|RT1|RO1|...|PO2|...|
4634 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4635 # | VLAN ID1 | COS1 |0| VLAN ID2 | COS2 |0|
4636 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4637 _VALUE_PACK_STR = '!BBBHH'
4646 ACTION_NAME = 'vlan_action'
4652 REWRITE_INNER = 1 << 4
4653 REWRITE_OUTER = 1 << 3
4655 def __init__(self, **kwargs):
4656 super(BGPFlowSpecVlanActionCommunity, self).__init__()
4657 kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_VLAN_ACTION
4658 self.do_init(BGPFlowSpecVlanActionCommunity, self, kwargs)
4661 def parse_value(cls, buf):
4662 (subtype, actions_1, actions_2,
4663 vlan_cos_1, vlan_cos_2) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
4667 'actions_1': actions_1,
4668 'vlan_1': int(vlan_cos_1 >> 4),
4669 'cos_1': int((vlan_cos_1 >> 1) & cls._COS_MASK),
4670 'actions_2': actions_2,
4671 'vlan_2': int(vlan_cos_2 >> 4),
4672 'cos_2': int((vlan_cos_2 >> 1) & cls._COS_MASK)
4675 def serialize_value(self):
4677 self._VALUE_PACK_STR,
4681 (self.vlan_1 << 4) + (self.cos_1 << 1),
4682 (self.vlan_2 << 4) + (self.cos_2 << 1),
4686 @_ExtendedCommunity.register_type(
4687 _ExtendedCommunity.FLOWSPEC_TPID_ACTION)
4688 class BGPFlowSpecTPIDActionCommunity(_ExtendedCommunity):
4690 Flow Specification TPID Actions.
4692 ========= =========================================================
4693 Attribute Description
4694 ========= =========================================================
4695 actions Bit representation of actions.
4696 Supported actions are
4697 ``TI(inner TPID action)`` and ``TO(outer TPID action)``.
4698 tpid_1 TPID used by ``TI``.
4699 tpid_2 TPID used by ``TO``.
4700 ========= =========================================================
4702 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4703 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4704 # | Type=0x08 | Sub-Type=0x0b |TI|TO| Reserved=0 |
4705 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4707 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4708 _VALUE_PACK_STR = '!BHHH'
4709 _VALUE_FIELDS = ['subtype', 'actions', 'tpid_1', 'tpid_2']
4710 ACTION_NAME = 'tpid_action'
4715 def __init__(self, **kwargs):
4716 super(BGPFlowSpecTPIDActionCommunity, self).__init__()
4717 kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_TPID_ACTION
4718 self.do_init(BGPFlowSpecTPIDActionCommunity, self, kwargs)
4721 def parse_value(cls, buf):
4722 (subtype, actions, tpid_1, tpid_2) = struct.unpack_from(
4723 cls._VALUE_PACK_STR, buf)
4732 def serialize_value(self):
4734 self._VALUE_PACK_STR,
4742 @_ExtendedCommunity.register_unknown_type()
4743 class BGPUnknownExtendedCommunity(_ExtendedCommunity):
4744 _VALUE_PACK_STR = '!7s' # opaque value
4746 def __init__(self, type_, **kwargs):
4747 super(BGPUnknownExtendedCommunity, self).__init__(type_=type_)
4748 self.do_init(BGPUnknownExtendedCommunity, self, kwargs, type_=type_)
4751 @_PathAttribute.register_type(BGP_ATTR_TYPE_MP_REACH_NLRI)
4752 class BGPPathAttributeMpReachNLRI(_PathAttribute):
4753 _VALUE_PACK_STR = '!HBB' # afi, safi, next_hop_len
4754 _VALUE_PACK_SIZE = struct.calcsize(_VALUE_PACK_STR)
4756 _RESERVED_LENGTH = 1
4757 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
4758 _class_suffixes = ['AddrPrefix']
4759 _opt_attributes = ['next_hop']
4766 def __init__(self, afi, safi, next_hop, nlri,
4767 flags=0, type_=None, length=None):
4768 super(BGPPathAttributeMpReachNLRI, self).__init__(
4769 flags=flags, type_=type_, length=length)
4772 if not isinstance(next_hop, (list, tuple)):
4773 next_hop = [next_hop]
4775 if not ip.valid_ipv4(n) and not ip.valid_ipv6(n):
4776 raise ValueError('Invalid address for next_hop: %s' % n)
4777 # Note: For the backward compatibility, stores the first next_hop
4778 # address and all next_hop addresses separately.
4780 self._next_hop = next_hop[0]
4782 self._next_hop = None
4783 self._next_hop_list = next_hop
4785 addr_cls = _get_addr_class(afi, safi)
4787 if not isinstance(i, addr_cls):
4788 raise ValueError('Invalid NRLI class for afi=%d and safi=%d'
4789 % (self.afi, self.safi))
4792 def split_bin_with_len(buf, unit_len):
4794 return [f.read(unit_len) for _ in range(0, len(buf), unit_len)]
4797 def parse_next_hop_ipv4(cls, buf, unit_len):
4799 for next_hop_bin in cls.split_bin_with_len(buf, unit_len):
4800 next_hop.append(addrconv.ipv4.bin_to_text(next_hop_bin[-4:]))
4804 def parse_next_hop_ipv6(cls, buf, unit_len):
4806 for next_hop_bin in cls.split_bin_with_len(buf, unit_len):
4807 next_hop.append(addrconv.ipv6.bin_to_text(next_hop_bin[-16:]))
4811 def parse_value(cls, buf):
4812 (afi, safi, next_hop_len,) = struct.unpack_from(
4813 cls._VALUE_PACK_STR, six.binary_type(buf))
4814 rest = buf[cls._VALUE_PACK_SIZE:]
4816 next_hop_bin = rest[:next_hop_len]
4817 rest = rest[next_hop_len:]
4818 reserved = rest[:cls._RESERVED_LENGTH]
4819 assert reserved == b'\0'
4821 nlri_bin = rest[cls._RESERVED_LENGTH:]
4822 addr_cls = _get_addr_class(afi, safi)
4825 n, nlri_bin = addr_cls.parser(nlri_bin)
4828 rf = RouteFamily(afi, safi)
4829 if rf == RF_IPv4_VPN:
4830 next_hop = cls.parse_next_hop_ipv4(next_hop_bin,
4832 next_hop_len -= cls._RD_LENGTH * len(next_hop)
4833 elif rf == RF_IPv6_VPN:
4834 next_hop = cls.parse_next_hop_ipv6(next_hop_bin,
4835 cls._RD_LENGTH + 16)
4836 next_hop_len -= cls._RD_LENGTH * len(next_hop)
4837 elif (afi == addr_family.IP
4838 or (rf == RF_L2_EVPN and next_hop_len < 16)):
4839 next_hop = cls.parse_next_hop_ipv4(next_hop_bin, 4)
4840 elif (afi == addr_family.IP6
4841 or (rf == RF_L2_EVPN and next_hop_len >= 16)):
4842 next_hop = cls.parse_next_hop_ipv6(next_hop_bin, 16)
4843 elif rf == RF_L2VPN_FLOWSPEC:
4846 raise ValueError('Invalid address family: afi=%d, safi=%d'
4852 'next_hop': next_hop,
4856 def serialize_next_hop(self):
4858 for next_hop in self.next_hop_list:
4859 if self.afi == addr_family.IP6:
4860 next_hop = str(netaddr.IPAddress(next_hop).ipv6())
4861 next_hop_bin = ip.text_to_bin(next_hop)
4862 if RouteFamily(self.afi, self.safi) in (RF_IPv4_VPN, RF_IPv6_VPN):
4863 # Empty label stack(RD=0:0) + IP address
4864 next_hop_bin = b'\x00' * self._RD_LENGTH + next_hop_bin
4869 def serialize_value(self):
4870 next_hop_bin = self.serialize_next_hop()
4873 next_hop_len = len(next_hop_bin)
4876 msg_pack_into(self._VALUE_PACK_STR, buf, 0,
4877 self.afi, self.safi, next_hop_len)
4879 buf += b'\0' # reserved
4881 nlri_bin = bytearray()
4883 nlri_bin += n.serialize()
4890 return self._next_hop
4893 def next_hop(self, addr):
4894 if not ip.valid_ipv4(addr) and not ip.valid_ipv6(addr):
4895 raise ValueError('Invalid address for next_hop: %s' % addr)
4896 self._next_hop = addr
4897 self.next_hop_list[0] = addr
4900 def next_hop_list(self):
4901 return self._next_hop_list
4903 @next_hop_list.setter
4904 def next_hop_list(self, addr_list):
4905 if not isinstance(addr_list, (list, tuple)):
4906 addr_list = [addr_list]
4907 for addr in addr_list:
4908 if not ip.valid_ipv4(addr) and not ip.valid_ipv6(addr):
4909 raise ValueError('Invalid address for next_hop: %s' % addr)
4910 self._next_hop = addr_list[0]
4911 self._next_hop_list = addr_list
4914 def route_family(self):
4915 return _rf_map[(self.afi, self.safi)]
4918 @_PathAttribute.register_type(BGP_ATTR_TYPE_MP_UNREACH_NLRI)
4919 class BGPPathAttributeMpUnreachNLRI(_PathAttribute):
4920 _VALUE_PACK_STR = '!HB' # afi, safi
4921 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
4922 _class_suffixes = ['AddrPrefix']
4924 def __init__(self, afi, safi, withdrawn_routes,
4925 flags=0, type_=None, length=None):
4926 super(BGPPathAttributeMpUnreachNLRI, self).__init__(
4927 flags=flags, type_=type_, length=length)
4930 self.withdrawn_routes = withdrawn_routes
4931 addr_cls = _get_addr_class(afi, safi)
4932 for i in withdrawn_routes:
4933 if not isinstance(i, addr_cls):
4934 raise ValueError('Invalid NRLI class for afi=%d and safi=%d'
4935 % (self.afi, self.safi))
4938 def parse_value(cls, buf):
4939 (afi, safi,) = struct.unpack_from(
4940 cls._VALUE_PACK_STR, six.binary_type(buf))
4942 nlri_bin = buf[struct.calcsize(cls._VALUE_PACK_STR):]
4943 addr_cls = _get_addr_class(afi, safi)
4946 n, nlri_bin = addr_cls.parser(nlri_bin)
4952 'withdrawn_routes': nlri,
4955 def serialize_value(self):
4957 msg_pack_into(self._VALUE_PACK_STR, buf, 0, self.afi, self.safi)
4959 nlri_bin = bytearray()
4960 for n in self.withdrawn_routes:
4961 nlri_bin += n.serialize()
4967 def route_family(self):
4968 return _rf_map[(self.afi, self.safi)]
4971 @_PathAttribute.register_type(BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE)
4972 class BGPPathAttributePmsiTunnel(_PathAttribute):
4974 P-Multicast Service Interface Tunnel (PMSI Tunnel) attribute
4977 # pmsi_flags, tunnel_type, mpls_label
4978 _VALUE_PACK_STR = '!BB3s'
4979 _PACK_STR_SIZE = struct.calcsize(_VALUE_PACK_STR)
4980 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE
4983 # +--------------------------------+
4984 # | Flags (1 octet) |
4985 # +--------------------------------+
4986 # | Tunnel Type (1 octets) |
4987 # +--------------------------------+
4988 # | MPLS Label (3 octets) |
4989 # +--------------------------------+
4990 # | Tunnel Identifier (variable) |
4991 # +--------------------------------+
4993 # The Flags field has the following format:
4998 # `L` refers to the Leaf Information Required.
5000 # Current, Tunnel Type supports following.
5001 # + 0 - No tunnel information present
5002 # + 6 - Ingress Replication
5003 TYPE_NO_TUNNEL_INFORMATION_PRESENT = 0
5004 TYPE_INGRESS_REPLICATION = 6
5007 # The following Tunnel Type are not supported.
5008 # Therefore, we will need to support in the future.
5009 # + 1 - RSVP-TE P2MP LSP
5010 # + 2 - mLDP P2MP LSP
5011 # + 3 - PIM-SSM Tree
5013 # + 5 - BIDIR-PIM Tree
5014 # + 7 - mLDP MP2MP LSP
5016 def __init__(self, pmsi_flags, tunnel_type,
5017 mpls_label=None, label=None, vni=None, tunnel_id=None,
5018 flags=0, type_=None, length=None):
5019 super(BGPPathAttributePmsiTunnel, self).__init__(flags=flags,
5022 self.pmsi_flags = pmsi_flags
5023 self.tunnel_type = tunnel_type
5024 self.tunnel_id = tunnel_id
5027 # If binary type label field value is specified, stores it
5028 # and decodes as MPLS label and VNI.
5030 self._mpls_label, _ = mpls.label_from_bin(label)
5031 self._vni = vxlan.vni_from_bin(label)
5033 # If either MPLS label or VNI is specified, stores it
5034 # and encodes into binary type label field value.
5035 self._label = self._serialize_label(mpls_label, vni)
5036 self._mpls_label = mpls_label
5040 def parse_value(cls, buf):
5043 label) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
5044 value = buf[cls._PACK_STR_SIZE:]
5047 'pmsi_flags': pmsi_flags,
5048 'tunnel_type': tunnel_type,
5050 'tunnel_id': _PmsiTunnelId.parse(tunnel_type, value)
5053 def serialize_value(self):
5055 msg_pack_into(self._VALUE_PACK_STR, buf, 0,
5056 self.pmsi_flags, self.tunnel_type, self._label)
5058 if self.tunnel_id is not None:
5059 buf += self.tunnel_id.serialize()
5063 def _serialize_label(self, mpls_label, vni):
5065 return mpls.label_to_bin(mpls_label, is_bos=True)
5067 return vxlan.vni_to_bin(vni)
5072 def mpls_label(self):
5073 return self._mpls_label
5076 def mpls_label(self, mpls_label):
5077 self._label = mpls.label_to_bin(mpls_label, is_bos=True)
5078 self._mpls_label = mpls_label
5079 self._vni = None # disables VNI
5087 self._label = vxlan.vni_to_bin(vni)
5088 self._mpls_label = None # disables MPLS label
5092 def from_jsondict(cls, dict_, decode_string=base64.b64decode,
5094 if isinstance(dict_['tunnel_id'], dict):
5095 tunnel_id = dict_.pop('tunnel_id')
5096 ins = super(BGPPathAttributePmsiTunnel,
5097 cls).from_jsondict(dict_,
5101 mod = import_module(cls.__module__)
5103 for key, value in tunnel_id.items():
5104 tunnel_id_cls = getattr(mod, key)
5105 ins.tunnel_id = tunnel_id_cls.from_jsondict(value,
5109 ins = super(BGPPathAttributePmsiTunnel,
5110 cls).from_jsondict(dict_,
5117 class _PmsiTunnelId(StringifyMixin, TypeDisp):
5120 def parse(cls, tunnel_type, buf):
5121 subcls = cls._lookup_type(tunnel_type)
5122 return subcls.parser(buf)
5125 @_PmsiTunnelId.register_unknown_type()
5126 class PmsiTunnelIdUnknown(_PmsiTunnelId):
5128 Unknown route type specific _PmsiTunnelId
5131 def __init__(self, value):
5132 super(PmsiTunnelIdUnknown, self).__init__()
5136 def parser(cls, buf):
5137 return cls(value=buf)
5139 def serialize(self):
5143 @_PmsiTunnelId.register_type(
5144 BGPPathAttributePmsiTunnel.TYPE_NO_TUNNEL_INFORMATION_PRESENT)
5145 class _PmsiTunnelIdNoInformationPresent(_PmsiTunnelId):
5148 def parser(cls, buf):
5152 @_PmsiTunnelId.register_type(
5153 BGPPathAttributePmsiTunnel.TYPE_INGRESS_REPLICATION)
5154 class PmsiTunnelIdIngressReplication(_PmsiTunnelId):
5155 # tunnel_endpoint_ip
5156 _VALUE_PACK_STR = '!%ds'
5159 'tunnel_endpoint_ip'
5163 def __init__(self, tunnel_endpoint_ip):
5164 super(PmsiTunnelIdIngressReplication, self).__init__()
5165 self.tunnel_endpoint_ip = tunnel_endpoint_ip
5168 def parser(cls, buf):
5169 (tunnel_endpoint_ip, ) = struct.unpack_from(
5170 cls._VALUE_PACK_STR % len(buf),
5171 six.binary_type(buf))
5172 return cls(tunnel_endpoint_ip=ip.bin_to_text(tunnel_endpoint_ip))
5174 def serialize(self):
5175 ip_bin = ip.text_to_bin(self.tunnel_endpoint_ip)
5176 return struct.pack(self._VALUE_PACK_STR % len(ip_bin),
5177 ip.text_to_bin(self.tunnel_endpoint_ip))
5180 class BGPNLRI(IPAddrPrefix):
5184 class BGPMessage(packet_base.PacketBase, TypeDisp):
5185 """Base class for BGP-4 messages.
5187 An instance has the following attributes at least.
5188 Most of them are same to the on-wire counterparts but in host byte
5190 __init__ takes the corresponding args in this order.
5192 ========================== ===============================================
5193 Attribute Description
5194 ========================== ===============================================
5195 marker Marker field. Ignored when encoding.
5196 len Length field. Ignored when encoding.
5197 type Type field. one of ``BGP_MSG_*`` constants.
5198 ========================== ===============================================
5201 _HDR_PACK_STR = '!16sHB' # marker, len, type
5202 _HDR_LEN = struct.calcsize(_HDR_PACK_STR)
5203 _class_prefixes = ['BGP']
5205 def __init__(self, marker=None, len_=None, type_=None):
5206 super(BGPMessage, self).__init__()
5208 self._marker = _MARKER
5210 self._marker = marker
5213 type_ = self._rev_lookup_type(self.__class__)
5217 def parser(cls, buf):
5218 if len(buf) < cls._HDR_LEN:
5219 raise stream_parser.StreamParser.TooSmallException(
5220 '%d < %d' % (len(buf), cls._HDR_LEN))
5221 (marker, len_, type_) = struct.unpack_from(cls._HDR_PACK_STR,
5222 six.binary_type(buf))
5224 if len(buf) < msglen:
5225 raise stream_parser.StreamParser.TooSmallException(
5226 '%d < %d' % (len(buf), msglen))
5227 binmsg = buf[cls._HDR_LEN:msglen]
5229 subcls = cls._lookup_type(type_)
5230 kwargs = subcls.parser(binmsg)
5231 return subcls(marker=marker, len_=len_, type_=type_,
5232 **kwargs), cls, rest
5234 def serialize(self, payload=None, prev=None):
5236 self._marker = _MARKER
5237 tail = self.serialize_tail()
5238 self.len = self._HDR_LEN + len(tail)
5240 hdr = bytearray(struct.pack(self._HDR_PACK_STR, self._marker,
5241 self.len, self.type))
5246 buf = self.serialize()
5250 @BGPMessage.register_type(BGP_MSG_OPEN)
5251 class BGPOpen(BGPMessage):
5252 """BGP-4 OPEN Message encoder/decoder class.
5254 An instance has the following attributes at least.
5255 Most of them are same to the on-wire counterparts but in host byte
5257 __init__ takes the corresponding args in this order.
5259 ========================== ===============================================
5260 Attribute Description
5261 ========================== ===============================================
5262 marker Marker field. Ignored when encoding.
5263 len Length field. Ignored when encoding.
5265 version Version field.
5266 my_as My Autonomous System field.
5267 2 octet unsigned integer.
5268 hold_time Hold Time field.
5269 2 octet unsigned integer.
5270 bgp_identifier BGP Identifier field.
5272 For example, '192.0.2.1'
5273 opt_param_len Optional Parameters Length field.
5274 Ignored when encoding.
5275 opt_param Optional Parameters field.
5276 A list of BGPOptParam instances.
5278 ========================== ===============================================
5281 _PACK_STR = '!BHH4sB'
5282 _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR)
5289 def __init__(self, my_as, bgp_identifier, type_=BGP_MSG_OPEN,
5290 opt_param_len=0, opt_param=None,
5291 version=_VERSION, hold_time=0, len_=None, marker=None):
5292 opt_param = opt_param if opt_param else []
5293 super(BGPOpen, self).__init__(marker=marker, len_=len_, type_=type_)
5294 self.version = version
5296 self.bgp_identifier = bgp_identifier
5297 self.hold_time = hold_time
5298 self.opt_param_len = opt_param_len
5299 self.opt_param = opt_param
5302 def opt_param_cap_map(self):
5304 for param in self.opt_param:
5305 if param.type == BGP_OPT_CAPABILITY:
5306 cap_map[param.cap_code] = param
5309 def get_opt_param_cap(self, cap_code):
5310 return self.opt_param_cap_map.get(cap_code)
5313 def parser(cls, buf):
5318 opt_param_len) = struct.unpack_from(cls._PACK_STR,
5319 six.binary_type(buf))
5320 rest = buf[struct.calcsize(cls._PACK_STR):]
5321 binopts = rest[:opt_param_len]
5324 opt, binopts = _OptParam.parser(binopts)
5325 opt_param.extend(opt)
5329 "hold_time": hold_time,
5330 "bgp_identifier": addrconv.ipv4.bin_to_text(bgp_identifier),
5331 "opt_param_len": opt_param_len,
5332 "opt_param": opt_param,
5335 def serialize_tail(self):
5337 self.version = _VERSION
5338 binopts = bytearray()
5339 for opt in self.opt_param:
5340 binopts += opt.serialize()
5341 self.opt_param_len = len(binopts)
5343 msg = bytearray(struct.pack(self._PACK_STR,
5347 addrconv.ipv4.text_to_bin(
5348 self.bgp_identifier),
5349 self.opt_param_len))
5354 @BGPMessage.register_type(BGP_MSG_UPDATE)
5355 class BGPUpdate(BGPMessage):
5356 """BGP-4 UPDATE Message encoder/decoder class.
5358 An instance has the following attributes at least.
5359 Most of them are same to the on-wire counterparts but in host byte
5361 __init__ takes the corresponding args in this order.
5363 .. tabularcolumns:: |l|L|
5365 ========================== ===============================================
5366 Attribute Description
5367 ========================== ===============================================
5368 marker Marker field. Ignored when encoding.
5369 len Length field. Ignored when encoding.
5371 withdrawn_routes_len Withdrawn Routes Length field.
5372 Ignored when encoding.
5373 withdrawn_routes Withdrawn Routes field.
5374 A list of BGPWithdrawnRoute instances.
5376 total_path_attribute_len Total Path Attribute Length field.
5377 Ignored when encoding.
5378 path_attributes Path Attributes field.
5379 A list of BGPPathAttribute instances.
5381 nlri Network Layer Reachability Information field.
5382 A list of BGPNLRI instances.
5384 ========================== ===============================================
5387 _MIN_LEN = BGPMessage._HDR_LEN
5389 def __init__(self, type_=BGP_MSG_UPDATE,
5390 withdrawn_routes_len=None,
5391 withdrawn_routes=None,
5392 total_path_attribute_len=None,
5393 path_attributes=None,
5395 len_=None, marker=None):
5396 withdrawn_routes = withdrawn_routes if withdrawn_routes else []
5397 path_attributes = path_attributes if path_attributes else []
5398 nlri = nlri if nlri else []
5399 super(BGPUpdate, self).__init__(marker=marker, len_=len_, type_=type_)
5400 self.withdrawn_routes_len = withdrawn_routes_len
5401 self.withdrawn_routes = withdrawn_routes
5402 self.total_path_attribute_len = total_path_attribute_len
5403 self.path_attributes = path_attributes
5407 def pathattr_map(self):
5409 for attr in self.path_attributes:
5410 passattr_map[attr.type] = attr
5413 def get_path_attr(self, attr_name):
5414 return self.pathattr_map.get(attr_name)
5417 def parser(cls, buf):
5419 buf = six.binary_type(buf)
5420 (withdrawn_routes_len,) = struct.unpack_from('!H', buf, offset)
5421 binroutes = buf[offset + 2:
5422 offset + 2 + withdrawn_routes_len]
5423 offset += 2 + withdrawn_routes_len
5424 (total_path_attribute_len,) = struct.unpack_from('!H', buf, offset)
5425 binpathattrs = buf[offset + 2:
5426 offset + 2 + total_path_attribute_len]
5427 binnlri = buf[offset + 2 + total_path_attribute_len:]
5428 withdrawn_routes = []
5430 r, binroutes = BGPWithdrawnRoute.parser(binroutes)
5431 withdrawn_routes.append(r)
5432 path_attributes = []
5434 pa, binpathattrs = _PathAttribute.parser(binpathattrs)
5435 path_attributes.append(pa)
5436 offset += 2 + total_path_attribute_len
5439 n, binnlri = BGPNLRI.parser(binnlri)
5442 "withdrawn_routes_len": withdrawn_routes_len,
5443 "withdrawn_routes": withdrawn_routes,
5444 "total_path_attribute_len": total_path_attribute_len,
5445 "path_attributes": path_attributes,
5449 def serialize_tail(self):
5451 binroutes = bytearray()
5452 for r in self.withdrawn_routes:
5453 binroutes += r.serialize()
5454 self.withdrawn_routes_len = len(binroutes)
5455 binpathattrs = bytearray()
5456 for pa in self.path_attributes:
5457 binpathattrs += pa.serialize()
5458 self.total_path_attribute_len = len(binpathattrs)
5459 binnlri = bytearray()
5461 binnlri += n.serialize()
5465 msg_pack_into('!H', msg, offset, self.withdrawn_routes_len)
5467 offset += 2 + self.withdrawn_routes_len
5468 msg_pack_into('!H', msg, offset, self.total_path_attribute_len)
5470 offset += 2 + self.total_path_attribute_len
5475 @BGPMessage.register_type(BGP_MSG_KEEPALIVE)
5476 class BGPKeepAlive(BGPMessage):
5477 """BGP-4 KEEPALIVE Message encoder/decoder class.
5479 An instance has the following attributes at least.
5480 Most of them are same to the on-wire counterparts but in host byte
5482 __init__ takes the corresponding args in this order.
5484 ========================== ===============================================
5485 Attribute Description
5486 ========================== ===============================================
5487 marker Marker field. Ignored when encoding.
5488 len Length field. Ignored when encoding.
5490 ========================== ===============================================
5493 _MIN_LEN = BGPMessage._HDR_LEN
5495 def __init__(self, type_=BGP_MSG_KEEPALIVE, len_=None, marker=None):
5496 super(BGPKeepAlive, self).__init__(marker=marker, len_=len_,
5500 def parser(cls, buf):
5503 def serialize_tail(self):
5507 @BGPMessage.register_type(BGP_MSG_NOTIFICATION)
5508 class BGPNotification(BGPMessage):
5509 """BGP-4 NOTIFICATION Message encoder/decoder class.
5511 An instance has the following attributes at least.
5512 Most of them are same to the on-wire counterparts but in host byte
5514 __init__ takes the corresponding args in this order.
5516 ========================== ===============================================
5517 Attribute Description
5518 ========================== ===============================================
5519 marker Marker field. Ignored when encoding.
5520 len Length field. Ignored when encoding.
5522 error_code Error code field.
5523 error_subcode Error subcode field.
5525 ========================== ===============================================
5529 _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR)
5532 (1, 1): 'Message Header Error: not synchronised',
5533 (1, 2): 'Message Header Error: bad message len',
5534 (1, 3): 'Message Header Error: bad message type',
5535 (2, 1): 'Open Message Error: unsupported version',
5536 (2, 2): 'Open Message Error: bad peer AS',
5537 (2, 3): 'Open Message Error: bad BGP identifier',
5538 (2, 4): 'Open Message Error: unsupported optional param',
5539 (2, 5): 'Open Message Error: authentication failure',
5540 (2, 6): 'Open Message Error: unacceptable hold time',
5541 (2, 7): 'Open Message Error: Unsupported Capability',
5542 (2, 8): 'Open Message Error: Unassigned',
5543 (3, 1): 'Update Message Error: malformed attribute list',
5544 (3, 2): 'Update Message Error: unrecognized well-known attr',
5545 (3, 3): 'Update Message Error: missing well-known attr',
5546 (3, 4): 'Update Message Error: attribute flags error',
5547 (3, 5): 'Update Message Error: attribute length error',
5548 (3, 6): 'Update Message Error: invalid origin attr',
5549 (3, 7): 'Update Message Error: as routing loop',
5550 (3, 8): 'Update Message Error: invalid next hop attr',
5551 (3, 9): 'Update Message Error: optional attribute error',
5552 (3, 10): 'Update Message Error: invalid network field',
5553 (3, 11): 'Update Message Error: malformed AS_PATH',
5554 (4, 1): 'Hold Timer Expired',
5555 (5, 1): 'Finite State Machine Error',
5556 (6, 1): 'Cease: Maximum Number of Prefixes Reached',
5557 (6, 2): 'Cease: Administrative Shutdown',
5558 (6, 3): 'Cease: Peer De-configured',
5559 (6, 4): 'Cease: Administrative Reset',
5560 (6, 5): 'Cease: Connection Rejected',
5561 (6, 6): 'Cease: Other Configuration Change',
5562 (6, 7): 'Cease: Connection Collision Resolution',
5563 (6, 8): 'Cease: Out of Resources',
5570 type_=BGP_MSG_NOTIFICATION, len_=None, marker=None):
5571 super(BGPNotification, self).__init__(marker=marker, len_=len_,
5573 self.error_code = error_code
5574 self.error_subcode = error_subcode
5578 def parser(cls, buf):
5579 (error_code, error_subcode,) = struct.unpack_from(cls._PACK_STR,
5580 six.binary_type(buf))
5581 data = bytes(buf[2:])
5583 "error_code": error_code,
5584 "error_subcode": error_subcode,
5588 def serialize_tail(self):
5589 msg = bytearray(struct.pack(self._PACK_STR, self.error_code,
5590 self.error_subcode))
5596 return self._REASONS.get((self.error_code, self.error_subcode))
5599 @BGPMessage.register_type(BGP_MSG_ROUTE_REFRESH)
5600 class BGPRouteRefresh(BGPMessage):
5601 """BGP-4 ROUTE REFRESH Message (RFC 2918) encoder/decoder class.
5603 An instance has the following attributes at least.
5604 Most of them are same to the on-wire counterparts but in host byte
5606 __init__ takes the corresponding args in this order.
5608 ========================== ===============================================
5609 Attribute Description
5610 ========================== ===============================================
5611 marker Marker field. Ignored when encoding.
5612 len Length field. Ignored when encoding.
5614 afi Address Family Identifier
5615 safi Subsequent Address Family Identifier
5616 ========================== ===============================================
5620 _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR)
5623 afi, safi, demarcation=0,
5624 type_=BGP_MSG_ROUTE_REFRESH, len_=None, marker=None):
5625 super(BGPRouteRefresh, self).__init__(marker=marker, len_=len_,
5629 self.demarcation = demarcation
5630 self.eor_sent = False
5633 def parser(cls, buf):
5634 (afi, demarcation, safi,) = struct.unpack_from(cls._PACK_STR,
5635 six.binary_type(buf))
5639 "demarcation": demarcation,
5642 def serialize_tail(self):
5643 return bytearray(struct.pack(self._PACK_STR, self.afi,
5644 self.demarcation, self.safi))
5647 class StreamParser(stream_parser.StreamParser):
5648 """Streaming parser for BGP-4 messages.
5650 This is a subclass of ryu.lib.packet.stream_parser.StreamParser.
5651 Its parse method returns a list of BGPMessage subclass instances.
5654 def try_parse(self, data):
5655 msg, _, rest = BGPMessage.parser(data)