from pox.core import core
from pox.openflow import *
import pox.openflow.libopenflow_01 as of
from pox.lib.packet.arp import arp
from pox.lib.packet.ethernet import ethernet, ETHER_BROADCAST
from pox.lib.packet.ipv4 import ipv4
from pox.lib.addresses import EthAddr, IPAddr
log = core.getLogger()
import time
import random
class SimpleLoadBalancer(object):
def __init__(self, service_ip, server_ips = []):
core.openflow.addListeners(self)
self.service_ip = service_ip
self.server_ips = server_ips
self.server_ip2mac = {}
self.mac2port = {}
self.ip2mac = {}
self.client2server = {}
def _handle_ConnectionUp(self, event):
self.lb_mac = EthAddr("0A:00:00:00:00:01")
self.connection = event.connection
for server_ip in self.server_ips:
self.send_proxied_arp_request(self.connection, server_ip)
def update_lb_mapping(self, client_ip):
rnd = random.randint(0, len(self.server_ip2mac)-1)
server_ip = self.server_ip2mac.keys()[rnd]
return server_ip
def send_proxied_arp_reply(self, packet, connection, outport, requested_mac):
r = arp()
r.opcode = arp.REPLY
r.hwsrc = requested_mac
r.hwdst = packet.src
r.protosrc = packet.next.protodst
r.protodst = packet.next.protosrc
e = ethernet(type=ethernet.ARP_TYPE, src=self.lb_mac, dst=packet.src)
e.set_payload(r)
log.debug("Load balancer reply to %s's ARP \"%s is at %s\"" %
(r.protodst, r.protosrc, r.hwsrc))
msg = of.ofp_packet_out()
msg.data = e.pack()
msg.actions.append(of.ofp_action_output(port=outport))
connection.send(msg)
def send_proxied_arp_request(self, connection, ip):
r = arp()
r.opcode = arp.REQUEST
r.hwsrc = self.lb_mac
r.hwdst = ETHER_BROADCAST
r.protosrc = self.service_ip
r.protodst = ip
e = ethernet(type=ethernet.ARP_TYPE, src=self.lb_mac, dst=ETHER_BROADCAST)
e.set_payload(r)
msg = of.ofp_packet_out()
msg.data = e.pack()
msg.actions.append(of.ofp_action_output(port=of.OFPP_FLOOD))
log.info("Load balancer ARPing for %s's MAC" % ip)
connection.send(msg)
def install_flow_rule_client_to_server(self, connection, outport, client_ip,
server_ip, buffer_id=of.NO_BUFFER):
match = of.ofp_match()
match.dl_type = ethernet.IP_TYPE
match.nw_src = client_ip
match.nw_dst = self.service_ip
msg = of.ofp_flow_mod()
msg.match = match
msg.buffer_id = buffer_id
msg.idle_timeout = 10
server_mac = self.server_ip2mac[server_ip]
msg.actions.append(of.ofp_action_dl_addr.set_src(self.lb_mac))
msg.actions.append(of.ofp_action_dl_addr.set_dst(server_mac))
msg.actions.append(of.ofp_action_nw_addr.set_dst(server_ip))
msg.actions.append(of.ofp_action_output(port=outport))
log.info("Installing flow rule for client %s to server %s" % (client_ip, server_ip))
connection.send(msg)
def install_flow_rule_server_to_client(self, connection, outport, server_ip,
client_ip, buffer_id=of.NO_BUFFER):
match = of.ofp_match()
match.dl_type = ethernet.IP_TYPE
match.nw_src = server_ip
match.nw_dst = client_ip
msg = of.ofp_flow_mod()
msg.match = match
msg.buffer_id = buffer_id
msg.idle_timeout = 10
client_mac = self.ip2mac[client_ip]
msg.actions.append(of.ofp_action_nw_addr.set_src(self.service_ip))
msg.actions.append(of.ofp_action_dl_addr.set_src(self.lb_mac))
msg.actions.append(of.ofp_action_dl_addr.set_dst(client_mac))
msg.actions.append(of.ofp_action_output(port=outport))
log.info("Installing flow rule for server %s to client %s" % (server_ip, client_ip))
connection.send(msg)
def _handle_PacketIn(self, event):
packet = event.parsed
connection = event.connection
inport = event.port
self.mac2port[packet.src] = inport
if packet.type == packet.ARP_TYPE:
r = packet.next
if r.opcode == r.REQUEST:
if r.protodst == self.service_ip:
log.info("%s ARPing for %s's MAC" % (str(r.protosrc), str(self.service_ip)))
self.send_proxied_arp_reply(packet, connection, inport, self.lb_mac)
elif r.protosrc in self.server_ips and r.protodst not in self.server_ips:
log.info("Server %s ARPing for client %s's MAC" % (r.protosrc, r.protodst))
self.send_proxied_arp_request(connection, r.protodst)
self.send_proxied_arp_reply(packet, connection, inport, self.lb_mac)
elif r.opcode == r.REPLY:
if r.protosrc in self.server_ips and r.protodst == self.service_ip:
log.info("Server %s reply to load balancer's ARP \"%s is at %s\"" %
(r.protosrc, r.protosrc, r.hwsrc))
self.server_ip2mac[r.protosrc] = r.hwsrc
elif r.protosrc not in self.server_ips and r.protodst == self.service_ip:
log.info("Client %s reply to load balancer's ARP \"%s is at %s\"" %
(r.protosrc, r.protosrc, r.hwsrc))
self.ip2mac[r.protosrc] = packet.src
elif packet.type == ethernet.IP_TYPE:
ip = packet.next
if ip.dstip == self.service_ip:
if ip.srcip not in self.server_ips:
server_ip = self.update_lb_mapping(ip.srcip)
server_mac = self.server_ip2mac[server_ip]
outport = self.mac2port[server_mac]
buffer_id = event.ofp.buffer_id
log.debug("client to server port: %s" % outport)
self.install_flow_rule_client_to_server(connection, outport,
ip.srcip, server_ip, buffer_id)
elif ip.srcip in self.server_ips and ip.dstip not in self.server_ips:
client_mac = self.ip2mac.get(ip.dstip)
if not client_mac:
self.send_proxied_arp_request(connection, ip.dstip)
return
outport = self.mac2port[client_mac]
log.debug("client to server port: %s" % outport)
self.install_flow_rule_server_to_client(connection, outport,
ip.srcip, ip.dstip)
else:
log.info("Unknown Packet type: %s" % packet.type)
return
return
def launch(ip, servers):
log.info("Loading Simple Load Balancer module")
server_ips = servers.replace(","," ").split()
server_ips = [IPAddr(x) for x in server_ips]
service_ip = IPAddr(ip)
core.registerNew(SimpleLoadBalancer, service_ip, server_ips)