LEFT | RIGHT |
1 # ~/ryu$ PYTHONPATH= ./bin/ryu-manager ryu/app/cpm_13.py --observe-links | 1 # ~/ryu$ PYTHONPATH= ./bin/ryu-manager ryu/app/cpm_13.py --observe-links |
2 | 2 |
3 from ryu.ofproto import ofproto_v1_3 | 3 from ryu.ofproto import ofproto_v1_3 |
4 from ryu.app import simple_switch_13 | 4 from ryu.app import simple_switch_13 |
5 from ryu.controller.handler import set_ev_cls | 5 from ryu.controller.handler import set_ev_cls |
6 from ryu.topology import event, switches | 6 from ryu.topology import event, switches |
7 from ryu.topology.api import get_switch, get_link | 7 from ryu.topology.api import get_switch, get_link |
8 from ryu.controller import ofp_event | 8 from ryu.controller import ofp_event |
9 from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER | 9 from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER |
10 from ryu.lib.packet import packet | 10 from ryu.lib.packet import packet |
11 from ryu.lib.packet import arp | 11 from ryu.lib.packet import arp |
12 from ryu.lib.packet import ethernet | 12 from ryu.lib.packet import ethernet |
13 from ryu.lib.packet import ether_types | 13 from ryu.lib.packet import ether_types |
14 from ryu.lib.packet import ipv4 | 14 from ryu.lib.packet import ipv4 |
15 from ryu.lib.packet import icmp | 15 from ryu.lib.packet import icmp |
16 from ryu.lib.packet import udp | 16 from ryu.lib.packet import udp |
17 from ryu.lib.packet import tcp | 17 from ryu.lib.packet import tcp |
18 from ryu.lib import hub | 18 from ryu.lib import hub |
19 import time | 19 import time |
20 from my_http_client import MyHTTPClient | 20 from my_http_client import MyHTTPClient |
21 | 21 |
22 | 22 |
23 class CPM13(simple_switch_13.SimpleSwitch13): | 23 class CPM13(simple_switch_13.SimpleSwitch13): |
24 "CPM module for topology discovery and SPF" | 24 "CPM module for topology discovery and SPF" |
25 | 25 |
26 OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] | 26 OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] |
27 | 27 |
28 def __init__(self, *args, **kwargs): | 28 def __init__(self, *args, **kwargs): |
29 super(CPM13, self).__init__(*args, **kwargs) | 29 super(CPM13, self).__init__(*args, **kwargs) |
| 30 self.fib = {} # 2017-11-18 |
30 self.topo_links = {} | 31 self.topo_links = {} |
31 self.switches = {} | 32 self.switches = {} |
32 self.neighbor_ports = {} # Temp 2017-10-02 | 33 self.neighbor_ports = {} # Temp 2017-10-02 |
33 self.port_metric = {} # 2017-10-16 | 34 self.port_metric = {} # 2017-10-16 |
34 self.hosts = {} | 35 self.hosts = {} |
35 self.sample_interval = 30 # How often to query the DM | 36 self.sample_interval = 30 # How often to query the DM |
36 self.ref_bw = 100000000.0 # 100 Mbps; Reference Bandwidth in Bps | 37 self.ref_bw = 100000000.0 # 100 Mbps; Reference Bandwidth in Bps |
37 self.ref_hc = 30.0 # 30 Hops; Reference Hop Count | 38 self.ref_hc = 30.0 # 30 Hops; Reference Hop Count |
38 # Connect with DM: | 39 # Connect with DM: |
39 self.dm_server = MyHTTPClient('http://192.16.125.183/API_REST/request.ph
p/get?') | 40 self.dm_server = MyHTTPClient('http://192.16.125.183/API_REST/request.ph
p/get?') |
40 self.port_counters_snapshot = {'object_index': 1000, 'switches': []} | 41 self.port_counters_snapshot = {'object_index': 1000, 'switches': []} |
41 self.get_dm_data_thread = hub.spawn(self._get_dm_data) | 42 hub.spawn(self._get_dm_data) |
| 43 |
| 44 # Connect with DM#2; need a second object of MyHTTPClient as |
| 45 # URL for POST requests is different. |
| 46 self.dm_server_2 = MyHTTPClient('http://192.16.125.183/API_REST/request.
php')· |
42 | 47 |
43 def _get_dm_data(self): | 48 def _get_dm_data(self): |
44 while True: | 49 while True: |
45 # Ask for DM data every self.sample_interval seconds: | 50 # Ask for DM data every self.sample_interval seconds: |
46 hub.sleep(self.sample_interval) | 51 hub.sleep(self.sample_interval) |
47 #body = self.dm_server.get_data_from_dm('port_counters', 1) | 52 #body = self.dm_server.get_data_from_dm('port_counters', 1) |
48 params_dict = {'module': 'nfm', 'row': 1, 'table': 'port_counters'} | 53 params_dict = {'module': 'nfm', 'row': 1, 'table': 'port_counters'} |
49 body = self.dm_server.send_GET(params_dict) | 54 body = self.dm_server.send_GET(params_dict) |
50 #print body | 55 #print body |
51 if body is None: # This is True if NO json in HTTP reponse | 56 if body is None: # This is True if NO json in HTTP reponse |
52 print 'No NFM monitoring data received from DM' | 57 print 'No NFM monitoring data received from DM' |
53 continue | 58 continue |
54 # print body | 59 # print body |
55 self._calculate_link_metric(body) | 60 self._calculate_link_metric(body) |
| 61 self._fib_update() |
| 62 |
| 63 def _fib_update(self): |
| 64 for (a, z) in self.fib.keys(): |
| 65 best_path = self._find_best_path(a, z) |
| 66 if self.fib[(a, z)] != best_path: |
| 67 self.fib[(a, z)] = best_path |
| 68 print '[UPDATED BEST PATH] ', a, '>', z, ':', self.fib[(a, z)] |
| 69 |
| 70 def _fib_lookup(self, src_switch, dst_switch): |
| 71 print self.fib |
| 72 print '[FIB LOOKUP]', (src_switch, dst_switch), self.fib[(src_switch, ds
t_switch)] |
| 73 return self.fib[(src_switch, dst_switch)] |
56 | 74 |
57 def _calculate_link_metric(self, data_dict): | 75 def _calculate_link_metric(self, data_dict): |
58 | 76 |
59 for switch in data_dict['switches']: | 77 for switch in data_dict['switches']: |
60 # each element is a dictionary as well | 78 # each element is a dictionary as well |
61 for port_counter in switch['port_counters']: | 79 for port_counter in switch['port_counters']: |
62 # each element is a dictionary as well | 80 # each element is a dictionary as well |
63 | 81 |
64 tx_metric = float(port_counter['tx_bw']) / self.ref_bw | 82 tx_metric = float(port_counter['tx_bw']) / self.ref_bw |
| 83 |
| 84 # Round to 3 decimal points |
| 85 # https://docs.python.org/2.7/library/functions.html#round |
| 86 tx_metric = round(tx_metric, 3) |
65 | 87 |
66 self.port_metric[(switch['switch_dpid'], | 88 self.port_metric[(switch['switch_dpid'], |
67 port_counter['port'])] = tx_metric | 89 port_counter['port'])] = tx_metric |
68 | 90 |
69 #print '[CALC] Port Metric', (switch['switch_dpid'], port_counte
r['port']), 'set to', tx_metric | 91 #print '[CALC] Port Metric', (switch['switch_dpid'], port_counte
r['port']), 'set to', tx_metric |
70 | 92 |
71 def _calculate_hop_count(self, paths): | 93 def _calculate_hop_count(self, paths): |
72 paths_dict = {} | 94 paths_dict = {} |
73 | 95 |
74 for path in paths: | 96 for path in paths: |
(...skipping 13 matching lines...) Expand all Loading... |
88 # v is the current switch dpid | 110 # v is the current switch dpid |
89 # path[k + 1] is the dpid of the next switch in path | 111 # path[k + 1] is the dpid of the next switch in path |
90 (tx_port, _) = self.neighbor_ports[(v, path[k + 1])] | 112 (tx_port, _) = self.neighbor_ports[(v, path[k + 1])] |
91 # Find busiest port in path: | 113 # Find busiest port in path: |
92 if worst_metric < self.port_metric[(v, tx_port)]: | 114 if worst_metric < self.port_metric[(v, tx_port)]: |
93 worst_metric = self.port_metric[(v, tx_port)] | 115 worst_metric = self.port_metric[(v, tx_port)] |
94 paths_dict[path] = worst_metric | 116 paths_dict[path] = worst_metric |
95 | 117 |
96 return paths_dict | 118 return paths_dict |
97 | 119 |
| 120 def _deny_flow(self, datapath, in_port, pkt_in, hard_timeout=1): |
| 121 ofproto = datapath.ofproto |
| 122 parser = datapath.ofproto_parser |
| 123 |
| 124 match_args = self._parse_ip_packet_headers(pkt_in) |
| 125 match_args['in_port'] = in_port |
| 126 |
| 127 # https://docs.python.org/2/tutorial/controlflow.html#unpacking-argument
-lists |
| 128 match = parser.OFPMatch(**match_args) |
| 129 |
| 130 inst = [parser.OFPInstructionActions( |
| 131 ofproto.OFPIT_CLEAR_ACTIONS, [])] |
| 132 mod = parser.OFPFlowMod(datapath=datapath, priority=65535, |
| 133 match=match, instructions=inst, |
| 134 hard_timeout=hard_timeout) |
| 135 datapath.send_msg(mod) |
| 136 |
98 @set_ev_cls(event.EventSwitchEnter) | 137 @set_ev_cls(event.EventSwitchEnter) |
99 def _handle_new_switch(self, ev): | 138 def _handle_new_switch(self, ev): |
100 # Gives the ryu.controller.controller.Datapath object of the switch: | 139 # Gives the ryu.controller.controller.Datapath object of the switch: |
101 # print ev.switch.dp | 140 # print ev.switch.dp |
102 new_switch = ev.switch.dp | 141 new_switch = ev.switch.dp |
103 self.switches[new_switch.id] = ev.switch.dp | 142 self.switches[new_switch.id] = ev.switch.dp |
104 self.topo_links[new_switch.id] = [] | 143 self.topo_links[new_switch.id] = [] |
105 | 144 |
106 @set_ev_cls(event.EventLinkAdd) | 145 @set_ev_cls(event.EventLinkAdd) |
107 def _handle_new_link(self, ev): | 146 def _handle_new_link(self, ev): |
(...skipping 18 matching lines...) Expand all Loading... |
126 # print 'Neighbors', self.topo_links | 165 # print 'Neighbors', self.topo_links |
127 | 166 |
128 # Temp 2017-10-02 | 167 # Temp 2017-10-02 |
129 self.neighbor_ports[(switchA.dpid, switchB.dpid)] = ( | 168 self.neighbor_ports[(switchA.dpid, switchB.dpid)] = ( |
130 switchA.port_no, switchB.port_no) | 169 switchA.port_no, switchB.port_no) |
131 # print 'Ports', self.neighbor_ports | 170 # print 'Ports', self.neighbor_ports |
132 | 171 |
133 # 2017-10-16 | 172 # 2017-10-16 |
134 self.port_metric[(switchA.dpid, switchA.port_no)] = 0 | 173 self.port_metric[(switchA.dpid, switchA.port_no)] = 0 |
135 #print 'Port Metric', (switchA.dpid, switchA.port_no), 'set to', 0 | 174 #print 'Port Metric', (switchA.dpid, switchA.port_no), 'set to', 0 |
| 175 |
| 176 # 2017-11-16 |
| 177 # Inform DM about this link |
| 178 new_topo_link_json_obj = {'src_switch': switchA.dpid, |
| 179 'src_sw_port': switchA.port_no, |
| 180 'dst_switch': switchB.dpid, |
| 181 'dst_sw_port': switchB.port_no, |
| 182 'module': 'topology', |
| 183 'event': 'link_up', |
| 184 'bandwidth': 100000} |
| 185 self.dm_server_2.send_POST(new_topo_link_json_obj) |
| 186 |
136 | 187 |
137 # https://www.python.org/doc/essays/graphs/ | 188 # https://www.python.org/doc/essays/graphs/ |
138 def _find_all_paths(self, neighbors, start, end, path=[]): | 189 def _find_all_paths(self, neighbors, start, end, path=[]): |
139 path = path + [start] | 190 path = path + [start] |
140 if start == end: | 191 if start == end: |
141 return [path] | 192 return [path] |
142 if start not in neighbors.keys(): | 193 if start not in neighbors.keys(): |
143 return [] | 194 return [] |
144 paths = [] | 195 paths = [] |
145 for node in neighbors[start]: | 196 for node in neighbors[start]: |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
180 best_metric = 100000000000 # Just an Arbitrary high value. | 231 best_metric = 100000000000 # Just an Arbitrary high value. |
181 # Lowest metric means best metric! (0 is the best possible) | 232 # Lowest metric means best metric! (0 is the best possible) |
182 best_path = [] | 233 best_path = [] |
183 for path in all_paths: | 234 for path in all_paths: |
184 composite_metric = w1 * hop_count[path] | 235 composite_metric = w1 * hop_count[path] |
185 composite_metric += w2 * nfm_metric[path] | 236 composite_metric += w2 * nfm_metric[path] |
186 if composite_metric < best_metric: | 237 if composite_metric < best_metric: |
187 best_metric = composite_metric | 238 best_metric = composite_metric |
188 best_path = path | 239 best_path = path |
189 | 240 |
190 print 'BEST PATH between', (src_switch, dst_switch), 'is', best_path | 241 #print 'BEST PATH between', (src_switch, dst_switch), 'is', best_path |
191 print 'with Composite metric', best_metric | 242 #print 'with Composite metric', best_metric |
192 | 243 |
193 return list(best_path) | 244 #return list(best_path) |
| 245 return best_path |
194 | 246 |
195 def _add_network_uni_flow(self, path, pkt, buffer_id=None): | 247 def _add_network_uni_flow(self, path, pkt, buffer_id=None): |
196 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) | 248 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) |
197 ip_src = pkt_ipv4.src | 249 ip_src = pkt_ipv4.src |
198 | 250 |
199 # input argument "path" is a list of switches; each item is a dpid | 251 # input argument "path" is a tuple of switches; each item is a dpid |
200 # First I need to find the ports of the two switches: | 252 # First I need to find the ports of the two switches: |
| 253 print 'path', path |
201 if len(path) == 1: | 254 if len(path) == 1: |
202 print 'Hosts are on same switch', path[0] | 255 print 'Hosts are on same switch', path[0] |
203 return | 256 return |
204 | 257 |
205 # Install flows from A >> Z in the reverse order, i.e. | 258 # Install flows from A >> Z in the reverse order, i.e. |
206 # from End to Start. | 259 # from End to Start. |
207 path.reverse() | 260 path = path[::-1] # New tuple with reversed order of items |
| 261 |
208 ip_dst = pkt_ipv4.dst | 262 ip_dst = pkt_ipv4.dst |
209 hostZ = self.hosts[ip_dst] # dst host | 263 hostZ = self.hosts[ip_dst] # dst host |
210 out_port = hostZ['port'] | 264 out_port = hostZ['port'] |
211 | 265 |
212 for k, v in enumerate(path[:-1]): | 266 for k, v in enumerate(path[:-1]): |
213 (in_port, new_out_port) = self.neighbor_ports[( | 267 (in_port, new_out_port) = self.neighbor_ports[( |
214 v, path[k + 1])] | 268 v, path[k + 1])] |
215 | 269 |
216 switch = self.switches[v] | 270 switch = self.switches[v] |
217 | 271 |
218 self._add_switch_uni_flow( | 272 self._add_switch_uni_flow( |
219 switch, in_port, out_port, pkt) | 273 switch, in_port, out_port, pkt) |
220 | 274 |
221 out_port = new_out_port | 275 out_port = new_out_port |
222 | 276 |
223 ip_src = pkt_ipv4.src | 277 ip_src = pkt_ipv4.src |
224 hostA = self.hosts[ip_src] # src host | 278 hostA = self.hosts[ip_src] # src host |
225 in_port = hostA['port'] | 279 in_port = hostA['port'] |
| 280 |
226 self._add_switch_uni_flow(self.switches[path[-1]], | 281 self._add_switch_uni_flow(self.switches[path[-1]], |
227 in_port, out_port, pkt, buffer_id) | 282 in_port, out_port, pkt, buffer_id) |
228 | 283 |
229 def _add_switch_uni_flow(self, datapath, in_port, out_port, pkt, buffer_id=N
one): | 284 def _add_switch_uni_flow(self, datapath, in_port, out_port, pkt, buffer_id=N
one): |
230 parser = datapath.ofproto_parser | 285 parser = datapath.ofproto_parser |
231 | 286 |
| 287 match_args = self._parse_ip_packet_headers(pkt) |
| 288 |
| 289 match_args['in_port'] = in_port |
| 290 |
| 291 if match_args['ip_proto'] == 1: |
| 292 # ICMP |
| 293 priority = 200 |
| 294 |
| 295 elif match_args['ip_proto'] == 6: |
| 296 # TCP |
| 297 # Add src and dst port |
| 298 priority = 400 |
| 299 |
| 300 elif match_args['ip_proto'] == 17: |
| 301 # UDP |
| 302 # Add src and dst port |
| 303 priority = 300 |
| 304 |
| 305 else: |
| 306 priority = 100 # Priority for everything else |
| 307 |
| 308 # https://docs.python.org/2/tutorial/controlflow.html#unpacking-argument
-lists |
| 309 match = parser.OFPMatch(**match_args) |
| 310 self.logger.info('%8d %8d %17s %17s %17s %17s %8d %8d %8d', |
| 311 datapath.id, |
| 312 in_port, |
| 313 match_args['eth_src'], match_args['ipv4_src'], |
| 314 match_args['eth_dst'], match_args['ipv4_dst'], |
| 315 out_port, |
| 316 match_args['ip_proto'], priority) |
| 317 |
| 318 actions = [parser.OFPActionOutput(out_port)] |
| 319 |
| 320 if buffer_id is not None: |
| 321 time.sleep(1) # Delay for FlowMod to ingress switch |
| 322 # This is to ensure that the client packet won't be forwarded |
| 323 # to next (transit) switch before it has already processed |
| 324 # the FlowMod sent to it. |
| 325 # See while loop in function _add_network_uni_flow() |
| 326 |
| 327 self.add_flow(datapath, priority, match, actions, buffer_id) |
| 328 |
| 329 def _parse_ip_packet_headers(self, pkt): |
232 # The following headers are available to all IPv4 packets | 330 # The following headers are available to all IPv4 packets |
233 pkt_ethernet = pkt.get_protocol(ethernet.ethernet) | 331 pkt_ethernet = pkt.get_protocol(ethernet.ethernet) |
234 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) | 332 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) |
235 eth_type = pkt_ethernet.ethertype | 333 eth_type = pkt_ethernet.ethertype |
236 eth_src = pkt_ethernet.src | 334 eth_src = pkt_ethernet.src |
237 ip_src = pkt_ipv4.src | 335 ip_src = pkt_ipv4.src |
238 eth_dst = pkt_ethernet.dst | 336 eth_dst = pkt_ethernet.dst |
239 ip_dst = pkt_ipv4.dst | 337 ip_dst = pkt_ipv4.dst |
240 ip_proto = pkt_ipv4.proto # 1 for ICMP, 6 for TCP, 17 for UDP | 338 ip_proto = pkt_ipv4.proto # 1 for ICMP, 6 for TCP, 17 for UDP |
241 | 339 |
242 match_args = { | 340 match_args = { |
243 'in_port': in_port, | |
244 'eth_type': eth_type, | 341 'eth_type': eth_type, |
245 'eth_src': eth_src, | 342 'eth_src': eth_src, |
246 'ipv4_src': ip_src, | 343 'ipv4_src': ip_src, |
247 'eth_dst': eth_dst, | 344 'eth_dst': eth_dst, |
248 'ipv4_dst': ip_dst, | 345 'ipv4_dst': ip_dst, |
249 'ip_proto': ip_proto | 346 'ip_proto': ip_proto |
250 } | 347 } |
251 | 348 |
252 if ip_proto == 1: | 349 if ip_proto == 1: |
253 # ICMP | 350 # ICMP |
254 # No other match argument needed | 351 # No other match argument needed |
255 priority = 200 | 352 pass |
256 | 353 |
257 elif ip_proto == 6: | 354 elif ip_proto == 6: |
258 # TCP | 355 # TCP |
259 # Add src and dst port | 356 # Add src and dst port |
260 priority = 400 | |
261 pkt_tcp = pkt.get_protocol(tcp.tcp) | 357 pkt_tcp = pkt.get_protocol(tcp.tcp) |
262 match_args['tcp_src'] = pkt_tcp.src_port | 358 match_args['tcp_src'] = pkt_tcp.src_port |
263 match_args['tcp_dst'] = pkt_tcp.dst_port | 359 match_args['tcp_dst'] = pkt_tcp.dst_port |
264 | 360 |
265 elif ip_proto == 17: | 361 elif ip_proto == 17: |
266 # UDP | 362 # UDP |
267 # Add src and dst port | 363 # Add src and dst port |
268 priority = 300 | |
269 pkt_udp = pkt.get_protocol(udp.udp) | 364 pkt_udp = pkt.get_protocol(udp.udp) |
270 match_args['udp_src'] = pkt_udp.src_port | 365 match_args['udp_src'] = pkt_udp.src_port |
271 match_args['udp_dst'] = pkt_udp.dst_port | 366 match_args['udp_dst'] = pkt_udp.dst_port |
272 | 367 |
273 else: | 368 else: |
274 priority = 100 # Priority for everything else | 369 # Everything else |
275 | 370 pass |
276 # https://docs.python.org/2/tutorial/controlflow.html#unpacking-argument
-lists | 371 |
277 match = parser.OFPMatch(**match_args) | 372 return match_args |
278 self.logger.info('%8d %8d %17s %17s %17s %17s %8d %8d %8d', | |
279 datapath.id, | |
280 in_port, eth_src, ip_src, | |
281 eth_dst, ip_dst, out_port, | |
282 ip_proto, priority) | |
283 | |
284 actions = [parser.OFPActionOutput(out_port)] | |
285 self.add_flow(datapath, priority, match, actions, buffer_id) | |
286 | 373 |
287 # Taken from Ryu book page 88: | 374 # Taken from Ryu book page 88: |
288 def _send_packet(self, datapath, port, pkt): | 375 def _send_packet(self, datapath, port, pkt): |
289 ofproto = datapath.ofproto | 376 ofproto = datapath.ofproto |
290 parser = datapath.ofproto_parser | 377 parser = datapath.ofproto_parser |
291 pkt.serialize() | 378 pkt.serialize() |
292 self.logger.debug("packet-out %s" % (pkt,)) | 379 self.logger.debug("packet-out %s" % (pkt,)) |
293 data = pkt.data | 380 data = pkt.data |
294 actions = [parser.OFPActionOutput(port=port)] | 381 actions = [parser.OFPActionOutput(port=port)] |
295 out = parser.OFPPacketOut(datapath=datapath, | 382 out = parser.OFPPacketOut(datapath=datapath, |
(...skipping 19 matching lines...) Expand all Loading... |
315 pkt_out.add_protocol(ethernet.ethernet( | 402 pkt_out.add_protocol(ethernet.ethernet( |
316 ethertype=pkt_in_ether.ethertype, | 403 ethertype=pkt_in_ether.ethertype, |
317 dst=pkt_in_ether.src, src=src_mac)) | 404 dst=pkt_in_ether.src, src=src_mac)) |
318 pkt_out.add_protocol(arp.arp(opcode=arp.ARP_REPLY, | 405 pkt_out.add_protocol(arp.arp(opcode=arp.ARP_REPLY, |
319 src_mac=src_mac, | 406 src_mac=src_mac, |
320 src_ip=pkt_in_arp.dst_ip, | 407 src_ip=pkt_in_arp.dst_ip, |
321 dst_mac=pkt_in_arp.src_mac, | 408 dst_mac=pkt_in_arp.src_mac, |
322 dst_ip=pkt_in_arp.src_ip)) | 409 dst_ip=pkt_in_arp.src_ip)) |
323 self._send_packet(datapath, in_port, pkt_out) | 410 self._send_packet(datapath, in_port, pkt_out) |
324 | 411 |
325 def _handle_new_host(self, datapath, switch_port, host_mac): | 412 def _handle_new_host(self, datapath, switch_port, host_mac, host_ip): |
| 413 # Update fib 2017-11-18 |
| 414 switches_with_hosts = [] |
| 415 for ip, params in self.hosts.items(): |
| 416 if params['switch'] not in switches_with_hosts: |
| 417 switches_with_hosts.append(params['switch']) |
| 418 for switch in switches_with_hosts: |
| 419 if switch == datapath.id: |
| 420 # Same switch, no need to add to FIB |
| 421 continue |
| 422 if (switch, datapath.id) not in self.fib: |
| 423 self.fib[(switch, datapath.id)] = self._find_best_path(switch, d
atapath.id) |
| 424 print self.fib |
| 425 if (datapath.id, switch) not in self.fib: |
| 426 self.fib[(datapath.id, switch)] = self._find_best_path(datapath.
id, switch) |
| 427 print self.fib |
| 428 |
| 429 # Add new host to host dict |
| 430 self.hosts[host_ip] = {'mac': host_mac, |
| 431 'switch': datapath.id, |
| 432 'port': switch_port} |
| 433 |
| 434 print "[DISCOVERED NEW HOST]", host_ip, self.hosts[host_ip] |
| 435 |
| 436 # Add flow on this switch, point to NEW HOST: |
326 parser = datapath.ofproto_parser | 437 parser = datapath.ofproto_parser |
327 match = parser.OFPMatch(eth_dst=host_mac) | 438 match = parser.OFPMatch(eth_dst=host_mac) |
328 actions = [parser.OFPActionOutput(switch_port)] | 439 actions = [parser.OFPActionOutput(switch_port)] |
329 self.add_flow(datapath, 50, match, actions) | 440 self.add_flow(datapath, 50, match, actions) |
330 | 441 |
331 @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) | 442 @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) |
332 def _packet_in_handler(self, ev): | 443 def _packet_in_handler(self, ev): |
333 # If you hit this you might want to increase | 444 # If you hit this you might want to increase |
334 # the "miss_send_length" of your switch | 445 # the "miss_send_length" of your switch |
335 if ev.msg.msg_len < ev.msg.total_len: | 446 if ev.msg.msg_len < ev.msg.total_len: |
336 self.logger.debug("packet truncated: only %s of %s bytes", | 447 self.logger.debug("packet truncated: only %s of %s bytes", |
337 ev.msg.msg_len, ev.msg.total_len) | 448 ev.msg.msg_len, ev.msg.total_len) |
338 | 449 |
339 msg = ev.msg | 450 msg = ev.msg |
340 datapath = msg.datapath | 451 datapath = msg.datapath |
341 in_port = msg.match['in_port'] | 452 in_port = msg.match['in_port'] |
342 | 453 |
343 pkt = packet.Packet(msg.data) | 454 pkt = packet.Packet(msg.data) |
344 eth = pkt.get_protocols(ethernet.ethernet)[0] | 455 eth = pkt.get_protocols(ethernet.ethernet)[0] |
345 | 456 |
346 if eth.ethertype == ether_types.ETH_TYPE_LLDP: | 457 if eth.ethertype == ether_types.ETH_TYPE_LLDP: |
347 # ignore lldp packet | 458 # ignore lldp packet |
348 return | 459 return |
349 | 460 |
350 print 'PACKET IN >>', datapath.id, in_port | 461 #print 'PACKET IN >>', datapath.id, in_port |
351 self.logger.debug("packet-in %s" % (pkt,)) | 462 self.logger.debug("packet-in %s" % (pkt,)) |
352 | 463 |
353 dpid = datapath.id | 464 dpid = datapath.id |
354 | 465 |
355 # self.hosts = {'10.0.0.1': {'mac': '00:00:00:00:00:01', 'switch': 11, '
port': 1}, | 466 # self.hosts = {'10.0.0.1': {'mac': '00:00:00:00:00:01', 'switch': 11, '
port': 1}, |
356 # '10.0.0.2': {'mac': '00:00:00:00:00:02', 'switch': 13, 'p
ort': 2}} | 467 # '10.0.0.2': {'mac': '00:00:00:00:00:02', 'switch': 13, 'p
ort': 2}} |
357 | 468 |
358 # Handle ARP Request packets: | 469 # Handle ARP Request packets: |
359 pkt_arp = pkt.get_protocol(arp.arp) | 470 pkt_arp = pkt.get_protocol(arp.arp) |
360 if pkt_arp: | 471 if pkt_arp: |
361 # Inspired by Ryu book page 88 | 472 # Inspired by Ryu book page 88 |
362 if pkt_arp: | 473 if pkt_arp: |
363 if pkt_arp.opcode != arp.ARP_REQUEST: | 474 if pkt_arp.opcode != arp.ARP_REQUEST: |
364 print 'Expected an ARP Request but this is not it.' | 475 print 'Expected an ARP Request but this is not it.' |
365 return | 476 return |
366 | 477 |
367 # Learn new source host: | 478 # Learn new source host: |
368 if pkt_arp.src_ip not in self.hosts: | 479 if pkt_arp.src_ip not in self.hosts: |
369 self.hosts[pkt_arp.src_ip] = { | 480 self._handle_new_host(datapath, in_port, eth.src, pkt_arp.sr
c_ip) |
370 'mac': eth.src, 'switch': dpid, 'port': in_port} | |
371 print "DISCOVERED NEW HOST", pkt_arp.src_ip, self.hosts[pkt_
arp.src_ip] | |
372 # Add flow on this switch, point to NEW HOST: | |
373 self._handle_new_host(datapath, in_port, eth.src) | |
374 return | 481 return |
375 | 482 |
376 # Reply to ARP request: | 483 # Reply to ARP request: |
377 if pkt_arp.dst_ip in self.hosts: | 484 if pkt_arp.dst_ip in self.hosts: |
378 self._handle_arp_request(datapath, in_port, pkt) | 485 self._handle_arp_request(datapath, in_port, pkt) |
379 return | 486 return |
380 | 487 |
381 # Handle IPv4 packets: | 488 # Handle IPv4 packets: |
382 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) | 489 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) |
383 if not pkt_ipv4: | 490 if not pkt_ipv4: |
384 print 'NOT an IPv4 Packet:' | 491 print 'NOT an IPv4 Packet:' |
385 self.logger.info("TABLE MISS: switch %s, packet-in %s", dpid, (pkt,)
) | 492 self.logger.info("TABLE MISS: switch %s, packet-in %s", dpid, (pkt,)
) |
386 return | 493 return |
387 else: | 494 else: |
| 495 # Block all similar future packets for 1 second: |
| 496 # self._deny_flow(datapath, in_port, pkt) |
| 497 |
388 dst_switch = self.hosts[pkt_ipv4.dst]['switch'] | 498 dst_switch = self.hosts[pkt_ipv4.dst]['switch'] |
389 # Find best path between (src_switch, dst_switch) | 499 # Find best path between (src_switch, dst_switch) |
390 best_path = self._find_best_path(dpid, dst_switch) | 500 # best_path = self._find_best_path(dpid, dst_switch) |
| 501 print datapath.id, in_port, pkt_ipv4.src, pkt_ipv4.dst |
| 502 best_path = self._fib_lookup(dpid, dst_switch) |
391 | 503 |
392 # Installing flows from A >> Z | 504 # Installing flows from A >> Z |
393 self._add_network_uni_flow(best_path, pkt, msg.buffer_id) | 505 self._add_network_uni_flow(best_path, pkt, msg.buffer_id) |
LEFT | RIGHT |