forked from hip2b2/poxstuff
-
Notifications
You must be signed in to change notification settings - Fork 0
/
of_sw_tutorial_oo.py
330 lines (278 loc) · 12.5 KB
/
of_sw_tutorial_oo.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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#!/usr/bin/python
# Copyright 2012 James McCauley, William Yu
#
# This file is part of POX.
#
# POX is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# POX is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with POX. If not, see <http://www.gnu.org/licenses/>.
"""
This is a demonstration file that has various switch implementations.
The first example is a basic "all match" switch followed by a
destination match, pair match then finally a more ideal pair match
switch.
Mininet Command: sudo mn --topo single,3 --mac
--switch ovsk
--controller remote
Command Line: ./pox.py py --completion
log.level --DEBUG
samples.of_sw_tutorial_oo
THIS VERSION SUPPORT resend() functionality in the betta branch POX.
Object-oriented version that allows user to switch switches via the
command line interface.
"""
# These next two imports are common POX convention
from pox.core import core
import pox.openflow.libopenflow_01 as of
from pox.lib.util import dpidToStr
# Even a simple usage of the logger is much nicer than print!
log = core.getLogger()
# Create the class to hold the switch tutorial implementations
class SwitchTutorial (object):
# This table maps (switch,MAC-addr) pairs to the port on 'switch' at
# which we last saw a packet *from* 'MAC-addr'.
# (In this case, we use a Connection object for the switch.)
table = {}
# Holds the object with the default switch
handlerName = 'SW_IDEALPAIRSWITCH'
# Holds the current active PacketIn listener object
listeners = None
# Constructor and sets default handler to Ideal Pair Switch
def __init__(self, handlerName = 'SW_IDEALPAIRSWITCH'):
log.debug("Initializing switch %s." % handlerName)
# Method for just sending a packet to any port (broadcast by default)
def send_packet(self, event, dst_port = of.OFPP_ALL):
msg = of.ofp_packet_out(in_port=event.ofp.in_port)
if event.ofp.buffer_id != -1 and event.ofp.buffer_id is not None:
msg.buffer_id = event.ofp.buffer_id
else:
if event.ofp.data:
return
msg.data = event.ofp.data
msg.actions.append(of.ofp_action_output(port = dst_port))
event.connection.send(msg)
# Optimal method for resending a packet
def resend_packet(self, event, dst_port = of.OFPP_ALL):
msg = of.ofp_packet_out(data = event.ofp)
msg.actions.append(of.ofp_action_output(port = dst_port))
event.connection.send(msg)
# DUMB HUB Implementation
# This is an implementation of a broadcast hub but all packets go
# to the controller since no flows are installed.
def _handle_dumbhub_packetin(self, event):
# Just send an instruction to the switch to send packet to all ports
packet = event.parsed
self.resend_packet(event, of.OFPP_ALL)
log.debug("Broadcasting %s.%i -> %s.%i" %
(packet.src, event.ofp.in_port, packet.dst, of.OFPP_ALL))
# PAIR-WISE MATCHING HUB Implementation
# This is an implementation of a broadcast hub with flows installed.
def _handle_pairhub_packetin(self, event):
packet = event.parsed
# Create flow that simply broadcasts any packet received
msg = of.ofp_flow_mod()
msg.data = event.ofp
msg.idle_timeout = 10
msg.hard_timeout = 30
msg.match.dl_src = packet.src
msg.match.dl_dst = packet.dst
msg.actions.append(of.ofp_action_output(port = of.OFPP_ALL))
event.connection.send(msg)
log.debug("Installing %s.%i -> %s.%i" %
(packet.src, event.ofp.in_port, packet.dst, of.OFPP_ALL))
# LAZY HUB Implementation (How hubs typically are)
# This is an implementation of a broadcast hub with flows installed.
def _handle_lazyhub_packetin(self, event):
packet = event.parsed
# Create flow that simply broadcasts any packet received
msg = of.ofp_flow_mod()
msg.data = event.ofp
msg.idle_timeout = 10
msg.hard_timeout = 30
msg.actions.append(of.ofp_action_output(port = of.OFPP_ALL))
event.connection.send(msg)
log.debug("Installing %s.%i -> %s.%i" %
("ff:ff:ff:ff:ff:ff", event.ofp.in_port, "ff:ff:ff:ff:ff:ff",
of.OFPP_ALL))
# BAD SWITCH Implementation
# This is an obvious but problematic implementation of switch that
# routes based on destination MAC addresses.
def _handle_badswitch_packetin(self, event):
packet = event.parsed
# Learn the source and fill up routing table
self.table[(event.connection,packet.src)] = event.port
# install appropriate flow rule when learned
msg = of.ofp_flow_mod()
msg.idle_timeout = 10
msg.hard_timeout = 30
msg.match.dl_dst = packet.src
msg.actions.append(of.ofp_action_output(port = event.port))
event.connection.send(msg)
log.debug("Installing %s.%i -> %s.%i" %
("ff:ff:ff:ff:ff:ff", event.ofp.in_port, packet.src, event.port))
# determine if appropriate destination route is available
dst_port = self.table.get((event.connection,packet.dst))
if dst_port is None:
# We don't know where the destination is yet. So, we'll just
# send the packet out all ports (except the one it came in on!)
# and hope the destination is out there somewhere. :)
# To send out all ports, we can use either of the special ports
# OFPP_FLOOD or OFPP_ALL. We'd like to just use OFPP_FLOOD,
# but it's not clear if all switches support this. :(
self.resend_packet(event, of.OFPP_ALL)
log.debug("Broadcasting %s.%i -> %s.%i" %
(packet.src, event.ofp.in_port, packet.dst, of.OFPP_ALL))
else:
# This is the packet that just came in -- we want send the packet
# if we know the destination.
self.resend_packet(event, dst_port)
log.debug("Sending %s.%i -> %s.%i" %
(packet.src, event.ofp.in_port, packet.dst, dst_port))
# PAIR-WISE MATCH SWITCH Implementation
# This is an implementation of an pair match switch. This only matches
# source and destination MAC addresses. Whenever a new source
# destination MAC address is detected it then add a new flow
# identifying the source destination pair. The routing table is updated
# using the detected destination MAC address to the destination port.
def _handle_pairswitch_packetin (self, event):
packet = event.parsed
# Learn the source and fill up routing table
self.table[(event.connection,packet.src)] = event.port
dst_port = self.table.get((event.connection,packet.dst))
if dst_port is None:
# We don't know where the destination is yet. So, we'll just
# send the packet out all ports (except the one it came in on!)
# and hope the destination is out there somewhere. :)
# To send out all ports, we can use either of the special ports
# OFPP_FLOOD or OFPP_ALL. We'd like to just use OFPP_FLOOD,
# but it's not clear if all switches support this. :(
self.resend_packet(event, of.OFPP_ALL)
log.debug("Broadcasting %s.%i -> %s.%i" %
(packet.src, event.ofp.in_port, packet.dst, of.OFPP_ALL))
else:
# This is the packet that just came in -- we want to
# install the rule and also resend the packet.
msg = of.ofp_flow_mod()
msg.data = event.ofp
msg.idle_timeout = 10
msg.hard_timeout = 30
msg.match.dl_src = packet.src
msg.match.dl_dst = packet.dst
msg.actions.append(of.ofp_action_output(port = dst_port))
event.connection.send(msg)
log.debug("Installing %s.%i -> %s.%i" %
(packet.src, event.ofp.in_port, packet.dst, dst_port))
# SMARTER PAIR-WISE MATCH SWITCH Implementation
# This is an implementation of an ideal pair switch. This optimizes the
# previous example by adding both direction in one entry.
def _handle_idealpairswitch_packetin(self, event):
packet = event.parsed
# Learn the source and fill up routing table
self.table[(event.connection,packet.src)] = event.port
dst_port = self.table.get((event.connection,packet.dst))
if dst_port is None:
# We don't know where the destination is yet. So, we'll just
# send the packet out all ports (except the one it came in on!)
# and hope the destination is out there somewhere. :)
# To send out all ports, we can use either of the special ports
# OFPP_FLOOD or OFPP_ALL. We'd like to just use OFPP_FLOOD,
# but it's not clear if all switches support this. :(
self.resend_packet(event, of.OFPP_ALL)
log.debug("Broadcasting %s.%i -> %s.%i" %
(packet.src, event.ofp.in_port, packet.dst, of.OFPP_ALL))
else:
# Since we know the switch ports for both the source and dest
# MACs, we can install rules for both directions.
msg = of.ofp_flow_mod()
msg.idle_timeout = 10
msg.hard_timeout = 30
msg.match.dl_dst = packet.src
msg.match.dl_src = packet.dst
msg.actions.append(of.ofp_action_output(port = event.port))
event.connection.send(msg)
# This is the packet that just came in -- we want to
# install the rule and also resend the packet.
msg = of.ofp_flow_mod()
msg.data = event.ofp
msg.idle_timeout = 10
msg.hard_timeout = 30
msg.match.dl_src = packet.src
msg.match.dl_dst = packet.dst
msg.actions.append(of.ofp_action_output(port = dst_port))
event.connection.send(msg)
log.debug("Installing %s.%i -> %s.%i AND %s.%i -> %s.%i" %
(packet.dst, dst_port, packet.src, event.ofp.in_port,
packet.src, event.ofp.in_port, packet.dst, dst_port))
# Define the proper handler
def _set_handler_name (self, handlerName = 'SW_IDEALPAIRSWITCH'):
self.handlerName = handlerName
# Function to grab the appropriate handler
def _get_handler (self, event):
return self.swMap[self.handlerName](self, event)
""" Here are functions that are meant to be called directly """
# Here is a function to list all possible switches
def list_available_listeners(self):
for key in self.swMap.iterkeys():
log.info("%s" % key)
# Here is a function to displaying possible methods
def help(self):
log.info("Methods available: %s %s %s %s %s" %
('list_available_listeners()',
'attach_packetin_listener(handlerName = \'SW_IDEALPAIRSWITCH\'',
'detach_packetin_listener()',
'clear_all_flows()',
'clear_flows(connection)'))
# Here is a function to attach the listener give the default handerName
def attach_packetin_listener (self, handlerName = 'SW_IDEALPAIRSWITCH'):
self._set_handler_name(handlerName)
self.listeners = core.openflow.addListenerByName("PacketIn",
self._get_handler)
log.debug("Attach switch %s." % handlerName)
# Here is a function to remove the listener
def detach_packetin_listener (self):
core.openflow.removeListener(self.listeners)
log.debug("Detaching switch %s." % self.handlerName)
# Function to clear all flows from a specified switch given
# a connection object
def clear_flows (self, connection):
msg = of.ofp_flow_mod(match=of.ofp_match(),command=of.OFPFC_DELETE)
connection.send(msg)
log.debug("Clearing all flows from %s." %
dpidToStr(connection.dpid))
# Function to clear all flows from all switches
def clear_all_flows (self):
msg = of.ofp_flow_mod(match=of.ofp_match(),command=of.OFPFC_DELETE)
for connection in core.openflow._connections.values():
connection.send(msg)
log.debug("Clearing all flows from %s." %
dpidToStr(connection.dpid))
# Define various switch handlers
swMap = {
'SW_DUMBHUB' : _handle_dumbhub_packetin,
'SW_PAIRHUB' : _handle_pairhub_packetin,
'SW_LAZYHUB' : _handle_lazyhub_packetin,
'SW_BADSWITCH' : _handle_badswitch_packetin,
'SW_PAIRSWITCH' : _handle_pairswitch_packetin,
'SW_IDEALPAIRSWITCH' : _handle_idealpairswitch_packetin,
}
# function that is invoked upon load to ensure that listeners are
# registered appropriately. Uncomment the hub/switch you would like
# to test. Only one at a time please.
def launch ():
# create new tutorial class object using the IDEAL PAIR SWITCH as default
MySwitch = SwitchTutorial('SW_IDEALPAIRSWITCH')
# add this class into core.Interactive.variables to ensure we can access
# it in the CLI.
core.Interactive.variables['MySwitch'] = MySwitch
# attach the corresponding default listener
MySwitch.attach_packetin_listener()