OLD | NEW |
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 |
| 15 from ryu.lib.packet import icmp |
| 16 from ryu.lib.packet import udp |
| 17 from ryu.lib.packet import tcp |
14 from ryu.lib import hub | 18 from ryu.lib import hub |
15 import time | 19 import time |
16 from my_http_client import MyHTTPClient | 20 from my_http_client import MyHTTPClient |
17 | 21 |
18 | 22 |
19 class CPM13(simple_switch_13.SimpleSwitch13): | 23 class CPM13(simple_switch_13.SimpleSwitch13): |
20 "CPM module for topology discovery and SPF" | 24 "CPM module for topology discovery and SPF" |
21 | 25 |
22 OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] | 26 OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] |
23 | 27 |
24 def __init__(self, *args, **kwargs): | 28 def __init__(self, *args, **kwargs): |
25 super(CPM13, self).__init__(*args, **kwargs) | 29 super(CPM13, self).__init__(*args, **kwargs) |
| 30 self.fib = {} # 2017-11-18 |
26 self.topo_links = {} | 31 self.topo_links = {} |
27 self.switches = {} | 32 self.switches = {} |
28 self.neighbor_ports = {} # Temp 2017-10-02 | 33 self.neighbor_ports = {} # Temp 2017-10-02 |
29 self.port_metric = {} # 2017-10-16 | 34 self.port_metric = {} # 2017-10-16 |
30 self.hosts = {} | 35 self.hosts = {} |
31 self.hosts_temp_ban = {} # 2017-10-17 | |
32 self.sample_interval = 30 # How often to query the DM | 36 self.sample_interval = 30 # How often to query the DM |
33 self.ref_bw = 100000000.0 # 100 Mbps; Reference Bandwidth in Bps | 37 self.ref_bw = 100000000.0 # 100 Mbps; Reference Bandwidth in Bps |
34 self.ref_hc = 30.0 # 30 Hops; Reference Hop Count | 38 self.ref_hc = 30.0 # 30 Hops; Reference Hop Count |
35 # Connect with DM: | 39 # Connect with DM: |
36 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?') |
37 self.port_counters_snapshot = {'object_index': 1000, 'switches': []} | 41 self.port_counters_snapshot = {'object_index': 1000, 'switches': []} |
38 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')· |
39 | 47 |
40 def _get_dm_data(self): | 48 def _get_dm_data(self): |
41 while True: | 49 while True: |
42 # Ask for DM data every self.sample_interval seconds: | 50 # Ask for DM data every self.sample_interval seconds: |
43 hub.sleep(self.sample_interval) | 51 hub.sleep(self.sample_interval) |
44 #body = self.dm_server.get_data_from_dm('port_counters', 1) | 52 #body = self.dm_server.get_data_from_dm('port_counters', 1) |
45 params_dict = {'module': 'nfm', 'row': 1, 'table': 'port_counters'} | 53 params_dict = {'module': 'nfm', 'row': 1, 'table': 'port_counters'} |
46 body = self.dm_server.send_GET(params_dict) | 54 body = self.dm_server.send_GET(params_dict) |
47 #print body | 55 #print body |
48 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 |
49 print 'No NFM monitoring data received from DM' | 57 print 'No NFM monitoring data received from DM' |
50 continue | 58 continue |
51 # print body | 59 # print body |
52 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)] |
53 | 74 |
54 def _calculate_link_metric(self, data_dict): | 75 def _calculate_link_metric(self, data_dict): |
55 | 76 |
56 for switch in data_dict['switches']: | 77 for switch in data_dict['switches']: |
57 # each element is a dictionary as well | 78 # each element is a dictionary as well |
58 for port_counter in switch['port_counters']: | 79 for port_counter in switch['port_counters']: |
59 # each element is a dictionary as well | 80 # each element is a dictionary as well |
60 | 81 |
61 tx_metric = float(port_counter['tx_bw']) / self.ref_bw | 82 tx_metric = float(port_counter['tx_bw']) / self.ref_bw |
62 | 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) |
| 87 |
63 self.port_metric[(switch['switch_dpid'], | 88 self.port_metric[(switch['switch_dpid'], |
64 port_counter['port'])] = tx_metric | 89 port_counter['port'])] = tx_metric |
65 | 90 |
66 #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 |
67 | 92 |
68 def _calculate_hop_count(self, paths): | 93 def _calculate_hop_count(self, paths): |
69 paths_dict = {} | 94 paths_dict = {} |
70 | 95 |
71 for path in paths: | 96 for path in paths: |
72 hop_count = len(path) | 97 hop_count = len(path) |
(...skipping 12 matching lines...) Expand all Loading... |
85 # v is the current switch dpid | 110 # v is the current switch dpid |
86 # 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 |
87 (tx_port, _) = self.neighbor_ports[(v, path[k + 1])] | 112 (tx_port, _) = self.neighbor_ports[(v, path[k + 1])] |
88 # Find busiest port in path: | 113 # Find busiest port in path: |
89 if worst_metric < self.port_metric[(v, tx_port)]: | 114 if worst_metric < self.port_metric[(v, tx_port)]: |
90 worst_metric = self.port_metric[(v, tx_port)] | 115 worst_metric = self.port_metric[(v, tx_port)] |
91 paths_dict[path] = worst_metric | 116 paths_dict[path] = worst_metric |
92 | 117 |
93 return paths_dict | 118 return paths_dict |
94 | 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 |
95 @set_ev_cls(event.EventSwitchEnter) | 137 @set_ev_cls(event.EventSwitchEnter) |
96 def _handle_new_switch(self, ev): | 138 def _handle_new_switch(self, ev): |
97 # Gives the ryu.controller.controller.Datapath object of the switch: | 139 # Gives the ryu.controller.controller.Datapath object of the switch: |
98 # print ev.switch.dp | 140 # print ev.switch.dp |
99 new_switch = ev.switch.dp | 141 new_switch = ev.switch.dp |
100 self.switches[new_switch.id] = ev.switch.dp | 142 self.switches[new_switch.id] = ev.switch.dp |
101 self.topo_links[new_switch.id] = [] | 143 self.topo_links[new_switch.id] = [] |
102 | 144 |
103 @set_ev_cls(event.EventLinkAdd) | 145 @set_ev_cls(event.EventLinkAdd) |
104 def _handle_new_link(self, ev): | 146 def _handle_new_link(self, ev): |
(...skipping 19 matching lines...) Expand all Loading... |
124 | 166 |
125 # Temp 2017-10-02 | 167 # Temp 2017-10-02 |
126 self.neighbor_ports[(switchA.dpid, switchB.dpid)] = ( | 168 self.neighbor_ports[(switchA.dpid, switchB.dpid)] = ( |
127 switchA.port_no, switchB.port_no) | 169 switchA.port_no, switchB.port_no) |
128 # print 'Ports', self.neighbor_ports | 170 # print 'Ports', self.neighbor_ports |
129 | 171 |
130 # 2017-10-16 | 172 # 2017-10-16 |
131 self.port_metric[(switchA.dpid, switchA.port_no)] = 0 | 173 self.port_metric[(switchA.dpid, switchA.port_no)] = 0 |
132 #print 'Port Metric', (switchA.dpid, switchA.port_no), 'set to', 0 | 174 #print 'Port Metric', (switchA.dpid, switchA.port_no), 'set to', 0 |
133 | 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 |
| 187 |
134 # https://www.python.org/doc/essays/graphs/ | 188 # https://www.python.org/doc/essays/graphs/ |
135 def _find_all_paths(self, neighbors, start, end, path=[]): | 189 def _find_all_paths(self, neighbors, start, end, path=[]): |
136 path = path + [start] | 190 path = path + [start] |
137 if start == end: | 191 if start == end: |
138 return [path] | 192 return [path] |
139 if start not in neighbors.keys(): | 193 if start not in neighbors.keys(): |
140 return [] | 194 return [] |
141 paths = [] | 195 paths = [] |
142 for node in neighbors[start]: | 196 for node in neighbors[start]: |
143 if node not in path: | 197 if node not in path: |
(...skipping 10 matching lines...) Expand all Loading... |
154 # same switch dpid i.e. the hosts are connected on the same switch | 208 # same switch dpid i.e. the hosts are connected on the same switch |
155 # So skip path calculation and return None | 209 # So skip path calculation and return None |
156 return None | 210 return None |
157 else: | 211 else: |
158 paths = self._find_all_paths(neighbors, start, end) | 212 paths = self._find_all_paths(neighbors, start, end) |
159 temp_paths = [] | 213 temp_paths = [] |
160 for j in paths: | 214 for j in paths: |
161 temp_paths.append(tuple(j)) | 215 temp_paths.append(tuple(j)) |
162 return temp_paths | 216 return temp_paths |
163 | 217 |
164 def _add_end_to_end_flow(self, path, src_host_ip, dst_host_ip): | 218 def _find_best_path(self, src_switch, dst_switch): |
165 # input argument "path" is a list of switches; each item is a dpid | 219 all_paths = self._find_paths(self.topo_links, src_switch, dst_switch) |
| 220 if all_paths is None: |
| 221 # Hosts are on same switch. I don't need to build |
| 222 # a flow. It was already done by _handle_new_host() |
| 223 return |
| 224 |
| 225 hop_count = self._calculate_hop_count(all_paths) |
| 226 nfm_metric = self._calculate_path_metric(all_paths) |
| 227 |
| 228 # Weights for composite metric: |
| 229 w1 = 0.1 # Weight for Hop Count |
| 230 w2 = 0.9 # Weight for NFM metric |
| 231 best_metric = 100000000000 # Just an Arbitrary high value. |
| 232 # Lowest metric means best metric! (0 is the best possible) |
| 233 best_path = [] |
| 234 for path in all_paths: |
| 235 composite_metric = w1 * hop_count[path] |
| 236 composite_metric += w2 * nfm_metric[path] |
| 237 if composite_metric < best_metric: |
| 238 best_metric = composite_metric |
| 239 best_path = path |
| 240 |
| 241 #print 'BEST PATH between', (src_switch, dst_switch), 'is', best_path |
| 242 #print 'with Composite metric', best_metric |
| 243 |
| 244 #return list(best_path) |
| 245 return best_path |
| 246 |
| 247 def _add_network_uni_flow(self, path, pkt, buffer_id=None): |
| 248 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) |
| 249 ip_src = pkt_ipv4.src |
| 250 |
| 251 # input argument "path" is a tuple of switches; each item is a dpid |
166 # 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 |
167 if len(path) == 1: | 254 if len(path) == 1: |
168 print 'Hosts are on same switch', path[0] | 255 print 'Hosts are on same switch', path[0] |
169 return | 256 return |
170 | 257 |
171 hostA = self.hosts[src_host_ip] # src_host | 258 # Install flows from A >> Z in the reverse order, i.e. |
172 hostZ = self.hosts[dst_host_ip] # dst_host | 259 # from End to Start. |
| 260 path = path[::-1] # New tuple with reversed order of items |
173 | 261 |
| 262 ip_dst = pkt_ipv4.dst |
| 263 hostZ = self.hosts[ip_dst] # dst host |
| 264 out_port = hostZ['port'] |
| 265 |
| 266 for k, v in enumerate(path[:-1]): |
| 267 (in_port, new_out_port) = self.neighbor_ports[( |
| 268 v, path[k + 1])] |
| 269 |
| 270 switch = self.switches[v] |
| 271 |
| 272 self._add_switch_uni_flow( |
| 273 switch, in_port, out_port, pkt) |
| 274 |
| 275 out_port = new_out_port |
| 276 |
| 277 ip_src = pkt_ipv4.src |
| 278 hostA = self.hosts[ip_src] # src host |
174 in_port = hostA['port'] | 279 in_port = hostA['port'] |
175 | 280 |
176 # Installing flows from A >> Z | 281 self._add_switch_uni_flow(self.switches[path[-1]], |
177 # I am ommiting last switch as _handle_new_host() | 282 in_port, out_port, pkt, buffer_id) |
178 # will add a flow for hostZ there. | |
179 for k, v in enumerate(path[:-1]): | |
180 switchX_dpid = v | |
181 switchY_dpid = path[k + 1] # Next switch in path | |
182 (switchX_out_port, switchY_in_port) = self.neighbor_ports[( | |
183 switchX_dpid, switchY_dpid)] | |
184 | 283 |
185 switchX_datapath = self.switches[v] | 284 def _add_switch_uni_flow(self, datapath, in_port, out_port, pkt, buffer_id=N
one): |
| 285 parser = datapath.ofproto_parser |
186 | 286 |
187 self._add_switch_flow( | 287 match_args = self._parse_ip_packet_headers(pkt) |
188 switchX_datapath, in_port, hostA['mac'], | |
189 hostZ['mac'], switchX_out_port) | |
190 | 288 |
191 in_port = switchY_in_port | 289 match_args['in_port'] = in_port |
192 | 290 |
193 def _add_switch_flow(self, datapath, in_port, eth_src, eth_dst, out_port): | 291 if match_args['ip_proto'] == 1: |
194 parser = datapath.ofproto_parser | 292 # ICMP |
195 priority = 100 | 293 priority = 200 |
196 match = parser.OFPMatch( | 294 |
197 in_port=in_port, | 295 elif match_args['ip_proto'] == 6: |
198 eth_src=eth_src, | 296 # TCP |
199 eth_dst=eth_dst | 297 # Add src and dst port |
200 ) | 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 |
201 actions = [parser.OFPActionOutput(out_port)] | 318 actions = [parser.OFPActionOutput(out_port)] |
202 self.logger.info('%8d %8d %17s %17s %8d %8d', datapath.id, | 319 |
203 in_port, eth_src, eth_dst, out_port, priority) | 320 if buffer_id is not None: |
204 self.add_flow(datapath, priority, match, actions) | 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): |
| 330 # The following headers are available to all IPv4 packets |
| 331 pkt_ethernet = pkt.get_protocol(ethernet.ethernet) |
| 332 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) |
| 333 eth_type = pkt_ethernet.ethertype |
| 334 eth_src = pkt_ethernet.src |
| 335 ip_src = pkt_ipv4.src |
| 336 eth_dst = pkt_ethernet.dst |
| 337 ip_dst = pkt_ipv4.dst |
| 338 ip_proto = pkt_ipv4.proto # 1 for ICMP, 6 for TCP, 17 for UDP |
| 339 |
| 340 match_args = { |
| 341 'eth_type': eth_type, |
| 342 'eth_src': eth_src, |
| 343 'ipv4_src': ip_src, |
| 344 'eth_dst': eth_dst, |
| 345 'ipv4_dst': ip_dst, |
| 346 'ip_proto': ip_proto |
| 347 } |
| 348 |
| 349 if ip_proto == 1: |
| 350 # ICMP |
| 351 # No other match argument needed |
| 352 pass |
| 353 |
| 354 elif ip_proto == 6: |
| 355 # TCP |
| 356 # Add src and dst port |
| 357 pkt_tcp = pkt.get_protocol(tcp.tcp) |
| 358 match_args['tcp_src'] = pkt_tcp.src_port |
| 359 match_args['tcp_dst'] = pkt_tcp.dst_port |
| 360 |
| 361 elif ip_proto == 17: |
| 362 # UDP |
| 363 # Add src and dst port |
| 364 pkt_udp = pkt.get_protocol(udp.udp) |
| 365 match_args['udp_src'] = pkt_udp.src_port |
| 366 match_args['udp_dst'] = pkt_udp.dst_port |
| 367 |
| 368 else: |
| 369 # Everything else |
| 370 pass |
| 371 |
| 372 return match_args |
205 | 373 |
206 # Taken from Ryu book page 88: | 374 # Taken from Ryu book page 88: |
207 def _send_packet(self, datapath, port, pkt): | 375 def _send_packet(self, datapath, port, pkt): |
208 ofproto = datapath.ofproto | 376 ofproto = datapath.ofproto |
209 parser = datapath.ofproto_parser | 377 parser = datapath.ofproto_parser |
210 pkt.serialize() | 378 pkt.serialize() |
211 self.logger.debug("packet-out %s" % (pkt,)) | 379 self.logger.debug("packet-out %s" % (pkt,)) |
212 data = pkt.data | 380 data = pkt.data |
213 actions = [parser.OFPActionOutput(port=port)] | 381 actions = [parser.OFPActionOutput(port=port)] |
214 out = parser.OFPPacketOut(datapath=datapath, | 382 out = parser.OFPPacketOut(datapath=datapath, |
215 buffer_id=ofproto.OFP_NO_BUFFER, | 383 buffer_id=ofproto.OFP_NO_BUFFER, |
216 in_port=ofproto.OFPP_CONTROLLER, | 384 in_port=ofproto.OFPP_CONTROLLER, |
217 actions=actions, data=data) | 385 actions=actions, data=data) |
218 datapath.send_msg(out) | 386 datapath.send_msg(out) |
219 | 387 |
220 # Inspired by Ryu book page 88: | 388 # Inspired by Ryu book page 88: |
221 def _send_arp_reply(self, datapath, port, pkt_ethernet, pkt_arp, src_mac, sr
c_ipv4): | 389 def _handle_arp_request(self, datapath, in_port, pkt_in): |
222 if pkt_arp.opcode != arp.ARP_REQUEST: | 390 pkt_in_ether = pkt_in.get_protocol(ethernet.ethernet) |
| 391 pkt_in_arp = pkt_in.get_protocol(arp.arp) |
| 392 |
| 393 if pkt_in_arp.opcode != arp.ARP_REQUEST: |
223 return | 394 return |
224 pkt = packet.Packet() | 395 elif pkt_in_arp.dst_ip not in self.hosts: |
225 pkt.add_protocol(ethernet.ethernet( | 396 print 'Unknown host. Cannot send ARP Reply.' |
226 ethertype=pkt_ethernet.ethertype, | 397 return |
227 dst=pkt_ethernet.src, src=src_mac)) | |
228 pkt.add_protocol(arp.arp(opcode=arp.ARP_REPLY, src_mac=src_mac, | |
229 src_ip=src_ipv4, dst_mac=pkt_arp.src_mac, | |
230 dst_ip=pkt_arp.src_ip)) | |
231 self._send_packet(datapath, port, pkt) | |
232 | 398 |
233 def _handle_arp_request(self, datapath, port, pkt_ethernet, pkt_arp, src_mac
, src_ipv4): | 399 src_mac = self.hosts[pkt_in_arp.dst_ip]['mac'] |
234 ofproto = datapath.ofproto | |
235 parser = datapath.ofproto_parser | |
236 match = parser.OFPMatch(in_port=port, eth_src=pkt_ethernet.src, eth_dst=
'FF:FF:FF:FF:FF:FF') | |
237 #match = parser.OFPMatch(in_port=port) | |
238 inst = [parser.OFPInstructionActions( | |
239 ofproto.OFPIT_CLEAR_ACTIONS, [])] | |
240 mod = parser.OFPFlowMod(datapath=datapath, priority=65535, | |
241 match=match, instructions=inst, hard_timeout=60) | |
242 datapath.send_msg(mod) # FIXME: I don't get any hits | |
243 self._send_arp_reply(datapath, port, pkt_ethernet, pkt_arp, src_mac, src
_ipv4) | |
244 | 400 |
245 def _handle_new_host(self, datapath, switch_port, host_mac): | 401 pkt_out = packet.Packet() |
246 ofproto = datapath.ofproto | 402 pkt_out.add_protocol(ethernet.ethernet( |
247 parser = datapath.ofproto_parser | 403 ethertype=pkt_in_ether.ethertype, |
248 match = parser.OFPMatch(in_port=switch_port, eth_src=host_mac, eth_dst='
FF:FF:FF:FF:FF:FF') | 404 dst=pkt_in_ether.src, src=src_mac)) |
249 inst = [parser.OFPInstructionActions( | 405 pkt_out.add_protocol(arp.arp(opcode=arp.ARP_REPLY, |
250 ofproto.OFPIT_CLEAR_ACTIONS, [])] | 406 src_mac=src_mac, |
251 mod = parser.OFPFlowMod(datapath=datapath, priority=65535, | 407 src_ip=pkt_in_arp.dst_ip, |
252 match=match, instructions=inst, hard_timeout=5) | 408 dst_mac=pkt_in_arp.src_mac, |
253 datapath.send_msg(mod) | 409 dst_ip=pkt_in_arp.src_ip)) |
| 410 self._send_packet(datapath, in_port, pkt_out) |
254 | 411 |
| 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: |
255 parser = datapath.ofproto_parser | 437 parser = datapath.ofproto_parser |
256 match = parser.OFPMatch(eth_dst=host_mac) | 438 match = parser.OFPMatch(eth_dst=host_mac) |
257 actions = [parser.OFPActionOutput(switch_port)] | 439 actions = [parser.OFPActionOutput(switch_port)] |
258 self.add_flow(datapath, 50, match, actions) | 440 self.add_flow(datapath, 50, match, actions) |
259 | 441 |
260 @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) | 442 @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) |
261 def _packet_in_handler(self, ev): | 443 def _packet_in_handler(self, ev): |
262 # If you hit this you might want to increase | 444 # If you hit this you might want to increase |
263 # the "miss_send_length" of your switch | 445 # the "miss_send_length" of your switch |
264 if ev.msg.msg_len < ev.msg.total_len: | 446 if ev.msg.msg_len < ev.msg.total_len: |
265 self.logger.debug("packet truncated: only %s of %s bytes", | 447 self.logger.debug("packet truncated: only %s of %s bytes", |
266 ev.msg.msg_len, ev.msg.total_len) | 448 ev.msg.msg_len, ev.msg.total_len) |
| 449 |
267 msg = ev.msg | 450 msg = ev.msg |
268 datapath = msg.datapath | 451 datapath = msg.datapath |
269 ofproto = datapath.ofproto | |
270 parser = datapath.ofproto_parser | |
271 in_port = msg.match['in_port'] | 452 in_port = msg.match['in_port'] |
272 | 453 |
273 pkt = packet.Packet(msg.data) | 454 pkt = packet.Packet(msg.data) |
274 eth = pkt.get_protocols(ethernet.ethernet)[0] | 455 eth = pkt.get_protocols(ethernet.ethernet)[0] |
275 | 456 |
276 if eth.ethertype == ether_types.ETH_TYPE_LLDP: | 457 if eth.ethertype == ether_types.ETH_TYPE_LLDP: |
277 # ignore lldp packet | 458 # ignore lldp packet |
278 return | 459 return |
279 | 460 |
280 print 'PACKET IN >>', datapath.id, in_port | 461 #print 'PACKET IN >>', datapath.id, in_port |
281 self.logger.debug("packet-in %s" % (pkt,)) | 462 self.logger.debug("packet-in %s" % (pkt,)) |
282 | 463 |
283 dst = eth.dst | |
284 src = eth.src | |
285 | |
286 dpid = datapath.id | 464 dpid = datapath.id |
287 | 465 |
288 # 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}, |
289 # '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}} |
290 | 468 |
291 # Check if this is a Layer2 broadcast: | 469 # Handle ARP Request packets: |
292 if eth.dst == 'ff:ff:ff:ff:ff:ff': | 470 pkt_arp = pkt.get_protocol(arp.arp) |
293 # Check if this is an ARP Request. | 471 if pkt_arp: |
294 # ARP Responses are unicast. | |
295 # Inspired by Ryu book page 88 | 472 # Inspired by Ryu book page 88 |
296 pkt_arp = pkt.get_protocol(arp.arp) | |
297 if pkt_arp: | 473 if pkt_arp: |
298 if pkt_arp.opcode != arp.ARP_REQUEST: | 474 if pkt_arp.opcode != arp.ARP_REQUEST: |
299 print 'Expected an ARP Request but this is not it.' | 475 print 'Expected an ARP Request but this is not it.' |
300 return | 476 return |
| 477 |
301 # Learn new source host: | 478 # Learn new source host: |
302 if pkt_arp.src_ip not in self.hosts: | 479 if pkt_arp.src_ip not in self.hosts: |
303 self.hosts[pkt_arp.src_ip] = { | 480 self._handle_new_host(datapath, in_port, eth.src, pkt_arp.sr
c_ip) |
304 'mac': src, 'switch': dpid, 'port': in_port} | |
305 print "DISCOVERED NEW HOST", pkt_arp.src_ip, self.hosts[pkt_
arp.src_ip] | |
306 # Add flow on this switch, point to NEW HOST: | |
307 self._handle_new_host(datapath, in_port, src) | |
308 return | 481 return |
309 | 482 |
310 # Run SPF and create end-to-end path | 483 # Reply to ARP request: |
311 if pkt_arp.dst_ip in self.hosts: | 484 if pkt_arp.dst_ip in self.hosts: |
312 dst_switch = self.hosts[pkt_arp.dst_ip]['switch'] | 485 self._handle_arp_request(datapath, in_port, pkt) |
313 | |
314 self._handle_arp_request( | |
315 datapath, in_port, eth, pkt_arp, | |
316 self.hosts[pkt_arp.dst_ip]['mac'], pkt_arp.dst_ip) | |
317 | |
318 current_time = int(time.time()) # 2017-10-17 | |
319 if pkt_arp.src_ip not in self.hosts_temp_ban: # 2017-10-17 | |
320 self.hosts_temp_ban[pkt_arp.src_ip] = current_time | |
321 print "BANNED HOST >>", pkt_arp.src_ip, "FROM ARP REQ FO
R 10sec" | |
322 elif current_time - self.hosts_temp_ban[pkt_arp.src_ip] < 10
: | |
323 print "HOST", pkt_arp.src_ip, "BANNED FROM ARP" | |
324 return | |
325 | |
326 | |
327 all_paths = self._find_paths(self.topo_links, dpid, dst_swit
ch) | |
328 if all_paths is None: | |
329 # Hosts are on same switch. I don't need to build | |
330 # a flow. It was already done by _handle_new_host() | |
331 return | |
332 | |
333 hop_count = self._calculate_hop_count(all_paths) | |
334 nfm_metric = self._calculate_path_metric(all_paths) | |
335 | |
336 # Weights for composite metric: | |
337 w1 = 0.1 # Weight for Hop Count | |
338 w2 = 0.9 # Weight for NFM metric | |
339 best_metric = 100000000000 # Just an Arbitrary high value. | |
340 # Lowest metric means best metric! (0 is the best possible) | |
341 best_path = [] | |
342 for path in all_paths: | |
343 composite_metric = w1 * hop_count[path] | |
344 composite_metric += w2 * nfm_metric[path] | |
345 if composite_metric < best_metric: | |
346 best_metric = composite_metric | |
347 best_path = path | |
348 | |
349 print 'BEST PATH between', (dpid, dst_switch), 'is', best_pa
th | |
350 print 'with Composite metric', best_metric | |
351 | |
352 | |
353 best_path = list(best_path) # Change to list, 2017-10-19 | |
354 # Installing flows from A >> Z | |
355 self._add_end_to_end_flow( | |
356 best_path, pkt_arp.src_ip, pkt_arp.dst_ip) | |
357 return | 486 return |
358 | 487 |
359 self.logger.info("TABLE MISS: switch %s, packet-in %s", dpid, (pkt,)) | 488 # Handle IPv4 packets: |
| 489 pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) |
| 490 if not pkt_ipv4: |
| 491 print 'NOT an IPv4 Packet:' |
| 492 self.logger.info("TABLE MISS: switch %s, packet-in %s", dpid, (pkt,)
) |
| 493 return |
| 494 else: |
| 495 # Block all similar future packets for 1 second: |
| 496 # self._deny_flow(datapath, in_port, pkt) |
| 497 |
| 498 dst_switch = self.hosts[pkt_ipv4.dst]['switch'] |
| 499 # Find best path between (src_switch, 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) |
| 503 |
| 504 # Installing flows from A >> Z |
| 505 self._add_network_uni_flow(best_path, pkt, msg.buffer_id) |
OLD | NEW |