Sponge
CS144's user-space TCP library
tcp_ipv4.cc
Go to the documentation of this file.
2 #include "tcp_config.hh"
3 #include "tcp_sponge_socket.hh"
4 #include "tun.hh"
5 
6 #include <cstdint>
7 #include <cstdlib>
8 #include <cstring>
9 #include <iostream>
10 #include <random>
11 #include <string>
12 #include <tuple>
13 
14 using namespace std;
15 
16 constexpr const char *TUN_DFLT = "tun144";
17 const string LOCAL_ADDRESS_DFLT = "169.254.144.9";
18 
19 static void show_usage(const char *argv0, const char *msg) {
20  cout << "Usage: " << argv0 << " [options] <host> <port>\n\n"
21  << " Option Default\n"
22  << " -- --\n\n"
23 
24  << " -l Server (listen) mode. (client mode)\n"
25  << " In server mode, <host>:<port> is the address to bind.\n\n"
26 
27  << " -a <addr> Set source address (client mode only) " << LOCAL_ADDRESS_DFLT << "\n"
28  << " -s <port> Set source port (client mode only) (random)\n\n"
29 
30  << " -w <winsz> Use a window of <winsz> bytes " << TCPConfig::MAX_PAYLOAD_SIZE
31  << "\n\n"
32 
33  << " -t <tmout> Set rt_timeout to tmout " << TCPConfig::TIMEOUT_DFLT << "\n\n"
34 
35  << " -d <tundev> Connect to tun <tundev> " << TUN_DFLT << "\n\n"
36 
37  << " -Lu <loss> Set uplink loss to <rate> (float in 0..1) (no loss)\n"
38  << " -Ld <loss> Set downlink loss to <rate> (float in 0..1) (no loss)\n\n"
39 
40  << " -h Show this message.\n\n";
41 
42  if (msg != nullptr) {
43  cout << msg;
44  }
45  cout << endl;
46 }
47 
48 static void check_argc(int argc, char **argv, int curr, const char *err) {
49  if (curr + 3 >= argc) {
50  show_usage(argv[0], err);
51  exit(1);
52  }
53 }
54 
56  TCPConfig c_fsm{};
57  FdAdapterConfig c_filt{};
58  char *tundev = nullptr;
59 
60  int curr = 1;
61  bool listen = false;
62 
63  string source_address = LOCAL_ADDRESS_DFLT;
64  string source_port = to_string(uint16_t(random_device()()));
65 
66  while (argc - curr > 2) {
67  if (strncmp("-l", argv[curr], 3) == 0) {
68  listen = true;
69  curr += 1;
70 
71  } else if (strncmp("-a", argv[curr], 3) == 0) {
72  check_argc(argc, argv, curr, "ERROR: -a requires one argument.");
73  source_address = argv[curr + 1];
74  curr += 2;
75 
76  } else if (strncmp("-s", argv[curr], 3) == 0) {
77  check_argc(argc, argv, curr, "ERROR: -s requires one argument.");
78  source_port = argv[curr + 1];
79  curr += 2;
80 
81  } else if (strncmp("-w", argv[curr], 3) == 0) {
82  check_argc(argc, argv, curr, "ERROR: -w requires one argument.");
83  c_fsm.recv_capacity = strtol(argv[curr + 1], nullptr, 0);
84  curr += 2;
85 
86  } else if (strncmp("-t", argv[curr], 3) == 0) {
87  check_argc(argc, argv, curr, "ERROR: -t requires one argument.");
88  c_fsm.rt_timeout = strtol(argv[curr + 1], nullptr, 0);
89  curr += 2;
90 
91  } else if (strncmp("-d", argv[curr], 3) == 0) {
92  check_argc(argc, argv, curr, "ERROR: -t requires one argument.");
93  tundev = argv[curr + 1];
94  curr += 2;
95 
96  } else if (strncmp("-Lu", argv[curr], 3) == 0) {
97  check_argc(argc, argv, curr, "ERROR: -Lu requires one argument.");
98  float lossrate = strtof(argv[curr + 1], nullptr);
99  using LossRateUpT = decltype(c_filt.loss_rate_up);
100  c_filt.loss_rate_up =
101  static_cast<LossRateUpT>(static_cast<float>(numeric_limits<LossRateUpT>::max()) * lossrate);
102  curr += 2;
103 
104  } else if (strncmp("-Ld", argv[curr], 3) == 0) {
105  check_argc(argc, argv, curr, "ERROR: -Lu requires one argument.");
106  float lossrate = strtof(argv[curr + 1], nullptr);
107  using LossRateDnT = decltype(c_filt.loss_rate_dn);
108  c_filt.loss_rate_dn =
109  static_cast<LossRateDnT>(static_cast<float>(numeric_limits<LossRateDnT>::max()) * lossrate);
110  curr += 2;
111 
112  } else if (strncmp("-h", argv[curr], 3) == 0) {
113  show_usage(argv[0], nullptr);
114  exit(0);
115 
116  } else {
117  show_usage(argv[0], string("ERROR: unrecognized option " + string(argv[curr])).c_str());
118  exit(1);
119  }
120  }
121 
122  // parse positional command-line arguments
123  if (listen) {
124  c_filt.source = {"0", argv[curr + 1]};
125  if (c_filt.source.port() == 0) {
126  show_usage(argv[0], "ERROR: listen port cannot be zero in server mode.");
127  exit(1);
128  }
129  } else {
130  c_filt.destination = {argv[curr], argv[curr + 1]};
131  c_filt.source = {source_address, source_port};
132  }
133 
134  return make_tuple(c_fsm, c_filt, listen, tundev);
135 }
136 
137 int main(int argc, char **argv) {
138  try {
139  if (argc < 3) {
140  show_usage(argv[0], "ERROR: required arguments are missing.");
141  return EXIT_FAILURE;
142  }
143 
144  auto [c_fsm, c_filt, listen, tun_dev_name] = get_config(argc, argv);
146  TCPOverIPv4OverTunFdAdapter(TunFD(tun_dev_name == nullptr ? TUN_DFLT : tun_dev_name))));
147 
148  if (listen) {
149  tcp_socket.listen_and_accept(c_fsm, c_filt);
150  } else {
151  tcp_socket.connect(c_fsm, c_filt);
152  }
153 
154  bidirectional_stream_copy(tcp_socket);
155  tcp_socket.wait_until_closed();
156  } catch (const exception &e) {
157  cerr << "Exception: " << e.what() << endl;
158  return EXIT_FAILURE;
159  }
160 
161  return EXIT_SUCCESS;
162 }
bidirectional_stream_copy.hh
std::strtol
T strtol(T... args)
TCPConfig::MAX_PAYLOAD_SIZE
static constexpr size_t MAX_PAYLOAD_SIZE
Max TCP payload that fits in either IPv4 or UDP datagram.
Definition: tcp_config.hh:15
std::make_tuple
T make_tuple(T... args)
TCPSpongeSocket::wait_until_closed
void wait_until_closed()
Definition: tcp_sponge_socket.cc:194
std::exception
TUN_DFLT
constexpr const char * TUN_DFLT
Definition: tcp_ipv4.cc:16
std::tuple
show_usage
static void show_usage(const char *argv0, const char *msg)
Definition: tcp_ipv4.cc:19
LOCAL_ADDRESS_DFLT
const string LOCAL_ADDRESS_DFLT
Definition: tcp_ipv4.cc:17
FdAdapterConfig
Config for classes derived from FdAdapter.
Definition: tcp_config.hh:26
tcp_config.hh
std::random_device
std::cout
tun.hh
LossyTCPOverIPv4OverTunFdAdapter
LossyFdAdapter< TCPOverIPv4OverTunFdAdapter > LossyTCPOverIPv4OverTunFdAdapter
Typedef for TCPOverIPv4OverTunFdAdapter.
Definition: tuntap_adapter.hh:41
listen
sock1 listen(1)
std::to_string
T to_string(T... args)
TCPSpongeSocket::connect
void connect(const TCPConfig &c_tcp, const FdAdapterConfig &c_ad)
Connect using the specified configurations; blocks until connect succeeds or fails.
Definition: tcp_sponge_socket.cc:206
std::uint16_t
TCPConfig
Config for TCP sender and receiver.
Definition: tcp_config.hh:12
TCPSpongeSocket::listen_and_accept
void listen_and_accept(const TCPConfig &c_tcp, const FdAdapterConfig &c_ad)
Listen and accept using the specified configurations; blocks until accept succeeds or fails.
Definition: tcp_sponge_socket.cc:234
bidirectional_stream_copy
void bidirectional_stream_copy(Socket &socket)
Copy socket input/output to stdin/stdout until finished.
Definition: bidirectional_stream_copy.cc:12
TCPOverIPv4OverTunFdAdapter
A FD adapter for IPv4 datagrams read from and written to a TUN device.
Definition: tuntap_adapter.hh:13
std::strncmp
T strncmp(T... args)
std::endl
T endl(T... args)
std
main
int main(int argc, char **argv)
Definition: tcp_ipv4.cc:137
std::strtof
T strtof(T... args)
TCPConfig::TIMEOUT_DFLT
static constexpr uint16_t TIMEOUT_DFLT
Default re-transmit timeout is 1 second.
Definition: tcp_config.hh:16
get_config
static tuple< TCPConfig, FdAdapterConfig, bool, char * > get_config(int argc, char **argv)
Definition: tcp_ipv4.cc:55
std::numeric_limits::max
T max(T... args)
TunFD
A FileDescriptor to a Linux TUN device.
Definition: tun.hh:16
check_argc
static void check_argc(int argc, char **argv, int curr, const char *err)
Definition: tcp_ipv4.cc:48
tcp_sponge_socket.hh
std::exit
T exit(T... args)
std::exception::what
T what(T... args)
TCPSpongeSocket
Multithreaded wrapper around TCPConnection that approximates the Unix sockets API.
Definition: tcp_sponge_socket.hh:21