2016-12-20 8 views
0

OpenFlow 1.3을 사용하여 mininet에서 Open vSwitch와 함께 Ryu SDN 컨트롤러를 사용하여 DHCP 패킷을 구문 분석합니다. 온라인 예제와 Ryu 리소스를 따라 DHCP 패킷 파서를 구현했습니다. 그러나 예상대로 작동하지 않으며, 첫 번째 솔루션이 작동하지 않는 이유에 대해 누구나 통찰력을 갖고 있는지 궁금합니다.Ryu의 get_protocol (dhcp.dhcp)을 사용하여 DHCP 패킷을 구문 분석 할 수 없음

가 DHCP 패킷을 분석하기위한 코드의 예는 다음과 같습니다

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) 

내 코드가 비슷한 맥락을 다음과 : 나는이 정확한 순서에 따라 오전으로

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 등과 같은 다른 프로토콜. 아래 예.

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가 지속적으로 None을 돌려 동안 나는 위의 목록 세 파서 실제로 데이터를 반환한다는 것입니다. 나는 스위치를 통해 DHCP 패킷을 보내서 이미 이것을 테스트했다.

다음 코드 스 니펫은 세 개 이상의 값을 가진 패킷 목록을 식별합니다. 인덱스 3의 값을 저장하고이를 내 DHCP 패킷으로 설정합니다. .는 DHCP 패킷에서, 나는 내가 관심이있는 데이터를 포함하는 인덱스 2에서 문자열을 구문 분석의 집중

# 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) 

인쇄를이 값에서 이렇게 다음과 같습니다

0x1 
0x1 
0x6 
0x0 
0x440x30x980x11 
0x00x0 
0x00x0 
0x00x00x00x0 
0x00x00x00x0 
0x00x00x00x0 
0x00x00x00x0 
0x7e0x1d0xcc0xe70xee0x4f 
0x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x0 

을 위의 코드는 DHCP 패킷의 내용을 관찰 할 수있게 해주지 만 실제로 pkt [ 'pkt']. get_protocol (dhcp.dhcp) 메소드를 사용하여 비슷한 결과를 얻지 못하는 이유를 파악하려고합니다.

답변

0

좋아, 문제가 발견되었습니다. dhcp.py의 200 - 218 행에는 try except 문이 있습니다. 나는 그것이 발생 오류를 확인하기 위해 시도 밖으로 cls._parser를 뽑아 나는이 얻을 :

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 vSwitch를 사용하고 있으며 128 바이트가 한계로 보입니다. 따라서 Ryu의 dhcp.py 파일과 충돌이 있습니다. 내 솔루션은 dhcp.py를 수정하는 것이 었습니다. 그 방법은 다음과 같습니다.

코드를 수정하기 전에 Ryu 컨트롤러를 먼저 업데이트하는 것이 좋습니다. 절차는 다음과 같습니다.

1 단계 : VM을 사용하는 경우. 지금 스냅 샷을 만들거나 복제하십시오.

2 단계 : 업데이트 류는

cd ryu 
git pull 

내가처럼 당신이 이전 버전을 실행 한 경우, 다음과 같은 오류가 발생할 수 있습니다 때 당신의 류 컨트롤러 실행 다음 시도 :

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 파일 업데이트

sudo pip install oslo.config --upgrade 

2 단계의 TypeError를 이제 res로 변경해야합니다. olved. (바라건대, 당신을 위해서 VM을 복제했다.)

3 단계 : 수정 류의 dhcp.py 파일/류/류/lib 디렉토리/패킷에있는

류의 dhcp.py 파일() 이상 235 바이트의 버퍼를받을 것으로 기대하고있다. 그렇지 않으면, throw 및 오류를 리턴하고 컨트롤러에 아무것도 리턴하지 않습니다. 내 버퍼는 약 81 바이트의 버퍼 크기만을 받기 때문에. Ryu dhcp.py 파일을 다음과 같이 수정했습니다.

dhcp.py가 '! BBBBIHH4s4s4s4s16s64s128s'의 문자열 형식을 지정하기 때문에 오류가 발생합니다. # 1에서는 두 번째 옵션을 만들었습니다. 그렇게하면 패킷이 100 바이트보다 작은 경우 패킷을 다르게 처리하는 if 문을 몇 개 삽입 할 수 있습니다. 마찬가지로 패킷이 235 바이트보다 큰 경우 dhcp.py는 패킷을 정상적으로 처리하고 추가 값을 반환합니다.

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:]) 

전체 파일은 곧 https://github.com/Ryuretic/RyureticLabs/tree/master/ryu/ryu/app/Ryuretic/Support_Files이됩니다.

당신이 dhcp.py 파일이 adjustements 할 경우 다음 헤더 필드에 접근 얻을 것입니다 :

============== ==================== 
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.