diff options
-rw-r--r-- | doc/source/library_packet_ref.rst | 4 | ||||
-rw-r--r-- | ryu/lib/packet/igmp.py | 112 | ||||
-rw-r--r-- | ryu/lib/packet/ipv4.py | 2 | ||||
-rw-r--r-- | ryu/ofproto/inet.py | 1 | ||||
-rw-r--r-- | ryu/tests/unit/packet/test_igmp.py | 147 |
5 files changed, 266 insertions, 0 deletions
diff --git a/doc/source/library_packet_ref.rst b/doc/source/library_packet_ref.rst index 351e95e6..ea9d07b0 100644 --- a/doc/source/library_packet_ref.rst +++ b/doc/source/library_packet_ref.rst @@ -80,3 +80,7 @@ Protocol Header classes :members: .. autoclass:: ryu.lib.packet.bpdu.RstBPDUs :members: + +.. autoclass:: ryu.lib.packet.igmp.igmp + :members: + diff --git a/ryu/lib/packet/igmp.py b/ryu/lib/packet/igmp.py new file mode 100644 index 00000000..1081e4d6 --- /dev/null +++ b/ryu/lib/packet/igmp.py @@ -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 diff --git a/ryu/lib/packet/ipv4.py b/ryu/lib/packet/ipv4.py index 8a07c815..032e4254 100644 --- a/ryu/lib/packet/ipv4.py +++ b/ryu/lib/packet/ipv4.py @@ -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) diff --git a/ryu/ofproto/inet.py b/ryu/ofproto/inet.py index d0ce694f..6990b72d 100644 --- a/ryu/ofproto/inet.py +++ b/ryu/ofproto/inet.py @@ -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 diff --git a/ryu/tests/unit/packet/test_igmp.py b/ryu/tests/unit/packet/test_igmp.py new file mode 100644 index 00000000..e5a810b7 --- /dev/null +++ b/ryu/tests/unit/packet/test_igmp.py @@ -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) |