diff options
author | IWASE Yusuke <iwase.yusuke0@gmail.com> | 2016-11-22 11:20:22 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2016-12-10 14:57:49 +0900 |
commit | 9e5f9d9d378772679da1e7e01cce3a8e1dcbbf40 (patch) | |
tree | f7960077700ba36c3065db57c80f092f176f2225 | |
parent | ad5ce12e706193c93c2c8acaafeaa5b1c64f0f17 (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.py | 103 | ||||
-rw-r--r-- | ryu/tests/packet_data/bgp4/bgp4-update_ipv6.pcap | bin | 168 -> 233 bytes | |||
-rw-r--r-- | ryu/tests/unit/packet/test_bgp.py | 42 |
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 Binary files differindex c2106692..49ed4d9e 100644 --- a/ryu/tests/packet_data/bgp4/bgp4-update_ipv6.pcap +++ b/ryu/tests/packet_data/bgp4/bgp4-update_ipv6.pcap 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') |