Re: [Ryu-devel] Possible bugs in dhcp parser
Brought to you by:
nz_gizmoguy
|
From: FUJITA T. <fuj...@la...> - 2015-08-30 12:52:34
|
Hi, thanks for the report!
On Fri, 28 Aug 2015 16:52:41 -0400
A Sydney <asy...@gm...> wrote:
> I believe I have found a bug in the parser and have provided a possible fix
> below. In addition, I believe there may be a second bug. I would
> appreciate your feedback on both.
>
> The text at the bottom of the email highlights the steps I took which
> generate the first bug and the possible fix.
>
> Questions:
>
> a) Are the steps in 3 below correct? If not, can you please provide
> feedback?
>
> b) Is the output of 4 below what one should expect?
>
> c) Second bug: In the following snippet (from ryu/lib/packet/dhcp.py), it
> appears that "parse_opt" is only set if len(buf) is greater than min_len
> (since there are no other instances of parse_opt in this file). In the
> event that len(buf) =< min_len, would this not result in an error at the
> "return" statement?
>
> ...
> if len(buf) > min_len:
> parse_opt = options.parser(buf[min_len:])
> length += parse_opt.options_len
>
> return (cls(op, addrconv.mac.bin_to_text(chaddr), parse_opt,
> htype, hlen, hops, xid, secs, flags,
> addrconv.ipv4.bin_to_text(ciaddr),
> addrconv.ipv4.bin_to_text(yiaddr),
> addrconv.ipv4.bin_to_text(siaddr),
> addrconv.ipv4.bin_to_text(giaddr),
> sname.decode('ascii'), boot_file),
> None, buf[length:])
> ...
Looks like the code works as you said. That's a bug? At that point,
the parser consumes 236 bytes. If there is still the buffer to parse,
the parse does, right?
> Thank you in advance for your assistance,
> Ali
>
> ==== FIRST BUG REPORT AND PROPOSED FIX ===
>
> 1. Action:
>
> Since the switch truncates pkts to the controller, I use the following to
> have the switch send the entire packet (I've assumed a max pkt size of 1500
> bytes).
> ...
> req = parser.OFPSetConfig(datapath, ofproto_v1_3.OFPC_FRAG_NORMAL, 1500)
> datapath.send_msg(req)
> ...
>
> I also use the following to grab the dhcp header:
>
> ...
> dhcp_pkt = packet.Packet(pkt.protocols[3], parse_cls=dhcp.dhcp)
> ...
>
> 2. Result:
>
> The following error results:
>
> ...
> dhcp_pkt = packet.Packet(pkt.protocols[3], parse_cls=dhcp.dhcp)
> File "/usr/local/lib/python2.7/site-packages/ryu/lib/packet/packet.py",
> line 46, in __init__
> self._parser(parse_cls)
> File "/usr/local/lib/python2.7/site-packages/ryu/lib/packet/packet.py",
> line 55, in _parser
> if proto:
> File
> "/usr/local/lib/python2.7/site-packages/ryu/lib/packet/packet_base.py",
> line 46, in __len__
> return self._MIN_LEN
> AttributeError: 'dhcp' object has no attribute '_MIN_LEN'
> ...
>
> 3. Action:
>
> I added the following two lines to the start of the dhcp class in the
> dhcp.py file around line 135 (PS. These lines also exist in the option
> class):
>
> _UNPACK_STR = '!B'
> _MIN_LEN = struct.calcsize(_UNPACK_STR)
> # existing code is below
> _HLEN_UNPACK_STR = '!BBB'
> _HLEN_UNPACK_LEN = struct.calcsize(_HLEN_UNPACK_STR)
I think that the dhcp parser expects at least 236 bytes so _MIN_LEN
should be 236. If _MIN_LEN is 1, then the parse could get 1 byte
buffer (and then the parser crashes).
btw, I think that the udp parse should properly handle dhcp. With the
following patch, you can just do:
pkt = packet.Packet(buf)
pkt should includes ethernet, ip, udp, and dhcp headers.
diff --git a/ryu/lib/packet/udp.py b/ryu/lib/packet/udp.py
index a80e101..bae6d73 100644
--- a/ryu/lib/packet/udp.py
+++ b/ryu/lib/packet/udp.py
@@ -17,6 +17,7 @@ import struct
from . import packet_base
from . import packet_utils
+from . import dhcp
class udp(packet_base.PacketBase):
@@ -49,11 +50,17 @@ class udp(packet_base.PacketBase):
self.csum = csum
@classmethod
+ def get_packet_type(cls, src_port, dst_port):
+ if (src_port == 68 and dst_port == 67) or (src_port == 67 and dst_port == 68):
+ return dhcp.dhcp
+ return None
+
+ @classmethod
def parser(cls, buf):
(src_port, dst_port, total_length, csum) = struct.unpack_from(
cls._PACK_STR, buf)
msg = cls(src_port, dst_port, total_length, csum)
- return msg, None, buf[msg._MIN_LEN:total_length]
+ return msg, cls.get_packet_type(src_port, dst_port), buf[msg._MIN_LEN:total_length]
def serialize(self, payload, prev):
if self.total_length == 0:
|