CS144 lab5~6

最后两个lab了,虽然很多大佬都说剩下的两个lab比起TCP的实现,“简直太简单了”,但是我认为做这两个之前需要补充一些额外的网络知识,不然直接上手去做的话,难度也是不小的。我举一个简单的例子,lab6中有一个函数需要我们实现:add_route(),这个函数的4个参数分别是 route_prefix,prefix_length,next_hop,interface_num,如果不了解IP地址和子网掩码的概念,可能连前2个参数为什么这么起名都理解不了,更别提写lab了。

这里建议可以阅读《网络是如何连接的》第二、三章的内容。

CS144 lab5:ARP协议

这个lab要求实现ARP协议,ARP协议(Address Resolution Protocol)可以将IP地址解析为MAC地址。在数据包发送前,主机需要知道目标主机的MAC地址才能发送数据包。ARP协议通过广播询问网络上其他主机的MAC地址,从而获取目标主机的MAC地址。

0.概述与思路

一个以太网帧:

A端点接到了一个B端点发来的以太网帧后做的事:

分析mac头部的协议,判断以太类型是 IP协议 or ARP协议

IP协议:按照IP协议提取报文即可。(推到队列稍后检索什么意思,后需要做的事是下一个lab的内容)

ARP协议:(本 lab 要实现的)

  1. 先缓存B端点的 ip_addr : MAC_addr
  2. 判断这是一个ARP request 还是 ARP reply
    • ARP request:满足你,给B端点返回一个ARP reply,告诉B端点A的mac地址。
    • ARP reply:说明B端点回复了自己的MAC地址,查看A端点是否有需要发送给B端点的以太网帧,有的话就包装为以太网帧(填上B的MAC地址)发送。(之前无法发送是因为没有B端点的MAC地址,所以程序中要设计一个map,key是ip地址,value一个waiting_list,waiting_list中是需要发送给这个ip的报文)
    • 关于上一点map的详细说明:如果A端点给B端点要发送一个以太网帧,现在已知了A的ip和mac、B的ip,接下来只需要知道B的mac就可以成功发送。
      1. 若B的ip:mac已经缓存在A的arp_cache中,则查询缓存即可得到,然后可以顺利发送
      2. 若B的mac没有缓存,就需要先把这个ip:datagram 放入waiting_list中,然后向同一以太网中所有设备发送ARP request广播,等待ARP reply到达后,再把waiting_list中的datagram取出来发送。(在ARP relpy到达前可能还有发送到B的若干以太网帧,所以需要缓存)

1.关于需要注意的时间问题

ARP协议的实现中,有两处需要注意的时间问题。

  1. ARP的缓存要要超时机制

    //caching_time超过30秒要从 _cahce中清除
    std::map<uint32_t, EthernetAddressEntry> _cache{};
    struct EthernetAddressEntry {
    size_t caching_time;
    EthernetAddress MAC_address;
    };
  2. 发送 APR request 前要检查 time_since_last_ARP_request_send,如果小于5s,就不发送。

    std::map<uint32_t, WaitingList> _queue_map{};
    struct WaitingList {
    size_t time_since_last_ARP_request_send = 0;
    std::queue<InternetDatagram> waiting_datagram{};
    };

CS144 LAB6 IP Router

0.概述

lab6要求实现一个“路由器”。

回顾一下,lab5已经实现了一个网络接口,这个接口具有接收和发送以太帧的功能:

  1. 接收以太帧:接收到以太帧后,如果该帧是ARP协议,则根据ARP request 或者ARP reply进行不同的动作;如果是IP协议,无动作(这正是留给lab6做的)。
  2. 发送以太帧:将IP包添加MAC头后,发送出去。

所以lab6的路由器最重要的功能就是路由匹配:接收到IP协议的以太帧后,丢掉旧的MAC头部,取出其中的IP包,对IP包进行路由匹配,然后调用网络接口的发送方法,装上新的MAC头后发送。

其中路由匹配的含义是:根据IP包要发送的目标IP地址,对比路由表中的各项,从而查询得到下一跳路由器的IP地址。

1.查询路由表的过程:

路由器匹配时会忽略主机号,只匹配网络号,一个路由表如下所示:(这里是为了和lab中的路由表对应,实际不止这么多项)

目的网络号 子网掩码 下一跳地址 接口号
10.1.1.0 255.255.255.0 192.168.0.1 eth2
10.2.2.0 255.255.255.0 192.168.0.2 eth1
0.0.0.0 0.0.0.0 192.168.0.254 eth1
192.168.1.0 255.255.255.0 192.168.0.3 eth1
192.168.2.5 255.255.255.255 192.168.0.4 eth0
172.16.0.0 255.240.0.0 192.168.0.5 eth2

lab中已经给出了add_route()函数的4个参数分别是 route_prefix,prefix_length,next_hop,interface_num,含义分别是 目的网络地址、子网掩码中连续1的长度、下一跳IP、接口号,含义分别是:

  • route_prefix:直译为“路由前缀”,含义是IP地址的网络号,用来标识一个网络,之所以用“前缀”,是因为IP地址分为网络号和主机号,网络号处于IP地址的前缀部分。
  • prefix_length:前缀长度,是指子网掩码中,连续1的长度,之所以叫前缀长度,是因为 (IP & 子网掩码 = 网络号),
  • next_hop:下一跳的IP地址,是我们要查询的内容。
  • interface_num:接口号,一个路由器可以有n个接口,每个接口可以发往不同IP地址,而是我们想查询的内容。

匹配时需要将目的IP地址和表中的子网掩码按位与,得到的结果与目的网络号比较,若一致则匹配成功,另外lab doc中讲到:If the router is directly attached to the network in question, the next hop will be an empty optional ,In that case, the next hop is the datagram’s destination address. .所以如果 next_hop 为空,说明目的IP就和本router直接相连,选择目的IP直接发送就好~

int match_index = -1;
//遍历路由表
for (size_t i = 0; i < _routing_table.size(); i++) {
auto mask = numeric_limits<int>::min()>>(_routing_table[i] - 1);
if ((dst_ip_addr & mask) == _routing_table[i]._route_prefix) {
//匹配成功
match_index = i;
}
} if(match_index == -1) {
//发送ICMP,但是lab6不要求
} //路由匹配完成:得到了下一跳的 IP地址 和 接口号
auto next_hop = _routing_table[match_index]._next_hop;
auto interface_num = _routing_table[match_index]._interface_num; if (next_hop.has_value()) {
_interfaces[interface_num].send_datagram(dgram, next_hop.value());
} else {
//
_interfaces[interface_num].send_datagram(dgram, Address::from_ipv4_numeric(dst_ip_addr));
}

2.TTL到底是秒数还是跳数?

在几乎所有的TTL中文资料中,都会讲到每经过一个路由器,TTL就会减1,所以TTL的含义是路由器的跳数,当减为0时,这个包会被抛弃,但是TTL是time to live,怎么和跳数联系到一起呢?为此,我查阅了RFC791,里面讲到:

也就是说,TTL的定义的确是,每经过一个路由器,处理时间小于1秒,也会按照1s计算,那处理时间是2s、3s是不是就要-2、-3了呢?是的,从TCP协议规定来看,作者是想要-2、-3的,但是从编码实现角度考虑,需要计算每个包在router之间传输的时间,个人猜测,很可能是牺牲了这部分时间精度,选择粗暴地每次都减1,以换来编码实现的简化。

维基百科中也提到了,实际的实现中都是每次减1,所以为了照顾这种实现,TCP协议在IPv6中已经将TTL改名为hop limit,哈哈哈,感觉还是很有意思的,协议的制定者为了协议的实现者而妥协,很有趣,不是么~

3.结尾

历时4周吧,终于写完了CS144,除了lab4花费一周时间,其余lab均花费约半周的时间,这种沉浸式体验的感觉还是很好的,吃饭,走路的时候脑子里都在想着这些lab,实在是太爽了,截止到目前为止,已经完成了CSAPP、MIT6.S081、CS144的所有lab,下一门向CMU15-445进发~

CS144 LAB5~LAB6的更多相关文章

  1. OS--lab0+lab1+lab4+lab5+lab6+lab7

    URL:https://github.com/Chasssser/MytestOR(Linux) git clone https://github.com/Chasssser/Mytest

  2. ucore操作系统学习(七) ucore lab7同步互斥

    1. ucore lab7介绍 ucore在前面的实验中实现了进程/线程机制,并在lab6中实现了抢占式的线程调度机制.基于中断的抢占式线程调度机制使得线程在执行的过程中随时可能被操作系统打断,被阻塞 ...

  3. 【计算机网络】Stanford CS144 Lab Assignments 学习笔记

    本文为我的斯坦福计算机网络课的编程实验(Lab Assignments)的学习总结.课程全称:CS 144: Introduction to Computer Networking. 事情发生于我读了 ...

  4. Lab6: Paxos

    Introduction In labs 6 and 7, you will replicate the lock service using the replicated state machine ...

  5. HIT Software Construction Lab6引发出来对锁的问题的探究

    前言 做完lab5开始做lab6了鸭,哈工大计算机学院的学生永不停歇.在做lab6的时候,我在想移动猴子是锁一整个ladder(ADT)还是只锁一个ladder的一个域Monkey数组呢?这两个好像差 ...

  6. 《ucore lab6》实验报告

    资源 ucore在线实验指导书 我的ucore实验代码 练习1: 使用 Round Robin 调度算法(不需要编码) 题目 完成练习0后,建议大家比较一下(可用kdiff3等文件比较软件) 个人完成 ...

  7. ucore操作系统学习(五) ucore lab5用户进程管理

    1. ucore lab5介绍 ucore在lab4中实现了进程/线程机制,能够创建并进行内核线程的调度.通过上下文的切换令线程分时的获得CPU,使得不同线程能够并发的运行. 在lab5中需要更进一步 ...

  8. ucore操作系统学习(六) ucore lab6线程调度器

    1. ucore lab6介绍 ucore在lab5中实现了较为完整的进程/线程机制,能够创建和管理位于内核态或用户态的多个线程,让不同的线程通过上下文切换并发的执行,最大化利用CPU硬件资源.uco ...

  9. ucore lab6 调度管理机制 学习笔记

    这节虽叫调度管理机制,整篇下来主要就讲了几个调度算法.兴许是考虑到LAB5难,LAB6就仁慈了一把,难度大跳水.平常讲两节原理做一个实验,这次就上了一节原理.权当大战后的小憩吧. schedule函数 ...

  10. CSAPP 六个重要的实验 lab5

    CSAPP  && lab5 实验指导书: http://download.csdn.net/detail/u011368821/7951657 实验材料: http://downlo ...

随机推荐

  1. c语言趣味编程(4)抓交通肇事犯

    一.问题描述 一辆卡车违反交通规则,撞人后逃跑.现场有三人目击该事件,但都没有记住车号,只记下车号的一些特征. 甲说:牌照的前两位数字是相同的: 乙说:牌照的后两位数字是相同的,但与前两位不同: 丙是 ...

  2. Tars-Cpp 协程实现分析

    作者:vivo 互联网服务器团队- Ye Feng 本文介绍了协程的概念,并讨论了 Tars Cpp 协程的实现原理和源码分析. 一.前言 Tars 是 Linux 基金会的开源项目(https:// ...

  3. flume组件以及通过命令监控大数据平台转态

    实验一.Flume 组件安装配置 1.下载和解压 Flume 可 以 从 官 网 下 载 Flume 组 件 安 装 包 , 下 载 地 址 如 下 URL 链 接 所 示 https://archi ...

  4. CopyOnWriteArrayList的使用和优缺点

    CopyOnWriteArrayList允许并发读,读操作无锁,性能较高: 而写操作(含删除),比如向容器中添加/删除一个元素,则首先将当前容器复制一份,然后在新副本上执行写操作,结束之后再将原容器的 ...

  5. 聊一聊redis十种数据类型及底层原理

    概述 Redis 是一个开源的高性能键值数据库,它支持多种数据类型,可以满足不同的业务需求.本文将介绍 Redis 的10种数据类型,分别是 string(字符串) hash(哈希) list(列表) ...

  6. blob转string,同步调用

    问题背景 通过接口下载文件的时候,后端设置的responseHeader content-disposition: attachment;filename=文件名.xlsx content-type: ...

  7. Git&GitHub简介与入手(二)

    四.GitHub 1.建账号,仓库 https://github.com/  用邮箱在官网注册: 增加远程库的地址取别名为origin,push为推送,fetch为取回: 2.推送操作 将本地当前所在 ...

  8. Git&GitHub简介与入手(一)

    一.Git版本控制 1.集中式版本控制工具:SVN(版本控制集中在服务器端,会有单点故障风险): 2.分布式版本控制工具:Git: 3.Git简史 Talk is cheap, show me the ...

  9. LOTO示波器如何测试阻抗的频响曲线

    LOTO示波器如何测试阻抗的频响曲线 模块的输入输出端口,在电路分析上,一般简单表征为电阻来进行计算和分析.但多数情况下,这些端口并不是纯电阻的特性,更精确一些,它可能是电阻电容以及电感的组合,表现为 ...

  10. 贪心算法基础及leetcode例题

    参考 理论 本质:找到每个阶段的局部最优,然后去推导得到全局最优 两个极端:常识&&很难: 很多同学通过了贪心的题目,但都不知道自己用了贪心算法,因为贪心有时候就是常识性的推导,所以会 ...