簡單介紹
上一篇文章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不通
流表項隻添加了一條
Packet-in/Packet-out事件不停止,流表重複添加
【注】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:
錯誤原因分析
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
-
(h1可與h2調換位置)h1 ping -c1 h2
-
(第2步可以與第3步調換順序)h3 ping -c1 h1
-
(第3步可以與第2步調換順序)h3 ping -c1 h2
-
pingall
全部能ping通
流表内容也沒有問題
下面來看抓包資料
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
-
(h1可與h2調換位置)h1 ping -c1 h2
-
(第2步可以與第3步調換順序)h1 ping -c1 h3
-
(第3步可以與第2步調換順序)h2 ping -c1 h3
-
pingall
也能全部ping通
但是流表内容就不一樣了
相比操作1的流表少了一條流表項,少的是處理到h3(00:00:00:00:00:03)資料包的流表項
下面來看看抓包截圖
對于上面的抓包圖,可以總結一點:
由于沒有處理到h3(00:00:00:00:00:03)資料包的流表項,是以導緻發往h3的資料包全部引起Packet-in事件交給Controller處理,而Controller的處理是洪泛至所有端口。
通過對每個主機網卡監聽可以證明,下圖是主機h1的網卡監聽,可以看到h1竟然收到h2發往h3的資料包。
為什麼操作1正常,而操作2就有問題呢?留給讀者思考。