summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorShinpei Muraoka <shinpei.muraoka@gmail.com>2017-02-09 16:29:22 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2017-02-11 14:21:04 +0900
commit79741fdc889d6353561e8f5d8de89792be1ada6f (patch)
tree064b56e0fb5ef91930beb5db3a524b5cb89b3b79
parent838708a32a042dc57dc4b7ba854a81a674993a66 (diff)
packet/bgp: Support Flow Specification
Signed-off-by: Shinpei Muraoka <shinpei.muraoka@gmail.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r--ryu/lib/packet/bgp.py496
-rw-r--r--ryu/lib/packet/safi.py2
2 files changed, 496 insertions, 2 deletions
diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py
index 8359412f..c787d82e 100644
--- a/ryu/lib/packet/bgp.py
+++ b/ryu/lib/packet/bgp.py
@@ -23,12 +23,14 @@ RFC 4271 BGP-4
# - RFC 4364 BGP/MPLS IP Virtual Private Networks (VPNs)
import abc
+import base64
import copy
import functools
import io
+import itertools
+import math
import socket
import struct
-import base64
import netaddr
import six
@@ -569,6 +571,8 @@ 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_FLOW_SPEC = RouteFamily(addr_family.IP, subaddr_family.IP_FLOW_SPEC)
+RF_VPNv4_FLOW_SPEC = RouteFamily(addr_family.IP, subaddr_family.VPN_FLOW_SPEC)
RF_RTC_UC = RouteFamily(addr_family.IP,
subaddr_family.ROUTE_TARGET_CONSTRAINTS)
@@ -580,6 +584,8 @@ _rf_map = {
(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_FLOW_SPEC): RF_IPv4_FLOW_SPEC,
+ (addr_family.IP, subaddr_family.VPN_FLOW_SPEC): RF_VPNv4_FLOW_SPEC,
(addr_family.IP, subaddr_family.ROUTE_TARGET_CONSTRAINTS): RF_RTC_UC
}
@@ -1979,6 +1985,378 @@ class EvpnIpPrefixNLRI(EvpnNLRI):
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) |
+ # +-----------------------------------+
+
+ _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
+
+ 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
+
+ rest = buf[offset:offset + length]
+ rules = []
+
+ while rest:
+ subcls, rest = _FlowSpecComponentBase.parse_header(rest)
+
+ while rest:
+ rule, rest = subcls.parse_body(rest)
+ rules.append(rule)
+
+ if (not isinstance(rule, _FlowSpecOperatorBase) or
+ rule.operator & rule.END_OF_LIST):
+ break
+
+ return cls(length, rules), rest
+
+ def serialize(self):
+ rules_bin = bytearray()
+ 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
+
+
+class FlowSpecIPv4NLRI(_FlowSpecNLRIBase):
+ """
+ Flow Specification NLRI class for IPv4 [RFC 5575]
+ """
+ ROUTE_FAMILY = RF_IPv4_FLOW_SPEC
+
+
+class FlowSpecVPNv4NLRI(_FlowSpecNLRIBase):
+ """
+ Flow Specification NLRI class for VPNv4 [RFC 5575]
+ """
+ ROUTE_FAMILY = RF_VPNv4_FLOW_SPEC
+
+
+class _FlowSpecComponentBase(StringifyMixin, TypeDisp):
+ """
+ Base class for Flow Specification NLRI component
+ """
+ _BASE_STR = '!B'
+ _BASE_STR_SIZE = struct.calcsize(_BASE_STR)
+
+ 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
+
+ def __init__(self, type_=None):
+ if type_ is None:
+ type_ = self._rev_lookup_type(self.__class__)
+ self.type = type_
+
+ @classmethod
+ def parse_header(cls, rest):
+ (type_,) = struct.unpack_from(
+ cls._BASE_STR, six.binary_type(rest))
+ rest = rest[cls._BASE_STR_SIZE:]
+ return cls._lookup_type(type_), rest
+
+ def serialize_header(self):
+ return struct.pack(self._BASE_STR, self.type)
+
+
+@_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(_FlowSpecComponentBase, IPAddrPrefix):
+ """
+ Prefix base class for Flow Specification NLRI component
+ """
+
+ def __init__(self, length, addr, type_=None):
+ super(_FlowSpecPrefixBase, self).__init__(type_)
+ self.length = length
+ self.addr = addr
+
+ @classmethod
+ def parse_body(cls, buf):
+ return cls.parser(buf)
+
+ def serialize_body(self):
+ return self.serialize()
+
+
+@_FlowSpecComponentBase.register_type(
+ _FlowSpecComponentBase.TYPE_DESTINATION_PREFIX)
+class FlowSpecDestPrefix(_FlowSpecPrefixBase):
+ """
+ Destination Prefix for Flow Specification NLRI component
+ """
+
+
+@_FlowSpecComponentBase.register_type(
+ _FlowSpecComponentBase.TYPE_SOURCE_PREFIX)
+class FlowSpecSrcPrefix(_FlowSpecPrefixBase):
+ """
+ Source Prefix for Flow Specification NLRI component
+ """
+
+
+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
+ _LENGTH_BIT_MASK = 0x30 # The mask for length of the value
+
+ 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):
+ length = (self.value.bit_length() + 7) // 8 or 1
+ self.operator |= int(math.log(length, 2)) << 4
+ buf = struct.pack(self._OPE_PACK_STR, self.operator)
+ value_type = type_desc.IntDescr(length)
+ buf += struct.pack(self._VAL_PACK_STR % length,
+ value_type.from_user(self.value))
+
+ return buf
+
+
+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
+
+
+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
+
+
+@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_PROTOCOL)
+class FlowSpecIPProtocol(_FlowSpecNumeric):
+ """IP Protocol for Flow Specification NLRI component
+
+ Set the IP protocol number at value.
+ """
+
+
+@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_PORT)
+class FlowSpecPort(_FlowSpecNumeric):
+ """Port number for Flow Specification NLRI component
+
+ Set the source or destination TCP/UDP ports at value.
+ """
+
+
+@_FlowSpecComponentBase.register_type(
+ _FlowSpecComponentBase.TYPE_DESTINATION_PORT)
+class FlowSpecDestPort(_FlowSpecNumeric):
+ """Destination port number for Flow Specification NLRI component
+
+ Set the destination port of a TCP or UDP packet at value.
+ """
+
+
+@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_SOURCE_PORT)
+class FlowSpecSrcPort(_FlowSpecNumeric):
+ """Source port number for Flow Specification NLRI component
+
+ Set the source port of a TCP or UDP packet at value.
+ """
+
+
+@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_ICMP)
+class FlowSpecIcmpType(_FlowSpecNumeric):
+ """ICMP type for Flow Specification NLRI component
+
+ Set the type field of an ICMP packet at value.
+ """
+
+
+@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_ICMP_CODE)
+class FlowSpecIcmpCode(_FlowSpecNumeric):
+ """ICMP code Flow Specification NLRI component
+
+ Set the code field of an ICMP packet at value.
+ """
+
+
+@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_TCP_FLAGS)
+class FlowSpecTCPFlags(_FlowSpecBitmask):
+ """TCP flags for Flow Specification NLRI component
+
+ Supported TCP flags are CWR, ECN, URGENT, ACK, PUSH, RST, SYN and FIN.
+ """
+
+ # bitmask format
+ # 0 1 2 3 4 5 6 7
+ # +----+----+----+----+----+----+----+----+
+ # |CWR |ECN |URG |ACK |PSH |RST |SYN |FIN |
+ # +----+----+----+----+----+----+----+----+
+
+ CWR = 1 << 6
+ ECN = 1 << 5
+ URGENT = 1 << 5
+ ACK = 1 << 4
+ PUSH = 1 << 3
+ RST = 1 << 2
+ SYN = 1 << 1
+ FIN = 1 << 0
+
+
+@_FlowSpecComponentBase.register_type(
+ _FlowSpecComponentBase.TYPE_PACKET_LENGTH)
+class FlowSpecPacketLen(_FlowSpecNumeric):
+ """Packet length for Flow Specification NLRI component
+
+ Set the total IP packet length at value.
+ """
+
+
+@_FlowSpecComponentBase.register_type(
+ _FlowSpecComponentBase.TYPE_DIFFSERV_CODE_POINT)
+class FlowSpecDSCP(_FlowSpecNumeric):
+ """Diffserv Code Point for Flow Specification NLRI component
+
+ Set the 6-bit DSCP field at value. [RFC2474]
+ """
+
+
+@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_FRAGMENT)
+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
+ ========== ===============================================
+ """
+
+ # 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
+
+
@functools.total_ordering
class RouteTargetMembershipNLRI(StringifyMixin):
"""Route Target Membership NLRI.
@@ -2089,6 +2467,8 @@ _ADDR_CLASSES = {
_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_FLOW_SPEC): FlowSpecIPv4NLRI,
+ _addr_class_key(RF_VPNv4_FLOW_SPEC): FlowSpecVPNv4NLRI,
_addr_class_key(RF_RTC_UC): RouteTargetMembershipNLRI,
}
@@ -2824,6 +3204,7 @@ class BGPPathAttributeClusterList(_PathAttribute):
# 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):
@@ -2897,6 +3278,15 @@ class _ExtendedCommunity(StringifyMixin, TypeDisp, _Value):
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
+ SUBTYPE_FLOWSPEC_TRAFFIC_RATE = 0x06
+ SUBTYPE_FLOWSPEC_TRAFFIC_ACTION = 0x07
+ SUBTYPE_FLOWSPEC_REDIRECT = 0x08
+ SUBTYPE_FLOWSPEC_TRAFFIC_REMARKING = 0x09
+ 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)
def __init__(self, type_=None):
if type_ is None:
@@ -3157,6 +3547,105 @@ class BGPEvpnEsImportRTExtendedCommunity(_ExtendedCommunity):
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
+ """
+ # 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']
+
+ def __init__(self, **kwargs):
+ super(BGPFlowSpecTrafficRateCommunity, self).__init__()
+ 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
+ """
+ # 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']
+ SAMPLE = 1 << 1
+ TERMINAL = 1 << 0
+
+ def __init__(self, **kwargs):
+ super(BGPFlowSpecTrafficActionCommunity, self).__init__()
+ self.do_init(BGPFlowSpecTrafficActionCommunity, self, kwargs)
+
+
+@_ExtendedCommunity.register_type(_ExtendedCommunity.FLOWSPEC_REDIRECT)
+class BGPFlowSpecRedirectCommunity(BGPTwoOctetAsSpecificExtendedCommunity):
+ """
+ Flow Specification Traffic Filtering Actions for Redirect
+ """
+
+
+@_ExtendedCommunity.register_type(
+ _ExtendedCommunity.FLOWSPEC_TRAFFIC_REMARKING)
+class BGPFlowSpecTrafficMarkingCommunity(_ExtendedCommunity):
+ """
+ Flow Specification Traffic Filtering Actions for Traffic Marking
+ """
+ # 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']
+
+ def __init__(self, **kwargs):
+ super(BGPFlowSpecTrafficMarkingCommunity, self).__init__()
+ 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)
+
+
@_ExtendedCommunity.register_unknown_type()
class BGPUnknownExtendedCommunity(_ExtendedCommunity):
_VALUE_PACK_STR = '!7s' # opaque value
@@ -3194,7 +3683,10 @@ class BGPPathAttributeMpReachNLRI(_PathAttribute):
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.
- self._next_hop = next_hop[0]
+ 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)
diff --git a/ryu/lib/packet/safi.py b/ryu/lib/packet/safi.py
index 973d3c7b..ac10d354 100644
--- a/ryu/lib/packet/safi.py
+++ b/ryu/lib/packet/safi.py
@@ -25,3 +25,5 @@ MPLS_LABEL = 4 # RFC 3107
EVPN = 70 # RFC 7432
MPLS_VPN = 128 # RFC 4364
ROUTE_TARGET_CONSTRAINTS = 132 # RFC 4684
+IP_FLOW_SPEC = 133 # RFC 5575
+VPN_FLOW_SPEC = 134 # RFC 5575