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. FBV和CBV的区别(源码分析)

    FBV和CBV源码分析 FBV直接调用user方法执行业务代码 CBV相当于在FBV上面封装了一层 from django.contrib import admin from django.urls ...

  2. 聊一聊 Valgrind 监视非托管内存泄露和崩溃

    一:背景 1. 讲故事 只要是程序总会出现各种莫名其妙的问题,比如:非托管内存泄露,程序崩溃,在 Windows 平台上一般用微软自家的官方工具 App Verifier 就可以洞察,那问题出在 Li ...

  3. 2022-09-19:给定字符串 S and T,找出 S 中最短的(连续)子串 W ,使得 T 是 W 的 子序列 。 如果 S 中没有窗口可以包含 T 中的所有字符,返回空字符串 ““。 如果有不

    2022-09-19:给定字符串 S and T,找出 S 中最短的(连续)子串 W ,使得 T 是 W 的 子序列 . 如果 S 中没有窗口可以包含 T 中的所有字符,返回空字符串 "&q ...

  4. 2022-08-28:把字符串 s 看作 “abcdefghijklmnopqrstuvwxyz“ 的无限环绕字符串, 所以 s 看起来是这样的: ...zabcdefghijklmnopqrstuv

    2022-08-28:把字符串 s 看作 "abcdefghijklmnopqrstuvwxyz" 的无限环绕字符串, 所以 s 看起来是这样的: -zabcdefghijklmn ...

  5. 2021-07-11:给定一个棵完全二叉树,返回这棵树的节点个数,要求时间复杂度小于O(树的节点数)。

    2021-07-11:给定一个棵完全二叉树,返回这棵树的节点个数,要求时间复杂度小于O(树的节点数). 福大大 答案2021-07-11: 右树最左节点层数==左树最左节点层数,左树是满二叉树,统计左 ...

  6. vue全家桶进阶之路5:DOM文档对象模型

    一.DOM对象 DOM,全称"DocumentObjectModel(文档对象模型)",它是由W3C组织定义的一个标准. 在前端开发时,我们往往需要在页面某个地方添加一个元素或者删 ...

  7. sql server 系统表详细说明

    sql server 系统表详细说明 sysaltfiles 主数据库 保存数据库的文件syscharsets 主数据库字符集与排序顺序sysconfigures 主数据库 配置选项 syscurco ...

  8. 前端自动识别CAD图纸提取信息方法总结

    前言 CAD图纸自动识别和提取信息具有许多意义,包括以下几个方面: 提高工作效率:传统上,对于大量的CAD图纸,人工识别和提取信息是一项耗时且繁琐的任务.通过自动化这一过程,可以大大提高工作效率,节省 ...

  9. GaussDB(DWS)迁移实践丨row_number输出结果不一致

    摘要:迁移前后结果集row_number字段值前后不一致,前在DWS上运行不一致. 本文分享自华为云社区<GaussDB(DWS)迁移 - oracle兼容 --row_number输出结果不一 ...

  10. JVM系统参数

    JVM(Java虚拟机)是Java程序的运行环境,它可以通过一些系统参数进行配置和优化.以下是一些常用的JVM系统参数: 1. -Xmx: 用于设置JVM堆的最大内存大小.例如,-Xmx1g表示将堆的 ...