lib/packet: add Internet Group Management Protocol
Signed-off-by: Yuichi Ito <ito.yuichi0@gmal.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
parent
bed38d4d12
commit
d84d9a7c93
@ -80,3 +80,7 @@ Protocol Header classes
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.bpdu.RstBPDUs
|
||||
:members:
|
||||
|
||||
.. autoclass:: ryu.lib.packet.igmp.igmp
|
||||
:members:
|
||||
|
||||
|
112
ryu/lib/packet/igmp.py
Normal file
112
ryu/lib/packet/igmp.py
Normal file
@ -0,0 +1,112 @@
|
||||
# Copyright (C) 2013 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.
|
||||
|
||||
"""
|
||||
Internet Group Management Protocol(IGMP) packet parser/serializer
|
||||
|
||||
RFC 1112
|
||||
IGMP v1 format
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|Version| Type | Unused | Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Group Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
RFC 2236
|
||||
IGMP v2 format
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Max Resp Time | Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Group Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
"""
|
||||
|
||||
import struct
|
||||
|
||||
from ryu.lib import addrconv
|
||||
from ryu.lib.packet import packet_base
|
||||
from ryu.lib.packet import packet_utils
|
||||
|
||||
|
||||
IGMP_TYPE_QUERY = 0x11
|
||||
IGMP_TYPE_REPORT_V1 = 0x12
|
||||
IGMP_TYPE_REPORT_V2 = 0x16
|
||||
IGMP_TYPE_LEAVE = 0x17
|
||||
|
||||
|
||||
class igmp(packet_base.PacketBase):
|
||||
"""
|
||||
Internet Group Management Protocol(IGMP, RFC 1112, RFC 2236)
|
||||
header encoder/decoder class.
|
||||
|
||||
http://www.ietf.org/rfc/rfc1112.txt
|
||||
|
||||
http://www.ietf.org/rfc/rfc2236.txt
|
||||
|
||||
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 correspondig args in this order.
|
||||
|
||||
=============== ====================================================
|
||||
Attribute Description
|
||||
=============== ====================================================
|
||||
msgtype a message type for v2, or a combination of
|
||||
version and a message type for v1.
|
||||
maxresp max response time in unit of 1/10 second. it is
|
||||
meaningful only in Query Message.
|
||||
csum a check sum value. 0 means automatically-calculate
|
||||
when encoding.
|
||||
address a group address value.
|
||||
=============== ====================================================
|
||||
|
||||
* NOTE: IGMP v3(RFC 3376) is not supported yet.
|
||||
"""
|
||||
_PACK_STR = '!BBH4s'
|
||||
_MIN_LEN = struct.calcsize(_PACK_STR)
|
||||
|
||||
def __init__(self, msgtype, maxresp, csum, address):
|
||||
super(igmp, self).__init__()
|
||||
self.msgtype = msgtype
|
||||
self.maxresp = maxresp
|
||||
self.csum = csum
|
||||
self.address = address
|
||||
|
||||
@classmethod
|
||||
def parser(cls, buf):
|
||||
assert cls._MIN_LEN <= len(buf)
|
||||
(msgtype, maxresp, csum, address
|
||||
) = struct.unpack_from(cls._PACK_STR, buf)
|
||||
return (cls(msgtype, maxresp, csum,
|
||||
addrconv.ipv4.bin_to_text(address)),
|
||||
None,
|
||||
buf[cls._MIN_LEN:])
|
||||
|
||||
def serialize(self, payload, prev):
|
||||
hdr = bytearray(struct.pack(self._PACK_STR, self.msgtype,
|
||||
self.maxresp, self.csum,
|
||||
addrconv.ipv4.text_to_bin(self.address)))
|
||||
|
||||
if self.csum == 0:
|
||||
self.csum = packet_utils.checksum(hdr)
|
||||
struct.pack_into('!H', hdr, 2, self.csum)
|
||||
|
||||
return hdr
|
@ -18,6 +18,7 @@ import struct
|
||||
from . import packet_base
|
||||
from . import packet_utils
|
||||
from . import icmp
|
||||
from . import igmp
|
||||
from . import udp
|
||||
from . import tcp
|
||||
from ryu.ofproto import inet
|
||||
@ -134,5 +135,6 @@ class ipv4(packet_base.PacketBase):
|
||||
return hdr
|
||||
|
||||
ipv4.register_packet_type(icmp.icmp, inet.IPPROTO_ICMP)
|
||||
ipv4.register_packet_type(igmp.igmp, inet.IPPROTO_IGMP)
|
||||
ipv4.register_packet_type(tcp.tcp, inet.IPPROTO_TCP)
|
||||
ipv4.register_packet_type(udp.udp, inet.IPPROTO_UDP)
|
||||
|
@ -17,6 +17,7 @@
|
||||
IPPROTO_IP = 0
|
||||
IPPROTO_HOPOPTS = 0
|
||||
IPPROTO_ICMP = 1
|
||||
IPPROTO_IGMP = 2
|
||||
IPPROTO_TCP = 6
|
||||
IPPROTO_UDP = 17
|
||||
IPPROTO_ROUTING = 43
|
||||
|
147
ryu/tests/unit/packet/test_igmp.py
Normal file
147
ryu/tests/unit/packet/test_igmp.py
Normal file
@ -0,0 +1,147 @@
|
||||
# Copyright (C) 2013 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.
|
||||
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
import unittest
|
||||
import inspect
|
||||
import logging
|
||||
|
||||
from struct import pack, unpack_from
|
||||
from nose.tools import ok_, eq_, raises
|
||||
from ryu.ofproto import ether
|
||||
from ryu.ofproto import inet
|
||||
from ryu.lib.packet.ethernet import ethernet
|
||||
from ryu.lib.packet.ipv4 import ipv4
|
||||
from ryu.lib.packet.packet import Packet
|
||||
from ryu.lib.packet.packet_utils import checksum
|
||||
from ryu.lib import addrconv
|
||||
from ryu.lib.packet.igmp import igmp
|
||||
from ryu.lib.packet.igmp import IGMP_TYPE_QUERY
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Test_igmp(unittest.TestCase):
|
||||
""" Test case for Internet Group Management Protocol
|
||||
"""
|
||||
def setUp(self):
|
||||
self.msgtype = IGMP_TYPE_QUERY
|
||||
self.maxresp = 100
|
||||
self.csum = 0
|
||||
self.address = '225.0.0.1'
|
||||
|
||||
self.buf = pack(igmp._PACK_STR, self.msgtype, self.maxresp,
|
||||
self.csum,
|
||||
addrconv.ipv4.text_to_bin(self.address))
|
||||
|
||||
self.g = igmp(self.msgtype, self.maxresp, self.csum,
|
||||
self.address)
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def find_protocol(self, pkt, name):
|
||||
for p in pkt.protocols:
|
||||
if p.protocol_name == name:
|
||||
return p
|
||||
|
||||
def test_init(self):
|
||||
eq_(self.msgtype, self.g.msgtype)
|
||||
eq_(self.maxresp, self.g.maxresp)
|
||||
eq_(self.csum, self.g.csum)
|
||||
eq_(self.address, self.g.address)
|
||||
|
||||
def test_parser(self):
|
||||
_res = self.g.parser(self.buf)
|
||||
if type(_res) is tuple:
|
||||
res = _res[0]
|
||||
else:
|
||||
res = _res
|
||||
|
||||
eq_(res.msgtype, self.msgtype)
|
||||
eq_(res.maxresp, self.maxresp)
|
||||
eq_(res.csum, self.csum)
|
||||
eq_(res.address, self.address)
|
||||
|
||||
def test_serialize(self):
|
||||
data = bytearray()
|
||||
prev = None
|
||||
buf = self.g.serialize(data, prev)
|
||||
|
||||
res = unpack_from(igmp._PACK_STR, buffer(buf))
|
||||
|
||||
eq_(res[0], self.msgtype)
|
||||
eq_(res[1], self.maxresp)
|
||||
eq_(res[2], checksum(self.buf))
|
||||
eq_(res[3], addrconv.ipv4.text_to_bin(self.address))
|
||||
|
||||
def _build_igmp(self):
|
||||
dl_dst = '11:22:33:44:55:66'
|
||||
dl_src = 'aa:bb:cc:dd:ee:ff'
|
||||
dl_type = ether.ETH_TYPE_IP
|
||||
e = ethernet(dl_dst, dl_src, dl_type)
|
||||
|
||||
total_length = 20 + igmp._MIN_LEN
|
||||
nw_proto = inet.IPPROTO_IGMP
|
||||
nw_dst = '11.22.33.44'
|
||||
nw_src = '55.66.77.88'
|
||||
i = ipv4(total_length=total_length, src=nw_src, dst=nw_dst,
|
||||
proto=nw_proto)
|
||||
|
||||
p = Packet()
|
||||
|
||||
p.add_protocol(e)
|
||||
p.add_protocol(i)
|
||||
p.add_protocol(self.g)
|
||||
p.serialize()
|
||||
return p
|
||||
|
||||
def test_build_igmp(self):
|
||||
p = self._build_igmp()
|
||||
|
||||
e = self.find_protocol(p, "ethernet")
|
||||
ok_(e)
|
||||
eq_(e.ethertype, ether.ETH_TYPE_IP)
|
||||
|
||||
i = self.find_protocol(p, "ipv4")
|
||||
ok_(i)
|
||||
eq_(i.proto, inet.IPPROTO_IGMP)
|
||||
|
||||
g = self.find_protocol(p, "igmp")
|
||||
ok_(g)
|
||||
|
||||
eq_(g.msgtype, self.msgtype)
|
||||
eq_(g.maxresp, self.maxresp)
|
||||
eq_(g.csum, checksum(self.buf))
|
||||
eq_(g.address, self.address)
|
||||
|
||||
def test_to_string(self):
|
||||
igmp_values = {'msgtype': repr(self.msgtype),
|
||||
'maxresp': repr(self.maxresp),
|
||||
'csum': repr(self.csum),
|
||||
'address': repr(self.address)}
|
||||
_g_str = ','.join(['%s=%s' % (k, igmp_values[k])
|
||||
for k, v in inspect.getmembers(self.g)
|
||||
if k in igmp_values])
|
||||
g_str = '%s(%s)' % (igmp.__name__, _g_str)
|
||||
|
||||
eq_(str(self.g), g_str)
|
||||
eq_(repr(self.g), g_str)
|
||||
|
||||
@raises(Exception)
|
||||
def test_malformed_igmp(self):
|
||||
m_short_buf = self.buf[1:igmp._MIN_LEN]
|
||||
igmp.parser(m_short_buf)
|
Loading…
x
Reference in New Issue
Block a user