#include #include #include #include #include #include #include "evt_core.h" #include "net_tools.h" #include "socks5.h" struct measlat_ctx { int count, size, interval, verbose; char *host, *port, *transport; }; struct measure_conf { uint64_t max_measure; uint64_t payload_size; char* payload; uint64_t counter; }; struct packet_header { uint64_t counter; struct timespec emit_time; }; struct measure_conf* create_measure_conf(int max_mes, int plsize) { struct measure_conf* mc = malloc(sizeof(struct measure_conf)); if (mc == NULL) { perror("Malloc failed"); exit(EXIT_FAILURE); } mc->counter = 0; mc->max_measure = max_mes; mc->payload_size = plsize; mc->payload = malloc(mc->payload_size); if (mc->payload == NULL) { perror("malloc failed"); exit(EXIT_FAILURE); } return mc; } void free_mesure_conf(void* v) { struct measure_conf* mc = (struct measure_conf*)v; free(mc->payload); free(mc); } int on_udp_err(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { return 1; } int on_udp(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { ssize_t res; int secs, nsecs; uint64_t micro_sec; struct timespec curr; struct measure_conf* mc = fdinfo->other; res = read(fdinfo->fd, mc->payload, mc->payload_size); if (res == -1 && errno == EAGAIN) return 1; if (res != mc->payload_size) { perror("read error"); exit(EXIT_FAILURE); } struct packet_header* head = (struct packet_header*) mc->payload; if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1){ perror("clock_gettime error"); exit(EXIT_FAILURE); } secs = curr.tv_sec - head->emit_time.tv_sec; nsecs = curr.tv_nsec - head->emit_time.tv_nsec; micro_sec = secs * 1000000 + nsecs / 1000; printf("Packet %llu latency %luµs\n", (unsigned long long)head->counter, micro_sec); if (head->counter >= mc->max_measure) { printf("Measurement done\n"); exit(EXIT_SUCCESS); } return 0; } void free_timer_conf(void* v) { struct measure_conf* mc = v; free(mc->payload); free(mc); } int on_timer(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { ssize_t s; uint64_t ticks; struct measure_conf* mc = fdinfo->other; s = read(fdinfo->fd, &ticks, sizeof(uint64_t)); if (s == -1 && errno == EAGAIN) return 1; if (s != sizeof(uint64_t)) { perror("Read error"); exit(EXIT_FAILURE); } if (ticks != 1) { fprintf(stderr, "Has ticked %lu times, expected 1 time. This is a bug\n", ticks); } mc->counter++; memset(mc->payload, 0, mc->payload_size); struct packet_header* head = (struct packet_header*)mc->payload; head->counter = mc->counter; if (clock_gettime(CLOCK_MONOTONIC, &head->emit_time) == -1) { perror("clock_gettime error"); exit(EXIT_FAILURE); } struct evt_core_fdinfo* tgtinfo = evt_core_get_first_from_cat (ctx, "udp-read"); if (tgtinfo == NULL) tgtinfo = evt_core_get_first_from_cat (ctx, "tcp-read"); if (tgtinfo == NULL) { printf("No connection yet\n"); return 1; } s = send(tgtinfo->fd, mc->payload, mc->payload_size, 0); if (s < 0) { perror("Send error"); //exit(EXIT_FAILURE); } return 0; } void register_timer(struct evt_core_ctx* evts, int udp, int interval, int count, int size) { struct timespec now; struct itimerspec timer_config; char url[1024]; struct evt_core_cat cat = {0}; struct evt_core_fdinfo fdinfo = {0}; fdinfo.cat = &cat; fdinfo.url = url; if (clock_gettime(CLOCK_REALTIME, &now) == -1) { perror("clock_gettime"); exit(EXIT_FAILURE); } uint64_t micro_sec = interval; timer_config.it_value.tv_sec = now.tv_sec + 1; timer_config.it_value.tv_nsec = now.tv_nsec; timer_config.it_interval.tv_sec = micro_sec / 1000; timer_config.it_interval.tv_nsec = micro_sec % 1000 * 1000000; fdinfo.fd = timerfd_create(CLOCK_REALTIME, 0); if (fdinfo.fd == -1) { perror("Unable to timerfd_create"); exit(EXIT_FAILURE); } if (timerfd_settime (fdinfo.fd, TFD_TIMER_ABSTIME, &timer_config, NULL) == -1) { perror("Unable to timerfd_time"); exit(EXIT_FAILURE); } fdinfo.cat->name = "timer"; fdinfo.other = create_measure_conf(count, size); fdinfo.free_other = free_timer_conf; sprintf(fdinfo.url, "timer:%d:%d", interval, count); evt_core_add_fd (evts, &fdinfo); printf("--- Timer registered\n"); } int on_socks5_success_measlat(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { char url[1024]; struct evt_core_cat cat = {0}; struct evt_core_fdinfo fdinfo_n = {0}; struct socks5_ctx* s5ctx = fdinfo->other; fdinfo_n.cat = &cat; fdinfo_n.url = url; struct evt_core_cat* ucat = evt_core_get_from_cat (ctx, "tcp-read"); if (ucat == NULL) { fprintf(stderr, "Category udp-read not found\n"); exit(EXIT_FAILURE); } struct measlat_ctx* mctx = ucat->app_ctx; fdinfo_n.fd = dup(fdinfo->fd); fdinfo_n.cat->name = "tcp-read"; fdinfo_n.other = create_measure_conf (mctx->count, mctx->size); fdinfo_n.free_other = free_mesure_conf; sprintf(fdinfo_n.url, "tcp:read:%s:%d", s5ctx->addr, s5ctx->port); evt_core_add_fd (ctx, &fdinfo_n); printf("--- Tor socket registered\n"); register_timer(ctx, fdinfo->fd, mctx->interval, mctx->count, mctx->size); return 1; } void spawn_tor_socket(struct evt_core_ctx* evts) { struct evt_core_cat* ucat = evt_core_get_from_cat (evts, "tcp-read"); if (ucat == NULL) { fprintf(stderr, "Category udp-read not found\n"); exit(EXIT_FAILURE); } struct measlat_ctx* mctx = ucat->app_ctx; socks5_create_dns_client (evts, "127.0.0.1", "9050", mctx->host, atoi(mctx->port)); } int on_socks5_failed_measlat(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { evt_core_rm_fd (ctx, fdinfo->fd); spawn_tor_socket(ctx); return 1; } void register_categories(struct evt_core_ctx* evts, struct measlat_ctx* mctx) { struct evt_core_cat template = {0}; template.app_ctx = mctx; evt_core_init(evts, mctx->verbose); template.cb = on_timer; template.name = "timer"; template.flags = EPOLLIN | EPOLLET; evt_core_add_cat(evts, &template); template.cb = on_udp; // intended but not elegant template.err_cb = on_udp_err; // intended but not elegant template.name = "tcp-read"; template.flags = EPOLLIN | EPOLLET; evt_core_add_cat(evts, &template); template.cb = on_udp; template.err_cb = on_udp_err; template.name = "udp-read"; template.flags = EPOLLIN | EPOLLET; evt_core_add_cat(evts, &template); template.cb = on_socks5_success_measlat; template.err_cb = on_socks5_failed_measlat; template.name = "socks5-success"; template.flags = EPOLLET; template.cb = on_socks5_failed_measlat; template.err_cb = on_socks5_failed_measlat; template.name = "socks5-failed"; template.flags = EPOLLET; socks5_init(evts); printf("--- Categories registered\n"); } void spawn_udp_socket(struct evt_core_ctx* evts) { struct evt_core_cat* ucat = evt_core_get_from_cat (evts, "udp-read"); if (ucat == NULL) { fprintf(stderr, "Category udp-read not found\n"); exit(EXIT_FAILURE); } struct measlat_ctx* mctx = ucat->app_ctx; int udp_sock = create_udp_client (mctx->host, mctx->port); char url[1024]; struct evt_core_cat cat = {0}; struct evt_core_fdinfo fdinfo = {0}; fdinfo.cat = &cat; fdinfo.url = url; fdinfo.fd = udp_sock; fdinfo.cat->name = "udp-read"; fdinfo.other = create_measure_conf (mctx->count, mctx->size); fdinfo.free_other = free_mesure_conf; sprintf(fdinfo.url, "udp:read:%s:%s", mctx->host, mctx->port); evt_core_add_fd (evts, &fdinfo); printf("--- UDP socket registered\n"); register_timer(evts, fdinfo.fd, mctx->interval, mctx->count, mctx->size); } int main(int argc, char** argv) { setvbuf(stdout, NULL, _IONBF, 0); printf("~ measlat ~\n"); int opt; struct measlat_ctx mctx = {0}; struct evt_core_ctx evts = {0}; // 1. Parse parameters while ((opt = getopt(argc, argv, "vh:p:c:s:i:t:")) != -1) { switch(opt) { case 'v': mctx.verbose++; break; case 'h': // host mctx.host = optarg; break; case 'p': // port mctx.port = optarg; break; case 't': // transport mctx.transport = optarg; break; case 'c': // count mctx.count = atoi(optarg); break; case 's': // size - payload in bytes mctx.size = atoi(optarg); break; case 'i': // interval - every ms mctx.interval = atoi(optarg); break; default: goto usage; } } // 2. Check and fix parameters size_t header_size = sizeof(struct packet_header); if (mctx.interval <= 0) mctx.interval = 1000; if (mctx.count <= 0) mctx.count = 1; if (mctx.size < header_size) mctx.size = header_size; if (mctx.transport == NULL) mctx.transport = "udp"; if (mctx.host == NULL || mctx.port == NULL) goto usage; // 3. Bind events register_categories(&evts, &mctx); if (strcmp(mctx.transport, "udp") == 0) spawn_udp_socket(&evts); else if (strcmp(mctx.transport, "tor") == 0) spawn_tor_socket(&evts); // 4. Run main loop evt_core_loop(&evts); return 0; usage: fprintf(stderr, "Usage: %s -h -p [-t ] [-c ] [-i ] [-s ]\n", argv[0]); exit(EXIT_FAILURE); }