diff options
author | Shinpei Muraoka <shinpei.muraoka@gmail.com> | 2016-10-27 09:55:21 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2016-10-29 06:32:11 +0900 |
commit | 6e22fb4a05b5aab3a92ec8db9a25613e99c98a7a (patch) | |
tree | 81b022438cd31ed16a659888bcaa5aad8283af1d | |
parent | 5360524b966417d042d441c208a7cf99eb052532 (diff) |
packet/bgp: Add PMSI Tunnel Attribute
This patch adds the support for BGP PMSI Tunnel Attribute [RFC6514].
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.py | 215 | ||||
-rw-r--r-- | ryu/tests/unit/packet/test_bgp.py | 57 |
2 files changed, 272 insertions, 0 deletions
diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py index c980bc45..9d77cdb9 100644 --- a/ryu/lib/packet/bgp.py +++ b/ryu/lib/packet/bgp.py @@ -28,6 +28,7 @@ import functools import numbers import socket import struct +import base64 import six @@ -36,10 +37,14 @@ 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 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 @@ -87,6 +92,7 @@ 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 @@ -3172,6 +3178,215 @@ class BGPPathAttributeMpUnreachNLRI(_PathAttribute): 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 diff --git a/ryu/tests/unit/packet/test_bgp.py b/ryu/tests/unit/packet/test_bgp.py index 2e6b9268..9638457f 100644 --- a/ryu/tests/unit/packet/test_bgp.py +++ b/ryu/tests/unit/packet/test_bgp.py @@ -32,6 +32,13 @@ from ryu.lib.packet import safi BGP4_PACKET_DATA_DIR = os.path.join( os.path.dirname(sys.modules[__name__].__file__), '../../packet_data/bgp4/') +PMSI_TYPE_NO_TUNNEL_INFORMATION_PRESENT = ( + bgp.BGPPathAttributePmsiTunnel.TYPE_NO_TUNNEL_INFORMATION_PRESENT +) +PMSI_TYPE_INGRESS_REPLICATION = ( + bgp.BGPPathAttributePmsiTunnel.TYPE_INGRESS_REPLICATION +) + class Test_bgp(unittest.TestCase): """ Test case for ryu.lib.packet.bgp @@ -140,6 +147,31 @@ class Test_bgp(unittest.TestCase): bgp.BGPPathAttributeOriginatorId(value='10.1.1.1'), bgp.BGPPathAttributeClusterList(value=['1.1.1.1', '2.2.2.2']), bgp.BGPPathAttributeExtendedCommunities(communities=ecommunities), + bgp.BGPPathAttributePmsiTunnel( + pmsi_flags=1, + tunnel_type=PMSI_TYPE_NO_TUNNEL_INFORMATION_PRESENT, + label=b'\xFF\xFF\xFF'), + bgp.BGPPathAttributePmsiTunnel( + pmsi_flags=1, + tunnel_type=PMSI_TYPE_NO_TUNNEL_INFORMATION_PRESENT, + tunnel_id=None), + bgp.BGPPathAttributePmsiTunnel( + pmsi_flags=1, + tunnel_type=PMSI_TYPE_INGRESS_REPLICATION, + mpls_label=0xfffff, + tunnel_id=bgp.PmsiTunnelIdIngressReplication( + tunnel_endpoint_ip="1.1.1.1")), + bgp.BGPPathAttributePmsiTunnel( + pmsi_flags=1, + tunnel_type=PMSI_TYPE_INGRESS_REPLICATION, + vni=0xffffff, + tunnel_id=bgp.PmsiTunnelIdIngressReplication( + tunnel_endpoint_ip="aa:bb:cc::dd:ee:ff")), + bgp.BGPPathAttributePmsiTunnel( + pmsi_flags=1, + tunnel_type=2, + label=b'\xFF\xFF\xFF', + tunnel_id=bgp.PmsiTunnelIdUnknown(value=b'test')), bgp.BGPPathAttributeAs4Path(value=[[1000000], {1000001, 1002}, [1003, 1000004]]), bgp.BGPPathAttributeAs4Aggregator(as_number=100040000, @@ -304,6 +336,31 @@ class Test_bgp(unittest.TestCase): addr='192.0.2.99'), bgp.BGPPathAttributeCommunities(communities=communities), bgp.BGPPathAttributeExtendedCommunities(communities=ecommunities), + bgp.BGPPathAttributePmsiTunnel( + pmsi_flags=1, + tunnel_type=PMSI_TYPE_NO_TUNNEL_INFORMATION_PRESENT, + label=b'\xFF\xFF\xFF'), + bgp.BGPPathAttributePmsiTunnel( + pmsi_flags=1, + tunnel_type=PMSI_TYPE_NO_TUNNEL_INFORMATION_PRESENT, + tunnel_id=None), + bgp.BGPPathAttributePmsiTunnel( + pmsi_flags=1, + tunnel_type=PMSI_TYPE_INGRESS_REPLICATION, + mpls_label=0xfffff, + tunnel_id=bgp.PmsiTunnelIdIngressReplication( + tunnel_endpoint_ip="1.1.1.1")), + bgp.BGPPathAttributePmsiTunnel( + pmsi_flags=1, + tunnel_type=PMSI_TYPE_INGRESS_REPLICATION, + vni=0xffffff, + tunnel_id=bgp.PmsiTunnelIdIngressReplication( + tunnel_endpoint_ip="aa:bb:cc::dd:ee:ff")), + bgp.BGPPathAttributePmsiTunnel( + pmsi_flags=1, + tunnel_type=2, + label=b'\xFF\xFF\xFF', + tunnel_id=bgp.PmsiTunnelIdUnknown(value=b'test')), bgp.BGPPathAttributeAs4Path(value=[[1000000], {1000001, 1002}, [1003, 1000004]]), bgp.BGPPathAttributeAs4Aggregator(as_number=100040000, |