backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / lib / packet / bgp.py
diff --git a/ryu/build/lib.linux-armv7l-2.7/ryu/lib/packet/bgp.py b/ryu/build/lib.linux-armv7l-2.7/ryu/lib/packet/bgp.py
new file mode 100644 (file)
index 0000000..9e50151
--- /dev/null
@@ -0,0 +1,5656 @@
+# Copyright (C) 2013,2014 Nippon Telegraph and Telephone Corporation.
+# Copyright (C) 2013,2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+RFC 4271 BGP-4
+"""
+
+# todo
+# - notify data
+# - RFC 4364 BGP/MPLS IP Virtual Private Networks (VPNs)
+
+import abc
+import base64
+import collections
+import copy
+import functools
+import io
+import itertools
+import math
+import re
+import socket
+import struct
+
+import netaddr
+import six
+
+from ryu.lib.stringify import StringifyMixin
+from ryu.lib.packet import afi as addr_family
+from ryu.lib.packet import safi as subaddr_family
+from ryu.lib.packet import packet_base
+from ryu.lib.packet import stream_parser
+from ryu.lib.packet import vxlan
+from ryu.lib.packet import mpls
+from ryu.lib import addrconv
+from ryu.lib import type_desc
+from ryu.lib.type_desc import TypeDisp
+from ryu.lib import ip
+from ryu.lib.pack_utils import msg_pack_into
+from ryu.utils import binary_str
+from ryu.utils import import_module
+
+reduce = six.moves.reduce
+
+TCP_SERVER_PORT = 179
+
+BGP_MSG_OPEN = 1
+BGP_MSG_UPDATE = 2
+BGP_MSG_NOTIFICATION = 3
+BGP_MSG_KEEPALIVE = 4
+BGP_MSG_ROUTE_REFRESH = 5  # RFC 2918
+
+_VERSION = 4
+_MARKER = 16 * b'\xff'
+
+BGP_OPT_CAPABILITY = 2  # RFC 5492
+
+BGP_CAP_MULTIPROTOCOL = 1  # RFC 4760
+BGP_CAP_ROUTE_REFRESH = 2  # RFC 2918
+BGP_CAP_CARRYING_LABEL_INFO = 4  # RFC 3107
+BGP_CAP_GRACEFUL_RESTART = 64  # RFC 4724
+BGP_CAP_FOUR_OCTET_AS_NUMBER = 65  # RFC 4893
+BGP_CAP_ENHANCED_ROUTE_REFRESH = 70  # https://tools.ietf.org/html/\
+# draft-ietf-idr-bgp-enhanced-route-refresh-05
+BGP_CAP_ROUTE_REFRESH_CISCO = 128  # in cisco routers, there are two\
+# route refresh code: one using the capability code of 128 (old),
+# another using the capability code of 2 (new).
+
+BGP_ATTR_FLAG_OPTIONAL = 1 << 7
+BGP_ATTR_FLAG_TRANSITIVE = 1 << 6
+BGP_ATTR_FLAG_PARTIAL = 1 << 5
+BGP_ATTR_FLAG_EXTENDED_LENGTH = 1 << 4
+
+BGP_ATTR_TYPE_ORIGIN = 1  # 0,1,2 (1 byte)
+BGP_ATTR_TYPE_AS_PATH = 2  # a list of AS_SET/AS_SEQUENCE  eg. {1 2 3} 4 5
+BGP_ATTR_TYPE_NEXT_HOP = 3  # an IPv4 address
+BGP_ATTR_TYPE_MULTI_EXIT_DISC = 4  # uint32 metric
+BGP_ATTR_TYPE_LOCAL_PREF = 5  # uint32
+BGP_ATTR_TYPE_ATOMIC_AGGREGATE = 6  # 0 bytes
+BGP_ATTR_TYPE_AGGREGATOR = 7  # AS number and IPv4 address
+BGP_ATTR_TYPE_COMMUNITIES = 8  # RFC 1997
+BGP_ATTR_TYPE_ORIGINATOR_ID = 9  # RFC 4456
+BGP_ATTR_TYPE_CLUSTER_LIST = 10  # RFC 4456
+BGP_ATTR_TYPE_MP_REACH_NLRI = 14  # RFC 4760
+BGP_ATTR_TYPE_MP_UNREACH_NLRI = 15  # RFC 4760
+BGP_ATTR_TYPE_EXTENDED_COMMUNITIES = 16  # RFC 4360
+BGP_ATTR_TYPE_AS4_PATH = 17  # RFC 4893
+BGP_ATTR_TYPE_AS4_AGGREGATOR = 18  # RFC 4893
+BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE = 22  # RFC 6514
+
+BGP_ATTR_ORIGIN_IGP = 0x00
+BGP_ATTR_ORIGIN_EGP = 0x01
+BGP_ATTR_ORIGIN_INCOMPLETE = 0x02
+
+AS_TRANS = 23456  # RFC 4893
+
+# Well known commmunities  (RFC 1997)
+BGP_COMMUNITY_NO_EXPORT = 0xffffff01
+BGP_COMMUNITY_NO_ADVERTISE = 0xffffff02
+BGP_COMMUNITY_NO_EXPORT_SUBCONFED = 0xffffff03
+
+# RFC 4360
+# The low-order octet of Type field (subtype)
+BGP_EXTENDED_COMMUNITY_ROUTE_TARGET = 0x02
+BGP_EXTENDED_COMMUNITY_ROUTE_ORIGIN = 0x03
+
+# NOTIFICATION Error Code and SubCode
+# Note: 0 is a valid SubCode.  (Unspecific)
+
+# NOTIFICATION Error Code  RFC 4271 4.5.
+BGP_ERROR_MESSAGE_HEADER_ERROR = 1
+BGP_ERROR_OPEN_MESSAGE_ERROR = 2
+BGP_ERROR_UPDATE_MESSAGE_ERROR = 3
+BGP_ERROR_HOLD_TIMER_EXPIRED = 4
+BGP_ERROR_FSM_ERROR = 5
+BGP_ERROR_CEASE = 6
+
+# NOTIFICATION Error Subcode for BGP_ERROR_MESSAGE_HEADER_ERROR
+BGP_ERROR_SUB_CONNECTION_NOT_SYNCHRONIZED = 1
+BGP_ERROR_SUB_BAD_MESSAGE_LENGTH = 2  # Data: the erroneous Length field
+BGP_ERROR_SUB_BAD_MESSAGE_TYPE = 3  # Data: the erroneous Type field
+
+# NOTIFICATION Error Subcode for BGP_ERROR_OPEN_MESSAGE_ERROR
+BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER = 1  # Data: 2 octet version number
+BGP_ERROR_SUB_BAD_PEER_AS = 2
+BGP_ERROR_SUB_BAD_BGP_IDENTIFIER = 3
+BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER = 4
+BGP_ERROR_SUB_AUTHENTICATION_FAILURE = 5  # deprecated RFC 1771
+BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME = 6
+
+# NOTIFICATION Error Subcode for BGP_ERROR_UPDATE_MESSAGE_ERROR
+BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST = 1
+BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE = 2  # Data: type of the attr
+BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE = 3  # Data: ditto
+BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR = 4  # Data: the attr (type, len, value)
+BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR = 5  # Data: ditto
+BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE = 6  # Data: ditto
+BGP_ERROR_SUB_ROUTING_LOOP = 7  # deprecated RFC 1771 AS Routing Loop
+BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE = 8  # Data: ditto
+BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR = 9  # Data: ditto
+BGP_ERROR_SUB_INVALID_NETWORK_FIELD = 10
+BGP_ERROR_SUB_MALFORMED_AS_PATH = 11
+
+# NOTIFICATION Error Subcode for BGP_ERROR_HOLD_TIMER_EXPIRED
+BGP_ERROR_SUB_HOLD_TIMER_EXPIRED = 1
+
+# NOTIFICATION Error Subcode for BGP_ERROR_FSM_ERROR
+BGP_ERROR_SUB_FSM_ERROR = 1
+
+# NOTIFICATION Error Subcode for BGP_ERROR_CEASE  (RFC 4486)
+BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED = 1  # Data: optional
+BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN = 2
+BGP_ERROR_SUB_PEER_DECONFIGURED = 3
+BGP_ERROR_SUB_ADMINISTRATIVE_RESET = 4
+BGP_ERROR_SUB_CONNECTION_RESET = 5
+BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE = 6
+BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION = 7
+BGP_ERROR_SUB_OUT_OF_RESOURCES = 8
+
+
+class _Value(object):
+    _VALUE_PACK_STR = None
+    _VALUE_FIELDS = ['value']
+
+    @staticmethod
+    def do_init(cls_type, self, kwargs, **extra_kwargs):
+        ourfields = {}
+        for f in cls_type._VALUE_FIELDS:
+            v = kwargs[f]
+            del kwargs[f]
+            ourfields[f] = v
+        kwargs.update(extra_kwargs)
+        super(cls_type, self).__init__(**kwargs)
+        self.__dict__.update(ourfields)
+
+    @classmethod
+    def parse_value(cls, buf):
+        values = struct.unpack_from(cls._VALUE_PACK_STR, six.binary_type(buf))
+        return dict(zip(cls._VALUE_FIELDS, values))
+
+    def serialize_value(self):
+        args = []
+        for f in self._VALUE_FIELDS:
+            args.append(getattr(self, f))
+        return struct.pack(self._VALUE_PACK_STR, *args)
+
+
+class BgpExc(Exception):
+    """Base bgp exception."""
+
+    CODE = 0
+    """BGP error code."""
+
+    SUB_CODE = 0
+    """BGP error sub-code."""
+
+    SEND_ERROR = True
+    """Flag if set indicates Notification message should be sent to peer."""
+
+    def __init__(self, data=''):
+        super(BgpExc, self).__init__()
+        self.data = data
+
+    def __str__(self):
+        return '<%s %r>' % (self.__class__.__name__, self.data)
+
+
+class BadNotification(BgpExc):
+    SEND_ERROR = False
+
+# ============================================================================
+# Message Header Errors
+# ============================================================================
+
+
+class NotSync(BgpExc):
+    CODE = BGP_ERROR_MESSAGE_HEADER_ERROR
+    SUB_CODE = BGP_ERROR_SUB_CONNECTION_NOT_SYNCHRONIZED
+
+
+class BadLen(BgpExc):
+    CODE = BGP_ERROR_MESSAGE_HEADER_ERROR
+    SUB_CODE = BGP_ERROR_SUB_BAD_MESSAGE_LENGTH
+
+    def __init__(self, msg_type_code, message_length):
+        super(BadLen, self).__init__()
+        self.msg_type_code = msg_type_code
+        self.length = message_length
+        self.data = struct.pack('!H', self.length)
+
+    def __str__(self):
+        return '<BadLen %d msgtype=%d>' % (self.length, self.msg_type_code)
+
+
+class BadMsg(BgpExc):
+    """Error to indicate un-recognized message type.
+
+    RFC says: If the Type field of the message header is not recognized, then
+    the Error Subcode MUST be set to Bad Message Type.  The Data field MUST
+    contain the erroneous Type field.
+    """
+    CODE = BGP_ERROR_MESSAGE_HEADER_ERROR
+    SUB_CODE = BGP_ERROR_SUB_BAD_MESSAGE_TYPE
+
+    def __init__(self, msg_type):
+        super(BadMsg, self).__init__()
+        self.msg_type = msg_type
+        self.data = struct.pack('B', msg_type)
+
+    def __str__(self):
+        return '<BadMsg %d>' % (self.msg_type,)
+
+# ============================================================================
+# OPEN Message Errors
+# ============================================================================
+
+
+class MalformedOptionalParam(BgpExc):
+    """If recognized optional parameters are malformed.
+
+    RFC says: If one of the Optional Parameters in the OPEN message is
+    recognized, but is malformed, then the Error Subcode MUST be set to 0
+    (Unspecific).
+    """
+    CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
+    SUB_CODE = 0
+
+
+class UnsupportedVersion(BgpExc):
+    """Error to indicate unsupport bgp version number.
+
+    RFC says: If the version number in the Version field of the received OPEN
+    message is not supported, then the Error Subcode MUST be set to Unsupported
+    Version Number.  The Data field is a 2-octet unsigned integer, which
+    indicates the largest, locally-supported version number less than the
+    version the remote BGP peer bid (as indicated in the received OPEN
+    message), or if the smallest, locally-supported version number is greater
+    than the version the remote BGP peer bid, then the smallest, locally-
+    supported version number.
+    """
+    CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER
+
+    def __init__(self, locally_support_version):
+        super(UnsupportedVersion, self).__init__()
+        self.data = struct.pack('H', locally_support_version)
+
+
+class BadPeerAs(BgpExc):
+    """Error to indicate open message has incorrect AS number.
+
+    RFC says: If the Autonomous System field of the OPEN message is
+    unacceptable, then the Error Subcode MUST be set to Bad Peer AS.  The
+    determination of acceptable Autonomous System numbers is configure peer AS.
+    """
+    CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_BAD_PEER_AS
+
+
+class BadBgpId(BgpExc):
+    """Error to indicate incorrect BGP Identifier.
+
+    RFC says: If the BGP Identifier field of the OPEN message is syntactically
+    incorrect, then the Error Subcode MUST be set to Bad BGP Identifier.
+    Syntactic correctness means that the BGP Identifier field represents a
+    valid unicast IP host address.
+    """
+    CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_BAD_BGP_IDENTIFIER
+
+
+class UnsupportedOptParam(BgpExc):
+    """Error to indicate unsupported optional parameters.
+
+    RFC says: If one of the Optional Parameters in the OPEN message is not
+    recognized, then the Error Subcode MUST be set to Unsupported Optional
+    Parameters.
+    """
+    CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER
+
+
+class AuthFailure(BgpExc):
+    CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_AUTHENTICATION_FAILURE
+
+
+class UnacceptableHoldTime(BgpExc):
+    """Error to indicate Unacceptable Hold Time in open message.
+
+    RFC says: If the Hold Time field of the OPEN message is unacceptable, then
+    the Error Subcode MUST be set to Unacceptable Hold Time.
+    """
+    CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME
+
+# ============================================================================
+# UPDATE message related errors
+# ============================================================================
+
+
+class MalformedAttrList(BgpExc):
+    """Error to indicate UPDATE message is malformed.
+
+    RFC says: Error checking of an UPDATE message begins by examining the path
+    attributes.  If the Withdrawn Routes Length or Total Attribute Length is
+    too large (i.e., if Withdrawn Routes Length + Total Attribute Length + 23
+    exceeds the message Length), then the Error Subcode MUST be set to
+    Malformed Attribute List.
+    """
+    CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST
+
+
+class UnRegWellKnowAttr(BgpExc):
+    CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE
+
+
+class MissingWellKnown(BgpExc):
+    """Error to indicate missing well-known attribute.
+
+    RFC says: If any of the well-known mandatory attributes are not present,
+    then the Error Subcode MUST be set to Missing Well-known Attribute.  The
+    Data field MUST contain the Attribute Type Code of the missing, well-known
+    attribute.
+    """
+    CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE
+
+    def __init__(self, pattr_type_code):
+        super(MissingWellKnown, self).__init__()
+        self.pattr_type_code = pattr_type_code
+        self.data = struct.pack('B', pattr_type_code)
+
+
+class AttrFlagError(BgpExc):
+    """Error to indicate recognized path attributes have incorrect flags.
+
+    RFC says: If any recognized attribute has Attribute Flags that conflict
+    with the Attribute Type Code, then the Error Subcode MUST be set to
+    Attribute Flags Error.  The Data field MUST contain the erroneous attribute
+    (type, length, and value).
+    """
+    CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR
+
+
+class AttrLenError(BgpExc):
+    CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR
+
+
+class InvalidOriginError(BgpExc):
+    """Error indicates undefined Origin attribute value.
+
+    RFC says: If the ORIGIN attribute has an undefined value, then the Error
+    Sub- code MUST be set to Invalid Origin Attribute.  The Data field MUST
+    contain the unrecognized attribute (type, length, and value).
+    """
+    CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE
+
+
+class RoutingLoop(BgpExc):
+    CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_ROUTING_LOOP
+
+
+class InvalidNextHop(BgpExc):
+    CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE
+
+
+class OptAttrError(BgpExc):
+    """Error indicates Optional Attribute is malformed.
+
+    RFC says: If an optional attribute is recognized, then the value of this
+    attribute MUST be checked.  If an error is detected, the attribute MUST be
+    discarded, and the Error Subcode MUST be set to Optional Attribute Error.
+    The Data field MUST contain the attribute (type, length, and value).
+    """
+    CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR
+
+
+class InvalidNetworkField(BgpExc):
+    CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_INVALID_NETWORK_FIELD
+
+
+class MalformedAsPath(BgpExc):
+    """Error to indicate if AP_PATH attribute is syntactically incorrect.
+
+    RFC says: The AS_PATH attribute is checked for syntactic correctness.  If
+    the path is syntactically incorrect, then the Error Subcode MUST be set to
+    Malformed AS_PATH.
+    """
+    CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
+    SUB_CODE = BGP_ERROR_SUB_MALFORMED_AS_PATH
+
+
+# ============================================================================
+# Hold Timer Expired
+# ============================================================================
+
+
+class HoldTimerExpired(BgpExc):
+    """Error to indicate Hold Timer expired.
+
+    RFC says: If a system does not receive successive KEEPALIVE, UPDATE, and/or
+    NOTIFICATION messages within the period specified in the Hold Time field of
+    the OPEN message, then the NOTIFICATION message with the Hold Timer Expired
+    Error Code is sent and the BGP connection is closed.
+    """
+    CODE = BGP_ERROR_HOLD_TIMER_EXPIRED
+    SUB_CODE = BGP_ERROR_SUB_HOLD_TIMER_EXPIRED
+
+# ============================================================================
+# Finite State Machine Error
+# ============================================================================
+
+
+class FiniteStateMachineError(BgpExc):
+    """Error to indicate any Finite State Machine Error.
+
+    RFC says: Any error detected by the BGP Finite State Machine (e.g., receipt
+    of an unexpected event) is indicated by sending the NOTIFICATION message
+    with the Error Code Finite State Machine Error.
+    """
+    CODE = BGP_ERROR_FSM_ERROR
+    SUB_CODE = BGP_ERROR_SUB_FSM_ERROR
+
+
+# ============================================================================
+# Cease Errors
+# ============================================================================
+
+class MaxPrefixReached(BgpExc):
+    CODE = BGP_ERROR_CEASE
+    SUB_CODE = BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED
+
+
+class AdminShutdown(BgpExc):
+    """Error to indicate Administrative shutdown.
+
+    RFC says: If a BGP speaker decides to administratively shut down its
+    peering with a neighbor, then the speaker SHOULD send a NOTIFICATION
+    message  with the Error Code Cease and the Error Subcode 'Administrative
+    Shutdown'.
+    """
+    CODE = BGP_ERROR_CEASE
+    SUB_CODE = BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN
+
+
+class PeerDeConfig(BgpExc):
+    CODE = BGP_ERROR_CEASE
+    SUB_CODE = BGP_ERROR_SUB_PEER_DECONFIGURED
+
+
+class AdminReset(BgpExc):
+    CODE = BGP_ERROR_CEASE
+    SUB_CODE = BGP_ERROR_SUB_ADMINISTRATIVE_RESET
+
+
+class ConnRejected(BgpExc):
+    """Error to indicate Connection Rejected.
+
+    RFC says: If a BGP speaker decides to disallow a BGP connection (e.g., the
+    peer is not configured locally) after the speaker accepts a transport
+    protocol connection, then the BGP speaker SHOULD send a NOTIFICATION
+    message with the Error Code Cease and the Error Subcode "Connection
+    Rejected".
+    """
+    CODE = BGP_ERROR_CEASE
+    SUB_CODE = BGP_ERROR_SUB_CONNECTION_RESET
+
+
+class OtherConfChange(BgpExc):
+    CODE = BGP_ERROR_CEASE
+    SUB_CODE = BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE
+
+
+class CollisionResolution(BgpExc):
+    """Error to indicate Connection Collision Resolution.
+
+    RFC says: If a BGP speaker decides to send a NOTIFICATION message with the
+    Error Code Cease as a result of the collision resolution procedure (as
+    described in [BGP-4]), then the subcode SHOULD be set to "Connection
+    Collision Resolution".
+    """
+    CODE = BGP_ERROR_CEASE
+    SUB_CODE = BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION
+
+
+class OutOfResource(BgpExc):
+    CODE = BGP_ERROR_CEASE
+    SUB_CODE = BGP_ERROR_SUB_OUT_OF_RESOURCES
+
+
+@functools.total_ordering
+class RouteFamily(StringifyMixin):
+    def __init__(self, afi, safi):
+        self.afi = afi
+        self.safi = safi
+
+    def __lt__(self, other):
+        return (self.afi, self.safi) < (other.afi, other.safi)
+
+    def __eq__(self, other):
+        return (self.afi, self.safi) == (other.afi, other.safi)
+
+    def __hash__(self):
+        return hash((self.afi, self.safi))
+
+
+# Route Family Singleton
+RF_IPv4_UC = RouteFamily(addr_family.IP, subaddr_family.UNICAST)
+RF_IPv6_UC = RouteFamily(addr_family.IP6, subaddr_family.UNICAST)
+RF_IPv4_VPN = RouteFamily(addr_family.IP, subaddr_family.MPLS_VPN)
+RF_IPv6_VPN = RouteFamily(addr_family.IP6, subaddr_family.MPLS_VPN)
+RF_IPv4_MPLS = RouteFamily(addr_family.IP, subaddr_family.MPLS_LABEL)
+RF_IPv6_MPLS = RouteFamily(addr_family.IP6, subaddr_family.MPLS_LABEL)
+RF_L2_EVPN = RouteFamily(addr_family.L2VPN, subaddr_family.EVPN)
+RF_IPv4_FLOWSPEC = RouteFamily(addr_family.IP, subaddr_family.IP_FLOWSPEC)
+RF_IPv6_FLOWSPEC = RouteFamily(addr_family.IP6, subaddr_family.IP_FLOWSPEC)
+RF_VPNv4_FLOWSPEC = RouteFamily(addr_family.IP, subaddr_family.VPN_FLOWSPEC)
+RF_VPNv6_FLOWSPEC = RouteFamily(addr_family.IP6, subaddr_family.VPN_FLOWSPEC)
+RF_L2VPN_FLOWSPEC = RouteFamily(
+    addr_family.L2VPN, subaddr_family.VPN_FLOWSPEC)
+RF_RTC_UC = RouteFamily(addr_family.IP,
+                        subaddr_family.ROUTE_TARGET_CONSTRAINTS)
+
+_rf_map = {
+    (addr_family.IP, subaddr_family.UNICAST): RF_IPv4_UC,
+    (addr_family.IP6, subaddr_family.UNICAST): RF_IPv6_UC,
+    (addr_family.IP, subaddr_family.MPLS_VPN): RF_IPv4_VPN,
+    (addr_family.IP6, subaddr_family.MPLS_VPN): RF_IPv6_VPN,
+    (addr_family.IP, subaddr_family.MPLS_LABEL): RF_IPv4_MPLS,
+    (addr_family.IP6, subaddr_family.MPLS_LABEL): RF_IPv6_MPLS,
+    (addr_family.L2VPN, subaddr_family.EVPN): RF_L2_EVPN,
+    (addr_family.IP, subaddr_family.IP_FLOWSPEC): RF_IPv4_FLOWSPEC,
+    (addr_family.IP6, subaddr_family.IP_FLOWSPEC): RF_IPv6_FLOWSPEC,
+    (addr_family.IP, subaddr_family.VPN_FLOWSPEC): RF_VPNv4_FLOWSPEC,
+    (addr_family.IP6, subaddr_family.VPN_FLOWSPEC): RF_VPNv6_FLOWSPEC,
+    (addr_family.L2VPN, subaddr_family.VPN_FLOWSPEC): RF_L2VPN_FLOWSPEC,
+    (addr_family.IP, subaddr_family.ROUTE_TARGET_CONSTRAINTS): RF_RTC_UC
+}
+
+
+def get_rf(afi, safi):
+    return _rf_map[(afi, safi)]
+
+
+def pad(binary, len_):
+    assert len(binary) <= len_
+    return binary + b'\0' * (len_ - len(binary))
+
+
+class _RouteDistinguisher(StringifyMixin, TypeDisp, _Value):
+    _PACK_STR = '!H'
+    TWO_OCTET_AS = 0
+    IPV4_ADDRESS = 1
+    FOUR_OCTET_AS = 2
+
+    def __init__(self, admin=0, assigned=0, type_=None):
+        if type_ is None:
+            type_ = self._rev_lookup_type(self.__class__)
+        self.type = type_
+        self.admin = admin
+        self.assigned = assigned
+
+    @classmethod
+    def parser(cls, buf):
+        assert len(buf) == 8
+        (type_,) = struct.unpack_from(cls._PACK_STR, six.binary_type(buf))
+        rest = buf[struct.calcsize(cls._PACK_STR):]
+        subcls = cls._lookup_type(type_)
+        return subcls(**subcls.parse_value(rest))
+
+    @classmethod
+    def from_str(cls, str_):
+        assert isinstance(str_, str)
+
+        first, second = str_.split(':')
+        if '.' in first:
+            type_ = cls.IPV4_ADDRESS
+        elif int(first) > (1 << 16):
+            type_ = cls.FOUR_OCTET_AS
+            first = int(first)
+        else:
+            type_ = cls.TWO_OCTET_AS
+            first = int(first)
+        subcls = cls._lookup_type(type_)
+        return subcls(admin=first, assigned=int(second))
+
+    def serialize(self):
+        value = self.serialize_value()
+        buf = bytearray()
+        msg_pack_into(self._PACK_STR, buf, 0, self.type)
+        return six.binary_type(buf + value)
+
+    @property
+    def formatted_str(self):
+        return "%s:%s" % (self.admin, self.assigned)
+
+
+@_RouteDistinguisher.register_type(_RouteDistinguisher.TWO_OCTET_AS)
+class BGPTwoOctetAsRD(_RouteDistinguisher):
+    _VALUE_PACK_STR = '!HI'
+    _VALUE_FIELDS = ['admin', 'assigned']
+
+    def __init__(self, **kwargs):
+        super(BGPTwoOctetAsRD, self).__init__()
+        self.do_init(BGPTwoOctetAsRD, self, kwargs)
+
+
+@_RouteDistinguisher.register_type(_RouteDistinguisher.IPV4_ADDRESS)
+class BGPIPv4AddressRD(_RouteDistinguisher):
+    _VALUE_PACK_STR = '!4sH'
+    _VALUE_FIELDS = ['admin', 'assigned']
+    _TYPE = {
+        'ascii': [
+            'admin'
+        ]
+    }
+
+    def __init__(self, **kwargs):
+        super(BGPIPv4AddressRD, self).__init__()
+        self.do_init(BGPIPv4AddressRD, self, kwargs)
+
+    @classmethod
+    def parse_value(cls, buf):
+        d_ = super(BGPIPv4AddressRD, cls).parse_value(buf)
+        d_['admin'] = addrconv.ipv4.bin_to_text(d_['admin'])
+        return d_
+
+    def serialize_value(self):
+        args = []
+        for f in self._VALUE_FIELDS:
+            v = getattr(self, f)
+            if f == 'admin':
+                v = bytes(addrconv.ipv4.text_to_bin(v))
+            args.append(v)
+        buf = bytearray()
+        msg_pack_into(self._VALUE_PACK_STR, buf, 0, *args)
+        return buf
+
+
+@_RouteDistinguisher.register_type(_RouteDistinguisher.FOUR_OCTET_AS)
+class BGPFourOctetAsRD(_RouteDistinguisher):
+    _VALUE_PACK_STR = '!IH'
+    _VALUE_FIELDS = ['admin', 'assigned']
+
+    def __init__(self, **kwargs):
+        super(BGPFourOctetAsRD, self).__init__()
+        self.do_init(BGPFourOctetAsRD, self, kwargs)
+
+
+@six.add_metaclass(abc.ABCMeta)
+class _AddrPrefix(StringifyMixin):
+    _PACK_STR = '!B'  # length
+
+    def __init__(self, length, addr, prefixes=None, **kwargs):
+        # length is on-wire bit length of prefixes+addr.
+        assert prefixes != ()
+        if isinstance(addr, tuple):
+            # for _AddrPrefix.parser
+            # also for _VPNAddrPrefix.__init__ etc
+            (addr,) = addr
+        self.length = length
+        if prefixes:
+            addr = prefixes + (addr,)
+        self.addr = addr
+
+    @classmethod
+    @abc.abstractmethod
+    def _to_bin(cls, addr):
+        pass
+
+    @classmethod
+    @abc.abstractmethod
+    def _from_bin(cls, addr):
+        pass
+
+    @classmethod
+    def parser(cls, buf):
+        (length, ) = struct.unpack_from(cls._PACK_STR, six.binary_type(buf))
+        rest = buf[struct.calcsize(cls._PACK_STR):]
+        byte_length = (length + 7) // 8
+        addr = cls._from_bin(rest[:byte_length])
+        rest = rest[byte_length:]
+        return cls(length=length, addr=addr), rest
+
+    def serialize(self):
+        # fixup
+        byte_length = (self.length + 7) // 8
+        bin_addr = self._to_bin(self.addr)
+        if (self.length % 8) == 0:
+            bin_addr = bin_addr[:byte_length]
+        else:
+            # clear trailing bits in the last octet.
+            # rfc doesn't require this.
+            mask = 0xff00 >> (self.length % 8)
+            last_byte = six.int2byte(
+                six.indexbytes(bin_addr, byte_length - 1) & mask)
+            bin_addr = bin_addr[:byte_length - 1] + last_byte
+        self.addr = self._from_bin(bin_addr)
+
+        buf = bytearray()
+        msg_pack_into(self._PACK_STR, buf, 0, self.length)
+        return buf + bytes(bin_addr)
+
+
+class _BinAddrPrefix(_AddrPrefix):
+    @classmethod
+    def _to_bin(cls, addr):
+        return addr
+
+    @classmethod
+    def _from_bin(cls, addr):
+        return addr
+
+
+class _LabelledAddrPrefix(_AddrPrefix):
+    _LABEL_PACK_STR = '!3B'
+    # RFC3107
+    # 3. Carrying Label Mapping Information
+    # The label information carried (as part of NLRI) in the Withdrawn
+    # Routes field should be set to 0x800000.  (Of course, terminating the
+    # BGP session also withdraws all the previously advertised routes.)
+    #
+    _WITHDRAW_LABEL = 0x800000
+
+    def __init__(self, length, addr, labels=None, **kwargs):
+        labels = labels if labels else []
+        assert isinstance(labels, list)
+        is_tuple = isinstance(addr, tuple)
+        if is_tuple:
+            # for _AddrPrefix.parser
+            assert not labels
+            labels = addr[0]
+            addr = addr[1:]
+        else:
+            length += struct.calcsize(self._LABEL_PACK_STR) * 8 * len(labels)
+        assert length > struct.calcsize(self._LABEL_PACK_STR) * 8 * len(labels)
+        prefixes = (labels,)
+        super(_LabelledAddrPrefix, self).__init__(prefixes=prefixes,
+                                                  length=length,
+                                                  addr=addr,
+                                                  **kwargs)
+
+    @classmethod
+    def _label_to_bin(cls, label):
+        buf = bytearray()
+        msg_pack_into(cls._LABEL_PACK_STR, buf, 0,
+                      (label & 0xff0000) >> 16,
+                      (label & 0x00ff00) >> 8,
+                      (label & 0x0000ff) >> 0)
+        return six.binary_type(buf)
+
+    @classmethod
+    def _label_from_bin(cls, label):
+        (b1, b2, b3) = struct.unpack_from(cls._LABEL_PACK_STR,
+                                          six.binary_type(label))
+        rest = label[struct.calcsize(cls._LABEL_PACK_STR):]
+        return (b1 << 16) | (b2 << 8) | b3, rest
+
+    @classmethod
+    def _to_bin(cls, addr):
+        labels = addr[0]
+        rest = addr[1:]
+        labels = [x << 4 for x in labels]
+        if labels and labels[-1] != cls._WITHDRAW_LABEL:
+            labels[-1] |= 1  # bottom of stack
+        bin_labels = list(cls._label_to_bin(l) for l in labels)
+        return bytes(reduce(lambda x, y: x + y, bin_labels,
+                            bytearray()) + cls._prefix_to_bin(rest))
+
+    @classmethod
+    def _has_no_label(cls, bin_):
+        try:
+            length = len(bin_)
+            labels = []
+            while True:
+                (label, bin_) = cls._label_from_bin(bin_)
+                labels.append(label)
+                if label & 1 or label == cls._WITHDRAW_LABEL:
+                    break
+            assert length > struct.calcsize(cls._LABEL_PACK_STR) * len(labels)
+        except struct.error:
+            return True
+        except AssertionError:
+            return True
+        return False
+
+    @classmethod
+    def _from_bin(cls, addr):
+        rest = addr
+        labels = []
+
+        if cls._has_no_label(rest):
+            return ([],) + cls._prefix_from_bin(rest)
+
+        while True:
+            (label, rest) = cls._label_from_bin(rest)
+            labels.append(label >> 4)
+            if label & 1 or label == cls._WITHDRAW_LABEL:
+                break
+        return (labels,) + cls._prefix_from_bin(rest)
+
+
+class _UnlabelledAddrPrefix(_AddrPrefix):
+    @classmethod
+    def _to_bin(cls, addr):
+        return cls._prefix_to_bin((addr,))
+
+    @classmethod
+    def _from_bin(cls, binaddr):
+        (addr,) = cls._prefix_from_bin(binaddr)
+        return addr
+
+
+class _IPAddrPrefix(_AddrPrefix):
+    @staticmethod
+    def _prefix_to_bin(addr):
+        (addr,) = addr
+        return addrconv.ipv4.text_to_bin(addr)
+
+    @staticmethod
+    def _prefix_from_bin(addr):
+        return addrconv.ipv4.bin_to_text(pad(addr, 4)),
+
+
+class _IP6AddrPrefix(_AddrPrefix):
+    @staticmethod
+    def _prefix_to_bin(addr):
+        (addr,) = addr
+        return addrconv.ipv6.text_to_bin(addr)
+
+    @staticmethod
+    def _prefix_from_bin(addr):
+        return addrconv.ipv6.bin_to_text(pad(addr, 16)),
+
+
+class _VPNAddrPrefix(_AddrPrefix):
+    _RD_PACK_STR = '!Q'
+
+    def __init__(self, length, addr, prefixes=(), route_dist=0):
+        if isinstance(addr, tuple):
+            # for _AddrPrefix.parser
+            assert not route_dist
+            assert length > struct.calcsize(self._RD_PACK_STR) * 8
+            route_dist = addr[0]
+            addr = addr[1:]
+        else:
+            length += struct.calcsize(self._RD_PACK_STR) * 8
+
+        if isinstance(route_dist, str):
+            route_dist = _RouteDistinguisher.from_str(route_dist)
+
+        prefixes = prefixes + (route_dist,)
+        super(_VPNAddrPrefix, self).__init__(prefixes=prefixes,
+                                             length=length,
+                                             addr=addr)
+
+    @classmethod
+    def _prefix_to_bin(cls, addr):
+        rd = addr[0]
+        rest = addr[1:]
+        binrd = rd.serialize()
+        return binrd + super(_VPNAddrPrefix, cls)._prefix_to_bin(rest)
+
+    @classmethod
+    def _prefix_from_bin(cls, binaddr):
+        binrd = binaddr[:8]
+        binrest = binaddr[8:]
+        rd = _RouteDistinguisher.parser(binrd)
+        return (rd,) + super(_VPNAddrPrefix, cls)._prefix_from_bin(binrest)
+
+
+class IPAddrPrefix(_UnlabelledAddrPrefix, _IPAddrPrefix):
+    ROUTE_FAMILY = RF_IPv4_UC
+    _TYPE = {
+        'ascii': [
+            'addr'
+        ]
+    }
+
+    @property
+    def prefix(self):
+        return self.addr + '/{0}'.format(self.length)
+
+    @property
+    def formatted_nlri_str(self):
+        return self.prefix
+
+
+class IP6AddrPrefix(_UnlabelledAddrPrefix, _IP6AddrPrefix):
+    ROUTE_FAMILY = RF_IPv6_UC
+    _TYPE = {
+        'ascii': [
+            'addr'
+        ]
+    }
+
+    @property
+    def prefix(self):
+        return self.addr + '/{0}'.format(self.length)
+
+    @property
+    def formatted_nlri_str(self):
+        return self.prefix
+
+
+class LabelledIPAddrPrefix(_LabelledAddrPrefix, _IPAddrPrefix):
+    ROUTE_FAMILY = RF_IPv4_MPLS
+
+
+class LabelledIP6AddrPrefix(_LabelledAddrPrefix, _IP6AddrPrefix):
+    ROUTE_FAMILY = RF_IPv6_MPLS
+
+
+class LabelledVPNIPAddrPrefix(_LabelledAddrPrefix, _VPNAddrPrefix,
+                              _IPAddrPrefix):
+    ROUTE_FAMILY = RF_IPv4_VPN
+
+    @property
+    def prefix(self):
+        masklen = self.length - struct.calcsize(self._RD_PACK_STR) * 8 \
+            - struct.calcsize(self._LABEL_PACK_STR) * 8 * len(self.addr[:-2])
+        return self.addr[-1] + '/{0}'.format(masklen)
+
+    @property
+    def route_dist(self):
+        return self.addr[-2].formatted_str
+
+    @property
+    def label_list(self):
+        return self.addr[0]
+
+    @property
+    def formatted_nlri_str(self):
+        return "%s:%s" % (self.route_dist, self.prefix)
+
+
+class LabelledVPNIP6AddrPrefix(_LabelledAddrPrefix, _VPNAddrPrefix,
+                               _IP6AddrPrefix):
+    ROUTE_FAMILY = RF_IPv6_VPN
+
+    @property
+    def prefix(self):
+        masklen = self.length - struct.calcsize(self._RD_PACK_STR) * 8 \
+            - struct.calcsize(self._LABEL_PACK_STR) * 8 * len(self.addr[:-2])
+        return self.addr[-1] + '/{0}'.format(masklen)
+
+    @property
+    def route_dist(self):
+        return self.addr[-2].formatted_str
+
+    @property
+    def label_list(self):
+        return self.addr[0]
+
+    @property
+    def formatted_nlri_str(self):
+        return "%s:%s" % (self.route_dist, self.prefix)
+
+
+class EvpnEsi(StringifyMixin, TypeDisp, _Value):
+    """
+    Ethernet Segment Identifier
+
+    The supported ESI Types:
+
+     - ``EvpnEsi.ARBITRARY`` indicates EvpnArbitraryEsi.
+
+     - ``EvpnEsi.LACP`` indicates EvpnLACPEsi.
+
+     - ``EvpnEsi.L2_BRIDGE`` indicates EvpnL2BridgeEsi.
+
+     - ``EvpnEsi.MAC_BASED`` indicates EvpnMacBasedEsi.
+
+     - ``EvpnEsi.ROUTER_ID`` indicates EvpnRouterIDEsi.
+
+     - ``EvpnEsi.AS_BASED`` indicates EvpnASBasedEsi.
+    """
+    _PACK_STR = "!B"  # ESI Type
+    _ESI_LEN = 10
+
+    ARBITRARY = 0x00
+    LACP = 0x01
+    L2_BRIDGE = 0x02
+    MAC_BASED = 0x03
+    ROUTER_ID = 0x04
+    AS_BASED = 0x05
+    MAX = 0xff  # Reserved
+
+    _TYPE_NAME = None  # must be defined in subclass
+
+    def __init__(self, type_=None):
+        if type_ is None:
+            type_ = self._rev_lookup_type(self.__class__)
+        self.type = type_
+
+    @classmethod
+    def parser(cls, buf):
+        (esi_type,) = struct.unpack_from(
+            cls._PACK_STR, six.binary_type(buf))
+        subcls = cls._lookup_type(esi_type)
+        return subcls(**subcls.parse_value(buf[1:cls._ESI_LEN]))
+
+    def serialize(self):
+        buf = bytearray()
+        msg_pack_into(EvpnEsi._PACK_STR, buf, 0, self.type)
+        return six.binary_type(buf + self.serialize_value())
+
+    @property
+    def formatted_str(self):
+        return '%s(%s)' % (
+            self._TYPE_NAME,
+            ','.join(str(getattr(self, v)) for v in self._VALUE_FIELDS))
+
+
+@EvpnEsi.register_unknown_type()
+class EvpnUnknownEsi(EvpnEsi):
+    """
+    ESI value for unknown type
+    """
+    _TYPE_NAME = 'unknown'
+    _VALUE_PACK_STR = '!9s'
+    _VALUE_FIELDS = ['value']
+
+    def __init__(self, value, type_=None):
+        super(EvpnUnknownEsi, self).__init__(type_)
+        self.value = value
+
+    @property
+    def formatted_str(self):
+        return '%s(%s)' % (self._TYPE_NAME, binary_str(self.value))
+
+
+@EvpnEsi.register_type(EvpnEsi.ARBITRARY)
+class EvpnArbitraryEsi(EvpnEsi):
+    """
+    Arbitrary 9-octet ESI value
+
+    This type indicates an arbitrary 9-octet ESI value,
+    which is managed and configured by the operator.
+    """
+    _TYPE_NAME = 'arbitrary'
+    _VALUE_PACK_STR = '!9s'
+    _VALUE_FIELDS = ['value']
+
+    def __init__(self, value, type_=None):
+        super(EvpnArbitraryEsi, self).__init__(type_)
+        self.value = value
+
+    @property
+    def formatted_str(self):
+        return '%s(%s)' % (self._TYPE_NAME, binary_str(self.value))
+
+
+@EvpnEsi.register_type(EvpnEsi.LACP)
+class EvpnLACPEsi(EvpnEsi):
+    """
+    ESI value for LACP
+
+    When IEEE 802.1AX LACP is used between the PEs and CEs,
+    this ESI type indicates an auto-generated ESI value
+    determined from LACP.
+    """
+    _TYPE_NAME = 'lacp'
+    _VALUE_PACK_STR = '!6sHx'
+    _VALUE_FIELDS = ['mac_addr', 'port_key']
+    _TYPE = {
+        'ascii': [
+            'mac_addr'
+        ]
+    }
+
+    def __init__(self, mac_addr, port_key, type_=None):
+        super(EvpnLACPEsi, self).__init__(type_)
+        self.mac_addr = mac_addr
+        self.port_key = port_key
+
+    @classmethod
+    def parse_value(cls, buf):
+        (mac_addr, port_key) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
+        return {
+            'mac_addr': addrconv.mac.bin_to_text(mac_addr),
+            'port_key': port_key,
+        }
+
+    def serialize_value(self):
+        return struct.pack(
+            self._VALUE_PACK_STR,
+            addrconv.mac.text_to_bin(self.mac_addr), self.port_key)
+
+
+@EvpnEsi.register_type(EvpnEsi.L2_BRIDGE)
+class EvpnL2BridgeEsi(EvpnEsi):
+    """
+    ESI value for Layer 2 Bridge
+
+    This type is used in the case of indirectly connected hosts
+    via a bridged LAN between the CEs and the PEs.
+    The ESI Value is auto-generated and determined based
+    on the Layer 2 bridge protocol.
+    """
+    _TYPE_NAME = 'l2_bridge'
+    _VALUE_PACK_STR = '!6sHx'
+    _VALUE_FIELDS = ['mac_addr', 'priority']
+    _TYPE = {
+        'ascii': [
+            'mac_addr'
+        ]
+    }
+
+    def __init__(self, mac_addr, priority, type_=None):
+        super(EvpnL2BridgeEsi, self).__init__(type_)
+        self.mac_addr = mac_addr
+        self.priority = priority
+
+    @classmethod
+    def parse_value(cls, buf):
+        (mac_addr, priority) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
+        return {
+            'mac_addr': addrconv.mac.bin_to_text(mac_addr),
+            'priority': priority,
+        }
+
+    def serialize_value(self):
+        return struct.pack(
+            self._VALUE_PACK_STR,
+            addrconv.mac.text_to_bin(self.mac_addr), self.priority)
+
+
+@EvpnEsi.register_type(EvpnEsi.MAC_BASED)
+class EvpnMacBasedEsi(EvpnEsi):
+    """
+    MAC-based ESI Value
+
+    This type indicates a MAC-based ESI Value that
+    can be auto-generated or configured by the operator.
+    """
+    _TYPE_NAME = 'mac_based'
+    _VALUE_PACK_STR = '!6s3s'
+    _VALUE_FIELDS = ['mac_addr', 'local_disc']
+    _TYPE = {
+        'ascii': [
+            'mac_addr'
+        ]
+    }
+
+    def __init__(self, mac_addr, local_disc, type_=None):
+        super(EvpnMacBasedEsi, self).__init__(type_)
+        self.mac_addr = mac_addr
+        self.local_disc = local_disc
+
+    @classmethod
+    def parse_value(cls, buf):
+        (mac_addr, local_disc) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
+        return {
+            'mac_addr': addrconv.mac.bin_to_text(mac_addr),
+            'local_disc': type_desc.Int3.to_user(local_disc),
+        }
+
+    def serialize_value(self):
+        return struct.pack(
+            self._VALUE_PACK_STR,
+            addrconv.mac.text_to_bin(self.mac_addr),
+            type_desc.Int3.from_user(self.local_disc))
+
+
+@EvpnEsi.register_type(EvpnEsi.ROUTER_ID)
+class EvpnRouterIDEsi(EvpnEsi):
+    """
+    Router-ID ESI Value
+
+    This type indicates a router-ID ESI Value that
+    can be auto-generated or configured by the operator.
+    """
+    _TYPE_NAME = 'router_id'
+    _VALUE_PACK_STR = '!4sIx'
+    _VALUE_FIELDS = ['router_id', 'local_disc']
+    _TYPE = {
+        'ascii': [
+            'router_id'
+        ]
+    }
+
+    def __init__(self, router_id, local_disc, type_=None):
+        super(EvpnRouterIDEsi, self).__init__(type_)
+        self.router_id = router_id
+        self.local_disc = local_disc
+
+    @classmethod
+    def parse_value(cls, buf):
+        (router_id, local_disc) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
+        return {
+            'router_id': addrconv.ipv4.bin_to_text(router_id),
+            'local_disc': local_disc,
+        }
+
+    def serialize_value(self):
+        return struct.pack(
+            self._VALUE_PACK_STR,
+            addrconv.ipv4.text_to_bin(self.router_id), self.local_disc)
+
+
+@EvpnEsi.register_type(EvpnEsi.AS_BASED)
+class EvpnASBasedEsi(EvpnEsi):
+    """
+    AS based ESI value
+
+    This type indicates an Autonomous System(AS)-based
+    ESI Value that can be auto-generated or configured by
+    the operator.
+    """
+    _TYPE_NAME = 'as_based'
+    _VALUE_PACK_STR = '!IIx'
+    _VALUE_FIELDS = ['as_number', 'local_disc']
+
+    def __init__(self, as_number, local_disc, type_=None):
+        super(EvpnASBasedEsi, self).__init__(type_)
+        self.as_number = as_number
+        self.local_disc = local_disc
+
+
+class EvpnNLRI(StringifyMixin, TypeDisp):
+    """
+    BGP Network Layer Reachability Information (NLRI) for EVPN
+    """
+    ROUTE_FAMILY = RF_L2_EVPN
+
+    # EVPN NLRI:
+    # +-----------------------------------+
+    # |    Route Type (1 octet)           |
+    # +-----------------------------------+
+    # |     Length (1 octet)              |
+    # +-----------------------------------+
+    # | Route Type specific (variable)    |
+    # +-----------------------------------+
+    _PACK_STR = "!BB"
+    _PACK_STR_SIZE = struct.calcsize(_PACK_STR)
+
+    ETHERNET_AUTO_DISCOVERY = 0x01
+    MAC_IP_ADVERTISEMENT = 0x02
+    INCLUSIVE_MULTICAST_ETHERNET_TAG = 0x03
+    ETHERNET_SEGMENT = 0x04
+    IP_PREFIX_ROUTE = 0x05
+
+    ROUTE_TYPE_NAME = None  # must be defined in subclass
+
+    # Reserved value for Ethernet Tag ID.
+    MAX_ET = 0xFFFFFFFF
+
+    # Dictionary of ROUTE_TYPE_NAME to subclass.
+    # e.g.)
+    #   _NAMES = {'eth_ad': EvpnEthernetAutoDiscoveryNLRI, ...}
+    _NAMES = {}
+
+    # List of the fields considered to be part of the prefix in the NLRI.
+    # This list should be defined in subclasses to format NLRI string
+    # representation.
+    NLRI_PREFIX_FIELDS = []
+
+    def __init__(self, type_=None, length=None):
+        if type_ is None:
+            type_ = self._rev_lookup_type(self.__class__)
+        self.type = type_
+        self.length = length
+        self.route_dist = None  # should be initialized in subclass
+
+    @classmethod
+    def register_type(cls, type_):
+        cls._TYPES = cls._TYPES.copy()
+        cls._NAMES = cls._NAMES.copy()
+
+        def _register_type(subcls):
+            cls._TYPES[type_] = subcls
+            cls._NAMES[subcls.ROUTE_TYPE_NAME] = subcls
+            cls._REV_TYPES = None
+            return subcls
+
+        return _register_type
+
+    @classmethod
+    def _lookup_type_name(cls, type_name):
+        try:
+            return cls._NAMES[type_name]
+        except KeyError:
+            return EvpnUnknownNLRI
+
+    @classmethod
+    def parser(cls, buf):
+        (route_type, length) = struct.unpack_from(
+            cls._PACK_STR, six.binary_type(buf))
+        offset = cls._PACK_STR_SIZE + length
+        subcls = cls._lookup_type(route_type)
+        values = subcls.parse_value(buf[cls._PACK_STR_SIZE:offset])
+        return subcls(type_=route_type, length=length,
+                      **values), buf[offset:]
+
+    def serialize_value(self):
+        # Overrided in subclass
+        return b''
+
+    def serialize(self):
+        value_bin = self.serialize_value()
+        # fixup
+        self.length = len(value_bin)
+        return struct.pack(EvpnNLRI._PACK_STR,
+                           self.type, self.length) + value_bin
+
+    @staticmethod
+    def _rd_from_bin(buf):
+        return _RouteDistinguisher.parser(buf[:8]), buf[8:]
+
+    @staticmethod
+    def _rd_to_bin(rd):
+        return six.binary_type(rd.serialize())
+
+    @staticmethod
+    def _esi_from_bin(buf):
+        return EvpnEsi.parser(buf[:10]), buf[10:]
+
+    @staticmethod
+    def _esi_to_bin(esi):
+        return esi.serialize()
+
+    @staticmethod
+    def _ethernet_tag_id_from_bin(buf):
+        return type_desc.Int4.to_user(six.binary_type(buf[:4])), buf[4:]
+
+    @staticmethod
+    def _ethernet_tag_id_to_bin(tag_id):
+        return type_desc.Int4.from_user(tag_id)
+
+    @staticmethod
+    def _mac_addr_len_from_bin(buf):
+        return type_desc.Int1.to_user(six.binary_type(buf[:1])), buf[1:]
+
+    @staticmethod
+    def _mac_addr_len_to_bin(mac_len):
+        return type_desc.Int1.from_user(mac_len)
+
+    @staticmethod
+    def _mac_addr_from_bin(buf, mac_len):
+        mac_len //= 8
+        return addrconv.mac.bin_to_text(buf[:mac_len]), buf[mac_len:]
+
+    @staticmethod
+    def _mac_addr_to_bin(mac_addr):
+        return addrconv.mac.text_to_bin(mac_addr)
+
+    @staticmethod
+    def _ip_addr_len_from_bin(buf):
+        return type_desc.Int1.to_user(six.binary_type(buf[:1])), buf[1:]
+
+    @staticmethod
+    def _ip_addr_len_to_bin(ip_len):
+        return type_desc.Int1.from_user(ip_len)
+
+    @staticmethod
+    def _ip_addr_from_bin(buf, ip_len):
+        return ip.bin_to_text(buf[:ip_len]), buf[ip_len:]
+
+    @staticmethod
+    def _ip_addr_to_bin(ip_addr):
+        return ip.text_to_bin(ip_addr)
+
+    @staticmethod
+    def _mpls_label_from_bin(buf):
+        mpls_label, is_bos = mpls.label_from_bin(buf)
+        rest = buf[3:]
+        return mpls_label, rest, is_bos
+
+    @staticmethod
+    def _mpls_label_to_bin(label, is_bos=True):
+        return mpls.label_to_bin(label, is_bos=is_bos)
+
+    @staticmethod
+    def _vni_from_bin(buf):
+        return vxlan.vni_from_bin(six.binary_type(buf[:3])), buf[3:]
+
+    @staticmethod
+    def _vni_to_bin(vni):
+        return vxlan.vni_to_bin(vni)
+
+    @property
+    def prefix(self):
+        def _format(i):
+            pairs = []
+            for k in i.NLRI_PREFIX_FIELDS:
+                v = getattr(i, k)
+                if k == 'esi':
+                    pairs.append('%s:%s' % (k, v.formatted_str))
+                else:
+                    pairs.append('%s:%s' % (k, v))
+            return ','.join(pairs)
+
+        return '%s(%s)' % (self.ROUTE_TYPE_NAME, _format(self))
+
+    @property
+    def formatted_nlri_str(self):
+        return '%s:%s' % (self.route_dist, self.prefix)
+
+
+@EvpnNLRI.register_unknown_type()
+class EvpnUnknownNLRI(EvpnNLRI):
+    """
+    Unknown route type specific EVPN NLRI
+    """
+    ROUTE_TYPE_NAME = 'unknown'
+    NLRI_PREFIX_FIELDS = ['value']
+
+    def __init__(self, value, type_, length=None):
+        super(EvpnUnknownNLRI, self).__init__(type_, length)
+        self.value = value
+
+    @classmethod
+    def parse_value(cls, buf):
+        return {
+            'value': buf
+        }
+
+    def serialize_value(self):
+        return self.value
+
+    @property
+    def formatted_nlri_str(self):
+        return '%s(%s)' % (self.ROUTE_TYPE_NAME, binary_str(self.value))
+
+
+@EvpnNLRI.register_type(EvpnNLRI.ETHERNET_AUTO_DISCOVERY)
+class EvpnEthernetAutoDiscoveryNLRI(EvpnNLRI):
+    """
+    Ethernet A-D route type specific EVPN NLRI
+    """
+    ROUTE_TYPE_NAME = 'eth_ad'
+
+    # +---------------------------------------+
+    # |  Route Distinguisher (RD) (8 octets)  |
+    # +---------------------------------------+
+    # |Ethernet Segment Identifier (10 octets)|
+    # +---------------------------------------+
+    # |  Ethernet Tag ID (4 octets)           |
+    # +---------------------------------------+
+    # |  MPLS Label (3 octets)                |
+    # +---------------------------------------+
+    _PACK_STR = "!8s10sI3s"
+    NLRI_PREFIX_FIELDS = ['esi', 'ethernet_tag_id']
+    _TYPE = {
+        'ascii': [
+            'route_dist',
+        ]
+    }
+
+    def __init__(self, route_dist, esi, ethernet_tag_id,
+                 mpls_label=None, vni=None, label=None,
+                 type_=None, length=None):
+        super(EvpnEthernetAutoDiscoveryNLRI, self).__init__(type_, length)
+        self.route_dist = route_dist
+        self.esi = esi
+        self.ethernet_tag_id = ethernet_tag_id
+        if label:
+            # If binary type label field value is specified, stores it
+            # and decodes as MPLS label and VNI.
+            self._label = label
+            self._mpls_label, _, _ = self._mpls_label_from_bin(label)
+            self._vni, _ = self._vni_from_bin(label)
+        else:
+            # If either MPLS label or VNI is specified, stores it
+            # and encodes into binary type label field value.
+            self._label = self._serialize_label(mpls_label, vni)
+            self._mpls_label = mpls_label
+            self._vni = vni
+
+    def _serialize_label(self, mpls_label, vni):
+        if mpls_label:
+            return self._mpls_label_to_bin(mpls_label, is_bos=True)
+        elif vni:
+            return self._vni_to_bin(vni)
+        else:
+            return b'\x00' * 3
+
+    @classmethod
+    def parse_value(cls, buf):
+        route_dist, rest = cls._rd_from_bin(buf)
+        esi, rest = cls._esi_from_bin(rest)
+        ethernet_tag_id, rest = cls._ethernet_tag_id_from_bin(rest)
+
+        return {
+            'route_dist': route_dist.formatted_str,
+            'esi': esi,
+            'ethernet_tag_id': ethernet_tag_id,
+            'label': rest,
+        }
+
+    def serialize_value(self):
+        route_dist = _RouteDistinguisher.from_str(self.route_dist)
+        return struct.pack(
+            self._PACK_STR, route_dist.serialize(), self.esi.serialize(),
+            self.ethernet_tag_id, self._label)
+
+    @property
+    def mpls_label(self):
+        return self._mpls_label
+
+    @mpls_label.setter
+    def mpls_label(self, mpls_label):
+        self._label = self._mpls_label_to_bin(mpls_label, is_bos=True)
+        self._mpls_label = mpls_label
+        self._vni = None  # disables VNI
+
+    @property
+    def vni(self):
+        return self._vni
+
+    @vni.setter
+    def vni(self, vni):
+        self._label = self._vni_to_bin(vni)
+        self._mpls_label = None  # disables MPLS label
+        self._vni = vni
+
+    @property
+    def label_list(self):
+        return [self.mpls_label]
+
+
+@EvpnNLRI.register_type(EvpnNLRI.MAC_IP_ADVERTISEMENT)
+class EvpnMacIPAdvertisementNLRI(EvpnNLRI):
+    """
+    MAC/IP Advertisement route type specific EVPN NLRI
+    """
+    ROUTE_TYPE_NAME = 'mac_ip_adv'
+
+    # +---------------------------------------+
+    # |  RD (8 octets)                        |
+    # +---------------------------------------+
+    # |Ethernet Segment Identifier (10 octets)|
+    # +---------------------------------------+
+    # |  Ethernet Tag ID (4 octets)           |
+    # +---------------------------------------+
+    # |  MAC Address Length (1 octet)         |
+    # +---------------------------------------+
+    # |  MAC Address (6 octets)               |
+    # +---------------------------------------+
+    # |  IP Address Length (1 octet)          |
+    # +---------------------------------------+
+    # |  IP Address (0, 4, or 16 octets)      |
+    # +---------------------------------------+
+    # |  MPLS Label1 (3 octets)               |
+    # +---------------------------------------+
+    # |  MPLS Label2 (0 or 3 octets)          |
+    # +---------------------------------------+
+    _PACK_STR = "!8s10sIB6sB%ds%ds"
+    # Note: mac_addr_len and ip_addr_len are omitted for readability.
+    NLRI_PREFIX_FIELDS = ['ethernet_tag_id', 'mac_addr', 'ip_addr']
+    _TYPE = {
+        'ascii': [
+            'route_dist',
+            'mac_addr',
+            'ip_addr',
+        ]
+    }
+
+    def __init__(self, route_dist, ethernet_tag_id, mac_addr, ip_addr,
+                 esi=None, mpls_labels=None, vni=None, labels=None,
+                 mac_addr_len=None, ip_addr_len=None,
+                 type_=None, length=None):
+        super(EvpnMacIPAdvertisementNLRI, self).__init__(type_, length)
+        self.route_dist = route_dist
+        self.esi = esi
+        self.ethernet_tag_id = ethernet_tag_id
+        self.mac_addr_len = mac_addr_len
+        self.mac_addr = mac_addr
+        self.ip_addr_len = ip_addr_len
+        self.ip_addr = ip_addr
+        if labels:
+            # If binary type labels field value is specified, stores it
+            # and decodes as MPLS labels and VNI.
+            self._mpls_labels, self._vni = self._parse_labels(labels)
+            self._labels = labels
+        else:
+            # If either MPLS labels or VNI is specified, stores it
+            # and encodes into binary type labels field value.
+            self._labels = self._serialize_labels(mpls_labels, vni)
+            self._mpls_labels = mpls_labels
+            self._vni = vni
+
+    def _parse_labels(self, labels):
+        mpls_label1, rest, is_bos = self._mpls_label_from_bin(labels)
+        mpls_labels = [mpls_label1]
+        if rest and not is_bos:
+            mpls_label2, rest, _ = self._mpls_label_from_bin(rest)
+            mpls_labels.append(mpls_label2)
+        vni, _ = self._vni_from_bin(labels)
+        return mpls_labels, vni
+
+    def _serialize_labels(self, mpls_labels, vni):
+        if mpls_labels:
+            return self._serialize_mpls_labels(mpls_labels)
+        elif vni:
+            return self._vni_to_bin(vni)
+        else:
+            return b'\x00' * 3
+
+    def _serialize_mpls_labels(self, mpls_labels):
+        if len(mpls_labels) == 1:
+            return self._mpls_label_to_bin(mpls_labels[0], is_bos=True)
+        elif len(mpls_labels) == 2:
+            return (self._mpls_label_to_bin(mpls_labels[0], is_bos=False) +
+                    self._mpls_label_to_bin(mpls_labels[1], is_bos=True))
+        else:
+            return b'\x00' * 3
+
+    @classmethod
+    def parse_value(cls, buf):
+        route_dist, rest = cls._rd_from_bin(buf)
+        esi, rest = cls._esi_from_bin(rest)
+        ethernet_tag_id, rest = cls._ethernet_tag_id_from_bin(rest)
+        mac_addr_len, rest = cls._mac_addr_len_from_bin(rest)
+        mac_addr, rest = cls._mac_addr_from_bin(rest, mac_addr_len)
+        ip_addr_len, rest = cls._ip_addr_len_from_bin(rest)
+        if ip_addr_len != 0:
+            ip_addr, rest = cls._ip_addr_from_bin(rest, ip_addr_len // 8)
+        else:
+            ip_addr = None
+
+        return {
+            'route_dist': route_dist.formatted_str,
+            'esi': esi,
+            'ethernet_tag_id': ethernet_tag_id,
+            'mac_addr_len': mac_addr_len,
+            'mac_addr': mac_addr,
+            'ip_addr_len': ip_addr_len,
+            'ip_addr': ip_addr,
+            'labels': rest,
+        }
+
+    def serialize_value(self):
+        route_dist = _RouteDistinguisher.from_str(self.route_dist)
+        mac_addr = self._mac_addr_to_bin(self.mac_addr)
+        self.mac_addr_len = len(mac_addr) * 8  # fixup
+        if self.ip_addr:
+            ip_addr = self._ip_addr_to_bin(self.ip_addr)
+        else:
+            ip_addr = b''
+        ip_addr_len = len(ip_addr)
+        self.ip_addr_len = ip_addr_len * 8  # fixup
+
+        return struct.pack(
+            self._PACK_STR % (ip_addr_len, len(self._labels)),
+            route_dist.serialize(), self.esi.serialize(),
+            self.ethernet_tag_id,
+            self.mac_addr_len, mac_addr,
+            self.ip_addr_len, ip_addr,
+            self._labels)
+
+    @property
+    def mpls_labels(self):
+        return self._mpls_labels
+
+    @mpls_labels.setter
+    def mpls_labels(self, mpls_labels):
+        self._labels = self._serialize_mpls_labels(mpls_labels)
+        self._mpls_labels = mpls_labels
+        self._vni = None  # disables VNI
+
+    @property
+    def vni(self):
+        return self._vni
+
+    @vni.setter
+    def vni(self, vni):
+        self._labels = self._vni_to_bin(vni)
+        self._mpls_labels = None  # disables MPLS labels
+        self._vni = vni
+
+    @property
+    def label_list(self):
+        return self.mpls_labels
+
+
+@EvpnNLRI.register_type(EvpnNLRI.INCLUSIVE_MULTICAST_ETHERNET_TAG)
+class EvpnInclusiveMulticastEthernetTagNLRI(EvpnNLRI):
+    """
+    Inclusive Multicast Ethernet Tag route type specific EVPN NLRI
+    """
+    ROUTE_TYPE_NAME = 'multicast_etag'
+
+    # +---------------------------------------+
+    # |  RD (8 octets)                        |
+    # +---------------------------------------+
+    # |  Ethernet Tag ID (4 octets)           |
+    # +---------------------------------------+
+    # |  IP Address Length (1 octet)          |
+    # +---------------------------------------+
+    # |  Originating Router's IP Address      |
+    # |          (4 or 16 octets)             |
+    # +---------------------------------------+
+    _PACK_STR = '!8sIB%ds'
+    NLRI_PREFIX_FIELDS = ['ethernet_tag_id', 'ip_addr']
+    _TYPE = {
+        'ascii': [
+            'route_dist',
+            'ip_addr',
+        ]
+    }
+
+    def __init__(self, route_dist, ethernet_tag_id, ip_addr,
+                 ip_addr_len=None, type_=None, length=None):
+        super(EvpnInclusiveMulticastEthernetTagNLRI,
+              self).__init__(type_, length)
+        self.route_dist = route_dist
+        self.ethernet_tag_id = ethernet_tag_id
+        self.ip_addr_len = ip_addr_len
+        self.ip_addr = ip_addr
+
+    @classmethod
+    def parse_value(cls, buf):
+        route_dist, rest = cls._rd_from_bin(buf)
+        ethernet_tag_id, rest = cls._ethernet_tag_id_from_bin(rest)
+        ip_addr_len, rest = cls._ip_addr_len_from_bin(rest)
+        ip_addr, rest = cls._ip_addr_from_bin(rest, ip_addr_len // 8)
+
+        return {
+            'route_dist': route_dist.formatted_str,
+            'ethernet_tag_id': ethernet_tag_id,
+            'ip_addr_len': ip_addr_len,
+            'ip_addr': ip_addr,
+        }
+
+    def serialize_value(self):
+        route_dist = _RouteDistinguisher.from_str(self.route_dist)
+        ip_addr = self._ip_addr_to_bin(self.ip_addr)
+        self.ip_addr_len = len(ip_addr) * 8  # fixup
+
+        return struct.pack(
+            self._PACK_STR % len(ip_addr),
+            route_dist.serialize(), self.ethernet_tag_id,
+            self.ip_addr_len, ip_addr)
+
+
+@EvpnNLRI.register_type(EvpnNLRI.ETHERNET_SEGMENT)
+class EvpnEthernetSegmentNLRI(EvpnNLRI):
+    """
+    Ethernet Segment route type specific EVPN NLRI
+    """
+    ROUTE_TYPE_NAME = 'eth_seg'
+
+    # +---------------------------------------+
+    # |  RD (8 octets)                        |
+    # +---------------------------------------+
+    # |Ethernet Segment Identifier (10 octets)|
+    # +---------------------------------------+
+    # |  IP Address Length (1 octet)          |
+    # +---------------------------------------+
+    # |  Originating Router's IP Address      |
+    # |          (4 or 16 octets)             |
+    # +---------------------------------------+
+    _PACK_STR = '!8s10sB%ds'
+    NLRI_PREFIX_FIELDS = ['esi', 'ip_addr']
+    _TYPE = {
+        'ascii': [
+            'route_dist',
+            'ip_addr',
+        ]
+    }
+
+    def __init__(self, route_dist, esi, ip_addr, ip_addr_len=None,
+                 type_=None, length=None):
+        super(EvpnEthernetSegmentNLRI, self).__init__(type_, length)
+        self.route_dist = route_dist
+        self.esi = esi
+        self.ip_addr_len = ip_addr_len
+        self.ip_addr = ip_addr
+
+    @classmethod
+    def parse_value(cls, buf):
+        route_dist, rest = cls._rd_from_bin(buf)
+        esi, rest = cls._esi_from_bin(rest)
+        ip_addr_len, rest = cls._ip_addr_len_from_bin(rest)
+        ip_addr, rest = cls._ip_addr_from_bin(rest, ip_addr_len // 8)
+
+        return {
+            'route_dist': route_dist.formatted_str,
+            'esi': esi,
+            'ip_addr_len': ip_addr_len,
+            'ip_addr': ip_addr,
+        }
+
+    def serialize_value(self):
+        route_dist = _RouteDistinguisher.from_str(self.route_dist)
+        ip_addr = self._ip_addr_to_bin(self.ip_addr)
+        # fixup
+        self.ip_addr_len = len(ip_addr) * 8
+
+        return struct.pack(
+            self._PACK_STR % len(ip_addr),
+            route_dist.serialize(), self.esi.serialize(),
+            self.ip_addr_len, ip_addr)
+
+
+@EvpnNLRI.register_type(EvpnNLRI.IP_PREFIX_ROUTE)
+class EvpnIpPrefixNLRI(EvpnNLRI):
+    """
+    IP Prefix advertisement route NLRI
+    """
+    ROUTE_TYPE_NAME = 'ip_prefix'
+
+    # +---------------------------------------+
+    # |      RD   (8 octets)                  |
+    # +---------------------------------------+
+    # |Ethernet Segment Identifier (10 octets)|
+    # +---------------------------------------+
+    # |  Ethernet Tag ID (4 octets)           |
+    # +---------------------------------------+
+    # |  IP Prefix Length (1 octet)           |
+    # +---------------------------------------+
+    # |  IP Prefix (4 or 16 octets)           |
+    # +---------------------------------------+
+    # |  GW IP Address (4 or 16 octets)       |
+    # +---------------------------------------+
+    # |  MPLS Label (3 octets)                |
+    # +---------------------------------------+
+    _PACK_STR = '!8s10sIB%ds%ds3s'
+    NLRI_PREFIX_FIELDS = ['ethernet_tag_id', 'ip_prefix']
+    _TYPE = {
+        'ascii': [
+            'route_dist',
+            'ip_prefix',
+            'gw_ip_addr'
+        ]
+    }
+    _LABEL_LEN = 3
+
+    def __init__(self, route_dist, ethernet_tag_id, ip_prefix,
+                 esi=None, gw_ip_addr=None,
+                 mpls_label=None, vni=None, label=None,
+                 type_=None, length=None):
+        super(EvpnIpPrefixNLRI, self).__init__(type_, length)
+        self.route_dist = route_dist
+        self.esi = esi
+        self.ethernet_tag_id = ethernet_tag_id
+        self._ip_prefix = None
+        self._ip_prefix_len = None
+        self.ip_prefix = ip_prefix
+
+        if gw_ip_addr is None:
+            if ':' not in self._ip_prefix:
+                self.gw_ip_addr = '0.0.0.0'
+            else:
+                self.gw_ip_addr = '::'
+        else:
+            self.gw_ip_addr = gw_ip_addr
+
+        if label:
+            # If binary type label field value is specified, stores it
+            # and decodes as MPLS label and VNI.
+            self._label = label
+            self._mpls_label, _, _ = self._mpls_label_from_bin(label)
+            self._vni, _ = self._vni_from_bin(label)
+        else:
+            # If either MPLS label or VNI is specified, stores it
+            # and encodes into binary type label field value.
+            self._label = self._serialize_label(mpls_label, vni)
+            self._mpls_label = mpls_label
+            self._vni = vni
+
+    def _serialize_label(self, mpls_label, vni):
+        if mpls_label:
+            return self._mpls_label_to_bin(mpls_label, is_bos=True)
+        elif vni:
+            return vxlan.vni_to_bin(vni)
+        else:
+            return b'\x00' * 3
+
+    @classmethod
+    def parse_value(cls, buf):
+        route_dist, rest = cls._rd_from_bin(buf)
+        esi, rest = cls._esi_from_bin(rest)
+        ethernet_tag_id, rest = cls._ethernet_tag_id_from_bin(rest)
+        ip_prefix_len, rest = cls._ip_addr_len_from_bin(rest)
+        _len = (len(rest) - cls._LABEL_LEN) // 2
+        ip_prefix, rest = cls._ip_addr_from_bin(rest, _len)
+        gw_ip_addr, rest = cls._ip_addr_from_bin(rest, _len)
+
+        return {
+            'route_dist': route_dist.formatted_str,
+            'esi': esi,
+            'ethernet_tag_id': ethernet_tag_id,
+            'ip_prefix': '%s/%s' % (ip_prefix, ip_prefix_len),
+            'gw_ip_addr': gw_ip_addr,
+            'label': rest,
+        }
+
+    def serialize_value(self):
+        route_dist = _RouteDistinguisher.from_str(self.route_dist)
+        ip_prefix = self._ip_addr_to_bin(self._ip_prefix)
+        gw_ip_addr = self._ip_addr_to_bin(self.gw_ip_addr)
+
+        return struct.pack(
+            self._PACK_STR % (len(ip_prefix), len(gw_ip_addr)),
+            route_dist.serialize(), self.esi.serialize(),
+            self.ethernet_tag_id, self._ip_prefix_len, ip_prefix,
+            gw_ip_addr, self._label)
+
+    @property
+    def ip_prefix(self):
+        return '%s/%s' % (self._ip_prefix, self._ip_prefix_len)
+
+    @ip_prefix.setter
+    def ip_prefix(self, ip_prefix):
+        self._ip_prefix, ip_prefix_len = ip_prefix.split('/')
+        self._ip_prefix_len = int(ip_prefix_len)
+
+    @property
+    def mpls_label(self):
+        return self._mpls_label
+
+    @mpls_label.setter
+    def mpls_label(self, mpls_label):
+        self._label = self._mpls_label_to_bin(mpls_label, is_bos=True)
+        self._mpls_label = mpls_label
+        self._vni = None  # disables VNI
+
+    @property
+    def vni(self):
+        return self._vni
+
+    @vni.setter
+    def vni(self, vni):
+        self._label = self._vni_to_bin(vni)
+        self._mpls_label = None  # disables MPLS label
+        self._vni = vni
+
+    @property
+    def label_list(self):
+        return [self.mpls_label]
+
+
+class _FlowSpecNLRIBase(StringifyMixin, TypeDisp):
+    """
+    Base class for Flow Specification NLRI
+    """
+
+    # flow-spec NLRI:
+    # +-----------------------------------+
+    # |    length (0xnn or 0xfn nn)       |
+    # +-----------------------------------+
+    # |     NLRI value  (variable)        |
+    # +-----------------------------------+
+    ROUTE_FAMILY = None
+    _LENGTH_SHORT_FMT = '!B'
+    LENGTH_SHORT_SIZE = struct.calcsize(_LENGTH_SHORT_FMT)
+    _LENGTH_LONG_FMT = '!H'
+    LENGTH_LONG_SIZE = struct.calcsize(_LENGTH_LONG_FMT)
+    _LENGTH_THRESHOLD = 0xf000
+    FLOWSPEC_FAMILY = ''
+
+    def __init__(self, length=0, rules=None):
+        self.length = length
+        rules = rules or []
+        for r in rules:
+            assert isinstance(r, _FlowSpecComponentBase)
+        self.rules = rules
+
+    @classmethod
+    def parser(cls, buf):
+        (length,) = struct.unpack_from(
+            cls._LENGTH_LONG_FMT, six.binary_type(buf))
+
+        if length < cls._LENGTH_THRESHOLD:
+            length >>= 8
+            offset = cls.LENGTH_SHORT_SIZE
+        else:
+            offset = cls.LENGTH_LONG_SIZE
+
+        kwargs = {'length': length}
+        rest = buf[offset:offset + length]
+
+        if cls.ROUTE_FAMILY.safi == subaddr_family.VPN_FLOWSPEC:
+            route_dist = _RouteDistinguisher.parser(rest[:8])
+            kwargs['route_dist'] = route_dist.formatted_str
+            rest = rest[8:]
+
+        rules = []
+
+        while rest:
+            subcls, rest = _FlowSpecComponentBase.parse_header(
+                rest, cls.ROUTE_FAMILY.afi)
+
+            while rest:
+                rule, rest = subcls.parse_body(rest)
+                rules.append(rule)
+
+                if (not isinstance(rule, _FlowSpecOperatorBase) or
+                        rule.operator & rule.END_OF_LIST):
+                    break
+
+        kwargs['rules'] = rules
+
+        return cls(**kwargs), rest
+
+    def serialize(self):
+        rules_bin = b''
+
+        if self.ROUTE_FAMILY.safi == subaddr_family.VPN_FLOWSPEC:
+            route_dist = _RouteDistinguisher.from_str(self.route_dist)
+            rules_bin += route_dist.serialize()
+
+        self.rules.sort(key=lambda x: x.type)
+        for _, rules in itertools.groupby(self.rules, key=lambda x: x.type):
+            rules = list(rules)
+            rules_bin += rules[0].serialize_header()
+
+            if isinstance(rules[-1], _FlowSpecOperatorBase):
+                rules[-1].operator |= rules[-1].END_OF_LIST
+
+            for r in rules:
+                rules_bin += r.serialize_body()
+
+        self.length = len(rules_bin)
+
+        if self.length < self._LENGTH_THRESHOLD:
+            buf = struct.pack(self._LENGTH_SHORT_FMT, self.length)
+        else:
+            buf = struct.pack(self._LENGTH_LONG_FMT, self.length)
+
+        return buf + rules_bin
+
+    @classmethod
+    def _from_user(cls, **kwargs):
+        rules = []
+        for k, v in kwargs.items():
+            subcls = _FlowSpecComponentBase.lookup_type_name(
+                k, cls.ROUTE_FAMILY.afi)
+            rule = subcls.from_str(str(v))
+            rules.extend(rule)
+        rules.sort(key=lambda x: x.type)
+        return cls(rules=rules)
+
+    @property
+    def prefix(self):
+        def _format(i):
+            pairs = []
+            i.rules.sort(key=lambda x: x.type)
+            previous_type = None
+            for r in i.rules:
+                if r.type == previous_type:
+                    if r.to_str()[0] != '&':
+                        pairs[-1] += '|'
+                    pairs[-1] += r.to_str()
+                else:
+                    pairs.append('%s:%s' % (r.COMPONENT_NAME, r.to_str()))
+                previous_type = r.type
+
+            return ','.join(pairs)
+
+        return '%s(%s)' % (self.FLOWSPEC_FAMILY, _format(self))
+
+    @property
+    def formatted_nlri_str(self):
+        return self.prefix
+
+
+class FlowSpecIPv4NLRI(_FlowSpecNLRIBase):
+    """
+    Flow Specification NLRI class for IPv4 [RFC 5575]
+    """
+    ROUTE_FAMILY = RF_IPv4_FLOWSPEC
+    FLOWSPEC_FAMILY = 'ipv4fs'
+
+    @classmethod
+    def from_user(cls, **kwargs):
+        """
+        Utility method for creating a NLRI instance.
+
+        This function returns a NLRI instance from human readable format value.
+
+        :param kwargs: The following arguments are available.
+
+        =========== ============= ========= ==============================
+        Argument    Value         Operator  Description
+        =========== ============= ========= ==============================
+        dst_prefix  IPv4 Prefix   Nothing   Destination Prefix.
+        src_prefix  IPv4 Prefix   Nothing   Source Prefix.
+        ip_proto    Integer       Numeric   IP Protocol.
+        port        Integer       Numeric   Port number.
+        dst_port    Integer       Numeric   Destination port number.
+        src_port    Integer       Numeric   Source port number.
+        icmp_type   Integer       Numeric   ICMP type.
+        icmp_code   Integer       Numeric   ICMP code.
+        tcp_flags   Fixed string  Bitmask   TCP flags.
+                                            Supported values are
+                                            ``CWR``, ``ECN``, ``URGENT``,
+                                            ``ACK``, ``PUSH``, ``RST``,
+                                            ``SYN`` and ``FIN``.
+        packet_len  Integer       Numeric   Packet length.
+        dscp        Integer       Numeric   Differentiated Services
+                                            Code Point.
+        fragment    Fixed string  Bitmask   Fragment.
+                                            Supported values are
+                                            ``DF`` (Don't fragment),
+                                            ``ISF`` (Is a fragment),
+                                            ``FF`` (First fragment) and
+                                            ``LF`` (Last fragment)
+        =========== ============= ========= ==============================
+
+        Example::
+
+            >>> msg = bgp.FlowSpecIPv4NLRI.from_user(
+            ...     dst_prefix='10.0.0.0/24',
+            ...     src_prefix='20.0.0.1/24',
+            ...     ip_proto=6,
+            ...     port='80 | 8000',
+            ...     dst_port='>9000 & <9050',
+            ...     src_port='>=8500 & <=9000',
+            ...     icmp_type=0,
+            ...     icmp_code=6,
+            ...     tcp_flags='SYN+ACK & !=URGENT',
+            ...     packet_len=1000,
+            ...     dscp='22 | 24',
+            ...     fragment='LF | ==FF')
+            >>>
+
+        You can specify conditions with the following keywords.
+
+        The following keywords can be used when the operator type is Numeric.
+
+        ========== ============================================================
+        Keyword    Description
+        ========== ============================================================
+        <          Less than comparison between data and value.
+        <=         Less than or equal to comparison between data and value.
+        >          Greater than comparison between data and value.
+        >=         Greater than or equal to comparison between data and value.
+        ==         Equality between data and value.
+                   This operator can be omitted.
+        ========== ============================================================
+
+        The following keywords can be used when the operator type is Bitmask.
+
+        ========== ================================================
+        Keyword    Description
+        ========== ================================================
+        !=         Not equal operation.
+        ==         Exact match operation if specified.
+                   Otherwise partial match operation.
+        `+`        Used for the summation of bitmask values.
+                   (e.g., SYN+ACK)
+        ========== ================================================
+
+        You can combine the multiple conditions with the following operators.
+
+        ========== =======================================
+        Keyword    Description
+        ========== =======================================
+        `|`        Logical OR operation
+        &          Logical AND operation
+        ========== =======================================
+
+        :return: A instance of FlowSpecVPNv4NLRI.
+        """
+        return cls._from_user(**kwargs)
+
+
+class FlowSpecVPNv4NLRI(_FlowSpecNLRIBase):
+    """
+    Flow Specification NLRI class for VPNv4 [RFC 5575]
+    """
+
+    # flow-spec NLRI:
+    # +-----------------------------------+
+    # |    length (0xnn or 0xfn nn)       |
+    # +-----------------------------------+
+    # |     RD   (8 octets)               |
+    # +-----------------------------------+
+    # |     NLRI value  (variable)        |
+    # +-----------------------------------+
+    ROUTE_FAMILY = RF_VPNv4_FLOWSPEC
+    FLOWSPEC_FAMILY = 'vpnv4fs'
+
+    def __init__(self, length=0, route_dist=None, rules=None):
+        super(FlowSpecVPNv4NLRI, self).__init__(length, rules)
+        assert route_dist is not None
+        self.route_dist = route_dist
+
+    @classmethod
+    def _from_user(cls, route_dist, **kwargs):
+        rules = []
+        for k, v in kwargs.items():
+            subcls = _FlowSpecComponentBase.lookup_type_name(
+                k, cls.ROUTE_FAMILY.afi)
+            rule = subcls.from_str(str(v))
+            rules.extend(rule)
+        rules.sort(key=lambda x: x.type)
+        return cls(route_dist=route_dist, rules=rules)
+
+    @classmethod
+    def from_user(cls, route_dist, **kwargs):
+        """
+        Utility method for creating a NLRI instance.
+
+        This function returns a NLRI instance from human readable format value.
+
+        :param route_dist: Route Distinguisher.
+        :param kwargs: See :py:mod:`ryu.lib.packet.bgp.FlowSpecIPv4NLRI`
+
+        Example::
+
+            >>> msg = bgp.FlowSpecIPv4NLRI.from_user(
+            ...     route_dist='65000:1000',
+            ...     dst_prefix='10.0.0.0/24',
+            ...     src_prefix='20.0.0.1/24',
+            ...     ip_proto=6,
+            ...     port='80 | 8000',
+            ...     dst_port='>9000 & <9050',
+            ...     src_port='>=8500 & <=9000',
+            ...     icmp_type=0,
+            ...     icmp_code=6,
+            ...     tcp_flags='SYN+ACK & !=URGENT',
+            ...     packet_len=1000,
+            ...     dscp='22 | 24',
+            ...     fragment='LF | ==FF')
+            >>>
+        """
+        return cls._from_user(route_dist, **kwargs)
+
+    @property
+    def formatted_nlri_str(self):
+        return '%s:%s' % (self.route_dist, self.prefix)
+
+
+class FlowSpecIPv6NLRI(_FlowSpecNLRIBase):
+    """
+    Flow Specification NLRI class for IPv6 [RFC draft-ietf-idr-flow-spec-v6-08]
+    """
+    ROUTE_FAMILY = RF_IPv6_FLOWSPEC
+    FLOWSPEC_FAMILY = 'ipv6fs'
+
+    @classmethod
+    def from_user(cls, **kwargs):
+        """
+        Utility method for creating a NLRI instance.
+
+        This function returns a NLRI instance from human readable format value.
+
+        :param kwargs: The following arguments are available.
+
+        =========== ============= ========= ==============================
+        Argument    Value         Operator  Description
+        =========== ============= ========= ==============================
+        dst_prefix  IPv6 Prefix   Nothing   Destination Prefix.
+        src_prefix  IPv6 Prefix   Nothing   Source Prefix.
+        next_header Integer       Numeric   Next Header.
+        port        Integer       Numeric   Port number.
+        dst_port    Integer       Numeric   Destination port number.
+        src_port    Integer       Numeric   Source port number.
+        icmp_type   Integer       Numeric   ICMP type.
+        icmp_code   Integer       Numeric   ICMP code.
+        tcp_flags   Fixed string  Bitmask   TCP flags.
+                                            Supported values are
+                                            ``CWR``, ``ECN``, ``URGENT``,
+                                            ``ACK``, ``PUSH``, ``RST``,
+                                            ``SYN`` and ``FIN``.
+        packet_len  Integer       Numeric   Packet length.
+        dscp        Integer       Numeric   Differentiated Services
+                                            Code Point.
+        fragment    Fixed string  Bitmask   Fragment.
+                                            Supported values are
+                                            ``ISF`` (Is a fragment),
+                                            ``FF`` (First fragment) and
+                                            ``LF`` (Last fragment)
+        flow_label   Intefer      Numeric   Flow Label.
+        =========== ============= ========= ==============================
+
+        .. Note::
+
+            For ``dst_prefix`` and ``src_prefix``, you can give "offset" value
+            like this: ``2001::2/128/32``. At this case, ``offset`` is 32.
+            ``offset`` can be omitted, then ``offset`` is treated as 0.
+        """
+        return cls._from_user(**kwargs)
+
+
+class FlowSpecVPNv6NLRI(_FlowSpecNLRIBase):
+    """
+    Flow Specification NLRI class for VPNv6 [draft-ietf-idr-flow-spec-v6-08]
+    """
+
+    # flow-spec NLRI:
+    # +-----------------------------------+
+    # |    length (0xnn or 0xfn nn)       |
+    # +-----------------------------------+
+    # |     RD   (8 octets)               |
+    # +-----------------------------------+
+    # |     NLRI value  (variable)        |
+    # +-----------------------------------+
+    ROUTE_FAMILY = RF_VPNv6_FLOWSPEC
+    FLOWSPEC_FAMILY = 'vpnv6fs'
+
+    def __init__(self, length=0, route_dist=None, rules=None):
+        super(FlowSpecVPNv6NLRI, self).__init__(length, rules)
+        assert route_dist is not None
+        self.route_dist = route_dist
+
+    @classmethod
+    def _from_user(cls, route_dist, **kwargs):
+        rules = []
+        for k, v in kwargs.items():
+            subcls = _FlowSpecComponentBase.lookup_type_name(
+                k, cls.ROUTE_FAMILY.afi)
+            rule = subcls.from_str(str(v))
+            rules.extend(rule)
+        rules.sort(key=lambda x: x.type)
+        return cls(route_dist=route_dist, rules=rules)
+
+    @classmethod
+    def from_user(cls, route_dist, **kwargs):
+        """
+        Utility method for creating a NLRI instance.
+
+        This function returns a NLRI instance from human readable format value.
+
+        :param route_dist: Route Distinguisher.
+        :param kwargs: See :py:mod:`ryu.lib.packet.bgp.FlowSpecIPv6NLRI`
+        """
+        return cls._from_user(route_dist, **kwargs)
+
+    @property
+    def formatted_nlri_str(self):
+        return '%s:%s' % (self.route_dist, self.prefix)
+
+
+class FlowSpecL2VPNNLRI(_FlowSpecNLRIBase):
+    """
+    Flow Specification NLRI class for L2VPN [draft-ietf-idr-flowspec-l2vpn-05]
+    """
+
+    # flow-spec NLRI:
+    # +-----------------------------------+
+    # |    length (0xnn or 0xfn nn)       |
+    # +-----------------------------------+
+    # |     RD   (8 octets)               |
+    # +-----------------------------------+
+    # |     NLRI value  (variable)        |
+    # +-----------------------------------+
+    ROUTE_FAMILY = RF_L2VPN_FLOWSPEC
+    FLOWSPEC_FAMILY = 'l2vpnfs'
+
+    def __init__(self, length=0, route_dist=None, rules=None):
+        super(FlowSpecL2VPNNLRI, self).__init__(length, rules)
+        assert route_dist is not None
+        self.route_dist = route_dist
+
+    @classmethod
+    def _from_user(cls, route_dist, **kwargs):
+        rules = []
+        for k, v in kwargs.items():
+            subcls = _FlowSpecComponentBase.lookup_type_name(
+                k, cls.ROUTE_FAMILY.afi)
+            rule = subcls.from_str(str(v))
+            rules.extend(rule)
+        rules.sort(key=lambda x: x.type)
+        return cls(route_dist=route_dist, rules=rules)
+
+    @classmethod
+    def from_user(cls, route_dist, **kwargs):
+        """
+        Utility method for creating a L2VPN NLRI instance.
+
+        This function returns a L2VPN NLRI instance
+        from human readable format value.
+
+        :param kwargs: The following arguments are available.
+
+        ============== ============= ========= ==============================
+        Argument       Value         Operator  Description
+        ============== ============= ========= ==============================
+        ether_type     Integer       Numeric   Ethernet Type.
+        src_mac        Mac Address   Nothing   Source Mac address.
+        dst_mac        Mac Address   Nothing   Destination Mac address.
+        llc_ssap       Integer       Numeric   Source Service Access Point
+                                               in LLC.
+        llc_dsap       Integer       Numeric   Destination Service Access
+                                               Point in LLC.
+        llc_control    Integer       Numeric   Control field in LLC.
+        snap           Integer       Numeric   Sub-Network Access Protocol
+                                               field.
+        vlan_id        Integer       Numeric   VLAN ID.
+        vlan_cos       Integer       Numeric   VLAN COS field.
+        inner_vlan_id  Integer       Numeric   Inner VLAN ID.
+        inner_vlan_cos Integer       Numeric   Inner VLAN COS field.
+        ============== ============= ========= ==============================
+        """
+        return cls._from_user(route_dist, **kwargs)
+
+    @property
+    def formatted_nlri_str(self):
+        return '%s:%s' % (self.route_dist, self.prefix)
+
+
+class _FlowSpecComponentBase(StringifyMixin, TypeDisp):
+    """
+    Base class for Flow Specification NLRI component
+    """
+    COMPONENT_NAME = None
+
+    _BASE_STR = '!B'
+    _BASE_STR_SIZE = struct.calcsize(_BASE_STR)
+
+    # Dictionary of COMPONENT_NAME to subclass.
+    # e.g.)
+    #   _NAMES = {'dst_prefix': FlowSpecDestPrefix, ...}
+    _NAMES = {}
+
+    def __init__(self, type_=None):
+        if type_ is None:
+            type_, _ = self._rev_lookup_type(self.__class__)
+        self.type = type_
+
+    @classmethod
+    def register_type(cls, type_, afi):
+        cls._TYPES = cls._TYPES.copy()
+        cls._NAMES = cls._NAMES.copy()
+
+        def _register_type(subcls):
+            cls._TYPES[(type_, afi)] = subcls
+            cls._NAMES[(subcls.COMPONENT_NAME, afi)] = subcls
+            cls._REV_TYPES = None
+            return subcls
+
+        return _register_type
+
+    @classmethod
+    def lookup_type_name(cls, type_name, afi):
+        return cls._NAMES[(type_name, afi)]
+
+    @classmethod
+    def _lookup_type(cls, type_, afi):
+        try:
+            return cls._TYPES[(type_, afi)]
+        except KeyError:
+            return cls._UNKNOWN_TYPE
+
+    @classmethod
+    def parse_header(cls, rest, afi):
+        (type_,) = struct.unpack_from(
+            cls._BASE_STR, six.binary_type(rest))
+        rest = rest[cls._BASE_STR_SIZE:]
+        return cls._lookup_type(type_, afi), rest
+
+    def serialize_header(self):
+        return struct.pack(self._BASE_STR, self.type)
+
+
+class _FlowSpecIPv4Component(_FlowSpecComponentBase):
+    """
+    Base class for Flow Specification for IPv4 NLRI component
+    """
+    TYPE_DESTINATION_PREFIX = 0x01
+    TYPE_SOURCE_PREFIX = 0x02
+    TYPE_PROTOCOL = 0x03
+    TYPE_PORT = 0x04
+    TYPE_DESTINATION_PORT = 0x05
+    TYPE_SOURCE_PORT = 0x06
+    TYPE_ICMP = 0x07
+    TYPE_ICMP_CODE = 0x08
+    TYPE_TCP_FLAGS = 0x09
+    TYPE_PACKET_LENGTH = 0x0a
+    TYPE_DIFFSERV_CODE_POINT = 0x0b
+    TYPE_FRAGMENT = 0x0c
+
+
+class _FlowSpecIPv6Component(_FlowSpecComponentBase):
+    """
+    Base class for Flow Specification for IPv6 NLRI component
+    """
+    TYPE_DESTINATION_PREFIX = 0x01
+    TYPE_SOURCE_PREFIX = 0x02
+    TYPE_NEXT_HEADER = 0x03
+    TYPE_PORT = 0x04
+    TYPE_DESTINATION_PORT = 0x05
+    TYPE_SOURCE_PORT = 0x06
+    TYPE_ICMP = 0x07
+    TYPE_ICMP_CODE = 0x08
+    TYPE_TCP_FLAGS = 0x09
+    TYPE_PACKET_LENGTH = 0x0a
+    TYPE_DIFFSERV_CODE_POINT = 0x0b
+    TYPE_FRAGMENT = 0x0c
+    TYPE_FLOW_LABEL = 0x0d
+
+
+class _FlowSpecL2VPNComponent(_FlowSpecComponentBase):
+    """
+    Base class for Flow Specification for L2VPN NLRI component
+    """
+    TYPE_ETHER_TYPE = 0x0e
+    TYPE_SOURCE_MAC = 0x0f
+    TYPE_DESTINATION_MAC = 0x10
+    TYPE_LLC_DSAP = 0x11
+    TYPE_LLC_SSAP = 0x12
+    TYPE_LLC_CONTROL = 0x13
+    TYPE_SNAP = 0x14
+    TYPE_VLAN_ID = 0x15
+    TYPE_VLAN_COS = 0x16
+    TYPE_INNER_VLAN_ID = 0x17
+    TYPE_INNER_VLAN_COS = 0x18
+
+
+@_FlowSpecComponentBase.register_unknown_type()
+class FlowSpecComponentUnknown(_FlowSpecComponentBase):
+    """
+    Unknown component type for Flow Specification NLRI component
+    """
+
+    def __init__(self, buf, type_=None):
+        super(FlowSpecComponentUnknown, self).__init__(type_)
+        self.buf = buf
+
+    @classmethod
+    def parse_body(cls, buf):
+        return cls(buf), None
+
+    def serialize_body(self):
+        return self.buf
+
+
+class _FlowSpecPrefixBase(_FlowSpecIPv4Component, IPAddrPrefix):
+    """
+    Prefix base class for Flow Specification NLRI component
+    """
+
+    def __init__(self, length, addr, type_=None):
+        super(_FlowSpecPrefixBase, self).__init__(type_)
+        self.length = length
+        prefix = "%s/%s" % (addr, length)
+        self.addr = str(netaddr.ip.IPNetwork(prefix).network)
+
+    @classmethod
+    def parse_body(cls, buf):
+        return cls.parser(buf)
+
+    def serialize_body(self):
+        return self.serialize()
+
+    @classmethod
+    def from_str(cls, value):
+        rule = []
+        addr, length = value.split('/')
+        rule.append(cls(int(length), addr))
+        return rule
+
+    @property
+    def value(self):
+        return "%s/%s" % (self.addr, self.length)
+
+    def to_str(self):
+        return self.value
+
+
+class _FlowSpecIPv6PrefixBase(_FlowSpecIPv6Component, IP6AddrPrefix):
+    """
+    Prefix base class for Flow Specification NLRI component
+    """
+    _PACK_STR = '!BB'  # length, offset
+
+    def __init__(self, length, addr, offset=0, type_=None):
+        super(_FlowSpecIPv6PrefixBase, self).__init__(type_)
+        self.length = length
+        self.offset = offset
+        prefix = "%s/%s" % (addr, length)
+        self.addr = str(netaddr.ip.IPNetwork(prefix).network)
+
+    @classmethod
+    def parser(cls, buf):
+        (length, offset) = struct.unpack_from(
+            cls._PACK_STR, six.binary_type(buf))
+        rest = buf[struct.calcsize(cls._PACK_STR):]
+        byte_length = (length + 7) // 8
+        addr = cls._from_bin(rest[:byte_length])
+        rest = rest[byte_length:]
+        return cls(length=length, offset=offset, addr=addr), rest
+
+    @classmethod
+    def parse_body(cls, buf):
+        return cls.parser(buf)
+
+    def serialize(self):
+        byte_length = (self.length + 7) // 8
+        bin_addr = self._to_bin(self.addr)[:byte_length]
+        buf = bytearray()
+        msg_pack_into(self._PACK_STR, buf, 0, self.length, self.offset)
+        return buf + bin_addr
+
+    def serialize_body(self):
+        return self.serialize()
+
+    @classmethod
+    def from_str(cls, value):
+        rule = []
+        values = value.split('/')
+        if len(values) == 3:
+            rule.append(cls(int(values[1]), values[0], offset=int(values[2])))
+        else:
+            rule.append(cls(int(values[1]), values[0]))
+        return rule
+
+    @property
+    def value(self):
+        return "%s/%s/%s" % (self.addr, self.length, self.offset)
+
+    def to_str(self):
+        return self.value
+
+
+class _FlowSpecL2VPNPrefixBase(_FlowSpecL2VPNComponent):
+    """
+    Prefix base class for Flow Specification NLRI component
+    """
+    _PACK_STR = "!B6s"
+
+    def __init__(self, length, addr, type_=None):
+        super(_FlowSpecL2VPNPrefixBase, self).__init__(type_)
+        self.length = length
+        self.addr = addr.lower()
+
+    @classmethod
+    def parse_body(cls, buf):
+        (length, addr) = struct.unpack_from(
+            cls._PACK_STR, six.binary_type(buf))
+        rest = buf[struct.calcsize(cls._PACK_STR):]
+        addr = addrconv.mac.bin_to_text(addr)
+        return cls(length=length, addr=addr), rest
+
+    def serialize(self):
+        addr = addrconv.mac.text_to_bin(self.addr)
+        return struct.pack(self._PACK_STR, self.length, addr)
+
+    def serialize_body(self):
+        return self.serialize()
+
+    @classmethod
+    def from_str(cls, value):
+        return [cls(len(value.split(':')), value)]
+
+    @property
+    def value(self):
+        return self.addr
+
+    def to_str(self):
+        return self.value
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv4Component.TYPE_DESTINATION_PREFIX, addr_family.IP)
+class FlowSpecDestPrefix(_FlowSpecPrefixBase):
+    """
+    Destination Prefix for Flow Specification NLRI component
+    """
+    COMPONENT_NAME = 'dst_prefix'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv4Component.TYPE_SOURCE_PREFIX, addr_family.IP)
+class FlowSpecSrcPrefix(_FlowSpecPrefixBase):
+    """
+    Source Prefix for Flow Specification NLRI component
+    """
+    COMPONENT_NAME = 'src_prefix'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv6Component.TYPE_DESTINATION_PREFIX, addr_family.IP6)
+class FlowSpecIPv6DestPrefix(_FlowSpecIPv6PrefixBase):
+    """
+    IPv6 destination Prefix for Flow Specification NLRI component
+    """
+    COMPONENT_NAME = 'dst_prefix'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv6Component.TYPE_SOURCE_PREFIX, addr_family.IP6)
+class FlowSpecIPv6SrcPrefix(_FlowSpecIPv6PrefixBase):
+    """
+    IPv6 source Prefix for Flow Specification NLRI component
+    """
+    COMPONENT_NAME = 'src_prefix'
+
+
+class _FlowSpecOperatorBase(_FlowSpecComponentBase):
+    """Operator base class for Flow Specification NLRI component
+
+    ===================== ===============================================
+    Attribute             Description
+    ===================== ===============================================
+    operator              Match conditions.
+    value                 Value of component.
+    ===================== ===============================================
+    """
+    _OPE_PACK_STR = '!B'
+    _OPE_PACK_STR_SIZE = struct.calcsize(_OPE_PACK_STR)
+    _VAL_PACK_STR = '!%ds'
+
+    END_OF_LIST = 1 << 7     # END OF LIST bit
+    AND = 1 << 6             # AND bit
+    OR = 0                   # OR
+    _LENGTH_BIT_MASK = 0x30  # The mask for length of the value
+
+    _logical_conditions = {
+        "|": OR,
+        "&": AND,
+    }
+    _comparison_conditions = {}
+
+    def __init__(self, operator, value, type_=None):
+        super(_FlowSpecOperatorBase, self).__init__(type_)
+        self.operator = operator
+        self.value = value
+
+    @classmethod
+    def parse_body(cls, rest):
+        (operator,) = struct.unpack_from(cls._OPE_PACK_STR,
+                                         six.binary_type(rest))
+        rest = rest[cls._OPE_PACK_STR_SIZE:]
+        length = 1 << ((operator & cls._LENGTH_BIT_MASK) >> 4)
+        value_type = type_desc.IntDescr(length)
+        value = value_type.to_user(rest)
+        rest = rest[length:]
+
+        return cls(operator, value), rest
+
+    def serialize_body(self):
+        byte_length = (self.value.bit_length() + 7) // 8 or 1
+        length = int(math.ceil(math.log(byte_length, 2)))
+        self.operator |= length << 4
+        buf = struct.pack(self._OPE_PACK_STR, self.operator)
+        value_type = type_desc.IntDescr(1 << length)
+        buf += struct.pack(self._VAL_PACK_STR % (1 << length),
+                           value_type.from_user(self.value))
+
+        return buf
+
+    @classmethod
+    def from_str(cls, val):
+        operator = 0
+        rules = []
+
+        # e.g.)
+        # value = '80 | ==90|>=8000&<=9000 | <100 & >110'
+        # elements = ['80', '|', '==', '90', '|', '>=', '8000', '&',
+        #             '<=', '9000', '|', '<', '100', '&', '>', '110']
+        elements = [v.strip() for v in re.split(
+            r'([0-9]+)|([A-Z]+)|(\|&\+)|([!=<>]+)', val) if v and v.strip()]
+
+        elms_iter = iter(elements)
+
+        for elm in elms_iter:
+            if elm in cls._logical_conditions:
+                # ['&', '|']
+                operator |= cls._logical_conditions[elm]
+                continue
+            elif elm in cls._comparison_conditions:
+                # ['=', '<', '>', '<=', '>=' ] or ['=', '!=']
+                operator |= cls._comparison_conditions[elm]
+                continue
+            elif elm == '+':
+                # If keyword "+" is used, add the value to the previous rule.
+                #  e.g.) 'SYN+ACK' or '!=SYN+ACK'
+                rules[-1].value |= cls._to_value(next(elms_iter))
+                continue
+
+            value = cls._to_value(elm)
+
+            operator = cls.normalize_operator(operator)
+
+            rules.append(cls(operator, value))
+            operator = 0
+
+        return rules
+
+    @classmethod
+    def _to_value(cls, value):
+        return value
+
+    @classmethod
+    def normalize_operator(cls, operator):
+        return operator
+
+
+class _FlowSpecNumeric(_FlowSpecOperatorBase):
+    """
+    Numeric operator class for Flow Specification NLRI component
+    """
+    # Numeric operator format
+    #  0   1   2   3   4   5   6   7
+    # +---+---+---+---+---+---+---+---+
+    # | e | a |  len  | 0 |lt |gt |eq |
+    # +---+---+---+---+---+---+---+---+
+
+    LT = 1 << 2  # Less than comparison bit
+    GT = 1 << 1  # Greater than comparison bit
+    EQ = 1 << 0  # Equality bit
+
+    _comparison_conditions = {
+        '==': EQ,
+        '<': LT,
+        '>': GT,
+        '<=': LT | EQ,
+        '>=': GT | EQ
+    }
+
+    @classmethod
+    def _to_value(cls, value):
+        try:
+            return int(str(value), 0)
+        except ValueError:
+            raise ValueError('Invalid params: %s="%s"' % (
+                cls.COMPONENT_NAME, value))
+
+    def to_str(self):
+        string = ""
+        if self.operator & self.AND:
+            string += "&"
+
+        operator = self.operator & (self.LT | self.GT | self.EQ)
+        for k, v in self._comparison_conditions.items():
+            if operator == v:
+                string += k
+
+        string += str(self.value)
+
+        return string
+
+    @classmethod
+    def normalize_operator(cls, operator):
+        if operator & (cls.LT | cls.GT | cls.EQ):
+            return operator
+        else:
+            return operator | cls.EQ
+
+
+class _FlowSpecBitmask(_FlowSpecOperatorBase):
+    """
+    Bitmask operator class for Flow Specification NLRI component
+    """
+    # Bitmask operator format
+    #  0   1   2   3   4   5   6   7
+    # +---+---+---+---+---+---+---+---+
+    # | e | a |  len  | 0 | 0 |not| m |
+    # +---+---+---+---+---+---+---+---+
+
+    NOT = 1 << 1    # NOT bit
+    MATCH = 1 << 0  # MATCH bit
+
+    _comparison_conditions = {
+        '!=': NOT,
+        '==': MATCH,
+    }
+
+    _bitmask_flags = {}
+
+    @classmethod
+    def _to_value(cls, value):
+        try:
+            return cls.__dict__[value]
+        except KeyError:
+            raise ValueError('Invalid params: %s="%s"' % (
+                cls.COMPONENT_NAME, value))
+
+    def to_str(self):
+        string = ""
+        if self.operator & self.AND:
+            string += "&"
+
+        operator = self.operator & (self.NOT | self.MATCH)
+        for k, v in self._comparison_conditions.items():
+            if operator == v:
+                string += k
+
+        plus = ""
+        for k, v in self._bitmask_flags.items():
+            if self.value & k:
+                string += plus + v
+                plus = "+"
+
+        return string
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv4Component.TYPE_PROTOCOL, addr_family.IP)
+class FlowSpecIPProtocol(_FlowSpecNumeric):
+    """IP Protocol for Flow Specification NLRI component
+
+    Set the IP protocol number at value.
+    """
+    COMPONENT_NAME = 'ip_proto'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv6Component.TYPE_NEXT_HEADER, addr_family.IP6)
+class FlowSpecNextHeader(_FlowSpecNumeric):
+    """Next Header value in IPv6 packets
+
+    Set the IP protocol number at value
+    """
+    COMPONENT_NAME = 'next_header'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv4Component.TYPE_PORT, addr_family.IP)
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv6Component.TYPE_PORT, addr_family.IP6)
+class FlowSpecPort(_FlowSpecNumeric):
+    """Port number for Flow Specification NLRI component
+
+    Set the source or destination TCP/UDP ports at value.
+    """
+    COMPONENT_NAME = 'port'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv4Component.TYPE_DESTINATION_PORT, addr_family.IP)
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv6Component.TYPE_DESTINATION_PORT, addr_family.IP6)
+class FlowSpecDestPort(_FlowSpecNumeric):
+    """Destination port number for Flow Specification NLRI component
+
+    Set the destination port of a TCP or UDP packet at value.
+    """
+    COMPONENT_NAME = 'dst_port'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv4Component.TYPE_SOURCE_PORT, addr_family.IP)
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv6Component.TYPE_SOURCE_PORT, addr_family.IP6)
+class FlowSpecSrcPort(_FlowSpecNumeric):
+    """Source port number for Flow Specification NLRI component
+
+    Set the source port of a TCP or UDP packet at value.
+    """
+    COMPONENT_NAME = 'src_port'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv4Component.TYPE_ICMP, addr_family.IP)
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv6Component.TYPE_ICMP, addr_family.IP6)
+class FlowSpecIcmpType(_FlowSpecNumeric):
+    """ICMP type for Flow Specification NLRI component
+
+    Set the type field of an ICMP packet at value.
+    """
+    COMPONENT_NAME = 'icmp_type'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv4Component.TYPE_ICMP_CODE, addr_family.IP)
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv6Component.TYPE_ICMP_CODE, addr_family.IP6)
+class FlowSpecIcmpCode(_FlowSpecNumeric):
+    """ICMP code Flow Specification NLRI component
+
+    Set the code field of an ICMP packet at value.
+    """
+    COMPONENT_NAME = 'icmp_code'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv4Component.TYPE_TCP_FLAGS, addr_family.IP)
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv6Component.TYPE_TCP_FLAGS, addr_family.IP6)
+class FlowSpecTCPFlags(_FlowSpecBitmask):
+    """TCP flags for Flow Specification NLRI component
+
+    Supported TCP flags are CWR, ECN, URGENT, ACK, PUSH, RST, SYN and FIN.
+    """
+    COMPONENT_NAME = 'tcp_flags'
+
+    # bitmask format
+    #  0    1    2    3    4    5    6    7
+    # +----+----+----+----+----+----+----+----+
+    # |CWR |ECN |URG |ACK |PSH |RST |SYN |FIN |
+    # +----+----+----+----+----+----+----+----+
+
+    CWR = 1 << 7
+    ECN = 1 << 6
+    URGENT = 1 << 5
+    ACK = 1 << 4
+    PUSH = 1 << 3
+    RST = 1 << 2
+    SYN = 1 << 1
+    FIN = 1 << 0
+
+    _bitmask_flags = collections.OrderedDict()
+    _bitmask_flags[SYN] = 'SYN'
+    _bitmask_flags[ACK] = 'ACK'
+    _bitmask_flags[FIN] = 'FIN'
+    _bitmask_flags[RST] = 'RST'
+    _bitmask_flags[PUSH] = 'PUSH'
+    _bitmask_flags[URGENT] = 'URGENT'
+    _bitmask_flags[ECN] = 'ECN'
+    _bitmask_flags[CWR] = 'CWR'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv4Component.TYPE_PACKET_LENGTH, addr_family.IP)
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv6Component.TYPE_PACKET_LENGTH, addr_family.IP6)
+class FlowSpecPacketLen(_FlowSpecNumeric):
+    """Packet length for Flow Specification NLRI component
+
+    Set the total IP packet length at value.
+    """
+    COMPONENT_NAME = 'packet_len'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv4Component.TYPE_DIFFSERV_CODE_POINT, addr_family.IP)
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv6Component.TYPE_DIFFSERV_CODE_POINT, addr_family.IP6)
+class FlowSpecDSCP(_FlowSpecNumeric):
+    """Diffserv Code Point for Flow Specification NLRI component
+
+    Set the 6-bit DSCP field at value. [RFC2474]
+    """
+    COMPONENT_NAME = 'dscp'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv4Component.TYPE_FRAGMENT, addr_family.IP)
+class FlowSpecFragment(_FlowSpecBitmask):
+    """Fragment for Flow Specification NLRI component
+
+    Set the bitmask for operand format at value.
+    The following values are supported.
+
+    ========== ===============================================
+    Attribute  Description
+    ========== ===============================================
+    LF         Last fragment
+    FF         First fragment
+    ISF        Is a fragment
+    DF         Don't fragment
+    ========== ===============================================
+    """
+    COMPONENT_NAME = 'fragment'
+
+    # bitmask format
+    #  0   1   2   3   4   5   6   7
+    # +---+---+---+---+---+---+---+---+
+    # |   Reserved    |LF |FF |IsF|DF |
+    # +---+---+---+---+---+---+---+---+
+
+    LF = 1 << 3
+    FF = 1 << 2
+    ISF = 1 << 1
+    DF = 1 << 0
+
+    _bitmask_flags = collections.OrderedDict()
+    _bitmask_flags[LF] = 'LF'
+    _bitmask_flags[FF] = 'FF'
+    _bitmask_flags[ISF] = 'ISF'
+    _bitmask_flags[DF] = 'DF'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv6Component.TYPE_FRAGMENT, addr_family.IP6)
+class FlowSpecIPv6Fragment(_FlowSpecBitmask):
+    """Fragment for Flow Specification for IPv6 NLRI component
+
+    ========== ===============================================
+    Attribute  Description
+    ========== ===============================================
+    LF         Last fragment
+    FF         First fragment
+    ISF        Is a fragment
+    ========== ===============================================
+    """
+    COMPONENT_NAME = 'fragment'
+
+    # bitmask format
+    #  0   1   2   3   4   5   6   7
+    # +---+---+---+---+---+---+---+---+
+    # |   Reserved    |LF |FF |IsF| 0 |
+    # +---+---+---+---+---+---+---+---+
+
+    LF = 1 << 3
+    FF = 1 << 2
+    ISF = 1 << 1
+
+    _bitmask_flags = collections.OrderedDict()
+    _bitmask_flags[LF] = 'LF'
+    _bitmask_flags[FF] = 'FF'
+    _bitmask_flags[ISF] = 'ISF'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecL2VPNComponent.TYPE_ETHER_TYPE, addr_family.L2VPN)
+class FlowSpecEtherType(_FlowSpecNumeric):
+    """Ethernet Type field in an Ethernet frame.
+
+    Set the 2 byte value of an Ethernet Type field at value.
+    """
+    COMPONENT_NAME = 'ether_type'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecL2VPNComponent.TYPE_SOURCE_MAC, addr_family.L2VPN)
+class FlowSpecSourceMac(_FlowSpecL2VPNPrefixBase):
+    """Source Mac Address.
+
+    Set the Mac Address at value.
+    """
+    COMPONENT_NAME = 'src_mac'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecL2VPNComponent.TYPE_DESTINATION_MAC, addr_family.L2VPN)
+class FlowSpecDestinationMac(_FlowSpecL2VPNPrefixBase):
+    """Destination Mac Address.
+
+    Set the Mac Address at value.
+    """
+    COMPONENT_NAME = 'dst_mac'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecL2VPNComponent.TYPE_LLC_DSAP, addr_family.L2VPN)
+class FlowSpecLLCDSAP(_FlowSpecNumeric):
+    """Destination SAP field in LLC header in an Ethernet frame.
+
+    Set the 2 byte value of an Destination SAP at value.
+    """
+    COMPONENT_NAME = 'llc_dsap'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecL2VPNComponent.TYPE_LLC_SSAP, addr_family.L2VPN)
+class FlowSpecLLCSSAP(_FlowSpecNumeric):
+    """Source SAP field in LLC header in an Ethernet frame.
+
+    Set the 2 byte value of an Source SAP at value.
+    """
+    COMPONENT_NAME = 'llc_ssap'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecL2VPNComponent.TYPE_LLC_CONTROL, addr_family.L2VPN)
+class FlowSpecLLCControl(_FlowSpecNumeric):
+    """Control field in LLC header in an Ethernet frame.
+
+    Set the Contorol field at value.
+    """
+    COMPONENT_NAME = 'llc_control'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecL2VPNComponent.TYPE_SNAP, addr_family.L2VPN)
+class FlowSpecSNAP(_FlowSpecNumeric):
+    """Sub-Network Access Protocol field in an Ethernet frame.
+
+    Set the 5 byte SNAP field at value.
+    """
+    COMPONENT_NAME = 'snap'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecL2VPNComponent.TYPE_VLAN_ID, addr_family.L2VPN)
+class FlowSpecVLANID(_FlowSpecNumeric):
+    """VLAN ID.
+
+    Set VLAN ID at value.
+    """
+    COMPONENT_NAME = 'vlan_id'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecL2VPNComponent.TYPE_VLAN_COS, addr_family.L2VPN)
+class FlowSpecVLANCoS(_FlowSpecNumeric):
+    """VLAN CoS Fields in an Ethernet frame.
+
+    Set the 3 bit CoS field at value.
+    """
+    COMPONENT_NAME = 'vlan_cos'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecL2VPNComponent.TYPE_INNER_VLAN_ID, addr_family.L2VPN)
+class FlowSpecInnerVLANID(_FlowSpecNumeric):
+    """Inner VLAN ID.
+
+    Set VLAN ID at value.
+    """
+    COMPONENT_NAME = 'inner_vlan_id'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecL2VPNComponent.TYPE_INNER_VLAN_COS, addr_family.L2VPN)
+class FlowSpecInnerVLANCoS(_FlowSpecNumeric):
+    """VLAN CoS Fields in an Inner Ethernet frame.
+
+    Set the 3 bit CoS field at value..
+    """
+    COMPONENT_NAME = 'inner_vlan_cos'
+
+
+@_FlowSpecComponentBase.register_type(
+    _FlowSpecIPv6Component.TYPE_FLOW_LABEL, addr_family.IP6)
+class FlowSpecIPv6FlowLabel(_FlowSpecNumeric):
+    COMPONENT_NAME = 'flow_label'
+
+
+@functools.total_ordering
+class RouteTargetMembershipNLRI(StringifyMixin):
+    """Route Target Membership NLRI.
+
+    Route Target membership NLRI is advertised in BGP UPDATE messages using
+    the MP_REACH_NLRI and MP_UNREACH_NLRI attributes.
+    """
+
+    ROUTE_FAMILY = RF_RTC_UC
+    DEFAULT_AS = '0:0'
+    DEFAULT_RT = '0:0'
+
+    def __init__(self, origin_as, route_target):
+        # If given is not default_as and default_rt
+        if not (origin_as is self.DEFAULT_AS and
+                route_target is self.DEFAULT_RT):
+            # We validate them
+            if (not self._is_valid_asn(origin_as) or
+                    not self._is_valid_ext_comm_attr(route_target)):
+                raise ValueError('Invalid params.')
+        self.origin_as = origin_as
+        self.route_target = route_target
+
+    def _is_valid_asn(self, asn):
+        """Returns True if the given AS number is Two or Four Octet."""
+        if isinstance(asn, six.integer_types) and 0 <= asn <= 0xffffffff:
+            return True
+        else:
+            return False
+
+    def _is_valid_ext_comm_attr(self, attr):
+        """Validates *attr* as string representation of RT or SOO.
+
+        Returns True if *attr* is as per our convention of RT or SOO, else
+        False. Our convention is to represent RT/SOO is a string with format:
+        *global_admin_part:local_admin_path*
+        """
+        is_valid = True
+
+        if not isinstance(attr, str):
+            is_valid = False
+        else:
+            first, second = attr.split(':')
+            try:
+                if '.' in first:
+                    socket.inet_aton(first)
+                else:
+                    int(first)
+                    int(second)
+            except (ValueError, socket.error):
+                is_valid = False
+
+        return is_valid
+
+    @property
+    def formatted_nlri_str(self):
+        return "%s:%s" % (self.origin_as, self.route_target)
+
+    def is_default_rtnlri(self):
+        if (self._origin_as is self.DEFAULT_AS and
+                self._route_target is self.DEFAULT_RT):
+            return True
+        return False
+
+    def __lt__(self, other):
+        return ((self.origin_as, self.route_target) <
+                (other.origin_as, other.route_target))
+
+    def __eq__(self, other):
+        return ((self.origin_as, self.route_target) ==
+                (other.origin_as, other.route_target))
+
+    def __hash__(self):
+        return hash((self.origin_as, self.route_target))
+
+    @classmethod
+    def parser(cls, buf):
+        idx = 0
+
+        # Extract origin AS.
+        origin_as, = struct.unpack_from('!I', buf, idx)
+        idx += 4
+
+        # Extract route target.
+        route_target = _ExtendedCommunity(buf[idx:])
+        return cls(origin_as, route_target)
+
+    def serialize(self):
+        rt_nlri = b''
+        if not self.is_default_rtnlri():
+            rt_nlri += struct.pack('!I', self.origin_as)
+            # Encode route target
+            rt_nlri += self.route_target.serialize()
+
+        # RT Nlri is 12 octets
+        return struct.pack('B', (8 * 12)) + rt_nlri
+
+
+def _addr_class_key(route_family):
+    return route_family.afi, route_family.safi
+
+
+_ADDR_CLASSES = {
+    _addr_class_key(RF_IPv4_UC): IPAddrPrefix,
+    _addr_class_key(RF_IPv6_UC): IP6AddrPrefix,
+    _addr_class_key(RF_IPv4_MPLS): LabelledIPAddrPrefix,
+    _addr_class_key(RF_IPv6_MPLS): LabelledIP6AddrPrefix,
+    _addr_class_key(RF_IPv4_VPN): LabelledVPNIPAddrPrefix,
+    _addr_class_key(RF_IPv6_VPN): LabelledVPNIP6AddrPrefix,
+    _addr_class_key(RF_L2_EVPN): EvpnNLRI,
+    _addr_class_key(RF_IPv4_FLOWSPEC): FlowSpecIPv4NLRI,
+    _addr_class_key(RF_IPv6_FLOWSPEC): FlowSpecIPv6NLRI,
+    _addr_class_key(RF_VPNv4_FLOWSPEC): FlowSpecVPNv4NLRI,
+    _addr_class_key(RF_VPNv6_FLOWSPEC): FlowSpecVPNv6NLRI,
+    _addr_class_key(RF_L2VPN_FLOWSPEC): FlowSpecL2VPNNLRI,
+    _addr_class_key(RF_RTC_UC): RouteTargetMembershipNLRI,
+}
+
+
+def _get_addr_class(afi, safi):
+    try:
+        return _ADDR_CLASSES[(afi, safi)]
+    except KeyError:
+        return _BinAddrPrefix
+
+
+class _OptParam(StringifyMixin, TypeDisp, _Value):
+    _PACK_STR = '!BB'  # type, length
+
+    def __init__(self, type_, value=None, length=None):
+        if type_ is None:
+            type_ = self._rev_lookup_type(self.__class__)
+        self.type = type_
+        self.length = length
+        if value is not None:
+            self.value = value
+
+    @classmethod
+    def parser(cls, buf):
+        (type_, length) = struct.unpack_from(cls._PACK_STR,
+                                             six.binary_type(buf))
+        rest = buf[struct.calcsize(cls._PACK_STR):]
+        value = bytes(rest[:length])
+        rest = rest[length:]
+        subcls = cls._lookup_type(type_)
+        caps = subcls.parse_value(value)
+        if not isinstance(caps, list):
+            caps = [subcls(type_=type_, length=length, **caps[0])]
+        return caps, rest
+
+    def serialize(self):
+        # fixup
+        value = self.serialize_value()
+        self.length = len(value)
+
+        buf = bytearray()
+        msg_pack_into(self._PACK_STR, buf, 0, self.type, self.length)
+        return buf + value
+
+
+@_OptParam.register_unknown_type()
+class BGPOptParamUnknown(_OptParam):
+    @classmethod
+    def parse_value(cls, buf):
+        return {
+            'value': buf
+        }, cls
+
+    def serialize_value(self):
+        return self.value
+
+
+@_OptParam.register_type(BGP_OPT_CAPABILITY)
+class _OptParamCapability(_OptParam, TypeDisp):
+    _CAP_HDR_PACK_STR = '!BB'
+
+    def __init__(self, cap_code=None, cap_value=None, cap_length=None,
+                 type_=None, length=None):
+        super(_OptParamCapability, self).__init__(type_=BGP_OPT_CAPABILITY,
+                                                  length=length)
+        if cap_code is None:
+            cap_code = self._rev_lookup_type(self.__class__)
+        self.cap_code = cap_code
+        if cap_value is not None:
+            self.cap_value = cap_value
+        if cap_length is not None:
+            self.cap_length = cap_length
+
+    @classmethod
+    def parse_value(cls, buf):
+        caps = []
+        while len(buf) > 0:
+            (code, length) = struct.unpack_from(cls._CAP_HDR_PACK_STR,
+                                                six.binary_type(buf))
+            value = buf[struct.calcsize(cls._CAP_HDR_PACK_STR):]
+            buf = buf[length + 2:]
+            kwargs = {
+                'cap_code': code,
+                'cap_length': length,
+            }
+            subcls = cls._lookup_type(code)
+            kwargs.update(subcls.parse_cap_value(value))
+            caps.append(subcls(type_=BGP_OPT_CAPABILITY, length=length + 2,
+                               **kwargs))
+        return caps
+
+    def serialize_value(self):
+        # fixup
+        cap_value = self.serialize_cap_value()
+        self.cap_length = len(cap_value)
+
+        buf = bytearray()
+        msg_pack_into(self._CAP_HDR_PACK_STR, buf, 0, self.cap_code,
+                      self.cap_length)
+        return buf + cap_value
+
+
+class _OptParamEmptyCapability(_OptParamCapability):
+    @classmethod
+    def parse_cap_value(cls, buf):
+        return {}
+
+    def serialize_cap_value(self):
+        return bytearray()
+
+
+@_OptParamCapability.register_unknown_type()
+class BGPOptParamCapabilityUnknown(_OptParamCapability):
+    @classmethod
+    def parse_cap_value(cls, buf):
+        return {'cap_value': buf}
+
+    def serialize_cap_value(self):
+        return self.cap_value
+
+
+@_OptParamCapability.register_type(BGP_CAP_ROUTE_REFRESH)
+class BGPOptParamCapabilityRouteRefresh(_OptParamEmptyCapability):
+    pass
+
+
+@_OptParamCapability.register_type(BGP_CAP_ROUTE_REFRESH_CISCO)
+class BGPOptParamCapabilityCiscoRouteRefresh(_OptParamEmptyCapability):
+    pass
+
+
+@_OptParamCapability.register_type(BGP_CAP_ENHANCED_ROUTE_REFRESH)
+class BGPOptParamCapabilityEnhancedRouteRefresh(_OptParamEmptyCapability):
+    pass
+
+
+@_OptParamCapability.register_type(BGP_CAP_GRACEFUL_RESTART)
+class BGPOptParamCapabilityGracefulRestart(_OptParamCapability):
+    _CAP_PACK_STR = "!H"
+
+    def __init__(self, flags, time, tuples, **kwargs):
+        super(BGPOptParamCapabilityGracefulRestart, self).__init__(**kwargs)
+        self.flags = flags
+        self.time = time
+        self.tuples = tuples
+
+    @classmethod
+    def parse_cap_value(cls, buf):
+        (restart, ) = struct.unpack_from(cls._CAP_PACK_STR,
+                                         six.binary_type(buf))
+        buf = buf[2:]
+        l = []
+        while len(buf) >= 4:
+            l.append(struct.unpack_from("!HBB", buf))
+            buf = buf[4:]
+        return {'flags': restart >> 12, 'time': restart & 0xfff, 'tuples': l}
+
+    def serialize_cap_value(self):
+        buf = bytearray()
+        msg_pack_into(self._CAP_PACK_STR, buf, 0, self.flags << 12 | self.time)
+        offset = 2
+        for i in self.tuples:
+            afi, safi, flags = i
+            msg_pack_into("!HBB", buf, offset, afi, safi, flags)
+            offset += 4
+        return buf
+
+
+@_OptParamCapability.register_type(BGP_CAP_FOUR_OCTET_AS_NUMBER)
+class BGPOptParamCapabilityFourOctetAsNumber(_OptParamCapability):
+    _CAP_PACK_STR = '!I'
+
+    def __init__(self, as_number, **kwargs):
+        super(BGPOptParamCapabilityFourOctetAsNumber, self).__init__(**kwargs)
+        self.as_number = as_number
+
+    @classmethod
+    def parse_cap_value(cls, buf):
+        (as_number, ) = struct.unpack_from(cls._CAP_PACK_STR,
+                                           six.binary_type(buf))
+        return {'as_number': as_number}
+
+    def serialize_cap_value(self):
+        buf = bytearray()
+        msg_pack_into(self._CAP_PACK_STR, buf, 0, self.as_number)
+        return buf
+
+
+@_OptParamCapability.register_type(BGP_CAP_MULTIPROTOCOL)
+class BGPOptParamCapabilityMultiprotocol(_OptParamCapability):
+    _CAP_PACK_STR = '!HBB'  # afi, reserved, safi
+
+    def __init__(self, afi, safi, reserved=0, **kwargs):
+        super(BGPOptParamCapabilityMultiprotocol, self).__init__(**kwargs)
+        self.afi = afi
+        self.reserved = reserved
+        self.safi = safi
+
+    @classmethod
+    def parse_cap_value(cls, buf):
+        (afi, reserved, safi,) = struct.unpack_from(cls._CAP_PACK_STR,
+                                                    six.binary_type(buf))
+        return {
+            'afi': afi,
+            'reserved': reserved,
+            'safi': safi,
+        }
+
+    def serialize_cap_value(self):
+        # fixup
+        self.reserved = 0
+
+        buf = bytearray()
+        msg_pack_into(self._CAP_PACK_STR, buf, 0,
+                      self.afi, self.reserved, self.safi)
+        return buf
+
+
+@_OptParamCapability.register_type(BGP_CAP_CARRYING_LABEL_INFO)
+class BGPOptParamCapabilityCarryingLabelInfo(_OptParamEmptyCapability):
+    pass
+
+
+class BGPWithdrawnRoute(IPAddrPrefix):
+    pass
+
+
+class _PathAttribute(StringifyMixin, TypeDisp, _Value):
+    _PACK_STR = '!BB'  # flags, type
+    _PACK_STR_LEN = '!B'  # length
+    _PACK_STR_EXT_LEN = '!H'  # length w/ BGP_ATTR_FLAG_EXTENDED_LENGTH
+    _ATTR_FLAGS = None
+
+    def __init__(self, value=None, flags=0, type_=None, length=None):
+        if type_ is None:
+            type_ = self._rev_lookup_type(self.__class__)
+        self.flags = flags
+        self.type = type_
+        self.length = length
+        if value is not None:
+            self.value = value
+
+    @classmethod
+    def parser(cls, buf):
+        (flags, type_) = struct.unpack_from(cls._PACK_STR,
+                                            six.binary_type(buf))
+        rest = buf[struct.calcsize(cls._PACK_STR):]
+        if (flags & BGP_ATTR_FLAG_EXTENDED_LENGTH) != 0:
+            len_pack_str = cls._PACK_STR_EXT_LEN
+        else:
+            len_pack_str = cls._PACK_STR_LEN
+        (length,) = struct.unpack_from(len_pack_str, six.binary_type(rest))
+        rest = rest[struct.calcsize(len_pack_str):]
+        value = bytes(rest[:length])
+        rest = rest[length:]
+        subcls = cls._lookup_type(type_)
+        return subcls(flags=flags, type_=type_, length=length,
+                      **subcls.parse_value(value)), rest
+
+    def serialize(self):
+        # fixup
+        if self._ATTR_FLAGS is not None:
+            self.flags = (
+                self.flags
+                & ~(BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE)
+                | self._ATTR_FLAGS)
+        value = self.serialize_value()
+        self.length = len(value)
+        if self.flags & BGP_ATTR_FLAG_EXTENDED_LENGTH:
+            len_pack_str = self._PACK_STR_EXT_LEN
+        elif self.length > 255:
+            self.flags |= BGP_ATTR_FLAG_EXTENDED_LENGTH
+            len_pack_str = self._PACK_STR_EXT_LEN
+        else:
+            self.flags &= ~BGP_ATTR_FLAG_EXTENDED_LENGTH
+            len_pack_str = self._PACK_STR_LEN
+
+        buf = bytearray()
+        msg_pack_into(self._PACK_STR, buf, 0, self.flags, self.type)
+        msg_pack_into(len_pack_str, buf, len(buf), self.length)
+        return buf + value
+
+
+@_PathAttribute.register_unknown_type()
+class BGPPathAttributeUnknown(_PathAttribute):
+    @classmethod
+    def parse_value(cls, buf):
+        return {
+            'value': buf
+        }
+
+    def serialize_value(self):
+        return self.value
+
+
+class _PathAttributeUint32(_PathAttribute):
+    _VALUE_PACK_STR = '!I'
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_ORIGIN)
+class BGPPathAttributeOrigin(_PathAttribute):
+    _VALUE_PACK_STR = '!B'
+    _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
+
+
+class _BGPPathAttributeAsPathCommon(_PathAttribute):
+    _AS_SET = 1
+    _AS_SEQUENCE = 2
+    _SEG_HDR_PACK_STR = '!BB'
+    _AS_PACK_STR = None
+    _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
+
+    def __init__(self, value, as_pack_str=None, flags=0, type_=None,
+                 length=None):
+        super(_BGPPathAttributeAsPathCommon, self).__init__(value=value,
+                                                            flags=flags,
+                                                            type_=type_,
+                                                            length=length)
+        if as_pack_str:
+            self._AS_PACK_STR = as_pack_str
+
+    @property
+    def path_seg_list(self):
+        return copy.deepcopy(self.value)
+
+    def get_as_path_len(self):
+        count = 0
+        for seg in self.value:
+            if isinstance(seg, list):
+                # Segment type 2 stored in list and all AS counted.
+                count += len(seg)
+            else:
+                # Segment type 1 stored in set and count as one.
+                count += 1
+
+        return count
+
+    def has_local_as(self, local_as, max_count=0):
+        """Check if *local_as* is already present on path list."""
+        _count = 0
+        for as_path_seg in self.value:
+            _count += list(as_path_seg).count(local_as)
+        return _count > max_count
+
+    def has_matching_leftmost(self, remote_as):
+        """Check if leftmost AS matches *remote_as*."""
+        if not self.value or not remote_as:
+            return False
+
+        leftmost_seg = self.path_seg_list[0]
+        if leftmost_seg and leftmost_seg[0] == remote_as:
+            return True
+
+        return False
+
+    @classmethod
+    def _is_valid_16bit_as_path(cls, buf):
+
+        two_byte_as_size = struct.calcsize('!H')
+
+        while buf:
+            (type_, num_as) = struct.unpack_from(cls._SEG_HDR_PACK_STR,
+                                                 six.binary_type(buf))
+
+            if type_ is not cls._AS_SET and type_ is not cls._AS_SEQUENCE:
+                return False
+
+            buf = buf[struct.calcsize(cls._SEG_HDR_PACK_STR):]
+
+            if len(buf) < num_as * two_byte_as_size:
+                return False
+
+            buf = buf[num_as * two_byte_as_size:]
+
+        return True
+
+    @classmethod
+    def parse_value(cls, buf):
+        result = []
+
+        if cls._is_valid_16bit_as_path(buf):
+            as_pack_str = '!H'
+        else:
+            as_pack_str = '!I'
+
+        while buf:
+            (type_, num_as) = struct.unpack_from(cls._SEG_HDR_PACK_STR,
+                                                 six.binary_type(buf))
+            buf = buf[struct.calcsize(cls._SEG_HDR_PACK_STR):]
+            l = []
+            for _ in range(0, num_as):
+                (as_number,) = struct.unpack_from(as_pack_str,
+                                                  six.binary_type(buf))
+                buf = buf[struct.calcsize(as_pack_str):]
+                l.append(as_number)
+            if type_ == cls._AS_SET:
+                result.append(set(l))
+            elif type_ == cls._AS_SEQUENCE:
+                result.append(l)
+            else:
+                # protocol error
+                raise struct.error('Unsupported segment type: %s' % type_)
+        return {
+            'value': result,
+            'as_pack_str': as_pack_str,
+        }
+
+    def serialize_value(self):
+        buf = bytearray()
+        offset = 0
+        for e in self.value:
+            if isinstance(e, set):
+                type_ = self._AS_SET
+            elif isinstance(e, list):
+                type_ = self._AS_SEQUENCE
+            else:
+                raise struct.error(
+                    'Element of %s.value must be of type set or list' %
+                    self.__class__.__name__)
+            l = list(e)
+            num_as = len(l)
+            if num_as == 0:
+                continue
+            msg_pack_into(self._SEG_HDR_PACK_STR, buf, offset, type_, num_as)
+            offset += struct.calcsize(self._SEG_HDR_PACK_STR)
+            for i in l:
+                msg_pack_into(self._AS_PACK_STR, buf, offset, i)
+                offset += struct.calcsize(self._AS_PACK_STR)
+        return buf
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_AS_PATH)
+class BGPPathAttributeAsPath(_BGPPathAttributeAsPathCommon):
+    # XXX depends on negotiated capability, AS numbers can be 32 bit.
+    # while wireshark seems to attempt auto-detect, it seems that
+    # there's no way to detect it reliably.  for example, the
+    # following byte sequence can be interpreted in two ways.
+    #   01 02 99 88 77 66 02 01 55 44
+    #   AS_SET num=2 9988 7766 AS_SEQUENCE num=1 5544
+    #   AS_SET num=2 99887766 02015544
+    # we first check whether AS path can be parsed in 16bit format and if
+    # it fails, we try to parse as 32bit
+    _AS_PACK_STR = '!H'
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_AS4_PATH)
+class BGPPathAttributeAs4Path(_BGPPathAttributeAsPathCommon):
+    _AS_PACK_STR = '!I'
+    _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL
+
+    @classmethod
+    def _is_valid_16bit_as_path(cls, buf):
+        return False
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_NEXT_HOP)
+class BGPPathAttributeNextHop(_PathAttribute):
+    _VALUE_PACK_STR = '!4s'
+    _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
+    _TYPE = {
+        'ascii': [
+            'value'
+        ]
+    }
+
+    @classmethod
+    def parse_value(cls, buf):
+        (ip_addr,) = struct.unpack_from(cls._VALUE_PACK_STR,
+                                        six.binary_type(buf))
+        return {
+            'value': addrconv.ipv4.bin_to_text(ip_addr),
+        }
+
+    def serialize_value(self):
+        buf = bytearray()
+        msg_pack_into(self._VALUE_PACK_STR, buf, 0,
+                      addrconv.ipv4.text_to_bin(self.value))
+        return buf
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+class BGPPathAttributeMultiExitDisc(_PathAttributeUint32):
+    _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_LOCAL_PREF)
+class BGPPathAttributeLocalPref(_PathAttributeUint32):
+    _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_ATOMIC_AGGREGATE)
+class BGPPathAttributeAtomicAggregate(_PathAttribute):
+    _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
+
+    @classmethod
+    def parse_value(cls, buf):
+        return {}
+
+    def serialize_value(self):
+        return b''
+
+
+class _BGPPathAttributeAggregatorCommon(_PathAttribute):
+    _VALUE_PACK_STR = None
+    _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE
+    _TYPE = {
+        'ascii': [
+            'addr'
+        ]
+    }
+
+    def __init__(self, as_number, addr, flags=0, type_=None, length=None):
+        super(_BGPPathAttributeAggregatorCommon, self).__init__(flags=flags,
+                                                                type_=type_,
+                                                                length=length)
+        self.as_number = as_number
+        self.addr = addr
+
+    @classmethod
+    def parse_value(cls, buf):
+        (as_number, addr) = struct.unpack_from(cls._VALUE_PACK_STR,
+                                               six.binary_type(buf))
+        return {
+            'as_number': as_number,
+            'addr': addrconv.ipv4.bin_to_text(addr),
+        }
+
+    def serialize_value(self):
+        buf = bytearray()
+        msg_pack_into(self._VALUE_PACK_STR, buf, 0, self.as_number,
+                      addrconv.ipv4.text_to_bin(self.addr))
+        return buf
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_AGGREGATOR)
+class BGPPathAttributeAggregator(_BGPPathAttributeAggregatorCommon):
+    # Note: AS numbers can be Two-Octet or Four-Octet.
+    # This class would detect it by the value length field.
+    # For example,
+    # - if the value field length is 6 (='!H4s'), AS number should
+    #   be Two-Octet.
+    # - else if the length is 8 (='!I4s'), AS number should be Four-Octet.
+    _TWO_OCTET_VALUE_PACK_STR = '!H4s'
+    _FOUR_OCTET_VALUE_PACK_STR = '!I4s'
+    _VALUE_PACK_STR = _TWO_OCTET_VALUE_PACK_STR  # Two-Octet by default
+    _FOUR_OCTET_VALUE_SIZE = struct.calcsize(_FOUR_OCTET_VALUE_PACK_STR)
+
+    @classmethod
+    def parse_value(cls, buf):
+        if len(buf) == cls._FOUR_OCTET_VALUE_SIZE:
+            cls._VALUE_PACK_STR = cls._FOUR_OCTET_VALUE_PACK_STR
+        return super(BGPPathAttributeAggregator, cls).parse_value(buf)
+
+    def serialize_value(self):
+        if self.as_number > 0xffff:
+            self._VALUE_PACK_STR = self._FOUR_OCTET_VALUE_PACK_STR
+        return super(BGPPathAttributeAggregator, self).serialize_value()
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_AS4_AGGREGATOR)
+class BGPPathAttributeAs4Aggregator(_BGPPathAttributeAggregatorCommon):
+    _VALUE_PACK_STR = '!I4s'
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_COMMUNITIES)
+class BGPPathAttributeCommunities(_PathAttribute):
+    _VALUE_PACK_STR = '!I'
+    _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE
+
+    # String constants of well-known-communities
+    NO_EXPORT = int('0xFFFFFF01', 16)
+    NO_ADVERTISE = int('0xFFFFFF02', 16)
+    NO_EXPORT_SUBCONFED = int('0xFFFFFF03', 16)
+    WELL_KNOW_COMMUNITIES = (NO_EXPORT, NO_ADVERTISE, NO_EXPORT_SUBCONFED)
+
+    def __init__(self, communities,
+                 flags=0, type_=None, length=None):
+        super(BGPPathAttributeCommunities, self).__init__(flags=flags,
+                                                          type_=type_,
+                                                          length=length)
+        self.communities = communities
+
+    @classmethod
+    def parse_value(cls, buf):
+        rest = buf
+        communities = []
+        elem_size = struct.calcsize(cls._VALUE_PACK_STR)
+        while len(rest) >= elem_size:
+            (comm, ) = struct.unpack_from(cls._VALUE_PACK_STR,
+                                          six.binary_type(rest))
+            communities.append(comm)
+            rest = rest[elem_size:]
+        return {
+            'communities': communities,
+        }
+
+    def serialize_value(self):
+        buf = bytearray()
+        for comm in self.communities:
+            bincomm = bytearray()
+            msg_pack_into(self._VALUE_PACK_STR, bincomm, 0, comm)
+            buf += bincomm
+        return buf
+
+    @staticmethod
+    def is_no_export(comm_attr):
+        """Returns True if given value matches well-known community NO_EXPORT
+         attribute value.
+         """
+        return comm_attr == BGPPathAttributeCommunities.NO_EXPORT
+
+    @staticmethod
+    def is_no_advertise(comm_attr):
+        """Returns True if given value matches well-known community
+        NO_ADVERTISE attribute value.
+        """
+        return comm_attr == BGPPathAttributeCommunities.NO_ADVERTISE
+
+    @staticmethod
+    def is_no_export_subconfed(comm_attr):
+        """Returns True if given value matches well-known community
+         NO_EXPORT_SUBCONFED attribute value.
+         """
+        return comm_attr == BGPPathAttributeCommunities.NO_EXPORT_SUBCONFED
+
+    def has_comm_attr(self, attr):
+        """Returns True if given community attribute is present."""
+
+        for comm_attr in self.communities:
+            if comm_attr == attr:
+                return True
+
+        return False
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_ORIGINATOR_ID)
+class BGPPathAttributeOriginatorId(_PathAttribute):
+    # ORIGINATOR_ID is a new optional, non-transitive BGP attribute of Type
+    # code 9. This attribute is 4 bytes long and it will be created by an
+    # RR in reflecting a route.
+    _VALUE_PACK_STR = '!4s'
+    _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
+    _TYPE = {
+        'asciilist': [
+            'value'
+        ]
+    }
+
+    @classmethod
+    def parse_value(cls, buf):
+        (originator_id,) = struct.unpack_from(cls._VALUE_PACK_STR,
+                                              six.binary_type(buf))
+        return {
+            'value': addrconv.ipv4.bin_to_text(originator_id),
+        }
+
+    def serialize_value(self):
+        buf = bytearray()
+        msg_pack_into(self._VALUE_PACK_STR, buf, 0,
+                      addrconv.ipv4.text_to_bin(self.value))
+        return buf
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_CLUSTER_LIST)
+class BGPPathAttributeClusterList(_PathAttribute):
+    # CLUSTER_LIST is a new, optional, non-transitive BGP attribute of Type
+    # code 10. It is a sequence of CLUSTER_ID values representing the
+    # reflection path that the route has passed.
+    _VALUE_PACK_STR = '!4s'
+    _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
+    _TYPE = {
+        'ascii': [
+            'value'
+        ]
+    }
+
+    @classmethod
+    def parse_value(cls, buf):
+        rest = buf
+        cluster_list = []
+        elem_size = struct.calcsize(cls._VALUE_PACK_STR)
+        while len(rest) >= elem_size:
+            (cluster_id, ) = struct.unpack_from(
+                cls._VALUE_PACK_STR, six.binary_type(rest))
+            cluster_list.append(addrconv.ipv4.bin_to_text(cluster_id))
+            rest = rest[elem_size:]
+        return {
+            'value': cluster_list,
+        }
+
+    def serialize_value(self):
+        buf = bytearray()
+        offset = 0
+        for cluster_id in self.value:
+            msg_pack_into(
+                self._VALUE_PACK_STR,
+                buf,
+                offset,
+                addrconv.ipv4.text_to_bin(cluster_id))
+            offset += struct.calcsize(self._VALUE_PACK_STR)
+        return buf
+
+
+# Extended Communities
+# RFC 4360
+# RFC 5668
+# IANA registry:
+# https://www.iana.org/assignments/bgp-extended-communities/
+# bgp-extended-communities.xml
+#
+# type
+# high  low
+# 00    sub-type    Two-Octet AS Specific Extended Community (transitive)
+# 40    sub-type    Two-Octet AS Specific Extended Community
+#                   payload:
+#                     2 byte Global Administrator (AS number)
+#                     4 byte Local Administrator (defined by sub-type)
+# 01    sub-type    IPv4 Address Specific Extended Community (transitive)
+# 41    sub-type    IPv4 Address Specific Extended Community
+#                   payload:
+#                     4 byte Global Administrator (IPv4 address)
+#                     2 byte Local Administrator (defined by sub-type)
+# 03    sub-type    Opaque Extended Community (transitive)
+# 43    sub-type    Opaque Extended Community
+#                   payload:
+#                     6 byte opaque value (defined by sub-type)
+#
+# 00    02          Route Target Community (two-octet AS specific)
+# 01    02          Route Target Community (IPv4 address specific)
+# 02    02          Route Target Community (four-octet AS specific, RFC 5668)
+# 00    03          Route Origin Community (two-octet AS specific)
+# 01    03          Route Origin Community (IPv4 address specific)
+# 02    03          Route Origin Community (four-octet AS specific, RFC 5668)
+# 06    sub-type    Ethernet VPN Extended Community (RFC 7432)
+# 80    sub-type    Flow Specification Extended Community (RFC 5575)
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_EXTENDED_COMMUNITIES)
+class BGPPathAttributeExtendedCommunities(_PathAttribute):
+    _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE
+    _class_prefixes = ['BGP']
+
+    def __init__(self, communities,
+                 flags=0, type_=None, length=None):
+        super(BGPPathAttributeExtendedCommunities,
+              self).__init__(flags=flags,
+                             type_=type_,
+                             length=length)
+        self.communities = communities
+
+    @classmethod
+    def parse_value(cls, buf):
+        rest = buf
+        communities = []
+        while rest:
+            comm, rest = _ExtendedCommunity.parse(rest)
+            communities.append(comm)
+        return {
+            'communities': communities,
+        }
+
+    def serialize_value(self):
+        buf = bytearray()
+        for comm in self.communities:
+            buf += comm.serialize()
+        return buf
+
+    def _community_list(self, subtype):
+        _list = []
+        for comm in (c for c in self.communities
+                     if hasattr(c, "subtype") and c.subtype == subtype):
+            if comm.type == 0 or comm.type == 2:
+                _list.append('%d:%d' % (comm.as_number,
+                                        comm.local_administrator))
+            elif comm.type == 1:
+                _list.append('%s:%d' % (comm.ipv4_address,
+                                        comm.local_administrator))
+        return _list
+
+    @property
+    def rt_list(self):
+        return self._community_list(2)
+
+    @property
+    def soo_list(self):
+        return self._community_list(3)
+
+
+class _ExtendedCommunity(StringifyMixin, TypeDisp, _Value):
+    _PACK_STR = '!B7s'  # type high (+ type low), value
+    _PACK_STR_SIZE = struct.calcsize(_PACK_STR)
+    _SUBTYPE_PACK_STR = '!B'  # subtype
+    IANA_AUTHORITY = 0x80
+    TRANSITIVE = 0x40
+    _TYPE_HIGH_MASK = ~TRANSITIVE
+
+    TWO_OCTET_AS_SPECIFIC = 0x00
+    IPV4_ADDRESS_SPECIFIC = 0x01
+    FOUR_OCTET_AS_SPECIFIC = 0x02
+    OPAQUE = 0x03
+    SUBTYPE_ENCAPSULATION = 0x0c
+    ENCAPSULATION = (OPAQUE, SUBTYPE_ENCAPSULATION)
+    EVPN = 0x06
+    SUBTYPE_EVPN_MAC_MOBILITY = 0x00
+    SUBTYPE_EVPN_ESI_LABEL = 0x01
+    SUBTYPE_EVPN_ES_IMPORT_RT = 0x02
+    EVPN_MAC_MOBILITY = (EVPN, SUBTYPE_EVPN_MAC_MOBILITY)
+    EVPN_ESI_LABEL = (EVPN, SUBTYPE_EVPN_ESI_LABEL)
+    EVPN_ES_IMPORT_RT = (EVPN, SUBTYPE_EVPN_ES_IMPORT_RT)
+    FLOWSPEC = 0x80
+    FLOWSPEC_L2VPN = 0x08
+    SUBTYPE_FLOWSPEC_TRAFFIC_RATE = 0x06
+    SUBTYPE_FLOWSPEC_TRAFFIC_ACTION = 0x07
+    SUBTYPE_FLOWSPEC_REDIRECT = 0x08
+    SUBTYPE_FLOWSPEC_TRAFFIC_REMARKING = 0x09
+    SUBTYPE_FLOWSPEC_VLAN_ACTION = 0x0a
+    SUBTYPE_FLOWSPEC_TPID_ACTION = 0x0b
+    FLOWSPEC_TRAFFIC_RATE = (FLOWSPEC, SUBTYPE_FLOWSPEC_TRAFFIC_RATE)
+    FLOWSPEC_TRAFFIC_ACTION = (FLOWSPEC, SUBTYPE_FLOWSPEC_TRAFFIC_ACTION)
+    FLOWSPEC_REDIRECT = (FLOWSPEC, SUBTYPE_FLOWSPEC_REDIRECT)
+    FLOWSPEC_TRAFFIC_REMARKING = (FLOWSPEC, SUBTYPE_FLOWSPEC_TRAFFIC_REMARKING)
+    FLOWSPEC_VLAN_ACTION = (FLOWSPEC_L2VPN, SUBTYPE_FLOWSPEC_VLAN_ACTION)
+    FLOWSPEC_TPID_ACTION = (FLOWSPEC_L2VPN, SUBTYPE_FLOWSPEC_TPID_ACTION)
+
+    def __init__(self, type_=None):
+        if type_ is None:
+            type_ = self._rev_lookup_type(self.__class__)
+            if isinstance(type_, (tuple, list)):
+                type_ = type_[0]
+        self.type = type_
+
+    @classmethod
+    def parse_subtype(cls, buf):
+        (subtype,) = struct.unpack_from(cls._SUBTYPE_PACK_STR, buf)
+        return subtype
+
+    @classmethod
+    def parse(cls, buf):
+        (type_, value) = struct.unpack_from(cls._PACK_STR, buf)
+        rest = buf[cls._PACK_STR_SIZE:]
+        type_low = type_ & cls._TYPE_HIGH_MASK
+        subtype = cls.parse_subtype(value)
+        subcls = cls._lookup_type((type_low, subtype))
+        if subcls == cls._UNKNOWN_TYPE:
+            subcls = cls._lookup_type(type_low)
+        return subcls(type_=type_, **subcls.parse_value(value)), rest
+
+    def serialize(self):
+        return struct.pack(self._PACK_STR, self.type,
+                           self.serialize_value())
+
+
+@_ExtendedCommunity.register_type(_ExtendedCommunity.TWO_OCTET_AS_SPECIFIC)
+class BGPTwoOctetAsSpecificExtendedCommunity(_ExtendedCommunity):
+    _VALUE_PACK_STR = '!BHI'  # sub type, as number, local adm
+    _VALUE_FIELDS = ['subtype', 'as_number', 'local_administrator']
+
+    def __init__(self, **kwargs):
+        super(BGPTwoOctetAsSpecificExtendedCommunity, self).__init__()
+        self.do_init(BGPTwoOctetAsSpecificExtendedCommunity, self, kwargs)
+
+
+@_ExtendedCommunity.register_type(_ExtendedCommunity.IPV4_ADDRESS_SPECIFIC)
+class BGPIPv4AddressSpecificExtendedCommunity(_ExtendedCommunity):
+    _VALUE_PACK_STR = '!B4sH'  # sub type, IPv4 address, local adm
+    _VALUE_FIELDS = ['subtype', 'ipv4_address', 'local_administrator']
+    _TYPE = {
+        'ascii': [
+            'ipv4_address'
+        ]
+    }
+
+    def __init__(self, **kwargs):
+        super(BGPIPv4AddressSpecificExtendedCommunity, self).__init__()
+        self.do_init(BGPIPv4AddressSpecificExtendedCommunity, self, kwargs)
+
+    @classmethod
+    def parse_value(cls, buf):
+        d_ = super(BGPIPv4AddressSpecificExtendedCommunity,
+                   cls).parse_value(buf)
+        d_['ipv4_address'] = addrconv.ipv4.bin_to_text(d_['ipv4_address'])
+        return d_
+
+    def serialize_value(self):
+        return struct.pack(self._VALUE_PACK_STR, self.subtype,
+                           addrconv.ipv4.text_to_bin(self.ipv4_address),
+                           self.local_administrator)
+
+
+@_ExtendedCommunity.register_type(_ExtendedCommunity.FOUR_OCTET_AS_SPECIFIC)
+class BGPFourOctetAsSpecificExtendedCommunity(_ExtendedCommunity):
+    _VALUE_PACK_STR = '!BIH'  # sub type, as number, local adm
+    _VALUE_FIELDS = ['subtype', 'as_number', 'local_administrator']
+
+    def __init__(self, **kwargs):
+        super(BGPFourOctetAsSpecificExtendedCommunity, self).__init__()
+        self.do_init(BGPFourOctetAsSpecificExtendedCommunity, self, kwargs)
+
+
+@_ExtendedCommunity.register_type(_ExtendedCommunity.OPAQUE)
+class BGPOpaqueExtendedCommunity(_ExtendedCommunity):
+    _VALUE_PACK_STR = '!B6s'
+    _VALUE_FIELDS = ['subtype', 'opaque']
+
+    def __init__(self, **kwargs):
+        super(BGPOpaqueExtendedCommunity, self).__init__()
+        self.do_init(BGPOpaqueExtendedCommunity, self, kwargs)
+
+
+@_ExtendedCommunity.register_type(_ExtendedCommunity.ENCAPSULATION)
+class BGPEncapsulationExtendedCommunity(_ExtendedCommunity):
+    _VALUE_PACK_STR = '!B4xH'
+    _VALUE_FIELDS = ['subtype', 'tunnel_type']
+
+    # BGP Tunnel Encapsulation Attribute Tunnel Types
+    # http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#tunnel-types
+    TUNNEL_TYPE_L2TPV3 = 1
+    TUNNEL_TYPE_GRE = 2
+    TUNNEL_TYPE_IP_IN_IP = 7
+    TUNNEL_TYPE_VXLAN = 8
+    TUNNEL_TYPE_NVGRE = 9
+    TUNNEL_TYPE_MPLS = 10
+    TUNNEL_TYPE_MPLS_IN_GRE = 11
+    TUNNEL_TYPE_VXLAN_GRE = 12
+    TUNNEL_TYPE_MPLS_IN_UDP = 13
+
+    def __init__(self, **kwargs):
+        super(BGPEncapsulationExtendedCommunity, self).__init__()
+        self.do_init(BGPEncapsulationExtendedCommunity, self, kwargs)
+
+    @classmethod
+    def from_str(cls, tunnel_type):
+        """
+        Returns an instance identified with the given `tunnel_type`.
+
+        `tunnel_type` should be a str type value and corresponding to
+        BGP Tunnel Encapsulation Attribute Tunnel Type constants name
+        omitting `TUNNEL_TYPE_` prefix.
+
+        Example:
+            - `gre` means TUNNEL_TYPE_GRE
+            - `vxlan` means TUNNEL_TYPE_VXLAN
+
+        And raises AttributeError when the corresponding Tunnel Type
+        is not found to the given `tunnel_type`.
+        """
+        return cls(subtype=_ExtendedCommunity.SUBTYPE_ENCAPSULATION,
+                   tunnel_type=getattr(cls,
+                                       'TUNNEL_TYPE_' + tunnel_type.upper()))
+
+
+@_ExtendedCommunity.register_type(_ExtendedCommunity.EVPN_MAC_MOBILITY)
+class BGPEvpnMacMobilityExtendedCommunity(_ExtendedCommunity):
+    """
+    MAC Mobility Extended Community
+    """
+    #  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
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # | Type=0x06     | Sub-Type=0x00 |Flags(1 octet)|  Reserved=0    |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # |                       Sequence Number                         |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    _VALUE_PACK_STR = '!BBxI'
+    _VALUE_FIELDS = ['subtype', 'flags', 'sequence_number']
+
+    def __init__(self, **kwargs):
+        super(BGPEvpnMacMobilityExtendedCommunity, self).__init__()
+        self.do_init(BGPEvpnMacMobilityExtendedCommunity, self, kwargs)
+
+
+@_ExtendedCommunity.register_type(_ExtendedCommunity.EVPN_ESI_LABEL)
+class BGPEvpnEsiLabelExtendedCommunity(_ExtendedCommunity):
+    """
+    ESI Label Extended Community
+    """
+    #  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
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # | Type=0x06     | Sub-Type=0x01 | Flags(1 octet)|  Reserved=0   |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # |  Reserved=0   |          ESI Label                            |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    _VALUE_PACK_STR = '!BB2x3s'
+    _VALUE_FIELDS = ['subtype', 'flags']
+
+    # Classification for Flags.
+    SINGLE_ACTIVE_BIT = 1 << 0
+
+    def __init__(self, label=None, mpls_label=None, vni=None, **kwargs):
+        super(BGPEvpnEsiLabelExtendedCommunity, self).__init__()
+        self.do_init(BGPEvpnEsiLabelExtendedCommunity, self, kwargs)
+
+        if label:
+            # If binary type label field value is specified, stores it
+            # and decodes as MPLS label and VNI.
+            self._label = label
+            self._mpls_label, _ = mpls.label_from_bin(label)
+            self._vni = vxlan.vni_from_bin(label)
+        else:
+            # If either MPLS label or VNI is specified, stores it
+            # and encodes into binary type label field value.
+            self._label = self._serialize_label(mpls_label, vni)
+            self._mpls_label = mpls_label
+            self._vni = vni
+
+    def _serialize_label(self, mpls_label, vni):
+        if mpls_label:
+            return mpls.label_to_bin(mpls_label, is_bos=True)
+        elif vni:
+            return vxlan.vni_to_bin(vni)
+        else:
+            return b'\x00' * 3
+
+    @classmethod
+    def parse_value(cls, buf):
+        (subtype, flags,
+         label) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
+        return {
+            'subtype': subtype,
+            'flags': flags,
+            'label': label,
+        }
+
+    def serialize_value(self):
+        return struct.pack(self._VALUE_PACK_STR, self.subtype, self.flags,
+                           self._label)
+
+    @property
+    def mpls_label(self):
+        return self._mpls_label
+
+    @mpls_label.setter
+    def mpls_label(self, mpls_label):
+        self._label = mpls.label_to_bin(mpls_label, is_bos=True)
+        self._mpls_label = mpls_label
+        self._vni = None  # disables VNI
+
+    @property
+    def vni(self):
+        return self._vni
+
+    @vni.setter
+    def vni(self, vni):
+        self._label = vxlan.vni_to_bin(vni)
+        self._mpls_label = None  # disables ESI label
+        self._vni = vni
+
+
+@_ExtendedCommunity.register_type(_ExtendedCommunity.EVPN_ES_IMPORT_RT)
+class BGPEvpnEsImportRTExtendedCommunity(_ExtendedCommunity):
+    """
+    ES-Import Route Target Extended Community
+    """
+    #  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
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # | Type=0x06     | Sub-Type=0x02 |          ES-Import            |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # |                     ES-Import Cont'd                          |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    _VALUE_PACK_STR = '!B6s'
+    _VALUE_FIELDS = ['subtype', 'es_import']
+    _TYPE = {
+        'ascii': [
+            'es_import'
+        ]
+    }
+
+    def __init__(self, **kwargs):
+        super(BGPEvpnEsImportRTExtendedCommunity, self).__init__()
+        self.do_init(BGPEvpnEsImportRTExtendedCommunity, self, kwargs)
+
+    @classmethod
+    def parse_value(cls, buf):
+        (subtype, es_import) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
+        return {
+            'subtype': subtype,
+            'es_import': addrconv.mac.bin_to_text(es_import),
+        }
+
+    def serialize_value(self):
+        return struct.pack(self._VALUE_PACK_STR, self.subtype,
+                           addrconv.mac.text_to_bin(self.es_import))
+
+
+@_ExtendedCommunity.register_type(_ExtendedCommunity.FLOWSPEC_TRAFFIC_RATE)
+class BGPFlowSpecTrafficRateCommunity(_ExtendedCommunity):
+    """
+    Flow Specification Traffic Filtering Actions for Traffic Rate.
+
+    ========================== ===============================================
+    Attribute                  Description
+    ========================== ===============================================
+    as_number                  Autonomous System number.
+    rate_info                  rate information.
+    ========================== ===============================================
+    """
+    # 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
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # | Type=0x80     | Sub-Type=0x06 |           AS number           |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # |                      Rate information                         |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    _VALUE_PACK_STR = '!BHf'
+    _VALUE_FIELDS = ['subtype', 'as_number', 'rate_info']
+    ACTION_NAME = 'traffic_rate'
+
+    def __init__(self, **kwargs):
+        super(BGPFlowSpecTrafficRateCommunity, self).__init__()
+        kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_TRAFFIC_RATE
+        self.do_init(BGPFlowSpecTrafficRateCommunity, self, kwargs)
+
+    @classmethod
+    def parse_value(cls, buf):
+        (subtype, as_number,
+         rate_info) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
+        return {
+            'subtype': subtype,
+            'as_number': as_number,
+            'rate_info': rate_info,
+        }
+
+    def serialize_value(self):
+        return struct.pack(self._VALUE_PACK_STR, self.subtype,
+                           self.as_number, self.rate_info)
+
+
+@_ExtendedCommunity.register_type(_ExtendedCommunity.FLOWSPEC_TRAFFIC_ACTION)
+class BGPFlowSpecTrafficActionCommunity(_ExtendedCommunity):
+    """
+    Flow Specification Traffic Filtering Actions for Traffic Action.
+
+    ========================== ===============================================
+    Attribute                  Description
+    ========================== ===============================================
+    action                     Apply action.
+                               The supported action are
+                               ``SAMPLE`` and ``TERMINAL``.
+    ========================== ===============================================
+    """
+    # 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
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # | Type=0x80     | Sub-Type=0x07 |          Traffic-action       |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # |                     Traffic-action Cont'd                     |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+    # Traffic-action format
+    #  40  41  42  43  44  45  46  47
+    # +---+---+---+---+---+---+---+---+
+    # |        reserved       | S | T |
+    # +---+---+---+---+---+---+---+---+
+
+    _VALUE_PACK_STR = '!B5xB'
+    _VALUE_FIELDS = ['subtype', 'action']
+    ACTION_NAME = 'traffic_action'
+    SAMPLE = 1 << 1
+    TERMINAL = 1 << 0
+
+    def __init__(self, **kwargs):
+        super(BGPFlowSpecTrafficActionCommunity, self).__init__()
+        kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_TRAFFIC_ACTION
+        self.do_init(BGPFlowSpecTrafficActionCommunity, self, kwargs)
+
+
+@_ExtendedCommunity.register_type(_ExtendedCommunity.FLOWSPEC_REDIRECT)
+class BGPFlowSpecRedirectCommunity(BGPTwoOctetAsSpecificExtendedCommunity):
+    """
+    Flow Specification Traffic Filtering Actions for Redirect.
+
+    ========================== ===============================================
+    Attribute                  Description
+    ========================== ===============================================
+    as_number                  Autonomous System number.
+    local_administrator        Local Administrator.
+    ========================== ===============================================
+    """
+    ACTION_NAME = 'redirect'
+
+    def __init__(self, **kwargs):
+        super(BGPTwoOctetAsSpecificExtendedCommunity, self).__init__()
+        kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_REDIRECT
+        self.do_init(BGPTwoOctetAsSpecificExtendedCommunity, self, kwargs)
+
+
+@_ExtendedCommunity.register_type(
+    _ExtendedCommunity.FLOWSPEC_TRAFFIC_REMARKING)
+class BGPFlowSpecTrafficMarkingCommunity(_ExtendedCommunity):
+    """
+    Flow Specification Traffic Filtering Actions for Traffic Marking.
+
+    ========================== ===============================================
+    Attribute                  Description
+    ========================== ===============================================
+    dscp                       Differentiated Services Code Point.
+    ========================== ===============================================
+    """
+    # 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
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # | Type=0x80     | Sub-Type=0x09 |           Reserved=0          |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # |                    Reserved=0                 |      Dscp     |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    _VALUE_PACK_STR = '!B5xB'
+    _VALUE_FIELDS = ['subtype', 'dscp']
+    ACTION_NAME = 'traffic_marking'
+
+    def __init__(self, **kwargs):
+        super(BGPFlowSpecTrafficMarkingCommunity, self).__init__()
+        kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_TRAFFIC_REMARKING
+        self.do_init(BGPFlowSpecTrafficMarkingCommunity, self, kwargs)
+
+    @classmethod
+    def parse_value(cls, buf):
+        (subtype, dscp) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
+        return {
+            'subtype': subtype,
+            'dscp': dscp,
+        }
+
+    def serialize_value(self):
+        return struct.pack(self._VALUE_PACK_STR, self.subtype, self.dscp)
+
+
+# TODO
+# Implement "Redirect-IPv6" [draft-ietf-idr-flow-spec-v6-08]
+
+
+@_ExtendedCommunity.register_type(
+    _ExtendedCommunity.FLOWSPEC_VLAN_ACTION)
+class BGPFlowSpecVlanActionCommunity(_ExtendedCommunity):
+    """
+    Flow Specification Vlan Actions.
+
+    ========= ===============================================
+    Attribute Description
+    ========= ===============================================
+    actions_1 Bit representation of actions.
+              Supported actions are
+              ``POP``, ``PUSH``, ``SWAP``, ``REWRITE_INNER``, ``REWRITE_OUTER``.
+    actions_2 Same as ``actions_1``.
+    vlan_1    VLAN ID used by ``actions_1``.
+    cos_1     Class of Service used by ``actions_1``.
+    vlan_2    VLAN ID used by ``actions_2``.
+    cos_2     Class of Service used by ``actions_2``.
+    ========= ===============================================
+    """
+    # 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
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # | Type=0x08     | Sub-Type=0x0a |PO1|PU1|SW1|RT1|RO1|...|PO2|...|
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # |       VLAN ID1      |  COS1 |0|       VLAN ID2      |  COS2 |0|
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    _VALUE_PACK_STR = '!BBBHH'
+    _VALUE_FIELDS = [
+        'subtype',
+        'actions_1',
+        'actions_2',
+        'vlan_1',
+        'vlan_2',
+        'cos_1',
+        'cos_2']
+    ACTION_NAME = 'vlan_action'
+    _COS_MASK = 0x07
+
+    POP = 1 << 7
+    PUSH = 1 << 6
+    SWAP = 1 << 5
+    REWRITE_INNER = 1 << 4
+    REWRITE_OUTER = 1 << 3
+
+    def __init__(self, **kwargs):
+        super(BGPFlowSpecVlanActionCommunity, self).__init__()
+        kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_VLAN_ACTION
+        self.do_init(BGPFlowSpecVlanActionCommunity, self, kwargs)
+
+    @classmethod
+    def parse_value(cls, buf):
+        (subtype, actions_1, actions_2,
+         vlan_cos_1, vlan_cos_2) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
+
+        return {
+            'subtype': subtype,
+            'actions_1': actions_1,
+            'vlan_1': int(vlan_cos_1 >> 4),
+            'cos_1': int((vlan_cos_1 >> 1) & cls._COS_MASK),
+            'actions_2': actions_2,
+            'vlan_2': int(vlan_cos_2 >> 4),
+            'cos_2': int((vlan_cos_2 >> 1) & cls._COS_MASK)
+        }
+
+    def serialize_value(self):
+        return struct.pack(
+            self._VALUE_PACK_STR,
+            self.subtype,
+            self.actions_1,
+            self.actions_2,
+            (self.vlan_1 << 4) + (self.cos_1 << 1),
+            (self.vlan_2 << 4) + (self.cos_2 << 1),
+        )
+
+
+@_ExtendedCommunity.register_type(
+    _ExtendedCommunity.FLOWSPEC_TPID_ACTION)
+class BGPFlowSpecTPIDActionCommunity(_ExtendedCommunity):
+    """
+    Flow Specification TPID Actions.
+
+    ========= =========================================================
+    Attribute Description
+    ========= =========================================================
+    actions   Bit representation of actions.
+              Supported actions are
+              ``TI(inner TPID action)`` and ``TO(outer TPID action)``.
+    tpid_1    TPID used by ``TI``.
+    tpid_2    TPID used by ``TO``.
+    ========= =========================================================
+    """
+    # 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
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # | Type=0x08     | Sub-Type=0x0b   |TI|TO|      Reserved=0       |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    # |             TPID1               |             TPID2           |
+    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    _VALUE_PACK_STR = '!BHHH'
+    _VALUE_FIELDS = ['subtype', 'actions', 'tpid_1', 'tpid_2']
+    ACTION_NAME = 'tpid_action'
+
+    TI = 1 << 15
+    TO = 1 << 14
+
+    def __init__(self, **kwargs):
+        super(BGPFlowSpecTPIDActionCommunity, self).__init__()
+        kwargs['subtype'] = self.SUBTYPE_FLOWSPEC_TPID_ACTION
+        self.do_init(BGPFlowSpecTPIDActionCommunity, self, kwargs)
+
+    @classmethod
+    def parse_value(cls, buf):
+        (subtype, actions, tpid_1, tpid_2) = struct.unpack_from(
+            cls._VALUE_PACK_STR, buf)
+
+        return {
+            'subtype': subtype,
+            'actions': actions,
+            'tpid_1': tpid_1,
+            'tpid_2': tpid_2,
+        }
+
+    def serialize_value(self):
+        return struct.pack(
+            self._VALUE_PACK_STR,
+            self.subtype,
+            self.actions,
+            self.tpid_1,
+            self.tpid_2,
+        )
+
+
+@_ExtendedCommunity.register_unknown_type()
+class BGPUnknownExtendedCommunity(_ExtendedCommunity):
+    _VALUE_PACK_STR = '!7s'  # opaque value
+
+    def __init__(self, type_, **kwargs):
+        super(BGPUnknownExtendedCommunity, self).__init__(type_=type_)
+        self.do_init(BGPUnknownExtendedCommunity, self, kwargs, type_=type_)
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_MP_REACH_NLRI)
+class BGPPathAttributeMpReachNLRI(_PathAttribute):
+    _VALUE_PACK_STR = '!HBB'  # afi, safi, next_hop_len
+    _VALUE_PACK_SIZE = struct.calcsize(_VALUE_PACK_STR)
+    _RD_LENGTH = 8
+    _RESERVED_LENGTH = 1
+    _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
+    _class_suffixes = ['AddrPrefix']
+    _opt_attributes = ['next_hop']
+    _TYPE = {
+        'ascii': [
+            'next_hop'
+        ]
+    }
+
+    def __init__(self, afi, safi, next_hop, nlri,
+                 flags=0, type_=None, length=None):
+        super(BGPPathAttributeMpReachNLRI, self).__init__(
+            flags=flags, type_=type_, length=length)
+        self.afi = afi
+        self.safi = safi
+        if not isinstance(next_hop, (list, tuple)):
+            next_hop = [next_hop]
+        for n in next_hop:
+            if not ip.valid_ipv4(n) and not ip.valid_ipv6(n):
+                raise ValueError('Invalid address for next_hop: %s' % n)
+        # Note: For the backward compatibility, stores the first next_hop
+        # address and all next_hop addresses separately.
+        if next_hop:
+            self._next_hop = next_hop[0]
+        else:
+            self._next_hop = None
+        self._next_hop_list = next_hop
+        self.nlri = nlri
+        addr_cls = _get_addr_class(afi, safi)
+        for i in nlri:
+            if not isinstance(i, addr_cls):
+                raise ValueError('Invalid NRLI class for afi=%d and safi=%d'
+                                 % (self.afi, self.safi))
+
+    @staticmethod
+    def split_bin_with_len(buf, unit_len):
+        f = io.BytesIO(buf)
+        return [f.read(unit_len) for _ in range(0, len(buf), unit_len)]
+
+    @classmethod
+    def parse_next_hop_ipv4(cls, buf, unit_len):
+        next_hop = []
+        for next_hop_bin in cls.split_bin_with_len(buf, unit_len):
+            next_hop.append(addrconv.ipv4.bin_to_text(next_hop_bin[-4:]))
+        return next_hop
+
+    @classmethod
+    def parse_next_hop_ipv6(cls, buf, unit_len):
+        next_hop = []
+        for next_hop_bin in cls.split_bin_with_len(buf, unit_len):
+            next_hop.append(addrconv.ipv6.bin_to_text(next_hop_bin[-16:]))
+        return next_hop
+
+    @classmethod
+    def parse_value(cls, buf):
+        (afi, safi, next_hop_len,) = struct.unpack_from(
+            cls._VALUE_PACK_STR, six.binary_type(buf))
+        rest = buf[cls._VALUE_PACK_SIZE:]
+
+        next_hop_bin = rest[:next_hop_len]
+        rest = rest[next_hop_len:]
+        reserved = rest[:cls._RESERVED_LENGTH]
+        assert reserved == b'\0'
+
+        nlri_bin = rest[cls._RESERVED_LENGTH:]
+        addr_cls = _get_addr_class(afi, safi)
+        nlri = []
+        while nlri_bin:
+            n, nlri_bin = addr_cls.parser(nlri_bin)
+            nlri.append(n)
+
+        rf = RouteFamily(afi, safi)
+        if rf == RF_IPv4_VPN:
+            next_hop = cls.parse_next_hop_ipv4(next_hop_bin,
+                                               cls._RD_LENGTH + 4)
+            next_hop_len -= cls._RD_LENGTH * len(next_hop)
+        elif rf == RF_IPv6_VPN:
+            next_hop = cls.parse_next_hop_ipv6(next_hop_bin,
+                                               cls._RD_LENGTH + 16)
+            next_hop_len -= cls._RD_LENGTH * len(next_hop)
+        elif (afi == addr_family.IP
+              or (rf == RF_L2_EVPN and next_hop_len < 16)):
+            next_hop = cls.parse_next_hop_ipv4(next_hop_bin, 4)
+        elif (afi == addr_family.IP6
+              or (rf == RF_L2_EVPN and next_hop_len >= 16)):
+            next_hop = cls.parse_next_hop_ipv6(next_hop_bin, 16)
+        elif rf == RF_L2VPN_FLOWSPEC:
+            next_hop = []
+        else:
+            raise ValueError('Invalid address family: afi=%d, safi=%d'
+                             % (afi, safi))
+
+        return {
+            'afi': afi,
+            'safi': safi,
+            'next_hop': next_hop,
+            'nlri': nlri,
+        }
+
+    def serialize_next_hop(self):
+        buf = bytearray()
+        for next_hop in self.next_hop_list:
+            if self.afi == addr_family.IP6:
+                next_hop = str(netaddr.IPAddress(next_hop).ipv6())
+            next_hop_bin = ip.text_to_bin(next_hop)
+            if RouteFamily(self.afi, self.safi) in (RF_IPv4_VPN, RF_IPv6_VPN):
+                # Empty label stack(RD=0:0) + IP address
+                next_hop_bin = b'\x00' * self._RD_LENGTH + next_hop_bin
+            buf += next_hop_bin
+
+        return buf
+
+    def serialize_value(self):
+        next_hop_bin = self.serialize_next_hop()
+
+        # fixup
+        next_hop_len = len(next_hop_bin)
+
+        buf = bytearray()
+        msg_pack_into(self._VALUE_PACK_STR, buf, 0,
+                      self.afi, self.safi, next_hop_len)
+        buf += next_hop_bin
+        buf += b'\0'  # reserved
+
+        nlri_bin = bytearray()
+        for n in self.nlri:
+            nlri_bin += n.serialize()
+        buf += nlri_bin
+
+        return buf
+
+    @property
+    def next_hop(self):
+        return self._next_hop
+
+    @next_hop.setter
+    def next_hop(self, addr):
+        if not ip.valid_ipv4(addr) and not ip.valid_ipv6(addr):
+            raise ValueError('Invalid address for next_hop: %s' % addr)
+        self._next_hop = addr
+        self.next_hop_list[0] = addr
+
+    @property
+    def next_hop_list(self):
+        return self._next_hop_list
+
+    @next_hop_list.setter
+    def next_hop_list(self, addr_list):
+        if not isinstance(addr_list, (list, tuple)):
+            addr_list = [addr_list]
+        for addr in addr_list:
+            if not ip.valid_ipv4(addr) and not ip.valid_ipv6(addr):
+                raise ValueError('Invalid address for next_hop: %s' % addr)
+        self._next_hop = addr_list[0]
+        self._next_hop_list = addr_list
+
+    @property
+    def route_family(self):
+        return _rf_map[(self.afi, self.safi)]
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYPE_MP_UNREACH_NLRI)
+class BGPPathAttributeMpUnreachNLRI(_PathAttribute):
+    _VALUE_PACK_STR = '!HB'  # afi, safi
+    _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
+    _class_suffixes = ['AddrPrefix']
+
+    def __init__(self, afi, safi, withdrawn_routes,
+                 flags=0, type_=None, length=None):
+        super(BGPPathAttributeMpUnreachNLRI, self).__init__(
+            flags=flags, type_=type_, length=length)
+        self.afi = afi
+        self.safi = safi
+        self.withdrawn_routes = withdrawn_routes
+        addr_cls = _get_addr_class(afi, safi)
+        for i in withdrawn_routes:
+            if not isinstance(i, addr_cls):
+                raise ValueError('Invalid NRLI class for afi=%d and safi=%d'
+                                 % (self.afi, self.safi))
+
+    @classmethod
+    def parse_value(cls, buf):
+        (afi, safi,) = struct.unpack_from(
+            cls._VALUE_PACK_STR, six.binary_type(buf))
+
+        nlri_bin = buf[struct.calcsize(cls._VALUE_PACK_STR):]
+        addr_cls = _get_addr_class(afi, safi)
+        nlri = []
+        while nlri_bin:
+            n, nlri_bin = addr_cls.parser(nlri_bin)
+            nlri.append(n)
+
+        return {
+            'afi': afi,
+            'safi': safi,
+            'withdrawn_routes': nlri,
+        }
+
+    def serialize_value(self):
+        buf = bytearray()
+        msg_pack_into(self._VALUE_PACK_STR, buf, 0, self.afi, self.safi)
+
+        nlri_bin = bytearray()
+        for n in self.withdrawn_routes:
+            nlri_bin += n.serialize()
+        buf += nlri_bin
+
+        return buf
+
+    @property
+    def route_family(self):
+        return _rf_map[(self.afi, self.safi)]
+
+
+@_PathAttribute.register_type(BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE)
+class BGPPathAttributePmsiTunnel(_PathAttribute):
+    """
+    P-Multicast Service Interface Tunnel (PMSI Tunnel) attribute
+    """
+
+    # pmsi_flags, tunnel_type, mpls_label
+    _VALUE_PACK_STR = '!BB3s'
+    _PACK_STR_SIZE = struct.calcsize(_VALUE_PACK_STR)
+    _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE
+
+    # RFC 6514
+    # +--------------------------------+
+    # |  Flags (1 octet)               |
+    # +--------------------------------+
+    # |  Tunnel Type (1 octets)        |
+    # +--------------------------------+
+    # |  MPLS Label (3 octets)         |
+    # +--------------------------------+
+    # |  Tunnel Identifier (variable)  |
+    # +--------------------------------+
+
+    # The Flags field has the following format:
+    #  0 1 2 3 4 5 6 7
+    # +-+-+-+-+-+-+-+-+
+    # |  reserved   |L|
+    # +-+-+-+-+-+-+-+-+
+    # `L` refers to the Leaf Information Required.
+
+    # Current, Tunnel Type supports following.
+    # + 0 - No tunnel information present
+    # + 6 - Ingress Replication
+    TYPE_NO_TUNNEL_INFORMATION_PRESENT = 0
+    TYPE_INGRESS_REPLICATION = 6
+
+    # TODO:
+    # The following Tunnel Type are not supported.
+    # Therefore, we will need to support in the future.
+    # + 1 - RSVP-TE P2MP LSP
+    # + 2 - mLDP P2MP LSP
+    # + 3 - PIM-SSM Tree
+    # + 4 - PIM-SM Tree
+    # + 5 - BIDIR-PIM Tree
+    # + 7 - mLDP MP2MP LSP
+
+    def __init__(self, pmsi_flags, tunnel_type,
+                 mpls_label=None, label=None, vni=None, tunnel_id=None,
+                 flags=0, type_=None, length=None):
+        super(BGPPathAttributePmsiTunnel, self).__init__(flags=flags,
+                                                         type_=type_,
+                                                         length=length)
+        self.pmsi_flags = pmsi_flags
+        self.tunnel_type = tunnel_type
+        self.tunnel_id = tunnel_id
+
+        if label:
+            # If binary type label field value is specified, stores it
+            # and decodes as MPLS label and VNI.
+            self._label = label
+            self._mpls_label, _ = mpls.label_from_bin(label)
+            self._vni = vxlan.vni_from_bin(label)
+        else:
+            # If either MPLS label or VNI is specified, stores it
+            # and encodes into binary type label field value.
+            self._label = self._serialize_label(mpls_label, vni)
+            self._mpls_label = mpls_label
+            self._vni = vni
+
+    @classmethod
+    def parse_value(cls, buf):
+        (pmsi_flags,
+         tunnel_type,
+         label) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
+        value = buf[cls._PACK_STR_SIZE:]
+
+        return {
+            'pmsi_flags': pmsi_flags,
+            'tunnel_type': tunnel_type,
+            'label': label,
+            'tunnel_id': _PmsiTunnelId.parse(tunnel_type, value)
+        }
+
+    def serialize_value(self):
+        buf = bytearray()
+        msg_pack_into(self._VALUE_PACK_STR, buf, 0,
+                      self.pmsi_flags, self.tunnel_type, self._label)
+
+        if self.tunnel_id is not None:
+            buf += self.tunnel_id.serialize()
+
+        return buf
+
+    def _serialize_label(self, mpls_label, vni):
+        if mpls_label:
+            return mpls.label_to_bin(mpls_label, is_bos=True)
+        elif vni:
+            return vxlan.vni_to_bin(vni)
+        else:
+            return b'\x00' * 3
+
+    @property
+    def mpls_label(self):
+        return self._mpls_label
+
+    @mpls_label.setter
+    def mpls_label(self, mpls_label):
+        self._label = mpls.label_to_bin(mpls_label, is_bos=True)
+        self._mpls_label = mpls_label
+        self._vni = None  # disables VNI
+
+    @property
+    def vni(self):
+        return self._vni
+
+    @vni.setter
+    def vni(self, vni):
+        self._label = vxlan.vni_to_bin(vni)
+        self._mpls_label = None  # disables MPLS label
+        self._vni = vni
+
+    @classmethod
+    def from_jsondict(cls, dict_, decode_string=base64.b64decode,
+                      **additional_args):
+        if isinstance(dict_['tunnel_id'], dict):
+            tunnel_id = dict_.pop('tunnel_id')
+            ins = super(BGPPathAttributePmsiTunnel,
+                        cls).from_jsondict(dict_,
+                                           decode_string,
+                                           **additional_args)
+
+            mod = import_module(cls.__module__)
+
+            for key, value in tunnel_id.items():
+                tunnel_id_cls = getattr(mod, key)
+                ins.tunnel_id = tunnel_id_cls.from_jsondict(value,
+                                                            decode_string,
+                                                            **additional_args)
+        else:
+            ins = super(BGPPathAttributePmsiTunnel,
+                        cls).from_jsondict(dict_,
+                                           decode_string,
+                                           **additional_args)
+
+        return ins
+
+
+class _PmsiTunnelId(StringifyMixin, TypeDisp):
+
+    @classmethod
+    def parse(cls, tunnel_type, buf):
+        subcls = cls._lookup_type(tunnel_type)
+        return subcls.parser(buf)
+
+
+@_PmsiTunnelId.register_unknown_type()
+class PmsiTunnelIdUnknown(_PmsiTunnelId):
+    """
+    Unknown route type specific _PmsiTunnelId
+    """
+
+    def __init__(self, value):
+        super(PmsiTunnelIdUnknown, self).__init__()
+        self.value = value
+
+    @classmethod
+    def parser(cls, buf):
+        return cls(value=buf)
+
+    def serialize(self):
+        return self.value
+
+
+@_PmsiTunnelId.register_type(
+    BGPPathAttributePmsiTunnel.TYPE_NO_TUNNEL_INFORMATION_PRESENT)
+class _PmsiTunnelIdNoInformationPresent(_PmsiTunnelId):
+
+    @classmethod
+    def parser(cls, buf):
+        return None
+
+
+@_PmsiTunnelId.register_type(
+    BGPPathAttributePmsiTunnel.TYPE_INGRESS_REPLICATION)
+class PmsiTunnelIdIngressReplication(_PmsiTunnelId):
+    # tunnel_endpoint_ip
+    _VALUE_PACK_STR = '!%ds'
+    _TYPE = {
+        'ascii': [
+            'tunnel_endpoint_ip'
+        ]
+    }
+
+    def __init__(self, tunnel_endpoint_ip):
+        super(PmsiTunnelIdIngressReplication, self).__init__()
+        self.tunnel_endpoint_ip = tunnel_endpoint_ip
+
+    @classmethod
+    def parser(cls, buf):
+        (tunnel_endpoint_ip, ) = struct.unpack_from(
+            cls._VALUE_PACK_STR % len(buf),
+            six.binary_type(buf))
+        return cls(tunnel_endpoint_ip=ip.bin_to_text(tunnel_endpoint_ip))
+
+    def serialize(self):
+        ip_bin = ip.text_to_bin(self.tunnel_endpoint_ip)
+        return struct.pack(self._VALUE_PACK_STR % len(ip_bin),
+                           ip.text_to_bin(self.tunnel_endpoint_ip))
+
+
+class BGPNLRI(IPAddrPrefix):
+    pass
+
+
+class BGPMessage(packet_base.PacketBase, TypeDisp):
+    """Base class for BGP-4 messages.
+
+    An instance has the following attributes at least.
+    Most of them are same to the on-wire counterparts but in host byte
+    order.
+    __init__ takes the corresponding args in this order.
+
+    ========================== ===============================================
+    Attribute                  Description
+    ========================== ===============================================
+    marker                     Marker field.  Ignored when encoding.
+    len                        Length field.  Ignored when encoding.
+    type                       Type field.  one of ``BGP_MSG_*`` constants.
+    ========================== ===============================================
+    """
+
+    _HDR_PACK_STR = '!16sHB'  # marker, len, type
+    _HDR_LEN = struct.calcsize(_HDR_PACK_STR)
+    _class_prefixes = ['BGP']
+
+    def __init__(self, marker=None, len_=None, type_=None):
+        super(BGPMessage, self).__init__()
+        if marker is None:
+            self._marker = _MARKER
+        else:
+            self._marker = marker
+        self.len = len_
+        if type_ is None:
+            type_ = self._rev_lookup_type(self.__class__)
+        self.type = type_
+
+    @classmethod
+    def parser(cls, buf):
+        if len(buf) < cls._HDR_LEN:
+            raise stream_parser.StreamParser.TooSmallException(
+                '%d < %d' % (len(buf), cls._HDR_LEN))
+        (marker, len_, type_) = struct.unpack_from(cls._HDR_PACK_STR,
+                                                   six.binary_type(buf))
+        msglen = len_
+        if len(buf) < msglen:
+            raise stream_parser.StreamParser.TooSmallException(
+                '%d < %d' % (len(buf), msglen))
+        binmsg = buf[cls._HDR_LEN:msglen]
+        rest = buf[msglen:]
+        subcls = cls._lookup_type(type_)
+        kwargs = subcls.parser(binmsg)
+        return subcls(marker=marker, len_=len_, type_=type_,
+                      **kwargs), cls, rest
+
+    def serialize(self, payload=None, prev=None):
+        # fixup
+        self._marker = _MARKER
+        tail = self.serialize_tail()
+        self.len = self._HDR_LEN + len(tail)
+
+        hdr = bytearray(struct.pack(self._HDR_PACK_STR, self._marker,
+                                    self.len, self.type))
+        return hdr + tail
+
+    def __len__(self):
+        # XXX destructive
+        buf = self.serialize()
+        return len(buf)
+
+
+@BGPMessage.register_type(BGP_MSG_OPEN)
+class BGPOpen(BGPMessage):
+    """BGP-4 OPEN Message encoder/decoder class.
+
+    An instance has the following attributes at least.
+    Most of them are same to the on-wire counterparts but in host byte
+    order.
+    __init__ takes the corresponding args in this order.
+
+    ========================== ===============================================
+    Attribute                  Description
+    ========================== ===============================================
+    marker                     Marker field.  Ignored when encoding.
+    len                        Length field.  Ignored when encoding.
+    type                       Type field.
+    version                    Version field.
+    my_as                      My Autonomous System field.
+                               2 octet unsigned integer.
+    hold_time                  Hold Time field.
+                               2 octet unsigned integer.
+    bgp_identifier             BGP Identifier field.
+                               An IPv4 address.
+                               For example, '192.0.2.1'
+    opt_param_len              Optional Parameters Length field.
+                               Ignored when encoding.
+    opt_param                  Optional Parameters field.
+                               A list of BGPOptParam instances.
+                               The default is [].
+    ========================== ===============================================
+    """
+
+    _PACK_STR = '!BHH4sB'
+    _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR)
+    _TYPE = {
+        'ascii': [
+            'bgp_identifier'
+        ]
+    }
+
+    def __init__(self, my_as, bgp_identifier, type_=BGP_MSG_OPEN,
+                 opt_param_len=0, opt_param=None,
+                 version=_VERSION, hold_time=0, len_=None, marker=None):
+        opt_param = opt_param if opt_param else []
+        super(BGPOpen, self).__init__(marker=marker, len_=len_, type_=type_)
+        self.version = version
+        self.my_as = my_as
+        self.bgp_identifier = bgp_identifier
+        self.hold_time = hold_time
+        self.opt_param_len = opt_param_len
+        self.opt_param = opt_param
+
+    @property
+    def opt_param_cap_map(self):
+        cap_map = {}
+        for param in self.opt_param:
+            if param.type == BGP_OPT_CAPABILITY:
+                cap_map[param.cap_code] = param
+        return cap_map
+
+    def get_opt_param_cap(self, cap_code):
+        return self.opt_param_cap_map.get(cap_code)
+
+    @classmethod
+    def parser(cls, buf):
+        (version,
+         my_as,
+         hold_time,
+         bgp_identifier,
+         opt_param_len) = struct.unpack_from(cls._PACK_STR,
+                                             six.binary_type(buf))
+        rest = buf[struct.calcsize(cls._PACK_STR):]
+        binopts = rest[:opt_param_len]
+        opt_param = []
+        while binopts:
+            opt, binopts = _OptParam.parser(binopts)
+            opt_param.extend(opt)
+        return {
+            "version": version,
+            "my_as": my_as,
+            "hold_time": hold_time,
+            "bgp_identifier": addrconv.ipv4.bin_to_text(bgp_identifier),
+            "opt_param_len": opt_param_len,
+            "opt_param": opt_param,
+        }
+
+    def serialize_tail(self):
+        # fixup
+        self.version = _VERSION
+        binopts = bytearray()
+        for opt in self.opt_param:
+            binopts += opt.serialize()
+        self.opt_param_len = len(binopts)
+
+        msg = bytearray(struct.pack(self._PACK_STR,
+                                    self.version,
+                                    self.my_as,
+                                    self.hold_time,
+                                    addrconv.ipv4.text_to_bin(
+                                        self.bgp_identifier),
+                                    self.opt_param_len))
+        msg += binopts
+        return msg
+
+
+@BGPMessage.register_type(BGP_MSG_UPDATE)
+class BGPUpdate(BGPMessage):
+    """BGP-4 UPDATE Message encoder/decoder class.
+
+    An instance has the following attributes at least.
+    Most of them are same to the on-wire counterparts but in host byte
+    order.
+    __init__ takes the corresponding args in this order.
+
+    .. tabularcolumns:: |l|L|
+
+    ========================== ===============================================
+    Attribute                  Description
+    ========================== ===============================================
+    marker                     Marker field.  Ignored when encoding.
+    len                        Length field.  Ignored when encoding.
+    type                       Type field.
+    withdrawn_routes_len       Withdrawn Routes Length field.
+                               Ignored when encoding.
+    withdrawn_routes           Withdrawn Routes field.
+                               A list of BGPWithdrawnRoute instances.
+                               The default is [].
+    total_path_attribute_len   Total Path Attribute Length field.
+                               Ignored when encoding.
+    path_attributes            Path Attributes field.
+                               A list of BGPPathAttribute instances.
+                               The default is [].
+    nlri                       Network Layer Reachability Information field.
+                               A list of BGPNLRI instances.
+                               The default is [].
+    ========================== ===============================================
+    """
+
+    _MIN_LEN = BGPMessage._HDR_LEN
+
+    def __init__(self, type_=BGP_MSG_UPDATE,
+                 withdrawn_routes_len=None,
+                 withdrawn_routes=None,
+                 total_path_attribute_len=None,
+                 path_attributes=None,
+                 nlri=None,
+                 len_=None, marker=None):
+        withdrawn_routes = withdrawn_routes if withdrawn_routes else []
+        path_attributes = path_attributes if path_attributes else []
+        nlri = nlri if nlri else []
+        super(BGPUpdate, self).__init__(marker=marker, len_=len_, type_=type_)
+        self.withdrawn_routes_len = withdrawn_routes_len
+        self.withdrawn_routes = withdrawn_routes
+        self.total_path_attribute_len = total_path_attribute_len
+        self.path_attributes = path_attributes
+        self.nlri = nlri
+
+    @property
+    def pathattr_map(self):
+        passattr_map = {}
+        for attr in self.path_attributes:
+            passattr_map[attr.type] = attr
+        return passattr_map
+
+    def get_path_attr(self, attr_name):
+        return self.pathattr_map.get(attr_name)
+
+    @classmethod
+    def parser(cls, buf):
+        offset = 0
+        buf = six.binary_type(buf)
+        (withdrawn_routes_len,) = struct.unpack_from('!H', buf, offset)
+        binroutes = buf[offset + 2:
+                        offset + 2 + withdrawn_routes_len]
+        offset += 2 + withdrawn_routes_len
+        (total_path_attribute_len,) = struct.unpack_from('!H', buf, offset)
+        binpathattrs = buf[offset + 2:
+                           offset + 2 + total_path_attribute_len]
+        binnlri = buf[offset + 2 + total_path_attribute_len:]
+        withdrawn_routes = []
+        while binroutes:
+            r, binroutes = BGPWithdrawnRoute.parser(binroutes)
+            withdrawn_routes.append(r)
+        path_attributes = []
+        while binpathattrs:
+            pa, binpathattrs = _PathAttribute.parser(binpathattrs)
+            path_attributes.append(pa)
+        offset += 2 + total_path_attribute_len
+        nlri = []
+        while binnlri:
+            n, binnlri = BGPNLRI.parser(binnlri)
+            nlri.append(n)
+        return {
+            "withdrawn_routes_len": withdrawn_routes_len,
+            "withdrawn_routes": withdrawn_routes,
+            "total_path_attribute_len": total_path_attribute_len,
+            "path_attributes": path_attributes,
+            "nlri": nlri,
+        }
+
+    def serialize_tail(self):
+        # fixup
+        binroutes = bytearray()
+        for r in self.withdrawn_routes:
+            binroutes += r.serialize()
+        self.withdrawn_routes_len = len(binroutes)
+        binpathattrs = bytearray()
+        for pa in self.path_attributes:
+            binpathattrs += pa.serialize()
+        self.total_path_attribute_len = len(binpathattrs)
+        binnlri = bytearray()
+        for n in self.nlri:
+            binnlri += n.serialize()
+
+        msg = bytearray()
+        offset = 0
+        msg_pack_into('!H', msg, offset, self.withdrawn_routes_len)
+        msg += binroutes
+        offset += 2 + self.withdrawn_routes_len
+        msg_pack_into('!H', msg, offset, self.total_path_attribute_len)
+        msg += binpathattrs
+        offset += 2 + self.total_path_attribute_len
+        msg += binnlri
+        return msg
+
+
+@BGPMessage.register_type(BGP_MSG_KEEPALIVE)
+class BGPKeepAlive(BGPMessage):
+    """BGP-4 KEEPALIVE Message encoder/decoder class.
+
+    An instance has the following attributes at least.
+    Most of them are same to the on-wire counterparts but in host byte
+    order.
+    __init__ takes the corresponding args in this order.
+
+    ========================== ===============================================
+    Attribute                  Description
+    ========================== ===============================================
+    marker                     Marker field.  Ignored when encoding.
+    len                        Length field.  Ignored when encoding.
+    type                       Type field.
+    ========================== ===============================================
+    """
+
+    _MIN_LEN = BGPMessage._HDR_LEN
+
+    def __init__(self, type_=BGP_MSG_KEEPALIVE, len_=None, marker=None):
+        super(BGPKeepAlive, self).__init__(marker=marker, len_=len_,
+                                           type_=type_)
+
+    @classmethod
+    def parser(cls, buf):
+        return {}
+
+    def serialize_tail(self):
+        return bytearray()
+
+
+@BGPMessage.register_type(BGP_MSG_NOTIFICATION)
+class BGPNotification(BGPMessage):
+    """BGP-4 NOTIFICATION Message encoder/decoder class.
+
+    An instance has the following attributes at least.
+    Most of them are same to the on-wire counterparts but in host byte
+    order.
+    __init__ takes the corresponding args in this order.
+
+    ========================== ===============================================
+    Attribute                  Description
+    ========================== ===============================================
+    marker                     Marker field.  Ignored when encoding.
+    len                        Length field.  Ignored when encoding.
+    type                       Type field.
+    error_code                 Error code field.
+    error_subcode              Error subcode field.
+    data                       Data field.
+    ========================== ===============================================
+    """
+
+    _PACK_STR = '!BB'
+    _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR)
+
+    _REASONS = {
+        (1, 1): 'Message Header Error: not synchronised',
+        (1, 2): 'Message Header Error: bad message len',
+        (1, 3): 'Message Header Error: bad message type',
+        (2, 1): 'Open Message Error: unsupported version',
+        (2, 2): 'Open Message Error: bad peer AS',
+        (2, 3): 'Open Message Error: bad BGP identifier',
+        (2, 4): 'Open Message Error: unsupported optional param',
+        (2, 5): 'Open Message Error: authentication failure',
+        (2, 6): 'Open Message Error: unacceptable hold time',
+        (2, 7): 'Open Message Error: Unsupported Capability',
+        (2, 8): 'Open Message Error: Unassigned',
+        (3, 1): 'Update Message Error: malformed attribute list',
+        (3, 2): 'Update Message Error: unrecognized well-known attr',
+        (3, 3): 'Update Message Error: missing well-known attr',
+        (3, 4): 'Update Message Error: attribute flags error',
+        (3, 5): 'Update Message Error: attribute length error',
+        (3, 6): 'Update Message Error: invalid origin attr',
+        (3, 7): 'Update Message Error: as routing loop',
+        (3, 8): 'Update Message Error: invalid next hop attr',
+        (3, 9): 'Update Message Error: optional attribute error',
+        (3, 10): 'Update Message Error: invalid network field',
+        (3, 11): 'Update Message Error: malformed AS_PATH',
+        (4, 1): 'Hold Timer Expired',
+        (5, 1): 'Finite State Machine Error',
+        (6, 1): 'Cease: Maximum Number of Prefixes Reached',
+        (6, 2): 'Cease: Administrative Shutdown',
+        (6, 3): 'Cease: Peer De-configured',
+        (6, 4): 'Cease: Administrative Reset',
+        (6, 5): 'Cease: Connection Rejected',
+        (6, 6): 'Cease: Other Configuration Change',
+        (6, 7): 'Cease: Connection Collision Resolution',
+        (6, 8): 'Cease: Out of Resources',
+    }
+
+    def __init__(self,
+                 error_code,
+                 error_subcode,
+                 data=b'',
+                 type_=BGP_MSG_NOTIFICATION, len_=None, marker=None):
+        super(BGPNotification, self).__init__(marker=marker, len_=len_,
+                                              type_=type_)
+        self.error_code = error_code
+        self.error_subcode = error_subcode
+        self.data = data
+
+    @classmethod
+    def parser(cls, buf):
+        (error_code, error_subcode,) = struct.unpack_from(cls._PACK_STR,
+                                                          six.binary_type(buf))
+        data = bytes(buf[2:])
+        return {
+            "error_code": error_code,
+            "error_subcode": error_subcode,
+            "data": data,
+        }
+
+    def serialize_tail(self):
+        msg = bytearray(struct.pack(self._PACK_STR, self.error_code,
+                                    self.error_subcode))
+        msg += self.data
+        return msg
+
+    @property
+    def reason(self):
+        return self._REASONS.get((self.error_code, self.error_subcode))
+
+
+@BGPMessage.register_type(BGP_MSG_ROUTE_REFRESH)
+class BGPRouteRefresh(BGPMessage):
+    """BGP-4 ROUTE REFRESH Message (RFC 2918) encoder/decoder class.
+
+    An instance has the following attributes at least.
+    Most of them are same to the on-wire counterparts but in host byte
+    order.
+    __init__ takes the corresponding args in this order.
+
+    ========================== ===============================================
+    Attribute                  Description
+    ========================== ===============================================
+    marker                     Marker field.  Ignored when encoding.
+    len                        Length field.  Ignored when encoding.
+    type                       Type field.
+    afi                        Address Family Identifier
+    safi                       Subsequent Address Family Identifier
+    ========================== ===============================================
+    """
+
+    _PACK_STR = '!HBB'
+    _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR)
+
+    def __init__(self,
+                 afi, safi, demarcation=0,
+                 type_=BGP_MSG_ROUTE_REFRESH, len_=None, marker=None):
+        super(BGPRouteRefresh, self).__init__(marker=marker, len_=len_,
+                                              type_=type_)
+        self.afi = afi
+        self.safi = safi
+        self.demarcation = demarcation
+        self.eor_sent = False
+
+    @classmethod
+    def parser(cls, buf):
+        (afi, demarcation, safi,) = struct.unpack_from(cls._PACK_STR,
+                                                       six.binary_type(buf))
+        return {
+            "afi": afi,
+            "safi": safi,
+            "demarcation": demarcation,
+        }
+
+    def serialize_tail(self):
+        return bytearray(struct.pack(self._PACK_STR, self.afi,
+                                     self.demarcation, self.safi))
+
+
+class StreamParser(stream_parser.StreamParser):
+    """Streaming parser for BGP-4 messages.
+
+    This is a subclass of ryu.lib.packet.stream_parser.StreamParser.
+    Its parse method returns a list of BGPMessage subclass instances.
+    """
+
+    def try_parse(self, data):
+        msg, _, rest = BGPMessage.parser(data)
+        return msg, rest