diff options
author | Frediano Ziglio <fziglio@redhat.com> | 2016-07-28 12:28:17 +0100 |
---|---|---|
committer | Frediano Ziglio <fziglio@redhat.com> | 2016-07-28 12:28:17 +0100 |
commit | 3827fbf367a5e897ed53b33ff8269a28f8abbc2d (patch) | |
tree | c119325ddf9693f04e6d47732c61d4f5778c17e3 | |
parent | 2539c359f67817984008a65cf613d32cd07c1120 (diff) |
change completely client/server protocol
Allows to specify options only on one end making easier to change them.
Also one UDP port is automatically assigned.
-rw-r--r-- | README.md | 38 | ||||
-rw-r--r-- | latency.c | 80 | ||||
-rw-r--r-- | tun.c | 121 | ||||
-rw-r--r-- | tun.h | 3 |
4 files changed, 209 insertions, 33 deletions
@@ -8,7 +8,16 @@ with high latency and low bandwidth. Currently requires root privileges as use tun/tap. -Syntax: `latency` *delay* *bandwidth* +Syntax: + `latency` *delay* *bandwidth* [`--client` *server_ip*] [*OPTION*] +or + `latency` `--server` [*OPTION*] + +Options: + + * `--port` *port* specify the port to use for client/server. + The default is `61234`. + * `--help` show usage help. For delay you can specify **ms** (milliseconds, default) or **s** (seconds) for unit. @@ -30,3 +39,30 @@ $ latency 10ms 2.3M Currently connecting to 192.168.127.1 allows to connect to the local machine with the connection modified as specified. + +To use the client/server launch the server first with + +```bash +$ latency --server +``` + +(you can optionally specify a port). Then on the client machine +you can launch + +```bash +$ latency 10ms 2.3M --client 192.168.0.11 +``` + +(where `192.168.0.11` is the address of the server). The client +will send latency/bandwidth to server at the beginning so launching +the client where is frequent to change latency/bandwidth make this +change easier (currently you have to stop the client and open with +new options). + +The client/server used UDP protocol. The server will bind to the UDP +port specified while the client will send packets to this port. If +something is not working check your firewall for this port/protocol. + +For better results use the restricted latency/bandwidth over a more +powerful connection (like 1 Gbit cable). This will allow to have for +instance a unrestricted connection (like ssh) and a restricted one. @@ -12,6 +12,8 @@ #include <string.h> #include <unistd.h> #include <limits.h> +#include <getopt.h> +#include <stdbool.h> #include <sys/wait.h> #include <arpa/inet.h> @@ -53,10 +55,19 @@ uint64_t latency_us; unsigned rate_bytes; static void -usage(void) +usage(bool error) { - fprintf(stderr, "syntax: latency <latency> <rate> [<ip> <port>]\n"); - exit(EXIT_FAILURE); + fprintf(error ? stderr : stdout, + "Usage:\n" + "\tlatency <latency> <rate> [--client <ip>] [OPTION]...\n" + "or\n" + "\tlatency --server [OPTION]...\n" + "\n" + "Options:\n" + " -h, --help Show this help\n" + " --port <PORT> Specify port to use (default 61234)\n" + ); + exit(error ? EXIT_FAILURE : EXIT_SUCCESS); } static const unit latency_units[] = { @@ -79,6 +90,11 @@ static const unit rate_units[] = { { NULL, 0 } }; +static const unit no_units[] = { + { "", 1 }, + { NULL, 0 } +}; + /**/ int main(int argc, char **argv) @@ -97,13 +113,57 @@ main(int argc, char **argv) if (ruid != euid) setuid(ruid); - if (argc < 3) - usage(); - - latency_us = parse_value(argv[1], 0, 10000000, latency_units); - rate_bytes = parse_value(argv[2], 1, INT_MAX, rate_units); - if (argc >= 5) - tun_set_ip_port(argv[3], atoi(argv[4])); + enum { MODE_undef, MODE_server, MODE_client } mode = MODE_undef; + enum { ARG_port = 256, ARG_client, ARG_server }; + static struct option long_options[] = { + {"client", required_argument, 0, ARG_client }, + {"server", no_argument, 0, ARG_server }, + {"port", required_argument, 0, ARG_port }, + {"help", no_argument, 0, 'h' }, + {0, 0, 0, 0 } + }; + + const char *str_port = "61234"; + const char *client_dest = NULL; + int ch; + while ((ch = getopt_long(argc, argv, "h", long_options, NULL)) != -1) { + switch (ch) { + case 'h': + usage(false); + case ARG_client: + if (mode == MODE_server) + usage(true); + mode = MODE_client; + client_dest = optarg; + break; + case ARG_server: + if (mode == MODE_client) + usage(true); + mode = MODE_server; + break; + case ARG_port: + str_port = optarg; + break; + default: + usage(true); + } + } + if (mode == MODE_undef) + mode = MODE_client; + + int port = parse_value(str_port, 1, 65535, no_units); + if (mode == MODE_client) { + if (optind + 2 > argc) + usage(true); + + latency_us = parse_value(argv[optind], 0, 10000000, latency_units); + rate_bytes = parse_value(argv[optind+1], 1, INT_MAX, rate_units); + tun_set_client(client_dest, port); + } else { + latency_us = 0; + rate_bytes = 100000000; + tun_set_server(port); + } setup_signals(); @@ -25,6 +25,28 @@ static int tun_fd = -1; static int remote_sock = -1; +static struct sockaddr_in remote_addr; +static bool remote_connected = false; +static bool is_server = false; + +/* Fake packets are used to send commands. + * A fake packet has a ip header with 0 check, saddr and daddr fields + * followed by all uint32_t fields. + * First field is the type, defined as follow. + */ +enum { + FAKE_settings = 1, +}; +enum { + FAKE_FIELD_type = 0, + FAKE_FIELD_latency_us = 1, + FAKE_FIELD_rate_bytes = 2, +}; + +typedef struct { + struct iphdr iphdr; + uint32_t fields[4]; +} fake_ip_packet; /** * @param dev name of interface. MUST have enough @@ -99,39 +121,62 @@ create_remote_socket(void) } void -tun_set_ip_port(const char *ip, int port) +tun_set_client(const char *ip, int port) { - static in_addr_t redirect_ip; + in_addr_t server_ip; if (port < 1 || port > 65535) { fprintf(stderr, "Wrong port value %d\n", port); exit(EXIT_FAILURE); } - redirect_ip = inet_addr(ip); - if (redirect_ip == INADDR_NONE) { + server_ip = inet_addr(ip); + if (server_ip == INADDR_NONE) { fprintf(stderr, "Wrong ip format %s\n", ip); exit(EXIT_FAILURE); } create_remote_socket(); + memset(&remote_addr, 0, sizeof(remote_addr)); + remote_addr.sin_family = AF_INET; + remote_addr.sin_port = htons((short) port); + remote_addr.sin_addr.s_addr = server_ip; + + /* initialize server */ + fake_ip_packet pkt; + memset(&pkt, 0, sizeof(pkt)); + pkt.fields[FAKE_FIELD_type] = htonl(FAKE_settings); + pkt.fields[FAKE_FIELD_latency_us] = htonl(latency_us); + pkt.fields[FAKE_FIELD_rate_bytes] = htonl(rate_bytes); + sendto(remote_sock, &pkt, sizeof(pkt), MSG_NOSIGNAL, + &remote_addr, sizeof(remote_addr)); + + remote_connected = true; +} + +void +tun_set_server(int port) +{ + if (port < 1 || port > 65535) { + fprintf(stderr, "Wrong port value %d\n", port); + exit(EXIT_FAILURE); + } + + create_remote_socket(); + struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons((short) port); - sin.sin_addr.s_addr = INADDR_ANY; + if (bind(remote_sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { perror("bind"); exit(EXIT_FAILURE); } - - sin.sin_addr.s_addr = redirect_ip; - if (connect(remote_sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { - perror("connect"); - exit(EXIT_FAILURE); - } + remote_connected = false; + is_server = true; } #define MIN_PKT_LEN 2000u @@ -252,10 +297,13 @@ writer_proc(void *ptr) uint64_t curr_time = get_time_us(); if (pkt->time_to_send > curr_time) usleep(pkt->time_to_send - curr_time); - if (remote_sock >= 0) - send(remote_sock, pkt->data, pkt->len, MSG_NOSIGNAL); - else + if (remote_sock >= 0) { + if (remote_connected) + sendto(remote_sock, pkt->data, pkt->len, MSG_NOSIGNAL, + &remote_addr, sizeof(remote_addr)); + } else { write(tun_fd, pkt->data, pkt->len); + } release_packet(pkt); } return NULL; @@ -271,6 +319,27 @@ bytes2time(int64_t bytes) return bytes * bytes2time_ratio; } +static bool +handle_fake_packet(const uint8_t *data, size_t len) +{ + const fake_ip_packet *pkt = (const fake_ip_packet *) data; + if (pkt->iphdr.check || pkt->iphdr.saddr || pkt->iphdr.daddr) + return false; + + const uint32_t *fields = pkt->fields; + + switch (ntohl(fields[FAKE_FIELD_type])) { + case FAKE_settings: + latency_us = ntohl(fields[FAKE_FIELD_latency_us]); + rate_bytes = ntohl(fields[FAKE_FIELD_rate_bytes]); + if (!rate_bytes) + rate_bytes = 1; + bytes2time_ratio = (double) 1000000.0 / rate_bytes; + break; + } + return true; +} + void handle_tun(void) { @@ -305,16 +374,26 @@ handle_tun(void) int len; if (fds[1].revents & POLLIN) { - len = recv(remote_sock, pkt->data, MIN_PKT_LEN, 0); + if (!is_server) { + len = recv(remote_sock, pkt->data, MIN_PKT_LEN, 0); + } else { + socklen_t sock_len = sizeof(remote_addr); + len = recvfrom(remote_sock, pkt->data, MIN_PKT_LEN, 0, + &remote_addr, &sock_len); + } if (len <= 0) break; - write(tun_fd, pkt->data, len); - continue; - } else if (fds[0].revents & POLLIN) { - len = read(tun_fd, pkt->data, MIN_PKT_LEN); - } else { - continue; + remote_connected = true; + if (len < sizeof(struct iphdr)) + continue; + if (!handle_fake_packet(pkt->data, len)) + write(tun_fd, pkt->data, len); } + + if ((fds[0].revents & POLLIN) == 0) + continue; + + len = read(tun_fd, pkt->data, MIN_PKT_LEN); if (len < 0) break; @@ -1,4 +1,5 @@ void tun_setup(void); -void tun_set_ip_port(const char *ip, int port); +void tun_set_client(const char *ip, int port); +void tun_set_server(int port); void handle_tun(void); |