一、实验目的
在现有的NetworkInterface基础上实现一个IP路由器。
二、实验内容
在本实验中,你将在现有的NetworkInterface基础上实现一个IP路由器,从而结束本课程。路由器有几个网络接口,可以在其中任何一个接口上接收互联网数据报。路由器的工作是根据路由表转发它得到的数据报:一个规则列表,它告诉路由器,对于任何给定的数据报:
发送到哪个接口;
下一跳的IP地址 ;
你的工作是实现一个路由器,它可以为任何给定的数据报计算出这两件事。(你不需要实现设置路由表的算法,例如RIP、OSPF、BGP或SDN控制器,只需要实现跟随路由表的算法)。
你对路由器的实现将使用带有新的Router类的Sponge库,以及在模拟网络中检查你的路由器功能的测试。本实验建立在你在上一个实验中对NetworkInterface的实现之上,但不使用你在之前实验中实现的TCP栈。IP路由器不需要知道任何关于TCP、ARP或以太网的信息(仅限IP)。我们希望你的实现将需要大约25-30行的代码。
三、实验过程
在minnow目录下输入git merge origin/check5-startercode获取Lab5
用文本编辑器打开./src/router.hh
修改代码
用文本编辑器打开./src/router.cc
修改代码
在build目录下输入make进行编译
输入make check5进行测试
测试成功,实验结束
四、实验体会
1.本实验中的 Router 实现比较简单,只需实现一下 IP 最长匹配并将数据包转发即可。
2.需要注意的是,联系计算机网络理论课和实验所学习的内容,在实际网络中,路由表会根据网络拓扑和路由策略进行配置,以确保数据包能够正确地转发到目标。路由表中的路由条目根据目标网络地址的前缀匹配来确定数据包的转发规则。当无法找到匹配的路由条目时,数据包将根据默认路由进行转发,或者如果没有默认路由,则会被丢弃。
五、代码附录
router.hh
#pragma once
#include "network_interface.hh"
#include <optional>
#include <queue>
// A wrapper for NetworkInterface that makes the host-side
// interface asynchronous: instead of returning received datagrams
// immediately (from the `recv_frame` method), it stores them for
// later retrieval. Otherwise, behaves identically to the underlying
// implementation of NetworkInterface.
class AsyncNetworkInterface : public NetworkInterface
{
std::queue<InternetDatagram> datagrams_in_ {};
public:
using NetworkInterface::NetworkInterface;
// Construct from a NetworkInterface
explicit AsyncNetworkInterface( 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 frame
void recv_frame( const EthernetFrame& frame )
{
auto optional_dgram = NetworkInterface::recv_frame( frame );
if ( optional_dgram.has_value() ) {
datagrams_in_.push( std::move( optional_dgram.value() ) );
}
};
// Access queue of Internet datagrams that have been received
std::optional<InternetDatagram> maybe_receive()
{
if ( datagrams_in_.empty() ) {
return {};
}
InternetDatagram datagram = std::move( datagrams_in_.front() );
datagrams_in_.pop();
return datagram;
}
};
// 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_ {};
// struct Route_entry//route_entry_
// {
// uint32_t route_prefix{};
// uint8_t prefix_length{};
// std::optional<Address> next_hop;
// size_t interface_num{};
// };
// //static typedef struct route_entry_ Route_entry;
// std::vector<Route_entry> route_table_{};
class Router
{
// The router's collection of network interfaces
std::vector<AsyncNetworkInterface> interfaces_ {};
struct Route_entry
{
uint32_t route_prefix {};
uint8_t prefix_length {};
std::optional<Address> next_hop;
size_t interface_num {};
};
std::vector<Route_entry> route_table_ {};
//std::vector<Route_entry>::iterator longest_prefix_match_( uint32_t dst_ip );
//static int match_length_( uint32_t src_ip, uint32_t tgt_ip, uint8_t tgt_len );
public:
// Add an interface to the router
// 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( size_t N ) { return interfaces_.at( N ); }
// Add a route (a forwarding rule)
void add_route( uint32_t route_prefix,
uint8_t prefix_length,
std::optional<Address> next_hop,
size_t interface_num );
// Route packets between the interfaces. For each interface, use the
// maybe_receive() method to consume every incoming datagram and
// send it on one of interfaces to the correct next hop. The router
// chooses the outbound interface and next-hop as specified by the
// route with the longest prefix_length that matches the datagram's
// destination address.
void route();
std::vector<Route_entry>::iterator longest_prefix_match_( uint32_t dst_ip );
int match_length_( uint32_t src_ip, uint32_t tgt_ip, uint8_t tgt_len );
// std::vector<Route_entry>::iterator longest_prefix_match_( uint32_t dst_ip );
// int match_length_( uint32_t src_ip, uint32_t tgt_ip, uint8_t tgt_len );
};
router.cc
#include "router.hh"
#include <iostream>
#include <limits>
using namespace std;
// route_prefix: The "up-to-32-bit" IPv4 address prefix to match the datagram's destination address against
// prefix_length: For this route to be applicable, how many high-order (most-significant) bits of
// the route_prefix will need to match the corresponding bits of the datagram's destination address?
// next_hop: The IP address of the next hop. Will be empty if the network is directly attached to the router (in
// which case, the next hop address should be the datagram's final destination).
// interface_num: The index of the interface to send the datagram out on.
// 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() << "/"
// << static_cast<int>( prefix_length ) << " => " << ( next_hop.has_value() ? next_hop->ip() : "(direct)" )
// << " on interface " << interface_num << "\n";
// // (void)route_prefix;
// // (void)prefix_length;
// // (void)next_hop;
// // (void)interface_num;
// route_table_.emplace_back(route_prefix, prefix_length, next_hop, interface_num);
// return;
// }
// void Router::route()
// {
// for ( auto& current_interface : interfaces_ ) {
// auto received_dgram = current_interface.maybe_receive();
// if ( received_dgram.has_value() ) {
// auto& dgram = received_dgram.value();
// if ( dgram.header.ttl > 1 ) {
// dgram.header.ttl--;
// // NOTE: important!!!
// dgram.header.compute_checksum();
// auto dst_ip = dgram.header.dst;
// auto it = longest_prefix_match_( dst_ip );
// if ( it != route_table_.end() ) {
// auto& target_interface = interface( it->interface_num );
// target_interface.send_datagram( dgram, it->next_hop.value_or( Address::from_ipv4_numeric( dst_ip ) ) );
// }
// }
// }
// }
// }
// std::vector<Router::Route_entry>::iterator Router::longest_prefix_match_( uint32_t dst_ip )
// {
// auto res = route_table_.end();
// int max_length = 0;
// for ( auto it = route_table_.begin(); it != route_table_.end(); ++it ) {
// int len = match_length_( dst_ip, it->route_prefix, it->prefix_length );
// if ( len > max_length ) {
// max_length = len;
// res = it;
// }
// }
// return res;
// }
// int Router::match_length_( uint32_t src_ip, uint32_t tgt_ip, uint8_t tgt_len )
// {
// if ( tgt_len == 0 ) {
// return 0;
// }
// if ( tgt_len > 32 ) {
// return -1;
// }
// // tgt_len < 32
// uint8_t const len = 32U - tgt_len;
// src_ip = src_ip >> len;
// tgt_ip = tgt_ip >> len;
// return src_ip == tgt_ip ? tgt_len : -1;
// }
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() << "/"
<< static_cast<int>( prefix_length ) << " => " << ( next_hop.has_value() ? next_hop->ip() : "(direct)" )
<< " on interface " << interface_num << "\n";
route_table_.emplace_back( route_prefix, prefix_length, next_hop, interface_num );
}
void Router::route()
{
for (uint32_t i=0; i<interfaces_.size();i++) {
auto received_dgram = interface(i).maybe_receive();
if ( received_dgram.has_value() ) {
auto dgram = received_dgram.value();
if ( dgram.header.ttl > 1 ) {
dgram.header.ttl--;
dgram.header.compute_checksum();
auto dst_ip = dgram.header.dst;
auto it = longest_prefix_match_( dst_ip );
if ( it != route_table_.end() ) {
//这里的bug
interface( it->interface_num ).send_datagram( dgram, it->next_hop.value_or( Address::from_ipv4_numeric( dst_ip ) ) );
}
}
}
}
}
std::vector<Router::Route_entry>::iterator Router::longest_prefix_match_( uint32_t dst_ip )
{
auto res = route_table_.end();
int max_length = -1;
for ( auto it = route_table_.begin(); it != route_table_.end(); ++it ) {
int len = match_length_( dst_ip, it->route_prefix, it->prefix_length );
if ( len > max_length ) {
max_length = len;
res = it;
}
}
return res;
}
int Router::match_length_( uint32_t src_ip, uint32_t tgt_ip, uint8_t tgt_len )
{
if ( tgt_len == 0 ) {
return 0;
}
if ( tgt_len > 32 ) {
return -1;
}
// tgt_len < 32
uint8_t len = 32U - tgt_len;
src_ip = src_ip >> len;
tgt_ip = tgt_ip >> len;
return src_ip == tgt_ip ? tgt_len : -1;
}