Signed-off-by: TAKAHASHI Minoru <[email protected]>
---
 ryu/lib/packet/ipv6.py             |  159 +++++++++++++++++-
 ryu/tests/unit/packet/test_ipv6.py |  319 ++++++++++++++++++++++++++++++++++++
 2 files changed, 476 insertions(+), 2 deletions(-)

diff --git a/ryu/lib/packet/ipv6.py b/ryu/lib/packet/ipv6.py
index b02ca51..20e5234 100644
--- a/ryu/lib/packet/ipv6.py
+++ b/ryu/lib/packet/ipv6.py
@@ -167,8 +167,6 @@ class header(stringify.StringifyMixin):
     def __len__(self):
         pass

-# TODO: implement a class for routing header
-

 class opt_header(header):
     """an abstract class for Hop-by-Hop Options header and destination
@@ -330,6 +328,163 @@ class option(stringify.StringifyMixin):
         return self._MIN_LEN + self.len_


[email protected]_header_type(inet.IPPROTO_ROUTING)
+class routing(header):
+    """An IPv6 Routing Header decoder class.
+    This class has only the parser method.
+
+    IPv6 Routing Header types.
+
+    http://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml
+
+    +-----------+----------------------------------+-------------------+
+    | Value     | Description                      | Reference         |
+    +===========+==================================+===================+
+    | 0         | Source Route (DEPRECATED)        | [[IPV6]][RFC5095] |
+    +-----------+----------------------------------+-------------------+
+    | 1         | Nimrod (DEPRECATED 2009-05-06)   |                   |
+    +-----------+----------------------------------+-------------------+
+    | 2         | Type 2 Routing Header            | [RFC6275]         |
+    +-----------+----------------------------------+-------------------+
+    | 3         | RPL Source Route Header          | [RFC6554]         |
+    +-----------+----------------------------------+-------------------+
+    | 4 - 252   | Unassigned                       |                   |
+    +-----------+----------------------------------+-------------------+
+    | 253       | RFC3692-style Experiment 1 [2]   | [RFC4727]         |
+    +-----------+----------------------------------+-------------------+
+    | 254       | RFC3692-style Experiment 2 [2]   | [RFC4727]         |
+    +-----------+----------------------------------+-------------------+
+    | 255       | Reserved                         |                   |
+    +-----------+----------------------------------+-------------------+
+    """
+
+    TYPE = inet.IPPROTO_ROUTING
+
+    _OFFSET_LEN = struct.calcsize('!2B')
+
+    # IPv6 Routing Header Type
+    ROUTING_TYPE_2 = 0x02
+    ROUTING_TYPE_3 = 0x03
+
+    @classmethod
+    def parser(cls, buf):
+        (type_, ) = struct.unpack_from('!B', buf, cls._OFFSET_LEN)
+        switch = {
+            # TODO: make parsers of type2.
+            cls.ROUTING_TYPE_2: None,
+            cls.ROUTING_TYPE_3: routing_type3
+        }
+        cls_ = switch.get(type_)
+        if cls_:
+            return cls_.parser(buf)
+        else:
+            return None
+
+
+class routing_type3(header):
+    """
+    An IPv6 Routing Header for Source Routes with the RPL (RFC 6554)
+    encoder/decoder class.
+
+    This is used with ryu.lib.packet.ipv6.ipv6.
+
+    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 corresponding args in this order.
+
+    .. tabularcolumns:: |l|L|
+
+    ============== =======================================
+    Attribute      Description
+    ============== =======================================
+    nxt            Next Header
+    size           The length of the Routing header,
+                   not include the first 8 octet.
+                   (0 means automatically-calculate when encoding)
+    type           Identifies the particular Routing header variant.
+    seg            Number of route segments remaining.
+    cmpi           Number of prefix octets from segments 1 through n-1.
+    cmpe           Number of prefix octets from segment n.
+    pad            Number of octets that are used for padding
+                   after Address[n] at the end of the SRH.
+    adrs           Vector of addresses, numbered 1 to n.
+    ============== =======================================
+    """
+
+    _PACK_STR = '!BBBBBB2x'
+    _MIN_LEN = struct.calcsize(_PACK_STR)
+
+    def __init__(self, nxt=inet.IPPROTO_TCP, size=0,
+                 type_=3, seg=0, cmpi=0, cmpe=0, adrs=None):
+        super(routing_type3, self).__init__(nxt)
+        self.size = size
+        self.type_ = type_
+        self.seg = seg
+        self.cmpi = cmpi
+        self.cmpe = cmpe
+        adrs = adrs or []
+        assert isinstance(adrs, list)
+        self.adrs = adrs
+        self._pad = (8 - ((len(self.adrs) - 1) * (16 - self.cmpi) +
+                    (16 - self.cmpe) % 8)) % 8
+
+    @classmethod
+    def _get_size(cls, size):
+        return (int(size) + 1) * 8
+
+    @classmethod
+    def parser(cls, buf):
+        (nxt, size, type_, seg, cmp_, pad) = struct.unpack_from(
+            cls._PACK_STR, buf)
+        data = cls._MIN_LEN
+        header_len = cls._get_size(size)
+        cmpi = int(cmp_ >> 4)
+        cmpe = int(cmp_ & 0xf)
+        pad = int(pad >> 4)
+        adrs = []
+        if size:
+            # Address[1..n-1] has size (16 - CmprI) octets
+            adrs_len_i = 16 - cmpi
+            # Address[n] has size (16 - CmprE) octets
+            adrs_len_e = 16 - cmpe
+            form_i = "%ds" % adrs_len_i
+            form_e = "%ds" % adrs_len_e
+            while data < (header_len - (adrs_len_e + pad)):
+                (adr, ) = struct.unpack_from(form_i, buf[data:])
+                adr = ('\x00' * cmpi) + adr
+                adrs.append(addrconv.ipv6.bin_to_text(adr))
+                data += adrs_len_i
+            (adr, ) = struct.unpack_from(form_e, buf[data:])
+            adr = ('\x00' * cmpe) + adr
+            adrs.append(addrconv.ipv6.bin_to_text(adr))
+        return cls(nxt, size, type_, seg, cmpi, cmpe, adrs)
+
+    def serialize(self):
+        if self.size == 0:
+            self.size = ((len(self.adrs) - 1) * (16 - self.cmpi) +
+                        (16 - self.cmpe) + self._pad) / 8
+        buf = struct.pack(self._PACK_STR, self.nxt, self.size,
+                          self.type_, self.seg, (self.cmpi << 4) | self.cmpe,
+                          self._pad << 4)
+        buf = bytearray(buf)
+        if self.size:
+            form_i = "%ds" % (16 - self.cmpi)
+            form_e = "%ds" % (16 - self.cmpe)
+            slice_i = slice(self.cmpi, 16)
+            slice_e = slice(self.cmpe, 16)
+            for adr in self.adrs[:-1]:
+                buf.extend(
+                    struct.pack(
+                        form_i, addrconv.ipv6.text_to_bin(adr)[slice_i]))
+            buf.extend(struct.pack(
+                form_e,
+                addrconv.ipv6.text_to_bin(self.adrs[-1])[slice_e]))
+        return buf
+
+    def __len__(self):
+        return routing_type3._get_size(self.size)
+
+
 @ipv6.register_header_type(inet.IPPROTO_FRAGMENT)
 class fragment(header):
     """IPv6 (RFC 2460) fragment header encoder/decoder class.
diff --git a/ryu/tests/unit/packet/test_ipv6.py 
b/ryu/tests/unit/packet/test_ipv6.py
index 4229b18..134a1c0 100644
--- a/ryu/tests/unit/packet/test_ipv6.py
+++ b/ryu/tests/unit/packet/test_ipv6.py
@@ -113,6 +113,34 @@ class Test_ipv6(unittest.TestCase):
             addrconv.ipv6.text_to_bin(self.dst))
         self.buf += self.dst_opts.serialize()

+    def setUp_with_routing_type3(self):
+        self.routing_nxt = 6
+        self.routing_size = 6
+        self.routing_type = 3
+        self.routing_seg = 2
+        self.routing_cmpi = 0
+        self.routing_cmpe = 0
+        self.routing_adrs = ["2001:db8:dead::1", "2001:db8:dead::2",
+                             "2001:db8:dead::3"]
+        self.routing = ipv6.routing_type3(
+            self.routing_nxt, self.routing_size,
+            self.routing_type, self.routing_seg,
+            self.routing_cmpi, self.routing_cmpe,
+            self.routing_adrs)
+        self.ext_hdrs = [self.routing]
+        self.payload_length += len(self.routing)
+        self.nxt = ipv6.routing.TYPE
+        self.ip = ipv6.ipv6(
+            self.version, self.traffic_class, self.flow_label,
+            self.payload_length, self.nxt, self.hop_limit, self.src,
+            self.dst, self.ext_hdrs)
+        self.buf = struct.pack(
+            ipv6.ipv6._PACK_STR, self.v_tc_flow,
+            self.payload_length, self.nxt, self.hop_limit,
+            addrconv.ipv6.text_to_bin(self.src),
+            addrconv.ipv6.text_to_bin(self.dst))
+        self.buf += self.routing.serialize()
+
     def setUp_with_fragment(self):
         self.fragment_nxt = 6
         self.fragment_offset = 50
@@ -218,6 +246,10 @@ class Test_ipv6(unittest.TestCase):
         self.setUp_with_dst_opts()
         self.test_init()

+    def test_init_with_routing_type3(self):
+        self.setUp_with_routing_type3()
+        self.test_init()
+
     def test_init_with_fragment(self):
         self.setUp_with_fragment()
         self.test_init()
@@ -255,6 +287,10 @@ class Test_ipv6(unittest.TestCase):
         self.setUp_with_dst_opts()
         self.test_parser()

+    def test_parser_with_routing_type3(self):
+        self.setUp_with_routing_type3()
+        self.test_parser()
+
     def test_parser_with_fragment(self):
         self.setUp_with_fragment()
         self.test_parser()
@@ -301,6 +337,16 @@ class Test_ipv6(unittest.TestCase):
         dst_opts = ipv6.dst_opts.parser(str(buf[ipv6.ipv6._MIN_LEN:]))
         eq_(repr(self.dst_opts), repr(dst_opts))

+    def test_serialize_with_routing_type3(self):
+        self.setUp_with_routing_type3()
+        self.test_serialize()
+
+        data = bytearray()
+        prev = None
+        buf = self.ip.serialize(data, prev)
+        routing = ipv6.routing.parser(str(buf[ipv6.ipv6._MIN_LEN:]))
+        eq_(repr(self.routing), repr(routing))
+
     def test_serialize_with_fragment(self):
         self.setUp_with_fragment()
         self.test_serialize()
@@ -384,6 +430,10 @@ class Test_ipv6(unittest.TestCase):
         self.setUp_with_dst_opts()
         eq_(len(self.ip), 40 + len(self.dst_opts))

+    def test_len_with_routing_type3(self):
+        self.setUp_with_routing_type3()
+        eq_(len(self.ip), 40 + len(self.routing))
+
     def test_len_with_fragment(self):
         self.setUp_with_fragment()
         eq_(len(self.ip), 40 + len(self.fragment))
@@ -438,6 +488,10 @@ class Test_ipv6(unittest.TestCase):
         self.setUp_with_dst_opts()
         self.test_json()

+    def test_json_with_routing_type3(self):
+        self.setUp_with_routing_type3()
+        self.test_json()
+
     def test_json_with_fragment(self):
         self.setUp_with_fragment()
         self.test_json()
@@ -692,6 +746,271 @@ class Test_option_padN(Test_option):
         eq_(self.len_, res[1])


+class Test_routing(unittest.TestCase):
+
+    def setUp(self):
+        self.nxt = 0
+        self.size = 6
+        self.type_ = ipv6.routing.ROUTING_TYPE_3
+        self.seg = 0
+        self.cmpi = 0
+        self.cmpe = 0
+        self.adrs = ["2001:db8:dead::1",
+                     "2001:db8:dead::2",
+                     "2001:db8:dead::3"]
+        # calculate pad
+        self.pad = (8 - ((len(self.adrs) - 1) * (16 - self.cmpi) +
+                   (16 - self.cmpe) % 8)) % 8
+        # create buf
+        self.form = '!BBBBBB2x16s16s16s'
+        self.buf = struct.pack(self.form, self.nxt, self.size,
+                               self.type_, self.seg,
+                               (self.cmpi << 4) | self.cmpe,
+                               self.pad << 4,
+                               addrconv.ipv6.text_to_bin(self.adrs[0]),
+                               addrconv.ipv6.text_to_bin(self.adrs[1]),
+                               addrconv.ipv6.text_to_bin(self.adrs[2]))
+
+    def tearDown(self):
+        pass
+
+    def test_parser(self):
+        _res = ipv6.routing.parser(self.buf)
+        if type(_res) is tuple:
+            res = _res[0]
+        else:
+            res = _res
+        eq_(self.nxt, res.nxt)
+        eq_(self.size, res.size)
+        eq_(self.type_, res.type_)
+        eq_(self.seg, res.seg)
+        eq_(self.cmpi, res.cmpi)
+        eq_(self.cmpe, res.cmpe)
+        eq_(self.pad, res._pad)
+        eq_(self.adrs[0], res.adrs[0])
+        eq_(self.adrs[1], res.adrs[1])
+        eq_(self.adrs[2], res.adrs[2])
+
+    def test_not_implemented_type(self):
+        not_implemented_buf = struct.pack(
+            '!BBBBBB2x', 0, 6, ipv6.routing.ROUTING_TYPE_2, 0, 0, 0)
+        instance = ipv6.routing.parser(not_implemented_buf)
+        assert None == instance
+
+    def test_invalid_type(self):
+        invalid_type = 99
+        invalid_buf = struct.pack('!BBBBBB2x', 0, 6, invalid_type, 0, 0, 0)
+        instance = ipv6.routing.parser(invalid_buf)
+        assert None == instance
+
+
+class Test_routing_type3(unittest.TestCase):
+
+    def setUp(self):
+        self.nxt = 0
+        self.size = 6
+        self.type_ = 3
+        self.seg = 0
+        self.cmpi = 0
+        self.cmpe = 0
+        self.adrs = ["2001:db8:dead::1",
+                     "2001:db8:dead::2",
+                     "2001:db8:dead::3"]
+        # calculate pad
+        self.pad = (8 - ((len(self.adrs) - 1) * (16 - self.cmpi) +
+                   (16 - self.cmpe) % 8)) % 8
+
+        self.routing = ipv6.routing_type3(
+            self.nxt, self.size, self.type_, self.seg, self.cmpi,
+            self.cmpe, self.adrs)
+        self.form = '!BBBBBB2x16s16s16s'
+        self.buf = struct.pack(self.form, self.nxt, self.size,
+                               self.type_, self.seg,
+                               (self.cmpi << 4) | self.cmpe,
+                               self.pad << 4,
+                               addrconv.ipv6.text_to_bin(self.adrs[0]),
+                               addrconv.ipv6.text_to_bin(self.adrs[1]),
+                               addrconv.ipv6.text_to_bin(self.adrs[2]))
+
+    def test_init(self):
+        eq_(self.nxt, self.routing.nxt)
+        eq_(self.size, self.routing.size)
+        eq_(self.type_, self.routing.type_)
+        eq_(self.seg, self.routing.seg)
+        eq_(self.cmpi, self.routing.cmpi)
+        eq_(self.cmpe, self.routing.cmpe)
+        eq_(self.pad, self.routing._pad)
+        eq_(self.adrs[0], self.routing.adrs[0])
+        eq_(self.adrs[1], self.routing.adrs[1])
+        eq_(self.adrs[2], self.routing.adrs[2])
+
+    def test_parser(self):
+        _res = ipv6.routing.parser(self.buf)
+        if type(_res) is tuple:
+            res = _res[0]
+        else:
+            res = _res
+        eq_(self.nxt, res.nxt)
+        eq_(self.size, res.size)
+        eq_(self.type_, res.type_)
+        eq_(self.seg, res.seg)
+        eq_(self.cmpi, res.cmpi)
+        eq_(self.cmpe, res.cmpe)
+        eq_(self.pad, res._pad)
+        eq_(self.adrs[0], res.adrs[0])
+        eq_(self.adrs[1], res.adrs[1])
+        eq_(self.adrs[2], res.adrs[2])
+
+    def test_serialize(self):
+        buf = self.routing.serialize()
+        res = struct.unpack_from(self.form, str(buf))
+        eq_(self.nxt, res[0])
+        eq_(self.size, res[1])
+        eq_(self.type_, res[2])
+        eq_(self.seg, res[3])
+        eq_(self.cmpi, res[4] >> 4)
+        eq_(self.cmpe, res[4] & 0xf)
+        eq_(self.pad, res[5])
+        eq_(addrconv.ipv6.text_to_bin(self.adrs[0]), res[6])
+        eq_(addrconv.ipv6.text_to_bin(self.adrs[1]), res[7])
+        eq_(addrconv.ipv6.text_to_bin(self.adrs[2]), res[8])
+
+    def test_parser_with_adrs_zero(self):
+        nxt = 0
+        size = 0
+        type_ = 3
+        seg = 0
+        cmpi = 0
+        cmpe = 0
+        adrs = []
+        # calculate pad
+        pad = (8 - ((len(adrs) - 1) * (16 - cmpi) + (16 - cmpe) % 8)) % 8
+
+        form = '!BBBBBB2x'
+        buf = struct.pack(form, nxt, size, type_, seg,
+                          (cmpi << 4) | cmpe, pad << 4)
+        _res = ipv6.routing.parser(buf)
+        if type(_res) is tuple:
+            res = _res[0]
+        else:
+            res = _res
+        eq_(nxt, res.nxt)
+        eq_(size, res.size)
+        eq_(type_, res.type_)
+        eq_(seg, res.seg)
+        eq_(cmpi, res.cmpi)
+        eq_(cmpe, res.cmpe)
+        eq_(pad, res._pad)
+
+    def test_serialize_with_adrs_zero(self):
+        nxt = 0
+        size = 0
+        type_ = 3
+        seg = 0
+        cmpi = 0
+        cmpe = 0
+        adrs = []
+        # calculate pad
+        pad = (8 - ((len(adrs) - 1) * (16 - cmpi) + (16 - cmpe) % 8)) % 8
+        routing = ipv6.routing_type3(
+            nxt, size, type_, seg, cmpi,
+            cmpe, pad)
+        buf = routing.serialize()
+        form = '!BBBBBB2x'
+        res = struct.unpack_from(form, str(buf))
+        eq_(nxt, res[0])
+        eq_(size, res[1])
+        eq_(type_, res[2])
+        eq_(seg, res[3])
+        eq_(cmpi, res[4] >> 4)
+        eq_(cmpe, res[4] & 0xf)
+        eq_(pad, res[5])
+
+    def test_parser_with_compression(self):
+        pass
+        nxt = 0
+        size = 3
+        type_ = 3
+        seg = 0
+        cmpi = 8
+        cmpe = 12
+        adrs = ["2001:0db8:dead:0123:4567:89ab:cdef:0001",
+                "2001:0db8:dead:0123:4567:89ab:cdef:0002",
+                "2001:0db8:dead:0123:4567:89ab:cdef:0003"]
+        # calculate pad
+        pad = (8 - ((len(adrs) - 1) * (16 - cmpi) + (16 - cmpe) % 8)) % 8
+        form = '!BBBBBB2x%ds%ds%ds' % (16-cmpi, 16-cmpi, 16-cmpe)
+        slice_i = slice(cmpi, 16)
+        slice_e = slice(cmpe, 16)
+        buf = struct.pack(form, nxt, size, type_, seg,
+                          (cmpi << 4) | cmpe, pad << 4,
+                          addrconv.ipv6.text_to_bin(adrs[0])[slice_i],
+                          addrconv.ipv6.text_to_bin(adrs[1])[slice_i],
+                          addrconv.ipv6.text_to_bin(adrs[2])[slice_e])
+        _res = ipv6.routing.parser(buf)
+        if type(_res) is tuple:
+            res = _res[0]
+        else:
+            res = _res
+        eq_(nxt, res.nxt)
+        eq_(size, res.size)
+        eq_(type_, res.type_)
+        eq_(seg, res.seg)
+        eq_(cmpi, res.cmpi)
+        eq_(cmpe, res.cmpe)
+        eq_(pad, res._pad)
+        eq_("::4567:89ab:cdef:1", res.adrs[0])
+        eq_("::4567:89ab:cdef:2", res.adrs[1])
+        eq_("::205.239.0.3", res.adrs[2])
+
+    def test_serialize_with_compression(self):
+        nxt = 0
+        size = 3
+        type_ = 3
+        seg = 0
+        cmpi = 8
+        cmpe = 8
+        adrs = ["2001:db8:dead::1",
+                "2001:db8:dead::2",
+                "2001:db8:dead::3"]
+        # calculate pad
+        pad = (8 - ((len(adrs) - 1) * (16 - cmpi) + (16 - cmpe) % 8)) % 8
+        slice_i = slice(cmpi, 16)
+        slice_e = slice(cmpe, 16)
+        routing = ipv6.routing_type3(
+            nxt, size, type_, seg, cmpi, cmpe, adrs)
+        buf = routing.serialize()
+        form = '!BBBBBB2x8s8s8s'
+        res = struct.unpack_from(form, str(buf))
+        eq_(nxt, res[0])
+        eq_(size, res[1])
+        eq_(type_, res[2])
+        eq_(seg, res[3])
+        eq_(cmpi, res[4] >> 4)
+        eq_(cmpe, res[4] & 0xf)
+        eq_(pad, res[5])
+        eq_(addrconv.ipv6.text_to_bin(adrs[0])[slice_i], res[6])
+        eq_(addrconv.ipv6.text_to_bin(adrs[1])[slice_i], res[7])
+        eq_(addrconv.ipv6.text_to_bin(adrs[2])[slice_e], res[8])
+
+    def test_len(self):
+        eq_((6 + 1) * 8, len(self.routing))
+
+    def test_default_args(self):
+        hdr = ipv6.routing_type3()
+        buf = hdr.serialize()
+        LOG.info(repr(buf))
+        res = struct.unpack_from(ipv6.routing_type3._PACK_STR, str(buf))
+        LOG.info(res)
+
+        eq_(res[0], 6)
+        eq_(res[1], 0)
+        eq_(res[2], 3)
+        eq_(res[3], 0)
+        eq_(res[4], (0 << 4) | 0)
+        eq_(res[5], 0)
+
+
 class Test_fragment(unittest.TestCase):

     def setUp(self):
-- 
1.7.10.4


------------------------------------------------------------------------------
Start Your Social Network Today - Download eXo Platform
Build your Enterprise Intranet with eXo Platform Software
Java Based Open Source Intranet - Social, Extensible, Cloud Ready
Get Started Now And Turn Your Intranet Into A Collaboration Platform
http://p.sf.net/sfu/ExoPlatform
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to