Home | History | Annotate | Download | only in contrib
      1 # This file is part of Scapy
      2 # Scapy is free software: you can redistribute it and/or modify
      3 # it under the terms of the GNU General Public License as published by
      4 # the Free Software Foundation, either version 2 of the License, or
      5 # any later version.
      6 #
      7 # Scapy is distributed in the hope that it will be useful,
      8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     10 # GNU General Public License for more details.
     11 #
     12 # You should have received a copy of the GNU General Public License
     13 # along with Scapy. If not, see <http://www.gnu.org/licenses/>.
     14 
     15 # scapy.contrib.description = BGP v0.1
     16 # scapy.contrib.status = loads
     17 
     18 """
     19 BGP (Border Gateway Protocol).
     20 """
     21 
     22 from __future__ import absolute_import
     23 import struct
     24 import re
     25 import socket
     26 
     27 from scapy import pton_ntop
     28 from scapy.packet import Packet, Packet_metaclass, bind_layers
     29 from scapy.fields import (Field, BitField, BitEnumField, XBitField, ByteField,
     30                           ByteEnumField, ShortField, ShortEnumField, IntField,
     31                           IntEnumField, LongField, IEEEFloatField, StrField,
     32                           StrLenField, StrFixedLenField, FieldLenField,
     33                           FieldListField, PacketField, PacketListField,
     34                           IPField, FlagsField, ConditionalField,
     35                           MultiEnumField)
     36 from scapy.layers.inet import TCP
     37 from scapy.layers.inet6 import IP6Field
     38 from scapy.config import conf, ConfClass
     39 from scapy.compat import *
     40 from scapy.error import log_runtime
     41 import scapy.modules.six as six
     42 
     43 
     44 #
     45 # Module configuration
     46 #
     47 
     48 
     49 class BGPConf(ConfClass):
     50     """
     51     BGP module configuration.
     52     """
     53 
     54     # By default, set to True in order to behave like an OLD speaker (RFC 6793)
     55     use_2_bytes_asn = True
     56 
     57 
     58 bgp_module_conf = BGPConf()
     59 
     60 
     61 #
     62 # Constants
     63 #
     64 
     65 # RFC 4271: "The maximum message size is 4096 octets. All implementations are
     66 # required to support this maximum message size."
     67 BGP_MAXIMUM_MESSAGE_SIZE = 4096
     68 
     69 # RFC 4271: "Each message has a fixed-size header." Marker (16 bytes) +
     70 # Length (2 bytes) + Type (1 byte)
     71 _BGP_HEADER_SIZE = 19
     72 
     73 # Marker included in every message (RFC 4271: "This 16-octet field is
     74 # included for compatibility; it MUST be set to all ones")
     75 _BGP_HEADER_MARKER = b"\xff" * 16
     76 
     77 # extended-length flag (RFC 4271 4.3. UPDATE Message Format -
     78 # Path Attributes)
     79 _BGP_PA_EXTENDED_LENGTH = 0x10
     80 
     81 # RFC 5492 (at least 2 bytes : code + length)
     82 _BGP_CAPABILITY_MIN_SIZE = 2
     83 
     84 # RFC 5492 (at least 3 bytes : type code + length)
     85 _BGP_PATH_ATTRIBUTE_MIN_SIZE = 3
     86 
     87 
     88 #
     89 # Fields and utilities
     90 #
     91 
     92 def _bits_to_bytes_len(length_in_bits):
     93     """
     94     Helper function that returns the numbers of bytes necessary to store the
     95     given number of bits.
     96     """
     97 
     98     return (length_in_bits + 7) // 8
     99 
    100 
    101 class BGPFieldIPv4(Field):
    102     """
    103     IPv4 Field (CIDR)
    104     """
    105 
    106     def mask2iplen(self, mask):
    107         """Get the IP field mask length (in bytes)."""
    108         return (mask + 7) // 8
    109 
    110     def h2i(self, pkt, h):
    111         """x.x.x.x/y to "internal" representation."""
    112         ip, mask = re.split("/", h)
    113         return int(mask), ip
    114 
    115     def i2h(self, pkt, i):
    116         """"Internal" representation to "human" representation
    117         (x.x.x.x/y)."""
    118         mask, ip = i
    119         return ip + "/" + str(mask)
    120 
    121     def i2repr(self, pkt, i):
    122         return self.i2h(pkt, i)
    123 
    124     def i2len(self, pkt, i):
    125         mask, ip = i
    126         return self.mask2iplen(mask) + 1
    127 
    128     def i2m(self, pkt, i):
    129         """"Internal" (IP as bytes, mask as int) to "machine"
    130         representation."""
    131         mask, ip = i
    132         ip = socket.inet_aton(ip)
    133         return struct.pack(">B", mask) + ip[:self.mask2iplen(mask)]
    134 
    135     def addfield(self, pkt, s, val):
    136         return s + self.i2m(pkt, val)
    137 
    138     def getfield(self, pkt, s):
    139         length = self.mask2iplen(orb(s[0])) + 1
    140         return s[length:], self.m2i(pkt, s[:length])
    141 
    142     def m2i(self, pkt, m):
    143         mask = orb(m[0])
    144         mask2iplen_res = self.mask2iplen(mask)
    145         ip = b"".join(m[i + 1:i + 2] if i < mask2iplen_res else b"\x00" for i in range(4))
    146         return (mask, socket.inet_ntoa(ip))
    147 
    148 
    149 class BGPFieldIPv6(Field):
    150     """IPv6 Field (CIDR)"""
    151 
    152     def mask2iplen(self, mask):
    153         """Get the IP field mask length (in bytes)."""
    154         return (mask + 7) // 8
    155 
    156     def h2i(self, pkt, h):
    157         """x.x.x.x/y to internal representation."""
    158         ip, mask = re.split("/", h)
    159         return int(mask), ip
    160 
    161     def i2h(self, pkt, i):
    162         """"Internal" representation to "human" representation."""
    163         mask, ip = i
    164         return ip + "/" + str(mask)
    165 
    166     def i2repr(self, pkt, i):
    167         return self.i2h(pkt, i)
    168 
    169     def i2len(self, pkt, i):
    170         mask, ip = i
    171         return self.mask2iplen(mask) + 1
    172 
    173     def i2m(self, pkt, i):
    174         """"Internal" (IP as bytes, mask as int) to "machine" representation."""
    175         mask, ip = i
    176         ip = pton_ntop.inet_pton(socket.AF_INET6, ip)
    177         return struct.pack(">B", mask) + ip[:self.mask2iplen(mask)]
    178 
    179     def addfield(self, pkt, s, val):
    180         return s + self.i2m(pkt, val)
    181 
    182     def getfield(self, pkt, s):
    183         length = self.mask2iplen(orb(s[0])) + 1
    184         return s[length:], self.m2i(pkt, s[:length])
    185 
    186     def m2i(self, pkt, m):
    187         mask = orb(m[0])
    188         ip = b"".join(m[i + 1:i + 2] if i < self.mask2iplen(mask) else b"\x00" for i in range(16))
    189         return (mask, pton_ntop.inet_ntop(socket.AF_INET6, ip))
    190 
    191 
    192 def has_extended_length(flags):
    193     """
    194     Used in BGPPathAttr to check if the extended-length flag is
    195     set.
    196     """
    197 
    198     return flags & _BGP_PA_EXTENDED_LENGTH == _BGP_PA_EXTENDED_LENGTH
    199 
    200 
    201 class BGPNLRI_IPv4(Packet):
    202     """
    203     Packet handling IPv4 NLRI fields.
    204     """
    205 
    206     name = "IPv4 NLRI"
    207     fields_desc = [BGPFieldIPv4("prefix", "0.0.0.0/0")]
    208 
    209 
    210 class BGPNLRI_IPv6(Packet):
    211     """
    212     Packet handling IPv6 NLRI fields.
    213     """
    214 
    215     name = "IPv6 NLRI"
    216     fields_desc = [BGPFieldIPv6("prefix", "::/0")]
    217 
    218 
    219 class BGPNLRIPacketListField(PacketListField):
    220     """
    221     PacketListField handling NLRI fields.
    222     """
    223 
    224     def getfield(self, pkt, s):
    225         lst = []
    226         length = None
    227         ret = b""
    228 
    229         if self.length_from is not None:
    230             length = self.length_from(pkt)
    231 
    232         if length is not None:
    233             remain, ret = s[:length], s[length:]
    234         else:
    235             index = s.find(_BGP_HEADER_MARKER)
    236             if index != -1:
    237                 remain = s[:index]
    238                 ret = s[index:]
    239             else:
    240                 remain = s
    241 
    242         while remain:
    243             mask_length_in_bits = orb(remain[0])
    244             mask_length_in_bytes = (mask_length_in_bits + 7) // 8
    245             current = remain[:mask_length_in_bytes + 1]
    246             remain = remain[mask_length_in_bytes + 1:]
    247             packet = self.m2i(pkt, current)
    248             lst.append(packet)
    249 
    250         return remain + ret, lst
    251 
    252 
    253 class _BGPInvalidDataException(Exception):
    254     """
    255     Raised when it is not possible to instantiate a BGP packet with the given
    256     data.
    257     """
    258 
    259     def __init__(self, details):
    260         Exception.__init__(
    261             self,
    262             "Impossible to build packet from the given data" + details
    263         )
    264 
    265 
    266 def _get_cls(name, fallback_cls=conf.raw_layer):
    267     """
    268     Returns class named "name" if it exists, fallback_cls otherwise.
    269     """
    270 
    271     return globals().get(name, fallback_cls)
    272 
    273 
    274 #
    275 # Common dictionaries
    276 #
    277 
    278 _bgp_message_types = {
    279     0: "NONE",
    280     1: "OPEN",
    281     2: "UPDATE",
    282     3: "NOTIFICATION",
    283     4: "KEEPALIVE",
    284     5: "ROUTE-REFRESH"
    285 }
    286 
    287 
    288 #
    289 # AFIs
    290 #
    291 
    292 address_family_identifiers = {
    293     0: "Reserved",
    294     1: "IP (IP version 4)",
    295     2: "IP6 (IP version 6)",
    296     3: "NSAP",
    297     4: "HDLC (8-bit multidrop)",
    298     5: "BBN 1822",
    299     6: "802 (includes all 802 media plus Ethernet \"canonical format\")",
    300     7: "E.163",
    301     8: "E.164 (SMDS, Frame Relay, ATM)",
    302     9: "F.69 (Telex)",
    303     10: "X.121 (X.25, Frame Relay)",
    304     11: "IPX",
    305     12: "Appletalk",
    306     13: "Decnet IV",
    307     14: "Banyan Vines",
    308     15: "E.164 with NSAP format subaddress",  # ANDY_MALIS
    309     16: "DNS (Domain Name System)",
    310     17: "Distinguished Name",  # CHARLES_LYNN
    311     18: "AS Number",  # CHARLES_LYNN
    312     19: "XTP over IP version 4",  # MIKE_SAUL
    313     20: "XTP over IP version 6",  # MIKE_SAUL
    314     21: "XTP native mode XTP",  # MIKE_SAUL
    315     22: "Fibre Channel World-Wide Port Name",  # MARK_BAKKE
    316     23: "Fibre Channel World-Wide Node Name",  # MARK_BAKKE
    317     24: "GWID",  # SUBRA_HEGDE
    318     25: "AFI for L2VPN information",  # RFC 6074
    319     26: "MPLS-TP Section Endpoint Identifier",  # RFC 7212
    320     27: "MPLS-TP LSP Endpoint Identifier",  # RFC 7212
    321     28: "MPLS-TP Pseudowire Endpoint Identifier",  # RFC 7212
    322     29: "MT IP: Multi-Topology IP version 4",  # RFC 7307
    323     30: "MT IPv6: Multi-Topology IP version 6",  # RFC 7307
    324     16384: "EIGRP Common Service Family",  # DONNIE_SAVAGE
    325     16385: "EIGRP IPv4 Service Family",  # DONNIE_SAVAGE
    326     16386: "EIGRP IPv6 Service Family",  # DONNIE_SAVAGE
    327     16387: "LISP Canonical Address Format (LCAF)",  # DAVID_MEYER
    328     16388: "BGP-LS",  # RFC 7752
    329     16389: "48-bit MAC",  # RFC 7042
    330     16390: "64-bit MAC",  # RFC 7042
    331     16391: "OUI",  # draft-ietf-trill-ia-appsubtlv
    332     16392: "MAC/24",  # draft-ietf-trill-ia-appsubtlv
    333     16393: "MAC/40",  # draft-ietf-trill-ia-appsubtlv
    334     16394: "IPv6/64",  # draft-ietf-trill-ia-appsubtlv
    335     16395: "RBridge Port ID",  # draft-ietf-trill-ia-appsubtlv
    336     16396: "TRILL Nickname",  # RFC 7455
    337     65535: "Reserved"
    338 }
    339 
    340 
    341 subsequent_afis = {
    342     0: "Reserved",  # RFC 4760
    343     1: "Network Layer Reachability Information used for unicast forwarding",  # RFC 4760
    344     2: "Network Layer Reachability Information used for multicast forwarding",  # RFC 4760
    345     3: "Reserved",  # RFC 4760
    346     4: "Network Layer Reachability Information (NLRI) with MPLS Labels",  # RFC 3107
    347     5: "MCAST-VPN",  # RFC 6514
    348     6: "Network Layer Reachability Information used for Dynamic Placement of\
    349         Multi-Segment Pseudowires", # RFC 7267
    350     7: "Encapsulation SAFI",  # RFC 5512
    351     8: "MCAST-VPLS",  # RFC 7117
    352     64: "Tunnel SAFI",  # DRAFT-NALAWADE-KAPOOR-TUNNEL-SAFI-01
    353     65: "Virtual Private LAN Service (VPLS)",  # RFC 6074
    354     66: "BGP MDT SAFI",  # RFC 6037
    355     67: "BGP 4over6 SAFI",  # RFC 5747
    356     68: "BGP 6over4 SAFI",  # YONG_CUI
    357     69: "Layer-1 VPN auto-discovery information",  # RFC 5195
    358     70: "BGP EVPNs",  # RFC 7432
    359     71: "BGP-LS",  # RFC 7752
    360     72: "BGP-LS-VPN",  # RFC 7752
    361     128: "MPLS-labeled VPN address",  # RFC 4364
    362     129: "Multicast for BGP/MPLS IP Virtual Private Networks (VPNs)",  # RFC 6514
    363     132: "Route Target constraint",  # RFC 4684
    364     133: "IPv4 dissemination of flow specification rules",  # RFC 5575
    365     134: "VPNv4 dissemination of flow specification rules",  # RFC 5575
    366     140: "VPN auto-discovery",  # draft-ietf-l3vpn-bgpvpn-auto
    367     255: "Reserved"  # RFC 4760
    368 }
    369 
    370 
    371 # Used by _bgp_dispatcher to instantiate the appropriate class
    372 _bgp_cls_by_type = {
    373     1: "BGPOpen",
    374     2: "BGPUpdate",
    375     3: "BGPNotification",
    376     4: "BGPKeepAlive",
    377     5: "BGPRouteRefresh",
    378 }
    379 
    380 
    381 #
    382 # Header
    383 #
    384 
    385 class BGPHeader(Packet):
    386     """
    387     The header of any BGP message.
    388     References: RFC 4271
    389     """
    390 
    391     name = "HEADER"
    392     fields_desc = [
    393         XBitField(
    394             "marker",
    395             0xffffffffffffffffffffffffffffffff,
    396             0x80
    397         ),
    398         ShortField("len", None),
    399         ByteEnumField("type", 4, _bgp_message_types)
    400     ]
    401 
    402     @classmethod
    403     def dispatch_hook(cls, _pkt=None, *args, **kargs):
    404         """
    405         Returns the right class for the given data.
    406         """
    407 
    408         return _bgp_dispatcher(_pkt)
    409 
    410     def post_build(self, p, pay):
    411         if self.len is None:
    412             length = len(p)
    413             if pay:
    414                 length = length + len(pay)
    415             p = p[:16] + struct.pack("!H", length) + p[18:]
    416         return p + pay
    417 
    418     def guess_payload_class(self, payload):
    419         return _get_cls(_bgp_cls_by_type.get(self.type, conf.raw_layer), conf.raw_layer)
    420 
    421 
    422 def _bgp_dispatcher(payload):
    423     """
    424     Returns the right class for a given BGP message.
    425     """
    426 
    427     cls = conf.raw_layer
    428 
    429     # By default, calling BGP() will build a BGPHeader.
    430     if payload is None:
    431         cls = _get_cls("BGPHeader", conf.raw_layer)
    432 
    433     else:
    434         if len(payload) >= _BGP_HEADER_SIZE and\
    435                 payload[:16] == _BGP_HEADER_MARKER:
    436 
    437             # Get BGP message type
    438             message_type = orb(payload[18])
    439             if message_type == 4:
    440                 cls = _get_cls("BGPKeepAlive")
    441             else:
    442                 cls = _get_cls("BGPHeader")
    443 
    444     return cls
    445 
    446 
    447 class BGP(Packet):
    448     """
    449     Every BGP message inherits from this class.
    450     """
    451 
    452     #
    453     # BGP messages types
    454 
    455     OPEN_TYPE = 1
    456     UPDATE_TYPE = 2
    457     NOTIFICATION_TYPE = 3
    458     KEEPALIVE_TYPE = 4
    459     ROUTEREFRESH_TYPE = 5
    460 
    461     @classmethod
    462     def dispatch_hook(cls, _pkt=None, *args, **kargs):
    463         """
    464         Returns the right class for the given data.
    465         """
    466 
    467         return _bgp_dispatcher(_pkt)
    468 
    469     def guess_payload_class(self, p):
    470         cls = None
    471         if len(p) > 15 and p[:16] == _BGP_HEADER_MARKER:
    472             cls = BGPHeader
    473         return cls
    474 
    475 
    476 #
    477 # KEEPALIVE
    478 #
    479 
    480 class BGPKeepAlive(BGP, BGPHeader):
    481 
    482     """
    483     KEEPALIVE message.
    484     """
    485 
    486     name = "KEEPALIVE"
    487 
    488 
    489 #
    490 # OPEN
    491 #
    492 
    493 #
    494 # Optional Parameters Codes
    495 #
    496 
    497 optional_parameter_codes = {
    498     0: "Reserved",
    499     1: "Authentication (deprecated)",
    500     2: "Capabilities"
    501 }
    502 
    503 
    504 #
    505 # Capabilities
    506 #
    507 
    508 _capabilities = {
    509     0: "Reserved",  # RFC 5492
    510     1: "Multiprotocol Extensions for BGP-4",  # RFC 2858
    511     2: "Route Refresh Capability for BGP-4",  # RFC 2918
    512     3: "Outbound Route Filtering Capability",  # RFC 5291
    513     4: "Multiple routes to a destination capability",  # RFC 3107
    514     5: "Extended Next Hop Encoding",  # RFC 5549
    515     6: "BGP-Extended Message",  # (TEMPORARY - registered 2015-09-30, expires 2016-09-30),
    516     # draft-ietf-idr-bgp-extended-messages
    517     64: "Graceful Restart Capability",  # RFC 4724
    518     65: "Support for 4-octet AS number capability",  # RFC 6793
    519     66: "Deprecated (2003-03-06)",
    520     67: "Support for Dynamic Capability (capability specific)",  # draft-ietf-idr-dynamic-cap
    521     68: "Multisession BGP Capability",  # draft-ietf-idr-bgp-multisession
    522     69: "ADD-PATH Capability",  # RFC-ietf-idr-add-paths-15
    523     70: "Enhanced Route Refresh Capability",  # RFC 7313
    524     71: "Long-Lived Graceful Restart (LLGR) Capability",  # draft-uttaro-idr-bgp-persistence
    525     73: "FQDN Capability",  # draft-walton-bgp-hostname-capability
    526     128: "Route Refresh Capability for BGP-4 (Cisco)",  # Cisco also uses 128 for RR capability
    527     130: "Outbound Route Filtering Capability (Cisco)",  # Cisco also uses 130 for ORF capability
    528 }
    529 
    530 
    531 _capabilities_objects = {
    532     0x01: "BGPCapMultiprotocol",  # RFC 2858
    533     0x02: "BGPCapGeneric",  # RFC 2918
    534     0x03: "BGPCapORF",  # RFC 5291
    535     0x40: "BGPCapGracefulRestart",  # RFC 4724
    536     0x41: "BGPCapFourBytesASN",  # RFC 4893
    537     0x46: "BGPCapGeneric",  # Enhanced Route Refresh Capability, RFC 7313
    538     0x82: "BGPCapORF",  # ORF / RFC 5291 (Cisco)
    539 }
    540 
    541 
    542 def _register_cls(registry, cls):
    543     registry[cls.__name__] = cls
    544     return cls
    545 
    546 
    547 _capabilities_registry = {}
    548 
    549 
    550 def _bgp_capability_dispatcher(payload):
    551     """
    552     Returns the right class for a given BGP capability.
    553     """
    554 
    555     cls = _capabilities_registry["BGPCapGeneric"]
    556 
    557     # By default, calling BGPCapability() will build a "generic" capability.
    558     if payload is None:
    559         cls = _capabilities_registry["BGPCapGeneric"]
    560 
    561     else:
    562         length = len(payload)
    563         if length >= _BGP_CAPABILITY_MIN_SIZE:
    564             code = orb(payload[0])
    565             cls = _get_cls(_capabilities_objects.get(code, "BGPCapGeneric"))
    566 
    567     return cls
    568 
    569 
    570 class _BGPCap_metaclass(type):
    571     def __new__(cls, clsname, bases, attrs):
    572         newclass = super(_BGPCap_metaclass, cls).__new__(
    573             cls, clsname, bases, attrs)
    574         _register_cls(_capabilities_registry, newclass)
    575         return newclass
    576 
    577 
    578 class _BGPCapability_metaclass(Packet_metaclass, _BGPCap_metaclass):
    579     pass
    580 
    581 
    582 class BGPCapability(six.with_metaclass(_BGPCapability_metaclass, Packet)):
    583     """
    584     Generic BGP capability.
    585     """
    586 
    587     @classmethod
    588     def dispatch_hook(cls, _pkt=None, *args, **kargs):
    589         """
    590         Returns the right class for the given data.
    591         """
    592 
    593         return _bgp_capability_dispatcher(_pkt)
    594 
    595     def pre_dissect(self, s):
    596         """
    597         Check that the payload is long enough (at least 2 bytes).
    598         """
    599         length = len(s)
    600         if length < _BGP_CAPABILITY_MIN_SIZE:
    601             err = " ({}".format(length) + " is < _BGP_CAPABILITY_MIN_SIZE "
    602             err += "({})).".format(_BGP_CAPABILITY_MIN_SIZE)
    603             raise _BGPInvalidDataException(err)
    604         return s
    605 
    606     # Every BGP capability object inherits from BGPCapability.
    607     def haslayer(self, cls):
    608         if cls == "BGPCapability":
    609             if isinstance(self, BGPCapability):
    610                 return True
    611         if issubclass(cls, BGPCapability):
    612             if isinstance(self, cls):
    613                 return True
    614         return super(BGPCapability, self).haslayer(cls)
    615 
    616     def getlayer(self, cls, nb=1, _track=None, _subclass=True, **flt):
    617         return super(BGPCapability, self).getlayer(
    618             cls, nb=nb, _track=_track, _subclass=True, **flt
    619         )
    620 
    621     def post_build(self, p, pay):
    622         length = 0
    623         if self.length is None:
    624             # capability packet length - capability code (1 byte) -
    625             # capability length (1 byte)
    626             length = len(p) - 2
    627             p = chb(p[0]) + chb(length) + p[2:]
    628         return p + pay
    629 
    630 
    631 class BGPCapGeneric(BGPCapability):
    632     """
    633     This class provides an implementation of a generic capability.
    634     """
    635 
    636     name = "BGP Capability"
    637     fields_desc = [
    638         ByteEnumField("code", 0, _capabilities),
    639         ByteField("length", 0),
    640         ConditionalField(
    641             StrLenField(
    642                 "cap_data",
    643                 '',
    644                 length_from=lambda p: p.length
    645             ),
    646             lambda p: p.length > 0
    647         )
    648     ]
    649 
    650 
    651 #
    652 # Multiprotocol Extensions for BGP-4
    653 #
    654 
    655 class BGPCapMultiprotocol(BGPCapability):
    656     """
    657     This class provides an implementation of the Multiprotocol
    658     capability.
    659     References: RFC 4760
    660     """
    661 
    662     name = "Multiprotocol Extensions for BGP-4"
    663     fields_desc = [
    664         ByteEnumField("code", 1, _capabilities),
    665         ByteField("length", 4),
    666         ShortEnumField("afi", 0, address_family_identifiers),
    667         ByteField("reserved", 0),
    668         ByteEnumField("safi", 0, subsequent_afis)
    669     ]
    670 
    671 
    672 #
    673 # Outbound Route Filtering Capability for BGP-4
    674 #
    675 
    676 _orf_types = {
    677     0: "Reserved",  # RFC 5291
    678     64: "Address Prefix ORF",  # RFC 5292
    679     65: "CP-ORF",  # RFC 7543
    680 }
    681 
    682 
    683 send_receive_values = {
    684     1: "receive",
    685     2: "send",
    686     3: "receive + send"
    687 }
    688 
    689 
    690 class BGPCapORFBlock(Packet):
    691     """
    692     The "ORFBlock" is made of <AFI, rsvd, SAFI, Number of ORFs, and
    693     <ORF Type, Send/Receive> entries.
    694     """
    695 
    696     class ORFTuple(Packet):
    697         """
    698         Packet handling <ORF Types, Send/Receive> tuples.
    699         """
    700 
    701         # (ORF Type (1 octet) / Send/Receive (1 octet)) ....
    702         name = "ORF Type"
    703         fields_desc = [
    704             ByteEnumField("orf_type", 0, _orf_types),
    705             ByteEnumField("send_receive", 0, send_receive_values)
    706         ]
    707 
    708     name = "ORF Capability Entry"
    709     fields_desc = [
    710         ShortEnumField("afi", 0, address_family_identifiers),
    711         ByteField("reserved", 0),
    712         ByteEnumField("safi", 0, subsequent_afis),
    713         FieldLenField(
    714             "orf_number",
    715             None,
    716             count_of="entries",
    717             fmt="!B"
    718         ),
    719         PacketListField(
    720             "entries",
    721             [],
    722             ORFTuple,
    723             count_from=lambda p: p.orf_number
    724         )
    725     ]
    726 
    727     def post_build(self, p, pay):
    728         count = None
    729         if self.orf_number is None:
    730             count = len(self.entries)  # orf_type (1 byte) + send_receive (1 byte)
    731             p = p[:4] + struct.pack("!B", count) + p[5:]
    732         return p + pay
    733 
    734 
    735 class BGPCapORFBlockPacketListField(PacketListField):
    736     """
    737     Handles lists of BGPCapORFBlocks.
    738     """
    739 
    740     def getfield(self, pkt, s):
    741         lst = []
    742         length = None
    743 
    744         if self.length_from is not None:
    745             length = self.length_from(pkt)
    746         remain = s
    747         if length is not None:
    748             remain = s[:length]
    749 
    750         while remain:
    751             # block length: afi (2 bytes) + reserved (1 byte) + safi (1 byte) +
    752             # orf_number (1 byte) + entries (2 bytes * orf_number)
    753             orf_number = orb(remain[4])
    754             entries_length = orf_number * 2
    755             current = remain[:5 + entries_length]
    756             remain = remain[5 + entries_length:]
    757             packet = self.m2i(pkt, current)
    758             lst.append(packet)
    759 
    760         return remain, lst
    761 
    762 
    763 class BGPCapORF(BGPCapability):
    764     """
    765     This class provides an implementation of the Outbound Route Filtering
    766     capability.
    767     References: RFC 5291
    768     """
    769 
    770     name = "Outbound Route Filtering Capability"
    771     fields_desc = [
    772         ByteEnumField("code", 3, _capabilities),
    773         ByteField("length", None),
    774         BGPCapORFBlockPacketListField(
    775             "orf",
    776             [],
    777             BGPCapORFBlock,
    778             length_from=lambda p: p.length
    779         )
    780     ]
    781 
    782 
    783 #
    784 # Graceful Restart capability
    785 #
    786 
    787 gr_address_family_flags = {
    788     128: "Forwarding state preserved (0x80: F bit set)"
    789 }
    790 
    791 
    792 class BGPCapGracefulRestart(BGPCapability):
    793     """
    794     This class provides an implementation of the Graceful Restart
    795     capability.
    796     References: RFC 4724
    797     """
    798 
    799     class GRTuple(Packet):
    800 
    801         """Tuple <AFI, SAFI, Flags for address family>"""
    802         name = "<AFI, SAFI, Flags for address family>"
    803         fields_desc = [ShortEnumField("afi", 0, address_family_identifiers),
    804                        ByteEnumField("safi", 0, subsequent_afis),
    805                        ByteEnumField("flags", 0, gr_address_family_flags)]
    806 
    807     name = "Graceful Restart Capability"
    808     fields_desc = [ByteEnumField("code", 64, _capabilities),
    809                    ByteField("length", None),
    810                    BitField("restart_flags", 0, 4),
    811                    BitField("restart_time", 0, 12),
    812                    PacketListField("entries", [], GRTuple)]
    813 
    814 
    815 #
    816 # Support for 4-octet AS number capability
    817 #
    818 
    819 class BGPCapFourBytesASN(BGPCapability):
    820     """
    821     This class provides an implementation of the 4-octet AS number
    822     capability.
    823     References: RFC 4893
    824     """
    825 
    826     name = "Support for 4-octet AS number capability"
    827     fields_desc = [ByteEnumField("code", 65, _capabilities),
    828                    ByteField("length", 4),
    829                    IntField("asn", 0)]
    830 
    831 
    832 #
    833 # Authentication Information optional parameter.
    834 #
    835 
    836 class BGPAuthenticationInformation(Packet):
    837 
    838     """
    839     Provides an implementation of the Authentication Information optional
    840     parameter, which is now obsolete.
    841     References: RFC 1771, RFC 1654, RFC 4271
    842     """
    843 
    844     name = "BGP Authentication Data"
    845     fields_desc = [ByteField("authentication_code", 0),
    846                    StrField("authentication_data", None)]
    847 
    848 
    849 #
    850 # Optional Parameter.
    851 #
    852 
    853 
    854 class BGPOptParamPacketListField(PacketListField):
    855     """
    856     PacketListField handling the optional parameters (OPEN message).
    857     """
    858 
    859     def getfield(self, pkt, s):
    860         lst = []
    861 
    862         length = 0
    863         if self.length_from is not None:
    864             length = self.length_from(pkt)
    865         remain = s
    866         if length is not None:
    867             remain, ret = s[:length], s[length:]
    868 
    869         while remain:
    870             param_len = orb(remain[1])  # Get param length
    871             current = remain[:2 + param_len]
    872             remain = remain[2 + param_len:]
    873             packet = self.m2i(pkt, current)
    874             lst.append(packet)
    875 
    876         return remain + ret, lst
    877 
    878 
    879 class BGPOptParam(Packet):
    880     """
    881     Provides an implementation the OPEN message optional parameters.
    882     References: RFC 4271
    883     """
    884 
    885     name = "Optional parameter"
    886     fields_desc = [
    887         ByteEnumField("param_type", 2, optional_parameter_codes),
    888         ByteField("param_length", None),
    889         ConditionalField(
    890             PacketField(
    891                 "param_value",
    892                 None,
    893                 BGPCapability
    894             ),
    895             lambda p: p.param_type == 2
    896         ),
    897         # It"s obsolete, but one can use it provided that
    898         # param_type == 1.
    899         ConditionalField(
    900             PacketField(
    901                 "authentication_data",
    902                 None,
    903                 BGPAuthenticationInformation
    904             ),
    905             lambda p: p.param_type == 1
    906         )
    907     ]
    908 
    909     def post_build(self, p, pay):
    910         length = None
    911         packet = p
    912         if self.param_length is None:
    913             if self.param_value is None and self.authentication_data is None:
    914                 length = 0
    915             else:
    916                 length = len(p) - \
    917                     2  # parameter type (1 byte) - parameter length (1 byte)
    918             packet = chb(p[0]) + chb(length)
    919             if (self.param_type == 2 and self.param_value is not None) or\
    920                     (self.param_type == 1 and self.authentication_data is not None):
    921                 packet = packet + p[2:]
    922 
    923         return packet + pay
    924 
    925 
    926 #
    927 # OPEN
    928 #
    929 
    930 class BGPOpen(BGP):
    931     """
    932     OPEN messages are exchanged in order to open a new BGP session.
    933     References: RFC 4271
    934     """
    935 
    936     name = "OPEN"
    937     fields_desc = [
    938         ByteField("version", 4),
    939         ShortField("my_as", 0),
    940         ShortField("hold_time", 0),
    941         IPField("bgp_id", "0.0.0.0"),
    942         FieldLenField(
    943             "opt_param_len",
    944             None,
    945             length_of="opt_params",
    946             fmt="!B"
    947         ),
    948         BGPOptParamPacketListField(
    949             "opt_params",
    950             [],
    951             BGPOptParam,
    952             length_from=lambda p: p.opt_param_len
    953         )
    954     ]
    955 
    956     def post_build(self, p, pay):
    957         if self.opt_param_len is None:
    958             length = len(p) - 10  # 10 is regular length with no additional
    959             # options
    960             p = p[:9] + struct.pack("!B", length) + p[10:]
    961         return p + pay
    962 
    963 
    964 #
    965 # UPDATE
    966 #
    967 
    968 #
    969 # Path attributes
    970 #
    971 
    972 #
    973 # Dictionaries
    974 
    975 path_attributes = {
    976     0: "Reserved",
    977     1: "ORIGIN",  # RFC 4271
    978     2: "AS_PATH",  # RFC 4271
    979     3: "NEXT_HOP",  # RFC 4271
    980     4: "MULTI_EXIT_DISC",  # RFC 4271
    981     5: "LOCAL_PREF",  # RFC 4271
    982     6: "ATOMIC_AGGREGATE",  # RFC 4271
    983     7: "AGGREGATOR",  # RFC 4271
    984     8: "COMMUNITY",  # RFC 1997
    985     9: "ORIGINATOR_ID",  # RFC 4456
    986     10: "CLUSTER_LIST",  # RFC 4456
    987     11: "DPA (deprecated)",  # RFC 6938
    988     12: "ADVERTISER  (Historic) (deprecated)",  # RFC 4223, RFC 6938
    989     13: "RCID_PATH / CLUSTER_ID (Historic) (deprecated)",  # RFC 4223, RFC 6938
    990     14: "MP_REACH_NLRI",  # RFC 4760
    991     15: "MP_UNREACH_NLRI",  # RFC 4760
    992     16: "EXTENDED COMMUNITIES",  # RFC 4360
    993     17: "AS4_PATH",  # RFC 6793
    994     18: "AS4_AGGREGATOR",  # RFC 6793
    995     19: "SAFI Specific Attribute (SSA) (deprecated)",  # draft-kapoor-nalawade-idr-bgp-ssa-00,
    996     # draft-nalawade-idr-mdt-safi-00, draft-wijnands-mt-discovery-00
    997     20: "Connector Attribute (deprecated)",  # RFC 6037
    998     21: "AS_PATHLIMIT (deprecated)",  # draft-ietf-idr-as-pathlimit
    999     22: "PMSI_TUNNEL",  # RFC 6514
   1000     23: "Tunnel Encapsulation Attribute",  # RFC 5512
   1001     24: "Traffic Engineering",  # RFC 5543
   1002     25: "IPv6 Address Specific Extended Community",  # RFC 5701
   1003     26: "AIGP",  # RFC 7311
   1004     27: "PE Distinguisher Labels",  # RFC 6514
   1005     28: "BGP Entropy Label Capability Attribute (deprecated)",  # RFC 6790, RFC 7447
   1006     29: "BGP-LS Attribute",  # RFC 7752
   1007     40: "BGP Prefix-SID",  # (TEMPORARY - registered 2015-09-30, expires 2016-09-30)
   1008     # draft-ietf-idr-bgp-prefix-sid
   1009     128: "ATTR_SET",  # RFC 6368
   1010     255: "Reserved for development"
   1011 }
   1012 
   1013 # http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xml
   1014 attributes_flags = {
   1015     1: 0x40,    # ORIGIN
   1016     2: 0x40,    # AS_PATH
   1017     3: 0x40,    # NEXT_HOP
   1018     4: 0x80,    # MULTI_EXIT_DISC
   1019     5: 0x40,    # LOCAL_PREF
   1020     6: 0x40,    # ATOMIC_AGGREGATE
   1021     7: 0xc0,    # AGGREGATOR
   1022     8: 0xc0,    # COMMUNITIES (RFC 1997)
   1023     9: 0x80,    # ORIGINATOR_ID (RFC 4456)
   1024     10: 0x80,   # CLUSTER_LIST (RFC 4456)
   1025     11: 0xc0,   # DPA (RFC 6938)
   1026     12: 0x80,   # ADVERTISER (RFC 1863, RFC 4223)
   1027     13: 0x80,   # RCID_PATH (RFC 1863, RFC 4223)
   1028     14: 0x80,   # MP_REACH_NLRI (RFC 4760)
   1029     15: 0x80,   # MP_UNREACH_NLRI (RFC 4760)
   1030     16: 0xc0,   # EXTENDED_COMMUNITIES (RFC 4360)
   1031     17: 0xc0,   # AS4_PATH (RFC 6793)
   1032     18: 0xc0,   # AS4_AGGREGATOR (RFC 6793)
   1033     19: 0xc0,   # SSA (draft-kapoor-nalawade-idr-bgp-ssa-00)
   1034     20: 0xc0,   # Connector (RFC 6037)
   1035     21: 0xc0,   # AS_PATHLIMIT (draft-ietf-idr-as-pathlimit)
   1036     22: 0xc0,   # PMSI_TUNNEL (RFC 6514)
   1037     23: 0xc0,   # Tunnel Encapsulation (RFC 5512)
   1038     24: 0x80,   # Traffic Engineering (RFC 5543)
   1039     25: 0xc0,   # IPv6 Address Specific Extended Community (RFC 5701)
   1040     26: 0x80,   # AIGP (RFC 7311)
   1041     27: 0xc0,   # PE Distinguisher Labels (RFC 6514)
   1042     28: 0xc0,   # BGP Entropy Label Capability Attribute
   1043     29: 0x80,   # BGP-LS Attribute
   1044     40: 0xc0,   # BGP Prefix-SID
   1045     128: 0xc0   # ATTR_SET (RFC 6368)
   1046 }
   1047 
   1048 
   1049 class BGPPathAttrPacketListField(PacketListField):
   1050     """
   1051     PacketListField handling the path attributes (UPDATE message).
   1052     """
   1053 
   1054     def getfield(self, pkt, s):
   1055         lst = []
   1056         length = 0
   1057 
   1058         if self.length_from is not None:
   1059             length = self.length_from(pkt)
   1060         ret = ""
   1061         remain = s
   1062         if length is not None:
   1063             remain, ret = s[:length], s[length:]
   1064 
   1065         while remain:
   1066             #
   1067             # Get the path attribute flags
   1068             flags = orb(remain[0])
   1069 
   1070             attr_len = 0
   1071             if has_extended_length(flags):
   1072                 attr_len = struct.unpack("!H", remain[2:4])[0]
   1073                 current = remain[:4 + attr_len]
   1074                 remain = remain[4 + attr_len:]
   1075             else:
   1076                 attr_len = orb(remain[2])
   1077                 current = remain[:3 + attr_len]
   1078                 remain = remain[3 + attr_len:]
   1079 
   1080             packet = self.m2i(pkt, current)
   1081             lst.append(packet)
   1082 
   1083         return remain + ret, lst
   1084 
   1085 
   1086 #
   1087 # ORIGIN
   1088 #
   1089 
   1090 class BGPPAOrigin(Packet):
   1091 
   1092     """
   1093     Packet handling the ORIGIN attribute value.
   1094     References: RFC 4271
   1095     """
   1096 
   1097     name = "ORIGIN"
   1098     fields_desc = [
   1099         ByteEnumField("origin", 0, {0: "IGP", 1: "EGP", 2: "INCOMPLETE"})]
   1100 
   1101 
   1102 #
   1103 # AS_PATH (2 bytes and 4 bytes)
   1104 #
   1105 
   1106 as_path_segment_types = {
   1107     # RFC 4271
   1108     1: "AS_SET",
   1109     2: "AS_SEQUENCE",
   1110 
   1111     # RFC 5065
   1112     3: "AS_CONFED_SEQUENCE",
   1113     4: "AS_CONFED_SET"
   1114 }
   1115 
   1116 
   1117 class ASPathSegmentPacketListField(PacketListField):
   1118     """
   1119     PacketListField handling AS_PATH segments.
   1120     """
   1121 
   1122     def getfield(self, pkt, s):
   1123         lst = []
   1124         remain = s
   1125 
   1126         while remain:
   1127             #
   1128             # Get the segment length
   1129             segment_length = orb(remain[1])
   1130 
   1131             if bgp_module_conf.use_2_bytes_asn:
   1132                 current = remain[:2 + segment_length * 2]
   1133                 remain = remain[2 + segment_length * 2:]
   1134             else:
   1135                 current = remain[:2 + segment_length * 4]
   1136                 remain = remain[2 + segment_length * 4:]
   1137 
   1138             packet = self.m2i(pkt, current)
   1139             lst.append(packet)
   1140 
   1141         return remain, lst
   1142 
   1143 
   1144 class BGPPAASPath(Packet):
   1145     """
   1146     Packet handling the AS_PATH attribute value (2 bytes ASNs, for old
   1147     speakers).
   1148     References: RFC 4271, RFC 5065
   1149     """
   1150 
   1151     AS_TRANS = 23456
   1152 
   1153     class ASPathSegment(Packet):
   1154         """
   1155         Provides an implementation for AS_PATH segments with 2 bytes ASNs.
   1156         """
   1157 
   1158         fields_desc = [
   1159             ByteEnumField("segment_type", 2, as_path_segment_types),
   1160             ByteField("segment_length", None),
   1161             FieldListField("segment_value", [], ShortField("asn", 0))
   1162         ]
   1163 
   1164         def post_build(self, p, pay):
   1165             segment_len = self.segment_length
   1166             if segment_len is None:
   1167                 segment_len = len(self.segment_value)
   1168                 p = chb(p[0]) + chb(segment_len) + p[2:]
   1169 
   1170             return p + pay
   1171 
   1172     name = "AS_PATH (RFC 4271)"
   1173     fields_desc = [
   1174         ASPathSegmentPacketListField("segments", [], ASPathSegment)]
   1175 
   1176 
   1177 class BGPPAAS4BytesPath(Packet):
   1178     """
   1179     Packet handling the AS_PATH attribute value (4 bytes ASNs, for new
   1180     speakers -> ASNs are encoded as IntFields).
   1181     References: RFC 4893
   1182     """
   1183 
   1184     class ASPathSegment(Packet):
   1185         """
   1186         Provides an implementation for AS_PATH segments with 4 bytes ASNs.
   1187         """
   1188 
   1189         fields_desc = [ByteEnumField("segment_type", 2, as_path_segment_types),
   1190                        ByteField("segment_length", None),
   1191                        FieldListField("segment_value", [], IntField("asn", 0))]
   1192 
   1193         def post_build(self, p, pay):
   1194             segment_len = self.segment_length
   1195             if segment_len is None:
   1196                 segment_len = len(self.segment_value)
   1197                 p = chb(p[0]) + chb(segment_len) + p[2:]
   1198 
   1199             return p + pay
   1200 
   1201     name = "AS_PATH (RFC 4893)"
   1202     fields_desc = [
   1203         ASPathSegmentPacketListField("segments", [], ASPathSegment)]
   1204 
   1205 
   1206 #
   1207 # NEXT_HOP
   1208 #
   1209 
   1210 class BGPPANextHop(Packet):
   1211     """
   1212     Packet handling the NEXT_HOP attribute value.
   1213     References: RFC 4271
   1214     """
   1215 
   1216     name = "NEXT_HOP"
   1217     fields_desc = [IPField("next_hop", "0.0.0.0")]
   1218 
   1219 
   1220 #
   1221 # MULTI_EXIT_DISC
   1222 #
   1223 
   1224 class BGPPAMultiExitDisc(Packet):
   1225     """
   1226     Packet handling the MULTI_EXIT_DISC attribute value.
   1227     References: RFC 4271
   1228     """
   1229 
   1230     name = "MULTI_EXIT_DISC"
   1231     fields_desc = [IntField("med", 0)]
   1232 
   1233 
   1234 #
   1235 # LOCAL_PREF
   1236 #
   1237 
   1238 class BGPPALocalPref(Packet):
   1239     """
   1240     Packet handling the LOCAL_PREF attribute value.
   1241     References: RFC 4271
   1242     """
   1243 
   1244     name = "LOCAL_PREF"
   1245     fields_desc = [IntField("local_pref", 0)]
   1246 
   1247 
   1248 #
   1249 # ATOMIC_AGGREGATE
   1250 #
   1251 
   1252 class BGPPAAtomicAggregate(Packet):
   1253     """
   1254     Packet handling the ATOMIC_AGGREGATE attribute value.
   1255     References: RFC 4271
   1256     """
   1257 
   1258     name = "ATOMIC_AGGREGATE"
   1259 
   1260 
   1261 #
   1262 # AGGREGATOR
   1263 #
   1264 
   1265 class BGPPAAggregator(Packet):
   1266     """
   1267     Packet handling the AGGREGATOR attribute value.
   1268     References: RFC 4271
   1269     """
   1270 
   1271     name = "AGGREGATOR"
   1272     fields_desc = [ShortField("aggregator_asn", 0),
   1273                    IPField("speaker_address", "0.0.0.0")]
   1274 
   1275 
   1276 #
   1277 # COMMUNITIES
   1278 #
   1279 
   1280 # http://www.iana.org/assignments/bgp-well-known-communities/bgp-well-known-communities.xml
   1281 well_known_communities = {
   1282     0xFFFFFF01: "NO_EXPORT",  # RFC 1997
   1283     0xFFFFFF02: "NO_ADVERTISE",  # RFC 1997
   1284     0xFFFFFF03: "NO_EXPORT_SUBCONFED",  # RFC 1997
   1285     0xFFFFFF04: "NOPEER",  # RFC 3765
   1286     0xFFFF0000: "planned-shut",  # draft-francois-bgp-gshut
   1287     0xFFFF0001: "ACCEPT-OWN",  # RFC 7611
   1288     0xFFFF0002: "ROUTE_FILTER_TRANSLATED_v4",  # draft-l3vpn-legacy-rtc
   1289     0xFFFF0003: "ROUTE_FILTER_v4",  # draft-l3vpn-legacy-rtc
   1290     0xFFFF0004: "ROUTE_FILTER_TRANSLATED_v6",  # draft-l3vpn-legacy-rtc
   1291     0xFFFF0005: "ROUTE_FILTER_v6",  # draft-l3vpn-legacy-rtc
   1292     0xFFFF0006: "LLGR_STALE",  # draft-uttaro-idr-bgp-persistence
   1293     0xFFFF0007: "NO_LLGR",  # draft-uttaro-idr-bgp-persistence
   1294     0xFFFF0008: "accept-own-nexthop",  # Ashutosh_Grewal
   1295 }
   1296 
   1297 
   1298 class BGPPACommunity(Packet):
   1299     """
   1300     Packet handling the COMMUNITIES attribute value.
   1301     References: RFC 1997
   1302     """
   1303 
   1304     name = "COMMUNITIES"
   1305     fields_desc = [IntEnumField("community", 0, well_known_communities)]
   1306 
   1307 
   1308 #
   1309 # ORIGINATOR_ID
   1310 #
   1311 
   1312 class BGPPAOriginatorID(Packet):
   1313     """
   1314     Packet handling the ORIGINATOR_ID attribute value.
   1315     References: RFC 4456
   1316     """
   1317 
   1318     name = "ORIGINATOR_ID"
   1319     fields_desc = [IPField("originator_id", "0.0.0.0")]
   1320 
   1321 
   1322 #
   1323 # CLUSTER_LIST
   1324 #
   1325 
   1326 class BGPPAClusterList(Packet):
   1327     """
   1328     Packet handling the CLUSTER_LIST attribute value.
   1329     References: RFC 4456
   1330     """
   1331 
   1332     name = "CLUSTER_LIST"
   1333     fields_desc = [
   1334         FieldListField("cluster_list", [], IntField("cluster_id", 0))]
   1335 
   1336 
   1337 #
   1338 # EXTENDED COMMUNITIES (RFC 4360)
   1339 #
   1340 
   1341 # BGP Transitive Extended Community Types
   1342 # http://www.iana.org/assignments/bgp-extended-communities/bgp-extended-communities.xhtml#transitive
   1343 _ext_comm_types = {
   1344     0x00: "Transitive Two-Octet AS-Specific Extended Community",  # RFC 7153
   1345     0x01: "Transitive IPv4-Address-Specific Extended Community",  # RFC 7153
   1346     0x02: "Transitive Four-Octet AS-Specific Extended Community",  # RFC 7153
   1347     0x03: "Transitive Opaque Extended Community",  # RFC 7153
   1348     0x04: "QoS Marking",  # Thomas_Martin_Knoll
   1349     0x05: "CoS Capability",  # Thomas_Martin_Knoll
   1350     0x06: "EVPN",  # RFC 7153
   1351     0x07: "Unassigned",
   1352     0x08: "Flow spec redirect/mirror to IP next-hop",  # draft-simpson-idr-flowspec-redirect
   1353 
   1354     # BGP Non-Transitive Extended Community Types
   1355     0x40: "Non-Transitive Two-Octet AS-Specific Extended Community",  # RFC 7153
   1356     0x41: "Non-Transitive IPv4-Address-Specific Extended Community",  # RFC 7153
   1357     0x42: "Non-Transitive Four-Octet AS-Specific Extended Community",  # RFC 7153
   1358     0x43: "Non-Transitive Opaque Extended Community",  # RFC 7153
   1359     0x44: "QoS Marking",  # Thomas_Martin_Knoll
   1360 
   1361     0x80: "Generic Transitive Experimental Use Extended Community",  # RFC 7153
   1362     0x81: "Generic Transitive Experimental Use Extended Community Part 2",  # RFC 7674
   1363     0x82: "Generic Transitive Experimental Use Extended Community Part 3",  # RFC 7674
   1364 }
   1365 
   1366 # EVPN Extended Community Sub-Types
   1367 _ext_comm_evpn_subtypes = {
   1368     0x00: "MAC Mobility",  # RFC 7432
   1369     0x01: "ESI Label",  # RFC 7432
   1370     0x02: "ES-Import Route Target",  # RFC 7432
   1371     0x03: "EVPN Router\"s MAC Extended Community",
   1372     # draft-sajassi-l2vpn-evpn-inter-subnet-forwarding
   1373     0x04: "Layer 2 Extended Community",  # draft-ietf-bess-evpn-vpws
   1374     0x05: "E-TREE Extended Community",  # draft-ietf-bess-evpn-etree
   1375     0x06: "DF Election Extended Community",  # draft-ietf-bess-evpn-df-election
   1376     0x07: "I-SID Extended Community",  # draft-sajassi-bess-evpn-virtual-eth-segment
   1377 }
   1378 
   1379 # Transitive Two-Octet AS-Specific Extended Community Sub-Types
   1380 _ext_comm_trans_two_octets_as_specific_subtypes = {
   1381     0x02: "Route Target",  # RFC 4360
   1382     0x03: "Route Origin",  # RFC 4360
   1383     0x04: "Unassigned",  # RFC 4360
   1384     0x05: "OSPF Domain Identifier",  # RFC 4577
   1385     0x08: "BGP Data Collection",  # RFC 4384
   1386     0x09: "Source AS",  # RFC 6514
   1387     0x0a: "L2VPN Identifier",  # RFC 6074
   1388     0x0010: "Cisco VPN-Distinguisher",  # Eric_Rosen
   1389 }
   1390 
   1391 # Non-Transitive Two-Octet AS-Specific Extended Community Sub-Types
   1392 _ext_comm_non_trans_two_octets_as_specific_subtypes = {
   1393     0x04: "Link Bandwidth Extended Community",  # draft-ietf-idr-link-bandwidth-00
   1394     0x80: "Virtual-Network Identifier Extended Community",
   1395     # draft-drao-bgp-l3vpn-virtual-network-overlays
   1396 }
   1397 
   1398 # Transitive Four-Octet AS-Specific Extended Community Sub-Types
   1399 _ext_comm_trans_four_octets_as_specific_subtypes = {
   1400     0x02: "Route Target",  # RFC 5668
   1401     0x03: "Route Origin",  # RFC 5668
   1402     0x04: "Generic",  # draft-ietf-idr-as4octet-extcomm-generic-subtype
   1403     0x05: "OSPF Domain Identifier",  # RFC 4577
   1404     0x08: "BGP Data Collection",  # RFC 4384
   1405     0x09: "Source AS",  # RFC 6514
   1406     0x10: "Cisco VPN Identifier",  # Eric_Rosen
   1407 }
   1408 
   1409 # Non-Transitive Four-Octet AS-Specific Extended Community Sub-Types
   1410 _ext_comm_non_trans_four_octets_as_specific_subtypes = {
   1411     0x04: "Generic",  # draft-ietf-idr-as4octet-extcomm-generic-subtype
   1412 }
   1413 
   1414 # Transitive IPv4-Address-Specific Extended Community Sub-Types
   1415 _ext_comm_trans_ipv4_addr_specific_subtypes = {
   1416     0x02: "Route Target",  # RFC 4360
   1417     0x03: "Route Origin",  # RFC 4360
   1418     0x05: "OSPF Domain Identifier",  # RFC 4577
   1419     0x07: "OSPF Route ID",  # RFC 4577
   1420     0x0a: "L2VPN Identifier",  # RFC 6074
   1421     0x0b: "VRF Route Import",  # RFC 6514
   1422     0x0c: "Flow-spec Redirect to IPv4",  # draft-ietf-idr-flowspec-redirect
   1423     0x10: "Cisco VPN-Distinguisher",  # Eric_Rosen
   1424     0x12: "Inter-Area P2MP Segmented Next-Hop",  # RFC 7524
   1425 }
   1426 
   1427 # Non-Transitive IPv4-Address-Specific Extended Community Sub-Types
   1428 _ext_comm_non_trans_ipv4_addr_specific_subtypes = {}
   1429 
   1430 # Transitive Opaque Extended Community Sub-Types
   1431 _ext_comm_trans_opaque_subtypes = {
   1432     0x01: "Cost Community",  # draft-ietf-idr-custom-decision
   1433     0x03: "CP-ORF",  # RFC 7543
   1434     0x04: "Extranet Source Extended Community",  # RFC 7900
   1435     0x05: "Extranet Separation Extended Community",  # RFC 7900
   1436     0x06: "OSPF Route Type",  # RFC 4577
   1437     0x07: "Additional PMSI Tunnel Attribute Flags",  # RFC 7902
   1438     0x0b: "Color Extended Community",  # RFC 5512
   1439     0x0c: "Encapsulation Extended Community",  # RFC 5512
   1440     0x0d: "Default Gateway",  # Yakov_Rekhter
   1441     0x0e: "Point-to-Point-to-Multipoint (PPMP) Label",  # Rishabh_Parekh
   1442     0x13: "Route-Target Record",  # draft-ietf-bess-service-chaining
   1443     0x14: "Consistent Hash Sort Order",  # draft-ietf-bess-service-chaining
   1444 }
   1445 
   1446 # Non-Transitive Opaque Extended Community Sub-Types
   1447 _ext_comm_non_trans_opaque_subtypes = {
   1448     0x00: "BGP Origin Validation State",  # draft-ietf-sidr-origin-validation-signaling
   1449     0x01: "Cost Community",  # draft-ietf-idr-custom-decision
   1450 }
   1451 
   1452 # Generic Transitive Experimental Use Extended Community Sub-Types
   1453 _ext_comm_generic_transitive_exp_subtypes = {
   1454     0x00: "OSPF Route Type (deprecated)",  # RFC 4577
   1455     0x01: "OSPF Router ID (deprecated)",  # RFC 4577
   1456     0x05: "OSPF Domain Identifier (deprecated)",  # RFC 4577
   1457     0x06: "Flow spec traffic-rate",  # RFC 5575
   1458     0x07: "Flow spec traffic-action",  # RFC 5575
   1459     0x08: "Flow spec redirect AS-2byte format",  # RFC 5575, RFC 7674
   1460     0x09: "Flow spec traffic-remarking",  # RFC 5575
   1461     0x0a: "Layer2 Info Extended Community",  # RFC 4761
   1462     0x0b: "E-Tree Info",  # RFC 7796
   1463 }
   1464 
   1465 # Generic Transitive Experimental Use Extended Community Part 2 Sub-Types
   1466 _ext_comm_generic_transitive_exp_part2_subtypes = {
   1467     0x08: "Flow spec redirect IPv4 format",  # RFC 7674
   1468 }
   1469 
   1470 # Generic Transitive Experimental Use Extended Community Part 3 Sub-Types
   1471 _ext_comm_generic_transitive_exp_part3_subtypes = {
   1472     0x08: "Flow spec redirect AS-4byte format",  # RFC 7674
   1473 }
   1474 
   1475 # Traffic Action Fields
   1476 _ext_comm_traffic_action_fields = {
   1477     47: "Terminal Action",  # RFC 5575
   1478     46: "Sample",  # RFC 5575
   1479 }
   1480 
   1481 # Transitive IPv6-Address-Specific Extended Community Types
   1482 _ext_comm_trans_ipv6_addr_specific_types = {
   1483     0x0002: "Route Target",  # RFC 5701
   1484     0x0003: "Route Origin",  # RFC 5701
   1485     0x0004: "OSPFv3 Route Attributes (DEPRECATED)",  # RFC 6565
   1486     0x000b: "VRF Route Import",  # RFC 6515, RFC 6514
   1487     0x000c: "Flow-spec Redirect to IPv6",  # draft-ietf-idr-flowspec-redirect-ip
   1488     0x0010: "Cisco VPN-Distinguisher",  # Eric_Rosen
   1489     0x0011: "UUID-based Route Target",  # Dhananjaya_Rao
   1490     0x0012: "Inter-Area P2MP Segmented Next-Hop",  # RFC 7524
   1491 }
   1492 
   1493 # Non-Transitive IPv6-Address-Specific Extended Community Types
   1494 _ext_comm_non_trans_ipv6_addr_specific_types = {}
   1495 
   1496 
   1497 _ext_comm_subtypes_classes = {
   1498     0x00: _ext_comm_trans_two_octets_as_specific_subtypes,
   1499     0x01: _ext_comm_trans_ipv4_addr_specific_subtypes,
   1500     0x02: _ext_comm_trans_four_octets_as_specific_subtypes,
   1501     0x03: _ext_comm_trans_opaque_subtypes,
   1502     0x06: _ext_comm_evpn_subtypes,
   1503     0x40: _ext_comm_non_trans_two_octets_as_specific_subtypes,
   1504     0x41: _ext_comm_non_trans_ipv4_addr_specific_subtypes,
   1505     0x42: _ext_comm_non_trans_four_octets_as_specific_subtypes,
   1506     0x43: _ext_comm_non_trans_opaque_subtypes,
   1507     0x80: _ext_comm_generic_transitive_exp_subtypes,
   1508     0x81: _ext_comm_generic_transitive_exp_part2_subtypes,
   1509     0x82: _ext_comm_generic_transitive_exp_part3_subtypes,
   1510 }
   1511 
   1512 
   1513 #
   1514 # Extended Community "templates"
   1515 #
   1516 
   1517 class BGPPAExtCommTwoOctetASSpecific(Packet):
   1518     """
   1519     Packet handling the Two-Octet AS Specific Extended Community attribute
   1520     value.
   1521     References: RFC 4360
   1522     """
   1523 
   1524     name = "Two-Octet AS Specific Extended Community"
   1525     fields_desc = [
   1526         ShortField("global_administrator", 0), IntField("local_administrator", 0)]
   1527 
   1528 
   1529 class BGPPAExtCommFourOctetASSpecific(Packet):
   1530     """
   1531     Packet handling the Four-Octet AS Specific Extended Community
   1532     attribute value.
   1533     References: RFC 5668
   1534     """
   1535 
   1536     name = "Four-Octet AS Specific Extended Community"
   1537     fields_desc = [
   1538         IntField("global_administrator", 0), ShortField("local_administrator", 0)]
   1539 
   1540 
   1541 class BGPPAExtCommIPv4AddressSpecific(Packet):
   1542     """
   1543     Packet handling the IPv4 Address Specific Extended Community attribute
   1544     value.
   1545     References: RFC 4360
   1546     """
   1547 
   1548     name = "IPv4 Address Specific Extended Community"
   1549     fields_desc = [
   1550         IntField("global_administrator", 0), ShortField("local_administrator", 0)]
   1551 
   1552 
   1553 class BGPPAExtCommOpaque(Packet):
   1554     """
   1555     Packet handling the Opaque Extended Community attribute value.
   1556     References: RFC 4360
   1557     """
   1558 
   1559     name = "Opaque Extended Community"
   1560     fields_desc = [StrFixedLenField("value", "", length=6)]
   1561 
   1562 
   1563 #
   1564 # FlowSpec related extended communities
   1565 #
   1566 
   1567 class BGPPAExtCommTrafficRate(Packet):
   1568     """
   1569     Packet handling the (FlowSpec) "traffic-rate" extended community.
   1570     References: RFC 5575
   1571     """
   1572 
   1573     name = "FlowSpec traffic-rate extended community"
   1574     fields_desc = [
   1575         ShortField("id", 0),
   1576         IEEEFloatField("rate", 0)
   1577     ]
   1578 
   1579 
   1580 class BGPPAExtCommTrafficAction(Packet):
   1581     """
   1582     Packet handling the (FlowSpec) "traffic-action" extended community.
   1583     References: RFC 5575
   1584     """
   1585 
   1586     name = "FlowSpec traffic-action extended community"
   1587     fields_desc = [
   1588         BitField("reserved", 0, 46),
   1589         BitField("sample", 0, 1),
   1590         BitField("terminal_action", 0, 1)
   1591     ]
   1592 
   1593 
   1594 class BGPPAExtCommRedirectAS2Byte(Packet):
   1595     """
   1596     Packet handling the (FlowSpec) "redirect AS-2byte" extended community
   1597     (RFC 7674).
   1598     References: RFC 7674
   1599     """
   1600 
   1601     name = "FlowSpec redirect AS-2byte extended community"
   1602     fields_desc = [
   1603         ShortField("asn", 0),
   1604         IntField("value", 0)
   1605     ]
   1606 
   1607 
   1608 class BGPPAExtCommRedirectIPv4(Packet):
   1609     """
   1610     Packet handling the (FlowSpec) "redirect IPv4" extended community.
   1611     (RFC 7674).
   1612     References: RFC 7674
   1613     """
   1614 
   1615     name = "FlowSpec redirect IPv4 extended community"
   1616     fields_desc = [
   1617         IntField("ip_addr", 0),
   1618         ShortField("value", 0)
   1619     ]
   1620 
   1621 
   1622 class BGPPAExtCommRedirectAS4Byte(Packet):
   1623     """
   1624     Packet handling the (FlowSpec) "redirect AS-4byte" extended community.
   1625     (RFC 7674).
   1626     References: RFC 7674
   1627     """
   1628 
   1629     name = "FlowSpec redirect AS-4byte extended community"
   1630     fields_desc = [
   1631         IntField("asn", 0),
   1632         ShortField("value", 0)
   1633     ]
   1634 
   1635 
   1636 class BGPPAExtCommTrafficMarking(Packet):
   1637     """
   1638     Packet handling the (FlowSpec) "traffic-marking" extended community.
   1639     References: RFC 5575
   1640     """
   1641 
   1642     name = "FlowSpec traffic-marking extended community"
   1643     fields_desc = [
   1644         BitEnumField("dscp", 48, 48, _ext_comm_traffic_action_fields)
   1645     ]
   1646 
   1647 
   1648 class _ExtCommValuePacketField(PacketField):
   1649     """
   1650     PacketField handling Extended Communities "value parts".
   1651     """
   1652 
   1653     __slots__ = ["type_from"]
   1654 
   1655     def __init__(self, name, default, cls, remain=0, type_from=(0, 0)):
   1656         PacketField.__init__(self, name, default, cls, remain)
   1657         self.type_from = type_from
   1658 
   1659     def m2i(self, pkt, m):
   1660         ret = None
   1661         type_high, type_low = self.type_from(pkt)
   1662 
   1663         if type_high == 0x00 or type_high == 0x40:
   1664             # Two-Octet AS Specific Extended Community
   1665             ret = BGPPAExtCommTwoOctetASSpecific(m)
   1666 
   1667         elif type_high == 0x01 or type_high == 0x41:
   1668             # IPv4 Address Specific
   1669             ret = BGPPAExtCommIPv4AddressSpecific(m)
   1670 
   1671         elif type_high == 0x02 or type_high == 0x42:
   1672             # Four-octet AS Specific Extended Community
   1673             ret = BGPPAExtCommFourOctetASSpecific(m)
   1674 
   1675         elif type_high == 0x03 or type_high == 0x43:
   1676             # Opaque
   1677             ret = BGPPAExtCommOpaque(m)
   1678 
   1679         elif type_high == 0x80:
   1680             # FlowSpec
   1681             if type_low == 0x06:
   1682                 ret = BGPPAExtCommTrafficRate(m)
   1683             elif type_low == 0x07:
   1684                 ret = BGPPAExtCommTrafficAction(m)
   1685             elif type_low == 0x08:
   1686                 ret = BGPPAExtCommRedirectAS2Byte(m)
   1687             elif type_low == 0x09:
   1688                 ret = BGPPAExtCommTrafficMarking(m)
   1689 
   1690         elif type_high == 0x81:
   1691             # FlowSpec
   1692             if type_low == 0x08:
   1693                 ret = BGPPAExtCommRedirectIPv4(m)
   1694 
   1695         elif type_high == 0x82:
   1696             # FlowSpec
   1697             if type_low == 0x08:
   1698                 ret = BGPPAExtCommRedirectAS4Byte(m)
   1699 
   1700         else:
   1701             ret = conf.raw_layer(m)
   1702 
   1703         return ret
   1704 
   1705 
   1706 class BGPPAIPv6AddressSpecificExtComm(Packet):
   1707     """
   1708     Provides an implementation of the IPv6 Address Specific Extended
   1709     Community attribute. This attribute is not defined using the existing
   1710     BGP Extended Community attribute (see the RFC 5701 excerpt below).
   1711     References: RFC 5701
   1712     """
   1713 
   1714     name = "IPv6 Address Specific Extended Community"
   1715     fields_desc = [
   1716         IP6Field("global_administrator", "::"), ShortField("local_administrator", 0)]
   1717 
   1718 
   1719 def _get_ext_comm_subtype(type_high):
   1720     """
   1721     Returns a ByteEnumField with the right sub-types dict for a given community.
   1722     http://www.iana.org/assignments/bgp-extended-communities/bgp-extended-communities.xhtml
   1723     """
   1724 
   1725     return _ext_comm_subtypes_classes.get(type_high, {})
   1726 
   1727 
   1728 class _TypeLowField(ByteField):
   1729     """
   1730     Field used to retrieve "dynamically" the right sub-type dict.
   1731     """
   1732 
   1733     __slots__ = ["enum_from"]
   1734 
   1735     def __init__(self, name, default, enum_from=None):
   1736         ByteField.__init__(self, name=name, default=default)
   1737         self.enum_from = enum_from
   1738 
   1739     def i2repr(self, pkt, i):
   1740         enum = self.enum_from(pkt)
   1741         return enum.get(i, i)
   1742 
   1743 
   1744 class BGPPAExtCommunity(Packet):
   1745     """
   1746     Provides an implementation of the Extended Communities attribute.
   1747     References: RFC 4360
   1748     """
   1749 
   1750     name = "EXTENDED_COMMUNITY"
   1751     fields_desc = [
   1752         ByteEnumField("type_high", 0, _ext_comm_types),
   1753         _TypeLowField(
   1754             "type_low",
   1755             0,
   1756             enum_from=lambda x: _get_ext_comm_subtype(x.type_high)
   1757         ),
   1758         _ExtCommValuePacketField(
   1759             "value",
   1760             None,
   1761             Packet,
   1762             type_from=lambda x: (x.type_high, x.type_low)
   1763         )
   1764     ]
   1765 
   1766     def post_build(self, p, pay):
   1767         if self.value is None:
   1768             p = p[:2]
   1769         return p + pay
   1770 
   1771 
   1772 class _ExtCommsPacketListField(PacketListField):
   1773     """
   1774     PacketListField handling a list of extended communities.
   1775     """
   1776 
   1777     def getfield(self, pkt, s):
   1778         lst = []
   1779         length = len(s)
   1780         remain = s[:length]
   1781 
   1782         while remain:
   1783             current = remain[:8]
   1784             remain = remain[8:]
   1785             packet = self.m2i(pkt, current)
   1786             lst.append(packet)
   1787 
   1788         return remain, lst
   1789 
   1790 
   1791 class BGPPAExtComms(Packet):
   1792     """
   1793     Packet handling the multiple extended communities.
   1794     """
   1795 
   1796     name = "EXTENDED_COMMUNITIES"
   1797     fields_desc = [
   1798         _ExtCommsPacketListField(
   1799             "extended_communities",
   1800             [],
   1801             BGPPAExtCommunity
   1802         )
   1803     ]
   1804 
   1805 
   1806 class MPReachNLRIPacketListField(PacketListField):
   1807     """
   1808     PacketListField handling the AFI specific part (except for the length of
   1809     Next Hop Network Address field, which is not AFI specific) of the
   1810     MP_REACH_NLRI attribute.
   1811     """
   1812 
   1813     def getfield(self, pkt, s):
   1814         lst = []
   1815         remain = s
   1816 
   1817         # IPv6
   1818         if pkt.afi == 2:
   1819             if pkt.safi == 1:
   1820                 # BGPNLRI_IPv6
   1821                 while remain:
   1822                     mask = orb(remain[0])
   1823                     length_in_bytes = (mask + 7) // 8
   1824                     current = remain[:length_in_bytes + 1]
   1825                     remain = remain[length_in_bytes + 1:]
   1826                     prefix = BGPNLRI_IPv6(current)
   1827                     lst.append(prefix)
   1828 
   1829         return remain, lst
   1830 
   1831 
   1832 class BGPPAMPReachNLRI(Packet):
   1833     """
   1834     Packet handling the MP_REACH_NLRI attribute value, for non IPv6
   1835     AFI.
   1836     References: RFC 4760
   1837     """
   1838 
   1839     name = "MP_REACH_NLRI"
   1840     fields_desc = [
   1841         ShortEnumField("afi", 0, address_family_identifiers),
   1842         ByteEnumField("safi", 0, subsequent_afis),
   1843         ByteField("nh_addr_len", 0),
   1844         ConditionalField(IPField("nh_v4_addr", "0.0.0.0"),
   1845                          lambda x: x.afi == 1 and x.nh_addr_len == 4),
   1846         ConditionalField(IP6Field("nh_v6_addr", "::"),
   1847                          lambda x: x.afi == 2 and x.nh_addr_len == 16),
   1848         ConditionalField(IP6Field("nh_v6_global", "::"),
   1849                          lambda x: x.afi == 2 and x.nh_addr_len == 32),
   1850         ConditionalField(IP6Field("nh_v6_link_local", "::"),
   1851                          lambda x: x.afi == 2 and x.nh_addr_len == 32),
   1852         ByteField("reserved", 0),
   1853         MPReachNLRIPacketListField("nlri", [], Packet)]
   1854 
   1855     def post_build(self, p, pay):
   1856         if self.nlri is None:
   1857             p = p[:3]
   1858 
   1859         return p + pay
   1860 
   1861 
   1862 #
   1863 # MP_UNREACH_NLRI
   1864 #
   1865 
   1866 class BGPPAMPUnreachNLRI_IPv6(Packet):
   1867     """
   1868     Packet handling the MP_UNREACH_NLRI attribute value, for IPv6 AFI.
   1869     """
   1870 
   1871     name = "MP_UNREACH_NLRI (IPv6 NLRI)"
   1872     fields_desc = [BGPNLRIPacketListField(
   1873         "withdrawn_routes", [], BGPNLRI_IPv6)]
   1874 
   1875 
   1876 class MPUnreachNLRIPacketField(PacketField):
   1877     """
   1878     PacketField handling the AFI specific part of the MP_UNREACH_NLRI
   1879     attribute.
   1880     """
   1881 
   1882     def m2i(self, pkt, m):
   1883         ret = None
   1884 
   1885         if pkt.afi == 2:
   1886             ret = BGPPAMPUnreachNLRI_IPv6(m)
   1887         else:
   1888             ret = conf.raw_layer(m)
   1889 
   1890         return ret
   1891 
   1892 
   1893 class BGPPAMPUnreachNLRI(Packet):
   1894     """
   1895     Packet handling the MP_UNREACH_NLRI attribute value, for non IPv6
   1896     AFI.
   1897     References: RFC 4760
   1898     """
   1899 
   1900     name = "MP_UNREACH_NLRI"
   1901     fields_desc = [ShortEnumField("afi", 0, address_family_identifiers),
   1902                    ByteEnumField("safi", 0, subsequent_afis),
   1903                    MPUnreachNLRIPacketField("afi_safi_specific", None, Packet)]
   1904 
   1905     def post_build(self, p, pay):
   1906         if self.afi_safi_specific is None:
   1907             p = p[:3]
   1908 
   1909         return p + pay
   1910 
   1911 
   1912 #
   1913 # AS4_PATH
   1914 #
   1915 
   1916 class BGPPAAS4Path(Packet):
   1917     """
   1918     Provides an implementation of the AS4_PATH attribute "value part".
   1919     References: RFC 4893
   1920     """
   1921 
   1922     name = "AS4_PATH"
   1923     fields_desc = [
   1924         ByteEnumField(
   1925             "segment_type",
   1926             2,
   1927             {1: "AS_SET", 2: "AS_SEQUENCE"}
   1928         ),
   1929         ByteField("segment_length", None),
   1930         FieldListField("segment_value", [], IntField("asn", 0))
   1931     ]
   1932 
   1933     def post_build(self, p, pay):
   1934         if self.segment_length is None:
   1935             segment_len = len(self.segment_value)
   1936             p = chb(p[0]) + chb(segment_len) + p[2:]
   1937 
   1938         return p + pay
   1939 
   1940 
   1941 #
   1942 # AS4_AGGREGATOR
   1943 #
   1944 
   1945 class BGPPAAS4Aggregator(Packet):
   1946     """
   1947     Provides an implementation of the AS4_AGGREGATOR attribute
   1948     "value part".
   1949     References: RFC 4893
   1950     """
   1951 
   1952     name = "AS4_AGGREGATOR "
   1953     fields_desc = [IntField("aggregator_asn", 0),
   1954                    IPField("speaker_address", "0.0.0.0")]
   1955 
   1956 
   1957 _path_attr_objects = {
   1958     0x01: "BGPPAOrigin",
   1959     0x02: "BGPPAASPath",  # if bgp_module_conf.use_2_bytes_asn, BGPPAAS4BytesPath otherwise
   1960     0x03: "BGPPANextHop",
   1961     0x04: "BGPPAMultiExitDisc",
   1962     0x05: "BGPPALocalPref",
   1963     0x06: "BGPPAAtomicAggregate",
   1964     0x07: "BGPPAAggregator",
   1965     0x08: "BGPPACommunity",
   1966     0x09: "BGPPAOriginatorID",
   1967     0x0A: "BGPPAClusterList",
   1968     0x0E: "BGPPAMPReachNLRI",
   1969     0x0F: "BGPPAMPUnreachNLRI",
   1970     0x10: "BGPPAExtComms",
   1971     0x11: "BGPPAAS4Path",
   1972     0x19: "BGPPAIPv6AddressSpecificExtComm"
   1973 }
   1974 
   1975 
   1976 class _PathAttrPacketField(PacketField):
   1977     """
   1978     PacketField handling path attribute value parts.
   1979     """
   1980 
   1981     def m2i(self, pkt, m):
   1982         ret = None
   1983         type_code = pkt.type_code
   1984 
   1985         # Reserved
   1986         if type_code == 0 or type_code == 255:
   1987             ret = conf.raw_layer(m)
   1988         # Unassigned
   1989         elif (type_code >= 30 and type_code <= 39) or\
   1990             (type_code >= 41 and type_code <= 127) or\
   1991             (type_code >= 129 and type_code <= 254):
   1992             ret = conf.raw_layer(m)
   1993         # Known path attributes
   1994         else:
   1995             if type_code == 0x02 and not bgp_module_conf.use_2_bytes_asn:
   1996                 ret = BGPPAAS4BytesPath(m)
   1997             else:
   1998                 ret = _get_cls(
   1999                     _path_attr_objects.get(type_code, conf.raw_layer))(m)
   2000 
   2001         return ret
   2002 
   2003 
   2004 class BGPPathAttr(Packet):
   2005     """
   2006     Provides an implementation of the path attributes.
   2007     References: RFC 4271
   2008     """
   2009 
   2010     name = "BGPPathAttr"
   2011     fields_desc = [
   2012         FlagsField("type_flags", 0x80, 8, [
   2013             "NA0",
   2014             "NA1",
   2015             "NA2",
   2016             "NA3",
   2017             "Extended-Length",
   2018             "Partial",
   2019             "Transitive",
   2020             "Optional"
   2021         ]),
   2022         ByteEnumField("type_code", 0, path_attributes),
   2023         ConditionalField(
   2024             ShortField("attr_ext_len", None),
   2025             lambda x: x.type_flags != None and\
   2026                 has_extended_length(x.type_flags)
   2027         ),
   2028         ConditionalField(
   2029             ByteField("attr_len", None),
   2030             lambda x: x.type_flags != None and not\
   2031                 has_extended_length(x.type_flags)
   2032         ),
   2033         _PathAttrPacketField("attribute", None, Packet)
   2034     ]
   2035 
   2036     def post_build(self, p, pay):
   2037         flags_value = None
   2038         length = None
   2039         packet = None
   2040         extended_length = False
   2041 
   2042         # Set default flags value ?
   2043         if self.type_flags is None:
   2044             # Set the standard value, if it is exists in attributes_flags.
   2045             if self.type_code in attributes_flags:
   2046                 flags_value = attributes_flags.get(self.type_code)
   2047 
   2048             # Otherwise, set to optional, non-transitive.
   2049             else:
   2050                 flags_value = 0x80
   2051 
   2052             extended_length = has_extended_length(flags_value)
   2053         else:
   2054             extended_length = has_extended_length(self.type_flags)
   2055 
   2056         # Set the flags
   2057         if flags_value is None:
   2058             packet = p[:2]
   2059         else:
   2060             packet = struct.pack("!B", flags_value) + p[1]
   2061 
   2062         # Add the length
   2063         if self.attr_len is None:
   2064             if self.attribute is None:
   2065                 length = 0
   2066             else:
   2067                 if extended_length:
   2068                     length = len(p) - 4  # Flags + Type + Length (2 bytes)
   2069                 else:
   2070                     length = len(p) - 3  # Flags + Type + Length (1 byte)
   2071 
   2072         if length is None:
   2073             if extended_length:
   2074                 packet = packet + p[2:4]
   2075             else:
   2076                 packet = packet + p[2]
   2077         else:
   2078             if extended_length:
   2079                 packet = packet + struct.pack("!H", length)
   2080             else:
   2081                 packet = packet + struct.pack("!B", length)
   2082 
   2083         # Append the rest of the message
   2084         if extended_length:
   2085             if self.attribute != None:
   2086                 packet = packet + p[4:]
   2087         else:
   2088             if self.attribute != None:
   2089                 packet = packet + p[3:]
   2090 
   2091         return packet + pay
   2092 
   2093 
   2094 #
   2095 # UPDATE
   2096 #
   2097 
   2098 class BGPUpdate(BGP):
   2099     """
   2100     UPDATE messages allow peers to exchange routes.
   2101     References: RFC 4271
   2102     """
   2103 
   2104     name = "UPDATE"
   2105     fields_desc = [
   2106         FieldLenField(
   2107             "withdrawn_routes_len",
   2108             None,
   2109             length_of="withdrawn_routes",
   2110             fmt="!H"
   2111         ),
   2112         BGPNLRIPacketListField(
   2113             "withdrawn_routes",
   2114             [],
   2115             BGPNLRI_IPv4,
   2116             length_from=lambda p: p.withdrawn_routes_len
   2117         ),
   2118         FieldLenField(
   2119             "path_attr_len",
   2120             None,
   2121             length_of="path_attr",
   2122             fmt="!H"
   2123         ),
   2124         BGPPathAttrPacketListField(
   2125             "path_attr",
   2126             [],
   2127             BGPPathAttr,
   2128             length_from=lambda p: p.path_attr_len
   2129         ),
   2130         BGPNLRIPacketListField("nlri", [], BGPNLRI_IPv4)
   2131     ]
   2132 
   2133     def post_build(self, p, pay):
   2134         subpacklen = lambda p: len(p)
   2135         packet = ""
   2136         if self.withdrawn_routes_len is None:
   2137             wl = sum(map(subpacklen, self.withdrawn_routes))
   2138             packet = p[:0] + struct.pack("!H", wl) + p[2:]
   2139         if self.path_attr_len is None:
   2140             length = sum(map(subpacklen, self.path_attr))
   2141             packet = p[:2 + wl] + struct.pack("!H", length) + p[4 + wl:]
   2142 
   2143         return packet + pay
   2144 
   2145 
   2146 #
   2147 # NOTIFICATION
   2148 #
   2149 
   2150 #
   2151 # RFC 4271, RFC 7313
   2152 # http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-3
   2153 #
   2154 _error_codes = {
   2155     0x01: "Message Header Error",
   2156     0x02: "OPEN Message Error",
   2157     0x03: "UPDATE Message Error",
   2158     0x04: "Hold Timer Expired",
   2159     0x05: "Finite State Machine Error",
   2160     0x06: "Cease",
   2161     0x07: "ROUTE-REFRESH Message Error",  # RFC 7313
   2162 }
   2163 
   2164 #
   2165 # http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-4
   2166 #
   2167 _error_subcodes = {
   2168     # Reserved
   2169     0: {},
   2170 
   2171     # Header (RFC 4271)
   2172     1:
   2173     {
   2174         0: "Unspecific",
   2175         1: "Connection Not Synchronized",
   2176         2: "Bad Message Length",
   2177         3: "Bad Message Type"
   2178     },
   2179 
   2180     # OPEN (RFC 4271, RFC 5492)
   2181     2:
   2182     {
   2183         0: "Reserved",
   2184         1: "Unsupported Version Number",
   2185         2: "Bad Peer AS",
   2186         3: "Bad BGP Identifier",
   2187         4: "Unsupported Optional Parameter",
   2188         5: "Authentication Failure - Deprecated (RFC 4271)",
   2189         6: "Unacceptable Hold Time",
   2190         7: "Unsupported Capability"
   2191     },
   2192 
   2193     # UPDATE (RFC 4271)
   2194     3:
   2195     {
   2196         0: "Reserved",
   2197         1: "Malformed Attribute List",
   2198         2: "Unrecognized Well-known Attribute",
   2199         3: "Missing Well-known Attribute",
   2200         4: "Attribute Flags Error",
   2201         5: "Attribute Length Error",
   2202         6: "Invalid ORIGIN Attribute",
   2203         7: "AS Routing Loop - Deprecated (RFC 4271)",
   2204         8: "Invalid NEXT_HOP Attribute",
   2205         9: "Optional Attribute Error",
   2206         10: "Invalid Network Field",
   2207         11: "Malformed AS_PATH"
   2208     },
   2209 
   2210     # Hold Timer Expired
   2211     4: {},
   2212 
   2213     # Finite State Machine Error (RFC 6608)
   2214     5:
   2215     {
   2216         0: "Unspecified Error",
   2217         1: "Receive Unexpected Message in OpenSent State",
   2218         2: "Receive Unexpected Message in OpenConfirm State",
   2219         3: "Receive Unexpected Message in Established State"
   2220     },
   2221 
   2222     # Cease (RFC 4486)
   2223     6:
   2224     {
   2225         0: "Unspecified Error",
   2226         1: "Maximum Number of Prefixes Reached",
   2227         2: "Administrative Shutdown",
   2228         3: "Peer De-configured",
   2229         4: "Administrative Reset",
   2230         5: "Connection Rejected",
   2231         6: "Other Configuration Change",
   2232         7: "Connection Collision Resolution",
   2233         8: "Out of Resources",
   2234     },
   2235 
   2236     # ROUTE-REFRESH (RFC 7313)
   2237     7:
   2238     {
   2239         0: "Reserved",
   2240         1: "Invalid Message Length"
   2241     },
   2242 }
   2243 
   2244 
   2245 class BGPNotification(BGP):
   2246     """
   2247     NOTIFICATION messages end a BGP session.
   2248     References: RFC 4271
   2249     """
   2250 
   2251     name = "NOTIFICATION"
   2252     fields_desc = [
   2253         ByteEnumField("error_code", 0, _error_codes),
   2254         MultiEnumField(
   2255             "error_subcode",
   2256             0,
   2257             _error_subcodes,
   2258             depends_on=lambda p: p.error_code,
   2259             fmt="B"
   2260         ),
   2261         StrField(name="data", default=None)
   2262     ]
   2263 
   2264 
   2265 #
   2266 # ROUTE_REFRESH
   2267 #
   2268 
   2269 _orf_when_to_refresh = {
   2270     0x01: "IMMEDIATE",
   2271     0x02: "DEFER"
   2272 }
   2273 
   2274 
   2275 _orf_actions = {
   2276     0: "ADD",
   2277     1: "REMOVE",
   2278     2: "REMOVE-ALL"
   2279 }
   2280 
   2281 
   2282 _orf_match = {
   2283     0: "PERMIT",
   2284     1: "DENY"
   2285 }
   2286 
   2287 
   2288 _orf_entry_afi = 1
   2289 _orf_entry_safi = 1
   2290 
   2291 
   2292 def _update_orf_afi_safi(afi, safi):
   2293     """
   2294     Helper function that sets the afi / safi values
   2295     of ORP entries.
   2296     """
   2297 
   2298     global _orf_entry_afi
   2299     global _orf_entry_safi
   2300 
   2301     _orf_entry_afi = afi
   2302     _orf_entry_safi = safi
   2303 
   2304 
   2305 class BGPORFEntry(Packet):
   2306     """
   2307     Provides an implementation of an ORF entry.
   2308     References: RFC 5291
   2309     """
   2310 
   2311     name = "ORF entry"
   2312     fields_desc = [
   2313         BitEnumField("action", 0, 2, _orf_actions),
   2314         BitEnumField("match", 0, 1, _orf_match),
   2315         BitField("reserved", 0, 5),
   2316         StrField("value", "")
   2317     ]
   2318 
   2319 
   2320 class _ORFNLRIPacketField(PacketField):
   2321     """
   2322     PacketField handling the ORF NLRI.
   2323     """
   2324 
   2325     def m2i(self, pkt, m):
   2326         ret = None
   2327 
   2328         if _orf_entry_afi == 1:
   2329             # IPv4
   2330             ret = BGPNLRI_IPv4(m)
   2331 
   2332         elif _orf_entry_afi == 2:
   2333             # IPv6
   2334             ret = BGPNLRI_IPv6(m)
   2335 
   2336         else:
   2337             ret = conf.raw_layer(m)
   2338 
   2339         return ret
   2340 
   2341 
   2342 class BGPORFAddressPrefix(BGPORFEntry):
   2343     """
   2344     Provides an implementation of the Address Prefix ORF (RFC 5292).
   2345     """
   2346 
   2347     name = "Address Prefix ORF"
   2348     fields_desc = [
   2349         BitEnumField("action", 0, 2, _orf_actions),
   2350         BitEnumField("match", 0, 1, _orf_match),
   2351         BitField("reserved", 0, 5),
   2352         IntField("sequence", 0),
   2353         ByteField("min_len", 0),
   2354         ByteField("max_len", 0),
   2355         _ORFNLRIPacketField("prefix", "", Packet),
   2356     ]
   2357 
   2358 
   2359 class BGPORFCoveringPrefix(Packet):
   2360     """
   2361     Provides an implementation of the CP-ORF (RFC 7543).
   2362     """
   2363 
   2364     name = "CP-ORF"
   2365     fields_desc = [
   2366         BitEnumField("action", 0, 2, _orf_actions),
   2367         BitEnumField("match", 0, 1, _orf_match),
   2368         BitField("reserved", 0, 5),
   2369         IntField("sequence", 0),
   2370         ByteField("min_len", 0),
   2371         ByteField("max_len", 0),
   2372         LongField("rt", 0),
   2373         LongField("import_rt", 0),
   2374         ByteField("route_type", 0),
   2375         PacketField("host_addr", None, Packet)
   2376     ]
   2377 
   2378 
   2379 class BGPORFEntryPacketListField(PacketListField):
   2380     """
   2381     PacketListField handling the ORF entries.
   2382     """
   2383 
   2384     def m2i(self, pkt, m):
   2385         ret = None
   2386 
   2387         # Cisco also uses 128
   2388         if pkt.orf_type == 64 or pkt.orf_type == 128:
   2389             ret = BGPORFAddressPrefix(m)
   2390 
   2391         elif pkt.orf_type == 65:
   2392             ret = BGPORFCoveringPrefix(m)
   2393 
   2394         else:
   2395             ret = conf.raw_layer(m)
   2396 
   2397         return ret
   2398 
   2399     def getfield(self, pkt, s):
   2400         lst = []
   2401         length = 0
   2402         if self.length_from is not None:
   2403             length = self.length_from(pkt)
   2404         remain = s
   2405         if length is not None:
   2406             remain, ret = s[:length], s[length:]
   2407 
   2408         while remain:
   2409             orf_len = 0
   2410 
   2411             # Get value length, depending on the ORF type
   2412             if pkt.orf_type == 64 or pkt.orf_type == 128:
   2413                 # Address Prefix ORF
   2414                 # Get the length, in bits, of the prefix
   2415                 prefix_len = _bits_to_bytes_len(
   2416                     orb(remain[6])
   2417                 )
   2418                 # flags (1 byte) + sequence (4 bytes) + min_len (1 byte) +
   2419                 # max_len (1 byte) + mask_len (1 byte) + prefix_len
   2420                 orf_len = 8 + prefix_len
   2421 
   2422             elif pkt.orf_type == 65:
   2423                 # Covering Prefix ORF
   2424 
   2425                 if _orf_entry_afi == 1:
   2426                     # IPv4
   2427                     # sequence (4 bytes) + min_len (1 byte) + max_len (1 byte) +
   2428                     # rt (8 bytes) + import_rt (8 bytes) + route_type (1 byte)
   2429                     orf_len = 23 + 4
   2430 
   2431                 elif _orf_entry_afi == 2:
   2432                     # IPv6
   2433                     # sequence (4 bytes) + min_len (1 byte) + max_len (1 byte) +
   2434                     # rt (8 bytes) + import_rt (8 bytes) + route_type (1 byte)
   2435                     orf_len = 23 + 16
   2436 
   2437                 elif _orf_entry_afi == 25:
   2438                     # sequence (4 bytes) + min_len (1 byte) + max_len (1 byte) +
   2439                     # rt (8 bytes) + import_rt (8 bytes)
   2440                     route_type = orb(remain[22])
   2441 
   2442                     if route_type == 2:
   2443                         # MAC / IP Advertisement Route
   2444                         orf_len = 23 + 6
   2445 
   2446                     else:
   2447                         orf_len = 23
   2448 
   2449             current = remain[:orf_len]
   2450             remain = remain[orf_len:]
   2451             packet = self.m2i(pkt, current)
   2452             lst.append(packet)
   2453 
   2454         return remain + ret, lst
   2455 
   2456 
   2457 class BGPORF(Packet):
   2458     """
   2459     Provides an implementation of ORFs carried in the RR message.
   2460     References: RFC 5291
   2461     """
   2462 
   2463     name = "ORF"
   2464     fields_desc = [
   2465         ByteEnumField("when_to_refresh", 0, _orf_when_to_refresh),
   2466         ByteEnumField("orf_type", 0, _orf_types),
   2467         FieldLenField("orf_len", None, length_of="entries", fmt="!H"),
   2468         BGPORFEntryPacketListField(
   2469             "entries",
   2470             [],
   2471             Packet,
   2472             length_from=lambda p: p.orf_len,
   2473         )
   2474     ]
   2475 
   2476 
   2477 # RFC 7313
   2478 # http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#route-refresh-subcodes
   2479 rr_message_subtypes = {
   2480     0: "Route-Refresh",
   2481     1: "BoRR",
   2482     2: "EoRR",
   2483     255: "Reserved"
   2484 }
   2485 
   2486 
   2487 class BGPRouteRefresh(BGP):
   2488     """
   2489     Provides an implementation of the ROUTE-REFRESH message.
   2490     References: RFC 2918, RFC 7313
   2491     """
   2492 
   2493     name = "ROUTE-REFRESH"
   2494     fields_desc = [
   2495         ShortEnumField("afi", 1, address_family_identifiers),
   2496         ByteEnumField("subtype", 0, rr_message_subtypes),
   2497         ByteEnumField("safi", 1, subsequent_afis),
   2498         PacketField(
   2499             'orf_data',
   2500             "", BGPORF,
   2501             lambda p: _update_orf_afi_safi(p.afi, p.safi)
   2502         )
   2503     ]
   2504 
   2505 
   2506 #
   2507 # Layer bindings
   2508 #
   2509 
   2510 bind_layers(TCP, BGP, dport=179)
   2511 bind_layers(TCP, BGP, sport=179)
   2512 bind_layers(BGPHeader, BGPOpen, {"type": 1})
   2513 bind_layers(BGPHeader, BGPUpdate, {"type": 2})
   2514 bind_layers(BGPHeader, BGPNotification, {"type": 3})
   2515 bind_layers(BGPHeader, BGPKeepAlive, {"type": 4})
   2516 bind_layers(BGPHeader, BGPRouteRefresh, {"type": 5})
   2517 
   2518 # When loading the module, display the current module configuration.
   2519 log_runtime.warning(
   2520     "[bgp.py] use_2_bytes_asn: %s", bgp_module_conf.use_2_bytes_asn)
   2521 
   2522