Skip to content

Commit

Permalink
Add Extended Next Hop Encoding Capability (Type 5) (#165)
Browse files Browse the repository at this point in the history
* ✨ Feat(open.py): add support for construct the type 5 capability in BGP OPEN Message

* 👌 Review(config.py): add "ext_nexthop" in cli params

* ✏️  Typo(config.py): dictOpt -> ListOpt
  • Loading branch information
yuangezhizao committed Mar 3, 2024
1 parent 24cbb73 commit 3a28d5c
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 9 deletions.
15 changes: 13 additions & 2 deletions yabgp/common/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,10 +312,20 @@
NEW_ROUTE_REFRESH = False
GRACEFUL_RESTART = False

# AFI_SAFI mapping
# AFI mapping
AFI_DICT = {
1: 'ipv4',
2: 'ipv6'
}
AFI_STR_DICT = {
'ipv4': 1,
'ipv6': 2
}

# AFI_SAFI mapping
AFI_SAFI_DICT = {
(1, 1): 'ipv4',
(1, 2): 'ipv4_mcast',
(2, 1): 'ipv6',
(1, 4): 'ipv4_lu',
(2, 4): 'ipv6_lu',
Expand All @@ -328,8 +338,9 @@
(2, 133): 'ipv6_flowspec'
}
AFI_SAFI_STR_DICT = {
'ipv6': (2, 1),
'ipv4': (1, 1),
'ipv4_mcast': (1, 2),
'ipv6': (2, 1),
'ipv4_lu': (1, 4),
'ipv6_lu': (2, 4),
'flowspec': (1, 133),
Expand Down
22 changes: 19 additions & 3 deletions yabgp/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@

from oslo_config import cfg

CONF = cfg.CONF
from yabgp.common.constants import AFI_SAFI_STR_DICT
from yabgp.common.constants import AFI_STR_DICT

CONF = cfg.CONF

BGP_CONFIG_OPTS = [
cfg.IntOpt('peer_start_interval',
Expand Down Expand Up @@ -50,7 +52,10 @@
help='if support cisco multi session'),
cfg.DictOpt('running_config',
default={},
help='The running configuration for BGP')
help='The running configuration for BGP'),
cfg.ListOpt('ext_nexthop',
default=[['ipv4', 'ipv6'], ['ipv4_mcast', 'ipv6'], ['vpnv4', 'ipv6']],
help='The ext_nexthop for BGP')
]

CONF.register_opts(BGP_CONFIG_OPTS, group='bgp')
Expand Down Expand Up @@ -129,10 +134,21 @@ def get_bgp_config():
'enhanced_route_refresh': CONF.bgp.enhanced_route_refresh,
'graceful_restart': CONF.bgp.graceful_restart,
'cisco_multi_session': CONF.bgp.cisco_multi_session,
'add_path': CONF.bgp.add_path},
'add_path': CONF.bgp.add_path
},
'remote': {}
}
}
if ('vpnv4' in CONF.bgp.afi_safi) or ('vpnv6' in CONF.bgp.afi_safi):
ext_nexthop_from_cli = CONF.bgp.ext_nexthop
ext_nexthop = []
for each in ext_nexthop_from_cli:
afi_safi, nexthop_afi = each
ext_nexthop.append({
'afi_safi': AFI_SAFI_STR_DICT[afi_safi],
'nexthop_afi': AFI_STR_DICT[nexthop_afi]
})
CONF.bgp.running_config['capability']['local']['ext_nexthop'] = ext_nexthop
else:
LOG.error('Please provide enough parameters!')
sys.exit()
31 changes: 27 additions & 4 deletions yabgp/message/open.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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'] = []
Expand All @@ -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:
Expand Down Expand Up @@ -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):

Expand All @@ -252,6 +254,11 @@ def construct(self, my_capability):
else:
if my_capability.get('four_bytes_as'):
capas += Capability(capa_code=65, capa_length=4, capa_value=self.asn).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)

# for add path
if my_capability.get('add_path'):
capas += Capability(capa_code=69, capa_length=4, capa_value=my_capability['add_path']).construct()
Expand Down Expand Up @@ -345,7 +352,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]
Expand Down Expand Up @@ -418,6 +425,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)

Expand Down
64 changes: 64 additions & 0 deletions yabgp/tests/unit/message/test_open.py
Original file line number Diff line number Diff line change
Expand Up @@ -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\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'
self.assertEqual(hope_hex, msg_hex)


if __name__ == '__main__':
unittest.main()

0 comments on commit 3a28d5c

Please sign in to comment.