build a Router based on Network Interface
- 在Network Interface的基础上实现一个Router
- 功能 : 根据 forwarding table (<ip_prefix - interface(port)>). 将来自ip的分组从正确的网卡接口转发出去.
- 匹配规则 : 最长前缀匹配
- 注意entry中需要记录next_hop_ip
- 我们的Router只实现了数据平面(转发),并没有实现控制平面(路由选择)
- 控制平面的路由选择算法有 LS , DV , RIP , OSPF. 详情见《自顶向下》
背景
本周在NetworkInterface之上实现一个IP Router. IP Router的任务是根据routing table转发. 对于收到的dgram,router有一系列规则
- 转发到正确的interface
- 获知下一跳的IP地址(为了使得Router将其传递给NetWork Interface , NetWork Interface根据ip获取下一跳的mac地址)
概述图
我们的任务是 实现一个router. 去解决上述两个事情.
- 我们只实现了router的数据平面 , 并没有实现router的控制平面.
- 也即我们只实现了根据router的forwarding table,并没有实现生成forwarding_table的routing algorithm路由选择算法
- You will not need to implement the algorithms that make the routing table, e.g. RIP, OSPF,BGP, or an SDN controller—just the algorithm that follows the routing table.
- IP router不必关系任何有关TCP,ARP,Ethernet协议字段的任何东西. 只关心IP协议字段即可.
接口
- Router class 应该负责
- 维护forwarding table
- Router负责处理收到的网络层的datagram. 对于每个datagram
- 正确的发到下一跳
- 转发到正确的NetworkInterface
- 重要成员 forwarding_table的entry
1
2
3
4
5
6
7
8
9
10
11
12struct RouterEntry{
const uint32_t route_prefix; // ip
const uint8_t prefix_length; // ip_perfix_len
const std::optional<Address> next_hop; // 下一跳的ip地址
const size_t interface_num; // 从哪个网卡转发出去 编号
RouterEntry(const uint32_t ip , const uint8_t prefix , const std::optional<Address> ne , const size_t interface_idx):
route_prefix(ip),prefix_length(prefix),next_hop(ne),interface_num(interface_idx)
{}
};
std::vector<RouterEntry> _forwarding_table{};
一来开始以为 既然我们的router只需要维护forwarding table并转发到正确的Interface. 因此forwarding table的entry并不需要记录next_hop_ip。只需要记录 <ip_perfix , interface>. 但由于之后forwarding_table要调用interface接口进行发送.为了使得interface可以获取到mac地址,需要传递给下一跳的ip地址, 故forwarding table的entry中还需要记录next_hop_ip
- void add_route(const uint32_t route_prefix, const uint8_t prefix_length, const optional< Address > next_hop, const size_t interface_num);
- 保存一条 {ip_perfix,network_interface}到forwarding_table中
- void route_one_datagram(InternetDatagram &dgram);
- “match-action” rule :
- 依据我们在《自顶向下》中所学,router应当对收到的dgram执行 匹配加动作 规则。而传统路由器的动作就是根据目的地转发. 也即本lab中实现的 匹配加转发
- 匹配 : 将dgram中的ip和forwarding_table中的ip进行 最长前缀匹配
- 动作 : 转发
- 如果router和dst ip在同一网络中(If the router is directly attached to the network in question) , 那么forwarding_table中的entry记录的next_hop_ip为empty, router传递给network_interface的next_hop_ip为dgram_dst_ip
- 如果router和dst ip不在同一网络中(But if the router is connected to the network in question through some other router) , 那么entry中会记录路径上的下一跳ip地址(应该是个router ip)
- 步骤如下 , 根据forwarding_table找到最长前缀匹配的entry
- 没找到 , drop the dgram
- 找到 , dgram TTL –.
- 如果TTL已经 <= 0 则 drop the dgram
- 否则 , 将dgram转发给正确的interface , 由interface将其封装成frame发送出去.
- “match-action” rule :
void Router::route()
- (Linker) 从network interface取出dgrams , (Internet) 调用route_one_datagram(dgram) , (Linker) 进而转发到正确的network interface 发送出去
- Linker -> Internet -> Linker
网络协议巧妙设计 : 分层. router只关注网络层.
- Router不必关注frame中封装的TCP 还是ARP ,也不必关注link layer frame类型.
- Router只需要关注 网络层的Internet datagram , 然后通过NetworkInterface 来和 链路层link layer进行交互
- When it comes to questions like, “How are link-layer addresses resolved?” or “Does the link layer even have its own addressing scheme distinct from IP?” or “What’s the format of the link-layer frames?” or “What’s the meaning of the datagram’s payload?”, the router just doesn’t care.
代码略 简单
ques : 如果router drop dgram : 没有找到对应entry , 或者ttl = 0 . 是否需要发送icmp 报文给源主机 ?
- 在真实生活中 , router需要发哦是那个icmp给源主机 . 但lab中这不是必需的.即便在真实生活中 , 不是所有router都需要发送icmp报文给源地址的.
测试
- 测试原理. test code中的NetWork 模拟的就是下图交互情况. NetWork中的_router就是图中中间的路由器
apleasure to cherry
app to internet
1
2
3
4
5
6
7cout << green << "\n\nSuccess! Testing applesauce sending to the Internet." << normal << "\n\n";
{
auto dgram_sent = network.host("applesauce").send_to({"1.2.3.4"});
dgram_sent.header().ttl--;
network.host("default_router").expect(dgram_sent);
network.simulate();
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22cout << green << "\n\nSuccess! Testing sending to the HS network and Internet." << normal << "\n\n";
{
auto dgram_sent = network.host("applesauce").send_to({"143.195.131.17"});
dgram_sent.header().ttl--;
network.host("hs_router").expect(dgram_sent);
network.simulate();
dgram_sent = network.host("cherrypie").send_to({"143.195.193.52"});
dgram_sent.header().ttl--;
network.host("hs_router").expect(dgram_sent);
network.simulate();
dgram_sent = network.host("cherrypie").send_to({"143.195.223.255"});
dgram_sent.header().ttl--;
network.host("hs_router").expect(dgram_sent);
network.simulate();
dgram_sent = network.host("cherrypie").send_to({"143.195.224.0"});
dgram_sent.header().ttl--;
network.host("default_router").expect(dgram_sent);
network.simulate();
}同一LAN
1
2
3
4auto dgram_sent = network.host("dm42").send_to(network.host("dm43").address());
dgram_sent.header().ttl--;
network.host("dm43").expect(dgram_sent);
network.simulate();