summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--doc/source/library_packet_ref.rst4
-rw-r--r--ryu/lib/packet/igmp.py112
-rw-r--r--ryu/lib/packet/ipv4.py2
-rw-r--r--ryu/ofproto/inet.py1
-rw-r--r--ryu/tests/unit/packet/test_igmp.py147
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)