# Simple Switch (1.3 version ) ## Library ``` from ryu.controller import ofp_event #open flow protocol Event from ryu.controller.handler import CONFIG_DISPATCHER , MAIN_DISPATCHER from ryu.controller.handler import set_ev_cls from ryu.ofproto import ofproto_v1_3 # import open flows version 1.3 from ryu.lib.packet import packet from ryu.lib.packet import ethernet ``` ## Class "Initial & inherit" 會先設定 OpenFlow 的 Version -> 1.3 ,MAC 位址表的 mac\_to\_port 也已經被定義。 ```python class SimpleSwitch13(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] def __init__(self , *args, **kwargs): super(SimpleSwitch13 , self).__init__(*args,**kwargs) self.mac_to_port = {} ``` ### inherit (app.app_manager,RyuApp) `meowheckerSimpleSwitch` class 它繼承自 `app.app_manager` 和 `RyuApp` 兩個父類 ## Event Handler Ryu 完成 Switch 3 handshake -> 後收到 from Swith 的 openflow Message(BUTTON XD 概念) -> 產生Event CONFIG_DISPATCHER(收發 dispatcher) 寫function 來處理OVS 發過來的Message (Event Handler) ![](https://hackmd.io/_uploads/r1Ugo0sY2.png) ## Create Table-miss Flow Entry OVS 連到Controller 時 ADDing Table-miss Flow Entry 到 Flow table 中為接收 Packet-In 訊息做準備。 ```python #ryu.controller.handler.CONFIG_DISPATCHER 接收 SwitchFeatures 訊息 @set_ev_cls(ofp_event,EventOFPSwitchFeatures,CONFIG_DISPATCHER) def switch_features_handler(self,ev): datapath = ev.msg.datapath # Switch ID (receive Message) ofproto = datapath.ofproto # switch 的 openFlow parser = datapath.parser # switch -> 解析 ``` ev.msg -> switch 送過來的Message >Table Miss: 在Flow Table尋找符合規則的Flow Entry時 找不到相對應的Flow Entry ev.msg -> Openflow message entry Datapath class 處理 OpenFlow Message e.g. - 與交換器的通訊 - Trigger 接收訊息相關的Event ```python # Establish Table-Miss FLOW (policy Rule) match = parser.OFPMatch() # mach ALLLLL packet actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,ofproto.OFPCML_NO_BUFFER)] self.add_flow(datapath,0,match,actions) ``` 他的OFP 應該是 openflowpacket 這個FLOW 可以 match 所有的封包 output action 指向 Ryu Controller OFPCML\_NO\_BUFFER -> OFP controller "max length" 送 ( packet Header + packet payload ) to controller ## packet IN: Unknown packet into the controller (No match Rules) ryu.controller.handler.MAIN_DISPATCHER 一般狀態 ```python @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def packet_in_handler(self,ev): # get switch ID && parser it FOr Add FLOWSSSSSSS !!! msg=ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.parser ``` ### Parse Packet Infromation && Updata Packet Table !!! ```python # Parse Packet Infromation ip_port = msg.match['in_port'] # packet Source pkt = packet.Packet(msg.data) eth = pkt.get_protocol(ethernet.ethernet)[0] #get packet protocol dst = eth.dst #protocol, dst src = eth.src #protocol, src dpid = datapath.id self.mac_to_port.setdefault(dpid,{}) #Mac Table Structure ! self.logger.info("packet 跑過來拉 ㄚㄚㄚㄚ packet information: %s %s %s %s ", dpid , src , dst , ip_port) # learn a mac address self.mac_to_port[dpid][src] = in_port #設定 MAC table , Output Port 取得其他 Node ``` in_port -> Controller 參考 ``` root@ubuntu:/home/user/Desktop# ovs-ofctl dump-flows s1 NXST_FLOW reply (xid=0x4): cookie=0x0, duration=5.097s, table=0, n_packets=6, n_bytes=588, idle_timeout=60, idle_age=0, priority=65535,icmp,in_port=2,vlan_tci=0x0000,dl_src=26:34:ef:a9:41:b4,dl_dst=ce:ce:b8:90:65:1e,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,icmp_type=0,icmp_code=0 actions=output:1 cookie=0x0, duration=4.096s, table=0, n_packets=6, n_bytes=588, idle_timeout=60, idle_age=0, priority=65535,icmp,in_port=1,vlan_tci=0x0000,dl_src=ce:ce:b8:90:65:1e,dl_dst=26:34:ef:a9:41:b4,nw_src=10.0.0.1,nw_dst=10.0.0.2,nw_tos=0,icmp_type=8,icmp_code=0 actions=output:2 cookie=0x0, duration=0.082s, table=0, n_packets=1, n_bytes=42, idle_timeout=60, idle_age=0, priority=65535,arp,in_port=2,vlan_tci=0x0000,dl_src=26:34:ef:a9:41:b4,dl_dst=ce:ce:b8:90:65:1e,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,arp_op=1 actions=output:1 cookie=0x0, duration=0.079s, table=0, n_packets=1, n_bytes=42, idle_timeout=60, idle_age=0, priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=ce:ce:b8:90:65:1e,dl_dst=26:34:ef:a9:41:b4,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,arp_op=2 actions=output:2 ``` src ,dst 為Ehernet 收到MAC aDDress Mac table structure ``` dpid {} (key:dpid,value:{}) { switch1 {src:in_port} switch2 {src:in_port} } ``` self.mac_to_port[dpid][src] = in_port 接到多個 OpenFlow Switch 紀錄它們的ID ### Packet Forwarding && Add the Flow(MAC 有找到 routing) ``` # Packet Forwarding (根據MAC table ) if dst in self.mac_to_port[dpid]: #Look up Mac Table out_port = self.mac_to_port[dpid][dst] else: out_port = ofproto.OFPP_FLOOD # HUB -> 找不到就 brocast (security Badddddd!!! ) actions = [parser.OFPActionOutput(out_port)] #install a flow to avoid next packet_in next Time if out_port != ofproto.OFPP_FLOOD: match = parser.OFPMatch(input=in_port, eth_dst=dst) self.add_flow(datapath,1,match,actions) ``` ## add_Flow function ```python def add_flow(self,datapath,priority,match,actions): ofproto = datapath.ofproto parser = datapath.ofproto_parser #Action inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,actions)] //isntruction (commend) ``` Apply Actions 是用來設定那些必須立即執行的 action (??) ### Adding the flow into Switch Flow Table OFPFlowMod "Class" ``` # Adding the flow into Switch Flow Table newFlow = parser.OFPFlowMod(datapath=datapath,property=property,match=match,isinstance = inst) #Send the Flow to Switch datapath.send_msg(newFlow) ``` OFPFlowMod 類別的inintial Parameter 很多 - datapath - cookie (0) //Controller 所設定儲存的資料 - table_id (0) //指定 Flow Entry 的 Table ID 。 - command (ofproto_v1_3.OFPFC_ADD ![](https://hackmd.io/_uploads/B1sNwVu5h.png) Flow Entry 的有效期限 - idle_timeout (0) Flow Entry 有被參照,則超過時間之後會 重新歸零計算 - hard_timeout (0) - priority (0 - out_port (0) - match - instructions ([]) ```python= data = None if msg.buffer_id == ofproto.OFP_NO_BUFFER: data = msg.data #forward Packet out = parser.OFPPacketOut(datapath=datapath,buffer_id=msg,buffer.id,in_port=in_port,actions=actions,data=data) datapath.send_msg(out) ``` 設定封包的 binary data 。主要用在 buffer_id 為 OFP_NO_BUFFER 的情況。如果使用 了 OpenFlow 交換器的緩衝區則可以省略 ### Switch Hub Code ```python= # openflow 13 from ryu.controller import ofp_event #open flow protocol Event from ryu.controller.handler import CONFIG_DISPATCHER , MAIN_DISPATCHER from ryu.controller.handler import set_ev_cls from ryu.ofproto import ofproto_v1_3 # import open flows version 1.3 from ryu.lib.packet import packet from ryu.lib.packet import ethernet class meowheckerSimpleSwitch(app.app_manager,RyuApp): OFP_VERSION = [ofproto_v1_3.OFP_VERSION] # ofp version #Initial Class def __init__(self,*args,**kwargs): super(meowheckerSimpleSwitch,self).__init__(*args,**kwargs) self.mac_to_port={} # MAC table #Event Handler # Deal with the First Connection #ryu.controller.handler.CONFIG_DISPATCHER 接收 SwitchFeatures 訊息 @set_ev_cls(ofp_event,EventOFPSwitchFeatures,CONFIG_DISPATCHER) def switch_features_handler(self,ev): datapath = ev.msg.datapath # Switch ID (receive Message) ofproto = datapath.ofproto # switch 的 openFlow parser = datapath.parser # switch -> 解析 # Establish Table-Miss FLOW (policy Rule) match = parser.OFPMatch() # mach ALLLLL packet actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,ofproto.OFPCML_NO_BUFFER)] self.add_flow(datapath,0,match,actions) # add_flow entry def add_flow(self,datapath,priority,match,actions): ofproto = datapath.ofproto parser = datapath.ofproto_parser #Action inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,actions)] //isntruction # Adding the flow into Switch Flow Table newFlow = parser.OFPFlowMod(datapath=datapath,property=property,match=match,isinstance = inst) #Send the Flow to Switch datapath.send_msg(newFlow) # Unknow packet into the controller (No match Rules) @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def packet_in_handler(self,ev): # get switch ID && parser it FOr Add FLOWSSSSSSS !!! msg=ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.parser # Parse Packet Infromation ip_port = msg.match['in_port'] # packet Source pkt = packet.Packet(msg.data) eth = pkt.get_protocol(ethernet.ethernet)[0] #get packet protocol dst = eth.dst #protocol, dst src = eth.src #protocol, src dpid = datapath.id self.mac_to_port.setdefault(dpid,{}) #Mac Table Structure ! self.logger.info("packet 跑過來拉 ㄚㄚㄚㄚ packet information: %s %s %s %s ", dpid , src , dst , ip_port) # learn a mac address self.mac_to_port[dpid][src] = in_port #設定 MAC table , Output Port 取得其他 Node # Packet Forwarding (根據MAC table ) if dst in self.mac_to_port[dpid]: #Look up Mac Table out_port = self.mac_to_port[dpid][dst] else: out_port = ofproto.OFPP_FLOOD # HUB -> 找不到就 brocast (security Badddddd!!! ) actions = [parser.OFPActionOutput(out_port)] #install a flow to avoid next packet_in next Time if out_port != ofproto.OFPP_FLOOD: match = parser.OFPMatch(input=in_port, eth_dst=dst) self.add_flow(datapath,1,match,actions) data = None if msg.buffer_id == ofproto.OFP_NO_BUFFER: data = msg.data #forward Packet out = parser.OFPPacketOut(datapath=datapath,buffer_id=msg,buffer.id,in_port=in_port,actions=actions,data=data) datapath.send_msg(out) ``` ## Executing Ryu Program ### Executing Mininet Controller 上 Run application ``` root@ubuntu:/home/user/Desktop# ryu-manager ddos2.py loading app ddos2.py loading app ryu.topology.switches loading app ryu.controller.ofp_handler instantiating app ryu.topology.switches of Switches instantiating app ryu.controller.ofp_handler of OFPHandler instantiating app ddos2.py of MyController pkt in!! pkt in!! pkt in!! pkt in!! pkt in!! ``` ![](https://hackmd.io/_uploads/SyOvWSO52.png) ---