On Tue, May 28, 2013 at 11:23:22AM +0900, 竹下昇 wrote:
> 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,
random.randint() is only evaluated at the time defining this class.
Probably you want something like
__init__(...xid = None...):
if xid is None:
xid = random.randint()
thanks,
> + 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
>
--
yamahata
------------------------------------------------------------------------------
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