Lab-6

IP Router

这个部分会实现一个简单的路由器,不考虑复杂的路由协议、以及路由算法,只需要对给定的数据包,确认发送接口以及下一跳的 IP 地址。简单的 match-action 行为。

参考实验文档,很容易做出来。

实现

只需要实现 add_routeroute_one_datagram 两个方法即可。

Router 类及辅助类声明如下,其他已实现的辅助类可以在同文件 libsponge/router.hh 下找到:

class RouteEntry {
public:
const uint32_t _route_prefix;
const uint8_t _prefix_length;
const std::optional<Address> _next_hop;
const size_t _interface_num;
const uint32_t _prefix_mask;
RouteEntry(uint32_t a, uint8_t b, std::optional<Address> c, size_t d, uint32_t e):_route_prefix(a), _prefix_length(b), _next_hop(c), _interface_num(d), _prefix_mask(e){}
};

class Router {
//! The router's collection of network interfaces
std::vector<AsyncNetworkInterface> _interfaces{};

std::vector<RouteEntry> _routing_table{};

//! 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.
void route_one_datagram(InternetDatagram &dgram);

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 interfaces
void route();
};

具体实现如下:

//! \param[in] route_prefix The "up-to-32-bit" IPv4 address prefix to match the datagram's destination address against
//! \param[in] 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?
//! \param[in] 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).
//! \param[in] 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) {
_routing_table.push_back(RouteEntry(
route_prefix,
prefix_length,
next_hop,
interface_num,
// 另一种计算方法
// prefix_length == 0 ? 0 : numeric_limits<int>::min() >> (prefix_length - 1)
(prefix_length == 0) ? 0 : (0xFFFFFFFF << (32 - prefix_length))
));
}

//! \param[in] dgram The datagram to be routed
void Router::route_one_datagram(InternetDatagram &dgram) {
// check TTL
// TTL 为 1 时,数据包在此结束生命周期
if(dgram.header().ttl <= 1) {
return;
}
// 最长前缀匹配
uint32_t dest = dgram.header().dst;
int match_idx = -1;
int max_match_len = -1;
for(size_t i = 0; i < _routing_table.size(); i++) {
auto mask = _routing_table[i]._prefix_mask;
if((dest & mask) == _routing_table[i]._route_prefix && max_match_len < _routing_table[i]._prefix_length) {
match_idx = i;
max_match_len = _routing_table[i]._prefix_length;
}
}
if(match_idx == -1) {
return;
}
// 不要忘记给 TTL 减1
dgram.header().ttl -= 1;
// send dgram
auto next_hop = _routing_table[match_idx]._next_hop;
auto interface_num = _routing_table[match_idx]._interface_num;
if(next_hop.has_value()) {
_interfaces[interface_num].send_datagram(dgram, next_hop.value());
} else {
_interfaces[interface_num].send_datagram(dgram, Address::from_ipv4_numeric(dest));
}
}

Lab-7

CSS144 的最后一部分,原来还要自己拼(误),因为内容不算多,就和 Lab-6 放一起了。

这部分不要求写代码,只需要对构建后的 build/apps/lab7 进行测试,具体步骤参考实验文档,应当可以证明前面的实现是成功的。

也可以使用 lab7 进行通信、文件传输测试,具体步骤同样参考实验文档。

测试代码采用事件驱动的模式,实现 EventLoop,通过事件循环处理 I/O 事件,也是网络编程的典型实践。线程通信使用原子变量 atomic<bool> 安全地终止线程。

完结撒花!