├── LoadBalancer.py └── README.md /LoadBalancer.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import MAIN_DISPATCHER 4 | from ryu.controller.handler import set_ev_cls 5 | from ryu.ofproto import ofproto_v1_3 6 | from ryu.lib.packet import packet 7 | from ryu.lib.packet.packet import Packet 8 | from ryu.lib.packet import arp 9 | from ryu.lib.packet import ethernet 10 | from ryu.lib.packet import ether_types 11 | 12 | 13 | class LoadBalancer(app_manager.RyuApp): 14 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] # Specify the use of OpenFlow v13 15 | virtual_ip = "10.0.0.10" # The virtual server IP 16 | ## Hosts 5 and 6 are servers. 17 | H5_mac = "00:00:00:00:00:05" # Host 5's mac 18 | H5_ip = "10.0.0.5" # Host 5's IP 19 | H6_mac = "00:00:00:00:00:06" # Host 6's mac 20 | H6_ip = "10.0.0.6" # Host 6's IP 21 | next_server = "" # Stores the IP of the next server to use in round robin manner 22 | current_server = "" # Stores the current server's IP 23 | ip_to_port = {H5_ip: 5, H6_ip: 6} 24 | ip_to_mac = {"10.0.0.1": "00:00:00:00:00:01", 25 | "10.0.0.2": "00:00:00:00:00:02", 26 | "10.0.0.3": "00:00:00:00:00:03", 27 | "10.0.0.4": "00:00:00:00:00:04"} 28 | 29 | def __init__(self, *args, **kwargs): 30 | super(LoadBalancer, self).__init__(*args, **kwargs) 31 | self.next_server = self.H5_ip 32 | self.current_server = self.H5_ip 33 | 34 | # This function is called when a packet arrives from the switch 35 | # after the initial handshake has been completed. 36 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 37 | def packet_in_handler(self, ev): 38 | msg = ev.msg 39 | dp = msg.datapath 40 | ofp = dp.ofproto 41 | ofp_parser = dp.ofproto_parser 42 | in_port = msg.match['in_port'] 43 | 44 | pkt = packet.Packet(msg.data) 45 | etherFrame = pkt.get_protocol(ethernet.ethernet) 46 | 47 | # If the packet is an ARP packet, create new flow table 48 | # entries and send an ARP response. 49 | if etherFrame.ethertype == ether_types.ETH_TYPE_ARP: 50 | self.add_flow(dp, pkt, ofp_parser, ofp, in_port) 51 | self.arp_response(dp, pkt, etherFrame, ofp_parser, ofp, in_port) 52 | self.current_server = self.next_server 53 | return 54 | else: 55 | return 56 | 57 | # Sends an ARP response to the contacting host with the 58 | # real MAC address of a server. 59 | def arp_response(self, datapath, packet, etherFrame, ofp_parser, ofp, in_port): 60 | arpPacket = packet.get_protocol(arp.arp) 61 | dstIp = arpPacket.src_ip 62 | srcIp = arpPacket.dst_ip 63 | dstMac = etherFrame.src 64 | 65 | # If the ARP request isn't from one of the two servers, 66 | # choose the target/source MAC address from one of the servers; 67 | # else the target MAC address is set to the one corresponding 68 | # to the target host's IP. 69 | if dstIp != self.H5_ip and dstIp != self.H6_ip: 70 | if self.next_server == self.H5_ip: 71 | srcMac = self.H5_mac 72 | self.next_server = self.H6_ip 73 | else: 74 | srcMac = self.H6_mac 75 | self.next_server = self.H5_ip 76 | else: 77 | srcMac = self.ip_to_mac[srcIp] 78 | 79 | e = ethernet.ethernet(dstMac, srcMac, ether_types.ETH_TYPE_ARP) 80 | a = arp.arp(1, 0x0800, 6, 4, 2, srcMac, srcIp, dstMac, dstIp) 81 | p = Packet() 82 | p.add_protocol(e) 83 | p.add_protocol(a) 84 | p.serialize() 85 | 86 | # ARP action list 87 | actions = [ofp_parser.OFPActionOutput(ofp.OFPP_IN_PORT)] 88 | # ARP output message 89 | out = ofp_parser.OFPPacketOut( 90 | datapath=datapath, 91 | buffer_id=ofp.OFP_NO_BUFFER, 92 | in_port=in_port, 93 | actions=actions, 94 | data=p.data 95 | ) 96 | datapath.send_msg(out) # Send out ARP reply 97 | 98 | # Sets up the flow table in the switch to map IP addresses correctly. 99 | def add_flow(self, datapath, packet, ofp_parser, ofp, in_port): 100 | srcIp = packet.get_protocol(arp.arp).src_ip 101 | 102 | # Don't push forwarding rules if an ARP request is received from a server. 103 | if srcIp == self.H5_ip or srcIp == self.H6_ip: 104 | return 105 | 106 | # Generate flow from host to server. 107 | match = ofp_parser.OFPMatch(in_port=in_port, 108 | ipv4_dst=self.virtual_ip, 109 | eth_type=0x0800) 110 | actions = [ofp_parser.OFPActionSetField(ipv4_dst=self.current_server), 111 | ofp_parser.OFPActionOutput(self.ip_to_port[self.current_server])] 112 | inst = [ofp_parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)] 113 | 114 | mod = ofp_parser.OFPFlowMod( 115 | datapath=datapath, 116 | priority=0, 117 | buffer_id=ofp.OFP_NO_BUFFER, 118 | match=match, 119 | instructions=inst) 120 | 121 | datapath.send_msg(mod) 122 | 123 | # Generate reverse flow from server to host. 124 | match = ofp_parser.OFPMatch(in_port=self.ip_to_port[self.current_server], 125 | ipv4_src=self.current_server, 126 | ipv4_dst=srcIp, 127 | eth_type=0x0800) 128 | actions = [ofp_parser.OFPActionSetField(ipv4_src=self.virtual_ip), 129 | ofp_parser.OFPActionOutput(in_port)] 130 | inst = [ofp_parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)] 131 | 132 | mod = ofp_parser.OFPFlowMod( 133 | datapath=datapath, 134 | priority=0, 135 | buffer_id=ofp.OFP_NO_BUFFER, 136 | match=match, 137 | instructions=inst) 138 | 139 | datapath.send_msg(mod) 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ryu-SDN-Load-Balancer 2 | A simple load balancing controller that utilizes the Ryu SDN framework to program a network switch. 3 | 4 | # Author: Marko Ljubicic 5 | 6 | Implemented in Python 2.7. 7 | 8 | This application was written using the Ryu SDN framework to shape OpenFlow network traffic. It was used to 9 | control a simple network topology with a single switch and five hosts, two of which represented servers. 10 | The controller programmed the switch to alternate traffic between the servers in a round-robin selection scheme. 11 | The network infrastucture was constructed using the Mininet network emulator. 12 | --------------------------------------------------------------------------------