Ryu控制器官方应用simple_switch_13.py解读2

时间:2023-01-17 20:40:03

简单介绍

上一篇文章Ryu控制器官方应用simple_switch_13.py解读提到我的两个疑问 :
 - 为什么要有3次Packet-in事件?
 - 为什么有两个匹配项:in_porteth_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)[0]

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, 1, match, actions, msg.buffer_id)
return
else:
self.add_flow(datapath, 1, 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
流表项只添加了一条
Ryu控制器官方应用simple_switch_13.py解读2
Packet-in/Packet-out事件不停止,流表重复添加
Ryu控制器官方应用simple_switch_13.py解读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

错误原因分析

   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)[0]

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

实验结果

  实验结果非常有戏剧性,会根据主机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
流表内容也没有问题
Ryu控制器官方应用simple_switch_13.py解读2

下面来看抓包数据
Ryu控制器官方应用simple_switch_13.py解读2
Ryu控制器官方应用simple_switch_13.py解读2
Ryu控制器官方应用simple_switch_13.py解读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
但是流表内容就不一样了
Ryu控制器官方应用simple_switch_13.py解读2
相比操作1的流表少了一条流表项,少的是处理到h3(00:00:00:00:00:03)数据包的流表项

下面来看看抓包截图
Ryu控制器官方应用simple_switch_13.py解读2
Ryu控制器官方应用simple_switch_13.py解读2
Ryu控制器官方应用simple_switch_13.py解读2
Ryu控制器官方应用simple_switch_13.py解读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就有问题呢?留给读者思考。