计算机网络project

Posted by chinaljr on June 8, 2018

OverOverview

这个实验是sjtu的计网课程实验,sjtu课程网站,实际上这个实验原创是wisc课程网站.

我的实验code放在github上,https://github.com/CHINALJR/Computernetwork_SJTU

Pj 1

overview

  • STEP1: Write Pinger
    • 正常socket模拟
  • STEP2: Write Iperfer
    • 正常socket模拟
  • STEP3: Mininet Tutorial
    • 我的建议是在mininet官网上第一种方案安装虚拟机
    • 强烈不建议在真机linux上安装,会因为版本问题产生各种问题
    • pj2 用到的环境必须要在2.2.0 或者 2.2.1 版本下
  • STEP4: Measurements in Mininet
    • 用mininet测试 step1 和 step2
  • STEP5: About ARP lookups, ICMP packets, Checksums, POX and environment
    • ARP 和 ICMP 与课本上一致
    • checksum 要注意需要在转发修改报文header之后 重新生成
    • POX 是一个软件,等于提供了mininet中的路由器 与 外界运行的路由器java实例建立连接
  • STEP6: Code Overview
    • 认真看一看代码,对于后面实验过程很有帮助
  • STEP7: Implement Router Data Plane
    • 实现数据层
  • STEP8: Implement Router Control Plane
    • 实现控制层

STEP7: Implement Router Data Plane

这部分主要来说实现四个个功能,这可以说是 对于 HandlePacket 的分情况讨论

  • IP forwarding
  • ARP
  • RIP interface
  • ICMP

对于链路层的etherPacket

  • 如果是IP4
    • RIP ①
    • 普通IP包 header检查 ⑤
      • Forwarding ②
      • ICMP ③
  • 如果是ARP ④

代码见github Router.java

	/**
	* Handle an Ethernet packet received on a specific interface.
	* @param etherPacket the Ethernet packet that was received
	* @param inIface the interface on which the packet was received
	*/
	public void handlePacket(Ethernet etherPacket, Iface inIface)

①rip.handlePacket

STEP 8 部分,STEP 7 只是调用接口.

②Forwarding

  • 解析获得目的IP
  • 最长前缀匹配查找 RouteTable 找不到ICMP-DEST_NET_UNREACHABLE
  • 修改链路层源MAC - 转发出口MAC地址 源MAC
  • 根据转发表,确定下一跳地址,0代表下一跳IP地址即为Packet的目的地
  • 调用ARP确定 下一跳 IP代表的 MAC地址 目的MAC
  • 设置链路层信息,转发!

代码见 github Router.java

private void forwardIpPacket(Ethernet etherPacket, Iface inIface)

③ ICMP

根据不同的情况调用

public void sendICMP(int type, Ethernet etherPacket)
  • 在调用 sendICMP 的时候如果发生错误
    • 转发表未找到转发项 – 直接return 发个鸡毛!
    • 如果ARP表为空,调用 arpCache.waitForArp 等待查询
  • 注意各层设置顺序, Top-down 否则会导致checksum的问题
  • 代码中的ICMP消息
      TIME_EXCEEDED: Type 11 byte 0
      DEST_NET_UNREACHABLE: Type 3 byte 0 网络层
      DEST_HOST_UNREACHABLE: Type 3 byte 1 链路层
      DEST_PORT_UNREACHABLE: Type 3 byte 3 router发消息是无效的
      ICMP_ECHO_REPLY: Type 0 byte 0
    

④ ARP

  • 处理 ARP.OP_REQUEST
    • 回复ARP请求
  • 处理 ARP.OP_REPLY
    • 更新ARP表
    • 更新表的时候 返回 等待列表 发送等待列表中的Packet
  • ARP定时查询
    • Java Thread 定时sleep,sleep之后发送一遍广播
    • 超过次数 交给 ICMP 处理

⑤ header检查

  • TLL 超时 ICMP
  • Checksum 直接返回
  • TLL– 之后 checksum 需要重新定义

⑥ 最长前缀匹配 getBest()

  • 遍历
  • 位运算
private RouteTableEntry getBest(int dstAddr){
	List<RouteTableEntry> fuck = routeTable.getEntries();
	RouteTableEntry bestMatch =null;
	long t = 0;
	for (RouteTableEntry x : fuck){
		int ccc1 = ( x.getMaskAddress() & dstAddr );
		int ccc2 = ( x.getDestinationAddress() & x.getMaskAddress() );
		if (ccc1 == ccc2){
			if (x.getMaskAddress()<t){
				System.out.println("Find best match!");		
				t = x.getMaskAddress();
				bestMatch = x;
			}
		}
	}
	return bestMatch;
}

STEP8: Implement Router Control Plane

  • UDP 的 520 端口
  • 广播 IP 为 224.0.0.9 广播MAC 为 FF:FF:FF:FF:FF:FF
  • 初始化的时候 发送 RIP 查询请求
  • 每 10s 发送一次 通告
  • 除了直接相连 其余 30s 失效
  • 为了防止表过大,需要有一个 maxinum = 16 意思为只记录 长度小于等于16的 转发表项
  • 为了方便,我对于给定的RouteTable 改了改,加上了时间参数timeAdded 和 路径长度hoptime

收到RIP Packet

  • 收到Request
    • 返回自己的表
  • 收到Response
    • 遍历
    • 更新转发表
      • 路径长度+1
      • 如果找到相同的项目,更新
      • 如果没有相同项目,并且长度小于maxhop,add
      • 如果没有相同项目,长度大于maxhop,如果不存在转发项(能够转发,即满足最长匹配),add(hoptime = maxhop)
      • 否则 啥也不干,因为不需要干.

发送RIP Packet

分情况讨论即可

  • 正常按时通告
  • 在初始化的时候,发送广播查询
  • 回答RIP询问 定向的

其他说明

  • 毒性逆转,有助于加速RIP的实时更新,但是对于复杂情况效果一般
  • 定时刷新,定时超时,与STEP7 中 ARP定时询问类似

Pj 2

overview

现在SDN,算是前沿的事物了.这个实验,主要实现两个SDN的应用,一个是routing应用,一个是分布式负载均衡.

环境配置方面,简直折磨人,最后找到了mininet官方给的VM,这个很不错,完全满足实验需求,建议不要再本机上尝试配置环境,很费劲.

Layer-3 Routing Application

改变

  • 不再是分布式的最短路了,是全局最短路
  • 不再使用路由表,使用通用的流表 路由表+MAC表
    • 有不同的匹配规则
    • 根据 Ethernet IP TCP UDP 等
  • 重要的函数,见Code Overview

  • 删除流表的内容
  • 添加流表的内容
  • 利用全局最短路算法,求得转发表,为每一个switch添加转发项目
  • 规则的其余问题
    • 超时
    • 优先级
  • 实验过程很简单的

device

  • deviceAdded updateHost
  • deviceRemoved
    • 删除设备
    • 其余switch中 removeRule
  • deviceMoved updateHost
  • switchAdded updateAllHost
  • switchRemoved updateAllHost
  • computeShortestPaths 在每次更改之后都会被调用

  • 核心代码
    private void updateAllHosts() {
      Collection<Host> hosts = this.getHosts();
      Map<Long, IOFSwitch> switches = this.getSwitches();
      this.predMaps = new ConcurrentHashMap<Long,Map<Long,Link>>();
      for (Long source : switches.keySet()) {
          this.predMaps.put(source, computeShortestPaths(source));
              for (Host host : hosts) {
                  removeRules(switches.get(source), host);
              }
          }
      for (Host host : hosts) {
          updateHost(host);
      }
    }
    

computeShortestPaths

  • 单源多汇最短路
  • 记录路径即可
  • 根据路径更新每个路由器

Distributed Load Balancer Application

  • 负载均衡之前是由DNS来做
  • 虚拟IP的概念
  • 类似于NAT

Install rules

  • 发起与虚拟IP的链接时,交给Controller
  • 对于虚拟IP 的 MAC地址的 ARP 查询需要回复
  • 对于虚拟IP通信,需要重写header
    • Rewrite the destination IP and MAC address of TCP packets sent from a client to the virtual IP
    • Rewrite the source IP and MAC address of TCP packets sent from server to client
  • 优先级更高!!!!
  • 20s 删除

switchAdded

  • 为虚拟IP添加 发给 controller 的规则 低优先级
  • ARP 查询
  • 其余的规则
		ArrayList<OFInstruction> instructions = null;
		for (Integer vip : this.instances.keySet()) {
			OFMatch matchCriteria = new OFMatch();
			matchCriteria.setDataLayerType(OFMatch.ETH_TYPE_IPV4);
			matchCriteria.setNetworkDestination(vip);
			OFAction action = new OFActionOutput(OFPort.OFPP_CONTROLLER);
			ArrayList<OFAction> actions = new ArrayList<OFAction>();
			actions.add(action);
			OFInstruction instruction = new OFInstructionApplyActions(actions);
			instructions = new ArrayList<OFInstruction>();
			instructions.add(instruction);
			SwitchCommands.installRule(sw, table, (short) (SwitchCommands.DEFAULT_PRIORITY + 1), matchCriteria, instructions);
		}
		OFMatch blk = new OFMatch();
		instructions = new ArrayList<OFInstruction>();
		OFInstructionGotoTable instruction = new OFInstructionGotoTable(L3Routing.table);
		instructions.add(instruction);
		SwitchCommands.installRule(sw, table, SwitchCommands.DEFAULT_PRIORITY, blk, instructions);

指令处理

if (ethPkt.getEtherType() == Ethernet.TYPE_ARP) 
	handleArp(sw, ethPkt, pktIn);
	if (ethPkt.getEtherType() == Ethernet.TYPE_IPv4) {
		IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
		if (ipv4Pkt.getProtocol() == IPv4.PROTOCOL_TCP) {
			TCP tcpPkt = (TCP) ipv4Pkt.getPayload();
		if (tcpPkt.getFlags() == TCP_FLAG_SYN) 
			handleTcpSyn(sw, ipv4Pkt);
	}
}

处理过程比较简单,注意优先级,新添加的要高

SwitchCommands.installRule(sw, table, (short) (SwitchCommands.DEFAULT_PRIORITY + 2), matchCriteria, instructions, HARD_TIMEOUT, IDLE_TIMEOUT);

总结

计网实验确实很复杂,一个系统的问题是方方面面的,而不是仅仅的一个算法. 但是整个系统的运行,也离不开每一个小算法的正确执行.

良好系统的开发过程

  • 需要完整的接口,最好还是优美
  • 正确的接口调度,接口使用
  • 正确的每一个子模块,需要涵盖各种情况