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