From 2f4fa291b95cc699a91fad218128fe9e0494f475 Mon Sep 17 00:00:00 2001 From: yuangezhizao-M1Pro Date: Fri, 23 Feb 2024 19:39:22 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Feat(open.py):=20add=20support=20fo?= =?UTF-8?q?r=20construct=20the=20type=205=20capability=20in=20BGP=20OPEN?= =?UTF-8?q?=20Message?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yabgp/message/open.py | 30 +++++++++++-- yabgp/tests/unit/message/test_open.py | 64 +++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/yabgp/message/open.py b/yabgp/message/open.py index 21a236f..8f1f22d 100644 --- a/yabgp/message/open.py +++ b/yabgp/message/open.py @@ -166,6 +166,7 @@ def parse(self, message): # (7) enhanced route refresh elif capability.capa_code == capability.ENHANCED_ROUTE_REFRESH: self.capa_dict['enhanced_route_refresh'] = True + # (8) add path elif capability.capa_code == capability.ADD_PATH: # could be more than one add path cap for different afi safi @@ -180,6 +181,7 @@ def parse(self, message): } ) capability.capa_value = capability.capa_value[4:] + # (9) Long-Lived Graceful Restart (LLGR) Capability elif capability.capa_code == capability.LLGR: self.capa_dict['LLGR'] = [] @@ -191,6 +193,8 @@ def parse(self, message): 'time': time }) capability.capa_value = capability.capa_value[7:] + + # (10) Extended Next Hop Encoding Capability elif capability.capa_code == capability.EXTENDED_NEXT_HOP: self.capa_dict['ext_nexthop'] = [] while len(capability.capa_value) > 0: @@ -225,9 +229,7 @@ def construct_header(msg): # Maker | Length | Type | msg | #---------------+--------+---------+------+ """ - return b'\xff'*16 + struct.pack('!HB', - len(msg) + 19, - 1) + msg + return b'\xff' * 16 + struct.pack('!HB', len(msg) + 19, 1) + msg def construct(self, my_capability): @@ -238,6 +240,10 @@ def construct(self, my_capability): # Multiprotocol extentions capability capas += Capability(capa_code=1, capa_length=4).construct(my_capability) + if 'ext_nexthop' in my_capability: + # Extended Next Hop Encoding Capability + capas += Capability(capa_code=5, capa_length=0).construct(my_capability) + if my_capability.get('cisco_route_refresh'): # Cisco Route refresh capability capas += Capability(capa_code=128, capa_length=0).construct(my_capability) @@ -345,7 +351,7 @@ class Capability(object): MULTISESSION_BGP = 0x44 # [Appanna] ADD_PATH = 0x45 # [draft-ietf-idr-add-paths] ENHANCED_ROUTE_REFRESH = 0x46 - LLGR = 0x47 # [draft-uttaro-idr-bgp-persistence] + LLGR = 0x47 # [draft-uttaro-idr-bgp-persistence] # 70-127 Unassigned CISCO_ROUTE_REFRESH = 0x80 # I Can only find reference to this in the router logs # 128-255 Reserved for Private Use [RFC5492] @@ -418,6 +424,22 @@ def construct(self, my_capability=None): '!BBBBHBB', 2, 6, self.ADD_PATH, self.capa_length, afi_safi[0], afi_safi[1], value) return add_path + # (10) Extended Next Hop Encoding Capability + elif self.capa_code == self.EXTENDED_NEXT_HOP: + ext_nexthop_value = b'' + # 1. Value + for each_ext_nexthop in my_capability['ext_nexthop']: + afi, safi = each_ext_nexthop['afi_safi'] + nhafi = each_ext_nexthop['nexthop_afi'] + ext_nexthop_value += struct.pack('!HHH', afi, safi, nhafi) + + # 2. Header + ext_nexthop_value_length = len(ext_nexthop_value) + capability_and_ext_nexthtop_header = struct.pack('!BBBB', 2, ext_nexthop_value_length + 1 + 1, + self.EXTENDED_NEXT_HOP, ext_nexthop_value_length) + ext_nexthop = capability_and_ext_nexthtop_header + ext_nexthop_value + return ext_nexthop + elif self.capa_code == self.ENHANCED_ROUTE_REFRESH: return struct.pack('!BBBB', 2, 2, self.ENHANCED_ROUTE_REFRESH, 0) diff --git a/yabgp/tests/unit/message/test_open.py b/yabgp/tests/unit/message/test_open.py index 40a4086..bd988c1 100644 --- a/yabgp/tests/unit/message/test_open.py +++ b/yabgp/tests/unit/message/test_open.py @@ -136,6 +136,70 @@ def test_parse_add_path_llgr(self): } self.assertEqual(results, self.open.parse(msg_hex[HDR_LEN:])) + def test_parse_ext_nexthop(self): + """ + + :return: + """ + real_msg_hex = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00S\x01' \ + b'\x04\xfd\xe8\x00\xb4\x01\x01\x01\x016\x02\x06\x01\x04\x00\x01\x00\x01\x02' \ + b'\x06\x01\x04\x00\x01\x00\x85\x02\x02\x80\x00\x02\x02\x02\x00\x02\x06A\x04' \ + b'\x00\x00\xfd\xe8\x02\x14\x05\x12\x00\x01\x00\x01\x00\x02\x00\x01\x00\x02' \ + b'\x00\x02\x00\x01\x00\x80\x00\x02' + open_msg = self.open.parse(real_msg_hex[HDR_LEN:]) + results = { + 'version': 4, + 'asn': 65000, + 'hold_time': 180, + 'bgp_id': '1.1.1.1', + 'capabilities': { + 'afi_safi': [ + (1, 1), + (1, 133) + ], + 'cisco_route_refresh': True, + 'route_refresh': True, + 'four_bytes_as': True, + 'ext_nexthop': [ + {'afi_safi': [1, 1], 'nexthop_afi': 2}, + {'afi_safi': [1, 2], 'nexthop_afi': 2}, + {'afi_safi': [1, 128], 'nexthop_afi': 2} + ] + } + } + self.assertEqual(results, open_msg) + + def test_construct_ext_nexthop(self): + """ + + :return: + """ + self.open.version = VERSION + self.open.asn = 65000 + self.open.hold_time = 180 + self.open.bgp_id = int(netaddr.IPAddress('1.1.1.1')) + my_capabilities = { + 'afi_safi': [ + (1, 1), + (1, 133) + ], + 'cisco_route_refresh': True, + 'route_refresh': True, + 'four_bytes_as': True, + 'ext_nexthop': [ + {'afi_safi': [1, 1], 'nexthop_afi': 2}, + {'afi_safi': [1, 2], 'nexthop_afi': 2}, + {'afi_safi': [1, 128], 'nexthop_afi': 2} + ] + } + msg_hex = self.open.construct(my_capabilities) + hope_hex = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00S\x01' \ + b'\x04\xfd\xe8\x00\xb4\x01\x01\x01\x016\x02\x06\x01\x04\x00\x01\x00\x01\x02' \ + b'\x06\x01\x04\x00\x01\x00\x85\x02\x14\x05\x12\x00\x01\x00\x01\x00\x02\x00' \ + b'\x01\x00\x02\x00\x02\x00\x01\x00\x80\x00\x02\x02\x02\x80\x00\x02\x02\x02' \ + b'\x00\x02\x06A\x04\x00\x00\xfd\xe8' + self.assertEqual(hope_hex, msg_hex) + if __name__ == '__main__': unittest.main()