Sponge
CS144's user-space TCP library
network_simulator.cc
Go to the documentation of this file.
1 #include "arp_message.hh"
2 #include "router.hh"
3 #include "util.hh"
4 
5 #include <iostream>
6 #include <list>
7 #include <unordered_map>
8 
9 using namespace std;
10 
12 
14  EthernetAddress addr;
15  for (auto &byte : addr) {
16  byte = rd(); // use a random local Ethernet address
17  }
18  addr.at(0) |= 0x02; // "10" in last two binary digits marks a private Ethernet address
19  addr.at(0) &= 0xfe;
20 
21  return addr;
22 }
23 
25  EthernetAddress addr;
26  for (auto &byte : addr) {
27  byte = rd(); // use a random local Ethernet address
28  }
29  addr.at(0) = 0x02; // "10" in last two binary digits marks a private Ethernet address
30  addr.at(1) = 0;
31  addr.at(2) = 0;
32 
33  return addr;
34 }
35 
36 uint32_t ip(const string &str) { return Address{str}.ipv4_numeric(); }
37 
38 template <typename T>
39 void clear(T &queue1, T &queue2) {
40  while (not queue1.empty()) {
41  queue1.pop();
42  queue2.pop();
43  }
44 }
45 
46 string summary(const EthernetFrame &frame) {
47  string ret;
48  ret += frame.header().to_string();
49  switch (frame.header().type) {
51  InternetDatagram dgram;
52  if (dgram.parse(frame.payload()) == ParseResult::NoError) {
53  ret += " " + dgram.header().summary();
54  ret += " payload=\"" + string(dgram.payload().concatenate()) + "\"";
55  } else {
56  ret += " (bad IPv4)";
57  }
58  } break;
60  ARPMessage arp;
61  if (arp.parse(frame.payload()) == ParseResult::NoError) {
62  ret += " " + arp.to_string();
63  } else {
64  ret += " (bad ARP)";
65  }
66  }
67  default:
68  break;
69  }
70  return ret;
71 }
72 
73 class Host {
74  string _name;
78 
79  std::list<InternetDatagram> _expecting_to_receive{};
80 
81  bool expecting(const InternetDatagram &expected) const {
82  for (const auto &x : _expecting_to_receive) {
83  if (x.serialize().concatenate() == expected.serialize().concatenate()) {
84  return true;
85  }
86  }
87  return false;
88  }
89 
90  void remove_expectation(const InternetDatagram &expected) {
91  for (auto it = _expecting_to_receive.begin(); it != _expecting_to_receive.end(); ++it) {
92  if (it->serialize().concatenate() == expected.serialize().concatenate()) {
93  _expecting_to_receive.erase(it);
94  return;
95  }
96  }
97  }
98 
99  public:
100  Host(const string &name, const Address &my_address, const Address &next_hop)
101  : _name(name)
102  , _my_address(my_address)
103  , _interface(random_host_ethernet_address(), _my_address)
104  , _next_hop(next_hop) {}
105 
106  InternetDatagram send_to(const Address &destination, const uint8_t ttl = 64) {
107  InternetDatagram dgram;
108  dgram.header().src = _my_address.ipv4_numeric();
109  dgram.header().dst = destination.ipv4_numeric();
110  dgram.payload() = "random payload: {" + to_string(rd()) + "}";
111  dgram.header().len = dgram.header().hlen * 4 + dgram.payload().size();
112  dgram.header().ttl = ttl;
113 
114  _interface.send_datagram(dgram, _next_hop);
115 
116  cerr << "Host " << _name << " trying to send datagram (with next hop = " << _next_hop.ip()
117  << "): " << dgram.header().summary() << " payload=\"" << dgram.payload().concatenate() << "\"\n";
118 
119  return dgram;
120  }
121 
122  const Address &address() { return _my_address; }
123 
124  AsyncNetworkInterface &interface() { return _interface; }
125 
126  void expect(const InternetDatagram &expected) { _expecting_to_receive.push_back(expected); }
127 
128  const string &name() { return _name; }
129 
130  void check() {
131  while (not _interface.datagrams_out().empty()) {
132  const auto &dgram_received = _interface.datagrams_out().front();
133  if (not expecting(dgram_received)) {
134  throw runtime_error("Host " + _name +
135  " received unexpected Internet datagram: " + dgram_received.header().summary() +
136  " payload=\"" + dgram_received.payload().concatenate() + "\"");
137  }
138  remove_expectation(dgram_received);
139  _interface.datagrams_out().pop();
140  }
141 
142  if (not _expecting_to_receive.empty()) {
143  auto &expected = _expecting_to_receive.front();
144  throw runtime_error("Host " + _name + " did NOT receive an expected Internet datagram: " +
145  expected.header().summary() + " payload=\"" + expected.payload().concatenate() + "\"");
146  }
147  }
148 };
149 
150 class Network {
151  private:
152  Router _router{};
153 
154  size_t default_id, eth0_id, eth1_id, eth2_id, uun3_id, hs4_id, mit5_id;
155 
157 
158  void exchange_frames(const string &x_name,
160  const string &y_name,
162  auto x_frames = x.frames_out(), y_frames = y.frames_out();
163 
164  deliver(x_name, x_frames, y_name, y);
165  deliver(y_name, y_frames, x_name, x);
166 
167  clear(x_frames, x.frames_out());
168  clear(y_frames, y.frames_out());
169  }
170 
171  void exchange_frames(const string &x_name,
173  const string &y_name,
175  const string &z_name,
177  auto x_frames = x.frames_out(), y_frames = y.frames_out(), z_frames = z.frames_out();
178 
179  deliver(x_name, x_frames, y_name, y);
180  deliver(x_name, x_frames, z_name, z);
181 
182  deliver(y_name, y_frames, x_name, x);
183  deliver(y_name, y_frames, z_name, z);
184 
185  deliver(z_name, z_frames, x_name, x);
186  deliver(z_name, z_frames, y_name, y);
187 
188  clear(x_frames, x.frames_out());
189  clear(y_frames, y.frames_out());
190  clear(z_frames, z.frames_out());
191  }
192 
193  void deliver(const string &src_name,
194  const queue<EthernetFrame> &src,
195  const string &dst_name,
196  AsyncNetworkInterface &dst) {
197  queue<EthernetFrame> to_send = src;
198  while (not to_send.empty()) {
199  to_send.front().payload() = to_send.front().payload().concatenate();
200  cerr << "Transferring frame from " << src_name << " to " << dst_name << ": " << summary(to_send.front())
201  << "\n";
202  dst.recv_frame(move(to_send.front()));
203  to_send.pop();
204  }
205  }
206 
207  public:
209  : default_id(_router.add_interface({random_router_ethernet_address(), {"171.67.76.46"}}))
210  , eth0_id(_router.add_interface({random_router_ethernet_address(), {"10.0.0.1"}}))
211  , eth1_id(_router.add_interface({random_router_ethernet_address(), {"172.16.0.1"}}))
212  , eth2_id(_router.add_interface({random_router_ethernet_address(), {"192.168.0.1"}}))
213  , uun3_id(_router.add_interface({random_router_ethernet_address(), {"198.178.229.1"}}))
214  , hs4_id(_router.add_interface({random_router_ethernet_address(), {"143.195.0.2"}}))
215  , mit5_id(_router.add_interface({random_router_ethernet_address(), {"128.30.76.255"}})) {
216  _hosts.insert({"applesauce", {"applesauce", {"10.0.0.2"}, {"10.0.0.1"}}});
217  _hosts.insert({"default_router", {"default_router", {"171.67.76.1"}, {"0"}}});
218  ;
219  _hosts.insert({"cherrypie", {"cherrypie", {"192.168.0.2"}, {"192.168.0.1"}}});
220  _hosts.insert({"hs_router", {"hs_router", {"143.195.0.1"}, {"0"}}});
221  _hosts.insert({"dm42", {"dm42", {"198.178.229.42"}, {"198.178.229.1"}}});
222  _hosts.insert({"dm43", {"dm43", {"198.178.229.43"}, {"198.178.229.1"}}});
223 
224  _router.add_route(ip("0.0.0.0"), 0, host("default_router").address(), default_id);
225  _router.add_route(ip("10.0.0.0"), 8, {}, eth0_id);
226  _router.add_route(ip("172.16.0.0"), 16, {}, eth1_id);
227  _router.add_route(ip("192.168.0.0"), 24, {}, eth2_id);
228  _router.add_route(ip("198.178.229.0"), 24, {}, uun3_id);
229  _router.add_route(ip("143.195.0.0"), 17, host("hs_router").address(), hs4_id);
230  _router.add_route(ip("143.195.128.0"), 18, host("hs_router").address(), hs4_id);
231  _router.add_route(ip("143.195.192.0"), 19, host("hs_router").address(), hs4_id);
232  _router.add_route(ip("128.30.76.255"), 16, Address{"128.30.0.1"}, mit5_id);
233  }
234 
236  exchange_frames(
237  "router.default", _router.interface(default_id), "default_router", host("default_router").interface());
238  exchange_frames("router.eth0", _router.interface(eth0_id), "applesauce", host("applesauce").interface());
239  exchange_frames("router.eth2", _router.interface(eth2_id), "cherrypie", host("cherrypie").interface());
240  exchange_frames("router.hs4", _router.interface(hs4_id), "hs_router", host("hs_router").interface());
241  exchange_frames("router.uun3",
242  _router.interface(uun3_id),
243  "dm42",
244  host("dm42").interface(),
245  "dm43",
246  host("dm43").interface());
247  }
248 
249  void simulate() {
250  for (unsigned int i = 0; i < 256; i++) {
251  _router.route();
252  simulate_physical_connections();
253  }
254 
255  for (auto &host : _hosts) {
256  host.second.check();
257  }
258  }
259 
260  Host &host(const string &name) {
261  auto it = _hosts.find(name);
262  if (it == _hosts.end()) {
263  throw runtime_error("unknown host: " + name);
264  }
265  if (it->second.name() != name) {
266  throw runtime_error("invalid host: " + name);
267  }
268  return it->second;
269  }
270 };
271 
273  const string green = "\033[32;1m", normal = "\033[m";
274 
275  cerr << green << "Constructing network." << normal << "\n";
276 
277  Network network;
278 
279  cout << green << "\n\nTesting traffic between two ordinary hosts (applesauce to cherrypie)..." << normal << "\n\n";
280  {
281  auto dgram_sent = network.host("applesauce").send_to(network.host("cherrypie").address());
282  dgram_sent.header().ttl--;
283  network.host("cherrypie").expect(dgram_sent);
284  network.simulate();
285  }
286 
287  cout << green << "\n\nTesting traffic between two ordinary hosts (cherrypie to applesauce)..." << normal << "\n\n";
288  {
289  auto dgram_sent = network.host("cherrypie").send_to(network.host("applesauce").address());
290  dgram_sent.header().ttl--;
291  network.host("applesauce").expect(dgram_sent);
292  network.simulate();
293  }
294 
295  cout << green << "\n\nSuccess! Testing applesauce sending to the Internet." << normal << "\n\n";
296  {
297  auto dgram_sent = network.host("applesauce").send_to({"1.2.3.4"});
298  dgram_sent.header().ttl--;
299  network.host("default_router").expect(dgram_sent);
300  network.simulate();
301  }
302 
303  cout << green << "\n\nSuccess! Testing sending to the HS network and Internet." << normal << "\n\n";
304  {
305  auto dgram_sent = network.host("applesauce").send_to({"143.195.131.17"});
306  dgram_sent.header().ttl--;
307  network.host("hs_router").expect(dgram_sent);
308  network.simulate();
309 
310  dgram_sent = network.host("cherrypie").send_to({"143.195.193.52"});
311  dgram_sent.header().ttl--;
312  network.host("hs_router").expect(dgram_sent);
313  network.simulate();
314 
315  dgram_sent = network.host("cherrypie").send_to({"143.195.223.255"});
316  dgram_sent.header().ttl--;
317  network.host("hs_router").expect(dgram_sent);
318  network.simulate();
319 
320  dgram_sent = network.host("cherrypie").send_to({"143.195.224.0"});
321  dgram_sent.header().ttl--;
322  network.host("default_router").expect(dgram_sent);
323  network.simulate();
324  }
325 
326  cout << green << "\n\nSuccess! Testing two hosts on the same network (dm42 to dm43)..." << normal << "\n\n";
327  {
328  auto dgram_sent = network.host("dm42").send_to(network.host("dm43").address());
329  dgram_sent.header().ttl--;
330  network.host("dm43").expect(dgram_sent);
331  network.simulate();
332  }
333 
334  cout << green << "\n\nSuccess! Testing TTL expiration..." << normal << "\n\n";
335  {
336  auto dgram_sent = network.host("applesauce").send_to({"1.2.3.4"}, 1);
337  network.simulate();
338 
339  dgram_sent = network.host("applesauce").send_to({"1.2.3.4"}, 0);
340  network.simulate();
341  }
342 
343  cout << "\n\n\033[32;1mCongratulations! All datagrams were routed successfully.\033[m\n";
344 }
345 
346 int main() {
347  try {
349  } catch (const exception &e) {
350  cerr << "\n\n\n";
351  cerr << "\033[31;1mError: " << e.what() << "\033[m\n";
352  return EXIT_FAILURE;
353  }
354 
355  return EXIT_SUCCESS;
356 }
ParseResult::NoError
@ NoError
Success.
Network::deliver
void deliver(const string &src_name, const queue< EthernetFrame > &src, const string &dst_name, AsyncNetworkInterface &dst)
Definition: network_simulator.cc:193
NetworkInterface::frames_out
std::queue< EthernetFrame > & frames_out()
Access queue of Ethernet frames awaiting transmission.
Definition: network_interface.hh:48
arp_message.hh
util.hh
EthernetFrame::header
const EthernetHeader & header() const
Definition: ethernet_frame.hh:22
Host::_name
string _name
Definition: network_simulator.cc:74
AsyncNetworkInterface::recv_frame
void recv_frame(const EthernetFrame &frame)
Receives and Ethernet frame and responds appropriately.
Definition: router.hh:30
std::string
std::exception
std::list
std::move
T move(T... args)
rd
auto rd
Definition: network_simulator.cc:11
Network::host
Host & host(const string &name)
Definition: network_simulator.cc:260
Host::_next_hop
Address _next_hop
Definition: network_simulator.cc:77
get_random_generator
mt19937 get_random_generator()
Seed a fast random generator.
Definition: util.cc:64
IPv4Header::summary
std::string summary() const
Return a string containing a human-readable summary of the header.
Definition: ipv4_header.cc:151
Host::check
void check()
Definition: network_simulator.cc:130
EthernetFrame
Ethernet frame.
Definition: ethernet_frame.hh:8
Host::expect
void expect(const InternetDatagram &expected)
Definition: network_simulator.cc:126
router.hh
Host::expecting
bool expecting(const InternetDatagram &expected) const
Definition: network_simulator.cc:81
std::queue< EthernetFrame >
Host::address
const Address & address()
Definition: network_simulator.cc:122
IPv4Header::src
uint32_t src
src address
Definition: ipv4_header.hh:45
Network::Network
Network()
Definition: network_simulator.cc:208
std::queue::front
T front(T... args)
Router
A router that has multiple network interfaces and performs longest-prefix-match routing between them.
Definition: router.hh:43
IPv4Datagram
IPv4 Internet datagram
Definition: ipv4_datagram.hh:8
AsyncNetworkInterface::datagrams_out
std::queue< InternetDatagram > & datagrams_out()
Access queue of Internet datagrams that have been received.
Definition: router.hh:38
summary
string summary(const EthernetFrame &frame)
Definition: network_simulator.cc:46
Address
Wrapper around IPv4 addresses and DNS operations.
Definition: address.hh:13
NetworkInterface::send_datagram
void send_datagram(const InternetDatagram &dgram, const Address &next_hop)
Sends an IPv4 datagram, encapsulated in an Ethernet frame (if it knows the Ethernet destination addre...
Definition: network_interface.cc:32
Network
Definition: network_simulator.cc:150
ARPMessage
ARP message
Definition: arp_message.hh:10
EthernetHeader::to_string
std::string to_string() const
Return a string containing a header in human-readable format.
Definition: ethernet_header.cc:65
EthernetFrame::payload
const BufferList & payload() const
Definition: ethernet_frame.hh:25
Host::interface
AsyncNetworkInterface & interface()
Definition: network_simulator.cc:124
Host::remove_expectation
void remove_expectation(const InternetDatagram &expected)
Definition: network_simulator.cc:90
Host
Definition: network_simulator.cc:73
std::cerr
IPv4Header::ttl
uint8_t ttl
time to live field
Definition: ipv4_header.hh:42
network_simulator
void network_simulator()
Definition: network_simulator.cc:272
IPv4Header::dst
uint32_t dst
Definition: ipv4_header.hh:46
Address::ipv4_numeric
uint32_t ipv4_numeric() const
Numeric IP address as an integer (i.e., in host byte order).
Definition: address.cc:108
Host::Host
Host(const string &name, const Address &my_address, const Address &next_hop)
Definition: network_simulator.cc:100
std::queue::pop
T pop(T... args)
std::to_string
T to_string(T... args)
EthernetHeader::TYPE_IPv4
static constexpr uint16_t TYPE_IPv4
Type number for IPv4.
Definition: ethernet_header.hh:20
std::array
Host::_interface
AsyncNetworkInterface _interface
Definition: network_simulator.cc:76
std::runtime_error
std::uint32_t
IPv4Datagram::parse
ParseResult parse(const Buffer buffer)
Parse the segment from a string.
Definition: ipv4_datagram.cc:11
random_router_ethernet_address
EthernetAddress random_router_ethernet_address()
Definition: network_simulator.cc:24
IPv4Header::hlen
uint8_t hlen
header length (multiples of 32 bits)
Definition: ipv4_header.hh:35
Network::uun3_id
size_t uun3_id
Definition: network_simulator.cc:154
Host::name
const string & name()
Definition: network_simulator.cc:128
Network::exchange_frames
void exchange_frames(const string &x_name, AsyncNetworkInterface &x, const string &y_name, AsyncNetworkInterface &y)
Definition: network_simulator.cc:158
std
AsyncNetworkInterface
A wrapper for NetworkInterface that makes the host-side interface asynchronous: instead of returning ...
Definition: router.hh:14
BufferList::size
size_t size() const
Size of the string.
Definition: buffer.cc:43
Network::exchange_frames
void exchange_frames(const string &x_name, AsyncNetworkInterface &x, const string &y_name, AsyncNetworkInterface &y, const string &z_name, AsyncNetworkInterface &z)
Definition: network_simulator.cc:171
std::queue::empty
T empty(T... args)
random_host_ethernet_address
EthernetAddress random_host_ethernet_address()
Definition: network_simulator.cc:13
IPv4Header::len
uint16_t len
total length of packet
Definition: ipv4_header.hh:37
EthernetHeader::type
uint16_t type
Definition: ethernet_header.hh:27
ip
uint32_t ip(const string &str)
Definition: network_simulator.cc:36
IPv4Datagram::serialize
BufferList serialize() const
Serialize the segment to a string.
Definition: ipv4_datagram.cc:23
Host::send_to
InternetDatagram send_to(const Address &destination, const uint8_t ttl=64)
Definition: network_simulator.cc:106
EthernetHeader::TYPE_ARP
static constexpr uint16_t TYPE_ARP
Type number for ARP.
Definition: ethernet_header.hh:21
Network::simulate_physical_connections
void simulate_physical_connections()
Definition: network_simulator.cc:235
std::unordered_map< string, Host >
Address::ip
std::string ip() const
Dotted-quad IP address string ("18.243.0.1").
Definition: address.hh:51
IPv4Datagram::header
const IPv4Header & header() const
Definition: ipv4_datagram.hh:22
main
int main()
Definition: network_simulator.cc:346
Host::_my_address
Address _my_address
Definition: network_simulator.cc:75
clear
void clear(T &queue1, T &queue2)
Definition: network_simulator.cc:39
std::exception::what
T what(T... args)
Network::simulate
void simulate()
Definition: network_simulator.cc:249
BufferList::concatenate
std::string concatenate() const
Make a copy to a new std::string.
Definition: buffer.cc:34
IPv4Datagram::payload
const BufferList & payload() const
Definition: ipv4_datagram.hh:25