Signed-off-by: YAMAMOTO Takashi <[email protected]>
---
 ryu/lib/packet/bgp.py             | 120 +++++++++++++++++++++++++++++++++-----
 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 1f8d539..80217dd 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 0f2975d..deebb9a 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,
-- 
1.8.3.1


------------------------------------------------------------------------------
CenturyLink Cloud: The Leader in Enterprise Cloud Services.
Learn Why More Businesses Are Choosing CenturyLink Cloud For
Critical Workloads, Development Environments & Everything In Between.
Get a Quote or Start a Free Trial Today. 
http://pubads.g.doubleclick.net/gampad/clk?id=119420431&iu=/4140/ostg.clktrk
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to