Skip to content

南京大学本科生实验报告

课程名称: 计算机网络 任课教师:田臣

学院 工程管理学院 专业(方向) 计算机金融
学号 211275022 姓名 田昊东
Email 1040880153@qq.com 开始/完成日期 2023/4/3~2023/4/4

实验名称

  • 计算机网络Lab 2: Learning Switch

实验目的

  • 学习交换机的基本原理,并实现几种不同的交换机算法

实验内容

basic switch

  • python if eth is None: log_info("Received a non-Ethernet packet?!") return if eth.dst in mymacs: table[str(eth.src)] = fromIface log_info("Received a packet intended for me") # 表中查询不到或者为广播地址 elif str(eth.dst) not in table or str(eth.dst)=='ff:ff:ff:ff:ff:ff': table[str(eth.src)] = fromIface for intf in my_interfaces: if fromIface!= intf.name: log_info (f"Flooding packet {packet} to {intf.name}") net.send_packet(intf, packet) # 在表中可以找到 else: table[str(eth.src)] = fromIface log_info (f"Sending packet {packet} to {intf.name}") net.send_packet(net.interface_by_name(table[str(eth.dst)]), packet)

  • 当接收到一个包时将包的来源地址与端口存储在字典中进行绑定

  • 接收到一个包时在字典中查找,如果能找到对应的信息,则只向对应的端口转发信息,而不再广播;如果目的地址不能找到或者为广播地址(ff:ff:ff:ff:ff:ff)则转发到除到达端口外的其他端口,进行转发

自定义测试

  • python # 从20:00:00:00:00:01发送到20:00:00:00:00:02的包 testpkt = new_packet("20:00:00:00:00:01", "20:00:00:00:00:02", "1.1.1.1", "2.2.2.2") s.expect(PacketInputEvent("eth0", testpkt, display=Ethernet), "An Ethernet frame from 20:00:00:00:00:01 to 20:00:00:00:00:02 should arrive on eth0") s.expect(PacketOutputEvent("eth1", testpkt, "eth2", testpkt, display=Ethernet), "Ethernet frame destined to 20:00:00:00:00:02 should be flooded out eth0 and eth2") # 从20:00:00:00:00:02发送到20:00:00:00:00:01的包 testpkt = new_packet("20:00:00:00:00:02", "20:00:00:00:00:01", "2.2.2.2", "1.1.1.1") s.expect(PacketInputEvent("eth1", testpkt, display=Ethernet), "An Ethernet frame from 20:00:00:00:00:02 to 20:00:00:00:00:01 should arrive on eth1") s.expect(PacketOutputEvent("eth0", testpkt, display=Ethernet), "Ethernet frame destined to 20:00:00:00:00:01 should be sended to eth0") # 从20:00:00:00:00:03的广播包 testpkt = new_packet("20:00:00:00:00:03", "ff:ff:ff:ff:ff:ff", "3.3.3.3", "255.255.255.255") s.expect(PacketInputEvent("eth2", testpkt, display=Ethernet), "An Ethernet frame from 20:00:00:00:00:02 to ff:ff:ff:ff:ff:ff should arrive on eth2") s.expect(PacketOutputEvent("eth0", testpkt, "eth1", testpkt, display=Ethernet), "Ethernet frame with a broadcast destination address should be flooded out eth0 and eth1")

  • 分别测试了table中可以检索到信息、不能检索到信息以及广播三种情况

  • 测试结果image-20230403192601039

抓包结果

  • 测试client ping -c 2 sever1

  • sever1Snipaste_2023-04-03_17-50-59

  • sever2Snipaste_2023-04-03_17-51-15

  • 分析:

  • 由于client不知道server1的mac地址,因此要通过arp进行询问,由于此时switch的转发表为空不知道,因此进行泛洪广播至整个网络,因此server1和server2都能收到该信息。

  • server1进行arp回复时由于switch表中已经存储了client对应的端口,因此不再进行泛洪,只向client所在端口进行转发,因此server1上会显示发送了一个arp包进行答复,而server2不会收到。

  • 之后进行在client和server1之间进行两次ping操作,由于此时client和server1对应的端口都已经被存储在switch表中了,因此都只向目标端口进行转发,不会再进行泛洪,因此server1上会显示对应的4个ICMP包,而server2不会收到或发出任何一个包。

timeouts

  • python if eth is None: log_info("Received a non-Ethernet packet?!") return if eth.dst in mymacs: table[str(eth.src)] = [fromIface,time.time()] log_info("Received a packet intended for me") # 表中查询不到或者为广播地址或超时 elif str(eth.dst) not in table or str(eth.dst)=='ff:ff:ff:ff:ff:ff' or time.time()-table[str(eth.dst)][1]>10: # 删除过时的表项 if str(eth.dst) in table and time.time()-table[str(eth.dst)][1]>10: del table[str(eth.dst)] table[str(eth.src)] = [fromIface,time.time()] for intf in my_interfaces: if fromIface!= intf.name: log_info (f"Flooding packet {packet} to {intf.name}") net.send_packet(intf, packet) else: table[str(eth.src)] = [fromIface,time.time()] log_info (f"Sending packet {packet} to {intf.name}") net.send_packet(net.interface_by_name(table[str(eth.dst)][0]), packet)

  • 在字典中存储一个列表[端口,记录时间]

  • 当收到包时对字典进行更新,如果不在字典中则加入进来,并记录时间;如果在字典中则更新时间
  • 除了广播、表中找不到地址外表中信息过期(设定为10s)也会导致泛洪,并且要从表中删除过期的信息

实验测试(给定测试)

  • 测试结果image-20230403195616437

自定义测试

  • python # 从20:00:00:00:00:01发送到20:00:00:00:00:02的包 testpkt = new_packet("20:00:00:00:00:01", "20:00:00:00:00:02", "1.1.1.1", "2.2.2.2") s.expect(PacketInputEvent("eth0", testpkt, display=Ethernet), "An Ethernet frame from 20:00:00:00:00:01 to 20:00:00:00:00:02 should arrive on eth0") s.expect(PacketOutputEvent("eth1", testpkt, "eth2", testpkt, display=Ethernet), "Ethernet frame destined to 20:00:00:00:00:02 should be flooded out eth0 and eth2") # 从20:00:00:00:00:02发送到20:00:00:00:00:01的包 testpkt = new_packet("20:00:00:00:00:02", "20:00:00:00:00:01", "2.2.2.2", "1.1.1.1") s.expect(PacketInputEvent("eth1", testpkt, display=Ethernet), "An Ethernet frame from 20:00:00:00:00:02 to 20:00:00:00:00:01 should arrive on eth1") s.expect(PacketOutputEvent("eth0", testpkt, display=Ethernet), "Ethernet frame destined to 20:00:00:00:00:01 should be sended to eth0") #等待5s s.expect(PacketInputTimeoutEvent(5),"wait 5s") # 从20:00:00:00:00:03的广播包 testpkt = new_packet("20:00:00:00:00:03", "ff:ff:ff:ff:ff:ff", "3.3.3.3", "255.255.255.255") s.expect(PacketInputEvent("eth2", testpkt, display=Ethernet), "An Ethernet frame from 20:00:00:00:00:02 to ff:ff:ff:ff:ff:ff should arrive on eth2") s.expect(PacketOutputEvent("eth0", testpkt, "eth1", testpkt, display=Ethernet), "Ethernet frame with a broadcast destination address should be flooded out eth0 and eth1") #等待5s s.expect(PacketInputTimeoutEvent(5),"wait 5s") # 从20:00:00:00:00:01发送到20:00:00:00:00:02的包 testpkt = new_packet("20:00:00:00:00:01", "20:00:00:00:00:02", "1.1.1.1", "2.2.2.2") s.expect(PacketInputEvent("eth0", testpkt, display=Ethernet), "An Ethernet frame from 20:00:00:00:00:01 to 20:00:00:00:00:02 should arrive on eth0") s.expect(PacketOutputEvent("eth1", testpkt, "eth2", testpkt, display=Ethernet), "Ethernet frame destined to 20:00:00:00:00:02 should be flooded out eth0 and eth2") # 从20:00:00:00:00:01发送到20:00:00:00:00:03的包 testpkt = new_packet("20:00:00:00:00:01", "20:00:00:00:00:03", "1.1.1.1", "3.3.3.3") s.expect(PacketInputEvent("eth0", testpkt, display=Ethernet), "An Ethernet frame from 20:00:00:00:00:01 to 20:00:00:00:00:03 should arrive on eth0") s.expect(PacketOutputEvent("eth2", testpkt, display=Ethernet), "Ethernet frame destined to 20:00:00:00:00:03 should be sended to eth2")

  • 先从20:00:00:00:00:01向20:00:00:00:00:02发送,发生泛洪

  • 再从20:00:00:00:00:02向20:00:00:00:00:01发送,由于表中有信息,直接转发
  • 等待5s从20:00:00:00:00:03广播
  • 再等待5s此时20:00:00:00:00:01和20:00:00:00:00:02的信息已经过期,因此20:00:00:00:00:01向20:00:00:00:00:02发送,发生泛洪
  • 由于20:00:00:00:00:03发送广播不足10s,因此其信息没有过期,故20:00:00:00:00:01向20:00:00:00:00:03发送,直接转发

  • 测试结果image-20230403205400184

抓包分析

  • 过程:先server1 ping server2,然后立即server2 ping server1,等待10s后再次server2 ping server1
  • 对client和server进行抓包
  • client image-20230403211605805
  • server2image-20230403211530933
  • server1 ping server2时由于表中没有server2的信息因此会进行泛洪,client和server2都会收到信息
    • ser而当server2进行回应时转发表中已经有了server1的信息,因此不会进行泛洪,所以不会显示在client中
  • 在server2 ping server1的过程中由于转发表中已经有了server1和server2的信息,因此ping的两个过程都不会进行泛洪,因此client不会收到响应的信息
  • 在第二次ping和第三次ping之间两个server之间通过arp再次确认对方的mac地址(可能是因为之前存储的信息过期了)
  • 由于已经过去超过10s,因此现在转发表为空,因此又需要通过泛洪进行传播,因此client又收到了第二个ICMP包

topo结构拓展

  • 由于以下算法都要求switch表的容量为5,因此对mininet结构进行拓展,改为六个节点,方便对算法进行测试

  • python # server1 server3 # \ | # sever5----switch----client # / | # server2 server4 #

LRU

  • python class node: def __init__(self, x, y): self.val = y self.address = x self.next = None self.prev = None head = node(0, 0) end = node(0, 0) head.next = end end.prev = head def addelement(x, y): if x in table: table[x].val = y return table[x] = node(x,y) table[x].next = end table[x].prev = end.prev table[x].prev.next = table[x] end.prev = table[x] if len(table) > 5: delelement() def delelement(): del table[head.next.address] head.next = head.next.next head.next.prev = head def getelement(x): table[x].prev.next = table[x].next table[x].next.prev = table[x].prev table[x].next = end table[x].prev = end.prev table[x].prev.next = table[x] end.prev = table[x] return table[x].val

  • 使用双向链表结合哈希表实现,可以把单次操作的时间复杂度降低为O(1)

  • 其中自定义node类型构造双向链表

  • 并且设计了增加、删除、获取元素三种接口

  • 思路:

  • 使用双向链表维护元素的顺序

    • 当插入元素时,如果元素已经存在则更新元素的值(如果变化了的话),然后将元素移动到链表的尾部;如果元素不存在则直接将元素插入到末尾
    • 如果插入元素后元素的数目大于限制,则从链表头删除一个元素
  • 使用哈希表实现对元素的快速随机访问

    • 查找一个元素是否存在于链表中、确定一个元素在链表中的位置是O(n)时间复杂度
    • 使用哈希表记录元素对应的链表节点的位置可以将这个访问过程的复杂度降低为O(1)

实验测试

  • image-20230403233203621

自定义测试

  • image-20230404145044010
  • 首先进行5次常规的通信,根据最近使用原则,其优先级顺序为:
    • 20:00:00:00:00:03>20:00:00:00:00:01>20:00:00:00:00:05>20:00:00:00:00:04>20:00:00:00:00:02
  • 从20:00:00:00:00:01发出一次广播,这不会对表产生影响
  • 从20:00:00:00:00:06向20:00:00:00:00:04发送信息,由于表已满因此20:00:00:00:00:02的信息会被删除
  • 再次测试20:00:00:00:00:01仍然在表中,而20:00:00:00:00:02确实已经被删除

  • image-20230404145015872

抓包分析

  • 操作流程:

  • shell server1 ping -c 1 server2 server3 ping -c 1 server4 server5 ping -c 1 client server5 ping -c 1 server1 server5 ping -c 1 server2

  • 分析(对server1和server2进行抓包观察)

  • image-20230404151444763
  • image-20230404151504652
  • server1 ping -c 1 server2:server1和server2相互发送信息

    • 由于ping是一个双向的过程,因此都会被加入到table中,且最后一个包(ARP)包的目的地是server2因此server2在table中具有更高的优先级
    • table:server1 server2
  • server3 ping -c 1 server4:server3和server4相互发送信息

    • table:server1 server2 server3 server4
  • server5 ping -c 1 client:server5和client相互发送信息,由于table已满,因此server1会被删除

    • table:server2 server3 server4 server5 client
  • server5 ping -c 1 server1:此时server1已经失效不在table中,因此会进行泛洪,不只server1,server2也会受到ICMP信息

    • table:server3 server4 client server5 server1
  • server5 ping -c 1 server2:此时server2已经失效不在table中,因此会进行泛洪,不只server2,server1也会受到ICMP信息

    • table:server4 client server1 server5 server2

traffic

  • python table = {} def addelement(mac, port): nonlocal table if mac in table: table[mac][0] = port else: if len(table) >= 5: table = dict(sorted(table.items(), key=lambda item: item[1][1], reverse=True)[0:4]) table[mac] = [port, 0]

  • 使用一个字典来存储转发表,记录mac地址、对应的ip和流量(作为目的地的次数)

  • 如果mac已经在表中了则只需要更新端口
  • 如果不在表中需要先检查元素数目,如果已经满了则要依据流量进行排序,删除流量最小的项,然后插入新的元素

  • python if str(eth.dst) == "ff:ff:ff:ff:ff:ff": for key in table: if key != str(eth.src): table[key][1] += 1 elif str(eth.dst) in table: table[str(eth.dst)][1] += 1

  • 首先是不是广播地址,如果是的话要向除了发送地址以外并且在表中的元素的流量加一

  • 如果不是广播地址,则直接判断目的地址在不在表中,如果在的话对流量进行加一

实验测试

  • image-20230404130412407

自定义测试

  • image-20230404133400245

  • 先进行常规的6次包发送操作,经过这6次后table被装满,对应的流量为

    • 21:00:00:00:00:01 21:00:00:00:00:02 21:00:00:00:00:03 21:00:00:00:00:04 21:00:00:00:00:05
      1 1 1 0 0
  • 然后从21:00:00:00:00:05进行一次广播,其他接口的流量都会增加1

    • 21:00:00:00:00:01 21:00:00:00:00:02 21:00:00:00:00:03 21:00:00:00:00:04 21:00:00:00:00:05
      2 2 2 1 0
  • 之后从21:00:00:00:00:06发送一条信息,由于表已满因此21:00:00:00:00:05(流量最小)会被移出

  • 再次向21:00:00:00:00:05发送信息,由于在表中已经找不到信息,因此发生泛洪

  • image-20230404133941750

抓包分析

  • 测试步骤

  • shell server1 ping -c 1 server2 server2 ping -c 1 server1 server3 ping -c 1 server4 server5 ping -c 1 server4 client ping -c 1 server5 server1 ping -c 1 server5

  • 测试结果

  • image-20230404165435988

  • image-20230404165532923

  • 分析

  • 由于除了ICMP包外还会有ARP包存在,因此实际情况比较复杂

  • server1 ping -c 1 server2

    • table:image-20230404170316643
  • server2 ping -c 1 server1

    • table:image-20230404170354981
  • server3 ping -c 1 server4

    • server1和server2都只会受到server3向server4发送的第一个ICMP包
    • table:image-20230404170500989
  • server5 ping -c 1 server4

    • 由于开始之前server4已经在table中,因此整个过程都不会被server1和server2捕获
    • table:image-20230404170541860
  • client ping -c 1 server5

    • 这个过程很有意思,首先由于client不在table中且table已经达到了上限,因此会删除流量最小的server5,然后插入client;但是server5又需要回复client,因此又会把server5删除而插入client,在之后的client中,这个过程又会重复多次,且都是以泛洪的形式进行,因此整个过程会被server1和server2全部捕获
    • table:image-20230404170653108
  • server1 ping -c 1 server5

    • 由于server5已经不在table中,因此先进行泛洪,会被server2捕捉,之后server5会取代client在table中的位置
    • table:image-20230404170710617

实验总结

  • 本次实验设计了几种不同的交换机算法,对交换机的运行原理有了更好的理解
  • 体会自己设计测试案例,学习了swyard的基本接口与设计方法
  • 结合wireshark进一步分析,对局域网内节点通讯、交换机运行过程有了进一步了解
  • 总的来说这次实验有一定的难度,主要在于细节的处理,保证交换机程序在各种情况下都能正确的运行