From 9e5f9d9d378772679da1e7e01cce3a8e1dcbbf40 Mon Sep 17 00:00:00 2001 From: IWASE Yusuke Date: Tue, 22 Nov 2016 11:20:22 +0900 Subject: [PATCH] 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 Signed-off-by: FUJITA Tomonori --- ryu/lib/packet/bgp.py | 103 ++++++++++++++---- .../packet_data/bgp4/bgp4-update_ipv6.pcap | Bin 168 -> 233 bytes 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) @@ -3281,6 +3319,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 c21066924a6da8afe4a3d489f2c22875a06e1ffd..49ed4d9e2bbbbc81f2f5c32153713526de20eb25 100644 GIT binary patch literal 233 zcmca|c+)~A1{MYcU}0bca_s-}Mcnz%$#4+J2H{hTf=3x-PB95SWsqsRn*&nb&Bmy} z$h(7qVbv-S7YLZp_>2sjPpr#*z&>a7^o*MU67~yQ(ij;2qXC8}CZJa520k4ICPoD` z6PW%rfDDEMCI$lq0j?bkOb(2U3=T|eOhC8(ebvAM_5hl}{~2UKCbKp0GXqTq0QpNr AGXMYp literal 168 zcmca|c+)~A1{MYw`2U}Qff2~r(