/
IpLoadBalancer.py
244 lines (204 loc) · 8.95 KB
/
IpLoadBalancer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
####################################################
#
# File Name : IpLoadBalancer
#
# author : Ramprasad Tamilselvan
#
###################################################
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.ipv4 import ipv4
from pox.lib.packet.ethernet import ethernet, ETHER_BROADCAST
from pox.lib.addresses import EthAddr, IPAddr
log = core.getLogger()
import time
import random
# timeout constants for of messages
IDLE_TIMEOUT = 50
HARD_TIMEOUT = 0
class IpLoadBalancer(object):
# initialize
def __init__(self, service_ip, server_ips = []):
core.openflow.addListeners(self)
self.serviceIp = service_ip
self.serverIp = server_ips
self.mac_table = {}
self.serverDetails = {}
self.mac_to_port = {}
# new switch connection
def _handle_ConnectionUp(self, event):
# fake mac id for service ip
self.lb_mac = EthAddr("0A:00:00:00:00:01")
self.connection = event.connection
# pre emptively collecting the mac and port number of server IPs
# by sending ARP request packets
for ip in self.serverIp:
# constructing ARP packet
arpPacket = arp()
arpPacket.opcode = arpPacket.REQUEST
arpPacket.hwsrc = self.lb_mac
arpPacket.hwdst = ETHER_BROADCAST
arpPacket.protosrc = self.serviceIp
arpPacket.protodst = ip
# constructing ethernet packet with ARP as payload
e = ethernet(type = ethernet.ARP_TYPE, src = self.connection.eth_addr, dst = ETHER_BROADCAST)
e.set_payload(arpPacket)
# constructing openflow out message
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
# sending message
self.connection.send(msg)
# sending ARP replies for the request with service IP
def sendProxyArpReply(self, event, packet):
log.info("Sending proxy ARP reply to IP = %s" %packet.protosrc)
# constructing ARP packet
arpPacket = arp()
arpPacket.opcode = arpPacket.REPLY
arpPacket.hwsrc = self.lb_mac
arpPacket.hwdst = packet.hwsrc
arpPacket.protosrc = self.serviceIp
arpPacket.protodst = packet.protosrc
# constructing ethernet packet
e = ethernet(type = ethernet.ARP_TYPE, src = self.connection.eth_addr, dst = ETHER_BROADCAST)
e.set_payload(arpPacket)
# constructing openflow out message
msg = of.ofp_packet_out()
msg.data = e.pack()
msg.actions.append(of.ofp_action_output(port = of.OFPP_IN_PORT))
msg.in_port = event.port
# sending message
self.connection.send(msg)
# updates MAC and port for the corresponding server IP
def updateServerDetails(self, inPort, serverMac, serverIp):
log.info("Updating MAC and Port for IP = %s" %serverIp)
tempList = [serverMac, inPort]
self.serverDetails[serverIp] = tempList
# Installs the flow in switch for communication from server to client
def installFlowFromServer(self, event, packet, serverIp):
log.info("Installing flow from server: server IP = %s client IP = %s" %(serverIp, packet.next.srcip))
# constructing open flow message
msg = of.ofp_flow_mod()
msg.idle_timeout = IDLE_TIMEOUT
msg.hard_timeout = HARD_TIMEOUT
msg.buffer_id = None
# matching criterias are defined
msg.match.in_port = self.serverDetails[serverIp][1]
msg.match.dl_src = self.serverDetails[serverIp][0]
msg.match.dl_dst = packet.src
msg.match.dl_type = ethernet.IP_TYPE
msg.match.nw_src = serverIp
msg.match.nw_dst = packet.next.srcip
# actions to be done are defined
msg.actions.append(of.ofp_action_nw_addr.set_src(self.serviceIp))
msg.actions.append(of.ofp_action_dl_addr.set_src(self.lb_mac))
msg.actions.append(of.ofp_action_output(port = event.port))
# sending message
self.connection.send(msg)
def installFlowFromClient(self, event, packet, serverIp):
log.info("Installing flow from client: server IP = %s client IP = %s" %(serverIp, packet.next.srcip))
# constructing open flow message
msg = of.ofp_flow_mod()
msg.idle_timeout = IDLE_TIMEOUT
msg.hard_timeout = HARD_TIMEOUT
msg.buffer_id = None
msg.data = event.ofp
# matching criterias are defined
msg.match.in_port = event.port
msg.match.dl_src = packet.src
msg.match.dl_dst = self.lb_mac
msg.match.dl_type = ethernet.IP_TYPE
msg.match.nw_src = packet.next.srcip
msg.match.nw_dst = self.serviceIp
# actions to be done are defined
msg.actions.append(of.ofp_action_nw_addr.set_dst(serverIp))
msg.actions.append(of.ofp_action_dl_addr.set_dst(self.serverDetails[serverIp][0]))
msg.actions.append(of.ofp_action_output(port = self.serverDetails[serverIp][1]))
# sending message
self.connection.send(msg)
# Handles the IP packet
def handleRequest(self, event, packet):
num = random.randint(1,100)
num = num % len(self.serverIp)
serverIp = self.serverIp[num]
log.info("Randomly chosen IP = %s " %serverIp)
self.installFlowFromServer(event, packet, serverIp)
self.installFlowFromClient(event, packet, serverIp)
# sends the packet to the specified port
def resend_packet (self, packet_in, out_port):
msg = of.ofp_packet_out()
msg.data = packet_in
action = of.ofp_action_output(port = out_port)
msg.actions.append(action)
self.connection.send(msg)
# Normal switch modules ( l2 switch )
def act_like_switch (self, packet, packet_in):
# mac port table is maintained
if packet.src not in self.mac_to_port:
self.mac_to_port[packet.src] = packet_in.in_port
# installs the flow in switch id dest mac is in table
# else floods the packet
if packet.dst in self.mac_to_port:
# constructing the open flow message
log.info("Installing flow for dest MAC = %s" %packet.dst)
msg = of.ofp_flow_mod()
msg.match = of.ofp_match.from_packet(packet)
action = of.ofp_action_output(port = self.mac_to_port[packet.dst])
msg.idle_timeout = IDLE_TIMEOUT
msg.hard_timeout = HARD_TIMEOUT
msg.data = packet_in
msg.buffer_id = packet_in.buffer_id
msg.actions.append(action)
self.connection.send(msg)
else:
self.resend_packet(packet_in, of.OFPP_ALL)
# handles the incoming packet
def _handle_PacketIn(self, event):
packet = event.parsed
connection = event.connection
inport = event.port
# if packet type is ARP
if packet.type == packet.ARP_TYPE:
arpPacket = packet.next
# if it is ARP reply
if arpPacket.opcode == arp.REPLY:
log.info("Received arp reply from ip = %s" %arpPacket.protosrc)
# if it has fake MAC
if arpPacket.hwdst == self.lb_mac:
log.info("Upating server details for %s" % arpPacket.protosrc)
self.updateServerDetails(event.port, arpPacket.hwsrc, IPAddr(arpPacket.protosrc))
else:
# else act like a normal switch
self.act_like_switch(packet, event.ofp)
elif arpPacket.opcode == arp.REQUEST:
log.info("Received arp request from ip = %s" % arpPacket.protosrc)
# if ARP request is for service IP
if arpPacket.protodst == self.serviceIp:
log.info("Sending proxy ARP reply")
self.sendProxyArpReply(event, arpPacket)
else:
# else act like a normal switch
self.act_like_switch(packet, event.ofp)
elif packet.type == packet.IP_TYPE:
ipPacket = packet.next
# if destination of ip is service ip
if ipPacket.dstip == self.serviceIp:
log.info("Receiving request to service IP")
self.handleRequest(event, packet)
else:
self.act_like_switch(packet, event.ofp)
else:
# unknow packet type
log.info("Unknown Packet type: %s" % packet.type)
return
return
# launch
def launch(ip, servers):
log.info("Loading Load Balancer module")
server_ips = servers.replace(","," ").split()
server_ips = [IPAddr(x) for x in server_ips]
service_ip = IPAddr(ip)
core.registerNew(IpLoadBalancer, service_ip, server_ips)