天天看點

Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2

簡單介紹

上一篇文章Ryu控制器官方應用simple_switch_13.py解讀提到我的兩個疑問 :

 - 為什麼要有3次Packet-in事件?

 - 為什麼有兩個比對項:

in_port

eth_dst

下面就做下實驗來嘗試解決這兩個疑問。

實驗1

  實驗1來驗證我的第一個想法,實作1次Packet-in就可以實作主機互通。

代碼修改

  隻修改Packet-in事件處理函數,這時候比對項隻有

eth_dst

。要不然意思就是:“把從1端口來的且目的位址是1端口主機MAC位址的資料包output到1端口”,顯然有問題。

@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def _packet_in_handler(self, ev):
        # If you hit this you might want to increase
        # the "miss_send_length" of your switch
        if ev.msg.msg_len < ev.msg.total_len:
            self.logger.debug("packet truncated: only %s of %s bytes",
                              ev.msg.msg_len, ev.msg.total_len)
        msg = ev.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        in_port = msg.match['in_port']

        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[]

        if eth.ethertype == ether_types.ETH_TYPE_LLDP:
            # ignore lldp packet
            return
        dst = eth.dst
        src = eth.src

        dpid = datapath.id
        self.mac_to_port.setdefault(dpid, {})

        self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)

        # learn a mac address to avoid FLOOD next time.
        self.mac_to_port[dpid][src] = in_port

        # *************修改内容*************
        actions = [parser.OFPActionOutput(in_port)]
        match = parser.OFPMatch(eth_dst=src)
        if msg.buffer_id != ofproto.OFP_NO_BUFFER:
            self.add_flow(datapath, , match, actions, msg.buffer_id)
            return
        else:
            self.add_flow(datapath, , match, actions)
        if dst == 'ff:ff:ff:ff:ff:ff':
            # 當資料包是廣播包時
            # 用于Packet-out中的action,洪泛廣播包
            actions = [parser.OFPActionOutput(ofproto.OFPP_FLOOD)]
        # *************修改結束*************

        # if dst in self.mac_to_port[dpid]:
        #     out_port = self.mac_to_port[dpid][dst]
        # else:
        #     out_port = ofproto.OFPP_FLOOD
        #
        # # install a flow to avoid packet_in next time
        # if out_port != ofproto.OFPP_FLOOD:
        #     match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
        #     # verify if we have a valid buffer_id, if yes avoid to send both
        #     # flow_mod & packet_out
        #     if msg.buffer_id != ofproto.OFP_NO_BUFFER:
        #         self.add_flow(datapath, 1, match, actions, msg.buffer_id)
        #         return
        #     else:
        #         self.add_flow(datapath, 1, match, actions)
        data = None
        if msg.buffer_id == ofproto.OFP_NO_BUFFER:
            data = msg.data

        out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
                                  in_port=in_port, actions=actions, data=data)
        datapath.send_msg(out)
           

【注】主要是修改了“下發流表操作”的相關代碼部分。

實驗結果

實驗結果完全沒按照套路出牌,首先是ping不通

Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2

流表項隻添加了一條

Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2

Packet-in/Packet-out事件不停止,流表重複添加

Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2

【注】flow_add中内容是一樣的。

  第一次Packet-in/Packet-out事件是因為ARP Request,後面的Packet-in/Packet-out事件是因為ICMP Request。流表項添加的内容都是一樣的,如下:

cookie=0x0, duration=351.158s, table=0, n_packets=1, n_bytes=42, priority=1,

  

dl_dst=00:00:00:00:00:01 actions=output:1

主機h2(h2)正常回複ARP Request:

Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2

錯誤原因分析

   ARP Request引發的第一次Packet-in屬于正常,然後添加了比對發往該request主機資料包的流表項;

cookie=0x0, duration=351.158s, table=0, n_packets=1, n_bytes=42, priority=1,

   

dl_dst=00:00:00:00:00:01 actions=output:1

  ARP Request被洪泛至除源端口外的所有端口,然後主機h2回複了ARP Reply。因為流表項的存在,ARP Reply被上面的流表項比對處理,ARP Reply沒有引起Packet-in,而是直接根據流表項的動作被發往實體端口1。然後主機h1擷取到ARP Reply,得到主機h2的IP與MAC對應關系,是以發送ICMP Request包,但是交換機并沒有流表項來處理ICMP Request包,是以引發Packet-in事件。但是代碼中處理這個Packet-in事件是根據包的來源資訊來添加到這個來源的流表項;因為都是主機h1發的資料包,是以這時新添加的流表項與之前的流表項内容一樣,并沒有什麼卵用。是以ICMP Request包得不到處理,就會一直引起Packet-in。

  主機h2回複的ARP Reply資料包沒有被Controller捕獲呢,沒有添加到主機h2的流表項,ICMP Request資料包可不知道發往哪裡啊,就隻有送Controller了。

結論

  Too yong too simple。考慮太少,沒有認真分析資料包的處理流程,沒有考慮到流表項的修改引起的一些列變化。

實驗2

  實驗2來驗證我的第一個想法,實作隻需要

eth_dst

這一個比對項。

代碼修改

  修改非常少,去掉in_port比對項就行

@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def _packet_in_handler(self, ev):
        msg = ev.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        in_port = msg.match['in_port']

        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[]

        dst = eth.dst
        src = eth.src

        dpid = datapath.id
        self.mac_to_port.setdefault(dpid, {})

        self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)

        self.mac_to_port[dpid][src] = in_port

        if dst in self.mac_to_port[dpid]:
            out_port = self.mac_to_port[dpid][dst]
        else:
            out_port = ofproto.OFPP_FLOOD

        actions = [parser.OFPActionOutput(out_port)]

        if out_port != ofproto.OFPP_FLOOD:
            #修改處
            # match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
            match = parser.OFPMatch(eth_dst=dst)
            self.add_flow(datapath, , match, actions)

        data = None
        if msg.buffer_id == ofproto.OFP_NO_BUFFER:
            data = msg.data

        out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
                                  in_port=in_port, actions=actions, data=data)
        datapath.send_msg(out)
           

實驗結果

  實驗結果非常有戲劇性,會根據主機h3的參與情況發生變化。

實驗操作1

  1. h1 ping -c1 h2

    (h1可與h2調換位置)
  2. h3 ping -c1 h1

    (第2步可以與第3步調換順序)
  3. h3 ping -c1 h2

    (第3步可以與第2步調換順序)
  4. pingall

全部能ping通

Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2

流表内容也沒有問題

Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2

下面來看抓包資料

Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2
Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2
Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2

  h1 ping h2時的過程與修改前的并沒有什麼差別,同樣是3次Packet-in事件,添加了兩條流表項

cookie=0x0, duration=36.552s, table=0, n_packets=3, n_bytes=182, priority=1,

  

dl_dst=00:00:00:00:00:02 actions=output:2

cookie=0x0, duration=36.554s, table=0, n_packets=4, n_bytes=280, priority=1,

  

dl_dst=00:00:00:00:00:01 actions=output:1

  h3 ping h1時的添加的流表項

cookie=0x0, duration=28.181s, table=0, n_packets=5, n_bytes=322, priority=1,

  

dl_dst=00:00:00:00:00:03 actions=output:3

實驗操作2

  1. h1 ping -c1 h2

    (h1可與h2調換位置)
  2. h1 ping -c1 h3

    (第2步可以與第3步調換順序)
  3. h2 ping -c1 h3

    (第3步可以與第2步調換順序)
  4. pingall

也能全部ping通

Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2

但是流表内容就不一樣了

Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2

相比操作1的流表少了一條流表項,少的是處理到h3(00:00:00:00:00:03)資料包的流表項

下面來看看抓包截圖

Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2
Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2
Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2
Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2

對于上面的抓包圖,可以總結一點:

  由于沒有處理到h3(00:00:00:00:00:03)資料包的流表項,是以導緻發往h3的資料包全部引起Packet-in事件交給Controller處理,而Controller的處理是洪泛至所有端口。

  通過對每個主機網卡監聽可以證明,下圖是主機h1的網卡監聽,可以看到h1竟然收到h2發往h3的資料包。

Ryu控制器官方應用simple_switch_13.py解讀2簡單介紹實驗1實驗2

為什麼操作1正常,而操作2就有問題呢?留給讀者思考。