Search code examples
pythonload-balancingmininetopenflow

Switch load balancing dynamic host topology


Below is code for setting up a network in mininet, where each host will run a client program (i.e. through hx.cmd('python pyexample.py')), and each server will run a server program which clients will connect to.

Firstly, I have connected all hosts to the switch, some of which are servers and some are hosts. How can I have this switch act as a load balancer when the number of hosts/clients/servers are not fixed?

Second, how can I get the client and server to connect to each other and return the result to a single client, in the instance multiple clients send requests to a single server?

#!/usr/bin/python

from mininet.topo import Topo
from mininet.net import Mininet
from mininet.util import dumpNodeConnections
from mininet.log import setLogLevel

import math

# num_hosts = number of servers + clients
# network with n hosts connected to one switch
class NetworkTopology(Topo, num_hosts, num_servers):
    clients = []
    servers = []

    def build(self, n=num_hosts):
        # Switch controlled to act as load balancer
        # TODO: Write controller
        switch = self.addSwitch('s1')

        # Generate 0..n-1 hosts - n = num_hosts
        for h in range(n):
            s = 0
            while s < num_servers:
                # Add server to the topology
                server = self.addHost('server%s' % (s+1))
                self.addLink(server, switch)
                servers.append(server)
            else:
                # Add client to the topology
                client = self.addHost('client%s' % ((h-s)+1))
                self.addLink(host, switch)
                clients.append(client)


def runTest(num_hosts, num_servers, num_levels, num_base_reports):
    topo = NetworkTopology(num_hosts, num_servers)


# Main function
if __name__ == '__main__':
    setLogLevel('info')

    num_base_reports = 100
    num_levels = math.log(num_base_reports, 10) # log to base 10
    num_hosts = round(4**(math.log(num_base_reports-1, 10)))
    num_servers = round(2**(math.log(num_base_reports-1, 10)))

    runTest(num_hosts, num_servers num_levels, num_base_reports)

EDIT:

Below is the code I am using for my sockets between the client and server Java programs, which will be run on the respective client and server hosts in the topology.

Server:

public class Server
{
    // Server socket
    protected ServerSocket serverSocket;
    // Server port
    public static final int PORT = 3000;

    public Server() throws IOException {
        this.serverSocket = new ServerSocket(PORT);
    }

    public ServerSocket getSocket() {
        return serverSocket;
    }

    public static void main(String[] args) {
        try {
            Server server = new Server();

            while(true) {
                // Get client socket
                Socket socket = server.getSocket().accept();
                new ClientThread(socket, server).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ClientThread:

public class ClientThread extends Thread
{

    private Socket socket;
    private static PDP server;

    public ClientThread(Socket sock, PDP server) {
        this.socket = sock;
        this.server = server;
    }

    public void run() {
        InputStream is = null;
        BufferedReader br = null;
        DataOutputStream dos = null;

        try {
            is = socket.getInputStream();
            br = new BufferedReader(new InputStreamReader(is));
            dos = new DataOutputStream(socket.getOutputStream());

            String request;
            while(true) {
                if((request = br.readLine()) != null) {
                    // Read request

                    // -- snip --

                    String response = "Done!";
                    // Send back response
                    dos.writeBytes(response + "\n\r");
                    dos.flush();
                } else {
                    socket.close();
                    System.out.println("Client Disconnected\n\r");
                    return;
                }
            }
        } catch (IOException e) {
            return;
        }
    }
}

Client:

public class Client
{
    protected static final int PORT = 3000;

    public Client() {
    }

    public static void main(String[] args) {
        try {
            Socket sock = new Socket("127.0.0.1", PORT);

            // Output Stream (sending to server)
            OutputStream os = sock.getOutputStream();
            PrintWriter pw = new PrintWriter(os, true);

            // Input Stream (receiving from server)
            InputStream is = sock.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));

            String request, response;
            while(true) {
                request = "MyRequest!";
                pw.println(request);
                if((response = br.readLine()) != null) {
                    System.out.println(response);
                    break;
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Solution

  • As you know, In SDN something new happened; Yes! Data plane and control plane separation! So you have to use a controller to build the load-balancing function in your network. SDN controller(s) and switch(es) use OpenFlow or another south-bound API's in order to connect to each other. Network Functionalities such as firewalling, NATing, load-balancing and other's will be implemented on controller software.

    You'll have many choices for the controller like ONOS, OpenDaylight, Floodlight, RYU and so on. If it is easy for you to write code in python, maybe you'd better choose RYU controller because the other mentioned controllers are java-based.

    You can find a sample code that has implemented a simple round-robin load-balancing application on the top of RYU controller:

    https://github.com/omi-uulm/openflow-loadbalancer/blob/master/load_balancer.py

    You should add your code in line 131 and this block:

    You must add your code after line 131 and in the following elif block:

    elif ip.proto == 0x06: # Resolve TCP
    

    for example something like this:

    elif ip.proto == 0x06: # Resolve TCP
    
                    if pkt_tcp.src_port == 3000: # Ignore packets from server when creating rule
                        return
    
                    ip_src = ip.src
    
                    # if ip of client has been learned (flow installed), no need to process
                    # helps with burst of traffic that creates duplicate flows
                    if ip_src in self.ip_to_mac:
                        return
    
                    out_port = self.net.servers[self.rr_counter].port
                    mac_dest = self.net.servers[self.rr_counter].mac
    
    
                    ip.dst = self.net.lb_ip
    
                    # Forward Received TCP Packet to the selected Server
                    p = packet.Packet()
                    p.add_protocol(ethernet.ethernet(dst=mac_dest, src=src, ethertype=ether_types.ETH_TYPE_IP))
                    p.add_protocol(ip)
                    p.add_protocol(pkt_tcp)
    
                    self.send_packet(p, msg, datapath, out_port)
    
                    # Add Flow Entry
                    match = datapath.ofproto_parser.OFPMatch(in_port=msg.in_port, dl_type=0x0800,
                                                             nw_proto=0x06, nw_src=ip_src, nw_dst=self.net.lb_ip)
                    actions = [parser.OFPActionSetDlDst(mac_dest), parser.OFPActionOutput(out_port)]
                    self.add_flow(match, actions)
    
                    # Insert reverse flow
                    match = datapath.ofproto_parser.OFPMatch(in_port=out_port, dl_type=0x0800,
                                                             nw_proto=0x06, nw_src=self.net.lb_ip, nw_dst=ip_src)
                    actions = [parser.OFPActionSetDlSrc(self.net.lb_mac), parser.OFPActionOutput(msg.in_port)]
                    self.add_flow(match, actions)
    
                    # Update ARP table
                    self.ip_to_mac.update({ip_src: src})
    
                    # Update RR counter
                    self.rr_counter = (self.rr_counter + 1) % self.net.servers_num
    

    Don't forget to add your lb and servers Mac and IP in network_setup.py class.


    Additional resources that may help you in this way:

    OpenFlow Switch Specification. Version 1.3.3

    RYU Controller Guide