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. Solidity 入门

    基本语法 版本指令 所有Solidity源码都必须指明版本,用于标明Solidity编译器的版本,这是为了避免将来新的编译器破坏代码 pragma solidity ^0.4.20; // 声明版本 ...

  2. Shell在日常工作中的应用实践

    作者:京东物流 李光新 1 Shell可以帮我们做什么 作为一名测试开发工程师,在与linux服务器交互过程中,大都遇到过以下这些问题: •一次申请多台服务器,多台服务器需要安装相同软件,配置相同的环 ...

  3. 人工智能AI图像风格迁移(StyleTransfer),基于双层ControlNet(Python3.10)

    图像风格迁移(Style Transfer)是一种计算机视觉技术,旨在将一幅图像的风格应用到另一幅图像上,从而生成一幅新图像,该新图像结合了两幅原始图像的特点,目的是达到一种风格化叠加的效果,本次我们 ...

  4. 一个.Net版本的ChatGPT SDK

    ChatGPT大火,用它来写代码.写表白书.写文章.写对联.写报告.写周边...... 啥都会! 个人.小公司没有能力开发大模型,但基于开放平台,根据特定的场景开发应用,却是非常火热的. 为了避免重复 ...

  5. Natasha V5.2.2.1 稳定版正式发布.

    DotNetCore.Natasha.CSharp v5.2.2.1 使用 NMS Template 接管 CI 的部分功能. 取消 SourceLink.GitHub 的继承性. 优化几处内存占用问 ...

  6. [C++基础入门] 7、 指针

    文章目录 7 指针 7.1 指针的基本概念 7.2 指针变量的定义和使用 7.3 指针所占内存空间 7.4 空指针和野指针 7.5 const修饰指针 7.6 指针和数组 7.7 指针和函数 7.8 ...

  7. MQTT-保留消息和遗嘱消息

    保留消息 为什么需要保留消息 ​ 如果不考虑持久会话的因素,那么MQTT订阅只有在客户端连接之后才能创建,所以服务端不能提前预知某个主题会被哪些服务端订阅或者某个客户端会订阅哪些主题,所以当消息到达服 ...

  8. 各种远程工具通过ssh连接服务器

    开头 最近遇到一个新的连接方式,不能使用日常的本地通过账号连接,要通过私钥和公钥的连接方式,然后连接到服务器之后才能连接到数据库.因为之前没试过这种连接方式,所以很多工具有不同的连接方式.所以现在就记 ...

  9. 2022-07-16:以下go语言代码输出什么?A:[];B:[5];C:[5 0 0 0 0];D:[0 0 0 0 0]。 package main import ( “fmt“ )

    2022-07-16:以下go语言代码输出什么?A:[]:B:[5]:C:[5 0 0 0 0]:D:[0 0 0 0 0]. package main import ( "fmt" ...

  10. Python-3.10安装步骤

    下载地址: https://www.python.org/ftp/python/3.10.4/python-3.10.4-amd64.exe 安装:   C:\Users\liujun>pyth ...