-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontroller_assignment1.py
More file actions
144 lines (114 loc) · 8.47 KB
/
controller_assignment1.py
File metadata and controls
144 lines (114 loc) · 8.47 KB
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
from pox.core import core
import pox.lib.packet as pkt
import pox.lib.packet.ethernet as eth
import pox.lib.packet.arp as arp
import pox.lib.packet.icmp as icmp
import pox.lib.packet.ipv4 as ip
import pox.openflow.libopenflow_01 as of
from pox.lib.util import dpidToStr
from pox.lib.addresses import EthAddr
log = core.getLogger()
table={}
rules=[
# queues required for all dest hosts except h1 (h1 doesn't have queues)
# priority 100: specific tcp port-based rules
# priority 80: rate limited traffic between specific hosts
# priority 60: general traffic rules
# priority 40: ARP rules
{'priority':100,'EthSrc':'00:00:00:00:00:01','EthDst':'00:00:00:00:00:03', 'TCPPort':40, 'queue':0, 'drop':False}, # cap at 30 Mb/s
{'priority':100,'EthSrc':'00:00:00:00:00:01','EthDst':'00:00:00:00:00:02', 'TCPPort':60, 'queue':1, 'drop':False}, # cap at 150 Mb/s
# => the first two example of rules have been added for you, you need now to add other rules to satisfy the assignment requirements. Notice that we will make decisions based on Ethernet address rather than IP address. Rate limiting is implemented by sending the pacet to the correct port and queue (the queues that you have specified in the topology file).
# ARP rule inserted for all ports outside those specific above
{'priority':40,'EthSrc':'00:00:00:00:00:01','EthDst':'00:00:00:00:00:03', 'queue':0, 'drop':False}, # ARP uncapped - This is actually capped at 30 Mb/s as it is q0.
{'priority':40,'EthSrc':'00:00:00:00:00:01','EthDst':'00:00:00:00:00:02', 'queue':0, 'drop':False}, # ARP uncapped
{'priority':60,'EthSrc':'00:00:00:00:00:01','EthDst':'00:00:00:00:00:04', 'queue':0, 'drop':False}, # uncapped
{'priority':60,'EthSrc':'00:00:00:00:00:04','EthDst':'00:00:00:00:00:01', 'queue':None, 'drop':False}, # uncapped
{'priority':80,'EthSrc':'00:00:00:00:00:02','EthDst':'00:00:00:00:00:04', 'queue':1, 'drop':False}, # cap at 200 Mb/s
{'priority':80,'EthSrc':'00:00:00:00:00:03','EthDst':'00:00:00:00:00:04', 'drop':True}, # blocked
{'priority':80,'EthSrc':'00:00:00:00:00:04','EthDst':'00:00:00:00:00:03', 'drop':True}, # blocked
{'priority':60,'EthSrc':'00:00:00:00:00:03','EthDst':'00:00:00:00:00:01', 'queue':None, 'drop':False}, # uncapped
{'priority':60,'EthSrc':'00:00:00:00:00:02','EthDst':'00:00:00:00:00:01', 'queue':None, 'drop':False}, # uncapped
{'priority':60,'EthSrc':'00:00:00:00:00:04','EthDst':'00:00:00:00:00:02', 'queue':0, 'drop':False}, # uncapped
]
def launch ():
core.openflow.addListenerByName("ConnectionUp", _handle_ConnectionUp)
core.openflow.addListenerByName("PacketIn", _handle_PacketIn)
log.info("Switch running.")
def _handle_ConnectionUp ( event):
log.info("Starting Switch %s", dpidToStr(event.dpid))
flow_msg = of.ofp_flow_mod(command = of.OFPFC_DELETE)
event.connection.send(flow_msg)
def _handle_PacketIn ( event): # Ths is the main class where your code goes, it will be called every time a packet is sent from the switch to the controller
dpid = event.connection.dpid
sw=dpidToStr(dpid)
inport = event.port # this records the port from which the packet entered the switch
eth_packet = event.parsed # this parses the incoming message as an Ethernet packet
log.debug("Event: switch %s port %s packet %s" % (sw, inport, eth_packet)) # this is the way you can add debugging information to your text
table[(dpid,eth_packet.src)] = event.port # this associates the given port with the sending node using the source address of the incoming packet
dst_port = table.get((dpid,eth_packet.dst)) # if available in the table this line determines the destination port of the incoming packet
# this part is now separate from next part and deals with ARP (Address Resolution Protocol) messages
######################################################################################
############ CODE SHOULD ONLY BE ADDED BELOW #################################
if dst_port is None and eth_packet.type == eth.ARP_TYPE and eth_packet.dst == EthAddr(b"\xff\xff\xff\xff\xff\xff"): # this identifies that the packet is an ARP broadcast
# => in this case you want to create a packet so that you can send the message as a broadcast
data_msg = of.ofp_packet_out(data = event.ofp)
data_msg.actions.append(of.ofp_action_output(port=of.OFPP_ALL))
event.connection.send(data_msg)
for rule in rules: # now you are adding rules to the flow tables like before. First you check whether there is a rule match based on Eth source and destination
if eth_packet.dst==EthAddr(rule['EthDst']) and eth_packet.src==EthAddr(rule['EthSrc']):
log.debug("Event: found rule from source %s to dest %s" % (eth_packet.src, eth_packet.dst))
# => start creating a new flow rule for matching the ethernet source and destination
flow_msg = of.ofp_flow_mod()
flow_msg.priority = rule['priority']
flow_msg.match.dl_dst = eth_packet.dst
flow_msg.match.dl_src = eth_packet.src
flow_msg.hard_timeout = 40
# soft timeout is not required for this exercise
# check if packet is ipv4 first before accessing protocol attribute
if not isinstance(eth_packet.payload, ip) or eth_packet.payload.protocol != ip.TCP_PROTOCOL:
if rule['drop'] is True:
flow_msg.actions.append(of.ofp_action_output(port=of.OFPP_NONE)) # drop packet at switch
event.connection.send(flow_msg)
continue # packet is dropped by not adding action
elif rule['queue'] is not None:
flow_msg.actions.append(of.ofp_action_enqueue(port=dst_port, queue_id=rule['queue']))
else:
flow_msg.actions.append(of.ofp_action_output(port = dst_port))
event.connection.send(flow_msg)
data_msg = of.ofp_packet_out()
data_msg.data = event.ofp
data_msg.actions.append(of.ofp_action_output(port=dst_port))
event.connection.send(data_msg)
# => now check if the rule contains also TCP port info. If not install the flow without any port restriction
# => also remember to check if this is a drop rule. The drop function can be added by not sending any action to the flow rule
# => also remember that if there is a QoS requirement, then you need to use the of.ofp_action_enqueue() function, instead of the of.ofp_action_output
# => and remember that in addition to creating a fow rule, you should also send out the message that came from the Switch
# => at the end remember to send out both flow rule and packet
else:
# => otherwise:
flow_msg.match.dl_type = eth.IP_TYPE
flow_msg.match.nw_proto = ip.TCP_PROTOCOL
if 'TCPPort' in rule:
# match the destination TCP port
flow_msg.match.tp_dst = rule['TCPPort']
if rule['drop']:
flow_msg.actions.append(of.ofp_action_output(port=of.OFPP_NONE)) # drop packet at switch
event.connection.send(flow_msg)
continue # packet is dropped by setting output port to none and skipping
elif rule['queue'] is not None:
flow_msg.actions.append(of.ofp_action_enqueue(port=dst_port, queue_id=rule['queue']))
else:
flow_msg.actions.append(of.ofp_action_output(port=dst_port))
event.connection.send(flow_msg)
# => if the packet is an IP packet, its protocol is TCP, and the TCP port of the packet matches the TCP rule above
# => add additioinal matching fields to the flow rule you are creating: IP-protocol type, TCP_protocol_type, destination TCP port.
# => like above if it requires QoS then use the of.ofp_action_enqueue() function
# => also remember to check if this is a drop rule.
# => at the end remember to send out both flow rule and packet
data_msg = of.ofp_packet_out()
data_msg.data = event.ofp
data_msg.actions.append(of.ofp_action_output(port=dst_port))
event.connection.send(data_msg)
########### THIS IS THE END OF THE AREA WHERE YOU NEED TO ADD CODE ##################################
#####################################################################################################
break