CS144Lab/apps/network_simulator.cc
2021-11-16 16:47:37 -08:00

357 lines
13 KiB
C++

#include "arp_message.hh"
#include "router.hh"
#include "util.hh"
#include <iostream>
#include <list>
#include <unordered_map>
using namespace std;
auto rd = get_random_generator();
EthernetAddress random_host_ethernet_address() {
EthernetAddress addr;
for (auto &byte : addr) {
byte = rd(); // use a random local Ethernet address
}
addr.at(0) |= 0x02; // "10" in last two binary digits marks a private Ethernet address
addr.at(0) &= 0xfe;
return addr;
}
EthernetAddress random_router_ethernet_address() {
EthernetAddress addr;
for (auto &byte : addr) {
byte = rd(); // use a random local Ethernet address
}
addr.at(0) = 0x02; // "10" in last two binary digits marks a private Ethernet address
addr.at(1) = 0;
addr.at(2) = 0;
return addr;
}
uint32_t ip(const string &str) { return Address{str}.ipv4_numeric(); }
template <typename T>
void clear(T &queue1, T &queue2) {
while (not queue1.empty()) {
queue1.pop();
queue2.pop();
}
}
string summary(const EthernetFrame &frame) {
string ret;
ret += frame.header().to_string();
switch (frame.header().type) {
case EthernetHeader::TYPE_IPv4: {
InternetDatagram dgram;
if (dgram.parse(frame.payload()) == ParseResult::NoError) {
ret += " " + dgram.header().summary();
ret += " payload=\"" + string(dgram.payload().concatenate()) + "\"";
} else {
ret += " (bad IPv4)";
}
} break;
case EthernetHeader::TYPE_ARP: {
ARPMessage arp;
if (arp.parse(frame.payload()) == ParseResult::NoError) {
ret += " " + arp.to_string();
} else {
ret += " (bad ARP)";
}
}
default:
break;
}
return ret;
}
class Host {
string _name;
Address _my_address;
AsyncNetworkInterface _interface;
Address _next_hop;
std::list<InternetDatagram> _expecting_to_receive{};
bool expecting(const InternetDatagram &expected) const {
for (const auto &x : _expecting_to_receive) {
if (x.serialize().concatenate() == expected.serialize().concatenate()) {
return true;
}
}
return false;
}
void remove_expectation(const InternetDatagram &expected) {
for (auto it = _expecting_to_receive.begin(); it != _expecting_to_receive.end(); ++it) {
if (it->serialize().concatenate() == expected.serialize().concatenate()) {
_expecting_to_receive.erase(it);
return;
}
}
}
public:
Host(const string &name, const Address &my_address, const Address &next_hop)
: _name(name)
, _my_address(my_address)
, _interface(random_host_ethernet_address(), _my_address)
, _next_hop(next_hop) {}
InternetDatagram send_to(const Address &destination, const uint8_t ttl = 64) {
InternetDatagram dgram;
dgram.header().src = _my_address.ipv4_numeric();
dgram.header().dst = destination.ipv4_numeric();
dgram.payload() = "random payload: {" + to_string(rd()) + "}";
dgram.header().len = dgram.header().hlen * 4 + dgram.payload().size();
dgram.header().ttl = ttl;
_interface.send_datagram(dgram, _next_hop);
cerr << "Host " << _name << " trying to send datagram (with next hop = " << _next_hop.ip()
<< "): " << dgram.header().summary() << " payload=\"" << dgram.payload().concatenate() << "\"\n";
return dgram;
}
const Address &address() { return _my_address; }
AsyncNetworkInterface &interface() { return _interface; }
void expect(const InternetDatagram &expected) { _expecting_to_receive.push_back(expected); }
const string &name() { return _name; }
void check() {
while (not _interface.datagrams_out().empty()) {
const auto &dgram_received = _interface.datagrams_out().front();
if (not expecting(dgram_received)) {
throw runtime_error("Host " + _name +
" received unexpected Internet datagram: " + dgram_received.header().summary() +
" payload=\"" + dgram_received.payload().concatenate() + "\"");
}
remove_expectation(dgram_received);
_interface.datagrams_out().pop();
}
if (not _expecting_to_receive.empty()) {
auto &expected = _expecting_to_receive.front();
throw runtime_error("Host " + _name + " did NOT receive an expected Internet datagram: " +
expected.header().summary() + " payload=\"" + expected.payload().concatenate() + "\"");
}
}
};
class Network {
private:
Router _router{};
size_t default_id, eth0_id, eth1_id, eth2_id, uun3_id, hs4_id, mit5_id;
std::unordered_map<string, Host> _hosts{};
void exchange_frames(const string &x_name,
AsyncNetworkInterface &x,
const string &y_name,
AsyncNetworkInterface &y) {
auto x_frames = x.frames_out(), y_frames = y.frames_out();
deliver(x_name, x_frames, y_name, y);
deliver(y_name, y_frames, x_name, x);
clear(x_frames, x.frames_out());
clear(y_frames, y.frames_out());
}
void exchange_frames(const string &x_name,
AsyncNetworkInterface &x,
const string &y_name,
AsyncNetworkInterface &y,
const string &z_name,
AsyncNetworkInterface &z) {
auto x_frames = x.frames_out(), y_frames = y.frames_out(), z_frames = z.frames_out();
deliver(x_name, x_frames, y_name, y);
deliver(x_name, x_frames, z_name, z);
deliver(y_name, y_frames, x_name, x);
deliver(y_name, y_frames, z_name, z);
deliver(z_name, z_frames, x_name, x);
deliver(z_name, z_frames, y_name, y);
clear(x_frames, x.frames_out());
clear(y_frames, y.frames_out());
clear(z_frames, z.frames_out());
}
void deliver(const string &src_name,
const queue<EthernetFrame> &src,
const string &dst_name,
AsyncNetworkInterface &dst) {
queue<EthernetFrame> to_send = src;
while (not to_send.empty()) {
to_send.front().payload() = to_send.front().payload().concatenate();
cerr << "Transferring frame from " << src_name << " to " << dst_name << ": " << summary(to_send.front())
<< "\n";
dst.recv_frame(move(to_send.front()));
to_send.pop();
}
}
public:
Network()
: default_id(_router.add_interface({random_router_ethernet_address(), {"171.67.76.46"}}))
, eth0_id(_router.add_interface({random_router_ethernet_address(), {"10.0.0.1"}}))
, eth1_id(_router.add_interface({random_router_ethernet_address(), {"172.16.0.1"}}))
, eth2_id(_router.add_interface({random_router_ethernet_address(), {"192.168.0.1"}}))
, uun3_id(_router.add_interface({random_router_ethernet_address(), {"198.178.229.1"}}))
, hs4_id(_router.add_interface({random_router_ethernet_address(), {"143.195.0.2"}}))
, mit5_id(_router.add_interface({random_router_ethernet_address(), {"128.30.76.255"}})) {
_hosts.insert({"applesauce", {"applesauce", {"10.0.0.2"}, {"10.0.0.1"}}});
_hosts.insert({"default_router", {"default_router", {"171.67.76.1"}, {"0"}}});
;
_hosts.insert({"cherrypie", {"cherrypie", {"192.168.0.2"}, {"192.168.0.1"}}});
_hosts.insert({"hs_router", {"hs_router", {"143.195.0.1"}, {"0"}}});
_hosts.insert({"dm42", {"dm42", {"198.178.229.42"}, {"198.178.229.1"}}});
_hosts.insert({"dm43", {"dm43", {"198.178.229.43"}, {"198.178.229.1"}}});
_router.add_route(ip("0.0.0.0"), 0, host("default_router").address(), default_id);
_router.add_route(ip("10.0.0.0"), 8, {}, eth0_id);
_router.add_route(ip("172.16.0.0"), 16, {}, eth1_id);
_router.add_route(ip("192.168.0.0"), 24, {}, eth2_id);
_router.add_route(ip("198.178.229.0"), 24, {}, uun3_id);
_router.add_route(ip("143.195.0.0"), 17, host("hs_router").address(), hs4_id);
_router.add_route(ip("143.195.128.0"), 18, host("hs_router").address(), hs4_id);
_router.add_route(ip("143.195.192.0"), 19, host("hs_router").address(), hs4_id);
_router.add_route(ip("128.30.76.255"), 16, Address{"128.30.0.1"}, mit5_id);
}
void simulate_physical_connections() {
exchange_frames(
"router.default", _router.interface(default_id), "default_router", host("default_router").interface());
exchange_frames("router.eth0", _router.interface(eth0_id), "applesauce", host("applesauce").interface());
exchange_frames("router.eth2", _router.interface(eth2_id), "cherrypie", host("cherrypie").interface());
exchange_frames("router.hs4", _router.interface(hs4_id), "hs_router", host("hs_router").interface());
exchange_frames("router.uun3",
_router.interface(uun3_id),
"dm42",
host("dm42").interface(),
"dm43",
host("dm43").interface());
}
void simulate() {
for (unsigned int i = 0; i < 256; i++) {
_router.route();
simulate_physical_connections();
}
for (auto &host : _hosts) {
host.second.check();
}
}
Host &host(const string &name) {
auto it = _hosts.find(name);
if (it == _hosts.end()) {
throw runtime_error("unknown host: " + name);
}
if (it->second.name() != name) {
throw runtime_error("invalid host: " + name);
}
return it->second;
}
};
void network_simulator() {
const string green = "\033[32;1m", normal = "\033[m";
cerr << green << "Constructing network." << normal << "\n";
Network network;
cout << green << "\n\nTesting traffic between two ordinary hosts (applesauce to cherrypie)..." << normal << "\n\n";
{
auto dgram_sent = network.host("applesauce").send_to(network.host("cherrypie").address());
dgram_sent.header().ttl--;
network.host("cherrypie").expect(dgram_sent);
network.simulate();
}
cout << green << "\n\nTesting traffic between two ordinary hosts (cherrypie to applesauce)..." << normal << "\n\n";
{
auto dgram_sent = network.host("cherrypie").send_to(network.host("applesauce").address());
dgram_sent.header().ttl--;
network.host("applesauce").expect(dgram_sent);
network.simulate();
}
cout << green << "\n\nSuccess! Testing applesauce sending to the Internet." << normal << "\n\n";
{
auto dgram_sent = network.host("applesauce").send_to({"1.2.3.4"});
dgram_sent.header().ttl--;
network.host("default_router").expect(dgram_sent);
network.simulate();
}
cout << green << "\n\nSuccess! Testing sending to the HS network and Internet." << normal << "\n\n";
{
auto dgram_sent = network.host("applesauce").send_to({"143.195.131.17"});
dgram_sent.header().ttl--;
network.host("hs_router").expect(dgram_sent);
network.simulate();
dgram_sent = network.host("cherrypie").send_to({"143.195.193.52"});
dgram_sent.header().ttl--;
network.host("hs_router").expect(dgram_sent);
network.simulate();
dgram_sent = network.host("cherrypie").send_to({"143.195.223.255"});
dgram_sent.header().ttl--;
network.host("hs_router").expect(dgram_sent);
network.simulate();
dgram_sent = network.host("cherrypie").send_to({"143.195.224.0"});
dgram_sent.header().ttl--;
network.host("default_router").expect(dgram_sent);
network.simulate();
}
cout << green << "\n\nSuccess! Testing two hosts on the same network (dm42 to dm43)..." << normal << "\n\n";
{
auto dgram_sent = network.host("dm42").send_to(network.host("dm43").address());
dgram_sent.header().ttl--;
network.host("dm43").expect(dgram_sent);
network.simulate();
}
cout << green << "\n\nSuccess! Testing TTL expiration..." << normal << "\n\n";
{
auto dgram_sent = network.host("applesauce").send_to({"1.2.3.4"}, 1);
network.simulate();
dgram_sent = network.host("applesauce").send_to({"1.2.3.4"}, 0);
network.simulate();
}
cout << "\n\n\033[32;1mCongratulations! All datagrams were routed successfully.\033[m\n";
}
int main() {
try {
network_simulator();
} catch (const exception &e) {
cerr << "\n\n\n";
cerr << "\033[31;1mError: " << e.what() << "\033[m\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}