4ee2f054d2
Signed-off-by: IWAMOTO Toshihiro <iwamoto@valinux.co.jp> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
948 lines
31 KiB
Python
948 lines
31 KiB
Python
# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
# implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import abc
|
|
import struct
|
|
import six
|
|
import sys
|
|
import array
|
|
import binascii
|
|
|
|
from . import packet_base
|
|
from . import packet_utils
|
|
from ryu.lib import addrconv
|
|
from ryu.lib import stringify
|
|
|
|
ICMPV6_DST_UNREACH = 1 # dest unreachable, codes:
|
|
ICMPV6_PACKET_TOO_BIG = 2 # packet too big
|
|
ICMPV6_TIME_EXCEEDED = 3 # time exceeded, code:
|
|
ICMPV6_PARAM_PROB = 4 # ip6 header bad
|
|
|
|
ICMPV6_ECHO_REQUEST = 128 # echo service
|
|
ICMPV6_ECHO_REPLY = 129 # echo reply
|
|
MLD_LISTENER_QUERY = 130 # multicast listener query
|
|
MLD_LISTENER_REPOR = 131 # multicast listener report
|
|
MLD_LISTENER_DONE = 132 # multicast listener done
|
|
MLDV2_LISTENER_REPORT = 143 # multicast listern report (v2)
|
|
|
|
# RFC2292 decls
|
|
ICMPV6_MEMBERSHIP_QUERY = 130 # group membership query
|
|
ICMPV6_MEMBERSHIP_REPORT = 131 # group membership report
|
|
ICMPV6_MEMBERSHIP_REDUCTION = 132 # group membership termination
|
|
|
|
ND_ROUTER_SOLICIT = 133 # router solicitation
|
|
ND_ROUTER_ADVERT = 134 # router advertisment
|
|
ND_NEIGHBOR_SOLICIT = 135 # neighbor solicitation
|
|
ND_NEIGHBOR_ADVERT = 136 # neighbor advertisment
|
|
ND_REDIREC = 137 # redirect
|
|
|
|
ICMPV6_ROUTER_RENUMBERING = 138 # router renumbering
|
|
|
|
ICMPV6_WRUREQUEST = 139 # who are you request
|
|
ICMPV6_WRUREPLY = 140 # who are you reply
|
|
ICMPV6_FQDN_QUERY = 139 # FQDN query
|
|
ICMPV6_FQDN_REPLY = 140 # FQDN reply
|
|
ICMPV6_NI_QUERY = 139 # node information request
|
|
ICMPV6_NI_REPLY = 140 # node information reply
|
|
|
|
ICMPV6_MAXTYPE = 201
|
|
|
|
# ND_OPTIONS from RFC 4861
|
|
ND_OPTION_SLA = 1 # Source Link-Layer Address
|
|
ND_OPTION_TLA = 2 # Target Link-Layer Address
|
|
ND_OPTION_PI = 3 # Prefix Information
|
|
ND_OPTION_RH = 4 # Redirected Header
|
|
ND_OPTION_MTU = 5 # MTU
|
|
|
|
MODE_IS_INCLUDE = 1
|
|
MODE_IS_EXCLUDE = 2
|
|
CHANGE_TO_INCLUDE_MODE = 3
|
|
CHANGE_TO_EXCLUDE_MODE = 4
|
|
ALLOW_NEW_SOURCES = 5
|
|
BLOCK_OLD_SOURCES = 6
|
|
|
|
|
|
class icmpv6(packet_base.PacketBase):
|
|
"""ICMPv6 (RFC 2463) header encoder/decoder class.
|
|
|
|
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|p{35em}|
|
|
|
|
============== ====================
|
|
Attribute Description
|
|
============== ====================
|
|
type\_ Type
|
|
code Code
|
|
csum CheckSum
|
|
(0 means automatically-calculate when encoding)
|
|
data Payload.
|
|
|
|
ryu.lib.packet.icmpv6.echo object, \
|
|
ryu.lib.packet.icmpv6.nd_neighbor object, \
|
|
ryu.lib.packet.icmpv6.nd_router_solicit object, \
|
|
ryu.lib.packet.icmpv6.nd_router_advert object, \
|
|
ryu.lib.packet.icmpv6.mld object, \
|
|
or a bytearray.
|
|
============== ====================
|
|
"""
|
|
_PACK_STR = '!BBH'
|
|
_MIN_LEN = struct.calcsize(_PACK_STR)
|
|
_ICMPV6_TYPES = {}
|
|
|
|
@staticmethod
|
|
def register_icmpv6_type(*args):
|
|
def _register_icmpv6_type(cls):
|
|
for type_ in args:
|
|
icmpv6._ICMPV6_TYPES[type_] = cls
|
|
return cls
|
|
return _register_icmpv6_type
|
|
|
|
def __init__(self, type_=0, code=0, csum=0, data=None):
|
|
super(icmpv6, self).__init__()
|
|
self.type_ = type_
|
|
self.code = code
|
|
self.csum = csum
|
|
self.data = data
|
|
|
|
@classmethod
|
|
def parser(cls, buf):
|
|
(type_, code, csum) = struct.unpack_from(cls._PACK_STR, buf)
|
|
msg = cls(type_, code, csum)
|
|
offset = cls._MIN_LEN
|
|
if len(buf) > offset:
|
|
cls_ = cls._ICMPV6_TYPES.get(type_, None)
|
|
if cls_:
|
|
msg.data = cls_.parser(buf, offset)
|
|
else:
|
|
msg.data = buf[offset:]
|
|
|
|
return msg, None, None
|
|
|
|
def serialize(self, payload, prev):
|
|
hdr = bytearray(struct.pack(icmpv6._PACK_STR, self.type_,
|
|
self.code, self.csum))
|
|
|
|
if self.data is not None:
|
|
if self.type_ in icmpv6._ICMPV6_TYPES:
|
|
hdr += self.data.serialize()
|
|
else:
|
|
hdr += self.data
|
|
if self.csum == 0:
|
|
self.csum = packet_utils.checksum_ip(prev, len(hdr), hdr + payload)
|
|
struct.pack_into('!H', hdr, 2, self.csum)
|
|
|
|
return hdr
|
|
|
|
def __len__(self):
|
|
length = self._MIN_LEN
|
|
if self.data is not None:
|
|
length += len(self.data)
|
|
return length
|
|
|
|
|
|
@icmpv6.register_icmpv6_type(ND_NEIGHBOR_SOLICIT, ND_NEIGHBOR_ADVERT)
|
|
class nd_neighbor(stringify.StringifyMixin):
|
|
"""ICMPv6 sub encoder/decoder class for Neighbor Solicitation and
|
|
Neighbor Advertisement messages. (RFC 4861)
|
|
|
|
This is used with ryu.lib.packet.icmpv6.icmpv6.
|
|
|
|
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|p{35em}|
|
|
|
|
============== ====================
|
|
Attribute Description
|
|
============== ====================
|
|
res R,S,O Flags for Neighbor Advertisement. \
|
|
The 3 MSBs of "Reserved" field for Neighbor Solicitation.
|
|
dst Target Address
|
|
option a derived object of ryu.lib.packet.icmpv6.nd_option \
|
|
or a bytearray. None if no options.
|
|
============== ====================
|
|
"""
|
|
|
|
_PACK_STR = '!I16s'
|
|
_MIN_LEN = struct.calcsize(_PACK_STR)
|
|
_ND_OPTION_TYPES = {}
|
|
_TYPE = {
|
|
'ascii': [
|
|
'dst'
|
|
]
|
|
}
|
|
|
|
@staticmethod
|
|
def register_nd_option_type(*args):
|
|
def _register_nd_option_type(cls):
|
|
nd_neighbor._ND_OPTION_TYPES[cls.option_type()] = cls
|
|
return cls
|
|
return _register_nd_option_type(args[0])
|
|
|
|
def __init__(self, res=0, dst='::', option=None):
|
|
self.res = res
|
|
self.dst = dst
|
|
self.option = option
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
(res, dst) = struct.unpack_from(cls._PACK_STR, buf, offset)
|
|
offset += cls._MIN_LEN
|
|
option = None
|
|
if len(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:
|
|
option = buf[offset:]
|
|
msg = cls(res >> 29, addrconv.ipv6.bin_to_text(dst), option)
|
|
return msg
|
|
|
|
def serialize(self):
|
|
res = self.res << 29
|
|
hdr = bytearray(struct.pack(
|
|
nd_neighbor._PACK_STR, res,
|
|
addrconv.ipv6.text_to_bin(self.dst)))
|
|
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)
|
|
|
|
def __len__(self):
|
|
length = self._MIN_LEN
|
|
if self.option is not None:
|
|
length += len(self.option)
|
|
return length
|
|
|
|
|
|
@icmpv6.register_icmpv6_type(ND_ROUTER_SOLICIT)
|
|
class nd_router_solicit(stringify.StringifyMixin):
|
|
"""ICMPv6 sub encoder/decoder class for Router Solicitation messages.
|
|
(RFC 4861)
|
|
|
|
This is used with ryu.lib.packet.icmpv6.icmpv6.
|
|
|
|
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|p{35em}|
|
|
|
|
============== ====================
|
|
Attribute Description
|
|
============== ====================
|
|
res This field is unused. It MUST be initialized to zero.
|
|
option a derived object of ryu.lib.packet.icmpv6.nd_option \
|
|
or a bytearray. None if no options.
|
|
============== ====================
|
|
"""
|
|
|
|
_PACK_STR = '!I'
|
|
_MIN_LEN = struct.calcsize(_PACK_STR)
|
|
_ND_OPTION_TYPES = {}
|
|
|
|
@staticmethod
|
|
def register_nd_option_type(*args):
|
|
def _register_nd_option_type(cls):
|
|
nd_router_solicit._ND_OPTION_TYPES[cls.option_type()] = cls
|
|
return cls
|
|
return _register_nd_option_type(args[0])
|
|
|
|
def __init__(self, res=0, option=None):
|
|
self.res = res
|
|
self.option = option
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
(res, ) = struct.unpack_from(cls._PACK_STR, buf, offset)
|
|
offset += cls._MIN_LEN
|
|
option = None
|
|
if len(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:
|
|
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.option is not None:
|
|
if isinstance(self.option, nd_option):
|
|
hdr.extend(self.option.serialize())
|
|
else:
|
|
hdr.extend(self.option)
|
|
return str(hdr)
|
|
|
|
def __len__(self):
|
|
length = self._MIN_LEN
|
|
if self.option is not None:
|
|
length += len(self.option)
|
|
return length
|
|
|
|
|
|
@icmpv6.register_icmpv6_type(ND_ROUTER_ADVERT)
|
|
class nd_router_advert(stringify.StringifyMixin):
|
|
"""ICMPv6 sub encoder/decoder class for Router Advertisement messages.
|
|
(RFC 4861)
|
|
|
|
This is used with ryu.lib.packet.icmpv6.icmpv6.
|
|
|
|
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|p{35em}|
|
|
|
|
============== ====================
|
|
Attribute Description
|
|
============== ====================
|
|
ch_l Cur Hop Limit.
|
|
res M,O Flags for Router Advertisement.
|
|
rou_l Router Lifetime.
|
|
rea_t Reachable Time.
|
|
ret_t Retrans Timer.
|
|
options List of a derived object of \
|
|
ryu.lib.packet.icmpv6.nd_option or a bytearray. \
|
|
None if no options.
|
|
============== ====================
|
|
"""
|
|
|
|
_PACK_STR = '!BBHII'
|
|
_MIN_LEN = struct.calcsize(_PACK_STR)
|
|
_ND_OPTION_TYPES = {}
|
|
|
|
@staticmethod
|
|
def register_nd_option_type(*args):
|
|
def _register_nd_option_type(cls):
|
|
nd_router_advert._ND_OPTION_TYPES[cls.option_type()] = cls
|
|
return cls
|
|
return _register_nd_option_type(args[0])
|
|
|
|
def __init__(self, ch_l=0, res=0, rou_l=0, rea_t=0, ret_t=0, 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
|
|
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)
|
|
offset += cls._MIN_LEN
|
|
options = []
|
|
while len(buf) > offset:
|
|
(type_, length) = struct.unpack_from('!BB', buf, offset)
|
|
cls_ = cls._ND_OPTION_TYPES.get(type_)
|
|
if cls_ is not None:
|
|
option = cls_.parser(buf, offset)
|
|
else:
|
|
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):
|
|
res = self.res << 6
|
|
hdr = bytearray(struct.pack(
|
|
nd_router_advert._PACK_STR, self.ch_l, res, self.rou_l,
|
|
self.rea_t, self.ret_t))
|
|
for option in self.options:
|
|
if isinstance(option, nd_option):
|
|
hdr.extend(option.serialize())
|
|
else:
|
|
hdr.extend(option)
|
|
return str(hdr)
|
|
|
|
def __len__(self):
|
|
length = self._MIN_LEN
|
|
for option in self.options:
|
|
length += len(option)
|
|
return length
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class nd_option(stringify.StringifyMixin):
|
|
@classmethod
|
|
@abc.abstractmethod
|
|
def option_type(cls):
|
|
pass
|
|
|
|
@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
|
|
|
|
def __len__(self):
|
|
return self._MIN_LEN
|
|
|
|
|
|
class nd_option_la(nd_option):
|
|
|
|
_PACK_STR = '!BB6s'
|
|
_MIN_LEN = struct.calcsize(_PACK_STR)
|
|
_TYPE = {
|
|
'ascii': [
|
|
'hw_src'
|
|
]
|
|
}
|
|
|
|
@abc.abstractmethod
|
|
def __init__(self, length, hw_src, data):
|
|
super(nd_option_la, self).__init__(self.option_type(), length)
|
|
self.hw_src = hw_src
|
|
self.data = data
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
(_, length, hw_src) = struct.unpack_from(cls._PACK_STR, buf, offset)
|
|
msg = cls(length, addrconv.mac.bin_to_text(hw_src))
|
|
offset += cls._MIN_LEN
|
|
if len(buf) > offset:
|
|
msg.data = buf[offset:]
|
|
|
|
return msg
|
|
|
|
def serialize(self):
|
|
buf = bytearray(struct.pack(
|
|
self._PACK_STR, self.option_type(), self.length,
|
|
addrconv.mac.text_to_bin(self.hw_src)))
|
|
if self.data is not None:
|
|
buf.extend(self.data)
|
|
mod = len(buf) % 8
|
|
if mod:
|
|
buf.extend(bytearray(8 - mod))
|
|
if 0 == self.length:
|
|
self.length = len(buf) // 8
|
|
struct.pack_into('!B', buf, 1, self.length)
|
|
return str(buf)
|
|
|
|
def __len__(self):
|
|
length = self._MIN_LEN
|
|
if self.data is not None:
|
|
length += len(self.data)
|
|
return length
|
|
|
|
|
|
@nd_neighbor.register_nd_option_type
|
|
@nd_router_solicit.register_nd_option_type
|
|
@nd_router_advert.register_nd_option_type
|
|
class nd_option_sla(nd_option_la):
|
|
"""ICMPv6 sub encoder/decoder class for Neighbor discovery
|
|
Source Link-Layer Address Option. (RFC 4861)
|
|
|
|
This is used with ryu.lib.packet.icmpv6.nd_neighbor,
|
|
ryu.lib.packet.icmpv6.nd_router_solicit or
|
|
ryu.lib.packet.icmpv6.nd_router_advert.
|
|
|
|
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|p{35em}|
|
|
|
|
============== ====================
|
|
Attribute Description
|
|
============== ====================
|
|
length length of the option. \
|
|
(0 means automatically-calculate when encoding)
|
|
hw_src Link-Layer Address. \
|
|
NOTE: If the address is longer than 6 octets this contains \
|
|
the first 6 octets in the address. \
|
|
This implementation assumes the address has at least \
|
|
6 octets.
|
|
data A bytearray which contains the rest of Link-Layer Address \
|
|
and padding. When encoding a packet, it's user's \
|
|
responsibility to provide necessary padding for 8-octets \
|
|
alignment required by the protocol.
|
|
============== ====================
|
|
"""
|
|
|
|
@classmethod
|
|
def option_type(cls):
|
|
return ND_OPTION_SLA
|
|
|
|
def __init__(self, length=0, hw_src='00:00:00:00:00:00', data=None):
|
|
super(nd_option_sla, self).__init__(length, hw_src, data)
|
|
|
|
|
|
@nd_neighbor.register_nd_option_type
|
|
class nd_option_tla(nd_option_la):
|
|
"""ICMPv6 sub encoder/decoder class for Neighbor discovery
|
|
Target Link-Layer Address Option. (RFC 4861)
|
|
|
|
This is used with ryu.lib.packet.icmpv6.nd_neighbor.
|
|
|
|
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|p{35em}|
|
|
|
|
============== ====================
|
|
Attribute Description
|
|
============== ====================
|
|
length length of the option. \
|
|
(0 means automatically-calculate when encoding)
|
|
hw_src Link-Layer Address. \
|
|
NOTE: If the address is longer than 6 octets this contains \
|
|
the first 6 octets in the address. \
|
|
This implementation assumes the address has at least \
|
|
6 octets.
|
|
data A bytearray which contains the rest of Link-Layer Address \
|
|
and padding. When encoding a packet, it's user's \
|
|
responsibility to provide necessary padding for 8-octets \
|
|
alignment required by the protocol.
|
|
============== ====================
|
|
"""
|
|
|
|
@classmethod
|
|
def option_type(cls):
|
|
return ND_OPTION_TLA
|
|
|
|
def __init__(self, length=0, hw_src='00:00:00:00:00:00', data=None):
|
|
super(nd_option_tla, self).__init__(length, hw_src, data)
|
|
|
|
|
|
@nd_router_advert.register_nd_option_type
|
|
class nd_option_pi(nd_option):
|
|
"""ICMPv6 sub encoder/decoder class for Neighbor discovery
|
|
Prefix Information Option. (RFC 4861)
|
|
|
|
This is used with ryu.lib.packet.icmpv6.nd_router_advert.
|
|
|
|
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|p{35em}|
|
|
|
|
============== ====================
|
|
Attribute Description
|
|
============== ====================
|
|
length length of the option. \
|
|
(0 means automatically-calculate when encoding)
|
|
pl Prefix Length.
|
|
res1 L,A,R\* Flags for Prefix Information.
|
|
val_l Valid Lifetime.
|
|
pre_l Preferred Lifetime.
|
|
res2 This field is unused. It MUST be initialized to zero.
|
|
prefix An IP address or a prefix of an IP address.
|
|
============== ====================
|
|
|
|
\*R flag is defined in (RFC 3775)
|
|
"""
|
|
|
|
_PACK_STR = '!BBBBIII16s'
|
|
_MIN_LEN = struct.calcsize(_PACK_STR)
|
|
_TYPE = {
|
|
'ascii': [
|
|
'prefix'
|
|
]
|
|
}
|
|
|
|
@classmethod
|
|
def option_type(cls):
|
|
return ND_OPTION_PI
|
|
|
|
def __init__(self, length=0, pl=0, res1=0, val_l=0, pre_l=0, res2=0,
|
|
prefix='::'):
|
|
super(nd_option_pi, self).__init__(self.option_type(), length)
|
|
self.pl = pl
|
|
self.res1 = res1
|
|
self.val_l = val_l
|
|
self.pre_l = pre_l
|
|
self.res2 = res2
|
|
self.prefix = prefix
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
(_, length, pl, res1, val_l, pre_l, res2, prefix
|
|
) = struct.unpack_from(cls._PACK_STR, buf, offset)
|
|
msg = cls(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.option_type(), self.length, self.pl,
|
|
res1, self.val_l, self.pre_l, self.res2,
|
|
addrconv.ipv6.text_to_bin(self.prefix)))
|
|
if 0 == self.length:
|
|
self.length = len(hdr) // 8
|
|
struct.pack_into('!B', hdr, 1, self.length)
|
|
return str(hdr)
|
|
|
|
|
|
@icmpv6.register_icmpv6_type(ICMPV6_ECHO_REPLY, ICMPV6_ECHO_REQUEST)
|
|
class echo(stringify.StringifyMixin):
|
|
"""ICMPv6 sub encoder/decoder class for Echo Request and Echo Reply
|
|
messages.
|
|
|
|
This is used with ryu.lib.packet.icmpv6.icmpv6 for
|
|
ICMPv6 Echo Request and Echo Reply messages.
|
|
|
|
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.
|
|
|
|
============== ====================
|
|
Attribute Description
|
|
============== ====================
|
|
id Identifier
|
|
seq Sequence Number
|
|
data Data
|
|
============== ====================
|
|
"""
|
|
|
|
_PACK_STR = '!HH'
|
|
_MIN_LEN = struct.calcsize(_PACK_STR)
|
|
|
|
def __init__(self, id_=0, seq=0, data=None):
|
|
self.id = id_
|
|
self.seq = seq
|
|
self.data = data
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
(id_, seq) = struct.unpack_from(cls._PACK_STR, buf, offset)
|
|
msg = cls(id_, seq)
|
|
offset += cls._MIN_LEN
|
|
|
|
if len(buf) > offset:
|
|
msg.data = buf[offset:]
|
|
|
|
return msg
|
|
|
|
def serialize(self):
|
|
hdr = bytearray(struct.pack(echo._PACK_STR, self.id,
|
|
self.seq))
|
|
if self.data is not None:
|
|
hdr += bytearray(self.data)
|
|
|
|
return hdr
|
|
|
|
def __len__(self):
|
|
length = self._MIN_LEN
|
|
if self.data is not None:
|
|
length += len(self.data)
|
|
return length
|
|
|
|
|
|
@icmpv6.register_icmpv6_type(
|
|
MLD_LISTENER_QUERY, MLD_LISTENER_REPOR, MLD_LISTENER_DONE)
|
|
class mld(stringify.StringifyMixin):
|
|
"""ICMPv6 sub encoder/decoder class for MLD Lister Query,
|
|
MLD Listener Report, and MLD Listener Done messages. (RFC 2710)
|
|
|
|
http://www.ietf.org/rfc/rfc2710.txt
|
|
|
|
This is used with ryu.lib.packet.icmpv6.icmpv6.
|
|
|
|
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.
|
|
|
|
============== =========================================
|
|
Attribute Description
|
|
============== =========================================
|
|
maxresp max response time in millisecond. it is
|
|
meaningful only in Query Message.
|
|
address a group address value.
|
|
============== =========================================
|
|
"""
|
|
|
|
_PACK_STR = '!H2x16s'
|
|
_MIN_LEN = struct.calcsize(_PACK_STR)
|
|
|
|
def __init__(self, maxresp=0, address='::'):
|
|
self.maxresp = maxresp
|
|
self.address = address
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
if cls._MIN_LEN < len(buf[offset:]):
|
|
msg = mldv2_query.parser(buf[offset:])
|
|
else:
|
|
(maxresp, address) = struct.unpack_from(
|
|
cls._PACK_STR, buf, offset)
|
|
msg = cls(maxresp, addrconv.ipv6.bin_to_text(address))
|
|
|
|
return msg
|
|
|
|
def serialize(self):
|
|
buf = struct.pack(mld._PACK_STR, self.maxresp,
|
|
addrconv.ipv6.text_to_bin(self.address))
|
|
return buf
|
|
|
|
def __len__(self):
|
|
return self._MIN_LEN
|
|
|
|
|
|
class mldv2_query(mld):
|
|
"""
|
|
ICMPv6 sub encoder/decoder class for MLD v2 Lister Query messages.
|
|
(RFC 3810)
|
|
|
|
http://www.ietf.org/rfc/rfc3810.txt
|
|
|
|
This is used with ryu.lib.packet.icmpv6.icmpv6.
|
|
|
|
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.
|
|
|
|
============== =========================================
|
|
Attribute Description
|
|
============== =========================================
|
|
maxresp max response time in millisecond. it is
|
|
meaningful only in Query Message.
|
|
address a group address value.
|
|
s_flg when set to 1, routers suppress the timer
|
|
process.
|
|
qrv robustness variable for a querier.
|
|
qqic an interval time for a querier in unit of
|
|
seconds.
|
|
num a number of the multicast servers.
|
|
srcs a list of IPv6 addresses of the multicast
|
|
servers.
|
|
============== =========================================
|
|
"""
|
|
|
|
_PACK_STR = '!H2x16sBBH'
|
|
_MIN_LEN = struct.calcsize(_PACK_STR)
|
|
|
|
def __init__(self, maxresp=0, address='::', s_flg=0, qrv=2,
|
|
qqic=0, num=0, srcs=None):
|
|
super(mldv2_query, self).__init__(maxresp, address)
|
|
self.s_flg = s_flg
|
|
self.qrv = qrv
|
|
self.qqic = qqic
|
|
self.num = num
|
|
srcs = srcs or []
|
|
assert isinstance(srcs, list)
|
|
for src in srcs:
|
|
assert isinstance(src, str)
|
|
self.srcs = srcs
|
|
|
|
@classmethod
|
|
def parser(cls, buf):
|
|
(maxresp, address, s_qrv, qqic, num
|
|
) = struct.unpack_from(cls._PACK_STR, buf)
|
|
s_flg = (s_qrv >> 3) & 0b1
|
|
qrv = s_qrv & 0b111
|
|
offset = cls._MIN_LEN
|
|
srcs = []
|
|
while 0 < len(buf[offset:]) and num > len(srcs):
|
|
assert 16 <= len(buf[offset:])
|
|
(src, ) = struct.unpack_from('16s', buf, offset)
|
|
srcs.append(addrconv.ipv6.bin_to_text(src))
|
|
offset += 16
|
|
assert num == len(srcs)
|
|
return cls(maxresp, addrconv.ipv6.bin_to_text(address), s_flg,
|
|
qrv, qqic, num, srcs)
|
|
|
|
def serialize(self):
|
|
s_qrv = self.s_flg << 3 | self.qrv
|
|
buf = bytearray(struct.pack(self._PACK_STR, self.maxresp,
|
|
addrconv.ipv6.text_to_bin(self.address), s_qrv,
|
|
self.qqic, self.num))
|
|
for src in self.srcs:
|
|
buf.extend(struct.pack('16s', addrconv.ipv6.text_to_bin(src)))
|
|
if 0 == self.num:
|
|
self.num = len(self.srcs)
|
|
struct.pack_into('!H', buf, 22, self.num)
|
|
return str(buf)
|
|
|
|
def __len__(self):
|
|
return self._MIN_LEN + len(self.srcs) * 16
|
|
|
|
|
|
@icmpv6.register_icmpv6_type(MLDV2_LISTENER_REPORT)
|
|
class mldv2_report(mld):
|
|
"""
|
|
ICMPv6 sub encoder/decoder class for MLD v2 Lister Report messages.
|
|
(RFC 3810)
|
|
|
|
http://www.ietf.org/rfc/rfc3810.txt
|
|
|
|
This is used with ryu.lib.packet.icmpv6.icmpv6.
|
|
|
|
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.
|
|
|
|
============== =========================================
|
|
Attribute Description
|
|
============== =========================================
|
|
record_num a number of the group records.
|
|
records a list of ryu.lib.packet.icmpv6.mldv2_report_group.
|
|
None if no records.
|
|
============== =========================================
|
|
"""
|
|
|
|
_PACK_STR = '!2xH'
|
|
_MIN_LEN = struct.calcsize(_PACK_STR)
|
|
_class_prefixes = ['mldv2_report_group']
|
|
|
|
def __init__(self, record_num=0, records=None):
|
|
self.record_num = record_num
|
|
records = records or []
|
|
assert isinstance(records, list)
|
|
for record in records:
|
|
assert isinstance(record, mldv2_report_group)
|
|
self.records = records
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
(record_num, ) = struct.unpack_from(cls._PACK_STR, buf, offset)
|
|
offset += cls._MIN_LEN
|
|
records = []
|
|
while 0 < len(buf[offset:]) and record_num > len(records):
|
|
record = mldv2_report_group.parser(buf[offset:])
|
|
records.append(record)
|
|
offset += len(record)
|
|
assert record_num == len(records)
|
|
return cls(record_num, records)
|
|
|
|
def serialize(self):
|
|
buf = bytearray(struct.pack(self._PACK_STR, self.record_num))
|
|
for record in self.records:
|
|
buf.extend(record.serialize())
|
|
if 0 == self.record_num:
|
|
self.record_num = len(self.records)
|
|
struct.pack_into('!H', buf, 2, self.record_num)
|
|
return str(buf)
|
|
|
|
def __len__(self):
|
|
records_len = 0
|
|
for record in self.records:
|
|
records_len += len(record)
|
|
return self._MIN_LEN + records_len
|
|
|
|
|
|
class mldv2_report_group(stringify.StringifyMixin):
|
|
"""
|
|
ICMPv6 sub encoder/decoder class for MLD v2 Lister Report Group
|
|
Record messages. (RFC 3810)
|
|
|
|
This is used with ryu.lib.packet.icmpv6.mldv2_report.
|
|
|
|
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.
|
|
|
|
=============== ====================================================
|
|
Attribute Description
|
|
=============== ====================================================
|
|
type\_ a group record type for v3.
|
|
aux_len the length of the auxiliary data in 32-bit words.
|
|
num a number of the multicast servers.
|
|
address a group address value.
|
|
srcs a list of IPv6 addresses of the multicast servers.
|
|
aux the auxiliary data.
|
|
=============== ====================================================
|
|
"""
|
|
_PACK_STR = '!BBH16s'
|
|
_MIN_LEN = struct.calcsize(_PACK_STR)
|
|
|
|
def __init__(self, type_=0, aux_len=0, num=0, address='::',
|
|
srcs=None, aux=None):
|
|
self.type_ = type_
|
|
self.aux_len = aux_len
|
|
self.num = num
|
|
self.address = address
|
|
srcs = srcs or []
|
|
assert isinstance(srcs, list)
|
|
for src in srcs:
|
|
assert isinstance(src, str)
|
|
self.srcs = srcs
|
|
self.aux = aux
|
|
|
|
@classmethod
|
|
def parser(cls, buf):
|
|
(type_, aux_len, num, address
|
|
) = struct.unpack_from(cls._PACK_STR, buf)
|
|
offset = cls._MIN_LEN
|
|
srcs = []
|
|
while 0 < len(buf[offset:]) and num > len(srcs):
|
|
assert 16 <= len(buf[offset:])
|
|
(src, ) = struct.unpack_from('16s', buf, offset)
|
|
srcs.append(addrconv.ipv6.bin_to_text(src))
|
|
offset += 16
|
|
assert num == len(srcs)
|
|
aux = None
|
|
if aux_len:
|
|
(aux, ) = struct.unpack_from('%ds' % (aux_len * 4), buf, offset)
|
|
msg = cls(type_, aux_len, num, addrconv.ipv6.bin_to_text(address),
|
|
srcs, aux)
|
|
return msg
|
|
|
|
def serialize(self):
|
|
buf = bytearray(struct.pack(self._PACK_STR, self.type_,
|
|
self.aux_len, self.num,
|
|
addrconv.ipv6.text_to_bin(self.address)))
|
|
for src in self.srcs:
|
|
buf.extend(struct.pack('16s', addrconv.ipv6.text_to_bin(src)))
|
|
if 0 == self.num:
|
|
self.num = len(self.srcs)
|
|
struct.pack_into('!H', buf, 2, self.num)
|
|
if self.aux is not None:
|
|
mod = len(self.aux) % 4
|
|
if mod:
|
|
self.aux += bytearray(4 - mod)
|
|
self.aux = str(self.aux)
|
|
buf.extend(self.aux)
|
|
if 0 == self.aux_len:
|
|
self.aux_len = len(self.aux) // 4
|
|
struct.pack_into('!B', buf, 1, self.aux_len)
|
|
return str(buf)
|
|
|
|
def __len__(self):
|
|
return self._MIN_LEN + len(self.srcs) * 16 + self.aux_len * 4
|
|
|
|
|
|
icmpv6.set_classes(icmpv6._ICMPV6_TYPES)
|
|
nd_neighbor.set_classes(nd_neighbor._ND_OPTION_TYPES)
|
|
nd_router_solicit.set_classes(nd_router_solicit._ND_OPTION_TYPES)
|
|
nd_router_advert.set_classes(nd_router_advert._ND_OPTION_TYPES)
|