summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ryu/lib/packet/ipv6.py83
-rw-r--r--ryu/tests/unit/packet/test_ipv6.py89
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)