summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
author竹下昇 <takeshita.noboru1@gmail.com>2013-05-30 13:54:07 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2013-05-31 17:53:56 +0900
commit6f2716b7ff1895235672f2a08cedf965fa83789e (patch)
treed8c6bf09a724a0907ec399a7a0d336f0befcaee7
parent77effb29a69eeb6d320dd9a7554924b2578c3c95 (diff)
lib/packet: DHCP packet parser/serializer
Add DHCP encoder/decoder class. Signed-off-by: TAKESHITA Noboru <takeshita.noboru@yes.nttcom.ne.jp> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r--ryu/lib/packet/dhcp.py296
1 files changed, 296 insertions, 0 deletions
diff --git a/ryu/lib/packet/dhcp.py b/ryu/lib/packet/dhcp.py
new file mode 100644
index 00000000..58450c99
--- /dev/null
+++ b/ryu/lib/packet/dhcp.py
@@ -0,0 +1,296 @@
+# 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=None, 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
+ if xid is None:
+ self.xid = random.randint(0, 0xffffffff)
+ else:
+ 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(object):
+ """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):
+ super(options, self).__init__()
+ if option_list is None:
+ self.option_list = []
+ else:
+ self.option_list = option_list
+ 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(object):
+ """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):
+ super(option, self).__init__()
+ 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)