backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / lib / packet / bgp.py
1 # Copyright (C) 2013,2014 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2013,2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 #    http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 """
18 RFC 4271 BGP-4
19 """
20
21 # todo
22 # - notify data
23 # - RFC 4364 BGP/MPLS IP Virtual Private Networks (VPNs)
24
25 import abc
26 import base64
27 import collections
28 import copy
29 import functools
30 import io
31 import itertools
32 import math
33 import re
34 import socket
35 import struct
36
37 import netaddr
38 import six
39
40 from ryu.lib.stringify import StringifyMixin
41 from ryu.lib.packet import afi as addr_family
42 from ryu.lib.packet import safi as subaddr_family
43 from ryu.lib.packet import packet_base
44 from ryu.lib.packet import stream_parser
45 from ryu.lib.packet import vxlan
46 from ryu.lib.packet import mpls
47 from ryu.lib import addrconv
48 from ryu.lib import type_desc
49 from ryu.lib.type_desc import TypeDisp
50 from ryu.lib import ip
51 from ryu.lib.pack_utils import msg_pack_into
52 from ryu.utils import binary_str
53 from ryu.utils import import_module
54
55 reduce = six.moves.reduce
56
57 TCP_SERVER_PORT = 179
58
59 BGP_MSG_OPEN = 1
60 BGP_MSG_UPDATE = 2
61 BGP_MSG_NOTIFICATION = 3
62 BGP_MSG_KEEPALIVE = 4
63 BGP_MSG_ROUTE_REFRESH = 5  # RFC 2918
64
65 _VERSION = 4
66 _MARKER = 16 * b'\xff'
67
68 BGP_OPT_CAPABILITY = 2  # RFC 5492
69
70 BGP_CAP_MULTIPROTOCOL = 1  # RFC 4760
71 BGP_CAP_ROUTE_REFRESH = 2  # RFC 2918
72 BGP_CAP_CARRYING_LABEL_INFO = 4  # RFC 3107
73 BGP_CAP_GRACEFUL_RESTART = 64  # RFC 4724
74 BGP_CAP_FOUR_OCTET_AS_NUMBER = 65  # RFC 4893
75 BGP_CAP_ENHANCED_ROUTE_REFRESH = 70  # https://tools.ietf.org/html/\
76 # draft-ietf-idr-bgp-enhanced-route-refresh-05
77 BGP_CAP_ROUTE_REFRESH_CISCO = 128  # in cisco routers, there are two\
78 # route refresh code: one using the capability code of 128 (old),
79 # another using the capability code of 2 (new).
80
81 BGP_ATTR_FLAG_OPTIONAL = 1 << 7
82 BGP_ATTR_FLAG_TRANSITIVE = 1 << 6
83 BGP_ATTR_FLAG_PARTIAL = 1 << 5
84 BGP_ATTR_FLAG_EXTENDED_LENGTH = 1 << 4
85
86 BGP_ATTR_TYPE_ORIGIN = 1  # 0,1,2 (1 byte)
87 BGP_ATTR_TYPE_AS_PATH = 2  # a list of AS_SET/AS_SEQUENCE  eg. {1 2 3} 4 5
88 BGP_ATTR_TYPE_NEXT_HOP = 3  # an IPv4 address
89 BGP_ATTR_TYPE_MULTI_EXIT_DISC = 4  # uint32 metric
90 BGP_ATTR_TYPE_LOCAL_PREF = 5  # uint32
91 BGP_ATTR_TYPE_ATOMIC_AGGREGATE = 6  # 0 bytes
92 BGP_ATTR_TYPE_AGGREGATOR = 7  # AS number and IPv4 address
93 BGP_ATTR_TYPE_COMMUNITIES = 8  # RFC 1997
94 BGP_ATTR_TYPE_ORIGINATOR_ID = 9  # RFC 4456
95 BGP_ATTR_TYPE_CLUSTER_LIST = 10  # RFC 4456
96 BGP_ATTR_TYPE_MP_REACH_NLRI = 14  # RFC 4760
97 BGP_ATTR_TYPE_MP_UNREACH_NLRI = 15  # RFC 4760
98 BGP_ATTR_TYPE_EXTENDED_COMMUNITIES = 16  # RFC 4360
99 BGP_ATTR_TYPE_AS4_PATH = 17  # RFC 4893
100 BGP_ATTR_TYPE_AS4_AGGREGATOR = 18  # RFC 4893
101 BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE = 22  # RFC 6514
102
103 BGP_ATTR_ORIGIN_IGP = 0x00
104 BGP_ATTR_ORIGIN_EGP = 0x01
105 BGP_ATTR_ORIGIN_INCOMPLETE = 0x02
106
107 AS_TRANS = 23456  # RFC 4893
108
109 # Well known commmunities  (RFC 1997)
110 BGP_COMMUNITY_NO_EXPORT = 0xffffff01
111 BGP_COMMUNITY_NO_ADVERTISE = 0xffffff02
112 BGP_COMMUNITY_NO_EXPORT_SUBCONFED = 0xffffff03
113
114 # RFC 4360
115 # The low-order octet of Type field (subtype)
116 BGP_EXTENDED_COMMUNITY_ROUTE_TARGET = 0x02
117 BGP_EXTENDED_COMMUNITY_ROUTE_ORIGIN = 0x03
118
119 # NOTIFICATION Error Code and SubCode
120 # Note: 0 is a valid SubCode.  (Unspecific)
121
122 # NOTIFICATION Error Code  RFC 4271 4.5.
123 BGP_ERROR_MESSAGE_HEADER_ERROR = 1
124 BGP_ERROR_OPEN_MESSAGE_ERROR = 2
125 BGP_ERROR_UPDATE_MESSAGE_ERROR = 3
126 BGP_ERROR_HOLD_TIMER_EXPIRED = 4
127 BGP_ERROR_FSM_ERROR = 5
128 BGP_ERROR_CEASE = 6
129
130 # NOTIFICATION Error Subcode for BGP_ERROR_MESSAGE_HEADER_ERROR
131 BGP_ERROR_SUB_CONNECTION_NOT_SYNCHRONIZED = 1
132 BGP_ERROR_SUB_BAD_MESSAGE_LENGTH = 2  # Data: the erroneous Length field
133 BGP_ERROR_SUB_BAD_MESSAGE_TYPE = 3  # Data: the erroneous Type field
134
135 # NOTIFICATION Error Subcode for BGP_ERROR_OPEN_MESSAGE_ERROR
136 BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER = 1  # Data: 2 octet version number
137 BGP_ERROR_SUB_BAD_PEER_AS = 2
138 BGP_ERROR_SUB_BAD_BGP_IDENTIFIER = 3
139 BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER = 4
140 BGP_ERROR_SUB_AUTHENTICATION_FAILURE = 5  # deprecated RFC 1771
141 BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME = 6
142
143 # NOTIFICATION Error Subcode for BGP_ERROR_UPDATE_MESSAGE_ERROR
144 BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST = 1
145 BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE = 2  # Data: type of the attr
146 BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE = 3  # Data: ditto
147 BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR = 4  # Data: the attr (type, len, value)
148 BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR = 5  # Data: ditto
149 BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE = 6  # Data: ditto
150 BGP_ERROR_SUB_ROUTING_LOOP = 7  # deprecated RFC 1771 AS Routing Loop
151 BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE = 8  # Data: ditto
152 BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR = 9  # Data: ditto
153 BGP_ERROR_SUB_INVALID_NETWORK_FIELD = 10
154 BGP_ERROR_SUB_MALFORMED_AS_PATH = 11
155
156 # NOTIFICATION Error Subcode for BGP_ERROR_HOLD_TIMER_EXPIRED
157 BGP_ERROR_SUB_HOLD_TIMER_EXPIRED = 1
158
159 # NOTIFICATION Error Subcode for BGP_ERROR_FSM_ERROR
160 BGP_ERROR_SUB_FSM_ERROR = 1
161
162 # NOTIFICATION Error Subcode for BGP_ERROR_CEASE  (RFC 4486)
163 BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED = 1  # Data: optional
164 BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN = 2
165 BGP_ERROR_SUB_PEER_DECONFIGURED = 3
166 BGP_ERROR_SUB_ADMINISTRATIVE_RESET = 4
167 BGP_ERROR_SUB_CONNECTION_RESET = 5
168 BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE = 6
169 BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION = 7
170 BGP_ERROR_SUB_OUT_OF_RESOURCES = 8
171
172
173 class _Value(object):
174     _VALUE_PACK_STR = None
175     _VALUE_FIELDS = ['value']
176
177     @staticmethod
178     def do_init(cls_type, self, kwargs, **extra_kwargs):
179         ourfields = {}
180         for f in cls_type._VALUE_FIELDS:
181             v = kwargs[f]
182             del kwargs[f]
183             ourfields[f] = v
184         kwargs.update(extra_kwargs)
185         super(cls_type, self).__init__(**kwargs)
186         self.__dict__.update(ourfields)
187
188     @classmethod
189     def parse_value(cls, buf):
190         values = struct.unpack_from(cls._VALUE_PACK_STR, six.binary_type(buf))
191         return dict(zip(cls._VALUE_FIELDS, values))
192
193     def serialize_value(self):
194         args = []
195         for f in self._VALUE_FIELDS:
196             args.append(getattr(self, f))
197         return struct.pack(self._VALUE_PACK_STR, *args)
198
199
200 class BgpExc(Exception):
201     """Base bgp exception."""
202
203     CODE = 0
204     """BGP error code."""
205
206     SUB_CODE = 0
207     """BGP error sub-code."""
208
209     SEND_ERROR = True
210     """Flag if set indicates Notification message should be sent to peer."""
211
212     def __init__(self, data=''):
213         super(BgpExc, self).__init__()
214         self.data = data
215
216     def __str__(self):
217         return '<%s %r>' % (self.__class__.__name__, self.data)
218
219
220 class BadNotification(BgpExc):
221     SEND_ERROR = False
222
223 # ============================================================================
224 # Message Header Errors
225 # ============================================================================
226
227
228 class NotSync(BgpExc):
229     CODE = BGP_ERROR_MESSAGE_HEADER_ERROR
230     SUB_CODE = BGP_ERROR_SUB_CONNECTION_NOT_SYNCHRONIZED
231
232
233 class BadLen(BgpExc):
234     CODE = BGP_ERROR_MESSAGE_HEADER_ERROR
235     SUB_CODE = BGP_ERROR_SUB_BAD_MESSAGE_LENGTH
236
237     def __init__(self, msg_type_code, message_length):
238         super(BadLen, self).__init__()
239         self.msg_type_code = msg_type_code
240         self.length = message_length
241         self.data = struct.pack('!H', self.length)
242
243     def __str__(self):
244         return '<BadLen %d msgtype=%d>' % (self.length, self.msg_type_code)
245
246
247 class BadMsg(BgpExc):
248     """Error to indicate un-recognized message type.
249
250     RFC says: If the Type field of the message header is not recognized, then
251     the Error Subcode MUST be set to Bad Message Type.  The Data field MUST
252     contain the erroneous Type field.
253     """
254     CODE = BGP_ERROR_MESSAGE_HEADER_ERROR
255     SUB_CODE = BGP_ERROR_SUB_BAD_MESSAGE_TYPE
256
257     def __init__(self, msg_type):
258         super(BadMsg, self).__init__()
259         self.msg_type = msg_type
260         self.data = struct.pack('B', msg_type)
261
262     def __str__(self):
263         return '<BadMsg %d>' % (self.msg_type,)
264
265 # ============================================================================
266 # OPEN Message Errors
267 # ============================================================================
268
269
270 class MalformedOptionalParam(BgpExc):
271     """If recognized optional parameters are malformed.
272
273     RFC says: If one of the Optional Parameters in the OPEN message is
274     recognized, but is malformed, then the Error Subcode MUST be set to 0
275     (Unspecific).
276     """
277     CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
278     SUB_CODE = 0
279
280
281 class UnsupportedVersion(BgpExc):
282     """Error to indicate unsupport bgp version number.
283
284     RFC says: If the version number in the Version field of the received OPEN
285     message is not supported, then the Error Subcode MUST be set to Unsupported
286     Version Number.  The Data field is a 2-octet unsigned integer, which
287     indicates the largest, locally-supported version number less than the
288     version the remote BGP peer bid (as indicated in the received OPEN
289     message), or if the smallest, locally-supported version number is greater
290     than the version the remote BGP peer bid, then the smallest, locally-
291     supported version number.
292     """
293     CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
294     SUB_CODE = BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER
295
296     def __init__(self, locally_support_version):
297         super(UnsupportedVersion, self).__init__()
298         self.data = struct.pack('H', locally_support_version)
299
300
301 class BadPeerAs(BgpExc):
302     """Error to indicate open message has incorrect AS number.
303
304     RFC says: If the Autonomous System field of the OPEN message is
305     unacceptable, then the Error Subcode MUST be set to Bad Peer AS.  The
306     determination of acceptable Autonomous System numbers is configure peer AS.
307     """
308     CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
309     SUB_CODE = BGP_ERROR_SUB_BAD_PEER_AS
310
311
312 class BadBgpId(BgpExc):
313     """Error to indicate incorrect BGP Identifier.
314
315     RFC says: If the BGP Identifier field of the OPEN message is syntactically
316     incorrect, then the Error Subcode MUST be set to Bad BGP Identifier.
317     Syntactic correctness means that the BGP Identifier field represents a
318     valid unicast IP host address.
319     """
320     CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
321     SUB_CODE = BGP_ERROR_SUB_BAD_BGP_IDENTIFIER
322
323
324 class UnsupportedOptParam(BgpExc):
325     """Error to indicate unsupported optional parameters.
326
327     RFC says: If one of the Optional Parameters in the OPEN message is not
328     recognized, then the Error Subcode MUST be set to Unsupported Optional
329     Parameters.
330     """
331     CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
332     SUB_CODE = BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER
333
334
335 class AuthFailure(BgpExc):
336     CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
337     SUB_CODE = BGP_ERROR_SUB_AUTHENTICATION_FAILURE
338
339
340 class UnacceptableHoldTime(BgpExc):
341     """Error to indicate Unacceptable Hold Time in open message.
342
343     RFC says: If the Hold Time field of the OPEN message is unacceptable, then
344     the Error Subcode MUST be set to Unacceptable Hold Time.
345     """
346     CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
347     SUB_CODE = BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME
348
349 # ============================================================================
350 # UPDATE message related errors
351 # ============================================================================
352
353
354 class MalformedAttrList(BgpExc):
355     """Error to indicate UPDATE message is malformed.
356
357     RFC says: Error checking of an UPDATE message begins by examining the path
358     attributes.  If the Withdrawn Routes Length or Total Attribute Length is
359     too large (i.e., if Withdrawn Routes Length + Total Attribute Length + 23
360     exceeds the message Length), then the Error Subcode MUST be set to
361     Malformed Attribute List.
362     """
363     CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
364     SUB_CODE = BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST
365
366
367 class UnRegWellKnowAttr(BgpExc):
368     CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
369     SUB_CODE = BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE
370
371
372 class MissingWellKnown(BgpExc):
373     """Error to indicate missing well-known attribute.
374
375     RFC says: If any of the well-known mandatory attributes are not present,
376     then the Error Subcode MUST be set to Missing Well-known Attribute.  The
377     Data field MUST contain the Attribute Type Code of the missing, well-known
378     attribute.
379     """
380     CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
381     SUB_CODE = BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE
382
383     def __init__(self, pattr_type_code):
384         super(MissingWellKnown, self).__init__()
385         self.pattr_type_code = pattr_type_code
386         self.data = struct.pack('B', pattr_type_code)
387
388
389 class AttrFlagError(BgpExc):
390     """Error to indicate recognized path attributes have incorrect flags.
391
392     RFC says: If any recognized attribute has Attribute Flags that conflict
393     with the Attribute Type Code, then the Error Subcode MUST be set to
394     Attribute Flags Error.  The Data field MUST contain the erroneous attribute
395     (type, length, and value).
396     """
397     CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
398     SUB_CODE = BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR
399
400
401 class AttrLenError(BgpExc):
402     CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
403     SUB_CODE = BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR
404
405
406 class InvalidOriginError(BgpExc):
407     """Error indicates undefined Origin attribute value.
408
409     RFC says: If the ORIGIN attribute has an undefined value, then the Error
410     Sub- code MUST be set to Invalid Origin Attribute.  The Data field MUST
411     contain the unrecognized attribute (type, length, and value).
412     """
413     CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
414     SUB_CODE = BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE
415
416
417 class RoutingLoop(BgpExc):
418     CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
419     SUB_CODE = BGP_ERROR_SUB_ROUTING_LOOP
420
421
422 class InvalidNextHop(BgpExc):
423     CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
424     SUB_CODE = BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE
425
426
427 class OptAttrError(BgpExc):
428     """Error indicates Optional Attribute is malformed.
429
430     RFC says: If an optional attribute is recognized, then the value of this
431     attribute MUST be checked.  If an error is detected, the attribute MUST be
432     discarded, and the Error Subcode MUST be set to Optional Attribute Error.
433     The Data field MUST contain the attribute (type, length, and value).
434     """
435     CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
436     SUB_CODE = BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR
437
438
439 class InvalidNetworkField(BgpExc):
440     CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
441     SUB_CODE = BGP_ERROR_SUB_INVALID_NETWORK_FIELD
442
443
444 class MalformedAsPath(BgpExc):
445     """Error to indicate if AP_PATH attribute is syntactically incorrect.
446
447     RFC says: The AS_PATH attribute is checked for syntactic correctness.  If
448     the path is syntactically incorrect, then the Error Subcode MUST be set to
449     Malformed AS_PATH.
450     """
451     CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
452     SUB_CODE = BGP_ERROR_SUB_MALFORMED_AS_PATH
453
454
455 # ============================================================================
456 # Hold Timer Expired
457 # ============================================================================
458
459
460 class HoldTimerExpired(BgpExc):
461     """Error to indicate Hold Timer expired.
462
463     RFC says: If a system does not receive successive KEEPALIVE, UPDATE, and/or
464     NOTIFICATION messages within the period specified in the Hold Time field of
465     the OPEN message, then the NOTIFICATION message with the Hold Timer Expired
466     Error Code is sent and the BGP connection is closed.
467     """
468     CODE = BGP_ERROR_HOLD_TIMER_EXPIRED
469     SUB_CODE = BGP_ERROR_SUB_HOLD_TIMER_EXPIRED
470
471 # ============================================================================
472 # Finite State Machine Error
473 # ============================================================================
474
475
476 class FiniteStateMachineError(BgpExc):
477     """Error to indicate any Finite State Machine Error.
478
479     RFC says: Any error detected by the BGP Finite State Machine (e.g., receipt
480     of an unexpected event) is indicated by sending the NOTIFICATION message
481     with the Error Code Finite State Machine Error.
482     """
483     CODE = BGP_ERROR_FSM_ERROR
484     SUB_CODE = BGP_ERROR_SUB_FSM_ERROR
485
486
487 # ============================================================================
488 # Cease Errors
489 # ============================================================================
490
491 class MaxPrefixReached(BgpExc):
492     CODE = BGP_ERROR_CEASE
493     SUB_CODE = BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED
494
495
496 class AdminShutdown(BgpExc):
497     """Error to indicate Administrative shutdown.
498
499     RFC says: If a BGP speaker decides to administratively shut down its
500     peering with a neighbor, then the speaker SHOULD send a NOTIFICATION
501     message  with the Error Code Cease and the Error Subcode 'Administrative
502     Shutdown'.
503     """
504     CODE = BGP_ERROR_CEASE
505     SUB_CODE = BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN
506
507
508 class PeerDeConfig(BgpExc):
509     CODE = BGP_ERROR_CEASE
510     SUB_CODE = BGP_ERROR_SUB_PEER_DECONFIGURED
511
512
513 class AdminReset(BgpExc):
514     CODE = BGP_ERROR_CEASE
515     SUB_CODE = BGP_ERROR_SUB_ADMINISTRATIVE_RESET
516
517
518 class ConnRejected(BgpExc):
519     """Error to indicate Connection Rejected.
520
521     RFC says: If a BGP speaker decides to disallow a BGP connection (e.g., the
522     peer is not configured locally) after the speaker accepts a transport
523     protocol connection, then the BGP speaker SHOULD send a NOTIFICATION
524     message with the Error Code Cease and the Error Subcode "Connection
525     Rejected".
526     """
527     CODE = BGP_ERROR_CEASE
528     SUB_CODE = BGP_ERROR_SUB_CONNECTION_RESET
529
530
531 class OtherConfChange(BgpExc):
532     CODE = BGP_ERROR_CEASE
533     SUB_CODE = BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE
534
535
536 class CollisionResolution(BgpExc):
537     """Error to indicate Connection Collision Resolution.
538
539     RFC says: If a BGP speaker decides to send a NOTIFICATION message with the
540     Error Code Cease as a result of the collision resolution procedure (as
541     described in [BGP-4]), then the subcode SHOULD be set to "Connection
542     Collision Resolution".
543     """
544     CODE = BGP_ERROR_CEASE
545     SUB_CODE = BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION
546
547
548 class OutOfResource(BgpExc):
549     CODE = BGP_ERROR_CEASE
550     SUB_CODE = BGP_ERROR_SUB_OUT_OF_RESOURCES
551
552
553 @functools.total_ordering
554 class RouteFamily(StringifyMixin):
555     def __init__(self, afi, safi):
556         self.afi = afi
557         self.safi = safi
558
559     def __lt__(self, other):
560         return (self.afi, self.safi) < (other.afi, other.safi)
561
562     def __eq__(self, other):
563         return (self.afi, self.safi) == (other.afi, other.safi)
564
565     def __hash__(self):
566         return hash((self.afi, self.safi))
567
568
569 # Route Family Singleton
570 RF_IPv4_UC = RouteFamily(addr_family.IP, subaddr_family.UNICAST)
571 RF_IPv6_UC = RouteFamily(addr_family.IP6, subaddr_family.UNICAST)
572 RF_IPv4_VPN = RouteFamily(addr_family.IP, subaddr_family.MPLS_VPN)
573 RF_IPv6_VPN = RouteFamily(addr_family.IP6, subaddr_family.MPLS_VPN)
574 RF_IPv4_MPLS = RouteFamily(addr_family.IP, subaddr_family.MPLS_LABEL)
575 RF_IPv6_MPLS = RouteFamily(addr_family.IP6, subaddr_family.MPLS_LABEL)
576 RF_L2_EVPN = RouteFamily(addr_family.L2VPN, subaddr_family.EVPN)
577 RF_IPv4_FLOWSPEC = RouteFamily(addr_family.IP, subaddr_family.IP_FLOWSPEC)
578 RF_IPv6_FLOWSPEC = RouteFamily(addr_family.IP6, subaddr_family.IP_FLOWSPEC)
579 RF_VPNv4_FLOWSPEC = RouteFamily(addr_family.IP, subaddr_family.VPN_FLOWSPEC)
580 RF_VPNv6_FLOWSPEC = RouteFamily(addr_family.IP6, subaddr_family.VPN_FLOWSPEC)
581 RF_L2VPN_FLOWSPEC = RouteFamily(
582     addr_family.L2VPN, subaddr_family.VPN_FLOWSPEC)
583 RF_RTC_UC = RouteFamily(addr_family.IP,
584                         subaddr_family.ROUTE_TARGET_CONSTRAINTS)
585
586 _rf_map = {
587     (addr_family.IP, subaddr_family.UNICAST): RF_IPv4_UC,
588     (addr_family.IP6, subaddr_family.UNICAST): RF_IPv6_UC,
589     (addr_family.IP, subaddr_family.MPLS_VPN): RF_IPv4_VPN,
590     (addr_family.IP6, subaddr_family.MPLS_VPN): RF_IPv6_VPN,
591     (addr_family.IP, subaddr_family.MPLS_LABEL): RF_IPv4_MPLS,
592     (addr_family.IP6, subaddr_family.MPLS_LABEL): RF_IPv6_MPLS,
593     (addr_family.L2VPN, subaddr_family.EVPN): RF_L2_EVPN,
594     (addr_family.IP, subaddr_family.IP_FLOWSPEC): RF_IPv4_FLOWSPEC,
595     (addr_family.IP6, subaddr_family.IP_FLOWSPEC): RF_IPv6_FLOWSPEC,
596     (addr_family.IP, subaddr_family.VPN_FLOWSPEC): RF_VPNv4_FLOWSPEC,
597     (addr_family.IP6, subaddr_family.VPN_FLOWSPEC): RF_VPNv6_FLOWSPEC,
598     (addr_family.L2VPN, subaddr_family.VPN_FLOWSPEC): RF_L2VPN_FLOWSPEC,
599     (addr_family.IP, subaddr_family.ROUTE_TARGET_CONSTRAINTS): RF_RTC_UC
600 }
601
602
603 def get_rf(afi, safi):
604     return _rf_map[(afi, safi)]
605
606
607 def pad(binary, len_):
608     assert len(binary) <= len_
609     return binary + b'\0' * (len_ - len(binary))
610
611
612 class _RouteDistinguisher(StringifyMixin, TypeDisp, _Value):
613     _PACK_STR = '!H'
614     TWO_OCTET_AS = 0
615     IPV4_ADDRESS = 1
616     FOUR_OCTET_AS = 2
617
618     def __init__(self, admin=0, assigned=0, type_=None):
619         if type_ is None:
620             type_ = self._rev_lookup_type(self.__class__)
621         self.type = type_
622         self.admin = admin
623         self.assigned = assigned
624
625     @classmethod
626     def parser(cls, buf):
627         assert len(buf) == 8
628         (type_,) = struct.unpack_from(cls._PACK_STR, six.binary_type(buf))
629         rest = buf[struct.calcsize(cls._PACK_STR):]
630         subcls = cls._lookup_type(type_)
631         return subcls(**subcls.parse_value(rest))
632
633     @classmethod
634     def from_str(cls, str_):
635         assert isinstance(str_, str)
636
637         first, second = str_.split(':')
638         if '.' in first:
639             type_ = cls.IPV4_ADDRESS
640         elif int(first) > (1 << 16):
641             type_ = cls.FOUR_OCTET_AS
642             first = int(first)
643         else:
644             type_ = cls.TWO_OCTET_AS
645             first = int(first)
646         subcls = cls._lookup_type(type_)
647         return subcls(admin=first, assigned=int(second))
648
649     def serialize(self):
650         value = self.serialize_value()
651         buf = bytearray()
652         msg_pack_into(self._PACK_STR, buf, 0, self.type)
653         return six.binary_type(buf + value)
654
655     @property
656     def formatted_str(self):
657         return "%s:%s" % (self.admin, self.assigned)
658
659
660 @_RouteDistinguisher.register_type(_RouteDistinguisher.TWO_OCTET_AS)
661 class BGPTwoOctetAsRD(_RouteDistinguisher):
662     _VALUE_PACK_STR = '!HI'
663     _VALUE_FIELDS = ['admin', 'assigned']
664
665     def __init__(self, **kwargs):
666         super(BGPTwoOctetAsRD, self).__init__()
667         self.do_init(BGPTwoOctetAsRD, self, kwargs)
668
669
670 @_RouteDistinguisher.register_type(_RouteDistinguisher.IPV4_ADDRESS)
671 class BGPIPv4AddressRD(_RouteDistinguisher):
672     _VALUE_PACK_STR = '!4sH'
673     _VALUE_FIELDS = ['admin', 'assigned']
674     _TYPE = {
675         'ascii': [
676             'admin'
677         ]
678     }
679
680     def __init__(self, **kwargs):
681         super(BGPIPv4AddressRD, self).__init__()
682         self.do_init(BGPIPv4AddressRD, self, kwargs)
683
684     @classmethod
685     def parse_value(cls, buf):
686         d_ = super(BGPIPv4AddressRD, cls).parse_value(buf)
687         d_['admin'] = addrconv.ipv4.bin_to_text(d_['admin'])
688         return d_
689
690     def serialize_value(self):
691         args = []
692         for f in self._VALUE_FIELDS:
693             v = getattr(self, f)
694             if f == 'admin':
695                 v = bytes(addrconv.ipv4.text_to_bin(v))
696             args.append(v)
697         buf = bytearray()
698         msg_pack_into(self._VALUE_PACK_STR, buf, 0, *args)
699         return buf
700
701
702 @_RouteDistinguisher.register_type(_RouteDistinguisher.FOUR_OCTET_AS)
703 class BGPFourOctetAsRD(_RouteDistinguisher):
704     _VALUE_PACK_STR = '!IH'
705     _VALUE_FIELDS = ['admin', 'assigned']
706
707     def __init__(self, **kwargs):
708         super(BGPFourOctetAsRD, self).__init__()
709         self.do_init(BGPFourOctetAsRD, self, kwargs)
710
711
712 @six.add_metaclass(abc.ABCMeta)
713 class _AddrPrefix(StringifyMixin):
714     _PACK_STR = '!B'  # length
715
716     def __init__(self, length, addr, prefixes=None, **kwargs):
717         # length is on-wire bit length of prefixes+addr.
718         assert prefixes != ()
719         if isinstance(addr, tuple):
720             # for _AddrPrefix.parser
721             # also for _VPNAddrPrefix.__init__ etc
722             (addr,) = addr
723         self.length = length
724         if prefixes:
725             addr = prefixes + (addr,)
726         self.addr = addr
727
728     @classmethod
729     @abc.abstractmethod
730     def _to_bin(cls, addr):
731         pass
732
733     @classmethod
734     @abc.abstractmethod
735     def _from_bin(cls, addr):
736         pass
737
738     @classmethod
739     def parser(cls, buf):
740         (length, ) = struct.unpack_from(cls._PACK_STR, six.binary_type(buf))
741         rest = buf[struct.calcsize(cls._PACK_STR):]
742         byte_length = (length + 7) // 8
743         addr = cls._from_bin(rest[:byte_length])
744         rest = rest[byte_length:]
745         return cls(length=length, addr=addr), rest
746
747     def serialize(self):
748         # fixup
749         byte_length = (self.length + 7) // 8
750         bin_addr = self._to_bin(self.addr)
751         if (self.length % 8) == 0:
752             bin_addr = bin_addr[:byte_length]
753         else:
754             # clear trailing bits in the last octet.
755             # rfc doesn't require this.
756             mask = 0xff00 >> (self.length % 8)
757             last_byte = six.int2byte(
758                 six.indexbytes(bin_addr, byte_length - 1) & mask)
759             bin_addr = bin_addr[:byte_length - 1] + last_byte
760         self.addr = self._from_bin(bin_addr)
761
762         buf = bytearray()
763         msg_pack_into(self._PACK_STR, buf, 0, self.length)
764         return buf + bytes(bin_addr)
765
766
767 class _BinAddrPrefix(_AddrPrefix):
768     @classmethod
769     def _to_bin(cls, addr):
770         return addr
771
772     @classmethod
773     def _from_bin(cls, addr):
774         return addr
775
776
777 class _LabelledAddrPrefix(_AddrPrefix):
778     _LABEL_PACK_STR = '!3B'
779     # RFC3107
780     # 3. Carrying Label Mapping Information
781     # The label information carried (as part of NLRI) in the Withdrawn
782     # Routes field should be set to 0x800000.  (Of course, terminating the
783     # BGP session also withdraws all the previously advertised routes.)
784     #
785     _WITHDRAW_LABEL = 0x800000
786
787     def __init__(self, length, addr, labels=None, **kwargs):
788         labels = labels if labels else []
789         assert isinstance(labels, list)
790         is_tuple = isinstance(addr, tuple)
791         if is_tuple:
792             # for _AddrPrefix.parser
793             assert not labels
794             labels = addr[0]
795             addr = addr[1:]
796         else:
797             length += struct.calcsize(self._LABEL_PACK_STR) * 8 * len(labels)
798         assert length > struct.calcsize(self._LABEL_PACK_STR) * 8 * len(labels)
799         prefixes = (labels,)
800         super(_LabelledAddrPrefix, self).__init__(prefixes=prefixes,
801                                                   length=length,
802                                                   addr=addr,
803                                                   **kwargs)
804
805     @classmethod
806     def _label_to_bin(cls, label):
807         buf = bytearray()
808         msg_pack_into(cls._LABEL_PACK_STR, buf, 0,
809                       (label & 0xff0000) >> 16,
810                       (label & 0x00ff00) >> 8,
811                       (label & 0x0000ff) >> 0)
812         return six.binary_type(buf)
813
814     @classmethod
815     def _label_from_bin(cls, label):
816         (b1, b2, b3) = struct.unpack_from(cls._LABEL_PACK_STR,
817                                           six.binary_type(label))
818         rest = label[struct.calcsize(cls._LABEL_PACK_STR):]
819         return (b1 << 16) | (b2 << 8) | b3, rest
820
821     @classmethod
822     def _to_bin(cls, addr):
823         labels = addr[0]
824         rest = addr[1:]
825         labels = [x << 4 for x in labels]
826         if labels and labels[-1] != cls._WITHDRAW_LABEL:
827             labels[-1] |= 1  # bottom of stack
828         bin_labels = list(cls._label_to_bin(l) for l in labels)
829         return bytes(reduce(lambda x, y: x + y, bin_labels,
830                             bytearray()) + cls._prefix_to_bin(rest))
831
832     @classmethod
833     def _has_no_label(cls, bin_):
834         try:
835             length = len(bin_)
836             labels = []
837             while True:
838                 (label, bin_) = cls._label_from_bin(bin_)
839                 labels.append(label)
840                 if label & 1 or label == cls._WITHDRAW_LABEL:
841                     break
842             assert length > struct.calcsize(cls._LABEL_PACK_STR) * len(labels)
843         except struct.error:
844             return True
845         except AssertionError:
846             return True
847         return False
848
849     @classmethod
850     def _from_bin(cls, addr):
851         rest = addr
852         labels = []
853
854         if cls._has_no_label(rest):
855             return ([],) + cls._prefix_from_bin(rest)
856
857         while True:
858             (label, rest) = cls._label_from_bin(rest)
859             labels.append(label >> 4)
860             if label & 1 or label == cls._WITHDRAW_LABEL:
861                 break
862         return (labels,) + cls._prefix_from_bin(rest)
863
864
865 class _UnlabelledAddrPrefix(_AddrPrefix):
866     @classmethod
867     def _to_bin(cls, addr):
868         return cls._prefix_to_bin((addr,))
869
870     @classmethod
871     def _from_bin(cls, binaddr):
872         (addr,) = cls._prefix_from_bin(binaddr)
873         return addr
874
875
876 class _IPAddrPrefix(_AddrPrefix):
877     @staticmethod
878     def _prefix_to_bin(addr):
879         (addr,) = addr
880         return addrconv.ipv4.text_to_bin(addr)
881
882     @staticmethod
883     def _prefix_from_bin(addr):
884         return addrconv.ipv4.bin_to_text(pad(addr, 4)),
885
886
887 class _IP6AddrPrefix(_AddrPrefix):
888     @staticmethod
889     def _prefix_to_bin(addr):
890         (addr,) = addr
891         return addrconv.ipv6.text_to_bin(addr)
892
893     @staticmethod
894     def _prefix_from_bin(addr):
895         return addrconv.ipv6.bin_to_text(pad(addr, 16)),
896
897
898 class _VPNAddrPrefix(_AddrPrefix):
899     _RD_PACK_STR = '!Q'
900
901     def __init__(self, length, addr, prefixes=(), route_dist=0):
902         if isinstance(addr, tuple):
903             # for _AddrPrefix.parser
904             assert not route_dist
905             assert length > struct.calcsize(self._RD_PACK_STR) * 8
906             route_dist = addr[0]
907             addr = addr[1:]
908         else:
909             length += struct.calcsize(self._RD_PACK_STR) * 8
910
911         if isinstance(route_dist, str):
912             route_dist = _RouteDistinguisher.from_str(route_dist)
913
914         prefixes = prefixes + (route_dist,)
915         super(_VPNAddrPrefix, self).__init__(prefixes=prefixes,
916                                              length=length,
917                                              addr=addr)
918
919     @classmethod
920     def _prefix_to_bin(cls, addr):
921         rd = addr[0]
922         rest = addr[1:]
923         binrd = rd.serialize()
924         return binrd + super(_VPNAddrPrefix, cls)._prefix_to_bin(rest)
925
926     @classmethod
927     def _prefix_from_bin(cls, binaddr):
928         binrd = binaddr[:8]
929         binrest = binaddr[8:]
930         rd = _RouteDistinguisher.parser(binrd)
931         return (rd,) + super(_VPNAddrPrefix, cls)._prefix_from_bin(binrest)
932
933
934 class IPAddrPrefix(_UnlabelledAddrPrefix, _IPAddrPrefix):
935     ROUTE_FAMILY = RF_IPv4_UC
936     _TYPE = {
937         'ascii': [
938             'addr'
939         ]
940     }
941
942     @property
943     def prefix(self):
944         return self.addr + '/{0}'.format(self.length)
945
946     @property
947     def formatted_nlri_str(self):
948         return self.prefix
949
950
951 class IP6AddrPrefix(_UnlabelledAddrPrefix, _IP6AddrPrefix):
952     ROUTE_FAMILY = RF_IPv6_UC
953     _TYPE = {
954         'ascii': [
955             'addr'
956         ]
957     }
958
959     @property
960     def prefix(self):
961         return self.addr + '/{0}'.format(self.length)
962
963     @property
964     def formatted_nlri_str(self):
965         return self.prefix
966
967
968 class LabelledIPAddrPrefix(_LabelledAddrPrefix, _IPAddrPrefix):
969     ROUTE_FAMILY = RF_IPv4_MPLS
970
971
972 class LabelledIP6AddrPrefix(_LabelledAddrPrefix, _IP6AddrPrefix):
973     ROUTE_FAMILY = RF_IPv6_MPLS
974
975
976 class LabelledVPNIPAddrPrefix(_LabelledAddrPrefix, _VPNAddrPrefix,
977                               _IPAddrPrefix):
978     ROUTE_FAMILY = RF_IPv4_VPN
979
980     @property
981     def prefix(self):
982         masklen = self.length - struct.calcsize(self._RD_PACK_STR) * 8 \
983             - struct.calcsize(self._LABEL_PACK_STR) * 8 * len(self.addr[:-2])
984         return self.addr[-1] + '/{0}'.format(masklen)
985
986     @property
987     def route_dist(self):
988         return self.addr[-2].formatted_str
989
990     @property
991     def label_list(self):
992         return self.addr[0]
993
994     @property
995     def formatted_nlri_str(self):
996         return "%s:%s" % (self.route_dist, self.prefix)
997
998
999 class LabelledVPNIP6AddrPrefix(_LabelledAddrPrefix, _VPNAddrPrefix,
1000                                _IP6AddrPrefix):
1001     ROUTE_FAMILY = RF_IPv6_VPN
1002
1003     @property
1004     def prefix(self):
1005         masklen = self.length - struct.calcsize(self._RD_PACK_STR) * 8 \
1006             - struct.calcsize(self._LABEL_PACK_STR) * 8 * len(self.addr[:-2])
1007         return self.addr[-1] + '/{0}'.format(masklen)
1008
1009     @property
1010     def route_dist(self):
1011         return self.addr[-2].formatted_str
1012
1013     @property
1014     def label_list(self):
1015         return self.addr[0]
1016
1017     @property
1018     def formatted_nlri_str(self):
1019         return "%s:%s" % (self.route_dist, self.prefix)
1020
1021
1022 class EvpnEsi(StringifyMixin, TypeDisp, _Value):
1023     """
1024     Ethernet Segment Identifier
1025
1026     The supported ESI Types:
1027
1028      - ``EvpnEsi.ARBITRARY`` indicates EvpnArbitraryEsi.
1029
1030      - ``EvpnEsi.LACP`` indicates EvpnLACPEsi.
1031
1032      - ``EvpnEsi.L2_BRIDGE`` indicates EvpnL2BridgeEsi.
1033
1034      - ``EvpnEsi.MAC_BASED`` indicates EvpnMacBasedEsi.
1035
1036      - ``EvpnEsi.ROUTER_ID`` indicates EvpnRouterIDEsi.
1037
1038      - ``EvpnEsi.AS_BASED`` indicates EvpnASBasedEsi.
1039     """
1040     _PACK_STR = "!B"  # ESI Type
1041     _ESI_LEN = 10
1042
1043     ARBITRARY = 0x00
1044     LACP = 0x01
1045     L2_BRIDGE = 0x02
1046     MAC_BASED = 0x03
1047     ROUTER_ID = 0x04
1048     AS_BASED = 0x05
1049     MAX = 0xff  # Reserved
1050
1051     _TYPE_NAME = None  # must be defined in subclass
1052
1053     def __init__(self, type_=None):
1054         if type_ is None:
1055             type_ = self._rev_lookup_type(self.__class__)
1056         self.type = type_
1057
1058     @classmethod
1059     def parser(cls, buf):
1060         (esi_type,) = struct.unpack_from(
1061             cls._PACK_STR, six.binary_type(buf))
1062         subcls = cls._lookup_type(esi_type)
1063         return subcls(**subcls.parse_value(buf[1:cls._ESI_LEN]))
1064
1065     def serialize(self):
1066         buf = bytearray()
1067         msg_pack_into(EvpnEsi._PACK_STR, buf, 0, self.type)
1068         return six.binary_type(buf + self.serialize_value())
1069
1070     @property
1071     def formatted_str(self):
1072         return '%s(%s)' % (
1073             self._TYPE_NAME,
1074             ','.join(str(getattr(self, v)) for v in self._VALUE_FIELDS))
1075
1076
1077 @EvpnEsi.register_unknown_type()
1078 class EvpnUnknownEsi(EvpnEsi):
1079     """
1080     ESI value for unknown type
1081     """
1082     _TYPE_NAME = 'unknown'
1083     _VALUE_PACK_STR = '!9s'
1084     _VALUE_FIELDS = ['value']
1085
1086     def __init__(self, value, type_=None):
1087         super(EvpnUnknownEsi, self).__init__(type_)
1088         self.value = value
1089
1090     @property
1091     def formatted_str(self):
1092         return '%s(%s)' % (self._TYPE_NAME, binary_str(self.value))
1093
1094
1095 @EvpnEsi.register_type(EvpnEsi.ARBITRARY)
1096 class EvpnArbitraryEsi(EvpnEsi):
1097     """
1098     Arbitrary 9-octet ESI value
1099
1100     This type indicates an arbitrary 9-octet ESI value,
1101     which is managed and configured by the operator.
1102     """
1103     _TYPE_NAME = 'arbitrary'
1104     _VALUE_PACK_STR = '!9s'
1105     _VALUE_FIELDS = ['value']
1106
1107     def __init__(self, value, type_=None):
1108         super(EvpnArbitraryEsi, self).__init__(type_)
1109         self.value = value
1110
1111     @property
1112     def formatted_str(self):
1113         return '%s(%s)' % (self._TYPE_NAME, binary_str(self.value))
1114
1115
1116 @EvpnEsi.register_type(EvpnEsi.LACP)
1117 class EvpnLACPEsi(EvpnEsi):
1118     """
1119     ESI value for LACP
1120
1121     When IEEE 802.1AX LACP is used between the PEs and CEs,
1122     this ESI type indicates an auto-generated ESI value
1123     determined from LACP.
1124     """
1125     _TYPE_NAME = 'lacp'
1126     _VALUE_PACK_STR = '!6sHx'
1127     _VALUE_FIELDS = ['mac_addr', 'port_key']
1128     _TYPE = {
1129         'ascii': [
1130             'mac_addr'
1131         ]
1132     }
1133
1134     def __init__(self, mac_addr, port_key, type_=None):
1135         super(EvpnLACPEsi, self).__init__(type_)
1136         self.mac_addr = mac_addr
1137         self.port_key = port_key
1138
1139     @classmethod
1140     def parse_value(cls, buf):
1141         (mac_addr, port_key) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
1142         return {
1143             'mac_addr': addrconv.mac.bin_to_text(mac_addr),
1144             'port_key': port_key,
1145         }
1146
1147     def serialize_value(self):
1148         return struct.pack(
1149             self._VALUE_PACK_STR,
1150             addrconv.mac.text_to_bin(self.mac_addr), self.port_key)
1151
1152
1153 @EvpnEsi.register_type(EvpnEsi.L2_BRIDGE)
1154 class EvpnL2BridgeEsi(EvpnEsi):
1155     """
1156     ESI value for Layer 2 Bridge
1157
1158     This type is used in the case of indirectly connected hosts
1159     via a bridged LAN between the CEs and the PEs.
1160     The ESI Value is auto-generated and determined based
1161     on the Layer 2 bridge protocol.
1162     """
1163     _TYPE_NAME = 'l2_bridge'
1164     _VALUE_PACK_STR = '!6sHx'
1165     _VALUE_FIELDS = ['mac_addr', 'priority']
1166     _TYPE = {
1167         'ascii': [
1168             'mac_addr'
1169         ]
1170     }
1171
1172     def __init__(self, mac_addr, priority, type_=None):
1173         super(EvpnL2BridgeEsi, self).__init__(type_)
1174         self.mac_addr = mac_addr
1175         self.priority = priority
1176
1177     @classmethod
1178     def parse_value(cls, buf):
1179         (mac_addr, priority) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
1180         return {
1181             'mac_addr': addrconv.mac.bin_to_text(mac_addr),
1182             'priority': priority,
1183         }
1184
1185     def serialize_value(self):
1186         return struct.pack(
1187             self._VALUE_PACK_STR,
1188             addrconv.mac.text_to_bin(self.mac_addr), self.priority)
1189
1190
1191 @EvpnEsi.register_type(EvpnEsi.MAC_BASED)
1192 class EvpnMacBasedEsi(EvpnEsi):
1193     """
1194     MAC-based ESI Value
1195
1196     This type indicates a MAC-based ESI Value that
1197     can be auto-generated or configured by the operator.
1198     """
1199     _TYPE_NAME = 'mac_based'
1200     _VALUE_PACK_STR = '!6s3s'
1201     _VALUE_FIELDS = ['mac_addr', 'local_disc']
1202     _TYPE = {
1203         'ascii': [
1204             'mac_addr'
1205         ]
1206     }
1207
1208     def __init__(self, mac_addr, local_disc, type_=None):
1209         super(EvpnMacBasedEsi, self).__init__(type_)
1210         self.mac_addr = mac_addr
1211         self.local_disc = local_disc
1212
1213     @classmethod
1214     def parse_value(cls, buf):
1215         (mac_addr, local_disc) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
1216         return {
1217             'mac_addr': addrconv.mac.bin_to_text(mac_addr),
1218             'local_disc': type_desc.Int3.to_user(local_disc),
1219         }
1220
1221     def serialize_value(self):
1222         return struct.pack(
1223             self._VALUE_PACK_STR,
1224             addrconv.mac.text_to_bin(self.mac_addr),
1225             type_desc.Int3.from_user(self.local_disc))
1226
1227
1228 @EvpnEsi.register_type(EvpnEsi.ROUTER_ID)
1229 class EvpnRouterIDEsi(EvpnEsi):
1230     """
1231     Router-ID ESI Value
1232
1233     This type indicates a router-ID ESI Value that
1234     can be auto-generated or configured by the operator.
1235     """
1236     _TYPE_NAME = 'router_id'
1237     _VALUE_PACK_STR = '!4sIx'
1238     _VALUE_FIELDS = ['router_id', 'local_disc']
1239     _TYPE = {
1240         'ascii': [
1241             'router_id'
1242         ]
1243     }
1244
1245     def __init__(self, router_id, local_disc, type_=None):
1246         super(EvpnRouterIDEsi, self).__init__(type_)
1247         self.router_id = router_id
1248         self.local_disc = local_disc
1249
1250     @classmethod
1251     def parse_value(cls, buf):
1252         (router_id, local_disc) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
1253         return {
1254             'router_id': addrconv.ipv4.bin_to_text(router_id),
1255             'local_disc': local_disc,
1256         }
1257
1258     def serialize_value(self):
1259         return struct.pack(
1260             self._VALUE_PACK_STR,
1261             addrconv.ipv4.text_to_bin(self.router_id), self.local_disc)
1262
1263
1264 @EvpnEsi.register_type(EvpnEsi.AS_BASED)
1265 class EvpnASBasedEsi(EvpnEsi):
1266     """
1267     AS based ESI value
1268
1269     This type indicates an Autonomous System(AS)-based
1270     ESI Value that can be auto-generated or configured by
1271     the operator.
1272     """
1273     _TYPE_NAME = 'as_based'
1274     _VALUE_PACK_STR = '!IIx'
1275     _VALUE_FIELDS = ['as_number', 'local_disc']
1276
1277     def __init__(self, as_number, local_disc, type_=None):
1278         super(EvpnASBasedEsi, self).__init__(type_)
1279         self.as_number = as_number
1280         self.local_disc = local_disc
1281
1282
1283 class EvpnNLRI(StringifyMixin, TypeDisp):
1284     """
1285     BGP Network Layer Reachability Information (NLRI) for EVPN
1286     """
1287     ROUTE_FAMILY = RF_L2_EVPN
1288
1289     # EVPN NLRI:
1290     # +-----------------------------------+
1291     # |    Route Type (1 octet)           |
1292     # +-----------------------------------+
1293     # |     Length (1 octet)              |
1294     # +-----------------------------------+
1295     # | Route Type specific (variable)    |
1296     # +-----------------------------------+
1297     _PACK_STR = "!BB"
1298     _PACK_STR_SIZE = struct.calcsize(_PACK_STR)
1299
1300     ETHERNET_AUTO_DISCOVERY = 0x01
1301     MAC_IP_ADVERTISEMENT = 0x02
1302     INCLUSIVE_MULTICAST_ETHERNET_TAG = 0x03
1303     ETHERNET_SEGMENT = 0x04
1304     IP_PREFIX_ROUTE = 0x05
1305
1306     ROUTE_TYPE_NAME = None  # must be defined in subclass
1307
1308     # Reserved value for Ethernet Tag ID.
1309     MAX_ET = 0xFFFFFFFF
1310
1311     # Dictionary of ROUTE_TYPE_NAME to subclass.
1312     # e.g.)
1313     #   _NAMES = {'eth_ad': EvpnEthernetAutoDiscoveryNLRI, ...}
1314     _NAMES = {}
1315
1316     # List of the fields considered to be part of the prefix in the NLRI.
1317     # This list should be defined in subclasses to format NLRI string
1318     # representation.
1319     NLRI_PREFIX_FIELDS = []
1320
1321     def __init__(self, type_=None, length=None):
1322         if type_ is None:
1323             type_ = self._rev_lookup_type(self.__class__)
1324         self.type = type_
1325         self.length = length
1326         self.route_dist = None  # should be initialized in subclass
1327
1328     @classmethod
1329     def register_type(cls, type_):
1330         cls._TYPES = cls._TYPES.copy()
1331         cls._NAMES = cls._NAMES.copy()
1332
1333         def _register_type(subcls):
1334             cls._TYPES[type_] = subcls
1335             cls._NAMES[subcls.ROUTE_TYPE_NAME] = subcls
1336             cls._REV_TYPES = None
1337             return subcls
1338
1339         return _register_type
1340
1341     @classmethod
1342     def _lookup_type_name(cls, type_name):
1343         try:
1344             return cls._NAMES[type_name]
1345         except KeyError:
1346             return EvpnUnknownNLRI
1347
1348     @classmethod
1349     def parser(cls, buf):
1350         (route_type, length) = struct.unpack_from(
1351             cls._PACK_STR, six.binary_type(buf))
1352         offset = cls._PACK_STR_SIZE + length
1353         subcls = cls._lookup_type(route_type)
1354         values = subcls.parse_value(buf[cls._PACK_STR_SIZE:offset])
1355         return subcls(type_=route_type, length=length,
1356                       **values), buf[offset:]
1357
1358     def serialize_value(self):
1359         # Overrided in subclass
1360         return b''
1361
1362     def serialize(self):
1363         value_bin = self.serialize_value()
1364         # fixup
1365         self.length = len(value_bin)
1366         return struct.pack(EvpnNLRI._PACK_STR,
1367                            self.type, self.length) + value_bin
1368
1369     @staticmethod
1370     def _rd_from_bin(buf):
1371         return _RouteDistinguisher.parser(buf[:8]), buf[8:]
1372
1373     @staticmethod
1374     def _rd_to_bin(rd):
1375         return six.binary_type(rd.serialize())
1376
1377     @staticmethod
1378     def _esi_from_bin(buf):
1379         return EvpnEsi.parser(buf[:10]), buf[10:]
1380
1381     @staticmethod
1382     def _esi_to_bin(esi):
1383         return esi.serialize()
1384
1385     @staticmethod
1386     def _ethernet_tag_id_from_bin(buf):
1387         return type_desc.Int4.to_user(six.binary_type(buf[:4])), buf[4:]
1388
1389     @staticmethod
1390     def _ethernet_tag_id_to_bin(tag_id):
1391         return type_desc.Int4.from_user(tag_id)
1392
1393     @staticmethod
1394     def _mac_addr_len_from_bin(buf):
1395         return type_desc.Int1.to_user(six.binary_type(buf[:1])), buf[1:]
1396
1397     @staticmethod
1398     def _mac_addr_len_to_bin(mac_len):
1399         return type_desc.Int1.from_user(mac_len)
1400
1401     @staticmethod
1402     def _mac_addr_from_bin(buf, mac_len):
1403         mac_len //= 8
1404         return addrconv.mac.bin_to_text(buf[:mac_len]), buf[mac_len:]
1405
1406     @staticmethod
1407     def _mac_addr_to_bin(mac_addr):
1408         return addrconv.mac.text_to_bin(mac_addr)
1409
1410     @staticmethod
1411     def _ip_addr_len_from_bin(buf):
1412         return type_desc.Int1.to_user(six.binary_type(buf[:1])), buf[1:]
1413
1414     @staticmethod
1415     def _ip_addr_len_to_bin(ip_len):
1416         return type_desc.Int1.from_user(ip_len)
1417
1418     @staticmethod
1419     def _ip_addr_from_bin(buf, ip_len):
1420         return ip.bin_to_text(buf[:ip_len]), buf[ip_len:]
1421
1422     @staticmethod
1423     def _ip_addr_to_bin(ip_addr):
1424         return ip.text_to_bin(ip_addr)
1425
1426     @staticmethod
1427     def _mpls_label_from_bin(buf):
1428         mpls_label, is_bos = mpls.label_from_bin(buf)
1429         rest = buf[3:]
1430         return mpls_label, rest, is_bos
1431
1432     @staticmethod
1433     def _mpls_label_to_bin(label, is_bos=True):
1434         return mpls.label_to_bin(label, is_bos=is_bos)
1435
1436     @staticmethod
1437     def _vni_from_bin(buf):
1438         return vxlan.vni_from_bin(six.binary_type(buf[:3])), buf[3:]
1439
1440     @staticmethod
1441     def _vni_to_bin(vni):
1442         return vxlan.vni_to_bin(vni)
1443
1444     @property
1445     def prefix(self):
1446         def _format(i):
1447             pairs = []
1448             for k in i.NLRI_PREFIX_FIELDS:
1449                 v = getattr(i, k)
1450                 if k == 'esi':
1451                     pairs.append('%s:%s' % (k, v.formatted_str))
1452                 else:
1453                     pairs.append('%s:%s' % (k, v))
1454             return ','.join(pairs)
1455
1456         return '%s(%s)' % (self.ROUTE_TYPE_NAME, _format(self))
1457
1458     @property
1459     def formatted_nlri_str(self):
1460         return '%s:%s' % (self.route_dist, self.prefix)
1461
1462
1463 @EvpnNLRI.register_unknown_type()
1464 class EvpnUnknownNLRI(EvpnNLRI):
1465     """
1466     Unknown route type specific EVPN NLRI
1467     """
1468     ROUTE_TYPE_NAME = 'unknown'
1469     NLRI_PREFIX_FIELDS = ['value']
1470
1471     def __init__(self, value, type_, length=None):
1472         super(EvpnUnknownNLRI, self).__init__(type_, length)
1473         self.value = value
1474
1475     @classmethod
1476     def parse_value(cls, buf):
1477         return {
1478             'value': buf
1479         }
1480
1481     def serialize_value(self):
1482         return self.value
1483
1484     @property
1485     def formatted_nlri_str(self):
1486         return '%s(%s)' % (self.ROUTE_TYPE_NAME, binary_str(self.value))
1487
1488
1489 @EvpnNLRI.register_type(EvpnNLRI.ETHERNET_AUTO_DISCOVERY)
1490 class EvpnEthernetAutoDiscoveryNLRI(EvpnNLRI):
1491     """
1492     Ethernet A-D route type specific EVPN NLRI
1493     """
1494     ROUTE_TYPE_NAME = 'eth_ad'
1495
1496     # +---------------------------------------+
1497     # |  Route Distinguisher (RD) (8 octets)  |
1498     # +---------------------------------------+
1499     # |Ethernet Segment Identifier (10 octets)|
1500     # +---------------------------------------+
1501     # |  Ethernet Tag ID (4 octets)           |
1502     # +---------------------------------------+
1503     # |  MPLS Label (3 octets)                |
1504     # +---------------------------------------+
1505     _PACK_STR = "!8s10sI3s"
1506     NLRI_PREFIX_FIELDS = ['esi', 'ethernet_tag_id']
1507     _TYPE = {
1508         'ascii': [
1509             'route_dist',
1510         ]
1511     }
1512
1513     def __init__(self, route_dist, esi, ethernet_tag_id,
1514                  mpls_label=None, vni=None, label=None,
1515                  type_=None, length=None):
1516         super(EvpnEthernetAutoDiscoveryNLRI, self).__init__(type_, length)
1517         self.route_dist = route_dist
1518         self.esi = esi
1519         self.ethernet_tag_id = ethernet_tag_id
1520         if label:
1521             # If binary type label field value is specified, stores it
1522             # and decodes as MPLS label and VNI.
1523             self._label = label
1524             self._mpls_label, _, _ = self._mpls_label_from_bin(label)
1525             self._vni, _ = self._vni_from_bin(label)
1526         else:
1527             # If either MPLS label or VNI is specified, stores it
1528             # and encodes into binary type label field value.
1529             self._label = self._serialize_label(mpls_label, vni)
1530             self._mpls_label = mpls_label
1531             self._vni = vni
1532
1533     def _serialize_label(self, mpls_label, vni):
1534         if mpls_label:
1535             return self._mpls_label_to_bin(mpls_label, is_bos=True)
1536         elif vni:
1537             return self._vni_to_bin(vni)
1538         else:
1539             return b'\x00' * 3
1540
1541     @classmethod
1542     def parse_value(cls, buf):
1543         route_dist, rest = cls._rd_from_bin(buf)
1544         esi, rest = cls._esi_from_bin(rest)
1545         ethernet_tag_id, rest = cls._ethernet_tag_id_from_bin(rest)
1546
1547         return {
1548             'route_dist': route_dist.formatted_str,
1549             'esi': esi,
1550             'ethernet_tag_id': ethernet_tag_id,
1551             'label': rest,
1552         }
1553
1554     def serialize_value(self):
1555         route_dist = _RouteDistinguisher.from_str(self.route_dist)
1556         return struct.pack(
1557             self._PACK_STR, route_dist.serialize(), self.esi.serialize(),
1558             self.ethernet_tag_id, self._label)
1559
1560     @property
1561     def mpls_label(self):
1562         return self._mpls_label
1563
1564     @mpls_label.setter
1565     def mpls_label(self, mpls_label):
1566         self._label = self._mpls_label_to_bin(mpls_label, is_bos=True)
1567         self._mpls_label = mpls_label
1568         self._vni = None  # disables VNI
1569
1570     @property
1571     def vni(self):
1572         return self._vni
1573
1574     @vni.setter
1575     def vni(self, vni):
1576         self._label = self._vni_to_bin(vni)
1577         self._mpls_label = None  # disables MPLS label
1578         self._vni = vni
1579
1580     @property
1581     def label_list(self):
1582         return [self.mpls_label]
1583
1584
1585 @EvpnNLRI.register_type(EvpnNLRI.MAC_IP_ADVERTISEMENT)
1586 class EvpnMacIPAdvertisementNLRI(EvpnNLRI):
1587     """
1588     MAC/IP Advertisement route type specific EVPN NLRI
1589     """
1590     ROUTE_TYPE_NAME = 'mac_ip_adv'
1591
1592     # +---------------------------------------+
1593     # |  RD (8 octets)                        |
1594     # +---------------------------------------+
1595     # |Ethernet Segment Identifier (10 octets)|
1596     # +---------------------------------------+
1597     # |  Ethernet Tag ID (4 octets)           |
1598     # +---------------------------------------+
1599     # |  MAC Address Length (1 octet)         |
1600     # +---------------------------------------+
1601     # |  MAC Address (6 octets)               |
1602     # +---------------------------------------+
1603     # |  IP Address Length (1 octet)          |
1604     # +---------------------------------------+
1605     # |  IP Address (0, 4, or 16 octets)      |
1606     # +---------------------------------------+
1607     # |  MPLS Label1 (3 octets)               |
1608     # +---------------------------------------+
1609     # |  MPLS Label2 (0 or 3 octets)          |
1610     # +---------------------------------------+
1611     _PACK_STR = "!8s10sIB6sB%ds%ds"
1612     # Note: mac_addr_len and ip_addr_len are omitted for readability.
1613     NLRI_PREFIX_FIELDS = ['ethernet_tag_id', 'mac_addr', 'ip_addr']
1614     _TYPE = {
1615         'ascii': [
1616             'route_dist',
1617             'mac_addr',
1618             'ip_addr',
1619         ]
1620     }
1621
1622     def __init__(self, route_dist, ethernet_tag_id, mac_addr, ip_addr,
1623                  esi=None, mpls_labels=None, vni=None, labels=None,
1624                  mac_addr_len=None, ip_addr_len=None,
1625                  type_=None, length=None):
1626         super(EvpnMacIPAdvertisementNLRI, self).__init__(type_, length)
1627         self.route_dist = route_dist
1628         self.esi = esi
1629         self.ethernet_tag_id = ethernet_tag_id
1630         self.mac_addr_len = mac_addr_len
1631         self.mac_addr = mac_addr
1632         self.ip_addr_len = ip_addr_len
1633         self.ip_addr = ip_addr
1634         if labels:
1635             # If binary type labels field value is specified, stores it
1636             # and decodes as MPLS labels and VNI.
1637             self._mpls_labels, self._vni = self._parse_labels(labels)
1638             self._labels = labels
1639         else:
1640             # If either MPLS labels or VNI is specified, stores it
1641             # and encodes into binary type labels field value.
1642             self._labels = self._serialize_labels(mpls_labels, vni)
1643             self._mpls_labels = mpls_labels
1644             self._vni = vni
1645
1646     def _parse_labels(self, labels):
1647         mpls_label1, rest, is_bos = self._mpls_label_from_bin(labels)
1648         mpls_labels = [mpls_label1]
1649         if rest and not is_bos:
1650             mpls_label2, rest, _ = self._mpls_label_from_bin(rest)
1651             mpls_labels.append(mpls_label2)
1652         vni, _ = self._vni_from_bin(labels)
1653         return mpls_labels, vni
1654
1655     def _serialize_labels(self, mpls_labels, vni):
1656         if mpls_labels:
1657             return self._serialize_mpls_labels(mpls_labels)
1658         elif vni:
1659             return self._vni_to_bin(vni)
1660         else:
1661             return b'\x00' * 3
1662
1663     def _serialize_mpls_labels(self, mpls_labels):
1664         if len(mpls_labels) == 1:
1665             return self._mpls_label_to_bin(mpls_labels[0], is_bos=True)
1666         elif len(mpls_labels) == 2:
1667             return (self._mpls_label_to_bin(mpls_labels[0], is_bos=False) +
1668                     self._mpls_label_to_bin(mpls_labels[1], is_bos=True))
1669         else:
1670             return b'\x00' * 3
1671
1672     @classmethod
1673     def parse_value(cls, buf):
1674         route_dist, rest = cls._rd_from_bin(buf)
1675         esi, rest = cls._esi_from_bin(rest)
1676         ethernet_tag_id, rest = cls._ethernet_tag_id_from_bin(rest)
1677         mac_addr_len, rest = cls._mac_addr_len_from_bin(rest)
1678         mac_addr, rest = cls._mac_addr_from_bin(rest, mac_addr_len)
1679         ip_addr_len, rest = cls._ip_addr_len_from_bin(rest)
1680         if ip_addr_len != 0:
1681             ip_addr, rest = cls._ip_addr_from_bin(rest, ip_addr_len // 8)
1682         else:
1683             ip_addr = None
1684
1685         return {
1686             'route_dist': route_dist.formatted_str,
1687             'esi': esi,
1688             'ethernet_tag_id': ethernet_tag_id,
1689             'mac_addr_len': mac_addr_len,
1690             'mac_addr': mac_addr,
1691             'ip_addr_len': ip_addr_len,
1692             'ip_addr': ip_addr,
1693             'labels': rest,
1694         }
1695
1696     def serialize_value(self):
1697         route_dist = _RouteDistinguisher.from_str(self.route_dist)
1698         mac_addr = self._mac_addr_to_bin(self.mac_addr)
1699         self.mac_addr_len = len(mac_addr) * 8  # fixup
1700         if self.ip_addr:
1701             ip_addr = self._ip_addr_to_bin(self.ip_addr)
1702         else:
1703             ip_addr = b''
1704         ip_addr_len = len(ip_addr)
1705         self.ip_addr_len = ip_addr_len * 8  # fixup
1706
1707         return struct.pack(
1708             self._PACK_STR % (ip_addr_len, len(self._labels)),
1709             route_dist.serialize(), self.esi.serialize(),
1710             self.ethernet_tag_id,
1711             self.mac_addr_len, mac_addr,
1712             self.ip_addr_len, ip_addr,
1713             self._labels)
1714
1715     @property
1716     def mpls_labels(self):
1717         return self._mpls_labels
1718
1719     @mpls_labels.setter
1720     def mpls_labels(self, mpls_labels):
1721         self._labels = self._serialize_mpls_labels(mpls_labels)
1722         self._mpls_labels = mpls_labels
1723         self._vni = None  # disables VNI
1724
1725     @property
1726     def vni(self):
1727         return self._vni
1728
1729     @vni.setter
1730     def vni(self, vni):
1731         self._labels = self._vni_to_bin(vni)
1732         self._mpls_labels = None  # disables MPLS labels
1733         self._vni = vni
1734
1735     @property
1736     def label_list(self):
1737         return self.mpls_labels
1738
1739
1740 @EvpnNLRI.register_type(EvpnNLRI.INCLUSIVE_MULTICAST_ETHERNET_TAG)
1741 class EvpnInclusiveMulticastEthernetTagNLRI(EvpnNLRI):
1742     """
1743     Inclusive Multicast Ethernet Tag route type specific EVPN NLRI
1744     """
1745     ROUTE_TYPE_NAME = 'multicast_etag'
1746
1747     # +---------------------------------------+
1748     # |  RD (8 octets)                        |
1749     # +---------------------------------------+
1750     # |  Ethernet Tag ID (4 octets)           |
1751     # +---------------------------------------+
1752     # |  IP Address Length (1 octet)          |
1753     # +---------------------------------------+
1754     # |  Originating Router's IP Address      |
1755     # |          (4 or 16 octets)             |
1756     # +---------------------------------------+
1757     _PACK_STR = '!8sIB%ds'
1758     NLRI_PREFIX_FIELDS = ['ethernet_tag_id', 'ip_addr']
1759     _TYPE = {
1760         'ascii': [
1761             'route_dist',
1762             'ip_addr',
1763         ]
1764     }
1765
1766     def __init__(self, route_dist, ethernet_tag_id, ip_addr,
1767                  ip_addr_len=None, type_=None, length=None):
1768         super(EvpnInclusiveMulticastEthernetTagNLRI,
1769               self).__init__(type_, length)
1770         self.route_dist = route_dist
1771         self.ethernet_tag_id = ethernet_tag_id
1772         self.ip_addr_len = ip_addr_len
1773         self.ip_addr = ip_addr
1774
1775     @classmethod
1776     def parse_value(cls, buf):
1777         route_dist, rest = cls._rd_from_bin(buf)
1778         ethernet_tag_id, rest = cls._ethernet_tag_id_from_bin(rest)
1779         ip_addr_len, rest = cls._ip_addr_len_from_bin(rest)
1780         ip_addr, rest = cls._ip_addr_from_bin(rest, ip_addr_len // 8)
1781
1782         return {
1783             'route_dist': route_dist.formatted_str,
1784             'ethernet_tag_id': ethernet_tag_id,
1785             'ip_addr_len': ip_addr_len,
1786             'ip_addr': ip_addr,
1787         }
1788
1789     def serialize_value(self):
1790         route_dist = _RouteDistinguisher.from_str(self.route_dist)
1791         ip_addr = self._ip_addr_to_bin(self.ip_addr)
1792         self.ip_addr_len = len(ip_addr) * 8  # fixup
1793
1794         return struct.pack(
1795             self._PACK_STR % len(ip_addr),
1796             route_dist.serialize(), self.ethernet_tag_id,
1797             self.ip_addr_len, ip_addr)
1798
1799
1800 @EvpnNLRI.register_type(EvpnNLRI.ETHERNET_SEGMENT)
1801 class EvpnEthernetSegmentNLRI(EvpnNLRI):
1802     """
1803     Ethernet Segment route type specific EVPN NLRI
1804     """
1805     ROUTE_TYPE_NAME = 'eth_seg'
1806
1807     # +---------------------------------------+
1808     # |  RD (8 octets)                        |
1809     # +---------------------------------------+
1810     # |Ethernet Segment Identifier (10 octets)|
1811     # +---------------------------------------+
1812     # |  IP Address Length (1 octet)          |
1813     # +---------------------------------------+
1814     # |  Originating Router's IP Address      |
1815     # |          (4 or 16 octets)             |
1816     # +---------------------------------------+
1817     _PACK_STR = '!8s10sB%ds'
1818     NLRI_PREFIX_FIELDS = ['esi', 'ip_addr']
1819     _TYPE = {
1820         'ascii': [
1821             'route_dist',
1822             'ip_addr',
1823         ]
1824     }
1825
1826     def __init__(self, route_dist, esi, ip_addr, ip_addr_len=None,
1827                  type_=None, length=None):
1828         super(EvpnEthernetSegmentNLRI, self).__init__(type_, length)
1829         self.route_dist = route_dist
1830         self.esi = esi
1831         self.ip_addr_len = ip_addr_len
1832         self.ip_addr = ip_addr
1833
1834     @classmethod
1835     def parse_value(cls, buf):
1836         route_dist, rest = cls._rd_from_bin(buf)
1837         esi, rest = cls._esi_from_bin(rest)
1838         ip_addr_len, rest = cls._ip_addr_len_from_bin(rest)
1839         ip_addr, rest = cls._ip_addr_from_bin(rest, ip_addr_len // 8)
1840
1841         return {
1842             'route_dist': route_dist.formatted_str,
1843             'esi': esi,
1844             'ip_addr_len': ip_addr_len,
1845             'ip_addr': ip_addr,
1846         }
1847
1848     def serialize_value(self):
1849         route_dist = _RouteDistinguisher.from_str(self.route_dist)
1850         ip_addr = self._ip_addr_to_bin(self.ip_addr)
1851         # fixup
1852         self.ip_addr_len = len(ip_addr) * 8
1853
1854         return struct.pack(
1855             self._PACK_STR % len(ip_addr),
1856             route_dist.serialize(), self.esi.serialize(),
1857             self.ip_addr_len, ip_addr)
1858
1859
1860 @EvpnNLRI.register_type(EvpnNLRI.IP_PREFIX_ROUTE)
1861 class EvpnIpPrefixNLRI(EvpnNLRI):
1862     """
1863     IP Prefix advertisement route NLRI
1864     """
1865     ROUTE_TYPE_NAME = 'ip_prefix'
1866
1867     # +---------------------------------------+
1868     # |      RD   (8 octets)                  |
1869     # +---------------------------------------+
1870     # |Ethernet Segment Identifier (10 octets)|
1871     # +---------------------------------------+
1872     # |  Ethernet Tag ID (4 octets)           |
1873     # +---------------------------------------+
1874     # |  IP Prefix Length (1 octet)           |
1875     # +---------------------------------------+
1876     # |  IP Prefix (4 or 16 octets)           |
1877     # +---------------------------------------+
1878     # |  GW IP Address (4 or 16 octets)       |
1879     # +---------------------------------------+
1880     # |  MPLS Label (3 octets)                |
1881     # +---------------------------------------+
1882     _PACK_STR = '!8s10sIB%ds%ds3s'
1883     NLRI_PREFIX_FIELDS = ['ethernet_tag_id', 'ip_prefix']
1884     _TYPE = {
1885         'ascii': [
1886             'route_dist',
1887             'ip_prefix',
1888             'gw_ip_addr'
1889         ]
1890     }
1891     _LABEL_LEN = 3
1892
1893     def __init__(self, route_dist, ethernet_tag_id, ip_prefix,
1894                  esi=None, gw_ip_addr=None,
1895                  mpls_label=None, vni=None, label=None,
1896                  type_=None, length=None):
1897         super(EvpnIpPrefixNLRI, self).__init__(type_, length)
1898         self.route_dist = route_dist
1899         self.esi = esi
1900         self.ethernet_tag_id = ethernet_tag_id
1901         self._ip_prefix = None
1902         self._ip_prefix_len = None
1903         self.ip_prefix = ip_prefix
1904
1905         if gw_ip_addr is None:
1906             if ':' not in self._ip_prefix:
1907                 self.gw_ip_addr = '0.0.0.0'
1908             else:
1909                 self.gw_ip_addr = '::'
1910         else:
1911             self.gw_ip_addr = gw_ip_addr
1912
1913         if label:
1914             # If binary type label field value is specified, stores it
1915             # and decodes as MPLS label and VNI.
1916             self._label = label
1917             self._mpls_label, _, _ = self._mpls_label_from_bin(label)
1918             self._vni, _ = self._vni_from_bin(label)
1919         else:
1920             # If either MPLS label or VNI is specified, stores it
1921             # and encodes into binary type label field value.
1922             self._label = self._serialize_label(mpls_label, vni)
1923             self._mpls_label = mpls_label
1924             self._vni = vni
1925
1926     def _serialize_label(self, mpls_label, vni):
1927         if mpls_label:
1928             return self._mpls_label_to_bin(mpls_label, is_bos=True)
1929         elif vni:
1930             return vxlan.vni_to_bin(vni)
1931         else:
1932             return b'\x00' * 3
1933
1934     @classmethod
1935     def parse_value(cls, buf):
1936         route_dist, rest = cls._rd_from_bin(buf)
1937         esi, rest = cls._esi_from_bin(rest)
1938         ethernet_tag_id, rest = cls._ethernet_tag_id_from_bin(rest)
1939         ip_prefix_len, rest = cls._ip_addr_len_from_bin(rest)
1940         _len = (len(rest) - cls._LABEL_LEN) // 2
1941         ip_prefix, rest = cls._ip_addr_from_bin(rest, _len)
1942         gw_ip_addr, rest = cls._ip_addr_from_bin(rest, _len)
1943
1944         return {
1945             'route_dist': route_dist.formatted_str,
1946             'esi': esi,
1947             'ethernet_tag_id': ethernet_tag_id,
1948             'ip_prefix': '%s/%s' % (ip_prefix, ip_prefix_len),
1949             'gw_ip_addr': gw_ip_addr,
1950             'label': rest,
1951         }
1952
1953     def serialize_value(self):
1954         route_dist = _RouteDistinguisher.from_str(self.route_dist)
1955         ip_prefix = self._ip_addr_to_bin(self._ip_prefix)
1956         gw_ip_addr = self._ip_addr_to_bin(self.gw_ip_addr)
1957
1958         return struct.pack(
1959             self._PACK_STR % (len(ip_prefix), len(gw_ip_addr)),
1960             route_dist.serialize(), self.esi.serialize(),
1961             self.ethernet_tag_id, self._ip_prefix_len, ip_prefix,
1962             gw_ip_addr, self._label)
1963
1964     @property
1965     def ip_prefix(self):
1966         return '%s/%s' % (self._ip_prefix, self._ip_prefix_len)
1967
1968     @ip_prefix.setter
1969     def ip_prefix(self, ip_prefix):
1970         self._ip_prefix, ip_prefix_len = ip_prefix.split('/')
1971         self._ip_prefix_len = int(ip_prefix_len)
1972
1973     @property
1974     def mpls_label(self):
1975         return self._mpls_label
1976
1977     @mpls_label.setter
1978     def mpls_label(self, mpls_label):
1979         self._label = self._mpls_label_to_bin(mpls_label, is_bos=True)
1980         self._mpls_label = mpls_label
1981         self._vni = None  # disables VNI
1982
1983     @property
1984     def vni(self):
1985         return self._vni
1986
1987     @vni.setter
1988     def vni(self, vni):
1989         self._label = self._vni_to_bin(vni)
1990         self._mpls_label = None  # disables MPLS label
1991         self._vni = vni
1992
1993     @property
1994     def label_list(self):
1995         return [self.mpls_label]
1996
1997
1998 class _FlowSpecNLRIBase(StringifyMixin, TypeDisp):
1999     """
2000     Base class for Flow Specification NLRI
2001     """
2002
2003     # flow-spec NLRI:
2004     # +-----------------------------------+
2005     # |    length (0xnn or 0xfn nn)       |
2006     # +-----------------------------------+
2007     # |     NLRI value  (variable)        |
2008     # +-----------------------------------+
2009     ROUTE_FAMILY = None
2010     _LENGTH_SHORT_FMT = '!B'
2011     LENGTH_SHORT_SIZE = struct.calcsize(_LENGTH_SHORT_FMT)
2012     _LENGTH_LONG_FMT = '!H'
2013     LENGTH_LONG_SIZE = struct.calcsize(_LENGTH_LONG_FMT)
2014     _LENGTH_THRESHOLD = 0xf000
2015     FLOWSPEC_FAMILY = ''
2016
2017     def __init__(self, length=0, rules=None):
2018         self.length = length
2019         rules = rules or []
2020         for r in rules:
2021             assert isinstance(r, _FlowSpecComponentBase)
2022         self.rules = rules
2023
2024     @classmethod
2025     def parser(cls, buf):
2026         (length,) = struct.unpack_from(
2027             cls._LENGTH_LONG_FMT, six.binary_type(buf))
2028
2029         if length < cls._LENGTH_THRESHOLD:
2030             length >>= 8
2031             offset = cls.LENGTH_SHORT_SIZE
2032         else:
2033             offset = cls.LENGTH_LONG_SIZE
2034
2035         kwargs = {'length': length}
2036         rest = buf[offset:offset + length]
2037
2038         if cls.ROUTE_FAMILY.safi == subaddr_family.VPN_FLOWSPEC:
2039             route_dist = _RouteDistinguisher.parser(rest[:8])
2040             kwargs['route_dist'] = route_dist.formatted_str
2041             rest = rest[8:]
2042
2043         rules = []
2044
2045         while rest:
2046             subcls, rest = _FlowSpecComponentBase.parse_header(
2047                 rest, cls.ROUTE_FAMILY.afi)
2048
2049             while rest:
2050                 rule, rest = subcls.parse_body(rest)
2051                 rules.append(rule)
2052
2053                 if (not isinstance(rule, _FlowSpecOperatorBase) or
2054                         rule.operator & rule.END_OF_LIST):
2055                     break
2056
2057         kwargs['rules'] = rules
2058
2059         return cls(**kwargs), rest
2060
2061     def serialize(self):
2062         rules_bin = b''
2063
2064         if self.ROUTE_FAMILY.safi == subaddr_family.VPN_FLOWSPEC:
2065             route_dist = _RouteDistinguisher.from_str(self.route_dist)
2066             rules_bin += route_dist.serialize()
2067
2068         self.rules.sort(key=lambda x: x.type)
2069         for _, rules in itertools.groupby(self.rules, key=lambda x: x.type):
2070             rules = list(rules)
2071             rules_bin += rules[0].serialize_header()
2072
2073             if isinstance(rules[-1], _FlowSpecOperatorBase):
2074                 rules[-1].operator |= rules[-1].END_OF_LIST
2075
2076             for r in rules:
2077                 rules_bin += r.serialize_body()
2078
2079         self.length = len(rules_bin)
2080
2081         if self.length < self._LENGTH_THRESHOLD:
2082             buf = struct.pack(self._LENGTH_SHORT_FMT, self.length)
2083         else:
2084             buf = struct.pack(self._LENGTH_LONG_FMT, self.length)
2085
2086         return buf + rules_bin
2087
2088     @classmethod
2089     def _from_user(cls, **kwargs):
2090         rules = []
2091         for k, v in kwargs.items():
2092             subcls = _FlowSpecComponentBase.lookup_type_name(
2093                 k, cls.ROUTE_FAMILY.afi)
2094             rule = subcls.from_str(str(v))
2095             rules.extend(rule)
2096         rules.sort(key=lambda x: x.type)
2097         return cls(rules=rules)
2098
2099     @property
2100     def prefix(self):
2101         def _format(i):
2102             pairs = []
2103             i.rules.sort(key=lambda x: x.type)
2104             previous_type = None
2105             for r in i.rules:
2106                 if r.type == previous_type:
2107                     if r.to_str()[0] != '&':
2108                         pairs[-1] += '|'
2109                     pairs[-1] += r.to_str()
2110                 else:
2111                     pairs.append('%s:%s' % (r.COMPONENT_NAME, r.to_str()))
2112                 previous_type = r.type
2113
2114             return ','.join(pairs)
2115
2116         return '%s(%s)' % (self.FLOWSPEC_FAMILY, _format(self))
2117
2118     @property
2119     def formatted_nlri_str(self):
2120         return self.prefix
2121
2122
2123 class FlowSpecIPv4NLRI(_FlowSpecNLRIBase):
2124     """
2125     Flow Specification NLRI class for IPv4 [RFC 5575]
2126     """
2127     ROUTE_FAMILY = RF_IPv4_FLOWSPEC
2128     FLOWSPEC_FAMILY = 'ipv4fs'
2129
2130     @classmethod
2131     def from_user(cls, **kwargs):
2132         """
2133         Utility method for creating a NLRI instance.
2134
2135         This function returns a NLRI instance from human readable format value.
2136
2137         :param kwargs: The following arguments are available.
2138
2139         =========== ============= ========= ==============================
2140         Argument    Value         Operator  Description
2141         =========== ============= ========= ==============================
2142         dst_prefix  IPv4 Prefix   Nothing   Destination Prefix.
2143         src_prefix  IPv4 Prefix   Nothing   Source Prefix.
2144         ip_proto    Integer       Numeric   IP Protocol.
2145         port        Integer       Numeric   Port number.
2146         dst_port    Integer       Numeric   Destination port number.
2147         src_port    Integer       Numeric   Source port number.
2148         icmp_type   Integer       Numeric   ICMP type.
2149         icmp_code   Integer       Numeric   ICMP code.
2150         tcp_flags   Fixed string  Bitmask   TCP flags.
2151                                             Supported values are
2152                                             ``CWR``, ``ECN``, ``URGENT``,
2153                                             ``ACK``, ``PUSH``, ``RST``,
2154                                             ``SYN`` and ``FIN``.
2155         packet_len  Integer       Numeric   Packet length.
2156         dscp        Integer       Numeric   Differentiated Services
2157                                             Code Point.
2158         fragment    Fixed string  Bitmask   Fragment.
2159                                             Supported values are
2160                                             ``DF`` (Don't fragment),
2161                                             ``ISF`` (Is a fragment),
2162                                             ``FF`` (First fragment) and
2163                                             ``LF`` (Last fragment)
2164         =========== ============= ========= ==============================
2165
2166         Example::
2167
2168             >>> msg = bgp.FlowSpecIPv4NLRI.from_user(
2169             ...     dst_prefix='10.0.0.0/24',
2170             ...     src_prefix='20.0.0.1/24',
2171             ...     ip_proto=6,
2172             ...     port='80 | 8000',
2173             ...     dst_port='>9000 & <9050',
2174             ...     src_port='>=8500 & <=9000',
2175             ...     icmp_type=0,
2176             ...     icmp_code=6,
2177             ...     tcp_flags='SYN+ACK & !=URGENT',
2178             ...     packet_len=1000,
2179             ...     dscp='22 | 24',
2180             ...     fragment='LF | ==FF')
2181             >>>
2182
2183         You can specify conditions with the following keywords.
2184
2185         The following keywords can be used when the operator type is Numeric.
2186
2187         ========== ============================================================
2188         Keyword    Description
2189         ========== ============================================================
2190         <          Less than comparison between data and value.
2191         <=         Less than or equal to comparison between data and value.
2192         >          Greater than comparison between data and value.
2193         >=         Greater than or equal to comparison between data and value.
2194         ==         Equality between data and value.
2195                    This operator can be omitted.
2196         ========== ============================================================
2197
2198         The following keywords can be used when the operator type is Bitmask.
2199
2200         ========== ================================================
2201         Keyword    Description
2202         ========== ================================================
2203         !=         Not equal operation.
2204         ==         Exact match operation if specified.
2205                    Otherwise partial match operation.
2206         `+`        Used for the summation of bitmask values.
2207                    (e.g., SYN+ACK)
2208         ========== ================================================
2209
2210         You can combine the multiple conditions with the following operators.
2211
2212         ========== =======================================
2213         Keyword    Description
2214         ========== =======================================
2215         `|`        Logical OR operation
2216         &          Logical AND operation
2217         ========== =======================================
2218
2219         :return: A instance of FlowSpecVPNv4NLRI.
2220         """
2221         return cls._from_user(**kwargs)
2222
2223
2224 class FlowSpecVPNv4NLRI(_FlowSpecNLRIBase):
2225     """
2226     Flow Specification NLRI class for VPNv4 [RFC 5575]
2227     """
2228
2229     # flow-spec NLRI:
2230     # +-----------------------------------+
2231     # |    length (0xnn or 0xfn nn)       |
2232     # +-----------------------------------+
2233     # |     RD   (8 octets)               |
2234     # +-----------------------------------+
2235     # |     NLRI value  (variable)        |
2236     # +-----------------------------------+
2237     ROUTE_FAMILY = RF_VPNv4_FLOWSPEC
2238     FLOWSPEC_FAMILY = 'vpnv4fs'
2239
2240     def __init__(self, length=0, route_dist=None, rules=None):
2241         super(FlowSpecVPNv4NLRI, self).__init__(length, rules)
2242         assert route_dist is not None
2243         self.route_dist = route_dist
2244
2245     @classmethod
2246     def _from_user(cls, route_dist, **kwargs):
2247         rules = []
2248         for k, v in kwargs.items():
2249             subcls = _FlowSpecComponentBase.lookup_type_name(
2250                 k, cls.ROUTE_FAMILY.afi)
2251             rule = subcls.from_str(str(v))
2252             rules.extend(rule)
2253         rules.sort(key=lambda x: x.type)
2254         return cls(route_dist=route_dist, rules=rules)
2255
2256     @classmethod
2257     def from_user(cls, route_dist, **kwargs):
2258         """
2259         Utility method for creating a NLRI instance.
2260
2261         This function returns a NLRI instance from human readable format value.
2262
2263         :param route_dist: Route Distinguisher.
2264         :param kwargs: See :py:mod:`ryu.lib.packet.bgp.FlowSpecIPv4NLRI`
2265
2266         Example::
2267
2268             >>> msg = bgp.FlowSpecIPv4NLRI.from_user(
2269             ...     route_dist='65000:1000',
2270             ...     dst_prefix='10.0.0.0/24',
2271             ...     src_prefix='20.0.0.1/24',
2272             ...     ip_proto=6,
2273             ...     port='80 | 8000',
2274             ...     dst_port='>9000 & <9050',
2275             ...     src_port='>=8500 & <=9000',
2276             ...     icmp_type=0,
2277             ...     icmp_code=6,
2278             ...     tcp_flags='SYN+ACK & !=URGENT',
2279             ...     packet_len=1000,
2280             ...     dscp='22 | 24',
2281             ...     fragment='LF | ==FF')
2282             >>>
2283         """
2284         return cls._from_user(route_dist, **kwargs)
2285
2286     @property
2287     def formatted_nlri_str(self):
2288         return '%s:%s' % (self.route_dist, self.prefix)
2289
2290
2291 class FlowSpecIPv6NLRI(_FlowSpecNLRIBase):
2292     """
2293     Flow Specification NLRI class for IPv6 [RFC draft-ietf-idr-flow-spec-v6-08]
2294     """
2295     ROUTE_FAMILY = RF_IPv6_FLOWSPEC
2296     FLOWSPEC_FAMILY = 'ipv6fs'
2297
2298     @classmethod
2299     def from_user(cls, **kwargs):
2300         """
2301         Utility method for creating a NLRI instance.
2302
2303         This function returns a NLRI instance from human readable format value.
2304
2305         :param kwargs: The following arguments are available.
2306
2307         =========== ============= ========= ==============================
2308         Argument    Value         Operator  Description
2309         =========== ============= ========= ==============================
2310         dst_prefix  IPv6 Prefix   Nothing   Destination Prefix.
2311         src_prefix  IPv6 Prefix   Nothing   Source Prefix.
2312         next_header Integer       Numeric   Next Header.
2313         port        Integer       Numeric   Port number.
2314         dst_port    Integer       Numeric   Destination port number.
2315         src_port    Integer       Numeric   Source port number.
2316         icmp_type   Integer       Numeric   ICMP type.
2317         icmp_code   Integer       Numeric   ICMP code.
2318         tcp_flags   Fixed string  Bitmask   TCP flags.
2319                                             Supported values are
2320                                             ``CWR``, ``ECN``, ``URGENT``,
2321                                             ``ACK``, ``PUSH``, ``RST``,
2322                                             ``SYN`` and ``FIN``.
2323         packet_len  Integer       Numeric   Packet length.
2324         dscp        Integer       Numeric   Differentiated Services
2325                                             Code Point.
2326         fragment    Fixed string  Bitmask   Fragment.
2327                                             Supported values are
2328                                             ``ISF`` (Is a fragment),
2329                                             ``FF`` (First fragment) and
2330                                             ``LF`` (Last fragment)
2331         flow_label   Intefer      Numeric   Flow Label.
2332         =========== ============= ========= ==============================
2333
2334         .. Note::
2335
2336             For ``dst_prefix`` and ``src_prefix``, you can give "offset" value
2337             like this: ``2001::2/128/32``. At this case, ``offset`` is 32.
2338             ``offset`` can be omitted, then ``offset`` is treated as 0.
2339         """
2340         return cls._from_user(**kwargs)
2341
2342
2343 class FlowSpecVPNv6NLRI(_FlowSpecNLRIBase):
2344     """
2345     Flow Specification NLRI class for VPNv6 [draft-ietf-idr-flow-spec-v6-08]
2346     """
2347
2348     # flow-spec NLRI:
2349     # +-----------------------------------+
2350     # |    length (0xnn or 0xfn nn)       |
2351     # +-----------------------------------+
2352     # |     RD   (8 octets)               |
2353     # +-----------------------------------+
2354     # |     NLRI value  (variable)        |
2355     # +-----------------------------------+
2356     ROUTE_FAMILY = RF_VPNv6_FLOWSPEC
2357     FLOWSPEC_FAMILY = 'vpnv6fs'
2358
2359     def __init__(self, length=0, route_dist=None, rules=None):
2360         super(FlowSpecVPNv6NLRI, self).__init__(length, rules)
2361         assert route_dist is not None
2362         self.route_dist = route_dist
2363
2364     @classmethod
2365     def _from_user(cls, route_dist, **kwargs):
2366         rules = []
2367         for k, v in kwargs.items():
2368             subcls = _FlowSpecComponentBase.lookup_type_name(
2369                 k, cls.ROUTE_FAMILY.afi)
2370             rule = subcls.from_str(str(v))
2371             rules.extend(rule)
2372         rules.sort(key=lambda x: x.type)
2373         return cls(route_dist=route_dist, rules=rules)
2374
2375     @classmethod
2376     def from_user(cls, route_dist, **kwargs):
2377         """
2378         Utility method for creating a NLRI instance.
2379
2380         This function returns a NLRI instance from human readable format value.
2381
2382         :param route_dist: Route Distinguisher.
2383         :param kwargs: See :py:mod:`ryu.lib.packet.bgp.FlowSpecIPv6NLRI`
2384         """
2385         return cls._from_user(route_dist, **kwargs)
2386
2387     @property
2388     def formatted_nlri_str(self):
2389         return '%s:%s' % (self.route_dist, self.prefix)
2390
2391
2392 class FlowSpecL2VPNNLRI(_FlowSpecNLRIBase):
2393     """
2394     Flow Specification NLRI class for L2VPN [draft-ietf-idr-flowspec-l2vpn-05]
2395     """
2396
2397     # flow-spec NLRI:
2398     # +-----------------------------------+
2399     # |    length (0xnn or 0xfn nn)       |
2400     # +-----------------------------------+
2401     # |     RD   (8 octets)               |
2402     # +-----------------------------------+
2403     # |     NLRI value  (variable)        |
2404     # +-----------------------------------+
2405     ROUTE_FAMILY = RF_L2VPN_FLOWSPEC
2406     FLOWSPEC_FAMILY = 'l2vpnfs'
2407
2408     def __init__(self, length=0, route_dist=None, rules=None):
2409         super(FlowSpecL2VPNNLRI, self).__init__(length, rules)
2410         assert route_dist is not None
2411         self.route_dist = route_dist
2412
2413     @classmethod
2414     def _from_user(cls, route_dist, **kwargs):
2415         rules = []
2416         for k, v in kwargs.items():
2417             subcls = _FlowSpecComponentBase.lookup_type_name(
2418                 k, cls.ROUTE_FAMILY.afi)
2419             rule = subcls.from_str(str(v))
2420             rules.extend(rule)
2421         rules.sort(key=lambda x: x.type)
2422         return cls(route_dist=route_dist, rules=rules)
2423
2424     @classmethod
2425     def from_user(cls, route_dist, **kwargs):
2426         """
2427         Utility method for creating a L2VPN NLRI instance.
2428
2429         This function returns a L2VPN NLRI instance
2430         from human readable format value.
2431
2432         :param kwargs: The following arguments are available.
2433
2434         ============== ============= ========= ==============================
2435         Argument       Value         Operator  Description
2436         ============== ============= ========= ==============================
2437         ether_type     Integer       Numeric   Ethernet Type.
2438         src_mac        Mac Address   Nothing   Source Mac address.
2439         dst_mac        Mac Address   Nothing   Destination Mac address.
2440         llc_ssap       Integer       Numeric   Source Service Access Point
2441                                                in LLC.
2442         llc_dsap       Integer       Numeric   Destination Service Access
2443                                                Point in LLC.
2444         llc_control    Integer       Numeric   Control field in LLC.
2445         snap           Integer       Numeric   Sub-Network Access Protocol
2446                                                field.
2447         vlan_id        Integer       Numeric   VLAN ID.
2448         vlan_cos       Integer       Numeric   VLAN COS field.
2449         inner_vlan_id  Integer       Numeric   Inner VLAN ID.
2450         inner_vlan_cos Integer       Numeric   Inner VLAN COS field.
2451         ============== ============= ========= ==============================
2452         """
2453         return cls._from_user(route_dist, **kwargs)
2454
2455     @property
2456     def formatted_nlri_str(self):
2457         return '%s:%s' % (self.route_dist, self.prefix)
2458
2459
2460 class _FlowSpecComponentBase(StringifyMixin, TypeDisp):
2461     """
2462     Base class for Flow Specification NLRI component
2463     """
2464     COMPONENT_NAME = None
2465
2466     _BASE_STR = '!B'
2467     _BASE_STR_SIZE = struct.calcsize(_BASE_STR)
2468
2469     # Dictionary of COMPONENT_NAME to subclass.
2470     # e.g.)
2471     #   _NAMES = {'dst_prefix': FlowSpecDestPrefix, ...}
2472     _NAMES = {}
2473
2474     def __init__(self, type_=None):
2475         if type_ is None:
2476             type_, _ = self._rev_lookup_type(self.__class__)
2477         self.type = type_
2478
2479     @classmethod
2480     def register_type(cls, type_, afi):
2481         cls._TYPES = cls._TYPES.copy()
2482         cls._NAMES = cls._NAMES.copy()
2483
2484         def _register_type(subcls):
2485             cls._TYPES[(type_, afi)] = subcls
2486             cls._NAMES[(subcls.COMPONENT_NAME, afi)] = subcls
2487             cls._REV_TYPES = None
2488             return subcls
2489
2490         return _register_type
2491
2492     @classmethod
2493     def lookup_type_name(cls, type_name, afi):
2494         return cls._NAMES[(type_name, afi)]
2495
2496     @classmethod
2497     def _lookup_type(cls, type_, afi):
2498         try:
2499             return cls._TYPES[(type_, afi)]
2500         except KeyError:
2501             return cls._UNKNOWN_TYPE
2502
2503     @classmethod
2504     def parse_header(cls, rest, afi):
2505         (type_,) = struct.unpack_from(
2506             cls._BASE_STR, six.binary_type(rest))
2507         rest = rest[cls._BASE_STR_SIZE:]
2508         return cls._lookup_type(type_, afi), rest
2509
2510     def serialize_header(self):
2511         return struct.pack(self._BASE_STR, self.type)
2512
2513
2514 class _FlowSpecIPv4Component(_FlowSpecComponentBase):
2515     """
2516     Base class for Flow Specification for IPv4 NLRI component
2517     """
2518     TYPE_DESTINATION_PREFIX = 0x01
2519     TYPE_SOURCE_PREFIX = 0x02
2520     TYPE_PROTOCOL = 0x03
2521     TYPE_PORT = 0x04
2522     TYPE_DESTINATION_PORT = 0x05
2523     TYPE_SOURCE_PORT = 0x06
2524     TYPE_ICMP = 0x07
2525     TYPE_ICMP_CODE = 0x08
2526     TYPE_TCP_FLAGS = 0x09
2527     TYPE_PACKET_LENGTH = 0x0a
2528     TYPE_DIFFSERV_CODE_POINT = 0x0b
2529     TYPE_FRAGMENT = 0x0c
2530
2531
2532 class _FlowSpecIPv6Component(_FlowSpecComponentBase):
2533     """
2534     Base class for Flow Specification for IPv6 NLRI component
2535     """
2536     TYPE_DESTINATION_PREFIX = 0x01
2537     TYPE_SOURCE_PREFIX = 0x02
2538     TYPE_NEXT_HEADER = 0x03
2539     TYPE_PORT = 0x04
2540     TYPE_DESTINATION_PORT = 0x05
2541     TYPE_SOURCE_PORT = 0x06
2542     TYPE_ICMP = 0x07
2543     TYPE_ICMP_CODE = 0x08
2544     TYPE_TCP_FLAGS = 0x09
2545     TYPE_PACKET_LENGTH = 0x0a
2546     TYPE_DIFFSERV_CODE_POINT = 0x0b
2547     TYPE_FRAGMENT = 0x0c
2548     TYPE_FLOW_LABEL = 0x0d
2549
2550
2551 class _FlowSpecL2VPNComponent(_FlowSpecComponentBase):
2552     """
2553     Base class for Flow Specification for L2VPN NLRI component
2554     """
2555     TYPE_ETHER_TYPE = 0x0e
2556     TYPE_SOURCE_MAC = 0x0f
2557     TYPE_DESTINATION_MAC = 0x10
2558     TYPE_LLC_DSAP = 0x11
2559     TYPE_LLC_SSAP = 0x12
2560     TYPE_LLC_CONTROL = 0x13
2561     TYPE_SNAP = 0x14
2562     TYPE_VLAN_ID = 0x15
2563     TYPE_VLAN_COS = 0x16
2564     TYPE_INNER_VLAN_ID = 0x17
2565     TYPE_INNER_VLAN_COS = 0x18
2566
2567
2568 @_FlowSpecComponentBase.register_unknown_type()
2569 class FlowSpecComponentUnknown(_FlowSpecComponentBase):
2570     """
2571     Unknown component type for Flow Specification NLRI component
2572     """
2573
2574     def __init__(self, buf, type_=None):
2575         super(FlowSpecComponentUnknown, self).__init__(type_)
2576         self.buf = buf
2577
2578     @classmethod
2579     def parse_body(cls, buf):
2580         return cls(buf), None
2581
2582     def serialize_body(self):
2583         return self.buf
2584
2585
2586 class _FlowSpecPrefixBase(_FlowSpecIPv4Component, IPAddrPrefix):
2587     """
2588     Prefix base class for Flow Specification NLRI component
2589     """
2590
2591     def __init__(self, length, addr, type_=None):
2592         super(_FlowSpecPrefixBase, self).__init__(type_)
2593         self.length = length
2594         prefix = "%s/%s" % (addr, length)
2595         self.addr = str(netaddr.ip.IPNetwork(prefix).network)
2596
2597     @classmethod
2598     def parse_body(cls, buf):
2599         return cls.parser(buf)
2600
2601     def serialize_body(self):
2602         return self.serialize()
2603
2604     @classmethod
2605     def from_str(cls, value):
2606         rule = []
2607         addr, length = value.split('/')
2608         rule.append(cls(int(length), addr))
2609         return rule
2610
2611     @property
2612     def value(self):
2613         return "%s/%s" % (self.addr, self.length)
2614
2615     def to_str(self):
2616         return self.value
2617
2618
2619 class _FlowSpecIPv6PrefixBase(_FlowSpecIPv6Component, IP6AddrPrefix):
2620     """
2621     Prefix base class for Flow Specification NLRI component
2622     """
2623     _PACK_STR = '!BB'  # length, offset
2624
2625     def __init__(self, length, addr, offset=0, type_=None):
2626         super(_FlowSpecIPv6PrefixBase, self).__init__(type_)
2627         self.length = length
2628         self.offset = offset
2629         prefix = "%s/%s" % (addr, length)
2630         self.addr = str(netaddr.ip.IPNetwork(prefix).network)
2631
2632     @classmethod
2633     def parser(cls, buf):
2634         (length, offset) = struct.unpack_from(
2635             cls._PACK_STR, six.binary_type(buf))
2636         rest = buf[struct.calcsize(cls._PACK_STR):]
2637         byte_length = (length + 7) // 8
2638         addr = cls._from_bin(rest[:byte_length])
2639         rest = rest[byte_length:]
2640         return cls(length=length, offset=offset, addr=addr), rest
2641
2642     @classmethod
2643     def parse_body(cls, buf):
2644         return cls.parser(buf)
2645
2646     def serialize(self):
2647         byte_length = (self.length + 7) // 8
2648         bin_addr = self._to_bin(self.addr)[:byte_length]
2649         buf = bytearray()
2650         msg_pack_into(self._PACK_STR, buf, 0, self.length, self.offset)
2651         return buf + bin_addr
2652
2653     def serialize_body(self):
2654         return self.serialize()
2655
2656     @classmethod
2657     def from_str(cls, value):
2658         rule = []
2659         values = value.split('/')
2660         if len(values) == 3:
2661             rule.append(cls(int(values[1]), values[0], offset=int(values[2])))
2662         else:
2663             rule.append(cls(int(values[1]), values[0]))
2664         return rule
2665
2666     @property
2667     def value(self):
2668         return "%s/%s/%s" % (self.addr, self.length, self.offset)
2669
2670     def to_str(self):
2671         return self.value
2672
2673
2674 class _FlowSpecL2VPNPrefixBase(_FlowSpecL2VPNComponent):
2675     """
2676     Prefix base class for Flow Specification NLRI component
2677     """
2678     _PACK_STR = "!B6s"
2679
2680     def __init__(self, length, addr, type_=None):
2681         super(_FlowSpecL2VPNPrefixBase, self).__init__(type_)
2682         self.length = length
2683         self.addr = addr.lower()
2684
2685     @classmethod
2686     def parse_body(cls, buf):
2687         (length, addr) = struct.unpack_from(
2688             cls._PACK_STR, six.binary_type(buf))
2689         rest = buf[struct.calcsize(cls._PACK_STR):]
2690         addr = addrconv.mac.bin_to_text(addr)
2691         return cls(length=length, addr=addr), rest
2692
2693     def serialize(self):
2694         addr = addrconv.mac.text_to_bin(self.addr)
2695         return struct.pack(self._PACK_STR, self.length, addr)
2696
2697     def serialize_body(self):
2698         return self.serialize()
2699
2700     @classmethod
2701     def from_str(cls, value):
2702         return [cls(len(value.split(':')), value)]
2703
2704     @property
2705     def value(self):
2706         return self.addr
2707
2708     def to_str(self):
2709         return self.value
2710
2711
2712 @_FlowSpecComponentBase.register_type(
2713     _FlowSpecIPv4Component.TYPE_DESTINATION_PREFIX, addr_family.IP)
2714 class FlowSpecDestPrefix(_FlowSpecPrefixBase):
2715     """
2716     Destination Prefix for Flow Specification NLRI component
2717     """
2718     COMPONENT_NAME = 'dst_prefix'
2719
2720
2721 @_FlowSpecComponentBase.register_type(
2722     _FlowSpecIPv4Component.TYPE_SOURCE_PREFIX, addr_family.IP)
2723 class FlowSpecSrcPrefix(_FlowSpecPrefixBase):
2724     """
2725     Source Prefix for Flow Specification NLRI component
2726     """
2727     COMPONENT_NAME = 'src_prefix'
2728
2729
2730 @_FlowSpecComponentBase.register_type(
2731     _FlowSpecIPv6Component.TYPE_DESTINATION_PREFIX, addr_family.IP6)
2732 class FlowSpecIPv6DestPrefix(_FlowSpecIPv6PrefixBase):
2733     """
2734     IPv6 destination Prefix for Flow Specification NLRI component
2735     """
2736     COMPONENT_NAME = 'dst_prefix'
2737
2738
2739 @_FlowSpecComponentBase.register_type(
2740     _FlowSpecIPv6Component.TYPE_SOURCE_PREFIX, addr_family.IP6)
2741 class FlowSpecIPv6SrcPrefix(_FlowSpecIPv6PrefixBase):
2742     """
2743     IPv6 source Prefix for Flow Specification NLRI component
2744     """
2745     COMPONENT_NAME = 'src_prefix'
2746
2747
2748 class _FlowSpecOperatorBase(_FlowSpecComponentBase):
2749     """Operator base class for Flow Specification NLRI component
2750
2751     ===================== ===============================================
2752     Attribute             Description
2753     ===================== ===============================================
2754     operator              Match conditions.
2755     value                 Value of component.
2756     ===================== ===============================================
2757     """
2758     _OPE_PACK_STR = '!B'
2759     _OPE_PACK_STR_SIZE = struct.calcsize(_OPE_PACK_STR)
2760     _VAL_PACK_STR = '!%ds'
2761
2762     END_OF_LIST = 1 << 7     # END OF LIST bit
2763     AND = 1 << 6             # AND bit
2764     OR = 0                   # OR
2765     _LENGTH_BIT_MASK = 0x30  # The mask for length of the value
2766
2767     _logical_conditions = {
2768         "|": OR,
2769         "&": AND,
2770     }
2771     _comparison_conditions = {}
2772
2773     def __init__(self, operator, value, type_=None):
2774         super(_FlowSpecOperatorBase, self).__init__(type_)
2775         self.operator = operator
2776         self.value = value
2777
2778     @classmethod
2779     def parse_body(cls, rest):
2780         (operator,) = struct.unpack_from(cls._OPE_PACK_STR,
2781                                          six.binary_type(rest))
2782         rest = rest[cls._OPE_PACK_STR_SIZE:]
2783         length = 1 << ((operator & cls._LENGTH_BIT_MASK) >> 4)
2784         value_type = type_desc.IntDescr(length)
2785         value = value_type.to_user(rest)
2786         rest = rest[length:]
2787
2788         return cls(operator, value), rest
2789
2790     def serialize_body(self):
2791         byte_length = (self.value.bit_length() + 7) // 8 or 1
2792         length = int(math.ceil(math.log(byte_length, 2)))
2793         self.operator |= length << 4
2794         buf = struct.pack(self._OPE_PACK_STR, self.operator)
2795         value_type = type_desc.IntDescr(1 << length)
2796         buf += struct.pack(self._VAL_PACK_STR % (1 << length),
2797                            value_type.from_user(self.value))
2798
2799         return buf
2800
2801     @classmethod
2802     def from_str(cls, val):
2803         operator = 0
2804         rules = []
2805
2806         # e.g.)
2807         # value = '80 | ==90|>=8000&<=9000 | <100 & >110'
2808         # elements = ['80', '|', '==', '90', '|', '>=', '8000', '&',
2809         #             '<=', '9000', '|', '<', '100', '&', '>', '110']
2810         elements = [v.strip() for v in re.split(
2811             r'([0-9]+)|([A-Z]+)|(\|&\+)|([!=<>]+)', val) if v and v.strip()]
2812
2813         elms_iter = iter(elements)
2814
2815         for elm in elms_iter:
2816             if elm in cls._logical_conditions:
2817                 # ['&', '|']
2818                 operator |= cls._logical_conditions[elm]
2819                 continue
2820             elif elm in cls._comparison_conditions:
2821                 # ['=', '<', '>', '<=', '>=' ] or ['=', '!=']
2822                 operator |= cls._comparison_conditions[elm]
2823                 continue
2824             elif elm == '+':
2825                 # If keyword "+" is used, add the value to the previous rule.
2826                 #  e.g.) 'SYN+ACK' or '!=SYN+ACK'
2827                 rules[-1].value |= cls._to_value(next(elms_iter))
2828                 continue
2829
2830             value = cls._to_value(elm)
2831
2832             operator = cls.normalize_operator(operator)
2833
2834             rules.append(cls(operator, value))
2835             operator = 0
2836
2837         return rules
2838
2839     @classmethod
2840     def _to_value(cls, value):
2841         return value
2842
2843     @classmethod
2844     def normalize_operator(cls, operator):
2845         return operator
2846
2847
2848 class _FlowSpecNumeric(_FlowSpecOperatorBase):
2849     """
2850     Numeric operator class for Flow Specification NLRI component
2851     """
2852     # Numeric operator format
2853     #  0   1   2   3   4   5   6   7
2854     # +---+---+---+---+---+---+---+---+
2855     # | e | a |  len  | 0 |lt |gt |eq |
2856     # +---+---+---+---+---+---+---+---+
2857
2858     LT = 1 << 2  # Less than comparison bit
2859     GT = 1 << 1  # Greater than comparison bit
2860     EQ = 1 << 0  # Equality bit
2861
2862     _comparison_conditions = {
2863         '==': EQ,
2864         '<': LT,
2865         '>': GT,
2866         '<=': LT | EQ,
2867         '>=': GT | EQ
2868     }
2869
2870     @classmethod
2871     def _to_value(cls, value):
2872         try:
2873             return int(str(value), 0)
2874         except ValueError:
2875             raise ValueError('Invalid params: %s="%s"' % (
2876                 cls.COMPONENT_NAME, value))
2877
2878     def to_str(self):
2879         string = ""
2880         if self.operator & self.AND:
2881             string += "&"
2882
2883         operator = self.operator & (self.LT | self.GT | self.EQ)
2884         for k, v in self._comparison_conditions.items():
2885             if operator == v:
2886                 string += k
2887
2888         string += str(self.value)
2889
2890         return string
2891
2892     @classmethod
2893     def normalize_operator(cls, operator):
2894         if operator & (cls.LT | cls.GT | cls.EQ):
2895             return operator
2896         else:
2897             return operator | cls.EQ
2898
2899
2900 class _FlowSpecBitmask(_FlowSpecOperatorBase):
2901     """
2902     Bitmask operator class for Flow Specification NLRI component
2903     """
2904     # Bitmask operator format
2905     #  0   1   2   3   4   5   6   7
2906     # +---+---+---+---+---+---+---+---+
2907     # | e | a |  len  | 0 | 0 |not| m |
2908     # +---+---+---+---+---+---+---+---+
2909
2910     NOT = 1 << 1    # NOT bit
2911     MATCH = 1 << 0  # MATCH bit
2912
2913     _comparison_conditions = {
2914         '!=': NOT,
2915         '==': MATCH,
2916     }
2917
2918     _bitmask_flags = {}
2919
2920     @classmethod
2921     def _to_value(cls, value):
2922         try:
2923             return cls.__dict__[value]
2924         except KeyError:
2925             raise ValueError('Invalid params: %s="%s"' % (
2926                 cls.COMPONENT_NAME, value))
2927
2928     def to_str(self):
2929         string = ""
2930         if self.operator & self.AND:
2931             string += "&"
2932
2933         operator = self.operator & (self.NOT | self.MATCH)
2934         for k, v in self._comparison_conditions.items():
2935             if operator == v:
2936                 string += k
2937
2938         plus = ""
2939         for k, v in self._bitmask_flags.items():
2940             if self.value & k:
2941                 string += plus + v
2942                 plus = "+"
2943
2944         return string
2945
2946
2947 @_FlowSpecComponentBase.register_type(
2948     _FlowSpecIPv4Component.TYPE_PROTOCOL, addr_family.IP)
2949 class FlowSpecIPProtocol(_FlowSpecNumeric):
2950     """IP Protocol for Flow Specification NLRI component
2951
2952     Set the IP protocol number at value.
2953     """
2954     COMPONENT_NAME = 'ip_proto'
2955
2956
2957 @_FlowSpecComponentBase.register_type(
2958     _FlowSpecIPv6Component.TYPE_NEXT_HEADER, addr_family.IP6)
2959 class FlowSpecNextHeader(_FlowSpecNumeric):
2960     """Next Header value in IPv6 packets
2961
2962     Set the IP protocol number at value
2963     """
2964     COMPONENT_NAME = 'next_header'
2965
2966
2967 @_FlowSpecComponentBase.register_type(
2968     _FlowSpecIPv4Component.TYPE_PORT, addr_family.IP)
2969 @_FlowSpecComponentBase.register_type(
2970     _FlowSpecIPv6Component.TYPE_PORT, addr_family.IP6)
2971 class FlowSpecPort(_FlowSpecNumeric):
2972     """Port number for Flow Specification NLRI component
2973
2974     Set the source or destination TCP/UDP ports at value.
2975     """
2976     COMPONENT_NAME = 'port'
2977
2978
2979 @_FlowSpecComponentBase.register_type(
2980     _FlowSpecIPv4Component.TYPE_DESTINATION_PORT, addr_family.IP)
2981 @_FlowSpecComponentBase.register_type(
2982     _FlowSpecIPv6Component.TYPE_DESTINATION_PORT, addr_family.IP6)
2983 class FlowSpecDestPort(_FlowSpecNumeric):
2984     """Destination port number for Flow Specification NLRI component
2985
2986     Set the destination port of a TCP or UDP packet at value.
2987     """
2988     COMPONENT_NAME = 'dst_port'
2989
2990
2991 @_FlowSpecComponentBase.register_type(
2992     _FlowSpecIPv4Component.TYPE_SOURCE_PORT, addr_family.IP)
2993 @_FlowSpecComponentBase.register_type(
2994     _FlowSpecIPv6Component.TYPE_SOURCE_PORT, addr_family.IP6)
2995 class FlowSpecSrcPort(_FlowSpecNumeric):
2996     """Source port number for Flow Specification NLRI component
2997
2998     Set the source port of a TCP or UDP packet at value.
2999     """
3000     COMPONENT_NAME = 'src_port'
3001
3002
3003 @_FlowSpecComponentBase.register_type(
3004     _FlowSpecIPv4Component.TYPE_ICMP, addr_family.IP)
3005 @_FlowSpecComponentBase.register_type(
3006     _FlowSpecIPv6Component.TYPE_ICMP, addr_family.IP6)
3007 class FlowSpecIcmpType(_FlowSpecNumeric):
3008     """ICMP type for Flow Specification NLRI component
3009
3010     Set the type field of an ICMP packet at value.
3011     """
3012     COMPONENT_NAME = 'icmp_type'
3013
3014
3015 @_FlowSpecComponentBase.register_type(
3016     _FlowSpecIPv4Component.TYPE_ICMP_CODE, addr_family.IP)
3017 @_FlowSpecComponentBase.register_type(
3018     _FlowSpecIPv6Component.TYPE_ICMP_CODE, addr_family.IP6)
3019 class FlowSpecIcmpCode(_FlowSpecNumeric):
3020     """ICMP code Flow Specification NLRI component
3021
3022     Set the code field of an ICMP packet at value.
3023     """
3024     COMPONENT_NAME = 'icmp_code'
3025
3026
3027 @_FlowSpecComponentBase.register_type(
3028     _FlowSpecIPv4Component.TYPE_TCP_FLAGS, addr_family.IP)
3029 @_FlowSpecComponentBase.register_type(
3030     _FlowSpecIPv6Component.TYPE_TCP_FLAGS, addr_family.IP6)
3031 class FlowSpecTCPFlags(_FlowSpecBitmask):
3032     """TCP flags for Flow Specification NLRI component
3033
3034     Supported TCP flags are CWR, ECN, URGENT, ACK, PUSH, RST, SYN and FIN.
3035     """
3036     COMPONENT_NAME = 'tcp_flags'
3037
3038     # bitmask format
3039     #  0    1    2    3    4    5    6    7
3040     # +----+----+----+----+----+----+----+----+
3041     # |CWR |ECN |URG |ACK |PSH |RST |SYN |FIN |
3042     # +----+----+----+----+----+----+----+----+
3043
3044     CWR = 1 << 7
3045     ECN = 1 << 6
3046     URGENT = 1 << 5
3047     ACK = 1 << 4
3048     PUSH = 1 << 3
3049     RST = 1 << 2
3050     SYN = 1 << 1
3051     FIN = 1 << 0
3052
3053     _bitmask_flags = collections.OrderedDict()
3054     _bitmask_flags[SYN] = 'SYN'
3055     _bitmask_flags[ACK] = 'ACK'
3056     _bitmask_flags[FIN] = 'FIN'
3057     _bitmask_flags[RST] = 'RST'
3058     _bitmask_flags[PUSH] = 'PUSH'
3059     _bitmask_flags[URGENT] = 'URGENT'
3060     _bitmask_flags[ECN] = 'ECN'
3061     _bitmask_flags[CWR] = 'CWR'
3062
3063
3064 @_FlowSpecComponentBase.register_type(
3065     _FlowSpecIPv4Component.TYPE_PACKET_LENGTH, addr_family.IP)
3066 @_FlowSpecComponentBase.register_type(
3067     _FlowSpecIPv6Component.TYPE_PACKET_LENGTH, addr_family.IP6)
3068 class FlowSpecPacketLen(_FlowSpecNumeric):
3069     """Packet length for Flow Specification NLRI component
3070
3071     Set the total IP packet length at value.
3072     """
3073     COMPONENT_NAME = 'packet_len'
3074
3075
3076 @_FlowSpecComponentBase.register_type(
3077     _FlowSpecIPv4Component.TYPE_DIFFSERV_CODE_POINT, addr_family.IP)
3078 @_FlowSpecComponentBase.register_type(
3079     _FlowSpecIPv6Component.TYPE_DIFFSERV_CODE_POINT, addr_family.IP6)
3080 class FlowSpecDSCP(_FlowSpecNumeric):
3081     """Diffserv Code Point for Flow Specification NLRI component
3082
3083     Set the 6-bit DSCP field at value. [RFC2474]
3084     """
3085     COMPONENT_NAME = 'dscp'
3086
3087
3088 @_FlowSpecComponentBase.register_type(
3089     _FlowSpecIPv4Component.TYPE_FRAGMENT, addr_family.IP)
3090 class FlowSpecFragment(_FlowSpecBitmask):
3091     """Fragment for Flow Specification NLRI component
3092
3093     Set the bitmask for operand format at value.
3094     The following values are supported.
3095
3096     ========== ===============================================
3097     Attribute  Description
3098     ========== ===============================================
3099     LF         Last fragment
3100     FF         First fragment
3101     ISF        Is a fragment
3102     DF         Don't fragment
3103     ========== ===============================================
3104     """
3105     COMPONENT_NAME = 'fragment'
3106
3107     # bitmask format
3108     #  0   1   2   3   4   5   6   7
3109     # +---+---+---+---+---+---+---+---+
3110     # |   Reserved    |LF |FF |IsF|DF |
3111     # +---+---+---+---+---+---+---+---+
3112
3113     LF = 1 << 3
3114     FF = 1 << 2
3115     ISF = 1 << 1
3116     DF = 1 << 0
3117
3118     _bitmask_flags = collections.OrderedDict()
3119     _bitmask_flags[LF] = 'LF'
3120     _bitmask_flags[FF] = 'FF'
3121     _bitmask_flags[ISF] = 'ISF'
3122     _bitmask_flags[DF] = 'DF'
3123
3124
3125 @_FlowSpecComponentBase.register_type(
3126     _FlowSpecIPv6Component.TYPE_FRAGMENT, addr_family.IP6)
3127 class FlowSpecIPv6Fragment(_FlowSpecBitmask):
3128     """Fragment for Flow Specification for IPv6 NLRI component
3129
3130     ========== ===============================================
3131     Attribute  Description
3132     ========== ===============================================
3133     LF         Last fragment
3134     FF         First fragment
3135     ISF        Is a fragment
3136     ========== ===============================================
3137     """
3138     COMPONENT_NAME = 'fragment'
3139
3140     # bitmask format
3141     #  0   1   2   3   4   5   6   7
3142     # +---+---+---+---+---+---+---+---+
3143     # |   Reserved    |LF |FF |IsF| 0 |
3144     # +---+---+---+---+---+---+---+---+
3145
3146     LF = 1 << 3
3147     FF = 1 << 2
3148     ISF = 1 << 1
3149
3150     _bitmask_flags = collections.OrderedDict()
3151     _bitmask_flags[LF] = 'LF'
3152     _bitmask_flags[FF] = 'FF'
3153     _bitmask_flags[ISF] = 'ISF'
3154
3155
3156 @_FlowSpecComponentBase.register_type(
3157     _FlowSpecL2VPNComponent.TYPE_ETHER_TYPE, addr_family.L2VPN)
3158 class FlowSpecEtherType(_FlowSpecNumeric):
3159     """Ethernet Type field in an Ethernet frame.
3160
3161     Set the 2 byte value of an Ethernet Type field at value.
3162     """
3163     COMPONENT_NAME = 'ether_type'
3164
3165
3166 @_FlowSpecComponentBase.register_type(
3167     _FlowSpecL2VPNComponent.TYPE_SOURCE_MAC, addr_family.L2VPN)
3168 class FlowSpecSourceMac(_FlowSpecL2VPNPrefixBase):
3169     """Source Mac Address.
3170
3171     Set the Mac Address at value.
3172     """
3173     COMPONENT_NAME = 'src_mac'
3174
3175
3176 @_FlowSpecComponentBase.register_type(
3177     _FlowSpecL2VPNComponent.TYPE_DESTINATION_MAC, addr_family.L2VPN)
3178 class FlowSpecDestinationMac(_FlowSpecL2VPNPrefixBase):
3179     """Destination Mac Address.
3180
3181     Set the Mac Address at value.
3182     """
3183     COMPONENT_NAME = 'dst_mac'
3184
3185
3186 @_FlowSpecComponentBase.register_type(
3187     _FlowSpecL2VPNComponent.TYPE_LLC_DSAP, addr_family.L2VPN)
3188 class FlowSpecLLCDSAP(_FlowSpecNumeric):
3189     """Destination SAP field in LLC header in an Ethernet frame.
3190
3191     Set the 2 byte value of an Destination SAP at value.
3192     """
3193     COMPONENT_NAME = 'llc_dsap'
3194
3195
3196 @_FlowSpecComponentBase.register_type(
3197     _FlowSpecL2VPNComponent.TYPE_LLC_SSAP, addr_family.L2VPN)
3198 class FlowSpecLLCSSAP(_FlowSpecNumeric):
3199     """Source SAP field in LLC header in an Ethernet frame.
3200
3201     Set the 2 byte value of an Source SAP at value.
3202     """
3203     COMPONENT_NAME = 'llc_ssap'
3204
3205
3206 @_FlowSpecComponentBase.register_type(
3207     _FlowSpecL2VPNComponent.TYPE_LLC_CONTROL, addr_family.L2VPN)
3208 class FlowSpecLLCControl(_FlowSpecNumeric):
3209     """Control field in LLC header in an Ethernet frame.
3210
3211     Set the Contorol field at value.
3212     """
3213     COMPONENT_NAME = 'llc_control'
3214
3215
3216 @_FlowSpecComponentBase.register_type(
3217     _FlowSpecL2VPNComponent.TYPE_SNAP, addr_family.L2VPN)
3218 class FlowSpecSNAP(_FlowSpecNumeric):
3219     """Sub-Network Access Protocol field in an Ethernet frame.
3220
3221     Set the 5 byte SNAP field at value.
3222     """
3223     COMPONENT_NAME = 'snap'
3224
3225
3226 @_FlowSpecComponentBase.register_type(
3227     _FlowSpecL2VPNComponent.TYPE_VLAN_ID, addr_family.L2VPN)
3228 class FlowSpecVLANID(_FlowSpecNumeric):
3229     """VLAN ID.
3230
3231     Set VLAN ID at value.
3232     """
3233     COMPONENT_NAME = 'vlan_id'
3234
3235
3236 @_FlowSpecComponentBase.register_type(
3237     _FlowSpecL2VPNComponent.TYPE_VLAN_COS, addr_family.L2VPN)
3238 class FlowSpecVLANCoS(_FlowSpecNumeric):
3239     """VLAN CoS Fields in an Ethernet frame.
3240
3241     Set the 3 bit CoS field at value.
3242     """
3243     COMPONENT_NAME = 'vlan_cos'
3244
3245
3246 @_FlowSpecComponentBase.register_type(
3247     _FlowSpecL2VPNComponent.TYPE_INNER_VLAN_ID, addr_family.L2VPN)
3248 class FlowSpecInnerVLANID(_FlowSpecNumeric):
3249     """Inner VLAN ID.
3250
3251     Set VLAN ID at value.
3252     """
3253     COMPONENT_NAME = 'inner_vlan_id'
3254
3255
3256 @_FlowSpecComponentBase.register_type(
3257     _FlowSpecL2VPNComponent.TYPE_INNER_VLAN_COS, addr_family.L2VPN)
3258 class FlowSpecInnerVLANCoS(_FlowSpecNumeric):
3259     """VLAN CoS Fields in an Inner Ethernet frame.
3260
3261     Set the 3 bit CoS field at value..
3262     """
3263     COMPONENT_NAME = 'inner_vlan_cos'
3264
3265
3266 @_FlowSpecComponentBase.register_type(
3267     _FlowSpecIPv6Component.TYPE_FLOW_LABEL, addr_family.IP6)
3268 class FlowSpecIPv6FlowLabel(_FlowSpecNumeric):
3269     COMPONENT_NAME = 'flow_label'
3270
3271
3272 @functools.total_ordering
3273 class RouteTargetMembershipNLRI(StringifyMixin):
3274     """Route Target Membership NLRI.
3275
3276     Route Target membership NLRI is advertised in BGP UPDATE messages using
3277     the MP_REACH_NLRI and MP_UNREACH_NLRI attributes.
3278     """
3279
3280     ROUTE_FAMILY = RF_RTC_UC
3281     DEFAULT_AS = '0:0'
3282     DEFAULT_RT = '0:0'
3283
3284     def __init__(self, origin_as, route_target):
3285         # If given is not default_as and default_rt
3286         if not (origin_as is self.DEFAULT_AS and
3287                 route_target is self.DEFAULT_RT):
3288             # We validate them
3289             if (not self._is_valid_asn(origin_as) or
3290                     not self._is_valid_ext_comm_attr(route_target)):
3291                 raise ValueError('Invalid params.')
3292         self.origin_as = origin_as
3293         self.route_target = route_target
3294
3295     def _is_valid_asn(self, asn):
3296         """Returns True if the given AS number is Two or Four Octet."""
3297         if isinstance(asn, six.integer_types) and 0 <= asn <= 0xffffffff:
3298             return True
3299         else:
3300             return False
3301
3302     def _is_valid_ext_comm_attr(self, attr):
3303         """Validates *attr* as string representation of RT or SOO.
3304
3305         Returns True if *attr* is as per our convention of RT or SOO, else
3306         False. Our convention is to represent RT/SOO is a string with format:
3307         *global_admin_part:local_admin_path*
3308         """
3309         is_valid = True
3310
3311         if not isinstance(attr, str):
3312             is_valid = False
3313         else:
3314             first, second = attr.split(':')
3315             try:
3316                 if '.' in first:
3317                     socket.inet_aton(first)
3318                 else:
3319                     int(first)
3320                     int(second)
3321             except (ValueError, socket.error):
3322                 is_valid = False
3323
3324         return is_valid
3325
3326     @property
3327     def formatted_nlri_str(self):
3328         return "%s:%s" % (self.origin_as, self.route_target)
3329
3330     def is_default_rtnlri(self):
3331         if (self._origin_as is self.DEFAULT_AS and
3332                 self._route_target is self.DEFAULT_RT):
3333             return True
3334         return False
3335
3336     def __lt__(self, other):
3337         return ((self.origin_as, self.route_target) <
3338                 (other.origin_as, other.route_target))
3339
3340     def __eq__(self, other):
3341         return ((self.origin_as, self.route_target) ==
3342                 (other.origin_as, other.route_target))
3343
3344     def __hash__(self):
3345         return hash((self.origin_as, self.route_target))
3346
3347     @classmethod
3348     def parser(cls, buf):
3349         idx = 0
3350
3351         # Extract origin AS.
3352         origin_as, = struct.unpack_from('!I', buf, idx)
3353         idx += 4
3354
3355         # Extract route target.
3356         route_target = _ExtendedCommunity(buf[idx:])
3357         return cls(origin_as, route_target)
3358
3359     def serialize(self):
3360         rt_nlri = b''
3361         if not self.is_default_rtnlri():
3362             rt_nlri += struct.pack('!I', self.origin_as)
3363             # Encode route target
3364             rt_nlri += self.route_target.serialize()
3365
3366         # RT Nlri is 12 octets
3367         return struct.pack('B', (8 * 12)) + rt_nlri
3368
3369
3370 def _addr_class_key(route_family):
3371     return route_family.afi, route_family.safi
3372
3373
3374 _ADDR_CLASSES = {
3375     _addr_class_key(RF_IPv4_UC): IPAddrPrefix,
3376     _addr_class_key(RF_IPv6_UC): IP6AddrPrefix,
3377     _addr_class_key(RF_IPv4_MPLS): LabelledIPAddrPrefix,
3378     _addr_class_key(RF_IPv6_MPLS): LabelledIP6AddrPrefix,
3379     _addr_class_key(RF_IPv4_VPN): LabelledVPNIPAddrPrefix,
3380     _addr_class_key(RF_IPv6_VPN): LabelledVPNIP6AddrPrefix,
3381     _addr_class_key(RF_L2_EVPN): EvpnNLRI,
3382     _addr_class_key(RF_IPv4_FLOWSPEC): FlowSpecIPv4NLRI,
3383     _addr_class_key(RF_IPv6_FLOWSPEC): FlowSpecIPv6NLRI,
3384     _addr_class_key(RF_VPNv4_FLOWSPEC): FlowSpecVPNv4NLRI,
3385     _addr_class_key(RF_VPNv6_FLOWSPEC): FlowSpecVPNv6NLRI,
3386     _addr_class_key(RF_L2VPN_FLOWSPEC): FlowSpecL2VPNNLRI,
3387     _addr_class_key(RF_RTC_UC): RouteTargetMembershipNLRI,
3388 }
3389
3390
3391 def _get_addr_class(afi, safi):
3392     try:
3393         return _ADDR_CLASSES[(afi, safi)]
3394     except KeyError:
3395         return _BinAddrPrefix
3396
3397
3398 class _OptParam(StringifyMixin, TypeDisp, _Value):
3399     _PACK_STR = '!BB'  # type, length
3400
3401     def __init__(self, type_, value=None, length=None):
3402         if type_ is None:
3403             type_ = self._rev_lookup_type(self.__class__)
3404         self.type = type_
3405         self.length = length
3406         if value is not None:
3407             self.value = value
3408
3409     @classmethod
3410     def parser(cls, buf):
3411         (type_, length) = struct.unpack_from(cls._PACK_STR,
3412                                              six.binary_type(buf))
3413         rest = buf[struct.calcsize(cls._PACK_STR):]
3414         value = bytes(rest[:length])
3415         rest = rest[length:]
3416         subcls = cls._lookup_type(type_)
3417         caps = subcls.parse_value(value)
3418         if not isinstance(caps, list):
3419             caps = [subcls(type_=type_, length=length, **caps[0])]
3420         return caps, rest
3421
3422     def serialize(self):
3423         # fixup
3424         value = self.serialize_value()
3425         self.length = len(value)
3426
3427         buf = bytearray()
3428         msg_pack_into(self._PACK_STR, buf, 0, self.type, self.length)
3429         return buf + value
3430
3431
3432 @_OptParam.register_unknown_type()
3433 class BGPOptParamUnknown(_OptParam):
3434     @classmethod
3435     def parse_value(cls, buf):
3436         return {
3437             'value': buf
3438         }, cls
3439
3440     def serialize_value(self):
3441         return self.value
3442
3443
3444 @_OptParam.register_type(BGP_OPT_CAPABILITY)
3445 class _OptParamCapability(_OptParam, TypeDisp):
3446     _CAP_HDR_PACK_STR = '!BB'
3447
3448     def __init__(self, cap_code=None, cap_value=None, cap_length=None,
3449                  type_=None, length=None):
3450         super(_OptParamCapability, self).__init__(type_=BGP_OPT_CAPABILITY,
3451                                                   length=length)
3452         if cap_code is None:
3453             cap_code = self._rev_lookup_type(self.__class__)
3454         self.cap_code = cap_code
3455         if cap_value is not None:
3456             self.cap_value = cap_value
3457         if cap_length is not None:
3458             self.cap_length = cap_length
3459
3460     @classmethod
3461     def parse_value(cls, buf):
3462         caps = []
3463         while len(buf) > 0:
3464             (code, length) = struct.unpack_from(cls._CAP_HDR_PACK_STR,
3465                                                 six.binary_type(buf))
3466             value = buf[struct.calcsize(cls._CAP_HDR_PACK_STR):]
3467             buf = buf[length + 2:]
3468             kwargs = {
3469                 'cap_code': code,
3470                 'cap_length': length,
3471             }
3472             subcls = cls._lookup_type(code)
3473             kwargs.update(subcls.parse_cap_value(value))
3474             caps.append(subcls(type_=BGP_OPT_CAPABILITY, length=length + 2,
3475                                **kwargs))
3476         return caps
3477
3478     def serialize_value(self):
3479         # fixup
3480         cap_value = self.serialize_cap_value()
3481         self.cap_length = len(cap_value)
3482
3483         buf = bytearray()
3484         msg_pack_into(self._CAP_HDR_PACK_STR, buf, 0, self.cap_code,
3485                       self.cap_length)
3486         return buf + cap_value
3487
3488
3489 class _OptParamEmptyCapability(_OptParamCapability):
3490     @classmethod
3491     def parse_cap_value(cls, buf):
3492         return {}
3493
3494     def serialize_cap_value(self):
3495         return bytearray()
3496
3497
3498 @_OptParamCapability.register_unknown_type()
3499 class BGPOptParamCapabilityUnknown(_OptParamCapability):
3500     @classmethod
3501     def parse_cap_value(cls, buf):
3502         return {'cap_value': buf}
3503
3504     def serialize_cap_value(self):
3505         return self.cap_value
3506
3507
3508 @_OptParamCapability.register_type(BGP_CAP_ROUTE_REFRESH)
3509 class BGPOptParamCapabilityRouteRefresh(_OptParamEmptyCapability):
3510     pass
3511
3512
3513 @_OptParamCapability.register_type(BGP_CAP_ROUTE_REFRESH_CISCO)
3514 class BGPOptParamCapabilityCiscoRouteRefresh(_OptParamEmptyCapability):
3515     pass
3516
3517
3518 @_OptParamCapability.register_type(BGP_CAP_ENHANCED_ROUTE_REFRESH)
3519 class BGPOptParamCapabilityEnhancedRouteRefresh(_OptParamEmptyCapability):
3520     pass
3521
3522
3523 @_OptParamCapability.register_type(BGP_CAP_GRACEFUL_RESTART)
3524 class BGPOptParamCapabilityGracefulRestart(_OptParamCapability):
3525     _CAP_PACK_STR = "!H"
3526
3527     def __init__(self, flags, time, tuples, **kwargs):
3528         super(BGPOptParamCapabilityGracefulRestart, self).__init__(**kwargs)
3529         self.flags = flags
3530         self.time = time
3531         self.tuples = tuples
3532
3533     @classmethod
3534     def parse_cap_value(cls, buf):
3535         (restart, ) = struct.unpack_from(cls._CAP_PACK_STR,
3536                                          six.binary_type(buf))
3537         buf = buf[2:]
3538         l = []
3539         while len(buf) >= 4:
3540             l.append(struct.unpack_from("!HBB", buf))
3541             buf = buf[4:]
3542         return {'flags': restart >> 12, 'time': restart & 0xfff, 'tuples': l}
3543
3544     def serialize_cap_value(self):
3545         buf = bytearray()
3546         msg_pack_into(self._CAP_PACK_STR, buf, 0, self.flags << 12 | self.time)
3547         offset = 2
3548         for i in self.tuples:
3549             afi, safi, flags = i
3550             msg_pack_into("!HBB", buf, offset, afi, safi, flags)
3551             offset += 4
3552         return buf
3553
3554
3555 @_OptParamCapability.register_type(BGP_CAP_FOUR_OCTET_AS_NUMBER)
3556 class BGPOptParamCapabilityFourOctetAsNumber(_OptParamCapability):
3557     _CAP_PACK_STR = '!I'
3558
3559     def __init__(self, as_number, **kwargs):
3560         super(BGPOptParamCapabilityFourOctetAsNumber, self).__init__(**kwargs)
3561         self.as_number = as_number
3562
3563     @classmethod
3564     def parse_cap_value(cls, buf):
3565         (as_number, ) = struct.unpack_from(cls._CAP_PACK_STR,
3566                                            six.binary_type(buf))
3567         return {'as_number': as_number}
3568
3569     def serialize_cap_value(self):
3570         buf = bytearray()
3571         msg_pack_into(self._CAP_PACK_STR, buf, 0, self.as_number)
3572         return buf
3573
3574
3575 @_OptParamCapability.register_type(BGP_CAP_MULTIPROTOCOL)
3576 class BGPOptParamCapabilityMultiprotocol(_OptParamCapability):
3577     _CAP_PACK_STR = '!HBB'  # afi, reserved, safi
3578
3579     def __init__(self, afi, safi, reserved=0, **kwargs):
3580         super(BGPOptParamCapabilityMultiprotocol, self).__init__(**kwargs)
3581         self.afi = afi
3582         self.reserved = reserved
3583         self.safi = safi
3584
3585     @classmethod
3586     def parse_cap_value(cls, buf):
3587         (afi, reserved, safi,) = struct.unpack_from(cls._CAP_PACK_STR,
3588                                                     six.binary_type(buf))
3589         return {
3590             'afi': afi,
3591             'reserved': reserved,
3592             'safi': safi,
3593         }
3594
3595     def serialize_cap_value(self):
3596         # fixup
3597         self.reserved = 0
3598
3599         buf = bytearray()
3600         msg_pack_into(self._CAP_PACK_STR, buf, 0,
3601                       self.afi, self.reserved, self.safi)
3602         return buf
3603
3604
3605 @_OptParamCapability.register_type(BGP_CAP_CARRYING_LABEL_INFO)
3606 class BGPOptParamCapabilityCarryingLabelInfo(_OptParamEmptyCapability):
3607     pass
3608
3609
3610 class BGPWithdrawnRoute(IPAddrPrefix):
3611     pass
3612
3613
3614 class _PathAttribute(StringifyMixin, TypeDisp, _Value):
3615     _PACK_STR = '!BB'  # flags, type
3616     _PACK_STR_LEN = '!B'  # length
3617     _PACK_STR_EXT_LEN = '!H'  # length w/ BGP_ATTR_FLAG_EXTENDED_LENGTH
3618     _ATTR_FLAGS = None
3619
3620     def __init__(self, value=None, flags=0, type_=None, length=None):
3621         if type_ is None:
3622             type_ = self._rev_lookup_type(self.__class__)
3623         self.flags = flags
3624         self.type = type_
3625         self.length = length
3626         if value is not None:
3627             self.value = value
3628
3629     @classmethod
3630     def parser(cls, buf):
3631         (flags, type_) = struct.unpack_from(cls._PACK_STR,
3632                                             six.binary_type(buf))
3633         rest = buf[struct.calcsize(cls._PACK_STR):]
3634         if (flags & BGP_ATTR_FLAG_EXTENDED_LENGTH) != 0:
3635             len_pack_str = cls._PACK_STR_EXT_LEN
3636         else:
3637             len_pack_str = cls._PACK_STR_LEN
3638         (length,) = struct.unpack_from(len_pack_str, six.binary_type(rest))
3639         rest = rest[struct.calcsize(len_pack_str):]
3640         value = bytes(rest[:length])
3641         rest = rest[length:]
3642         subcls = cls._lookup_type(type_)
3643         return subcls(flags=flags, type_=type_, length=length,
3644                       **subcls.parse_value(value)), rest
3645
3646     def serialize(self):
3647         # fixup
3648         if self._ATTR_FLAGS is not None:
3649             self.flags = (
3650                 self.flags
3651                 & ~(BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE)
3652                 | self._ATTR_FLAGS)
3653         value = self.serialize_value()
3654         self.length = len(value)
3655         if self.flags & BGP_ATTR_FLAG_EXTENDED_LENGTH:
3656             len_pack_str = self._PACK_STR_EXT_LEN
3657         elif self.length > 255:
3658             self.flags |= BGP_ATTR_FLAG_EXTENDED_LENGTH
3659             len_pack_str = self._PACK_STR_EXT_LEN
3660         else:
3661             self.flags &= ~BGP_ATTR_FLAG_EXTENDED_LENGTH
3662             len_pack_str = self._PACK_STR_LEN
3663
3664         buf = bytearray()
3665         msg_pack_into(self._PACK_STR, buf, 0, self.flags, self.type)
3666         msg_pack_into(len_pack_str, buf, len(buf), self.length)
3667         return buf + value
3668
3669
3670 @_PathAttribute.register_unknown_type()
3671 class BGPPathAttributeUnknown(_PathAttribute):
3672     @classmethod
3673     def parse_value(cls, buf):
3674         return {
3675             'value': buf
3676         }
3677
3678     def serialize_value(self):
3679         return self.value
3680
3681
3682 class _PathAttributeUint32(_PathAttribute):
3683     _VALUE_PACK_STR = '!I'
3684
3685
3686 @_PathAttribute.register_type(BGP_ATTR_TYPE_ORIGIN)
3687 class BGPPathAttributeOrigin(_PathAttribute):
3688     _VALUE_PACK_STR = '!B'
3689     _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
3690
3691
3692 class _BGPPathAttributeAsPathCommon(_PathAttribute):
3693     _AS_SET = 1
3694     _AS_SEQUENCE = 2
3695     _SEG_HDR_PACK_STR = '!BB'
3696     _AS_PACK_STR = None
3697     _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
3698
3699     def __init__(self, value, as_pack_str=None, flags=0, type_=None,
3700                  length=None):
3701         super(_BGPPathAttributeAsPathCommon, self).__init__(value=value,
3702                                                             flags=flags,
3703                                                             type_=type_,
3704                                                             length=length)
3705         if as_pack_str:
3706             self._AS_PACK_STR = as_pack_str
3707
3708     @property
3709     def path_seg_list(self):
3710         return copy.deepcopy(self.value)
3711
3712     def get_as_path_len(self):
3713         count = 0
3714         for seg in self.value:
3715             if isinstance(seg, list):
3716                 # Segment type 2 stored in list and all AS counted.
3717                 count += len(seg)
3718             else:
3719                 # Segment type 1 stored in set and count as one.
3720                 count += 1
3721
3722         return count
3723
3724     def has_local_as(self, local_as, max_count=0):
3725         """Check if *local_as* is already present on path list."""
3726         _count = 0
3727         for as_path_seg in self.value:
3728             _count += list(as_path_seg).count(local_as)
3729         return _count > max_count
3730
3731     def has_matching_leftmost(self, remote_as):
3732         """Check if leftmost AS matches *remote_as*."""
3733         if not self.value or not remote_as:
3734             return False
3735
3736         leftmost_seg = self.path_seg_list[0]
3737         if leftmost_seg and leftmost_seg[0] == remote_as:
3738             return True
3739
3740         return False
3741
3742     @classmethod
3743     def _is_valid_16bit_as_path(cls, buf):
3744
3745         two_byte_as_size = struct.calcsize('!H')
3746
3747         while buf:
3748             (type_, num_as) = struct.unpack_from(cls._SEG_HDR_PACK_STR,
3749                                                  six.binary_type(buf))
3750
3751             if type_ is not cls._AS_SET and type_ is not cls._AS_SEQUENCE:
3752                 return False
3753
3754             buf = buf[struct.calcsize(cls._SEG_HDR_PACK_STR):]
3755
3756             if len(buf) < num_as * two_byte_as_size:
3757                 return False
3758
3759             buf = buf[num_as * two_byte_as_size:]
3760
3761         return True
3762
3763     @classmethod
3764     def parse_value(cls, buf):
3765         result = []
3766
3767         if cls._is_valid_16bit_as_path(buf):
3768             as_pack_str = '!H'
3769         else:
3770             as_pack_str = '!I'
3771
3772         while buf:
3773             (type_, num_as) = struct.unpack_from(cls._SEG_HDR_PACK_STR,
3774                                                  six.binary_type(buf))
3775             buf = buf[struct.calcsize(cls._SEG_HDR_PACK_STR):]
3776             l = []
3777             for _ in range(0, num_as):
3778                 (as_number,) = struct.unpack_from(as_pack_str,
3779                                                   six.binary_type(buf))
3780                 buf = buf[struct.calcsize(as_pack_str):]
3781                 l.append(as_number)
3782             if type_ == cls._AS_SET:
3783                 result.append(set(l))
3784             elif type_ == cls._AS_SEQUENCE:
3785                 result.append(l)
3786             else:
3787                 # protocol error
3788                 raise struct.error('Unsupported segment type: %s' % type_)
3789         return {
3790             'value': result,
3791             'as_pack_str': as_pack_str,
3792         }
3793
3794     def serialize_value(self):
3795         buf = bytearray()
3796         offset = 0
3797         for e in self.value:
3798             if isinstance(e, set):
3799                 type_ = self._AS_SET
3800             elif isinstance(e, list):
3801                 type_ = self._AS_SEQUENCE
3802             else:
3803                 raise struct.error(
3804                     'Element of %s.value must be of type set or list' %
3805                     self.__class__.__name__)
3806             l = list(e)
3807             num_as = len(l)
3808             if num_as == 0:
3809                 continue
3810             msg_pack_into(self._SEG_HDR_PACK_STR, buf, offset, type_, num_as)
3811             offset += struct.calcsize(self._SEG_HDR_PACK_STR)
3812             for i in l:
3813                 msg_pack_into(self._AS_PACK_STR, buf, offset, i)
3814                 offset += struct.calcsize(self._AS_PACK_STR)
3815         return buf
3816
3817
3818 @_PathAttribute.register_type(BGP_ATTR_TYPE_AS_PATH)
3819 class BGPPathAttributeAsPath(_BGPPathAttributeAsPathCommon):
3820     # XXX depends on negotiated capability, AS numbers can be 32 bit.
3821     # while wireshark seems to attempt auto-detect, it seems that
3822     # there's no way to detect it reliably.  for example, the
3823     # following byte sequence can be interpreted in two ways.
3824     #   01 02 99 88 77 66 02 01 55 44
3825     #   AS_SET num=2 9988 7766 AS_SEQUENCE num=1 5544
3826     #   AS_SET num=2 99887766 02015544
3827     # we first check whether AS path can be parsed in 16bit format and if
3828     # it fails, we try to parse as 32bit
3829     _AS_PACK_STR = '!H'
3830
3831
3832 @_PathAttribute.register_type(BGP_ATTR_TYPE_AS4_PATH)
3833 class BGPPathAttributeAs4Path(_BGPPathAttributeAsPathCommon):
3834     _AS_PACK_STR = '!I'
3835     _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL
3836
3837     @classmethod
3838     def _is_valid_16bit_as_path(cls, buf):
3839         return False
3840
3841
3842 @_PathAttribute.register_type(BGP_ATTR_TYPE_NEXT_HOP)
3843 class BGPPathAttributeNextHop(_PathAttribute):
3844     _VALUE_PACK_STR = '!4s'
3845     _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
3846     _TYPE = {
3847         'ascii': [
3848             'value'
3849         ]
3850     }
3851
3852     @classmethod
3853     def parse_value(cls, buf):
3854         (ip_addr,) = struct.unpack_from(cls._VALUE_PACK_STR,
3855                                         six.binary_type(buf))
3856         return {
3857             'value': addrconv.ipv4.bin_to_text(ip_addr),
3858         }
3859
3860     def serialize_value(self):
3861         buf = bytearray()
3862         msg_pack_into(self._VALUE_PACK_STR, buf, 0,
3863                       addrconv.ipv4.text_to_bin(self.value))
3864         return buf
3865
3866
3867 @_PathAttribute.register_type(BGP_ATTR_TYPE_MULTI_EXIT_DISC)
3868 class BGPPathAttributeMultiExitDisc(_PathAttributeUint32):
3869     _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
3870
3871
3872 @_PathAttribute.register_type(BGP_ATTR_TYPE_LOCAL_PREF)
3873 class BGPPathAttributeLocalPref(_PathAttributeUint32):
3874     _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
3875
3876
3877 @_PathAttribute.register_type(BGP_ATTR_TYPE_ATOMIC_AGGREGATE)
3878 class BGPPathAttributeAtomicAggregate(_PathAttribute):
3879     _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
3880
3881     @classmethod
3882     def parse_value(cls, buf):
3883         return {}
3884
3885     def serialize_value(self):
3886         return b''
3887
3888
3889 class _BGPPathAttributeAggregatorCommon(_PathAttribute):
3890     _VALUE_PACK_STR = None
3891     _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE
3892     _TYPE = {
3893         'ascii': [
3894             'addr'
3895         ]
3896     }
3897
3898     def __init__(self, as_number, addr, flags=0, type_=None, length=None):
3899         super(_BGPPathAttributeAggregatorCommon, self).__init__(flags=flags,
3900                                                                 type_=type_,
3901                                                                 length=length)
3902         self.as_number = as_number
3903         self.addr = addr
3904
3905     @classmethod
3906     def parse_value(cls, buf):
3907         (as_number, addr) = struct.unpack_from(cls._VALUE_PACK_STR,
3908                                                six.binary_type(buf))
3909         return {
3910             'as_number': as_number,
3911             'addr': addrconv.ipv4.bin_to_text(addr),
3912         }
3913
3914     def serialize_value(self):
3915         buf = bytearray()
3916         msg_pack_into(self._VALUE_PACK_STR, buf, 0, self.as_number,
3917                       addrconv.ipv4.text_to_bin(self.addr))
3918         return buf
3919
3920
3921 @_PathAttribute.register_type(BGP_ATTR_TYPE_AGGREGATOR)
3922 class BGPPathAttributeAggregator(_BGPPathAttributeAggregatorCommon):
3923     # Note: AS numbers can be Two-Octet or Four-Octet.
3924     # This class would detect it by the value length field.
3925     # For example,
3926     # - if the value field length is 6 (='!H4s'), AS number should
3927     #   be Two-Octet.
3928     # - else if the length is 8 (='!I4s'), AS number should be Four-Octet.
3929     _TWO_OCTET_VALUE_PACK_STR = '!H4s'
3930     _FOUR_OCTET_VALUE_PACK_STR = '!I4s'
3931     _VALUE_PACK_STR = _TWO_OCTET_VALUE_PACK_STR  # Two-Octet by default
3932     _FOUR_OCTET_VALUE_SIZE = struct.calcsize(_FOUR_OCTET_VALUE_PACK_STR)
3933
3934     @classmethod
3935     def parse_value(cls, buf):
3936         if len(buf) == cls._FOUR_OCTET_VALUE_SIZE:
3937             cls._VALUE_PACK_STR = cls._FOUR_OCTET_VALUE_PACK_STR
3938         return super(BGPPathAttributeAggregator, cls).parse_value(buf)
3939
3940     def serialize_value(self):
3941         if self.as_number > 0xffff:
3942             self._VALUE_PACK_STR = self._FOUR_OCTET_VALUE_PACK_STR
3943         return super(BGPPathAttributeAggregator, self).serialize_value()
3944
3945
3946 @_PathAttribute.register_type(BGP_ATTR_TYPE_AS4_AGGREGATOR)
3947 class BGPPathAttributeAs4Aggregator(_BGPPathAttributeAggregatorCommon):
3948     _VALUE_PACK_STR = '!I4s'
3949
3950
3951 @_PathAttribute.register_type(BGP_ATTR_TYPE_COMMUNITIES)
3952 class BGPPathAttributeCommunities(_PathAttribute):
3953     _VALUE_PACK_STR = '!I'
3954     _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE
3955
3956     # String constants of well-known-communities
3957     NO_EXPORT = int('0xFFFFFF01', 16)
3958     NO_ADVERTISE = int('0xFFFFFF02', 16)
3959     NO_EXPORT_SUBCONFED = int('0xFFFFFF03', 16)
3960     WELL_KNOW_COMMUNITIES = (NO_EXPORT, NO_ADVERTISE, NO_EXPORT_SUBCONFED)
3961
3962     def __init__(self, communities,
3963                  flags=0, type_=None, length=None):
3964         super(BGPPathAttributeCommunities, self).__init__(flags=flags,
3965                                                           type_=type_,
3966                                                           length=length)
3967         self.communities = communities
3968
3969     @classmethod
3970     def parse_value(cls, buf):
3971         rest = buf
3972         communities = []
3973         elem_size = struct.calcsize(cls._VALUE_PACK_STR)
3974         while len(rest) >= elem_size:
3975             (comm, ) = struct.unpack_from(cls._VALUE_PACK_STR,
3976                                           six.binary_type(rest))
3977             communities.append(comm)
3978             rest = rest[elem_size:]
3979         return {
3980             'communities': communities,
3981         }
3982
3983     def serialize_value(self):
3984         buf = bytearray()
3985         for comm in self.communities:
3986             bincomm = bytearray()
3987             msg_pack_into(self._VALUE_PACK_STR, bincomm, 0, comm)
3988             buf += bincomm
3989         return buf
3990
3991     @staticmethod
3992     def is_no_export(comm_attr):
3993         """Returns True if given value matches well-known community NO_EXPORT
3994          attribute value.
3995          """
3996         return comm_attr == BGPPathAttributeCommunities.NO_EXPORT
3997
3998     @staticmethod
3999     def is_no_advertise(comm_attr):
4000         """Returns True if given value matches well-known community
4001         NO_ADVERTISE attribute value.
4002         """
4003         return comm_attr == BGPPathAttributeCommunities.NO_ADVERTISE
4004
4005     @staticmethod
4006     def is_no_export_subconfed(comm_attr):
4007         """Returns True if given value matches well-known community
4008          NO_EXPORT_SUBCONFED attribute value.
4009          """
4010         return comm_attr == BGPPathAttributeCommunities.NO_EXPORT_SUBCONFED
4011
4012     def has_comm_attr(self, attr):
4013         """Returns True if given community attribute is present."""
4014
4015         for comm_attr in self.communities:
4016             if comm_attr == attr:
4017                 return True
4018
4019         return False
4020
4021
4022 @_PathAttribute.register_type(BGP_ATTR_TYPE_ORIGINATOR_ID)
4023 class BGPPathAttributeOriginatorId(_PathAttribute):
4024     # ORIGINATOR_ID is a new optional, non-transitive BGP attribute of Type
4025     # code 9. This attribute is 4 bytes long and it will be created by an
4026     # RR in reflecting a route.
4027     _VALUE_PACK_STR = '!4s'
4028     _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
4029     _TYPE = {
4030         'asciilist': [
4031             'value'
4032         ]
4033     }
4034
4035     @classmethod
4036     def parse_value(cls, buf):
4037         (originator_id,) = struct.unpack_from(cls._VALUE_PACK_STR,
4038                                               six.binary_type(buf))
4039         return {
4040             'value': addrconv.ipv4.bin_to_text(originator_id),
4041         }
4042
4043     def serialize_value(self):
4044         buf = bytearray()
4045         msg_pack_into(self._VALUE_PACK_STR, buf, 0,
4046                       addrconv.ipv4.text_to_bin(self.value))
4047         return buf
4048
4049
4050 @_PathAttribute.register_type(BGP_ATTR_TYPE_CLUSTER_LIST)
4051 class BGPPathAttributeClusterList(_PathAttribute):
4052     # CLUSTER_LIST is a new, optional, non-transitive BGP attribute of Type
4053     # code 10. It is a sequence of CLUSTER_ID values representing the
4054     # reflection path that the route has passed.
4055     _VALUE_PACK_STR = '!4s'
4056     _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
4057     _TYPE = {
4058         'ascii': [
4059             'value'
4060         ]
4061     }
4062
4063     @classmethod
4064     def parse_value(cls, buf):
4065         rest = buf
4066         cluster_list = []
4067         elem_size = struct.calcsize(cls._VALUE_PACK_STR)
4068         while len(rest) >= elem_size:
4069             (cluster_id, ) = struct.unpack_from(
4070                 cls._VALUE_PACK_STR, six.binary_type(rest))
4071             cluster_list.append(addrconv.ipv4.bin_to_text(cluster_id))
4072             rest = rest[elem_size:]
4073         return {
4074             'value': cluster_list,
4075         }
4076
4077     def serialize_value(self):
4078         buf = bytearray()
4079         offset = 0
4080         for cluster_id in self.value:
4081             msg_pack_into(
4082                 self._VALUE_PACK_STR,
4083                 buf,
4084                 offset,
4085                 addrconv.ipv4.text_to_bin(cluster_id))
4086             offset += struct.calcsize(self._VALUE_PACK_STR)
4087         return buf
4088
4089
4090 # Extended Communities
4091 # RFC 4360
4092 # RFC 5668
4093 # IANA registry:
4094 # https://www.iana.org/assignments/bgp-extended-communities/
4095 # bgp-extended-communities.xml
4096 #
4097 # type
4098 # high  low
4099 # 00    sub-type    Two-Octet AS Specific Extended Community (transitive)
4100 # 40    sub-type    Two-Octet AS Specific Extended Community
4101 #                   payload:
4102 #                     2 byte Global Administrator (AS number)
4103 #                     4 byte Local Administrator (defined by sub-type)
4104 # 01    sub-type    IPv4 Address Specific Extended Community (transitive)
4105 # 41    sub-type    IPv4 Address Specific Extended Community
4106 #                   payload:
4107 #                     4 byte Global Administrator (IPv4 address)
4108 #                     2 byte Local Administrator (defined by sub-type)
4109 # 03    sub-type    Opaque Extended Community (transitive)
4110 # 43    sub-type    Opaque Extended Community
4111 #                   payload:
4112 #                     6 byte opaque value (defined by sub-type)
4113 #
4114 # 00    02          Route Target Community (two-octet AS specific)
4115 # 01    02          Route Target Community (IPv4 address specific)
4116 # 02    02          Route Target Community (four-octet AS specific, RFC 5668)
4117 # 00    03          Route Origin Community (two-octet AS specific)
4118 # 01    03          Route Origin Community (IPv4 address specific)
4119 # 02    03          Route Origin Community (four-octet AS specific, RFC 5668)
4120 # 06    sub-type    Ethernet VPN Extended Community (RFC 7432)
4121 # 80    sub-type    Flow Specification Extended Community (RFC 5575)
4122
4123 @_PathAttribute.register_type(BGP_ATTR_TYPE_EXTENDED_COMMUNITIES)
4124 class BGPPathAttributeExtendedCommunities(_PathAttribute):
4125     _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE
4126     _class_prefixes = ['BGP']
4127
4128     def __init__(self, communities,
4129                  flags=0, type_=None, length=None):
4130         super(BGPPathAttributeExtendedCommunities,
4131               self).__init__(flags=flags,
4132                              type_=type_,
4133                              length=length)
4134         self.communities = communities
4135
4136     @classmethod
4137     def parse_value(cls, buf):
4138         rest = buf
4139         communities = []
4140         while rest:
4141             comm, rest = _ExtendedCommunity.parse(rest)
4142             communities.append(comm)
4143         return {
4144             'communities': communities,
4145         }
4146
4147     def serialize_value(self):
4148         buf = bytearray()
4149         for comm in self.communities:
4150             buf += comm.serialize()
4151         return buf
4152
4153     def _community_list(self, subtype):
4154         _list = []
4155         for comm in (c for c in self.communities
4156                      if hasattr(c, "subtype") and c.subtype == subtype):
4157             if comm.type == 0 or comm.type == 2:
4158                 _list.append('%d:%d' % (comm.as_number,
4159                                         comm.local_administrator))
4160             elif comm.type == 1:
4161                 _list.append('%s:%d' % (comm.ipv4_address,
4162                                         comm.local_administrator))
4163         return _list
4164
4165     @property
4166     def rt_list(self):
4167         return self._community_list(2)
4168
4169     @property
4170     def soo_list(self):
4171         return self._community_list(3)
4172
4173
4174 class _ExtendedCommunity(StringifyMixin, TypeDisp, _Value):
4175     _PACK_STR = '!B7s'  # type high (+ type low), value
4176     _PACK_STR_SIZE = struct.calcsize(_PACK_STR)
4177     _SUBTYPE_PACK_STR = '!B'  # subtype
4178     IANA_AUTHORITY = 0x80
4179     TRANSITIVE = 0x40
4180     _TYPE_HIGH_MASK = ~TRANSITIVE
4181
4182     TWO_OCTET_AS_SPECIFIC = 0x00
4183     IPV4_ADDRESS_SPECIFIC = 0x01
4184     FOUR_OCTET_AS_SPECIFIC = 0x02
4185     OPAQUE = 0x03
4186     SUBTYPE_ENCAPSULATION = 0x0c
4187     ENCAPSULATION = (OPAQUE, SUBTYPE_ENCAPSULATION)
4188     EVPN = 0x06
4189     SUBTYPE_EVPN_MAC_MOBILITY = 0x00
4190     SUBTYPE_EVPN_ESI_LABEL = 0x01
4191     SUBTYPE_EVPN_ES_IMPORT_RT = 0x02
4192     EVPN_MAC_MOBILITY = (EVPN, SUBTYPE_EVPN_MAC_MOBILITY)
4193     EVPN_ESI_LABEL = (EVPN, SUBTYPE_EVPN_ESI_LABEL)
4194     EVPN_ES_IMPORT_RT = (EVPN, SUBTYPE_EVPN_ES_IMPORT_RT)
4195     FLOWSPEC = 0x80
4196     FLOWSPEC_L2VPN = 0x08
4197     SUBTYPE_FLOWSPEC_TRAFFIC_RATE = 0x06
4198     SUBTYPE_FLOWSPEC_TRAFFIC_ACTION = 0x07
4199     SUBTYPE_FLOWSPEC_REDIRECT = 0x08
4200     SUBTYPE_FLOWSPEC_TRAFFIC_REMARKING = 0x09
4201     SUBTYPE_FLOWSPEC_VLAN_ACTION = 0x0a
4202     SUBTYPE_FLOWSPEC_TPID_ACTION = 0x0b
4203     FLOWSPEC_TRAFFIC_RATE = (FLOWSPEC, SUBTYPE_FLOWSPEC_TRAFFIC_RATE)
4204     FLOWSPEC_TRAFFIC_ACTION = (FLOWSPEC, SUBTYPE_FLOWSPEC_TRAFFIC_ACTION)
4205     FLOWSPEC_REDIRECT = (FLOWSPEC, SUBTYPE_FLOWSPEC_REDIRECT)
4206     FLOWSPEC_TRAFFIC_REMARKING = (FLOWSPEC, SUBTYPE_FLOWSPEC_TRAFFIC_REMARKING)
4207     FLOWSPEC_VLAN_ACTION = (FLOWSPEC_L2VPN, SUBTYPE_FLOWSPEC_VLAN_ACTION)
4208     FLOWSPEC_TPID_ACTION = (FLOWSPEC_L2VPN, SUBTYPE_FLOWSPEC_TPID_ACTION)
4209
4210     def __init__(self, type_=None):
4211         if type_ is None:
4212             type_ = self._rev_lookup_type(self.__class__)
4213             if isinstance(type_, (tuple, list)):
4214                 type_ = type_[0]
4215         self.type = type_
4216
4217     @classmethod
4218     def parse_subtype(cls, buf):
4219         (subtype,) = struct.unpack_from(cls._SUBTYPE_PACK_STR, buf)
4220         return subtype
4221
4222     @classmethod
4223     def parse(cls, buf):
4224         (type_, value) = struct.unpack_from(cls._PACK_STR, buf)
4225         rest = buf[cls._PACK_STR_SIZE:]
4226         type_low = type_ & cls._TYPE_HIGH_MASK
4227         subtype = cls.parse_subtype(value)
4228         subcls = cls._lookup_type((type_low, subtype))
4229         if subcls == cls._UNKNOWN_TYPE:
4230             subcls = cls._lookup_type(type_low)
4231         return subcls(type_=type_, **subcls.parse_value(value)), rest
4232
4233     def serialize(self):
4234         return struct.pack(self._PACK_STR, self.type,
4235                            self.serialize_value())
4236
4237
4238 @_ExtendedCommunity.register_type(_ExtendedCommunity.TWO_OCTET_AS_SPECIFIC)
4239 class BGPTwoOctetAsSpecificExtendedCommunity(_ExtendedCommunity):
4240     _VALUE_PACK_STR = '!BHI'  # sub type, as number, local adm
4241     _VALUE_FIELDS = ['subtype', 'as_number', 'local_administrator']
4242
4243     def __init__(self, **kwargs):
4244         super(BGPTwoOctetAsSpecificExtendedCommunity, self).__init__()
4245         self.do_init(BGPTwoOctetAsSpecificExtendedCommunity, self, kwargs)
4246
4247
4248 @_ExtendedCommunity.register_type(_ExtendedCommunity.IPV4_ADDRESS_SPECIFIC)
4249 class BGPIPv4AddressSpecificExtendedCommunity(_ExtendedCommunity):
4250     _VALUE_PACK_STR = '!B4sH'  # sub type, IPv4 address, local adm
4251     _VALUE_FIELDS = ['subtype', 'ipv4_address', 'local_administrator']
4252     _TYPE = {
4253         'ascii': [
4254             'ipv4_address'
4255         ]
4256     }
4257
4258     def __init__(self, **kwargs):
4259         super(BGPIPv4AddressSpecificExtendedCommunity, self).__init__()
4260         self.do_init(BGPIPv4AddressSpecificExtendedCommunity, self, kwargs)
4261
4262     @classmethod
4263     def parse_value(cls, buf):
4264         d_ = super(BGPIPv4AddressSpecificExtendedCommunity,
4265                    cls).parse_value(buf)
4266         d_['ipv4_address'] = addrconv.ipv4.bin_to_text(d_['ipv4_address'])
4267         return d_
4268
4269     def serialize_value(self):
4270         return struct.pack(self._VALUE_PACK_STR, self.subtype,
4271                            addrconv.ipv4.text_to_bin(self.ipv4_address),
4272                            self.local_administrator)
4273
4274
4275 @_ExtendedCommunity.register_type(_ExtendedCommunity.FOUR_OCTET_AS_SPECIFIC)
4276 class BGPFourOctetAsSpecificExtendedCommunity(_ExtendedCommunity):
4277     _VALUE_PACK_STR = '!BIH'  # sub type, as number, local adm
4278     _VALUE_FIELDS = ['subtype', 'as_number', 'local_administrator']
4279
4280     def __init__(self, **kwargs):
4281         super(BGPFourOctetAsSpecificExtendedCommunity, self).__init__()
4282         self.do_init(BGPFourOctetAsSpecificExtendedCommunity, self, kwargs)
4283
4284
4285 @_ExtendedCommunity.register_type(_ExtendedCommunity.OPAQUE)
4286 class BGPOpaqueExtendedCommunity(_ExtendedCommunity):
4287     _VALUE_PACK_STR = '!B6s'
4288     _VALUE_FIELDS = ['subtype', 'opaque']
4289
4290     def __init__(self, **kwargs):
4291         super(BGPOpaqueExtendedCommunity, self).__init__()
4292         self.do_init(BGPOpaqueExtendedCommunity, self, kwargs)
4293
4294
4295 @_ExtendedCommunity.register_type(_ExtendedCommunity.ENCAPSULATION)
4296 class BGPEncapsulationExtendedCommunity(_ExtendedCommunity):
4297     _VALUE_PACK_STR = '!B4xH'
4298     _VALUE_FIELDS = ['subtype', 'tunnel_type']
4299
4300     # BGP Tunnel Encapsulation Attribute Tunnel Types
4301     # http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#tunnel-types
4302     TUNNEL_TYPE_L2TPV3 = 1
4303     TUNNEL_TYPE_GRE = 2
4304     TUNNEL_TYPE_IP_IN_IP = 7
4305     TUNNEL_TYPE_VXLAN = 8
4306     TUNNEL_TYPE_NVGRE = 9
4307     TUNNEL_TYPE_MPLS = 10
4308     TUNNEL_TYPE_MPLS_IN_GRE = 11
4309     TUNNEL_TYPE_VXLAN_GRE = 12
4310     TUNNEL_TYPE_MPLS_IN_UDP = 13
4311
4312     def __init__(self, **kwargs):
4313         super(BGPEncapsulationExtendedCommunity, self).__init__()
4314         self.do_init(BGPEncapsulationExtendedCommunity, self, kwargs)
4315
4316     @classmethod
4317     def from_str(cls, tunnel_type):
4318         """
4319         Returns an instance identified with the given `tunnel_type`.
4320
4321         `tunnel_type` should be a str type value and corresponding to
4322         BGP Tunnel Encapsulation Attribute Tunnel Type constants name
4323         omitting `TUNNEL_TYPE_` prefix.
4324
4325         Example:
4326             - `gre` means TUNNEL_TYPE_GRE
4327             - `vxlan` means TUNNEL_TYPE_VXLAN
4328
4329         And raises AttributeError when the corresponding Tunnel Type
4330         is not found to the given `tunnel_type`.
4331         """
4332         return cls(subtype=_ExtendedCommunity.SUBTYPE_ENCAPSULATION,
4333                    tunnel_type=getattr(cls,
4334                                        'TUNNEL_TYPE_' + tunnel_type.upper()))
4335
4336
4337 @_ExtendedCommunity.register_type(_ExtendedCommunity.EVPN_MAC_MOBILITY)
4338 class BGPEvpnMacMobilityExtendedCommunity(_ExtendedCommunity):
4339     """
4340     MAC Mobility Extended Community
4341     """
4342     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4343     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4344     # | Type=0x06     | Sub-Type=0x00 |Flags(1 octet)|  Reserved=0    |
4345     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4346     # |                       Sequence Number                         |
4347     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4348     _VALUE_PACK_STR = '!BBxI'
4349     _VALUE_FIELDS = ['subtype', 'flags', 'sequence_number']
4350
4351     def __init__(self, **kwargs):
4352         super(BGPEvpnMacMobilityExtendedCommunity, self).__init__()
4353         self.do_init(BGPEvpnMacMobilityExtendedCommunity, self, kwargs)
4354
4355
4356 @_ExtendedCommunity.register_type(_ExtendedCommunity.EVPN_ESI_LABEL)
4357 class BGPEvpnEsiLabelExtendedCommunity(_ExtendedCommunity):
4358     """
4359     ESI Label Extended Community
4360     """
4361     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4362     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4363     # | Type=0x06     | Sub-Type=0x01 | Flags(1 octet)|  Reserved=0   |
4364     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4365     # |  Reserved=0   |          ESI Label                            |
4366     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4367     _VALUE_PACK_STR = '!BB2x3s'
4368     _VALUE_FIELDS = ['subtype', 'flags']
4369
4370     # Classification for Flags.
4371     SINGLE_ACTIVE_BIT = 1 << 0
4372
4373     def __init__(self, label=None, mpls_label=None, vni=None, **kwargs):
4374         super(BGPEvpnEsiLabelExtendedCommunity, self).__init__()
4375         self.do_init(BGPEvpnEsiLabelExtendedCommunity, self, kwargs)
4376
4377         if label:
4378             # If binary type label field value is specified, stores it
4379             # and decodes as MPLS label and VNI.
4380             self._label = label
4381             self._mpls_label, _ = mpls.label_from_bin(label)
4382             self._vni = vxlan.vni_from_bin(label)
4383         else:
4384             # If either MPLS label or VNI is specified, stores it
4385             # and encodes into binary type label field value.
4386             self._label = self._serialize_label(mpls_label, vni)
4387             self._mpls_label = mpls_label
4388             self._vni = vni
4389
4390     def _serialize_label(self, mpls_label, vni):
4391         if mpls_label:
4392             return mpls.label_to_bin(mpls_label, is_bos=True)
4393         elif vni:
4394             return vxlan.vni_to_bin(vni)
4395         else:
4396             return b'\x00' * 3
4397
4398     @classmethod
4399     def parse_value(cls, buf):
4400         (subtype, flags,
4401          label) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
4402         return {
4403             'subtype': subtype,
4404             'flags': flags,
4405             'label': label,
4406         }
4407
4408     def serialize_value(self):
4409         return struct.pack(self._VALUE_PACK_STR, self.subtype, self.flags,
4410                            self._label)
4411
4412     @property
4413     def mpls_label(self):
4414         return self._mpls_label
4415
4416     @mpls_label.setter
4417     def mpls_label(self, mpls_label):
4418         self._label = mpls.label_to_bin(mpls_label, is_bos=True)
4419         self._mpls_label = mpls_label
4420         self._vni = None  # disables VNI
4421
4422     @property
4423     def vni(self):
4424         return self._vni
4425
4426     @vni.setter
4427     def vni(self, vni):
4428         self._label = vxlan.vni_to_bin(vni)
4429         self._mpls_label = None  # disables ESI label
4430         self._vni = vni
4431
4432
4433 @_ExtendedCommunity.register_type(_ExtendedCommunity.EVPN_ES_IMPORT_RT)
4434 class BGPEvpnEsImportRTExtendedCommunity(_ExtendedCommunity):
4435     """
4436     ES-Import Route Target Extended Community
4437     """
4438     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4439     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4440     # | Type=0x06     | Sub-Type=0x02 |          ES-Import            |
4441     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4442     # |                     ES-Import Cont'd                          |
4443     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4444     _VALUE_PACK_STR = '!B6s'
4445     _VALUE_FIELDS = ['subtype', 'es_import']
4446     _TYPE = {
4447         'ascii': [
4448             'es_import'
4449         ]
4450     }
4451
4452     def __init__(self, **kwargs):
4453         super(BGPEvpnEsImportRTExtendedCommunity, self).__init__()
4454         self.do_init(BGPEvpnEsImportRTExtendedCommunity, self, kwargs)
4455
4456     @classmethod
4457     def parse_value(cls, buf):
4458         (subtype, es_import) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
4459         return {
4460             'subtype': subtype,
4461             'es_import': addrconv.mac.bin_to_text(es_import),
4462         }
4463
4464     def serialize_value(self):
4465         return struct.pack(self._VALUE_PACK_STR, self.subtype,
4466                            addrconv.mac.text_to_bin(self.es_import))
4467
4468
4469 @_ExtendedCommunity.register_type(_ExtendedCommunity.FLOWSPEC_TRAFFIC_RATE)
4470 class BGPFlowSpecTrafficRateCommunity(_ExtendedCommunity):
4471     """
4472     Flow Specification Traffic Filtering Actions for Traffic Rate.
4473
4474     ========================== ===============================================
4475     Attribute                  Description
4476     ========================== ===============================================
4477     as_number                  Autonomous System number.
4478     rate_info                  rate information.
4479     ========================== ===============================================
4480     """
4481     # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4482     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4483     # | Type=0x80     | Sub-Type=0x06 |           AS number           |
4484     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4485     # |                      Rate information                         |
4486     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4487     _VALUE_PACK_STR = '!BHf'
4488     _VALUE_FIELDS = ['subtype', 'as_number', 'rate_info']
4489     ACTION_NAME = 'traffic_rate'
4490
4491     def __init__(self, **kwargs):
4492         super(BGPFlowSpecTrafficRateCommunity, self).__init__()
4493         kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_TRAFFIC_RATE
4494         self.do_init(BGPFlowSpecTrafficRateCommunity, self, kwargs)
4495
4496     @classmethod
4497     def parse_value(cls, buf):
4498         (subtype, as_number,
4499          rate_info) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
4500         return {
4501             'subtype': subtype,
4502             'as_number': as_number,
4503             'rate_info': rate_info,
4504         }
4505
4506     def serialize_value(self):
4507         return struct.pack(self._VALUE_PACK_STR, self.subtype,
4508                            self.as_number, self.rate_info)
4509
4510
4511 @_ExtendedCommunity.register_type(_ExtendedCommunity.FLOWSPEC_TRAFFIC_ACTION)
4512 class BGPFlowSpecTrafficActionCommunity(_ExtendedCommunity):
4513     """
4514     Flow Specification Traffic Filtering Actions for Traffic Action.
4515
4516     ========================== ===============================================
4517     Attribute                  Description
4518     ========================== ===============================================
4519     action                     Apply action.
4520                                The supported action are
4521                                ``SAMPLE`` and ``TERMINAL``.
4522     ========================== ===============================================
4523     """
4524     # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4525     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4526     # | Type=0x80     | Sub-Type=0x07 |          Traffic-action       |
4527     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4528     # |                     Traffic-action Cont'd                     |
4529     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4530
4531     # Traffic-action format
4532     #  40  41  42  43  44  45  46  47
4533     # +---+---+---+---+---+---+---+---+
4534     # |        reserved       | S | T |
4535     # +---+---+---+---+---+---+---+---+
4536
4537     _VALUE_PACK_STR = '!B5xB'
4538     _VALUE_FIELDS = ['subtype', 'action']
4539     ACTION_NAME = 'traffic_action'
4540     SAMPLE = 1 << 1
4541     TERMINAL = 1 << 0
4542
4543     def __init__(self, **kwargs):
4544         super(BGPFlowSpecTrafficActionCommunity, self).__init__()
4545         kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_TRAFFIC_ACTION
4546         self.do_init(BGPFlowSpecTrafficActionCommunity, self, kwargs)
4547
4548
4549 @_ExtendedCommunity.register_type(_ExtendedCommunity.FLOWSPEC_REDIRECT)
4550 class BGPFlowSpecRedirectCommunity(BGPTwoOctetAsSpecificExtendedCommunity):
4551     """
4552     Flow Specification Traffic Filtering Actions for Redirect.
4553
4554     ========================== ===============================================
4555     Attribute                  Description
4556     ========================== ===============================================
4557     as_number                  Autonomous System number.
4558     local_administrator        Local Administrator.
4559     ========================== ===============================================
4560     """
4561     ACTION_NAME = 'redirect'
4562
4563     def __init__(self, **kwargs):
4564         super(BGPTwoOctetAsSpecificExtendedCommunity, self).__init__()
4565         kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_REDIRECT
4566         self.do_init(BGPTwoOctetAsSpecificExtendedCommunity, self, kwargs)
4567
4568
4569 @_ExtendedCommunity.register_type(
4570     _ExtendedCommunity.FLOWSPEC_TRAFFIC_REMARKING)
4571 class BGPFlowSpecTrafficMarkingCommunity(_ExtendedCommunity):
4572     """
4573     Flow Specification Traffic Filtering Actions for Traffic Marking.
4574
4575     ========================== ===============================================
4576     Attribute                  Description
4577     ========================== ===============================================
4578     dscp                       Differentiated Services Code Point.
4579     ========================== ===============================================
4580     """
4581     # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4582     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4583     # | Type=0x80     | Sub-Type=0x09 |           Reserved=0          |
4584     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4585     # |                    Reserved=0                 |      Dscp     |
4586     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4587     _VALUE_PACK_STR = '!B5xB'
4588     _VALUE_FIELDS = ['subtype', 'dscp']
4589     ACTION_NAME = 'traffic_marking'
4590
4591     def __init__(self, **kwargs):
4592         super(BGPFlowSpecTrafficMarkingCommunity, self).__init__()
4593         kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_TRAFFIC_REMARKING
4594         self.do_init(BGPFlowSpecTrafficMarkingCommunity, self, kwargs)
4595
4596     @classmethod
4597     def parse_value(cls, buf):
4598         (subtype, dscp) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
4599         return {
4600             'subtype': subtype,
4601             'dscp': dscp,
4602         }
4603
4604     def serialize_value(self):
4605         return struct.pack(self._VALUE_PACK_STR, self.subtype, self.dscp)
4606
4607
4608 # TODO
4609 # Implement "Redirect-IPv6" [draft-ietf-idr-flow-spec-v6-08]
4610
4611
4612 @_ExtendedCommunity.register_type(
4613     _ExtendedCommunity.FLOWSPEC_VLAN_ACTION)
4614 class BGPFlowSpecVlanActionCommunity(_ExtendedCommunity):
4615     """
4616     Flow Specification Vlan Actions.
4617
4618     ========= ===============================================
4619     Attribute Description
4620     ========= ===============================================
4621     actions_1 Bit representation of actions.
4622               Supported actions are
4623               ``POP``, ``PUSH``, ``SWAP``, ``REWRITE_INNER``, ``REWRITE_OUTER``.
4624     actions_2 Same as ``actions_1``.
4625     vlan_1    VLAN ID used by ``actions_1``.
4626     cos_1     Class of Service used by ``actions_1``.
4627     vlan_2    VLAN ID used by ``actions_2``.
4628     cos_2     Class of Service used by ``actions_2``.
4629     ========= ===============================================
4630     """
4631     # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4632     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4633     # | Type=0x08     | Sub-Type=0x0a |PO1|PU1|SW1|RT1|RO1|...|PO2|...|
4634     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4635     # |       VLAN ID1      |  COS1 |0|       VLAN ID2      |  COS2 |0|
4636     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4637     _VALUE_PACK_STR = '!BBBHH'
4638     _VALUE_FIELDS = [
4639         'subtype',
4640         'actions_1',
4641         'actions_2',
4642         'vlan_1',
4643         'vlan_2',
4644         'cos_1',
4645         'cos_2']
4646     ACTION_NAME = 'vlan_action'
4647     _COS_MASK = 0x07
4648
4649     POP = 1 << 7
4650     PUSH = 1 << 6
4651     SWAP = 1 << 5
4652     REWRITE_INNER = 1 << 4
4653     REWRITE_OUTER = 1 << 3
4654
4655     def __init__(self, **kwargs):
4656         super(BGPFlowSpecVlanActionCommunity, self).__init__()
4657         kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_VLAN_ACTION
4658         self.do_init(BGPFlowSpecVlanActionCommunity, self, kwargs)
4659
4660     @classmethod
4661     def parse_value(cls, buf):
4662         (subtype, actions_1, actions_2,
4663          vlan_cos_1, vlan_cos_2) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
4664
4665         return {
4666             'subtype': subtype,
4667             'actions_1': actions_1,
4668             'vlan_1': int(vlan_cos_1 >> 4),
4669             'cos_1': int((vlan_cos_1 >> 1) & cls._COS_MASK),
4670             'actions_2': actions_2,
4671             'vlan_2': int(vlan_cos_2 >> 4),
4672             'cos_2': int((vlan_cos_2 >> 1) & cls._COS_MASK)
4673         }
4674
4675     def serialize_value(self):
4676         return struct.pack(
4677             self._VALUE_PACK_STR,
4678             self.subtype,
4679             self.actions_1,
4680             self.actions_2,
4681             (self.vlan_1 << 4) + (self.cos_1 << 1),
4682             (self.vlan_2 << 4) + (self.cos_2 << 1),
4683         )
4684
4685
4686 @_ExtendedCommunity.register_type(
4687     _ExtendedCommunity.FLOWSPEC_TPID_ACTION)
4688 class BGPFlowSpecTPIDActionCommunity(_ExtendedCommunity):
4689     """
4690     Flow Specification TPID Actions.
4691
4692     ========= =========================================================
4693     Attribute Description
4694     ========= =========================================================
4695     actions   Bit representation of actions.
4696               Supported actions are
4697               ``TI(inner TPID action)`` and ``TO(outer TPID action)``.
4698     tpid_1    TPID used by ``TI``.
4699     tpid_2    TPID used by ``TO``.
4700     ========= =========================================================
4701     """
4702     # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
4703     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4704     # | Type=0x08     | Sub-Type=0x0b   |TI|TO|      Reserved=0       |
4705     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4706     # |             TPID1               |             TPID2           |
4707     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4708     _VALUE_PACK_STR = '!BHHH'
4709     _VALUE_FIELDS = ['subtype', 'actions', 'tpid_1', 'tpid_2']
4710     ACTION_NAME = 'tpid_action'
4711
4712     TI = 1 << 15
4713     TO = 1 << 14
4714
4715     def __init__(self, **kwargs):
4716         super(BGPFlowSpecTPIDActionCommunity, self).__init__()
4717         kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_TPID_ACTION
4718         self.do_init(BGPFlowSpecTPIDActionCommunity, self, kwargs)
4719
4720     @classmethod
4721     def parse_value(cls, buf):
4722         (subtype, actions, tpid_1, tpid_2) = struct.unpack_from(
4723             cls._VALUE_PACK_STR, buf)
4724
4725         return {
4726             'subtype': subtype,
4727             'actions': actions,
4728             'tpid_1': tpid_1,
4729             'tpid_2': tpid_2,
4730         }
4731
4732     def serialize_value(self):
4733         return struct.pack(
4734             self._VALUE_PACK_STR,
4735             self.subtype,
4736             self.actions,
4737             self.tpid_1,
4738             self.tpid_2,
4739         )
4740
4741
4742 @_ExtendedCommunity.register_unknown_type()
4743 class BGPUnknownExtendedCommunity(_ExtendedCommunity):
4744     _VALUE_PACK_STR = '!7s'  # opaque value
4745
4746     def __init__(self, type_, **kwargs):
4747         super(BGPUnknownExtendedCommunity, self).__init__(type_=type_)
4748         self.do_init(BGPUnknownExtendedCommunity, self, kwargs, type_=type_)
4749
4750
4751 @_PathAttribute.register_type(BGP_ATTR_TYPE_MP_REACH_NLRI)
4752 class BGPPathAttributeMpReachNLRI(_PathAttribute):
4753     _VALUE_PACK_STR = '!HBB'  # afi, safi, next_hop_len
4754     _VALUE_PACK_SIZE = struct.calcsize(_VALUE_PACK_STR)
4755     _RD_LENGTH = 8
4756     _RESERVED_LENGTH = 1
4757     _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
4758     _class_suffixes = ['AddrPrefix']
4759     _opt_attributes = ['next_hop']
4760     _TYPE = {
4761         'ascii': [
4762             'next_hop'
4763         ]
4764     }
4765
4766     def __init__(self, afi, safi, next_hop, nlri,
4767                  flags=0, type_=None, length=None):
4768         super(BGPPathAttributeMpReachNLRI, self).__init__(
4769             flags=flags, type_=type_, length=length)
4770         self.afi = afi
4771         self.safi = safi
4772         if not isinstance(next_hop, (list, tuple)):
4773             next_hop = [next_hop]
4774         for n in next_hop:
4775             if not ip.valid_ipv4(n) and not ip.valid_ipv6(n):
4776                 raise ValueError('Invalid address for next_hop: %s' % n)
4777         # Note: For the backward compatibility, stores the first next_hop
4778         # address and all next_hop addresses separately.
4779         if next_hop:
4780             self._next_hop = next_hop[0]
4781         else:
4782             self._next_hop = None
4783         self._next_hop_list = next_hop
4784         self.nlri = nlri
4785         addr_cls = _get_addr_class(afi, safi)
4786         for i in nlri:
4787             if not isinstance(i, addr_cls):
4788                 raise ValueError('Invalid NRLI class for afi=%d and safi=%d'
4789                                  % (self.afi, self.safi))
4790
4791     @staticmethod
4792     def split_bin_with_len(buf, unit_len):
4793         f = io.BytesIO(buf)
4794         return [f.read(unit_len) for _ in range(0, len(buf), unit_len)]
4795
4796     @classmethod
4797     def parse_next_hop_ipv4(cls, buf, unit_len):
4798         next_hop = []
4799         for next_hop_bin in cls.split_bin_with_len(buf, unit_len):
4800             next_hop.append(addrconv.ipv4.bin_to_text(next_hop_bin[-4:]))
4801         return next_hop
4802
4803     @classmethod
4804     def parse_next_hop_ipv6(cls, buf, unit_len):
4805         next_hop = []
4806         for next_hop_bin in cls.split_bin_with_len(buf, unit_len):
4807             next_hop.append(addrconv.ipv6.bin_to_text(next_hop_bin[-16:]))
4808         return next_hop
4809
4810     @classmethod
4811     def parse_value(cls, buf):
4812         (afi, safi, next_hop_len,) = struct.unpack_from(
4813             cls._VALUE_PACK_STR, six.binary_type(buf))
4814         rest = buf[cls._VALUE_PACK_SIZE:]
4815
4816         next_hop_bin = rest[:next_hop_len]
4817         rest = rest[next_hop_len:]
4818         reserved = rest[:cls._RESERVED_LENGTH]
4819         assert reserved == b'\0'
4820
4821         nlri_bin = rest[cls._RESERVED_LENGTH:]
4822         addr_cls = _get_addr_class(afi, safi)
4823         nlri = []
4824         while nlri_bin:
4825             n, nlri_bin = addr_cls.parser(nlri_bin)
4826             nlri.append(n)
4827
4828         rf = RouteFamily(afi, safi)
4829         if rf == RF_IPv4_VPN:
4830             next_hop = cls.parse_next_hop_ipv4(next_hop_bin,
4831                                                cls._RD_LENGTH + 4)
4832             next_hop_len -= cls._RD_LENGTH * len(next_hop)
4833         elif rf == RF_IPv6_VPN:
4834             next_hop = cls.parse_next_hop_ipv6(next_hop_bin,
4835                                                cls._RD_LENGTH + 16)
4836             next_hop_len -= cls._RD_LENGTH * len(next_hop)
4837         elif (afi == addr_family.IP
4838               or (rf == RF_L2_EVPN and next_hop_len < 16)):
4839             next_hop = cls.parse_next_hop_ipv4(next_hop_bin, 4)
4840         elif (afi == addr_family.IP6
4841               or (rf == RF_L2_EVPN and next_hop_len >= 16)):
4842             next_hop = cls.parse_next_hop_ipv6(next_hop_bin, 16)
4843         elif rf == RF_L2VPN_FLOWSPEC:
4844             next_hop = []
4845         else:
4846             raise ValueError('Invalid address family: afi=%d, safi=%d'
4847                              % (afi, safi))
4848
4849         return {
4850             'afi': afi,
4851             'safi': safi,
4852             'next_hop': next_hop,
4853             'nlri': nlri,
4854         }
4855
4856     def serialize_next_hop(self):
4857         buf = bytearray()
4858         for next_hop in self.next_hop_list:
4859             if self.afi == addr_family.IP6:
4860                 next_hop = str(netaddr.IPAddress(next_hop).ipv6())
4861             next_hop_bin = ip.text_to_bin(next_hop)
4862             if RouteFamily(self.afi, self.safi) in (RF_IPv4_VPN, RF_IPv6_VPN):
4863                 # Empty label stack(RD=0:0) + IP address
4864                 next_hop_bin = b'\x00' * self._RD_LENGTH + next_hop_bin
4865             buf += next_hop_bin
4866
4867         return buf
4868
4869     def serialize_value(self):
4870         next_hop_bin = self.serialize_next_hop()
4871
4872         # fixup
4873         next_hop_len = len(next_hop_bin)
4874
4875         buf = bytearray()
4876         msg_pack_into(self._VALUE_PACK_STR, buf, 0,
4877                       self.afi, self.safi, next_hop_len)
4878         buf += next_hop_bin
4879         buf += b'\0'  # reserved
4880
4881         nlri_bin = bytearray()
4882         for n in self.nlri:
4883             nlri_bin += n.serialize()
4884         buf += nlri_bin
4885
4886         return buf
4887
4888     @property
4889     def next_hop(self):
4890         return self._next_hop
4891
4892     @next_hop.setter
4893     def next_hop(self, addr):
4894         if not ip.valid_ipv4(addr) and not ip.valid_ipv6(addr):
4895             raise ValueError('Invalid address for next_hop: %s' % addr)
4896         self._next_hop = addr
4897         self.next_hop_list[0] = addr
4898
4899     @property
4900     def next_hop_list(self):
4901         return self._next_hop_list
4902
4903     @next_hop_list.setter
4904     def next_hop_list(self, addr_list):
4905         if not isinstance(addr_list, (list, tuple)):
4906             addr_list = [addr_list]
4907         for addr in addr_list:
4908             if not ip.valid_ipv4(addr) and not ip.valid_ipv6(addr):
4909                 raise ValueError('Invalid address for next_hop: %s' % addr)
4910         self._next_hop = addr_list[0]
4911         self._next_hop_list = addr_list
4912
4913     @property
4914     def route_family(self):
4915         return _rf_map[(self.afi, self.safi)]
4916
4917
4918 @_PathAttribute.register_type(BGP_ATTR_TYPE_MP_UNREACH_NLRI)
4919 class BGPPathAttributeMpUnreachNLRI(_PathAttribute):
4920     _VALUE_PACK_STR = '!HB'  # afi, safi
4921     _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
4922     _class_suffixes = ['AddrPrefix']
4923
4924     def __init__(self, afi, safi, withdrawn_routes,
4925                  flags=0, type_=None, length=None):
4926         super(BGPPathAttributeMpUnreachNLRI, self).__init__(
4927             flags=flags, type_=type_, length=length)
4928         self.afi = afi
4929         self.safi = safi
4930         self.withdrawn_routes = withdrawn_routes
4931         addr_cls = _get_addr_class(afi, safi)
4932         for i in withdrawn_routes:
4933             if not isinstance(i, addr_cls):
4934                 raise ValueError('Invalid NRLI class for afi=%d and safi=%d'
4935                                  % (self.afi, self.safi))
4936
4937     @classmethod
4938     def parse_value(cls, buf):
4939         (afi, safi,) = struct.unpack_from(
4940             cls._VALUE_PACK_STR, six.binary_type(buf))
4941
4942         nlri_bin = buf[struct.calcsize(cls._VALUE_PACK_STR):]
4943         addr_cls = _get_addr_class(afi, safi)
4944         nlri = []
4945         while nlri_bin:
4946             n, nlri_bin = addr_cls.parser(nlri_bin)
4947             nlri.append(n)
4948
4949         return {
4950             'afi': afi,
4951             'safi': safi,
4952             'withdrawn_routes': nlri,
4953         }
4954
4955     def serialize_value(self):
4956         buf = bytearray()
4957         msg_pack_into(self._VALUE_PACK_STR, buf, 0, self.afi, self.safi)
4958
4959         nlri_bin = bytearray()
4960         for n in self.withdrawn_routes:
4961             nlri_bin += n.serialize()
4962         buf += nlri_bin
4963
4964         return buf
4965
4966     @property
4967     def route_family(self):
4968         return _rf_map[(self.afi, self.safi)]
4969
4970
4971 @_PathAttribute.register_type(BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE)
4972 class BGPPathAttributePmsiTunnel(_PathAttribute):
4973     """
4974     P-Multicast Service Interface Tunnel (PMSI Tunnel) attribute
4975     """
4976
4977     # pmsi_flags, tunnel_type, mpls_label
4978     _VALUE_PACK_STR = '!BB3s'
4979     _PACK_STR_SIZE = struct.calcsize(_VALUE_PACK_STR)
4980     _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE
4981
4982     # RFC 6514
4983     # +--------------------------------+
4984     # |  Flags (1 octet)               |
4985     # +--------------------------------+
4986     # |  Tunnel Type (1 octets)        |
4987     # +--------------------------------+
4988     # |  MPLS Label (3 octets)         |
4989     # +--------------------------------+
4990     # |  Tunnel Identifier (variable)  |
4991     # +--------------------------------+
4992
4993     # The Flags field has the following format:
4994     #  0 1 2 3 4 5 6 7
4995     # +-+-+-+-+-+-+-+-+
4996     # |  reserved   |L|
4997     # +-+-+-+-+-+-+-+-+
4998     # `L` refers to the Leaf Information Required.
4999
5000     # Current, Tunnel Type supports following.
5001     # + 0 - No tunnel information present
5002     # + 6 - Ingress Replication
5003     TYPE_NO_TUNNEL_INFORMATION_PRESENT = 0
5004     TYPE_INGRESS_REPLICATION = 6
5005
5006     # TODO:
5007     # The following Tunnel Type are not supported.
5008     # Therefore, we will need to support in the future.
5009     # + 1 - RSVP-TE P2MP LSP
5010     # + 2 - mLDP P2MP LSP
5011     # + 3 - PIM-SSM Tree
5012     # + 4 - PIM-SM Tree
5013     # + 5 - BIDIR-PIM Tree
5014     # + 7 - mLDP MP2MP LSP
5015
5016     def __init__(self, pmsi_flags, tunnel_type,
5017                  mpls_label=None, label=None, vni=None, tunnel_id=None,
5018                  flags=0, type_=None, length=None):
5019         super(BGPPathAttributePmsiTunnel, self).__init__(flags=flags,
5020                                                          type_=type_,
5021                                                          length=length)
5022         self.pmsi_flags = pmsi_flags
5023         self.tunnel_type = tunnel_type
5024         self.tunnel_id = tunnel_id
5025
5026         if label:
5027             # If binary type label field value is specified, stores it
5028             # and decodes as MPLS label and VNI.
5029             self._label = label
5030             self._mpls_label, _ = mpls.label_from_bin(label)
5031             self._vni = vxlan.vni_from_bin(label)
5032         else:
5033             # If either MPLS label or VNI is specified, stores it
5034             # and encodes into binary type label field value.
5035             self._label = self._serialize_label(mpls_label, vni)
5036             self._mpls_label = mpls_label
5037             self._vni = vni
5038
5039     @classmethod
5040     def parse_value(cls, buf):
5041         (pmsi_flags,
5042          tunnel_type,
5043          label) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
5044         value = buf[cls._PACK_STR_SIZE:]
5045
5046         return {
5047             'pmsi_flags': pmsi_flags,
5048             'tunnel_type': tunnel_type,
5049             'label': label,
5050             'tunnel_id': _PmsiTunnelId.parse(tunnel_type, value)
5051         }
5052
5053     def serialize_value(self):
5054         buf = bytearray()
5055         msg_pack_into(self._VALUE_PACK_STR, buf, 0,
5056                       self.pmsi_flags, self.tunnel_type, self._label)
5057
5058         if self.tunnel_id is not None:
5059             buf += self.tunnel_id.serialize()
5060
5061         return buf
5062
5063     def _serialize_label(self, mpls_label, vni):
5064         if mpls_label:
5065             return mpls.label_to_bin(mpls_label, is_bos=True)
5066         elif vni:
5067             return vxlan.vni_to_bin(vni)
5068         else:
5069             return b'\x00' * 3
5070
5071     @property
5072     def mpls_label(self):
5073         return self._mpls_label
5074
5075     @mpls_label.setter
5076     def mpls_label(self, mpls_label):
5077         self._label = mpls.label_to_bin(mpls_label, is_bos=True)
5078         self._mpls_label = mpls_label
5079         self._vni = None  # disables VNI
5080
5081     @property
5082     def vni(self):
5083         return self._vni
5084
5085     @vni.setter
5086     def vni(self, vni):
5087         self._label = vxlan.vni_to_bin(vni)
5088         self._mpls_label = None  # disables MPLS label
5089         self._vni = vni
5090
5091     @classmethod
5092     def from_jsondict(cls, dict_, decode_string=base64.b64decode,
5093                       **additional_args):
5094         if isinstance(dict_['tunnel_id'], dict):
5095             tunnel_id = dict_.pop('tunnel_id')
5096             ins = super(BGPPathAttributePmsiTunnel,
5097                         cls).from_jsondict(dict_,
5098                                            decode_string,
5099                                            **additional_args)
5100
5101             mod = import_module(cls.__module__)
5102
5103             for key, value in tunnel_id.items():
5104                 tunnel_id_cls = getattr(mod, key)
5105                 ins.tunnel_id = tunnel_id_cls.from_jsondict(value,
5106                                                             decode_string,
5107                                                             **additional_args)
5108         else:
5109             ins = super(BGPPathAttributePmsiTunnel,
5110                         cls).from_jsondict(dict_,
5111                                            decode_string,
5112                                            **additional_args)
5113
5114         return ins
5115
5116
5117 class _PmsiTunnelId(StringifyMixin, TypeDisp):
5118
5119     @classmethod
5120     def parse(cls, tunnel_type, buf):
5121         subcls = cls._lookup_type(tunnel_type)
5122         return subcls.parser(buf)
5123
5124
5125 @_PmsiTunnelId.register_unknown_type()
5126 class PmsiTunnelIdUnknown(_PmsiTunnelId):
5127     """
5128     Unknown route type specific _PmsiTunnelId
5129     """
5130
5131     def __init__(self, value):
5132         super(PmsiTunnelIdUnknown, self).__init__()
5133         self.value = value
5134
5135     @classmethod
5136     def parser(cls, buf):
5137         return cls(value=buf)
5138
5139     def serialize(self):
5140         return self.value
5141
5142
5143 @_PmsiTunnelId.register_type(
5144     BGPPathAttributePmsiTunnel.TYPE_NO_TUNNEL_INFORMATION_PRESENT)
5145 class _PmsiTunnelIdNoInformationPresent(_PmsiTunnelId):
5146
5147     @classmethod
5148     def parser(cls, buf):
5149         return None
5150
5151
5152 @_PmsiTunnelId.register_type(
5153     BGPPathAttributePmsiTunnel.TYPE_INGRESS_REPLICATION)
5154 class PmsiTunnelIdIngressReplication(_PmsiTunnelId):
5155     # tunnel_endpoint_ip
5156     _VALUE_PACK_STR = '!%ds'
5157     _TYPE = {
5158         'ascii': [
5159             'tunnel_endpoint_ip'
5160         ]
5161     }
5162
5163     def __init__(self, tunnel_endpoint_ip):
5164         super(PmsiTunnelIdIngressReplication, self).__init__()
5165         self.tunnel_endpoint_ip = tunnel_endpoint_ip
5166
5167     @classmethod
5168     def parser(cls, buf):
5169         (tunnel_endpoint_ip, ) = struct.unpack_from(
5170             cls._VALUE_PACK_STR % len(buf),
5171             six.binary_type(buf))
5172         return cls(tunnel_endpoint_ip=ip.bin_to_text(tunnel_endpoint_ip))
5173
5174     def serialize(self):
5175         ip_bin = ip.text_to_bin(self.tunnel_endpoint_ip)
5176         return struct.pack(self._VALUE_PACK_STR % len(ip_bin),
5177                            ip.text_to_bin(self.tunnel_endpoint_ip))
5178
5179
5180 class BGPNLRI(IPAddrPrefix):
5181     pass
5182
5183
5184 class BGPMessage(packet_base.PacketBase, TypeDisp):
5185     """Base class for BGP-4 messages.
5186
5187     An instance has the following attributes at least.
5188     Most of them are same to the on-wire counterparts but in host byte
5189     order.
5190     __init__ takes the corresponding args in this order.
5191
5192     ========================== ===============================================
5193     Attribute                  Description
5194     ========================== ===============================================
5195     marker                     Marker field.  Ignored when encoding.
5196     len                        Length field.  Ignored when encoding.
5197     type                       Type field.  one of ``BGP_MSG_*`` constants.
5198     ========================== ===============================================
5199     """
5200
5201     _HDR_PACK_STR = '!16sHB'  # marker, len, type
5202     _HDR_LEN = struct.calcsize(_HDR_PACK_STR)
5203     _class_prefixes = ['BGP']
5204
5205     def __init__(self, marker=None, len_=None, type_=None):
5206         super(BGPMessage, self).__init__()
5207         if marker is None:
5208             self._marker = _MARKER
5209         else:
5210             self._marker = marker
5211         self.len = len_
5212         if type_ is None:
5213             type_ = self._rev_lookup_type(self.__class__)
5214         self.type = type_
5215
5216     @classmethod
5217     def parser(cls, buf):
5218         if len(buf) < cls._HDR_LEN:
5219             raise stream_parser.StreamParser.TooSmallException(
5220                 '%d < %d' % (len(buf), cls._HDR_LEN))
5221         (marker, len_, type_) = struct.unpack_from(cls._HDR_PACK_STR,
5222                                                    six.binary_type(buf))
5223         msglen = len_
5224         if len(buf) < msglen:
5225             raise stream_parser.StreamParser.TooSmallException(
5226                 '%d < %d' % (len(buf), msglen))
5227         binmsg = buf[cls._HDR_LEN:msglen]
5228         rest = buf[msglen:]
5229         subcls = cls._lookup_type(type_)
5230         kwargs = subcls.parser(binmsg)
5231         return subcls(marker=marker, len_=len_, type_=type_,
5232                       **kwargs), cls, rest
5233
5234     def serialize(self, payload=None, prev=None):
5235         # fixup
5236         self._marker = _MARKER
5237         tail = self.serialize_tail()
5238         self.len = self._HDR_LEN + len(tail)
5239
5240         hdr = bytearray(struct.pack(self._HDR_PACK_STR, self._marker,
5241                                     self.len, self.type))
5242         return hdr + tail
5243
5244     def __len__(self):
5245         # XXX destructive
5246         buf = self.serialize()
5247         return len(buf)
5248
5249
5250 @BGPMessage.register_type(BGP_MSG_OPEN)
5251 class BGPOpen(BGPMessage):
5252     """BGP-4 OPEN Message encoder/decoder class.
5253
5254     An instance has the following attributes at least.
5255     Most of them are same to the on-wire counterparts but in host byte
5256     order.
5257     __init__ takes the corresponding args in this order.
5258
5259     ========================== ===============================================
5260     Attribute                  Description
5261     ========================== ===============================================
5262     marker                     Marker field.  Ignored when encoding.
5263     len                        Length field.  Ignored when encoding.
5264     type                       Type field.
5265     version                    Version field.
5266     my_as                      My Autonomous System field.
5267                                2 octet unsigned integer.
5268     hold_time                  Hold Time field.
5269                                2 octet unsigned integer.
5270     bgp_identifier             BGP Identifier field.
5271                                An IPv4 address.
5272                                For example, '192.0.2.1'
5273     opt_param_len              Optional Parameters Length field.
5274                                Ignored when encoding.
5275     opt_param                  Optional Parameters field.
5276                                A list of BGPOptParam instances.
5277                                The default is [].
5278     ========================== ===============================================
5279     """
5280
5281     _PACK_STR = '!BHH4sB'
5282     _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR)
5283     _TYPE = {
5284         'ascii': [
5285             'bgp_identifier'
5286         ]
5287     }
5288
5289     def __init__(self, my_as, bgp_identifier, type_=BGP_MSG_OPEN,
5290                  opt_param_len=0, opt_param=None,
5291                  version=_VERSION, hold_time=0, len_=None, marker=None):
5292         opt_param = opt_param if opt_param else []
5293         super(BGPOpen, self).__init__(marker=marker, len_=len_, type_=type_)
5294         self.version = version
5295         self.my_as = my_as
5296         self.bgp_identifier = bgp_identifier
5297         self.hold_time = hold_time
5298         self.opt_param_len = opt_param_len
5299         self.opt_param = opt_param
5300
5301     @property
5302     def opt_param_cap_map(self):
5303         cap_map = {}
5304         for param in self.opt_param:
5305             if param.type == BGP_OPT_CAPABILITY:
5306                 cap_map[param.cap_code] = param
5307         return cap_map
5308
5309     def get_opt_param_cap(self, cap_code):
5310         return self.opt_param_cap_map.get(cap_code)
5311
5312     @classmethod
5313     def parser(cls, buf):
5314         (version,
5315          my_as,
5316          hold_time,
5317          bgp_identifier,
5318          opt_param_len) = struct.unpack_from(cls._PACK_STR,
5319                                              six.binary_type(buf))
5320         rest = buf[struct.calcsize(cls._PACK_STR):]
5321         binopts = rest[:opt_param_len]
5322         opt_param = []
5323         while binopts:
5324             opt, binopts = _OptParam.parser(binopts)
5325             opt_param.extend(opt)
5326         return {
5327             "version": version,
5328             "my_as": my_as,
5329             "hold_time": hold_time,
5330             "bgp_identifier": addrconv.ipv4.bin_to_text(bgp_identifier),
5331             "opt_param_len": opt_param_len,
5332             "opt_param": opt_param,
5333         }
5334
5335     def serialize_tail(self):
5336         # fixup
5337         self.version = _VERSION
5338         binopts = bytearray()
5339         for opt in self.opt_param:
5340             binopts += opt.serialize()
5341         self.opt_param_len = len(binopts)
5342
5343         msg = bytearray(struct.pack(self._PACK_STR,
5344                                     self.version,
5345                                     self.my_as,
5346                                     self.hold_time,
5347                                     addrconv.ipv4.text_to_bin(
5348                                         self.bgp_identifier),
5349                                     self.opt_param_len))
5350         msg += binopts
5351         return msg
5352
5353
5354 @BGPMessage.register_type(BGP_MSG_UPDATE)
5355 class BGPUpdate(BGPMessage):
5356     """BGP-4 UPDATE Message encoder/decoder class.
5357
5358     An instance has the following attributes at least.
5359     Most of them are same to the on-wire counterparts but in host byte
5360     order.
5361     __init__ takes the corresponding args in this order.
5362
5363     .. tabularcolumns:: |l|L|
5364
5365     ========================== ===============================================
5366     Attribute                  Description
5367     ========================== ===============================================
5368     marker                     Marker field.  Ignored when encoding.
5369     len                        Length field.  Ignored when encoding.
5370     type                       Type field.
5371     withdrawn_routes_len       Withdrawn Routes Length field.
5372                                Ignored when encoding.
5373     withdrawn_routes           Withdrawn Routes field.
5374                                A list of BGPWithdrawnRoute instances.
5375                                The default is [].
5376     total_path_attribute_len   Total Path Attribute Length field.
5377                                Ignored when encoding.
5378     path_attributes            Path Attributes field.
5379                                A list of BGPPathAttribute instances.
5380                                The default is [].
5381     nlri                       Network Layer Reachability Information field.
5382                                A list of BGPNLRI instances.
5383                                The default is [].
5384     ========================== ===============================================
5385     """
5386
5387     _MIN_LEN = BGPMessage._HDR_LEN
5388
5389     def __init__(self, type_=BGP_MSG_UPDATE,
5390                  withdrawn_routes_len=None,
5391                  withdrawn_routes=None,
5392                  total_path_attribute_len=None,
5393                  path_attributes=None,
5394                  nlri=None,
5395                  len_=None, marker=None):
5396         withdrawn_routes = withdrawn_routes if withdrawn_routes else []
5397         path_attributes = path_attributes if path_attributes else []
5398         nlri = nlri if nlri else []
5399         super(BGPUpdate, self).__init__(marker=marker, len_=len_, type_=type_)
5400         self.withdrawn_routes_len = withdrawn_routes_len
5401         self.withdrawn_routes = withdrawn_routes
5402         self.total_path_attribute_len = total_path_attribute_len
5403         self.path_attributes = path_attributes
5404         self.nlri = nlri
5405
5406     @property
5407     def pathattr_map(self):
5408         passattr_map = {}
5409         for attr in self.path_attributes:
5410             passattr_map[attr.type] = attr
5411         return passattr_map
5412
5413     def get_path_attr(self, attr_name):
5414         return self.pathattr_map.get(attr_name)
5415
5416     @classmethod
5417     def parser(cls, buf):
5418         offset = 0
5419         buf = six.binary_type(buf)
5420         (withdrawn_routes_len,) = struct.unpack_from('!H', buf, offset)
5421         binroutes = buf[offset + 2:
5422                         offset + 2 + withdrawn_routes_len]
5423         offset += 2 + withdrawn_routes_len
5424         (total_path_attribute_len,) = struct.unpack_from('!H', buf, offset)
5425         binpathattrs = buf[offset + 2:
5426                            offset + 2 + total_path_attribute_len]
5427         binnlri = buf[offset + 2 + total_path_attribute_len:]
5428         withdrawn_routes = []
5429         while binroutes:
5430             r, binroutes = BGPWithdrawnRoute.parser(binroutes)
5431             withdrawn_routes.append(r)
5432         path_attributes = []
5433         while binpathattrs:
5434             pa, binpathattrs = _PathAttribute.parser(binpathattrs)
5435             path_attributes.append(pa)
5436         offset += 2 + total_path_attribute_len
5437         nlri = []
5438         while binnlri:
5439             n, binnlri = BGPNLRI.parser(binnlri)
5440             nlri.append(n)
5441         return {
5442             "withdrawn_routes_len": withdrawn_routes_len,
5443             "withdrawn_routes": withdrawn_routes,
5444             "total_path_attribute_len": total_path_attribute_len,
5445             "path_attributes": path_attributes,
5446             "nlri": nlri,
5447         }
5448
5449     def serialize_tail(self):
5450         # fixup
5451         binroutes = bytearray()
5452         for r in self.withdrawn_routes:
5453             binroutes += r.serialize()
5454         self.withdrawn_routes_len = len(binroutes)
5455         binpathattrs = bytearray()
5456         for pa in self.path_attributes:
5457             binpathattrs += pa.serialize()
5458         self.total_path_attribute_len = len(binpathattrs)
5459         binnlri = bytearray()
5460         for n in self.nlri:
5461             binnlri += n.serialize()
5462
5463         msg = bytearray()
5464         offset = 0
5465         msg_pack_into('!H', msg, offset, self.withdrawn_routes_len)
5466         msg += binroutes
5467         offset += 2 + self.withdrawn_routes_len
5468         msg_pack_into('!H', msg, offset, self.total_path_attribute_len)
5469         msg += binpathattrs
5470         offset += 2 + self.total_path_attribute_len
5471         msg += binnlri
5472         return msg
5473
5474
5475 @BGPMessage.register_type(BGP_MSG_KEEPALIVE)
5476 class BGPKeepAlive(BGPMessage):
5477     """BGP-4 KEEPALIVE Message encoder/decoder class.
5478
5479     An instance has the following attributes at least.
5480     Most of them are same to the on-wire counterparts but in host byte
5481     order.
5482     __init__ takes the corresponding args in this order.
5483
5484     ========================== ===============================================
5485     Attribute                  Description
5486     ========================== ===============================================
5487     marker                     Marker field.  Ignored when encoding.
5488     len                        Length field.  Ignored when encoding.
5489     type                       Type field.
5490     ========================== ===============================================
5491     """
5492
5493     _MIN_LEN = BGPMessage._HDR_LEN
5494
5495     def __init__(self, type_=BGP_MSG_KEEPALIVE, len_=None, marker=None):
5496         super(BGPKeepAlive, self).__init__(marker=marker, len_=len_,
5497                                            type_=type_)
5498
5499     @classmethod
5500     def parser(cls, buf):
5501         return {}
5502
5503     def serialize_tail(self):
5504         return bytearray()
5505
5506
5507 @BGPMessage.register_type(BGP_MSG_NOTIFICATION)
5508 class BGPNotification(BGPMessage):
5509     """BGP-4 NOTIFICATION Message encoder/decoder class.
5510
5511     An instance has the following attributes at least.
5512     Most of them are same to the on-wire counterparts but in host byte
5513     order.
5514     __init__ takes the corresponding args in this order.
5515
5516     ========================== ===============================================
5517     Attribute                  Description
5518     ========================== ===============================================
5519     marker                     Marker field.  Ignored when encoding.
5520     len                        Length field.  Ignored when encoding.
5521     type                       Type field.
5522     error_code                 Error code field.
5523     error_subcode              Error subcode field.
5524     data                       Data field.
5525     ========================== ===============================================
5526     """
5527
5528     _PACK_STR = '!BB'
5529     _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR)
5530
5531     _REASONS = {
5532         (1, 1): 'Message Header Error: not synchronised',
5533         (1, 2): 'Message Header Error: bad message len',
5534         (1, 3): 'Message Header Error: bad message type',
5535         (2, 1): 'Open Message Error: unsupported version',
5536         (2, 2): 'Open Message Error: bad peer AS',
5537         (2, 3): 'Open Message Error: bad BGP identifier',
5538         (2, 4): 'Open Message Error: unsupported optional param',
5539         (2, 5): 'Open Message Error: authentication failure',
5540         (2, 6): 'Open Message Error: unacceptable hold time',
5541         (2, 7): 'Open Message Error: Unsupported Capability',
5542         (2, 8): 'Open Message Error: Unassigned',
5543         (3, 1): 'Update Message Error: malformed attribute list',
5544         (3, 2): 'Update Message Error: unrecognized well-known attr',
5545         (3, 3): 'Update Message Error: missing well-known attr',
5546         (3, 4): 'Update Message Error: attribute flags error',
5547         (3, 5): 'Update Message Error: attribute length error',
5548         (3, 6): 'Update Message Error: invalid origin attr',
5549         (3, 7): 'Update Message Error: as routing loop',
5550         (3, 8): 'Update Message Error: invalid next hop attr',
5551         (3, 9): 'Update Message Error: optional attribute error',
5552         (3, 10): 'Update Message Error: invalid network field',
5553         (3, 11): 'Update Message Error: malformed AS_PATH',
5554         (4, 1): 'Hold Timer Expired',
5555         (5, 1): 'Finite State Machine Error',
5556         (6, 1): 'Cease: Maximum Number of Prefixes Reached',
5557         (6, 2): 'Cease: Administrative Shutdown',
5558         (6, 3): 'Cease: Peer De-configured',
5559         (6, 4): 'Cease: Administrative Reset',
5560         (6, 5): 'Cease: Connection Rejected',
5561         (6, 6): 'Cease: Other Configuration Change',
5562         (6, 7): 'Cease: Connection Collision Resolution',
5563         (6, 8): 'Cease: Out of Resources',
5564     }
5565
5566     def __init__(self,
5567                  error_code,
5568                  error_subcode,
5569                  data=b'',
5570                  type_=BGP_MSG_NOTIFICATION, len_=None, marker=None):
5571         super(BGPNotification, self).__init__(marker=marker, len_=len_,
5572                                               type_=type_)
5573         self.error_code = error_code
5574         self.error_subcode = error_subcode
5575         self.data = data
5576
5577     @classmethod
5578     def parser(cls, buf):
5579         (error_code, error_subcode,) = struct.unpack_from(cls._PACK_STR,
5580                                                           six.binary_type(buf))
5581         data = bytes(buf[2:])
5582         return {
5583             "error_code": error_code,
5584             "error_subcode": error_subcode,
5585             "data": data,
5586         }
5587
5588     def serialize_tail(self):
5589         msg = bytearray(struct.pack(self._PACK_STR, self.error_code,
5590                                     self.error_subcode))
5591         msg += self.data
5592         return msg
5593
5594     @property
5595     def reason(self):
5596         return self._REASONS.get((self.error_code, self.error_subcode))
5597
5598
5599 @BGPMessage.register_type(BGP_MSG_ROUTE_REFRESH)
5600 class BGPRouteRefresh(BGPMessage):
5601     """BGP-4 ROUTE REFRESH Message (RFC 2918) encoder/decoder class.
5602
5603     An instance has the following attributes at least.
5604     Most of them are same to the on-wire counterparts but in host byte
5605     order.
5606     __init__ takes the corresponding args in this order.
5607
5608     ========================== ===============================================
5609     Attribute                  Description
5610     ========================== ===============================================
5611     marker                     Marker field.  Ignored when encoding.
5612     len                        Length field.  Ignored when encoding.
5613     type                       Type field.
5614     afi                        Address Family Identifier
5615     safi                       Subsequent Address Family Identifier
5616     ========================== ===============================================
5617     """
5618
5619     _PACK_STR = '!HBB'
5620     _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR)
5621
5622     def __init__(self,
5623                  afi, safi, demarcation=0,
5624                  type_=BGP_MSG_ROUTE_REFRESH, len_=None, marker=None):
5625         super(BGPRouteRefresh, self).__init__(marker=marker, len_=len_,
5626                                               type_=type_)
5627         self.afi = afi
5628         self.safi = safi
5629         self.demarcation = demarcation
5630         self.eor_sent = False
5631
5632     @classmethod
5633     def parser(cls, buf):
5634         (afi, demarcation, safi,) = struct.unpack_from(cls._PACK_STR,
5635                                                        six.binary_type(buf))
5636         return {
5637             "afi": afi,
5638             "safi": safi,
5639             "demarcation": demarcation,
5640         }
5641
5642     def serialize_tail(self):
5643         return bytearray(struct.pack(self._PACK_STR, self.afi,
5644                                      self.demarcation, self.safi))
5645
5646
5647 class StreamParser(stream_parser.StreamParser):
5648     """Streaming parser for BGP-4 messages.
5649
5650     This is a subclass of ryu.lib.packet.stream_parser.StreamParser.
5651     Its parse method returns a list of BGPMessage subclass instances.
5652     """
5653
5654     def try_parse(self, data):
5655         msg, _, rest = BGPMessage.parser(data)
5656         return msg, rest