Skip to content

Commit 3463e62

Browse files
Merge pull request #1255 from Quentique/1-add-srv6-support-bgp-ls
Implement RFC 9514
2 parents 38e553e + b421863 commit 3463e62

File tree

13 files changed

+775
-1
lines changed

13 files changed

+775
-1
lines changed

qa/decoding/bgp-ls-10

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
update bgp-ls bgp-ls
2+
ffffffffffffffffffffffffffffffff014c0200000135900e007140044710fc302200000d0000000000000000000f00000200580200000000000000000100001a02000004000030220201000400000000020300060000000000130101001b0200000400003022020100040000000002030007000000000014030102000800000010000000000107000200024001010040020040050400000064801daf044100044cee6b28044700030003e804530024003980000000000000000014fc302200000de002000000000000000004e400042010104004530024003900000000000000000014fc302200000de003000000000000000004e400042010104004530024003980800000000000000014fc302201000de006000000000000000004e400042010104004530024003900800000000000000014fc302201000de007000000000000000004e4000420101040
3+
{ "exabgp": "5.0.0", "time": 1750925172.4396412, "host" : "quentin-21hd00b1fr", "pid" : 1397954, "ppid" : 1323016, "counter": 1, "type": "update", "neighbor": { "address": { "local": "127.0.0.1", "peer": "127.0.0.1" }, "asn": { "local": 65533, "peer": 65533 } , "direction": "in", "message": { "update": { "attribute": { "origin": "igp", "local-preference": 100, "bgp-ls": { "maximum-link-bandwidth": 125000000.0, "igp-metric": 1000, "srv6-lan-endx-isis": [ {"flags": {"B": 1, "S": 0, "P": 0, "RSV": 0}, "neighbor-id": "000000000014", "behavior": 57, "algorithm": 0, "weight": 0, "sid": "fc30:2200:d:e002::", "srv6-sid-structure": {"loc_block_len": 32, "loc_node_len": 16, "func_len": 16, "arg_len": 64}}, {"flags": {"B": 0, "S": 0, "P": 0, "RSV": 0}, "neighbor-id": "000000000014", "behavior": 57, "algorithm": 0, "weight": 0, "sid": "fc30:2200:d:e003::", "srv6-sid-structure": {"loc_block_len": 32, "loc_node_len": 16, "func_len": 16, "arg_len": 64}}, {"flags": {"B": 1, "S": 0, "P": 0, "RSV": 0}, "neighbor-id": "000000000014", "behavior": 57, "algorithm": 128, "weight": 0, "sid": "fc30:2201:d:e006::", "srv6-sid-structure": {"loc_block_len": 32, "loc_node_len": 16, "func_len": 16, "arg_len": 64}}, {"flags": {"B": 0, "S": 0, "P": 0, "RSV": 0}, "neighbor-id": "000000000014", "behavior": 57, "algorithm": 128, "weight": 0, "sid": "fc30:2201:d:e007::", "srv6-sid-structure": {"loc_block_len": 32, "loc_node_len": 16, "func_len": 16, "arg_len": 64}} ] } }, "announce": { "bgp-ls bgp-ls": { "fc30:2200:d::f": [ { "ls-nlri-type": "bgpls-link", "l3-routing-topology": 0, "protocol-id": 2, "local-node-descriptors": [ { "autonomous-system": 12322 }, { "bgp-ls-identifier": "0" }, { "router-id": "000000000013" } ], "remote-node-descriptors": [ { "autonomous-system": 12322 }, { "bgp-ls-identifier": "0" }, { "router-id": "00000000001403", "psn": "3" } ], "interface-addresses": [ ], "neighbor-addresses": [ ], "multi-topology-ids": [ 2 ], "link-identifiers": [ ] } ] } } } } } }

qa/decoding/bgp-ls-6

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
update bgp-ls bgp-ls
22
0000 01D9 900E 0070 4004 4710 FC00 1000 0001 0000 0000 0000 0000 0001 0000 0200 5702 0000 0000 0000 0000 0100 001A 0200 0004 0002 1C90 0201 0004 0000 0000 0203 0006 0000 0000 0015 0101 001A 0200 0004 0002 1C90 0201 0004 0000 0000 0203 0006 0003 0000 0009 0102 0008 0000 0027 0000 0035 0107 0002 0002 4001 0100 4002 0040 0504 0000 0064 901D 0153 0404 0004 0A00 CA01 0405 0010 FC00 1000 0112 0000 0000 0000 0000 0001 0406 0004 0A00 0201 0407 0010 FC00 1000 0002 0000 0000 0000 0000 0001 0441 0004 4E95 02F9 0447 0003 0000 0A04 5200 1E00 3980 0000 00FC 0010 0001 12E0 0200 0000 0000 0000 0004 E400 0420 1010 0004 5200 1E00 3900 0000 00FC 0010 0001 12E0 0300 0000 0000 0000 0004 E400 0420 1010 0004 5200 1E00 3980 8100 00FC 0010 0101 12E0 0200 0000 0000 0000 0004 E400 0420 1010 0004 5200 1E00 3900 8100 00FC 0010 0101 12E0 0300 0000 0000 0000 0004 E400 0420 1010 0004 5200 1E00 3980 8200 00FC 0010 0301 12E0 0200 0000 0000 0000 0004 E400 0420 1010 0004 5200 1E00 3900 8200 00FC 0010 0301 12E0 0300 0000 0000 0000 0004 E400 0420 1010 0004 5A00 0400 0000 0A04 5B00 0800 0000 0A00 0000 0A04 5C00 0400 0000 0004 6200 2004 0400 0010 0000 0000 0000 0004 4400 0400 0000 0A04 5B00 0800 0000 0A00 0000 00
3-
{ "exabgp": "5.0.0", "time": 1653385370.350801, "host" : "MacBook-Pro-2.local", "pid" : 35933, "ppid" : 55846, "counter": 1, "type": "update", "neighbor": { "address": { "local": "127.0.0.1", "peer": "127.0.0.1" }, "asn": { "local": 65533, "peer": 65533 } , "direction": "in", "message": { "update": { "attribute": { "origin": "igp", "local-preference": 100, "bgp-ls": { "local-te-router-ids": ["10.0.202.1"], "local-te-router-ids": ["fc00:1000:112::1"], "remote-te-router-id": "10.0.2.1", "remote-te-router-id": "fc00:1000:2::1", "maximum-link-bandwidth": 1250000000.0, "igp-metric": 10, "generic-lsid-1106": ["0x003980000000FC0010000112E002000000000000000004E4000420101000", "0x003900000000FC0010000112E003000000000000000004E4000420101000", "0x003980810000FC0010010112E002000000000000000004E4000420101000", "0x003900810000FC0010010112E003000000000000000004E4000420101000", "0x003980820000FC0010030112E002000000000000000004E4000420101000", "0x003900820000FC0010030112E003000000000000000004E4000420101000"], "generic-lsid-1114": ["0x0000000A"], "generic-lsid-1115": ["0x0000000A0000000A"], "generic-lsid-1116": ["0x00000000"], "generic-lsid-1122": ["0x040400001000000000000000044400040000000A045B00080000000A00000000"] } }, "announce": { "bgp-ls bgp-ls": { "fc00:1000:1::1": [ { "ls-nlri-type": "bgpls-link", "l3-routing-topology": 0, "protocol-id": 2, "local-node-descriptors": [ { "autonomous-system": 138384 }, { "bgp-ls-identifier": "0" }, { "router-id": "000000000015" } ], "remote-node-descriptors": [ { "autonomous-system": 138384 }, { "bgp-ls-identifier": "0" }, { "router-id": "000300000009" } ], "interface-addresses": [ ], "neighbor-addresses": [ ], "multi-topology-ids": [ 2 ], "link-identifiers": [ ] } ] } } } } } }
3+
{ "exabgp": "5.0.0", "time": 1653385370.350801, "host" : "MacBook-Pro-2.local", "pid" : 35933, "ppid" : 55846, "counter": 1, "type": "update", "neighbor": { "address": { "local": "127.0.0.1", "peer": "127.0.0.1" }, "asn": { "local": 65533, "peer": 65533 } , "direction": "in", "message": { "update": { "attribute": { "origin": "igp", "local-preference": 100, "bgp-ls": { "local-te-router-ids": ["10.0.202.1"], "local-te-router-ids": ["fc00:1000:112::1"], "remote-te-router-id": "10.0.2.1", "remote-te-router-id": "fc00:1000:2::1", "maximum-link-bandwidth": 1250000000.0, "igp-metric": 10, "srv6-endx": [ {"flags": {"B": 1, "S": 0, "P": 0, "RSV": 0}, "behavior": 57, "algorithm": 0, "weight": 0, "sid": "fc00:1000:112:e002::", "srv6-sid-structure": {"loc_block_len": 32, "loc_node_len": 16, "func_len": 16, "arg_len": 0}}, {"flags": {"B": 0, "S": 0, "P": 0, "RSV": 0}, "behavior": 57, "algorithm": 0, "weight": 0, "sid": "fc00:1000:112:e003::", "srv6-sid-structure": {"loc_block_len": 32, "loc_node_len": 16, "func_len": 16, "arg_len": 0}}, {"flags": {"B": 1, "S": 0, "P": 0, "RSV": 0}, "behavior": 57, "algorithm": 129, "weight": 0, "sid": "fc00:1001:112:e002::", "srv6-sid-structure": {"loc_block_len": 32, "loc_node_len": 16, "func_len": 16, "arg_len": 0}}, {"flags": {"B": 0, "S": 0, "P": 0, "RSV": 0}, "behavior": 57, "algorithm": 129, "weight": 0, "sid": "fc00:1001:112:e003::", "srv6-sid-structure": {"loc_block_len": 32, "loc_node_len": 16, "func_len": 16, "arg_len": 0}}, {"flags": {"B": 1, "S": 0, "P": 0, "RSV": 0}, "behavior": 57, "algorithm": 130, "weight": 0, "sid": "fc00:1003:112:e002::", "srv6-sid-structure": {"loc_block_len": 32, "loc_node_len": 16, "func_len": 16, "arg_len": 0}}, {"flags": {"B": 0, "S": 0, "P": 0, "RSV": 0}, "behavior": 57, "algorithm": 130, "weight": 0, "sid": "fc00:1003:112:e003::", "srv6-sid-structure": {"loc_block_len": 32, "loc_node_len": 16, "func_len": 16, "arg_len": 0}} ], "generic-lsid-1114": ["0x0000000A"], "generic-lsid-1115": ["0x0000000A0000000A"], "generic-lsid-1116": ["0x00000000"], "generic-lsid-1122": ["0x040400001000000000000000044400040000000A045B00080000000A00000000"] } }, "announce": { "bgp-ls bgp-ls": { "fc00:1000:1::1": [ { "ls-nlri-type": "bgpls-link", "l3-routing-topology": 0, "protocol-id": 2, "local-node-descriptors": [ { "autonomous-system": 138384 }, { "bgp-ls-identifier": "0" }, { "router-id": "000000000015" } ], "remote-node-descriptors": [ { "autonomous-system": 138384 }, { "bgp-ls-identifier": "0" }, { "router-id": "000300000009" } ], "interface-addresses": [ ], "neighbor-addresses": [ ], "multi-topology-ids": [ 2 ], "link-identifiers": [ ] } ] } } } } } }

src/exabgp/bgp/message/update/attribute/bgpls/link/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,21 @@
6969
# draft-gredler-idr-bgp-ls-segment-routing-ext-03
7070
from exabgp.bgp.message.update.attribute.bgpls.link.sradj import SrAdjacency
7171
from exabgp.bgp.message.update.attribute.bgpls.link.sradjlan import SrAdjacencyLan
72+
73+
# +================+=============================+===========+
74+
# | TLV Code Point | Description | Reference |
75+
# +================+=============================+===========+
76+
# | 1038 | SRv6 Capabilities TLV | RFC 9514 |
77+
# | 1106 | SRv6 End.X SID TLV | RFC 9514 |
78+
# | 1107 | SRv6 LAN End.X SID ISIS TLV | RFC 9514 |
79+
# | 1108 | SRv6 LAN End.X SID OSPF TLV | RFC 9514 |
80+
# | 1162 | SRv6 Locator TLV | RFC 9514 |
81+
# | 1250 | SRv6 Endpoint Behavior TLV | RFC 9514 |
82+
# | 1252 | SRv6 SID Structure TLV | RFC 9514 |
83+
# +----------------+-----------------------------+-----------+
84+
from exabgp.bgp.message.update.attribute.bgpls.link.srv6endx import Srv6EndX
85+
from exabgp.bgp.message.update.attribute.bgpls.link.srv6sidstructure import Srv6SidStructure
86+
from exabgp.bgp.message.update.attribute.bgpls.link.srv6capabilities import Srv6Capabilities
87+
from exabgp.bgp.message.update.attribute.bgpls.link.srv6locator import Srv6Locator
88+
from exabgp.bgp.message.update.attribute.bgpls.link.srv6endpointbehavior import Srv6EndpointBehavior
89+
from exabgp.bgp.message.update.attribute.bgpls.link.srv6lanendx import Srv6LanEndXISIS, Srv6LanEndXOSPF
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# encoding: utf-8
2+
"""
3+
srv6endx.py
4+
5+
Created by Quentin De Muynck
6+
Copyright (c) 2025 Exa Networks. All rights reserved.
7+
"""
8+
9+
from __future__ import annotations
10+
11+
import json
12+
13+
from exabgp.bgp.message.update.attribute.bgpls.linkstate import BaseLS
14+
from exabgp.bgp.message.update.attribute.bgpls.linkstate import LinkState
15+
16+
# RFC 9514: 3.1. SRv6 Capabilities TLV
17+
# 0 1 2 3
18+
# 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
19+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20+
# | Type | Length |
21+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22+
# | Flags | Reserved |
23+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
24+
#
25+
# Figure 1: SRv6 Capabilities TLV Format
26+
27+
28+
@LinkState.register()
29+
class Srv6Capabilities(BaseLS):
30+
TLV = 1038
31+
registered_subsubtlvs = dict()
32+
33+
def __init__(self, flags):
34+
self.flags = flags
35+
36+
def __repr__(self):
37+
return 'flags: %s' % (self.flags)
38+
39+
@classmethod
40+
def register(cls):
41+
def register_subsubtlv(klass):
42+
code = klass.TLV
43+
if code in cls.registered_subsubtlvs:
44+
raise RuntimeError('only one class can be registered per SRv6 Capabilities Sub-TLV type')
45+
cls.registered_subsubtlvs[code] = klass
46+
return klass
47+
48+
return register_subsubtlv
49+
50+
@classmethod
51+
def unpack(cls, data):
52+
flags_value = int.from_bytes(data[0:2], byteorder='big')
53+
flags = {'O': flags_value & (1 << 6)}
54+
return cls(flags=flags)
55+
56+
def json(self, compact=None):
57+
return '"srv6-capabilities": ' + json.dumps(
58+
{
59+
'flags': self.flags,
60+
},
61+
indent=compact,
62+
)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# encoding: utf-8
2+
"""
3+
srv6endpointbehavior.py
4+
5+
Created by Quentin De Muynck
6+
Copyright (c) 2025 Exa Networks. All rights reserved.
7+
"""
8+
9+
from __future__ import annotations
10+
11+
import json
12+
from struct import unpack
13+
14+
from exabgp.bgp.message.update.attribute.bgpls.linkstate import BaseLS
15+
from exabgp.bgp.message.update.attribute.bgpls.linkstate import LinkState
16+
17+
# RFC 9514 : 7.1. SRv6 Endpoint Behavior TLV
18+
# 0 1 2 3
19+
# 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
20+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21+
# | Type | Length |
22+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23+
# | Endpoint Behavior | Flags | Algorithm |
24+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25+
#
26+
# Figure 7: SRv6 Endpoint Behavior TLV
27+
28+
29+
@LinkState.register()
30+
class Srv6EndpointBehavior(BaseLS):
31+
TLV = 1250
32+
33+
def __init__(
34+
self,
35+
endpoint_behavior,
36+
flags,
37+
algorithm,
38+
):
39+
self.endpoint_behavior = endpoint_behavior
40+
self.flags = flags
41+
self.algorithm = algorithm
42+
43+
@classmethod
44+
def unpack(cls, data):
45+
flags = [] # No flags defined according to RFC 9514 and 9352
46+
algorithm = data[3]
47+
endpoint_behavior = unpack('!H', data[0:2])[0]
48+
49+
return cls(endpoint_behavior=endpoint_behavior, flags=flags, algorithm=algorithm)
50+
51+
def __str__(self):
52+
return 'srv6-endpoint-behavior [0x%s, flags: %s, algorithm: %d]' % (
53+
self.endpoint_behavior,
54+
self.flags,
55+
self.algorithm,
56+
)
57+
58+
def json(self, compact=None):
59+
return '"srv6-endpoint-behavior": ' + json.dumps(
60+
{
61+
'endpoint-behavior': self.endpoint_behavior,
62+
'flags': self.flags,
63+
'algorithm': self.algorithm,
64+
},
65+
indent=compact,
66+
)
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# encoding: utf-8
2+
"""
3+
srv6endx.py
4+
5+
Created by Vincent Bernat
6+
Copyright (c) 2025 Exa Networks. All rights reserved.
7+
"""
8+
9+
from __future__ import annotations
10+
11+
import json
12+
from struct import unpack
13+
from exabgp.util import hexstring
14+
15+
from exabgp.bgp.message.update.attribute.bgpls.linkstate import FlagLS
16+
from exabgp.bgp.message.update.attribute.bgpls.linkstate import LinkState
17+
from exabgp.protocol.ip import IPv6
18+
19+
# RFC 9514: 4.1. SRv6 End.X SID TLV
20+
# 0 1 2 3
21+
# 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
22+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23+
# | Type | Length |
24+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25+
# | Endpoint Behavior | Flags | Algorithm |
26+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
27+
# | Weight | Reserved | SID (16 octets) ... |
28+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
29+
# | SID (cont ...) |
30+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
31+
# | SID (cont ...) |
32+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33+
# | SID (cont ...) |
34+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35+
# | SID (cont ...) | Sub-TLVs (variable) . . .
36+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37+
38+
39+
@LinkState.register()
40+
class Srv6EndX(FlagLS):
41+
TLV = 1106
42+
FLAGS = ['B', 'S', 'P', 'RSV', 'RSV', 'RSV', 'RSV', 'RSV']
43+
MERGE = True
44+
registered_subsubtlvs = dict()
45+
46+
def __init__(self, content):
47+
self.content = [content]
48+
49+
def __repr__(self):
50+
return '\n'.join(
51+
[
52+
'behavior: %s, flags: %s, algorithm: %s, weight: %s, sid: %s'
53+
% (d.behavior, d.flags, d.algorithm, d.weight, d.sid)
54+
for d in self.content
55+
]
56+
)
57+
58+
@classmethod
59+
def register(cls):
60+
def register_subsubtlv(klass):
61+
code = klass.TLV
62+
if code in cls.registered_subsubtlvs:
63+
raise RuntimeError('only one class can be registered per SRv6 End.X Sub-TLV type')
64+
cls.registered_subsubtlvs[code] = klass
65+
return klass
66+
67+
return register_subsubtlv
68+
69+
@classmethod
70+
def unpack(cls, data):
71+
behavior = unpack('!I', bytes([0, 0]) + data[:2])[0]
72+
flags = cls.unpack_flags(data[2:3])
73+
algorithm = data[3]
74+
weight = data[4]
75+
sid = IPv6.ntop(data[6:22])
76+
data = data[22:]
77+
subtlvs = []
78+
79+
while data and len(data) >= 4:
80+
code = unpack('!H', data[0:2])[0]
81+
length = unpack('!H', data[2:4])[0]
82+
83+
if code in cls.registered_subsubtlvs:
84+
subsubtlv = cls.registered_subsubtlvs[code].unpack(data[4 : length + 4])
85+
else:
86+
subsubtlv = hexstring(data[4 : length + 4])
87+
data = data[length + 4 :]
88+
89+
subtlvs.append(subsubtlv.json())
90+
91+
content = {**{
92+
'flags': flags,
93+
'behavior': behavior,
94+
'algorithm': algorithm,
95+
'weight': weight,
96+
'sid': sid,
97+
}, **json.loads('{' + ', '.join(subtlvs) + '}')}
98+
99+
return cls(content=content)
100+
101+
def json(self, compact=None):
102+
return '"srv6-endx": [ %s ]' % ', '.join([json.dumps(d, indent=compact) for d in self.content])

0 commit comments

Comments
 (0)