-
Notifications
You must be signed in to change notification settings - Fork 445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement RFC6514 MCAST-VPN (incomplete) #1234
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
process mvpn { | ||
run ./run/api-mvpn.run; | ||
encoder json; | ||
} | ||
|
||
neighbor 127.0.0.1 { | ||
router-id 32.32.32.32; | ||
local-address 127.0.0.1; | ||
local-as 65000; | ||
peer-as 65000; | ||
group-updates false; | ||
auto-flush true; | ||
|
||
family { | ||
ipv4 mcast-vpn; | ||
ipv6 mcast-vpn; | ||
} | ||
api { | ||
processes [ mvpn ]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
neighbor 127.0.0.1 { | ||
router-id 32.32.32.32; | ||
local-address 127.0.0.1; | ||
local-as 65000; | ||
peer-as 65000; | ||
group-updates true; | ||
auto-flush true; | ||
|
||
family { | ||
ipv4 mcast-vpn; | ||
ipv6 mcast-vpn; | ||
} | ||
|
||
announce { | ||
ipv4 { | ||
mcast-vpn shared-join rp 10.99.199.1 group 239.251.255.228 rd 65000:99999 source-as 65000 next-hop 10.10.6.3 extended-community [ target:192.168.94.12:5 ]; | ||
mcast-vpn source-join source 10.99.12.2 group 239.251.255.228 rd 65000:99999 source-as 65000 next-hop 10.10.6.3 extended-community [ target:192.168.94.12:5 ]; | ||
mcast-vpn source-ad source 10.99.12.4 group 239.251.255.228 rd 65000:99999 next-hop 10.10.6.4 extended-community [ target:65000:99999 ]; | ||
} | ||
ipv6 { | ||
mcast-vpn shared-join rp fd00::1 group ff0e::1 rd 65000:99999 source-as 65000 next-hop 10.10.6.3 extended-community [ target:192.168.94.12:5 ]; | ||
mcast-vpn source-join source fd12::2 group ff0e::1 rd 65000:99999 source-as 65000 next-hop 10.10.6.3 extended-community [ target:192.168.94.12:5 ]; | ||
mcast-vpn source-ad source fd12::4 group ff0e::1 rd 65000:99999 next-hop 10.10.6.4 extended-community [ target:65000:99999 ]; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#!/usr/bin/python3 | ||
|
||
import os | ||
import sys | ||
import time | ||
|
||
time.sleep(2) # let the EOR pass | ||
|
||
|
||
routes = [ | ||
'ipv4 mcast-vpn shared-join rp 10.99.199.1 group 239.251.255.228 rd 65000:99999 source-as 65000 next-hop 10.10.6.3 extended-community [ target:192.168.94.12:5 ]', | ||
'ipv4 mcast-vpn source-join source 10.99.12.2 group 239.251.255.228 rd 65000:99999 source-as 65000 next-hop 10.10.6.3 extended-community [ target:192.168.94.12:5 ]', | ||
'ipv6 mcast-vpn shared-join rp fd00::1 group ff0e::1 rd 65000:99999 source-as 65000 next-hop 10.10.6.3 extended-community [ target:192.168.94.12:5 ]', | ||
'ipv6 mcast-vpn source-join source fd12::2 group ff0e::1 rd 65000:99999 source-as 65000 next-hop 10.10.6.3 extended-community [ target:192.168.94.12:5 ]', | ||
'ipv6 mcast-vpn source-ad source fd12::4 group ff0e::1 rd 65000:99999 next-hop 10.10.6.4 extended-community [ target:65000:99999 ]', | ||
'ipv4 mcast-vpn source-ad source 10.99.12.4 group 239.251.255.228 rd 65000:99999 next-hop 10.10.6.4 extended-community [ target:65000:99999 ]', | ||
] | ||
|
||
for r in routes: | ||
sys.stdout.write('announce ' + r + '\n') | ||
sys.stdout.flush() | ||
time.sleep(0.3) | ||
|
||
time.sleep(5) | ||
|
||
for r in routes: | ||
sys.stdout.write('withdraw ' + r + '\n') | ||
sys.stdout.flush() | ||
time.sleep(0.3) | ||
|
||
try: | ||
now = time.time() | ||
while os.getppid() != 1 and time.time() < now + 15: | ||
line = sys.stdin.readline().strip() | ||
if not line or 'shutdown' in line: | ||
break | ||
time.sleep(1) | ||
except IOError: | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
api-mvpn.conf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:001E:02:00000007900F0003000105 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:001E:02:00000007900F0003000205 | ||
|
||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:005B:02:00000044400101004002004003040A0A060340050400000064C010080102C0A85E0C0005800E21000105040A0A06030006160000FDE80001869F0000FDE8200A63C70120EFFBFFE4 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:005B:02:00000044400101004002004003040A0A060340050400000064C010080102C0A85E0C0005800E21000105040A0A06030007160000FDE80001869F0000FDE8200A630C0220EFFBFFE4 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0073:02:0000005C400101004002004003040A0A060340050400000064C010080102C0A85E0C0005800E39000205040A0A060300062E0000FDE80001869F0000FDE880FD00000000000000000000000000000180FF0E0000000000000000000000000001 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0073:02:0000005C400101004002004003040A0A060340050400000064C010080102C0A85E0C0005800E39000205040A0A060300072E0000FDE80001869F0000FDE880FD12000000000000000000000000000280FF0E0000000000000000000000000001 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:006F:02:00000058400101004002004003040A0A060440050400000064C010080002FDE80001869F800E35000205040A0A060400052A0000FDE80001869F80FD12000000000000000000000000000480FF0E0000000000000000000000000001 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0057:02:00000040400101004002004003040A0A060440050400000064C010080002FDE80001869F800E1D000105040A0A06040005120000FDE80001869F200A630C0420EFFBFFE4 | ||
|
||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0055:02:0000003E400101004002004003040A0A060340050400000064C010080102C0A85E0C0005800F1B00010506160000FDE80001869F0000FDE8200A63C70120EFFBFFE4 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0055:02:0000003E400101004002004003040A0A060340050400000064C010080102C0A85E0C0005800F1B00010507160000FDE80001869F0000FDE8200A630C0220EFFBFFE4 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:006D:02:00000056400101004002004003040A0A060340050400000064C010080102C0A85E0C0005800F33000205062E0000FDE80001869F0000FDE880FD00000000000000000000000000000180FF0E0000000000000000000000000001 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:006D:02:00000056400101004002004003040A0A060340050400000064C010080102C0A85E0C0005800F33000205072E0000FDE80001869F0000FDE880FD12000000000000000000000000000280FF0E0000000000000000000000000001 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0069:02:00000052400101004002004003040A0A060440050400000064C010080002FDE80001869F800F2F000205052A0000FDE80001869F80FD12000000000000000000000000000480FF0E0000000000000000000000000001 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0051:02:0000003A400101004002004003040A0A060440050400000064C010080002FDE80001869F800F1700010505120000FDE80001869F200A630C0420EFFBFFE4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
conf-mvpn.conf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0073:02:0000005C400101004002004003040A0A060340050400000064C010080102C0A85E0C0005800E39000105040A0A06030006160000FDE80001869F0000FDE8200A63C70120EFFBFFE407160000FDE80001869F0000FDE8200A630C0220EFFBFFE4 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:00A3:02:0000008C400101004002004003040A0A060340050400000064C010080102C0A85E0C0005800E69000205040A0A060300062E0000FDE80001869F0000FDE880FD00000000000000000000000000000180FF0E0000000000000000000000000001072E0000FDE80001869F0000FDE880FD12000000000000000000000000000280FF0E0000000000000000000000000001 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0057:02:00000040400101004002004003040A0A060440050400000064C010080002FDE80001869F800E1D000105040A0A06040005120000FDE80001869F200A630C0420EFFBFFE4 | ||
1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:006F:02:00000058400101004002004003040A0A060440050400000064C010080002FDE80001869F800E35000205040A0A060400052A0000FDE80001869F80FD12000000000000000000000000000480FF0E0000000000000000000000000001 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Every MVPN should be imported from this file | ||
# as it makes sure that all the registering decorator are run | ||
|
||
# flake8: noqa: F401,E261 | ||
|
||
from exabgp.bgp.message.update.nlri.mvpn.nlri import MVPN | ||
|
||
from exabgp.bgp.message.update.nlri.mvpn.sourcead import SourceAD | ||
from exabgp.bgp.message.update.nlri.mvpn.sourcejoin import SourceJoin | ||
from exabgp.bgp.message.update.nlri.mvpn.sharedjoin import SharedJoin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
from struct import pack | ||
|
||
from exabgp.protocol.family import AFI | ||
from exabgp.protocol.family import SAFI | ||
|
||
from exabgp.bgp.message import Action | ||
|
||
from exabgp.bgp.message.update.nlri import NLRI | ||
|
||
# https://datatracker.ietf.org/doc/html/rfc6514 | ||
|
||
# +-----------------------------------+ | ||
# | Route Type (1 octet) | | ||
# +-----------------------------------+ | ||
# | Length (1 octet) | | ||
# +-----------------------------------+ | ||
# | Route Type specific (variable) | | ||
# +-----------------------------------+ | ||
|
||
# ========================================================================= MVPN | ||
|
||
|
||
@NLRI.register(AFI.ipv4, SAFI.mcast_vpn) | ||
@NLRI.register(AFI.ipv6, SAFI.mcast_vpn) | ||
class MVPN(NLRI): | ||
registered_mvpn = dict() | ||
|
||
# NEED to be defined in the subclasses | ||
CODE = -1 | ||
NAME = 'Unknown' | ||
SHORT_NAME = 'unknown' | ||
|
||
def __init__(self, afi, action=Action.UNSET, addpath=None): | ||
NLRI.__init__(self, afi=afi, safi=SAFI.mcast_vpn, action=action) | ||
self._packed = b'' | ||
|
||
def __hash__(self): | ||
return hash("%s:%s:%s:%s" % (self.afi, self.safi, self.CODE, self._packed)) | ||
|
||
def __len__(self): | ||
return len(self._packed) + 2 | ||
|
||
def __eq__(self, other): | ||
return NLRI.__eq__(self, other) and self.CODE == other.CODE | ||
|
||
def __str__(self): | ||
return "mvpn:%s:%s" % ( | ||
self.registered_mvpn.get(self.CODE, self).SHORT_NAME.lower(), | ||
'0x' + ''.join('%02x' % _ for _ in self._packed), | ||
) | ||
|
||
def __repr__(self): | ||
return str(self) | ||
|
||
def feedback(self, action): | ||
# if self.nexthop is None and action == Action.ANNOUNCE: | ||
# return 'mvpn nlri next-hop is missing' | ||
return '' | ||
|
||
def _prefix(self): | ||
return "mvpn:%s:" % (self.registered_mvpn.get(self.CODE, self).SHORT_NAME.lower()) | ||
|
||
def pack_nlri(self, negotiated=None): | ||
# XXX: addpath not supported yet | ||
return pack('!BB', self.CODE, len(self._packed)) + self._packed | ||
|
||
@classmethod | ||
def register(cls, klass): | ||
if klass.CODE in cls.registered_mvpn: | ||
raise RuntimeError('only one MVPN registration allowed') | ||
cls.registered_mvpn[klass.CODE] = klass | ||
return klass | ||
|
||
@classmethod | ||
def unpack_nlri(cls, afi, safi, bgp, action, addpath): | ||
code = bgp[0] | ||
length = bgp[1] | ||
|
||
if code in cls.registered_mvpn: | ||
klass = cls.registered_mvpn[code].unpack(bgp[2 : length + 2], afi) | ||
else: | ||
klass = GenericMVPN(afi, code, bgp[2 : length + 2]) | ||
klass.CODE = code | ||
klass.action = action | ||
klass.addpath = addpath | ||
|
||
return klass, bgp[length + 2 :] | ||
|
||
def _raw(self): | ||
return ''.join('%02X' % _ for _ in self.pack_nlri()) | ||
|
||
|
||
class GenericMVPN(MVPN): | ||
def __init__(self, afi, code, packed): | ||
MVPN.__init__(self, afi) | ||
self.CODE = code | ||
self._pack(packed) | ||
|
||
def _pack(self, packed=None): | ||
if self._packed: | ||
return self._packed | ||
|
||
if packed: | ||
self._packed = packed | ||
return packed | ||
|
||
def json(self, compact=None): | ||
return '{ "code": %d, "parsed": false, "raw": "%s" }' % (self.CODE, self._raw()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
from exabgp.protocol.family import AFI | ||
from exabgp.protocol.family import SAFI | ||
|
||
from exabgp.bgp.message.update.nlri.qualifier import RouteDistinguisher | ||
from exabgp.bgp.message.update.nlri.mvpn.nlri import MVPN | ||
from exabgp.bgp.message.notification import Notify | ||
from exabgp.protocol.ip import IP | ||
from struct import pack | ||
|
||
# +-----------------------------------+ | ||
# | RD (8 octets) | | ||
# +-----------------------------------+ | ||
# | Source AS (4 octets) | | ||
# +-----------------------------------+ | ||
# | Multicast Source Length (1 octet) | | ||
# +-----------------------------------+ | ||
# | Multicast Source (variable) | | ||
# +-----------------------------------+ | ||
# | Multicast Group Length (1 octet) | | ||
# +-----------------------------------+ | ||
# | Multicast Group (variable) | | ||
# +-----------------------------------+ | ||
|
||
|
||
@MVPN.register | ||
class SharedJoin(MVPN): | ||
CODE = 6 | ||
NAME = "C-Multicast Shared Tree Join route" | ||
SHORT_NAME = "Shared-Join" | ||
|
||
def __init__(self, rd, afi, source, group, source_as, packed=None, action=None, addpath=None): | ||
MVPN.__init__(self, afi=afi, action=action, addpath=addpath) | ||
self.rd = rd | ||
self.group = group | ||
self.source = source | ||
self.source_as = source_as | ||
self._pack(packed) | ||
|
||
def __eq__(self, other): | ||
return ( | ||
isinstance(other, SharedJoin) | ||
and self.CODE == other.CODE | ||
and self.rd == other.rd | ||
and self.source == other.source | ||
and self.group == other.group | ||
) | ||
|
||
def __ne__(self, other): | ||
return not self.__eq__(other) | ||
|
||
def __str__(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this represent the route in a way which could be parsed by the configuration code? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't get this one. can you share an example? |
||
return f'{self._prefix()}:{self.rd._str()}:{str(self.source_as)}:{str(self.source)}:{str(self.group)}' | ||
|
||
def __hash__(self): | ||
return hash((self.rd, self.source, self.group, self.source_as)) | ||
|
||
def _pack(self, packed=None): | ||
if self._packed: | ||
return self._packed | ||
|
||
if packed: | ||
self._packed = packed | ||
return packed | ||
self._packed = ( | ||
self.rd.pack() | ||
+ pack('!I', self.source_as) | ||
+ bytes([len(self.source) * 8]) | ||
+ self.source.pack() | ||
+ bytes([len(self.group) * 8]) | ||
+ self.group.pack() | ||
) | ||
return self._packed | ||
|
||
@classmethod | ||
def unpack(cls, data, afi): | ||
datalen = len(data) | ||
if datalen not in (22, 46): # IPv4 or IPv6 | ||
raise Notify(3, 5, f"Invalid C-Multicast Route length ({datalen} bytes).") | ||
cursor = 0 | ||
rd = RouteDistinguisher.unpack(data[cursor:8]) | ||
cursor += 8 | ||
source_as = int.from_bytes(data[cursor : cursor + 4], "big") | ||
cursor += 4 | ||
sourceiplen = int(data[cursor] / 8) | ||
cursor += 1 | ||
if sourceiplen != 4 and sourceiplen != 16: | ||
raise Notify( | ||
3, | ||
5, | ||
f"Invalid C-Multicast Route length ({sourceiplen*8} bits). Expected 32 bits (IPv4) or 128 bits (IPv6).", | ||
) | ||
sourceip = IP.unpack(data[cursor : cursor + sourceiplen]) | ||
cursor += sourceiplen | ||
groupiplen = int(data[cursor] / 8) | ||
cursor += 1 | ||
if groupiplen != 4 and groupiplen != 16: | ||
raise Notify( | ||
3, | ||
5, | ||
f"Invalid C-Multicast Route length ({groupiplen*8} bits). Expected 32 bits (IPv4) or 128 bits (IPv6).", | ||
) | ||
groupip = IP.unpack(data[cursor : cursor + groupiplen]) | ||
return cls(afi=afi, rd=rd, source=sourceip, group=groupip, source_as=source_as, packed=data) | ||
|
||
def json(self, compact=None): | ||
content = ' "code": %d, ' % self.CODE | ||
content += '"parsed": true, ' | ||
content += '"raw": "%s", ' % self._raw() | ||
content += '"name": "%s", ' % self.NAME | ||
content += '%s, ' % self.rd.json() | ||
content += '"source-as": "%s", ' % str(self.source_as) | ||
content += '"source": "%s", ' % str(self.source) | ||
content += '"group": "%s"' % str(self.group) | ||
return '{%s}' % content |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This name may be used when the route is printed and may need to be lower case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean NAME, SHORT_NAME or both? I tried to follow example from other nlri code (like mup and evpn), but didn't notice a pattern regarding lower-case use.
Notice SHORT_NAME is already lower-cased here when printing by _prefix():
exabgp/src/exabgp/bgp/message/update/nlri/mvpn/sharedjoin.py
Lines 51 to 52 in 279a289
exabgp/src/exabgp/bgp/message/update/nlri/mvpn/nlri.py
Lines 60 to 61 in 279a289
This is the print output you mean right?