summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ryu/lib/packet/bgp.py153
-rw-r--r--ryu/services/protocols/bgp/api/base.py1
-rw-r--r--ryu/services/protocols/bgp/api/prefix.py12
-rw-r--r--ryu/services/protocols/bgp/bgpspeaker.py16
-rw-r--r--ryu/services/protocols/bgp/core_managers/table_manager.py7
-rw-r--r--ryu/services/protocols/bgp/info_base/vpn.py7
-rw-r--r--ryu/services/protocols/bgp/info_base/vrf.py14
-rw-r--r--ryu/services/protocols/bgp/utils/validation.py9
8 files changed, 174 insertions, 45 deletions
diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py
index 6d7cf3f6..c980bc45 100644
--- a/ryu/lib/packet/bgp.py
+++ b/ryu/lib/packet/bgp.py
@@ -1446,6 +1446,14 @@ class EvpnNLRI(StringifyMixin, _TypeDisp):
label = label << 4 | 1
return six.binary_type(_LabelledAddrPrefix._label_to_bin(label))
+ @staticmethod
+ def _vni_from_bin(buf):
+ return type_desc.Int3.to_user(six.binary_type(buf[:3])), buf[3:]
+
+ @staticmethod
+ def _vni_to_bin(vni):
+ return type_desc.Int3.from_user(vni)
+
@property
def prefix(self):
def _format(i):
@@ -1509,34 +1517,78 @@ class EvpnEthernetAutoDiscoveryNLRI(EvpnNLRI):
# +---------------------------------------+
_PACK_STR = "!8s10sI3s"
NLRI_PREFIX_FIELDS = ['esi', 'ethernet_tag_id']
+ _TYPE = {
+ 'ascii': [
+ 'route_dist',
+ ]
+ }
- def __init__(self, route_dist, esi, ethernet_tag_id, mpls_label,
+ def __init__(self, route_dist, esi, ethernet_tag_id,
+ mpls_label=None, vni=None, label=None,
type_=None, length=None):
super(EvpnEthernetAutoDiscoveryNLRI, self).__init__(type_, length)
self.route_dist = route_dist
self.esi = esi
self.ethernet_tag_id = ethernet_tag_id
- self.mpls_label = mpls_label
+ 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, _, _ = self._mpls_label_from_bin(label)
+ self._vni, _ = self._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
+
+ def _serialize_label(self, mpls_label, vni):
+ if mpls_label:
+ return self._mpls_label_to_bin(mpls_label, is_stack=True)
+ elif vni:
+ return self._vni_to_bin(vni)
+ else:
+ return b'\x00' * 3
@classmethod
def parse_value(cls, buf):
route_dist, rest = cls._rd_from_bin(buf)
esi, rest = cls._esi_from_bin(rest)
ethernet_tag_id, rest = cls._ethernet_tag_id_from_bin(rest)
- mpls_label, rest, _ = cls._mpls_label_from_bin(rest)
return {
'route_dist': route_dist.formatted_str,
'esi': esi,
'ethernet_tag_id': ethernet_tag_id,
- 'mpls_label': mpls_label,
+ 'label': rest,
}
def serialize_value(self):
route_dist = _RouteDistinguisher.from_str(self.route_dist)
return struct.pack(
self._PACK_STR, route_dist.serialize(), self.esi.serialize(),
- self.ethernet_tag_id, self._mpls_label_to_bin(self.mpls_label))
+ self.ethernet_tag_id, self._label)
+
+ @property
+ def mpls_label(self):
+ return self._mpls_label
+
+ @mpls_label.setter
+ def mpls_label(self, mpls_label):
+ self._label = self._mpls_label_to_bin(mpls_label, is_stack=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 = self._vni_to_bin(vni)
+ self._mpls_label = None # disables MPLS label
+ self._vni = vni
@property
def label_list(self):
@@ -1569,18 +1621,20 @@ class EvpnMacIPAdvertisementNLRI(EvpnNLRI):
# +---------------------------------------+
# | MPLS Label2 (0 or 3 octets) |
# +---------------------------------------+
- _PACK_STR = "!8s10sIB6sB%ds3s%ds"
+ _PACK_STR = "!8s10sIB6sB%ds%ds"
# Note: mac_addr_len and ip_addr_len are omitted for readability.
NLRI_PREFIX_FIELDS = ['ethernet_tag_id', 'mac_addr', 'ip_addr']
_TYPE = {
'ascii': [
+ 'route_dist',
'mac_addr',
'ip_addr',
]
}
def __init__(self, route_dist, esi, ethernet_tag_id, mac_addr, ip_addr,
- mpls_labels, mac_addr_len=None, ip_addr_len=None,
+ mpls_labels=None, vni=None, labels=None,
+ mac_addr_len=None, ip_addr_len=None,
type_=None, length=None):
super(EvpnMacIPAdvertisementNLRI, self).__init__(type_, length)
self.route_dist = route_dist
@@ -1590,7 +1644,43 @@ class EvpnMacIPAdvertisementNLRI(EvpnNLRI):
self.mac_addr = mac_addr
self.ip_addr_len = ip_addr_len
self.ip_addr = ip_addr
- self.mpls_labels = mpls_labels
+ if labels:
+ # If binary type labels field value is specified, stores it
+ # and decodes as MPLS labels and VNI.
+ self._mpls_labels, self._vni = self._parse_labels(labels)
+ self._labels = labels
+ else:
+ # If either MPLS labels or VNI is specified, stores it
+ # and encodes into binary type labels field value.
+ self._labels = self._serialize_labels(mpls_labels, vni)
+ self._mpls_labels = mpls_labels
+ self._vni = vni
+
+ def _parse_labels(self, labels):
+ mpls_label1, rest, is_stack = self._mpls_label_from_bin(labels)
+ mpls_labels = [mpls_label1]
+ if rest and is_stack:
+ mpls_label2, rest, _ = self._mpls_label_from_bin(rest)
+ mpls_labels.append(mpls_label2)
+ vni, _ = self._vni_from_bin(labels)
+ return mpls_labels, vni
+
+ def _serialize_labels(self, mpls_labels, vni):
+ if mpls_labels:
+ return self._serialize_mpls_labels(mpls_labels)
+ elif vni:
+ return self._vni_to_bin(vni)
+ else:
+ return b'\x00' * 3
+
+ def _serialize_mpls_labels(self, mpls_labels):
+ if len(mpls_labels) == 1:
+ return self._mpls_label_to_bin(mpls_labels[0], is_stack=False)
+ elif len(mpls_labels) == 2:
+ return (self._mpls_label_to_bin(mpls_labels[0], is_stack=True) +
+ self._mpls_label_to_bin(mpls_labels[1], is_stack=False))
+ else:
+ return b'\x00' * 3
@classmethod
def parse_value(cls, buf):
@@ -1604,11 +1694,6 @@ class EvpnMacIPAdvertisementNLRI(EvpnNLRI):
ip_addr, rest = cls._ip_addr_from_bin(rest, ip_addr_len)
else:
ip_addr = None
- mpls_label1, rest, is_stack = cls._mpls_label_from_bin(rest)
- mpls_labels = [mpls_label1]
- if rest and is_stack:
- mpls_label2, rest, _ = cls._mpls_label_from_bin(rest)
- mpls_labels.append(mpls_label2)
return {
'route_dist': route_dist.formatted_str,
@@ -1618,7 +1703,7 @@ class EvpnMacIPAdvertisementNLRI(EvpnNLRI):
'mac_addr': mac_addr,
'ip_addr_len': ip_addr_len,
'ip_addr': ip_addr,
- 'mpls_labels': mpls_labels,
+ 'labels': rest,
}
def serialize_value(self):
@@ -1631,24 +1716,34 @@ class EvpnMacIPAdvertisementNLRI(EvpnNLRI):
ip_addr = b''
ip_addr_len = len(ip_addr)
self.ip_addr_len = ip_addr_len * 8 # fixup
- mpls_label1 = b''
- mpls_label2 = b''
- if len(self.mpls_labels) == 1:
- mpls_label1 = self._mpls_label_to_bin(self.mpls_labels[0],
- is_stack=False)
- elif len(self.mpls_labels) == 2:
- mpls_label1 = self._mpls_label_to_bin(self.mpls_labels[0],
- is_stack=True)
- mpls_label2 = self._mpls_label_to_bin(self.mpls_labels[1],
- is_stack=False)
return struct.pack(
- self._PACK_STR % (ip_addr_len, len(mpls_label2)),
+ self._PACK_STR % (ip_addr_len, len(self._labels)),
route_dist.serialize(), self.esi.serialize(),
self.ethernet_tag_id,
self.mac_addr_len, mac_addr,
self.ip_addr_len, ip_addr,
- mpls_label1, mpls_label2)
+ self._labels)
+
+ @property
+ def mpls_labels(self):
+ return self._mpls_labels
+
+ @mpls_labels.setter
+ def mpls_labels(self, mpls_labels):
+ self._labels = self._serialize_mpls_labels(mpls_labels)
+ self._mpls_labels = mpls_labels
+ self._vni = None # disables VNI
+
+ @property
+ def vni(self):
+ return self._vni
+
+ @vni.setter
+ def vni(self, vni):
+ self._labels = self._vni_to_bin(vni)
+ self._mpls_labels = None # disables MPLS labels
+ self._vni = vni
@property
def label_list(self):
@@ -1676,7 +1771,8 @@ class EvpnInclusiveMulticastEthernetTagNLRI(EvpnNLRI):
NLRI_PREFIX_FIELDS = ['ethernet_tag_id', 'ip_addr']
_TYPE = {
'ascii': [
- 'ip_addr'
+ 'route_dist',
+ 'ip_addr',
]
}
@@ -1735,7 +1831,8 @@ class EvpnEthernetSegmentNLRI(EvpnNLRI):
NLRI_PREFIX_FIELDS = ['esi', 'ip_addr']
_TYPE = {
'ascii': [
- 'ip_addr'
+ 'route_dist',
+ 'ip_addr',
]
}
diff --git a/ryu/services/protocols/bgp/api/base.py b/ryu/services/protocols/bgp/api/base.py
index 49e78063..9007da0f 100644
--- a/ryu/services/protocols/bgp/api/base.py
+++ b/ryu/services/protocols/bgp/api/base.py
@@ -50,6 +50,7 @@ MAC_ADDR = 'mac_addr'
IP_ADDR = 'ip_addr'
MPLS_LABELS = 'mpls_labels'
TUNNEL_TYPE = 'tunnel_type'
+EVPN_VNI = 'vni'
# API call registry
_CALL_REGISTRY = {}
diff --git a/ryu/services/protocols/bgp/api/prefix.py b/ryu/services/protocols/bgp/api/prefix.py
index 2bcb4e08..8963f513 100644
--- a/ryu/services/protocols/bgp/api/prefix.py
+++ b/ryu/services/protocols/bgp/api/prefix.py
@@ -31,6 +31,7 @@ from ryu.services.protocols.bgp.api.base import PREFIX
from ryu.services.protocols.bgp.api.base import RegisterWithArgChecks
from ryu.services.protocols.bgp.api.base import ROUTE_DISTINGUISHER
from ryu.services.protocols.bgp.api.base import VPN_LABEL
+from ryu.services.protocols.bgp.api.base import EVPN_VNI
from ryu.services.protocols.bgp.api.base import TUNNEL_TYPE
from ryu.services.protocols.bgp.base import add_bgp_error_metadata
from ryu.services.protocols.bgp.base import PREFIX_ERROR_CODE
@@ -137,6 +138,13 @@ def is_valid_mpls_labels(labels):
conf_value=labels)
+@validate(name=EVPN_VNI)
+def is_valid_vni(vni):
+ if not validation.is_valid_vni(vni):
+ raise ConfigValueError(conf_name=EVPN_VNI,
+ conf_value=vni)
+
+
@validate(name=TUNNEL_TYPE)
def is_valid_tunnel_type(tunnel_type):
if tunnel_type not in SUPPORTED_TUNNEL_TYPES:
@@ -193,7 +201,7 @@ def delete_local(route_dist, prefix, route_family=VRF_RF_IPV4):
req_args=[EVPN_ROUTE_TYPE, ROUTE_DISTINGUISHER,
NEXT_HOP],
opt_args=[EVPN_ESI, EVPN_ETHERNET_TAG_ID, MAC_ADDR,
- IP_ADDR, TUNNEL_TYPE])
+ IP_ADDR, EVPN_VNI, TUNNEL_TYPE])
def add_evpn_local(route_type, route_dist, next_hop, **kwargs):
"""Adds EVPN route from VRF identified by *route_dist*.
"""
@@ -220,7 +228,7 @@ def add_evpn_local(route_type, route_dist, next_hop, **kwargs):
@RegisterWithArgChecks(name='evpn_prefix.delete_local',
req_args=[EVPN_ROUTE_TYPE, ROUTE_DISTINGUISHER],
opt_args=[EVPN_ESI, EVPN_ETHERNET_TAG_ID, MAC_ADDR,
- IP_ADDR])
+ IP_ADDR, EVPN_VNI])
def delete_evpn_local(route_type, route_dist, **kwargs):
"""Deletes/withdraws EVPN route from VRF identified by *route_dist*.
"""
diff --git a/ryu/services/protocols/bgp/bgpspeaker.py b/ryu/services/protocols/bgp/bgpspeaker.py
index 879eaf24..62ba1462 100644
--- a/ryu/services/protocols/bgp/bgpspeaker.py
+++ b/ryu/services/protocols/bgp/bgpspeaker.py
@@ -31,10 +31,12 @@ from ryu.services.protocols.bgp.api.base import MAC_ADDR
from ryu.services.protocols.bgp.api.base import NEXT_HOP
from ryu.services.protocols.bgp.api.base import ROUTE_DISTINGUISHER
from ryu.services.protocols.bgp.api.base import ROUTE_FAMILY
+from ryu.services.protocols.bgp.api.base import EVPN_VNI
from ryu.services.protocols.bgp.api.base import TUNNEL_TYPE
from ryu.services.protocols.bgp.api.prefix import EVPN_MAC_IP_ADV_ROUTE
from ryu.services.protocols.bgp.api.prefix import EVPN_MULTICAST_ETAG_ROUTE
-from ryu.services.protocols.bgp.api.prefix import TUNNEL_TYPE_MPLS
+from ryu.services.protocols.bgp.api.prefix import TUNNEL_TYPE_VXLAN
+from ryu.services.protocols.bgp.api.prefix import TUNNEL_TYPE_NVGRE
from ryu.services.protocols.bgp.rtconf.common import LOCAL_AS
from ryu.services.protocols.bgp.rtconf.common import ROUTER_ID
from ryu.services.protocols.bgp.rtconf.common import BGP_SERVER_PORT
@@ -492,7 +494,7 @@ class BGPSpeaker(object):
def evpn_prefix_add(self, route_type, route_dist, esi=0,
ethernet_tag_id=None, mac_addr=None, ip_addr=None,
- next_hop=None, tunnel_type=TUNNEL_TYPE_MPLS):
+ vni=None, next_hop=None, tunnel_type=None):
""" This method adds a new EVPN route to be advertised.
``route_type`` specifies one of the EVPN route type name. The
@@ -510,10 +512,15 @@ class BGPSpeaker(object):
``ip_addr`` specifies an IPv4 or IPv6 address to advertise.
+ ``vni`` specifies an Virtual Network Identifier for VXLAN
+ or Virtual Subnet Identifier for NVGRE.
+ If tunnel_type is not 'vxlan' or 'nvgre', this field is ignored.
+
``next_hop`` specifies the next hop address for this prefix.
``tunnel_type`` specifies the data plane encapsulation type
- to advertise. The default is the MPLS encapsulation type.
+ to advertise. By the default, this encapsulation attribute is
+ not advertised.
"""
func_name = 'evpn_prefix.add_local'
@@ -538,6 +545,9 @@ class BGPSpeaker(object):
MAC_ADDR: mac_addr,
IP_ADDR: ip_addr,
})
+ # Set tunnel type specific arguments
+ if tunnel_type in [TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE]:
+ kwargs[EVPN_VNI] = vni
elif route_type == EVPN_MULTICAST_ETAG_ROUTE:
kwargs.update({
EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
diff --git a/ryu/services/protocols/bgp/core_managers/table_manager.py b/ryu/services/protocols/bgp/core_managers/table_manager.py
index 5320dbfa..4b958537 100644
--- a/ryu/services/protocols/bgp/core_managers/table_manager.py
+++ b/ryu/services/protocols/bgp/core_managers/table_manager.py
@@ -538,6 +538,13 @@ class TableCoreManager(object):
if esi is not None:
# Note: Currently, we support arbitrary 9-octet ESI value only.
kwargs['esi'] = EvpnArbitraryEsi(type_desc.Int9.from_user(esi))
+ if 'vni' in kwargs:
+ # Disable to generate MPLS labels, because encapsulation type
+ # is not MPLS.
+ from ryu.services.protocols.bgp.api.prefix import (
+ TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE)
+ assert tunnel_type in [TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE]
+ gen_lbl = False
prefix = subclass(**kwargs)
else:
raise BgpCoreError(
diff --git a/ryu/services/protocols/bgp/info_base/vpn.py b/ryu/services/protocols/bgp/info_base/vpn.py
index 46cf47fb..87c44376 100644
--- a/ryu/services/protocols/bgp/info_base/vpn.py
+++ b/ryu/services/protocols/bgp/info_base/vpn.py
@@ -65,10 +65,9 @@ class VpnPath(Path):
def clone_to_vrf(self, is_withdraw=False):
if self.ROUTE_FAMILY == RF_L2_EVPN:
- nlri_cls = self.NLRI_CLASS._lookup_type(self._nlri.type)
- kwargs = dict(self._nlri.__dict__)
- kwargs.pop('type', None)
- vrf_nlri = nlri_cls(**kwargs)
+ # Because NLRI class is the same if the route family is EVPN,
+ # we re-use the NLRI instance.
+ vrf_nlri = self._nlri
else: # self.ROUTE_FAMILY in [RF_IPv4_VPN, RF_IPv46_VPN]
vrf_nlri = self.NLRI_CLASS(self._nlri.prefix)
diff --git a/ryu/services/protocols/bgp/info_base/vrf.py b/ryu/services/protocols/bgp/info_base/vrf.py
index 6a60fc82..5e22e299 100644
--- a/ryu/services/protocols/bgp/info_base/vrf.py
+++ b/ryu/services/protocols/bgp/info_base/vrf.py
@@ -157,10 +157,9 @@ class VrfTable(Table):
source = VRF_TABLE
if self.VPN_ROUTE_FAMILY == RF_L2_EVPN:
- nlri_cls = self.NLRI_CLASS._lookup_type(vpn_path.nlri.type)
- kwargs = dict(vpn_path.nlri.__dict__)
- kwargs.pop('type', None)
- vrf_nlri = nlri_cls(**kwargs)
+ # Because NLRI class is the same if the route family is EVPN,
+ # we re-use the NLRI instance.
+ vrf_nlri = vpn_path.nlri
else: # self.VPN_ROUTE_FAMILY in [RF_IPv4_VPN, RF_IPv6_VPN]
# Copy NLRI instance
ip, masklen = vpn_path.nlri.prefix.split('/')
@@ -528,10 +527,9 @@ class VrfPath(Path):
def clone_to_vpn(self, route_dist, for_withdrawal=False):
if self.ROUTE_FAMILY == RF_L2_EVPN:
- nlri_cls = self.VPN_NLRI_CLASS._lookup_type(self._nlri.type)
- kwargs = dict(self._nlri.__dict__)
- kwargs.pop('type', None)
- vpn_nlri = nlri_cls(**kwargs)
+ # Because NLRI class is the same if the route family is EVPN,
+ # we re-use the NLRI instance.
+ vpn_nlri = self._nlri
else: # self.ROUTE_FAMILY in [RF_IPv4_UC, RF_IPv6_UC]
ip, masklen = self._nlri.prefix.split('/')
vpn_nlri = self.VPN_NLRI_CLASS(length=int(masklen),
diff --git a/ryu/services/protocols/bgp/utils/validation.py b/ryu/services/protocols/bgp/utils/validation.py
index 6cf292da..ff6ed506 100644
--- a/ryu/services/protocols/bgp/utils/validation.py
+++ b/ryu/services/protocols/bgp/utils/validation.py
@@ -250,3 +250,12 @@ def is_valid_ethernet_tag_id(etag_id):
Ethernet Tag ID should be a 32-bit field number.
"""
return isinstance(etag_id, numbers.Integral) and 0 <= etag_id <= 0xffffffff
+
+
+def is_valid_vni(vni):
+ """Returns True if the given Virtual Network Identifier for VXLAN
+ is valid.
+
+ Virtual Network Identifier should be a 24-bit field number.
+ """
+ return isinstance(vni, numbers.Integral) and 0 <= vni <= 0xffffff