diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba0430d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ \ No newline at end of file diff --git a/lab0/README.md b/lab0/README.md new file mode 100644 index 0000000..18924d2 --- /dev/null +++ b/lab0/README.md @@ -0,0 +1,18 @@ +# Warmm up Exercise + +## Analyzing h1 and h3 + +Run iperf h1 - h3 tcp for 20 seconds and analyzing the latency + +```sh +iperf -c 10.0.0.3 -p 5566 -t 20 -P 20 +``` +We have th following results +![mtr tcp report 20](images/latency.png) + +The throughput using Iperf `-P` option +![Throughput result tcp](images/throughput.png) + +The average throughput h1 = 32.5/20 = `1.67 Mb/s` +The average throughput h3 = 14.4/20 = `0.7 Mb/s` + diff --git a/lab0/images/latency.png b/lab0/images/latency.png new file mode 100644 index 0000000..70a5370 Binary files /dev/null and b/lab0/images/latency.png differ diff --git a/lab0/images/throughput.png b/lab0/images/throughput.png new file mode 100644 index 0000000..9aeea71 Binary files /dev/null and b/lab0/images/throughput.png differ diff --git a/lab0/network_topo.py b/lab0/network_topo.py index 39852e4..627248c 100644 --- a/lab0/network_topo.py +++ b/lab0/network_topo.py @@ -17,6 +17,7 @@ #!/usr/bin/python from mininet.topo import Topo +from mininet.link import TCLink class BridgeTopo(Topo): "Creat a bridge-like customized network topology according to Figure 1 in the lab0 description." @@ -25,6 +26,21 @@ def __init__(self): Topo.__init__(self) - # TODO: add nodes and links to construct the topology; remember to specify the link properties + # Add the hosts and Switches + h1 = self.addHost('h1') + h2 = self.addHost('h2') + h3 = self.addHost('h3') + h4 = self.addHost('h4') + + # Add the switches + s1 = self.addSwitch('s1') + s2 = self.addSwitch('s2') + + # Do the linking + self.addLink(h1, s1, cls=TCLink, bw=15, delay=10) # e1 + self.addLink(h2, s1, cls=TCLink, bw=15, delay=10) # e2 + self.addLink(s1, s2, cls=TCLink, bw=20, delay=45) # e5 + self.addLink(s2, h3, cls=TCLink, bw=15, delay=10) # e3 + self.addLink(s2, h4, cls=TCLink, bw=15, delay=10) # e4 topos = {'bridge': (lambda: BridgeTopo())} diff --git a/lab1/ans_controller.py b/lab1/ans_controller.py index 7d57302..420ba24 100644 --- a/lab1/ans_controller.py +++ b/lab1/ans_controller.py @@ -20,6 +20,8 @@ from ryu.controller.handler import set_ev_cls from ryu.ofproto import ofproto_v1_3 +from ryu.lib.packet import packet, ethernet, arp, ipv4, ether_types, icmp +from ryu.ofproto import ether class LearningSwitch(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] @@ -28,37 +30,219 @@ def __init__(self, *args, **kwargs): super(LearningSwitch, self).__init__(*args, **kwargs) # Here you can initialize the data structures you want to keep at the controller - + self.mac_to_port = {} + + # Router specific info + self.port_to_own_mac = { + 1: "00:00:00:00:01:01", + 2: "00:00:00:00:01:02", + 3: "00:00:00:00:01:03" + } + # Router port (gateways) IP addresses assumed by the controller + self.port_to_own_ip = { + 1: "10.0.1.1", + 2: "10.0.2.1", + 3: "192.168.1.1" + } + + # ARP table ip_mac & ip port + self.ip_to_mac = {} + self.ip_to_port = {} @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): datapath = ev.msg.datapath - ofproto = datapath.ofproto - parser = datapath.ofproto_parser + of_proto = datapath.ofproto + of_parser = datapath.ofproto_parser # Initial flow entry for matching misses - match = parser.OFPMatch() - actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, - ofproto.OFPCML_NO_BUFFER)] + match = of_parser.OFPMatch() + actions = [of_parser.OFPActionOutput(of_proto.OFPP_CONTROLLER, + of_proto.OFPCML_NO_BUFFER)] self.add_flow(datapath, 0, match, actions) # Add a flow entry to the flow-table def add_flow(self, datapath, priority, match, actions): - ofproto = datapath.ofproto - parser = datapath.ofproto_parser + of_proto = datapath.ofproto + of_parser = datapath.ofproto_parser # Construct flow_mod message and send it - inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod(datapath=datapath, priority=priority, + inst = [of_parser.OFPInstructionActions(of_proto.OFPIT_APPLY_ACTIONS, actions)] + mod = of_parser.OFPFlowMod(datapath=datapath, priority=priority, match=match, instructions=inst) datapath.send_msg(mod) # Handle the packet_in event @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def _packet_in_handler(self, ev): - msg = ev.msg datapath = msg.datapath + of_proto = datapath.ofproto + of_parser = datapath.ofproto_parser + + # create packet from message data + pkt = packet.Packet(msg.data) + + # icmp generated when host ping its own gateway + icmp_pkt = pkt.get_protocol(icmp.icmp) + self.logger.info('icmp: %s', pkt) + + # extract the src + # dest mac addresses from the ethernet protocol layer + eth = pkt.get_protocol(ethernet.ethernet) + dst = eth.dst + src = eth.src + + dpid = datapath.id + in_port = msg.match['in_port'] + + # If it's a switch (s1 or s2): act as learning switch + # Use datapath 1 and 2 + if dpid in [1, 2]: + self.mac_to_port.setdefault(dpid, {}) + self.mac_to_port[dpid][src] = in_port + + # Case: dst mac is in mac_to_port + # we get the outport + if dst in self.mac_to_port[dpid]: + out_port = self.mac_to_port[dpid][dst] + + # Else we flood + else: + out_port = of_proto.OFPP_FLOOD + + # we then add a flow + actions = [of_parser.OFPActionOutput(out_port)] + match = of_parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) + self.add_flow(datapath, 1, match, actions) + + + # Case: Router (s3): act as IP router + elif dpid == 3: + + # Check if its an Adress resolution (ARP) + if eth.ethertype == ether_types.ETH_TYPE_ARP: + arp_pkt = pkt.get_protocol(arp.arp) + + # ARP - REQUEST + if arp_pkt.opcode == arp.ARP_REQUEST: + self.handle_arp_request(datapath, in_port, eth, arp_pkt) + return + + # ARP - REPLY + # Keep track of IP and MAC for ARP REPLY + elif arp_pkt.opcode == arp.ARP_REPLY: + self.ip_to_mac[arp_pkt.src_ip] = arp_pkt.src_mac + self.ip_to_port[arp_pkt.src_ip] = in_port + return + + # Packet is an IP packet + elif eth.ethertype == ether_types.ETH_TYPE_IP: + ip_pkt = pkt.get_protocol(ipv4.ipv4) + src_ip = ip_pkt.src + dst_ip = ip_pkt.dst + self.ip_to_mac[src_ip] = src + self.ip_to_port[src_ip] = in_port + + # block ext (192.168.1.x) from pinging internal hosts + if src_ip.startswith("192.168.1.") and dst_ip.startswith("10.0."): + return + + # block ext <-> ser for TCP/UDP + if ("192.168.1." in src_ip and dst_ip == "10.0.2.2") or ("192.168.1." in dst_ip and src_ip == "10.0.2.2"): + return + + # Find out port, src and dst mac to send ip packet + if dst_ip in self.ip_to_mac and dst_ip in self.ip_to_port: + out_port = self.ip_to_port[dst_ip] + dst_mac = self.ip_to_mac[dst_ip] + src_mac = self.port_to_own_mac[out_port] + + actions = [ + of_parser.OFPActionSetField(eth_src=src_mac), + of_parser.OFPActionSetField(eth_dst=dst_mac), + of_parser.OFPActionOutput(out_port) + ] + match = of_parser.OFPMatch( + eth_type=ether_types.ETH_TYPE_IP, + ipv4_dst=dst_ip, + ipv4_src=src_ip + ) + self.add_flow(datapath, 10, match, actions) + + + # Host trying to test its own gateway with ICMP + if icmp_pkt: + self.logger.info('checking %s ', self.port_to_own_ip.items()) + ip_pkt = pkt.get_protocol(ipv4.ipv4) + src_mac = None + if ip_pkt and ip_pkt.dst in self.port_to_own_ip.values(): + for p, ip in self.port_to_own_ip.items(): + if ip == ip_pkt.dst: + src_mac = self.port_to_own_mac[p] + self.logger.info('Mac port found %s, %s', src_mac, p) + + eth_p = ethernet.ethernet(dst=eth.src, src=src_mac, ethertype=eth.ethertype) + ip = ipv4.ipv4(dst=ip_pkt.src, src=ip_pkt.dst, proto=ip_pkt.proto) + icmp_reply = icmp.icmp(type_=0, code=0, csum=0, data=icmp_pkt.data) + pkt = packet.Packet() + pkt.add_protocol(eth_p) + pkt.add_protocol(ip) + pkt.add_protocol(icmp_reply) + pkt.serialize() + + actions = [of_parser.OFPActionOutput(p)] + out = of_parser.OFPPacketOut( + datapath=datapath, + buffer_id=0xffffffff, + in_port=datapath.ofproto.OFPP_CONTROLLER, + actions=actions, + data=pkt.data + ) + datapath.send_msg(out) + + self.logger.info('switch Learnt Mac and ports %s', self.mac_to_port) + self.logger.info('Learnt gateway Ips port match %s', self.ip_to_mac) + self.logger.info('Learnt IP -> MAC %s', self.ip_to_mac) + + def handle_arp_request(self, datapath, port, eth, arp_pkt): + """ + + """ + of_proto = datapath.ofproto + of_parser = datapath.ofproto_parser + + target_ip = arp_pkt.dst_ip + if target_ip not in self.port_to_own_ip.values(): + return + + for p, ip in self.port_to_own_ip.items(): + if ip == target_ip: + target_mac = self.port_to_own_mac[p] + + # Create a new packet to be used as reply + arp_reply = packet.Packet() + arp_reply.add_protocol(ethernet.ethernet( + ethertype=ether.ETH_TYPE_ARP, + src=target_mac, + dst=eth.src + )) + arp_reply.add_protocol(arp.arp( + opcode=arp.ARP_REPLY, + src_mac=target_mac, + src_ip=target_ip, + dst_mac=eth.src, + dst_ip=arp_pkt.src_ip + )) + arp_reply.serialize() + + actions = [of_parser.OFPActionOutput(port)] - # Your controller implementation should start here \ No newline at end of file + # We will send the packet to the original requester + out = of_parser.OFPPacketOut(datapath=datapath, + buffer_id=of_proto.OFP_NO_BUFFER, + in_port=of_proto.OFPP_CONTROLLER, + actions=actions, + data=arp_reply.data) + datapath.send_msg(out) diff --git a/lab1/run_network.py b/lab1/run_network.py index 86298c8..c0f9f38 100644 --- a/lab1/run_network.py +++ b/lab1/run_network.py @@ -30,7 +30,32 @@ def __init__(self): Topo.__init__(self) - # Build the specified network topology here + # Internal hosts + h1 = self.addHost('h1', ip='10.0.1.2/24', defaultRoute='via 10.0.1.1') + h2 = self.addHost('h2', ip='10.0.1.3/24', defaultRoute='via 10.0.1.1') + + # Internal server + ser = self.addHost('ser', ip='10.0.2.2/24', defaultRoute='via 10.0.2.1') + + # External host + ext= self.addHost('ext', ip='192.168.1.123/24', defaultRoute='via 192.169.1.1') + + # Add switches + s3 = self.addSwitch('s3') # router + s1 = self.addSwitch('s1') + s2 = self.addSwitch('s2') + + + # Add host links + self.addLink(h1, s1, cls=TCLink, bw=15, delay=10) + self.addLink(h2, s1, cls=TCLink, bw=15, delay=10) + self.addLink(ser, s2, cls=TCLink, bw=15, delay=10) + self.addLink(ext, s3, cls=TCLink, bw=15, delay=10) + + # Add switch links + self.addLink(s2, s3, cls=TCLink, bw=15, delay=10) + self.addLink(s1, s3, cls=TCLink, bw=15, delay=10) + def run(): topo = NetworkTopo()