Skip to content

Commit

Permalink
fixed issue for supporting multi nlri in one bgp flowspec messgae
Browse files Browse the repository at this point in the history
Signed-off-by: Peng Xiao <xiaoquwl@gmail.com>
  • Loading branch information
xiaopeng163 committed Jul 10, 2016
1 parent 2f41813 commit 2a67e1b
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 84 deletions.
2 changes: 1 addition & 1 deletion yabgp/core/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ def update_received(self, timestamp, msg):
self.msg_recv_stat['Updates'] += 1
self.fsm.update_received()
return

afi_safi = None
# process messages
if result['nlri'] or result['withdraw']:
afi_safi = 'ipv4'
Expand Down
28 changes: 19 additions & 9 deletions yabgp/message/attribute/mpreachnlri.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,24 @@ def parse(cls, value):
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
if len(nlri_bin) >= 240:
nlri_bin = nlri_bin[2:]
else:
nlri_bin = nlri_bin[1:]
nlri = IPv4FlowSpec.parse(nlri_bin)
nlri_list = []
while nlri_bin:
length = ord(nlri_bin[0])
if length >> 4 == 0xf and len(nlri_bin) > 2:
length = struct.unpack('!H', nlri_bin[:2])[0]
nlri_tmp = nlri_bin[2: length + 2]
nlri_bin = nlri_bin[length + 2:]
else:
nlri_tmp = nlri_bin[1: length + 1]
nlri_bin = nlri_bin[length + 1:]
nlri = IPv4FlowSpec.parse(nlri_tmp)
if nlri:
nlri_list.append(nlri)
if nexthop_bin:
nexthop = str(netaddr.IPAddress(int(binascii.b2a_hex(nexthop_bin), 16)))
else:
nexthop = ''
return dict(afi_safi=(afi, safi), nexthop=nexthop, nlri=nlri)
return dict(afi_safi=(afi, safi), nexthop=nexthop, nlri=nlri_list)
else:
nlri = repr(nlri_bin)

Expand Down Expand Up @@ -201,10 +209,12 @@ def construct(cls, value):
nexthop = netaddr.IPAddress(value['nexthop']).packed
except netaddr.core.AddrFormatError:
nexthop = ''
nlri = IPv4FlowSpec.construct(value=value['nlri'])
if nlri:
nlri_hex = b''
for nlri in value['nlri']:
nlri_hex += IPv4FlowSpec.construct(value=nlri)
if nlri_hex:
attr_value = struct.pack('!H', afi) + struct.pack('!B', safi) + \
struct.pack('!B', len(nexthop)) + nexthop + b'\x00' + nlri
struct.pack('!B', len(nexthop)) + nexthop + b'\x00' + nlri_hex
return struct.pack('!B', cls.FLAG) + struct.pack('!B', cls.ID) \
+ struct.pack('!B', len(attr_value)) + attr_value
except Exception as e:
Expand Down
37 changes: 24 additions & 13 deletions yabgp/message/attribute/mpunreachnlri.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,21 @@ def parse(cls, value):
# BGP flow spec
elif safi == safn.SAFNUM_FSPEC_RULE:
# if nlri length is greater than 240 bytes, it is encoded over 2 bytes
if len(nlri_bin) >= 240:
nlri_bin = nlri_bin[2:]
else:
nlri_bin = nlri_bin[1:]
return dict(afi_safi=(afi, safi), withdraw=IPv4FlowSpec().parse(value=nlri_bin))
withdraw_list = []
while nlri_bin:
length = ord(nlri_bin[0])
if length >> 4 == 0xf and len(nlri_bin) > 2:
length = struct.unpack('!H', nlri_bin[:2])[0]
nlri_tmp = nlri_bin[2: length + 2]
nlri_bin = nlri_bin[length + 2:]
else:
nlri_tmp = nlri_bin[1: length + 1]
nlri_bin = nlri_bin[length + 1:]
nlri = IPv4FlowSpec.parse(nlri_tmp)
if nlri:
withdraw_list.append(nlri)

return dict(afi_safi=(afi, safi), withdraw=withdraw_list)
else:
return dict(afi_safi=(afn.AFNUM_INET, safi), withdraw=repr(nlri_bin))
# for ipv6
Expand Down Expand Up @@ -121,15 +131,16 @@ def construct(cls, value):
return None
elif safi == safn.SAFNUM_FSPEC_RULE:
try:
nlri = IPv4FlowSpec.construct(value=value['withdraw'])
if nlri:
attr_value = struct.pack('!H', afi) + struct.pack('!B', safi) + \
nlri
return struct.pack('!B', cls.FLAG) + struct.pack('!B', cls.ID) \
+ struct.pack('!B', len(attr_value)) + attr_value
else:
nlri_list = value.get('withdraw') or []
if not nlri_list:
return None
pass
nlri_hex = b''
for nlri in nlri_list:
nlri_hex += IPv4FlowSpec.construct(value=nlri)
attr_value = struct.pack('!H', afi) + struct.pack('!B', safi) + nlri_hex
return struct.pack('!B', cls.FLAG) + struct.pack('!B', cls.ID) \
+ struct.pack('!B', len(attr_value)) + attr_value

except Exception:
raise excep.ConstructAttributeFailed(
reason='failed to construct attributes',
Expand Down
51 changes: 24 additions & 27 deletions yabgp/message/attribute/nlri/ipv4_flowspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def parse(cls, value):
# +------------------------------+
# | NLRI value (variable) |
# +------------------------------+
nlri_list = []
nlri_dict = {}
while value:
offset = 0
flowspec_type = struct.unpack('!B', value[0])[0]
Expand All @@ -48,20 +48,20 @@ def parse(cls, value):
if flowspec_type in [bgp_cons.BGPNLRI_FSPEC_DST_PFIX, bgp_cons.BGPNLRI_FSPEC_SRC_PFIX]:
prefix, offset_tmp = cls.parse_prefix(value[offset:])
offset += offset_tmp
nlri_list.append({flowspec_type: prefix})
nlri_dict[flowspec_type] = prefix
value = value[offset:]
elif flowspec_type in [bgp_cons.BGPNLRI_FSPEC_IP_PROTO, bgp_cons.BGPNLRI_FSPEC_DST_PORT,
bgp_cons.BGPNLRI_FSPEC_SRC_PORT, bgp_cons.BGPNLRI_FSPEC_ICMP_TP,
bgp_cons.BGPNLRI_FSPEC_ICMP_CD,
bgp_cons.BGPNLRI_FSPEC_PCK_LEN]:
operator_list, offset = cls.parse_operators(value[offset:])
nlri_list.append({flowspec_type: operator_dict_to_str(operator_list)})
nlri_dict[flowspec_type] = operator_dict_to_str(operator_list)
value = value[offset:]
else:
operator_list, offset = cls.parse_operators(value[offset:])
nlri_list.append({flowspec_type: operator_dict_to_str(operator_list)})
nlri_dict[flowspec_type] = operator_dict_to_str(operator_list)
value = value[offset:]
return nlri_list
return nlri_dict

@classmethod
def construct(cls, value):
Expand All @@ -70,28 +70,25 @@ def construct(cls, value):
@classmethod
def construct_nlri(cls, data):
""" Construct NLRI """
nlri_data = ''
for nlri in data:
nlri_tmp = ''
# there may have many filters in each nlri
for f_type in nlri:
if int(f_type) in [bgp_cons.BGPNLRI_FSPEC_DST_PFIX, bgp_cons.BGPNLRI_FSPEC_SRC_PFIX]:
nlri_tmp += struct.pack('!B', int(f_type)) + cls.construct_prefix(nlri[f_type])
elif int(f_type) in [bgp_cons.BGPNLRI_FSPEC_IP_PROTO, bgp_cons.BGPNLRI_FSPEC_DST_PORT,
bgp_cons.BGPNLRI_FSPEC_SRC_PORT, bgp_cons.BGPNLRI_FSPEC_ICMP_TP,
bgp_cons.BGPNLRI_FSPEC_ICMP_CD, bgp_cons.BGPNLRI_FSPEC_PCK_LEN]:
# translate from expression to binary
# expr = '>8080&<8088|=3128'
if nlri[f_type] > 255:
nlri_tmp += struct.pack('!B', int(f_type)) + '\x91' + struct.pack('!H', nlri[f_type])
else:
nlri_tmp += struct.pack('!B', int(f_type)) + '\x81' + struct.pack('!B', nlri[f_type])
# TODO(penxiao) other flow type
nlri_data += nlri_tmp
if len(nlri_data) >= 240:
return struct.pack('!H', len(nlri_data)) + nlri_data
elif nlri_data:
return struct.pack('!B', len(nlri_data)) + nlri_data
# there may have many filters in each nlri
nlri_tmp = b''
for f_type in data:
if int(f_type) in [bgp_cons.BGPNLRI_FSPEC_DST_PFIX, bgp_cons.BGPNLRI_FSPEC_SRC_PFIX]:
nlri_tmp += struct.pack('!B', int(f_type)) + cls.construct_prefix(data[f_type])
elif int(f_type) in [bgp_cons.BGPNLRI_FSPEC_IP_PROTO, bgp_cons.BGPNLRI_FSPEC_DST_PORT,
bgp_cons.BGPNLRI_FSPEC_SRC_PORT, bgp_cons.BGPNLRI_FSPEC_ICMP_TP,
bgp_cons.BGPNLRI_FSPEC_ICMP_CD, bgp_cons.BGPNLRI_FSPEC_PCK_LEN]:
# translate from expression to binary
# expr = '>8080&<8088|=3128'
if data[f_type] > 255:
nlri_tmp += struct.pack('!B', int(f_type)) + '\x91' + struct.pack('!H', data[f_type])
else:
nlri_tmp += struct.pack('!B', int(f_type)) + '\x81' + struct.pack('!B', data[f_type])
# TODO(penxiao) other flow type
if len(nlri_tmp) >= 240:
return struct.pack('!H', len(nlri_tmp)) + nlri_tmp
elif nlri_tmp:
return struct.pack('!B', len(nlri_tmp)) + nlri_tmp

@staticmethod
def parse_prefix(data):
Expand Down
35 changes: 12 additions & 23 deletions yabgp/tests/unit/message/attribute/nlri/test_ipv4_flowspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

from yabgp.common import constants as bgp_cons
from yabgp.message.attribute.nlri.ipv4_flowspec import IPv4FlowSpec
from yabgp.message.attribute.nlri.ipv4_flowspec import operator_str_dict


class TestIPv4FlowSpec(unittest.TestCase):
Expand All @@ -30,49 +29,39 @@ def test_parse(self):
'\x03\x01\x05\x81\x06\x08\x81\x02\x09\x81\x28\x0a\x01\xfe\x03\xfe\xd5' \
'\x01\x2c\x0b\x01\x28\x81\x30\x0c\x81\x01'
self.assertEqual(
[{1: '2.2.2.0/24'}, {2: '3.3.0.0/16'}, {3: '=0 =47 =88 =1 =2 =89 =103'}, {5: '=8082 =8083'},
{6: '=80 =8080 =8081 =8082 =8083'}, {7: '=2 =3 =5 =6'}, {8: '=2'}, {9: '=40'}, {10: '=254 >=254&<=300'},
{11: '=40 =48'}, {12: '=1'}], IPv4FlowSpec.parse(raw_hex))
{1: '2.2.2.0/24', 2: '3.3.0.0/16', 3: '=0 =47 =88 =1 =2 =89 =103', 5: '=8082 =8083',
6: '=80 =8080 =8081 =8082 =8083', 7: '=2 =3 =5 =6', 8: '=2', 9: '=40', 10: '=254 >=254&<=300',
11: '=40 =48', 12: '=1'}, IPv4FlowSpec.parse(raw_hex))

def test_parse_nlri_prefix(self):
raw_hex = '\x01\x18\x6e\x01\x01'
self.assertEqual([{bgp_cons.BGPNLRI_FSPEC_DST_PFIX: '110.1.1.0/24'}], IPv4FlowSpec.parse(raw_hex))
raw_hex = b'\x01\x18\x6e\x01\x01'
self.assertEqual({bgp_cons.BGPNLRI_FSPEC_DST_PFIX: '110.1.1.0/24'}, IPv4FlowSpec.parse(raw_hex))

def test_parse_nlri_packet_length(self):
raw_hex = '\x0a\x01\xfe\x03\xfe\xd5\x01\x2c'
self.assertEqual([{bgp_cons.BGPNLRI_FSPEC_PCK_LEN: '=254 >=254&<=300'}], IPv4FlowSpec.parse(raw_hex))
self.assertEqual({bgp_cons.BGPNLRI_FSPEC_PCK_LEN: '=254 >=254&<=300'}, IPv4FlowSpec.parse(raw_hex))

def test_parse_nlri_ip_protocol(self):
raw_hex = '\x03\x01\x00\x01\x2f\x01\x58\x01\x01\x01\x02\x01\x59\x81\x67'
self.assertEqual([{bgp_cons.BGPNLRI_FSPEC_IP_PROTO: '=0 =47 =88 =1 =2 =89 =103'}], IPv4FlowSpec.parse(raw_hex))
self.assertEqual({bgp_cons.BGPNLRI_FSPEC_IP_PROTO: '=0 =47 =88 =1 =2 =89 =103'}, IPv4FlowSpec.parse(raw_hex))

def test_parse_nlri_des_port(self):
raw_hex = '\x05\x01\x50\x11\x1f\x90\x11\x1f\x91\x11\x1f\x92\x91\x1f\x93'
self.assertEqual([{bgp_cons.BGPNLRI_FSPEC_DST_PORT: '=80 =8080 =8081 =8082 =8083'}],
self.assertEqual({bgp_cons.BGPNLRI_FSPEC_DST_PORT: '=80 =8080 =8081 =8082 =8083'},
IPv4FlowSpec.parse(raw_hex))

def test_parse_nlri_src_port(self):
raw_hex = '\x06\x01\x50\x11\x1f\x90\x11\x1f\x91\x11\x1f\x92\x91\x1f\x93'
self.assertEqual([{bgp_cons.BGPNLRI_FSPEC_SRC_PORT: '=80 =8080 =8081 =8082 =8083'}],
self.assertEqual({bgp_cons.BGPNLRI_FSPEC_SRC_PORT: '=80 =8080 =8081 =8082 =8083'},
IPv4FlowSpec.parse(raw_hex))

def test_parse_nlri_icmp_type(self):
raw_hex = '\x07\x01\x02\x01\x03\x01\x05\x81\x06'
self.assertEqual([{bgp_cons.BGPNLRI_FSPEC_ICMP_TP: '=2 =3 =5 =6'}], IPv4FlowSpec.parse(raw_hex))
self.assertEqual({bgp_cons.BGPNLRI_FSPEC_ICMP_TP: '=2 =3 =5 =6'}, IPv4FlowSpec.parse(raw_hex))

def test_parse_nlri_icmp_code(self):
raw_hex = '\x08\x81\x02'
self.assertEqual([{bgp_cons.BGPNLRI_FSPEC_ICMP_CD: '=2'}], IPv4FlowSpec.parse(raw_hex))

def test_operator_str_dict(self):
operator_str1 = '=123'
operator_str2 = '<=123'
operator_str3 = '<=1024 >=2048'
operator_str4 = '<=1024 >=2048&<=3789'
print operator_str_dict(operator_str1)
print operator_str_dict(operator_str2)
print operator_str_dict(operator_str3)
print operator_str_dict(operator_str4)
self.assertEqual({bgp_cons.BGPNLRI_FSPEC_ICMP_CD: '=2'}, IPv4FlowSpec.parse(raw_hex))

def test_construct_prefix(self):
prefix_bin = b'\x18\xc0\x55\x02'
Expand All @@ -81,7 +70,7 @@ def test_construct_prefix(self):

def test_construct_nlri(self):
nlri_bin = b'\x0a\x01\x18\xc0\x55\x02\x02\x18\xc0\x55\x01'
nlri_list = [{1: '192.85.2.0/24'}, {2: '192.85.1.0/24'}]
nlri_list = {1: '192.85.2.0/24', 2: '192.85.1.0/24'}
self.assertEqual(nlri_bin, IPv4FlowSpec.construct_nlri(nlri_list))


Expand Down
31 changes: 22 additions & 9 deletions yabgp/tests/unit/message/attribute/test_mpreachnlri.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,21 +122,34 @@ def test_ipv6_mpls_vpn_construct(self):
}
self.assertEqual(data_bin, MpReachNLRI.construct(data_hoped))

def test_ipv4_flowspec_parse(self):
data_bin = b'\x80\x0e\x10\x00\x01\x85\x00\x00\x0a\x01\x18\xc0\x55\x02\x02\x18\xc0\x55\x01'
data_dict = {'afi_safi': (1, 133), 'nexthop': '', 'nlri': [{1: '192.85.2.0/24'}, {2: '192.85.1.0/24'}]}
self.assertEqual(data_dict, MpReachNLRI.parse(data_bin[3:]))

def test_ipv4_flowspec_parse_nexthop(self):
data_bin = b'\x0e\x00\x14\x00\x01\x85\x04\x02\x02\x02\x02\x00\x0a\x01\x18\x02\x02\x02\x02\x18\x01\x01\x01'
data_dict = {'afi_safi': (1, 133), 'nexthop': '2.2.2.2', 'nlri': [{1: '2.2.2.0/24'}, {2: '1.1.1.0/24'}]}
def test_ipv4_flowspec_parse_multi_nlri_with_nexthop(self):
data_bin = b'\x0e\x00\x1b\x00\x01\x85\x00\x00\x0a\x01\x18\xc0\x58\x03\x02\x18\xc0\x59\x03\x0a' \
b'\x01\x18\xc0\x58\x04\x02\x18\xc0\x59\x04'
data_dict = {
'afi_safi': (1, 133),
'nexthop': '',
'nlri': [
{1: '192.88.3.0/24', 2: '192.89.3.0/24'},
{1: '192.88.4.0/24', 2: '192.89.4.0/24'}
]}
self.assertEqual(data_dict, MpReachNLRI.parse(data_bin[3:]))

def test_ipv4_flowspec_construct(self):
data_bin = b'\x80\x0e\x10\x00\x01\x85\x00\x00\x0a\x01\x18\xc0\x55\x02\x02\x18\xc0\x55\x01'
data_dict = {'afi_safi': (1, 133), 'nexthop': '', 'nlri': [{1: '192.85.2.0/24'}, {2: '192.85.1.0/24'}]}
data_dict = {'afi_safi': (1, 133), 'nexthop': '', 'nlri': [{1: '192.85.2.0/24', 2: '192.85.1.0/24'}]}
self.assertEqual(data_bin, MpReachNLRI.construct(data_dict))

def test_ipv4_flowspec_construct_multi_nlri(self):
data_dict = {
'afi_safi': (1, 133),
'nexthop': '',
'nlri': [
{1: '192.88.3.0/24', 2: '192.89.3.0/24'},
{1: '192.88.4.0/24', 2: '192.89.4.0/24'}
]}
data_bin_cons = MpReachNLRI.construct(data_dict)
self.assertEqual(data_dict, MpReachNLRI.parse(data_bin_cons[3:]))

def test_l2vpn_evpn_parse_construct_route_type1(self):
data_dict = {
"afi_safi": (25, 70),
Expand Down
4 changes: 2 additions & 2 deletions yabgp/tests/unit/message/attribute/test_mpunreachnlri.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ def test_ipv6_mpls_vpn_parse_construct(self):

def test_ipv4_flowspec_parse(self):
data_bin = b'\x00\x01\x85\x0a\x01\x18\xc0\x55\x02\x02\x18\xc0\x55\x01'
nlri_dict = {'afi_safi': (1, 133), 'withdraw': [{1: '192.85.2.0/24'}, {2: '192.85.1.0/24'}]}
nlri_dict = {'afi_safi': (1, 133), 'withdraw': [{1: '192.85.2.0/24', 2: '192.85.1.0/24'}]}
self.assertEqual(nlri_dict, MpUnReachNLRI.parse(data_bin))

def test_ipv4_flowspec_construct(self):
data_bin = b'\x00\x01\x85\x0a\x01\x18\xc0\x55\x02\x02\x18\xc0\x55\x01'
nlri_dict = {'afi_safi': (1, 133), 'withdraw': [{1: '192.85.2.0/24'}, {2: '192.85.1.0/24'}]}
nlri_dict = {'afi_safi': (1, 133), 'withdraw': [{1: '192.85.2.0/24', 2: '192.85.1.0/24'}]}
self.assertEqual(data_bin, MpUnReachNLRI.construct(nlri_dict)[3:])

def test_l2vpn_evpn_route_type_1_parse_construct(self):
Expand Down

0 comments on commit 2a67e1b

Please sign in to comment.