SDN course design, using Ryu to control, gnawing on the source code is really tired
Project background
At present, smart cities are developing more and more, but with the application of various network devices and sensors, the network burden of cities is also increasing. If data analysis and other operations are done by the city's data center, the network traffic will be overwhelmed. Fog computing is a good idea. However, sometimes it needs to be regulated as a whole. We use the idea of SDN to realize it.
This is the topology of the traffic lights and the processor behind the traffic intersection. We all know that the duration of the traffic lights can be adjusted intelligently through the current road conditions to make the traffic more unobstructed. We assume that when the traffic flow is not very large, the camera 10.1.1.100 collects the traffic flow information, and the MCU(10.3.3.150) in the LAN can make decisions and adjust its duration; However, in the rush hour, it is necessary to upload the traffic flow information to the data processing center (10.2.2.130). The data center makes decisions according to the traffic conditions of the whole city, so as to avoid the conflict of decision-making results at each intersection.
However, it is obvious that a camera will not have such intelligent function. It knows when to send the stream to the MCU of the LAN or the remote data center. We are like setting a virtual IP(10.1.1.77) and the corresponding virtual MAC (66:66:66:66:66). After receiving the packet with the destination address of virtual IP, the router selects whether to send it to the near or far for processing according to the control of Ryu, and the MCU and data center can send information to the camera normally. In this way, through virtual IP, it is transparent to the camera. It just sends it and doesn't care about anything else.
Notes in the course design of liver
Method 1
It is equivalent to using Ubuntu as the switch of ovs. The advantage is that it can simulate the real environment and directly use linux system. Possible problems: this topology may have problems when it is cleared, such as clearing the arp table, etc. (it can't start from zero every time you run the topology like mininet simulation.)
Installing Open vSwitch for Ubuntu
Interpretation of RYU core source code:
RYU official document (which contains an introduction to simulated arp, and you can view guide1.dox)
RYU:OpenFlow protocol source code
Method 2
mininet is used to build the whole topology, and ryu is also directly used as the controller of the topology network
Advantages: it is convenient to debug and update, and use wireshark to capture packets, ping and tcpdump to create traffic (this can also be done in method 1)
Disadvantages: they are all carried out on one virtual machine, and interconnection may become a problem
An example of mininet+ryu step-by-step control
Mininet and real network connection
Communication between host and extranet in Mininet
Official start
packet class of ryu source code analysis
Combined with this example, learn to construct package distribution
Step by step packaging:
e = ethernet.ethernet(dst='ff:ff:ff:ff:ff:ff', src='08:60:6e:7f:74:e7', ethertype=ether.ETH_TYPE_ARP) a = arp.arp(hwtype=1, proto=0x0800, hlen=6, plen=4, opcode=2, src_mac='08:60:6e:7f:74:e7', src_ip='192.0.2.1', dst_mac='00:00:00:00:00:00', dst_ip='192.0.2.2') p = packet.Packet() p.add_protocol(e) p.add_protocol(a)
Add contract under action event
actions = [parser.OFPActionOutput(port=port)] out =parser.OFPPacketOut(datapath=datapath,buffer_id=ofproto.OFP_NO_BUFFER,in_port=ofproto.OFPP_CONTROLLER,actions=actions,data=data) datapath.send_msg(out)
Ryu RESTFUL protocol remotely obtains control surface information
Required for cross segment communication
OFPInstructionActions 4550 lines
pkt = packet.Packet(msg.data) eth = pkt.get_protocols(ethernet.ethernet)[0] arp_pkt = pkt.get_protocol(arp.arp) if eth: eth_dst = eth.dst eth_src = eth.src
if udp_pkt: print('***********Received virtual udp package*********') match = parser.OFPMatch(eth_dst='66:66:66:66:66:66') print("match:", match) #kwargs = dict(eth_dst='66:66:66:66:66:66') #match1 = parser.OFPMatch(** kwargs) #print("match1:", match1) actions = [parser.OFPActionSetField(ipv4_dst='10.1.1.110'),parser.OFPActionSetField(eth_dst=src_mac),parser.OFPActionOutput(in_port)] # datapath = self._find_dp(int(switch)) # assert datapath is not None inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] # idle and hardtimeout set to 0,making the entry permanent # reference openflow spec mod = datapath.ofproto_parser.OFPFlowMod( datapath=datapath, match=match, idle_timeout=0, hard_timeout=0, priority=32768, instructions=inst ) datapath.send_msg(mod)
In the official document of RYU, the external communication API based on RESTFUL protocol is given, Ryu RESTFUL protocol remotely obtains control surface information , encapsulated in Ryu app. ofctl_ The rest class mainly provides the following operations:
1. Obtain ovs switch information
2. Obtain flow information
3. Add stream and new group
Because it is based on RESTFUL API, we can easily use python's requests module, and its method is no different from the general http method.
In order to obtain the bridge information managed by RYU, first GET the url according to the official document:“ http://localhost:8080:stats/switchs ”, use the GET method to GET all the bridge dpid s and store them in the list.
def __init__(self): self.URL='http://localhost:8080' self.dpid=[] def getall_sw(self): url=self.URL+'/stats/switches' data = requests.get(url) if data.status_code == 200: data_all = data.json() print(data_all) for i in data_all: self.dpid.append(i)
After traversing the list, use url:“ http://localhost:8080:stats/flows ”, and the dpid in the list are combined to obtain the specific information of the dpid bridge. Through the GET method, the obtained data is transformed into json format, and then the action mode, the number of packets and bytes are extracted to obtain the desired output information.
def getall_sw_data(self): for i in self.dpid: url=self.URL+'/stats/flow/'+str(i) data = requests.get(url) if data.status_code == 200: data_all = data.json()[str(i)] action=data_all[0]['actions'] packet_count=data_all[3]['packet_count'] priority=data_all[6]['priority'] byte_count=data_all[8]['byte_count'] print("Switch{}of action Mode is{}\n"\ "The packets flowing through are{}\n"\ "The bytes passed are{}\n"\ "Priority is{}\n".format(i,action,packet_count,byte_count,priority))
Overall code analysis
According to the topology shown in the figure:
55 lines of code
self.host_mac_to={}
The purpose is to correspond the id of each device to its MAC address, so that we can know which device it is later.
Line 89:
self.arp_table.update({VIP:VMAC}) self.arp_table.update({VIP2:VMAC})
Prepare the IP and MAC of the virtual address in advance so that the arp reply can be sent.
When the camera contracts to the virtual address for the first time, because there is no mac address, it will send an arp request (line 578), and we will reply to the virtual arp.
Then, the camera gets the mac into a frame and sends out the contract through 439 lines:
# To virtual ip if pkt_eth.dst == VMAC: flag = 1 print('!!!!!Send to virtual ip!!!!!') tsecond = time.asctime(time.localtime(time.time())) second = tsecond.split(' ')[3].split(':')[2] print(second, '!!GET TIME!!') if pkt_ipv4: if second <= '20': # Send to local computing unit in the first 20s if pkt_ipv4.src == pc1_ip: print('Top 20 s,pc1 Send to pc2') dstmac = pc2_mac dstip = pc2_ip if pkt_ipv4.src == pc4_ip: print('Top 20 s,pc4 Send to pc5') dstmac = pc5_mac dstip = pc5_ip data = msg.data actions = [parser.OFPActionSetField(ipv4_dst=dstip), parser.OFPActionSetField(eth_dst=dstmac), parser.OFPActionOutput(port=2)] self.sendpaket(parser, ofproto, data, datapath, actions, buffer_id, in_port)
As mentioned earlier, split and assemble the packet, replace the destination IP, and then send it out from the corresponding port.
In order to simulate, we pretend that 20s~40s per minute is the rush hour. At this time, the message will be forwarded to the remote server.
if second > '20' and second <= '40': # Send to the remote server in the middle 20s if pkt_ipv4.src == pc1_ip: print('Middle 20 s,pc1 Send to pc3') smac = gate1_mac if pkt_ipv4.src == pc4_ip: print('Middle 20 s,pc4 Send to pc3') smac = gate3_mac actions = [parser.OFPActionOutput(port=gate_port)] data = msg.data self.sendpaket(parser, ofproto, data, datapath, actions, buffer_id, in_port) actions = [parser.OFPActionSetField(eth_src=smac), parser.OFPActionSetField(ipv4_dst=pc3_ip), parser.OFPActionSetField(eth_dst=gate2_mac), parser.OFPActionOutput(port=gate_port)] data = msg.data datapath = self.datapaths[0] self.sendpaket(parser, ofproto, data, datapath, actions, buffer_id, in_port) actions = [parser.OFPActionSetField(eth_src=gate2_mac), parser.OFPActionSetField(ipv4_dst=pc3_ip), parser.OFPActionSetField(eth_dst=pc3_mac), parser.OFPActionOutput(port=1)] data = msg.data datapath = self.datapaths[0] self.sendpaket(parser, ofproto, data, datapath, actions, buffer_id, in_port)
Additional features:
Line 204:
# The destination Mac of ipv6 broadcast message starts at 33:33“ if '33:33' in dst_mac[:5]: # the controller has not flooded this packet before if (src_mac,dst_mac) not in self.flood_history[dpid]: # we remember this packet self.flood_history[dpid].append((src_mac,dst_mac)) else: # the controller have flooded this packet before,we do nothing and return return
Prevent ipv6 broadcast storm.
In order to detect the effect of the whole network, a flash is also written as the back-end web server, which can check the network load and router online at any time:
import requests import json class ryu_restful: def __init__(self): self.URL='http://localhost:8080' self.dpid=[] def getall_sw(self): url=self.URL+'/stats/switches' data = requests.get(url) if data.status_code == 200: data_all = data.json() print(data_all) for i in data_all: self.dpid.append(i) def getall_sw_data(self): for i in self.dpid: url=self.URL+'/stats/flow/'+str(i) data = requests.get(url) if data.status_code == 200: data_all = data.json()[str(i)] action=data_all[0]['actions'] packet_count=data_all[3]['packet_count'] priority=data_all[6]['priority'] byte_count=data_all[8]['byte_count'] print("Switch{}of action Mode is{}\n"\ "The packets flowing through are{}\n"\ "The bytes passed are{}\n"\ "Priority is{}\n".format(i,action,packet_count,byte_count,priority)) def add_flow(self): url=sel.URL+'/stats/flowentry/add' data= '{"dpid": 1,"cookie": 1,"cookie_mask": 1,"table_id": 0,"idle_timeout": 30,'\ ' "hard_timeout": 30, "priority": 11111,"flags": 1,"match":{"in_port":1}, "actions":[{"type":"OUTPUT","port": 2}]}' res=requests.post(url=url,data=data) print(res.text) def get_flow(self): for i in self.dpid: url=self.URL+'/stats/flow'+str(i) data=requests.get(url) if data.status_code==200: data_all=data.json()[str(i)][0] match=data_all['match'] actions=data_all['actions'] print('flow{}The matching table for is{},Action is{}'.format(i,match,actions)) if __name__ == "__main__": ryu=ryu_restful() ryu.getall_sw() ryu.getall_sw_data() ryu.get_flow()
Final effect
Send UDP message to virtual address:
Remote monitoring of network operation: