summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorIWASE Yusuke <iwase.yusuke0@gmail.com>2016-07-06 15:12:23 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2016-07-11 16:45:43 +0900
commit55d955f4841189d2ce683988d67cab3d5836fe5c (patch)
tree0b62a10c49ae6314fd8ee3ce9a34a95f83996e0b
parent2039347560967f175d54e53db4d1146cc42acbe6 (diff)
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>
-rw-r--r--ryu/lib/packet/bgp.py41
-rw-r--r--ryu/services/protocols/bgp/bgpspeaker.py31
-rw-r--r--ryu/services/protocols/bgp/peer.py270
-rw-r--r--ryu/services/protocols/bgp/rtconf/base.py13
-rw-r--r--ryu/services/protocols/bgp/rtconf/common.py4
-rw-r--r--ryu/services/protocols/bgp/rtconf/neighbors.py27
-rw-r--r--ryu/services/protocols/bgp/speaker.py41
-rw-r--r--ryu/services/protocols/bgp/utils/validation.py22
8 files changed, 380 insertions, 69 deletions
diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py
index 937fdeb5..68105a89 100644
--- a/ryu/lib/packet/bgp.py
+++ b/ryu/lib/packet/bgp.py
@@ -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):
diff --git a/ryu/services/protocols/bgp/bgpspeaker.py b/ryu/services/protocols/bgp/bgpspeaker.py
index ce3eaedf..2ce6372a 100644
--- a/ryu/services/protocols/bgp/bgpspeaker.py
+++ b/ryu/services/protocols/bgp/bgpspeaker.py
@@ -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
diff --git a/ryu/services/protocols/bgp/peer.py b/ryu/services/protocols/bgp/peer.py
index 50c280b3..89b7bd47 100644
--- a/ryu/services/protocols/bgp/peer.py
+++ b/ryu/services/protocols/bgp/peer.py
@@ -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
@@ -395,6 +402,10 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
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.
diff --git a/ryu/services/protocols/bgp/rtconf/base.py b/ryu/services/protocols/bgp/rtconf/base.py
index c61798dc..8746b2dd 100644
--- a/ryu/services/protocols/bgp/rtconf/base.py
+++ b/ryu/services/protocols/bgp/rtconf/base.py
@@ -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
diff --git a/ryu/services/protocols/bgp/rtconf/common.py b/ryu/services/protocols/bgp/rtconf/common.py
index d285bb6d..acf4634f 100644
--- a/ryu/services/protocols/bgp/rtconf/common.py
+++ b/ryu/services/protocols/bgp/rtconf/common.py
@@ -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
diff --git a/ryu/services/protocols/bgp/rtconf/neighbors.py b/ryu/services/protocols/bgp/rtconf/neighbors.py
index a12af816..bc27542d 100644
--- a/ryu/services/protocols/bgp/rtconf/neighbors.py
+++ b/ryu/services/protocols/bgp/rtconf/neighbors.py
@@ -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(
@@ -458,6 +466,17 @@ class NeighborConf(ConfWithId, ConfWithStats):
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):
diff --git a/ryu/services/protocols/bgp/speaker.py b/ryu/services/protocols/bgp/speaker.py
index 31553b27..9c185daa 100644
--- a/ryu/services/protocols/bgp/speaker.py
+++ b/ryu/services/protocols/bgp/speaker.py
@@ -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)
diff --git a/ryu/services/protocols/bgp/utils/validation.py b/ryu/services/protocols/bgp/utils/validation.py
index 659ea248..f0fb6e57 100644
--- a/ryu/services/protocols/bgp/utils/validation.py
+++ b/ryu/services/protocols/bgp/utils/validation.py
@@ -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):