Getting started
首先是版本控制操作,从lab5分支创建用于lab6开发的分支,我是在Clion的Git图形操作界面设定的。然后按照文档,从远程仓库拉取lab6的实验内容
//在新分支dev-lab6下
git fetch
git merge origin/lab6-startercode
make -j4
会弹出一个询问界面,让你提交此次合并到你本地仓库分支的说明commit,可以不管,直接ctrl+X键。
简易路由实现
本次实验是让你实现一个简易路由,其功能是对于接收到的任何数据包:
- 确认发送接口
- 下一跳的IP地址
无需处理任何复杂的路由协议,例如:RIP,OSPF,BGP······记住,此次代码不依赖Lab0~4的TCP栈,不要去修改webget.cc的内容,整个代码量推荐 25~30行。
下图就是lab6的抽象:
实现
根据 router.hh 中已经给出的代码和注释可知,只需要在Router类中再添加一个成员变量routetable用来记录路由,每一个路由都有路由前缀、前缀长度、下一跳IP地址、网络接口等数据构成,所以需要单独构造一个数据类型来表示:
//! 路有条目
struct RouteEntry{
uint32_t route_prefix;
uint8_t prefix_length;
std::optional<Address> next_hop;
size_t interface_num;
RouteEntry() = default;
RouteEntry(const uint32_t _route_prefix, const uint8_t _prefix_length,\
const std::optional<Address> &_next_hop, const size_t _interface_num)\
:
route_prefix(_route_prefix),
prefix_length(_prefix_length),
next_hop(_next_hop),
interface_num(_interface_num)
{}
};
然后添加一个成员变量,_route_table:
//! 路有表
std::vector<RouteEntry> _route_table{};
文档指明,对于每一个数据报,路由的时间复杂度为 O(N) 是可以接受的。
注意: 在转发数据报时候,如果路由直接连接当前网络,下一跳将是一个空的可选项,即下一跳就是数据报的目的地址,如果路由器是通过其他路由器连接到当前网络,则下一跳将包含沿路径的下一个路由器的IP地址
** 路由仅需要知道IP地址和路由表,通过 ==最长前缀匹配匹配(longest-prefix match)来决定下一跳的 NetworkInterface**
源代码:
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";
// DUMMY_CODE(route_prefix, prefix_length, next_hop, interface_num);
// Your code here.
//在路有表末尾构造新的列表项
_route_table.emplace_back(route_prefix,prefix_length,next_hop,interface_num);
}
//! \param[in] dgram The datagram to be routed
void Router::route_one_datagram(InternetDatagram &dgram) {
// DUMMY_CODE(dgram);
// Your code here.
//进行最长前缀匹配
//先得到IP字段
uint32_t ip = dgram.header().dst;
auto max_match_iter = _route_table.end();
for(auto route_iter = _route_table.begin(); \
route_iter != _route_table.end(); route_iter =std::next(route_iter)){
//如果前缀匹配相同,或者前缀匹配长度为0
//! NOTE: 根据CSAPP,32位整数,右移量只取最低5位,即 mod 32,因此直接右移32位
//! 等价右移0位,需要特殊判断
if(route_iter->prefix_length == 0 || (route_iter->route_prefix ^ ip) >> \
(32 - route_iter->prefix_length) == 0){
//成立则需要更新最佳匹配
if(max_match_iter == _route_table.end() || \
route_iter->prefix_length > max_match_iter->prefix_length){
max_match_iter = route_iter;
}
}
}
//匹配了路有规则还需要看TTL>1
if(max_match_iter != _route_table.end() && dgram.header().ttl > 1){
--dgram.header().ttl;
AsyncNetworkInterface &next_interface = interface(max_match_iter->interface_num);
//如果路由器直接连接的相关网络,下一跳为目的地IP地址,否则为下一跳路有的IP地址
if(max_match_iter->next_hop.has_value()){
next_interface.send_datagram(dgram,max_match_iter->next_hop.value());
}
else{
next_interface.send_datagram(dgram,Address::from_ipv4_numeric(ip));
}
}
//对于TTL为1和0,直接丢弃,在本实验中不做ICMP回复
}
注意: 在CSAPP中,32位整数,右移32位等价于右移0位
然后可以进行测试了,打开terminal,进入build目录:
make -j4
make check_lab6
测试结果:
也可以直接做另外的测试,同样是在build目录下执行:
sudo ./apps/network_simulator
结果如下:
这就是一个非常简易的路由实现呀~