forked from FRRouting/frr
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Farid Mihoub <farid.mihoub@6wind.com>
- Loading branch information
Farid Mihoub
committed
Jul 12, 2023
1 parent
6934a1d
commit 875511c
Showing
10 changed files
with
1,152 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
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,34 @@ | ||
# SPDX-License-Identifier: ISC | ||
|
||
# Copyright 2023 6WIND S.A. | ||
# Authored by Farid Mihoub <farid.mihoub@6wind.com> | ||
# | ||
import ipaddress | ||
import struct | ||
|
||
|
||
class BGPOpen: | ||
UNPACK_STR = '!16sHBBHH4sB' | ||
|
||
@classmethod | ||
def dissect(cls, data): | ||
(marker, | ||
length, | ||
open_type, | ||
version, | ||
my_as, | ||
hold_time, | ||
bgp_id, | ||
optional_params_len) = struct.unpack_from(cls.UNPACK_STR, data) | ||
|
||
data = data[struct.calcsize(cls.UNPACK_STR) + optional_params_len:] | ||
|
||
# XXX: parse optional parameters | ||
|
||
return data, { | ||
'version': version, | ||
'my_as': my_as, | ||
'hold_time': hold_time, | ||
'bgp_id': ipaddress.ip_address(bgp_id), | ||
'optional_params_len': optional_params_len, | ||
} |
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,54 @@ | ||
# SPDX-License-Identifier: ISC | ||
|
||
# Copyright 2023 6WIND S.A. | ||
# Authored by Farid Mihoub <farid.mihoub@6wind.com> | ||
# | ||
import ipaddress | ||
import struct | ||
|
||
from .nlri import NlriIPv4Unicast | ||
from .path_attributes import PathAttribute | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class BGPUpdate: | ||
UNPACK_STR = '!16sHBH' | ||
STATIC_SIZE = 23 | ||
|
||
@classmethod | ||
def dissect(cls, data): | ||
msg = {'bmp_log_type': 'update'} | ||
common_size = struct.calcsize(cls.UNPACK_STR) | ||
(marker, | ||
length, | ||
update_type, | ||
withdrawn_routes_len) = struct.unpack_from(cls.UNPACK_STR, data) | ||
|
||
# get withdrawn routes | ||
withdrawn_routes = '' | ||
if withdrawn_routes_len: | ||
withdrawn_routes = NlriIPv4Unicast.parse( | ||
data[common_size:common_size + withdrawn_routes_len] | ||
) | ||
msg['bmp_log_type'] = 'withdraw' | ||
msg.update(withdrawn_routes) | ||
|
||
# get path attributes | ||
(total_path_attrs_len,) = struct.unpack_from( | ||
'!H', data[common_size+withdrawn_routes_len:]) | ||
|
||
if total_path_attrs_len: | ||
offset = cls.STATIC_SIZE + withdrawn_routes_len | ||
path_attrs_data = data[offset:offset + total_path_attrs_len] | ||
while path_attrs_data: | ||
path_attrs_data, pattr = PathAttribute.dissect(path_attrs_data) | ||
if pattr: | ||
msg = {**msg, **pattr} | ||
|
||
# get nlri | ||
nlri_len = length - cls.STATIC_SIZE - withdrawn_routes_len - total_path_attrs_len | ||
if nlri_len > 0: | ||
nlri = NlriIPv4Unicast.parse(data[length - nlri_len:length]) | ||
msg.update(nlri) | ||
|
||
return data[length:], msg |
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,53 @@ | ||
# SPDX-License-Identifier: ISC | ||
|
||
# Copyright 2023 6WIND S.A. | ||
# Authored by Farid Mihoub <farid.mihoub@6wind.com> | ||
# | ||
|
||
# IANA Address Family Identifier | ||
AFI_IP = 1 | ||
AFI_IP6 = 2 | ||
AFI_L2VPN = 25 | ||
|
||
# IANA Subsequent Address Family Idenitifier | ||
SAFI_UNICAST = 1 | ||
SAFI_MULTICAST = 2 | ||
SAFI_MPLS_LABEL = 4 | ||
SAFI_EVPN = 70 | ||
SAFI_MPLS_VPN = 128 | ||
SAFI_IP_FLOWSPEC = 133 | ||
SAFI_VPN_FLOWSPEC = 134 | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class AddressFamily: | ||
def __init__(self, afi, safi): | ||
self.afi = afi | ||
self.safi = safi | ||
|
||
def __eq__(self, other): | ||
if not isinstance(other, type(self)): | ||
return False | ||
return (self.afi, self.safi) == (other.afi, other.safi) | ||
|
||
def __str__(self): | ||
return f'afi: {self.afi}, safi: {self.safi}' | ||
|
||
def __hash__(self): | ||
return hash((self.afi, self.safi)) | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class AF: | ||
IPv4_UNICAST = AddressFamily(AFI_IP, SAFI_UNICAST) | ||
IPv6_UNICAST = AddressFamily(AFI_IP6, SAFI_UNICAST) | ||
IPv4_VPN = AddressFamily(AFI_IP, SAFI_MPLS_VPN) | ||
IPv6_VPN = AddressFamily(AFI_IP6, SAFI_MPLS_VPN) | ||
IPv4_MPLS = AddressFamily(AFI_IP, SAFI_MPLS_LABEL) | ||
IPv6_MPLS = AddressFamily(AFI_IP6, SAFI_MPLS_LABEL) | ||
IPv4_FLOWSPEC = AddressFamily(AFI_IP, SAFI_IP_FLOWSPEC) | ||
IPv6_FLOWSPEC = AddressFamily(AFI_IP6, SAFI_IP_FLOWSPEC) | ||
VPNv4_FLOWSPEC = AddressFamily(AFI_IP, SAFI_VPN_FLOWSPEC) | ||
VPNv6_FLOWSPEC = AddressFamily(AFI_IP6, SAFI_VPN_FLOWSPEC) | ||
L2EVPN = AddressFamily(AFI_L2VPN, SAFI_EVPN) | ||
L2VPN_FLOWSPEC = AddressFamily(AFI_L2VPN, SAFI_VPN_FLOWSPEC) |
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,140 @@ | ||
# SPDX-License-Identifier: ISC | ||
|
||
# Copyright 2023 6WIND S.A. | ||
# Authored by Farid Mihoub <farid.mihoub@6wind.com> | ||
# | ||
import ipaddress | ||
import struct | ||
|
||
from .af import AddressFamily, AF | ||
from .rd import RouteDistinguisher | ||
|
||
|
||
def decode_label(label): | ||
# from frr | ||
# frr encode just one label | ||
return (label[0] << 12) | (label[1] << 4) | (label[2] & 0xf0) >> 4 | ||
|
||
def padding(databin, len_): | ||
""" | ||
Assumption: | ||
One nlri per update/withdraw message, so we can add | ||
a padding to the prefix without worrying about its length | ||
""" | ||
if len(databin) >= len_: | ||
return databin | ||
return databin + b'\0' * (len_ - len(databin)) | ||
|
||
def dissect_nlri(nlri_data, afi, safi): | ||
""" | ||
Exract nlri information based on the address family | ||
""" | ||
addr_family = AddressFamily(afi, safi) | ||
if addr_family == AF.IPv6_VPN: | ||
return NlriIPv6Vpn.parse(nlri_data) | ||
elif addr_family == AF.IPv4_VPN: | ||
return NlriIPv4Vpn.parse(nlri_data) | ||
elif addr_family == AF.IPv6_UNICAST: | ||
return NlriIPv6Unicast.parse(nlri_data) | ||
|
||
return {'ip_prefix': 'Unknown'} | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class NlriIPv4Unicast: | ||
|
||
@staticmethod | ||
def parse(data): | ||
"""parses prefixes from withdrawn_routes or nrli data""" | ||
(prefix_len,) = struct.unpack_from('!B', data) | ||
prefix = padding(data[1:], 4) | ||
|
||
return {'ip_prefix': f'{ipaddress.IPv4Address(prefix)}/{prefix_len}'} | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class NlriIPv6Unicast: | ||
@staticmethod | ||
def parse(data): | ||
"""parses prefixes from withdrawn_routes or nrli data""" | ||
(prefix_len,) = struct.unpack_from('!B', data) | ||
prefix = padding(data[1:], 16) | ||
|
||
return {'ip_prefix': f'{ipaddress.IPv6Address(prefix)}/{prefix_len}'} | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class NlriIPv4Vpn: | ||
UNPACK_STR = '!B3s8s' | ||
|
||
@classmethod | ||
def parse(cls, data): | ||
(bit_len, label, rd) = struct.unpack_from(cls.UNPACK_STR, data) | ||
offset = struct.calcsize(cls.UNPACK_STR) | ||
|
||
ipv4 = padding(data[offset:], 4) | ||
# prefix_len = total_bits_len - label_bits_len - rd_bits_len | ||
prefix_len = bit_len - 3*8 - 8*8 | ||
return { | ||
'label': decode_label(label), | ||
'rd': str(RouteDistinguisher(rd)), | ||
'ip_prefix': f'{ipaddress.IPv4Address(ipv4)}/{prefix_len}', | ||
} | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class NlriIPv6Vpn: | ||
UNPACK_STR = '!B3s8s' | ||
|
||
@classmethod | ||
def parse(cls, data): | ||
# rfc 3107, 8227 | ||
(bit_len, label, rd) = struct.unpack_from(cls.UNPACK_STR, data) | ||
offset = struct.calcsize(cls.UNPACK_STR) | ||
|
||
ipv6 = padding(data[offset:], 16) | ||
prefix_len = bit_len - 3*8 - 8*8 | ||
return { | ||
'label': decode_label(label), | ||
'rd': str(RouteDistinguisher(rd)), | ||
'ip_prefix': f'{ipaddress.IPv6Address(ipv6)}/{prefix_len}', | ||
} | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class NlriIPv4Mpls: | ||
pass | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class NlriIPv6Mpls: | ||
pass | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class NlriIPv4FlowSpec: | ||
pass | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class NlriIPv6FlowSpec: | ||
pass | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class NlriVpn4FlowSpec: | ||
pass | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class NlriVpn6FlowSpec: | ||
pass | ||
|
||
|
||
#------------------------------------------------------------------------------ | ||
class NlriL2EVPN: | ||
pass | ||
|
||
#------------------------------------------------------------------------------ | ||
class NlriL2VPNFlowSpec: | ||
pass |
Oops, something went wrong.