bgp: add stuff for integration with bgp speaker

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 03:30:33 +00:00 committed by FUJITA Tomonori
parent a1fe3d92db
commit bdad6267de

View File

@ -25,23 +25,69 @@ RFC 4271 BGP-4
import abc import abc
import six import six
import struct import struct
import copy
from ryu.ofproto.ofproto_parser import msg_pack_into from ryu.ofproto.ofproto_parser import msg_pack_into
from ryu.lib.stringify import StringifyMixin from ryu.lib.stringify import StringifyMixin
from ryu.lib.packet import afi
from ryu.lib.packet import safi
from ryu.lib.packet import packet_base from ryu.lib.packet import packet_base
from ryu.lib.packet import stream_parser from ryu.lib.packet import stream_parser
from ryu.lib import addrconv from ryu.lib import addrconv
import safi
import afi
BGP_MSG_OPEN = 1 BGP_MSG_OPEN = 1
BGP_MSG_UPDATE = 2 BGP_MSG_UPDATE = 2
BGP_MSG_NOTIFICATION = 3 BGP_MSG_NOTIFICATION = 3
BGP_MSG_KEEPALIVE = 4 BGP_MSG_KEEPALIVE = 4
BGP_MSG_ROUTE_REFRESH = 5 # RFC 2918 BGP_MSG_ROUTE_REFRESH = 5 # RFC 2918
_VERSION = 4
_MARKER = 16 * '\xff'
BGP_OPT_CAPABILITY = 2 # RFC 5492
BGP_CAP_MULTIPROTOCOL = 1 # RFC 4760
BGP_CAP_ROUTE_REFRESH = 2 # RFC 2918
BGP_CAP_CARRYING_LABEL_INFO = 4 # RFC 3107
BGP_CAP_FOUR_OCTET_AS_NUMBER = 65 # RFC 4893
BGP_CAP_ENHANCED_ROUTE_REFRESH = 70 # https://tools.ietf.org/html/\
# draft-ietf-idr-bgp-enhanced-route-refresh-05
BGP_ATTR_FLAG_OPTIONAL = 1 << 7
BGP_ATTR_FLAG_TRANSITIVE = 1 << 6
BGP_ATTR_FLAG_PARTIAL = 1 << 5
BGP_ATTR_FLAG_EXTENDED_LENGTH = 1 << 4
BGP_ATTR_TYPE_ORIGIN = 1 # 0,1,2 (1 byte)
BGP_ATTR_TYPE_AS_PATH = 2 # a list of AS_SET/AS_SEQUENCE eg. {1 2 3} 4 5
BGP_ATTR_TYPE_NEXT_HOP = 3 # an IPv4 address
BGP_ATTR_TYPE_MULTI_EXIT_DISC = 4 # uint32 metric
BGP_ATTR_TYPE_LOCAL_PREF = 5 # uint32
BGP_ATTR_TYPE_ATOMIC_AGGREGATE = 6 # 0 bytes
BGP_ATTR_TYPE_AGGREGATOR = 7 # AS number and IPv4 address
BGP_ATTR_TYPE_COMMUNITIES = 8 # RFC 1997
BGP_ATTR_TYPE_MP_REACH_NLRI = 14 # RFC 4760
BGP_ATTR_TYPE_MP_UNREACH_NLRI = 15 # RFC 4760
BGP_ATTR_TYPE_EXTENDED_COMMUNITIES = 16 # RFC 4360
BGP_ATTR_TYPE_AS4_PATH = 17 # RFC 4893
BGP_ATTR_TYPE_AS4_AGGREGATOR = 18 # RFC 4893
BGP_ATTR_ORIGIN_IGP = 0x00
BGP_ATTR_ORIGIN_EGP = 0x01
BGP_ATTR_ORIGIN_INCOMPLETE = 0x02
AS_TRANS = 23456 # RFC 4893
# Well known commmunities (RFC 1997)
BGP_COMMUNITY_NO_EXPORT = 0xffffff01
BGP_COMMUNITY_NO_ADVERTISE = 0xffffff02
BGP_COMMUNITY_NO_EXPORT_SUBCONFED = 0xffffff03
# RFC 4360
# The low-order octet of Type field (subtype)
BGP_EXTENDED_COMMUNITY_ROUTE_TARGET = 0x02
BGP_EXTENDED_COMMUNITY_ROUTE_ORIGIN = 0x03
# NOTIFICATION Error Code and SubCode # NOTIFICATION Error Code and SubCode
# Note: 0 is a valid SubCode. (Unspecific) # Note: 0 is a valid SubCode. (Unspecific)
@ -63,7 +109,7 @@ BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER = 1 # Data: 2 octet version number
BGP_ERROR_SUB_BAD_PEER_AS = 2 BGP_ERROR_SUB_BAD_PEER_AS = 2
BGP_ERROR_SUB_BAD_BGP_IDENTIFIER = 3 BGP_ERROR_SUB_BAD_BGP_IDENTIFIER = 3
BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER = 4 BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER = 4
# 5 is deprecated RFC 1771 Authentication Failure BGP_ERROR_SUB_AUTHENTICATION_FAILURE = 5 # deprecated RFC 1771
BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME = 6 BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME = 6
# NOTIFICATION Error Subcode for BGP_ERROR_UPDATE_MESSAGE_ERROR # NOTIFICATION Error Subcode for BGP_ERROR_UPDATE_MESSAGE_ERROR
@ -73,12 +119,18 @@ BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE = 3 # Data: ditto
BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR = 4 # Data: the attr (type, len, value) BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR = 4 # Data: the attr (type, len, value)
BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR = 5 # Data: ditto BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR = 5 # Data: ditto
BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE = 6 # Data: ditto BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE = 6 # Data: ditto
# 7 is deprecated RFC 1771 AS Routing Loop BGP_ERROR_SUB_ROUTING_LOOP = 7 # deprecated RFC 1771 AS Routing Loop
BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE = 8 # Data: ditto BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE = 8 # Data: ditto
BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR = 9 # Data: ditto BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR = 9 # Data: ditto
BGP_ERROR_SUB_INVALID_NETWORK_FIELD = 10 BGP_ERROR_SUB_INVALID_NETWORK_FIELD = 10
BGP_ERROR_SUB_MALFORMED_AS_PATH = 11 BGP_ERROR_SUB_MALFORMED_AS_PATH = 11
# NOTIFICATION Error Subcode for BGP_ERROR_HOLD_TIMER_EXPIRED
BGP_ERROR_SUB_HOLD_TIMER_EXPIRED = 1
# NOTIFICATION Error Subcode for BGP_ERROR_FSM_ERROR
BGP_ERROR_SUB_FSM_ERROR = 1
# NOTIFICATION Error Subcode for BGP_ERROR_CEASE (RFC 4486) # NOTIFICATION Error Subcode for BGP_ERROR_CEASE (RFC 4486)
BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED = 1 # Data: optional BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED = 1 # Data: optional
BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN = 2 BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN = 2
@ -89,48 +141,369 @@ BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE = 6
BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION = 7 BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION = 7
BGP_ERROR_SUB_OUT_OF_RESOURCES = 8 BGP_ERROR_SUB_OUT_OF_RESOURCES = 8
_VERSION = 4
_MARKER = 16 * '\xff'
BGP_OPT_CAPABILITY = 2 # RFC 5492 class BgpExc(Exception):
"""Base bgp exception."""
BGP_CAP_MULTIPROTOCOL = 1 # RFC 4760 CODE = 0
BGP_CAP_ROUTE_REFRESH = 2 # RFC 2918 """BGP error code."""
BGP_CAP_CARRYING_LABEL_INFO = 4 # RFC 3107
BGP_CAP_FOUR_OCTET_AS_NUMBER = 65 # RFC 4893
BGP_CAP_ENHANCED_ROUTE_REFRESH = 70 # https://tools.ietf.org/html/\
# draft-ietf-idr-bgp-enhanced-route-refresh-05
BGP_ATTR_FLAG_OPTIONAL = 1 << 7 SUB_CODE = 0
BGP_ATTR_FLAG_TRANSITIVE = 1 << 6 """BGP error sub-code."""
BGP_ATTR_FLAG_PARTIAL = 1 << 5
BGP_ATTR_FLAG_EXTENDED_LENGTH = 1 << 4
BGP_ATTR_TYPE_ORIGIN = 1 # 0,1,2 (1 byte) SEND_ERROR = True
BGP_ATTR_TYPE_AS_PATH = 2 # a list of AS_SET/AS_SEQUENCE eg. {1 2 3} 4 5 """Flag if set indicates Notification message should be sent to peer."""
BGP_ATTR_TYPE_NEXT_HOP = 3 # an IPv4 address
BGP_ATTR_TYPE_MULTI_EXIT_DISC = 4 # uint32 metric
BGP_ATTR_TYPE_LOCAL_PREF = 5 # uint32
BGP_ATTR_TYPE_ATOMIC_AGGREGATE = 6 # 0 bytes
BGP_ATTR_TYPE_AGGREGATOR = 7 # AS number and IPv4 address
BGP_ATTR_TYPE_COMMUNITIES = 8 # RFC 1997
BGP_ATTR_TYPE_MP_REACH_NLRI = 14 # RFC 4760
BGP_ATTR_TYPE_MP_UNREACH_NLRI = 15 # RFC 4760
BGP_ATTR_TYPE_EXTENDED_COMMUNITIES = 16 # RFC 4360
BGP_ATTR_TYPE_AS4_PATH = 17 # RFC 4893
BGP_ATTR_TYPE_AS4_AGGREGATOR = 18 # RFC 4893
AS_TRANS = 23456 # RFC 4893 def __init__(self, data=''):
self.data = data
# Well known commmunities (RFC 1997) def __str__(self):
BGP_COMMUNITY_NO_EXPORT = 0xffffff01 return '<%s %r>' % (self.__class__.__name__, self.data)
BGP_COMMUNITY_NO_ADVERTISE = 0xffffff02
BGP_COMMUNITY_NO_EXPORT_SUBCONFED = 0xffffff03
# RFC 4360
# The low-order octet of Type field (subtype) class BadNotification(BgpExc):
BGP_EXTENDED_COMMUNITY_ROUTE_TARGET = 0x02 SEND_ERROR = False
BGP_EXTENDED_COMMUNITY_ROUTE_ORIGIN = 0x03
# ============================================================================
# Message Header Errors
# ============================================================================
class NotSync(BgpExc):
CODE = BGP_ERROR_MESSAGE_HEADER_ERROR
SUB_CODE = BGP_ERROR_SUB_CONNECTION_NOT_SYNCHRONIZED
class BadLen(BgpExc):
CODE = BGP_ERROR_MESSAGE_HEADER_ERROR
SUB_CODE = BGP_ERROR_SUB_BAD_MESSAGE_LENGTH
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 = BGP_ERROR_MESSAGE_HEADER_ERROR
SUB_CODE = BGP_ERROR_SUB_BAD_MESSAGE_TYPE
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 = BGP_ERROR_OPEN_MESSAGE_ERROR
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 = BGP_ERROR_OPEN_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER
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 = BGP_ERROR_OPEN_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_BAD_PEER_AS
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 = BGP_ERROR_OPEN_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_BAD_BGP_IDENTIFIER
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 = BGP_ERROR_OPEN_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER
class AuthFailure(BgpExc):
CODE = BGP_ERROR_OPEN_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_AUTHENTICATION_FAILURE
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 = BGP_ERROR_OPEN_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME
# ============================================================================
# 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 = BGP_ERROR_UPDATE_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST
class UnRegWellKnowAttr(BgpExc):
CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE
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 = BGP_ERROR_UPDATE_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE
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 = BGP_ERROR_UPDATE_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR
class AttrLenError(BgpExc):
CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR
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 = BGP_ERROR_UPDATE_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE
class RoutingLoop(BgpExc):
CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_ROUTING_LOOP
class InvalidNextHop(BgpExc):
CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE
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 = BGP_ERROR_UPDATE_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR
class InvalidNetworkField(BgpExc):
CODE = BGP_ERROR_UPDATE_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_INVALID_NETWORK_FIELD
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 = BGP_ERROR_UPDATE_MESSAGE_ERROR
SUB_CODE = BGP_ERROR_SUB_MALFORMED_AS_PATH
# ============================================================================
# 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 = BGP_ERROR_HOLD_TIMER_EXPIRED
SUB_CODE = BGP_ERROR_SUB_HOLD_TIMER_EXPIRED
# ============================================================================
# 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 = BGP_ERROR_FSM_ERROR
SUB_CODE = BGP_ERROR_SUB_FSM_ERROR
# ============================================================================
# Cease Errors
# ============================================================================
class MaxPrefixReached(BgpExc):
CODE = BGP_ERROR_CEASE
SUB_CODE = BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED
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 = BGP_ERROR_CEASE
SUB_CODE = BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN
class PeerDeConfig(BgpExc):
CODE = BGP_ERROR_CEASE
SUB_CODE = BGP_ERROR_SUB_PEER_DECONFIGURED
class AdminReset(BgpExc):
CODE = BGP_ERROR_CEASE
SUB_CODE = BGP_ERROR_SUB_ADMINISTRATIVE_RESET
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 = BGP_ERROR_CEASE
SUB_CODE = BGP_ERROR_SUB_CONNECTION_RESET
class OtherConfChange(BgpExc):
CODE = BGP_ERROR_CEASE
SUB_CODE = BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE
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 = BGP_ERROR_CEASE
SUB_CODE = BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION
class OutOfResource(BgpExc):
CODE = BGP_ERROR_CEASE
SUB_CODE = BGP_ERROR_SUB_OUT_OF_RESOURCES
class RouteFamily(StringifyMixin):
def __init__(self, afi, safi):
self.afi = afi
self.safi = safi
def __cmp__(self, other):
return cmp((other.afi, other.safi), (self.afi, self.safi))
# Route Family Singleton
RF_IPv4_UC = RouteFamily(afi.IP, safi.UNICAST)
RF_IPv6_UC = RouteFamily(afi.IP6, safi.UNICAST)
RF_IPv4_VPN = RouteFamily(afi.IP, safi.MPLS_VPN)
RF_IPv6_VPN = RouteFamily(afi.IP6, safi.MPLS_VPN)
RF_RTC_UC = RouteFamily(afi.IP, safi.ROUTE_TARGET_CONSTRTAINS)
def pad(bin, len_): def pad(bin, len_):
@ -288,6 +661,23 @@ class _IPAddrPrefix(_AddrPrefix):
return (addrconv.ipv4.bin_to_text(pad(addr, 4)),) return (addrconv.ipv4.bin_to_text(pad(addr, 4)),)
class _IP6AddrPrefix(_AddrPrefix):
_TYPE = {
'ascii': [
'addr'
]
}
@staticmethod
def _prefix_to_bin(addr):
(addr,) = addr
return addrconv.ipv6.text_to_bin(addr)
@staticmethod
def _prefix_from_bin(addr):
return (addrconv.ipv6.bin_to_text(pad(addr, 16)),)
class _VPNAddrPrefix(_AddrPrefix): class _VPNAddrPrefix(_AddrPrefix):
_RD_PACK_STR = '!Q' _RD_PACK_STR = '!Q'
@ -321,18 +711,125 @@ class _VPNAddrPrefix(_AddrPrefix):
return (rd,) + super(_VPNAddrPrefix, cls)._prefix_from_bin(binrest) return (rd,) + super(_VPNAddrPrefix, cls)._prefix_from_bin(binrest)
class IPAddrPrefix(_UnlabelledAddrPrefix, _IPAddrPrefix):
ROUTE_FAMILY = RF_IPv4_UC
@property
def prefix(self):
return self.addr
class IP6AddrPrefix(_UnlabelledAddrPrefix, _IP6AddrPrefix):
ROUTE_FAMILY = RF_IPv6_UC
@property
def prefix(self):
return self.addr
class LabelledVPNIPAddrPrefix(_LabelledAddrPrefix, _VPNAddrPrefix, class LabelledVPNIPAddrPrefix(_LabelledAddrPrefix, _VPNAddrPrefix,
_IPAddrPrefix): _IPAddrPrefix):
pass ROUTE_FAMILY = RF_IPv4_VPN
class IPAddrPrefix(_UnlabelledAddrPrefix, _IPAddrPrefix): class LabelledVPNIP6AddrPrefix(_LabelledAddrPrefix, _VPNAddrPrefix,
pass _IP6AddrPrefix):
ROUTE_FAMILY = RF_IPv6_VPN
class RouteTargetMembershipNLRI(StringifyMixin):
"""Route Target Membership NLRI.
Route Target membership NLRI is advertised in BGP UPDATE messages using
the MP_REACH_NLRI and MP_UNREACH_NLRI attributes.
"""
ROUTE_FAMILY = RF_RTC_UC
DEFAULT_AS = '0:0'
DEFAULT_RT = '0:0'
def __init__(self, origin_as, route_target):
# 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 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 __cmp__(self, other):
return cmp(
(self._origin_as, self._route_target),
(other.origin_as, other.route_target),
)
@classmethod
def parser(cls, buf):
idx = 0
# Extract origin AS.
origin_as, = struct.unpack_from('!I', buf, idx)
idx += 4
# Extract route target.
route_target = ''
etype, esubtype, payload = struct.unpack_from('BB6s', buf, 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 serialize(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
_addr_class_key = lambda x: (x.afi, x.safi)
_ADDR_CLASSES = { _ADDR_CLASSES = {
(afi.IP, safi.UNICAST): IPAddrPrefix, _addr_class_key(RF_IPv4_UC): IPAddrPrefix,
(afi.IP, safi.MPLS_VPN): LabelledVPNIPAddrPrefix, _addr_class_key(RF_IPv6_UC): IP6AddrPrefix,
_addr_class_key(RF_IPv4_VPN): LabelledVPNIPAddrPrefix,
_addr_class_key(RF_IPv6_VPN): LabelledVPNIP6AddrPrefix,
_addr_class_key(RF_RTC_UC): RouteTargetMembershipNLRI,
} }
@ -417,7 +914,7 @@ class _OptParam(StringifyMixin, _TypeDisp, _Value):
type_ = self._rev_lookup_type(self.__class__) type_ = self._rev_lookup_type(self.__class__)
self.type = type_ self.type = type_
self.length = length self.length = length
if not value is None: if value is not None:
self.value = value self.value = value
@classmethod @classmethod
@ -463,9 +960,9 @@ class _OptParamCapability(_OptParam, _TypeDisp):
if cap_code is None: if cap_code is None:
cap_code = self._rev_lookup_type(self.__class__) cap_code = self._rev_lookup_type(self.__class__)
self.cap_code = cap_code self.cap_code = cap_code
if not cap_value is None: if cap_value is not None:
self.cap_value = cap_value self.cap_value = cap_value
if not cap_length is None: if cap_length is not None:
self.cap_length = cap_length self.cap_length = cap_length
@classmethod @classmethod
@ -516,6 +1013,11 @@ class BGPOptParamCapabilityRouteRefresh(_OptParamEmptyCapability):
pass pass
@_OptParamCapability.register_type(BGP_CAP_ENHANCED_ROUTE_REFRESH)
class BGPOptParamCapabilityEnhancedRouteRefresh(_OptParamEmptyCapability):
pass
@_OptParamCapability.register_type(BGP_CAP_FOUR_OCTET_AS_NUMBER) @_OptParamCapability.register_type(BGP_CAP_FOUR_OCTET_AS_NUMBER)
class BGPOptParamCapabilityFourOctetAsNumber(_OptParamCapability): class BGPOptParamCapabilityFourOctetAsNumber(_OptParamCapability):
_CAP_PACK_STR = '!I' _CAP_PACK_STR = '!I'
@ -586,7 +1088,7 @@ class _PathAttribute(StringifyMixin, _TypeDisp, _Value):
self.flags = flags self.flags = flags
self.type = type_ self.type = type_
self.length = length self.length = length
if not value is None: if value is not None:
self.value = value self.value = value
@classmethod @classmethod
@ -607,7 +1109,7 @@ class _PathAttribute(StringifyMixin, _TypeDisp, _Value):
def serialize(self): def serialize(self):
# fixup # fixup
if not self._ATTR_FLAGS is None: if self._ATTR_FLAGS is not None:
self.flags = self.flags \ self.flags = self.flags \
& ~(BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE) \ & ~(BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE) \
| self._ATTR_FLAGS | self._ATTR_FLAGS
@ -655,6 +1157,41 @@ class _BGPPathAttributeAsPathCommon(_PathAttribute):
_AS_PACK_STR = None _AS_PACK_STR = None
_ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE
@property
def path_seg_list(self):
return copy.deepcopy(self.value)
def get_as_path_len(self):
count = 0
for seg in self.value:
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.value:
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.value 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
@classmethod @classmethod
def parse_value(cls, buf): def parse_value(cls, buf):
result = [] result = []
@ -808,6 +1345,12 @@ class BGPPathAttributeCommunities(_PathAttribute):
_VALUE_PACK_STR = '!I' _VALUE_PACK_STR = '!I'
_ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE
# 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, communities, def __init__(self, communities,
flags=0, type_=None, length=None): flags=0, type_=None, length=None):
super(BGPPathAttributeCommunities, self).__init__(flags=flags, super(BGPPathAttributeCommunities, self).__init__(flags=flags,
@ -836,6 +1379,36 @@ class BGPPathAttributeCommunities(_PathAttribute):
buf += bincomm buf += bincomm
return buf return buf
@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.communities:
if comm_attr == attr:
return True
return False
# Extended Communities # Extended Communities
# RFC 4360 # RFC 4360
@ -1292,6 +1865,8 @@ class BGPUpdate(BGPMessage):
========================== =============================================== ========================== ===============================================
""" """
_MIN_LEN = BGPMessage._HDR_LEN
def __init__(self, type_=BGP_MSG_UPDATE, def __init__(self, type_=BGP_MSG_UPDATE,
withdrawn_routes_len=None, withdrawn_routes_len=None,
withdrawn_routes=[], withdrawn_routes=[],
@ -1304,8 +1879,18 @@ class BGPUpdate(BGPMessage):
self.withdrawn_routes = withdrawn_routes self.withdrawn_routes = withdrawn_routes
self.total_path_attribute_len = total_path_attribute_len self.total_path_attribute_len = total_path_attribute_len
self.path_attributes = path_attributes self.path_attributes = path_attributes
self._pathattr_map = {}
for attr in path_attributes:
self._pathattr_map[attr.type] = attr
self.nlri = nlri self.nlri = nlri
@property
def pathattr_map(self):
return self._pathattr_map
def get_path_attr(self, attr_name):
return self._pathattr_map.get(attr_name)
@classmethod @classmethod
def parser(cls, buf): def parser(cls, buf):
offset = 0 offset = 0
@ -1422,6 +2007,41 @@ class BGPNotification(BGPMessage):
_PACK_STR = '!BB' _PACK_STR = '!BB'
_MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR) _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR)
_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, def __init__(self,
error_code, error_code,
error_subcode, error_subcode,
@ -1450,6 +2070,10 @@ class BGPNotification(BGPMessage):
msg += self.data msg += self.data
return msg return msg
@property
def reason(self):
return self._REASONS.get((self.error_code, self.error_subcode))
@BGPMessage.register_type(BGP_MSG_ROUTE_REFRESH) @BGPMessage.register_type(BGP_MSG_ROUTE_REFRESH)
class BGPRouteRefresh(BGPMessage): class BGPRouteRefresh(BGPMessage):