This patch adds the support for BGP PMSI Tunnel Attribute [RFC6514].
Signed-off-by: Shinpei Muraoka <[email protected]>
---
ryu/lib/packet/bgp.py | 215 ++++++++++++++++++++++++++++++++++++++
ryu/tests/unit/packet/test_bgp.py | 57 ++++++++++
2 files changed, 272 insertions(+)
diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py
index c980bc4..9d77cdb 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 2e6b926..9638457 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,
--
2.7.4
------------------------------------------------------------------------------
The Command Line: Reinvented for Modern Developers
Did the resurgence of CLI tooling catch you by surprise?
Reconnect with the command line and become more productive.
Learn the new .NET and ASP.NET CLI. Get your free copy!
http://sdm.link/telerik
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel