Merge remote-tracking branch 'official/lab7-startercode'
This commit is contained in:
commit
7dd3fed217
@ -9,3 +9,5 @@ add_sponge_exec (tcp_ip_ethernet stream_copy)
|
||||
add_sponge_exec (webget)
|
||||
add_sponge_exec (tcp_benchmark)
|
||||
add_sponge_exec (network_simulator)
|
||||
add_sponge_exec (lab7 stream_copy)
|
||||
add_sponge_exec (bouncer)
|
||||
|
||||
70
apps/bouncer.cc
Normal file
70
apps/bouncer.cc
Normal 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
313
apps/lab7.cc
Normal 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
34
writeups/lab7.md
Normal 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]
|
||||
Loading…
Reference in New Issue
Block a user