diff options
author | Yuichi Ito <ito.yuichi0@gmail.com> | 2013-09-17 13:36:02 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2013-09-24 02:09:14 +0900 |
commit | 40e979b92b129fbeb2983c69b7b274ad0cedd6ec (patch) | |
tree | b4d2bd804d0c32b0851134431884b043acf6fbfc | |
parent | 06e50ce7104f55ffe28a3320605402c69e5c16db (diff) |
packet lib: ipv6: prepare to support extension headers
Signed-off-by: itoyuichi <ito.yuichi0@gmail.com>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r-- | ryu/lib/packet/ipv6.py | 83 | ||||
-rw-r--r-- | ryu/tests/unit/packet/test_ipv6.py | 89 |
2 files changed, 151 insertions, 21 deletions
diff --git a/ryu/lib/packet/ipv6.py b/ryu/lib/packet/ipv6.py index 325f8349..6bcc6086 100644 --- a/ryu/lib/packet/ipv6.py +++ b/ryu/lib/packet/ipv6.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import abc import struct import socket from . import packet_base @@ -21,6 +22,7 @@ from . import icmpv6 from . import tcp from ryu.ofproto import inet from ryu.lib import addrconv +from ryu.lib import stringify IPV6_ADDRESS_PACK_STR = '!16s' @@ -51,14 +53,23 @@ class ipv6(packet_base.PacketBase): hop_limit Hop Limit src Source Address 'ff02::1' dst Destination Address '::' + ext_hdrs Extension Headers ============== ======================================== ================== """ _PACK_STR = '!IHBB16s16s' _MIN_LEN = struct.calcsize(_PACK_STR) + _IPV6_EXT_HEADER_TYPE = {} + + @staticmethod + def register_header_type(type_): + def _register_header_type(cls): + ipv6._IPV6_EXT_HEADER_TYPE[type_] = cls + return cls + return _register_header_type def __init__(self, version, traffic_class, flow_label, payload_length, - nxt, hop_limit, src, dst): + nxt, hop_limit, src, dst, ext_hdrs=[]): super(ipv6, self).__init__() self.version = version self.traffic_class = traffic_class @@ -68,6 +79,19 @@ class ipv6(packet_base.PacketBase): self.hop_limit = hop_limit self.src = src self.dst = dst + if ext_hdrs: + assert isinstance(ext_hdrs, list) + last_hdr = None + for ext_hdr in ext_hdrs: + assert isinstance(ext_hdr, header) + if last_hdr: + ext_hdr.set_nxt(last_hdr.nxt) + last_hdr.nxt = ext_hdr.TYPE + else: + ext_hdr.set_nxt(self.nxt) + self.nxt = ext_hdr.TYPE + last_hdr = ext_hdr + self.ext_hdrs = ext_hdrs @classmethod def parser(cls, buf): @@ -77,11 +101,24 @@ class ipv6(packet_base.PacketBase): traffic_class = (v_tc_flow >> 20) & 0xff flow_label = v_tc_flow & 0xfffff hop_limit = hlim + offset = cls._MIN_LEN + last = nxt + ext_hdrs = [] + while True: + cls_ = cls._IPV6_EXT_HEADER_TYPE.get(last) + if not cls_: + break + hdr = cls_.parser(buf[offset:]) + ext_hdrs.append(hdr) + offset += len(hdr) + last = hdr.nxt + # call ipv6.__init__() using 'nxt' of the last extension + # header that points the next protocol. msg = cls(version, traffic_class, flow_label, payload_length, - nxt, hop_limit, addrconv.ipv6.bin_to_text(src), - addrconv.ipv6.bin_to_text(dst)) - return (msg, ipv6.get_packet_type(nxt), - buf[cls._MIN_LEN:cls._MIN_LEN+payload_length]) + last, hop_limit, addrconv.ipv6.bin_to_text(src), + addrconv.ipv6.bin_to_text(dst), ext_hdrs) + return (msg, ipv6.get_packet_type(last), + buf[offset:offset+payload_length]) def serialize(self, payload, prev): hdr = bytearray(40) @@ -91,7 +128,43 @@ class ipv6(packet_base.PacketBase): self.payload_length, self.nxt, self.hop_limit, addrconv.ipv6.text_to_bin(self.src), addrconv.ipv6.text_to_bin(self.dst)) + if self.ext_hdrs: + for ext_hdr in self.ext_hdrs: + hdr.extend(ext_hdr.serialize()) return hdr + def __len__(self): + ext_hdrs_len = 0 + for ext_hdr in self.ext_hdrs: + ext_hdrs_len += len(ext_hdr) + return self._MIN_LEN + ext_hdrs_len + ipv6.register_packet_type(icmpv6.icmpv6, inet.IPPROTO_ICMPV6) ipv6.register_packet_type(tcp.tcp, inet.IPPROTO_TCP) + + +class header(stringify.StringifyMixin): + """extension header abstract class.""" + + __metaclass__ = abc.ABCMeta + + def __init__(self): + self.nxt = None + + def set_nxt(self, nxt): + self.nxt = nxt + + @classmethod + @abc.abstractmethod + def parser(cls, buf): + pass + + @abc.abstractmethod + def serialize(self): + pass + + @abc.abstractmethod + def __len__(self): + pass + +# TODO: implement a class for routing header diff --git a/ryu/tests/unit/packet/test_ipv6.py b/ryu/tests/unit/packet/test_ipv6.py index 5454fbfd..e4c0e5e1 100644 --- a/ryu/tests/unit/packet/test_ipv6.py +++ b/ryu/tests/unit/packet/test_ipv6.py @@ -17,9 +17,11 @@ import unittest import logging import inspect +import struct from nose.tools import * from nose.plugins.skip import Skip, SkipTest +from ryu.lib import addrconv from ryu.lib import ip from ryu.lib.packet import ipv6 @@ -29,24 +31,75 @@ LOG = logging.getLogger(__name__) class Test_ipv6(unittest.TestCase): - version = 6 - traffic_class = 0 - flow_label = 0 - payload_length = 817 - nxt = 6 - hop_limit = 128 - src = ip.ipv6_to_bin('2002:4637:d5d3::4637:d5d3') - dst = ip.ipv6_to_bin('2001:4860:0:2001::68') - - ip = ipv6.ipv6(version, traffic_class, flow_label, payload_length, - nxt, hop_limit, src, dst) - def setUp(self): - pass + self.version = 6 + self.traffic_class = 0 + self.flow_label = 0 + self.payload_length = 817 + self.nxt = 6 + self.hop_limit = 128 + self.src = '2002:4637:d5d3::4637:d5d3' + self.dst = '2001:4860:0:2001::68' + self.ext_hdrs = [] + 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.v_tc_flow = ( + self.version << 28 | self.traffic_class << 20 | + self.flow_label << 12) + 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)) def tearDown(self): pass + def test_init(self): + eq_(self.version, self.ip.version) + eq_(self.traffic_class, self.ip.traffic_class) + eq_(self.flow_label, self.ip.flow_label) + eq_(self.payload_length, self.ip.payload_length) + eq_(self.nxt, self.ip.nxt) + eq_(self.hop_limit, self.ip.hop_limit) + eq_(self.src, self.ip.src) + eq_(self.dst, self.ip.dst) + eq_(str(self.ext_hdrs), str(self.ip.ext_hdrs)) + + def test_parser(self): + _res = self.ip.parser(str(self.buf)) + if type(_res) is tuple: + res = _res[0] + else: + res = _res + + eq_(self.version, res.version) + eq_(self.traffic_class, res.traffic_class) + eq_(self.flow_label, res.flow_label) + eq_(self.payload_length, res.payload_length) + eq_(self.nxt, res.nxt) + eq_(self.hop_limit, res.hop_limit) + eq_(self.src, res.src) + eq_(self.dst, res.dst) + eq_(str(self.ext_hdrs), str(res.ext_hdrs)) + + def test_serialize(self): + data = bytearray() + prev = None + buf = self.ip.serialize(data, prev) + + res = struct.unpack_from(ipv6.ipv6._PACK_STR, str(buf)) + + eq_(self.v_tc_flow, res[0]) + eq_(self.payload_length, res[1]) + eq_(self.nxt, res[2]) + eq_(self.hop_limit, res[3]) + eq_(self.src, addrconv.ipv6.bin_to_text(res[4])) + eq_(self.dst, addrconv.ipv6.bin_to_text(res[5])) + def test_to_string(self): ipv6_values = {'version': self.version, 'traffic_class': self.traffic_class, @@ -54,12 +107,16 @@ class Test_ipv6(unittest.TestCase): 'payload_length': self.payload_length, 'nxt': self.nxt, 'hop_limit': self.hop_limit, - 'src': self.src, - 'dst': self.dst} - _ipv6_str = ','.join(['%s=%s' % (k, repr(ipv6_values[k])) + 'src': repr(self.src), + 'dst': repr(self.dst), + 'ext_hdrs': self.ext_hdrs} + _ipv6_str = ','.join(['%s=%s' % (k, ipv6_values[k]) for k, v in inspect.getmembers(self.ip) if k in ipv6_values]) ipv6_str = '%s(%s)' % (ipv6.ipv6.__name__, _ipv6_str) eq_(str(self.ip), ipv6_str) eq_(repr(self.ip), ipv6_str) + + def test_len(self): + eq_(len(self.ip), 40) |