From bb38fb2f3032325f5dc723fe14913fa563205f19 Mon Sep 17 00:00:00 2001 From: Satoshi Fujimoto Date: Tue, 9 May 2017 16:09:45 +0900 Subject: packet/bgp: Support IPv6 Flow Spec Signed-off-by: Satoshi Fujimoto Signed-off-by: FUJITA Tomonori --- ryu/lib/packet/bgp.py | 361 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 324 insertions(+), 37 deletions(-) diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py index d9d70c23..29c5963f 100644 --- a/ryu/lib/packet/bgp.py +++ b/ryu/lib/packet/bgp.py @@ -574,7 +574,9 @@ 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_RTC_UC = RouteFamily(addr_family.IP, subaddr_family.ROUTE_TARGET_CONSTRAINTS) @@ -587,7 +589,9 @@ _rf_map = { (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.IP, subaddr_family.ROUTE_TARGET_CONSTRAINTS): RF_RTC_UC } @@ -2035,7 +2039,8 @@ class _FlowSpecNLRIBase(StringifyMixin, TypeDisp): rules = [] while rest: - subcls, rest = _FlowSpecComponentBase.parse_header(rest) + subcls, rest = _FlowSpecComponentBase.parse_header( + rest, cls.ROUTE_FAMILY.afi) while rest: rule, rest = subcls.parse_body(rest) @@ -2080,7 +2085,8 @@ class _FlowSpecNLRIBase(StringifyMixin, TypeDisp): def _from_user(cls, **kwargs): rules = [] for k, v in kwargs.items(): - subcls = _FlowSpecComponentBase.lookup_type_name(k) + 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) @@ -2236,7 +2242,8 @@ class FlowSpecVPNv4NLRI(_FlowSpecNLRIBase): def _from_user(cls, route_dist, **kwargs): rules = [] for k, v in kwargs.items(): - subcls = _FlowSpecComponentBase.lookup_type_name(k) + 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) @@ -2277,6 +2284,107 @@ class FlowSpecVPNv4NLRI(_FlowSpecNLRIBase): 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 _FlowSpecComponentBase(StringifyMixin, TypeDisp): """ Base class for Flow Specification NLRI component @@ -2286,19 +2394,6 @@ class _FlowSpecComponentBase(StringifyMixin, TypeDisp): _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 - # Dictionary of COMPONENT_NAME to subclass. # e.g.) # _NAMES = {'dst_prefix': FlowSpecDestPrefix, ...} @@ -2306,37 +2401,81 @@ class _FlowSpecComponentBase(StringifyMixin, TypeDisp): def __init__(self, type_=None): if type_ is None: - type_ = self._rev_lookup_type(self.__class__) + type_, _ = self._rev_lookup_type(self.__class__) self.type = type_ @classmethod - def register_type(cls, type_): + def register_type(cls, type_, afi): cls._TYPES = cls._TYPES.copy() cls._NAMES = cls._NAMES.copy() def _register_type(subcls): - cls._TYPES[type_] = subcls - cls._NAMES[subcls.COMPONENT_NAME] = 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): - return cls._NAMES[type_name] + def lookup_type_name(cls, type_name, afi): + return cls._NAMES[(type_name, afi)] @classmethod - def parse_header(cls, rest): + 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_), rest + 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 + + @_FlowSpecComponentBase.register_unknown_type() class FlowSpecComponentUnknown(_FlowSpecComponentBase): """ @@ -2355,7 +2494,7 @@ class FlowSpecComponentUnknown(_FlowSpecComponentBase): return self.buf -class _FlowSpecPrefixBase(_FlowSpecComponentBase, IPAddrPrefix): +class _FlowSpecPrefixBase(_FlowSpecIPv4Component, IPAddrPrefix): """ Prefix base class for Flow Specification NLRI component """ @@ -2388,8 +2527,63 @@ class _FlowSpecPrefixBase(_FlowSpecComponentBase, IPAddrPrefix): 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 + + @_FlowSpecComponentBase.register_type( - _FlowSpecComponentBase.TYPE_DESTINATION_PREFIX) + _FlowSpecIPv4Component.TYPE_DESTINATION_PREFIX, addr_family.IP) class FlowSpecDestPrefix(_FlowSpecPrefixBase): """ Destination Prefix for Flow Specification NLRI component @@ -2398,7 +2592,7 @@ class FlowSpecDestPrefix(_FlowSpecPrefixBase): @_FlowSpecComponentBase.register_type( - _FlowSpecComponentBase.TYPE_SOURCE_PREFIX) + _FlowSpecIPv4Component.TYPE_SOURCE_PREFIX, addr_family.IP) class FlowSpecSrcPrefix(_FlowSpecPrefixBase): """ Source Prefix for Flow Specification NLRI component @@ -2406,6 +2600,24 @@ class FlowSpecSrcPrefix(_FlowSpecPrefixBase): 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 @@ -2604,7 +2816,8 @@ class _FlowSpecBitmask(_FlowSpecOperatorBase): return string -@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_PROTOCOL) +@_FlowSpecComponentBase.register_type( + _FlowSpecIPv4Component.TYPE_PROTOCOL, addr_family.IP) class FlowSpecIPProtocol(_FlowSpecNumeric): """IP Protocol for Flow Specification NLRI component @@ -2613,7 +2826,20 @@ class FlowSpecIPProtocol(_FlowSpecNumeric): COMPONENT_NAME = 'ip_proto' -@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_PORT) +@_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 @@ -2623,7 +2849,9 @@ class FlowSpecPort(_FlowSpecNumeric): @_FlowSpecComponentBase.register_type( - _FlowSpecComponentBase.TYPE_DESTINATION_PORT) + _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 @@ -2632,7 +2860,10 @@ class FlowSpecDestPort(_FlowSpecNumeric): COMPONENT_NAME = 'dst_port' -@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_SOURCE_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 @@ -2641,7 +2872,10 @@ class FlowSpecSrcPort(_FlowSpecNumeric): COMPONENT_NAME = 'src_port' -@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_ICMP) +@_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 @@ -2650,7 +2884,10 @@ class FlowSpecIcmpType(_FlowSpecNumeric): COMPONENT_NAME = 'icmp_type' -@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_ICMP_CODE) +@_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 @@ -2659,7 +2896,10 @@ class FlowSpecIcmpCode(_FlowSpecNumeric): COMPONENT_NAME = 'icmp_code' -@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_TCP_FLAGS) +@_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 @@ -2694,7 +2934,9 @@ class FlowSpecTCPFlags(_FlowSpecBitmask): @_FlowSpecComponentBase.register_type( - _FlowSpecComponentBase.TYPE_PACKET_LENGTH) + _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 @@ -2704,7 +2946,9 @@ class FlowSpecPacketLen(_FlowSpecNumeric): @_FlowSpecComponentBase.register_type( - _FlowSpecComponentBase.TYPE_DIFFSERV_CODE_POINT) + _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 @@ -2713,7 +2957,8 @@ class FlowSpecDSCP(_FlowSpecNumeric): COMPONENT_NAME = 'dscp' -@_FlowSpecComponentBase.register_type(_FlowSpecComponentBase.TYPE_FRAGMENT) +@_FlowSpecComponentBase.register_type( + _FlowSpecIPv4Component.TYPE_FRAGMENT, addr_family.IP) class FlowSpecFragment(_FlowSpecBitmask): """Fragment for Flow Specification NLRI component @@ -2749,6 +2994,43 @@ class FlowSpecFragment(_FlowSpecBitmask): _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( + _FlowSpecIPv6Component.TYPE_FLOW_LABEL, addr_family.IP6) +class FlowSpecIPv6FlowLabel(_FlowSpecNumeric): + COMPONENT_NAME = 'flow_label' + + @functools.total_ordering class RouteTargetMembershipNLRI(StringifyMixin): """Route Target Membership NLRI. @@ -2860,7 +3142,9 @@ _ADDR_CLASSES = { _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_RTC_UC): RouteTargetMembershipNLRI, } @@ -4077,6 +4361,9 @@ class BGPFlowSpecTrafficMarkingCommunity(_ExtendedCommunity): return struct.pack(self._VALUE_PACK_STR, self.subtype, self.dscp) +# TODO +# Implement "Redirect-IPv6" [draft-ietf-idr-flow-spec-v6-08] + @_ExtendedCommunity.register_unknown_type() class BGPUnknownExtendedCommunity(_ExtendedCommunity): _VALUE_PACK_STR = '!7s' # opaque value -- cgit v1.2.3