--- /dev/null
+/*
+ * A tool for measuring FWP latency. This program aims to be simpler
+ * than wme_test and is intended to be run on a single machine with
+ * multiple wifi interfaces and send-to-self kernel patch applied.
+ *
+ * This program creates both sides of communication and measures the
+ * communication delays.
+ */
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <frsh.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <fwp_res.h>
+#include <error.h>
+#include <sys/mman.h>
+
+int opt_budget = 1024;
+int opt_period = 20;
+
+struct in_addr src, dst;
+
+void set_rt_prio(int priority)
+{
+ int maxpri, minpri;
+ static struct sched_param param;
+
+ if ((maxpri = sched_get_priority_max(SCHED_FIFO)) == -1) {
+ fprintf(stderr, "warning: sched_get_priority_max failed");
+ }
+
+ if ((minpri = sched_get_priority_min(SCHED_FIFO)) == -1) {
+ fprintf(stderr, "warning: sched_get_priority_min failed");
+ }
+
+ if (priority > maxpri) {
+ fprintf(stderr, "warning: maximum priority allowed is %d.\n", maxpri);
+ }
+ if (priority < minpri) {
+ fprintf(stderr, "warning: minimum priority allowed is %d.\n", minpri);
+ }
+
+ param.sched_priority = priority;
+
+ if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
+ fprintf(stderr, "warning: sched_setscheduler failed");
+ }
+
+ mlockall(MCL_CURRENT | MCL_FUTURE);
+}
+
+
+int negotiate_contract(frsh_vres_id_t *vres)
+{
+ frsh_contract_t contract;
+ int ret;
+ frsh_rel_time_t period;
+ frsh_rel_time_t budget;
+ frsh_rel_time_t deadline;
+ fres_block_fwp *fwp;
+
+ ret = frsh_contract_init(&contract);
+ if (ret) PERROR_AND_EXIT(ret, "frsh_contract_init");
+
+ ret = frsh_contract_set_resource_and_label(
+ &contract,
+ FRSH_RT_NETWORK, FRSH_NETPF_FWP,
+ NULL);
+ if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_resource_and_label");
+
+ frsh_network_bytes_to_budget(FRSH_NETPF_FWP, opt_budget, &budget);
+ period = fosa_msec_to_rel_time(opt_period);
+ ret = frsh_contract_set_basic_params(&contract,
+ &budget,
+ &period,
+ FRSH_WT_BOUNDED,
+ FRSH_CT_REGULAR);
+ if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_basic_params");
+
+
+ /* FWP doesn't accept smaller deadlines than 30 ms. */
+ if (frsh_rel_time_smaller(period, frsh_msec_to_rel_time(30)))
+ deadline = frsh_msec_to_rel_time(30);
+ else
+ deadline = period;
+ ret = frsh_contract_set_timing_reqs(&contract, false, &deadline);
+ if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_timing_reqs");
+
+ fwp = malloc(sizeof(*fwp));
+ if (!fwp) PERROR_AND_EXIT(errno, "malloc");
+ fwp->src = src.s_addr;
+ ret = fres_contract_add_fwp(contract, fwp);
+ if (ret) PERROR_AND_EXIT(ret, "fres_contract_add_fwp");
+
+ ret = frsh_contract_negotiate(&contract, vres);
+ if (ret) PERROR_AND_EXIT(ret, "frsh_contract_negotiate");
+
+ frsh_contract_destroy(&contract);
+
+ return 0;
+}
+
+void create_endpoints(frsh_vres_id_t vres,
+ frsh_send_endpoint_t *epsrc,
+ frsh_receive_endpoint_t *epdst)
+{
+ int ret;
+ frsh_send_endpoint_protocol_info_t spi = { NULL, 0 };
+ frsh_receive_endpoint_protocol_info_t rpi = { NULL, 0 };
+ frsh_endpoint_queueing_info_t qi = { .queue_size=0,
+ .queue_policy=FRSH_QRP_OLDEST };
+
+ ret = frsh_receive_endpoint_create(FRSH_NETPF_FWP, 0, qi, rpi,
+ epdst);
+ if (ret != 0) error(1, errno, "fwp_receive_endpoint_create");
+
+ unsigned int port;
+ frsh_receive_endpoint_get_params(*epdst, NULL, &port, NULL, NULL);
+
+ ret = frsh_send_endpoint_create(FRSH_NETPF_FWP,
+ dst.s_addr, port, spi,
+ epsrc);
+ if (ret < 0) error(1, errno, "frsh_send_endpoint_create()");
+
+ ret = frsh_send_endpoint_bind(vres, *epsrc);
+ if (ret != 0) error(1, errno, "frsh_send_endpoint_bind");
+}
+
+
+static struct option long_opts[] = {
+ { "period", 1, 0, 'p' },
+ { "budget", 1, 0, 'b' },
+ { "source", 1, 0, 's' },
+ { "dest", 1, 0, 'd' },
+ { 0, 0, 0, 0}
+};
+
+static void
+usage(void)
+{
+ printf("usage: fwp-timing [ options ]\n");
+ printf(" -p, --period <ms> period in miliseconds\n");
+ printf(" -b, --budget <bytes> how many bytes is sent in each period\n");
+ printf(" -s, --source <ip> source IP address\n");
+ printf(" -d, --dest <ip:port> destination IP address and port\n");
+}
+
+void parse_opts(int argc, char *argv[])
+{
+ char opt;
+ int ret;
+
+ while ((opt = getopt_long(argc, argv, "b:p:s:d:", long_opts, NULL)) != -1) {
+ switch (opt) {
+ case 'b':
+ opt_budget = atoi(optarg);
+ break;
+ case 'p':
+ opt_period = atoi(optarg);
+ break;
+ case 's':
+ ret = inet_aton(optarg, &src);
+ if (!ret) {
+ fprintf(stderr, "Source IP address not recognized: %s\n",
+ optarg);
+ usage();
+ exit(1);
+ }
+ break;
+ case 'd':
+ ret = inet_aton(optarg, &dst);
+ if (!ret) {
+ fprintf(stderr, "Destination IP address not recognized: %s\n",
+ optarg);
+ usage();
+ exit(1);
+ }
+ break;
+ default:
+ usage();
+ exit(1);
+ }
+ }
+}
+
+volatile bool exit_flag = false;
+
+void stopper()
+{
+ exit_flag = true;
+}
+
+int
+timespec_subtract (result, x, y)
+ struct timespec *result, *x, *y;
+{
+ /* Perform the carry for the later subtraction by updating Y. */
+ if (x->tv_nsec < y->tv_nsec) {
+ int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
+ y->tv_nsec -= 1000000000 * nsec;
+ y->tv_sec += nsec;
+ }
+ if (x->tv_nsec - y->tv_nsec > 1000000000) {
+ int nsec = (x->tv_nsec - y->tv_nsec) / 1000000000;
+ y->tv_nsec += 1000000000 * nsec;
+ y->tv_sec -= nsec;
+ }
+
+ /* Compute the time remaining to wait.
+ `tv_nsec' is certainly positive. */
+ result->tv_sec = x->tv_sec - y->tv_sec;
+ result->tv_nsec = x->tv_nsec - y->tv_nsec;
+
+ /* Return 1 if result is negative. */
+ return x->tv_sec < y->tv_sec;
+}
+
+static inline double ts2d(struct timespec *ts)
+{
+ return ts->tv_sec + 1e-9*ts->tv_nsec;
+}
+
+static inline double tsdiff2d(struct timespec *x,
+ struct timespec *y)
+{
+ struct timespec r;
+ timespec_subtract(&r, x, y);
+ return ts2d(&r);
+}
+
+struct msg {
+ int cnt;
+ struct timespec ts;
+};
+
+void *receiver(void *arg)
+{
+ frsh_receive_endpoint_t epdst = (frsh_receive_endpoint_t)arg;
+ size_t mlen;
+ int ret;
+ struct timespec tss, tsr;
+ struct msg *msg;
+ msg = malloc(opt_budget);
+ if (!msg) error(1, errno, "malloc msg");
+
+ while (!exit_flag) {
+ ret = frsh_receive_sync(epdst, msg, opt_budget, &mlen, NULL);
+ clock_gettime(CLOCK_MONOTONIC, &tsr);
+ tss = msg->ts;
+ printf("%10d: %10.3lf ms\n",
+ msg->cnt, tsdiff2d(&tsr, &tss)*1000);
+ }
+ return NULL;
+}
+
+void run()
+{
+ frsh_vres_id_t vres;
+ frsh_send_endpoint_t epsrc;
+ frsh_receive_endpoint_t epdst;
+ int ret;
+ struct msg *msg;
+ long int cnt=0;
+ pthread_t receiver_id;
+
+ msg = malloc(opt_budget);
+ if (!msg) error(1, errno, "malloc msg");
+
+ ret = frsh_init();
+ if (ret) PERROR_AND_EXIT(ret, "frsh_init");
+
+ negotiate_contract(&vres);
+ create_endpoints(vres, &epsrc, &epdst);
+
+ set_rt_prio(50);
+
+ ret = pthread_create(&receiver_id, NULL, receiver, epdst);
+
+ if (signal(SIGTERM, stopper) == SIG_ERR)
+ error(1, errno, "Error in signal registration");
+ if (signal(SIGINT, stopper) == SIG_ERR)
+ error(1, errno, "Signal handler registration error");
+
+ struct timespec next_period, tss;
+ clock_gettime(CLOCK_MONOTONIC, &next_period);
+ while (!exit_flag) {
+ clock_gettime(CLOCK_MONOTONIC, &tss);
+ msg->cnt = cnt++;
+ msg->ts = tss;
+ ret = frsh_send_async(epsrc, msg, opt_budget);
+
+ next_period.tv_nsec += opt_period * 1000000;
+ if (next_period.tv_nsec >= 1000000000) {
+ next_period.tv_nsec -= 1000000000;
+ next_period.tv_sec++;
+ }
+
+ clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
+ &next_period, NULL);
+ }
+
+ frsh_contract_cancel(vres);
+}
+
+int main(int argc, char *argv[])
+{
+ src.s_addr = htonl(INADDR_LOOPBACK);
+ dst.s_addr = htonl(INADDR_LOOPBACK);
+
+ parse_opts(argc, argv);
+ run();
+ return 0;
+}