diff options
author | YAMAMOTO Takashi <yamamoto@valinux.co.jp> | 2013-10-01 15:49:37 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2013-10-08 07:31:47 +0900 |
commit | 95a1cfbba26aae4bdae7b79dfa719466cc1441fa (patch) | |
tree | 5a7eeafb0886a4d35753bf1d53a963634787239c | |
parent | a7416dc3b7a2135681006fd8e998195b2c142875 (diff) |
packet.bgp: parse/serialize path attribute values
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.py | 212 | ||||
-rw-r--r-- | ryu/tests/unit/packet/test_bgp.py | 15 |
2 files changed, 209 insertions, 18 deletions
diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py index 4c2119c9..2ff1b2bd 100644 --- a/ryu/lib/packet/bgp.py +++ b/ryu/lib/packet/bgp.py @@ -62,13 +62,13 @@ 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_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_MP_REACH_NLRI = 14 # RFC 4760 BGP_ATTR_TYPE_MP_UNREACH_NLRI = 15 # RFC 4760 @@ -142,16 +142,40 @@ class BGPWithdrawnRoute(_IPAddrPrefix): pass -class BGPPathAttribute(StringifyMixin): +class _PathAttribute(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): + _TYPES = {} + _REV_TYPES = None + _ATTR_FLAGS = None + + def __init__(self, value=None, flags=0, type_=None, length=None): + if type_ is None: + if self._REV_TYPES is None: + self._REV_TYPES = dict((v, k) for k, v in + self._TYPES.iteritems()) + type_ = self._REV_TYPES[self.__class__] self.flags = flags self.type = type_ self.length = length - self.value = value + if not value is None: + self.value = value + + @classmethod + def register_type(cls, type_): + def _register_type(subcls): + cls._TYPES[type_] = subcls + cls._REV_TYPES = None + return subcls + return _register_type + + @classmethod + def _lookup_type(cls, type_): + try: + return cls._TYPES[type_] + except KeyError: + return BGPPathAttributeUnknown @classmethod def parser(cls, buf): @@ -165,11 +189,25 @@ class BGPPathAttribute(StringifyMixin): 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 + subcls = cls._lookup_type(type_) + return subcls(flags=flags, type_=type_, length=length, + **subcls.parse_value(value)), rest + + @classmethod + def parse_value(cls, buf): + (value,) = struct.unpack_from(cls._VALUE_PACK_STR, buffer(buf)) + return { + 'value': value + } def serialize(self): # fixup - self.length = len(self.value) + if not self._ATTR_FLAGS is None: + self.flags = self.flags \ + & ~(BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANSITIVE) \ + | self._ATTR_FLAGS + value = self.serialize_value() + self.length = len(value) if self.length > 255: self.flags |= BGP_ATTR_FLAG_EXTENDED_LENGTH len_pack_str = self._PACK_STR_EXT_LEN @@ -180,7 +218,151 @@ class BGPPathAttribute(StringifyMixin): 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) + return buf + value + + def serialize_value(self): + buf = bytearray() + msg_pack_into(self._VALUE_PACK_STR, buf, 0, self.value) + return buf + + +class BGPPathAttributeUnknown(_PathAttribute): + @classmethod + def parse_value(cls, buf): + return { + 'value': buf + } + + def serialize_value(self): + return self.value + + +class _PathAttributeUint32(_PathAttribute): + _VALUE_PACK_STR = '!I' + + +@_PathAttribute.register_type(BGP_ATTR_TYPE_ORIGIN) +class BGPPathAttributeOrigin(_PathAttribute): + _VALUE_PACK_STR = '!B' + _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE + + +@_PathAttribute.register_type(BGP_ATTR_TYPE_AS_PATH) +class BGPPathAttributeAsPath(_PathAttribute): + _AS_SET = 1 + _AS_SEQUENCE = 2 + _SEG_HDR_PACK_STR = '!BB' + _AS_PACK_STR = '!H' + _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE + + @classmethod + def parse_value(cls, buf): + result = [] + while buf: + (type_, num_as) = struct.unpack_from(cls._SEG_HDR_PACK_STR, + buffer(buf)) + buf = buf[struct.calcsize(cls._SEG_HDR_PACK_STR):] + l = [] + for i in xrange(0, num_as): + (as_number,) = struct.unpack_from(cls._AS_PACK_STR, + buffer(buf)) + buf = buf[struct.calcsize(cls._AS_PACK_STR):] + l.append(as_number) + if type_ == cls._AS_SET: + result.append(set(l)) + elif type_ == cls._AS_SEQUENCE: + result.append(l) + else: + assert(0) # protocol error + return { + 'value': result + } + + def serialize_value(self): + buf = bytearray() + offset = 0 + for e in self.value: + if isinstance(e, set): + type_ = self._AS_SET + elif isinstance(e, list): + type_ = self._AS_SEQUENCE + l = list(e) + num_as = len(l) + msg_pack_into(self._SEG_HDR_PACK_STR, buf, offset, type_, num_as) + offset += struct.calcsize(self._SEG_HDR_PACK_STR) + for i in l: + msg_pack_into(self._AS_PACK_STR, buf, offset, i) + offset += struct.calcsize(self._AS_PACK_STR) + return buf + + +@_PathAttribute.register_type(BGP_ATTR_TYPE_NEXT_HOP) +class BGPPathAttributeNextHop(_PathAttribute): + _VALUE_PACK_STR = '!4s' + _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE + + @classmethod + def parse_value(cls, buf): + (ip_addr,) = struct.unpack_from(cls._VALUE_PACK_STR, buffer(buf)) + return { + 'value': addrconv.ipv4.bin_to_text(ip_addr), + } + + def serialize_value(self): + buf = bytearray() + msg_pack_into(self._VALUE_PACK_STR, buf, 0, + addrconv.ipv4.text_to_bin(self.value)) + return buf + + +@_PathAttribute.register_type(BGP_ATTR_TYPE_MULTI_EXIT_DISC) +class BGPPathAttributeMultiExitDisc(_PathAttributeUint32): + _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL + + +@_PathAttribute.register_type(BGP_ATTR_TYPE_LOCAL_PREF) +class BGPPathAttributeLocalPref(_PathAttributeUint32): + _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE + + +@_PathAttribute.register_type(BGP_ATTR_TYPE_ATOMIC_AGGREGATE) +class BGPPathAttributeAtomicAggregate(_PathAttribute): + _ATTR_FLAGS = BGP_ATTR_FLAG_TRANSITIVE + + @classmethod + def parse_value(cls, buf): + return {} + + def serialize_value(self): + return '' + + +@_PathAttribute.register_type(BGP_ATTR_TYPE_AGGREGATOR) +class BGPPathAttributeAggregator(_PathAttribute): + _VALUE_PACK_STR = '!H4s' + _ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANSITIVE + + def __init__(self, as_number, ip_addr, flags=0, type_=None, length=None): + super(BGPPathAttributeAggregator, self).__init__(flags=flags, + type_=type_, + length=length) + self.as_number = as_number + self.ip_addr = ip_addr + + @classmethod + def parse_value(cls, buf): + (as_number, ip_addr) = struct.unpack_from(cls._VALUE_PACK_STR, + buffer(buf)) + return { + 'as_number': as_number, + 'ip_addr': addrconv.ipv4.bin_to_text(ip_addr), + } + + def serialize_value(self): + buf = bytearray() + msg_pack_into(self._VALUE_PACK_STR, buf, 0, self.as_number, + addrconv.ipv4.text_to_bin(self.ip_addr)) + return buf class BGPNLRI(_IPAddrPrefix): @@ -401,7 +583,7 @@ class BGPUpdate(BGPMessage): withdrawn_routes.append(r) path_attributes = [] while binpathattrs: - pa, binpathattrs = BGPPathAttribute.parser(binpathattrs) + pa, binpathattrs = _PathAttribute.parser(binpathattrs) path_attributes.append(pa) offset += 2 + total_path_attribute_len nlri = [] diff --git a/ryu/tests/unit/packet/test_bgp.py b/ryu/tests/unit/packet/test_bgp.py index 808b2db8..8d3469ac 100644 --- a/ryu/tests/unit/packet/test_bgp.py +++ b/ryu/tests/unit/packet/test_bgp.py @@ -68,9 +68,18 @@ class Test_bgp(unittest.TestCase): ip_addr='192.0.2.13'), bgp.BGPWithdrawnRoute(length=32, ip_addr='192.0.2.13')] - path_attributes = [bgp.BGPPathAttribute(flags=0, type_=1, value='foo'), - bgp.BGPPathAttribute(flags=0, type_=2, - value=300*'bar')] + path_attributes = [ + bgp.BGPPathAttributeOrigin(value=1), + bgp.BGPPathAttributeAsPath(value=[[1000], set([1001, 1002]), + [1003, 1004]]), + bgp.BGPPathAttributeNextHop(value='192.0.2.199'), + bgp.BGPPathAttributeMultiExitDisc(value=2000000000), + bgp.BGPPathAttributeLocalPref(value=1000000000), + bgp.BGPPathAttributeAtomicAggregate(), + bgp.BGPPathAttributeAggregator(as_number=40000, + ip_addr='192.0.2.99'), + bgp.BGPPathAttributeUnknown(flags=0, type_=100, value=300*'bar') + ] nlri = [ bgp.BGPNLRI(length=24, ip_addr='203.0.113.1'), bgp.BGPNLRI(length=16, ip_addr='203.0.113.0') |