CS144(2024 Winter)Lab Checkpoint 5: down the stack (the network interface)

0.Overview

check5.pdf

在本节实验中,我们将下降到网络栈的底层,实现一个 NetworkInterface 接口类,完成网络层和数据链路层之间的交互传输组件。

和前几次实验相比,这次实验的难度算得上是相当简单,就连给出的单元测试也只有 2 个。

但是因为课程提倡向仓库发起对于测试集的 PR,所以每个新 lab 的测试代码都会新增一些代码发布时没有的 unit test。
因此建议是每 merge 一个新的分支,都重新跑一次最完整的一个 unit test(一般都是指 checkpoint3 的 unit test),保证自己以前的代码是完全没问题的。

1.Getting started

这个 checkpoint 的实验很简单了,因为数据链路层的服务是“尽最大努力交付”,所以我们在实现时只需要不断往外丢数据帧即可。

然后因为数据链路层是主要负责把网络层传来的 IP 数据报包装为以太网帧,或者是把以太网帧解离为 IP 数据报,所以我们还需要实现数据报解析和转发过程中必不可少的地址解析协议 ARP。

难点也不是很多,接下来只介绍一下给定的 starter code。

顺便一提,本节实验可以使用 Docker 完成。

2.需求分析

前置知识:
在数据链路层中,ARP 协议实际上负责 IP 地址和 MAC 地址(代码中称其为 ethernet_address_,以太网地址)之间的映射,这个映射关系完全由抽象的网络接口 NetworkInterface 自己学习。
NetworkInterface 转发 IP 数据报时发现找不到下一跳 IP 地址所对应的以太网地址(查表),就会向网络中广播一个 ARP 请求,并缓存这个数据报直到 ARP 请求得到响应。
相对的,如果 NetworkInterface 收到了一个 ARP 请求,并且该请求所请求的 IP 地址和自己的对上了,就会向该请求的发送方回传一个 ARP 响应。
为了学习网络中 IP-以太网地址的映射关系,NetworkInterface 需要维护一张映射表。而为了适应动态变化的网络拓扑结构,这张映射表肯定需要定时强制更新;文档要求:这张表中的每个映射关系最长只能存在 30 秒。

2.1 NetworkInterface::send_datagram 方法

在该方法中,我们要将给定的下一跳 IP 地址转换为对应的以太网地址,并把 IP 数据报封装为以太网帧的 payload。

当目的以太网地址已知时,使用 serialize() 函数将 dgram 序列化为 std::vector<std::string> 类型,并装入 EthernetFrame::payload 中;接着完成以太网帧头部 EthernetFrame::header 的变量设置,最后把组装好的数据帧转发出去。

如果目的以太网地址未知,这时就需要组装一个 ARPMessage 请求对应的以太网地址,再将这个 ARP 请求序列化后装载以太网帧中发出。

文档提到:为了避免频繁的 ARP 请求阻塞网络,我们需要保证五秒内,相同的 IP 地址的 ARP 请求只发出一次。

send_datagram

2.2 NetworkInterface::recv_frame 方法

这个方法需要过滤掉目的以太网地址既不是广播地址(ETHERNET_BROADCAST)、也不是本接口的以太网地址(ehternet_address_)的数据帧。

如果数据帧的目的地址是本接口,那么就需要按照数据帧头部指出的协议类型,将数据帧解析为对应的数据报类型(使用 parse())。

当数据帧的协议是 IPv4 时,且解析成功(parse() 返回值为 true),那么就把解析得到的 InternetDatagram 数据报推入队列 datagrams_received_ 中。

若数据帧协议为 ARP,且解析成功,那么按照得到的 ARPMessage 中的信息,分析是否是请求本接口的 IP 地址和以太网地址的映射关系、或者是否是响应之前本接口发出的 ARP 请求。如果是 ARP 请求,那么就组装对应的 ARPMessage 并发送给请求发送方;如果是 ARP 响应,那么就将先前缓存的、现在能发送的 IP 数据报全部发送出去。

注意这里无论是 ARP 请求还是 ARP 响应,只要数据帧解析成功,都要从中学习新的地址映射关系。

recv_frame

2.3 NetworkInterface::tick 方法

这个方法负责告知接口已经过去了多长时间,用于更新映射表和 ARP 请求限制(5 秒内不能发出对相同 IP 的 ARP 请求)。

2.4 Q&A

Q&A 中指出了几点细节:

  1. 100 到 150 行的代码实现是合理的;
  2. 调用 transmit() 方法发出数据帧;
  3. 自行决定 IP 和以太网地址的映射关系的数据结构实现;
  4. 使用 Address::ipv4_numeric() 方法将一个 ipv4 地址转换为 32 位无符号整数;
  5. 不用考虑 ARP 请求始终未得到响应的可能。

3.代码实现

从需求分析可以得知,本次实验有比较频繁的查表需求。

值得高兴的是,文档提到 IP 地址本身可以被转换为一个无符号整数,所以我们可以使用诸如 std::mapstd::unordered_map 等容器设计我们的映射表、数据报缓存等等。而我们主要要面对的工作是从 IP 地址出发去查找对应的以太网地址,因此插入和查询开销接近 O(1) 的 std::unordered_map 就是不二之选。

所以现在可以有这样一个思路:

  1. 映射表和 ARP 请求限制使用 std::unordered_map 实现,主键就是 uint32_t 状态下的 IP 地址;
  2. 数据报缓存使用 std::unordered_multimap 实现,主键定义同上,这是因为同一个目的 IP 地址可以对应很多个 IP 报文。

因为同一个 IP 地址可以对应多个报文,所以在收到一个 ARP 响应时,可以使用 std::unordered_multimap::equal_range 找出所有下一跳地址相同的 IP 报文,然后逐一转发即可。

network_interface

test_result

  • 15
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
这个错误信息是由于 MySQL 找不到一个有效的检查点 (checkpoint) 导致的。在 MySQL 中,检查点是一个内部数据结构,用于记录数据库的状态,防止在恢复数据库时出现数据丢失。 当 MySQL 启动时,它会检查最后一个检查点,并从该检查点开始恢复数据。如果 MySQL 找不到最后一个检查点,或者找到的检查点已经过时或已损坏,则会出现类似 “Ignoring the redo log due to missing MLOG_CHECKPOINT” 的错误信息。 解决这个问题的方法是尝试恢复数据库。您可以使用 `mysqlbinlog` 命令来查看 MySQL 重做日志文件,并尝试手动恢复数据。 具体步骤如下: 1. 找到 MySQL 重做日志文件的位置。在 MySQL 配置文件中查找 `datadir` 参数,该参数指定了 MySQL 数据目录的位置。在该目录下,找到名为 `ib_logfile0` 和 `ib_logfile1` 的文件,这些文件就是 MySQL 重做日志文件。 2. 备份 MySQL 数据库。在执行任何恢复操作之前,务必备份数据库,以避免数据丢失。 3. 将重做日志文件转换为可读格式。使用以下命令将重做日志文件转换为可读格式: ```shell mysqlbinlog --start-position=<checkpoint_position> --stop-position=<end_position> <logfile> > <output_file> ``` 其中,`<checkpoint_position>` 是检查点的位置,可以在错误信息中找到;`<end_position>` 是重做日志文件的结尾位置,可以通过查看文件大小来确定;`<logfile>` 是重做日志文件的路径;`<output_file>` 是输出文件的路径。 4. 手动恢复数据。根据输出文件中的信息,手动恢复数据。这可能需要一些 MySQL 知识和经验。 5. 重启 MySQL 服务。在完成恢复操作后,重启 MySQL 服务,并确保一切正常。 需要注意的是,手动恢复数据有一定的风险,可能会导致数据丢失或不一致。因此,在进行恢复操作之前,请务必备份数据库,并确保您知道自己在做什么。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值