1# Copyright (C) 2013,2014 Nippon Telegraph and Telephone Corporation. 2# Copyright (C) 2013,2014 YAMAMOTO Takashi <yamamoto at valinux co jp> 3# 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 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 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 13# implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17""" 18RFC 4271 BGP-4 19""" 20 21# todo 22# - notify data 23# - RFC 4364 BGP/MPLS IP Virtual Private Networks (VPNs) 24 25import abc 26import base64 27import collections 28import copy 29import functools 30import io 31import itertools 32import math 33import re 34import socket 35import struct 36 37import netaddr 38import six 39 40from ryu.lib.stringify import StringifyMixin 41from ryu.lib.packet import afi as addr_family 42from ryu.lib.packet import safi as subaddr_family 43from ryu.lib.packet import packet_base 44from ryu.lib.packet import stream_parser 45from ryu.lib.packet import vxlan 46from ryu.lib.packet import mpls 47from ryu.lib import addrconv 48from ryu.lib import type_desc 49from ryu.lib.type_desc import TypeDisp 50from ryu.lib import ip 51from ryu.lib.pack_utils import msg_pack_into 52from ryu.utils import binary_str 53from ryu.utils import import_module 54 55reduce = six.moves.reduce 56 57TCP_SERVER_PORT = 179 58 59BGP_MSG_OPEN = 1 60BGP_MSG_UPDATE = 2 61BGP_MSG_NOTIFICATION = 3 62BGP_MSG_KEEPALIVE = 4 63BGP_MSG_ROUTE_REFRESH = 5 # RFC 2918 64 65_VERSION = 4 66_MARKER = 16 * b'\xff' 67 68BGP_OPT_CAPABILITY = 2 # RFC 5492 69 70BGP_CAP_MULTIPROTOCOL = 1 # RFC 4760 71BGP_CAP_ROUTE_REFRESH = 2 # RFC 2918 72BGP_CAP_CARRYING_LABEL_INFO = 4 # RFC 3107 73BGP_CAP_GRACEFUL_RESTART = 64 # RFC 4724 74BGP_CAP_FOUR_OCTET_AS_NUMBER = 65 # RFC 4893 75BGP_CAP_ENHANCED_ROUTE_REFRESH = 70 # https://tools.ietf.org/html/\ 76# draft-ietf-idr-bgp-enhanced-route-refresh-05 77BGP_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). 80 81BGP_ATTR_FLAG_OPTIONAL = 1 << 7 82BGP_ATTR_FLAG_TRANSITIVE = 1 << 6 83BGP_ATTR_FLAG_PARTIAL = 1 << 5 84BGP_ATTR_FLAG_EXTENDED_LENGTH = 1 << 4 85 86BGP_ATTR_TYPE_ORIGIN = 1 # 0,1,2 (1 byte) 87BGP_ATTR_TYPE_AS_PATH = 2 # a list of AS_SET/AS_SEQUENCE eg. {1 2 3} 4 5 88BGP_ATTR_TYPE_NEXT_HOP = 3 # an IPv4 address 89BGP_ATTR_TYPE_MULTI_EXIT_DISC = 4 # uint32 metric 90BGP_ATTR_TYPE_LOCAL_PREF = 5 # uint32 91BGP_ATTR_TYPE_ATOMIC_AGGREGATE = 6 # 0 bytes 92BGP_ATTR_TYPE_AGGREGATOR = 7 # AS number and IPv4 address 93BGP_ATTR_TYPE_COMMUNITIES = 8 # RFC 1997 94BGP_ATTR_TYPE_ORIGINATOR_ID = 9 # RFC 4456 95BGP_ATTR_TYPE_CLUSTER_LIST = 10 # RFC 4456 96BGP_ATTR_TYPE_MP_REACH_NLRI = 14 # RFC 4760 97BGP_ATTR_TYPE_MP_UNREACH_NLRI = 15 # RFC 4760 98BGP_ATTR_TYPE_EXTENDED_COMMUNITIES = 16 # RFC 4360 99BGP_ATTR_TYPE_AS4_PATH = 17 # RFC 4893 100BGP_ATTR_TYPE_AS4_AGGREGATOR = 18 # RFC 4893 101BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE = 22 # RFC 6514 102 103BGP_ATTR_ORIGIN_IGP = 0x00 104BGP_ATTR_ORIGIN_EGP = 0x01 105BGP_ATTR_ORIGIN_INCOMPLETE = 0x02 106 107AS_TRANS = 23456 # RFC 4893 108 109# Well known commmunities (RFC 1997) 110BGP_COMMUNITY_NO_EXPORT = 0xffffff01 111BGP_COMMUNITY_NO_ADVERTISE = 0xffffff02 112BGP_COMMUNITY_NO_EXPORT_SUBCONFED = 0xffffff03 113 114# RFC 4360 115# The low-order octet of Type field (subtype) 116BGP_EXTENDED_COMMUNITY_ROUTE_TARGET = 0x02 117BGP_EXTENDED_COMMUNITY_ROUTE_ORIGIN = 0x03 118 119# NOTIFICATION Error Code and SubCode 120# Note: 0 is a valid SubCode. (Unspecific) 121 122# NOTIFICATION Error Code RFC 4271 4.5. 123BGP_ERROR_MESSAGE_HEADER_ERROR = 1 124BGP_ERROR_OPEN_MESSAGE_ERROR = 2 125BGP_ERROR_UPDATE_MESSAGE_ERROR = 3 126BGP_ERROR_HOLD_TIMER_EXPIRED = 4 127BGP_ERROR_FSM_ERROR = 5 128BGP_ERROR_CEASE = 6 129 130# NOTIFICATION Error Subcode for BGP_ERROR_MESSAGE_HEADER_ERROR 131BGP_ERROR_SUB_CONNECTION_NOT_SYNCHRONIZED = 1 132BGP_ERROR_SUB_BAD_MESSAGE_LENGTH = 2 # Data: the erroneous Length field 133BGP_ERROR_SUB_BAD_MESSAGE_TYPE = 3 # Data: the erroneous Type field 134 135# NOTIFICATION Error Subcode for BGP_ERROR_OPEN_MESSAGE_ERROR 136BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER = 1 # Data: 2 octet version number 137BGP_ERROR_SUB_BAD_PEER_AS = 2 138BGP_ERROR_SUB_BAD_BGP_IDENTIFIER = 3 139BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER = 4 140BGP_ERROR_SUB_AUTHENTICATION_FAILURE = 5 # deprecated RFC 1771 141BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME = 6 142 143# NOTIFICATION Error Subcode for BGP_ERROR_UPDATE_MESSAGE_ERROR 144BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST = 1 145BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE = 2 # Data: type of the attr 146BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE = 3 # Data: ditto 147BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR = 4 # Data: the attr (type, len, value) 148BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR = 5 # Data: ditto 149BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE = 6 # Data: ditto 150BGP_ERROR_SUB_ROUTING_LOOP = 7 # deprecated RFC 1771 AS Routing Loop 151BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE = 8 # Data: ditto 152BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR = 9 # Data: ditto 153BGP_ERROR_SUB_INVALID_NETWORK_FIELD = 10 154BGP_ERROR_SUB_MALFORMED_AS_PATH = 11 155 156# NOTIFICATION Error Subcode for BGP_ERROR_HOLD_TIMER_EXPIRED 157BGP_ERROR_SUB_HOLD_TIMER_EXPIRED = 1 158 159# NOTIFICATION Error Subcode for BGP_ERROR_FSM_ERROR 160BGP_ERROR_SUB_FSM_ERROR = 1 161 162# NOTIFICATION Error Subcode for BGP_ERROR_CEASE (RFC 4486) 163BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED = 1 # Data: optional 164BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN = 2 165BGP_ERROR_SUB_PEER_DECONFIGURED = 3 166BGP_ERROR_SUB_ADMINISTRATIVE_RESET = 4 167BGP_ERROR_SUB_CONNECTION_RESET = 5 168BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE = 6 169BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION = 7 170BGP_ERROR_SUB_OUT_OF_RESOURCES = 8 171 172 173class _Value(object): 174 _VALUE_PACK_STR = None 175 _VALUE_FIELDS = ['value'] 176 177 @staticmethod 178 def do_init(cls_type, self, kwargs, **extra_kwargs): 179 ourfields = {} 180 for f in cls_type._VALUE_FIELDS: 181 v = kwargs[f] 182 del kwargs[f] 183 ourfields[f] = v 184 kwargs.update(extra_kwargs) 185 super(cls_type, self).__init__(**kwargs) 186 self.__dict__.update(ourfields) 187 188 @classmethod 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)) 192 193 def serialize_value(self): 194 args = [] 195 for f in self._VALUE_FIELDS: 196 args.append(getattr(self, f)) 197 return struct.pack(self._VALUE_PACK_STR, *args) 198 199 200class BgpExc(Exception): 201 """Base bgp exception.""" 202 203 CODE = 0 204 """BGP error code.""" 205 206 SUB_CODE = 0 207 """BGP error sub-code.""" 208 209 SEND_ERROR = True 210 """Flag if set indicates Notification message should be sent to peer.""" 211 212 def __init__(self, data=''): 213 super(BgpExc, self).__init__() 214 self.data = data 215 216 def __str__(self): 217 return '<%s %r>' % (self.__class__.__name__, self.data) 218 219 220class BadNotification(BgpExc): 221 SEND_ERROR = False 222 223# ============================================================================ 224# Message Header Errors 225# ============================================================================ 226 227 228class NotSync(BgpExc): 229 CODE = BGP_ERROR_MESSAGE_HEADER_ERROR 230 SUB_CODE = BGP_ERROR_SUB_CONNECTION_NOT_SYNCHRONIZED 231 232 233class BadLen(BgpExc): 234 CODE = BGP_ERROR_MESSAGE_HEADER_ERROR 235 SUB_CODE = BGP_ERROR_SUB_BAD_MESSAGE_LENGTH 236 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) 242 243 def __str__(self): 244 return '<BadLen %d msgtype=%d>' % (self.length, self.msg_type_code) 245 246 247class BadMsg(BgpExc): 248 """Error to indicate un-recognized message type. 249 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. 253 """ 254 CODE = BGP_ERROR_MESSAGE_HEADER_ERROR 255 SUB_CODE = BGP_ERROR_SUB_BAD_MESSAGE_TYPE 256 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) 261 262 def __str__(self): 263 return '<BadMsg %d>' % (self.msg_type,) 264 265# ============================================================================ 266# OPEN Message Errors 267# ============================================================================ 268 269 270class MalformedOptionalParam(BgpExc): 271 """If recognized optional parameters are malformed. 272 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 275 (Unspecific). 276 """ 277 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR 278 SUB_CODE = 0 279 280 281class UnsupportedVersion(BgpExc): 282 """Error to indicate unsupport bgp version number. 283 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. 292 """ 293 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR 294 SUB_CODE = BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER 295 296 def __init__(self, locally_support_version): 297 super(UnsupportedVersion, self).__init__() 298 self.data = struct.pack('H', locally_support_version) 299 300 301class BadPeerAs(BgpExc): 302 """Error to indicate open message has incorrect AS number. 303 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. 307 """ 308 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR 309 SUB_CODE = BGP_ERROR_SUB_BAD_PEER_AS 310 311 312class BadBgpId(BgpExc): 313 """Error to indicate incorrect BGP Identifier. 314 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. 319 """ 320 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR 321 SUB_CODE = BGP_ERROR_SUB_BAD_BGP_IDENTIFIER 322 323 324class UnsupportedOptParam(BgpExc): 325 """Error to indicate unsupported optional parameters. 326 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 329 Parameters. 330 """ 331 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR 332 SUB_CODE = BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER 333 334 335class AuthFailure(BgpExc): 336 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR 337 SUB_CODE = BGP_ERROR_SUB_AUTHENTICATION_FAILURE 338 339 340class UnacceptableHoldTime(BgpExc): 341 """Error to indicate Unacceptable Hold Time in open message. 342 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. 345 """ 346 CODE = BGP_ERROR_OPEN_MESSAGE_ERROR 347 SUB_CODE = BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME 348 349# ============================================================================ 350# UPDATE message related errors 351# ============================================================================ 352 353 354class MalformedAttrList(BgpExc): 355 """Error to indicate UPDATE message is malformed. 356 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. 362 """ 363 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR 364 SUB_CODE = BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST 365 366 367class UnRegWellKnowAttr(BgpExc): 368 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR 369 SUB_CODE = BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE 370 371 372class MissingWellKnown(BgpExc): 373 """Error to indicate missing well-known attribute. 374 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 378 attribute. 379 """ 380 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR 381 SUB_CODE = BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE 382 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) 387 388 389class AttrFlagError(BgpExc): 390 """Error to indicate recognized path attributes have incorrect flags. 391 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). 396 """ 397 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR 398 SUB_CODE = BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR 399 400 401class AttrLenError(BgpExc): 402 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR 403 SUB_CODE = BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR 404 405 406class InvalidOriginError(BgpExc): 407 """Error indicates undefined Origin attribute value. 408 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). 412 """ 413 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR 414 SUB_CODE = BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE 415 416 417class RoutingLoop(BgpExc): 418 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR 419 SUB_CODE = BGP_ERROR_SUB_ROUTING_LOOP 420 421 422class InvalidNextHop(BgpExc): 423 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR 424 SUB_CODE = BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE 425 426 427class OptAttrError(BgpExc): 428 """Error indicates Optional Attribute is malformed. 429 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). 434 """ 435 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR 436 SUB_CODE = BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR 437 438 439class InvalidNetworkField(BgpExc): 440 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR 441 SUB_CODE = BGP_ERROR_SUB_INVALID_NETWORK_FIELD 442 443 444class MalformedAsPath(BgpExc): 445 """Error to indicate if AP_PATH attribute is syntactically incorrect. 446 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 449 Malformed AS_PATH. 450 """ 451 CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR 452 SUB_CODE = BGP_ERROR_SUB_MALFORMED_AS_PATH 453 454 455# ============================================================================ 456# Hold Timer Expired 457# ============================================================================ 458 459 460class HoldTimerExpired(BgpExc): 461 """Error to indicate Hold Timer expired. 462 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. 467 """ 468 CODE = BGP_ERROR_HOLD_TIMER_EXPIRED 469 SUB_CODE = BGP_ERROR_SUB_HOLD_TIMER_EXPIRED 470 471# ============================================================================ 472# Finite State Machine Error 473# ============================================================================ 474 475 476class FiniteStateMachineError(BgpExc): 477 """Error to indicate any Finite State Machine Error. 478 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. 482 """ 483 CODE = BGP_ERROR_FSM_ERROR 484 SUB_CODE = BGP_ERROR_SUB_FSM_ERROR 485 486 487# ============================================================================ 488# Cease Errors 489# ============================================================================ 490 491class MaxPrefixReached(BgpExc): 492 CODE = BGP_ERROR_CEASE 493 SUB_CODE = BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED 494 495 496class AdminShutdown(BgpExc): 497 """Error to indicate Administrative shutdown. 498 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 502 Shutdown'. 503 """ 504 CODE = BGP_ERROR_CEASE 505 SUB_CODE = BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN 506 507 508class PeerDeConfig(BgpExc): 509 CODE = BGP_ERROR_CEASE 510 SUB_CODE = BGP_ERROR_SUB_PEER_DECONFIGURED 511 512 513class AdminReset(BgpExc): 514 CODE = BGP_ERROR_CEASE 515 SUB_CODE = BGP_ERROR_SUB_ADMINISTRATIVE_RESET 516 517 518class ConnRejected(BgpExc): 519 """Error to indicate Connection Rejected. 520 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 525 Rejected". 526 """ 527 CODE = BGP_ERROR_CEASE 528 SUB_CODE = BGP_ERROR_SUB_CONNECTION_RESET 529 530 531class OtherConfChange(BgpExc): 532 CODE = BGP_ERROR_CEASE 533 SUB_CODE = BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE 534 535 536class CollisionResolution(BgpExc): 537 """Error to indicate Connection Collision Resolution. 538 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". 543 """ 544 CODE = BGP_ERROR_CEASE 545 SUB_CODE = BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION 546 547 548class OutOfResource(BgpExc): 549 CODE = BGP_ERROR_CEASE 550 SUB_CODE = BGP_ERROR_SUB_OUT_OF_RESOURCES 551 552 553@functools.total_ordering 554class RouteFamily(StringifyMixin): 555 def __init__(self, afi, safi): 556 self.afi = afi 557 self.safi = safi 558 559 def __lt__(self, other): 560 return (self.afi, self.safi) < (other.afi, other.safi) 561 562 def __eq__(self, other): 563 return (self.afi, self.safi) == (other.afi, other.safi) 564 565 def __hash__(self): 566 return hash((self.afi, self.safi)) 567 568 569# Route Family Singleton 570RF_IPv4_UC = RouteFamily(addr_family.IP, subaddr_family.UNICAST) 571RF_IPv6_UC = RouteFamily(addr_family.IP6, subaddr_family.UNICAST) 572RF_IPv4_VPN = RouteFamily(addr_family.IP, subaddr_family.MPLS_VPN) 573RF_IPv6_VPN = RouteFamily(addr_family.IP6, subaddr_family.MPLS_VPN) 574RF_IPv4_MPLS = RouteFamily(addr_family.IP, subaddr_family.MPLS_LABEL) 575RF_IPv6_MPLS = RouteFamily(addr_family.IP6, subaddr_family.MPLS_LABEL) 576RF_L2_EVPN = RouteFamily(addr_family.L2VPN, subaddr_family.EVPN) 577RF_IPv4_FLOWSPEC = RouteFamily(addr_family.IP, subaddr_family.IP_FLOWSPEC) 578RF_IPv6_FLOWSPEC = RouteFamily(addr_family.IP6, subaddr_family.IP_FLOWSPEC) 579RF_VPNv4_FLOWSPEC = RouteFamily(addr_family.IP, subaddr_family.VPN_FLOWSPEC) 580RF_VPNv6_FLOWSPEC = RouteFamily(addr_family.IP6, subaddr_family.VPN_FLOWSPEC) 581RF_L2VPN_FLOWSPEC = RouteFamily( 582 addr_family.L2VPN, subaddr_family.VPN_FLOWSPEC) 583RF_RTC_UC = RouteFamily(addr_family.IP, 584 subaddr_family.ROUTE_TARGET_CONSTRAINTS) 585 586_rf_map = { 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 600} 601 602 603def get_rf(afi, safi): 604 return _rf_map[(afi, safi)] 605 606 607def pad(binary, len_): 608 assert len(binary) <= len_ 609 return binary + b'\0' * (len_ - len(binary)) 610 611 612class _RouteDistinguisher(StringifyMixin, TypeDisp, _Value): 613 _PACK_STR = '!H' 614 TWO_OCTET_AS = 0 615 IPV4_ADDRESS = 1 616 FOUR_OCTET_AS = 2 617 618 def __init__(self, admin=0, assigned=0, type_=None): 619 if type_ is None: 620 type_ = self._rev_lookup_type(self.__class__) 621 self.type = type_ 622 self.admin = admin 623 self.assigned = assigned 624 625 @classmethod 626 def parser(cls, buf): 627 assert len(buf) == 8 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)) 632 633 @classmethod 634 def from_str(cls, str_): 635 assert isinstance(str_, str) 636 637 first, second = str_.split(':') 638 if '.' in first: 639 type_ = cls.IPV4_ADDRESS 640 elif int(first) > (1 << 16): 641 type_ = cls.FOUR_OCTET_AS 642 first = int(first) 643 else: 644 type_ = cls.TWO_OCTET_AS 645 first = int(first) 646 subcls = cls._lookup_type(type_) 647 return subcls(admin=first, assigned=int(second)) 648 649 def serialize(self): 650 value = self.serialize_value() 651 buf = bytearray() 652 msg_pack_into(self._PACK_STR, buf, 0, self.type) 653 return six.binary_type(buf + value) 654 655 @property 656 def formatted_str(self): 657 return "%s:%s" % (self.admin, self.assigned) 658 659 660@_RouteDistinguisher.register_type(_RouteDistinguisher.TWO_OCTET_AS) 661class BGPTwoOctetAsRD(_RouteDistinguisher): 662 _VALUE_PACK_STR = '!HI' 663 _VALUE_FIELDS = ['admin', 'assigned'] 664 665 def __init__(self, **kwargs): 666 super(BGPTwoOctetAsRD, self).__init__() 667 self.do_init(BGPTwoOctetAsRD, self, kwargs) 668 669 670@_RouteDistinguisher.register_type(_RouteDistinguisher.IPV4_ADDRESS) 671class BGPIPv4AddressRD(_RouteDistinguisher): 672 _VALUE_PACK_STR = '!4sH' 673 _VALUE_FIELDS = ['admin', 'assigned'] 674 _TYPE = { 675 'ascii': [ 676 'admin' 677 ] 678 } 679 680 def __init__(self, **kwargs): 681 super(BGPIPv4AddressRD, self).__init__() 682 self.do_init(BGPIPv4AddressRD, self, kwargs) 683 684 @classmethod 685 def parse_value(cls, buf): 686 d_ = super(BGPIPv4AddressRD, cls).parse_value(buf) 687 d_['admin'] = addrconv.ipv4.bin_to_text(d_['admin']) 688 return d_ 689 690 def serialize_value(self): 691 args = [] 692 for f in self._VALUE_FIELDS: 693 v = getattr(self, f) 694 if f == 'admin': 695 v = bytes(addrconv.ipv4.text_to_bin(v)) 696 args.append(v) 697 buf = bytearray() 698 msg_pack_into(self._VALUE_PACK_STR, buf, 0, *args) 699 return buf 700 701 702@_RouteDistinguisher.register_type(_RouteDistinguisher.FOUR_OCTET_AS) 703class BGPFourOctetAsRD(_RouteDistinguisher): 704 _VALUE_PACK_STR = '!IH' 705 _VALUE_FIELDS = ['admin', 'assigned'] 706 707 def __init__(self, **kwargs): 708 super(BGPFourOctetAsRD, self).__init__() 709 self.do_init(BGPFourOctetAsRD, self, kwargs) 710 711 712@six.add_metaclass(abc.ABCMeta) 713class _AddrPrefix(StringifyMixin): 714 _PACK_STR = '!B' # length 715 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 722 (addr,) = addr 723 self.length = length 724 if prefixes: 725 addr = prefixes + (addr,) 726 self.addr = addr 727 728 @classmethod 729 @abc.abstractmethod 730 def _to_bin(cls, addr): 731 pass 732 733 @classmethod 734 @abc.abstractmethod 735 def _from_bin(cls, addr): 736 pass 737 738 @classmethod 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 746 747 def serialize(self): 748 # fixup 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] 753 else: 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) 761 762 buf = bytearray() 763 msg_pack_into(self._PACK_STR, buf, 0, self.length) 764 return buf + bytes(bin_addr) 765 766 767class _BinAddrPrefix(_AddrPrefix): 768 @classmethod 769 def _to_bin(cls, addr): 770 return addr 771 772 @classmethod 773 def _from_bin(cls, addr): 774 return addr 775 776 777class _LabelledAddrPrefix(_AddrPrefix): 778 _LABEL_PACK_STR = '!3B' 779 # RFC3107 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.) 784 # 785 _WITHDRAW_LABEL = 0x800000 786 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) 791 if is_tuple: 792 # for _AddrPrefix.parser 793 assert not labels 794 labels = addr[0] 795 addr = addr[1:] 796 else: 797 length += struct.calcsize(self._LABEL_PACK_STR) * 8 * len(labels) 798 assert length > struct.calcsize(self._LABEL_PACK_STR) * 8 * len(labels) 799 prefixes = (labels,) 800 super(_LabelledAddrPrefix, self).__init__(prefixes=prefixes, 801 length=length, 802 addr=addr, 803 **kwargs) 804 805 @classmethod 806 def _label_to_bin(cls, label): 807 buf = bytearray() 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) 813 814 @classmethod 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 820 821 @classmethod 822 def _to_bin(cls, addr): 823 labels = addr[0] 824 rest = addr[1:] 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)) 831 832 @classmethod 833 def _has_no_label(cls, bin_): 834 try: 835 length = len(bin_) 836 labels = [] 837 while True: 838 (label, bin_) = cls._label_from_bin(bin_) 839 labels.append(label) 840 if label & 1 or label == cls._WITHDRAW_LABEL: 841 break 842 assert length > struct.calcsize(cls._LABEL_PACK_STR) * len(labels) 843 except struct.error: 844 return True 845 except AssertionError: 846 return True 847 return False 848 849 @classmethod 850 def _from_bin(cls, addr): 851 rest = addr 852 labels = [] 853 854 if cls._has_no_label(rest): 855 return ([],) + cls._prefix_from_bin(rest) 856 857 while True: 858 (label, rest) = cls._label_from_bin(rest) 859 labels.append(label >> 4) 860 if label & 1 or label == cls._WITHDRAW_LABEL: 861 break 862 return (labels,) + cls._prefix_from_bin(rest) 863 864 865class _UnlabelledAddrPrefix(_AddrPrefix): 866 @classmethod 867 def _to_bin(cls, addr): 868 return cls._prefix_to_bin((addr,)) 869 870 @classmethod 871 def _from_bin(cls, binaddr): 872 (addr,) = cls._prefix_from_bin(binaddr) 873 return addr 874 875 876class _IPAddrPrefix(_AddrPrefix): 877 @staticmethod 878 def _prefix_to_bin(addr): 879 (addr,) = addr 880 return addrconv.ipv4.text_to_bin(addr) 881 882 @staticmethod 883 def _prefix_from_bin(addr): 884 return addrconv.ipv4.bin_to_text(pad(addr, 4)), 885 886 887class _IP6AddrPrefix(_AddrPrefix): 888 @staticmethod 889 def _prefix_to_bin(addr): 890 (addr,) = addr 891 return addrconv.ipv6.text_to_bin(addr) 892 893 @staticmethod 894 def _prefix_from_bin(addr): 895 return addrconv.ipv6.bin_to_text(pad(addr, 16)), 896 897 898class _VPNAddrPrefix(_AddrPrefix): 899 _RD_PACK_STR = '!Q' 900 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 906 route_dist = addr[0] 907 addr = addr[1:] 908 else: 909 length += struct.calcsize(self._RD_PACK_STR) * 8 910 911 if isinstance(route_dist, str): 912 route_dist = _RouteDistinguisher.from_str(route_dist) 913 914 prefixes = prefixes + (route_dist,) 915 super(_VPNAddrPrefix, self).__init__(prefixes=prefixes, 916 length=length, 917 addr=addr) 918 919 @classmethod 920 def _prefix_to_bin(cls, addr): 921 rd = addr[0] 922 rest = addr[1:] 923 binrd = rd.serialize() 924 return binrd + super(_VPNAddrPrefix, cls)._prefix_to_bin(rest) 925 926 @classmethod 927 def _prefix_from_bin(cls, binaddr): 928 binrd = binaddr[:8] 929 binrest = binaddr[8:] 930 rd = _RouteDistinguisher.parser(binrd) 931 return (rd,) + super(_VPNAddrPrefix, cls)._prefix_from_bin(binrest) 932 933 934class IPAddrPrefix(_UnlabelledAddrPrefix, _IPAddrPrefix): 935 ROUTE_FAMILY = RF_IPv4_UC 936 _TYPE = { 937 'ascii': [ 938 'addr' 939 ] 940 } 941 942 @property 943 def prefix(self): 944 return self.addr + '/{0}'.format(self.length) 945 946 @property 947 def formatted_nlri_str(self): 948 return self.prefix 949 950 951class IP6AddrPrefix(_UnlabelledAddrPrefix, _IP6AddrPrefix): 952 ROUTE_FAMILY = RF_IPv6_UC 953 _TYPE = { 954 'ascii': [ 955 'addr' 956 ] 957 } 958 959 @property 960 def prefix(self): 961 return self.addr + '/{0}'.format(self.length) 962 963 @property 964 def formatted_nlri_str(self): 965 return self.prefix 966 967 968class LabelledIPAddrPrefix(_LabelledAddrPrefix, _IPAddrPrefix): 969 ROUTE_FAMILY = RF_IPv4_MPLS 970 971 972class LabelledIP6AddrPrefix(_LabelledAddrPrefix, _IP6AddrPrefix): 973 ROUTE_FAMILY = RF_IPv6_MPLS 974 975 976class LabelledVPNIPAddrPrefix(_LabelledAddrPrefix, _VPNAddrPrefix, 977 _IPAddrPrefix): 978 ROUTE_FAMILY = RF_IPv4_VPN 979 980 @property 981 def prefix(self): 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) 985 986 @property 987 def route_dist(self): 988 return self.addr[-2].formatted_str 989 990 @property 991 def label_list(self): 992 return self.addr[0] 993 994 @property 995 def formatted_nlri_str(self): 996 return "%s:%s" % (self.route_dist, self.prefix) 997 998 999class LabelledVPNIP6AddrPrefix(_LabelledAddrPrefix, _VPNAddrPrefix, 1000 _IP6AddrPrefix): 1001 ROUTE_FAMILY = RF_IPv6_VPN 1002 1003 @property 1004 def prefix(self): 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) 1008 1009 @property 1010 def route_dist(self): 1011 return self.addr[-2].formatted_str 1012 1013 @property 1014 def label_list(self): 1015 return self.addr[0] 1016 1017 @property 1018 def formatted_nlri_str(self): 1019 return "%s:%s" % (self.route_dist, self.prefix) 1020 1021 1022class EvpnEsi(StringifyMixin, TypeDisp, _Value): 1023 """ 1024 Ethernet Segment Identifier 1025 1026 The supported ESI Types: 1027 1028 - ``EvpnEsi.ARBITRARY`` indicates EvpnArbitraryEsi. 1029 1030 - ``EvpnEsi.LACP`` indicates EvpnLACPEsi. 1031 1032 - ``EvpnEsi.L2_BRIDGE`` indicates EvpnL2BridgeEsi. 1033 1034 - ``EvpnEsi.MAC_BASED`` indicates EvpnMacBasedEsi. 1035 1036 - ``EvpnEsi.ROUTER_ID`` indicates EvpnRouterIDEsi. 1037 1038 - ``EvpnEsi.AS_BASED`` indicates EvpnASBasedEsi. 1039 """ 1040 _PACK_STR = "!B" # ESI Type 1041 _ESI_LEN = 10 1042 1043 ARBITRARY = 0x00 1044 LACP = 0x01 1045 L2_BRIDGE = 0x02 1046 MAC_BASED = 0x03 1047 ROUTER_ID = 0x04 1048 AS_BASED = 0x05 1049 MAX = 0xff # Reserved 1050 1051 _TYPE_NAME = None # must be defined in subclass 1052 1053 def __init__(self, type_=None): 1054 if type_ is None: 1055 type_ = self._rev_lookup_type(self.__class__) 1056 self.type = type_ 1057 1058 @classmethod 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])) 1064 1065 def serialize(self): 1066 buf = bytearray() 1067 msg_pack_into(EvpnEsi._PACK_STR, buf, 0, self.type) 1068 return six.binary_type(buf + self.serialize_value()) 1069 1070 @property 1071 def formatted_str(self): 1072 return '%s(%s)' % ( 1073 self._TYPE_NAME, 1074 ','.join(str(getattr(self, v)) for v in self._VALUE_FIELDS)) 1075 1076 1077@EvpnEsi.register_unknown_type() 1078class EvpnUnknownEsi(EvpnEsi): 1079 """ 1080 ESI value for unknown type 1081 """ 1082 _TYPE_NAME = 'unknown' 1083 _VALUE_PACK_STR = '!9s' 1084 _VALUE_FIELDS = ['value'] 1085 1086 def __init__(self, value, type_=None): 1087 super(EvpnUnknownEsi, self).__init__(type_) 1088 self.value = value 1089 1090 @property 1091 def formatted_str(self): 1092 return '%s(%s)' % (self._TYPE_NAME, binary_str(self.value)) 1093 1094 1095@EvpnEsi.register_type(EvpnEsi.ARBITRARY) 1096class EvpnArbitraryEsi(EvpnEsi): 1097 """ 1098 Arbitrary 9-octet ESI value 1099 1100 This type indicates an arbitrary 9-octet ESI value, 1101 which is managed and configured by the operator. 1102 """ 1103 _TYPE_NAME = 'arbitrary' 1104 _VALUE_PACK_STR = '!9s' 1105 _VALUE_FIELDS = ['value'] 1106 1107 def __init__(self, value, type_=None): 1108 super(EvpnArbitraryEsi, self).__init__(type_) 1109 self.value = value 1110 1111 @property 1112 def formatted_str(self): 1113 return '%s(%s)' % (self._TYPE_NAME, binary_str(self.value)) 1114 1115 1116@EvpnEsi.register_type(EvpnEsi.LACP) 1117class EvpnLACPEsi(EvpnEsi): 1118 """ 1119 ESI value for LACP 1120 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. 1124 """ 1125 _TYPE_NAME = 'lacp' 1126 _VALUE_PACK_STR = '!6sHx' 1127 _VALUE_FIELDS = ['mac_addr', 'port_key'] 1128 _TYPE = { 1129 'ascii': [ 1130 'mac_addr' 1131 ] 1132 } 1133 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 1138 1139 @classmethod 1140 def parse_value(cls, buf): 1141 (mac_addr, port_key) = struct.unpack_from(cls._VALUE_PACK_STR, buf) 1142 return { 1143 'mac_addr': addrconv.mac.bin_to_text(mac_addr), 1144 'port_key': port_key, 1145 } 1146 1147 def serialize_value(self): 1148 return struct.pack( 1149 self._VALUE_PACK_STR, 1150 addrconv.mac.text_to_bin(self.mac_addr), self.port_key) 1151 1152 1153@EvpnEsi.register_type(EvpnEsi.L2_BRIDGE) 1154class EvpnL2BridgeEsi(EvpnEsi): 1155 """ 1156 ESI value for Layer 2 Bridge 1157 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. 1162 """ 1163 _TYPE_NAME = 'l2_bridge' 1164 _VALUE_PACK_STR = '!6sHx' 1165 _VALUE_FIELDS = ['mac_addr', 'priority'] 1166 _TYPE = { 1167 'ascii': [ 1168 'mac_addr' 1169 ] 1170 } 1171 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 1176 1177 @classmethod 1178 def parse_value(cls, buf): 1179 (mac_addr, priority) = struct.unpack_from(cls._VALUE_PACK_STR, buf) 1180 return { 1181 'mac_addr': addrconv.mac.bin_to_text(mac_addr), 1182 'priority': priority, 1183 } 1184 1185 def serialize_value(self): 1186 return struct.pack( 1187 self._VALUE_PACK_STR, 1188 addrconv.mac.text_to_bin(self.mac_addr), self.priority) 1189 1190 1191@EvpnEsi.register_type(EvpnEsi.MAC_BASED) 1192class EvpnMacBasedEsi(EvpnEsi): 1193 """ 1194 MAC-based ESI Value 1195 1196 This type indicates a MAC-based ESI Value that 1197 can be auto-generated or configured by the operator. 1198 """ 1199 _TYPE_NAME = 'mac_based' 1200 _VALUE_PACK_STR = '!6s3s' 1201 _VALUE_FIELDS = ['mac_addr', 'local_disc'] 1202 _TYPE = { 1203 'ascii': [ 1204 'mac_addr' 1205 ] 1206 } 1207 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 1212 1213 @classmethod 1214 def parse_value(cls, buf): 1215 (mac_addr, local_disc) = struct.unpack_from(cls._VALUE_PACK_STR, buf) 1216 return { 1217 'mac_addr': addrconv.mac.bin_to_text(mac_addr), 1218 'local_disc': type_desc.Int3.to_user(local_disc), 1219 } 1220 1221 def serialize_value(self): 1222 return struct.pack( 1223 self._VALUE_PACK_STR, 1224 addrconv.mac.text_to_bin(self.mac_addr), 1225 type_desc.Int3.from_user(self.local_disc)) 1226 1227 1228@EvpnEsi.register_type(EvpnEsi.ROUTER_ID) 1229class EvpnRouterIDEsi(EvpnEsi): 1230 """ 1231 Router-ID ESI Value 1232 1233 This type indicates a router-ID ESI Value that 1234 can be auto-generated or configured by the operator. 1235 """ 1236 _TYPE_NAME = 'router_id' 1237 _VALUE_PACK_STR = '!4sIx' 1238 _VALUE_FIELDS = ['router_id', 'local_disc'] 1239 _TYPE = { 1240 'ascii': [ 1241 'router_id' 1242 ] 1243 } 1244 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 1249 1250 @classmethod 1251 def parse_value(cls, buf): 1252 (router_id, local_disc) = struct.unpack_from(cls._VALUE_PACK_STR, buf) 1253 return { 1254 'router_id': addrconv.ipv4.bin_to_text(router_id), 1255 'local_disc': local_disc, 1256 } 1257 1258 def serialize_value(self): 1259 return struct.pack( 1260 self._VALUE_PACK_STR, 1261 addrconv.ipv4.text_to_bin(self.router_id), self.local_disc) 1262 1263 1264@EvpnEsi.register_type(EvpnEsi.AS_BASED) 1265class EvpnASBasedEsi(EvpnEsi): 1266 """ 1267 AS based ESI value 1268 1269 This type indicates an Autonomous System(AS)-based 1270 ESI Value that can be auto-generated or configured by 1271 the operator. 1272 """ 1273 _TYPE_NAME = 'as_based' 1274 _VALUE_PACK_STR = '!IIx' 1275 _VALUE_FIELDS = ['as_number', 'local_disc'] 1276 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 1281 1282 1283class EvpnNLRI(StringifyMixin, TypeDisp): 1284 """ 1285 BGP Network Layer Reachability Information (NLRI) for EVPN 1286 """ 1287 ROUTE_FAMILY = RF_L2_EVPN 1288 1289 # EVPN NLRI: 1290 # +-----------------------------------+ 1291 # | Route Type (1 octet) | 1292 # +-----------------------------------+ 1293 # | Length (1 octet) | 1294 # +-----------------------------------+ 1295 # | Route Type specific (variable) | 1296 # +-----------------------------------+ 1297 _PACK_STR = "!BB" 1298 _PACK_STR_SIZE = struct.calcsize(_PACK_STR) 1299 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 1305 1306 ROUTE_TYPE_NAME = None # must be defined in subclass 1307 1308 # Reserved value for Ethernet Tag ID. 1309 MAX_ET = 0xFFFFFFFF 1310 1311 # Dictionary of ROUTE_TYPE_NAME to subclass. 1312 # e.g.) 1313 # _NAMES = {'eth_ad': EvpnEthernetAutoDiscoveryNLRI, ...} 1314 _NAMES = {} 1315 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 1318 # representation. 1319 NLRI_PREFIX_FIELDS = [] 1320 1321 def __init__(self, type_=None, length=None): 1322 if type_ is None: 1323 type_ = self._rev_lookup_type(self.__class__) 1324 self.type = type_ 1325 self.length = length 1326 self.route_dist = None # should be initialized in subclass 1327 1328 @classmethod 1329 def register_type(cls, type_): 1330 cls._TYPES = cls._TYPES.copy() 1331 cls._NAMES = cls._NAMES.copy() 1332 1333 def _register_type(subcls): 1334 cls._TYPES[type_] = subcls 1335 cls._NAMES[subcls.ROUTE_TYPE_NAME] = subcls 1336 cls._REV_TYPES = None 1337 return subcls 1338 1339 return _register_type 1340 1341 @classmethod 1342 def _lookup_type_name(cls, type_name): 1343 try: 1344 return cls._NAMES[type_name] 1345 except KeyError: 1346 return EvpnUnknownNLRI 1347 1348 @classmethod 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:] 1357 1358 def serialize_value(self): 1359 # Overrided in subclass 1360 return b'' 1361 1362 def serialize(self): 1363 value_bin = self.serialize_value() 1364 # fixup 1365 self.length = len(value_bin) 1366 return struct.pack(EvpnNLRI._PACK_STR, 1367 self.type, self.length) + value_bin 1368 1369 @staticmethod 1370 def _rd_from_bin(buf): 1371 return _RouteDistinguisher.parser(buf[:8]), buf[8:] 1372 1373 @staticmethod 1374 def _rd_to_bin(rd): 1375 return six.binary_type(rd.serialize()) 1376 1377 @staticmethod 1378 def _esi_from_bin(buf): 1379 return EvpnEsi.parser(buf[:10]), buf[10:] 1380 1381 @staticmethod 1382 def _esi_to_bin(esi): 1383 return esi.serialize() 1384 1385 @staticmethod 1386 def _ethernet_tag_id_from_bin(buf): 1387 return type_desc.Int4.to_user(six.binary_type(buf[:4])), buf[4:] 1388 1389 @staticmethod 1390 def _ethernet_tag_id_to_bin(tag_id): 1391 return type_desc.Int4.from_user(tag_id) 1392 1393 @staticmethod 1394 def _mac_addr_len_from_bin(buf): 1395 return type_desc.Int1.to_user(six.binary_type(buf[:1])), buf[1:] 1396 1397 @staticmethod 1398 def _mac_addr_len_to_bin(mac_len): 1399 return type_desc.Int1.from_user(mac_len) 1400 1401 @staticmethod 1402 def _mac_addr_from_bin(buf, mac_len): 1403 mac_len //= 8 1404 return addrconv.mac.bin_to_text(buf[:mac_len]), buf[mac_len:] 1405 1406 @staticmethod 1407 def _mac_addr_to_bin(mac_addr): 1408 return addrconv.mac.text_to_bin(mac_addr) 1409 1410 @staticmethod 1411 def _ip_addr_len_from_bin(buf): 1412 return type_desc.Int1.to_user(six.binary_type(buf[:1])), buf[1:] 1413 1414 @staticmethod 1415 def _ip_addr_len_to_bin(ip_len): 1416 return type_desc.Int1.from_user(ip_len) 1417 1418 @staticmethod 1419 def _ip_addr_from_bin(buf, ip_len): 1420 return ip.bin_to_text(buf[:ip_len]), buf[ip_len:] 1421 1422 @staticmethod 1423 def _ip_addr_to_bin(ip_addr): 1424 return ip.text_to_bin(ip_addr) 1425 1426 @staticmethod 1427 def _mpls_label_from_bin(buf): 1428 mpls_label, is_bos = mpls.label_from_bin(buf) 1429 rest = buf[3:] 1430 return mpls_label, rest, is_bos 1431 1432 @staticmethod 1433 def _mpls_label_to_bin(label, is_bos=True): 1434 return mpls.label_to_bin(label, is_bos=is_bos) 1435 1436 @staticmethod 1437 def _vni_from_bin(buf): 1438 return vxlan.vni_from_bin(six.binary_type(buf[:3])), buf[3:] 1439 1440 @staticmethod 1441 def _vni_to_bin(vni): 1442 return vxlan.vni_to_bin(vni) 1443 1444 @property 1445 def prefix(self): 1446 def _format(i): 1447 pairs = [] 1448 for k in i.NLRI_PREFIX_FIELDS: 1449 v = getattr(i, k) 1450 if k == 'esi': 1451 pairs.append('%s:%s' % (k, v.formatted_str)) 1452 else: 1453 pairs.append('%s:%s' % (k, v)) 1454 return ','.join(pairs) 1455 1456 return '%s(%s)' % (self.ROUTE_TYPE_NAME, _format(self)) 1457 1458 @property 1459 def formatted_nlri_str(self): 1460 return '%s:%s' % (self.route_dist, self.prefix) 1461 1462 1463@EvpnNLRI.register_unknown_type() 1464class EvpnUnknownNLRI(EvpnNLRI): 1465 """ 1466 Unknown route type specific EVPN NLRI 1467 """ 1468 ROUTE_TYPE_NAME = 'unknown' 1469 NLRI_PREFIX_FIELDS = ['value'] 1470 1471 def __init__(self, value, type_, length=None): 1472 super(EvpnUnknownNLRI, self).__init__(type_, length) 1473 self.value = value 1474 1475 @classmethod 1476 def parse_value(cls, buf): 1477 return { 1478 'value': buf 1479 } 1480 1481 def serialize_value(self): 1482 return self.value 1483 1484 @property 1485 def formatted_nlri_str(self): 1486 return '%s(%s)' % (self.ROUTE_TYPE_NAME, binary_str(self.value)) 1487 1488 1489@EvpnNLRI.register_type(EvpnNLRI.ETHERNET_AUTO_DISCOVERY) 1490class EvpnEthernetAutoDiscoveryNLRI(EvpnNLRI): 1491 """ 1492 Ethernet A-D route type specific EVPN NLRI 1493 """ 1494 ROUTE_TYPE_NAME = 'eth_ad' 1495 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'] 1507 _TYPE = { 1508 'ascii': [ 1509 'route_dist', 1510 ] 1511 } 1512 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 1518 self.esi = esi 1519 self.ethernet_tag_id = ethernet_tag_id 1520 if label: 1521 # If binary type label field value is specified, stores it 1522 # and decodes as MPLS label and VNI. 1523 self._label = label 1524 self._mpls_label, _, _ = self._mpls_label_from_bin(label) 1525 self._vni, _ = self._vni_from_bin(label) 1526 else: 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 1531 self._vni = vni 1532 1533 def _serialize_label(self, mpls_label, vni): 1534 if mpls_label: 1535 return self._mpls_label_to_bin(mpls_label, is_bos=True) 1536 elif vni: 1537 return self._vni_to_bin(vni) 1538 else: 1539 return b'\x00' * 3 1540 1541 @classmethod 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) 1546 1547 return { 1548 'route_dist': route_dist.formatted_str, 1549 'esi': esi, 1550 'ethernet_tag_id': ethernet_tag_id, 1551 'label': rest, 1552 } 1553 1554 def serialize_value(self): 1555 route_dist = _RouteDistinguisher.from_str(self.route_dist) 1556 return struct.pack( 1557 self._PACK_STR, route_dist.serialize(), self.esi.serialize(), 1558 self.ethernet_tag_id, self._label) 1559 1560 @property 1561 def mpls_label(self): 1562 return self._mpls_label 1563 1564 @mpls_label.setter 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 1569 1570 @property 1571 def vni(self): 1572 return self._vni 1573 1574 @vni.setter 1575 def vni(self, vni): 1576 self._label = self._vni_to_bin(vni) 1577 self._mpls_label = None # disables MPLS label 1578 self._vni = vni 1579 1580 @property 1581 def label_list(self): 1582 return [self.mpls_label] 1583 1584 1585@EvpnNLRI.register_type(EvpnNLRI.MAC_IP_ADVERTISEMENT) 1586class EvpnMacIPAdvertisementNLRI(EvpnNLRI): 1587 """ 1588 MAC/IP Advertisement route type specific EVPN NLRI 1589 """ 1590 ROUTE_TYPE_NAME = 'mac_ip_adv' 1591 1592 # +---------------------------------------+ 1593 # | RD (8 octets) | 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'] 1614 _TYPE = { 1615 'ascii': [ 1616 'route_dist', 1617 'mac_addr', 1618 'ip_addr', 1619 ] 1620 } 1621 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 1628 self.esi = esi 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 1634 if labels: 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 1639 else: 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 1644 self._vni = vni 1645 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 1654 1655 def _serialize_labels(self, mpls_labels, vni): 1656 if mpls_labels: 1657 return self._serialize_mpls_labels(mpls_labels) 1658 elif vni: 1659 return self._vni_to_bin(vni) 1660 else: 1661 return b'\x00' * 3 1662 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)) 1669 else: 1670 return b'\x00' * 3 1671 1672 @classmethod 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) 1682 else: 1683 ip_addr = None 1684 1685 return { 1686 'route_dist': route_dist.formatted_str, 1687 'esi': esi, 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, 1692 'ip_addr': ip_addr, 1693 'labels': rest, 1694 } 1695 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 1700 if self.ip_addr: 1701 ip_addr = self._ip_addr_to_bin(self.ip_addr) 1702 else: 1703 ip_addr = b'' 1704 ip_addr_len = len(ip_addr) 1705 self.ip_addr_len = ip_addr_len * 8 # fixup 1706 1707 return struct.pack( 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, 1713 self._labels) 1714 1715 @property 1716 def mpls_labels(self): 1717 return self._mpls_labels 1718 1719 @mpls_labels.setter 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 1724 1725 @property 1726 def vni(self): 1727 return self._vni 1728 1729 @vni.setter 1730 def vni(self, vni): 1731 self._labels = self._vni_to_bin(vni) 1732 self._mpls_labels = None # disables MPLS labels 1733 self._vni = vni 1734 1735 @property 1736 def label_list(self): 1737 return self.mpls_labels 1738 1739 1740@EvpnNLRI.register_type(EvpnNLRI.INCLUSIVE_MULTICAST_ETHERNET_TAG) 1741class EvpnInclusiveMulticastEthernetTagNLRI(EvpnNLRI): 1742 """ 1743 Inclusive Multicast Ethernet Tag route type specific EVPN NLRI 1744 """ 1745 ROUTE_TYPE_NAME = 'multicast_etag' 1746 1747 # +---------------------------------------+ 1748 # | RD (8 octets) | 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'] 1759 _TYPE = { 1760 'ascii': [ 1761 'route_dist', 1762 'ip_addr', 1763 ] 1764 } 1765 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 1774 1775 @classmethod 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) 1781 1782 return { 1783 'route_dist': route_dist.formatted_str, 1784 'ethernet_tag_id': ethernet_tag_id, 1785 'ip_addr_len': ip_addr_len, 1786 'ip_addr': ip_addr, 1787 } 1788 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 1793 1794 return struct.pack( 1795 self._PACK_STR % len(ip_addr), 1796 route_dist.serialize(), self.ethernet_tag_id, 1797 self.ip_addr_len, ip_addr) 1798 1799 1800@EvpnNLRI.register_type(EvpnNLRI.ETHERNET_SEGMENT) 1801class EvpnEthernetSegmentNLRI(EvpnNLRI): 1802 """ 1803 Ethernet Segment route type specific EVPN NLRI 1804 """ 1805 ROUTE_TYPE_NAME = 'eth_seg' 1806 1807 # +---------------------------------------+ 1808 # | RD (8 octets) | 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'] 1819 _TYPE = { 1820 'ascii': [ 1821 'route_dist', 1822 'ip_addr', 1823 ] 1824 } 1825 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 1830 self.esi = esi 1831 self.ip_addr_len = ip_addr_len 1832 self.ip_addr = ip_addr 1833 1834 @classmethod 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) 1840 1841 return { 1842 'route_dist': route_dist.formatted_str, 1843 'esi': esi, 1844 'ip_addr_len': ip_addr_len, 1845 'ip_addr': ip_addr, 1846 } 1847 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) 1851 # fixup 1852 self.ip_addr_len = len(ip_addr) * 8 1853 1854 return struct.pack( 1855 self._PACK_STR % len(ip_addr), 1856 route_dist.serialize(), self.esi.serialize(), 1857 self.ip_addr_len, ip_addr) 1858 1859 1860@EvpnNLRI.register_type(EvpnNLRI.IP_PREFIX_ROUTE) 1861class EvpnIpPrefixNLRI(EvpnNLRI): 1862 """ 1863 IP Prefix advertisement route NLRI 1864 """ 1865 ROUTE_TYPE_NAME = 'ip_prefix' 1866 1867 # +---------------------------------------+ 1868 # | RD (8 octets) | 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'] 1884 _TYPE = { 1885 'ascii': [ 1886 'route_dist', 1887 'ip_prefix', 1888 'gw_ip_addr' 1889 ] 1890 } 1891 _LABEL_LEN = 3 1892 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 1899 self.esi = esi 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 1904 1905 if gw_ip_addr is None: 1906 if ':' not in self._ip_prefix: 1907 self.gw_ip_addr = '0.0.0.0' 1908 else: 1909 self.gw_ip_addr = '::' 1910 else: 1911 self.gw_ip_addr = gw_ip_addr 1912 1913 if label: 1914 # If binary type label field value is specified, stores it 1915 # and decodes as MPLS label and VNI. 1916 self._label = label 1917 self._mpls_label, _, _ = self._mpls_label_from_bin(label) 1918 self._vni, _ = self._vni_from_bin(label) 1919 else: 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 1924 self._vni = vni 1925 1926 def _serialize_label(self, mpls_label, vni): 1927 if mpls_label: 1928 return self._mpls_label_to_bin(mpls_label, is_bos=True) 1929 elif vni: 1930 return vxlan.vni_to_bin(vni) 1931 else: 1932 return b'\x00' * 3 1933 1934 @classmethod 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) 1943 1944 return { 1945 'route_dist': route_dist.formatted_str, 1946 'esi': esi, 1947 'ethernet_tag_id': ethernet_tag_id, 1948 'ip_prefix': '%s/%s' % (ip_prefix, ip_prefix_len), 1949 'gw_ip_addr': gw_ip_addr, 1950 'label': rest, 1951 } 1952 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) 1957 1958 return struct.pack( 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) 1963 1964 @property 1965 def ip_prefix(self): 1966 return '%s/%s' % (self._ip_prefix, self._ip_prefix_len) 1967 1968 @ip_prefix.setter 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) 1972 1973 @property 1974 def mpls_label(self): 1975 return self._mpls_label 1976 1977 @mpls_label.setter 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 1982 1983 @property 1984 def vni(self): 1985 return self._vni 1986 1987 @vni.setter 1988 def vni(self, vni): 1989 self._label = self._vni_to_bin(vni) 1990 self._mpls_label = None # disables MPLS label 1991 self._vni = vni 1992 1993 @property 1994 def label_list(self): 1995 return [self.mpls_label] 1996 1997 1998class _FlowSpecNLRIBase(StringifyMixin, TypeDisp): 1999 """ 2000 Base class for Flow Specification NLRI 2001 """ 2002 2003 # flow-spec NLRI: 2004 # +-----------------------------------+ 2005 # | length (0xnn or 0xfn nn) | 2006 # +-----------------------------------+ 2007 # | NLRI value (variable) | 2008 # +-----------------------------------+ 2009 ROUTE_FAMILY = None 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 = '' 2016 2017 def __init__(self, length=0, rules=None): 2018 self.length = length 2019 rules = rules or [] 2020 for r in rules: 2021 assert isinstance(r, _FlowSpecComponentBase) 2022 self.rules = rules 2023 2024 @classmethod 2025 def parser(cls, buf): 2026 (length,) = struct.unpack_from( 2027 cls._LENGTH_LONG_FMT, six.binary_type(buf)) 2028 2029 if length < cls._LENGTH_THRESHOLD: 2030 length >>= 8 2031 offset = cls.LENGTH_SHORT_SIZE 2032 else: 2033 offset = cls.LENGTH_LONG_SIZE 2034 2035 kwargs = {'length': length} 2036 rest = buf[offset:offset + length] 2037 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 2041 rest = rest[8:] 2042 2043 rules = [] 2044 2045 while rest: 2046 subcls, rest = _FlowSpecComponentBase.parse_header( 2047 rest, cls.ROUTE_FAMILY.afi) 2048 2049 while rest: 2050 rule, rest = subcls.parse_body(rest) 2051 rules.append(rule) 2052 2053 if (not isinstance(rule, _FlowSpecOperatorBase) or 2054 rule.operator & rule.END_OF_LIST): 2055 break 2056 2057 kwargs['rules'] = rules 2058 2059 return cls(**kwargs), rest 2060 2061 def serialize(self): 2062 rules_bin = b'' 2063 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() 2067 2068 self.rules.sort(key=lambda x: x.type) 2069 for _, rules in itertools.groupby(self.rules, key=lambda x: x.type): 2070 rules = list(rules) 2071 rules_bin += rules[0].serialize_header() 2072 2073 if isinstance(rules[-1], _FlowSpecOperatorBase): 2074 rules[-1].operator |= rules[-1].END_OF_LIST 2075 2076 for r in rules: 2077 rules_bin += r.serialize_body() 2078 2079 self.length = len(rules_bin) 2080 2081 if self.length < self._LENGTH_THRESHOLD: 2082 buf = struct.pack(self._LENGTH_SHORT_FMT, self.length) 2083 else: 2084 buf = struct.pack(self._LENGTH_LONG_FMT, self.length) 2085 2086 return buf + rules_bin 2087 2088 @classmethod 2089 def _from_user(cls, **kwargs): 2090 rules = [] 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)) 2095 rules.extend(rule) 2096 rules.sort(key=lambda x: x.type) 2097 return cls(rules=rules) 2098 2099 @property 2100 def prefix(self): 2101 def _format(i): 2102 pairs = [] 2103 i.rules.sort(key=lambda x: x.type) 2104 previous_type = None 2105 for r in i.rules: 2106 if r.type == previous_type: 2107 if r.to_str()[0] != '&': 2108 pairs[-1] += '|' 2109 pairs[-1] += r.to_str() 2110 else: 2111 pairs.append('%s:%s' % (r.COMPONENT_NAME, r.to_str())) 2112 previous_type = r.type 2113 2114 return ','.join(pairs) 2115 2116 return '%s(%s)' % (self.FLOWSPEC_FAMILY, _format(self)) 2117 2118 @property 2119 def formatted_nlri_str(self): 2120 return self.prefix 2121 2122 2123class FlowSpecIPv4NLRI(_FlowSpecNLRIBase): 2124 """ 2125 Flow Specification NLRI class for IPv4 [RFC 5575] 2126 """ 2127 ROUTE_FAMILY = RF_IPv4_FLOWSPEC 2128 FLOWSPEC_FAMILY = 'ipv4fs' 2129 2130 @classmethod 2131 def from_user(cls, **kwargs): 2132 """ 2133 Utility method for creating a NLRI instance. 2134 2135 This function returns a NLRI instance from human readable format value. 2136 2137 :param kwargs: The following arguments are available. 2138 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 2157 Code Point. 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 =========== ============= ========= ============================== 2165 2166 Example:: 2167 2168 >>> msg = bgp.FlowSpecIPv4NLRI.from_user( 2169 ... dst_prefix='10.0.0.0/24', 2170 ... src_prefix='20.0.0.1/24', 2171 ... ip_proto=6, 2172 ... port='80 | 8000', 2173 ... dst_port='>9000 & <9050', 2174 ... src_port='>=8500 & <=9000', 2175 ... icmp_type=0, 2176 ... icmp_code=6, 2177 ... tcp_flags='SYN+ACK & !=URGENT', 2178 ... packet_len=1000, 2179 ... dscp='22 | 24', 2180 ... fragment='LF | ==FF') 2181 >>> 2182 2183 You can specify conditions with the following keywords. 2184 2185 The following keywords can be used when the operator type is Numeric. 2186 2187 ========== ============================================================ 2188 Keyword Description 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 ========== ============================================================ 2197 2198 The following keywords can be used when the operator type is Bitmask. 2199 2200 ========== ================================================ 2201 Keyword Description 2202 ========== ================================================ 2203 != Not equal operation. 2204 == Exact match operation if specified. 2205 Otherwise partial match operation. 2206 `+` Used for the summation of bitmask values. 2207 (e.g., SYN+ACK) 2208 ========== ================================================ 2209 2210 You can combine the multiple conditions with the following operators. 2211 2212 ========== ======================================= 2213 Keyword Description 2214 ========== ======================================= 2215 `|` Logical OR operation 2216 & Logical AND operation 2217 ========== ======================================= 2218 2219 :return: A instance of FlowSpecVPNv4NLRI. 2220 """ 2221 return cls._from_user(**kwargs) 2222 2223 2224class FlowSpecVPNv4NLRI(_FlowSpecNLRIBase): 2225 """ 2226 Flow Specification NLRI class for VPNv4 [RFC 5575] 2227 """ 2228 2229 # flow-spec NLRI: 2230 # +-----------------------------------+ 2231 # | length (0xnn or 0xfn nn) | 2232 # +-----------------------------------+ 2233 # | RD (8 octets) | 2234 # +-----------------------------------+ 2235 # | NLRI value (variable) | 2236 # +-----------------------------------+ 2237 ROUTE_FAMILY = RF_VPNv4_FLOWSPEC 2238 FLOWSPEC_FAMILY = 'vpnv4fs' 2239 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 2244 2245 @classmethod 2246 def _from_user(cls, route_dist, **kwargs): 2247 rules = [] 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)) 2252 rules.extend(rule) 2253 rules.sort(key=lambda x: x.type) 2254 return cls(route_dist=route_dist, rules=rules) 2255 2256 @classmethod 2257 def from_user(cls, route_dist, **kwargs): 2258 """ 2259 Utility method for creating a NLRI instance. 2260 2261 This function returns a NLRI instance from human readable format value. 2262 2263 :param route_dist: Route Distinguisher. 2264 :param kwargs: See :py:mod:`ryu.lib.packet.bgp.FlowSpecIPv4NLRI` 2265 2266 Example:: 2267 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', 2272 ... ip_proto=6, 2273 ... port='80 | 8000', 2274 ... dst_port='>9000 & <9050', 2275 ... src_port='>=8500 & <=9000', 2276 ... icmp_type=0, 2277 ... icmp_code=6, 2278 ... tcp_flags='SYN+ACK & !=URGENT', 2279 ... packet_len=1000, 2280 ... dscp='22 | 24', 2281 ... fragment='LF | ==FF') 2282 >>> 2283 """ 2284 return cls._from_user(route_dist, **kwargs) 2285 2286 @property 2287 def formatted_nlri_str(self): 2288 return '%s:%s' % (self.route_dist, self.prefix) 2289 2290 2291class FlowSpecIPv6NLRI(_FlowSpecNLRIBase): 2292 """ 2293 Flow Specification NLRI class for IPv6 [RFC draft-ietf-idr-flow-spec-v6-08] 2294 """ 2295 ROUTE_FAMILY = RF_IPv6_FLOWSPEC 2296 FLOWSPEC_FAMILY = 'ipv6fs' 2297 2298 @classmethod 2299 def from_user(cls, **kwargs): 2300 """ 2301 Utility method for creating a NLRI instance. 2302 2303 This function returns a NLRI instance from human readable format value. 2304 2305 :param kwargs: The following arguments are available. 2306 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 2325 Code Point. 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 =========== ============= ========= ============================== 2333 2334 .. Note:: 2335 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. 2339 """ 2340 return cls._from_user(**kwargs) 2341 2342 2343class FlowSpecVPNv6NLRI(_FlowSpecNLRIBase): 2344 """ 2345 Flow Specification NLRI class for VPNv6 [draft-ietf-idr-flow-spec-v6-08] 2346 """ 2347 2348 # flow-spec NLRI: 2349 # +-----------------------------------+ 2350 # | length (0xnn or 0xfn nn) | 2351 # +-----------------------------------+ 2352 # | RD (8 octets) | 2353 # +-----------------------------------+ 2354 # | NLRI value (variable) | 2355 # +-----------------------------------+ 2356 ROUTE_FAMILY = RF_VPNv6_FLOWSPEC 2357 FLOWSPEC_FAMILY = 'vpnv6fs' 2358 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 2363 2364 @classmethod 2365 def _from_user(cls, route_dist, **kwargs): 2366 rules = [] 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)) 2371 rules.extend(rule) 2372 rules.sort(key=lambda x: x.type) 2373 return cls(route_dist=route_dist, rules=rules) 2374 2375 @classmethod 2376 def from_user(cls, route_dist, **kwargs): 2377 """ 2378 Utility method for creating a NLRI instance. 2379 2380 This function returns a NLRI instance from human readable format value. 2381 2382 :param route_dist: Route Distinguisher. 2383 :param kwargs: See :py:mod:`ryu.lib.packet.bgp.FlowSpecIPv6NLRI` 2384 """ 2385 return cls._from_user(route_dist, **kwargs) 2386 2387 @property 2388 def formatted_nlri_str(self): 2389 return '%s:%s' % (self.route_dist, self.prefix) 2390 2391 2392class FlowSpecL2VPNNLRI(_FlowSpecNLRIBase): 2393 """ 2394 Flow Specification NLRI class for L2VPN [draft-ietf-idr-flowspec-l2vpn-05] 2395 """ 2396 2397 # flow-spec NLRI: 2398 # +-----------------------------------+ 2399 # | length (0xnn or 0xfn nn) | 2400 # +-----------------------------------+ 2401 # | RD (8 octets) | 2402 # +-----------------------------------+ 2403 # | NLRI value (variable) | 2404 # +-----------------------------------+ 2405 ROUTE_FAMILY = RF_L2VPN_FLOWSPEC 2406 FLOWSPEC_FAMILY = 'l2vpnfs' 2407 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 2412 2413 @classmethod 2414 def _from_user(cls, route_dist, **kwargs): 2415 rules = [] 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)) 2420 rules.extend(rule) 2421 rules.sort(key=lambda x: x.type) 2422 return cls(route_dist=route_dist, rules=rules) 2423 2424 @classmethod 2425 def from_user(cls, route_dist, **kwargs): 2426 """ 2427 Utility method for creating a L2VPN NLRI instance. 2428 2429 This function returns a L2VPN NLRI instance 2430 from human readable format value. 2431 2432 :param kwargs: The following arguments are available. 2433 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 2441 in LLC. 2442 llc_dsap Integer Numeric Destination Service Access 2443 Point in LLC. 2444 llc_control Integer Numeric Control field in LLC. 2445 snap Integer Numeric Sub-Network Access Protocol 2446 field. 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 ============== ============= ========= ============================== 2452 """ 2453 return cls._from_user(route_dist, **kwargs) 2454 2455 @property 2456 def formatted_nlri_str(self): 2457 return '%s:%s' % (self.route_dist, self.prefix) 2458 2459 2460class _FlowSpecComponentBase(StringifyMixin, TypeDisp): 2461 """ 2462 Base class for Flow Specification NLRI component 2463 """ 2464 COMPONENT_NAME = None 2465 2466 _BASE_STR = '!B' 2467 _BASE_STR_SIZE = struct.calcsize(_BASE_STR) 2468 2469 # Dictionary of COMPONENT_NAME to subclass. 2470 # e.g.) 2471 # _NAMES = {'dst_prefix': FlowSpecDestPrefix, ...} 2472 _NAMES = {} 2473 2474 def __init__(self, type_=None): 2475 if type_ is None: 2476 type_, _ = self._rev_lookup_type(self.__class__) 2477 self.type = type_ 2478 2479 @classmethod 2480 def register_type(cls, type_, afi): 2481 cls._TYPES = cls._TYPES.copy() 2482 cls._NAMES = cls._NAMES.copy() 2483 2484 def _register_type(subcls): 2485 cls._TYPES[(type_, afi)] = subcls 2486 cls._NAMES[(subcls.COMPONENT_NAME, afi)] = subcls 2487 cls._REV_TYPES = None 2488 return subcls 2489 2490 return _register_type 2491 2492 @classmethod 2493 def lookup_type_name(cls, type_name, afi): 2494 return cls._NAMES[(type_name, afi)] 2495 2496 @classmethod 2497 def _lookup_type(cls, type_, afi): 2498 try: 2499 return cls._TYPES[(type_, afi)] 2500 except KeyError: 2501 return cls._UNKNOWN_TYPE 2502 2503 @classmethod 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 2509 2510 def serialize_header(self): 2511 return struct.pack(self._BASE_STR, self.type) 2512 2513 2514class _FlowSpecIPv4Component(_FlowSpecComponentBase): 2515 """ 2516 Base class for Flow Specification for IPv4 NLRI component 2517 """ 2518 TYPE_DESTINATION_PREFIX = 0x01 2519 TYPE_SOURCE_PREFIX = 0x02 2520 TYPE_PROTOCOL = 0x03 2521 TYPE_PORT = 0x04 2522 TYPE_DESTINATION_PORT = 0x05 2523 TYPE_SOURCE_PORT = 0x06 2524 TYPE_ICMP = 0x07 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 2530 2531 2532class _FlowSpecIPv6Component(_FlowSpecComponentBase): 2533 """ 2534 Base class for Flow Specification for IPv6 NLRI component 2535 """ 2536 TYPE_DESTINATION_PREFIX = 0x01 2537 TYPE_SOURCE_PREFIX = 0x02 2538 TYPE_NEXT_HEADER = 0x03 2539 TYPE_PORT = 0x04 2540 TYPE_DESTINATION_PORT = 0x05 2541 TYPE_SOURCE_PORT = 0x06 2542 TYPE_ICMP = 0x07 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 2549 2550 2551class _FlowSpecL2VPNComponent(_FlowSpecComponentBase): 2552 """ 2553 Base class for Flow Specification for L2VPN NLRI component 2554 """ 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 2561 TYPE_SNAP = 0x14 2562 TYPE_VLAN_ID = 0x15 2563 TYPE_VLAN_COS = 0x16 2564 TYPE_INNER_VLAN_ID = 0x17 2565 TYPE_INNER_VLAN_COS = 0x18 2566 2567 2568@_FlowSpecComponentBase.register_unknown_type() 2569class FlowSpecComponentUnknown(_FlowSpecComponentBase): 2570 """ 2571 Unknown component type for Flow Specification NLRI component 2572 """ 2573 2574 def __init__(self, buf, type_=None): 2575 super(FlowSpecComponentUnknown, self).__init__(type_) 2576 self.buf = buf 2577 2578 @classmethod 2579 def parse_body(cls, buf): 2580 return cls(buf), None 2581 2582 def serialize_body(self): 2583 return self.buf 2584 2585 2586class _FlowSpecPrefixBase(_FlowSpecIPv4Component, IPAddrPrefix): 2587 """ 2588 Prefix base class for Flow Specification NLRI component 2589 """ 2590 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) 2596 2597 @classmethod 2598 def parse_body(cls, buf): 2599 return cls.parser(buf) 2600 2601 def serialize_body(self): 2602 return self.serialize() 2603 2604 @classmethod 2605 def from_str(cls, value): 2606 rule = [] 2607 addr, length = value.split('/') 2608 rule.append(cls(int(length), addr)) 2609 return rule 2610 2611 @property 2612 def value(self): 2613 return "%s/%s" % (self.addr, self.length) 2614 2615 def to_str(self): 2616 return self.value 2617 2618 2619class _FlowSpecIPv6PrefixBase(_FlowSpecIPv6Component, IP6AddrPrefix): 2620 """ 2621 Prefix base class for Flow Specification NLRI component 2622 """ 2623 _PACK_STR = '!BB' # length, offset 2624 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) 2631 2632 @classmethod 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 2641 2642 @classmethod 2643 def parse_body(cls, buf): 2644 return cls.parser(buf) 2645 2646 def serialize(self): 2647 byte_length = (self.length + 7) // 8 2648 bin_addr = self._to_bin(self.addr)[:byte_length] 2649 buf = bytearray() 2650 msg_pack_into(self._PACK_STR, buf, 0, self.length, self.offset) 2651 return buf + bin_addr 2652 2653 def serialize_body(self): 2654 return self.serialize() 2655 2656 @classmethod 2657 def from_str(cls, value): 2658 rule = [] 2659 values = value.split('/') 2660 if len(values) == 3: 2661 rule.append(cls(int(values[1]), values[0], offset=int(values[2]))) 2662 else: 2663 rule.append(cls(int(values[1]), values[0])) 2664 return rule 2665 2666 @property 2667 def value(self): 2668 return "%s/%s/%s" % (self.addr, self.length, self.offset) 2669 2670 def to_str(self): 2671 return self.value 2672 2673 2674class _FlowSpecL2VPNPrefixBase(_FlowSpecL2VPNComponent): 2675 """ 2676 Prefix base class for Flow Specification NLRI component 2677 """ 2678 _PACK_STR = "!B6s" 2679 2680 def __init__(self, length, addr, type_=None): 2681 super(_FlowSpecL2VPNPrefixBase, self).__init__(type_) 2682 self.length = length 2683 self.addr = addr.lower() 2684 2685 @classmethod 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 2692 2693 def serialize(self): 2694 addr = addrconv.mac.text_to_bin(self.addr) 2695 return struct.pack(self._PACK_STR, self.length, addr) 2696 2697 def serialize_body(self): 2698 return self.serialize() 2699 2700 @classmethod 2701 def from_str(cls, value): 2702 return [cls(len(value.split(':')), value)] 2703 2704 @property 2705 def value(self): 2706 return self.addr 2707 2708 def to_str(self): 2709 return self.value 2710 2711 2712@_FlowSpecComponentBase.register_type( 2713 _FlowSpecIPv4Component.TYPE_DESTINATION_PREFIX, addr_family.IP) 2714class FlowSpecDestPrefix(_FlowSpecPrefixBase): 2715 """ 2716 Destination Prefix for Flow Specification NLRI component 2717 """ 2718 COMPONENT_NAME = 'dst_prefix' 2719 2720 2721@_FlowSpecComponentBase.register_type( 2722 _FlowSpecIPv4Component.TYPE_SOURCE_PREFIX, addr_family.IP) 2723class FlowSpecSrcPrefix(_FlowSpecPrefixBase): 2724 """ 2725 Source Prefix for Flow Specification NLRI component 2726 """ 2727 COMPONENT_NAME = 'src_prefix' 2728 2729 2730@_FlowSpecComponentBase.register_type( 2731 _FlowSpecIPv6Component.TYPE_DESTINATION_PREFIX, addr_family.IP6) 2732class FlowSpecIPv6DestPrefix(_FlowSpecIPv6PrefixBase): 2733 """ 2734 IPv6 destination Prefix for Flow Specification NLRI component 2735 """ 2736 COMPONENT_NAME = 'dst_prefix' 2737 2738 2739@_FlowSpecComponentBase.register_type( 2740 _FlowSpecIPv6Component.TYPE_SOURCE_PREFIX, addr_family.IP6) 2741class FlowSpecIPv6SrcPrefix(_FlowSpecIPv6PrefixBase): 2742 """ 2743 IPv6 source Prefix for Flow Specification NLRI component 2744 """ 2745 COMPONENT_NAME = 'src_prefix' 2746 2747 2748class _FlowSpecOperatorBase(_FlowSpecComponentBase): 2749 """Operator base class for Flow Specification NLRI component 2750 2751 ===================== =============================================== 2752 Attribute Description 2753 ===================== =============================================== 2754 operator Match conditions. 2755 value Value of component. 2756 ===================== =============================================== 2757 """ 2758 _OPE_PACK_STR = '!B' 2759 _OPE_PACK_STR_SIZE = struct.calcsize(_OPE_PACK_STR) 2760 _VAL_PACK_STR = '!%ds' 2761 2762 END_OF_LIST = 1 << 7 # END OF LIST bit 2763 AND = 1 << 6 # AND bit 2764 OR = 0 # OR 2765 _LENGTH_BIT_MASK = 0x30 # The mask for length of the value 2766 2767 _logical_conditions = { 2768 "|": OR, 2769 "&": AND, 2770 } 2771 _comparison_conditions = {} 2772 2773 def __init__(self, operator, value, type_=None): 2774 super(_FlowSpecOperatorBase, self).__init__(type_) 2775 self.operator = operator 2776 self.value = value 2777 2778 @classmethod 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:] 2787 2788 return cls(operator, value), rest 2789 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)) 2798 2799 return buf 2800 2801 @classmethod 2802 def from_str(cls, val): 2803 operator = 0 2804 rules = [] 2805 2806 # e.g.) 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()] 2812 2813 elms_iter = iter(elements) 2814 2815 for elm in elms_iter: 2816 if elm in cls._logical_conditions: 2817 # ['&', '|'] 2818 operator |= cls._logical_conditions[elm] 2819 continue 2820 elif elm in cls._comparison_conditions: 2821 # ['=', '<', '>', '<=', '>=' ] or ['=', '!='] 2822 operator |= cls._comparison_conditions[elm] 2823 continue 2824 elif 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)) 2828 continue 2829 2830 value = cls._to_value(elm) 2831 2832 operator = cls.normalize_operator(operator) 2833 2834 rules.append(cls(operator, value)) 2835 operator = 0 2836 2837 return rules 2838 2839 @classmethod 2840 def _to_value(cls, value): 2841 return value 2842 2843 @classmethod 2844 def normalize_operator(cls, operator): 2845 return operator 2846 2847 2848class _FlowSpecNumeric(_FlowSpecOperatorBase): 2849 """ 2850 Numeric operator class for Flow Specification NLRI component 2851 """ 2852 # Numeric operator format 2853 # 0 1 2 3 4 5 6 7 2854 # +---+---+---+---+---+---+---+---+ 2855 # | e | a | len | 0 |lt |gt |eq | 2856 # +---+---+---+---+---+---+---+---+ 2857 2858 LT = 1 << 2 # Less than comparison bit 2859 GT = 1 << 1 # Greater than comparison bit 2860 EQ = 1 << 0 # Equality bit 2861 2862 _comparison_conditions = { 2863 '==': EQ, 2864 '<': LT, 2865 '>': GT, 2866 '<=': LT | EQ, 2867 '>=': GT | EQ 2868 } 2869 2870 @classmethod 2871 def _to_value(cls, value): 2872 try: 2873 return int(str(value), 0) 2874 except ValueError: 2875 raise ValueError('Invalid params: %s="%s"' % ( 2876 cls.COMPONENT_NAME, value)) 2877 2878 def to_str(self): 2879 string = "" 2880 if self.operator & self.AND: 2881 string += "&" 2882 2883 operator = self.operator & (self.LT | self.GT | self.EQ) 2884 for k, v in self._comparison_conditions.items(): 2885 if operator == v: 2886 string += k 2887 2888 string += str(self.value) 2889 2890 return string 2891 2892 @classmethod 2893 def normalize_operator(cls, operator): 2894 if operator & (cls.LT | cls.GT | cls.EQ): 2895 return operator 2896 else: 2897 return operator | cls.EQ 2898 2899 2900class _FlowSpecBitmask(_FlowSpecOperatorBase): 2901 """ 2902 Bitmask operator class for Flow Specification NLRI component 2903 """ 2904 # Bitmask operator format 2905 # 0 1 2 3 4 5 6 7 2906 # +---+---+---+---+---+---+---+---+ 2907 # | e | a | len | 0 | 0 |not| m | 2908 # +---+---+---+---+---+---+---+---+ 2909 2910 NOT = 1 << 1 # NOT bit 2911 MATCH = 1 << 0 # MATCH bit 2912 2913 _comparison_conditions = { 2914 '!=': NOT, 2915 '==': MATCH, 2916 } 2917 2918 _bitmask_flags = {} 2919 2920 @classmethod 2921 def _to_value(cls, value): 2922 try: 2923 return cls.__dict__[value] 2924 except KeyError: 2925 raise ValueError('Invalid params: %s="%s"' % ( 2926 cls.COMPONENT_NAME, value)) 2927 2928 def to_str(self): 2929 string = "" 2930 if self.operator & self.AND: 2931 string += "&" 2932 2933 operator = self.operator & (self.NOT | self.MATCH) 2934 for k, v in self._comparison_conditions.items(): 2935 if operator == v: 2936 string += k 2937 2938 plus = "" 2939 for k, v in self._bitmask_flags.items(): 2940 if self.value & k: 2941 string += plus + v 2942 plus = "+" 2943 2944 return string 2945 2946 2947@_FlowSpecComponentBase.register_type( 2948 _FlowSpecIPv4Component.TYPE_PROTOCOL, addr_family.IP) 2949class FlowSpecIPProtocol(_FlowSpecNumeric): 2950 """IP Protocol for Flow Specification NLRI component 2951 2952 Set the IP protocol number at value. 2953 """ 2954 COMPONENT_NAME = 'ip_proto' 2955 2956 2957@_FlowSpecComponentBase.register_type( 2958 _FlowSpecIPv6Component.TYPE_NEXT_HEADER, addr_family.IP6) 2959class FlowSpecNextHeader(_FlowSpecNumeric): 2960 """Next Header value in IPv6 packets 2961 2962 Set the IP protocol number at value 2963 """ 2964 COMPONENT_NAME = 'next_header' 2965 2966 2967@_FlowSpecComponentBase.register_type( 2968 _FlowSpecIPv4Component.TYPE_PORT, addr_family.IP) 2969@_FlowSpecComponentBase.register_type( 2970 _FlowSpecIPv6Component.TYPE_PORT, addr_family.IP6) 2971class FlowSpecPort(_FlowSpecNumeric): 2972 """Port number for Flow Specification NLRI component 2973 2974 Set the source or destination TCP/UDP ports at value. 2975 """ 2976 COMPONENT_NAME = 'port' 2977 2978 2979@_FlowSpecComponentBase.register_type( 2980 _FlowSpecIPv4Component.TYPE_DESTINATION_PORT, addr_family.IP) 2981@_FlowSpecComponentBase.register_type( 2982 _FlowSpecIPv6Component.TYPE_DESTINATION_PORT, addr_family.IP6) 2983class FlowSpecDestPort(_FlowSpecNumeric): 2984 """Destination port number for Flow Specification NLRI component 2985 2986 Set the destination port of a TCP or UDP packet at value. 2987 """ 2988 COMPONENT_NAME = 'dst_port' 2989 2990 2991@_FlowSpecComponentBase.register_type( 2992 _FlowSpecIPv4Component.TYPE_SOURCE_PORT, addr_family.IP) 2993@_FlowSpecComponentBase.register_type( 2994 _FlowSpecIPv6Component.TYPE_SOURCE_PORT, addr_family.IP6) 2995class FlowSpecSrcPort(_FlowSpecNumeric): 2996 """Source port number for Flow Specification NLRI component 2997 2998 Set the source port of a TCP or UDP packet at value. 2999 """ 3000 COMPONENT_NAME = 'src_port' 3001 3002 3003@_FlowSpecComponentBase.register_type( 3004 _FlowSpecIPv4Component.TYPE_ICMP, addr_family.IP) 3005@_FlowSpecComponentBase.register_type( 3006 _FlowSpecIPv6Component.TYPE_ICMP, addr_family.IP6) 3007class FlowSpecIcmpType(_FlowSpecNumeric): 3008 """ICMP type for Flow Specification NLRI component 3009 3010 Set the type field of an ICMP packet at value. 3011 """ 3012 COMPONENT_NAME = 'icmp_type' 3013 3014 3015@_FlowSpecComponentBase.register_type( 3016 _FlowSpecIPv4Component.TYPE_ICMP_CODE, addr_family.IP) 3017@_FlowSpecComponentBase.register_type( 3018 _FlowSpecIPv6Component.TYPE_ICMP_CODE, addr_family.IP6) 3019class FlowSpecIcmpCode(_FlowSpecNumeric): 3020 """ICMP code Flow Specification NLRI component 3021 3022 Set the code field of an ICMP packet at value. 3023 """ 3024 COMPONENT_NAME = 'icmp_code' 3025 3026 3027@_FlowSpecComponentBase.register_type( 3028 _FlowSpecIPv4Component.TYPE_TCP_FLAGS, addr_family.IP) 3029@_FlowSpecComponentBase.register_type( 3030 _FlowSpecIPv6Component.TYPE_TCP_FLAGS, addr_family.IP6) 3031class FlowSpecTCPFlags(_FlowSpecBitmask): 3032 """TCP flags for Flow Specification NLRI component 3033 3034 Supported TCP flags are CWR, ECN, URGENT, ACK, PUSH, RST, SYN and FIN. 3035 """ 3036 COMPONENT_NAME = 'tcp_flags' 3037 3038 # bitmask format 3039 # 0 1 2 3 4 5 6 7 3040 # +----+----+----+----+----+----+----+----+ 3041 # |CWR |ECN |URG |ACK |PSH |RST |SYN |FIN | 3042 # +----+----+----+----+----+----+----+----+ 3043 3044 CWR = 1 << 7 3045 ECN = 1 << 6 3046 URGENT = 1 << 5 3047 ACK = 1 << 4 3048 PUSH = 1 << 3 3049 RST = 1 << 2 3050 SYN = 1 << 1 3051 FIN = 1 << 0 3052 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' 3062 3063 3064@_FlowSpecComponentBase.register_type( 3065 _FlowSpecIPv4Component.TYPE_PACKET_LENGTH, addr_family.IP) 3066@_FlowSpecComponentBase.register_type( 3067 _FlowSpecIPv6Component.TYPE_PACKET_LENGTH, addr_family.IP6) 3068class FlowSpecPacketLen(_FlowSpecNumeric): 3069 """Packet length for Flow Specification NLRI component 3070 3071 Set the total IP packet length at value. 3072 """ 3073 COMPONENT_NAME = 'packet_len' 3074 3075 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) 3080class FlowSpecDSCP(_FlowSpecNumeric): 3081 """Diffserv Code Point for Flow Specification NLRI component 3082 3083 Set the 6-bit DSCP field at value. [RFC2474] 3084 """ 3085 COMPONENT_NAME = 'dscp' 3086 3087 3088@_FlowSpecComponentBase.register_type( 3089 _FlowSpecIPv4Component.TYPE_FRAGMENT, addr_family.IP) 3090class FlowSpecFragment(_FlowSpecBitmask): 3091 """Fragment for Flow Specification NLRI component 3092 3093 Set the bitmask for operand format at value. 3094 The following values are supported. 3095 3096 ========== =============================================== 3097 Attribute Description 3098 ========== =============================================== 3099 LF Last fragment 3100 FF First fragment 3101 ISF Is a fragment 3102 DF Don't fragment 3103 ========== =============================================== 3104 """ 3105 COMPONENT_NAME = 'fragment' 3106 3107 # bitmask format 3108 # 0 1 2 3 4 5 6 7 3109 # +---+---+---+---+---+---+---+---+ 3110 # | Reserved |LF |FF |IsF|DF | 3111 # +---+---+---+---+---+---+---+---+ 3112 3113 LF = 1 << 3 3114 FF = 1 << 2 3115 ISF = 1 << 1 3116 DF = 1 << 0 3117 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' 3123 3124 3125@_FlowSpecComponentBase.register_type( 3126 _FlowSpecIPv6Component.TYPE_FRAGMENT, addr_family.IP6) 3127class FlowSpecIPv6Fragment(_FlowSpecBitmask): 3128 """Fragment for Flow Specification for IPv6 NLRI component 3129 3130 ========== =============================================== 3131 Attribute Description 3132 ========== =============================================== 3133 LF Last fragment 3134 FF First fragment 3135 ISF Is a fragment 3136 ========== =============================================== 3137 """ 3138 COMPONENT_NAME = 'fragment' 3139 3140 # bitmask format 3141 # 0 1 2 3 4 5 6 7 3142 # +---+---+---+---+---+---+---+---+ 3143 # | Reserved |LF |FF |IsF| 0 | 3144 # +---+---+---+---+---+---+---+---+ 3145 3146 LF = 1 << 3 3147 FF = 1 << 2 3148 ISF = 1 << 1 3149 3150 _bitmask_flags = collections.OrderedDict() 3151 _bitmask_flags[LF] = 'LF' 3152 _bitmask_flags[FF] = 'FF' 3153 _bitmask_flags[ISF] = 'ISF' 3154 3155 3156@_FlowSpecComponentBase.register_type( 3157 _FlowSpecL2VPNComponent.TYPE_ETHER_TYPE, addr_family.L2VPN) 3158class FlowSpecEtherType(_FlowSpecNumeric): 3159 """Ethernet Type field in an Ethernet frame. 3160 3161 Set the 2 byte value of an Ethernet Type field at value. 3162 """ 3163 COMPONENT_NAME = 'ether_type' 3164 3165 3166@_FlowSpecComponentBase.register_type( 3167 _FlowSpecL2VPNComponent.TYPE_SOURCE_MAC, addr_family.L2VPN) 3168class FlowSpecSourceMac(_FlowSpecL2VPNPrefixBase): 3169 """Source Mac Address. 3170 3171 Set the Mac Address at value. 3172 """ 3173 COMPONENT_NAME = 'src_mac' 3174 3175 3176@_FlowSpecComponentBase.register_type( 3177 _FlowSpecL2VPNComponent.TYPE_DESTINATION_MAC, addr_family.L2VPN) 3178class FlowSpecDestinationMac(_FlowSpecL2VPNPrefixBase): 3179 """Destination Mac Address. 3180 3181 Set the Mac Address at value. 3182 """ 3183 COMPONENT_NAME = 'dst_mac' 3184 3185 3186@_FlowSpecComponentBase.register_type( 3187 _FlowSpecL2VPNComponent.TYPE_LLC_DSAP, addr_family.L2VPN) 3188class FlowSpecLLCDSAP(_FlowSpecNumeric): 3189 """Destination SAP field in LLC header in an Ethernet frame. 3190 3191 Set the 2 byte value of an Destination SAP at value. 3192 """ 3193 COMPONENT_NAME = 'llc_dsap' 3194 3195 3196@_FlowSpecComponentBase.register_type( 3197 _FlowSpecL2VPNComponent.TYPE_LLC_SSAP, addr_family.L2VPN) 3198class FlowSpecLLCSSAP(_FlowSpecNumeric): 3199 """Source SAP field in LLC header in an Ethernet frame. 3200 3201 Set the 2 byte value of an Source SAP at value. 3202 """ 3203 COMPONENT_NAME = 'llc_ssap' 3204 3205 3206@_FlowSpecComponentBase.register_type( 3207 _FlowSpecL2VPNComponent.TYPE_LLC_CONTROL, addr_family.L2VPN) 3208class FlowSpecLLCControl(_FlowSpecNumeric): 3209 """Control field in LLC header in an Ethernet frame. 3210 3211 Set the Contorol field at value. 3212 """ 3213 COMPONENT_NAME = 'llc_control' 3214 3215 3216@_FlowSpecComponentBase.register_type( 3217 _FlowSpecL2VPNComponent.TYPE_SNAP, addr_family.L2VPN) 3218class FlowSpecSNAP(_FlowSpecNumeric): 3219 """Sub-Network Access Protocol field in an Ethernet frame. 3220 3221 Set the 5 byte SNAP field at value. 3222 """ 3223 COMPONENT_NAME = 'snap' 3224 3225 3226@_FlowSpecComponentBase.register_type( 3227 _FlowSpecL2VPNComponent.TYPE_VLAN_ID, addr_family.L2VPN) 3228class FlowSpecVLANID(_FlowSpecNumeric): 3229 """VLAN ID. 3230 3231 Set VLAN ID at value. 3232 """ 3233 COMPONENT_NAME = 'vlan_id' 3234 3235 3236@_FlowSpecComponentBase.register_type( 3237 _FlowSpecL2VPNComponent.TYPE_VLAN_COS, addr_family.L2VPN) 3238class FlowSpecVLANCoS(_FlowSpecNumeric): 3239 """VLAN CoS Fields in an Ethernet frame. 3240 3241 Set the 3 bit CoS field at value. 3242 """ 3243 COMPONENT_NAME = 'vlan_cos' 3244 3245 3246@_FlowSpecComponentBase.register_type( 3247 _FlowSpecL2VPNComponent.TYPE_INNER_VLAN_ID, addr_family.L2VPN) 3248class FlowSpecInnerVLANID(_FlowSpecNumeric): 3249 """Inner VLAN ID. 3250 3251 Set VLAN ID at value. 3252 """ 3253 COMPONENT_NAME = 'inner_vlan_id' 3254 3255 3256@_FlowSpecComponentBase.register_type( 3257 _FlowSpecL2VPNComponent.TYPE_INNER_VLAN_COS, addr_family.L2VPN) 3258class FlowSpecInnerVLANCoS(_FlowSpecNumeric): 3259 """VLAN CoS Fields in an Inner Ethernet frame. 3260 3261 Set the 3 bit CoS field at value.. 3262 """ 3263 COMPONENT_NAME = 'inner_vlan_cos' 3264 3265 3266@_FlowSpecComponentBase.register_type( 3267 _FlowSpecIPv6Component.TYPE_FLOW_LABEL, addr_family.IP6) 3268class FlowSpecIPv6FlowLabel(_FlowSpecNumeric): 3269 COMPONENT_NAME = 'flow_label' 3270 3271 3272@functools.total_ordering 3273class RouteTargetMembershipNLRI(StringifyMixin): 3274 """Route Target Membership NLRI. 3275 3276 Route Target membership NLRI is advertised in BGP UPDATE messages using 3277 the MP_REACH_NLRI and MP_UNREACH_NLRI attributes. 3278 """ 3279 3280 ROUTE_FAMILY = RF_RTC_UC 3281 DEFAULT_AS = '0:0' 3282 DEFAULT_RT = '0:0' 3283 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): 3288 # We validate them 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 3294 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: 3298 return True 3299 else: 3300 return False 3301 3302 def _is_valid_ext_comm_attr(self, attr): 3303 """Validates *attr* as string representation of RT or SOO. 3304 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* 3308 """ 3309 is_valid = True 3310 3311 if not isinstance(attr, str): 3312 is_valid = False 3313 else: 3314 first, second = attr.split(':') 3315 try: 3316 if '.' in first: 3317 socket.inet_aton(first) 3318 else: 3319 int(first) 3320 int(second) 3321 except (ValueError, socket.error): 3322 is_valid = False 3323 3324 return is_valid 3325 3326 @property 3327 def formatted_nlri_str(self): 3328 return "%s:%s" % (self.origin_as, self.route_target) 3329 3330 def is_default_rtnlri(self): 3331 if (self._origin_as is self.DEFAULT_AS and 3332 self._route_target is self.DEFAULT_RT): 3333 return True 3334 return False 3335 3336 def __lt__(self, other): 3337 return ((self.origin_as, self.route_target) < 3338 (other.origin_as, other.route_target)) 3339 3340 def __eq__(self, other): 3341 return ((self.origin_as, self.route_target) == 3342 (other.origin_as, other.route_target)) 3343 3344 def __hash__(self): 3345 return hash((self.origin_as, self.route_target)) 3346 3347 @classmethod 3348 def parser(cls, buf): 3349 idx = 0 3350 3351 # Extract origin AS. 3352 origin_as, = struct.unpack_from('!I', buf, idx) 3353 idx += 4 3354 3355 # Extract route target. 3356 route_target = _ExtendedCommunity(buf[idx:]) 3357 return cls(origin_as, route_target) 3358 3359 def serialize(self): 3360 rt_nlri = b'' 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() 3365 3366 # RT Nlri is 12 octets 3367 return struct.pack('B', (8 * 12)) + rt_nlri 3368 3369 3370def _addr_class_key(route_family): 3371 return route_family.afi, route_family.safi 3372 3373 3374_ADDR_CLASSES = { 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, 3388} 3389 3390 3391def _get_addr_class(afi, safi): 3392 try: 3393 return _ADDR_CLASSES[(afi, safi)] 3394 except KeyError: 3395 return _BinAddrPrefix 3396 3397 3398class _OptParam(StringifyMixin, TypeDisp, _Value): 3399 _PACK_STR = '!BB' # type, length 3400 3401 def __init__(self, type_, value=None, length=None): 3402 if type_ is None: 3403 type_ = self._rev_lookup_type(self.__class__) 3404 self.type = type_ 3405 self.length = length 3406 if value is not None: 3407 self.value = value 3408 3409 @classmethod 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])] 3420 return caps, rest 3421 3422 def serialize(self): 3423 # fixup 3424 value = self.serialize_value() 3425 self.length = len(value) 3426 3427 buf = bytearray() 3428 msg_pack_into(self._PACK_STR, buf, 0, self.type, self.length) 3429 return buf + value 3430 3431 3432@_OptParam.register_unknown_type() 3433class BGPOptParamUnknown(_OptParam): 3434 @classmethod 3435 def parse_value(cls, buf): 3436 return { 3437 'value': buf 3438 }, cls 3439 3440 def serialize_value(self): 3441 return self.value 3442 3443 3444@_OptParam.register_type(BGP_OPT_CAPABILITY) 3445class _OptParamCapability(_OptParam, TypeDisp): 3446 _CAP_HDR_PACK_STR = '!BB' 3447 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, 3451 length=length) 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 3459 3460 @classmethod 3461 def parse_value(cls, buf): 3462 caps = [] 3463 while len(buf) > 0: 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:] 3468 kwargs = { 3469 'cap_code': code, 3470 'cap_length': length, 3471 } 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, 3475 **kwargs)) 3476 return caps 3477 3478 def serialize_value(self): 3479 # fixup 3480 cap_value = self.serialize_cap_value() 3481 self.cap_length = len(cap_value) 3482 3483 buf = bytearray() 3484 msg_pack_into(self._CAP_HDR_PACK_STR, buf, 0, self.cap_code, 3485 self.cap_length) 3486 return buf + cap_value 3487 3488 3489class _OptParamEmptyCapability(_OptParamCapability): 3490 @classmethod 3491 def parse_cap_value(cls, buf): 3492 return {} 3493 3494 def serialize_cap_value(self): 3495 return bytearray() 3496 3497 3498@_OptParamCapability.register_unknown_type() 3499class BGPOptParamCapabilityUnknown(_OptParamCapability): 3500 @classmethod 3501 def parse_cap_value(cls, buf): 3502 return {'cap_value': buf} 3503 3504 def serialize_cap_value(self): 3505 return self.cap_value 3506 3507 3508@_OptParamCapability.register_type(BGP_CAP_ROUTE_REFRESH) 3509class BGPOptParamCapabilityRouteRefresh(_OptParamEmptyCapability): 3510 pass 3511 3512 3513@_OptParamCapability.register_type(BGP_CAP_ROUTE_REFRESH_CISCO) 3514class BGPOptParamCapabilityCiscoRouteRefresh(_OptParamEmptyCapability): 3515 pass 3516 3517 3518@_OptParamCapability.register_type(BGP_CAP_ENHANCED_ROUTE_REFRESH) 3519class BGPOptParamCapabilityEnhancedRouteRefresh(_OptParamEmptyCapability): 3520 pass 3521 3522 3523@_OptParamCapability.register_type(BGP_CAP_GRACEFUL_RESTART) 3524class BGPOptParamCapabilityGracefulRestart(_OptParamCapability): 3525 _CAP_PACK_STR = "!H" 3526 3527 def __init__(self, flags, time, tuples, **kwargs): 3528 super(BGPOptParamCapabilityGracefulRestart, self).__init__(**kwargs) 3529 self.flags = flags 3530 self.time = time 3531 self.tuples = tuples 3532 3533 @classmethod 3534 def parse_cap_value(cls, buf): 3535 (restart, ) = struct.unpack_from(cls._CAP_PACK_STR, 3536 six.binary_type(buf)) 3537 buf = buf[2:] 3538 l = [] 3539 while len(buf) >= 4: 3540 l.append(struct.unpack_from("!HBB", buf)) 3541 buf = buf[4:] 3542 return {'flags': restart >> 12, 'time': restart & 0xfff, 'tuples': l} 3543 3544 def serialize_cap_value(self): 3545 buf = bytearray() 3546 msg_pack_into(self._CAP_PACK_STR, buf, 0, self.flags << 12 | self.time) 3547 offset = 2 3548 for i in self.tuples: 3549 afi, safi, flags = i 3550 msg_pack_into("!HBB", buf, offset, afi, safi, flags) 3551 offset += 4 3552 return buf 3553 3554 3555@_OptParamCapability.register_type(BGP_CAP_FOUR_OCTET_AS_NUMBER) 3556class BGPOptParamCapabilityFourOctetAsNumber(_OptParamCapability): 3557 _CAP_PACK_STR = '!I' 3558 3559 def __init__(self, as_number, **kwargs): 3560 super(BGPOptParamCapabilityFourOctetAsNumber, self).__init__(**kwargs) 3561 self.as_number = as_number 3562 3563 @classmethod 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} 3568 3569 def serialize_cap_value(self): 3570 buf = bytearray() 3571 msg_pack_into(self._CAP_PACK_STR, buf, 0, self.as_number) 3572 return buf 3573 3574 3575@_OptParamCapability.register_type(BGP_CAP_MULTIPROTOCOL) 3576class BGPOptParamCapabilityMultiprotocol(_OptParamCapability): 3577 _CAP_PACK_STR = '!HBB' # afi, reserved, safi 3578 3579 def __init__(self, afi, safi, reserved=0, **kwargs): 3580 super(BGPOptParamCapabilityMultiprotocol, self).__init__(**kwargs) 3581 self.afi = afi 3582 self.reserved = reserved 3583 self.safi = safi 3584 3585 @classmethod 3586 def parse_cap_value(cls, buf): 3587 (afi, reserved, safi,) = struct.unpack_from(cls._CAP_PACK_STR, 3588 six.binary_type(buf)) 3589 return { 3590 'afi': afi, 3591 'reserved': reserved, 3592 'safi': safi, 3593 } 3594 3595 def serialize_cap_value(self): 3596 # fixup 3597 self.reserved = 0 3598 3599 buf = bytearray() 3600 msg_pack_into(self._CAP_PACK_STR, buf, 0, 3601 self.afi, self.reserved, self.safi) 3602 return buf 3603 3604 3605@_OptParamCapability.register_type(BGP_CAP_CARRYING_LABEL_INFO) 3606class BGPOptParamCapabilityCarryingLabelInfo(_OptParamEmptyCapability): 3607 pass 3608 3609 3610class BGPWithdrawnRoute(IPAddrPrefix): 3611 pass 3612 3613 3614class _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 3618 _ATTR_FLAGS = None 3619 3620 def __init__(self, value=None, flags=0, type_=None, length=None): 3621 if type_ is None: 3622 type_ = self._rev_lookup_type(self.__class__) 3623 self.flags = flags 3624 self.type = type_ 3625 self.length = length 3626 if value is not None: 3627 self.value = value 3628 3629 @classmethod 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 3636 else: 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 3645 3646 def serialize(self): 3647 # fixup 3648 if self._ATTR_FLAGS is not None: 3649 self.flags = ( 3650 self.flags 3651 & ~(BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE) 3652 | self._ATTR_FLAGS) 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 3660 else: 3661 self.flags &= ~BGP_ATTR_FLAG_EXTENDED_LENGTH 3662 len_pack_str = self._PACK_STR_LEN 3663 3664 buf = bytearray() 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) 3667 return buf + value 3668 3669 3670@_PathAttribute.register_unknown_type() 3671class BGPPathAttributeUnknown(_PathAttribute): 3672 @classmethod 3673 def parse_value(cls, buf): 3674 return { 3675 'value': buf 3676 } 3677 3678 def serialize_value(self): 3679 return self.value 3680 3681 3682class _PathAttributeUint32(_PathAttribute): 3683 _VALUE_PACK_STR = '!I' 3684 3685 3686@_PathAttribute.register_type(BGP_ATTR_TYPE_ORIGIN) 3687class BGPPathAttributeOrigin(_PathAttribute): 3688 _VALUE_PACK_STR = '!B' 3689 _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE 3690 3691 3692class _BGPPathAttributeAsPathCommon(_PathAttribute): 3693 _AS_SET = 1 3694 _AS_SEQUENCE = 2 3695 _SEG_HDR_PACK_STR = '!BB' 3696 _AS_PACK_STR = None 3697 _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE 3698 3699 def __init__(self, value, as_pack_str=None, flags=0, type_=None, 3700 length=None): 3701 super(_BGPPathAttributeAsPathCommon, self).__init__(value=value, 3702 flags=flags, 3703 type_=type_, 3704 length=length) 3705 if as_pack_str: 3706 self._AS_PACK_STR = as_pack_str 3707 3708 @property 3709 def path_seg_list(self): 3710 return copy.deepcopy(self.value) 3711 3712 def get_as_path_len(self): 3713 count = 0 3714 for seg in self.value: 3715 if isinstance(seg, list): 3716 # Segment type 2 stored in list and all AS counted. 3717 count += len(seg) 3718 else: 3719 # Segment type 1 stored in set and count as one. 3720 count += 1 3721 3722 return count 3723 3724 def has_local_as(self, local_as, max_count=0): 3725 """Check if *local_as* is already present on path list.""" 3726 _count = 0 3727 for as_path_seg in self.value: 3728 _count += list(as_path_seg).count(local_as) 3729 return _count > max_count 3730 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: 3734 return False 3735 3736 leftmost_seg = self.path_seg_list[0] 3737 if leftmost_seg and leftmost_seg[0] == remote_as: 3738 return True 3739 3740 return False 3741 3742 @classmethod 3743 def _is_valid_16bit_as_path(cls, buf): 3744 3745 two_byte_as_size = struct.calcsize('!H') 3746 3747 while buf: 3748 (type_, num_as) = struct.unpack_from(cls._SEG_HDR_PACK_STR, 3749 six.binary_type(buf)) 3750 3751 if type_ is not cls._AS_SET and type_ is not cls._AS_SEQUENCE: 3752 return False 3753 3754 buf = buf[struct.calcsize(cls._SEG_HDR_PACK_STR):] 3755 3756 if len(buf) < num_as * two_byte_as_size: 3757 return False 3758 3759 buf = buf[num_as * two_byte_as_size:] 3760 3761 return True 3762 3763 @classmethod 3764 def parse_value(cls, buf): 3765 result = [] 3766 3767 if cls._is_valid_16bit_as_path(buf): 3768 as_pack_str = '!H' 3769 else: 3770 as_pack_str = '!I' 3771 3772 while 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):] 3776 l = [] 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):] 3781 l.append(as_number) 3782 if type_ == cls._AS_SET: 3783 result.append(set(l)) 3784 elif type_ == cls._AS_SEQUENCE: 3785 result.append(l) 3786 else: 3787 # protocol error 3788 raise struct.error('Unsupported segment type: %s' % type_) 3789 return { 3790 'value': result, 3791 'as_pack_str': as_pack_str, 3792 } 3793 3794 def serialize_value(self): 3795 buf = bytearray() 3796 offset = 0 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 3802 else: 3803 raise struct.error( 3804 'Element of %s.value must be of type set or list' % 3805 self.__class__.__name__) 3806 l = list(e) 3807 num_as = len(l) 3808 if num_as == 0: 3809 continue 3810 msg_pack_into(self._SEG_HDR_PACK_STR, buf, offset, type_, num_as) 3811 offset += struct.calcsize(self._SEG_HDR_PACK_STR) 3812 for i in l: 3813 msg_pack_into(self._AS_PACK_STR, buf, offset, i) 3814 offset += struct.calcsize(self._AS_PACK_STR) 3815 return buf 3816 3817 3818@_PathAttribute.register_type(BGP_ATTR_TYPE_AS_PATH) 3819class 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 3829 _AS_PACK_STR = '!H' 3830 3831 3832@_PathAttribute.register_type(BGP_ATTR_TYPE_AS4_PATH) 3833class BGPPathAttributeAs4Path(_BGPPathAttributeAsPathCommon): 3834 _AS_PACK_STR = '!I' 3835 _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL 3836 3837 @classmethod 3838 def _is_valid_16bit_as_path(cls, buf): 3839 return False 3840 3841 3842@_PathAttribute.register_type(BGP_ATTR_TYPE_NEXT_HOP) 3843class BGPPathAttributeNextHop(_PathAttribute): 3844 _VALUE_PACK_STR = '!4s' 3845 _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE 3846 _TYPE = { 3847 'ascii': [ 3848 'value' 3849 ] 3850 } 3851 3852 @classmethod 3853 def parse_value(cls, buf): 3854 (ip_addr,) = struct.unpack_from(cls._VALUE_PACK_STR, 3855 six.binary_type(buf)) 3856 return { 3857 'value': addrconv.ipv4.bin_to_text(ip_addr), 3858 } 3859 3860 def serialize_value(self): 3861 buf = bytearray() 3862 msg_pack_into(self._VALUE_PACK_STR, buf, 0, 3863 addrconv.ipv4.text_to_bin(self.value)) 3864 return buf 3865 3866 3867@_PathAttribute.register_type(BGP_ATTR_TYPE_MULTI_EXIT_DISC) 3868class BGPPathAttributeMultiExitDisc(_PathAttributeUint32): 3869 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL 3870 3871 3872@_PathAttribute.register_type(BGP_ATTR_TYPE_LOCAL_PREF) 3873class BGPPathAttributeLocalPref(_PathAttributeUint32): 3874 _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE 3875 3876 3877@_PathAttribute.register_type(BGP_ATTR_TYPE_ATOMIC_AGGREGATE) 3878class BGPPathAttributeAtomicAggregate(_PathAttribute): 3879 _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE 3880 3881 @classmethod 3882 def parse_value(cls, buf): 3883 return {} 3884 3885 def serialize_value(self): 3886 return b'' 3887 3888 3889class _BGPPathAttributeAggregatorCommon(_PathAttribute): 3890 _VALUE_PACK_STR = None 3891 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE 3892 _TYPE = { 3893 'ascii': [ 3894 'addr' 3895 ] 3896 } 3897 3898 def __init__(self, as_number, addr, flags=0, type_=None, length=None): 3899 super(_BGPPathAttributeAggregatorCommon, self).__init__(flags=flags, 3900 type_=type_, 3901 length=length) 3902 self.as_number = as_number 3903 self.addr = addr 3904 3905 @classmethod 3906 def parse_value(cls, buf): 3907 (as_number, addr) = struct.unpack_from(cls._VALUE_PACK_STR, 3908 six.binary_type(buf)) 3909 return { 3910 'as_number': as_number, 3911 'addr': addrconv.ipv4.bin_to_text(addr), 3912 } 3913 3914 def serialize_value(self): 3915 buf = bytearray() 3916 msg_pack_into(self._VALUE_PACK_STR, buf, 0, self.as_number, 3917 addrconv.ipv4.text_to_bin(self.addr)) 3918 return buf 3919 3920 3921@_PathAttribute.register_type(BGP_ATTR_TYPE_AGGREGATOR) 3922class BGPPathAttributeAggregator(_BGPPathAttributeAggregatorCommon): 3923 # Note: AS numbers can be Two-Octet or Four-Octet. 3924 # This class would detect it by the value length field. 3925 # For example, 3926 # - if the value field length is 6 (='!H4s'), AS number should 3927 # be Two-Octet. 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) 3933 3934 @classmethod 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) 3939 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() 3944 3945 3946@_PathAttribute.register_type(BGP_ATTR_TYPE_AS4_AGGREGATOR) 3947class BGPPathAttributeAs4Aggregator(_BGPPathAttributeAggregatorCommon): 3948 _VALUE_PACK_STR = '!I4s' 3949 3950 3951@_PathAttribute.register_type(BGP_ATTR_TYPE_COMMUNITIES) 3952class BGPPathAttributeCommunities(_PathAttribute): 3953 _VALUE_PACK_STR = '!I' 3954 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE 3955 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) 3961 3962 def __init__(self, communities, 3963 flags=0, type_=None, length=None): 3964 super(BGPPathAttributeCommunities, self).__init__(flags=flags, 3965 type_=type_, 3966 length=length) 3967 self.communities = communities 3968 3969 @classmethod 3970 def parse_value(cls, buf): 3971 rest = buf 3972 communities = [] 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:] 3979 return { 3980 'communities': communities, 3981 } 3982 3983 def serialize_value(self): 3984 buf = bytearray() 3985 for comm in self.communities: 3986 bincomm = bytearray() 3987 msg_pack_into(self._VALUE_PACK_STR, bincomm, 0, comm) 3988 buf += bincomm 3989 return buf 3990 3991 @staticmethod 3992 def is_no_export(comm_attr): 3993 """Returns True if given value matches well-known community NO_EXPORT 3994 attribute value. 3995 """ 3996 return comm_attr == BGPPathAttributeCommunities.NO_EXPORT 3997 3998 @staticmethod 3999 def is_no_advertise(comm_attr): 4000 """Returns True if given value matches well-known community 4001 NO_ADVERTISE attribute value. 4002 """ 4003 return comm_attr == BGPPathAttributeCommunities.NO_ADVERTISE 4004 4005 @staticmethod 4006 def is_no_export_subconfed(comm_attr): 4007 """Returns True if given value matches well-known community 4008 NO_EXPORT_SUBCONFED attribute value. 4009 """ 4010 return comm_attr == BGPPathAttributeCommunities.NO_EXPORT_SUBCONFED 4011 4012 def has_comm_attr(self, attr): 4013 """Returns True if given community attribute is present.""" 4014 4015 for comm_attr in self.communities: 4016 if comm_attr == attr: 4017 return True 4018 4019 return False 4020 4021 4022@_PathAttribute.register_type(BGP_ATTR_TYPE_ORIGINATOR_ID) 4023class 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 4029 _TYPE = { 4030 'asciilist': [ 4031 'value' 4032 ] 4033 } 4034 4035 @classmethod 4036 def parse_value(cls, buf): 4037 (originator_id,) = struct.unpack_from(cls._VALUE_PACK_STR, 4038 six.binary_type(buf)) 4039 return { 4040 'value': addrconv.ipv4.bin_to_text(originator_id), 4041 } 4042 4043 def serialize_value(self): 4044 buf = bytearray() 4045 msg_pack_into(self._VALUE_PACK_STR, buf, 0, 4046 addrconv.ipv4.text_to_bin(self.value)) 4047 return buf 4048 4049 4050@_PathAttribute.register_type(BGP_ATTR_TYPE_CLUSTER_LIST) 4051class 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 4057 _TYPE = { 4058 'ascii': [ 4059 'value' 4060 ] 4061 } 4062 4063 @classmethod 4064 def parse_value(cls, buf): 4065 rest = buf 4066 cluster_list = [] 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:] 4073 return { 4074 'value': cluster_list, 4075 } 4076 4077 def serialize_value(self): 4078 buf = bytearray() 4079 offset = 0 4080 for cluster_id in self.value: 4081 msg_pack_into( 4082 self._VALUE_PACK_STR, 4083 buf, 4084 offset, 4085 addrconv.ipv4.text_to_bin(cluster_id)) 4086 offset += struct.calcsize(self._VALUE_PACK_STR) 4087 return buf 4088 4089 4090# Extended Communities 4091# RFC 4360 4092# RFC 5668 4093# IANA registry: 4094# https://www.iana.org/assignments/bgp-extended-communities/ 4095# bgp-extended-communities.xml 4096# 4097# type 4098# high low 4099# 00 sub-type Two-Octet AS Specific Extended Community (transitive) 4100# 40 sub-type Two-Octet AS Specific Extended Community 4101# payload: 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 4106# payload: 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 4111# payload: 4112# 6 byte opaque value (defined by sub-type) 4113# 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) 4122 4123@_PathAttribute.register_type(BGP_ATTR_TYPE_EXTENDED_COMMUNITIES) 4124class BGPPathAttributeExtendedCommunities(_PathAttribute): 4125 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE 4126 _class_prefixes = ['BGP'] 4127 4128 def __init__(self, communities, 4129 flags=0, type_=None, length=None): 4130 super(BGPPathAttributeExtendedCommunities, 4131 self).__init__(flags=flags, 4132 type_=type_, 4133 length=length) 4134 self.communities = communities 4135 4136 @classmethod 4137 def parse_value(cls, buf): 4138 rest = buf 4139 communities = [] 4140 while rest: 4141 comm, rest = _ExtendedCommunity.parse(rest) 4142 communities.append(comm) 4143 return { 4144 'communities': communities, 4145 } 4146 4147 def serialize_value(self): 4148 buf = bytearray() 4149 for comm in self.communities: 4150 buf += comm.serialize() 4151 return buf 4152 4153 def _community_list(self, subtype): 4154 _list = [] 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)) 4163 return _list 4164 4165 @property 4166 def rt_list(self): 4167 return self._community_list(2) 4168 4169 @property 4170 def soo_list(self): 4171 return self._community_list(3) 4172 4173 4174class _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 4179 TRANSITIVE = 0x40 4180 _TYPE_HIGH_MASK = ~TRANSITIVE 4181 4182 TWO_OCTET_AS_SPECIFIC = 0x00 4183 IPV4_ADDRESS_SPECIFIC = 0x01 4184 FOUR_OCTET_AS_SPECIFIC = 0x02 4185 OPAQUE = 0x03 4186 SUBTYPE_ENCAPSULATION = 0x0c 4187 ENCAPSULATION = (OPAQUE, SUBTYPE_ENCAPSULATION) 4188 EVPN = 0x06 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) 4195 FLOWSPEC = 0x80 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) 4209 4210 def __init__(self, type_=None): 4211 if type_ is None: 4212 type_ = self._rev_lookup_type(self.__class__) 4213 if isinstance(type_, (tuple, list)): 4214 type_ = type_[0] 4215 self.type = type_ 4216 4217 @classmethod 4218 def parse_subtype(cls, buf): 4219 (subtype,) = struct.unpack_from(cls._SUBTYPE_PACK_STR, buf) 4220 return subtype 4221 4222 @classmethod 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 4232 4233 def serialize(self): 4234 return struct.pack(self._PACK_STR, self.type, 4235 self.serialize_value()) 4236 4237 4238@_ExtendedCommunity.register_type(_ExtendedCommunity.TWO_OCTET_AS_SPECIFIC) 4239class BGPTwoOctetAsSpecificExtendedCommunity(_ExtendedCommunity): 4240 _VALUE_PACK_STR = '!BHI' # sub type, as number, local adm 4241 _VALUE_FIELDS = ['subtype', 'as_number', 'local_administrator'] 4242 4243 def __init__(self, **kwargs): 4244 super(BGPTwoOctetAsSpecificExtendedCommunity, self).__init__() 4245 self.do_init(BGPTwoOctetAsSpecificExtendedCommunity, self, kwargs) 4246 4247 4248@_ExtendedCommunity.register_type(_ExtendedCommunity.IPV4_ADDRESS_SPECIFIC) 4249class BGPIPv4AddressSpecificExtendedCommunity(_ExtendedCommunity): 4250 _VALUE_PACK_STR = '!B4sH' # sub type, IPv4 address, local adm 4251 _VALUE_FIELDS = ['subtype', 'ipv4_address', 'local_administrator'] 4252 _TYPE = { 4253 'ascii': [ 4254 'ipv4_address' 4255 ] 4256 } 4257 4258 def __init__(self, **kwargs): 4259 super(BGPIPv4AddressSpecificExtendedCommunity, self).__init__() 4260 self.do_init(BGPIPv4AddressSpecificExtendedCommunity, self, kwargs) 4261 4262 @classmethod 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']) 4267 return d_ 4268 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) 4273 4274 4275@_ExtendedCommunity.register_type(_ExtendedCommunity.FOUR_OCTET_AS_SPECIFIC) 4276class BGPFourOctetAsSpecificExtendedCommunity(_ExtendedCommunity): 4277 _VALUE_PACK_STR = '!BIH' # sub type, as number, local adm 4278 _VALUE_FIELDS = ['subtype', 'as_number', 'local_administrator'] 4279 4280 def __init__(self, **kwargs): 4281 super(BGPFourOctetAsSpecificExtendedCommunity, self).__init__() 4282 self.do_init(BGPFourOctetAsSpecificExtendedCommunity, self, kwargs) 4283 4284 4285@_ExtendedCommunity.register_type(_ExtendedCommunity.OPAQUE) 4286class BGPOpaqueExtendedCommunity(_ExtendedCommunity): 4287 _VALUE_PACK_STR = '!B6s' 4288 _VALUE_FIELDS = ['subtype', 'opaque'] 4289 4290 def __init__(self, **kwargs): 4291 super(BGPOpaqueExtendedCommunity, self).__init__() 4292 self.do_init(BGPOpaqueExtendedCommunity, self, kwargs) 4293 4294 4295@_ExtendedCommunity.register_type(_ExtendedCommunity.ENCAPSULATION) 4296class BGPEncapsulationExtendedCommunity(_ExtendedCommunity): 4297 _VALUE_PACK_STR = '!B4xH' 4298 _VALUE_FIELDS = ['subtype', 'tunnel_type'] 4299 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 4303 TUNNEL_TYPE_GRE = 2 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 4311 4312 def __init__(self, **kwargs): 4313 super(BGPEncapsulationExtendedCommunity, self).__init__() 4314 self.do_init(BGPEncapsulationExtendedCommunity, self, kwargs) 4315 4316 @classmethod 4317 def from_str(cls, tunnel_type): 4318 """ 4319 Returns an instance identified with the given `tunnel_type`. 4320 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. 4324 4325 Example: 4326 - `gre` means TUNNEL_TYPE_GRE 4327 - `vxlan` means TUNNEL_TYPE_VXLAN 4328 4329 And raises AttributeError when the corresponding Tunnel Type 4330 is not found to the given `tunnel_type`. 4331 """ 4332 return cls(subtype=_ExtendedCommunity.SUBTYPE_ENCAPSULATION, 4333 tunnel_type=getattr(cls, 4334 'TUNNEL_TYPE_' + tunnel_type.upper())) 4335 4336 4337@_ExtendedCommunity.register_type(_ExtendedCommunity.EVPN_MAC_MOBILITY) 4338class BGPEvpnMacMobilityExtendedCommunity(_ExtendedCommunity): 4339 """ 4340 MAC Mobility Extended Community 4341 """ 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'] 4350 4351 def __init__(self, **kwargs): 4352 super(BGPEvpnMacMobilityExtendedCommunity, self).__init__() 4353 self.do_init(BGPEvpnMacMobilityExtendedCommunity, self, kwargs) 4354 4355 4356@_ExtendedCommunity.register_type(_ExtendedCommunity.EVPN_ESI_LABEL) 4357class BGPEvpnEsiLabelExtendedCommunity(_ExtendedCommunity): 4358 """ 4359 ESI Label Extended Community 4360 """ 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'] 4369 4370 # Classification for Flags. 4371 SINGLE_ACTIVE_BIT = 1 << 0 4372 4373 def __init__(self, label=None, mpls_label=None, vni=None, **kwargs): 4374 super(BGPEvpnEsiLabelExtendedCommunity, self).__init__() 4375 self.do_init(BGPEvpnEsiLabelExtendedCommunity, self, kwargs) 4376 4377 if label: 4378 # If binary type label field value is specified, stores it 4379 # and decodes as MPLS label and VNI. 4380 self._label = label 4381 self._mpls_label, _ = mpls.label_from_bin(label) 4382 self._vni = vxlan.vni_from_bin(label) 4383 else: 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 4388 self._vni = vni 4389 4390 def _serialize_label(self, mpls_label, vni): 4391 if mpls_label: 4392 return mpls.label_to_bin(mpls_label, is_bos=True) 4393 elif vni: 4394 return vxlan.vni_to_bin(vni) 4395 else: 4396 return b'\x00' * 3 4397 4398 @classmethod 4399 def parse_value(cls, buf): 4400 (subtype, flags, 4401 label) = struct.unpack_from(cls._VALUE_PACK_STR, buf) 4402 return { 4403 'subtype': subtype, 4404 'flags': flags, 4405 'label': label, 4406 } 4407 4408 def serialize_value(self): 4409 return struct.pack(self._VALUE_PACK_STR, self.subtype, self.flags, 4410 self._label) 4411 4412 @property 4413 def mpls_label(self): 4414 return self._mpls_label 4415 4416 @mpls_label.setter 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 4421 4422 @property 4423 def vni(self): 4424 return self._vni 4425 4426 @vni.setter 4427 def vni(self, vni): 4428 self._label = vxlan.vni_to_bin(vni) 4429 self._mpls_label = None # disables ESI label 4430 self._vni = vni 4431 4432 4433@_ExtendedCommunity.register_type(_ExtendedCommunity.EVPN_ES_IMPORT_RT) 4434class BGPEvpnEsImportRTExtendedCommunity(_ExtendedCommunity): 4435 """ 4436 ES-Import Route Target Extended Community 4437 """ 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'] 4446 _TYPE = { 4447 'ascii': [ 4448 'es_import' 4449 ] 4450 } 4451 4452 def __init__(self, **kwargs): 4453 super(BGPEvpnEsImportRTExtendedCommunity, self).__init__() 4454 self.do_init(BGPEvpnEsImportRTExtendedCommunity, self, kwargs) 4455 4456 @classmethod 4457 def parse_value(cls, buf): 4458 (subtype, es_import) = struct.unpack_from(cls._VALUE_PACK_STR, buf) 4459 return { 4460 'subtype': subtype, 4461 'es_import': addrconv.mac.bin_to_text(es_import), 4462 } 4463 4464 def serialize_value(self): 4465 return struct.pack(self._VALUE_PACK_STR, self.subtype, 4466 addrconv.mac.text_to_bin(self.es_import)) 4467 4468 4469@_ExtendedCommunity.register_type(_ExtendedCommunity.FLOWSPEC_TRAFFIC_RATE) 4470class BGPFlowSpecTrafficRateCommunity(_ExtendedCommunity): 4471 """ 4472 Flow Specification Traffic Filtering Actions for Traffic Rate. 4473 4474 ========================== =============================================== 4475 Attribute Description 4476 ========================== =============================================== 4477 as_number Autonomous System number. 4478 rate_info rate information. 4479 ========================== =============================================== 4480 """ 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' 4490 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) 4495 4496 @classmethod 4497 def parse_value(cls, buf): 4498 (subtype, as_number, 4499 rate_info) = struct.unpack_from(cls._VALUE_PACK_STR, buf) 4500 return { 4501 'subtype': subtype, 4502 'as_number': as_number, 4503 'rate_info': rate_info, 4504 } 4505 4506 def serialize_value(self): 4507 return struct.pack(self._VALUE_PACK_STR, self.subtype, 4508 self.as_number, self.rate_info) 4509 4510 4511@_ExtendedCommunity.register_type(_ExtendedCommunity.FLOWSPEC_TRAFFIC_ACTION) 4512class BGPFlowSpecTrafficActionCommunity(_ExtendedCommunity): 4513 """ 4514 Flow Specification Traffic Filtering Actions for Traffic Action. 4515 4516 ========================== =============================================== 4517 Attribute Description 4518 ========================== =============================================== 4519 action Apply action. 4520 The supported action are 4521 ``SAMPLE`` and ``TERMINAL``. 4522 ========================== =============================================== 4523 """ 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 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 4530 4531 # Traffic-action format 4532 # 40 41 42 43 44 45 46 47 4533 # +---+---+---+---+---+---+---+---+ 4534 # | reserved | S | T | 4535 # +---+---+---+---+---+---+---+---+ 4536 4537 _VALUE_PACK_STR = '!B5xB' 4538 _VALUE_FIELDS = ['subtype', 'action'] 4539 ACTION_NAME = 'traffic_action' 4540 SAMPLE = 1 << 1 4541 TERMINAL = 1 << 0 4542 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) 4547 4548 4549@_ExtendedCommunity.register_type(_ExtendedCommunity.FLOWSPEC_REDIRECT) 4550class BGPFlowSpecRedirectCommunity(BGPTwoOctetAsSpecificExtendedCommunity): 4551 """ 4552 Flow Specification Traffic Filtering Actions for Redirect. 4553 4554 ========================== =============================================== 4555 Attribute Description 4556 ========================== =============================================== 4557 as_number Autonomous System number. 4558 local_administrator Local Administrator. 4559 ========================== =============================================== 4560 """ 4561 ACTION_NAME = 'redirect' 4562 4563 def __init__(self, **kwargs): 4564 super(BGPTwoOctetAsSpecificExtendedCommunity, self).__init__() 4565 kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_REDIRECT 4566 self.do_init(BGPTwoOctetAsSpecificExtendedCommunity, self, kwargs) 4567 4568 4569@_ExtendedCommunity.register_type( 4570 _ExtendedCommunity.FLOWSPEC_TRAFFIC_REMARKING) 4571class BGPFlowSpecTrafficMarkingCommunity(_ExtendedCommunity): 4572 """ 4573 Flow Specification Traffic Filtering Actions for Traffic Marking. 4574 4575 ========================== =============================================== 4576 Attribute Description 4577 ========================== =============================================== 4578 dscp Differentiated Services Code Point. 4579 ========================== =============================================== 4580 """ 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' 4590 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) 4595 4596 @classmethod 4597 def parse_value(cls, buf): 4598 (subtype, dscp) = struct.unpack_from(cls._VALUE_PACK_STR, buf) 4599 return { 4600 'subtype': subtype, 4601 'dscp': dscp, 4602 } 4603 4604 def serialize_value(self): 4605 return struct.pack(self._VALUE_PACK_STR, self.subtype, self.dscp) 4606 4607 4608# TODO 4609# Implement "Redirect-IPv6" [draft-ietf-idr-flow-spec-v6-08] 4610 4611 4612@_ExtendedCommunity.register_type( 4613 _ExtendedCommunity.FLOWSPEC_VLAN_ACTION) 4614class BGPFlowSpecVlanActionCommunity(_ExtendedCommunity): 4615 """ 4616 Flow Specification Vlan Actions. 4617 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 ========= =============================================== 4630 """ 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' 4638 _VALUE_FIELDS = [ 4639 'subtype', 4640 'actions_1', 4641 'actions_2', 4642 'vlan_1', 4643 'vlan_2', 4644 'cos_1', 4645 'cos_2'] 4646 ACTION_NAME = 'vlan_action' 4647 _COS_MASK = 0x07 4648 4649 POP = 1 << 7 4650 PUSH = 1 << 6 4651 SWAP = 1 << 5 4652 REWRITE_INNER = 1 << 4 4653 REWRITE_OUTER = 1 << 3 4654 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) 4659 4660 @classmethod 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) 4664 4665 return { 4666 'subtype': subtype, 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) 4673 } 4674 4675 def serialize_value(self): 4676 return struct.pack( 4677 self._VALUE_PACK_STR, 4678 self.subtype, 4679 self.actions_1, 4680 self.actions_2, 4681 (self.vlan_1 << 4) + (self.cos_1 << 1), 4682 (self.vlan_2 << 4) + (self.cos_2 << 1), 4683 ) 4684 4685 4686@_ExtendedCommunity.register_type( 4687 _ExtendedCommunity.FLOWSPEC_TPID_ACTION) 4688class BGPFlowSpecTPIDActionCommunity(_ExtendedCommunity): 4689 """ 4690 Flow Specification TPID Actions. 4691 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 ========= ========================================================= 4701 """ 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 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 4706 # | TPID1 | TPID2 | 4707 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 4708 _VALUE_PACK_STR = '!BHHH' 4709 _VALUE_FIELDS = ['subtype', 'actions', 'tpid_1', 'tpid_2'] 4710 ACTION_NAME = 'tpid_action' 4711 4712 TI = 1 << 15 4713 TO = 1 << 14 4714 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) 4719 4720 @classmethod 4721 def parse_value(cls, buf): 4722 (subtype, actions, tpid_1, tpid_2) = struct.unpack_from( 4723 cls._VALUE_PACK_STR, buf) 4724 4725 return { 4726 'subtype': subtype, 4727 'actions': actions, 4728 'tpid_1': tpid_1, 4729 'tpid_2': tpid_2, 4730 } 4731 4732 def serialize_value(self): 4733 return struct.pack( 4734 self._VALUE_PACK_STR, 4735 self.subtype, 4736 self.actions, 4737 self.tpid_1, 4738 self.tpid_2, 4739 ) 4740 4741 4742@_ExtendedCommunity.register_unknown_type() 4743class BGPUnknownExtendedCommunity(_ExtendedCommunity): 4744 _VALUE_PACK_STR = '!7s' # opaque value 4745 4746 def __init__(self, type_, **kwargs): 4747 super(BGPUnknownExtendedCommunity, self).__init__(type_=type_) 4748 self.do_init(BGPUnknownExtendedCommunity, self, kwargs, type_=type_) 4749 4750 4751@_PathAttribute.register_type(BGP_ATTR_TYPE_MP_REACH_NLRI) 4752class BGPPathAttributeMpReachNLRI(_PathAttribute): 4753 _VALUE_PACK_STR = '!HBB' # afi, safi, next_hop_len 4754 _VALUE_PACK_SIZE = struct.calcsize(_VALUE_PACK_STR) 4755 _RD_LENGTH = 8 4756 _RESERVED_LENGTH = 1 4757 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL 4758 _class_suffixes = ['AddrPrefix'] 4759 _opt_attributes = ['next_hop'] 4760 _TYPE = { 4761 'ascii': [ 4762 'next_hop' 4763 ] 4764 } 4765 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) 4770 self.afi = afi 4771 self.safi = safi 4772 if not isinstance(next_hop, (list, tuple)): 4773 next_hop = [next_hop] 4774 for n in 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. 4779 if next_hop: 4780 self._next_hop = next_hop[0] 4781 else: 4782 self._next_hop = None 4783 self._next_hop_list = next_hop 4784 self.nlri = nlri 4785 addr_cls = _get_addr_class(afi, safi) 4786 for i in nlri: 4787 if not isinstance(i, addr_cls): 4788 raise ValueError('Invalid NRLI class for afi=%d and safi=%d' 4789 % (self.afi, self.safi)) 4790 4791 @staticmethod 4792 def split_bin_with_len(buf, unit_len): 4793 f = io.BytesIO(buf) 4794 return [f.read(unit_len) for _ in range(0, len(buf), unit_len)] 4795 4796 @classmethod 4797 def parse_next_hop_ipv4(cls, buf, unit_len): 4798 next_hop = [] 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:])) 4801 return next_hop 4802 4803 @classmethod 4804 def parse_next_hop_ipv6(cls, buf, unit_len): 4805 next_hop = [] 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:])) 4808 return next_hop 4809 4810 @classmethod 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:] 4815 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' 4820 4821 nlri_bin = rest[cls._RESERVED_LENGTH:] 4822 addr_cls = _get_addr_class(afi, safi) 4823 nlri = [] 4824 while nlri_bin: 4825 n, nlri_bin = addr_cls.parser(nlri_bin) 4826 nlri.append(n) 4827 4828 rf = RouteFamily(afi, safi) 4829 if rf == RF_IPv4_VPN: 4830 next_hop = cls.parse_next_hop_ipv4(next_hop_bin, 4831 cls._RD_LENGTH + 4) 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: 4844 next_hop = [] 4845 else: 4846 raise ValueError('Invalid address family: afi=%d, safi=%d' 4847 % (afi, safi)) 4848 4849 return { 4850 'afi': afi, 4851 'safi': safi, 4852 'next_hop': next_hop, 4853 'nlri': nlri, 4854 } 4855 4856 def serialize_next_hop(self): 4857 buf = bytearray() 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 4865 buf += next_hop_bin 4866 4867 return buf 4868 4869 def serialize_value(self): 4870 next_hop_bin = self.serialize_next_hop() 4871 4872 # fixup 4873 next_hop_len = len(next_hop_bin) 4874 4875 buf = bytearray() 4876 msg_pack_into(self._VALUE_PACK_STR, buf, 0, 4877 self.afi, self.safi, next_hop_len) 4878 buf += next_hop_bin 4879 buf += b'\0' # reserved 4880 4881 nlri_bin = bytearray() 4882 for n in self.nlri: 4883 nlri_bin += n.serialize() 4884 buf += nlri_bin 4885 4886 return buf 4887 4888 @property 4889 def next_hop(self): 4890 return self._next_hop 4891 4892 @next_hop.setter 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 4898 4899 @property 4900 def next_hop_list(self): 4901 return self._next_hop_list 4902 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 4912 4913 @property 4914 def route_family(self): 4915 return _rf_map[(self.afi, self.safi)] 4916 4917 4918@_PathAttribute.register_type(BGP_ATTR_TYPE_MP_UNREACH_NLRI) 4919class BGPPathAttributeMpUnreachNLRI(_PathAttribute): 4920 _VALUE_PACK_STR = '!HB' # afi, safi 4921 _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL 4922 _class_suffixes = ['AddrPrefix'] 4923 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) 4928 self.afi = afi 4929 self.safi = safi 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)) 4936 4937 @classmethod 4938 def parse_value(cls, buf): 4939 (afi, safi,) = struct.unpack_from( 4940 cls._VALUE_PACK_STR, six.binary_type(buf)) 4941 4942 nlri_bin = buf[struct.calcsize(cls._VALUE_PACK_STR):] 4943 addr_cls = _get_addr_class(afi, safi) 4944 nlri = [] 4945 while nlri_bin: 4946 n, nlri_bin = addr_cls.parser(nlri_bin) 4947 nlri.append(n) 4948 4949 return { 4950 'afi': afi, 4951 'safi': safi, 4952 'withdrawn_routes': nlri, 4953 } 4954 4955 def serialize_value(self): 4956 buf = bytearray() 4957 msg_pack_into(self._VALUE_PACK_STR, buf, 0, self.afi, self.safi) 4958 4959 nlri_bin = bytearray() 4960 for n in self.withdrawn_routes: 4961 nlri_bin += n.serialize() 4962 buf += nlri_bin 4963 4964 return buf 4965 4966 @property 4967 def route_family(self): 4968 return _rf_map[(self.afi, self.safi)] 4969 4970 4971@_PathAttribute.register_type(BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE) 4972class BGPPathAttributePmsiTunnel(_PathAttribute): 4973 """ 4974 P-Multicast Service Interface Tunnel (PMSI Tunnel) attribute 4975 """ 4976 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 4981 4982 # RFC 6514 4983 # +--------------------------------+ 4984 # | Flags (1 octet) | 4985 # +--------------------------------+ 4986 # | Tunnel Type (1 octets) | 4987 # +--------------------------------+ 4988 # | MPLS Label (3 octets) | 4989 # +--------------------------------+ 4990 # | Tunnel Identifier (variable) | 4991 # +--------------------------------+ 4992 4993 # The Flags field has the following format: 4994 # 0 1 2 3 4 5 6 7 4995 # +-+-+-+-+-+-+-+-+ 4996 # | reserved |L| 4997 # +-+-+-+-+-+-+-+-+ 4998 # `L` refers to the Leaf Information Required. 4999 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 5005 5006 # TODO: 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 5012 # + 4 - PIM-SM Tree 5013 # + 5 - BIDIR-PIM Tree 5014 # + 7 - mLDP MP2MP LSP 5015 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, 5020 type_=type_, 5021 length=length) 5022 self.pmsi_flags = pmsi_flags 5023 self.tunnel_type = tunnel_type 5024 self.tunnel_id = tunnel_id 5025 5026 if label: 5027 # If binary type label field value is specified, stores it 5028 # and decodes as MPLS label and VNI. 5029 self._label = label 5030 self._mpls_label, _ = mpls.label_from_bin(label) 5031 self._vni = vxlan.vni_from_bin(label) 5032 else: 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 5037 self._vni = vni 5038 5039 @classmethod 5040 def parse_value(cls, buf): 5041 (pmsi_flags, 5042 tunnel_type, 5043 label) = struct.unpack_from(cls._VALUE_PACK_STR, buf) 5044 value = buf[cls._PACK_STR_SIZE:] 5045 5046 return { 5047 'pmsi_flags': pmsi_flags, 5048 'tunnel_type': tunnel_type, 5049 'label': label, 5050 'tunnel_id': _PmsiTunnelId.parse(tunnel_type, value) 5051 } 5052 5053 def serialize_value(self): 5054 buf = bytearray() 5055 msg_pack_into(self._VALUE_PACK_STR, buf, 0, 5056 self.pmsi_flags, self.tunnel_type, self._label) 5057 5058 if self.tunnel_id is not None: 5059 buf += self.tunnel_id.serialize() 5060 5061 return buf 5062 5063 def _serialize_label(self, mpls_label, vni): 5064 if mpls_label: 5065 return mpls.label_to_bin(mpls_label, is_bos=True) 5066 elif vni: 5067 return vxlan.vni_to_bin(vni) 5068 else: 5069 return b'\x00' * 3 5070 5071 @property 5072 def mpls_label(self): 5073 return self._mpls_label 5074 5075 @mpls_label.setter 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 5080 5081 @property 5082 def vni(self): 5083 return self._vni 5084 5085 @vni.setter 5086 def vni(self, vni): 5087 self._label = vxlan.vni_to_bin(vni) 5088 self._mpls_label = None # disables MPLS label 5089 self._vni = vni 5090 5091 @classmethod 5092 def from_jsondict(cls, dict_, decode_string=base64.b64decode, 5093 **additional_args): 5094 if isinstance(dict_['tunnel_id'], dict): 5095 tunnel_id = dict_.pop('tunnel_id') 5096 ins = super(BGPPathAttributePmsiTunnel, 5097 cls).from_jsondict(dict_, 5098 decode_string, 5099 **additional_args) 5100 5101 mod = import_module(cls.__module__) 5102 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, 5106 decode_string, 5107 **additional_args) 5108 else: 5109 ins = super(BGPPathAttributePmsiTunnel, 5110 cls).from_jsondict(dict_, 5111 decode_string, 5112 **additional_args) 5113 5114 return ins 5115 5116 5117class _PmsiTunnelId(StringifyMixin, TypeDisp): 5118 5119 @classmethod 5120 def parse(cls, tunnel_type, buf): 5121 subcls = cls._lookup_type(tunnel_type) 5122 return subcls.parser(buf) 5123 5124 5125@_PmsiTunnelId.register_unknown_type() 5126class PmsiTunnelIdUnknown(_PmsiTunnelId): 5127 """ 5128 Unknown route type specific _PmsiTunnelId 5129 """ 5130 5131 def __init__(self, value): 5132 super(PmsiTunnelIdUnknown, self).__init__() 5133 self.value = value 5134 5135 @classmethod 5136 def parser(cls, buf): 5137 return cls(value=buf) 5138 5139 def serialize(self): 5140 return self.value 5141 5142 5143@_PmsiTunnelId.register_type( 5144 BGPPathAttributePmsiTunnel.TYPE_NO_TUNNEL_INFORMATION_PRESENT) 5145class _PmsiTunnelIdNoInformationPresent(_PmsiTunnelId): 5146 5147 @classmethod 5148 def parser(cls, buf): 5149 return None 5150 5151 5152@_PmsiTunnelId.register_type( 5153 BGPPathAttributePmsiTunnel.TYPE_INGRESS_REPLICATION) 5154class PmsiTunnelIdIngressReplication(_PmsiTunnelId): 5155 # tunnel_endpoint_ip 5156 _VALUE_PACK_STR = '!%ds' 5157 _TYPE = { 5158 'ascii': [ 5159 'tunnel_endpoint_ip' 5160 ] 5161 } 5162 5163 def __init__(self, tunnel_endpoint_ip): 5164 super(PmsiTunnelIdIngressReplication, self).__init__() 5165 self.tunnel_endpoint_ip = tunnel_endpoint_ip 5166 5167 @classmethod 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)) 5173 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)) 5178 5179 5180class BGPNLRI(IPAddrPrefix): 5181 pass 5182 5183 5184class BGPMessage(packet_base.PacketBase, TypeDisp): 5185 """Base class for BGP-4 messages. 5186 5187 An instance has the following attributes at least. 5188 Most of them are same to the on-wire counterparts but in host byte 5189 order. 5190 __init__ takes the corresponding args in this order. 5191 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 ========================== =============================================== 5199 """ 5200 5201 _HDR_PACK_STR = '!16sHB' # marker, len, type 5202 _HDR_LEN = struct.calcsize(_HDR_PACK_STR) 5203 _class_prefixes = ['BGP'] 5204 5205 def __init__(self, marker=None, len_=None, type_=None): 5206 super(BGPMessage, self).__init__() 5207 if marker is None: 5208 self._marker = _MARKER 5209 else: 5210 self._marker = marker 5211 self.len = len_ 5212 if type_ is None: 5213 type_ = self._rev_lookup_type(self.__class__) 5214 self.type = type_ 5215 5216 @classmethod 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)) 5223 msglen = len_ 5224 if len(buf) < msglen: 5225 raise stream_parser.StreamParser.TooSmallException( 5226 '%d < %d' % (len(buf), msglen)) 5227 binmsg = buf[cls._HDR_LEN:msglen] 5228 rest = buf[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 5233 5234 def serialize(self, payload=None, prev=None): 5235 # fixup 5236 self._marker = _MARKER 5237 tail = self.serialize_tail() 5238 self.len = self._HDR_LEN + len(tail) 5239 5240 hdr = bytearray(struct.pack(self._HDR_PACK_STR, self._marker, 5241 self.len, self.type)) 5242 return hdr + tail 5243 5244 def __len__(self): 5245 # XXX destructive 5246 buf = self.serialize() 5247 return len(buf) 5248 5249 5250@BGPMessage.register_type(BGP_MSG_OPEN) 5251class BGPOpen(BGPMessage): 5252 """BGP-4 OPEN Message encoder/decoder class. 5253 5254 An instance has the following attributes at least. 5255 Most of them are same to the on-wire counterparts but in host byte 5256 order. 5257 __init__ takes the corresponding args in this order. 5258 5259 ========================== =============================================== 5260 Attribute Description 5261 ========================== =============================================== 5262 marker Marker field. Ignored when encoding. 5263 len Length field. Ignored when encoding. 5264 type Type field. 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. 5271 An IPv4 address. 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. 5277 The default is []. 5278 ========================== =============================================== 5279 """ 5280 5281 _PACK_STR = '!BHH4sB' 5282 _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR) 5283 _TYPE = { 5284 'ascii': [ 5285 'bgp_identifier' 5286 ] 5287 } 5288 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 5295 self.my_as = my_as 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 5300 5301 @property 5302 def opt_param_cap_map(self): 5303 cap_map = {} 5304 for param in self.opt_param: 5305 if param.type == BGP_OPT_CAPABILITY: 5306 cap_map[param.cap_code] = param 5307 return cap_map 5308 5309 def get_opt_param_cap(self, cap_code): 5310 return self.opt_param_cap_map.get(cap_code) 5311 5312 @classmethod 5313 def parser(cls, buf): 5314 (version, 5315 my_as, 5316 hold_time, 5317 bgp_identifier, 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] 5322 opt_param = [] 5323 while binopts: 5324 opt, binopts = _OptParam.parser(binopts) 5325 opt_param.extend(opt) 5326 return { 5327 "version": version, 5328 "my_as": my_as, 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, 5333 } 5334 5335 def serialize_tail(self): 5336 # fixup 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) 5342 5343 msg = bytearray(struct.pack(self._PACK_STR, 5344 self.version, 5345 self.my_as, 5346 self.hold_time, 5347 addrconv.ipv4.text_to_bin( 5348 self.bgp_identifier), 5349 self.opt_param_len)) 5350 msg += binopts 5351 return msg 5352 5353 5354@BGPMessage.register_type(BGP_MSG_UPDATE) 5355class BGPUpdate(BGPMessage): 5356 """BGP-4 UPDATE Message encoder/decoder class. 5357 5358 An instance has the following attributes at least. 5359 Most of them are same to the on-wire counterparts but in host byte 5360 order. 5361 __init__ takes the corresponding args in this order. 5362 5363 .. tabularcolumns:: |l|L| 5364 5365 ========================== =============================================== 5366 Attribute Description 5367 ========================== =============================================== 5368 marker Marker field. Ignored when encoding. 5369 len Length field. Ignored when encoding. 5370 type Type field. 5371 withdrawn_routes_len Withdrawn Routes Length field. 5372 Ignored when encoding. 5373 withdrawn_routes Withdrawn Routes field. 5374 A list of BGPWithdrawnRoute instances. 5375 The default is []. 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. 5380 The default is []. 5381 nlri Network Layer Reachability Information field. 5382 A list of BGPNLRI instances. 5383 The default is []. 5384 ========================== =============================================== 5385 """ 5386 5387 _MIN_LEN = BGPMessage._HDR_LEN 5388 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, 5394 nlri=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 5404 self.nlri = nlri 5405 5406 @property 5407 def pathattr_map(self): 5408 passattr_map = {} 5409 for attr in self.path_attributes: 5410 passattr_map[attr.type] = attr 5411 return passattr_map 5412 5413 def get_path_attr(self, attr_name): 5414 return self.pathattr_map.get(attr_name) 5415 5416 @classmethod 5417 def parser(cls, buf): 5418 offset = 0 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 = [] 5429 while binroutes: 5430 r, binroutes = BGPWithdrawnRoute.parser(binroutes) 5431 withdrawn_routes.append(r) 5432 path_attributes = [] 5433 while binpathattrs: 5434 pa, binpathattrs = _PathAttribute.parser(binpathattrs) 5435 path_attributes.append(pa) 5436 offset += 2 + total_path_attribute_len 5437 nlri = [] 5438 while binnlri: 5439 n, binnlri = BGPNLRI.parser(binnlri) 5440 nlri.append(n) 5441 return { 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, 5446 "nlri": nlri, 5447 } 5448 5449 def serialize_tail(self): 5450 # fixup 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() 5460 for n in self.nlri: 5461 binnlri += n.serialize() 5462 5463 msg = bytearray() 5464 offset = 0 5465 msg_pack_into('!H', msg, offset, self.withdrawn_routes_len) 5466 msg += binroutes 5467 offset += 2 + self.withdrawn_routes_len 5468 msg_pack_into('!H', msg, offset, self.total_path_attribute_len) 5469 msg += binpathattrs 5470 offset += 2 + self.total_path_attribute_len 5471 msg += binnlri 5472 return msg 5473 5474 5475@BGPMessage.register_type(BGP_MSG_KEEPALIVE) 5476class BGPKeepAlive(BGPMessage): 5477 """BGP-4 KEEPALIVE Message encoder/decoder class. 5478 5479 An instance has the following attributes at least. 5480 Most of them are same to the on-wire counterparts but in host byte 5481 order. 5482 __init__ takes the corresponding args in this order. 5483 5484 ========================== =============================================== 5485 Attribute Description 5486 ========================== =============================================== 5487 marker Marker field. Ignored when encoding. 5488 len Length field. Ignored when encoding. 5489 type Type field. 5490 ========================== =============================================== 5491 """ 5492 5493 _MIN_LEN = BGPMessage._HDR_LEN 5494 5495 def __init__(self, type_=BGP_MSG_KEEPALIVE, len_=None, marker=None): 5496 super(BGPKeepAlive, self).__init__(marker=marker, len_=len_, 5497 type_=type_) 5498 5499 @classmethod 5500 def parser(cls, buf): 5501 return {} 5502 5503 def serialize_tail(self): 5504 return bytearray() 5505 5506 5507@BGPMessage.register_type(BGP_MSG_NOTIFICATION) 5508class BGPNotification(BGPMessage): 5509 """BGP-4 NOTIFICATION Message encoder/decoder class. 5510 5511 An instance has the following attributes at least. 5512 Most of them are same to the on-wire counterparts but in host byte 5513 order. 5514 __init__ takes the corresponding args in this order. 5515 5516 ========================== =============================================== 5517 Attribute Description 5518 ========================== =============================================== 5519 marker Marker field. Ignored when encoding. 5520 len Length field. Ignored when encoding. 5521 type Type field. 5522 error_code Error code field. 5523 error_subcode Error subcode field. 5524 data Data field. 5525 ========================== =============================================== 5526 """ 5527 5528 _PACK_STR = '!BB' 5529 _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR) 5530 5531 _REASONS = { 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', 5564 } 5565 5566 def __init__(self, 5567 error_code, 5568 error_subcode, 5569 data=b'', 5570 type_=BGP_MSG_NOTIFICATION, len_=None, marker=None): 5571 super(BGPNotification, self).__init__(marker=marker, len_=len_, 5572 type_=type_) 5573 self.error_code = error_code 5574 self.error_subcode = error_subcode 5575 self.data = data 5576 5577 @classmethod 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:]) 5582 return { 5583 "error_code": error_code, 5584 "error_subcode": error_subcode, 5585 "data": data, 5586 } 5587 5588 def serialize_tail(self): 5589 msg = bytearray(struct.pack(self._PACK_STR, self.error_code, 5590 self.error_subcode)) 5591 msg += self.data 5592 return msg 5593 5594 @property 5595 def reason(self): 5596 return self._REASONS.get((self.error_code, self.error_subcode)) 5597 5598 5599@BGPMessage.register_type(BGP_MSG_ROUTE_REFRESH) 5600class BGPRouteRefresh(BGPMessage): 5601 """BGP-4 ROUTE REFRESH Message (RFC 2918) encoder/decoder class. 5602 5603 An instance has the following attributes at least. 5604 Most of them are same to the on-wire counterparts but in host byte 5605 order. 5606 __init__ takes the corresponding args in this order. 5607 5608 ========================== =============================================== 5609 Attribute Description 5610 ========================== =============================================== 5611 marker Marker field. Ignored when encoding. 5612 len Length field. Ignored when encoding. 5613 type Type field. 5614 afi Address Family Identifier 5615 safi Subsequent Address Family Identifier 5616 ========================== =============================================== 5617 """ 5618 5619 _PACK_STR = '!HBB' 5620 _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR) 5621 5622 def __init__(self, 5623 afi, safi, demarcation=0, 5624 type_=BGP_MSG_ROUTE_REFRESH, len_=None, marker=None): 5625 super(BGPRouteRefresh, self).__init__(marker=marker, len_=len_, 5626 type_=type_) 5627 self.afi = afi 5628 self.safi = safi 5629 self.demarcation = demarcation 5630 self.eor_sent = False 5631 5632 @classmethod 5633 def parser(cls, buf): 5634 (afi, demarcation, safi,) = struct.unpack_from(cls._PACK_STR, 5635 six.binary_type(buf)) 5636 return { 5637 "afi": afi, 5638 "safi": safi, 5639 "demarcation": demarcation, 5640 } 5641 5642 def serialize_tail(self): 5643 return bytearray(struct.pack(self._PACK_STR, self.afi, 5644 self.demarcation, self.safi)) 5645 5646 5647class StreamParser(stream_parser.StreamParser): 5648 """Streaming parser for BGP-4 messages. 5649 5650 This is a subclass of ryu.lib.packet.stream_parser.StreamParser. 5651 Its parse method returns a list of BGPMessage subclass instances. 5652 """ 5653 5654 def try_parse(self, data): 5655 msg, _, rest = BGPMessage.parser(data) 5656 return msg, rest 5657