summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorIWASE Yusuke <iwase.yusuke0@gmail.com>2016-11-22 11:20:22 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2016-12-10 14:57:49 +0900
commit9e5f9d9d378772679da1e7e01cce3a8e1dcbbf40 (patch)
treef7960077700ba36c3065db57c80f092f176f2225
parentad5ce12e706193c93c2c8acaafeaa5b1c64f0f17 (diff)
packet/bgp: Support multiple next_hop in BGPPathAttributeMpReachNLRI
In case of the IPv6 address family in MP-BGP, NLRI might has multipule next_hop addresses (e.g., one is global unicast address and the other is link local unicast address). This patch fixes to support multipule next_hop addresses in MP-BGP NLRIs. For the backward compatibility, this patch make it to stor the first next_hop address as next_hop attribute and all next_hop addresses as next_hop_list. Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r--ryu/lib/packet/bgp.py103
-rw-r--r--ryu/tests/packet_data/bgp4/bgp4-update_ipv6.pcapbin168 -> 233 bytes
-rw-r--r--ryu/tests/unit/packet/test_bgp.py42
3 files changed, 125 insertions, 20 deletions
diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py
index a2b875a7..26a805d6 100644
--- a/ryu/lib/packet/bgp.py
+++ b/ryu/lib/packet/bgp.py
@@ -25,6 +25,7 @@ RFC 4271 BGP-4
import abc
import copy
import functools
+import io
import socket
import struct
import base64
@@ -3194,6 +3195,7 @@ class BGPPathAttributeMpReachNLRI(_PathAttribute):
_RESERVED_LENGTH = 1
_ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
_class_suffixes = ['AddrPrefix']
+ _opt_attributes = ['next_hop']
_TYPE = {
'ascii': [
'next_hop'
@@ -3206,10 +3208,15 @@ class BGPPathAttributeMpReachNLRI(_PathAttribute):
flags=flags, type_=type_, length=length)
self.afi = afi
self.safi = safi
- if (not netaddr.valid_ipv4(next_hop)
- and not netaddr.valid_ipv6(next_hop)):
- raise ValueError('Invalid address for next_hop: %s' % next_hop)
- self.next_hop = next_hop
+ if not isinstance(next_hop, (list, tuple)):
+ next_hop = [next_hop]
+ for n in next_hop:
+ if not netaddr.valid_ipv4(n) and not netaddr.valid_ipv6(n):
+ raise ValueError('Invalid address for next_hop: %s' % n)
+ # Note: For the backward compatibility, stores the first next_hop
+ # address and all next_hop addresses separately.
+ self._next_hop = next_hop[0]
+ self._next_hop_list = next_hop
self.nlri = nlri
addr_cls = _get_addr_class(afi, safi)
for i in nlri:
@@ -3217,6 +3224,25 @@ class BGPPathAttributeMpReachNLRI(_PathAttribute):
raise ValueError('Invalid NRLI class for afi=%d and safi=%d'
% (self.afi, self.safi))
+ @staticmethod
+ def split_bin_with_len(buf, unit_len):
+ f = io.BytesIO(buf)
+ return [f.read(unit_len) for _ in range(0, len(buf), unit_len)]
+
+ @classmethod
+ def parse_next_hop_ipv4(cls, buf, unit_len):
+ next_hop = []
+ for next_hop_bin in cls.split_bin_with_len(buf, unit_len):
+ next_hop.append(addrconv.ipv4.bin_to_text(next_hop_bin[-4:]))
+ return next_hop
+
+ @classmethod
+ def parse_next_hop_ipv6(cls, buf, unit_len):
+ next_hop = []
+ for next_hop_bin in cls.split_bin_with_len(buf, unit_len):
+ next_hop.append(addrconv.ipv6.bin_to_text(next_hop_bin[-16:]))
+ return next_hop
+
@classmethod
def parse_value(cls, buf):
(afi, safi, next_hop_len,) = struct.unpack_from(
@@ -3236,16 +3262,20 @@ class BGPPathAttributeMpReachNLRI(_PathAttribute):
nlri.append(n)
rf = RouteFamily(afi, safi)
- if rf == RF_IPv6_VPN:
- next_hop = addrconv.ipv6.bin_to_text(next_hop_bin[cls._RD_LENGTH:])
- next_hop_len -= cls._RD_LENGTH
- elif rf == RF_IPv4_VPN:
- next_hop = addrconv.ipv4.bin_to_text(next_hop_bin[cls._RD_LENGTH:])
- next_hop_len -= cls._RD_LENGTH
- elif afi == addr_family.IP or (rf == RF_L2_EVPN and next_hop_len == 4):
- next_hop = addrconv.ipv4.bin_to_text(next_hop_bin)
- elif afi == addr_family.IP6 or (rf == RF_L2_EVPN and next_hop_len > 4):
- next_hop = addrconv.ipv6.bin_to_text(next_hop_bin)
+ if rf == RF_IPv4_VPN:
+ next_hop = cls.parse_next_hop_ipv4(next_hop_bin,
+ cls._RD_LENGTH + 4)
+ next_hop_len -= cls._RD_LENGTH * len(next_hop)
+ elif rf == RF_IPv6_VPN:
+ next_hop = cls.parse_next_hop_ipv6(next_hop_bin,
+ cls._RD_LENGTH + 16)
+ next_hop_len -= cls._RD_LENGTH * len(next_hop)
+ elif (afi == addr_family.IP
+ or (rf == RF_L2_EVPN and next_hop_len < 16)):
+ next_hop = cls.parse_next_hop_ipv4(next_hop_bin, 4)
+ elif (afi == addr_family.IP6
+ or (rf == RF_L2_EVPN and next_hop_len >= 16)):
+ next_hop = cls.parse_next_hop_ipv6(next_hop_bin, 16)
else:
raise ValueError('Invalid address family: afi=%d, safi=%d'
% (afi, safi))
@@ -3257,13 +3287,21 @@ class BGPPathAttributeMpReachNLRI(_PathAttribute):
'nlri': nlri,
}
+ def serialize_next_hop(self):
+ buf = bytearray()
+ for next_hop in self.next_hop_list:
+ if self.afi == addr_family.IP6:
+ next_hop = str(netaddr.IPAddress(next_hop).ipv6())
+ next_hop_bin = ip.text_to_bin(next_hop)
+ if RouteFamily(self.afi, self.safi) in (RF_IPv4_VPN, RF_IPv6_VPN):
+ # Empty label stack(RD=0:0) + IP address
+ next_hop_bin = b'\x00' * self._RD_LENGTH + next_hop_bin
+ buf += next_hop_bin
+
+ return buf
+
def serialize_value(self):
- if self.afi == addr_family.IP6:
- self.next_hop = str(netaddr.IPAddress(self.next_hop).ipv6())
- next_hop_bin = ip.text_to_bin(self.next_hop)
- if RouteFamily(self.afi, self.safi) in (RF_IPv4_VPN, RF_IPv6_VPN):
- # Empty label stack(RD=0:0) + IP address
- next_hop_bin = b'\x00' * self._RD_LENGTH + next_hop_bin
+ next_hop_bin = self.serialize_next_hop()
# fixup
next_hop_len = len(next_hop_bin)
@@ -3282,6 +3320,31 @@ class BGPPathAttributeMpReachNLRI(_PathAttribute):
return buf
@property
+ def next_hop(self):
+ return self._next_hop
+
+ @next_hop.setter
+ def next_hop(self, addr):
+ if not netaddr.valid_ipv4(addr) and not netaddr.valid_ipv6(addr):
+ raise ValueError('Invalid address for next_hop: %s' % addr)
+ self._next_hop = addr
+ self.next_hop_list[0] = addr
+
+ @property
+ def next_hop_list(self):
+ return self._next_hop_list
+
+ @next_hop_list.setter
+ def next_hop_list(self, addr_list):
+ if not isinstance(addr_list, (list, tuple)):
+ addr_list = [addr_list]
+ for addr in addr_list:
+ if not netaddr.valid_ipv4(addr) and not netaddr.valid_ipv6(addr):
+ raise ValueError('Invalid address for next_hop: %s' % addr)
+ self._next_hop = addr_list[0]
+ self._next_hop_list = addr_list
+
+ @property
def route_family(self):
return _rf_map[(self.afi, self.safi)]
diff --git a/ryu/tests/packet_data/bgp4/bgp4-update_ipv6.pcap b/ryu/tests/packet_data/bgp4/bgp4-update_ipv6.pcap
index c2106692..49ed4d9e 100644
--- a/ryu/tests/packet_data/bgp4/bgp4-update_ipv6.pcap
+++ b/ryu/tests/packet_data/bgp4/bgp4-update_ipv6.pcap
Binary files differ
diff --git a/ryu/tests/unit/packet/test_bgp.py b/ryu/tests/unit/packet/test_bgp.py
index 760e01b7..6c764b67 100644
--- a/ryu/tests/unit/packet/test_bgp.py
+++ b/ryu/tests/unit/packet/test_bgp.py
@@ -115,6 +115,17 @@ class Test_bgp(unittest.TestCase):
mp_nlri2 = [
bgp.LabelledIPAddrPrefix(24, '192.168.0.0', labels=[1, 2, 3])
]
+ mp_nlri_v6 = [
+ bgp.LabelledVPNIP6AddrPrefix(64, '2001:db8:1111::',
+ route_dist='200:200',
+ labels=[1, 2, 3]),
+ bgp.LabelledVPNIP6AddrPrefix(64, '2001:db8:2222::',
+ route_dist='10.0.0.1:10000',
+ labels=[5, 6, 7, 8]),
+ ]
+ mp_nlri2_v6 = [
+ bgp.LabelledIP6AddrPrefix(64, '2001:db8:3333::', labels=[1, 2, 3])
+ ]
communities = [
bgp.BGP_COMMUNITY_NO_EXPORT,
bgp.BGP_COMMUNITY_NO_ADVERTISE,
@@ -191,6 +202,13 @@ class Test_bgp(unittest.TestCase):
bgp.BGPPathAttributeMpReachNLRI(afi=afi.IP, safi=safi.MPLS_LABEL,
next_hop='1.1.1.1',
nlri=mp_nlri2),
+ bgp.BGPPathAttributeMpReachNLRI(afi=afi.IP6, safi=safi.MPLS_VPN,
+ next_hop=['2001:db8::1'],
+ nlri=mp_nlri_v6),
+ bgp.BGPPathAttributeMpReachNLRI(afi=afi.IP6, safi=safi.MPLS_LABEL,
+ next_hop=['2001:db8::1',
+ 'fe80::1'],
+ nlri=mp_nlri2_v6),
bgp.BGPPathAttributeMpUnreachNLRI(afi=afi.IP, safi=safi.MPLS_VPN,
withdrawn_routes=mp_nlri),
bgp.BGPPathAttributeUnknown(flags=0, type_=100, value=300 * b'bar')
@@ -315,6 +333,20 @@ class Test_bgp(unittest.TestCase):
route_dist='10.0.0.1:10000',
labels=[5, 6, 7, 8]),
]
+ mp_nlri2 = [
+ bgp.LabelledIPAddrPrefix(24, '192.168.0.0', labels=[1, 2, 3])
+ ]
+ mp_nlri_v6 = [
+ bgp.LabelledVPNIP6AddrPrefix(64, '2001:db8:1111::',
+ route_dist='200:200',
+ labels=[1, 2, 3]),
+ bgp.LabelledVPNIP6AddrPrefix(64, '2001:db8:2222::',
+ route_dist='10.0.0.1:10000',
+ labels=[5, 6, 7, 8]),
+ ]
+ mp_nlri2_v6 = [
+ bgp.LabelledIP6AddrPrefix(64, '2001:db8:3333::', labels=[1, 2, 3])
+ ]
communities = [
bgp.BGP_COMMUNITY_NO_EXPORT,
bgp.BGP_COMMUNITY_NO_ADVERTISE,
@@ -386,6 +418,16 @@ class Test_bgp(unittest.TestCase):
bgp.BGPPathAttributeMpReachNLRI(afi=afi.IP, safi=safi.MPLS_VPN,
next_hop='1.1.1.1',
nlri=mp_nlri),
+ bgp.BGPPathAttributeMpReachNLRI(afi=afi.IP, safi=safi.MPLS_LABEL,
+ next_hop='1.1.1.1',
+ nlri=mp_nlri2),
+ bgp.BGPPathAttributeMpReachNLRI(afi=afi.IP6, safi=safi.MPLS_VPN,
+ next_hop=['2001:db8::1'],
+ nlri=mp_nlri_v6),
+ bgp.BGPPathAttributeMpReachNLRI(afi=afi.IP6, safi=safi.MPLS_LABEL,
+ next_hop=['2001:db8::1',
+ 'fe80::1'],
+ nlri=mp_nlri2_v6),
bgp.BGPPathAttributeMpUnreachNLRI(afi=afi.IP, safi=safi.MPLS_VPN,
withdrawn_routes=mp_nlri),
bgp.BGPPathAttributeUnknown(flags=0, type_=100, value=300 * b'bar')