/
trans_agent.py
175 lines (157 loc) · 6.67 KB
/
trans_agent.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
"""
In this agent, the Tornado TCP server will use one process to manage all the classes.
Therefore, the total throughput of this script will be affected.
"""
import errno
import functools
import tornado.ioloop as ioloop
import socket
import libopenflow as of
import Queue
#create a connection between socket fd of (controller, sw) and sw class
fdmap = {}
num = 0
"""
Print connection information
"""
def print_connection(connection, address):
print "connection:", connection, address
"""
Create a new socket
The parameter ``block`` determine if the return socket is blocking
or nonblocking socket. Use '1' when creating a socket which connect
a controller.
"""
def new_sock(block):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setblocking(block)
return sock
"""
The switch class maintains the connection between controller and individual
switches. For each OpenFlow switch, this transparent agent create a object of this
class.
"""
class switch():
def __init__(self, sock_sw, sock_con):
self.sock_sw = sock_sw
self.sock_con = sock_con
self.fd_sw = sock_sw.fileno()
self.fd_con = sock_con.fileno()
self.queue_con = Queue.Queue()
self.queue_sw = Queue.Queue()
def controller_handler(self, address, fd, events):
if events & io_loop.READ:
data = self.sock_con.recv(1024)
if data == '':
print "controller disconnected"
io_loop.remove_handler(self.fd_con)
print "closing connection to switch"
self.sock_sw.close()
io_loop.remove_handler(self.fd_sw)
else:
rmsg = of.ofp_header(data)
# Here, we can manipulate OpenFlow packets from CONTROLLER.
rmsg.show()
io_loop.update_handler(self.fd_sw, io_loop.WRITE)
self.queue_sw.put(str(data))
if events & io_loop.WRITE:
#print "trying to send packet to controller"
try:
next_msg = self.queue_con.get_nowait()
except Queue.Empty:
#print "%s queue empty" % str(address)
io_loop.update_handler(self.fd_con, io_loop.READ)
else:
print 'sending "%s" to %s' % (of.ofp_type[of.ofp_header(next_msg).type], self.sock_con.getpeername())
self.sock_con.send(next_msg)
def switch_handler(self, address, fd, events):
if events & io_loop.READ:
data = self.sock_sw.recv(1024)
if data == '':
print "switch disconnected"
io_loop.remove_handler(self.fd_sw)
print "closing connection to controller"
self.sock_con.close()
io_loop.remove_handler(self.fd_con)
else:
rmsg = of.ofp_header(data)
rmsg.show()
# Here, we can manipulate OpenFlow packets from SWITCH.
io_loop.update_handler(self.fd_con, io_loop.WRITE)
self.queue_con.put(str(data))
if events & io_loop.WRITE:
try:
next_msg = self.queue_sw.get_nowait()
except Queue.Empty:
#print "%s queue empty" % str(address)
io_loop.update_handler(self.fd_sw, io_loop.READ)
else:
print 'sending "%s" to %s' % (of.ofp_type[of.ofp_header(next_msg).type], self.sock_sw.getpeername())
self.sock_sw.send(next_msg)
"""
For the callback function of socket listening, the agent function will first
try to accept the connection started by switch. And if the connection is successful,
this function will continue on creating another socket to connect the controller.
If the controller cannot be reached, there will be ``ECONNREFUSED`` error.
After all these things are done, we will have two sockets, one from OpenFlow switch, another
one to controller. Send these two sockets as parameter, a new switch object can be
created. Before exit the agent function, this function will add ``new_switch.switch_handler``
and ``new_switch.controller_handler`` to callback function of their own socket.
"""
def agent(sock, fd, events):
#TODO: create a new class for switches. when a switch connected to agent, new class
#also, the sw is connected to controller using another socket.
#print fd, sock, events
#1. accept connection from switch
try:
connection, address = sock.accept()
except socket.error, e:
if e.args[0] not in (errno.EWOULDBLOCK, errno.EAGAIN):
raise
return
connection.setblocking(0)
#2. connecting to controller
#no idea why, but when not blocking, it says: error: [Errno 36] Operation now in progress
sock_control = new_sock(1)
try:
sock_control.connect(("10.2.30.198",6633))#controller's IP, better change it into an argument
except socket.error, e:
if e.args[0] not in (errno.ECONNREFUSED, errno.EINPROGRESS):
raise
if e.args[0] == errno.ECONNREFUSED:
print "cannot establish connection to controller, please check your config."
return
sock_control.setblocking(0)
#3. create sw class object
global num
num = num + 1
new_switch = switch(connection, sock_control)
print "switch instance No.", num
fdmap[connection.fileno()] = new_switch
fdmap[sock_control.fileno()] = new_switch
#print_connection(connection, address)
#print_connection(sock_control, sock_control.getpeername())
controller_handler = functools.partial(new_switch.controller_handler, address)
io_loop.add_handler(sock_control.fileno(), controller_handler, io_loop.READ)
print "agent: connected to controller"
switch_handler = functools.partial(new_switch.switch_handler, address)
io_loop.add_handler(connection.fileno(), switch_handler, io_loop.READ)
print "agent: connected to switch", num
if __name__ == '__main__':
"""
For Tornado, there usually is only one thread, listening to the socket
below. And also, this code block uses ``ioloop.add_handler()`` function
to register a callback function if ``ioloop.READ`` event happens.
When a new request from of switch, it will trigger ``ioloop.READ`` event
in Tornado. And Tornado will execute the callback function ``agent()``.
"""
sock = new_sock(0)
sock.bind(("", 6633))
sock.listen(6633)
num = 0
io_loop = ioloop.IOLoop.instance()
callback = functools.partial(agent, sock)
print sock, sock.getsockname()
io_loop.add_handler(sock.fileno(), callback, io_loop.READ)
io_loop.start()