Stanford CS144 Lab 6

building an IP router

Checkpoint 6 是最后一个需要在实验框架中填充代码的 checkpoint。我们将在 NetworkInterface 的基础上实现一个 IP router。如同现实世界的 router 一样,这个 router 包含若干个 interfaces,按照路由表将 IP 包路由到不同的 interface。遗憾的是,这里不涉及路由表生成的算法。我们只需遵照路由表完成转发。

3 Implementing the Router #

实验框架给出的添加路由的方法签名是这样的:

1void Router::add_route( const uint32_t route_prefix,
2                        const uint8_t prefix_length,
3                        const optional<Address> next_hop,
4                        const size_t interface_num );

route_prefix 是将路由前缀用 32 位无符号整数表达的形式,prefix_length 相当于子网掩码。例如 route_prefix 为 $305070080$,prefix_length 为 $16$ 的时候,就是一条向 18.47.0.0/16 的路由。

所有路由表采用最大前缀匹配 (longest-prefix-match):在所有匹配的路由表条目中,选择 prefix_length 最大的那个。一旦成功匹配,则调用 interface_num 对应的 NetworkInterface,将数据包转发到对应的 next_hop。特别地,假如 next_hopNone,则转发到数据包头的目标地址。

此外还需要注意转发时要给数据包的 TTL 减去 1,对于 TTL 归零的数据包直接丢弃。

顺着这个函数签名,我添加了如下私有成员用于表示路由表:

1struct Entry
2{
3    uint32_t route_prefix;
4    uint8_t prefix_length;
5    std::optional<Address> next_hop;
6    size_t interface_num;
7};
8
9std::vector<Entry> routing_table_ {};
 1void Router::add_route( const uint32_t route_prefix,
 2                        const uint8_t prefix_length,
 3                        const optional<Address> next_hop,
 4                        const size_t interface_num )
 5{
 6  // cerr << "DEBUG: adding route " << Address::from_ipv4_numeric( route_prefix ).ip() << "/"
 7  //      << static_cast<int>( prefix_length ) << " => " << ( next_hop.has_value() ? next_hop->ip() : "(direct)" )
 8  //      << " on interface " << interface_num << "\n";
 9
10  routing_table_.push_back( Entry { route_prefix, prefix_length, next_hop, interface_num } );
11}
 1void Router::route()
 2{
 3  for ( auto& interface : interfaces_ ) {
 4    auto& datagrams_received = interface->datagrams_received();
 5    while ( !datagrams_received.empty() ) {
 6      auto dgram = move( datagrams_received.front() );
 7      datagrams_received.pop();
 8
 9      if ( dgram.header.ttl <= 1 ) {
10        // TTL (will be) expired, drop the datagram
11        continue;
12      }
13      dgram.header.ttl--;
14      dgram.header.compute_checksum();
15
16      const uint32_t dest_ip = dgram.header.dst;
17      int best_prefix_len = -1;
18      optional<Address> best_next_hop = nullopt;
19      size_t best_interface_num = 0;
20      for ( auto& entry : routing_table_ ) {
21        const uint32_t mask = entry.prefix_length == 0 ? 0 : 0xffffffff << ( 32 - entry.prefix_length );
22        if ( ( dest_ip & mask ) == ( entry.route_prefix & mask ) ) {
23          if ( static_cast<int>( entry.prefix_length ) > best_prefix_len ) {
24            best_prefix_len = entry.prefix_length;
25            best_next_hop = entry.next_hop;
26            best_interface_num = entry.interface_num;
27          }
28        }
29      }
30      if ( best_prefix_len == -1 ) {
31        // No matching route, drop the datagram
32        continue;
33      }
34      Address next_hop = best_next_hop.has_value() ? *best_next_hop : Address::from_ipv4_numeric( dest_ip );
35      interfaces_[best_interface_num]->send_datagram( dgram, next_hop );
36    }
37  }
38}

代码量很小,唯一需要注意的就是操作 TTL 之后,不要忘了重新计算 checksum。