LEFT | RIGHT |
(no file at all) | |
1 # ~/ryu$ PYTHONPATH= ./bin/ryu-manager ryu/app/cpm_13.py --observe-links | |
2 | |
3 from ryu.ofproto import ofproto_v1_3 | 1 from ryu.ofproto import ofproto_v1_3 |
4 from ryu.app import simple_switch_13 | 2 from ryu.app import simple_switch_13 |
5 from ryu.controller.handler import set_ev_cls | 3 from ryu.controller.handler import set_ev_cls |
6 from ryu.topology import event, switches | 4 from ryu.topology import event, switches |
7 from ryu.topology.api import get_switch, get_link | 5 from ryu.topology.api import get_switch, get_link |
8 from ryu.controller import ofp_event | 6 from ryu.controller import ofp_event |
9 from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER | 7 from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER |
10 from ryu.lib.packet import packet | 8 from ryu.lib.packet import packet |
11 from ryu.lib.packet import arp | 9 from ryu.lib.packet import arp |
12 from ryu.lib.packet import ethernet | 10 from ryu.lib.packet import ethernet |
13 from ryu.lib.packet import ether_types | 11 from ryu.lib.packet import ether_types |
14 from ryu.lib.packet import ipv4 | 12 from ryu.lib.packet import ipv4 |
15 from ryu.lib.packet import icmp | 13 from ryu.lib.packet import icmp |
16 from ryu.lib.packet import udp | 14 from ryu.lib.packet import udp |
17 from ryu.lib.packet import tcp | 15 from ryu.lib.packet import tcp |
18 from ryu.lib import hub | 16 from ryu.lib import hub |
19 import time | 17 import time |
20 from my_http_client import MyHTTPClient | 18 from my_http_client import MyHTTPClient |
| 19 from cpm_13_lib import SwitchPair |
21 | 20 |
22 | 21 |
23 class CPM13(simple_switch_13.SimpleSwitch13): | 22 class CPM13(simple_switch_13.SimpleSwitch13): |
24 "CPM module for topology discovery and SPF" | 23 "CPM module for topology discovery and packet forwarding" |
25 | 24 |
26 OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] | 25 OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] |
27 | 26 |
28 def __init__(self, *args, **kwargs): | 27 def __init__(self, *args, **kwargs): |
29 super(CPM13, self).__init__(*args, **kwargs) | 28 super(CPM13, self).__init__(*args, **kwargs) |
| 29 self.rib = {} # Contains all paths |
| 30 self.fib = {} # Contains only paths used for packet forwarding |
| 31 |
30 self.topo_links = {} | 32 self.topo_links = {} |
31 self.switches = {} | 33 self.switches = {} |
32 self.neighbor_ports = {} # Temp 2017-10-02 | 34 self.neighbor_ports = {} # Temp 2017-10-02 |
33 self.port_metric = {} # 2017-10-16 | 35 self.port_metric = {} # 2017-10-16 |
34 self.hosts = {} | 36 self.hosts = {} |
35 self.sample_interval = 30 # How often to query the DM | 37 self.sample_interval = 15 # How often to query the DM |
36 self.ref_bw = 100000000.0 # 100 Mbps; Reference Bandwidth in Bps | 38 self.ref_bw = 100000000.0 # 100 Mbps; Reference Bandwidth in Bps |
37 self.ref_hc = 30.0 # 30 Hops; Reference Hop Count | 39 self.ref_hc = 30.0 # 30 Hops; Reference Hop Count |
38 # Connect with DM: | 40 # Connect with DM: |
39 self.dm_server = MyHTTPClient('http://192.16.125.183/API_REST/request.ph
p/get?') | 41 self.dm_server = MyHTTPClient( |
| 42 'http://192.16.125.183/API_REST/request.php/get?') |
40 self.port_counters_snapshot = {'object_index': 1000, 'switches': []} | 43 self.port_counters_snapshot = {'object_index': 1000, 'switches': []} |
41 hub.spawn(self._get_dm_data) | 44 hub.spawn(self._get_dm_data) |
42 | 45 |
43 # Connect with DM#2; need a second object of MyHTTPClient as | 46 # Connect with DM#2; need a second object of MyHTTPClient as |
44 # URL for POST requests is different. | 47 # URL for POST requests is different. |
45 self.dm_server_2 = MyHTTPClient('http://192.16.125.183/API_REST/request.
php')· | 48 self.dm_server_2 = MyHTTPClient( |
| 49 'http://192.16.125.183/API_REST/request.php') |
| 50 reset_topo_link_json_obj = {'src_switch': '', |
| 51 'src_sw_port': '', |
| 52 'dst_switch': '', |
| 53 'dst_sw_port': '', |
| 54 'module': 'topology', |
| 55 'event': 'new_topology', |
| 56 'bandwidth': ''} |
| 57 self.dm_server_2.send_POST(reset_topo_link_json_obj) |
46 | 58 |
47 def _get_dm_data(self): | 59 def _get_dm_data(self): |
48 while True: | 60 while True: |
49 # Ask for DM data every self.sample_interval seconds: | 61 # Ask for DM data every self.sample_interval seconds: |
50 hub.sleep(self.sample_interval) | 62 hub.sleep(self.sample_interval) |
51 #body = self.dm_server.get_data_from_dm('port_counters', 1) | 63 #body = self.dm_server.get_data_from_dm('port_counters', 1) |
52 params_dict = {'module': 'nfm', 'row': 1, 'table': 'port_counters'} | 64 #params_dict = {'module': 'nfm', 'row': 1, 'table': 'port_counters'} |
53 body = self.dm_server.send_GET(params_dict) | 65 #body = self.dm_server.send_GET(params_dict) |
54 #print body | 66 #print body |
55 if body is None: # This is True if NO json in HTTP reponse | 67 params_dict1 = {'module': 'hum', 'row': 1} |
56 print 'No NFM monitoring data received from DM' | 68 body1 = self.dm_server.send_GET(params_dict1) |
57 continue | 69 print body1 |
58 # print body | 70 # print body |
59 self._calculate_link_metric(body) | 71 #if body is None: # This is True if NO json in HTTP reponse |
| 72 # print 'No NFM monitoring data received from DM' |
| 73 # continue |
| 74 if body1 is None: |
| 75 print 'No HUM monitoring data received from DM'··· |
| 76 # print body |
| 77 #self._calculate_link_metric(body) |
| 78 self._calculate_hum_metric(body1) |
| 79 self._rib_update() |
| 80 |
| 81 def _rib_update(self): |
| 82 # Update the metric for all paths in RIB |
| 83 for (a, z), pair in self.rib.items(): |
| 84 all_paths = pair.getAllPaths().keys() |
| 85 for path in all_paths: |
| 86 metric = self._calculate_path_uni_metric(path, 3) |
| 87 pair.updateSingleMetric(path, metric) |
| 88 rv_path = path[::-1] |
| 89 rv_metric = self._calculate_path_uni_metric(rv_path, 3) |
| 90 pair.updateSingleMetric(rv_path, rv_metric) |
| 91 time.sleep(0.00002) # sleep thread for 20ns |
| 92 |
| 93 # Also update FIB |
| 94 self._fib_update() |
| 95 |
| 96 def _rib_lookup(self, switches_tuple): |
| 97 # switches is a tuple with two elements |
| 98 # Fuction returns the corresponding SwitchPair object |
| 99 if switches_tuple in self.rib.keys(): |
| 100 pair = self.rib[switches_tuple] |
| 101 elif (switches_tuple[1], switches_tuple[0]) in self.rib.keys(): |
| 102 pair = self.rib[(switches_tuple[1], switches_tuple[0])] |
| 103 else: |
| 104 pair = None |
| 105 print '[FIB UPDATE] Something went wrong; pair not in RIB' |
| 106 |
| 107 return pair |
| 108 |
| 109 def _fib_update(self): |
| 110 for (a, z) in self.fib.keys(): |
| 111 pair = self._rib_lookup((a, z)) |
| 112 if pair is None: |
| 113 print '[FIB UPDATE] Something went wrong; pair not in RIB' |
| 114 |
| 115 (best_path, best_metric) = pair.getBestPath(a, z) |
| 116 if self.fib[(a, z)] != best_path: |
| 117 self.fib[(a, z)] = best_path |
| 118 print '[FIB] updated ', (a, z), self.fib[(a, z)], best_metric |
| 119 |
| 120 def _fib_lookup(self, src_switch, dst_switch): |
| 121 |
| 122 # Not in FIB |
| 123 if (src_switch, dst_switch) not in self.fib: |
| 124 pair = self._rib_lookup((src_switch, dst_switch)) |
| 125 (best_path, best_metric) = pair.getBestPath(src_switch, dst_switch) |
| 126 print '[FIB] added', (src_switch, dst_switch), best_path, best_metri
c· |
| 127 self.fib[(src_switch, dst_switch)] = best_path |
| 128 |
| 129 return self.fib[(src_switch, dst_switch)] |
| 130 |
| 131 def _fib_next_hop(self, current_switch, src_ip, dst_ip): |
| 132 # There should be no situation where current_switch |
| 133 # is the last switch of a path. This is due to the flow |
| 134 # installed by handle_new_host() |
| 135 |
| 136 a = self.hosts[src_ip]['switch'] |
| 137 z = self.hosts[dst_ip]['switch'] |
| 138 path = self._fib_lookup(a, z) |
| 139 |
| 140 if current_switch in path: |
| 141 next_switch = path.index(current_switch) + 1 |
| 142 (out_port, _) = self.neighbor_ports[(current_switch, |
| 143 path[next_switch])] |
| 144 print '[SWITCH]', current_switch, ': Packet', src_ip, dst_ip, 'Out p
ort', out_port |
| 145 return out_port |
| 146 else: |
| 147 print '[SWITCH]', current_switch, 'cannot FWD', src_ip, dst_ip |
| 148 return None |
| 149 |
| 150 def _add_switch_flow(self, datapath, in_port, pkt, buffer_id=None, hard_time
out=1): |
| 151 parser = datapath.ofproto_parser |
| 152 |
| 153 match_args = self._parse_ip_packet_headers(pkt) |
| 154 |
| 155 match_args['in_port'] = in_port |
| 156 |
| 157 if match_args['ip_proto'] == 1: |
| 158 # ICMP |
| 159 priority = 200 |
| 160 |
| 161 elif match_args['ip_proto'] == 6: |
| 162 # TCP |
| 163 # Add src and dst port |
| 164 priority = 400 |
| 165 |
| 166 elif match_args['ip_proto'] == 17: |
| 167 # UDP |
| 168 # Add src and dst port |
| 169 priority = 300 |
| 170 |
| 171 else: |
| 172 priority = 100 # Priority for everything else |
| 173 |
| 174 out_port = self._fib_next_hop(datapath.id, |
| 175 match_args['ipv4_src'], |
| 176 match_args['ipv4_dst']) |
| 177 |
| 178 # https://docs.python.org/2/tutorial/controlflow.html#unpacking-argument
-lists |
| 179 match = parser.OFPMatch(**match_args) |
| 180 self.logger.info('%8d %8d %17s %17s %17s %17s %8d %8d %8d', |
| 181 datapath.id, |
| 182 in_port, |
| 183 match_args['eth_src'], match_args['ipv4_src'], |
| 184 match_args['eth_dst'], match_args['ipv4_dst'], |
| 185 out_port, |
| 186 match_args['ip_proto'], priority) |
| 187 |
| 188 actions = [parser.OFPActionOutput(out_port)] |
| 189 |
| 190 |
| 191 self._add_flow(datapath, priority, match, actions, buffer_id , hard_time
out) |
| 192 |
| 193 |
| 194 |
| 195 def _add_flow(self, datapath, priority, match, actions, buffer_id=None, hard
_timeout=1): |
| 196 ofproto = datapath.ofproto |
| 197 parser = datapath.ofproto_parser |
| 198 |
| 199 # construct flow_mod message and send it. |
| 200 inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, |
| 201 actions)] |
| 202 mod = parser.OFPFlowMod(datapath=datapath, priority=priority, |
| 203 match=match, instructions=inst,hard_timeout= har
d_timeout) |
| 204 datapath.send_msg(mod) |
| 205 |
60 | 206 |
61 def _calculate_link_metric(self, data_dict): | 207 def _calculate_link_metric(self, data_dict): |
62 | 208 |
63 for switch in data_dict['switches']: | 209 for switch in data_dict['switches']: |
64 # each element is a dictionary as well | 210 # each element is a dictionary as well |
65 for port_counter in switch['port_counters']: | 211 for port_counter in switch['port_counters']: |
66 # each element is a dictionary as well | 212 # each element is a dictionary as well |
67 | 213 |
68 tx_metric = float(port_counter['tx_bw']) / self.ref_bw | 214 tx_metric = float(port_counter['tx_bw']) / self.ref_bw |
69 | 215 |
| 216 # Round to 3 decimal points |
| 217 # https://docs.python.org/2.7/library/functions.html#round |
| 218 tx_metric = round(tx_metric, 3) |
| 219 |
70 self.port_metric[(switch['switch_dpid'], | 220 self.port_metric[(switch['switch_dpid'], |
71 port_counter['port'])] = tx_metric | 221 port_counter['port'])] = tx_metric |
72 | 222 |
73 #print '[CALC] Port Metric', (switch['switch_dpid'], port_counte
r['port']), 'set to', tx_metric | 223 # print '[CALC] Port Metric', (switch['switch_dpid'], port_count
er['port']), 'set to', tx_metric |
74 | 224 |
75 def _calculate_hop_count(self, paths): | 225 |
76 paths_dict = {} | 226 def _calculate_hum_metric(self, data_dict): |
77 | 227 |
78 for path in paths: | 228 for key in data_dict.keys(): |
79 hop_count = len(path) | 229 data_dict[key] = [float(data_dict[key][0])] |
80 paths_dict[path] = hop_count / self.ref_hc | 230 print data_dict········ |
81 | 231 |
82 return paths_dict | 232 def _calculate_path_uni_metric(self, path, metric_type=1): |
83 | 233 |
84 def _calculate_path_metric(self, paths): | 234 # Hop Count |
85 paths_dict = {} | 235 hop_count = len(path) |
86 | 236 hop_metric = hop_count / self.ref_hc |
87 for path in paths: | 237 |
88 # Find the busiest port in the path. | 238 if metric_type == 1: |
89 # Path metric is equal to the busiest port's metric. | 239 # Return Hop Metric |
90 worst_metric = 0 | 240 return hop_metric |
91 for k, v in enumerate(path[:-1]): | 241 |
92 # v is the current switch dpid | 242 # NFM metric |
93 # path[k + 1] is the dpid of the next switch in path | 243 # Find the busiest port in the path. |
94 (tx_port, _) = self.neighbor_ports[(v, path[k + 1])] | 244 # Path metric is equal to the busiest port's metric. |
95 # Find busiest port in path: | 245 worst_metric = 0 |
96 if worst_metric < self.port_metric[(v, tx_port)]: | 246 for k, v in enumerate(path[:-1]): |
97 worst_metric = self.port_metric[(v, tx_port)] | 247 # v is the current switch dpid |
98 paths_dict[path] = worst_metric | 248 # path[k + 1] is the dpid of the next switch in path |
99 | 249 (tx_port, _) = self.neighbor_ports[(v, path[k + 1])] |
100 return paths_dict | 250 # Find busiest port in path: |
| 251 if worst_metric < self.port_metric[(v, tx_port)]: |
| 252 worst_metric = self.port_metric[(v, tx_port)] |
| 253 |
| 254 if metric_type == 2: |
| 255 # Return NFM Metric |
| 256 return worst_metric |
| 257 |
| 258 # Composite (Hop Count and NFM metric) |
| 259 # Weights for composite metric: |
| 260 w1 = 0.1 # Weight for Hop Count |
| 261 w2 = 0.9 # Weight for NFM metric |
| 262 # Lowest metric means best metric! (0 is the best possible) |
| 263 composite_metric = w1 * hop_count + w2 * worst_metric |
| 264 |
| 265 if metric_type == 3: |
| 266 # Return Composite metric |
| 267 return composite_metric |
101 | 268 |
102 def _deny_flow(self, datapath, in_port, pkt_in, hard_timeout=1): | 269 def _deny_flow(self, datapath, in_port, pkt_in, hard_timeout=1): |
103 ofproto = datapath.ofproto | 270 ofproto = datapath.ofproto |
104 parser = datapath.ofproto_parser | 271 parser = datapath.ofproto_parser |
105 | 272 |
106 match_args = self._parse_ip_packet_headers(pkt_in) | 273 match_args = self._parse_ip_packet_headers(pkt_in) |
107 match_args['in_port'] = in_port | 274 match_args['in_port'] = in_port |
108 | 275 |
109 # https://docs.python.org/2/tutorial/controlflow.html#unpacking-argument
-lists | 276 # https://docs.python.org/2/tutorial/controlflow.html#unpacking-argument
-lists |
110 match = parser.OFPMatch(**match_args) | 277 match = parser.OFPMatch(**match_args) |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
146 | 313 |
147 # print 'Neighbors', self.topo_links | 314 # print 'Neighbors', self.topo_links |
148 | 315 |
149 # Temp 2017-10-02 | 316 # Temp 2017-10-02 |
150 self.neighbor_ports[(switchA.dpid, switchB.dpid)] = ( | 317 self.neighbor_ports[(switchA.dpid, switchB.dpid)] = ( |
151 switchA.port_no, switchB.port_no) | 318 switchA.port_no, switchB.port_no) |
152 # print 'Ports', self.neighbor_ports | 319 # print 'Ports', self.neighbor_ports |
153 | 320 |
154 # 2017-10-16 | 321 # 2017-10-16 |
155 self.port_metric[(switchA.dpid, switchA.port_no)] = 0 | 322 self.port_metric[(switchA.dpid, switchA.port_no)] = 0 |
156 #print 'Port Metric', (switchA.dpid, switchA.port_no), 'set to', 0 | 323 # print 'Port Metric', (switchA.dpid, switchA.port_no), 'set to', 0 |
157 | 324 |
158 # 2017-11-16 | 325 # 2017-11-16 |
159 # Inform DM about this link | 326 # Inform DM about this link |
160 new_topo_link_json_obj = {'src_switch': switchA.dpid, | 327 new_topo_link_json_obj = {'src_switch': switchA.dpid, |
161 'src_sw_port': switchA.port_no, | 328 'src_sw_port': switchA.port_no, |
162 'dst_switch': switchB.dpid, | 329 'dst_switch': switchB.dpid, |
163 'dst_sw_port': switchB.port_no, | 330 'dst_sw_port': switchB.port_no, |
164 'module': 'topology', | 331 'module': 'topology', |
165 'event': 'new_link'} | 332 'event': 'link_up', |
| 333 'bandwidth': 100000000} |
166 self.dm_server_2.send_POST(new_topo_link_json_obj) | 334 self.dm_server_2.send_POST(new_topo_link_json_obj) |
167 | |
168 | 335 |
169 # https://www.python.org/doc/essays/graphs/ | 336 # https://www.python.org/doc/essays/graphs/ |
170 def _find_all_paths(self, neighbors, start, end, path=[]): | 337 def _find_all_paths(self, neighbors, start, end, path=[]): |
171 path = path + [start] | 338 path = path + [start] |
172 if start == end: | 339 if start == end: |
173 return [path] | 340 return [path] |
174 if start not in neighbors.keys(): | 341 if start not in neighbors.keys(): |
175 return [] | 342 return [] |
176 paths = [] | 343 paths = [] |
177 for node in neighbors[start]: | 344 for node in neighbors[start]: |
178 if node not in path: | 345 if node not in path: |
179 newpaths = self._find_all_paths(neighbors, node, end, path) | 346 newpaths = self._find_all_paths(neighbors, node, end, path) |
180 for newpath in newpaths: | 347 for newpath in newpaths: |
181 paths.append(newpath) | 348 paths.append(newpath) |
182 return paths | 349 return paths |
183 | 350 |
184 def _find_paths(self, neighbors, start, end): | |
185 # 2017-10-19 | |
186 # Change to list of tuples, instead of list of lists | |
187 | |
188 if start == end: | |
189 # same switch dpid i.e. the hosts are connected on the same switch | |
190 # So skip path calculation and return None | |
191 return None | |
192 else: | |
193 paths = self._find_all_paths(neighbors, start, end) | |
194 temp_paths = [] | |
195 for j in paths: | |
196 temp_paths.append(tuple(j)) | |
197 return temp_paths | |
198 | |
199 def _find_best_path(self, src_switch, dst_switch): | |
200 all_paths = self._find_paths(self.topo_links, src_switch, dst_switch) | |
201 if all_paths is None: | |
202 # Hosts are on same switch. I don't need to build | |
203 # a flow. It was already done by _handle_new_host() | |
204 return | |
205 | |
206 hop_count = self._calculate_hop_count(all_paths) | |
207 nfm_metric = self._calculate_path_metric(all_paths) | |
208 | |
209 # Weights for composite metric: | |
210 w1 = 0.1 # Weight for Hop Count | |
211 w2 = 0.9 # Weight for NFM metric | |
212 best_metric = 100000000000 # Just an Arbitrary high value. | |
213 # Lowest metric means best metric! (0 is the best possible) | |
214 best_path = [] | |
215 for path in all_paths: | |
216 composite_metric = w1 * hop_count[path] | |
217 composite_metric += w2 * nfm_metric[path] | |
218 if composite_metric < best_metric: | |
219 best_metric = composite_metric | |
220 best_path = path | |
221 | |
222 print 'BEST PATH between', (src_switch, dst_switch), 'is', best_path | |
223 print 'with Composite metric', best_metric | |
224 | |
225 return list(best_path) | |
226 | |
227 def _add_network_uni_flow(self, path, pkt, buffer_id=None): | |
228 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) | |
229 ip_src = pkt_ipv4.src | |
230 | |
231 # input argument "path" is a list of switches; each item is a dpid | |
232 # First I need to find the ports of the two switches: | |
233 if len(path) == 1: | |
234 print 'Hosts are on same switch', path[0] | |
235 return | |
236 | |
237 # Install flows from A >> Z in the reverse order, i.e. | |
238 # from End to Start. | |
239 path.reverse() | |
240 ip_dst = pkt_ipv4.dst | |
241 hostZ = self.hosts[ip_dst] # dst host | |
242 out_port = hostZ['port'] | |
243 | |
244 for k, v in enumerate(path[:-1]): | |
245 (in_port, new_out_port) = self.neighbor_ports[( | |
246 v, path[k + 1])] | |
247 | |
248 switch = self.switches[v] | |
249 | |
250 self._add_switch_uni_flow( | |
251 switch, in_port, out_port, pkt) | |
252 | |
253 out_port = new_out_port | |
254 | |
255 ip_src = pkt_ipv4.src | |
256 hostA = self.hosts[ip_src] # src host | |
257 in_port = hostA['port'] | |
258 | |
259 self._add_switch_uni_flow(self.switches[path[-1]], | |
260 in_port, out_port, pkt, buffer_id) | |
261 | |
262 def _add_switch_uni_flow(self, datapath, in_port, out_port, pkt, buffer_id=N
one): | |
263 parser = datapath.ofproto_parser | |
264 | |
265 match_args = self._parse_ip_packet_headers(pkt) | |
266 | |
267 match_args['in_port'] = in_port | |
268 | |
269 if match_args['ip_proto'] == 1: | |
270 # ICMP | |
271 priority = 200 | |
272 | |
273 elif match_args['ip_proto'] == 6: | |
274 # TCP | |
275 # Add src and dst port | |
276 priority = 400 | |
277 | |
278 elif match_args['ip_proto'] == 17: | |
279 # UDP | |
280 # Add src and dst port | |
281 priority = 300 | |
282 | |
283 else: | |
284 priority = 100 # Priority for everything else | |
285 | |
286 # https://docs.python.org/2/tutorial/controlflow.html#unpacking-argument
-lists | |
287 match = parser.OFPMatch(**match_args) | |
288 self.logger.info('%8d %8d %17s %17s %17s %17s %8d %8d %8d', | |
289 datapath.id, | |
290 in_port, | |
291 match_args['eth_src'], match_args['ipv4_src'], | |
292 match_args['eth_dst'], match_args['ipv4_dst'], | |
293 out_port, | |
294 match_args['ip_proto'], priority) | |
295 | |
296 actions = [parser.OFPActionOutput(out_port)] | |
297 | |
298 if buffer_id is not None: | |
299 time.sleep(1) # Delay for FlowMod to ingress switch | |
300 # This is to ensure that the client packet won't be forwarded | |
301 # to next (transit) switch before it has already processed | |
302 # the FlowMod sent to it. | |
303 # See while loop in function _add_network_uni_flow() | |
304 | |
305 self.add_flow(datapath, priority, match, actions, buffer_id) | |
306 | |
307 def _parse_ip_packet_headers(self, pkt): | 351 def _parse_ip_packet_headers(self, pkt): |
308 # The following headers are available to all IPv4 packets | 352 # The following headers are available to all IPv4 packets |
309 pkt_ethernet = pkt.get_protocol(ethernet.ethernet) | 353 pkt_ethernet = pkt.get_protocol(ethernet.ethernet) |
310 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) | 354 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) |
311 eth_type = pkt_ethernet.ethertype | 355 eth_type = pkt_ethernet.ethertype |
312 eth_src = pkt_ethernet.src | 356 eth_src = pkt_ethernet.src |
313 ip_src = pkt_ipv4.src | 357 ip_src = pkt_ipv4.src |
314 eth_dst = pkt_ethernet.dst | 358 eth_dst = pkt_ethernet.dst |
315 ip_dst = pkt_ipv4.dst | 359 ip_dst = pkt_ipv4.dst |
316 ip_proto = pkt_ipv4.proto # 1 for ICMP, 6 for TCP, 17 for UDP | 360 ip_proto = pkt_ipv4.proto # 1 for ICMP, 6 for TCP, 17 for UDP |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
380 pkt_out.add_protocol(ethernet.ethernet( | 424 pkt_out.add_protocol(ethernet.ethernet( |
381 ethertype=pkt_in_ether.ethertype, | 425 ethertype=pkt_in_ether.ethertype, |
382 dst=pkt_in_ether.src, src=src_mac)) | 426 dst=pkt_in_ether.src, src=src_mac)) |
383 pkt_out.add_protocol(arp.arp(opcode=arp.ARP_REPLY, | 427 pkt_out.add_protocol(arp.arp(opcode=arp.ARP_REPLY, |
384 src_mac=src_mac, | 428 src_mac=src_mac, |
385 src_ip=pkt_in_arp.dst_ip, | 429 src_ip=pkt_in_arp.dst_ip, |
386 dst_mac=pkt_in_arp.src_mac, | 430 dst_mac=pkt_in_arp.src_mac, |
387 dst_ip=pkt_in_arp.src_ip)) | 431 dst_ip=pkt_in_arp.src_ip)) |
388 self._send_packet(datapath, in_port, pkt_out) | 432 self._send_packet(datapath, in_port, pkt_out) |
389 | 433 |
390 def _handle_new_host(self, datapath, switch_port, host_mac): | 434 def _rib_init(self, new_switch): |
| 435 switches_with_hosts = [] |
| 436 for ip, params in self.hosts.items(): |
| 437 if params['switch'] not in switches_with_hosts: |
| 438 switches_with_hosts.append(params['switch']) |
| 439 for switch in switches_with_hosts: |
| 440 if switch == new_switch: |
| 441 # Same switch, no need to add to RIB |
| 442 continue |
| 443 if (new_switch, switch) not in self.rib and (switch, new_switch) not
in self.rib: |
| 444 new_pair = SwitchPair() |
| 445 new_pair.importAllPaths(self._find_all_paths( |
| 446 self.topo_links, new_switch, switch)) |
| 447 self.rib[(new_switch, switch)] = new_pair |
| 448 print '[RIB] Initialized for', (new_switch, switch), ' and ', (s
witch, new_switch) |
| 449 else: |
| 450 print 'RIB Initialization failed', self.rib |
| 451 |
| 452 def _handle_new_host(self, datapath, switch_port, host_mac, host_ip): |
| 453 # Add new host to host dict |
| 454 self.hosts[host_ip] = {'mac': host_mac, |
| 455 'switch': datapath.id, |
| 456 'port': switch_port} |
| 457 |
| 458 print "[DISCOVERED NEW HOST]", host_ip, self.hosts[host_ip] |
| 459 |
| 460 # Add flow on this switch, point to NEW HOST: |
391 parser = datapath.ofproto_parser | 461 parser = datapath.ofproto_parser |
392 match = parser.OFPMatch(eth_dst=host_mac) | 462 match = parser.OFPMatch(eth_dst=host_mac) |
393 actions = [parser.OFPActionOutput(switch_port)] | 463 actions = [parser.OFPActionOutput(switch_port)] |
394 self.add_flow(datapath, 50, match, actions) | 464 self._add_flow(datapath, 50, match, actions,15) |
| 465 |
| 466 # Initialize RIB |
| 467 self._rib_init(datapath.id) |
| 468 |
| 469 def _check_pkt_priority(self, pkt): |
| 470 # This functions gives the packet a priority according |
| 471 # to it's header information |
| 472 |
| 473 packet_headers = self._parse_ip_packet_headers(pkt) |
| 474 # Priorities; 3 = high, 2 = medium, 1 = low, 0 = garbage |
| 475 if packet_headers['ip_proto'] == 1: # ICMP |
| 476 priority = 1 |
| 477 |
| 478 elif packet_headers['ip_proto'] == 6: # TCP |
| 479 tcp_src = packet_headers['tcp_src'] |
| 480 tcp_dst = packet_headers['tcp_dst'] |
| 481 |
| 482 if tcp_src == 10001 or tcp_dst == 10001: |
| 483 priority = 1 |
| 484 elif tcp_src == 10002 or tcp_dst == 10002: |
| 485 priority = 2 |
| 486 elif tcp_src == 10003 or tcp_dst == 10003: |
| 487 priority = 3 |
| 488 else: |
| 489 priority = 0 |
| 490 |
| 491 elif packet_headers['ip_proto'] == 17: # UDP |
| 492 udp_src = packet_headers['udp_src'] |
| 493 udp_dst = packet_headers['udp_dst'] |
| 494 |
| 495 if udp_src == 10001 or udp_dst == 10001: |
| 496 priority = 1 |
| 497 elif udp_src == 10002 or udp_dst == 10002: |
| 498 priority = 2 |
| 499 elif udp_src == 10003 or udp_dst == 10003: |
| 500 priority = 3 |
| 501 else: |
| 502 priority = 0 |
| 503 |
| 504 else: |
| 505 priority = 0 |
| 506 |
| 507 return priority |
| 508 |
| 509 def admission_control(self, datapath, pkt): |
| 510 priority = self._check_pkt_priority(pkt) |
| 511 |
| 512 switch = datapath.id |
| 513 tx_port = [] |
| 514 allow = True # Return True (False) for pkt flow to be allowed (denied) |
| 515 |
| 516 # neighbor_switches is a Python list of neighbor dpid-s |
| 517 neighbor_switches = self.topo_links[switch] |
| 518 |
| 519 for neighbor in neighbor_switches: |
| 520 (switch_port, _) = self.neighbor_ports[(switch, neighbor)] |
| 521 # Python list; each element an egress port |
| 522 tx_port.append(switch_port) |
| 523 |
| 524 lowest_metric = 1 # This is the worst possible metric |
| 525 for port in tx_port: |
| 526 current_metric = self.port_metric[(switch, port)] |
| 527 # print 'switch dpid:', switch, 'port', port, 'congestion', current_
metric |
| 528 if current_metric < lowest_metric: |
| 529 lowest_metric = current_metric |
| 530 else: |
| 531 continue |
| 532 |
| 533 # Decision making: |
| 534 if (0.25 <= lowest_metric < 0.5) and priority == 0: |
| 535 # Don't allow Priority 0 |
| 536 allow = False |
| 537 elif (0.5 <= lowest_metric < 0.75) and priority in [0, 1]: |
| 538 # Don't allow Priority 0 and 1 |
| 539 allow = False |
| 540 elif (0.75 <= lowest_metric < 1) and priority in [0, 1, 2]: |
| 541 # Don't allow Priority 0, 1 and 2 |
| 542 allow = False |
| 543 else: |
| 544 allow = True |
| 545 |
| 546 print '[AC] DPID', datapath.id, 'LM:', lowest_metric, 'P:', priority, 'A
:', allow |
| 547 |
| 548 return allow |
395 | 549 |
396 @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) | 550 @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) |
397 def _packet_in_handler(self, ev): | 551 def _packet_in_handler(self, ev): |
398 # If you hit this you might want to increase | 552 # If you hit this you might want to increase |
399 # the "miss_send_length" of your switch | 553 # the "miss_send_length" of your switch |
400 if ev.msg.msg_len < ev.msg.total_len: | 554 if ev.msg.msg_len < ev.msg.total_len: |
401 self.logger.debug("packet truncated: only %s of %s bytes", | 555 self.logger.debug("packet truncated: only %s of %s bytes", |
402 ev.msg.msg_len, ev.msg.total_len) | 556 ev.msg.msg_len, ev.msg.total_len) |
403 | 557 |
404 msg = ev.msg | 558 msg = ev.msg |
405 datapath = msg.datapath | 559 datapath = msg.datapath |
406 in_port = msg.match['in_port'] | 560 in_port = msg.match['in_port'] |
407 | 561 |
408 pkt = packet.Packet(msg.data) | 562 pkt = packet.Packet(msg.data) |
409 eth = pkt.get_protocols(ethernet.ethernet)[0] | 563 eth = pkt.get_protocols(ethernet.ethernet)[0] |
410 | 564 |
411 if eth.ethertype == ether_types.ETH_TYPE_LLDP: | 565 if eth.ethertype == ether_types.ETH_TYPE_LLDP: |
412 # ignore lldp packet | 566 # ignore lldp packet |
413 return | 567 return |
414 | 568 |
415 print 'PACKET IN >>', datapath.id, in_port | 569 # print 'PACKET IN >>', datapath.id, in_port |
416 self.logger.debug("packet-in %s" % (pkt,)) | 570 self.logger.debug("packet-in %s" % (pkt,)) |
417 | 571 |
418 dpid = datapath.id | 572 dpid = datapath.id |
419 | 573 |
420 # self.hosts = {'10.0.0.1': {'mac': '00:00:00:00:00:01', 'switch': 11, '
port': 1}, | 574 # self.hosts = {'10.0.0.1': {'mac': '00:00:00:00:00:01', 'switch': 11, '
port': 1}, |
421 # '10.0.0.2': {'mac': '00:00:00:00:00:02', 'switch': 13, 'p
ort': 2}} | 575 # '10.0.0.2': {'mac': '00:00:00:00:00:02', 'switch': 13, 'p
ort': 2}} |
422 | 576 |
423 # Handle ARP Request packets: | 577 # Handle ARP Request packets: |
424 pkt_arp = pkt.get_protocol(arp.arp) | 578 pkt_arp = pkt.get_protocol(arp.arp) |
425 if pkt_arp: | 579 if pkt_arp: |
426 # Inspired by Ryu book page 88 | 580 # Inspired by Ryu book page 88 |
427 if pkt_arp: | 581 if pkt_arp: |
428 if pkt_arp.opcode != arp.ARP_REQUEST: | 582 if pkt_arp.opcode != arp.ARP_REQUEST: |
429 print 'Expected an ARP Request but this is not it.' | 583 print 'Expected an ARP Request but this is not it.' |
430 return | 584 return |
431 | 585 |
432 # Learn new source host: | 586 # Learn new source host: |
433 if pkt_arp.src_ip not in self.hosts: | 587 if pkt_arp.src_ip not in self.hosts: |
434 self.hosts[pkt_arp.src_ip] = { | 588 self._handle_new_host( |
435 'mac': eth.src, 'switch': dpid, 'port': in_port} | 589 datapath, in_port, eth.src, pkt_arp.src_ip) |
436 print "DISCOVERED NEW HOST", pkt_arp.src_ip, self.hosts[pkt_
arp.src_ip] | |
437 # Add flow on this switch, point to NEW HOST: | |
438 self._handle_new_host(datapath, in_port, eth.src) | |
439 return | 590 return |
440 | 591 |
441 # Reply to ARP request: | 592 # Reply to ARP request: |
442 if pkt_arp.dst_ip in self.hosts: | 593 if pkt_arp.dst_ip in self.hosts: |
443 self._handle_arp_request(datapath, in_port, pkt) | 594 self._handle_arp_request(datapath, in_port, pkt) |
444 return | 595 return |
445 | 596 |
446 # Handle IPv4 packets: | 597 # Handle IPv4 packets: |
447 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) | 598 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) |
448 if not pkt_ipv4: | 599 if not pkt_ipv4: |
449 print 'NOT an IPv4 Packet:' | 600 print 'NOT an IPv4 Packet:' |
450 self.logger.info("TABLE MISS: switch %s, packet-in %s", dpid, (pkt,)
) | 601 self.logger.info( |
| 602 "TABLE MISS: switch %s, packet-in %s", dpid, (pkt,)) |
451 return | 603 return |
452 else: | 604 else: |
453 # Block all similar future packets for 1 second: | 605 # Admission Control |
454 self._deny_flow(datapath, in_port, pkt) | 606 if self.admission_control(datapath, pkt) is False: |
455 | 607 self._deny_flow(datapath, in_port, pkt, 15) |
456 dst_switch = self.hosts[pkt_ipv4.dst]['switch'] | 608 print '[FLOW DENIED]', pkt_ipv4, 'at', datapath.id, 'port', in_p
ort |
457 # Find best path between (src_switch, dst_switch) | 609 else: |
458 best_path = self._find_best_path(dpid, dst_switch) | 610 print '[FLOW ALLOWED]', pkt_ipv4, 'at', datapath.id, 'port', in_
port |
459 | 611 self._add_switch_flow(datapath, in_port, pkt, msg.buffer_id,15) |
460 # Installing flows from A >> Z | |
461 self._add_network_uni_flow(best_path, pkt, msg.buffer_id) | |
LEFT | RIGHT |