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>
This commit is contained in:
ISHIDA Wataru 2014-04-16 04:14:12 +00:00 committed by FUJITA Tomonori
parent 9d5e66fa4e
commit 660c8e68b4
6 changed files with 0 additions and 3089 deletions

View File

@ -1,7 +0,0 @@
try:
from collections import OrderedDict
except ImportError:
from ordereddict import OrderedDict
# Pointer to active/available OrderedDict.
OrderedDict = OrderedDict

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff