Add DHCP encoder/decoder class.

Signed-off-by: TAKESHITA Noboru <[email protected]>
---
 ryu/lib/packet/dhcp.py |  289 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 289 insertions(+)
 create mode 100644 ryu/lib/packet/dhcp.py

diff --git a/ryu/lib/packet/dhcp.py b/ryu/lib/packet/dhcp.py
new file mode 100644
index 0000000..8d6dc31
--- /dev/null
+++ b/ryu/lib/packet/dhcp.py
@@ -0,0 +1,289 @@
+# 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.
+
+"""
+DHCP packet parser/serializer
+
+RFC 2131
+DHCP packet 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
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |     op (1)    |   htype (1)   |   hlen (1)    |   hops (1)    |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                            xid (4)                            |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |           secs (2)            |           flags (2)           |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                          ciaddr  (4)                          |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                          yiaddr  (4)                          |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                          siaddr  (4)                          |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                          giaddr  (4)                          |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
+    |                          chaddr  (16)                         |
+    |                                                               |
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
+    |                          sname   (64)                         |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
+    |                          file    (128)                        |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
+    |                          options (variable)                   |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+"""
+import binascii
+import random
+import socket
+import struct
+
+from . import packet_base
+
+DHCP_BOOT_REQUEST = 1
+DHCP_BOOT_REPLY = 2
+
+# DHCP message type code
+DHCP_DISCOVER = 1
+DHCP_OFFER = 2
+DHCP_REQUEST = 3
+DHCP_ACK = 5
+
+# DHCP options tag code
+DHCP_PAD_OPT = 0
+DHCP_SUBNET_MASK_OPT = 1
+DHCP_GATEWAY_ADDR_OPT = 3
+DHCP_DNS_SERVER_ADDR_OPT = 6
+DHCP_HOST_NAME_OPT = 12
+DHCP_REQUESTED_IP_ADDR_OPT = 50
+DHCP_IP_ADDR_LEASE_TIME_OPT = 51
+DHCP_MESSAGE_TYPE_OPT = 53
+DHCP_SERVER_IDENTIFIER_OPT = 54
+DHCP_PARAMETER_REQUEST_LIST_OPT = 55
+DHCP_RENEWAL_TIME_OPT = 58
+DHCP_REBINDING_TIME_OPT = 59
+DHCP_END_OPT = 255
+
+
+class dhcp(packet_base.PacketBase):
+    """DHCP (RFC 2131) header encoder/decoder class.
+
+    The serialized packet would looks like the ones described
+    in the following sections.
+
+    * RFC 2131 DHCP packet format
+
+    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
+    ============== ====================
+    op             Message op code / message type.\
+                   1 = BOOTREQUEST, 2 = BOOTREPLY
+    htype          Hardware address type (e.g.  '1' = 10mb ethernet).
+    hlen           Hardware address length (e.g.  '6' = 10mb ethernet).
+    hops           Client sets to zero, optionally used by relay agent\
+                   when booting via a relay agent.
+    xid            Transaction ID, a random number chosen by the client,\
+                   used by the client and serverto associate messages\
+                   and responses between a client and a server.
+    secs           Filled in by client, seconds elapsed since client\
+                   began address acquisition or renewal process.
+    flags          Flags.
+    ciaddr         Client IP address; only filled in if client is in\
+                   BOUND, RENEW or REBINDING state and can respond\
+                   to ARP requests.
+    yiaddr         'your' (client) IP address.
+    siaddr         IP address of next server to use in bootstrap;\
+                   returned in DHCPOFFER, DHCPACK by server.
+    giaddr         Relay agent IP address, used in booting via a\
+                   relay agent.
+    chaddr         Client hardware address.
+    sname          Optional server host name, null terminated string.
+    boot_file      Boot file name, null terminated string; "generic"\
+                   name or null in DHCPDISCOVER, fully qualified\
+                   directory-path name in DHCPOFFER.
+    options        Optional parameters field\
+                   ('DHCP message type' option must be included in\
+                    every DHCP message).
+    ============== ====================
+    """
+    _HLEN_UNPACK_STR = '!BBB'
+    _HLEN_UNPACK_LEN = struct.calcsize(_HLEN_UNPACK_STR)
+    _DHCP_UNPACK_STR = '!BIHHIIII%ds%ds64s128s'
+    _DHCP_PACK_STR = '!BBBBIHHIIII16s64s128s'
+    _DHCP_CHADDR_LEN = 16
+    _HARDWARE_TYPE_ETHERNET = 1
+
+    def __init__(self, op, chaddr, options, htype=_HARDWARE_TYPE_ETHERNET,
+                 hlen=0, hops=0, xid=random.randint(0, 0xffffffff), secs=0,
+                 flags=0, ciaddr=0, yiaddr=0, siaddr=0, giaddr=0, sname='',
+                 boot_file=''):
+        super(dhcp, self).__init__()
+        self.op = op
+        self.htype = htype
+        if hlen == 0:
+            self.hlen = len(chaddr)
+        else:
+            self.hlen = hlen
+        self.hops = hops
+        self.xid = xid
+        self.secs = secs
+        self.flags = flags
+        self.ciaddr = ciaddr
+        self.yiaddr = yiaddr
+        self.siaddr = siaddr
+        self.giaddr = giaddr
+        self.chaddr = chaddr
+        self.sname = sname
+        self.boot_file = boot_file
+        self.options = options
+
+    @classmethod
+    def parser(cls, buf):
+        (op, htype, hlen) = struct.unpack_from(cls._HLEN_UNPACK_STR, buf)
+        buf = buf[cls._HLEN_UNPACK_LEN:]
+        unpack_str = cls._DHCP_UNPACK_STR % (hlen,
+                                             (cls._DHCP_CHADDR_LEN - hlen))
+        min_len = struct.calcsize(unpack_str)
+        (hops, xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr, chaddr,
+         dummy, sname, boot_file
+         ) = struct.unpack_from(unpack_str, buf)
+        if len(buf) > min_len:
+            parse_opt = options.parser(buf[min_len:])
+        return cls(op, chaddr, parse_opt, htype, hlen, hops, xid, secs, flags,
+                   ciaddr, yiaddr, siaddr, giaddr, sname, boot_file)
+
+    def serialize(self, payload, prev):
+        seri_opt = self.options.serialize()
+        pack_str = '%s%ds' % (self._DHCP_PACK_STR,
+                              self.options.options_len)
+        return struct.pack(pack_str, self.op, self.htype, self.hlen,
+                           self.hops, self.xid, self.secs, self.flags,
+                           self.ciaddr, self.yiaddr, self.siaddr, self.giaddr,
+                           self.chaddr, self.sname, self.boot_file, seri_opt)
+
+
+class options:
+    """DHCP (RFC 2132) options encoder/decoder class.
+
+    This is used with ryu.lib.packet.dhcp.dhcp.
+
+    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
+    ============== ====================
+    option_list    'end option' and 'pad option' are added automatically\
+                   after the option class is stored in array.
+    options_len    Option's byte length.\
+                   ('magic cookie', 'end option' and 'pad option'\
+                    length including.)
+    magic_cookie   The first four octets contain the decimal values\
+                   99, 130, 83 and 99.
+    ============== ====================
+    """
+    _MAGIC_COOKIE_UNPACK_STR = '!I'
+    # same magic cookie as is defined in RFC 1497
+    _MAGIC_COOKIE = socket.inet_aton("99.130.83.99")
+    _OPT_TAG_LEN_BYTE = 2
+
+    def __init__(self, option_list=None, options_len=0,
+                 magic_cookie=_MAGIC_COOKIE):
+        self.option_list = option_list or []
+        self.options_len = options_len
+        self.magic_cookie = magic_cookie
+
+    @classmethod
+    def parser(cls, buf):
+        opt_parse_list = []
+        offset = struct.calcsize(cls._MAGIC_COOKIE_UNPACK_STR)
+        magic_cookie = struct.unpack_from(cls._MAGIC_COOKIE_UNPACK_STR, buf)[0]
+        while len(buf) > offset:
+            opt_buf = buf[offset:]
+            opt = option.parser(opt_buf)
+            if opt is None:
+                break
+            opt_parse_list.append(opt)
+            offset += opt.length + cls._OPT_TAG_LEN_BYTE
+        return cls(opt_parse_list, len(buf), magic_cookie)
+
+    def serialize(self):
+        seri_opt = self.magic_cookie
+        for opt in self.option_list:
+            seri_opt += opt.serialize()
+        seri_opt += binascii.a2b_hex('%x' % DHCP_END_OPT)
+        if self.options_len == 0:
+            self.options_len = len(seri_opt)
+        return seri_opt
+
+
+class option:
+    """DHCP (RFC 2132) options encoder/decoder class.
+
+    This is used with ryu.lib.packet.dhcp.dhcp.options.
+
+    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
+    ============== ====================
+    tag            Option type.\
+                   (except for the 'magic cookie', 'pad option'\
+                    and 'end option'.)
+    value          Option's value.\
+                   (set the value that has been converted to hexadecimal.)
+    length         Option's value length.\
+                   (calculated automatically from the length of value.)
+    ============== ====================
+    """
+    _UNPACK_STR = '!B'
+    _MIN_LEN = struct.calcsize(_UNPACK_STR)
+
+    def __init__(self, tag, value, length=0):
+        self.tag = tag
+        self.value = value
+        self.length = length
+
+    @classmethod
+    def parser(cls, buf):
+        tag = struct.unpack_from(cls._UNPACK_STR, buf)[0]
+        if tag == DHCP_END_OPT or tag == DHCP_PAD_OPT:
+            return None
+        buf = buf[cls._MIN_LEN:]
+        length = struct.unpack_from(cls._UNPACK_STR, buf)[0]
+        buf = buf[cls._MIN_LEN:]
+        value_unpack_str = '%ds' % length
+        value = struct.unpack_from(value_unpack_str, buf)[0]
+        return cls(tag, value, length)
+
+    def serialize(self):
+        if self.length == 0:
+            self.length = len(self.value)
+        options_pack_str = '!BB%ds' % self.length
+        return struct.pack(options_pack_str, self.tag, self.length, self.value)
-- 
1.7.10.4


------------------------------------------------------------------------------
Try New Relic Now & We'll Send You this Cool Shirt
New Relic is the only SaaS-based application performance monitoring service 
that delivers powerful full stack analytics. Optimize and monitor your
browser, app, & servers with just a few lines of code. Try New Relic
and get this awesome Nerd Life shirt! http://p.sf.net/sfu/newrelic_d2d_may
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to