-
Notifications
You must be signed in to change notification settings - Fork 1
/
firewall.py
196 lines (156 loc) · 7.7 KB
/
firewall.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
from pox.core import core
import pox.lib.packet as pkt
from pox.lib.addresses import IPAddr
import pox.openflow.libopenflow_01 as of
log = core.getLogger()
IDLE_TIMOUT = 5
HARD_TIMEOUT = 15
def launch (blacklist=None, whitelist=None):
"""
Launch is the entry point of the module, much like __main__
"""
global BLACKLIST, WHITELIST
if blacklist:
BLACKLIST = blacklist.split(",")
else:
BLACKLIST = []
if whitelist:
WHITELIST = whitelist.split(",")
else:
WHITELIST = []
# Register the switch_component to the system
core.registerNew(SwitchComponent)
class SwitchComponent(object):
'''
The switch component is the handler of opendlow events for our
application
'''
def __init__(self):
log.info("Starting SwitchComponent")
# Make the switch component a listener to openflow events
core.openflow.addListeners(self)
def _handle_ConnectionUp(self, event):
log.info("Creating switch device on %s" % (event.connection,))
# Create a new Hub on the device having this connection
Switch(event.connection)
class Switch(object):
'''
The switch class is instantiated once for each openflow device
that connects to the switch component. The switch class tranforms
the said device to an ethernet learning switch
'''
# The connection to device object
connection = None
def __init__(self, connection):
'''
Create the Switch instance
'''
# Store the connection object in the instance
self.connection = connection
# The MAC table linking MAC addresses with ports
self.mac_table = {}
# Register to the connection to listen events from the device
# and pass them to the instance (self)
connection.addListeners(self)
def _handle_PacketIn (self, event):
'''
Callback function that receives packet_in events and responds
to them. Packet in events are sent from the device and carry a
data packet that the device does not know what to do with
'''
# Extract the data packet from the packet in event
data = event.parsed
firewall_rule_updated = self.handle_firewall(event, BLACKLIST)
if firewall_rule_updated:
return
# Update the MAC table. The MAC address that is the source of
# the frame is served from the port where this packet originated
# We should store (or update) this relation
self.mac_table[data.src] = event.port
# We extract the destination MAC address from the data
dst = data.dst
if dst.is_multicast:
# If the destination MAC is a multicast address, send the
# frame out of all ports, like a hub
log.info("Received multicast from %s" % (self.connection,))
# Create a new flow_mod message
msg = of.ofp_flow_mod()
# Set the hard and idle timeouts to sane values
msg.idle_timeout = IDLE_TIMOUT
msg.hard_timeout = HARD_TIMEOUT
# Match the destination mac address that triggered this packet_in event
msg.match = of.ofp_match(dl_dst=dst)
# Flood the packet to all ports (but the incoming)
msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD))
# Set the buffer of the message that triggered the packet in event
# This way that message will be treated by the new flow and will
# not be lost
msg.buffer_id = event.ofp.buffer_id
# Send the message to the switch
self.connection.send(msg)
else:
if dst in self.mac_table:
# If the destination MAC address exists in the MAC table
# we will send the packet out from the port the MAC
# table has stored, unless it is the same as the
# incoming port
if event.port == self.mac_table[dst]:
# If the source port is the same as the destination
# port do nothing (efectively drop the packet)
pass
else:
# If the source port is different than the destinationport
# create a new flow matching the port-mac_src-mac_dst and
# push it to the device
log.info("Received unicast from %s with known destination %s" % (self.connection, dst))
# Create a new flow_mod message
msg = of.ofp_flow_mod()
# Match the destination MAC address with the one received in the message carried by the packet_in
msg.match = of.ofp_match(dl_dst=data.dst)
# Set timeouts to sane values
msg.idle_timeout = IDLE_TIMOUT
msg.hard_timeout = HARD_TIMEOUT
# Send this message to the port indicated by the MAC table
msg.actions.append(of.ofp_action_output(port = self.mac_table[dst]))
# Set the buffer of the message that triggered the packet in event
# This way that message will be treated by the new flow and will
# not be lost
msg.buffer_id = event.ofp.buffer_id
# Send the message to the switch
self.connection.send(msg)
else:
# If the destination MAC address is not in the MAC table
# Flood the message to all ports excpet the incoming one
# Hopefully the destination will answer and eventually his
# port will become known
log.info("Received unicast from %s with uknown destination %s" % (self.connection, dst))
# Create a new packet_out message
msg = of.ofp_packet_out()
# Indicate the port this packet came from
msg.in_port = event.port
# Set the action to flood the packet
msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD))
# Set the data to flood
msg.data = event.ofp
# Send the message to the switch
self.connection.send(msg)
def handle_firewall(self, event, blacklist):
log.debug("Got a packet in event from switch %s", event.dpid)
log.debug("Event packet type: %s", type(event.parsed))
# The packet sent to the controller is an ethernet packet (L2)
# This encapsulates the higher layer packet which we can get by calling next on
# the L2 one.
decapsulated_packet = event.parsed.next
# There are many kinds of things this decapsulated packet could be eg
# - any kind of L3 packet (IP, ICMP, RIP etc)
# - a resolution packet (ARP, NDP)
# We need to handle only the ones we care about,
# isinstance can used to do that.
# The packet types defined for pox and documentation on them is
# available in ~/pox/pox/lib/packet/
# every filename is a packet class that can be used here
# You can see in each packet's __init__ which fields it defines.
if not isinstance(decapsulated_packet, pkt.ipv4):
return False
# Implement firewall here and return true if the firewall block something
return False