diff options
author | YAMAMOTO Takashi <yamamoto@valinux.co.jp> | 2014-01-15 12:01:17 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2014-01-15 19:54:58 +0900 |
commit | dd70570eab84746978e370a2a8ee96ce79c29f23 (patch) | |
tree | 1def775519da752354083e50e3f74e39b0523b62 | |
parent | 4209fc01709a829927ba9a863870aa1276a9d2ee (diff) |
bgp: implement the rest of RFC 3107
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 | 120 | ||||
-rw-r--r-- | ryu/tests/unit/packet/test_bgp.py | 8 |
2 files changed, 111 insertions, 17 deletions
diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py index 1f8d539b..80217dd5 100644 --- a/ryu/lib/packet/bgp.py +++ b/ryu/lib/packet/bgp.py @@ -21,7 +21,6 @@ RFC 4271 BGP-4 # todo # - notify data # - notify subcode constants -# - RFC 3107 Carrying Label Information in BGP-4 # - RFC 4364 BGP/MPLS IP Virtual Private Networks (VPNs) # - RFC 4486 Subcodes for BGP Cease Notification Message @@ -34,6 +33,9 @@ from ryu.lib.packet import packet_base from ryu.lib.packet import stream_parser from ryu.lib import addrconv +import safi +import afi + BGP_MSG_OPEN = 1 BGP_MSG_UPDATE = 2 @@ -108,19 +110,19 @@ class _AddrPrefix(StringifyMixin): @staticmethod @abc.abstractmethod def _to_bin(addr): - return addr + pass @staticmethod @abc.abstractmethod - def _to_text(addr): - return addr + def _from_bin(addr): + pass @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 - addr = cls._to_text(rest[:byte_length]) + addr = cls._from_bin(rest[:byte_length]) rest = rest[byte_length:] return cls(length=length, addr=addr), rest @@ -136,7 +138,7 @@ class _AddrPrefix(StringifyMixin): mask = 0xff00 >> (self.length % 8) last_byte = chr(ord(bin_addr[byte_length - 1]) & mask) bin_addr = bin_addr[:byte_length - 1] + last_byte - self.addr = self._to_text(bin_addr) + self.addr = self._from_bin(bin_addr) buf = bytearray() msg_pack_into(self._PACK_STR, buf, 0, self.length) @@ -149,10 +151,73 @@ class _BinAddrPrefix(_AddrPrefix): return addr @staticmethod - def _to_text(addr): + def _from_bin(addr): return addr +class _LabelledAddrPrefix(_AddrPrefix): + _LABEL_PACK_STR = '!3B' + + def __init__(self, length, addr, labels=[]): + assert isinstance(labels, list) + if isinstance(addr, tuple): + assert not labels + our_addr = addr + our_length = length + else: + label_length = struct.calcsize(self._LABEL_PACK_STR) * 8 * \ + len(labels) + our_length = label_length + length + our_addr = (labels, addr) + super(_LabelledAddrPrefix, self).__init__(length=our_length, + addr=our_addr) + + @classmethod + def _label_to_bin(cls, label): + buf = bytearray() + msg_pack_into(cls._LABEL_PACK_STR, buf, 0, + (label & 0xff0000) >> 16, + (label & 0x00ff00) >> 8, + (label & 0x0000ff) >> 0) + return buf + + @classmethod + def _label_from_bin(cls, bin): + (b1, b2, b3) = struct.unpack_from(cls._LABEL_PACK_STR, buffer(bin)) + rest = bin[struct.calcsize(cls._LABEL_PACK_STR):] + return (b1 << 16) | (b2 << 8) | b3, rest + + @classmethod + def _to_bin(cls, addr): + (labels, prefix) = addr + labels = map(lambda x: x << 4, labels) + if labels: + labels[-1] |= 1 # bottom of stack + bin_labels = map(cls._label_to_bin, labels) + return bytes(reduce(lambda x, y: x + y, bin_labels, + bytearray()) + cls._prefix_to_bin(prefix)) + + @classmethod + def _from_bin(cls, addr): + labels = [] + while True: + (label, addr) = cls._label_from_bin(addr) + labels.append(label >> 4) + if label & 1: # bottom of stack + break + return (labels, cls._prefix_from_bin(addr)) + + +class _UnlabelledAddrPrefix(_AddrPrefix): + @classmethod + def _to_bin(cls, addr): + return cls._prefix_to_bin(addr) + + @classmethod + def _from_bin(cls, addr): + return cls._prefix_from_bin(addr) + + class _IPAddrPrefix(_AddrPrefix): _TYPE = { 'ascii': [ @@ -161,14 +226,35 @@ class _IPAddrPrefix(_AddrPrefix): } @staticmethod - def _to_bin(addr): + def _prefix_to_bin(addr): return addrconv.ipv4.text_to_bin(addr) @staticmethod - def _to_text(addr): + def _prefix_from_bin(addr): return addrconv.ipv4.bin_to_text(pad(addr, 4)) +class LabelledIPAddrPrefix(_LabelledAddrPrefix, _IPAddrPrefix): + pass + + +class IPAddrPrefix(_UnlabelledAddrPrefix, _IPAddrPrefix): + pass + + +_ADDR_CLASSES = { + (afi.IP, safi.UNICAST): IPAddrPrefix, + (afi.IP, safi.MPLS_VPN): LabelledIPAddrPrefix, +} + + +def _get_addr_class(afi, safi): + try: + return _ADDR_CLASSES[(afi, safi)] + except KeyError: + return _BinAddrPrefix + + class _Value(object): _VALUE_PACK_STR = None _VALUE_FIELDS = ['value'] @@ -396,7 +482,7 @@ class BGPOptParamCapabilityCarryingLabelInfo(_OptParamEmptyCapability): pass -class BGPWithdrawnRoute(_IPAddrPrefix): +class BGPWithdrawnRoute(IPAddrPrefix): pass @@ -847,6 +933,9 @@ class BGPPathAttributeMpReachNLRI(_PathAttribute): self.next_hop = next_hop self.reserved = reserved self.nlri = nlri + addr_cls = _get_addr_class(afi, safi) + for i in nlri: + assert isinstance(i, addr_cls) @classmethod def parse_value(cls, buf): @@ -857,9 +946,10 @@ class BGPPathAttributeMpReachNLRI(_PathAttribute): rest = rest[next_hop_len:] reserved = rest[:1] binnlri = rest[1:] + addr_cls = _get_addr_class(afi, safi) nlri = [] while binnlri: - n, binnlri = _BinAddrPrefix.parser(binnlri) + n, binnlri = addr_cls.parser(binnlri) nlri.append(n) return { 'afi': afi, @@ -901,14 +991,18 @@ class BGPPathAttributeMpUnreachNLRI(_PathAttribute): self.afi = afi self.safi = safi self.withdrawn_routes = withdrawn_routes + addr_cls = _get_addr_class(afi, safi) + for i in withdrawn_routes: + assert isinstance(i, addr_cls) @classmethod def parse_value(cls, buf): (afi, safi,) = struct.unpack_from(cls._VALUE_PACK_STR, buffer(buf)) binnlri = buf[struct.calcsize(cls._VALUE_PACK_STR):] + addr_cls = _get_addr_class(afi, safi) nlri = [] while binnlri: - n, binnlri = _BinAddrPrefix.parser(binnlri) + n, binnlri = addr_cls.parser(binnlri) nlri.append(n) return { 'afi': afi, @@ -926,7 +1020,7 @@ class BGPPathAttributeMpUnreachNLRI(_PathAttribute): return buf -class BGPNLRI(_IPAddrPrefix): +class BGPNLRI(IPAddrPrefix): pass diff --git a/ryu/tests/unit/packet/test_bgp.py b/ryu/tests/unit/packet/test_bgp.py index 0f2975d1..deebb9ab 100644 --- a/ryu/tests/unit/packet/test_bgp.py +++ b/ryu/tests/unit/packet/test_bgp.py @@ -79,8 +79,8 @@ class Test_bgp(unittest.TestCase): bgp.BGPWithdrawnRoute(length=32, addr='192.0.2.13')] mp_nlri = [ - bgp._BinAddrPrefix(32, 'efgh\0\0'), - bgp._BinAddrPrefix(16, 'ij\0\0\0\0'), + bgp.LabelledIPAddrPrefix(24, '192.0.9.0', labels=[1, 2, 3]), + bgp.LabelledIPAddrPrefix(26, '192.0.10.192', labels=[5, 6, 7, 8]), ] communities = [ bgp.BGP_COMMUNITY_NO_EXPORT, @@ -224,8 +224,8 @@ class Test_bgp(unittest.TestCase): bgp.BGPWithdrawnRoute(length=32, addr='192.0.2.13')] mp_nlri = [ - bgp._BinAddrPrefix(32, 'efgh\0\0'), - bgp._BinAddrPrefix(16, 'ij\0\0\0\0'), + bgp.LabelledIPAddrPrefix(24, '192.0.9.0', labels=[1, 2, 3]), + bgp.LabelledIPAddrPrefix(26, '192.0.10.192', labels=[5, 6, 7, 8]), ] communities = [ bgp.BGP_COMMUNITY_NO_EXPORT, |