summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ryu/lib/packet/dhcp.py168
1 files changed, 81 insertions, 87 deletions
diff --git a/ryu/lib/packet/dhcp.py b/ryu/lib/packet/dhcp.py
index c8e849fa..327c8925 100644
--- a/ryu/lib/packet/dhcp.py
+++ b/ryu/lib/packet/dhcp.py
@@ -15,51 +15,49 @@
"""
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
+# 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 random
import struct
-from . import packet_base
+import netaddr
+
from ryu.lib import addrconv
from ryu.lib import stringify
-
+from . import packet_base
DHCP_BOOT_REQUEST = 1
DHCP_BOOT_REPLY = 2
@@ -136,12 +134,9 @@ class dhcp(packet_base.PacketBase):
every DHCP message).
============== ====================
"""
- _MIN_LEN = 236
- _HLEN_UNPACK_STR = '!BBB'
- _HLEN_UNPACK_LEN = struct.calcsize(_HLEN_UNPACK_STR)
- _DHCP_UNPACK_STR = '!BIHH4s4s4s4s%ds%ds64s128s'
_DHCP_PACK_STR = '!BBBBIHH4s4s4s4s16s64s128s'
- _DHCP_CHADDR_LEN = 16
+ _MIN_LEN = struct.calcsize(_DHCP_PACK_STR)
+ _MAC_ADDRESS_LEN = 6
_HARDWARE_TYPE_ETHERNET = 1
_class_prefixes = ['options']
_TYPE = {
@@ -150,17 +145,14 @@ class dhcp(packet_base.PacketBase):
]
}
- def __init__(self, op, chaddr, options, htype=_HARDWARE_TYPE_ETHERNET,
+ def __init__(self, op, chaddr, options=None, htype=_HARDWARE_TYPE_ETHERNET,
hlen=0, hops=0, xid=None, secs=0, flags=0,
ciaddr='0.0.0.0', yiaddr='0.0.0.0', siaddr='0.0.0.0',
giaddr='0.0.0.0', sname='', boot_file=b''):
super(dhcp, self).__init__()
self.op = op
self.htype = htype
- if hlen == 0:
- self.hlen = len(addrconv.mac.text_to_bin(chaddr))
- else:
- self.hlen = hlen
+ self.hlen = hlen
self.hops = hops
if xid is None:
self.xid = random.randint(0, 0xffffffff)
@@ -178,20 +170,20 @@ class dhcp(packet_base.PacketBase):
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)
- length = min_len
- if len(buf) > min_len:
- parse_opt = options.parser(buf[min_len:])
+ def parser(cls, buf):
+ (op, htype, hlen, hops, xid, secs, flags,
+ ciaddr, yiaddr, siaddr, giaddr, chaddr, sname,
+ boot_file) = struct.unpack_from(cls._DHCP_PACK_STR, buf)
+
+ if hlen == cls._MAC_ADDRESS_LEN:
+ chaddr = addrconv.mac.bin_to_text(chaddr[:cls._MAC_ADDRESS_LEN])
+
+ length = cls._MIN_LEN
+ parse_opt = None
+ if len(buf) > length:
+ parse_opt = options.parser(buf[length:])
length += parse_opt.options_len
- return (cls(op, addrconv.mac.bin_to_text(chaddr), parse_opt,
+ return (cls(op, chaddr, parse_opt,
htype, hlen, hops, xid, secs, flags,
addrconv.ipv4.bin_to_text(ciaddr),
addrconv.ipv4.bin_to_text(yiaddr),
@@ -200,25 +192,24 @@ class dhcp(packet_base.PacketBase):
sname.decode('ascii'), boot_file),
None, buf[length:])
- @classmethod
- def parser(cls, buf):
- try:
- return cls._parser(buf)
- except:
- return None, None, buf
-
- 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,
+ def serialize(self, _payload=None, _prev=None):
+ opt_buf = bytearray()
+ if self.options is not None:
+ opt_buf = self.options.serialize()
+ if netaddr.valid_mac(self.chaddr):
+ chaddr = addrconv.mac.text_to_bin(self.chaddr)
+ else:
+ chaddr = self.chaddr
+ self.hlen = len(chaddr)
+ return struct.pack(self._DHCP_PACK_STR, self.op, self.htype, self.hlen,
self.hops, self.xid, self.secs, self.flags,
addrconv.ipv4.text_to_bin(self.ciaddr),
addrconv.ipv4.text_to_bin(self.yiaddr),
addrconv.ipv4.text_to_bin(self.siaddr),
addrconv.ipv4.text_to_bin(self.giaddr),
- addrconv.mac.text_to_bin(self.chaddr),
- self.sname.encode('ascii'), self.boot_file, seri_opt)
+ chaddr,
+ self.sname.encode('ascii'),
+ self.boot_file) + opt_buf
class options(stringify.StringifyMixin):
@@ -258,10 +249,7 @@ class options(stringify.StringifyMixin):
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.option_list = option_list or []
self.options_len = options_len
self.magic_cookie = magic_cookie
@@ -272,7 +260,11 @@ class options(stringify.StringifyMixin):
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)
+ try:
+ opt = option.parser(opt_buf)
+ except struct.error:
+ opt_parse_list.append(opt_buf)
+ break
if opt is None:
break
opt_parse_list.append(opt)
@@ -283,10 +275,13 @@ class options(stringify.StringifyMixin):
def serialize(self):
seri_opt = addrconv.ipv4.text_to_bin(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)
+ if isinstance(opt, option):
+ seri_opt += opt.serialize()
+ else:
+ seri_opt += opt
+ if isinstance(self.option_list[-1], option):
+ seri_opt += b'\xff'
+ self.options_len = len(seri_opt)
return seri_opt
@@ -333,7 +328,6 @@ class option(stringify.StringifyMixin):
return cls(tag, value, length)
def serialize(self):
- if self.length == 0:
- self.length = len(self.value)
+ self.length = len(self.value)
options_pack_str = '!BB%ds' % self.length
return struct.pack(options_pack_str, self.tag, self.length, self.value)