首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >不能用Ryu's get_protocol(dhcp.dhcp)解析DHCP数据包

不能用Ryu's get_protocol(dhcp.dhcp)解析DHCP数据包
EN

Stack Overflow用户
提问于 2016-12-20 14:17:55
回答 1查看 672关注 0票数 0

我正在使用Ryu控制器,并在小型vSwitch上使用一个开放的OpenFlow,使用OpenFlow 1.3解析DHCP数据包。下面是在线示例和Ryu资源,我实现了一个DHCP数据包解析器。然而,它并不像我预期的那样起作用,我想知道是否有人对我的第一个解决方案不起作用有任何见解?

下面是解析DHCP数据包的代码片段的示例:

代码语言:javascript
复制
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)

我的代码也有类似的内容:

代码语言:javascript
复制
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)

这似乎是合理的,因为我遵循这个序列与其他协议,如ARP,ICMP,IP等。

代码语言:javascript
复制
pkt['arp'] = pkt['pkt'].get_protocol(arp.arp)
pkt['ip'] = pkt['pkt'].get_protocol(ipv4.ipv4)
pkt['icmp'] = pkt['pkt'].get_protocol(icmp.icmp)

唯一的问题是上面列出的三个解析器实际上返回数据,而DHCP的get_protocol始终不返回任何数据。我已经通过我的交换机发送DHCP数据包来测试这一点。

真正起作用的是下面的代码片段,我在其中识别具有三个以上值的数据包列表。我将值保存在索引3并将其设置为我的DHCP数据包。在DHCP包中,我集中在索引2处解析字符串,其中包含我感兴趣的数据。

代码语言:javascript
复制
# 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)

这些值的打印如下所示:

代码语言:javascript
复制
0x1
0x1
0x6
0x0
0x440x30x980x11
0x00x0
0x00x0
0x00x00x00x0
0x00x00x00x0
0x00x00x00x0
0x00x00x00x0
0x7e0x1d0xcc0xe70xee0x4f
0x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x0

上面的代码允许我观察DHCP数据包的内容,但是我真的想弄清楚为什么我没有使用pkt‘pkt’..get_protocol(dhcp.dhcp)方法实现类似的结果?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-12-21 21:31:56

好吧,我发现问题了。在dhcp.py的第200-218行中,有一个try语句。我将cls._parser从试图查看它抛出的错误中提取出来,得到如下结果:

代码语言:javascript
复制
Ryuretic_coupler: Exception occurred during handler processing. Backtrace    
from offending handler [initial_event] servicing event [EventOFPPacketIn] follows.
Traceback (most recent call last):
  File "/home/ubuntu/ryu/ryu/base/app_manager.py", line 290, in _event_loop
    handler(ev)
  File "/home/ubuntu/ryu/ryu/app/Ryuretic/Ryuretic.py", line 72, in initial_event
    pkt = parsPkt.handle_pkt(ev)
  File "/home/ubuntu/ryu/ryu/app/Ryuretic/Pkt_Parse13.py", line 81, in handle_pkt
    dhcp_p = pkt['dhcp'] = dhcp.dhcp.parser(pkt['pkt'][3])
  File "/home/ubuntu/ryu/ryu/lib/packet/dhcp.py", line 212, in parser
    return cls._parser(buf)
  File "/home/ubuntu/ryu/ryu/lib/packet/dhcp.py", line 192, in _parser
    ) = struct.unpack_from(unpack_str, buf)
error: unpack_from requires a buffer of at least 233 bytes

因此,dhcp.py没有接收它所需的233个字节。不幸的是,我在SDN提供的VM上使用了一个Open,而128字节似乎是一个限制。所以,我和Ryu的dhcp.py文件有冲突。我的解决方案是修改dhcp.py。接下来是如何做到的。

在修改代码之前,我建议您首先更新Ryu控制器。这些程序如下:

步骤1:如果您使用的是VM。现在就拍一张木桩或者克隆它。

步骤2:更新Ryu

代码语言:javascript
复制
cd ryu
git pull

如果您像我一样运行旧版本,那么当您下一次尝试运行Ryu控制器时可能会发生以下错误:

代码语言:javascript
复制
Traceback (most recent call last):
File "./bin/ryu-manager", line 18, in <module>
  from ryu.cmd.manager import main
File "/home/ubuntu/ryu/ryu/cmd/manager.py", line 31, in <module>
  from ryu.base.app_manager import AppManager
File "/home/ubuntu/ryu/ryu/base/app_manager.py", line 37, in <module>
  from ryu.controller.controller import Datapath
File "/home/ubuntu/ryu/ryu/controller/controller.py", line 74, in <module>
  help='Maximum number of unreplied echo requests before datapath is disconnected.')
File "/usr/local/lib/python2.7/dist-packages/oslo_config/cfg.py", line 1033, in __init__
  super(IntOpt, self).__init__(name, type=types.Integer(), **kwargs)
TypeError: __init__() got an unexpected keyword argument 'min'

步骤3:更新oslo_config文件

代码语言:javascript
复制
sudo pip install oslo.config --upgrade

现在应该解决步骤2中的TypeError。(希望您克隆了VM,以防万一。)

步骤3:修改Ryu的dhcp.py文件

ryu的dhcp.py文件(位于/ryu/ryu/lib/packet)期望接收超过235个字节的缓冲区。否则,它会抛出并出错,并且不会向控制器返回任何内容。因为我的缓冲区只接收约81个字节的缓冲区大小。我对Ryu dhcp.py文件进行了如下修改。

发生此错误是因为dhcp.py指定的字符串格式为‘!BBBBIHH4s4s4s4s4s16s64s128s’。在#1中,我创建了第二个选项。这样做,允许我插入一些if语句,以便在数据包到达小于100个字节的情况下以不同的方式处理数据包。同样,如果数据包大于235个字节,dhcp.py将正常处理数据包并返回额外的值。

代码语言:javascript
复制
class dhcp(packet_base.PacketBase):
    """DHCP (RFC 2131) header encoder/decoder class.
                ....deleted....
    """
    _MIN_LEN = 236
    _HLEN_UNPACK_STR = '!BBB'
    _HLEN_UNPACK_LEN = struct.calcsize(_HLEN_UNPACK_STR)
    _DHCP_UNPACK_STR = '!BIHH4s4s4s4s%ds%ds64s128s'
    ##################################################
    #1(mod) Created second option for unpacking string
    _DHCP_UNPACK_STR2 = '!BIHH4s4s4s4s%ds%ds40s'
    _DHCP_PACK_STR = '!BBBBIHH4s4s4s4s16s64s128s'
    #################################################
    _DHCP_CHADDR_LEN = 16
    _HARDWARE_TYPE_ETHERNET = 1
    _class_prefixes = ['options']
    _TYPE = {
        'ascii': [
            'ciaddr', 'yiaddr', 'siaddr', 'giaddr', 'chaddr', 'sname'
        ]
    }

    def __init__(self, op, chaddr, options, 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__()
        #...Deleted No Changes made to init. 

    @classmethod
    def _parser(cls, buf):
        (op, htype, hlen) = struct.unpack_from(cls._HLEN_UNPACK_STR, buf)
        buf = buf[cls._HLEN_UNPACK_LEN:]
        ####################################################
        #2(mod) provided option for smaller packet sizes
        if len(buf) < 100:
            unpack_str = cls._DHCP_UNPACK_STR2 % (hlen,
                                             (cls._DHCP_CHADDR_LEN - hlen))
        else:
            unpack_str = cls._DHCP_UNPACK_STR % (hlen,
                                             (cls._DHCP_CHADDR_LEN - hlen))            
        #####################################################
        min_len = struct.calcsize(unpack_str)
        ######################################################
        #3(mod) provided option for smaller packets, set bootfile to b''
        if min_len > 233:
            (hops, xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr,  
             chaddr, dummy, sname, boot_file
             ) = struct.unpack_from(unpack_str, buf)
        else:
            boot_file=b''
            (hops, xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr,  
             chaddr, dummy, sname, boot_file
             ) = struct.unpack_from(unpack_str, buf)+(boot_file,)
        ########################################################    
        length = min_len
        ########################################################
        # (mod) provided option for smaller packet sizes, no parse_opt
        if len(buf) > 233: 
            parse_opt = options.parser(buf[min_len:])
            length += parse_opt.options_len
        else:
            parse_opt = None
            length = min_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:])

完整的文件将很快出现在Files上。

如果您对dhcp.py文件进行了这些调整,那么您将访问以下头字段:

代码语言:javascript
复制
============== ====================
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, DHserver.
giaddr         Relay agent IP address, used in booting via a\
               relay agent.
chaddr         Client hardware address.
sname(partial) Optional server host name, null terminated string.
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41244431

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档