I'm using the Ryu SDN controller with an Open vSwitch on mininet using OpenFlow 1.3 to parse DHCP packets. Following online examples and the Ryu resources, I've implemented a DHCP packet parser. However, it does not work as I expected it to, and I'm wondering if anyone has any insight as to why my first solution does not work?
An example of a code snippet for parsing a DHCP packet is below:
from ryu.lib.packet import dhcp
...
...
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
pkt = packet.Packet(msg.data)
dhcpPacket = pkt.get_protocol(dhcp.dhcp)
My code follows a similar vein:
from ryu.lib.packet import dhcp
...
...
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
pkt = {}
pkt['msg'] = ev.msg
pkt['dp'] = pkt['msg'].datapath
pkt['pkt'] = packet.Packet(pkt['msg'].data)
pkt['dhcp'] = pkt['pkt'].get_protocol(dhcp.dhcp)
This seems reasonable as I am following this exact sequence with other protocols like ARP, ICMP, IP, etc. Examples below.
pkt['arp'] = pkt['pkt'].get_protocol(arp.arp)
pkt['ip'] = pkt['pkt'].get_protocol(ipv4.ipv4)
pkt['icmp'] = pkt['pkt'].get_protocol(icmp.icmp)
The only problem is that the three parsers I list above actually return data, while the get_protocol for DHCP consistently returns None. I have already tested this by sending DHCP packets through my switch.
What does work is the following code snippet where I identify packet lists having more than three values. I save the value at index three and set that as my DHCP packet. In the DHCP packet, I concentrate of parsing the string at index 2. That contains the data I'm interested in.
# pkt['dhcp'] = pkt['pkt'].get_protocol(dhcp.dhcp)
# Check if pkt['pkt]] > 3 elements, if so, parse DHCP string
#Standard pkt['dhcp'] = (None, None, String)
if len(pkt['pkt']) > 3:
pkt['dhcp'] = dhcp.dhcp.parser(pkt['pkt'][3])
pkt['op'] = hex(ord(dhcp_p[2][0]))
pkt['htype'] = hex(ord(dhcp_p[2][1]))
pkt['hlen'] = hex(ord(dhcp_p[2][2]))
pkt['hops'] = hex(ord(dhcp_p[2][3]))
def parseDHCP(pkt_d,start,stop):
s_value = ''
stop += 1
for val in range(start,stop):
s_value += str(hex(ord(pkt_d[val])))
return s_value
pkt['xid'] = parseDHCP(dhcp_p[2],4,7)
pkt['secs'] = parseDHCP(dhcp_p[2],8,9)
pkt['flags'] = parseDHCP(dhcp_p[2],10,11)
pkt['ciaddr'] = parseDHCP(dhcp_p[2],12,15)
pkt['yiaddr'] = parseDHCP(dhcp_p[2],16,19)
pkt['siaddr'] = parseDHCP(dhcp_p[2],20,23)
pkt['giaddr'] = parseDHCP(dhcp_p[2],24,27)
pkt['chaddr'] = parseDHCP(dhcp_p[2],28,33)
pkt['pad'] = parseDHCP(dhcp_p[2],34,43)
A print out of these values looks like so:
0x1
0x1
0x6
0x0
0x440x30x980x11
0x00x0
0x00x0
0x00x00x00x0
0x00x00x00x0
0x00x00x00x0
0x00x00x00x0
0x7e0x1d0xcc0xe70xee0x4f
0x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x0
The above code allows me to observe the contents of DHCP packets, but I'm really trying to figure out why I'm not achieving similar results using the pkt['pkt'].get_protocol(dhcp.dhcp) method?
Ok, I found the problem. In lines 200 – 218 of dhcp.py there is a try except statement. I pulled the cls._parser out of the try to see the error it throws and I get this:
So, dhcp.py is not receiving the 233 bytes that it requires. Unfortunately, I am using an Open vSwitch on the VM provided by SDN hub, and 128 bytes seems to be a limit. So, I have a conflict with Ryu's dhcp.py file. My solution was to modify dhcp.py. How that was done follows.
Before modifying the code, I recommend you first update your Ryu controller. The procedures are as follows:
Step 1: If you are using a VM. Take a stapshot or clone it now.
Step 2: Update Ryu
If you were running an older version like I was, then the following error may occur when you next attempt to run your Ryu controller:
Step 3: Update your oslo_config file
The TypeError from step 2 should now be resolved. (Hopefully, you cloned your VM just in case.)
Step 3: Modify Ryu's dhcp.py file
Ryu's dhcp.py file (located at /ryu/ryu/lib/packet) expects to receive a buffer of more than 235 bytes. Otherwise, it throws and error and returns nothing to the controller. Since my buffer only receives a buffer size of about 81 bytes. I modified the Ryu dhcp.py file as follows.
The error occurs because dhcp.py specifies a string format of '!BBBBIHH4s4s4s4s16s64s128s'. In #1, I created a second option. Do so, allows me to insert a few if statements to handle the packet differently if a packet arrives that is smaller than 100 bytes. Likewise, if the packet is greater than 235 bytes, then dhcp.py's handles the packet normally and returns the extra values.
The complete file will soon be on https://github.com/Ryuretic/RyureticLabs/tree/master/ryu/ryu/app/Ryuretic/Support_Files.
If you make these adjustements to the dhcp.py file, then you will gain access to the following header fields: