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>
This commit is contained in:
Shinpei Muraoka 2017-02-09 16:29:22 +09:00 committed by FUJITA Tomonori
parent 838708a32a
commit 79741fdc88
2 changed files with 496 additions and 2 deletions

View File

@ -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)

View File

@ -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