CS 144 Lab Six -- building an IP router
CS 144 Lab Six -- building an IP router
- 引言
- 路由器的实现
- 测试
对应课程视频: 【计算机网络】 斯坦福大学CS144课程
Lab Six 对应的PDF: Lab Checkpoint 5: building an IP router
引言
在本实验中,你将在现有的NetworkInterface
基础上实现一个IP路由器,从而结束本课程。路由器有几个网络接口,可以在其中任何一个接口上接收互联网数据报。路由器的工作是根据路由表转发它得到的数据报:一个规则列表,它告诉路由器,对于任何给定的数据报:
- 发送到哪个接口;
- 下一跳的IP地址 ;
你的工作是实现一个路由器,它可以为任何给定的数据报计算出这两件事。(你不需要实现设置路由表的算法,例如RIP、OSPF、BGP或SDN控制器,只需要实现跟随路由表的算法)。
你对路由器的实现将使用带有新的Router
类的Sponge库,以及在模拟网络中检查你的路由器功能的测试。实验6建立在你在实验5中对NetworkInterface
的实现之上,但不使用你在实验0-4中实现的TCP栈。IP路由器不需要知道任何关于TCP、ARP或以太网的信息(仅限IP)。我们希望你的实现将需要大约25-30行的代码。
图1:路由器包含多个网络接口,可以在其中任何一个接口上接收IP数据报。路由器将接收到的任何数据报转发到相应出站接口上的下一跳,路由表告诉路由器如何做出这个决定。
路由器的实现
AsyncNetworkInterface:
- 它是对 NetworkInterface 类的包装,用于使主机端接口变成异步的。
- 在原始的 NetworkInterface 类的基础上,AsyncNetworkInterface 将接收到的数据报保存在队列中,而不是立即返回给调用者,以便稍后检索。
- 同时,AsyncNetworkInterface 在其他方面与底层的 NetworkInterface 实现完全相同。
class AsyncNetworkInterface : public NetworkInterface {std::queue<InternetDatagram> _datagrams_out{};public:using NetworkInterface::NetworkInterface;//! Construct from a NetworkInterfaceAsyncNetworkInterface(NetworkInterface &&interface) : NetworkInterface(interface) {}//! \brief Receives and Ethernet frame and responds appropriately.//! - If type is IPv4, pushes to the `datagrams_out` queue for later retrieval by the owner.//! - If type is ARP request, learn a mapping from the "sender" fields, and send an ARP reply.//! - If type is ARP reply, learn a mapping from the "target" fields.//!//! \param[in] frame the incoming Ethernet framevoid recv_frame(const EthernetFrame &frame) {auto optional_dgram = NetworkInterface::recv_frame(frame);// 只会将IPV4数据报放入数据报接收队列中if (optional_dgram.has_value()) {_datagrams_out.push(std::move(optional_dgram.value()));}};//! Access queue of Internet datagrams that have been receivedstd::queue<InternetDatagram> &datagrams_out() { return _datagrams_out; }
};
这里的 Router 实现比较简单,只需实现一下 IP 最长匹配并将数据包转发即可:
Router.hh:
//! \brief A router that has multiple network interfaces and
//! performs longest-prefix-match routing between them.
class Router {//! The router's collection of network interfaces// 当前路由器的网络接口集合std::vector<AsyncNetworkInterface> _interfaces{};//! Send a single datagram from the appropriate outbound interface to the next hop,//! as specified by the route with the longest prefix_length that matches the//! datagram's destination address.// 路由一个IP数据报void route_one_datagram(InternetDatagram &dgram);// 路由表条目struct RouterTableEntry {// 路由前缀const uint32_t route_prefix;// 前缀长度const uint8_t prefix_length;// 下一跳的IP地址const std::optional<Address> next_hop;// 对应哪一个网络接口const size_t interface_idx;};// 路由表std::vector<RouterTableEntry> _router_table{};public://! Add an interface to the router//! \param[in] interface an already-constructed network interface//! \returns The index of the interface after it has been added to the router// 向路由表添加网络接口size_t add_interface(AsyncNetworkInterface &&interface) {_interfaces.push_back(std::move(interface));return _interfaces.size() - 1;}//! Access an interface by index -- 根据索引获取某一个网络接口AsyncNetworkInterface &interface(const size_t N) { return _interfaces.at(N); }//! Add a route (a forwarding rule)// 增加路由条目void add_route(const uint32_t route_prefix,const uint8_t prefix_length,const std::optional<Address> next_hop,const size_t interface_num);//! Route packets between the interfacesvoid route();
};
Router.cc:
- add_route : 向路由表中添加路由条目
// 向路由表中添加路由条目
void Router::add_route(const uint32_t route_prefix,const uint8_t prefix_length,const optional<Address> next_hop,const size_t interface_num) {cerr << "DEBUG: adding route " << Address::from_ipv4_numeric(route_prefix).ip() << "/" << int(prefix_length)<< " => " << (next_hop.has_value() ? next_hop->ip() : "(direct)") << " on interface " << interface_num << "\n";_router_table.push_back({route_prefix, prefix_length, next_hop, interface_num});
}
- route_one_datagram: 根据路由表完成当前IP数据报的路由工作
//! \param[in] dgram The datagram to be routed
// 根据路由表进行路由
void Router::route_one_datagram(InternetDatagram &dgram) {// 获取目的ip地址const uint32_t dst_ip_addr = dgram.header().dst;auto max_matched_entry = _router_table.end();// 开始查询for (auto router_entry_iter = _router_table.begin(); router_entry_iter != _router_table.end();router_entry_iter++) {// 如果前缀匹配匹配长度为 0,或者前缀匹配相同if (router_entry_iter->prefix_length == 0 ||(router_entry_iter->route_prefix ^ dst_ip_addr) >> (32 - router_entry_iter->prefix_length) == 0) {// 如果条件符合,则更新最匹配的条目if (max_matched_entry == _router_table.end() ||max_matched_entry->prefix_length < router_entry_iter->prefix_length)max_matched_entry = router_entry_iter;}}// 将数据包 TTL 减去1// 如果存在最匹配的,并且数据包仍然存活,则将其转发if (max_matched_entry != _router_table.end() && dgram.header().ttl-- > 1) {// 获取下一条IP地址const optional<Address> next_hop = max_matched_entry->next_hop;// 获取对应的网络接口AsyncNetworkInterface &interface = _interfaces[max_matched_entry->interface_idx];// 目标主机是否位于与路由器相同的网络中。// 在这种情况下,下一跳字段可能为空,因为目标主机可以直接通过局域网访问,无需经过路由器。if (next_hop.has_value())// 交给NetworkInterface,将这个数据报发送出去interface.send_datagram(dgram, next_hop.value());else// 目的主机与路由器位于相同的网络中interface.send_datagram(dgram, Address::from_ipv4_numeric(dst_ip_addr));}// 其他情况下则丢弃该数据包
}
上面的代码中,next_hop.has_value()
为 false
表示没有下一跳(next hop)地址,即无法找到用于转发数据包的下一跳。这可能发生在以下情况下:
-
直接连接目标主机: 路由表中可能存在直接连接目标主机的路由条目,也就是目标主机位于与路由器相同的网络中。在这种情况下,下一跳字段可能为空,因为目标主机可以直接通过局域网访问,无需经过路由器。
-
默认路由: 路由表中通常会包含默认路由(default route),也称为默认网关(default gateway)。默认路由是指当没有更精确的路由匹配时,所有未知目标IP地址的数据包将会通过默认路由进行转发。在这种情况下,下一跳字段可能为空,因为默认路由指定了一个特定的网络接口,将数据包发送到该接口,由默认网关负责将数据包转发到外部网络。
需要注意的是,在实际网络中,路由表会根据网络拓扑和路由策略进行配置,以确保数据包能够正确地转发到目标。路由表中的路由条目根据目标网络地址的前缀匹配来确定数据包的转发规则。当无法找到匹配的路由条目时,数据包将根据默认路由进行转发,或者如果没有默认路由,则会被丢弃。
- Route: AsyncNetworkInterface会将接收到的IP数据报暂存在队列中,由Route方法负责从队列取出并进行路由
void Router::route() {// Go through all the interfaces, and route every incoming datagram to its proper outgoing interface.// 依次遍历当前路由器内部每个网络接口,依次取出每个AsyncNetworkInterface的待传输队列datagrams_outfor (auto &interface : _interfaces) {auto &queue = interface.datagrams_out();// 如果待路由队列不为空,则依次取出进行路由while (not queue.empty()) {route_one_datagram(queue.front());queue.pop();}}
}
测试
这是 CS144 的测试网络拓扑:
测试结果:
相关文章:

CS 144 Lab Six -- building an IP router
CS 144 Lab Six -- building an IP router 引言路由器的实现测试 对应课程视频: 【计算机网络】 斯坦福大学CS144课程 Lab Six 对应的PDF: Lab Checkpoint 5: building an IP router 引言 在本实验中,你将在现有的NetworkInterface基础上实现一个IP路由器…...

edge://settings/defaultbrowser default ie
Microsoft Edge 中的 Internet Explorer 模式 有些网站专为与 Internet Explorer 一起使用,它们具有 Microsoft Edge 等新式浏览器不支持的功能。 如果你需要查看其中的某个网站,可使用 Microsoft Edge 中的 Internet Explorer 模式。 大多数网站在新…...

Centos7安装jdk8教程——rpm安装
1. rpm文件下载 下载链接 Java SE 8 Archive Downloads (JDK 8u211 and later) 2.上传到服务器指定路径下并安装 切换到上传目录,然后执行以下命令 rpm -ivh jdk-8u221-linux-x64.rpm3. 设置环境变量并重载配置 # 设置环境变量 vim /etc/profile# 文件末尾添加…...
Node.js-path模块操作路径的基本使用
path模块提供了操作路径的功能,以下为常用的API。 path.resolve():拼接规范的绝对路径 const path require("path"); // 目录的绝对路径 // __dirname: D:\node\path const pathStr path.resolve(__dirname, "index.html"); // 拼…...
油猴脚本:验证码识别辅助器
脚本信息 描述:当鼠标放在验证码图片上时,显示弹窗并提供识别选项 实现逻辑 定义了一个isRectangle函数,用于判断图片是否符合验证码的特征。判断条件是:图片的宽高比大于1.5,宽度大于等于80且高度大于等于30&#…...
【力扣】24. 两两交换链表中的节点 <栈>
【力扣】24. 两两交换链表中的节点 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。 示例 1: 输入:hea…...
C#中控件的invoke方法
https://www.exyb.cn/news/show-280348.html 在用.NET Framework框架的WinForm构建GUI程序界面时,如果要在控件的事件响应函数中改变控件的状态,例如:某个按钮上的文本原先叫“打开”,单击之后按钮上的文本显示“关闭”࿰…...

x光下危险物品/违禁物品目标识别的模型训练与推理代码
前言 1.安检在公共场合的重要性不言而喻,保障群众人身安全是其首要任务。在各种场合,安检都是不可或缺的环节。x光安检机作为安检的重要工具,尽管其具有人工监控判断成像的特性,但是其局限性也十分明显。 为了解决这一局限性为出…...
基于Matlab实现图像融合技术(附上多个仿真源码+数据)
图像融合技术是一种将多幅图像融合为一幅图像的方法,使得这幅融合图像包含原始图像的所有信息。近年来,图像融合技术已经广泛应用于图像分割、变换和裁剪等领域。本文将介绍如何使用Matlab实现图像融合技术。 实现步骤 首先,我们需要了解图…...

国家级与省级开发区设立超长时间段数据(1970-2022年)
在基于因果推断方法的政策评估备受经济学研究欢迎的今天,将一个重要政策作为外生冲击进行计量建模这一做法,是很多顶刊论文的“宠儿”,大家整理分享的国家级与省级开发区设立超长时间段数据正是其中的代表。 我国各级人民ZF均将开发区设立作为…...
数据结构 10-排序4 统计工龄 桶排序/计数排序(C语言)
给定公司名员工的工龄,要求按工龄增序输出每个工龄段有多少员工。 输入格式: 输入首先给出正整数(≤),即员工总人数;随后给出个整数,即每个员工的工龄,范围在[0, 50]。 输出格式: 按工龄的递…...

SpringBoot复习:(16)TomcatStarter
直接在idea里运行SpringBoot程序时,内嵌的tomcat容器会调用TomcatStarter这个类的onStartup方法。TomcatStarter继承自ServletContainerInitializer 其onStartup方法会调用ServletContextInitializer(不是ServletContainerInitializer)的onStartup方法.…...

RISCV 5 RISC-V调用规则
RISCV 5 RISC-V调用规则 1 Register Convention1.1 Integer Register Convention1.2 Floating-point Register Convention 2. Procedure Calling Convention2.1 Integer Calling Convention2.2 Hardware Floating-point Calling Convention2.3 ILP32E Calling Convention2.4 Na…...

Spring Boot如何整合mybatis
文章目录 1. 相关配置和代码2. 整合原理2.1 springboot自动配置2.2 MybatisAutoConfiguration2.3 debug过程2.3.1 AutoConfiguredMapperScannerRegistrar2.3.2 MapperScannerConfigurer2.3.4 创建MapperFactoryBean2.3.5 创建MybatisAutoConfiguration2.3.6 创建sqlSessionFact…...
TypeScript中 interface 和 type 的区别
区别1 使用 interface 和 type 都是表示给定数据结构的常用方法。定义的方式略有不同。type 定义的时候有 “” 符号 interface User {name: string,age: number } type User {name: string,age: number }区别2 interface 可以多次声明同一接口。它们将合并在一起形成一个接…...
题解 | #B.Distance# 2023牛客暑期多校6
B.Distance 贪心(?) 题目大意 对于两个大小相同的多重集 A , B \mathbb{A},\mathbb{B} A,B ,可以选择其中任一元素 x x x 执行操作 x x 1 xx1 xx1 任意次数,最少的使得 A , B \mathbb{A},\mathbb{B} A,B 相同的操作次数记为 C ( A , B ) C(\m…...

【flink】开启savepoint
先启动一个任务 flink run -c com.yang.flink.CDCJob test-cdc.jar开启savepoint 命令: flink savepoint JobID 文件地址 flink savepoint e929a11d79bdc5e6f140f2cfb92e1335 file:///workspace/flinkSavepoints/backend这样就开启好了 操作中的错误 详细信…...

【C++】开源:事件驱动网络库libevent配置使用
😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍事件驱动库libevent配置使用。 无专精则不能成,无涉猎则不能通。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下…...
业务测试——历史数据
业务测试历史数据的必要性 1.保留上一版本的呈现效果以及数据正确性 2.做发版前后数据、样式一致性校验 3.后端处理历史数据,覆盖各类场景,保证客户的现有数据不会被影响,造成线上事务 4.为测试过程的覆盖度以及产品迭代的质量保驾护航 如何…...

【Linux】计算机网络套接字编写
文章目录 前言TCP协议和UDP协议网络字节序socket接口sockaddr结构1.创建套接字 cs2.绑定端口号 s3.监听socket s4.接受请求 s5.建立连接 c 地址转换函数字符串转in_addrin_addr转字符串 recvfrom和sendto 前言 上篇文章我们学习了计算机网络分层,了解了网络通信的本…...

智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建
【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...

篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...

倒装芯片凸点成型工艺
UBM(Under Bump Metallization)与Bump(焊球)形成工艺流程。我们可以将整张流程图分为三大阶段来理解: 🔧 一、UBM(Under Bump Metallization)工艺流程(黄色区域ÿ…...