Signed-off-by: ISHIDA Wataru <[email protected]>
---
 ryu/lib/packet/bgp.py                        |  315 +++++++++++++++++---------
 ryu/services/protocols/bgp/info_base/base.py |    2 +-
 ryu/tests/unit/packet/test_bgp.py            |    8 +-
 3 files changed, 218 insertions(+), 107 deletions(-)

diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py
index 13858db..54a8daa 100644
--- a/ryu/lib/packet/bgp.py
+++ b/ryu/lib/packet/bgp.py
@@ -142,6 +142,72 @@ BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION = 7
 BGP_ERROR_SUB_OUT_OF_RESOURCES = 8


+class _Value(object):
+    _VALUE_PACK_STR = None
+    _VALUE_FIELDS = ['value']
+
+    @staticmethod
+    def do_init(cls, self, kwargs, **extra_kwargs):
+        ourfields = {}
+        for f in cls._VALUE_FIELDS:
+            v = kwargs[f]
+            del kwargs[f]
+            ourfields[f] = v
+        kwargs.update(extra_kwargs)
+        super(cls, self).__init__(**kwargs)
+        self.__dict__.update(ourfields)
+
+    @classmethod
+    def parse_value(cls, buf):
+        values = struct.unpack_from(cls._VALUE_PACK_STR, buffer(buf))
+        return dict(zip(cls._VALUE_FIELDS, values))
+
+    def serialize_value(self):
+        args = []
+        for f in self._VALUE_FIELDS:
+            args.append(getattr(self, f))
+        buf = bytearray()
+        msg_pack_into(self._VALUE_PACK_STR, buf, 0, *args)
+        return buf
+
+
+class _TypeDisp(object):
+    _TYPES = {}
+    _REV_TYPES = None
+    _UNKNOWN_TYPE = None
+
+    @classmethod
+    def register_unknown_type(cls):
+        def _register_type(subcls):
+            cls._UNKNOWN_TYPE = subcls
+            return subcls
+        return _register_type
+
+    @classmethod
+    def register_type(cls, type_):
+        cls._TYPES = cls._TYPES.copy()
+
+        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 cls._UNKNOWN_TYPE
+
+    @classmethod
+    def _rev_lookup_type(cls, targ_cls):
+        if cls._REV_TYPES is None:
+            rev = dict((v, k) for k, v in cls._TYPES.iteritems())
+            cls._REV_TYPES = rev
+        return cls._REV_TYPES[targ_cls]
+
+
 class BgpExc(Exception):
     """Base bgp exception."""

@@ -505,12 +571,115 @@ 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)

+_rf_map = {
+        (afi.IP, safi.UNICAST) : RF_IPv4_UC,
+        (afi.IP6, safi.UNICAST) : RF_IPv6_UC,
+        (afi.IP, safi.MPLS_VPN) : RF_IPv4_VPN,
+        (afi.IP6, safi.MPLS_VPN) : RF_IPv6_VPN,
+        (afi.IP, safi.ROUTE_TARGET_CONSTRTAINS) : RF_RTC_UC
+        }

 def pad(bin, len_):
     assert len(bin) <= len_
     return bin + (len_ - len(bin)) * '\0'


+class _RouteDistinguisher(StringifyMixin, _TypeDisp, _Value):
+    _PACK_STR = '!H'
+    TWO_OCTET_AS = 0
+    IPV4_ADDRESS = 1
+    FOUR_OCTET_AS = 2
+
+    def __init__(self, type_, admin=0, assigned=0):
+        self.type = type_
+        self.admin = admin
+        self.assigned = assigned
+
+    @classmethod
+    def parser(cls, buf):
+        assert len(buf) == 8
+        (type_,) = struct.unpack_from(cls._PACK_STR, buffer(buf))
+        rest = buf[struct.calcsize(cls._PACK_STR):]
+        subcls = cls._lookup_type(type_)
+        return subcls(type_=type_, **subcls.parse_value(rest))
+
+    @classmethod
+    def from_str(cls, str_):
+        assert isinstance(str_, str)
+
+        first, second = str_.split(':')
+        if '.' in first:
+            type_ = cls.IPV4_ADDRESS
+        elif int(first) > (1 << 16):
+            type_ = cls.FOUR_OCTET_AS
+            first = int(first)
+        else:
+            type_  = cls.TWO_OCTET_AS
+            first = int(first)
+        subcls =  cls._lookup_type(type_)
+        return subcls(type_=type_, admin=first, assigned=int(second))
+
+    def serialize(self):
+        value = self.serialize_value()
+        buf = bytearray()
+        msg_pack_into(self._PACK_STR, buf, 0, self.type)
+        return buf + value
+
+    @property
+    def formatted_str(self):
+        return "%s:%s" % (str(self.admin), str(self.assigned))
+
+
+@_RouteDistinguisher.register_type(_RouteDistinguisher.TWO_OCTET_AS)
+class BGPTwoOctetAsRD(_RouteDistinguisher):
+    _VALUE_PACK_STR = '!HI'
+    _VALUE_FIELDS = ['admin', 'assigned']
+
+    def __init__(self, type_=_RouteDistinguisher.TWO_OCTET_AS, **kwargs):
+        self.do_init(BGPTwoOctetAsRD, self, kwargs, type_=type_)
+
+
+@_RouteDistinguisher.register_type(_RouteDistinguisher.IPV4_ADDRESS)
+class BGPIPv4AddressRD(_RouteDistinguisher):
+    _VALUE_PACK_STR = '!4sH'
+    _VALUE_FIELDS = ['admin', 'assigned']
+    _TYPE = {
+        'ascii': [
+            'admin'
+        ]
+    }
+
+    def __init__(self, type_=_RouteDistinguisher.IPV4_ADDRESS, **kwargs):
+        self.do_init(BGPIPv4AddressRD, self, kwargs, type_=type_)
+
+    @classmethod
+    def parse_value(cls, buf):
+        d_ = super(BGPIPv4AddressRD, cls).parse_value(buf)
+        d_['admin'] = addrconv.ipv4.bin_to_text(d_['admin'])
+        return d_
+
+    def serialize_value(self):
+        args = []
+        for f in self._VALUE_FIELDS:
+            v = getattr(self, f)
+            if f == 'admin':
+                v = bytes(addrconv.ipv4.text_to_bin(v))
+            args.append(v)
+        buf = bytearray()
+        msg_pack_into(self._VALUE_PACK_STR, buf, 0, *args)
+        return buf
+
+
+@_RouteDistinguisher.register_type(_RouteDistinguisher.FOUR_OCTET_AS)
+class BGPFourOctetAsRD(_RouteDistinguisher):
+    _VALUE_PACK_STR = '!IH'
+    _VALUE_FIELDS = ['admin', 'assigned']
+
+    def __init__(self, type_=_RouteDistinguisher.FOUR_OCTET_AS,
+                 **kwargs):
+        self.do_init(BGPFourOctetAsRD, self, kwargs, type_=type_)
+
+
 @six.add_metaclass(abc.ABCMeta)
 class _AddrPrefix(StringifyMixin):
     _PACK_STR = '!B'  # length
@@ -690,6 +859,10 @@ class _VPNAddrPrefix(_AddrPrefix):
             addr = addr[1:]
         else:
             length += struct.calcsize(self._RD_PACK_STR) * 8
+
+        if isinstance(route_dist, str):
+            route_dist = _RouteDistinguisher.from_str(route_dist)
+
         prefixes = prefixes + (route_dist,)
         super(_VPNAddrPrefix, self).__init__(prefixes=prefixes,
                                              length=length,
@@ -699,15 +872,14 @@ class _VPNAddrPrefix(_AddrPrefix):
     def _prefix_to_bin(cls, addr):
         rd = addr[0]
         rest = addr[1:]
-        binrd = bytearray()
-        msg_pack_into(cls._RD_PACK_STR, binrd, 0, rd)
+        binrd = rd.serialize()
         return binrd + super(_VPNAddrPrefix, cls)._prefix_to_bin(rest)

     @classmethod
     def _prefix_from_bin(cls, binaddr):
         binrd = binaddr[:8]
         binrest = binaddr[8:]
-        (rd,) = struct.unpack_from(cls._RD_PACK_STR, buffer(binrd))
+        rd = _RouteDistinguisher.parser(binrd)
         return (rd,) + super(_VPNAddrPrefix, cls)._prefix_from_bin(binrest)


@@ -741,11 +913,13 @@ class LabelledVPNIPAddrPrefix(_LabelledAddrPrefix, 
_VPNAddrPrefix,

     @property
     def prefix(self):
-        return self.addr[-1] + '/{0}'.format(self.length)
+        masklen = self.length - struct.calcsize(self._RD_PACK_STR) * 8 \
+            - struct.calcsize(self._LABEL_PACK_STR) * 8 * len(self.addr[:-2])
+        return self.addr[-1]+'/{0}'.format(masklen)

     @property
-    def route_disc(self):
-        return self.addr[-2]
+    def route_dist(self):
+        return self.addr[-2].formatted_str

     @property
     def label_list(self):
@@ -762,11 +936,13 @@ class LabelledVPNIP6AddrPrefix(_LabelledAddrPrefix, 
_VPNAddrPrefix,

     @property
     def prefix(self):
-        return self.addr[-1] + '/{0}'.format(self.length)
+        masklen = self.length - struct.calcsize(self._RD_PACK_STR) * 8 \
+            - struct.calcsize(self._LABEL_PACK_STR) * 8 * len(self.addr[:-2])
+        return self.addr[-1]+'/{0}'.format(masklen)

     @property
-    def route_disc(self):
-        return self.addr[-2]
+    def route_dist(self):
+        return self.addr[-2].formatted_str

     @property
     def label_list(self):
@@ -861,27 +1037,7 @@ class RouteTargetMembershipNLRI(StringifyMixin):
         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))
-
+        route_target = _ExtendedCommunity(buf[idx:])
         return cls(origin_as, route_target)

     def serialize(self):
@@ -889,12 +1045,7 @@ class RouteTargetMembershipNLRI(StringifyMixin):
         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 += self.route_target.serialize()

         # RT Nlri is 12 octets
         return struct.pack('B', (8 * 12)) + rt_nlri
@@ -917,72 +1068,6 @@ def _get_addr_class(afi, safi):
         return _BinAddrPrefix


-class _Value(object):
-    _VALUE_PACK_STR = None
-    _VALUE_FIELDS = ['value']
-
-    @staticmethod
-    def do_init(cls, self, kwargs, **extra_kwargs):
-        ourfields = {}
-        for f in cls._VALUE_FIELDS:
-            v = kwargs[f]
-            del kwargs[f]
-            ourfields[f] = v
-        kwargs.update(extra_kwargs)
-        super(cls, self).__init__(**kwargs)
-        self.__dict__.update(ourfields)
-
-    @classmethod
-    def parse_value(cls, buf):
-        values = struct.unpack_from(cls._VALUE_PACK_STR, buffer(buf))
-        return dict(zip(cls._VALUE_FIELDS, values))
-
-    def serialize_value(self):
-        args = []
-        for f in self._VALUE_FIELDS:
-            args.append(getattr(self, f))
-        buf = bytearray()
-        msg_pack_into(self._VALUE_PACK_STR, buf, 0, *args)
-        return buf
-
-
-class _TypeDisp(object):
-    _TYPES = {}
-    _REV_TYPES = None
-    _UNKNOWN_TYPE = None
-
-    @classmethod
-    def register_unknown_type(cls):
-        def _register_type(subcls):
-            cls._UNKNOWN_TYPE = subcls
-            return subcls
-        return _register_type
-
-    @classmethod
-    def register_type(cls, type_):
-        cls._TYPES = cls._TYPES.copy()
-
-        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 cls._UNKNOWN_TYPE
-
-    @classmethod
-    def _rev_lookup_type(cls, targ_cls):
-        if cls._REV_TYPES is None:
-            rev = dict((v, k) for k, v in cls._TYPES.iteritems())
-            cls._REV_TYPES = rev
-        return cls._REV_TYPES[targ_cls]
-
-
 class _OptParam(StringifyMixin, _TypeDisp, _Value):
     _PACK_STR = '!BB'  # type, length

@@ -1549,6 +1634,25 @@ class 
BGPPathAttributeExtendedCommunities(_PathAttribute):
             buf += comm.serialize()
         return buf

+    def _community_list(self, subtype):
+        _list = []
+        for comm in (c for c in self.communities if c.subtype == subtype):
+            if comm.type == 0 or comm.type == 2:
+                _list.append('%d:%d' % (comm.as_number,
+                                        comm.local_administrator))
+            elif comm.type == 1:
+                _list.append('%s:%d' % (comm.ipv4_address,
+                                        comm.local_administrator))
+        return _list
+
+    @property
+    def rt_list(self):
+        return self._community_list(2)
+
+    @property
+    def soo_list(self):
+        return self._community_list(3)
+

 class _ExtendedCommunity(StringifyMixin, _TypeDisp, _Value):
     _PACK_STR = '!B7s'  # type high (+ type low) + value
@@ -1716,6 +1820,9 @@ class BGPPathAttributeMpReachNLRI(_PathAttribute):
         buf += binnlri
         return buf

+    @property
+    def route_family(self):
+        return _rf_map[(self.afi, self.safi)]

 @_PathAttribute.register_type(BGP_ATTR_TYPE_MP_UNREACH_NLRI)
 class BGPPathAttributeMpUnreachNLRI(_PathAttribute):
@@ -1759,6 +1866,10 @@ class BGPPathAttributeMpUnreachNLRI(_PathAttribute):
         buf += binnlri
         return buf

+    @property
+    def route_family(self):
+        return _rf_map[(self.afi, self.safi)]
+

 class BGPNLRI(IPAddrPrefix):
     pass
diff --git a/ryu/services/protocols/bgp/info_base/base.py 
b/ryu/services/protocols/bgp/info_base/base.py
index c735c30..9d177c5 100644
--- a/ryu/services/protocols/bgp/info_base/base.py
+++ b/ryu/services/protocols/bgp/info_base/base.py
@@ -765,7 +765,7 @@ class Path(object):
         if extcomm_attr is None:
             rts = []
         else:
-            rts = extcomm_attr.rt_list[:]
+            rts = extcomm_attr.rt_list
         return rts

     def has_rts_in(self, interested_rts):
diff --git a/ryu/tests/unit/packet/test_bgp.py 
b/ryu/tests/unit/packet/test_bgp.py
index 6ae8dca..00804dd 100644
--- a/ryu/tests/unit/packet/test_bgp.py
+++ b/ryu/tests/unit/packet/test_bgp.py
@@ -80,10 +80,10 @@ class Test_bgp(unittest.TestCase):
                                                   addr='192.0.2.13')]
         mp_nlri = [
             bgp.LabelledVPNIPAddrPrefix(24, '192.0.9.0',
-                                        route_dist=100,
+                                        route_dist='100:100',
                                         labels=[1, 2, 3]),
             bgp.LabelledVPNIPAddrPrefix(26, '192.0.10.192',
-                                        route_dist=4000000000,
+                                        route_dist='10.0.0.1:10000',
                                         labels=[5, 6, 7, 8]),
         ]
         communities = [
@@ -223,10 +223,10 @@ class Test_bgp(unittest.TestCase):
                                                   addr='192.0.2.13')]
         mp_nlri = [
             bgp.LabelledVPNIPAddrPrefix(24, '192.0.9.0',
-                                        route_dist=100,
+                                        route_dist='100:100',
                                         labels=[1, 2, 3]),
             bgp.LabelledVPNIPAddrPrefix(26, '192.0.10.192',
-                                        route_dist=4000000000,
+                                        route_dist='10.0.0.1:10000',
                                         labels=[5, 6, 7, 8]),
         ]
         communities = [
-- 
1.7.10.4




------------------------------------------------------------------------------
Time is money. Stop wasting it! Get your web API in 5 minutes.
www.restlet.com/download
http://p.sf.net/sfu/restlet
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to