summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrediano Ziglio <fziglio@redhat.com>2016-07-28 12:28:17 +0100
committerFrediano Ziglio <fziglio@redhat.com>2016-07-28 12:28:17 +0100
commit3827fbf367a5e897ed53b33ff8269a28f8abbc2d (patch)
treec119325ddf9693f04e6d47732c61d4f5778c17e3
parent2539c359f67817984008a65cf613d32cd07c1120 (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.md38
-rw-r--r--latency.c80
-rw-r--r--tun.c121
-rw-r--r--tun.h3
4 files changed, 209 insertions, 33 deletions
diff --git a/README.md b/README.md
index 88b4415..de08cd9 100644
--- a/README.md
+++ b/README.md
@@ -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.
diff --git a/latency.c b/latency.c
index 2c70041..c75a4ba 100644
--- a/latency.c
+++ b/latency.c
@@ -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();
diff --git a/tun.c b/tun.c
index 64614fd..fae6a96 100644
--- a/tun.c
+++ b/tun.c
@@ -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;
diff --git a/tun.h b/tun.h
index 90a1a83..b914731 100644
--- a/tun.h
+++ b/tun.h
@@ -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);