From 82bf56cafff2ffb848fc6fe1a44a171334d4f34c Mon Sep 17 00:00:00 2001 From: Eugene233 Date: Mon, 14 Apr 2025 22:59:20 +0200 Subject: [PATCH 1/4] create topology: analysis1 --- lab0/README.md | 18 ++++++++++++++++++ lab0/images/latency.png | Bin 0 -> 26670 bytes lab0/images/throughput.png | Bin 0 -> 59212 bytes lab0/network_topo.py | 18 +++++++++++++++++- 4 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 lab0/README.md create mode 100644 lab0/images/latency.png create mode 100644 lab0/images/throughput.png 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 0000000000000000000000000000000000000000..70a5370cfeba3042e801e2087aed516daec125da GIT binary patch literal 26670 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 z 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 literal 0 HcmV?d00001 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())} From 269905e24888804b2b4cd8e91d317bd309ea7e25 Mon Sep 17 00:00:00 2001 From: Eugene233 Date: Thu, 8 May 2025 02:03:22 +0200 Subject: [PATCH 2/4] Implement logic for router --- lab1/ans_controller.py | 102 +++++++++++++++++++++++++++++++++++++++-- lab1/run_network.py | 26 ++++++++++- 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/lab1/ans_controller.py b/lab1/ans_controller.py index 7d57302..7c9f040 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, ethernet, arp + class LearningSwitch(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] @@ -28,7 +30,9 @@ 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.data_plane = {} + self.known_ips = {} + @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): @@ -57,8 +61,100 @@ def add_flow(self, datapath, priority, match, actions): # 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 - # Your controller implementation should start here \ No newline at end of file + of_proto = datapath.ofproto + of_parser = datapath.ofproto_parser + dp_id = datapath.id + source_port = msg.match['in_port'] + + # create a packet using the event raw data + pkt = packet.Packet(msg.data) + pkt_header = pkt.get_protocols(ethernet.ethernet) + + try: + ethernet_test = pkt_header[0] + + except: + pass + + + # TODO: Check if request header has 'arp' key + if ethernet_test.ethertype == 2054: + arp_info = pkt.get_protocols(arp.arp)[0] + src_ip = arp_info.src_ip + dst_ip = arp_info.dst_ip + + self.logger.info("arp packet info: %s", arp_info) + self.known_ips.setdefault(dp_id, "") + self.known_ips[dp_id] = dst_ip + + # Logg info for verification + self.logger.info('ARP packket in from %s to %s line %s', src_ip, dst_ip, dp_id) + + if self.known_ips[dp_id] == dst_ip: + new_dest_ip = dst_ip + + else: + self.logger.info('destingation subnet not found so flooding ->>') + new_dest_ip = of_proto.OFPP_FLOOD + + self.logger.info('ip to be formed', new_dest_ip) + + + # In every case we create a flow + actions = of_proto.OFPP_NORMAL + + if new_dest_ip != of_proto.OFPP_FLOOD: + match = of_parser.OFPMatch(ipv4_src=src_ip) + self.add_flow(datapath, 1, match, actions) + else: + match = of_parser.OFPMatch() + self.add_flow(datapath, 0, match, actions) + + else: + # no: check dest_mac in data_plane: + + source_port = msg.match['in_port'] + # get ethernet header + eth_header = pkt.get_protocols(ethernet.ethernet)[0] + + # source and destination macs + source_mac = eth_header.src + dest_mac = eth_header.dst + + # Add a new structure for the dataplane entries + self.data_plane.setdefault(dp_id, {}) + + # Logg info for verification + self.logger.info('Incoming packket in data path %s from %s to %s in_port %s', + dp_id, source_mac, dest_mac, source_port) + + # assign the inport of the source switch in datapatch + # self-learning - avoid flooding + self.data_plane[dp_id][source_mac] = source_port + + if dest_mac in self.data_plane[dp_id]: + dest_port = self.data_plane[dp_id][dest_mac] + + else: + + self.logger.info('destingation mac not found back to controller ->>') + dest_port = of_proto.OFPP_FLOOD + + # We have a record for the destination so we create an action + actions = [of_parser.OFPActionOutput(dest_port)] + + # we are not flooding so we implement a soruce port match for the switch + # and set its priority + if dest_port != of_proto.OFPP_FLOOD: + match = of_parser.OFPMatch(in_port=source_port) + self.add_flow(datapath, 1, match, actions) + else: + match = of_parser.OFPMatch() + self.add_flow(datapath, 0, match, actions) + + + self.logger.info("known macs %s", self.data_plane) + self.logger.info("known ips %s", self.known_ips) \ No newline at end of file diff --git a/lab1/run_network.py b/lab1/run_network.py index 86298c8..dd5d054 100644 --- a/lab1/run_network.py +++ b/lab1/run_network.py @@ -30,7 +30,31 @@ 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 + s1 = self.addSwitch('s1') + s2 = self.addSwitch('s2') + s3 = self.addSwitch('s3') # router + + # 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(s1, s3, cls=TCLink, bw=15, delay=10) + self.addLink(s2, s3, cls=TCLink, bw=15, delay=10) + def run(): topo = NetworkTopo() From fc80d684ea620e2c115aa137c6a727c4fc900c39 Mon Sep 17 00:00:00 2001 From: Eugene233 Date: Mon, 12 May 2025 16:37:10 +0200 Subject: [PATCH 3/4] ping several times before work --- lab1/ans_controller.py | 240 +++++++++++++++++++++++++---------------- 1 file changed, 148 insertions(+), 92 deletions(-) diff --git a/lab1/ans_controller.py b/lab1/ans_controller.py index 7c9f040..aec06be 100644 --- a/lab1/ans_controller.py +++ b/lab1/ans_controller.py @@ -20,8 +20,8 @@ from ryu.controller.handler import set_ev_cls from ryu.ofproto import ofproto_v1_3 -from ryu.lib.packet import packet, ethernet, ethernet, arp - +from ryu.lib.packet import packet, ethernet, arp, ipv4, ether_types +from ryu.ofproto import ether class LearningSwitch(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] @@ -30,31 +30,46 @@ 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.data_plane = {} - self.known_ips = {} - + 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) @@ -63,98 +78,139 @@ def add_flow(self, datapath, priority, match, actions): def _packet_in_handler(self, ev): msg = ev.msg datapath = msg.datapath - of_proto = datapath.ofproto of_parser = datapath.ofproto_parser - dp_id = datapath.id - source_port = msg.match['in_port'] - - # create a packet using the event raw data - pkt = packet.Packet(msg.data) - pkt_header = pkt.get_protocols(ethernet.ethernet) - - try: - ethernet_test = pkt_header[0] - except: - pass + # create packet from message data + pkt = packet.Packet(msg.data) + # extract the src + # dest mac addresses from the ethernet protocol layer + eth = pkt.get_protocol(ethernet.ethernet) + dst = eth.dst + src = eth.src - # TODO: Check if request header has 'arp' key - if ethernet_test.ethertype == 2054: - arp_info = pkt.get_protocols(arp.arp)[0] - src_ip = arp_info.src_ip - dst_ip = arp_info.dst_ip + dpid = datapath.id + in_port = msg.match['in_port'] - self.logger.info("arp packet info: %s", arp_info) - self.known_ips.setdefault(dp_id, "") - self.known_ips[dp_id] = dst_ip - - # Logg info for verification - self.logger.info('ARP packket in from %s to %s line %s', src_ip, dst_ip, dp_id) - - if self.known_ips[dp_id] == dst_ip: - new_dest_ip = dst_ip - else: - self.logger.info('destingation subnet not found so flooding ->>') - new_dest_ip = of_proto.OFPP_FLOOD + 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) - self.logger.info('ip to be formed', new_dest_ip) + # 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] - # In every case we create a flow - actions = of_proto.OFPP_NORMAL - - if new_dest_ip != of_proto.OFPP_FLOOD: - match = of_parser.OFPMatch(ipv4_src=src_ip) - self.add_flow(datapath, 1, match, actions) + # Else we flood else: - match = of_parser.OFPMatch() - self.add_flow(datapath, 0, match, actions) - - else: - # no: check dest_mac in data_plane: + out_port = of_proto.OFPP_FLOOD - source_port = msg.match['in_port'] - # get ethernet header - eth_header = pkt.get_protocols(ethernet.ethernet)[0] + # 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) - # source and destination macs - source_mac = eth_header.src - dest_mac = eth_header.dst - # Add a new structure for the dataplane entries - self.data_plane.setdefault(dp_id, {}) - - # Logg info for verification - self.logger.info('Incoming packket in data path %s from %s to %s in_port %s', - dp_id, source_mac, dest_mac, source_port) + # Case: Router (s3): act as IP router + elif dpid == 3: - # assign the inport of the source switch in datapatch - # self-learning - avoid flooding - self.data_plane[dp_id][source_mac] = source_port + # 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) + + + def handle_arp_request(self, datapath, port, eth, arp_pkt): + """ - if dest_mac in self.data_plane[dp_id]: - dest_port = self.data_plane[dp_id][dest_mac] - - else: - - self.logger.info('destingation mac not found back to controller ->>') - dest_port = of_proto.OFPP_FLOOD - - # We have a record for the destination so we create an action - actions = [of_parser.OFPActionOutput(dest_port)] - - # we are not flooding so we implement a soruce port match for the switch - # and set its priority - if dest_port != of_proto.OFPP_FLOOD: - match = of_parser.OFPMatch(in_port=source_port) - self.add_flow(datapath, 1, match, actions) - else: - match = of_parser.OFPMatch() - self.add_flow(datapath, 0, match, actions) + """ + of_proto = datapath.ofproto + of_parser = datapath.ofproto_parser - - self.logger.info("known macs %s", self.data_plane) - self.logger.info("known ips %s", self.known_ips) \ No newline at end of file + 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)] + + # 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) From f1df4354f9aa7ab13d94fa1ca6bd8feb7a186bf7 Mon Sep 17 00:00:00 2001 From: Eugene233 Date: Tue, 13 May 2025 21:36:31 +0200 Subject: [PATCH 4/4] Ping self gateway --- .gitignore | 1 + lab1/ans_controller.py | 46 +++++++++++++++++++++++++++++++++++------- lab1/run_network.py | 5 +++-- 3 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 .gitignore 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/lab1/ans_controller.py b/lab1/ans_controller.py index aec06be..420ba24 100644 --- a/lab1/ans_controller.py +++ b/lab1/ans_controller.py @@ -20,7 +20,7 @@ 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 +from ryu.lib.packet import packet, ethernet, arp, ipv4, ether_types, icmp from ryu.ofproto import ether class LearningSwitch(app_manager.RyuApp): @@ -83,6 +83,10 @@ def _packet_in_handler(self, ev): # 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 @@ -93,11 +97,6 @@ def _packet_in_handler(self, ev): dpid = datapath.id in_port = msg.match['in_port'] - - 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) - # If it's a switch (s1 or s2): act as learning switch # Use datapath 1 and 2 if dpid in [1, 2]: @@ -129,7 +128,6 @@ def _packet_in_handler(self, ev): # ARP - REQUEST if arp_pkt.opcode == arp.ARP_REQUEST: self.handle_arp_request(datapath, in_port, eth, arp_pkt) - return # ARP - REPLY @@ -173,6 +171,40 @@ def _packet_in_handler(self, ev): ) 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): """ diff --git a/lab1/run_network.py b/lab1/run_network.py index dd5d054..c0f9f38 100644 --- a/lab1/run_network.py +++ b/lab1/run_network.py @@ -41,9 +41,10 @@ def __init__(self): 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') - s3 = self.addSwitch('s3') # router + # Add host links self.addLink(h1, s1, cls=TCLink, bw=15, delay=10) @@ -52,8 +53,8 @@ def __init__(self): 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) - self.addLink(s2, s3, cls=TCLink, bw=15, delay=10) def run():