Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 13 additions & 24 deletions yabgp/message/attribute/mpreachnlri.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class MpReachNLRI(Attribute):
FLAG = AttributeFlag.OPTIONAL + AttributeFlag.EXTENDED_LENGTH

@classmethod
def parse(cls, value):
def parse(cls, value, add_path=False):
"""parse
"""
try:
Expand All @@ -87,30 +87,25 @@ def parse(cls, value):
# parse nexthop
nexthop = str(netaddr.IPAddress(int(binascii.b2a_hex(nexthop_bin), 16)))
# parse nlri
nlri = IPv4Unicast.parse(nlri_bin)
nlri = IPv4Unicast.parse(nlri_bin, addpath=add_path)
return dict(afi_safi=(afi, safi), nexthop=nexthop, nlri=nlri)

if safi == safn.SAFNUM_LAB_VPNUNICAST:
# MPLS VPN
# parse nexthop
rd_bin = nexthop_bin[0:8]
rd_type = struct.unpack('!H', rd_bin[0:2])[0]
rd_value_bin = rd_bin[2:]
if rd_type == 0:
asn, an = struct.unpack('!HI', rd_value_bin)
ipv4 = str(netaddr.IPAddress(int(binascii.b2a_hex(nexthop_bin[8:]), 16)))
nexthop = {'rd': '%s:%s' % (asn, an), 'str': ipv4}
else:
nexthop = binascii.b2a_hex((nexthop_bin[8:]))
nexthop_rd = IPv4MPLSVPN.parse_rd(rd_bin)
ipv4 = str(netaddr.IPAddress(int(binascii.b2a_hex(nexthop_bin[8:]), 16)))
nexthop = {'rd': nexthop_rd, 'str': ipv4}
# parse nlri
nlri = IPv4MPLSVPN.parse(nlri_bin)
nlri = IPv4MPLSVPN.parse(nlri_bin, addpath=add_path)
return dict(afi_safi=(afi, safi), nexthop=nexthop, nlri=nlri)
elif safi == safn.SAFNUM_MPLS_LABEL:
if nexthop_bin:
nexthop = str(netaddr.IPAddress(int(binascii.b2a_hex(nexthop_bin), 16)))
else:
nexthop = ''
nlri = IPv4LabeledUnicast.parse(nlri_bin)
nlri = IPv4LabeledUnicast.parse(nlri_bin, addpath=add_path)
return dict(afi_safi=(afi, safi), nexthop=nexthop, nlri=nlri)
elif safi == safn.SAFNUM_FSPEC_RULE:
# if nlri length is greater than 240 bytes, it is encoded over 2 bytes
Expand Down Expand Up @@ -157,7 +152,7 @@ def parse(cls, value):
# has link local address
has_link_local = True
linklocal_nexthop = str(netaddr.IPAddress(int(binascii.b2a_hex(nexthop_bin[nexthop_addrlen:]), 16)))
nlri = IPv6Unicast.parse(nlri_bin)
nlri = IPv6Unicast.parse(nlri_bin, addpath=add_path)
if has_link_local:
return dict(afi_safi=(afi, safi), nexthop=nexthop, linklocal_nexthop=linklocal_nexthop, nlri=nlri)
else:
Expand All @@ -166,24 +161,18 @@ def parse(cls, value):
# IPv6 MPLS VPN
# parse nexthop
rd_bin = nexthop_bin[0:8]
rd_type = struct.unpack('!H', rd_bin[0:2])[0]
rd_value_bin = rd_bin[2:]
if rd_type == 0:
asn, an = struct.unpack('!HI', rd_value_bin)
ipv6 = str(netaddr.IPAddress(int(binascii.b2a_hex(nexthop_bin[8:]), 16)))
nexthop = {'rd': '%s:%s' % (asn, an), 'str': ipv6}
# TODO(xiaoquwl) for other RD type decoding
else:
nexthop = binascii.b2a_hex(nexthop_bin[8:])
nexthop_rd = IPv6MPLSVPN.parse_rd(rd_bin)
ipv6 = str(netaddr.IPAddress(int(binascii.b2a_hex(nexthop_bin[8:]), 16)))
nexthop = {'rd': nexthop_rd, 'str': ipv6}
# parse nlri
nlri = IPv6MPLSVPN.parse(nlri_bin)
nlri = IPv6MPLSVPN.parse(nlri_bin, addpath=add_path)
return dict(afi_safi=(afi, safi), nexthop=nexthop, nlri=nlri)
elif safi == safn.SAFNUM_MPLS_LABEL:
if nexthop_bin:
nexthop = str(netaddr.IPAddress(int(binascii.b2a_hex(nexthop_bin), 16)))
else:
nexthop = ''
nlri = IPv6LabeledUnicast.parse(nlri_bin)
nlri = IPv6LabeledUnicast.parse(nlri_bin, addpath=add_path)
return dict(afi_safi=(afi, safi), nexthop=nexthop, nlri=nlri)
else:
return dict(afi_safi=(afi, safi), nexthop=nexthop_bin, nlri=nlri_bin)
Expand Down
14 changes: 9 additions & 5 deletions yabgp/message/attribute/mpunreachnlri.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from yabgp.message.attribute import AttributeFlag
from yabgp.message.attribute import AttributeID
from yabgp.message.attribute.nlri.ipv4_mpls_vpn import IPv4MPLSVPN
from yabgp.message.attribute.nlri.ipv4_unicast import IPv4Unicast
from yabgp.message.attribute.nlri.ipv6_mpls_vpn import IPv6MPLSVPN
from yabgp.message.attribute.nlri.ipv4_flowspec import IPv4FlowSpec
from yabgp.message.attribute.nlri.ipv6_unicast import IPv6Unicast
Expand Down Expand Up @@ -57,7 +58,7 @@ class MpUnReachNLRI(Attribute):
FLAG = AttributeFlag.OPTIONAL + AttributeFlag.EXTENDED_LENGTH

@classmethod
def parse(cls, value):
def parse(cls, value, add_path=False):
try:
afi, safi = struct.unpack('!HB', value[0:3])
except Exception:
Expand All @@ -68,10 +69,12 @@ def parse(cls, value):

# for IPv4
if afi == afn.AFNUM_INET:
if safi == safn.SAFNUM_UNICAST:
return dict(afi_safi=(afi, safi), withdraw=IPv4Unicast.parse(nlri_bin, addpath=add_path))

# VPNv4
if safi == safn.SAFNUM_LAB_VPNUNICAST:
nlri = IPv4MPLSVPN.parse(nlri_bin, iswithdraw=True)
elif safi == safn.SAFNUM_LAB_VPNUNICAST:
nlri = IPv4MPLSVPN.parse(nlri_bin, iswithdraw=True, addpath=add_path)
return dict(afi_safi=(afi, safi), withdraw=nlri)
# BGP flow spec
elif safi == safn.SAFNUM_FSPEC_RULE:
Expand All @@ -97,9 +100,10 @@ def parse(cls, value):
elif afi == afn.AFNUM_INET6:
# for ipv6 unicast
if safi == safn.SAFNUM_UNICAST:
return dict(afi_safi=(afi, safi), withdraw=IPv6Unicast.parse(nlri_data=nlri_bin))
return dict(afi_safi=(afi, safi), withdraw=IPv6Unicast.parse(nlri_data=nlri_bin, addpath=add_path))
elif safi == safn.SAFNUM_LAB_VPNUNICAST:
return dict(afi_safi=(afi, safi), withdraw=IPv6MPLSVPN.parse(value=nlri_bin, iswithdraw=True))
return dict(afi_safi=(afi, safi), withdraw=IPv6MPLSVPN.parse(value=nlri_bin, iswithdraw=True,
addpath=add_path))
else:
return dict(afi_safi=(afi, safi), withdraw=repr(nlri_bin))
# for l2vpn
Expand Down
4 changes: 2 additions & 2 deletions yabgp/message/attribute/nlri/ipv4_mpls_vpn.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class IPv4MPLSVPN(MPLSVPN):
SAFI = SAFNUM_LAB_VPNUNICAST

@classmethod
def parse(cls, value, iswithdraw=False):
return super(IPv4MPLSVPN, cls).parse(value, iswithdraw=iswithdraw)
def parse(cls, value, iswithdraw=False, addpath=False):
return super(IPv4MPLSVPN, cls).parse(value, iswithdraw=iswithdraw, addpath=addpath)

@classmethod
def construct(cls, value, iswithdraw=False):
Expand Down
4 changes: 2 additions & 2 deletions yabgp/message/attribute/nlri/ipv6_mpls_vpn.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ class IPv6MPLSVPN(MPLSVPN):
SAFI = SAFNUM_LAB_VPNUNICAST

@classmethod
def parse(cls, value, iswithdraw=False):
return super(IPv6MPLSVPN, cls).parse(value, iswithdraw=iswithdraw)
def parse(cls, value, iswithdraw=False, addpath=False):
return super(IPv6MPLSVPN, cls).parse(value, iswithdraw=iswithdraw, addpath=addpath)

@classmethod
def construct(cls, value, iswithdraw=False):
Expand Down
11 changes: 9 additions & 2 deletions yabgp/message/attribute/nlri/ipv6_unicast.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
class IPv6Unicast(NLRI):

@classmethod
def parse(cls, nlri_data):
def parse(cls, nlri_data, addpath=False):
"""
decode IPv6 NLRI data
:param nlri_data: NLRI raw hex data
Expand All @@ -37,6 +37,10 @@ def parse(cls, nlri_data):
# Note: Fix wrong decoding to ["0.0.0.0/0", "0.0.0.0/0"]
nlri_data = nlri_data[2:]
continue
path_id = None
if addpath:
path_id = struct.unpack("!I", nlri_data[:4])[0]
nlri_data = nlri_data[4:]
if isinstance(nlri_data[0], int):
prefix_bit_len = int(nlri_data[0])
else:
Expand All @@ -53,7 +57,10 @@ def parse(cls, nlri_data):
prefix_bit += b'\x00'

prefix_addr = str(netaddr.IPAddress(int(binascii.b2a_hex(prefix_bit), 16))) + '/%s' % prefix_bit_len
nlri_list.append(prefix_addr)
if addpath:
nlri_list.append({'prefix': prefix_addr, 'path_id': path_id})
else:
nlri_list.append(prefix_addr)
nlri_data = nlri_data[offset:]

return nlri_list
Expand Down
12 changes: 10 additions & 2 deletions yabgp/message/attribute/nlri/labeled_unicast/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,14 @@ class LabeledUnicast(NLRI):
SAFI = SAFNUM_MPLS_LABEL

@classmethod
def parse(cls, nlri_data):
def parse(cls, nlri_data, addpath=False):
nlri_list = []
while nlri_data:
path_id = None
if addpath:
path_id = struct.unpack("!I", nlri_data[0:4])[0]
nlri_data = nlri_data[4:]

nlri_bit_len = ord(nlri_data[0:1])
# get the byte lenght of label stack + prefix
if nlri_bit_len % 8 == 0:
Expand All @@ -66,7 +71,10 @@ def parse(cls, nlri_data):
prefix_hex += b'\x00'
prefix = str(netaddr.IPAddress(int(binascii.b2a_hex(prefix_hex), 16))) + '/' + str(prefix_mask)
nlri_data = nlri_data[offset:]
nlri_list.append({'prefix': prefix, 'label': label})
if addpath:
nlri_list.append({'path_id': path_id, 'prefix': prefix, 'label': label})
else:
nlri_list.append({'prefix': prefix, 'label': label})
return nlri_list

@classmethod
Expand Down
8 changes: 7 additions & 1 deletion yabgp/message/attribute/nlri/mpls_vpn.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class MPLSVPN(NLRI):
SAFI = None

@classmethod
def parse(cls, value, iswithdraw=False):
def parse(cls, value, iswithdraw=False, addpath=False):
"""
parse nlri
:param value: the raw hex nlri value
Expand All @@ -63,6 +63,12 @@ def parse(cls, value, iswithdraw=False):
nlri_list = []
while value:
nlri_dict = {}
path_id = None
if addpath:
path_id = struct.unpack('!I', value[:4])[0]
value = value[4:]
nlri_dict['path_id'] = path_id

# for python2 and python3
if isinstance(value[0], int):
prefix_bit_len = value[0]
Expand Down
9 changes: 5 additions & 4 deletions yabgp/message/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def parse(cls, t, msg_hex, asn4=False, add_path_remote=False, add_path_local=Fal
results['err_data'] = ''
try:
# parse attributes
results['attr'] = cls.parse_attributes(attribute_data, asn4)
results['attr'] = cls.parse_attributes(attribute_data, asn4, add_path_remote)
except excep.UpdateMessageError as e:
LOG.error(e)
results['sub_error'] = e.sub_error
Expand Down Expand Up @@ -278,12 +278,13 @@ def parse_prefix_list(data, addpath=False):
return prefixes

@staticmethod
def parse_attributes(data, asn4=False):
def parse_attributes(data, asn4=False, add_path=False):
"""
Parses an RFC4271 encoded blob of BGP attributes into a list

:param data:
:param asn4: support 4 bytes asn or not
:param add_path: support add_path or not
:return:
"""
attributes = {}
Expand Down Expand Up @@ -360,13 +361,13 @@ def parse_attributes(data, asn4=False):
decode_value = LargeCommunity.parse(value=attr_value)

elif type_code == bgp_cons.BGPTYPE_MP_REACH_NLRI:
decode_value = MpReachNLRI.parse(value=attr_value)
decode_value = MpReachNLRI.parse(value=attr_value, add_path=add_path)
if decode_value['nlri'][0] and type(decode_value['nlri'][0]) is dict:
if decode_value['nlri'][0].get("protocol_id"):
bgpls_pro_id = decode_value['nlri'][0]["protocol_id"]

elif type_code == bgp_cons.BGPTYPE_MP_UNREACH_NLRI:
decode_value = MpUnReachNLRI.parse(value=attr_value)
decode_value = MpUnReachNLRI.parse(value=attr_value, add_path=add_path)

elif type_code == bgp_cons.BGPTYPE_EXTENDED_COMMUNITY:
decode_value = ExtCommunity.parse(value=attr_value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ def test_parse_with_multi_label(self):
nlri_hex = b'\x48\x00\x14\x10\x00\x14\x21\x22\x01\x29\x48\x00\x14\x10\x00\x14\x21\x22\x01\x2a'
self.assertEqual(nlri_list, IPv4LabeledUnicast.parse(nlri_hex))

def test_parse_enable_add_path(self):
nlri_list = [
{'path_id': 1, 'prefix': '5.5.5.5/32', 'label': [3]}
]
nlri_hex = b'\x00\x00\x00\x01\x38\x00\x00\x31\x05\x05\x05\x05'
self.assertEqual(nlri_list, IPv4LabeledUnicast.parse(nlri_hex, addpath=True))


if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ def test_parse(self):

self.assertEqual(nlri_list, IPv6LabeledUnicast.parse(nlri_hex))

def test_parse_enable_add_path(self):
nlri_list = [
{'path_id': 3, 'label': [2], 'prefix': '5::5/128'}
]
nlri_hex = b'\x00\x00\x00\x03\x98\x00\x00\x21\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05'
self.assertEqual(nlri_list, IPv6LabeledUnicast.parse(nlri_hex, addpath=True))


if __name__ == '__main__':
unittest.main()
6 changes: 6 additions & 0 deletions yabgp/tests/unit/message/attribute/nlri/test_ipv4_mpls_vpn.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ def test_parse(self):
self.assertEqual(
[{'label': [25], 'rd': '100:100', 'prefix': '170.0.0.0/32'}], IPv4MPLSVPN.parse(nlri_hex))

def test_parse_1(self):
nlri_hex = b'\x00\x00\x00\x64\x78\x00\x01\x91\x00\x00\x00\x64\x00\x00\x00\x64\xaa\x00\x00\x00'
self.assertEqual(
[{'path_id': 100, 'label': [25], 'rd': '100:100', 'prefix': '170.0.0.0/32'}],
IPv4MPLSVPN.parse(nlri_hex, addpath=True))

def test_construct_1(self):
nlri_hex = b'\x76\x00\x01\x41\x00\x00\xfd\xea\x00\x00\x00\x01\x17\x00\x00\x00'
nlri_list = [{'label': [20], 'rd': '65002:1', 'prefix': '23.0.0.0/30'}]
Expand Down
5 changes: 5 additions & 0 deletions yabgp/tests/unit/message/attribute/nlri/test_ipv6_mpls_vpn.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ def test_update_parse(self):
]
self.assertEqual(nlri_list, IPv6MPLSVPN.parse(value=nlri_hex))

def test_update_parse_enable_add_path(self):
nlri_hex = b'\x00\x00\x00\x64\x98\x00\x03\x61\x00\x00\x00\x64\x00\x00\x00\x0c\x20\x10\x00\x00\x00\x12\x00\x04'
nlri_list = [{'path_id': 100, 'label': [54], 'rd': '100:12', 'prefix': '2010:0:12:4::/64'}]
self.assertEqual(nlri_list, IPv6MPLSVPN.parse(value=nlri_hex, addpath=True))

def test_update_construct(self):
nlri_hex = b'\x98\x00\x03\x61\x00\x00\x00\x64\x00\x00\x00\x0c\x20\x10\x00\x00\x00\x12\x00\x04' \
b'\x98\x00\x03\x71\x00\x00\x00\x64\x00\x00\x00\x0c\x20\x10\x00\x01\x00\x12\x00\x00'
Expand Down
6 changes: 6 additions & 0 deletions yabgp/tests/unit/message/attribute/nlri/test_ipv6_unicast.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ def test_parse_2(self):
value_hoped = ['326c:ce92:25ea:365e:4d71:5945:2200:0/112']
self.assertEqual(value_hoped, value_parsed)

def test_parse_3(self):
nlri_data = b'\x00\x00\x03\xe9\x40\x20\x01\x0d\xb8\x00\x00\x00\x00'
value_parsed = IPv6Unicast().parse(nlri_data, addpath=True)
value_hoped = [{'path_id': 1001, 'prefix': '2001:db8::/64'}]
self.assertEqual(value_hoped, value_parsed)

def test_parse_withdraw(self):
nlri_data = b'\x40\x20\x01\x0d\xb8\x00\x01\x00\x02\x40\x20\x01\x0d\xb8\x00\x01\x00\x01\x40\x20\x01\x0d' \
b'\xb8\x00\x01\x00\x00'
Expand Down
Loading