Skip to content

Commit 59dc51b

Browse files
authored
Add support for parsing the Path Attribute: BGP Prefix-SID (Type 40) 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
1 parent 3a28d5c commit 59dc51b

File tree

17 files changed

+462
-25
lines changed

17 files changed

+462
-25
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@ jobs:
1717
strategy:
1818
fail-fast: false
1919
matrix:
20-
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
20+
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
2121
os: [ ubuntu-20.04, macos-latest, windows-latest ]
22-
python-version: [ "2.7", "3.6", "3.x" ]
22+
python-version: [ "3.6", "3.x" ]
2323
exclude:
24-
- os: windows-latest
25-
python-version: "2.7"
24+
- python-version: "3.x"
2625

2726
steps:
2827
- uses: actions/checkout@v3

.travis.yml

Lines changed: 0 additions & 14 deletions
This file was deleted.

yabgp/common/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
BGPTYPE_TUNNEL_ENCAPS_ATTR = 23 # RFC5512
9090
BGPTYPE_LINK_STATE = 29
9191
BGPTYPE_LARGE_COMMUNITY = 32
92+
BGPTYPE_BGP_PREFIX_SID = 40 # RFC8669 & RFC9252
9293
BGPTYPE_ATTRIBUTE_SET = 128
9394

9495
# BGP Tunnel Encapsulation Attribute Tunnel Types

yabgp/message/attribute/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class AttributeFlag(int):
4646
PARTIAL = 0x20 # 32 RFC 4271
4747
TRANSITIVE = 0x40 # 64 RFC 4271
4848
OPTIONAL = 0x80 # 128 RFC 4271
49+
4950
# OPTIONAL_TRANSITIVE = 0xc0 # 192 RFC 4271
5051

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

yabgp/message/attribute/linkstate/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@
5252
from .link.unidirect_avail_bw import UnidirectAvailBw # noqa
5353
from .link.unidirect_bw_util import UnidirectBwUtil # noqa
5454
from .link.extend_admin_group import ExtendedAdminGroup # noqa
55-
from .link.srv6_end_x_sid import SRv6EndXSid # noqa
56-
from .link.srv6_lan_end_x_sid import SRv6LANEndXSid # noqa
55+
from .link.srv6_end_x_sid import SRv6EndXSID # noqa
56+
from .link.srv6_lan_end_x_sid import SRv6LANEndXSID # noqa
5757
from .link.srv6_sid import SRv6SID # noqa
5858
from .prefix.prefix_metric import PrefixMetric # noqa
5959
from .prefix.prefix_sid import PrefixSID # noqa

yabgp/message/attribute/linkstate/link/srv6_end_x_sid.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424

2525
@LinkState.register()
26-
class SRv6EndXSid(TLV):
26+
class SRv6EndXSID(TLV):
2727
"""
2828
SRv6 End.X SID
2929
"""

yabgp/message/attribute/linkstate/link/srv6_lan_end_x_sid.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
@LinkState.register(_type=1107) # IS-IS
2727
@LinkState.register(_type=1108) # OSPFv3
28-
class SRv6LANEndXSid(TLV):
28+
class SRv6LANEndXSID(TLV):
2929
"""
3030
SRv6 LAN End.X SID
3131
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright 2024 Cisco Systems, Inc.
2+
# All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
from .bgpprefixsid import BGPPrefixSID # noqa
17+
from .srv6.l3service import SRv6L3Service # noqa
18+
from .srv6.sidinformation import SRv6SIDInformation # noqa
19+
from .srv6.sidstructure import SRv6SIDStructure # noqa
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Copyright 2024 Cisco Systems, Inc.
2+
# All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
import struct
17+
18+
import binascii
19+
20+
from yabgp.message.attribute import Attribute, AttributeFlag, AttributeID
21+
22+
23+
class BGPPrefixSID(Attribute):
24+
"""
25+
BGP Prefix SID
26+
27+
Original: https://datatracker.ietf.org/doc/html/rfc8669#section-3
28+
Extend: https://datatracker.ietf.org/doc/html/rfc9252#section-2
29+
"""
30+
31+
ID = AttributeID.BGP_PREFIX_SID
32+
FLAG = AttributeFlag.OPTIONAL + AttributeFlag.TRANSITIVE
33+
34+
registered_tlvs = dict()
35+
36+
def __init__(self, value, hex_value=None):
37+
self.value = value
38+
self.hex_value = hex_value
39+
40+
@classmethod
41+
def register(cls, _type=None):
42+
"""
43+
44+
:param _type:
45+
:return:
46+
"""
47+
48+
def decorator(klass):
49+
"""
50+
51+
:param klass:
52+
:return:
53+
"""
54+
_id = klass.TYPE if _type is None else _type
55+
if _id in cls.registered_tlvs:
56+
raise RuntimeError('Duplicated attribute type')
57+
cls.registered_tlvs[_id] = klass
58+
return klass
59+
60+
return decorator
61+
62+
@classmethod
63+
def unpack(cls, data):
64+
"""
65+
66+
:param data:
67+
:return:
68+
"""
69+
tlvs = []
70+
while data:
71+
type_code = data[0] # Note: Type = 1 octet
72+
length = struct.unpack('!H', data[1:3])[0] # Note: Length = 2 octet
73+
value = data[3: 3 + length]
74+
75+
if type_code in cls.registered_tlvs:
76+
tlvs.append(cls.registered_tlvs[type_code].unpack(value))
77+
else:
78+
tlvs.append({
79+
'type': type_code,
80+
'value': str(binascii.b2a_hex(value))
81+
})
82+
data = data[3 + length:]
83+
return tlvs
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# +---------------+---------------------------------+----------+-----------------+
2+
# | TLV Code | Description | Length | Reference |
3+
# | Point | | | |
4+
# +---------------+---------------------------------+----------+-----------------+
5+
# | 5 | SRv6 L3 Service TLV | variable | Section 2 |
6+
# | 1 | SRv6 SID Information Sub-TLV | variable | Section 3.1 |
7+
# | 1 | SRv6 SID Structure Sub-Sub-TLV | 6 | Section 3.2.1 |
8+
# +---------------+---------------------------------+----------+-----------------+
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Copyright 2024 Cisco Systems, Inc.
2+
# All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
import struct
17+
18+
import binascii
19+
20+
from yabgp.tlv import TLV
21+
from ..bgpprefixsid import BGPPrefixSID
22+
23+
24+
# 2. SRv6 Services TLVs
25+
#
26+
# 0 1 2 3
27+
# 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
28+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
29+
# | TLV Type | TLV Length | RESERVED |
30+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
31+
# | SRv6 Service Sub-TLVs //
32+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33+
#
34+
# Figure 1: SRv6 Service TLVs
35+
36+
37+
@BGPPrefixSID.register()
38+
class SRv6L3Service(TLV):
39+
"""
40+
SRv6 L3 Service
41+
"""
42+
TYPE = 5 # https://datatracker.ietf.org/doc/html/rfc9252.html#section-2
43+
TYPE_STR = 'srv6_l3_service'
44+
45+
registered_tlvs = dict()
46+
47+
@classmethod
48+
def register(cls, _type=None):
49+
"""
50+
51+
:param _type:
52+
:return:
53+
"""
54+
55+
def decorator(klass):
56+
"""
57+
58+
:param klass:
59+
:return:
60+
"""
61+
_id = klass.TYPE if _type is None else _type
62+
if _id in cls.registered_tlvs:
63+
raise RuntimeError('Duplicated SRv6 Service Sub-TLV type')
64+
cls.registered_tlvs[_id] = klass
65+
return klass
66+
67+
return decorator
68+
69+
@classmethod
70+
def unpack(cls, data):
71+
"""
72+
73+
:param data:
74+
:return:
75+
"""
76+
tlvs = []
77+
78+
# reserved = data[0:1] # Note: First byte is reserved
79+
data = data[1:]
80+
while data:
81+
srv6_service_sub_tlv_type_code = data[0] # Note: Type = 1 octet
82+
srv6_service_sub_tlv_length = struct.unpack('!H', data[1:3])[0] # Note: Length = 2 octet
83+
value = data[3: 3 + srv6_service_sub_tlv_length]
84+
85+
if srv6_service_sub_tlv_type_code in cls.registered_tlvs:
86+
tlvs.append(cls.registered_tlvs[srv6_service_sub_tlv_type_code].unpack(value))
87+
else:
88+
tlvs.append({
89+
'type': srv6_service_sub_tlv_type_code,
90+
'value': str(binascii.b2a_hex(value))
91+
})
92+
data = data[3 + srv6_service_sub_tlv_length:]
93+
value = {
94+
'srv6_service_sub_tlvs': tlvs
95+
}
96+
return {cls.TYPE_STR: value}

0 commit comments

Comments
 (0)