diff --git a/tox.ini b/tox.ini index 8ceb2fa..b910ef0 100644 --- a/tox.ini +++ b/tox.ini @@ -39,7 +39,8 @@ commands = python -m testtools.run \ yabgp.tests.unit.message.attribute.test_community \ yabgp.tests.unit.message.attribute.test_originatorid \ yabgp.tests.unit.message.attribute.test_clusterlist \ - yabgp.tests.unit.message.attribute.test_extcommunity + yabgp.tests.unit.message.attribute.test_extcommunity \ + yabgp.tests.unit.message.attribute.nlri.test_ipv4_mpls_vpn [testenv:docs] deps= diff --git a/yabgp/common/constants.py b/yabgp/common/constants.py index b32d440..47eb78b 100644 --- a/yabgp/common/constants.py +++ b/yabgp/common/constants.py @@ -115,6 +115,11 @@ # Unkonw BGP_EXT_COM_UNKNOW = 0x0000 +# route distinguisher type +BGP_ROUTE_DISTINGUISHER_TYPE_0 = 0x0000 +BGP_ROUTE_DISTINGUISHER_TYPE_1 = 0x0001 +BGP_ROUTE_DISTINGUISHER_TYPE_2 = 0x0001 + # NLRI type as define in BGP flow spec RFC BGPNLRI_FSPEC_DST_PFIX = 1 # RFC 5575 BGPNLRI_FSPEC_SRC_PFIX = 2 # RFC 5575 diff --git a/yabgp/message/attribute/mpreachnlri.py b/yabgp/message/attribute/mpreachnlri.py new file mode 100644 index 0000000..8bb6bd2 --- /dev/null +++ b/yabgp/message/attribute/mpreachnlri.py @@ -0,0 +1,80 @@ +# Copyright 2015 Cisco Systems, Inc. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""BGP Attribute MP_REACH_NLRI +""" + +import struct + +from yabgp.message.attribute import Attribute +from yabgp.message.attribute import AttributeFlag +from yabgp.message.attribute import AttributeID +from yabgp.common import afn +from yabgp.common import safn +from yabgp.common import exception as excep +from yabgp.common import constants as bgp_cons +from yabgp.message.attribute.nlri.ipv4_mpls_vpn import IPv4MPLSVPN + + +class MpReachNLRI(Attribute): + """ + MP_REACH_NLRI (type code 14) is used to carry the set of reachable + destinations together with the next hop information to be used for + forwarding to these destinations (RFC 4760 page 2). + + MP_REACH_NLRI coding information + +---------------------------------------------------------+ + | Address Family Identifier (2 octets) | + +---------------------------------------------------------+ + | Subsequent Address Family Identifier (1 octet) | + +---------------------------------------------------------+ + | Length of Next Hop Network Address (1 octet) | + +---------------------------------------------------------+ + | Network Address of Next Hop (variable) | + +---------------------------------------------------------+ + | Reserved (1 octet) | + +---------------------------------------------------------+ + | Network Layer Reachability Information (variable) | + +---------------------------------------------------------+ + """ + + ID = AttributeID.MP_REACH_NLRI + FLAG = AttributeFlag.OPTIONAL + + @classmethod + def parse(cls, value): + + try: + afi, safi, nexthop_length = struct.unpack('!HBB', value[0:4]) + nexthop_data = value[4:4 + nexthop_length] + nlri_data = value[5 + nexthop_length:] + except Exception: + # error when lenght is wrong + raise excep.UpdateMessageError( + sub_error=bgp_cons.ERR_MSG_UPDATE_ATTR_LEN, + data=repr(value)) + if afi == afn.AFNUM_INET: + if safi == safn.SAFNUM_LAB_VPNUNICAST: + nlri = IPv4MPLSVPN.parse(nlri_data) + + return { + 'afi_safi': (afi, safi), + 'nexthop': nexthop_data, + 'nlri': nlri + } + + @classmethod + def construct(cls, value): + pass diff --git a/yabgp/message/attribute/mpunreachnlri.py b/yabgp/message/attribute/mpunreachnlri.py new file mode 100644 index 0000000..af10b1f --- /dev/null +++ b/yabgp/message/attribute/mpunreachnlri.py @@ -0,0 +1,51 @@ +# Copyright 2015 Cisco Systems, Inc. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""BGP Attribute MP_UNREACH_NLRI +""" + +from yabgp.message.attribute import Attribute +from yabgp.message.attribute import AttributeFlag +from yabgp.message.attribute import AttributeID + + +class MpUnReachNLRI(Attribute): + + """ + This is an optional non-transitive attribute that can be used for the + purpose of withdrawing multiple unfeasible routes from service. + An UPDATE message that contains the MP_UNREACH_NLRI is not required + to carry any other path attributes. + + MP_UNREACH_NLRI coding information + +---------------------------------------------------------+ + | Address Family Identifier (2 octets) | + +---------------------------------------------------------+ + | Subsequent Address Family Identifier (1 octet) | + +---------------------------------------------------------+ + | Withdrawn Routes (variable) | + +---------------------------------------------------------+ + """ + + ID = AttributeID.MP_UNREACH_NLRI + FLAG = AttributeFlag.OPTIONAL + + @classmethod + def parse(cls, value): + pass + + @classmethod + def construct(cls, value): + pass diff --git a/yabgp/message/attribute/nlri/__init__.py b/yabgp/message/attribute/nlri/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yabgp/message/attribute/nlri/ipv4_flow_spec.py b/yabgp/message/attribute/nlri/ipv4_flow_spec.py new file mode 100644 index 0000000..d055752 --- /dev/null +++ b/yabgp/message/attribute/nlri/ipv4_flow_spec.py @@ -0,0 +1,33 @@ +# Copyright 2015 Cisco Systems, Inc. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""IPv4 Flowspec NLRI +""" + + +class IPv4FlowSpec(object): + + @classmethod + def parse(cls, value): + """ + parse IPv4 flowspec NLRI + :param value: + :return: + """ + pass + + @classmethod + def construct(cls, value): + pass diff --git a/yabgp/message/attribute/nlri/ipv4_mpls_vpn.py b/yabgp/message/attribute/nlri/ipv4_mpls_vpn.py new file mode 100644 index 0000000..6ebbc54 --- /dev/null +++ b/yabgp/message/attribute/nlri/ipv4_mpls_vpn.py @@ -0,0 +1,107 @@ +# coding=utf-8 +# Copyright 2015 Cisco Systems, Inc. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""IPv4 MPLS VPN NLRI +""" + +import struct +import logging +import binascii + +import netaddr + +from yabgp.common import constants as bgp_cons + +LOG = logging.getLogger(__name__) + + +class IPv4MPLSVPN(object): + + """ + IPv4 MPLS VPN NLRI + + +---------------------------+ + | Length (1 octet) | + +---------------------------+ + | Label (3 octet) | + +---------------------------+ + |...........................| + +---------------------------+ + | Prefix (variable) | + +---------------------------+ + a) Length: The Length field indicates the length, in bits, of the address prefix. + b) Label: (24 bits) Carries one or more labels in a stack, although a BGP update + has only one label. This field carries the following parts of the MPLS shim header: + Label Value–—20 Bits + Experimental bits—3 Bits + Bottom of stack bit—1 bit + c) Prefix: different coding way according to different SAFI + Route Distinguisher (8 bytes) plus IPv4 prefix (32 bits) or IPv6 prefix(128 bits) + rd (Route Distinguisher) structure (RFC 4364) + """ + + @classmethod + def parse(cls, value): + """ + parse nlri + :param value: the raw hex nlri value + :return: nlri list + """ + nlri_list = [] + while value: + nlri_dict = {} + # for python2 and python3 + if isinstance(value[0], int): + prefix_bit_len = value[0] + else: + prefix_bit_len = ord(value[0]) + if prefix_bit_len % 8 == 0: + prefix_byte_len = int(prefix_bit_len / 8) + else: + prefix_byte_len = int(prefix_bit_len / 8) + 1 + + label_int = int(binascii.b2a_hex(value[1:4]), 16) + nlri_dict['label'] = (label_int >> 4, label_int & 1) + + rd = value[4:12] + rd_type = struct.unpack('!H', rd[0:2])[0] + rd_value = rd[2:8] + nlri_dict['rd_type'] = rd_type + if rd_type == bgp_cons.BGP_ROUTE_DISTINGUISHER_TYPE_0: + asn, an = struct.unpack('!HI', rd_value) + nlri_dict['rd'] = '%s:%s' % (asn, an) + elif rd_type == bgp_cons.BGP_ROUTE_DISTINGUISHER_TYPE_1: + ip = str(netaddr.IPAddress(struct.unpack('!I', rd_value[0:4])[0])) + an = struct.unpack('!H', rd_value[4:6])[0] + nlri_dict['rd'] = '%s:%s' % (ip, an) + elif rd_type == bgp_cons.BGP_ROUTE_DISTINGUISHER_TYPE_2: + asn, an = struct.unpack('!IH', rd_value) + nlri_dict['rd'] = '%s:%s' % (asn, an) + else: + LOG.warning('unknow rd type for nlri, type=%s' % rd_type) + nlri_dict['rd'] = '%s:%s' % (0, 0) + prefix = value[12:prefix_byte_len + 1] + if len(prefix) < 4: + prefix += '\x00' * (4 - len(prefix)) + nlri_dict['str'] = str(netaddr.IPAddress(struct.unpack('!I', prefix)[0])) +\ + '/%s' % (prefix_bit_len - 88) + value = value[prefix_byte_len + 1:] + nlri_list.append(nlri_dict) + return nlri_list + + @classmethod + def contruct(cls, value): + pass diff --git a/yabgp/message/attribute/nlri/ipv6_mpls_vpn.py b/yabgp/message/attribute/nlri/ipv6_mpls_vpn.py new file mode 100644 index 0000000..ec1660a --- /dev/null +++ b/yabgp/message/attribute/nlri/ipv6_mpls_vpn.py @@ -0,0 +1,17 @@ +# Copyright 2015 Cisco Systems, Inc. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""IPv6 MPLS VPN NLRI +""" diff --git a/yabgp/tests/unit/message/attribute/nlri/__init__.py b/yabgp/tests/unit/message/attribute/nlri/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yabgp/tests/unit/message/attribute/nlri/test_ipv4_mpls_vpn.py b/yabgp/tests/unit/message/attribute/nlri/test_ipv4_mpls_vpn.py new file mode 100644 index 0000000..de07904 --- /dev/null +++ b/yabgp/tests/unit/message/attribute/nlri/test_ipv4_mpls_vpn.py @@ -0,0 +1,31 @@ +# Copyright 2015 Cisco Systems, Inc. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" Test IPv4 MPLS VPN NLRI """ + +import unittest + +from yabgp.message.attribute.nlri.ipv4_mpls_vpn import IPv4MPLSVPN + + +class TestIPv4MPLSVPN(unittest.TestCase): + + def test_parse(self): + nlri_hex = b'\x78\x00\x01\x91\x00\x00\x00\x64\x00\x00\x00\x64\xaa\x00\x00\x00' + self.assertEqual( + [{'label': (25, 1), 'rd': '100:100', 'rd_type': 0, 'str': '170.0.0.0/32'}], IPv4MPLSVPN.parse(nlri_hex)) + +if __name__ == '__main__': + unittest.main()