5、Ryu应用开发学习第一篇
1、Hub集线器
1.1 原理
集线器的原理非常简单,就是将数据进行泛洪转发即可。
1.2 实现
''' openflow常用消息 Hello消息、Feature消息,Echo消息,以及Packet_in、Packet_out和Flow_Removed等 1、Hello:Openflow已经初始化 2、Features: 控制器通过向交换机发送features消息,用于请求交换机身份和基本能力信息,且交换机必须对此类消息进行应答。features消息通常在安全通道建立时执行。 Features Request(controller)、 Features Reply(switch)。 3、Echo:该消息用于测量延迟、带宽等。 4、Packet-In: 交换机收到一个网络数据包,在流表中没有匹配项,则发送Packet-In消息给控制器。如果交换机缓存足够多,网络数据包被临时存放在缓存中,网络数据包部分内容和在交换机缓存中的序号一同发给控制器;如果交换机缓存不足则将整个数据包发送给控制器。 5、Packet-Out: 用于控制器通过交换机指定端口转发数据包。 6、FLow_Mod:相当于流表项。 先进行switch_features_handler ''' from ryu.base import app_manager from ryu.ofproto import ofproto_v1_3 from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER,CONFIG_DISPATCHER import ryu.controller.handler class Hub(app_manager.RyuApp): # 明确使用的协议 OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] # 初始化 def __init__(self,*args,**kwargs): super(Hub,self).__init__(*args,**kwargs) # @ryu.controller.handler.set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) @ryu.controller.handler.set_ev_cls(ofp_event.EventOFPSwitchFeatures,CONFIG_DISPATCHER) def switch_features_handler(self,ev): msg = ev.msg # datapath包含流表管道、流表组、端口等信息 datapath = msg.datapath # 协议 ofproto = datapath.ofproto # 解析协议 ofp_parser = datapath.ofproto_parser '''在连接建立成功以后,控制器需要下发一个默认流表''' '''流表项由 priority、matchFields、instruction 等信息组成''' # 匹配域包含mac地址、ip地址... match = ofp_parser.OFPMatch() # 执行的动作: ''' 必备动作:从端口转发、转发到controller(不能处理的)、丢弃 ''' # OFPActionOutput将数据包发送出去, # 第一个参数OFPP_CONTROLLER是接收端口, # 第二个是数据包在交换机上缓存buffer_id,由于我们将数据包全部传送到控制器,所以不在交换机上缓存 actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,ofproto.OFPCML_NO_BUFFER)] # 优先级设置为0,最后执行 priority = 0 remind_content = "default flow entry" self.add_flow(datapath,priority,match,actions,remind_content) ''' 1、Datapath是逻辑上的网络设备,负责转发和处理数据,无控制能力。(描述一个交换的网桥,也是和控制器通信的 实体) 2、一个SDN DataPath包含控制数据平面接口(Control Data Plane Interface,CDPI) 、代理、转发引擎(Forwarding Engine)表和处理功能(Processing Function)。 3、SDN数据面(转发面)的关键技术:对数据面进行抽象建模。 ''' def add_flow(self,datapath,priority,match,actions,remind_content): # datapath属性 ofproto = datapath.ofproto ofp_parser = datapath.ofproto_parser inst = [ofp_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,actions)] # flow_mod 相当于流表项 # https://www.sdnlab.com/15786.html mod = ofp_parser.OFPFlowMod(datapath=datapath,priority=priority,match=match,instructions=inst) print("install to datapath, "+remind_content) # 发送数据 datapath.send_msg(mod) @ryu.controller.handler.set_ev_cls(ofp_event.EventOFPPacketIn,MAIN_DISPATCHER) def packet_in_handler(self,ev): print("packet_in_handler".center(50,"-")) print("packet In message") msg = ev.msg print("\033[34;1mmsg\033[0m") print(msg) datapath = msg.datapath print("\033[35;1mdatapath\033[0m") print(datapath) ofproto = datapath.ofproto ofp_parser = datapath.ofproto_parser # 获取源端口 in_port = msg.match['in_port'] print("get packet in,install flow entry,and lookback packet to datapath") # 匹配域,为空匹配所有 match = ofp_parser.OFPMatch() # 动作:泛洪法 actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_FLOOD)] # 优先级 priority = 1 remind_content = "hub flow entry" # 把流表项发送,构建流表,指导后续的数据包的转发 self.add_flow(datapath,priority,match,actions,remind_content) out = ofp_parser.OFPPacketOut(datapath=datapath,buffer_id = msg.buffer_id,in_port=in_port, actions=actions,data= msg.data) # 注意:我们将流表项下发了,但是数据包我们这次接收的,并没有处理 # 就是再将控制器上的数据包,重新发送给datapath,让他按照流表项处理 # buffer_id是这个数据包,存放在控制器中的缓冲区位置,是在事件中的buffer_id获取 print("packet out message".center(50,"-")) print("out") print(out) datapath.send_msg(out)
1.3 分析
2、Switch交换机
2.1 原理
(1)A向B发送数据,向controllerPacket-In报文。但此时不知道B所在的端口,因此向Controller发送packetIn,Controller也不知道B所在的端口,因此A进行泛洪操作。
(2)由于A进行泛洪发送,因此B和C都能接到来自A的消息,但只有B向A发送应答的消息。但B不知道A所在的端口,因此向Controller发送packetIn,而此时Controller已经知道了A所在的端口,所以,下发流表项。
(3)A收到B的应答消息后,A向B发送数据,但此时A还是不知道B所在的端口位置,因此,继续向Controller发送packetIn。而Controller此时已经知道B所在的端口,所以,不在需要洪泛发送。下发流表项。
(4)至此,A和B之间的通信已经建立(形象的比喻),因此,以后A、B之间发送数据,只需根据相应的流表项进行转发即可(应该可以认为不再经过Controller)。
2.2 实现
# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. """ An OpenFlow 1.0 L2 learning switch implementation. """ from ryu.base import app_manager from ryu.ofproto import ofproto_v1_3 from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER,CONFIG_DISPATCHER import ryu.controller.handler class SimpleSwitch(app_manager.RyuApp): # 明确使用的协议 OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] # 初始化 def __init__(self,*args,**kwargs): super(Hub,self).__init__(*args,**kwargs) # @ryu.controller.handler.set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) @ryu.controller.handler.set_ev_cls(ofp_event.EventOFPSwitchFeatures,CONFIG_DISPATCHER) def switch_features_handler(self,ev): msg = ev.msg # datapath包含流表管道、流表组、端口等信息 datapath = msg.datapath # 协议 ofproto = datapath.ofproto # 解析协议 ofp_parser = datapath.ofproto_parser '''在连接建立成功以后,控制器需要下发一个默认流表''' '''流表项由 priority、matchFields、instruction 等信息组成''' # 匹配域包含mac地址、ip地址... match = ofp_parser.OFPMatch() # 执行的动作: ''' 必备动作:从端口转发、转发到controller(不能处理的)、丢弃 ''' # OFPActionOutput将数据包发送出去, # 第一个参数OFPP_CONTROLLER是接收端口, # 第二个是数据包在交换机上缓存buffer_id,由于我们将数据包全部传送到控制器,所以不在交换机上缓存 actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,ofproto.OFPCML_NO_BUFFER)] # 优先级设置为0,最后执行 priority = 0 remind_content = "default flow entry" self.add_flow(datapath,priority,match,actions,remind_content) ''' 1、Datapath是逻辑上的网络设备,负责转发和处理数据,无控制能力。(描述一个交换的网桥,也是和控制器通信的 实体) 2、一个SDN DataPath包含控制数据平面接口(Control Data Plane Interface,CDPI) 、代理、转发引擎(Forwarding Engine)表和处理功能(Processing Function)。 3、SDN数据面(转发面)的关键技术:对数据面进行抽象建模。 ''' def add_flow(self,datapath,priority,match,actions,remind_content): # datapath属性 ofproto = datapath.ofproto ofp_parser = datapath.ofproto_parser inst = [ofp_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,actions)] # flow_mod 相当于流表项 # https://www.sdnlab.com/15786.html mod = ofp_parser.OFPFlowMod(datapath=datapath,priority=priority,match=match,instructions=inst) print("install to datapath, "+remind_content) # 发送数据 datapath.send_msg(mod) @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def _packet_in_handler(self, ev): msg = ev.msg # 解析datapath datapath = msg.datapath ofproto = datapath.ofproto ofp_parser = datapath.ofproto_parser ''' 每个交换机都有一个独立的转发表(流表),但是Controller下面会挂载很多Switch 所以,必须先确定哪个的mac地址(主机)在哪个datapath(交换机)上 ''' '''# get datapath id to identify switch''' # 获取datapath的id,即交换机 dpid = datapath.id # setdefault:当字典中存在key时,原字典不会改变value,当字典不存在key时,key对应的就为{} # 将id存入mac_to_port中 self.mac_to_port.setdefault(dpid,{}) '''# parser and analyse the receive packet message''' # 把二进制数据解析成数据包(报文) pkt = packet.Packet(msg.data) # 获取以太网的数据包 eth_pkt = pkt.get_protocols(ethernet.ethernet)[0] # 获取源和目的mac地址 dst = eth_pkt.dst src = eth_pkt.src # 获取源端口 ip_port = msg.match['ip_port'] # 输出消息 self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) '''# 学习mac地址到端口的映射关系,避免下次发送相同的端口时还是继续泛洪''' self.mac_to_port[dpid][src] = ip_port '''# 判断流表中(mac_to_port)是否存在此映射,存在直接发送,不存在泛洪''' # 存在,不需要洪泛 if dst in self.mac_to_port[dpid]: out_port = self.mac_to_port[dpid][dst] # 不存在,洪泛 else: out_port = ofproto.FLOOD '''# actions:1、转发/2、泛洪''' actions = [ofp_parser.OFPActionOutput(out_port)] ''' #####1存在##### # 下发flow_mod,指导怎么发送 ''' # 如果out_port存在 if out_port != ofproto.FLOOD: # 通过in_port和目的的mac地址匹配 match = ofp_parser.OFPMatch(in_port = in_port,eth_dst = dst) self.add_flow(datapath,1,match,actions) ''' #####2不存在##### # 泛洪 ''' ofp_parser.OFPPacketOut(datapath=datapath,buffer_id=msg.buffer_id,in_port=in_port,actions=actions,data=msg.data) datapath.send_msg(out)
2.3 分析
启动Minient topo结构
Ryu
当h1 ping h2时
当h1 ping h3 时
当h2 ping h3时