int netem_set_params(const char *iface, struct netem_params *params) { struct rtnl_link *link; struct rtnl_qdisc *qdisc; int err; pthread_mutex_lock(&nl_sock_mutex); /* filter link by name */ if ((link = rtnl_link_get_by_name(link_cache, iface)) == NULL) { fprintf(stderr, "unknown interface/link name.\n"); pthread_mutex_unlock(&nl_sock_mutex); return -1; } if (!(qdisc = rtnl_qdisc_alloc())) { /* OOM error */ fprintf(stderr, "couldn't alloc qdisc\n"); pthread_mutex_unlock(&nl_sock_mutex); return -1; } rtnl_tc_set_link(TC_CAST(qdisc), link); rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT); rtnl_tc_set_kind(TC_CAST(qdisc), "netem"); rtnl_netem_set_delay(qdisc, params->delay * 1000); /* expects microseconds */ rtnl_netem_set_jitter(qdisc, params->jitter * 1000); /* params->loss is given in 10ths of a percent */ rtnl_netem_set_loss(qdisc, (params->loss * (UINT_MAX / 1000))); /* Submit request to kernel and wait for response */ err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE | NLM_F_REPLACE); /* Return the qdisc object to free memory resources */ rtnl_qdisc_put(qdisc); if (err < 0) { fprintf(stderr, "Unable to add qdisc: %s\n", nl_geterror(err)); pthread_mutex_unlock(&nl_sock_mutex); return err; } if ((err = nl_cache_refill(sock, link_cache)) < 0) { fprintf(stderr, "Unable to resync link cache: %s\n", nl_geterror(err)); pthread_mutex_unlock(&nl_sock_mutex); return -1; } pthread_mutex_unlock(&nl_sock_mutex); return 0; }
static int dist_load(int argc, char *argv[]) { FILE *fp; double mu, sigma, rho; if (argc == 1) { if (!(fp = fopen(argv[0], "r"))) error(-1, errno, "Failed to open file: %s", argv[0]); } else fp = stdin; short *inverse = dist_make(fp, &mu, &sigma, &rho); if (!inverse) error(-1, 0, "Failed to generate distribution"); int ret; struct nl_sock *sock; struct rtnl_link *link; struct rtnl_tc *qdisc_prio = NULL; struct rtnl_tc *qdisc_netem = NULL; struct rtnl_tc *cls_fw = NULL; /* Create connection to netlink */ sock = nl_socket_alloc(); nl_connect(sock, NETLINK_ROUTE); /* Get interface */ link = tc_get_link(sock, cfg.dev); if (!link) error(-1, 0, "Interface does not exist: %s", cfg.dev); /* Reset TC subsystem */ ret = tc_reset(sock, link); if (ret && ret != -NLE_OBJ_NOTFOUND) error(-1, 0, "Failed to reset TC: %s", nl_geterror(ret)); /* Setup TC subsystem */ if ((ret = tc_prio(sock, link, &qdisc_prio))) error(-1, 0, "Failed to setup TC: prio qdisc: %s", nl_geterror(ret)); if ((ret = tc_classifier(sock, link, &cls_fw, cfg.mark, cfg.mask))) error(-1, 0, "Failed to setup TC: fw filter: %s", nl_geterror(ret)); if ((ret = tc_netem(sock, link, &qdisc_netem))) error(-1, 0, "Failed to setup TC: netem qdisc: %s", nl_geterror(ret)); /* We will use the default normal distribution for now */ if (rtnl_netem_set_delay_distribution_data((struct rtnl_qdisc *) qdisc_netem, inverse, TABLESIZE)) error(-1, 0, "Failed to set netem delay distrubtion: %s", nl_geterror(ret)); rtnl_netem_set_delay((struct rtnl_qdisc *) qdisc_netem, mu); rtnl_netem_set_jitter((struct rtnl_qdisc *) qdisc_netem, sigma); nl_close(sock); nl_socket_free(sock); return 0; }