summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorShinpei Muraoka <shinpei.muraoka@gmail.com>2016-10-27 09:55:21 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2016-10-29 06:32:11 +0900
commit6e22fb4a05b5aab3a92ec8db9a25613e99c98a7a (patch)
tree81b022438cd31ed16a659888bcaa5aad8283af1d
parent5360524b966417d042d441c208a7cf99eb052532 (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.py215
-rw-r--r--ryu/tests/unit/packet/test_bgp.py57
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,