mirror of
https://github.com/faucetsdn/ryu.git
synced 2026-05-08 22:06:10 +02:00
BGPSpeaker: Support Four-Octet AS number
Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
parent
2039347560
commit
55d955f484
@ -1045,24 +1045,18 @@ class RouteTargetMembershipNLRI(StringifyMixin):
|
||||
if not (origin_as is self.DEFAULT_AS and
|
||||
route_target is self.DEFAULT_RT):
|
||||
# We validate them
|
||||
if (not self._is_valid_old_asn(origin_as) or
|
||||
if (not self._is_valid_asn(origin_as) or
|
||||
not self._is_valid_ext_comm_attr(route_target)):
|
||||
raise ValueError('Invalid params.')
|
||||
self.origin_as = origin_as
|
||||
self.route_target = route_target
|
||||
|
||||
def _is_valid_old_asn(self, asn):
|
||||
"""Returns true if given asn is a 16 bit number.
|
||||
|
||||
Old AS numbers are 16 but unsigned number.
|
||||
"""
|
||||
valid = True
|
||||
# AS number should be a 16 bit number
|
||||
if (not isinstance(asn, numbers.Integral) or (asn < 0) or
|
||||
(asn > ((2 ** 16) - 1))):
|
||||
valid = False
|
||||
|
||||
return valid
|
||||
def _is_valid_asn(self, asn):
|
||||
"""Returns True if the given AS number is Two or Four Octet."""
|
||||
if isinstance(asn, six.integer_types) and 0 <= asn <= 0xffffffff:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _is_valid_ext_comm_attr(self, attr):
|
||||
"""Validates *attr* as string representation of RT or SOO.
|
||||
@ -2311,6 +2305,17 @@ class BGPOpen(BGPMessage):
|
||||
self.opt_param_len = opt_param_len
|
||||
self.opt_param = opt_param
|
||||
|
||||
@property
|
||||
def opt_param_cap_map(self):
|
||||
cap_map = {}
|
||||
for param in self.opt_param:
|
||||
if param.type == BGP_OPT_CAPABILITY:
|
||||
cap_map[param.cap_code] = param
|
||||
return cap_map
|
||||
|
||||
def get_opt_param_cap(self, cap_code):
|
||||
return self.opt_param_cap_map.get(cap_code)
|
||||
|
||||
@classmethod
|
||||
def parser(cls, buf):
|
||||
(version,
|
||||
@ -2403,17 +2408,17 @@ class BGPUpdate(BGPMessage):
|
||||
self.withdrawn_routes = withdrawn_routes
|
||||
self.total_path_attribute_len = total_path_attribute_len
|
||||
self.path_attributes = path_attributes
|
||||
self._pathattr_map = {}
|
||||
for attr in path_attributes:
|
||||
self._pathattr_map[attr.type] = attr
|
||||
self.nlri = nlri
|
||||
|
||||
@property
|
||||
def pathattr_map(self):
|
||||
return self._pathattr_map
|
||||
passattr_map = {}
|
||||
for attr in self.path_attributes:
|
||||
passattr_map[attr.type] = attr
|
||||
return passattr_map
|
||||
|
||||
def get_path_attr(self, attr_name):
|
||||
return self._pathattr_map.get(attr_name)
|
||||
return self.pathattr_map.get(attr_name)
|
||||
|
||||
@classmethod
|
||||
def parser(cls, buf):
|
||||
|
||||
@ -45,13 +45,14 @@ from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_IPV6
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV4
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV6
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_ENHANCED_REFRESH
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_FOUR_OCTET_AS_NUMBER
|
||||
from ryu.services.protocols.bgp.rtconf.base import MULTI_EXIT_DISC
|
||||
from ryu.services.protocols.bgp.rtconf.base import SITE_OF_ORIGINS
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_IPV4
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_VPNV4
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_VPNV6
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors \
|
||||
import DEFAULT_CAP_ENHANCED_REFRESH
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors import (
|
||||
DEFAULT_CAP_ENHANCED_REFRESH, DEFAULT_CAP_FOUR_OCTET_AS_NUMBER)
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CONNECT_MODE
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors import PEER_NEXT_HOP
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors import PASSWORD
|
||||
@ -237,6 +238,7 @@ class BGPSpeaker(object):
|
||||
enable_vpnv4=DEFAULT_CAP_MBGP_VPNV4,
|
||||
enable_vpnv6=DEFAULT_CAP_MBGP_VPNV6,
|
||||
enable_enhanced_refresh=DEFAULT_CAP_ENHANCED_REFRESH,
|
||||
enable_four_octet_as_number=DEFAULT_CAP_FOUR_OCTET_AS_NUMBER,
|
||||
next_hop=None, password=None, multi_exit_disc=None,
|
||||
site_of_origins=None, is_route_server_client=False,
|
||||
is_next_hop_self=False, local_address=None,
|
||||
@ -262,9 +264,12 @@ class BGPSpeaker(object):
|
||||
``enable_vpnv6`` enables VPNv6 address family for this
|
||||
neighbor. The default is False.
|
||||
|
||||
``enable_enhanced_refresh`` enable Enhanced Route Refresh for this
|
||||
``enable_enhanced_refresh`` enables Enhanced Route Refresh for this
|
||||
neighbor. The default is False.
|
||||
|
||||
``enable_four_octet_as_number`` enables Four-Octet AS Number
|
||||
capability for this neighbor. The default is True.
|
||||
|
||||
``next_hop`` specifies the next hop IP address. If not
|
||||
specified, host's ip address to access to a peer is used.
|
||||
|
||||
@ -298,15 +303,17 @@ class BGPSpeaker(object):
|
||||
CONNECT_MODE_BOTH use both methods.
|
||||
The default is CONNECT_MODE_BOTH.
|
||||
"""
|
||||
bgp_neighbor = {}
|
||||
bgp_neighbor[neighbors.IP_ADDRESS] = address
|
||||
bgp_neighbor[neighbors.REMOTE_AS] = remote_as
|
||||
bgp_neighbor[PEER_NEXT_HOP] = next_hop
|
||||
bgp_neighbor[PASSWORD] = password
|
||||
bgp_neighbor[IS_ROUTE_SERVER_CLIENT] = is_route_server_client
|
||||
bgp_neighbor[IS_NEXT_HOP_SELF] = is_next_hop_self
|
||||
bgp_neighbor[CONNECT_MODE] = connect_mode
|
||||
bgp_neighbor[CAP_ENHANCED_REFRESH] = enable_enhanced_refresh
|
||||
bgp_neighbor = {
|
||||
neighbors.IP_ADDRESS: address,
|
||||
neighbors.REMOTE_AS: remote_as,
|
||||
PEER_NEXT_HOP: next_hop,
|
||||
PASSWORD: password,
|
||||
IS_ROUTE_SERVER_CLIENT: is_route_server_client,
|
||||
IS_NEXT_HOP_SELF: is_next_hop_self,
|
||||
CONNECT_MODE: connect_mode,
|
||||
CAP_ENHANCED_REFRESH: enable_enhanced_refresh,
|
||||
CAP_FOUR_OCTET_AS_NUMBER: enable_four_octet_as_number,
|
||||
}
|
||||
# v6 advertizement is available with only v6 peering
|
||||
if netaddr.valid_ipv4(address):
|
||||
bgp_neighbor[CAP_MBGP_IPV4] = enable_ipv4
|
||||
|
||||
@ -21,6 +21,8 @@ import socket
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from six.moves import zip_longest
|
||||
|
||||
from ryu.services.protocols.bgp.base import Activity
|
||||
from ryu.services.protocols.bgp.base import Sink
|
||||
from ryu.services.protocols.bgp.base import Source
|
||||
@ -43,6 +45,7 @@ from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4, VRF_RF_IPV6
|
||||
from ryu.services.protocols.bgp.utils import bgp as bgp_utils
|
||||
from ryu.services.protocols.bgp.utils.evtlet import EventletIOFactory
|
||||
from ryu.services.protocols.bgp.utils import stats
|
||||
from ryu.services.protocols.bgp.utils.validation import is_valid_old_asn
|
||||
|
||||
from ryu.lib.packet import bgp
|
||||
|
||||
@ -69,6 +72,7 @@ from ryu.lib.packet.bgp import BGP_MSG_ROUTE_REFRESH
|
||||
|
||||
from ryu.lib.packet.bgp import BGPPathAttributeNextHop
|
||||
from ryu.lib.packet.bgp import BGPPathAttributeAsPath
|
||||
from ryu.lib.packet.bgp import BGPPathAttributeAs4Path
|
||||
from ryu.lib.packet.bgp import BGPPathAttributeLocalPref
|
||||
from ryu.lib.packet.bgp import BGPPathAttributeExtendedCommunities
|
||||
from ryu.lib.packet.bgp import BGPPathAttributeMpReachNLRI
|
||||
@ -77,7 +81,10 @@ from ryu.lib.packet.bgp import BGPPathAttributeCommunities
|
||||
from ryu.lib.packet.bgp import BGPPathAttributeMultiExitDisc
|
||||
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_ORIGIN
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AGGREGATOR
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS4_AGGREGATOR
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS_PATH
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS4_PATH
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_NEXT_HOP
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_MP_REACH_NLRI
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_MP_UNREACH_NLRI
|
||||
@ -394,6 +401,10 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
|
||||
def local_as(self):
|
||||
return self._neigh_conf.local_as
|
||||
|
||||
@property
|
||||
def cap_four_octet_as_number(self):
|
||||
return self._neigh_conf.cap_four_octet_as_number
|
||||
|
||||
@property
|
||||
def in_filters(self):
|
||||
return self._in_filters
|
||||
@ -465,6 +476,11 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
|
||||
raise ValueError('Invalid request: Peer not in established state')
|
||||
return self._protocol.is_mbgp_cap_valid(route_family)
|
||||
|
||||
def is_four_octet_as_number_cap_valid(self):
|
||||
if not self.in_established:
|
||||
raise ValueError('Invalid request: Peer not in established state')
|
||||
return self._protocol.is_four_octet_as_number_cap_valid()
|
||||
|
||||
def is_ebgp_peer(self):
|
||||
"""Returns *True* if this is a eBGP peer, else *False*."""
|
||||
return self._common_conf.local_as != self._neigh_conf.remote_as
|
||||
@ -820,6 +836,124 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
|
||||
from netaddr import IPAddress
|
||||
return str(IPAddress(ipv4_address).ipv6())
|
||||
|
||||
def _construct_as_path_attr(self, as_path_attr, as4_path_attr):
|
||||
"""Marge AS_PATH and AS4_PATH attribute instances into
|
||||
a single AS_PATH instance."""
|
||||
|
||||
def _listify(li):
|
||||
"""Reconstruct AS_PATH list.
|
||||
|
||||
Example::
|
||||
|
||||
>>> _listify([[1, 2, 3], {4, 5}, [6, 7]])
|
||||
[1, 2, 3, {4, 5}, 6, 7]
|
||||
"""
|
||||
lo = []
|
||||
for l in li:
|
||||
if isinstance(l, list):
|
||||
lo.extend(l)
|
||||
elif isinstance(l, set):
|
||||
lo.append(l)
|
||||
else:
|
||||
pass
|
||||
return lo
|
||||
|
||||
# If AS4_PATH attribute is None, returns the given AS_PATH attribute
|
||||
if as4_path_attr is None:
|
||||
return as_path_attr
|
||||
|
||||
# If AS_PATH is shorter than AS4_PATH, AS4_PATH should be ignored.
|
||||
if as_path_attr.get_as_path_len() < as4_path_attr.get_as_path_len():
|
||||
return as_path_attr
|
||||
|
||||
org_as_path_list = _listify(as_path_attr.path_seg_list)
|
||||
as4_path_list = _listify(as4_path_attr.path_seg_list)
|
||||
|
||||
# Reverse to compare backward.
|
||||
org_as_path_list.reverse()
|
||||
as4_path_list.reverse()
|
||||
|
||||
new_as_path_list = []
|
||||
tmp_list = []
|
||||
for as_path, as4_path in zip_longest(org_as_path_list, as4_path_list):
|
||||
if as4_path is None:
|
||||
if isinstance(as_path, int):
|
||||
tmp_list.insert(0, as_path)
|
||||
elif isinstance(as_path, set):
|
||||
if tmp_list:
|
||||
new_as_path_list.insert(0, tmp_list)
|
||||
tmp_list = []
|
||||
new_as_path_list.insert(0, as_path)
|
||||
else:
|
||||
pass
|
||||
elif isinstance(as4_path, int):
|
||||
tmp_list.insert(0, as4_path)
|
||||
elif isinstance(as4_path, set):
|
||||
if tmp_list:
|
||||
new_as_path_list.insert(0, tmp_list)
|
||||
tmp_list = []
|
||||
new_as_path_list.insert(0, as4_path)
|
||||
else:
|
||||
pass
|
||||
if tmp_list:
|
||||
new_as_path_list.insert(0, tmp_list)
|
||||
|
||||
return bgp.BGPPathAttributeAsPath(new_as_path_list)
|
||||
|
||||
def _trans_as_path(self, as_path_list):
|
||||
"""Translates Four-Octet AS number to AS_TRANS and separates
|
||||
AS_PATH list into AS_PATH and AS4_PATH lists if needed.
|
||||
|
||||
If the neighbor does not support Four-Octet AS number,
|
||||
this method constructs AS4_PATH list from AS_PATH list and swaps
|
||||
non-mappable AS number in AS_PATH with AS_TRANS, then
|
||||
returns AS_PATH list and AS4_PATH list.
|
||||
If the neighbor supports Four-Octet AS number, returns
|
||||
the given AS_PATH list and None.
|
||||
"""
|
||||
|
||||
def _swap(n):
|
||||
if is_valid_old_asn(n):
|
||||
# mappable
|
||||
return n
|
||||
else:
|
||||
# non-mappable
|
||||
return bgp.AS_TRANS
|
||||
|
||||
# If the neighbor supports Four-Octet AS number, returns
|
||||
# the given AS_PATH list and None.
|
||||
if self.is_four_octet_as_number_cap_valid():
|
||||
return as_path_list, None
|
||||
|
||||
# If the neighbor does not support Four-Octet AS number,
|
||||
# constructs AS4_PATH list from AS_PATH list and swaps
|
||||
# non-mappable AS number in AS_PATH with AS_TRANS.
|
||||
else:
|
||||
new_as_path_list = []
|
||||
for as_path in as_path_list:
|
||||
if isinstance(as_path, set):
|
||||
path_set = set()
|
||||
for as_num in as_path:
|
||||
path_set.add(_swap(as_num))
|
||||
new_as_path_list.append(path_set)
|
||||
elif isinstance(as_path, list):
|
||||
path_list = list()
|
||||
for as_num in as_path:
|
||||
path_list.append(_swap(as_num))
|
||||
new_as_path_list.append(path_list)
|
||||
else:
|
||||
# Ignore invalid as_path type
|
||||
pass
|
||||
|
||||
# If all of the AS_PATH list is composed of mappable four-octet
|
||||
# AS numbers only, returns the given AS_PATH list
|
||||
# Assumption: If the constructed AS_PATH list is the same as
|
||||
# the given AS_PATH list, all AS number is mappable.
|
||||
if as_path_list == new_as_path_list:
|
||||
return as_path_list, None
|
||||
|
||||
return new_as_path_list, as_path_list
|
||||
|
||||
def _construct_update(self, outgoing_route):
|
||||
"""Construct update message with Outgoing-routes path attribute
|
||||
appropriately cloned/copied/updated.
|
||||
@ -847,14 +981,18 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
|
||||
# Supported and un-supported/unknown attributes.
|
||||
origin_attr = None
|
||||
nexthop_attr = None
|
||||
aspath_attr = None
|
||||
as_path_attr = None
|
||||
as4_path_attr = None
|
||||
aggregator_attr = None
|
||||
as4_aggregator_attr = None
|
||||
extcomm_attr = None
|
||||
community_attr = None
|
||||
localpref_attr = None
|
||||
unknown_opttrans_attrs = None
|
||||
nlri_list = [path.nlri]
|
||||
|
||||
# By default we use BGPS's interface IP with this peer as next_hop.
|
||||
# By default, we use BGPSpeaker's interface IP with this peer
|
||||
# as next_hop.
|
||||
if self.is_ebgp_peer():
|
||||
next_hop = self._session_next_hop(path)
|
||||
if path.is_local() and path.has_nexthop():
|
||||
@ -887,18 +1025,18 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
|
||||
assert origin_attr, 'Missing ORIGIN mandatory attribute.'
|
||||
|
||||
# AS_PATH Attribute.
|
||||
# Construct AS-path-attr using paths aspath attr. with local AS as
|
||||
# Construct AS-path-attr using paths AS_PATH attr. with local AS as
|
||||
# first item.
|
||||
path_aspath = pathattr_map.get(BGP_ATTR_TYPE_AS_PATH)
|
||||
assert path_aspath, 'Missing AS_PATH mandatory attribute.'
|
||||
# Deep copy aspath_attr value
|
||||
path_seg_list = path_aspath.path_seg_list
|
||||
# Deep copy AS_PATH attr value
|
||||
as_path_list = path_aspath.path_seg_list
|
||||
# If this is a iBGP peer.
|
||||
if not self.is_ebgp_peer():
|
||||
# When a given BGP speaker advertises the route to an internal
|
||||
# peer, the advertising speaker SHALL NOT modify the AS_PATH
|
||||
# attribute associated with the route.
|
||||
aspath_attr = BGPPathAttributeAsPath(path_seg_list)
|
||||
pass
|
||||
else:
|
||||
# When a given BGP speaker advertises the route to an external
|
||||
# peer, the advertising speaker updates the AS_PATH attribute
|
||||
@ -920,13 +1058,42 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
|
||||
# 3) if the AS_PATH is empty, the local system creates a path
|
||||
# segment of type AS_SEQUENCE, places its own AS into that
|
||||
# segment, and places that segment into the AS_PATH.
|
||||
if (len(path_seg_list) > 0 and
|
||||
isinstance(path_seg_list[0], list) and
|
||||
len(path_seg_list[0]) < 255):
|
||||
path_seg_list[0].insert(0, self.local_as)
|
||||
if (len(as_path_list) > 0 and
|
||||
isinstance(as_path_list[0], list) and
|
||||
len(as_path_list[0]) < 255):
|
||||
as_path_list[0].insert(0, self.local_as)
|
||||
else:
|
||||
path_seg_list.insert(0, [self.local_as])
|
||||
aspath_attr = BGPPathAttributeAsPath(path_seg_list)
|
||||
as_path_list.insert(0, [self.local_as])
|
||||
# Construct AS4_PATH list from AS_PATH list and swap
|
||||
# non-mappable AS number with AS_TRANS in AS_PATH.
|
||||
as_path_list, as4_path_list = self._trans_as_path(
|
||||
as_path_list)
|
||||
# If the neighbor supports Four-Octet AS number, send AS_PATH
|
||||
# in Four-Octet.
|
||||
if self.is_four_octet_as_number_cap_valid():
|
||||
as_path_attr = BGPPathAttributeAsPath(
|
||||
as_path_list, as_pack_str='!I') # specify Four-Octet.
|
||||
# Otherwise, send AS_PATH in Two-Octet.
|
||||
else:
|
||||
as_path_attr = BGPPathAttributeAsPath(as_path_list)
|
||||
# If needed, send AS4_PATH attribute.
|
||||
if as4_path_list:
|
||||
as4_path_attr = BGPPathAttributeAs4Path(as4_path_list)
|
||||
|
||||
# AGGREGATOR Attribute.
|
||||
aggregator_attr = pathattr_map.get(BGP_ATTR_TYPE_AGGREGATOR)
|
||||
# If the neighbor does not support Four-Octet AS number,
|
||||
# swap non-mappable AS number with AS_TRANS.
|
||||
if (aggregator_attr and
|
||||
not self.is_four_octet_as_number_cap_valid()):
|
||||
# If AS number of AGGREGATOR is Four-Octet AS number,
|
||||
# swap with AS_TRANS, else do not.
|
||||
aggregator_as_number = aggregator_attr.as_number
|
||||
if not is_valid_old_asn(aggregator_as_number):
|
||||
aggregator_attr = bgp.BGPPathAttributeAggregator(
|
||||
bgp.AS_TRANS, aggregator_attr.addr)
|
||||
as4_aggregator_attr = bgp.BGPPathAttributeAs4Aggregator(
|
||||
aggregator_as_number, aggregator_attr.addr)
|
||||
|
||||
# MULTI_EXIT_DISC Attribute.
|
||||
# For eBGP session we can send multi-exit-disc if configured.
|
||||
@ -1010,7 +1177,13 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
|
||||
new_pathattr.append(mpnlri_attr)
|
||||
|
||||
new_pathattr.append(origin_attr)
|
||||
new_pathattr.append(aspath_attr)
|
||||
new_pathattr.append(as_path_attr)
|
||||
if as4_path_attr:
|
||||
new_pathattr.append(as4_path_attr)
|
||||
if aggregator_attr:
|
||||
new_pathattr.append(aggregator_attr)
|
||||
if as4_aggregator_attr:
|
||||
new_pathattr.append(as4_aggregator_attr)
|
||||
if multi_exit_disc:
|
||||
new_pathattr.append(multi_exit_disc)
|
||||
if localpref_attr:
|
||||
@ -1192,6 +1365,9 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
|
||||
Current setting include capabilities, timers and ids.
|
||||
"""
|
||||
asnum = self.local_as
|
||||
# If local AS number is not Two-Octet AS number, swaps with AS_TRANS.
|
||||
if not is_valid_old_asn(asnum):
|
||||
asnum = bgp.AS_TRANS
|
||||
bgpid = self._common_conf.router_id
|
||||
holdtime = self._neigh_conf.hold_time
|
||||
|
||||
@ -1329,8 +1505,10 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
|
||||
# Increment count of update received.
|
||||
mp_reach_attr = update_msg.get_path_attr(BGP_ATTR_TYPE_MP_REACH_NLRI)
|
||||
mp_unreach_attr = update_msg.get_path_attr(
|
||||
BGP_ATTR_TYPE_MP_UNREACH_NLRI
|
||||
)
|
||||
BGP_ATTR_TYPE_MP_UNREACH_NLRI)
|
||||
|
||||
# Extract advertised path attributes and reconstruct AS_PATH attribute
|
||||
self._extract_and_reconstruct_as_path(update_msg)
|
||||
|
||||
nlri_list = update_msg.nlri
|
||||
withdraw_list = update_msg.withdrawn_routes
|
||||
@ -1349,6 +1527,68 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
|
||||
if withdraw_list:
|
||||
self._extract_and_handle_bgp4_withdraws(withdraw_list)
|
||||
|
||||
def _extract_and_reconstruct_as_path(self, update_msg):
|
||||
"""Extracts advertised AS path attributes in the given update message
|
||||
and reconstructs AS_PATH from AS_PATH and AS4_PATH if needed."""
|
||||
umsg_pattrs = update_msg.pathattr_map
|
||||
|
||||
as_aggregator = umsg_pattrs.get(BGP_ATTR_TYPE_AGGREGATOR, None)
|
||||
as4_aggregator = umsg_pattrs.get(BGP_ATTR_TYPE_AS4_AGGREGATOR, None)
|
||||
if as_aggregator and as4_aggregator:
|
||||
# When both AGGREGATOR and AS4_AGGREGATOR are received,
|
||||
# if the AS number in the AGGREGATOR attribute is not AS_TRANS,
|
||||
# then:
|
||||
# - the AS4_AGGREGATOR attribute and the AS4_PATH attribute SHALL
|
||||
# be ignored,
|
||||
# - the AGGREGATOR attribute SHALL be taken as the information
|
||||
# about the aggregating node, and
|
||||
# - the AS_PATH attribute SHALL be taken as the AS path
|
||||
# information.
|
||||
if as_aggregator.as_number != bgp.AS_TRANS:
|
||||
update_msg.path_attributes.remove(as4_aggregator)
|
||||
as4_path = umsg_pattrs.pop(BGP_ATTR_TYPE_AS4_PATH, None)
|
||||
if as4_path:
|
||||
update_msg.path_attributes.remove(as4_path)
|
||||
# Otherwise,
|
||||
# - the AGGREGATOR attribute SHALL be ignored,
|
||||
# - the AS4_AGGREGATOR attribute SHALL be taken as the
|
||||
# information about the aggregating node, and
|
||||
# - the AS path information would need to be constructed,
|
||||
# as in all other cases.
|
||||
else:
|
||||
update_msg.path_attributes.remove(as_aggregator)
|
||||
update_msg.path_attributes.remove(as4_aggregator)
|
||||
update_msg.path_attributes.append(
|
||||
bgp.BGPPathAttributeAggregator(
|
||||
as_number=as4_aggregator.as_number,
|
||||
addr=as4_aggregator.addr,
|
||||
)
|
||||
)
|
||||
|
||||
as_path = umsg_pattrs.get(BGP_ATTR_TYPE_AS_PATH, None)
|
||||
as4_path = umsg_pattrs.get(BGP_ATTR_TYPE_AS4_PATH, None)
|
||||
if as_path and as4_path:
|
||||
# If the number of AS numbers in the AS_PATH attribute is
|
||||
# less than the number of AS numbers in the AS4_PATH attribute,
|
||||
# then the AS4_PATH attribute SHALL be ignored, and the AS_PATH
|
||||
# attribute SHALL be taken as the AS path information.
|
||||
if as_path.get_as_path_len() < as4_path.get_as_path_len():
|
||||
update_msg.path_attributes.remove(as4_path)
|
||||
|
||||
# If the number of AS numbers in the AS_PATH attribute is larger
|
||||
# than or equal to the number of AS numbers in the AS4_PATH
|
||||
# attribute, then the AS path information SHALL be constructed
|
||||
# by taking as many AS numbers and path segments as necessary
|
||||
# from the leading part of the AS_PATH attribute, and then
|
||||
# prepending them to the AS4_PATH attribute so that the AS path
|
||||
# information has a number of AS numbers identical to that of
|
||||
# the AS_PATH attribute.
|
||||
else:
|
||||
update_msg.path_attributes.remove(as_path)
|
||||
update_msg.path_attributes.remove(as4_path)
|
||||
as_path = self._construct_as_path_attr(as_path, as4_path)
|
||||
update_msg.path_attributes.append(as_path)
|
||||
|
||||
def _extract_and_handle_bgp4_new_paths(self, update_msg):
|
||||
"""Extracts new paths advertised in the given update message's
|
||||
*MpReachNlri* attribute.
|
||||
|
||||
@ -30,7 +30,7 @@ from ryu.services.protocols.bgp.base import get_validator
|
||||
from ryu.services.protocols.bgp.base import RUNTIME_CONF_ERROR_CODE
|
||||
from ryu.services.protocols.bgp.base import validate
|
||||
from ryu.services.protocols.bgp.utils import validation
|
||||
from ryu.services.protocols.bgp.utils.validation import is_valid_old_asn
|
||||
from ryu.services.protocols.bgp.utils.validation import is_valid_asn
|
||||
|
||||
LOG = logging.getLogger('bgpspeaker.rtconf.base')
|
||||
|
||||
@ -39,6 +39,7 @@ LOG = logging.getLogger('bgpspeaker.rtconf.base')
|
||||
#
|
||||
CAP_REFRESH = 'cap_refresh'
|
||||
CAP_ENHANCED_REFRESH = 'cap_enhanced_refresh'
|
||||
CAP_FOUR_OCTET_AS_NUMBER = 'cap_four_octet_as_number'
|
||||
CAP_MBGP_IPV4 = 'cap_mbgp_ipv4'
|
||||
CAP_MBGP_IPV6 = 'cap_mbgp_ipv6'
|
||||
CAP_MBGP_VPNV4 = 'cap_mbgp_vpnv4'
|
||||
@ -594,6 +595,14 @@ def validate_cap_enhanced_refresh(cer):
|
||||
return cer
|
||||
|
||||
|
||||
@validate(name=CAP_FOUR_OCTET_AS_NUMBER)
|
||||
def validate_cap_four_octet_as_number(cfoan):
|
||||
if cfoan not in (True, False):
|
||||
raise ConfigTypeError(desc='Invalid Four-Octet AS Number capability '
|
||||
'settings: %s boolean value expected' % cfoan)
|
||||
return cfoan
|
||||
|
||||
|
||||
@validate(name=CAP_MBGP_IPV4)
|
||||
def validate_cap_mbgp_ipv4(cmv4):
|
||||
if cmv4 not in (True, False):
|
||||
@ -641,7 +650,7 @@ def validate_cap_rtc(cap_rtc):
|
||||
|
||||
@validate(name=RTC_AS)
|
||||
def validate_cap_rtc_as(rtc_as):
|
||||
if not is_valid_old_asn(rtc_as):
|
||||
if not is_valid_asn(rtc_as):
|
||||
raise ConfigValueError(desc='Invalid RTC AS configuration value: %s'
|
||||
% rtc_as)
|
||||
return rtc_as
|
||||
|
||||
@ -20,7 +20,7 @@ import logging
|
||||
import numbers
|
||||
|
||||
from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4
|
||||
from ryu.services.protocols.bgp.utils.validation import is_valid_old_asn
|
||||
from ryu.services.protocols.bgp.utils.validation import is_valid_asn
|
||||
|
||||
from ryu.services.protocols.bgp import rtconf
|
||||
from ryu.services.protocols.bgp.rtconf.base import BaseConf
|
||||
@ -85,7 +85,7 @@ def validate_local_as(asn):
|
||||
if asn is None:
|
||||
raise MissingRequiredConf(conf_name=LOCAL_AS)
|
||||
|
||||
if not is_valid_old_asn(asn):
|
||||
if not is_valid_asn(asn):
|
||||
raise ConfigValueError(desc='Invalid local_as configuration value: %s'
|
||||
% asn)
|
||||
return asn
|
||||
|
||||
@ -26,9 +26,11 @@ from ryu.lib.packet.bgp import RF_IPv6_UC
|
||||
from ryu.lib.packet.bgp import RF_IPv4_VPN
|
||||
from ryu.lib.packet.bgp import RF_IPv6_VPN
|
||||
from ryu.lib.packet.bgp import RF_RTC_UC
|
||||
from ryu.lib.packet.bgp import BGPOptParamCapabilityFourOctetAsNumber
|
||||
from ryu.lib.packet.bgp import BGPOptParamCapabilityEnhancedRouteRefresh
|
||||
from ryu.lib.packet.bgp import BGPOptParamCapabilityMultiprotocol
|
||||
from ryu.lib.packet.bgp import BGPOptParamCapabilityRouteRefresh
|
||||
from ryu.lib.packet.bgp import BGP_CAP_FOUR_OCTET_AS_NUMBER
|
||||
from ryu.lib.packet.bgp import BGP_CAP_ENHANCED_ROUTE_REFRESH
|
||||
from ryu.lib.packet.bgp import BGP_CAP_MULTIPROTOCOL
|
||||
from ryu.lib.packet.bgp import BGP_CAP_ROUTE_REFRESH
|
||||
@ -38,6 +40,7 @@ from ryu.services.protocols.bgp.rtconf.base import ADVERTISE_PEER_AS
|
||||
from ryu.services.protocols.bgp.rtconf.base import BaseConf
|
||||
from ryu.services.protocols.bgp.rtconf.base import BaseConfListener
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_ENHANCED_REFRESH
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_FOUR_OCTET_AS_NUMBER
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_IPV4
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_IPV6
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV4
|
||||
@ -60,7 +63,7 @@ from ryu.services.protocols.bgp.rtconf.base import SITE_OF_ORIGINS
|
||||
from ryu.services.protocols.bgp.rtconf.base import validate
|
||||
from ryu.services.protocols.bgp.rtconf.base import validate_med
|
||||
from ryu.services.protocols.bgp.rtconf.base import validate_soo_list
|
||||
from ryu.services.protocols.bgp.utils.validation import is_valid_old_asn
|
||||
from ryu.services.protocols.bgp.utils.validation import is_valid_asn
|
||||
from ryu.services.protocols.bgp.info_base.base import Filter
|
||||
from ryu.services.protocols.bgp.info_base.base import PrefixFilter
|
||||
from ryu.services.protocols.bgp.info_base.base import AttributeMap
|
||||
@ -92,6 +95,7 @@ CONNECT_MODE_BOTH = 'both'
|
||||
DEFAULT_CAP_GR_NULL = True
|
||||
DEFAULT_CAP_REFRESH = True
|
||||
DEFAULT_CAP_ENHANCED_REFRESH = False
|
||||
DEFAULT_CAP_FOUR_OCTET_AS_NUMBER = True
|
||||
DEFAULT_CAP_MBGP_IPV4 = True
|
||||
DEFAULT_CAP_MBGP_IPV6 = False
|
||||
DEFAULT_CAP_MBGP_VPNV4 = False
|
||||
@ -181,7 +185,7 @@ def validate_local_port(port):
|
||||
|
||||
@validate(name=REMOTE_AS)
|
||||
def validate_remote_as(asn):
|
||||
if not is_valid_old_asn(asn):
|
||||
if not is_valid_asn(asn):
|
||||
raise ConfigValueError(desc='Invalid remote as value %s' % asn)
|
||||
return asn
|
||||
|
||||
@ -295,6 +299,7 @@ class NeighborConf(ConfWithId, ConfWithStats):
|
||||
REQUIRED_SETTINGS = frozenset([REMOTE_AS, IP_ADDRESS])
|
||||
OPTIONAL_SETTINGS = frozenset([CAP_REFRESH,
|
||||
CAP_ENHANCED_REFRESH,
|
||||
CAP_FOUR_OCTET_AS_NUMBER,
|
||||
CAP_MBGP_IPV4, CAP_MBGP_IPV6,
|
||||
CAP_MBGP_VPNV4, CAP_MBGP_VPNV6,
|
||||
CAP_RTC, RTC_AS, HOLD_TIME,
|
||||
@ -314,6 +319,9 @@ class NeighborConf(ConfWithId, ConfWithStats):
|
||||
CAP_REFRESH, DEFAULT_CAP_REFRESH, **kwargs)
|
||||
self._settings[CAP_ENHANCED_REFRESH] = compute_optional_conf(
|
||||
CAP_ENHANCED_REFRESH, DEFAULT_CAP_ENHANCED_REFRESH, **kwargs)
|
||||
self._settings[CAP_FOUR_OCTET_AS_NUMBER] = compute_optional_conf(
|
||||
CAP_FOUR_OCTET_AS_NUMBER,
|
||||
DEFAULT_CAP_FOUR_OCTET_AS_NUMBER, **kwargs)
|
||||
self._settings[CAP_MBGP_IPV4] = compute_optional_conf(
|
||||
CAP_MBGP_IPV4, DEFAULT_CAP_MBGP_IPV4, **kwargs)
|
||||
self._settings[CAP_MBGP_IPV6] = compute_optional_conf(
|
||||
@ -457,6 +465,17 @@ class NeighborConf(ConfWithId, ConfWithStats):
|
||||
def cap_enhanced_refresh(self):
|
||||
return self._settings[CAP_ENHANCED_REFRESH]
|
||||
|
||||
@property
|
||||
def cap_four_octet_as_number(self):
|
||||
return self._settings[CAP_FOUR_OCTET_AS_NUMBER]
|
||||
|
||||
@cap_four_octet_as_number.setter
|
||||
def cap_four_octet_as_number(self, cap):
|
||||
kwargs = {CAP_FOUR_OCTET_AS_NUMBER: cap}
|
||||
self._settings[CAP_FOUR_OCTET_AS_NUMBER] = compute_optional_conf(
|
||||
CAP_FOUR_OCTET_AS_NUMBER,
|
||||
DEFAULT_CAP_FOUR_OCTET_AS_NUMBER, **kwargs)
|
||||
|
||||
@property
|
||||
def cap_mbgp_ipv4(self):
|
||||
return self._settings[CAP_MBGP_IPV4]
|
||||
@ -599,6 +618,10 @@ class NeighborConf(ConfWithId, ConfWithStats):
|
||||
capabilities[BGP_CAP_ENHANCED_ROUTE_REFRESH] = [
|
||||
BGPOptParamCapabilityEnhancedRouteRefresh()]
|
||||
|
||||
if self.cap_four_octet_as_number:
|
||||
capabilities[BGP_CAP_FOUR_OCTET_AS_NUMBER] = [
|
||||
BGPOptParamCapabilityFourOctetAsNumber(self.local_as)]
|
||||
|
||||
return capabilities
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
@ -24,6 +24,7 @@ from socket import IPPROTO_TCP, TCP_NODELAY
|
||||
from eventlet import semaphore
|
||||
|
||||
from ryu.lib.packet import bgp
|
||||
from ryu.lib.packet.bgp import AS_TRANS
|
||||
from ryu.lib.packet.bgp import BGPMessage
|
||||
from ryu.lib.packet.bgp import BGPOpen
|
||||
from ryu.lib.packet.bgp import BGPUpdate
|
||||
@ -34,6 +35,7 @@ from ryu.lib.packet.bgp import BGP_MSG_UPDATE
|
||||
from ryu.lib.packet.bgp import BGP_MSG_KEEPALIVE
|
||||
from ryu.lib.packet.bgp import BGP_MSG_NOTIFICATION
|
||||
from ryu.lib.packet.bgp import BGP_MSG_ROUTE_REFRESH
|
||||
from ryu.lib.packet.bgp import BGP_CAP_FOUR_OCTET_AS_NUMBER
|
||||
from ryu.lib.packet.bgp import BGP_CAP_ENHANCED_ROUTE_REFRESH
|
||||
from ryu.lib.packet.bgp import BGP_CAP_MULTIPROTOCOL
|
||||
from ryu.lib.packet.bgp import BGP_ERROR_HOLD_TIMER_EXPIRED
|
||||
@ -49,7 +51,6 @@ from ryu.services.protocols.bgp.constants import BGP_FSM_OPEN_CONFIRM
|
||||
from ryu.services.protocols.bgp.constants import BGP_FSM_OPEN_SENT
|
||||
from ryu.services.protocols.bgp.constants import BGP_VERSION_NUM
|
||||
from ryu.services.protocols.bgp.protocol import Protocol
|
||||
from ryu.services.protocols.bgp.utils.validation import is_valid_old_asn
|
||||
|
||||
LOG = logging.getLogger('bgpspeaker.speaker')
|
||||
|
||||
@ -99,7 +100,7 @@ class BgpProtocol(Protocol, Activity):
|
||||
self._remotename,
|
||||
self._localname))
|
||||
Activity.__init__(self, name=activity_name)
|
||||
# Intialize instance variables.
|
||||
# Initialize instance variables.
|
||||
self._peer = None
|
||||
self._recv_buff = b''
|
||||
self._socket = socket
|
||||
@ -120,6 +121,7 @@ class BgpProtocol(Protocol, Activity):
|
||||
self.sent_open_msg = None
|
||||
self.recv_open_msg = None
|
||||
self._is_bound = False
|
||||
self.cap_four_octet_as_number = False
|
||||
|
||||
@property
|
||||
def is_reactive(self):
|
||||
@ -247,12 +249,18 @@ class BgpProtocol(Protocol, Activity):
|
||||
return afs
|
||||
|
||||
def is_mbgp_cap_valid(self, route_family):
|
||||
"""Returns true if both sides of this protocol have advertise
|
||||
"""Returns True if both sides of this protocol have advertise
|
||||
capability for this address family.
|
||||
"""
|
||||
return (self.is_route_family_adv(route_family) and
|
||||
self.is_route_family_adv_recv(route_family))
|
||||
|
||||
def is_four_octet_as_number_cap_valid(self):
|
||||
"""Returns True if both sides of this protocol have Four-Octet
|
||||
AS number capability."""
|
||||
return (self.cap_four_octet_as_number and
|
||||
self._peer.cap_four_octet_as_number)
|
||||
|
||||
def _run(self, peer):
|
||||
"""Sends open message to peer and handles received messages.
|
||||
|
||||
@ -405,11 +413,26 @@ class BgpProtocol(Protocol, Activity):
|
||||
either one of them we have to end session.
|
||||
"""
|
||||
assert open_msg.type == BGP_MSG_OPEN
|
||||
# Validate remote ASN.
|
||||
remote_asnum = open_msg.my_as
|
||||
# Since 4byte AS is not yet supported, we validate AS as old style AS.
|
||||
if (not is_valid_old_asn(remote_asnum) or
|
||||
remote_asnum != self._peer.remote_as):
|
||||
|
||||
opt_param_cap_map = open_msg.opt_param_cap_map
|
||||
|
||||
# Validate remote AS number.
|
||||
remote_as = open_msg.my_as
|
||||
# Try to get AS number from Four-Octet AS number capability.
|
||||
cap4as = opt_param_cap_map.get(BGP_CAP_FOUR_OCTET_AS_NUMBER, None)
|
||||
if cap4as is None:
|
||||
if remote_as == AS_TRANS:
|
||||
# Raise Bad Peer AS error message, if my_as is AS_TRANS
|
||||
# and without Four-Octet AS number capability.
|
||||
raise bgp.BadPeerAs()
|
||||
self.cap_four_octet_as_number = False
|
||||
else:
|
||||
# Note: Even if the peer has Four-Octet AS number capability,
|
||||
# keep the local capability setting
|
||||
remote_as = cap4as.as_number
|
||||
self.cap_four_octet_as_number = True
|
||||
# Validate remote AS number with local setting.
|
||||
if remote_as != self._peer.remote_as:
|
||||
raise bgp.BadPeerAs()
|
||||
|
||||
# Validate bgp version number.
|
||||
@ -426,7 +449,7 @@ class BgpProtocol(Protocol, Activity):
|
||||
LOG.debug('Received msg from %s << %s', self._remotename, msg)
|
||||
|
||||
# If we receive open message we try to bind to protocol
|
||||
if (msg.type == BGP_MSG_OPEN):
|
||||
if msg.type == BGP_MSG_OPEN:
|
||||
if self.state == BGP_FSM_OPEN_SENT:
|
||||
# Validate open message.
|
||||
self._validate_open_msg(msg)
|
||||
|
||||
@ -19,6 +19,8 @@
|
||||
import numbers
|
||||
import socket
|
||||
|
||||
import six
|
||||
|
||||
|
||||
def is_valid_ipv4(ipv4):
|
||||
"""Returns True if given is a valid ipv4 address.
|
||||
@ -115,17 +117,19 @@ def is_valid_ipv6_prefix(ipv6_prefix):
|
||||
|
||||
|
||||
def is_valid_old_asn(asn):
|
||||
"""Returns true if given asn is a 16 bit number.
|
||||
"""Returns True if the given AS number is Two Octet."""
|
||||
if isinstance(asn, six.integer_types) and 0 <= asn <= 0xffff:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
Old AS numbers are 16 but unsigned number.
|
||||
"""
|
||||
valid = True
|
||||
# AS number should be a 16 bit number
|
||||
if (not isinstance(asn, numbers.Integral) or (asn < 0) or
|
||||
(asn > ((2 ** 16) - 1))):
|
||||
valid = False
|
||||
|
||||
return valid
|
||||
def is_valid_asn(asn):
|
||||
"""Returns True if the given AS number is Two or Four Octet."""
|
||||
if isinstance(asn, six.integer_types) and 0 <= asn <= 0xffffffff:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def is_valid_vpnv4_prefix(prefix):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user