summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorYAMAMOTO Takashi <yamamoto@valinux.co.jp>2013-09-24 10:19:53 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2013-09-26 04:11:01 +0900
commitba92a9e634e8d60a4ea9b8f80d1a0b7a30fc0609 (patch)
tree8296cbe6c66ae89b496da9e87684f86fe28219e3
parentba7bde95fdff81732fbe9c1e1f04f07073d840a1 (diff)
packet lib: implement basic part of BGP-4
Signed-off-by: YAMAMOTO Takashi <yamamoto@valinux.co.jp> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r--ryu/lib/packet/bgp.py519
1 files changed, 519 insertions, 0 deletions
diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py
new file mode 100644
index 00000000..a6d5f4d0
--- /dev/null
+++ b/ryu/lib/packet/bgp.py
@@ -0,0 +1,519 @@
+# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
+# Copyright (C) 2013 YAMAMOTO Takashi <yamamoto at valinux co jp>
+#
+# 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.
+
+"""
+RFC 4271 BGP-4
+"""
+
+# todo
+# - streaming parser
+# - notify data
+# - notify subcode constants
+# - RFC 1997 BGP Communities Attribute
+# - RFC 2918 Route Refresh Capability for BGP-4
+# - RFC 3107 Carrying Label Information in BGP-4
+# - RFC 4360 BGP Extended Communities Attribute
+# - RFC 4364 BGP/MPLS IP Virtual Private Networks (VPNs)
+# - RFC 4486 Subcodes for BGP Cease Notification Message
+# - RFC 4760 Multiprotocol Extensions for BGP-4
+
+import struct
+
+from ryu.ofproto.ofproto_parser import msg_pack_into
+from ryu.lib.stringify import StringifyMixin
+from ryu.lib.packet import packet_base
+from ryu.lib import addrconv
+
+
+BGP_MSG_OPEN = 1
+BGP_MSG_UPDATE = 2
+BGP_MSG_NOTIFICATION = 3
+BGP_MSG_KEEPALIVE = 4
+BGP_MSG_ROUTE_REFRESH = 5 # RFC 2918
+
+# RFC 4271 4.5.
+BGP_ERROR_MESSAGE_HEADER_ERROR = 1
+BGP_ERROR_OPEN_MESSAGE_ERROR = 2
+BGP_ERROR_UPDATE_MESSAGE_ERROR = 3
+BGP_ERROR_HOLD_TIMER_EXPIRED = 4
+BGP_ERROR_FSM_ERROR = 5
+BGP_ERROR_CEASE = 6
+
+_VERSION = 4
+_MARKER = 16 * '\xff'
+
+BGP_OPT_CAPABILITY = 2 # RFC 3392
+
+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
+BGP_ATTR_TYPE_AS_PATH = 2
+BGP_ATTR_TYPE_NEXT_HOP = 3
+BGP_ATTR_TYPE_MULTI_EXIT_DISC = 4
+BGP_ATTR_TYPE_LOCAL_PREF = 5
+BGP_ATTR_TYPE_ATOMIC_AGGREGATE = 6
+BGP_ATTR_TYPE_AGGREGATOR = 7
+BGP_ATTR_TYPE_MP_REACH_NLRI = 14 # RFC 4760
+BGP_ATTR_TYPE_MP_UNREACH_NLRI = 15 # RFC 4760
+
+
+def pad(bin, len_):
+ assert len(bin) <= len_
+ return bin + (len_ - len(bin)) * '\0'
+
+
+class _IPAddrPrefix(StringifyMixin):
+ _PACK_STR = '!B' # length
+
+ def __init__(self, length, ip_addr):
+ self.length = length
+ self.ip_addr = ip_addr
+
+ @classmethod
+ def parser(cls, buf):
+ (length, ) = struct.unpack_from(cls._PACK_STR, buffer(buf))
+ rest = buf[struct.calcsize(cls._PACK_STR):]
+ byte_length = (length + 7) / 8
+ ip_addr = addrconv.ipv4.bin_to_text(pad(rest[:byte_length], 4))
+ rest = rest[byte_length:]
+ return cls(length=length, ip_addr=ip_addr), rest
+
+ def serialize(self):
+ # fixup
+ byte_length = (self.length + 7) / 8
+ bin_ip_addr = addrconv.ipv4.text_to_bin(self.ip_addr)
+ if (self.length % 8) == 0:
+ bin_ip_addr = bin_ip_addr[:byte_length]
+ else:
+ # clear trailing bits in the last octet.
+ # rfc doesn't require this.
+ mask = 0xff00 >> (self.length % 8)
+ last_byte = chr(ord(bin_ip_addr[byte_length - 1]) & mask)
+ bin_ip_addr = bin_ip_addr[:byte_length - 1] + last_byte
+ self.ip_addr = addrconv.ipv4.bin_to_text(pad(bin_ip_addr, 4))
+
+ buf = bytearray()
+ msg_pack_into(self._PACK_STR, buf, 0, self.length)
+ return buf + bytes(bin_ip_addr)
+
+
+class BGPOptParam(StringifyMixin):
+ _PACK_STR = '!BB' # type, length
+
+ def __init__(self, type_, value, length=None):
+ self.type = type_
+ self.length = length
+ self.value = value
+
+ @classmethod
+ def parser(cls, buf):
+ (type_, length) = struct.unpack_from(cls._PACK_STR, buffer(buf))
+ rest = buf[struct.calcsize(cls._PACK_STR):]
+ value = bytes(rest[:length])
+ rest = rest[length:]
+ return cls(type_=type_, length=length, value=value), rest
+
+ def serialize(self):
+ # fixup
+ self.length = len(self.value)
+
+ buf = bytearray()
+ msg_pack_into(self._PACK_STR, buf, 0, self.type, self.length)
+ return buf + bytes(self.value)
+
+
+class BGPWithdrawnRoute(_IPAddrPrefix):
+ pass
+
+
+class BGPPathAttribute(StringifyMixin):
+ _PACK_STR = '!BB' # flags, type
+ _PACK_STR_LEN = '!B' # length
+ _PACK_STR_EXT_LEN = '!H' # length w/ BGP_ATTR_FLAG_EXTENDED_LENGTH
+
+ def __init__(self, flags, type_, value, length=None):
+ self.flags = flags
+ self.type = type_
+ self.length = length
+ self.value = value
+
+ @classmethod
+ def parser(cls, buf):
+ (flags, type_) = struct.unpack_from(cls._PACK_STR, buffer(buf))
+ rest = buf[struct.calcsize(cls._PACK_STR):]
+ if (flags & BGP_ATTR_FLAG_EXTENDED_LENGTH) != 0:
+ len_pack_str = cls._PACK_STR_EXT_LEN
+ else:
+ len_pack_str = cls._PACK_STR_LEN
+ (length,) = struct.unpack_from(len_pack_str, buffer(rest))
+ rest = rest[struct.calcsize(len_pack_str):]
+ value = bytes(rest[:length])
+ rest = rest[length:]
+ return cls(flags=flags, type_=type_, length=length, value=value), rest
+
+ def serialize(self):
+ # fixup
+ self.length = len(self.value)
+ if self.length > 255:
+ self.flags |= BGP_ATTR_FLAG_EXTENDED_LENGTH
+ len_pack_str = self._PACK_STR_EXT_LEN
+ else:
+ self.flags &= ~BGP_ATTR_FLAG_EXTENDED_LENGTH
+ len_pack_str = self._PACK_STR_LEN
+
+ buf = bytearray()
+ msg_pack_into(self._PACK_STR, buf, 0, self.flags, self.type)
+ msg_pack_into(len_pack_str, buf, len(buf), self.length)
+ return buf + bytes(self.value)
+
+
+class BGPNLRI(_IPAddrPrefix):
+ pass
+
+
+class BGPMessage(packet_base.PacketBase):
+ """Base class for BGP-4 messages.
+
+ An instance has the following attributes at least.
+ Most of them are same to the on-wire counterparts but in host byte
+ order.
+ __init__ takes the correspondig args in this order.
+
+ ========================== ===============================================
+ Attribute Description
+ ========================== ===============================================
+ marker Marker field. Ignored when encoding.
+ len Length field. Ignored when encoding.
+ type Type field. one of BGP\_MSG\_ constants.
+ ========================== ===============================================
+ """
+
+ _HDR_PACK_STR = '!16sHB' # marker, len, type
+ _HDR_LEN = struct.calcsize(_HDR_PACK_STR)
+ _TYPES = {}
+
+ @classmethod
+ def register_type(cls, type_):
+ def _register_type(subcls):
+ cls._TYPES[type_] = subcls
+ return subcls
+ return _register_type
+
+ def __init__(self, type_, len_=None, marker=None):
+ if marker is None:
+ self.marker = _MARKER
+ else:
+ self.marker = marker
+ self.len = len_
+ self.type = type_
+
+ @classmethod
+ def parser(cls, buf):
+ (marker, len_, type_) = struct.unpack_from(cls._HDR_PACK_STR,
+ buffer(buf))
+ subcls = cls._TYPES[type_]
+ kwargs = subcls.parser(buf[cls._HDR_LEN:])
+ return subcls(marker=marker, len_=len_, type_=type_, **kwargs)
+
+ def serialize(self):
+ # fixup
+ self.marker = _MARKER
+ tail = self.serialize_tail()
+ self.len = self._HDR_LEN + len(tail)
+
+ hdr = bytearray(struct.pack(self._HDR_PACK_STR, self.marker,
+ self.len, self.type))
+ return hdr + tail
+
+ def __len__(self):
+ # XXX destructive
+ buf = self.serialize()
+ return len(buf)
+
+
+@BGPMessage.register_type(BGP_MSG_OPEN)
+class BGPOpen(BGPMessage):
+ """BGP-4 OPEN Message encoder/decoder class.
+
+ An instance has the following attributes at least.
+ Most of them are same to the on-wire counterparts but in host byte
+ order.
+ __init__ takes the correspondig args in this order.
+
+ ========================== ===============================================
+ Attribute Description
+ ========================== ===============================================
+ marker Marker field. Ignored when encoding.
+ len Length field. Ignored when encoding.
+ type Type field. The default is BGP_MSG_OPEN.
+ version Version field. The default is 4.
+ my_as My Autonomous System field. 2 octet unsigned
+ integer.
+ hold_time Hold Time field. 2 octet unsigned integer.
+ The default is 0.
+ bgp_identifier BGP Identifier field. An IPv4 address.
+ For example, '192.0.2.1'
+ opt_param_len Optional Parameters Length field.
+ Ignored when encoding.
+ opt_param Optional Parameters field. A list of
+ BGPOptParam instances. The default is [].
+ ========================== ===============================================
+ """
+
+ _PACK_STR = '!BHH4sB'
+ _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR)
+
+ def __init__(self, my_as, bgp_identifier, type_=BGP_MSG_OPEN,
+ opt_param_len=0, opt_param=[],
+ version=_VERSION, hold_time=0, len_=None, marker=None):
+ super(BGPOpen, self).__init__(marker=marker, len_=len_, type_=type_)
+ self.version = version
+ self.my_as = my_as
+ self.bgp_identifier = bgp_identifier
+ self.hold_time = hold_time
+ self.opt_param_len = opt_param_len
+ self.opt_param = opt_param
+
+ @classmethod
+ def parser(cls, buf):
+ (version, my_as, hold_time,
+ bgp_identifier, opt_param_len) = struct.unpack_from(cls._PACK_STR,
+ buffer(buf))
+ rest = buf[struct.calcsize(cls._PACK_STR):]
+ binopts = rest[:opt_param_len]
+ opt_param = []
+ while binopts:
+ opt, binopts = BGPOptParam.parser(binopts)
+ opt_param.append(opt)
+ return {
+ "version": version,
+ "my_as": my_as,
+ "hold_time": hold_time,
+ "bgp_identifier": addrconv.ipv4.bin_to_text(bgp_identifier),
+ "opt_param_len": opt_param_len,
+ "opt_param": opt_param,
+ }
+
+ def serialize_tail(self):
+ # fixup
+ self.version = _VERSION
+ binopts = bytearray()
+ for opt in self.opt_param:
+ binopts += opt.serialize()
+ self.opt_param_len = len(binopts)
+
+ msg = bytearray(struct.pack(self._PACK_STR,
+ self.version,
+ self.my_as,
+ self.hold_time,
+ addrconv.ipv4.text_to_bin(
+ self.bgp_identifier),
+ self.opt_param_len))
+ msg += binopts
+ return msg
+
+
+@BGPMessage.register_type(BGP_MSG_UPDATE)
+class BGPUpdate(BGPMessage):
+ """BGP-4 UPDATE Message encoder/decoder class.
+
+ An instance has the following attributes at least.
+ Most of them are same to the on-wire counterparts but in host byte
+ order.
+ __init__ takes the correspondig args in this order.
+
+ ========================== ===============================================
+ Attribute Description
+ ========================== ===============================================
+ marker Marker field. Ignored when encoding.
+ len Length field. Ignored when encoding.
+ type Type field. The default is BGP_MSG_UPDATE.
+ withdrawn_routes_len Withdrawn Routes Length field.
+ Ignored when encoding.
+ withdrawn_routes Withdrawn Routes field. A list of
+ BGPWithdrawnRoute instances.
+ The default is [].
+ total_path_attribute_len Total Path Attribute Length field.
+ Ignored when encoding.
+ path_attributes Path Attributes field. A list of
+ BGPPathAttribute instances.
+ The default is [].
+ nlri Network Layer Reachability Information field.
+ A list of BGPNLRI instances.
+ The default is [].
+ ========================== ===============================================
+ """
+
+ def __init__(self, type_=BGP_MSG_UPDATE,
+ withdrawn_routes_len=None,
+ withdrawn_routes=[],
+ total_path_attribute_len=None,
+ path_attributes=[],
+ nlri=[],
+ len_=None, marker=None):
+ super(BGPUpdate, self).__init__(marker=marker, len_=len_, type_=type_)
+ self.withdrawn_routes_len = withdrawn_routes_len
+ self.withdrawn_routes = withdrawn_routes
+ self.total_path_attribute_len = total_path_attribute_len
+ self.path_attributes = path_attributes
+ self.nlri = nlri
+
+ @classmethod
+ def parser(cls, buf):
+ offset = 0
+ (withdrawn_routes_len,) = struct.unpack_from('!H', buffer(buf), offset)
+ binroutes = buffer(buf[offset + 2:
+ offset + 2 + withdrawn_routes_len])
+ offset += 2 + withdrawn_routes_len
+ (total_path_attribute_len,) = struct.unpack_from('!H', buffer(buf),
+ offset)
+ binpathattrs = buffer(buf[offset + 2:
+ offset + 2 + total_path_attribute_len])
+ binnlri = buffer(buf[offset + 2 + total_path_attribute_len:])
+ withdrawn_routes = []
+ while binroutes:
+ r, binroutes = BGPWithdrawnRoute.parser(binroutes)
+ withdrawn_routes.append(r)
+ path_attributes = []
+ while binpathattrs:
+ pa, binpathattrs = BGPPathAttribute.parser(binpathattrs)
+ path_attributes.append(pa)
+ offset += 2 + total_path_attribute_len
+ nlri = []
+ while binnlri:
+ n, binnlri = BGPNLRI.parser(binnlri)
+ nlri.append(n)
+ return {
+ "withdrawn_routes_len": withdrawn_routes_len,
+ "withdrawn_routes": withdrawn_routes,
+ "total_path_attribute_len": total_path_attribute_len,
+ "path_attributes": path_attributes,
+ "nlri": nlri,
+ }
+
+ def serialize_tail(self):
+ # fixup
+ binroutes = bytearray()
+ for r in self.withdrawn_routes:
+ binroutes += r.serialize()
+ self.withdrawn_routes_len = len(binroutes)
+ binpathattrs = bytearray()
+ for pa in self.path_attributes:
+ binpathattrs += pa.serialize()
+ self.total_path_attribute_len = len(binpathattrs)
+ binnlri = bytearray()
+ for n in self.nlri:
+ binnlri += n.serialize()
+
+ msg = bytearray()
+ offset = 0
+ msg_pack_into('!H', msg, offset, self.withdrawn_routes_len)
+ msg += binroutes
+ offset += 2 + self.withdrawn_routes_len
+ msg_pack_into('!H', msg, offset, self.total_path_attribute_len)
+ msg += binpathattrs
+ offset += 2 + self.total_path_attribute_len
+ msg += binnlri
+ return msg
+
+
+@BGPMessage.register_type(BGP_MSG_KEEPALIVE)
+class BGPKeepAlive(BGPMessage):
+ """BGP-4 KEEPALIVE Message encoder/decoder class.
+
+ An instance has the following attributes at least.
+ Most of them are same to the on-wire counterparts but in host byte
+ order.
+ __init__ takes the correspondig args in this order.
+
+ ========================== ===============================================
+ Attribute Description
+ ========================== ===============================================
+ marker Marker field. Ignored when encoding.
+ len Length field. Ignored when encoding.
+ type Type field. The default is BGP_MSG_KEEPALIVE.
+ ========================== ===============================================
+ """
+
+ _MIN_LEN = BGPMessage._HDR_LEN
+
+ def __init__(self, type_=BGP_MSG_KEEPALIVE, len_=None, marker=None):
+ super(BGPKeepAlive, self).__init__(marker=marker, len_=len_,
+ type_=type_)
+
+ @classmethod
+ def parser(cls, buf):
+ return {}
+
+ def serialize_tail(self):
+ return bytearray()
+
+
+@BGPMessage.register_type(BGP_MSG_NOTIFICATION)
+class BGPNotification(BGPMessage):
+ """BGP-4 NOTIFICATION Message encoder/decoder class.
+
+ An instance has the following attributes at least.
+ Most of them are same to the on-wire counterparts but in host byte
+ order.
+ __init__ takes the correspondig args in this order.
+
+ ========================== ===============================================
+ Attribute Description
+ ========================== ===============================================
+ marker Marker field. Ignored when encoding.
+ len Length field. Ignored when encoding.
+ type Type field. The default is
+ BGP_MSG_NOTIFICATION.
+ error_code Error code field.
+ error_subcode Error subcode field.
+ data Data field. The default is ''.
+ ========================== ===============================================
+ """
+
+ _PACK_STR = '!BB'
+ _MIN_LEN = BGPMessage._HDR_LEN + struct.calcsize(_PACK_STR)
+
+ def __init__(self,
+ error_code,
+ error_subcode,
+ data='',
+ type_=BGP_MSG_NOTIFICATION, len_=None, marker=None):
+ super(BGPNotification, self).__init__(marker=marker, len_=len_,
+ type_=type_)
+ self.error_code = error_code
+ self.error_subcode = error_subcode
+ self.data = data
+
+ @classmethod
+ def parser(cls, buf):
+ (error_code, error_subcode,) = struct.unpack_from(cls._PACK_STR,
+ buffer(buf))
+ data = bytes(buf[2:])
+ return {
+ "error_code": error_code,
+ "error_subcode": error_subcode,
+ "data": data,
+ }
+
+ def serialize_tail(self):
+ msg = bytearray(struct.pack(self._PACK_STR, self.error_code,
+ self.error_subcode))
+ msg += self.data
+ return msg