summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>2014-04-16 04:14:12 +0000
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2014-04-17 08:38:43 +0900
commit660c8e68b476757a83ff3c413e76621592fb8c4f (patch)
treec20676d4600f62e54339f9c24c0e2c1a23225aef
parent9d5e66fa4e49ce66103dbc9640a33bd9c22fab3f (diff)
bgp: remove original bgp packet library
Signed-off-by: ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r--ryu/services/protocols/bgp/protocols/bgp/__init__.py7
-rw-r--r--ryu/services/protocols/bgp/protocols/bgp/capabilities.py280
-rw-r--r--ryu/services/protocols/bgp/protocols/bgp/exceptions.py349
-rw-r--r--ryu/services/protocols/bgp/protocols/bgp/messages.py536
-rw-r--r--ryu/services/protocols/bgp/protocols/bgp/nlri.py841
-rw-r--r--ryu/services/protocols/bgp/protocols/bgp/pathattr.py1076
6 files changed, 0 insertions, 3089 deletions
diff --git a/ryu/services/protocols/bgp/protocols/bgp/__init__.py b/ryu/services/protocols/bgp/protocols/bgp/__init__.py
deleted file mode 100644
index 99c53380..00000000
--- a/ryu/services/protocols/bgp/protocols/bgp/__init__.py
+++ /dev/null
@@ -1,7 +0,0 @@
-try:
- from collections import OrderedDict
-except ImportError:
- from ordereddict import OrderedDict
-
-# Pointer to active/available OrderedDict.
-OrderedDict = OrderedDict
diff --git a/ryu/services/protocols/bgp/protocols/bgp/capabilities.py b/ryu/services/protocols/bgp/protocols/bgp/capabilities.py
deleted file mode 100644
index ed2acaa9..00000000
--- a/ryu/services/protocols/bgp/protocols/bgp/capabilities.py
+++ /dev/null
@@ -1,280 +0,0 @@
-# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-This module provides BGP protocol capabilities classes and utility methods to
-encode and decode them.
-"""
-
-from abc import ABCMeta
-from abc import abstractmethod
-import logging
-import struct
-
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import \
- MalformedOptionalParam
-from ryu.services.protocols.bgp.protocols.bgp.nlri import get_rf
-from ryu.services.protocols.bgp.protocols.bgp.nlri import \
- RouteFamily as route_fmly
-
-
-# Logger instance for this module
-LOG = logging.getLogger('bgpspeaker.bgp.proto.capabilities')
-
-# Registry for bgp capability class by their code.
-# <Key>: <Value> - <capability-code>: <capability-class>
-_BGP_CAPABILITY_REGISTRY = {}
-
-
-def _register_bgp_capabilities(cls):
- """Utility decorator used to register bgp supported/recognized
- capabilities.
-
- Capabilities classes are registered by their capability-code.
- """
- assert issubclass(cls, Capability)
- assert hasattr(cls, 'CODE')
- assert _BGP_CAPABILITY_REGISTRY.get(cls.CODE) is None
- _BGP_CAPABILITY_REGISTRY[cls.CODE] = cls
- return cls
-
-
-def is_recognized_cap_codes(cap_code):
- return cap_code in _BGP_CAPABILITY_REGISTRY
-
-
-def decode(byte_value):
- """Decodes given `byte_value` into appropriate capabilities.
-
- Parameter:
- - `byte_value`: (str) byte representation of one capability
- advertisement
- Returns:
- - list of capabilities decoded from given bytes
- Note: Different routers pack capability in one capability
- advertisement/optional parameter or group them into several capability
- advertisements. Hence we return a list of one or more decoded
- capabilities.
- """
- idx = 0
- total_len = len(byte_value)
- caps = []
- # Parse one of more capabilities packed inside given capability-
- # advertisement payload
- while idx < total_len:
- cap_code, clen = struct.unpack_from('BB', byte_value, idx)
- idx += 2
- cap = byte_value[idx:idx + clen]
- idx += clen
-
- cap_cls = _BGP_CAPABILITY_REGISTRY.get(cap_code)
- if cap_cls:
- cap = cap_cls.from_bytes(cap)
- caps.append(cap)
- else:
- # RFC 5492 says: If a BGP speaker receives from its peer a
- # capability that it does not itself support or recognize, it MUST
- # ignore that capability. In particular, the Unsupported
- # Capability NOTIFICATION message MUST NOT be generated and the BGP
- # session MUST NOT be terminated in response to reception of a
- # capability that is not supported by the local speaker.
- cap = UnSupportedCap(cap_code, cap)
-
- return caps
-
-
-class Capability(object):
- """Super class of all bgp capability optional parameters.
- """
- __metaclass__ = ABCMeta
- CODE = -1
- NAME = 'abstract-cap'
-
- @abstractmethod
- def packvalue(self):
- """Encode this bgp capability."""
- raise NotImplementedError()
-
- def encode(self):
- """Encodes this bgp capability with header and body."""
- body = self.packvalue()
- return struct.pack('BB', self.__class__.CODE, len(body)) + body
-
- def __repr__(self):
- return '<%s>' % self.__class__.NAME
-
-
-class UnSupportedCap(Capability):
- """Represents unknown capability.
-
- According to RFC 5492 it is recommended to that we do not sent NOTIFICATION
- message for "Unsupported Capability".
- """
- NAME = 'unsupported-cap'
-
- def __init__(self, code, value):
- self.CODE = code
- self._value = value
-
- def packvalue(self):
- return self._value
-
- def __repr__(self):
- return '<UnSupportedCap(code=%s)>' % self.CODE
-
-
-@_register_bgp_capabilities
-class MultiprotocolExtentionCap(Capability):
- """This class represents bgp multi-protocol extension capability.
- """
- CODE = 1
- NAME = 'mbgp'
-
- def __init__(self, route_family):
- if not route_fmly.is_valid(route_family):
- raise ValueError('Invalid argument %s' % route_family)
-
- Capability.__init__(self)
- self.route_family = route_family
-
- def packvalue(self):
- return struct.pack('!HH', self.route_family.afi,
- self.route_family.safi)
-
- @classmethod
- def from_bytes(cls, value):
- afi, _, safi = struct.unpack_from('!HBB', value)
- return cls(get_rf(afi, safi))
-
- def __repr__(self):
- return ('<MultiprotocolExtenstionCap(af=%s, saf=%s)>' %
- (self.route_family.afi, self.route_family.safi))
-
- def __eq__(self, other):
- if (other.__class__.CODE == self.__class__.CODE and
- other.route_family.afi == self.route_family.afi and
- other.route_family.safi == self.route_family.safi):
- return True
- return False
-
-
-class ZeroLengthCap(Capability):
- """This is a super class represent all bgp capability with zero length."""
- CODE = -1
- NAME = 'zero-length'
-
- def packvalue(self):
- return ''
-
- @classmethod
- def from_bytes(cls, value):
- if len(value) > 0:
- LOG.error('Zero length capability has non-zero length value!')
- raise MalformedOptionalParam()
- return cls.get_singleton()
-
- @staticmethod
- def get_singleton():
- raise NotImplementedError()
-
-
-@_register_bgp_capabilities
-class RouteRefreshCap(ZeroLengthCap):
- CODE = 2
- NAME = 'route-refresh'
-
- def __str__(self):
- return RouteRefreshCap.NAME
-
- @staticmethod
- def get_singleton():
- return _ROUTE_REFRESH_CAP
-
-
-@_register_bgp_capabilities
-class OldRouteRefreshCap(ZeroLengthCap):
- CODE = 128
- NAME = 'old-route-refresh'
-
- def __str__(self):
- return OldRouteRefreshCap.NAME
-
- @staticmethod
- def get_singleton():
- return _OLD_ROUTE_REFRESH_CAP
-
-
-# Since four byte as capability is not fully supported, we do not register it
-# as supported/recognized capability.
-@_register_bgp_capabilities
-class GracefulRestartCap(Capability):
- CODE = 64
- NAME = 'graceful-restart'
-
- def __init__(self, value):
- # TODO(PH): Provide implementation
- Capability.__init__(self)
- self.value = value
-
- def packvalue(self):
- # TODO(PH): Provide implementation
- return self.value
-
- @classmethod
- def from_bytes(cls, value):
- return cls(value)
-
-
-# Since four byte as capability is not fully supported, we do not register it
-# as supported/recognized capability.
-@_register_bgp_capabilities
-class FourByteAsCap(Capability):
- CODE = 65
- NAME = '4byteas'
-
- def __init__(self, four_byte_as):
- Capability.__init__(self)
- self.four_byte_as = four_byte_as
-
- def packvalue(self):
- return struct.pack('!I', self.four_byte_as)
-
- @classmethod
- def from_bytes(cls, value):
- value, = struct.unpack('!I', value)
- return cls(value)
-
- def __repr__(self):
- return '<FourByteAsCap(%s)>' % self.four_byte_as
-
- def __eq__(self, other):
- if (other and other.four_byte_as == self.four_byte_as):
- return True
- return False
-
-
-@_register_bgp_capabilities
-class EnhancedRouteRefreshCap(ZeroLengthCap):
- CODE = 70
- NAME = 'enhanced-refresh'
-
- @staticmethod
- def get_singleton():
- return _ENHANCED_ROUTE_REFRESH_CAP
-
-# Zero length capability singletons
-_ROUTE_REFRESH_CAP = RouteRefreshCap()
-_ENHANCED_ROUTE_REFRESH_CAP = EnhancedRouteRefreshCap()
-_OLD_ROUTE_REFRESH_CAP = OldRouteRefreshCap()
diff --git a/ryu/services/protocols/bgp/protocols/bgp/exceptions.py b/ryu/services/protocols/bgp/protocols/bgp/exceptions.py
deleted file mode 100644
index dba5ed6e..00000000
--- a/ryu/services/protocols/bgp/protocols/bgp/exceptions.py
+++ /dev/null
@@ -1,349 +0,0 @@
-import struct
-
-
-class BgpExc(Exception):
- """Base bgp exception."""
-
- CODE = 0
- """BGP error code."""
-
- SUB_CODE = 0
- """BGP error sub-code."""
-
- SEND_ERROR = True
- """Flag if set indicates Notification message should be sent to peer."""
-
- def __init__(self, data=''):
- self.data = data
-
- def __str__(self):
- return '<%s %r>' % (self.__class__.__name__, self.data)
-
-
-class BadNotification(BgpExc):
- SEND_ERROR = False
-
-#=============================================================================
-# Message Header Errors
-#=============================================================================
-
-
-class NotSync(BgpExc):
- CODE = 1
- SUB_CODE = 1
-
-
-class BadLen(BgpExc):
- CODE = 1
- SUB_CODE = 2
-
- def __init__(self, msg_type_code, message_length):
- self.msg_type_code = msg_type_code
- self.length = message_length
- self.data = struct.pack('!H', self.length)
-
- def __str__(self):
- return '<BadLen %d msgtype=%d>' % (self.length, self.msg_type_code)
-
-
-class BadMsg(BgpExc):
- """Error to indicate un-recognized message type.
-
- RFC says: If the Type field of the message header is not recognized, then
- the Error Subcode MUST be set to Bad Message Type. The Data field MUST
- contain the erroneous Type field.
- """
- CODE = 1
- SUB_CODE = 3
-
- def __init__(self, msg_type):
- self.msg_type = msg_type
- self.data = struct.pack('B', msg_type)
-
- def __str__(self):
- return '<BadMsg %d>' % (self.msg,)
-
-#=============================================================================
-# OPEN Message Errors
-#=============================================================================
-
-
-class MalformedOptionalParam(BgpExc):
- """If recognized optional parameters are malformed.
-
- RFC says: If one of the Optional Parameters in the OPEN message is
- recognized, but is malformed, then the Error Subcode MUST be set to 0
- (Unspecific).
- """
- CODE = 2
- SUB_CODE = 0
-
-
-class UnsupportedVersion(BgpExc):
- """Error to indicate unsupport bgp version number.
-
- RFC says: If the version number in the Version field of the received OPEN
- message is not supported, then the Error Subcode MUST be set to Unsupported
- Version Number. The Data field is a 2-octet unsigned integer, which
- indicates the largest, locally-supported version number less than the
- version the remote BGP peer bid (as indicated in the received OPEN
- message), or if the smallest, locally-supported version number is greater
- than the version the remote BGP peer bid, then the smallest, locally-
- supported version number.
- """
- CODE = 2
- SUB_CODE = 1
-
- def __init__(self, locally_support_version):
- self.data = struct.pack('H', locally_support_version)
-
-
-class BadPeerAs(BgpExc):
- """Error to indicate open message has incorrect AS number.
-
- RFC says: If the Autonomous System field of the OPEN message is
- unacceptable, then the Error Subcode MUST be set to Bad Peer AS. The
- determination of acceptable Autonomous System numbers is configure peer AS.
- """
- CODE = 2
- SUB_CODE = 2
-
-
-class BadBgpId(BgpExc):
- """Error to indicate incorrect BGP Identifier.
-
- RFC says: If the BGP Identifier field of the OPEN message is syntactically
- incorrect, then the Error Subcode MUST be set to Bad BGP Identifier.
- Syntactic correctness means that the BGP Identifier field represents a
- valid unicast IP host address.
- """
- CODE = 2
- SUB_CODE = 3
-
-
-class UnsupportedOptParam(BgpExc):
- """Error to indicate unsupported optional parameters.
-
- RFC says: If one of the Optional Parameters in the OPEN message is not
- recognized, then the Error Subcode MUST be set to Unsupported Optional
- Parameters.
- """
- CODE = 2
- SUB_CODE = 4
-
-
-class AuthFailure(BgpExc):
- CODE = 2
- SUB_CODE = 5
-
-
-class UnacceptableHoldTime(BgpExc):
- """Error to indicate Unacceptable Hold Time in open message.
-
- RFC says: If the Hold Time field of the OPEN message is unacceptable, then
- the Error Subcode MUST be set to Unacceptable Hold Time.
- """
- CODE = 2
- SUB_CODE = 6
-
-#=============================================================================
-# UPDATE message related errors
-#=============================================================================
-
-
-class MalformedAttrList(BgpExc):
- """Error to indicate UPDATE message is malformed.
-
- RFC says: Error checking of an UPDATE message begins by examining the path
- attributes. If the Withdrawn Routes Length or Total Attribute Length is
- too large (i.e., if Withdrawn Routes Length + Total Attribute Length + 23
- exceeds the message Length), then the Error Subcode MUST be set to
- Malformed Attribute List.
- """
- CODE = 3
- SUB_CODE = 1
-
-
-class UnRegWellKnowAttr(BgpExc):
- CODE = 3
- SUB_CODE = 2
-
-
-class MissingWellKnown(BgpExc):
- """Error to indicate missing well-known attribute.
-
- RFC says: If any of the well-known mandatory attributes are not present,
- then the Error Subcode MUST be set to Missing Well-known Attribute. The
- Data field MUST contain the Attribute Type Code of the missing, well-known
- attribute.
- """
- CODE = 3
- SUB_CODE = 3
-
- def __init__(self, pattr_type_code):
- self.pattr_type_code = pattr_type_code
- self.data = struct.pack('B', pattr_type_code)
-
-
-class AttrFlagError(BgpExc):
- """Error to indicate recognized path attributes have incorrect flags.
-
- RFC says: If any recognized attribute has Attribute Flags that conflict
- with the Attribute Type Code, then the Error Subcode MUST be set to
- Attribute Flags Error. The Data field MUST contain the erroneous attribute
- (type, length, and value).
- """
- CODE = 3
- SUB_CODE = 4
-
-
-class AttrLenError(BgpExc):
- CODE = 3
- SUB_CODE = 5
-
-
-class InvalidOriginError(BgpExc):
- """Error indicates undefined Origin attribute value.
-
- RFC says: If the ORIGIN attribute has an undefined value, then the Error
- Sub- code MUST be set to Invalid Origin Attribute. The Data field MUST
- contain the unrecognized attribute (type, length, and value).
- """
- CODE = 3
- SUB_CODE = 6
-
-
-class RoutingLoop(BgpExc):
- CODE = 3
- SUB_CODE = 7
-
-
-class InvalidNextHop(BgpExc):
- CODE = 3
- SUB_CODE = 8
-
-
-class OptAttrError(BgpExc):
- """Error indicates Optional Attribute is malformed.
-
- RFC says: If an optional attribute is recognized, then the value of this
- attribute MUST be checked. If an error is detected, the attribute MUST be
- discarded, and the Error Subcode MUST be set to Optional Attribute Error.
- The Data field MUST contain the attribute (type, length, and value).
- """
- CODE = 3
- SUB_CODE = 9
-
-
-class InvalidNetworkField(BgpExc):
- CODE = 3
- SUB_CODE = 10
-
-
-class MalformedAsPath(BgpExc):
- """Error to indicate if AP_PATH attribute is syntactically incorrect.
-
- RFC says: The AS_PATH attribute is checked for syntactic correctness. If
- the path is syntactically incorrect, then the Error Subcode MUST be set to
- Malformed AS_PATH.
- """
- CODE = 3
- SUB_CODE = 11
-
-
-#=============================================================================
-# Hold Timer Expired
-#=============================================================================
-
-
-class HoldTimerExpired(BgpExc):
- """Error to indicate Hold Timer expired.
-
- RFC says: If a system does not receive successive KEEPALIVE, UPDATE, and/or
- NOTIFICATION messages within the period specified in the Hold Time field of
- the OPEN message, then the NOTIFICATION message with the Hold Timer Expired
- Error Code is sent and the BGP connection is closed.
- """
- CODE = 4
- SUB_CODE = 1
-
-#=============================================================================
-# Finite State Machine Error
-#=============================================================================
-
-
-class FiniteStateMachineError(BgpExc):
- """Error to indicate any Finite State Machine Error.
-
- RFC says: Any error detected by the BGP Finite State Machine (e.g., receipt
- of an unexpected event) is indicated by sending the NOTIFICATION message
- with the Error Code Finite State Machine Error.
- """
- CODE = 5
- SUB_CODE = 1
-
-
-#=============================================================================
-# Cease Errors
-#=============================================================================
-
-class MaxPrefixReached(BgpExc):
- CODE = 6
- SUB_CODE = 1
-
-
-class AdminShutdown(BgpExc):
- """Error to indicate Administrative shutdown.
-
- RFC says: If a BGP speaker decides to administratively shut down its
- peering with a neighbor, then the speaker SHOULD send a NOTIFICATION
- message with the Error Code Cease and the Error Subcode 'Administrative
- Shutdown'.
- """
- CODE = 6
- SUB_CODE = 2
-
-
-class PeerDeConfig(BgpExc):
- CODE = 6
- SUB_CODE = 3
-
-
-class AdminReset(BgpExc):
- CODE = 6
- SUB_CODE = 4
-
-
-class ConnRejected(BgpExc):
- """Error to indicate Connection Rejected.
-
- RFC says: If a BGP speaker decides to disallow a BGP connection (e.g., the
- peer is not configured locally) after the speaker accepts a transport
- protocol connection, then the BGP speaker SHOULD send a NOTIFICATION
- message with the Error Code Cease and the Error Subcode "Connection
- Rejected".
- """
- CODE = 6
- SUB_CODE = 5
-
-
-class OtherConfChange(BgpExc):
- CODE = 6
- SUB_CODE = 6
-
-
-class CollisionResolution(BgpExc):
- """Error to indicate Connection Collision Resolution.
-
- RFC says: If a BGP speaker decides to send a NOTIFICATION message with the
- Error Code Cease as a result of the collision resolution procedure (as
- described in [BGP-4]), then the subcode SHOULD be set to "Connection
- Collision Resolution".
- """
- CODE = 6
- SUB_CODE = 7
-
-
-class OutOfResource(BgpExc):
- CODE = 6
- SUB_CODE = 8
diff --git a/ryu/services/protocols/bgp/protocols/bgp/messages.py b/ryu/services/protocols/bgp/protocols/bgp/messages.py
deleted file mode 100644
index 6d74b1c6..00000000
--- a/ryu/services/protocols/bgp/protocols/bgp/messages.py
+++ /dev/null
@@ -1,536 +0,0 @@
-# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-This module provides BGP protocol message classes and utility methods to encode
-and decode them.
-
-This file is adapted from pybgp open source project.
-"""
-from abc import ABCMeta
-from abc import abstractmethod
-from copy import copy
-import cStringIO
-import logging
-import socket
-import struct
-
-from ryu.services.protocols.bgp.protocols.bgp import capabilities
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import BadBgpId
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import BadLen
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import BadMsg
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import BadNotification
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import \
- MalformedAttrList
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import \
- UnacceptableHoldTime
-from ryu.services.protocols.bgp.protocols.bgp import nlri
-from ryu.services.protocols.bgp.protocols.bgp.nlri import get_rf
-from ryu.services.protocols.bgp.protocols.bgp import OrderedDict
-from ryu.services.protocols.bgp.protocols.bgp import pathattr
-from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4
-from ryu.services.protocols.bgp.utils.validation import is_valid_old_asn
-
-
-LOG = logging.getLogger('bgpspeaker.bgp.proto.messages')
-
-# BGP capability optional parameter type
-CAP_OPT_PARA_TYPE = 2
-
-# Registry for bgp message class by their type code.
-# <key>: <value> - <type-code>: <msg class>
-_BGP_MESSAGE_REGISTRY = {}
-
-
-def _register_bgp_message(cls):
- """Used as class decorator for registering bgp message class by their
- type-code.
- """
- assert _BGP_MESSAGE_REGISTRY.get(cls.TYPE_CODE) is None
- assert hasattr(cls, 'from_bytes')
- _BGP_MESSAGE_REGISTRY[cls.TYPE_CODE] = cls
- return cls
-
-
-class BgpMessage(object):
- """Super class of all bgp messages.
- """
- __metaclass__ = ABCMeta
- TYPE_CODE = 0
- MSG_NAME = 'abstract-msg'
- HEADER_SIZE = 19
-
- @abstractmethod
- def packvalue(self):
- """Encodes the body of this bgp message."""
- raise NotImplementedError()
-
- def encode(self):
- """Encodes this bgp message with header and body."""
- body = self.packvalue()
- return struct.pack('!16sHB', '\xff' * 16, 19 + len(body),
- self.__class__.TYPE_CODE) + body
-
-
-class RecognizedBgpMessage(BgpMessage):
- """Represents recognized/supported bgp message.
-
- Declares a factory method to create an instance from bytes.
- """
- @classmethod
- def from_bytes(cls, recv_bytes, total_msg_length):
- raise NotImplementedError()
-
-
-@_register_bgp_message
-class Open(RecognizedBgpMessage):
- """Represents bgp OPEN message.
-
- This is the first message sent by each peer after TCP connection is
- established.
- """
- MSG_NAME = 'open'
- TYPE_CODE = 1
- MIN_LENGTH = 29
-
- def __init__(self, version, asnum, holdtime, bgpid, caps,
- unrec_params=None):
- # Validate arguments.
- if version < 1:
- raise ValueError('Invalid version number %s' % version)
- if not is_valid_old_asn(asnum):
- raise ValueError('Invalid AS number %s' % asnum)
- if holdtime <= 2:
- raise ValueError('Holdtime has to be greater than 2 sec.')
- if not caps:
- raise ValueError('Invalid capabilities.')
- if not is_valid_ipv4(bgpid):
- raise ValueError('Invalid bgp ID, should be valid IPv4, '
- 'but given %s' % bgpid)
-
- BgpMessage.__init__(self)
- self._version = version
- self._holdtime = holdtime
- self._asnum = asnum
- self._bgpid = bgpid
- self._caps = caps
- self._unrec_params = unrec_params
- if not unrec_params:
- self._unrec_params = OrderedDict()
-
- @property
- def version(self):
- return self._version
-
- @property
- def holdtime(self):
- return self._holdtime
-
- @property
- def asnum(self):
- return self._asnum
-
- @property
- def bgpid(self):
- return self._bgpid
-
- @property
- def caps(self):
- return copy(self._caps)
-
- @property
- def unrec_params(self):
- return copy(self._unrec_params)
-
- @classmethod
- def from_bytes(cls, recv_bytes, total_msg_len):
- # Validate OPEN message length.
- if len(recv_bytes) < 10:
- raise BadLen(Open.TYPE_CODE, len(recv_bytes) + cls.HEADER_SIZE)
-
- version, asnum, holdtime, bgpid, paramlen = \
- struct.unpack_from('!BHH4sB', recv_bytes)
-
- if len(recv_bytes) != (10 + paramlen):
- # TODO(PH): Check what RFC says to do here.
- LOG.debug('Open message: too short.')
-
- offset = 10
-
- # BGP implementation MUST reject Hold Time values of one or two
- # seconds.
- if holdtime <= 2:
- raise UnacceptableHoldTime()
-
- # BGP Identifier field MUST represents a valid unicast IP host address.
- bgpid = socket.inet_ntoa(bgpid)
- if not is_valid_ipv4(bgpid):
- raise BadBgpId()
-
- # Parse optional parameters.
- caps = OrderedDict()
- unrec_params = OrderedDict()
- while offset < len(recv_bytes):
- ptype, plen = struct.unpack_from('BB', recv_bytes, offset)
- offset += 2
- value = recv_bytes[offset:offset + plen]
- offset += plen
-
- # Parse capabilities optional parameter.
- if ptype == CAP_OPT_PARA_TYPE:
- bgp_caps = capabilities.decode(value)
- # store decoded bgp capabilities by their capability-code
- for cap in bgp_caps:
- cap_code = cap.CODE
- if cap_code in caps:
- caps[cap_code].append(cap)
- else:
- caps[cap_code] = [cap]
- else:
- # Other unrecognized optional parameters.
- unrec_params[ptype] = value
-
- # Un-recognized capabilities are passed on, its up to application to
- # check if unrec-optional-paramters are a problem and send NOTIFICATION
- return cls(version, asnum, holdtime, bgpid, caps, unrec_params)
-
- def packvalue(self):
- params = cStringIO.StringIO()
- # Capabilities optional parameters.
- for capability in self.caps.itervalues():
- for cap in capability:
- encoded_cap = cStringIO.StringIO()
- encoded_cap.write(cap.encode())
- encoded_cap_value = encoded_cap.getvalue()
- encoded_cap.close()
- params.write(struct.pack('BB',
- CAP_OPT_PARA_TYPE,
- len(encoded_cap_value)))
- params.write(encoded_cap_value)
-
- # Other optional parameters.
- for ptype, pvalue in self.unrec_params.items():
- params.write(struct.pack('BB', ptype, len(pvalue)))
- params.write(pvalue)
-
- bgpid = socket.inet_aton(self.bgpid)
- params_value = params.getvalue()
- params.close()
- return struct.pack('!BHH4sB', self.version, self.asnum, self.holdtime,
- bgpid, len(params_value)) + params_value
-
- def __str__(self):
- str_rep = cStringIO.StringIO()
- str_rep.write('Open message Ver=%s As#=%s Hold Time=%s Bgp Id=%s' %
- (self.version, self.asnum, self.holdtime, self.bgpid))
- for param, value in self.unrec_params.items():
- str_rep.write(' unrec_param %s=%r' % (param, value))
- for cap, value in self.caps.items():
- str_rep.write(' cap %s=%r' % (cap, value))
- return str_rep.getvalue()
-
-
-@_register_bgp_message
-class Keepalive(BgpMessage):
- MSG_NAME = 'keepalive'
- TYPE_CODE = 4
-
- @classmethod
- def from_bytes(cls, recv_bytes, total_msg_len):
- # Validate KeepAlive msg. length
- if len(recv_bytes):
- LOG.info("Received keepalive msg. with data! %r" % (recv_bytes,))
- raise BadLen(
- Keepalive.TYPE_CODE,
- len(recv_bytes) + cls.HEADER_SIZE
- )
-
- self = cls()
- return self
-
- def packvalue(self):
- return ''
-
- def __str__(self):
- return 'Keepalive message'
-
-
-@_register_bgp_message
-class RouteRefresh(BgpMessage):
- MSG_NAME = 'route-refresh'
- TYPE_CODE = 5
-
- def __init__(self, route_family, demarcation=0):
- BgpMessage.__init__(self)
- self._route_family = route_family
- self._demarcation = demarcation
- self.eor_sent = False
-
- @property
- def route_family(self):
- return self._route_family
-
- @property
- def demarcation(self):
- return self._demarcation
-
- @classmethod
- def from_bytes(cls, recv_bytes, total_msg_len):
- # Validate length of RouteRefresh message.
- if len(recv_bytes) != 4:
- raise BadLen(
- RouteRefresh.TYPE_CODE,
- len(recv_bytes) + cls.HEADER_SIZE
- )
-
- afi, reserved, safi = struct.unpack_from('!HBB', recv_bytes)
- route_family = get_rf(afi, safi)
- return cls(route_family, reserved)
-
- def packvalue(self):
- return struct.pack('!HBB', self.route_family.afi, self.demarcation,
- self._route_family.safi)
-
- def __str__(self):
- return 'Route-refresh message (%s, %s)' % \
- (self.route_family, self.demarcation)
-
-
-@_register_bgp_message
-class Notification(BgpMessage):
- MSG_NAME = 'notification'
- TYPE_CODE = 3
- REASONS = {
- (1, 1): 'Message Header Error: not synchronised',
- (1, 2): 'Message Header Error: bad message len',
- (1, 3): 'Message Header Error: bad message type',
- (2, 1): 'Open Message Error: unsupported version',
- (2, 2): 'Open Message Error: bad peer AS',
- (2, 3): 'Open Message Error: bad BGP identifier',
- (2, 4): 'Open Message Error: unsupported optional param',
- (2, 5): 'Open Message Error: authentication failure',
- (2, 6): 'Open Message Error: unacceptable hold time',
- (2, 7): 'Open Message Error: Unsupported Capability',
- (2, 8): 'Open Message Error: Unassigned',
- (3, 1): 'Update Message Error: malformed attribute list',
- (3, 2): 'Update Message Error: unrecognized well-known attr',
- (3, 3): 'Update Message Error: missing well-known attr',
- (3, 4): 'Update Message Error: attribute flags error',
- (3, 5): 'Update Message Error: attribute length error',
- (3, 6): 'Update Message Error: invalid origin attr',
- (3, 7): 'Update Message Error: as routing loop',
- (3, 8): 'Update Message Error: invalid next hop attr',
- (3, 9): 'Update Message Error: optional attribute error',
- (3, 10): 'Update Message Error: invalid network field',
- (3, 11): 'Update Message Error: malformed AS_PATH',
- (4, 1): 'Hold Timer Expired',
- (5, 1): 'Finite State Machine Error',
- (6, 1): 'Cease: Maximum Number of Prefixes Reached',
- (6, 2): 'Cease: Administrative Shutdown',
- (6, 3): 'Cease: Peer De-configured',
- (6, 4): 'Cease: Administrative Reset',
- (6, 5): 'Cease: Connection Rejected',
- (6, 6): 'Cease: Other Configuration Change',
- (6, 7): 'Cease: Connection Collision Resolution',
- (6, 8): 'Cease: Out of Resources',
- }
-
- def __init__(self, code, subcode, data=''):
- BgpMessage.__init__(self)
- self._code = code
- self._subcode = subcode
- self._data = data
-
- @property
- def code(self):
- return self._code
-
- @property
- def subcode(self):
- return self._subcode
-
- @property
- def data(self):
- return self._data
-
- @classmethod
- def from_bytes(cls, recv_bytes, total_msg_len):
- # Validate NOTIFICATION msg. length.
- if len(recv_bytes) < 2:
- LOG.error('Received NOTIFICATION msg. with bad length %s' %
- (len(recv_bytes) + cls.HEADER_SIZE))
- raise BadNotification()
-
- code, subcode = struct.unpack_from('BB', recv_bytes)
- data = recv_bytes[2:]
-
- # Check code or sub-code are recognized.
- if not Notification.REASONS.get((code, subcode)):
- LOG.error('Received notification msg. with unrecognized Error '
- 'code or Sub-code (%s, %s)' % (code, subcode))
- raise BadNotification()
-
- return cls(code, subcode, data)
-
- def __str__(self):
- c, s = self.code, self.subcode
- if (c, s) in self.REASONS:
- return ('Notification "%s" params %r' %
- (self.REASONS[c, s], self.data))
- return ('Notification message code=%d subcode=%d params=%r' %
- (self.code, self.subcode, self.data))
-
- def packvalue(self):
- v = struct.pack('BB', self.code, self.subcode)
- if self.data:
- v += self.data
- return v
-
-
-@_register_bgp_message
-class Update(BgpMessage):
- MSG_NAME = 'update'
- TYPE_CODE = 2
- WITHDRAW_NLRI = 'withdraw_nlri'
- PATH_ATTR_AND_NLRI = 'path_attr_and_nlri'
- MIN_LENGTH = 23
-
- def __init__(self, pathattr_map=None, nlri_list=None, withdraw_list=None):
- """Initailizes a new `Update` instance.
-
- Parameter:
- - `pathattr_map`: (OrderedDict) key -> attribute name,
- value -> attribute.
- - `nlri_list`: (list/iterable) NLRIs.
- - `withdraw_list`: (list/iterable) Withdraw routes.
- """
- if nlri_list is None:
- nlri_list = []
- if withdraw_list is None:
- withdraw_list = []
- if not pathattr_map:
- pathattr_map = OrderedDict()
-
- self._nlri_list = list(nlri_list)
- self._withdraw_list = list(withdraw_list)
- self._pathattr_map = copy(pathattr_map)
-
- @property
- def nlri_list(self):
- return self._nlri_list[:]
-
- @property
- def withdraw_list(self):
- return self._withdraw_list[:]
-
- @property
- def pathattr_map(self):
- return copy(self._pathattr_map)
-
- def get_path_attr(self, attr_name):
- return self._pathattr_map.get(attr_name)
-
- @classmethod
- def from_bytes(cls, recv_bytes, total_msg_len):
- # Validate UPDATE message length
- if len(recv_bytes) < 4:
- raise BadLen(Update.TYPE_CODE, len(recv_bytes) + cls.HEADER_SIZE)
- withdraw_list = None
- nlri_list = None
- pathattr_map = OrderedDict()
-
- d = {}
- idx = 0
- # Compute withdraw route length + total attribute length.
- recv_len = 0
- for kind in (Update.WITHDRAW_NLRI, Update.PATH_ATTR_AND_NLRI):
- plen, = struct.unpack_from('!H', recv_bytes, idx)
- idx += 2
- d[kind] = recv_bytes[idx: (idx + plen)]
- idx += plen
- recv_len += plen
-
- if d[Update.WITHDRAW_NLRI]:
- withdraw_list = nlri.parse(d[Update.WITHDRAW_NLRI])
- # TODO(PH): We have to test how ipv4 nlri packed after path-attr are
- # getting parsed.
- nlri_list = nlri.parse(recv_bytes[idx:])
-
- idx = 0
- recv_bytes = d[Update.PATH_ATTR_AND_NLRI]
- while idx < len(recv_bytes):
- used, pattr = pathattr.decode(recv_bytes, idx)
- # TODO(PH) Can optimize here by checking if path attribute is
- # MpReachNlri and stop parsing if RT are not interesting.
- idx += used
- pathattr_map[pattr.ATTR_NAME] = pattr
-
- return cls(pathattr_map=pathattr_map,
- nlri_list=nlri_list, withdraw_list=withdraw_list)
-
- def __repr__(self):
- str_rep = cStringIO.StringIO()
- str_rep.write('<Update message withdraw=%r' % (self._withdraw_list,))
- for ptype, pattr in self._pathattr_map.items():
- str_rep.write('\n path attr %s, %s' % (ptype, pattr,))
-# if ptype in (MpReachNlri.ATTR_NAME, MpUnreachNlri):
-# for nnlri in pattr.nlri_list:
-# str_rep.write('\n nlri=%s' % (nnlri,))
- for nnlri in self._nlri_list:
- str_rep.write('\nmp nlri %s' % (nnlri,))
-
- str_rep.write('>')
-
- return str_rep.getvalue()
-
- def __cmp__(self, other):
- if isinstance(other, Update):
- return cmp(
- (self._pathattr_map, self._withdraw_list, self._nlri_list),
- (other.pathattr_map, other.withdraw_list, other.nlri_list),
- )
- return -1
-
- def packvalue(self):
- bvalue = ''
-
- bwithdraw = ''
- for awithdraw in self._withdraw_list:
- bwithdraw += awithdraw.encode()
-
- bvalue += struct.pack('!H', len(bwithdraw))
- bvalue += bwithdraw
-
- pattr = ''
- for _, attr in self._pathattr_map.items():
- if attr is not None:
- pattr += attr.encode()
- bvalue += struct.pack('!H', len(pattr))
- bvalue += pattr
-
- for anlri in self._nlri_list:
- bvalue += anlri.encode()
-
- return bvalue
-
-
-def decode(ptype, payload, msg_len):
- """Decodes given payload into bgp message instance of given type.
- """
- bgp_msg_class = _BGP_MESSAGE_REGISTRY.get(ptype)
- if not bgp_msg_class:
- raise BadMsg(ptype)
-
- return bgp_msg_class.from_bytes(payload, msg_len)
diff --git a/ryu/services/protocols/bgp/protocols/bgp/nlri.py b/ryu/services/protocols/bgp/protocols/bgp/nlri.py
deleted file mode 100644
index bed279d3..00000000
--- a/ryu/services/protocols/bgp/protocols/bgp/nlri.py
+++ /dev/null
@@ -1,841 +0,0 @@
-# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
- Module related to BGP Network layer reachability information (NLRI).
-"""
-
-from abc import ABCMeta
-import logging
-import socket
-import struct
-from types import IntType
-
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import OptAttrError
-from ryu.services.protocols.bgp.utils.other import bytes2hex
-from ryu.services.protocols.bgp.utils.other import hex2byte
-from ryu.services.protocols.bgp.utils.validation import is_valid_ext_comm_attr
-from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4
-from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4_prefix
-from ryu.services.protocols.bgp.utils.validation import is_valid_ipv6_prefix
-from ryu.services.protocols.bgp.utils.validation import is_valid_mpls_label
-from ryu.services.protocols.bgp.utils.validation import is_valid_old_asn
-from ryu.services.protocols.bgp.utils.validation import is_valid_route_disc
-
-
-LOG = logging.getLogger('protocols.bgp.nlri')
-
-# Registry for bgp message class by their type code.
-# <key>: <value> - <afi, safi>: <nlri class>
-_NLRI_REGISTRY = {}
-
-
-def _register_nlri(cls):
- """Used as class decorator for registering NLRI classes by their afi/safi.
- """
- assert _NLRI_REGISTRY.get((cls.AFI, cls.SAFI)) is None
- _NLRI_REGISTRY[(cls.AFI, cls.SAFI)] = cls
- return cls
-
-
-#
-# AddressFamily
-#
-class AddressFamily(object):
- """Subclasses of this class hold methods for a specific AF and
- help the calling code to stay AF-independent.
-
- Each subclass need have just a singleton instance (see below).
- """
-
- def __init__(self, afi):
- self.afi = afi
-
- def __hash__(self):
- return hash(self.afi)
-
- def __cmp__(self, other):
- afi1 = None
- afi2 = None
- if isinstance(other, IntType):
- afi2 = other
- else:
- afi2 = other.afi
- if isinstance(self, IntType):
- afi1 = self
- else:
- afi1 = self.afi
- return cmp(afi1, afi2)
-
-
-class AfiIpv4(AddressFamily):
- def __init__(self):
- super(AfiIpv4, self).__init__(1)
-
- def __repr__(self):
- return "IPv4"
-
-
-class AfiIpv6(AddressFamily):
- def __init__(self):
- super(AfiIpv6, self).__init__(2)
-
- def __repr__(self):
- return "IPv6"
-
-
-#
-# SubAddressFamily
-#
-# An sub-address family as defined by BGP.
-#
-class SubAddressFamily(object):
-
- def __init__(self, safi):
- self.safi = safi
-
- def __hash__(self):
- return hash(self.safi)
-
- def __cmp__(self, other):
- safi1 = None
- safi2 = None
- if isinstance(self, IntType):
- safi1 = self
- else:
- safi1 = self.safi
- if isinstance(other, IntType):
- safi2 = other
- else:
- safi2 = other.safi
- return cmp(safi1, safi2)
-
-
-class SafiNlriUnicast(SubAddressFamily):
- def __init__(self):
- super(SafiNlriUnicast, self).__init__(1)
-
- def __repr__(self):
- return "SafiNlriUnicast"
-
-
-class SafiVpn(SubAddressFamily):
- def __init__(self):
- super(SafiVpn, self).__init__(128)
-
- def __repr__(self):
- return "SafiVpn"
-
-
-class SafiRtc(SubAddressFamily):
- def __init__(self):
- super(SafiRtc, self).__init__(132)
-
- def __repr__(self):
- return "SafiRtc"
-
-NLRI_UC = SafiNlriUnicast()
-SAF_VPN = SafiVpn()
-SAF_RTC = SafiRtc()
-
-# Singleton objects for each AF.
-AF_IPv4 = AfiIpv4()
-AF_IPv6 = AfiIpv6()
-
-# Constants to represent address family and sub-address family.
-ADD_FMLY = 'afi'
-SUB_ADD_FMLY = 'safi'
-
-
-#
-# RouteFamily
-#
-class RouteFamily(object):
- """The family that a given route (or Network Layer Reachability
- Information) belongs to.
-
- Basically represents a combination of AFI/SAFI.
- """
- __slots__ = ('_add_fmly', '_sub_add_fmly')
-
- def __init__(self, add_fmly, sub_add_fmly):
- # Validate i/p.
- if not add_fmly or not sub_add_fmly:
- raise ValueError('Invalid arguments.')
-
- self._add_fmly = add_fmly
- self._sub_add_fmly = sub_add_fmly
-
- @property
- def afi(self):
- return self._add_fmly.afi
-
- @property
- def safi(self):
- return self._sub_add_fmly.safi
-
- def __repr__(self):
- return ('RouteFamily(afi=%s, safi=%s)' % (self.afi, self.safi))
-
- def __cmp__(self, other):
- other_rf = (other.afi, other.safi)
- self_rf = (self.afi, self.safi)
- return cmp(self_rf, other_rf)
-
- @staticmethod
- def is_valid(other):
- if other and (hasattr(other, 'afi') and hasattr(other, 'safi')):
- return True
- return False
-
-# Various route family singletons.
-RF_IPv4_UC = RouteFamily(AF_IPv4, NLRI_UC)
-RF_IPv6_UC = RouteFamily(AF_IPv6, NLRI_UC)
-RF_IPv4_VPN = RouteFamily(AF_IPv4, SAF_VPN)
-RF_IPv6_VPN = RouteFamily(AF_IPv6, SAF_VPN)
-RF_RTC_UC = RouteFamily(AF_IPv4, SAF_RTC)
-
-_rf_by_afi_safi = {
- (1, 1): RF_IPv4_UC,
- (2, 1): RF_IPv6_UC,
- (1, 128): RF_IPv4_VPN,
- (2, 128): RF_IPv6_VPN,
- (1, 132): RF_RTC_UC
-}
-
-
-def get_rf(afi, safi):
- """Returns *RouteFamily* singleton instance for given *afi* and *safi*."""
- if not isinstance(afi, IntType):
- afi = int(afi)
- if not isinstance(safi, IntType):
- safi = int(safi)
- return _rf_by_afi_safi.get((afi, safi))
-
-
-# TODO(PH): Consider trade-offs of making this extend Internable.
-class Nlri(object):
- """Represents super class of all Network Layer Reachability Information.
- """
- __meta__ = ABCMeta
- __slots__ = ()
-
- # Sub-classes should set afi/safi constants appropriately.
- AFI = 0
- SAFI = 0
-
- @classmethod
- def encode(self):
- raise NotImplementedError()
-
- @property
- def route_family(self):
- return get_rf(self.__class__.AFI, self.__class__.SAFI)
-
-
-@_register_nlri
-class Vpnv4(Nlri):
- """Vpnv4 NLRI.
- """
- __slots__ = ('_labels', '_route_disc', '_prefix')
-
- AFI = 1
- SAFI = 128
-
- def __init__(self, labels, route_disc, prefix):
- Nlri.__init__(self)
- if not labels:
- labels = []
-
- # Validate given params
- for label in labels:
- if not is_valid_mpls_label(label):
- raise ValueError('Invalid label %s' % label)
- if (not is_valid_ipv4_prefix(prefix) or
- not is_valid_route_disc(route_disc)):
- raise ValueError('Invalid parameter value(s).')
-
- self._labels = labels
- self._route_disc = route_disc
- self._prefix = prefix
-
- @property
- def label_list(self):
- return self._labels[:]
-
- @property
- def route_disc(self):
- return self._route_disc
-
- @property
- def prefix(self):
- return self._prefix
-
- @property
- def formatted_nlri_str(self):
- return "%s:%s" % (self._route_disc, self.prefix)
-
- def __repr__(self):
- if self._labels:
- l = ','.join([str(l) for l in self._labels])
- else:
- l = 'none'
-
- return ('Vpnv4(label=%s, route_disc=%s, prefix=%s)' %
- (l, self.route_disc, self.prefix))
-
- def __str__(self):
- return 'Vpnv4 %s:%s, %s' % (self.route_disc, self.prefix, self._labels)
-
- def __cmp__(self, other):
- return cmp(
- (self._labels, self.route_disc, self.prefix),
- (other.label_list, other.route_disc, other.prefix),
- )
-
- def encode(self):
- plen = 0
- v = ''
- labels = self._labels[:]
-
- if not labels:
- return '\0'
-
- labels = [l << 4 for l in labels]
- labels[-1] |= 1
-
- for l in labels:
- lo = l & 0xff
- hi = (l & 0xffff00) >> 8
- v += struct.pack('>HB', hi, lo)
- plen += 24
-
- l, r = self.route_disc.split(':')
- if '.' in l:
- ip = socket.inet_aton(l)
- route_disc = struct.pack('!H4sH', 1, ip, int(r))
- else:
- route_disc = struct.pack('!HHI', 0, int(l), int(r))
-
- v += route_disc
- plen += 64
-
- ip, masklen = self.prefix.split('/')
- ip = socket.inet_aton(ip)
- masklen = int(masklen)
-
- plen += masklen
- if masklen > 24:
- v += ip
- elif masklen > 16:
- v += ip[:3]
- elif masklen > 8:
- v += ip[:2]
- elif masklen > 0:
- v += ip[:1]
- else:
- pass
-
- return struct.pack('B', plen) + v
-
- @classmethod
- def from_bytes(cls, plen, val):
-
- if plen == 0:
- # TODO(PH): Confirm this is valid case and implementation.
- return cls([], '0:0', '0.0.0.0/0')
-
- idx = 0
-
- # plen is the length, in bits, of all the MPLS labels,
- # plus the 8-byte RD, plus the IP prefix
- labels = []
- while True:
- ls, = struct.unpack_from('3s', val, idx)
- idx += 3
- plen -= 24
-
- if ls == '\x80\x00\x00':
- # special null label for vpnv4 withdraws
- labels = None
- break
-
- label, = struct.unpack_from('!I', '\x00' + ls)
- bottom = label & 1
-
- labels.append(label >> 4)
- if bottom:
- break
- # TODO(PH): We are breaking after first label as we support only
- # one label for now. Revisit if we need to support stack of labels.
- break
-
- rdtype, route_disc = struct.unpack_from('!H6s', val, idx)
- if rdtype == 1:
- rdip, num = struct.unpack('!4sH', route_disc)
- rdip = socket.inet_ntoa(rdip)
- route_disc = '%s:%s' % (rdip, num)
- else:
- num1, num2 = struct.unpack('!HI', route_disc)
- route_disc = '%s:%s' % (num1, num2)
-
- idx += 8
- plen -= 64
-
- ipl = pb(plen)
- ip = val[idx:idx + ipl]
- idx += ipl
-
- prefix = unpack_ipv4(ip, plen)
-
- return cls(labels, route_disc, prefix)
-
-
-@_register_nlri
-class Vpnv6(Nlri):
- """Vpnv4 NLRI.
- """
- __slots__ = ('_labels', '_route_disc', '_prefix')
-
- AFI = 2
- SAFI = 128
-
- def __init__(self, labels, route_disc, prefix):
- Nlri.__init__(self)
- if not labels:
- labels = []
-
- # Validate given params
- for label in labels:
- if not is_valid_mpls_label(label):
- raise ValueError('Invalid label %s' % label)
- if (not is_valid_route_disc(route_disc) or
- not is_valid_ipv6_prefix(prefix)):
- raise ValueError('Invalid parameter value(s).')
-
- self._labels = labels
- self._route_disc = route_disc
- self._prefix = prefix
-
- @property
- def label_list(self):
- return self._labels[:]
-
- @property
- def route_disc(self):
- return self._route_disc
-
- @property
- def prefix(self):
- return self._prefix
-
- @property
- def formatted_nlri_str(self):
- return "%s:%s" % (self._route_disc, self.prefix)
-
- def __repr__(self):
- if self._labels:
- l = ','.join([str(l) for l in self._labels])
- else:
- l = 'none'
-
- return ('Vpnv6(label=%s, route_disc=%s, prefix=%s)' %
- (l, self.route_disc, self.prefix))
-
- def __str__(self):
- return 'Vpnv6 %s:%s, %s' % (self.route_disc, self.prefix, self._labels)
-
- def __cmp__(self, other):
- return cmp(
- (self._labels, self.route_disc, Ipv6(self.prefix).encode()),
- (other.label_list, other.route_disc, Ipv6(other.prefix).encode()),
- )
-
- def encode(self):
- plen = 0
- v = ''
- labels = self._labels[:]
-
- if not labels:
- return '\0'
-
- labels = [l << 4 for l in labels]
- labels[-1] |= 1
-
- for l in labels:
- lo = l & 0xff
- hi = (l & 0xffff00) >> 8
- v += struct.pack('>HB', hi, lo)
- plen += 24
-
- l, r = self.route_disc.split(':')
- if '.' in l:
- ip = socket.inet_aton(l)
- route_disc = struct.pack('!H4sH', 1, ip, int(r))
- else:
- route_disc = struct.pack('!HHI', 0, int(l), int(r))
- v += route_disc
- plen += 64
-
- ip, masklen = self.prefix.split('/')
- ip = socket.inet_pton(socket.AF_INET6, ip)
- masklen = int(masklen)
-
- plen += masklen
- v += ip[:pb6(masklen)]
-
- return struct.pack('B', plen) + v
-
- @classmethod
- def from_bytes(cls, plen, val):
- if plen == 0:
- # TODO(PH): Confirm this is valid case and implementation.
- return cls([], '0:0', '::/0')
-
- idx = 0
-
- # plen is the length, in bits, of all the MPLS labels,
- # plus the 8-byte RD, plus the IP prefix
- labels = []
- while True:
- ls, = struct.unpack_from('3s', val, idx)
- idx += 3
- plen -= 24
-
- if ls == '\x80\x00\x00':
- # special null label for vpnv4 withdraws
- labels = None
- break
-
- label, = struct.unpack_from('!I', '\x00' + ls)
- bottom = label & 1
-
- labels.append(label >> 4)
- if bottom:
- break
- # TODO(PH): We are breaking after first label as we support only
- # one label for now. Revisit if we need to support stack of labels.
- break
-
- rdtype, route_disc = struct.unpack_from('!H6s', val, idx)
- if rdtype == 1:
- rdip, num = struct.unpack('!4sH', route_disc)
- rdip = socket.inet_ntoa(rdip)
- route_disc = '%s:%s' % (rdip, num)
- else:
- num1, num2 = struct.unpack('!HI', route_disc)
- route_disc = '%s:%s' % (num1, num2)
-
- idx += 8
- plen -= 64
-
- ipl = pb6(plen)
- ip = val[idx:idx + ipl]
- idx += ipl
-
- prefix = unpack_ipv6(ip, plen)
-
- return cls(labels, route_disc, prefix)
-
-
-@_register_nlri
-class Ipv4(Nlri):
- __slots__ = ('_prefix')
-
- AFI = 1
- SAFI = 1
-
- def __init__(self, prefix):
- if not is_valid_ipv4_prefix(prefix):
- raise ValueError('Invalid prefix %s.' % prefix)
- Nlri.__init__(self)
- self._prefix = prefix
-
- @property
- def prefix(self):
- return self._prefix
-
- @property
- def formatted_nlri_str(self):
- return self._prefix
-
- def __cmp__(self, other):
- aip, alen = self.prefix.split('/')
- alen = int(alen)
- aip = socket.inet_aton(aip)
-
- bip, blen = other.prefix.split('/')
- blen = int(blen)
- bip = socket.inet_aton(bip)
-
- return cmp((aip, alen), (bip, blen))
-
- def encode(self):
- plen = 0
- v = ''
-
- ip, masklen = self.prefix.split('/')
- ip = socket.inet_aton(ip)
- masklen = int(masklen)
-
- plen += masklen
- if masklen > 24:
- v += ip
- elif masklen > 16:
- v += ip[:3]
- elif masklen > 8:
- v += ip[:2]
- elif masklen > 0:
- v += ip[:1]
- else:
- pass
-
- return struct.pack('B', plen) + v
-
- def __repr__(self):
- return 'Ipv4(%s)' % (self.prefix)
-
- def __str__(self):
- return 'Ipv4 ' + self.prefix
-
- @classmethod
- def from_bytes(cls, plen, val):
- return cls(unpack_ipv4(val, plen))
-
-
-@_register_nlri
-class Ipv6(Nlri):
- __slots__ = ('_prefix')
-
- AFI = 2
- SAFI = 1
-
- def __init__(self, prefix):
- if not is_valid_ipv6_prefix(prefix):
- raise ValueError('Invalid prefix %s.' % prefix)
- Nlri.__init__(self)
- self._prefix = prefix
-
- @property
- def prefix(self):
- return self._prefix
-
- @property
- def formatted_nlri_str(self):
- return self._prefix
-
- def __cmp__(self, other):
- abin = self.encode()
- bbin = other.encode()
- return cmp(abin, bbin)
-
- def encode(self):
- plen = 0
- v = ''
-
- ip, masklen = self.prefix.split('/')
- ip = socket.inet_pton(socket.AF_INET6, ip)
- masklen = int(masklen)
-
- plen += masklen
- ip_slice = pb6(masklen)
- v += ip[:ip_slice]
-
- return struct.pack('B', plen) + v
-
- def __repr__(self):
- return 'Ipv6(%s)' % (self.prefix)
-
- def __str__(self):
- return 'Ipv6 ' + self.prefix
-
- @classmethod
- def from_bytes(cls, plen, val):
- return cls(unpack_ipv6(val, plen))
-
-
-@_register_nlri
-class RtNlri(Nlri):
- """Route Target Membership NLRI.
-
- Route Target membership NLRI is advertised in BGP UPDATE messages using
- the MP_REACH_NLRI and MP_UNREACH_NLRI attributes.
- """
- __slots__ = ('_origin_as', '_route_target')
-
- AFI = 1
- SAFI = 132
- DEFAULT_AS = '0:0'
- DEFAULT_RT = '0:0'
-
- def __init__(self, origin_as, route_target):
- Nlri.__init__(self)
- # If given is not default_as and default_rt
- if not (origin_as is RtNlri.DEFAULT_AS and
- route_target is RtNlri.DEFAULT_RT):
- # We validate them
- if (not is_valid_old_asn(origin_as) or
- not is_valid_ext_comm_attr(route_target)):
- raise ValueError('Invalid params.')
- self._origin_as = origin_as
- self._route_target = route_target
-
- @property
- def origin_as(self):
- return self._origin_as
-
- @property
- def route_target(self):
- return self._route_target
-
- @property
- def formatted_nlri_str(self):
- return "%s:%s" % (self.origin_as, self.route_target)
-
- def is_default_rtnlri(self):
- if (self._origin_as is RtNlri.DEFAULT_AS and
- self._route_target is RtNlri.DEFAULT_RT):
- return True
- return False
-
- def __str__(self):
- return 'RtNlri ' + str(self._origin_as) + ':' + self._route_target
-
- def __repr__(self):
- return 'RtNlri(%s, %s)' % (self._origin_as, self._route_target)
-
- def __cmp__(self, other):
- return cmp(
- (self._origin_as, self._route_target),
- (other.origin_as, other.route_target),
- )
-
- @classmethod
- def from_bytes(cls, plen, val):
- idx = 0
- if plen == 0 and not val:
- return cls(RtNlri.DEFAULT_AS, RtNlri.DEFAULT_RT)
-
- # Extract origin AS.
- origin_as, = struct.unpack_from('!I', val, idx)
- idx += 4
-
- # Extract route target.
- route_target = ''
- etype, esubtype, payload = struct.unpack_from('BB6s', val, idx)
- # RFC says: The value of the high-order octet of the Type field for the
- # Route Target Community can be 0x00, 0x01, or 0x02. The value of the
- # low-order octet of the Type field for this community is 0x02.
- # TODO(PH): Remove this exception when it breaks something Here we make
- # exception as Routem packs lower-order octet as 0x00
- if etype in (0, 2) and esubtype in (0, 2):
- # If we have route target community in AS number format.
- asnum, i = struct.unpack('!HI', payload)
- route_target = ('%s:%s' % (asnum, i))
- elif etype == 1 and esubtype == 2:
- # If we have route target community in IP address format.
- ip_addr, i = struct.unpack('!4sH', payload)
- ip_addr = socket.inet_ntoa(ip_addr)
- route_target = ('%s:%s' % (ip_addr, i))
- elif etype == 0 and esubtype == 1:
- # TODO(PH): Parsing of RtNlri 1:1:100:1
- asnum, i = struct.unpack('!HI', payload)
- route_target = ('%s:%s' % (asnum, i))
-
- return cls(origin_as, route_target)
-
- def encode(self):
- rt_nlri = ''
- if not self.is_default_rtnlri():
- rt_nlri += struct.pack('!I', self.origin_as)
- # Encode route target
- first, second = self.route_target.split(':')
- if '.' in first:
- ip_addr = socket.inet_aton(first)
- rt_nlri += struct.pack('!BB4sH', 1, 2, ip_addr, int(second))
- else:
- rt_nlri += struct.pack('!BBHI', 0, 2, int(first), int(second))
-
- # RT Nlri is 12 octets
- return struct.pack('B', (8 * 12)) + rt_nlri
-
-
-def pb(masklen):
- if masklen > 24:
- return 4
- elif masklen > 16:
- return 3
- elif masklen > 8:
- return 2
- elif masklen > 0:
- return 1
- return 0
-
-_v6_bits = range(120, -8, -8)
-_v6_bytes = [i / 8 for i in range(128, 0, -8)]
-
-
-def pb6(masklen):
- for idx, bits in enumerate(_v6_bits):
- if masklen > bits:
- return _v6_bytes[idx]
- return 0
-
-
-def unpack_ipv4(pi, masklen):
- pi += '\x00' * 4
- return '%s/%s' % (socket.inet_ntoa(pi[:4]), masklen)
-
-
-def unpack_ipv6(pi, masklen):
- pi += '\x00' * 16
- ip = socket.inet_ntop(socket.AF_INET6, pi[:16])
- return '%s/%s' % (ip, masklen)
-
-
-def ipv4_mapped_ipv6(ipv4):
- if not is_valid_ipv4(ipv4):
- raise ValueError('Invalid ipv4 address given %s.' % ipv4)
- ipv4n = socket.inet_pton(socket.AF_INET, ipv4)
- ipv6_hex = '00' * 10 + 'ff' * 2 + bytes2hex(ipv4n)
- ipv6n = hex2byte(ipv6_hex)
- ipv6 = socket.inet_ntop(socket.AF_INET6, ipv6n)
- return ipv6
-
-
-# TODO(PH): Consider refactoring common functionality new methods
-# Look at previous commit
-def parse(received, afi=1, safi=1):
- recv_nlri_list = []
-
- klass = _NLRI_REGISTRY.get((afi, safi))
- if not klass:
- raise ValueError('Asked to parse unsupported NLRI afi/safi: '
- '(%s, %s)' % (afi, safi))
-
- try:
- idx = 0
- while idx < len(received):
- plen, = struct.unpack_from('B', received, idx)
- idx += 1
- nbytes, rest = divmod(plen, 8)
- if rest:
- nbytes += 1
- val = received[idx:idx + nbytes]
- idx += nbytes
- recv_nlri_list.append(klass.from_bytes(plen, val))
- except Exception:
- raise OptAttrError()
-
- return recv_nlri_list
diff --git a/ryu/services/protocols/bgp/protocols/bgp/pathattr.py b/ryu/services/protocols/bgp/protocols/bgp/pathattr.py
deleted file mode 100644
index 04499d4b..00000000
--- a/ryu/services/protocols/bgp/protocols/bgp/pathattr.py
+++ /dev/null
@@ -1,1076 +0,0 @@
-# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-This module provides BGP Path Attributes classes and utility methods to
-encode and decode them.
-
-This file was adapted from pybgp open source project.
-"""
-from abc import ABCMeta
-from abc import abstractmethod
-import copy
-import logging
-import socket
-import StringIO
-import struct
-
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import AttrFlagError
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import AttrLenError
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import InvalidNextHop
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import \
- InvalidOriginError
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import MalformedAsPath
-from ryu.services.protocols.bgp.protocols.bgp.exceptions import OptAttrError
-from ryu.services.protocols.bgp.protocols.bgp import nlri
-from ryu.services.protocols.bgp.protocols.bgp.nlri import get_rf
-from ryu.services.protocols.bgp.protocols.bgp.nlri import RF_IPv4_VPN
-from ryu.services.protocols.bgp.protocols.bgp.nlri import RF_IPv6_VPN
-from ryu.services.protocols.bgp.protocols.bgp.nlri import RF_RTC_UC
-from ryu.services.protocols.bgp.utils.internable import Internable
-from ryu.services.protocols.bgp.utils import validation
-from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4
-from ryu.services.protocols.bgp.utils.validation import is_valid_ipv6
-
-
-LOG = logging.getLogger('protocols.bgp.pathattr')
-
-# BGP Attribute flags
-EXTENDED_LEN_BGP_ATTR = 0x10
-PARTIAL_BGP_ATTR = 0x20
-TRANSITIVE_BGP_ATTR = 0x40
-OPTIONAL_BGP_ATTR = 0x80
-
-# BGP flag mask
-DEFAULT_FLAGS_MASK = 0x3f
-
-# BGP recognized path attribute class registry by type-code.
-# i.e. <key, value>: <type-code, path-attr-class>
-_PATH_ATTR_REGISTRY = {}
-
-
-def _register_path_attr(cls):
- """Used as decorator for registering recognized bgp path attribute class
- by their type-code.
- """
- assert issubclass(cls, RcgPathAttr)
- assert hasattr(cls, 'TYPE_CODE') and hasattr(cls, 'FLAGS')
- assert _PATH_ATTR_REGISTRY.get(cls.TYPE_CODE) is None
- _PATH_ATTR_REGISTRY[cls.TYPE_CODE] = cls
- return cls
-
-
-def decode(received, idx=0):
- """Decodes given bytes into corresponding BGP path attribute.
- """
- iidx = idx
- flagb, path_attr_type = struct.unpack_from('BB', received, idx)
- idx += 2
- used = 2
-
- if flagb & 16:
- length, = struct.unpack_from('>H', received, idx)
- idx += 2
- used += 2
- else:
- length, = struct.unpack_from('!B', received, idx)
- idx += 1
- used += 1
-
- recv_data = received[idx:(idx + length)]
- used += length
-
- # Check if this attribute type is recognized.
- path_attr_class = _PATH_ATTR_REGISTRY.get(path_attr_type)
- path_attr = None
- if path_attr_class:
- # Check if flags match expected from known/recognized attribute type.
- if not path_attr_class.check_flags(flagb):
- LOG.error(
- "Flags(%s) of pathattr %s received in update don't "
- "match expected flags(%s)"
- % (
- flagb,
- str(path_attr_class),
- path_attr_class.FLAGS
- )
- )
- raise AttrFlagError(data=received[iidx:used])
-
- try:
- path_attr = path_attr_class.from_bytes(recv_data)
- except (AttrLenError, InvalidOriginError, InvalidNextHop,
- OptAttrError) as e:
- # Set attribute type, length and value as data/payload.
- e.data = received[iidx:used]
- raise e
- else:
- path_attr = UnRcgPathAttr(recv_data, flagb, path_attr_type)
-
- return used, path_attr
-
-
-class PathAttr(Internable):
- """Abstract base class for bgp path attributes.
-
- Defines interface for all path attributes and provides some default util.
- methods.
- """
- __metaclass__ = ABCMeta
- __slots__ = ('_flags')
- TYPE_CODE = 0
- ATTR_NAME = 'default'
-
- def __init__(self, flags):
- super(PathAttr, self).__init__()
- self._flags = flags
-
- @property
- def flags(self):
- return self._flags
-
- @abstractmethod
- def packvalue(self):
- """Encodes path-attribute value/pay-load into binary format."""
- raise NotImplementedError()
-
- def encode(self):
- """Wraps packed path-attribute value with headers, such as,
- flags, type-code and length.
- """
- valueb = self.packvalue()
- flags = self._flags
- type_code = self.__class__.TYPE_CODE
-
- if len(valueb) > 255:
- flags = flags | 16
- return struct.pack('!BBH', flags, type_code, len(valueb)) + valueb
-
- flags = flags & (0xff ^ 16)
- return struct.pack('BBB', flags, type_code, len(valueb)) + valueb
-
- def str_flags(self):
- """Returns a list of attribute category for this bgp attribute."""
-
- if self._flags:
- flag_cat = []
- if self._flags & OPTIONAL_BGP_ATTR:
- flag_cat.append('optional')
- else:
- flag_cat.append('well-known')
-
- if self._flags & TRANSITIVE_BGP_ATTR:
- flag_cat.append('transitive')
- else:
- flag_cat.append('non-transitive')
-
- if self._flags & PARTIAL_BGP_ATTR:
- flag_cat.append('partial')
- else:
- flag_cat.append('complete')
-
- if self._flags & EXTENDED_LEN_BGP_ATTR:
- flag_cat.append('ext-length')
- else:
- flag_cat.append('regular-length')
-
- return ','.join(flag_cat)
-
- return 'None'
-
- def __repr__(self):
- return '<%s type/num=%s/%s flags %s>' % (
- self.__class__.__name__, 'unknown', self.__class__.TYPE_CODE,
- self.str_flags())
-
- def __cmp__(self, other):
- if isinstance(other, PathAttr):
- if other.__class__ == self.__class__:
- return cmp(self._flags, other._flags)
-
- return -1
-
- def __hash__(self):
- return hash(self._flags, self.__class__.TYPE_CODE)
-
-
-class RcgPathAttr(PathAttr):
- """Base class for all recognized path attributes."""
- # Flags for this type of known path attribute.
- # Sub-classes should provide value as per RFC.
- FLAGS = None
-
- # There are some flags we don't care about. By default we don't care about
- # extended-length bit and partial bit, so mask is 0x3f (0011 1111)
- FLAGS_MASK = DEFAULT_FLAGS_MASK
-
- def __init__(self):
- PathAttr.__init__(self, self.__class__.FLAGS)
-
- @classmethod
- def from_bytes(cls, val):
- raise NotImplementedError()
-
- @classmethod
- def check_flags(cls, flags):
- """Check if provided flags match flags required by RFC (cls.FLAGS).
-
- RFC path attribute higher order bit rules:
- 0 1 2 3 4 5 6 7
-
- Well Known
- 0 1 - always
- 0 - always
- 0 - attribute length 1 octet
- 1 - attribute length 2 octet
-
- Optional
- 1 1 - Transitive
- 1 - Partial
- 0 - Complete
- 0 - Non-Transitive
- 0 - always
- """
- return cls.FLAGS | cls.FLAGS_MASK == flags | cls.FLAGS_MASK
-
-
-class UnRcgPathAttr(PathAttr):
- """Class for all un-supported/un-recognized bgp path-attributes.
- """
- __slots__ = ('_type_code', '_value')
- ATTR_NAME = 'unknown'
-
- def __init__(self, value, flags, type_code):
- PathAttr.__init__(self, flags)
- self._type_code = type_code
- self._value = value
-
- @property
- def value(self):
- return self._value
-
- @property
- def type_code(self):
- return self._type_code
-
- def packvalue(self):
- return self._value
-
- def encode(self):
- all_flags = self._flags
- valueb = self.packvalue()
-
- if len(valueb) > 255:
- all_flags = all_flags | 16
- return struct.pack('!BBH', all_flags, self.type_code,
- len(valueb)) + valueb
-
- all_flags = all_flags & (0xff ^ 16)
- return struct.pack('BBB', all_flags, self.type_code,
- len(valueb)) + valueb
-
- def is_optional_transitive(self):
- """Returns true if this is an optional path attribute.
- """
- return self._flags & OPTIONAL_BGP_ATTR
-
- def is_transitive(self):
- """Returns true if this is an transitive path attribute.
- """
- return self._flags & TRANSITIVE_BGP_ATTR
-
- def __repr__(self):
- return '<%s type/num=%s/%s flags %s value %r>' % (
- self.__class__.__name__, 'unknown', self.type_code, self.flags,
- self.value)
-
-
-@_register_path_attr
-class Origin(RcgPathAttr):
- """ORIGIN is a well-known mandatory bgp path-attribute."""
- __slots__ = ('_value')
- TYPE_CODE = 1
- ATTR_NAME = 'origin'
- # 010 - well known, transitive, complete
- FLAGS = TRANSITIVE_BGP_ATTR
-
- # Various Origin values.
- IGP = 'igp'
- EGP = 'egp'
- INCOMPLETE = 'incomplete'
-
- def __init__(self, value='incomplete'):
- RcgPathAttr.__init__(self)
- if value not in (Origin.IGP, Origin.EGP, Origin.INCOMPLETE):
- raise ValueError('Invalid Origin attribute value.')
- self._value = value
-
- @property
- def value(self):
- return self._value
-
- @classmethod
- def from_bytes(cls, value):
- """Decodes bgp path-attribute with type-code 1, i.e. ORIGIN.
- """
- if value == '\x00':
- value = Origin.IGP
- elif value == '\x01':
- value = Origin.EGP
- elif value == '\x02':
- value = Origin.INCOMPLETE
- else:
- raise InvalidOriginError()
-
- return cls(value)
-
- def packvalue(self):
- if self.value == Origin.IGP:
- return '\x00'
- elif self.value == Origin.EGP:
- return '\x01'
- elif self.value == Origin.INCOMPLETE:
- return '\x02'
- return self.value
-
- def __repr__(self):
- return '<Origin ' + self.value + '>'
-
- def __str__(self):
- return str(self.value)
-
-
-@_register_path_attr
-class AsPath(RcgPathAttr):
- """AS_PATH is a well-known mandatory bgp path attribute.
- """
- __slots__ = ('_path_seg_list')
- TYPE_CODE = 2
- ATTR_NAME = 'aspath'
- # Higher order bits: 010 - well known, transitive, complete
- FLAGS = TRANSITIVE_BGP_ATTR
- SEG_TYP_AS_SET = 1
- SEG_TYP_AS_SEQ = 2
-
- def __init__(self, path_seg_list):
- RcgPathAttr.__init__(self)
- self._path_seg_list = None
- if isinstance(path_seg_list, str):
- self._path_seg_list = []
- for seg in path_seg_list.split():
- if seg.startswith('set(') and seg.endswith(')'):
- seg = set([int(s) for s in seg[4:-1].split(',')])
- else:
- seg = [int(s) for s in seg.split(',')]
- self._path_seg_list.append(seg)
- else:
- self._path_seg_list = path_seg_list[:]
-
- @property
- def path_seg_list(self):
- return copy.deepcopy(self._path_seg_list)
-
- def get_as_path_len(self):
- count = 0
- for seg in self._path_seg_list:
- if isinstance(seg, list):
- # Segment type 2 stored in list and all AS counted.
- count += len(seg)
- else:
- # Segment type 1 stored in set and count as one.
- count += 1
-
- return count
-
- def has_local_as(self, local_as):
- """Check if *local_as* is already present on path list."""
- for as_path_seg in self._path_seg_list:
- for as_num in as_path_seg:
- if as_num == local_as:
- return True
- return False
-
- def has_matching_leftmost(self, remote_as):
- """Check if leftmost AS matches *remote_as*."""
- if not self._path_seg_list or not remote_as:
- return False
-
- leftmost_seg = self.path_seg_list[0]
- if leftmost_seg and leftmost_seg[0] == remote_as:
- return True
-
- return False
-
- @property
- def value(self):
- ret = []
- for as_path_seg in self._path_seg_list:
- for as_num in as_path_seg:
- ret.append(as_num)
- return ret
-
- def __repr__(self):
- rstring = StringIO.StringIO()
- rstring.write('<AsPath')
- for as_path_seg in self._path_seg_list:
- if isinstance(as_path_seg, set):
- rstring.write(' set(')
- rstring.write(','.join([str(asnum) for asnum in as_path_seg]))
- rstring.write(')')
- else:
- rstring.write(' ')
- rstring.write(','.join([str(asnum) for asnum in as_path_seg]))
- rstring.write('>')
- return rstring.getvalue()
-
- def __str__(self):
- ret = '['
- for as_path_seg in self._path_seg_list:
- ret += ', '.join([str(asnum) for asnum in as_path_seg])
- return ret + ']'
-
- @classmethod
- def from_bytes(cls, val):
- """Decodes bgp path-attribute with type-code 2, i.e. AS_PATH.
- """
- path_seg_list = []
- iidx = 0
-
- while iidx < len(val):
- segtype, numas = struct.unpack_from('BB', val, iidx)
- iidx += 2
-
- if segtype == AsPath.SEG_TYP_AS_SET:
- container = set()
- add = container.add
- elif segtype == AsPath.SEG_TYP_AS_SEQ:
- container = []
- add = container.append
- else:
- raise MalformedAsPath()
-
- for _ in range(numas):
- asnum, = struct.unpack_from('!H', val, iidx)
- iidx += 2
- add(asnum)
- path_seg_list.append(container)
-
- return cls(path_seg_list)
-
- def packvalue(self):
- valueb = ''
- for seg in self._path_seg_list:
- if isinstance(seg, set):
- segtype = 1
- elif isinstance(seg, (tuple, list)):
- segtype = 2
- else:
- raise Exception('unknown segment type %r' % (seg,))
-
- valueb += struct.pack('BB', segtype, len(seg))
- try:
- iter(seg)
- except TypeError:
- valueb += struct.pack('!H', int(seg))
- else:
- for asnum in seg:
- if not isinstance(asnum, int):
- asnum = int(asnum)
- valueb += struct.pack('!H', asnum)
-
- return valueb
-
-
-@_register_path_attr
-class NextHop(RcgPathAttr):
- """NEXT_HOP is well-known mandatory bgp path-attribute.
- """
- __slots__ = ()
- TYPE_CODE = 3
- ATTR_NAME = 'nexthop'
- # Higher order bits: 010 - well known, transitive, complete
- FLAGS = TRANSITIVE_BGP_ATTR
-
- def __init__(self, ip_address):
- if not is_valid_ipv4(ip_address):
- raise ValueError('Invalid ipv4 address %s.' % ip_address)
- RcgPathAttr.__init__(self)
- self._ip_address = ip_address
-
- @property
- def ip_address(self):
- return self._ip_address
-
- def __repr__(self):
- return '<nexthop %s>' % (self.ip_address)
-
- def __str__(self):
- return str(self.ip_address)
-
- @classmethod
- def from_bytes(cls, value):
- """Decodes bgp path-attribute with type-code 3, i.e. NEXT_HOP.
- """
- value = socket.inet_ntoa(value)
- return cls(value)
-
- def packvalue(self):
- return socket.inet_aton(self._ip_address)
-
-
-@_register_path_attr
-class IntAttr(RcgPathAttr):
- """Super class of all bgp path-attribute whose value is an unsigned
- integer.
- """
- __slots__ = ('_value')
-
- def __init__(self, value):
- if not value:
- value = 0
- self._value = value
- RcgPathAttr.__init__(self)
-
- @property
- def value(self):
- return self._value
-
- def __repr__(self):
- return '<%s(%d)>' % (self.__class__.__name__, self.value)
-
- def __str__(self):
- return str(self.value)
-
- @classmethod
- def from_bytes(cls, val):
- """Decode bgp path-attributes whose value is an unsigned integer.
- """
- value, = struct.unpack_from('!I', val)
- return cls(value)
-
- def packvalue(self):
- return struct.pack('!I', self.value)
-
-
-@_register_path_attr
-class Med(IntAttr):
- """MED is optional non-transitive bgp path-attribute."""
- __slots__ = ()
- TYPE_CODE = 4
- ATTR_NAME = 'med'
- # Higher order bits: 100 - optional, non-transitive, complete
- FLAGS = OPTIONAL_BGP_ATTR
-
- def __init__(self, value):
- IntAttr.__init__(self, value)
-
-
-@_register_path_attr
-class LocalPref(IntAttr):
- """LOCAL_PREF is a well-known discretionary attribute."""
- __slots__ = ()
- TYPE_CODE = 5
- ATTR_NAME = 'localpref'
- # Higher order bits: 010 - well-known, transitive, complete
- FLAGS = TRANSITIVE_BGP_ATTR
-
- def __init__(self, value):
- IntAttr.__init__(self, value)
-
-
-@_register_path_attr
-class Originator(RcgPathAttr):
- """ORIGINATOR_ID is a optional non-transitive attribute."""
- __slots__ = ('_value')
- TYPE_CODE = 9
- ATTR_NAME = 'originator'
- FLAGS = OPTIONAL_BGP_ATTR
-
- def __init__(self, value):
- RcgPathAttr.__init__(self)
- self._value = value
-
- @property
- def value(self):
- return self._value
-
- @classmethod
- def from_bytes(cls, val):
- """Decodes bgp path-attribute with type code 9, i.e. ORIGINATOR_ID.
- """
- if len(val) == 4:
- value = socket.inet_ntoa(val)
- else:
- raise Exception('Invalid originator')
-
- return cls(value)
-
- def packvalue(self):
- return socket.inet_aton(self.value)
-
-
-@_register_path_attr
-class ClusterList(RcgPathAttr):
- """CLUSTER_LIST is a optional non-transitive bgp path-attribute.
- """
- __slots__ = ('_cluster_list')
- TYPE_CODE = 10
- ATTR_NAME = 'cluster-list'
- FLAGS = OPTIONAL_BGP_ATTR
-
- def __init__(self, cluster_list):
- if not cluster_list:
- raise ValueError('Invalid cluster list.')
- # TODO(PH): add more validation of input here.
- RcgPathAttr.__init__(self)
- self._cluster_list = cluster_list
-
- @property
- def cluster_list(self):
- return self._cluster_list
-
- @classmethod
- def from_bytes(cls, val):
- """Decodes bgp path-attribute with type-code 10, i.e. CLUSTER_LIST.
- """
- cluster_list = []
- iidx = 0
- while iidx < len(val):
- cluster_list.append(
- socket.inet_ntoa(struct.unpack_from('4s', val, iidx)[0])
- )
- iidx += 4
- return cls(cluster_list)
-
- def packvalue(self):
- valueb = ''
- for c in self.cluster_list:
- valueb += socket.inet_aton(c)
- return valueb
-
-
-@_register_path_attr
-class MpReachNlri(RcgPathAttr):
- """MP_REACH_NLRI is a optional non-transitive bgp path-attribute.
- """
- __slots__ = ('_route_family', '_next_hop', '_nlri_list', '_reserved')
- TYPE_CODE = 14
- ATTR_NAME = 'mp-reach-nlri'
- NEXT_HOP = 'nh'
- NLRI = 'nlri'
- RESERVED = 'reserved'
- # Higher order bits: 100 - optional, non-transitive, complete
- FLAGS = OPTIONAL_BGP_ATTR
-
- def __init__(self, route_family, next_hop, nlri_list, reserved=None):
- if not (hasattr(route_family, 'afi') and
- hasattr(route_family, 'safi')):
- raise ValueError('Invalid parameter value for route_family %s.' %
- route_family)
-
- if not next_hop:
- raise ValueError('Invalid next_hop %s' % next_hop)
-
- # MpReachNlri attribute should have next-hop belonging to same
- # route-family
- if ((route_family == RF_IPv4_VPN and not is_valid_ipv4(next_hop)) or
- (route_family == RF_IPv6_VPN and not is_valid_ipv6(next_hop))):
- raise ValueError('Next hop should belong to %s route family' %
- route_family)
-
- if not nlri_list:
- nlri_list = []
-
- RcgPathAttr.__init__(self)
- self._route_family = route_family
- self._next_hop = next_hop
- self._nlri_list = nlri_list
- self._reserved = reserved
-
- @property
- def route_family(self):
- return self._route_family
-
- @property
- def next_hop(self):
- return self._next_hop
-
- @property
- def nlri_list(self):
- return self._nlri_list[:]
-
- @property
- def reserved(self):
- return self._reserved
-
- def __repr__(self):
- return '<MpReachNlri route_family=%r next_hop=%r nlri_list=%r>' % (
- self.route_family, self.next_hop, self._nlri_list)
-
- @classmethod
- def from_bytes(cls, val):
- """Decodes bgp path-attribute with type code 14, i.e MP_REACH_NLRI.
- """
- afi, safi, nhlen = struct.unpack_from('!HBB', val)
- fmt = '%dsB' % (nhlen,)
- next_hop, reserved = struct.unpack_from(fmt, val, 4)
-
- if afi == 1 and safi is 128:
- # Vpnv4
- _, _, nhip = struct.unpack('!II4s', next_hop)
- next_hop = socket.inet_ntop(socket.AF_INET, nhip)
- elif afi == 1 and safi == 132:
- # RtNlri
- nhip, = struct.unpack('!4s', next_hop)
- next_hop = socket.inet_ntop(socket.AF_INET, nhip)
- elif afi == 2 and safi == 128:
- # Vpnv6
- _, _, nhip = struct.unpack('!II16s', next_hop)
- next_hop = socket.inet_ntop(socket.AF_INET6, nhip)
- else:
- LOG.error('Received NLRI for afi/safi (%s/%s), which is not'
- ' supported yet!' % (afi, safi))
- raise OptAttrError()
-
- n_nlri = nlri.parse(val[5 + nhlen:], afi, safi)
- route_family = get_rf(afi, safi)
- return cls(route_family, next_hop, n_nlri, reserved)
-
- def packvalue(self):
- afi = self._route_family.afi
- safi = self._route_family.safi
- if self._route_family == RF_IPv4_VPN:
- next_hop = '\0' * 8
- next_hop += socket.inet_aton(self.next_hop)
- elif self._route_family == RF_RTC_UC:
- next_hop = socket.inet_aton(self.next_hop)
- elif self._route_family == RF_IPv6_VPN:
- next_hop = '\0' * 8
- next_hop += socket.inet_pton(socket.AF_INET6, self.next_hop)
- else:
- next_hop = self.next_hop
-
- valueb = struct.pack('!HBB', afi, safi, len(next_hop))
- valueb += next_hop
- valueb += chr(self.reserved or 0)
-
- for n_nlri in self._nlri_list:
- valueb += n_nlri.encode()
- return valueb
-
-
-@_register_path_attr
-class MpUnreachNlri(RcgPathAttr):
- """MP_UNREACH_NLRI is a optional non-transitive bgp path-attribute.
- """
- __slots__ = ('_route_family', '_nlri_list')
- TYPE_CODE = 15
- ATTR_NAME = 'mp-unreach-nlri'
- NLRI = 'withdraw_nlri'
- # Higher order bits: 100 - optional, non-transitive, complete
- FLAGS = OPTIONAL_BGP_ATTR
-
- def __init__(self, route_family, nlri_list):
- if not (hasattr(route_family, 'afi') and
- hasattr(route_family, 'safi')):
- raise ValueError('Invalid parameter value for route_family %s' %
- route_family)
- if not nlri_list:
- nlri_list = []
-
- RcgPathAttr.__init__(self)
- self._route_family = route_family
- self._nlri_list = nlri_list
-
- @property
- def nlri_list(self):
- return self._nlri_list[:]
-
- @property
- def route_family(self):
- return self._route_family
-
- def __repr__(self):
- return '<MpUneachNlri route_family=%r nlri_list=%r>' % (
- self._route_family, self._nlri_list)
-
- @classmethod
- def from_bytes(cls, val):
- """Decodes bgp path-attribute of type-code 15, i.e. MP_UNREACH_NLRI.
- """
- afi, safi = struct.unpack_from('!HB', val)
- route_family = get_rf(afi, safi)
- w_nlri = nlri.parse(val[3:], afi, safi)
- return cls(route_family, w_nlri)
-
- def packvalue(self):
- afi = self._route_family.afi
- safi = self._route_family.safi
-
- valueb = struct.pack('!HB', afi, safi)
-
- for w_nlri in self._nlri_list:
- valueb += w_nlri.encode()
- return valueb
-
-
-@_register_path_attr
-class Community(RcgPathAttr):
- """COMMUNITY is a optional transitive bgp path-attribute.
- """
- __slots__ = ('_attr_list')
- TYPE_CODE = 8
- ATTR_NAME = 'community'
- FLAGS = TRANSITIVE_BGP_ATTR | OPTIONAL_BGP_ATTR
-
- # String constants of well-known-communities
- NO_EXPORT = int('0xFFFFFF01', 16)
- NO_ADVERTISE = int('0xFFFFFF02', 16)
- NO_EXPORT_SUBCONFED = int('0xFFFFFF03', 16)
- WELL_KNOW_COMMUNITIES = (NO_EXPORT, NO_ADVERTISE, NO_EXPORT_SUBCONFED)
-
- def __init__(self, *attrs):
- if not attrs:
- raise ValueError('Invalid parameter for community attribute '
- 'list %r.' % attrs)
- self._attr_list = []
- for attr in attrs:
- if not isinstance(attr, int):
- raise ValueError('Invalid community attribute value %s.' %
- attr)
- self._attr_list.append(attr)
-
- RcgPathAttr.__init__(self)
-
- @property
- def attr_list(self):
- return self._attr_list[:]
-
- @classmethod
- def from_bytes(cls, val):
- """Decodes path attribute of type code 8, i.e. Community attribute.
- """
- att_list = []
- iidx = 0
- while iidx < len(val):
- comm_attr, = struct.unpack_from('!I', val, iidx)
- att_list.append(comm_attr)
- iidx += 4
- return cls(*att_list)
-
- def packvalue(self):
- commu_attr = ''
- for attr in self._attr_list:
- commu_attr += struct.pack('!I', int(attr))
- return commu_attr
-
- @staticmethod
- def is_no_export(comm_attr):
- """Returns True if given value matches well-known community NO_EXPORT
- attribute value.
- """
- return comm_attr == Community.NO_EXPORT
-
- @staticmethod
- def is_no_advertise(comm_attr):
- """Returns True if given value matches well-known community
- NO_ADVERTISE attribute value.
- """
- return comm_attr == Community.NO_ADVERTISE
-
- @staticmethod
- def is_no_export_subconfed(comm_attr):
- """Returns True if given value matches well-known community
- NO_EXPORT_SUBCONFED attribute value.
- """
- return comm_attr == Community.NO_EXPORT_SUBCONFED
-
- def has_comm_attr(self, attr):
- """Returns True if given community attribute is present."""
-
- for comm_attr in self._attr_list:
- if comm_attr == attr:
- return True
-
- return False
-
- def _community_repr(self, comm_attr):
- """Matches given value with all well-known community attribute values.
-
- Returns string representation of the well-known attribute if we
- have a match else returns given value.
- """
-
- if self.is_no_export(comm_attr):
- return 'NO_EXPORT'
- elif self.is_no_advertise(comm_attr):
- return 'NO_ADVERTISE'
- elif self.is_no_export_subconfed(comm_attr):
- return 'NO_EXPORT_SUBCONFED'
- return (str(comm_attr >> 16) + ':' +
- str(comm_attr & int('0x0000ffff', 16)))
-
- def __repr__(self):
- attr_list_repr = (','.join([self._community_repr(ca)
- for ca in self._attr_list]))
- return ('<Community([%s])>' % attr_list_repr)
-
-
-@_register_path_attr
-class ExtCommunity(RcgPathAttr):
- """EXTENDED COMMUNITIES is a optional and transitive bgp path-attribute.
- """
- __slots__ = ('_rt_list', '_soo_list', '_unknowns')
- TYPE_CODE = 16
- ATTR_NAME = 'extcommunity'
- RT = 'route_target'
- SOO = 'site_of_origin'
- UNKNOWN = 'unknown_community'
- # Higher order bits: 110 - optional, transitive, complete
- FLAGS = TRANSITIVE_BGP_ATTR | OPTIONAL_BGP_ATTR
-
- def __str__(self):
- return 'rt_list: {0}, soo_list: {1}'.format(
- self.rt_list,
- self.soo_list
- )
-
- def __init__(self, rt_list, soo_list, unknowns=None):
- if not rt_list and not soo_list:
- raise ValueError('Have to provide at-least one RT/SOO attribute.')
- if not rt_list:
- rt_list = []
- if not soo_list:
- soo_list = []
- if not unknowns:
- unknowns = {}
-
- ExtCommunity.validate_supported_attributes(rt_list)
- ExtCommunity.validate_supported_attributes(soo_list)
-
- RcgPathAttr.__init__(self)
- self._rt_list = list(rt_list)
- self._soo_list = list(soo_list)
- self._unknowns = unknowns
-
- @property
- def rt_list(self):
- """Returns a list of extracted/configured route target community."""
- # TODO(PH): Make sure we do not raise Exception here but return empty
- # list instead.
- return self._rt_list[:]
-
- @property
- def soo_list(self):
- """Returns route origin community."""
- return self._soo_list[:]
-
- def __repr__(self):
- return '<%s type/num=%s/%s flags %s, rts: %s, soo: %s>' % (
- self.__class__.__name__, self.__class__.ATTR_NAME,
- self.__class__.TYPE_CODE,
- self.str_flags(), self.rt_list, self.soo_list)
-
- def has_unknown_communities(self):
- """Returns True if we have extracted/configured community other than
- route target or route origin community.
- """
- return True if self._unknowns else False
-
- @classmethod
- def validate_supported_attributes(cls, attr_list):
- """Validate *attr_list* has all valid RTs or SOO attribute
- representations.
-
- RTs and SOO are represented as string in following format:
- *global_admin_part:local_admin_part*
- """
- for attr in attr_list:
- if not validation.is_valid_ext_comm_attr(attr):
- raise ValueError('Attribute %s is not a valid RT/SOO' % attr)
-
- @classmethod
- def from_bytes(cls, val):
- """Decodes ext-community path-attribute.
- """
- rt_list = []
- soo_list = []
- unknowns = {}
- iidx = 0
- while iidx < len(val):
- etype, esubtype, payload = struct.unpack_from('BB6s', val, iidx)
- # RFC says: The value of the high-order octet of the Type field for
- # the Route Target Community can be 0x00, 0x01, or 0x02. The value
- # of the low-order octet of the Type field for this community is
- # 0x02. TODO(PH): Remove this exception when it breaks something
- # Here we make exception as Routem packs lower-order octet as 0x00
- if etype in (0, 2) and esubtype in (0, 2):
- # If we have route target community in AS number format.
- asnum, i = struct.unpack('!HI', payload)
- rt_list.append('%s:%s' % (asnum, i))
- elif etype == 1 and esubtype == 2:
- # If we have route target community in IP address format.
- ip_addr, i = struct.unpack('!4sH', payload)
- ip_addr = socket.inet_ntoa(ip_addr)
- rt_list.append('%s:%s' % (ip_addr, i))
- elif etype in (0, 2) and esubtype == 3:
- # If we have route origin community in AS number format.
- asnum, nnum = struct.unpack('!HI', payload)
- soo_list.append('%s:%s' % (asnum, nnum))
- elif etype == 1 and esubtype == 3:
- # If we have route origin community in IP address format.
- ip_addr, nnum = struct.unpack('!4sH', payload)
- ip_addr = socket.inet_ntoa(ip_addr)
- soo_list.append('%s:%s' % (ip_addr, nnum))
- else:
- # All other communities, other than RT and SOO are unknown.
- unknown_list = unknowns.get(etype)
- if unknown_list is None:
- unknown_list = []
- unknowns[etype] = unknown_list
- unknown_list.append(
- '%s:%s' % (etype, val[iidx + 1:iidx + 8].encode('hex'))
- )
- iidx += 8
-
- return cls(rt_list, soo_list, unknowns)
-
- def packvalue(self):
- excomb = ''
- # Pack route target community attrs.
- for route_target in self._rt_list:
- first, second = route_target.split(':')
- if '.' in first:
- ip_addr = socket.inet_aton(first)
- excomb += struct.pack('!BB4sH', 1, 2, ip_addr,
- int(second))
- else:
- excomb += struct.pack('!BBHI', 0, 2, int(first),
- int(second))
- # Pack route origin community attrs.
- for route_origin in self._soo_list:
- first, second = route_origin.split(':')
- if '.' in first:
- ip_addr = socket.inet_aton(first)
- excomb += struct.pack('!BB4sH', 1, 3, ip_addr,
- int(second))
- else:
- excomb += struct.pack('!BBHI', 0, 3, int(first),
- int(second))
- for type, attr_list in self._unknowns.items():
- # Pack all unknown ext. attrs.
- excomb += struct.pack('B', int(type))
- excomb += attr_list.decode('hex')
- return excomb