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:
IWASE Yusuke 2016-07-06 15:12:23 +09:00 committed by FUJITA Tomonori
parent 2039347560
commit 55d955f484
8 changed files with 380 additions and 69 deletions

View File

@ -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):

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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)

View File

@ -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):