Skip to content

Commit

Permalink
Add support for parsing the Path Attribute: BGP Prefix-SID (Type 40) …
Browse files Browse the repository at this point in the history
…in BGP UPDATE Message (#167)

* 🎨 Style(__init__.py): replace "Sid" with "SID"

* ✨ Feat(bgpprefixsid.py): add support for parsing the Path Attribute: BGP Prefix-SID (Type 40)

Refer:
https://github.com/Exa-Networks/exabgp/blob/main/src/exabgp/bgp/message/update/attribute/sr/prefixsid.py

* ✅ Test(test_bgpprefixsid.py): add unittest and format result

* 💚 Fix-ci(ci.yml): remove Python 2.7.x from the CI matrix
Refer: actions/setup-python#672

* 🔥 Prune(.travis.yml): remove Travis CI
  • Loading branch information
yuangezhizao committed Mar 6, 2024
1 parent 3a28d5c commit 59dc51b
Show file tree
Hide file tree
Showing 17 changed files with 462 additions and 25 deletions.
7 changes: 3 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ jobs:
strategy:
fail-fast: false
matrix:
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
os: [ ubuntu-20.04, macos-latest, windows-latest ]
python-version: [ "2.7", "3.6", "3.x" ]
python-version: [ "3.6", "3.x" ]
exclude:
- os: windows-latest
python-version: "2.7"
- python-version: "3.x"

steps:
- uses: actions/checkout@v3
Expand Down
14 changes: 0 additions & 14 deletions .travis.yml

This file was deleted.

1 change: 1 addition & 0 deletions yabgp/common/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
BGPTYPE_TUNNEL_ENCAPS_ATTR = 23 # RFC5512
BGPTYPE_LINK_STATE = 29
BGPTYPE_LARGE_COMMUNITY = 32
BGPTYPE_BGP_PREFIX_SID = 40 # RFC8669 & RFC9252
BGPTYPE_ATTRIBUTE_SET = 128

# BGP Tunnel Encapsulation Attribute Tunnel Types
Expand Down
2 changes: 2 additions & 0 deletions yabgp/message/attribute/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class AttributeFlag(int):
PARTIAL = 0x20 # 32 RFC 4271
TRANSITIVE = 0x40 # 64 RFC 4271
OPTIONAL = 0x80 # 128 RFC 4271

# OPTIONAL_TRANSITIVE = 0xc0 # 192 RFC 4271

def __str_(self):
Expand Down Expand Up @@ -110,6 +111,7 @@ class AttributeID(int):
Traffic_Engineering = 0x18 # 24 [RFC5543]
IPv6_Address_Specific_Extended_Community = 0x19 # 25 [RFC5701]
LARGE_COMMUNITY = 0x20 # 32 [8092]
BGP_PREFIX_SID = 0x28 # 40 [RFC8669 & RFC9252]
LINKSTATE = 0x1d
ATTR_SET = 0x80 # 128 [RFC6368]

Expand Down
4 changes: 2 additions & 2 deletions yabgp/message/attribute/linkstate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
from .link.unidirect_avail_bw import UnidirectAvailBw # noqa
from .link.unidirect_bw_util import UnidirectBwUtil # noqa
from .link.extend_admin_group import ExtendedAdminGroup # noqa
from .link.srv6_end_x_sid import SRv6EndXSid # noqa
from .link.srv6_lan_end_x_sid import SRv6LANEndXSid # noqa
from .link.srv6_end_x_sid import SRv6EndXSID # noqa
from .link.srv6_lan_end_x_sid import SRv6LANEndXSID # noqa
from .link.srv6_sid import SRv6SID # noqa
from .prefix.prefix_metric import PrefixMetric # noqa
from .prefix.prefix_sid import PrefixSID # noqa
Expand Down
2 changes: 1 addition & 1 deletion yabgp/message/attribute/linkstate/link/srv6_end_x_sid.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@


@LinkState.register()
class SRv6EndXSid(TLV):
class SRv6EndXSID(TLV):
"""
SRv6 End.X SID
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

@LinkState.register(_type=1107) # IS-IS
@LinkState.register(_type=1108) # OSPFv3
class SRv6LANEndXSid(TLV):
class SRv6LANEndXSID(TLV):
"""
SRv6 LAN End.X SID
Expand Down
19 changes: 19 additions & 0 deletions yabgp/message/attribute/sr/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2024 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.

from .bgpprefixsid import BGPPrefixSID # noqa
from .srv6.l3service import SRv6L3Service # noqa
from .srv6.sidinformation import SRv6SIDInformation # noqa
from .srv6.sidstructure import SRv6SIDStructure # noqa
83 changes: 83 additions & 0 deletions yabgp/message/attribute/sr/bgpprefixsid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Copyright 2024 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.

import struct

import binascii

from yabgp.message.attribute import Attribute, AttributeFlag, AttributeID


class BGPPrefixSID(Attribute):
"""
BGP Prefix SID
Original: https://datatracker.ietf.org/doc/html/rfc8669#section-3
Extend: https://datatracker.ietf.org/doc/html/rfc9252#section-2
"""

ID = AttributeID.BGP_PREFIX_SID
FLAG = AttributeFlag.OPTIONAL + AttributeFlag.TRANSITIVE

registered_tlvs = dict()

def __init__(self, value, hex_value=None):
self.value = value
self.hex_value = hex_value

@classmethod
def register(cls, _type=None):
"""
:param _type:
:return:
"""

def decorator(klass):
"""
:param klass:
:return:
"""
_id = klass.TYPE if _type is None else _type
if _id in cls.registered_tlvs:
raise RuntimeError('Duplicated attribute type')
cls.registered_tlvs[_id] = klass
return klass

return decorator

@classmethod
def unpack(cls, data):
"""
:param data:
:return:
"""
tlvs = []
while data:
type_code = data[0] # Note: Type = 1 octet
length = struct.unpack('!H', data[1:3])[0] # Note: Length = 2 octet
value = data[3: 3 + length]

if type_code in cls.registered_tlvs:
tlvs.append(cls.registered_tlvs[type_code].unpack(value))
else:
tlvs.append({
'type': type_code,
'value': str(binascii.b2a_hex(value))
})
data = data[3 + length:]
return tlvs
8 changes: 8 additions & 0 deletions yabgp/message/attribute/sr/srv6/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# +---------------+---------------------------------+----------+-----------------+
# | TLV Code | Description | Length | Reference |
# | Point | | | |
# +---------------+---------------------------------+----------+-----------------+
# | 5 | SRv6 L3 Service TLV | variable | Section 2 |
# | 1 | SRv6 SID Information Sub-TLV | variable | Section 3.1 |
# | 1 | SRv6 SID Structure Sub-Sub-TLV | 6 | Section 3.2.1 |
# +---------------+---------------------------------+----------+-----------------+
96 changes: 96 additions & 0 deletions yabgp/message/attribute/sr/srv6/l3service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Copyright 2024 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.

import struct

import binascii

from yabgp.tlv import TLV
from ..bgpprefixsid import BGPPrefixSID


# 2. SRv6 Services TLVs
#
# 0 1 2 3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | TLV Type | TLV Length | RESERVED |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | SRv6 Service Sub-TLVs //
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#
# Figure 1: SRv6 Service TLVs


@BGPPrefixSID.register()
class SRv6L3Service(TLV):
"""
SRv6 L3 Service
"""
TYPE = 5 # https://datatracker.ietf.org/doc/html/rfc9252.html#section-2
TYPE_STR = 'srv6_l3_service'

registered_tlvs = dict()

@classmethod
def register(cls, _type=None):
"""
:param _type:
:return:
"""

def decorator(klass):
"""
:param klass:
:return:
"""
_id = klass.TYPE if _type is None else _type
if _id in cls.registered_tlvs:
raise RuntimeError('Duplicated SRv6 Service Sub-TLV type')
cls.registered_tlvs[_id] = klass
return klass

return decorator

@classmethod
def unpack(cls, data):
"""
:param data:
:return:
"""
tlvs = []

# reserved = data[0:1] # Note: First byte is reserved
data = data[1:]
while data:
srv6_service_sub_tlv_type_code = data[0] # Note: Type = 1 octet
srv6_service_sub_tlv_length = struct.unpack('!H', data[1:3])[0] # Note: Length = 2 octet
value = data[3: 3 + srv6_service_sub_tlv_length]

if srv6_service_sub_tlv_type_code in cls.registered_tlvs:
tlvs.append(cls.registered_tlvs[srv6_service_sub_tlv_type_code].unpack(value))
else:
tlvs.append({
'type': srv6_service_sub_tlv_type_code,
'value': str(binascii.b2a_hex(value))
})
data = data[3 + srv6_service_sub_tlv_length:]
value = {
'srv6_service_sub_tlvs': tlvs
}
return {cls.TYPE_STR: value}

0 comments on commit 59dc51b

Please sign in to comment.