mySDN Lab8
mySDN Lab8
mySDN Lab8
h2
http server
h5 h6
h3 h4
Servers are periodically probed to see if they're alive by sending them ARPs.
"""
"""
A very sloppy IP load balancer.
import pox.openflow.libopenflow_01 as of
import time
import random
FLOW_IDLE_TIMEOUT = 10
FLOW_MEMORY_TIMEOUT = 60 * 5
selected_server=0
class MemoryEntry (object):
"""
Record for flows we are balancing
Table entries in the switch "remember" flows for a period of time, but
rather than set their expirations to some long value (potentially leading
to lots of rules for dead connections), we let them expire from the
switch relatively quickly and remember them here in the controller for
longer.
Another tactic would be to increase the timeouts on the switch and use
the Nicira extension witch can match packets with FIN set to remove them
when the connection closes.
"""
def __init__ (self, server, first_packet, client_port):
self.server = server
self.first_packet = first_packet
self.client_port = client_port
self.refresh()
@property
def is_expired (self):
return time.time() > self.timeout
@property
def key1 (self):
ethp = self.first_packet
ipp = ethp.find('ipv4')
tcpp = ethp.find('tcp')
return ipp.srcip,ipp.dstip,tcpp.srcport,tcpp.dstport
@property
def key2 (self):
ethp = self.first_packet
ipp = ethp.find('ipv4')
tcpp = ethp.find('tcp')
return self.server,ipp.srcip,tcpp.dstport,tcpp.srcport
class iplb (object):
"""
A simple IP load balancer
try:
self.log = log.getChild(dpid_to_str(self.con.dpid))
except:
# Be nice to Python 2.6 (ugh)
self.log = log
# How long do we wait for an ARP reply before we consider a server dead?
self.arp_timeout = 3
# Expire probes
for ip,expire_at in self.outstanding_probes.items():
if t > expire_at:
self.outstanding_probes.pop(ip, None)
if ip in self.live_servers:
self.log.warn("Server %s down", ip)
del self.live_servers[ip]
server = self.servers.pop(0)
self.servers.append(server)
r = arp()
r.hwtype = r.HW_TYPE_ETHERNET
r.prototype = r.PROTO_TYPE_IP
r.opcode = r.REQUEST
r.hwdst = ETHER_BROADCAST
r.protodst = server
r.hwsrc = self.mac
r.protosrc = self.service_ip
e = ethernet(type=ethernet.ARP_TYPE, src=self.mac,
dst=ETHER_BROADCAST)
e.set_payload(r)
#self.log.debug("ARPing for %s", server)
msg = of.ofp_packet_out()
msg.data = e.pack()
msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD))
msg.in_port = of.OFPP_NONE
self.con.send(msg)
core.callDelayed(self._probe_wait_time, self._do_probe)
@property
def _probe_wait_time (self):
"""
Time to wait between probes
"""
r = self.probe_cycle_time / float(len(self.servers))
r = max(.25, r) # Cap it at four per second
return r
tcpp = packet.find('tcp')
if not tcpp:
arpp = packet.find('arp')
if arpp:
# Handle replies to our server-liveness probes
if arpp.opcode == arpp.REPLY:
if arpp.protosrc in self.outstanding_probes:
#print "server:", arpp.hwsrc, "is still up"
# A server is (still?) up; cool.
del self.outstanding_probes[arpp.protosrc]
if (self.live_servers.get(arpp.protosrc, (None,None))
== (arpp.hwsrc,inport)):
# Ah, nothing new here.
pass
else:
# Ooh, new server.
self.live_servers[arpp.protosrc] = arpp.hwsrc,inport
self.log.info("Server %s up", arpp.protosrc)
return
# Not TCP and not ARP. Don't know what to do with this. Drop it.
return drop()
# It's TCP.
ipp = packet.find('ipv4')
if ipp.srcip in self.servers:
# It's FROM one of our balanced servers.
# Rewrite it BACK to the client
key = ipp.srcip,ipp.dstip,tcpp.srcport,tcpp.dstport
entry = self.memory.get(key)
if entry is None:
# We either didn't install it, or we forgot about it.
self.log.debug("No client for %s", key)
return drop()
actions = []
actions.append(of.ofp_action_dl_addr.set_src(self.mac))
actions.append(of.ofp_action_nw_addr.set_src(self.service_ip))
actions.append(of.ofp_action_output(port = entry.client_port))
match = of.ofp_match.from_packet(packet, inport)
msg = of.ofp_flow_mod(command=of.OFPFC_ADD,
idle_timeout=FLOW_IDLE_TIMEOUT,
hard_timeout=of.OFP_FLOW_PERMANENT,
data=event.ofp,
actions=actions,
match=match)
self.con.send(msg)
elif ipp.dstip == self.service_ip:
# Ah, it's for our service IP and needs to be load balanced
#print "ipp.dstip == self.service_ip ", self.service_ip
# Update timestamp
entry.refresh()
msg = of.ofp_flow_mod(command=of.OFPFC_ADD,
idle_timeout=FLOW_IDLE_TIMEOUT,
hard_timeout=of.OFP_FLOW_PERMANENT,
data=event.ofp,
actions=actions,
match=match)
self.con.send(msg)
if _dpid != event.dpid:
log.warn("Ignoring switch %s", event.connection)
else:
log.info("Load Balancing on %s", event.connection)
# Gross hack
core.iplb.con = event.connection
event.connection.addListeners(core.iplb)
core.openflow.addListenerByName("ConnectionUp", _handle_ConnectionUp)
References
• Run a simple web server and client,
http://mininet.org/walkthrough/#run-a-
simple-web-server-and-client
• ip_loadbalancer.py,
https://gitshell.com/warcy/pox_SRTP/blob/m
aster/pox/misc/ip_loadbalancer.py
•