[SDN course design] Ryu controls traffic forwarding, virtual address ping, intelligent regulation (smart city background)

SDN course design, using Ryu to control, gnawing on the source code is really tired

Github source code here

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

Ubuntu ovs installation

Installing Open vSwitch for Ubuntu

ryu RESTAPI

Interpretation of RYU core source code:

RYU getting started

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

Ryu+mininet+Wireshark

An example of mininet+ryu step-by-step control

Mininet and real network connection

Communication between host and extranet in Mininet

An example of packet capture

Official start

packet class of ryu source code analysis

ryupacket source code

Packet document

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

Ofppatch 607 line

Ofpactinoutput line 4682

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:

Keywords: Python Operation & Maintenance Load Balance network switch

Added by Browzer on Thu, 10 Feb 2022 16:59:59 +0200