建议的实验思路¶
推荐的实验流程是:
- 在 TanLabs 上创建仓库,克隆到本地,认真阅读文档
- 运行 Example 下面的程序,保证自己环境正确配置了
- 进行 Homework 的编写,完成四个编程作业
- 把上一步实现的几个函数和 HAL 配合使用,实现一个真实可用的路由器
建议采用的一些调试工具和方法:
- Wireshark:无论是抓包还是查看评测用到的所有数据的格式,都是非常有用的,一定要学会
- 编写测试的输入输出,这个仓库的
Datagen
目录下有一个用 Rust 编写的 PCAP 测试样例生成程序,你可以修改它以得到更适合你的代码的测试样例,利用 Wireshark 确认你构造的样例确实是合法的,但请不要放到作业仓库中影响评测结果 - 运行一些成熟的软件,然后抓包看它们的输出是怎样的,特别是调试 RIP 协议的时候,可以自己用 BIRD(BIRD Internet Routing Daemon)跑 RIP 协议然后抓包,有条件的同学也可以自己找一台企业级的路由器进行配置(选计算机网络专题训练体验一下),当你的程序写好了也可以让你的路由器和它进行互通测试。当大家都和标准实现兼容的时候,大家之间兼容的机会就更高了。
关于第四步,一个可能的大概的框架如下:
int main() {
// 0a. 初始化 HAL,打开调试信息
HAL_Init(1, addrs);
// 0b. 创建若干条 /24 直连路由
for (int i = 0; i < N_IFACE_ON_BOARD;i++) {
RoutingTableEntry entry = {
.addr = addrs[i] & 0x00FFFFFF, // 大端
.len = 24,
.if_index = i,
.nexthop = 0 // 表示直连路由
};
update(true, entry);
}
uint64_t last_time = 0;
while (1) {
// 获取当前时间,处理定时任务
uint64_t time = HAL_GetTicks();
if (time > last_time + 5 * 1000) {
// 每 5s 做什么
// 例如:超时?发 RIP Request/Response?
last_time = time;
}
// 轮询
int mask = (1 << N_IFACE_ON_BOARD) - 1;
macaddr_t src_mac;
macaddr_t dst_mac;
int if_index;
int res = HAL_ReceiveIPPacket(mask, packet, sizeof(packet), src_mac,
dst_mac, 1000, &if_index); // 超时为 1s
if (res > 0) {
// 1. 检查是否是合法的 IP 包,可以用你编写的 validateIPChecksum 函数,还需要一些额外的检查
// 2. 检查目的地址,如果是路由器自己的 IP(或者是 RIP 的组播地址),进入 3a;否则进入 3b
// 3a.1 检查是否是合法的 RIP 包,可以用你编写的 disassemble 函数检查并从中提取出数据
// 3a.2 如果是 Response 包,就调用你编写的 query 和 update 函数进行查询和更新,
// 注意此时的 RoutingTableEntry 可能要添加新的字段(如metric、timestamp),
// 如果有路由更新的情况,可能需要构造出 RipPacket 结构体,调用你编写的 assemble 函数,
// 再把 IP 和 UDP 头补充在前面,通过 HAL_SendIPPacket 把它发到别的网口上
// 3a.3 如果是 Request 包,就遍历本地的路由表,构造出一个 RipPacket 结构体,
// 然后调用你编写的 assemble 函数,另外再把 IP 和 UDP 头补充在前面,
// 通过 HAL_SendIPPacket 发回询问的网口
// 3b.1 此时目的 IP 地址不是路由器本身,则调用你编写的 query 函数查询,
// 如果查到目的地址,如果是直连路由(nexthop = 0 表示直连路由), nexthop 改为目的 IP 地址,
// 用 HAL_ArpGetMacAddress 获取 nexthop 的 MAC 地址,如果找到了,
// 就调用你编写的 forward 函数进行 TTL 和 Checksum 的更新,
// 通过 HAL_SendIPPacket 发到指定的网口,
// 在 TTL 减到 0 的时候要求构造一个 ICMP Time Exceeded 返回给发送者;
// 如果没查到目的地址的路由,要求返回一个 ICMP Destination Network Unreachable;
// 如果没查到下一跳的 MAC 地址,HAL 会自动发出 ARP 请求,在对方回复后,下次转发时就知道了。
} else if (res == 0) {
// 一段时间没有收到包,属于正常情况,忽略即可
} else {
fprintf(stderr, "Error: %d\n", res);
break;
}
}
return 0;
}
你需要基于 Homework/router
下的代码,把里面的代码实现完全。代码中在发送 RIP 包的时候,会涉及到 IP 头的构造,由于不需要用各种高级特性,可以这么设定:V=4,IHL=5,TOS(DSCP/ECN)=0,ID=0,FLAGS/OFF=0,TTL=1,其余按照要求实现即可。对于构造的 ICMP 包,可以设置 TTL=64 。
关于如何启动并配置一个比较标准的 RIP 实现,请参考 这个页面。
关于如何在一台计算机上进行真实测试,请参考 这个页面 的第二部分。
最后更新: 2020年9月12日
作者: