Merge remote-tracking branch 'official/lab7-startercode'

This commit is contained in:
ridethepig 2023-02-18 15:01:51 +00:00
commit 7dd3fed217
4 changed files with 419 additions and 0 deletions

View File

@ -9,3 +9,5 @@ add_sponge_exec (tcp_ip_ethernet stream_copy)
add_sponge_exec (webget) add_sponge_exec (webget)
add_sponge_exec (tcp_benchmark) add_sponge_exec (tcp_benchmark)
add_sponge_exec (network_simulator) add_sponge_exec (network_simulator)
add_sponge_exec (lab7 stream_copy)
add_sponge_exec (bouncer)

70
apps/bouncer.cc Normal file
View File

@ -0,0 +1,70 @@
#include "eventloop.hh"
#include "socket.hh"
#include <cstdlib>
#include <iostream>
#include <optional>
using namespace std;
void program_body() {
EventLoop loop;
vector<UDPSocket> sockets;
vector<optional<Address>> peers;
sockets.reserve(66000);
peers.reserve(66000);
for (uint16_t lower_port = 1024; lower_port <= 64000; lower_port += 2) {
sockets.emplace_back();
sockets.emplace_back();
peers.emplace_back();
peers.emplace_back();
UDPSocket &x = sockets.at(sockets.size() - 2);
UDPSocket &y = sockets.at(sockets.size() - 1);
optional<Address> &x_peer = peers.at(peers.size() - 2);
optional<Address> &y_peer = peers.at(peers.size() - 1);
x.bind(Address{"0", lower_port});
y.bind(Address{"0", uint16_t(lower_port + 1)});
loop.add_rule(x, Direction::In, [&] {
auto rec = x.recv();
if (not x_peer.has_value() or x_peer.value() != rec.source_address) {
x_peer = rec.source_address;
cerr << "Learned new address for X ( " << x.local_address().to_string() << " at "
<< x_peer.value().to_string() << "\n";
}
if (y_peer.has_value() and not rec.payload.empty()) {
y.sendto(y_peer.value(), rec.payload);
}
});
loop.add_rule(y, Direction::In, [&] {
auto rec = y.recv();
if (not y_peer.has_value() or y_peer.value() != rec.source_address) {
y_peer = rec.source_address;
cerr << "Learned new address for Y ( " << y.local_address().to_string() << " at "
<< y_peer.value().to_string() << "\n";
}
if (x_peer.has_value() and not rec.payload.empty()) {
x.sendto(x_peer.value(), rec.payload);
}
});
}
cerr << "Starting event loop...\n";
while (EventLoop::Result::Exit != loop.wait_next_event(-1)) {
}
}
int main() {
try {
program_body();
} catch (const exception &e) {
cerr << e.what() << "\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

313
apps/lab7.cc Normal file
View File

@ -0,0 +1,313 @@
#include "address.hh"
#include "arp_message.hh"
#include "bidirectional_stream_copy.hh"
#include "router.hh"
#include "tcp_over_ip.hh"
#include "tcp_sponge_socket.cc"
#include "util.hh"
#include <cstdlib>
#include <iostream>
#include <thread>
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;
}
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().concatenate()) == ParseResult::NoError) {
ret += " " + dgram.header().summary();
if (dgram.header().proto == IPv4Header::PROTO_TCP) {
TCPSegment tcp_seg;
if (tcp_seg.parse(dgram.payload().concatenate(), dgram.header().pseudo_cksum()) ==
ParseResult::NoError) {
ret += " " + tcp_seg.header().summary();
}
}
} 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 NetworkInterfaceAdapter : public TCPOverIPv4Adapter {
private:
NetworkInterface _interface;
Address _next_hop;
pair<FileDescriptor, FileDescriptor> _data_socket_pair = socket_pair_helper(SOCK_DGRAM);
void send_pending() {
while (not _interface.frames_out().empty()) {
_data_socket_pair.first.write(_interface.frames_out().front().serialize());
_interface.frames_out().pop();
}
}
public:
NetworkInterfaceAdapter(const Address &ip_address, const Address &next_hop)
: _interface(random_host_ethernet_address(), ip_address), _next_hop(next_hop) {}
optional<TCPSegment> read() {
EthernetFrame frame;
if (frame.parse(_data_socket_pair.first.read()) != ParseResult::NoError) {
return {};
}
// Give the frame to the NetworkInterface. Get back an Internet datagram if frame was carrying one.
optional<InternetDatagram> ip_dgram = _interface.recv_frame(frame);
// The incoming frame may have caused the NetworkInterface to send a frame
send_pending();
// Try to interpret IPv4 datagram as TCP
if (ip_dgram) {
return unwrap_tcp_in_ip(ip_dgram.value());
}
return {};
}
void write(TCPSegment &seg) {
_interface.send_datagram(wrap_tcp_in_ip(seg), _next_hop);
send_pending();
}
void tick(const size_t ms_since_last_tick) {
_interface.tick(ms_since_last_tick);
send_pending();
}
NetworkInterface &interface() { return _interface; }
queue<EthernetFrame> frames_out() { return _interface.frames_out(); }
operator FileDescriptor &() { return _data_socket_pair.first; }
FileDescriptor &frame_fd() { return _data_socket_pair.second; }
};
class TCPSocketLab7 : public TCPSpongeSocket<NetworkInterfaceAdapter> {
Address _local_address;
public:
TCPSocketLab7(const Address &ip_address, const Address &next_hop)
: TCPSpongeSocket<NetworkInterfaceAdapter>(NetworkInterfaceAdapter(ip_address, next_hop))
, _local_address(ip_address) {}
void connect(const Address &address) {
FdAdapterConfig multiplexer_config;
_local_address = Address{_local_address.ip(), uint16_t(random_device()())};
cerr << "DEBUG: Connecting from " << _local_address.to_string() << "...\n";
multiplexer_config.source = _local_address;
multiplexer_config.destination = address;
TCPSpongeSocket<NetworkInterfaceAdapter>::connect({}, multiplexer_config);
}
void bind(const Address &address) {
if (address.ip() != _local_address.ip()) {
throw runtime_error("Cannot bind to " + address.to_string());
}
_local_address = Address{_local_address.ip(), address.port()};
}
void listen_and_accept() {
FdAdapterConfig multiplexer_config;
multiplexer_config.source = _local_address;
TCPSpongeSocket<NetworkInterfaceAdapter>::listen_and_accept({}, multiplexer_config);
}
NetworkInterfaceAdapter &adapter() { return _datagram_adapter; }
};
void program_body(bool is_client, const string &bounce_host, const string &bounce_port, const bool debug) {
UDPSocket internet_socket;
Address bounce_address{bounce_host, bounce_port};
/* let bouncer know where we are */
internet_socket.sendto(bounce_address, "");
internet_socket.sendto(bounce_address, "");
internet_socket.sendto(bounce_address, "");
/* set up the router */
Router router;
unsigned int host_side, internet_side;
if (is_client) {
host_side = router.add_interface({random_router_ethernet_address(), {"192.168.0.1"}});
internet_side = router.add_interface({random_router_ethernet_address(), {"10.0.0.192"}});
router.add_route(Address{"192.168.0.0"}.ipv4_numeric(), 16, {}, host_side);
router.add_route(Address{"10.0.0.0"}.ipv4_numeric(), 8, {}, internet_side);
router.add_route(Address{"172.16.0.0"}.ipv4_numeric(), 12, Address{"10.0.0.172"}, internet_side);
} else {
host_side = router.add_interface({random_router_ethernet_address(), {"172.16.0.1"}});
internet_side = router.add_interface({random_router_ethernet_address(), {"10.0.0.172"}});
router.add_route(Address{"172.16.0.0"}.ipv4_numeric(), 12, {}, host_side);
router.add_route(Address{"10.0.0.0"}.ipv4_numeric(), 8, {}, internet_side);
router.add_route(Address{"192.168.0.0"}.ipv4_numeric(), 16, Address{"10.0.0.192"}, internet_side);
}
/* set up the client */
TCPSocketLab7 sock =
is_client ? TCPSocketLab7{{"192.168.0.50"}, {"192.168.0.1"}} : TCPSocketLab7{{"172.16.0.100"}, {"172.16.0.1"}};
atomic<bool> exit_flag{};
/* set up the network */
thread network_thread([&]() {
try {
EventLoop event_loop;
// Frames from host to router
event_loop.add_rule(sock.adapter().frame_fd(), Direction::In, [&] {
EthernetFrame frame;
if (frame.parse(sock.adapter().frame_fd().read()) != ParseResult::NoError) {
return;
}
if (debug) {
cerr << " Host->router: " << summary(frame) << "\n";
}
router.interface(host_side).recv_frame(frame);
router.route();
});
// Frames from router to host
event_loop.add_rule(sock.adapter().frame_fd(),
Direction::Out,
[&] {
auto &f = router.interface(host_side).frames_out();
if (debug) {
cerr << " Router->host: " << summary(f.front()) << "\n";
}
sock.adapter().frame_fd().write(f.front().serialize());
f.pop();
},
[&] { return not router.interface(host_side).frames_out().empty(); });
// Frames from router to Internet
event_loop.add_rule(internet_socket,
Direction::Out,
[&] {
auto &f = router.interface(internet_side).frames_out();
if (debug) {
cerr << " Router->Internet: " << summary(f.front()) << "\n";
}
internet_socket.sendto(bounce_address, f.front().serialize());
f.pop();
},
[&] { return not router.interface(internet_side).frames_out().empty(); });
// Frames from Internet to router
event_loop.add_rule(internet_socket, Direction::In, [&] {
EthernetFrame frame;
if (frame.parse(internet_socket.read()) != ParseResult::NoError) {
return;
}
if (debug) {
cerr << " Internet->router: " << summary(frame) << "\n";
}
router.interface(internet_side).recv_frame(frame);
router.route();
});
while (true) {
if (EventLoop::Result::Exit == event_loop.wait_next_event(50)) {
cerr << "Exiting...\n";
return;
}
router.interface(host_side).tick(50);
router.interface(internet_side).tick(50);
if (exit_flag) {
return;
}
}
} catch (const exception &e) {
cerr << "Thread ending from exception: " << e.what() << "\n";
}
});
try {
if (is_client) {
sock.connect({"172.16.0.100", 1234});
} else {
sock.bind({"172.16.0.100", 1234});
sock.listen_and_accept();
}
bidirectional_stream_copy(sock);
sock.wait_until_closed();
} catch (const exception &e) {
cerr << "Exception: " << e.what() << "\n";
}
cerr << "Exiting... ";
exit_flag = true;
network_thread.join();
cerr << "done.\n";
}
void print_usage(const string &argv0) {
cerr << "Usage: " << argv0 << " client HOST PORT [debug]\n";
cerr << "or " << argv0 << " server HOST PORT [debug]\n";
}
int main(int argc, char *argv[]) {
try {
if (argc <= 0) {
abort(); // For sticklers: don't try to access argv[0] if argc <= 0.
}
if (argc != 4 and argc != 5) {
print_usage(argv[0]);
return EXIT_FAILURE;
}
if (argv[1] != "client"s and argv[1] != "server"s) {
print_usage(argv[0]);
return EXIT_FAILURE;
}
program_body(argv[1] == "client"s, argv[2], argv[3], argc == 5);
} catch (const exception &e) {
cerr << e.what() << "\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

34
writeups/lab7.md Normal file
View File

@ -0,0 +1,34 @@
Lab 7 Writeup
=============
My name: [your name here]
My SUNet ID: [your sunetid here]
My lab partner's SUNet ID: [your sunetid here]
I also worked with or collaborated with: [their sunetids here]
I would like to thank/reward these classmates for their help: [list sunetids here]
This lab took me about [n] hours to do. I [did/did not] attend the lab session.
Solo portion:
[]
Group portion:
[]
Creative portion (optional):
[]
Other remarks:
[]
- Optional: I had unexpected difficulty with: [describe]
- Optional: I think you could make this lab better by: [describe]
- Optional: I was surprised by: [describe]
- Optional: I'm not sure about: [describe]