Lab Checkpoint 4: down the stack (the network interface)
0. Overview
Lab4 要求实现网络接口部分,打通网络数据报(Internet datagrams)和链路层的以太网帧(link-layer Ethernet frames)之间的桥梁。之前的实验实现了 TCP segments 在使用 TCP 协议的设备之间的传输,而 TCP 报文有三种方式可被传送至远程服务器:
TCP-in-UDP-in-IP:TCP 报文会被置于用户的数据报的 payload 中,在用户空间下这是最简单的实现方式:Linux 提供接口(如 UDPSocket),而用户仅需要提供 payload,目标地址,Linux 内核会负责将 UDP 报部,IP 报头,以太网报头组装起来,将这个网络包发向下一个 hop。Linux 内核会保证每个 socket 具有独占的本地与远端地址以及端口号,并且保证这些数据在应用层的相互隔离。
TCP-in-IP:一般情况下,TCP 报文会直接放在 Internet datagrams 中,这通常被成为 “TCP/IP”。Linux 会提供一个 TUN 设备接口,需要应用层提供整个 Internet datagram,而 Linux 内核则会处理剩下的部分。但此时应用层需要自己构建整个 IP 报头以及 payload 部分。
TCP-in-IP-in-Ethernet:以上的方法依赖 Linux 内核来实现的协议栈操作,每次用户向 TUN 设备写入 IP datagrams 时,Linux 都需要构建正确的带有 IP datagrams 的以太网帧作为 payload。这意味着 Linux 需要知悉下一跳的 IP 地址对应的以太网目的地址,否则 Linux 会以广播的形式请求这些信息:”Who claims the following IP address ? “、”What’s your Ethernet address ? “
这些功能是由 Network Interface 实现的,该组件能将 IP 数据报转义成以太网帧等等,之后会传入 TAP 设备(类似 TUN 设备但更底层),实现对 link-layer 的数据帧的传输。
网络接口的大部分工作是:为每个下一跳 IP 地址查找(和缓存)对应的以太网地址。而对应的协议被称为:**地址解析协议 ARP (Address Resolution Protocol)**。
2. Checkpoint 4: The Address Resolution Protocol
Lab4 Network Interface 的主要任务:维护一个 IP 地址到 Ethernet 地址的映射表。这个映射类似缓存,或称作 “soft state”,能提高网络栈的传输效率。
void NetworkInterface::send_datagram(const InternetDatagram &dgram, constAddress &next_hop);
该方法被 TCPConnection 或者 router 所调用,这个接口就是将待发送的 Internet (IP) datagrams 转换成以太网帧并最终发送出去。如果以太网目的地址已知就直接发送,创建以太网帧(
type = EthernetHeader::TYPE_IPv4
),将 payload 设置为串行的数据报文,并设置源地址和目标地址。如果以太网目的地址未知,广播下一跳的以太网地址的 ARP 请求,并将 IP 报文放入队列中待 ARP 回复收到后将其发送出去。
注意事项:需要间隔 $5$ 秒再发送相同的 ARP 请求,并且只有在收到目的以太网地址后再将数据报放入队列中。若没有收到目的以太网地址,需要将 datagram 以及其对应的 Address 都暂存在列表中
optional<InternetDatagram> NetworkInterface::recv_frame(const EthernetFrame& frame);
该方法接收来自网络的以太网帧,但需要忽略任何目的地址非网络接口部分的帧 (也就是只接受广播地址或者接口自身的以太网地址)。若为 IPv4 帧就将其以 InternetDatagram 进行解析,若成功则将解析的 InternetDatagram 返回给调用者。
若为 ARP 帧就将其以 ARPMessage 进行解析,若成功则缓存发送方 IP 地址与以太网帧的映射 30 秒。若这个 ARP 请求是询问我们的 IP 地址,就发送正确的 ARP 答复。
std::optional<EthernetFrame> maybe_send();
在必要时发送 EthernetFrame。void NetworkInterface::tick(const size_t ms_since_last_tick);
记录时间,使得任何已经过期的 IP 地址到 Ethernet 地址的映射失效。
整体代码如下:
network_interface.hh
1 |
|
network_interface.cc
1 |
|
运行测试,结果如下: