// 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. classAsyncNetworkInterface :public NetworkInterface { std::queue<InternetDatagram> datagrams_in_ {};
public: using NetworkInterface::NetworkInterface;
// Construct from a NetworkInterface explicitAsyncNetworkInterface( 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 voidrecv_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 {}; }
// A router that has multiple network interfaces and // performs longest-prefix-match routing between them. classRouter { structItem { uint32_t route_prefix {}; uint8_t prefix_length {}; std::optional<Address> next_hop; size_t interface_num {}; };
// The router's collection of network interfaces std::vector<AsyncNetworkInterface> interfaces_ {};
std::vector<Item> routing_table_ {};
std::vector<Item>::iterator longest_prefix_match_( uint32_t dst_ip ); staticintmatch_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_tadd_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) voidadd_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. voidroute(); };
// 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. voidRouter::add_route( constuint32_t route_prefix, constuint8_t prefix_length, const optional<Address> next_hop, constsize_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";
voidRouter::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 != routing_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::Item>::iterator Router::longest_prefix_match_( uint32_t dst_ip ) { auto res = routing_table_.end(); auto max_length = -1; for ( auto it = routing_table_.begin(); it != routing_table_.end(); ++it ) { auto len = match_length_( dst_ip, it->route_prefix, it->prefix_length ); if ( len > max_length ) { max_length = len; res = it; } } return res; }