the purpose is to simplify the creation of the nd_router_advert object.
Signed-off-by: Yuichi Ito <[email protected]>
---
ryu/lib/packet/icmpv6.py | 231 ++++++++++++++++------------------
ryu/tests/unit/packet/test_icmpv6.py | 66 +++++-----
2 files changed, 139 insertions(+), 158 deletions(-)
diff --git a/ryu/lib/packet/icmpv6.py b/ryu/lib/packet/icmpv6.py
index 0353949..7d9f814 100644
--- a/ryu/lib/packet/icmpv6.py
+++ b/ryu/lib/packet/icmpv6.py
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import abc
import struct
import sys
import array
@@ -157,14 +158,8 @@ class nd_neighbor(stringify.StringifyMixin):
res R,S,O Flags for Neighbor Advertisement. \
The 3 MSBs of "Reserved" field for Neighbor Solicitation.
dst Target Address
- type\_ "Type" field of the first option. None if no options. \
- NOTE: This implementation doesn't support two or more \
- options.
- length "Length" field of the first option. None if no options.
- data An object to describe the first option. \
- None if no options. \
- Either ryu.lib.packet.icmpv6.nd_option_la object \
- or a bytearray.
+ option a derived object of ryu.lib.packet.icmpv6.nd_option \
+ or a bytearray. None if no options.
============== ====================
"""
@@ -180,27 +175,24 @@ class nd_neighbor(stringify.StringifyMixin):
return cls
return _register_nd_option_type
- def __init__(self, res, dst, type_=None, length=None, data=None):
+ def __init__(self, res, dst, option=None):
self.res = res
self.dst = dst
- self.type_ = type_
- self.length = length
- self.data = data
+ self.option = option
@classmethod
def parser(cls, buf, offset):
(res, dst) = struct.unpack_from(cls._PACK_STR, buf, offset)
- msg = cls(res >> 29, addrconv.ipv6.bin_to_text(dst))
offset += cls._MIN_LEN
+ option = None
if len(buf) > offset:
- (msg.type_, msg.length) = struct.unpack_from('!BB', buf, offset)
- cls_ = cls._ND_OPTION_TYPES.get(msg.type_, None)
- offset += 2
- if cls_:
- msg.data = cls_.parser(buf, offset)
+ (type_, ) = struct.unpack_from('!B', buf, offset)
+ cls_ = cls._ND_OPTION_TYPES.get(type_)
+ if cls_ is not None:
+ option = cls_.parser(buf, offset)
else:
- msg.data = buf[offset:]
-
+ option = buf[offset:]
+ msg = cls(res >> 29, addrconv.ipv6.bin_to_text(dst), option)
return msg
def serialize(self):
@@ -208,14 +200,12 @@ class nd_neighbor(stringify.StringifyMixin):
hdr = bytearray(struct.pack(
nd_neighbor._PACK_STR, res,
addrconv.ipv6.text_to_bin(self.dst)))
- if self.type_ is not None:
- hdr += bytearray(struct.pack('!BB', self.type_, self.length))
- if self.type_ in nd_neighbor._ND_OPTION_TYPES:
- hdr += self.data.serialize()
- elif self.data is not None:
- hdr += bytearray(self.data)
-
- return hdr
+ if self.option is not None:
+ if isinstance(self.option, nd_option):
+ hdr.extend(self.option.serialize())
+ else:
+ hdr.extend(self.option)
+ return str(hdr)
@icmpv6.register_icmpv6_type(ND_ROUTER_SOLICIT)
@@ -235,14 +225,8 @@ class nd_router_solicit(stringify.StringifyMixin):
Attribute Description
============== ====================
res This field is unused. It MUST be initialized to zero.
- type\_ "Type" field of the first option. None if no options. \
- NOTE: This implementation doesn't support two or more \
- options.
- length "Length" field of the first option. None if no options.
- data An object to describe the first option. \
- None if no options. \
- Either ryu.lib.packet.icmpv6.nd_option_la object \
- or a bytearray.
+ option a derived object of ryu.lib.packet.icmpv6.nd_option \
+ or a bytearray. None if no options.
============== ====================
"""
@@ -258,39 +242,34 @@ class nd_router_solicit(stringify.StringifyMixin):
return cls
return _register_nd_option_type
- def __init__(self, res, type_=None, length=None, data=None):
+ def __init__(self, res, option=None):
self.res = res
- self.type_ = type_
- self.length = length
- self.data = data
+ self.option = option
@classmethod
def parser(cls, buf, offset):
- res = struct.unpack_from(cls._PACK_STR, buf, offset)
- msg = cls(res)
+ (res, ) = struct.unpack_from(cls._PACK_STR, buf, offset)
offset += cls._MIN_LEN
+ option = None
if len(buf) > offset:
- (msg.type_, msg.length) = struct.unpack_from('!BB', buf, offset)
- cls_ = cls._ND_OPTION_TYPES.get(msg.type_, None)
- offset += 2
- if cls_:
- msg.data = cls_.parser(buf, offset)
+ (type_, ) = struct.unpack_from('!B', buf, offset)
+ cls_ = cls._ND_OPTION_TYPES.get(type_)
+ if cls_ is not None:
+ option = cls_.parser(buf, offset)
else:
- msg.data = buf[offset:]
-
+ option = buf[offset:]
+ msg = cls(res, option)
return msg
def serialize(self):
- hdr = bytearray(struct.pack(nd_router_solicit._PACK_STR, self.res))
-
- if self.type_ is not None:
- hdr += bytearray(struct.pack('!BB', self.type_, self.length))
- if self.type_ in nd_router_solicit._ND_OPTION_TYPES:
- hdr += self.data.serialize()
- elif self.data is not None:
- hdr += bytearray(self.data)
-
- return hdr
+ hdr = bytearray(struct.pack(
+ nd_router_solicit._PACK_STR, self.res))
+ if self.option is not None:
+ if isinstance(self.option, nd_option):
+ hdr.extend(self.option.serialize())
+ else:
+ hdr.extend(self.option)
+ return str(hdr)
@icmpv6.register_icmpv6_type(ND_ROUTER_ADVERT)
@@ -314,17 +293,9 @@ class nd_router_advert(stringify.StringifyMixin):
rou_l Router Lifetime.
rea_t Reachable Time.
ret_t Retrans Timer.
- type\_ List of option type. Each index refers to an option. \
- None if no options. \
- NOTE: This implementation support one or more \
- options.
- length List of option length. Each index refers to an option. \
- None if no options. \
- data List of option data. Each index refers to an option. \
- None if no options. \
- ryu.lib.packet.icmpv6.nd_option_la object, \
- ryu.lib.packet.icmpv6.nd_option_pi object \
- or a bytearray.
+ options List of a derived object of \
+ ryu.lib.packet.icmpv6.nd_option or a bytearray. \
+ None if no options.
============== ====================
"""
@@ -340,41 +311,32 @@ class nd_router_advert(stringify.StringifyMixin):
return cls
return _register_nd_option_type
- def __init__(self, ch_l, res, rou_l, rea_t, ret_t, type_=None, length=None,
- data=None):
+ def __init__(self, ch_l, res, rou_l, rea_t, ret_t, options=None):
self.ch_l = ch_l
self.res = res
self.rou_l = rou_l
self.rea_t = rea_t
self.ret_t = ret_t
- self.type_ = type_
- self.length = length
- self.data = data
+ options = options or []
+ assert isinstance(options, list)
+ self.options = options
@classmethod
def parser(cls, buf, offset):
- (ch_l, res, rou_l, rea_t, ret_t) = struct.unpack_from(cls._PACK_STR,
- buf, offset)
- msg = cls(ch_l, res >> 6, rou_l, rea_t, ret_t)
+ (ch_l, res, rou_l, rea_t, ret_t
+ ) = struct.unpack_from(cls._PACK_STR, buf, offset)
offset += cls._MIN_LEN
- msg.type_ = list()
- msg.length = list()
- msg.data = list()
+ options = []
while len(buf) > offset:
(type_, length) = struct.unpack_from('!BB', buf, offset)
- msg.type_.append(type_)
- msg.length.append(length)
- cls_ = cls._ND_OPTION_TYPES.get(type_, None)
- offset += 2
- byte_len = length * 8 - 2
- if cls_:
- msg.data.append(cls_.parser(buf[:offset+cls_._MIN_LEN],
- offset))
- offset += cls_._MIN_LEN
+ cls_ = cls._ND_OPTION_TYPES.get(type_)
+ if cls_ is not None:
+ option = cls_.parser(buf, offset)
else:
- msg.data.append(buf[offset:offset + byte_len])
- offset += byte_len
-
+ option = buf[offset:offset + (length * 8 - 2)]
+ options.append(option)
+ offset += len(option)
+ msg = cls(ch_l, res >> 6, rou_l, rea_t, ret_t, options)
return msg
def serialize(self):
@@ -382,22 +344,37 @@ class nd_router_advert(stringify.StringifyMixin):
hdr = bytearray(struct.pack(
nd_router_advert._PACK_STR, self.ch_l, res, self.rou_l,
self.rea_t, self.ret_t))
- if self.type_ is not None:
- for i in range(len(self.type_)):
- hdr += bytearray(struct.pack('!BB', self.type_[i],
- self.length[i]))
- if self.type_[i] in nd_router_advert._ND_OPTION_TYPES:
- hdr += self.data[i].serialize()
- elif self.data[i] is not None:
- hdr += bytearray(self.data[i])
+ for option in self.options:
+ if isinstance(option, nd_option):
+ hdr.extend(option.serialize())
+ else:
+ hdr.extend(option)
+ return str(hdr)
- return hdr
+
+class nd_option(stringify.StringifyMixin):
+
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ def __init__(self, type_, length):
+ self.type_ = type_
+ self.length = length
+
+ @classmethod
+ @abc.abstractmethod
+ def parser(cls, buf):
+ pass
+
+ @abc.abstractmethod
+ def serialize(self):
+ pass
@nd_neighbor.register_nd_option_type(ND_OPTION_SLA, ND_OPTION_TLA)
@nd_router_solicit.register_nd_option_type(ND_OPTION_SLA)
@nd_router_advert.register_nd_option_type(ND_OPTION_SLA)
-class nd_option_la(stringify.StringifyMixin):
+class nd_option_la(nd_option):
"""ICMPv6 sub encoder/decoder class for Neighbor discovery
Source/Target Link-Layer Address Option. (RFC 4861)
@@ -414,6 +391,8 @@ class nd_option_la(stringify.StringifyMixin):
============== ====================
Attribute Description
============== ====================
+ type\_ type of the option.
+ length length of the option.
hw_src Link-Layer Address. \
NOTE: If the address is longer than 6 octets this contains \
the first 6 octets in the address. \
@@ -426,17 +405,19 @@ class nd_option_la(stringify.StringifyMixin):
============== ====================
"""
- _PACK_STR = '!6s'
+ _PACK_STR = '!BB6s'
_MIN_LEN = struct.calcsize(_PACK_STR)
- def __init__(self, hw_src, data=None):
+ def __init__(self, type_, length, hw_src, data=None):
+ super(nd_option_la, self).__init__(type_, length)
self.hw_src = hw_src
self.data = data
@classmethod
def parser(cls, buf, offset):
- (hw_src, ) = struct.unpack_from(cls._PACK_STR, buf, offset)
- msg = cls(addrconv.mac.bin_to_text(hw_src))
+ (type_, length, hw_src
+ ) = struct.unpack_from(cls._PACK_STR, buf, offset)
+ msg = cls(type_, length, addrconv.mac.bin_to_text(hw_src))
offset += cls._MIN_LEN
if len(buf) > offset:
msg.data = buf[offset:]
@@ -444,17 +425,19 @@ class nd_option_la(stringify.StringifyMixin):
return msg
def serialize(self):
- hdr = bytearray(struct.pack(self._PACK_STR,
- addrconv.mac.text_to_bin(self.hw_src)))
-
+ buf = bytearray(struct.pack(
+ self._PACK_STR, self.type_, self.length,
+ addrconv.mac.text_to_bin(self.hw_src)))
if self.data is not None:
- hdr += bytearray(self.data)
-
- return hdr
+ buf.extend(self.data)
+ mod = len(buf) % 8
+ if mod:
+ buf.extend(bytearray(8 - mod))
+ return str(buf)
@nd_router_advert.register_nd_option_type(ND_OPTION_PI)
-class nd_option_pi(stringify.StringifyMixin):
+class nd_option_pi(nd_option):
"""ICMPv6 sub encoder/decoder class for Neighbor discovery
Prefix Information Option. (RFC 4861)
@@ -469,6 +452,8 @@ class nd_option_pi(stringify.StringifyMixin):
============== ====================
Attribute Description
============== ====================
+ type\_ type of the option.
+ length length of the option.
pl Prefix Length.
res1 L,A,R\* Flags for Prefix Information.
val_l Valid Lifetime.
@@ -483,7 +468,8 @@ class nd_option_pi(stringify.StringifyMixin):
_PACK_STR = '!BBIII16s'
_MIN_LEN = struct.calcsize(_PACK_STR)
- def __init__(self, pl, res1, val_l, pre_l, res2, prefix):
+ def __init__(self, type_, length, pl, res1, val_l, pre_l, res2, prefix):
+ super(nd_option_pi, self).__init__(self, type_, length)
self.pl = pl
self.res1 = res1
self.val_l = val_l
@@ -493,20 +479,19 @@ class nd_option_pi(stringify.StringifyMixin):
@classmethod
def parser(cls, buf, offset):
- (pl, res1, val_l, pre_l, res2, prefix) = struct.unpack_from(cls.
- _PACK_STR,
- buf,
- offset)
- msg = cls(pl, res1 >> 5, val_l, pre_l, res2,
+ (type_, length, pl, res1, val_l, pre_l, res2, prefix
+ ) = struct.unpack_from(cls._PACK_STR, buf, offset)
+ msg = cls(type_, length, pl, res1 >> 5, val_l, pre_l, res2,
addrconv.ipv6.bin_to_text(prefix))
return msg
def serialize(self):
res1 = self.res1 << 5
- hdr = bytearray(struct.pack(self._PACK_STR, self.pl, res1,
- self.val_l, self.pre_l, self.res2,
- addrconv.ipv6.text_to_bin(self.prefix)))
+ hdr = bytearray(struct.pack(
+ self._PACK_STR, self.type_, self.length, self.pl,
+ res1, self.val_l, self.pre_l, self.res2,
+ addrconv.ipv6.text_to_bin(self.prefix)))
return hdr
diff --git a/ryu/tests/unit/packet/test_icmpv6.py
b/ryu/tests/unit/packet/test_icmpv6.py
index c24839c..520d64e 100644
--- a/ryu/tests/unit/packet/test_icmpv6.py
+++ b/ryu/tests/unit/packet/test_icmpv6.py
@@ -216,9 +216,7 @@ class Test_icmpv6_neighbor_solicit(unittest.TestCase):
nd = icmpv6.nd_neighbor(self.res, self.dst)
eq_(nd.res, self.res)
eq_(nd.dst, self.dst)
- eq_(nd.type_, None)
- eq_(nd.length, None)
- eq_(nd.data, None)
+ eq_(nd.option, None)
def _test_parser(self, data=None):
buf = self.buf + str(data or '')
@@ -232,11 +230,11 @@ class Test_icmpv6_neighbor_solicit(unittest.TestCase):
addrconv.ipv6.text_to_bin(self.dst))
eq_(n, None)
if data:
- nd = msg.data
+ nd = msg.data.option
eq_(nd.type_, self.nd_type)
eq_(nd.length, self.nd_length)
- eq_(nd.data.hw_src, self.nd_hw_src)
- eq_(nd.data.data, None)
+ eq_(nd.hw_src, self.nd_hw_src)
+ eq_(nd.data, None)
def test_parser_without_data(self):
self._test_parser()
@@ -264,9 +262,9 @@ class Test_icmpv6_neighbor_solicit(unittest.TestCase):
eq_(data, '')
def test_serialize_with_data(self):
- nd_opt = icmpv6.nd_option_la(self.nd_hw_src)
- nd = icmpv6.nd_neighbor(
- self.res, self.dst, self.nd_type, self.nd_length, nd_opt)
+ nd_opt = icmpv6.nd_option_la(
+ self.nd_type, self.nd_length, self.nd_hw_src)
+ nd = icmpv6.nd_neighbor(self.res, self.dst, nd_opt)
prev = ipv6(6, 0, 0, 32, 64, 255, self.src_ipv6, self.dst_ipv6)
nd_csum = icmpv6_csum(prev, self.buf + self.data)
@@ -276,7 +274,7 @@ class Test_icmpv6_neighbor_solicit(unittest.TestCase):
(type_, code, csum) = struct.unpack_from(icmp._PACK_STR, buf, 0)
(res, dst) = struct.unpack_from(nd._PACK_STR, buf, icmp._MIN_LEN)
(nd_type, nd_length, nd_hw_src) = struct.unpack_from(
- '!BB6s', buf, icmp._MIN_LEN + nd._MIN_LEN)
+ nd_opt._PACK_STR, buf, icmp._MIN_LEN + nd._MIN_LEN)
data = buf[(icmp._MIN_LEN + nd._MIN_LEN + 8):]
eq_(type_, self.type_)
@@ -289,12 +287,14 @@ class Test_icmpv6_neighbor_solicit(unittest.TestCase):
eq_(nd_hw_src, addrconv.mac.text_to_bin(self.nd_hw_src))
def test_to_string(self):
- nd_opt = icmpv6.nd_option_la(self.nd_hw_src)
- nd = icmpv6.nd_neighbor(
- self.res, self.dst, self.nd_type, self.nd_length, nd_opt)
+ nd_opt = icmpv6.nd_option_la(
+ self.nd_type, self.nd_length, self.nd_hw_src)
+ nd = icmpv6.nd_neighbor(self.res, self.dst, nd_opt)
ic = icmpv6.icmpv6(self.type_, self.code, self.csum, nd)
- nd_opt_values = {'hw_src': self.nd_hw_src,
+ nd_opt_values = {'type_': self.nd_type,
+ 'length': self.nd_length,
+ 'hw_src': self.nd_hw_src,
'data': None}
_nd_opt_str = ','.join(['%s=%s' % (k, repr(nd_opt_values[k]))
for k, v in inspect.getmembers(nd_opt)
@@ -303,9 +303,7 @@ class Test_icmpv6_neighbor_solicit(unittest.TestCase):
nd_values = {'res': repr(nd.res),
'dst': repr(self.dst),
- 'type_': repr(self.nd_type),
- 'length': repr(self.nd_length),
- 'data': nd_opt_str}
+ 'option': nd_opt_str}
_nd_str = ','.join(['%s=%s' % (k, nd_values[k])
for k, v in inspect.getmembers(nd)
if k in nd_values])
@@ -362,9 +360,7 @@ class Test_icmpv6_router_solicit(unittest.TestCase):
def test_init(self):
rs = icmpv6.nd_router_solicit(self.res)
eq_(rs.res, self.res)
- eq_(rs.type_, None)
- eq_(rs.length, None)
- eq_(rs.data, None)
+ eq_(rs.option, None)
def _test_parser(self, data=None):
buf = self.buf + str(data or '')
@@ -374,14 +370,14 @@ class Test_icmpv6_router_solicit(unittest.TestCase):
eq_(msg.code, self.code)
eq_(msg.csum, self.csum)
if data is not None:
- eq_(msg.data.res[0], self.res)
+ eq_(msg.data.res, self.res)
eq_(n, None)
if data:
- rs = msg.data
+ rs = msg.data.option
eq_(rs.type_, self.nd_type)
eq_(rs.length, self.nd_length)
- eq_(rs.data.hw_src, self.nd_hw_src)
- eq_(rs.data.data, None)
+ eq_(rs.hw_src, self.nd_hw_src)
+ eq_(rs.data, None)
def test_parser_without_data(self):
self._test_parser()
@@ -408,9 +404,9 @@ class Test_icmpv6_router_solicit(unittest.TestCase):
eq_(data, '')
def test_serialize_with_data(self):
- nd_opt = icmpv6.nd_option_la(self.nd_hw_src)
- rs = icmpv6.nd_router_solicit(self.res, self.nd_type, self.nd_length,
- nd_opt)
+ nd_opt = icmpv6.nd_option_la(
+ self.nd_type, self.nd_length, self.nd_hw_src)
+ rs = icmpv6.nd_router_solicit(self.res, nd_opt)
prev = ipv6(6, 0, 0, 16, 64, 255, self.src_ipv6, self.dst_ipv6)
rs_csum = icmpv6_csum(prev, self.buf + self.data)
@@ -420,7 +416,7 @@ class Test_icmpv6_router_solicit(unittest.TestCase):
(type_, code, csum) = struct.unpack_from(icmp._PACK_STR, buf, 0)
res = struct.unpack_from(rs._PACK_STR, buf, icmp._MIN_LEN)
(nd_type, nd_length, nd_hw_src) = struct.unpack_from(
- '!BB6s', buf, icmp._MIN_LEN + rs._MIN_LEN)
+ nd_opt._PACK_STR, buf, icmp._MIN_LEN + rs._MIN_LEN)
data = buf[(icmp._MIN_LEN + rs._MIN_LEN + 8):]
eq_(type_, self.type_)
@@ -432,12 +428,14 @@ class Test_icmpv6_router_solicit(unittest.TestCase):
eq_(nd_hw_src, addrconv.mac.text_to_bin(self.nd_hw_src))
def test_to_string(self):
- nd_opt = icmpv6.nd_option_la(self.nd_hw_src)
- rs = icmpv6.nd_router_solicit(
- self.res, self.nd_type, self.nd_length, nd_opt)
+ nd_opt = icmpv6.nd_option_la(
+ self.nd_type, self.nd_length, self.nd_hw_src)
+ rs = icmpv6.nd_router_solicit(self.res, nd_opt)
ic = icmpv6.icmpv6(self.type_, self.code, self.csum, rs)
- nd_opt_values = {'hw_src': self.nd_hw_src,
+ nd_opt_values = {'type_': self.nd_type,
+ 'length': self.nd_length,
+ 'hw_src': self.nd_hw_src,
'data': None}
_nd_opt_str = ','.join(['%s=%s' % (k, repr(nd_opt_values[k]))
for k, v in inspect.getmembers(nd_opt)
@@ -445,9 +443,7 @@ class Test_icmpv6_router_solicit(unittest.TestCase):
nd_opt_str = '%s(%s)' % (icmpv6.nd_option_la.__name__, _nd_opt_str)
rs_values = {'res': repr(rs.res),
- 'type_': repr(self.nd_type),
- 'length': repr(self.nd_length),
- 'data': nd_opt_str}
+ 'option': nd_opt_str}
_rs_str = ','.join(['%s=%s' % (k, rs_values[k])
for k, v in inspect.getmembers(rs)
if k in rs_values])
--
1.7.10.4
------------------------------------------------------------------------------
November Webinars for C, C++, Fortran Developers
Accelerate application performance with scalable programming models. Explore
techniques for threading, error checking, porting, and tuning. Get the most
from the latest Intel processors and coprocessors. See abstracts and register
http://pubads.g.doubleclick.net/gampad/clk?id=60136231&iu=/4140/ostg.clktrk
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel