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; }
/** * @ingroup cli * @defgroup cli_tc Traffic Control * @{ */ void nl_cli_tc_parse_dev(struct rtnl_tc *tc, struct nl_cache *link_cache, char *name) { struct rtnl_link *link; link = rtnl_link_get_by_name(link_cache, name); if (!link) nl_cli_fatal(ENOENT, "Link \"%s\" does not exist.", name); rtnl_tc_set_link(tc, link); rtnl_link_put(link); }
/* * Function that adds a new filter and attach it to a hash table * and set next hash table link with hash mask * */ static int u32_add_filter_on_ht_with_hashmask(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask, uint32_t htid, uint32_t htlink, uint32_t hmask, uint32_t hoffset, struct rtnl_act *act, struct rtnl_act *act2) { struct rtnl_cls *cls; int err; cls=rtnl_cls_alloc(); if (!(cls)) { printf("Can not allocate classifier\n"); nl_socket_free(sock); exit(1); } rtnl_tc_set_link(TC_CAST(cls), rtnlLink); if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) { printf("Can not set classifier as u32\n"); return 1; } rtnl_cls_set_prio(cls, prio); rtnl_cls_set_protocol(cls, ETH_P_IP); rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(0xffff, 0)); if (htid) rtnl_u32_set_hashtable(cls, htid); rtnl_u32_add_key_uint32(cls, keyval, keymask, keyoff, keyoffmask); rtnl_u32_set_hashmask(cls, hmask, hoffset); rtnl_u32_set_link(cls, htlink); rtnl_u32_add_action(cls, act); rtnl_u32_add_action(cls, act2); if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) { printf("Can not add classifier: %s\n", nl_geterror(err)); return -1; } rtnl_cls_put(cls); return 0; }
/* * function that adds a new ingress qdisc and set the default class for unclassified traffic */ static int qdisc_add_ingress(struct nl_sock *sock, struct rtnl_link *rtnlLink) { struct rtnl_qdisc *qdisc; int err; /* Allocation of a qdisc object */ if (!(qdisc = rtnl_qdisc_alloc())) { printf("Can not allocate Qdisc\n"); return -1; } //rtnl_tc_set_ifindex(TC_CAST(qdisc), master_index); rtnl_tc_set_link(TC_CAST(qdisc), rtnlLink); rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT); //printf("Delete current qdisc\n"); rtnl_qdisc_delete(sock, qdisc); //rtnl_qdisc_put(qdisc); rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(0xffff, 0)); if ((err = rtnl_tc_set_kind(TC_CAST(qdisc), "ingress"))) { printf("Can not allocate ingress\n"); return -1; } /* Submit request to kernel and wait for response */ if ((err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE))) { printf("Can not allocate ingress Qdisc\n"); return -1; } /* Return the qdisc object to free memory resources */ rtnl_qdisc_put(qdisc); return 0; }
/* * function that creates a new hash table */ static int u32_add_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, uint32_t htid, uint32_t divisor) { int err; struct rtnl_cls *cls; cls=rtnl_cls_alloc(); if (!(cls)) { printf("Can not allocate classifier\n"); nl_socket_free(sock); exit(1); } rtnl_tc_set_link(TC_CAST(cls), rtnlLink); if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) { printf("Can not set classifier as u32\n"); return 1; } rtnl_cls_set_prio(cls, prio); rtnl_cls_set_protocol(cls, ETH_P_IP); rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(0xffff, 0)); rtnl_u32_set_handle(cls, htid, 0x0, 0x0); //printf("htid: 0x%X\n", htid); rtnl_u32_set_divisor(cls, divisor); if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) { printf("Can not add classifier: %s\n", nl_geterror(err)); return -1; } rtnl_cls_put(cls); return 0; }
int rtnl_tc_msg_parse(struct nlmsghdr *n, struct rtnl_tc *tc) { struct nl_cache *link_cache; struct rtnl_tc_ops *ops; struct nlattr *tb[TCA_MAX + 1]; char kind[TCKINDSIZ]; struct tcmsg *tm; int err; tc->ce_msgtype = n->nlmsg_type; err = nlmsg_parse(n, sizeof(*tm), tb, TCA_MAX, tc_policy); if (err < 0) return err; if (tb[TCA_KIND] == NULL) return -NLE_MISSING_ATTR; nla_strlcpy(kind, tb[TCA_KIND], sizeof(kind)); rtnl_tc_set_kind(tc, kind); tm = nlmsg_data(n); tc->tc_family = tm->tcm_family; tc->tc_ifindex = tm->tcm_ifindex; tc->tc_handle = tm->tcm_handle; tc->tc_parent = tm->tcm_parent; tc->tc_info = tm->tcm_info; tc->ce_mask |= (TCA_ATTR_FAMILY | TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE| TCA_ATTR_PARENT | TCA_ATTR_INFO); if (tb[TCA_OPTIONS]) { tc->tc_opts = nl_data_alloc_attr(tb[TCA_OPTIONS]); if (!tc->tc_opts) return -NLE_NOMEM; tc->ce_mask |= TCA_ATTR_OPTS; } if (tb[TCA_STATS2]) { struct nlattr *tbs[TCA_STATS_MAX + 1]; err = nla_parse_nested(tbs, TCA_STATS_MAX, tb[TCA_STATS2], tc_stats2_policy); if (err < 0) return err; if (tbs[TCA_STATS_BASIC]) { struct gnet_stats_basic *bs; bs = nla_data(tbs[TCA_STATS_BASIC]); tc->tc_stats[RTNL_TC_BYTES] = bs->bytes; tc->tc_stats[RTNL_TC_PACKETS] = bs->packets; } if (tbs[TCA_STATS_RATE_EST]) { struct gnet_stats_rate_est *re; re = nla_data(tbs[TCA_STATS_RATE_EST]); tc->tc_stats[RTNL_TC_RATE_BPS] = re->bps; tc->tc_stats[RTNL_TC_RATE_PPS] = re->pps; } if (tbs[TCA_STATS_QUEUE]) { struct gnet_stats_queue *q; q = nla_data(tbs[TCA_STATS_QUEUE]); tc->tc_stats[RTNL_TC_QLEN] = q->qlen; tc->tc_stats[RTNL_TC_BACKLOG] = q->backlog; tc->tc_stats[RTNL_TC_DROPS] = q->drops; tc->tc_stats[RTNL_TC_REQUEUES] = q->requeues; tc->tc_stats[RTNL_TC_OVERLIMITS] = q->overlimits; } tc->ce_mask |= TCA_ATTR_STATS; if (tbs[TCA_STATS_APP]) { tc->tc_xstats = nl_data_alloc_attr(tbs[TCA_STATS_APP]); if (tc->tc_xstats == NULL) return -NLE_NOMEM; tc->ce_mask |= TCA_ATTR_XSTATS; } else goto compat_xstats; } else { if (tb[TCA_STATS]) { struct tc_stats *st = nla_data(tb[TCA_STATS]); tc->tc_stats[RTNL_TC_BYTES] = st->bytes; tc->tc_stats[RTNL_TC_PACKETS] = st->packets; tc->tc_stats[RTNL_TC_RATE_BPS] = st->bps; tc->tc_stats[RTNL_TC_RATE_PPS] = st->pps; tc->tc_stats[RTNL_TC_QLEN] = st->qlen; tc->tc_stats[RTNL_TC_BACKLOG] = st->backlog; tc->tc_stats[RTNL_TC_DROPS] = st->drops; tc->tc_stats[RTNL_TC_OVERLIMITS]= st->overlimits; tc->ce_mask |= TCA_ATTR_STATS; } compat_xstats: if (tb[TCA_XSTATS]) { tc->tc_xstats = nl_data_alloc_attr(tb[TCA_XSTATS]); if (tc->tc_xstats == NULL) return -NLE_NOMEM; tc->ce_mask |= TCA_ATTR_XSTATS; } } ops = rtnl_tc_get_ops(tc); if (ops && ops->to_msg_parser) { void *data = rtnl_tc_data(tc); if (!data) return -NLE_NOMEM; err = ops->to_msg_parser(tc, data); if (err < 0) return err; } if ((link_cache = __nl_cache_mngt_require("route/link"))) { struct rtnl_link *link; if ((link = rtnl_link_get(link_cache, tc->tc_ifindex))) { rtnl_tc_set_link(tc, link); /* rtnl_tc_set_link incs refcnt */ rtnl_link_put(link); } } return 0; }
int netem_get_params(char *iface, struct netem_params *params) { struct rtnl_link *link; struct rtnl_qdisc *filter_qdisc; struct rtnl_qdisc *found_qdisc = NULL; int err; int delay, jitter, loss; pthread_mutex_lock(&nl_sock_mutex); if ((err = nl_cache_refill(sock, link_cache)) < 0) { fprintf(stderr, "Unable to resync link cache: %s\n", nl_geterror(err)); goto cleanup; } if ((err = nl_cache_refill(sock, qdisc_cache)) < 0) { fprintf(stderr, "Unable to resync link cache: %s\n", nl_geterror(err)); goto cleanup; } /* filter link by name */ if ((link = rtnl_link_get_by_name(link_cache, iface)) == NULL) { fprintf(stderr, "unknown interface/link name.\n"); goto cleanup; } if (!(filter_qdisc = rtnl_qdisc_alloc())) { /* OOM error */ fprintf(stderr, "couldn't alloc qdisc\n"); goto cleanup_link; } rtnl_tc_set_link(TC_CAST(filter_qdisc), link); rtnl_tc_set_parent(TC_CAST(filter_qdisc), TC_H_ROOT); rtnl_tc_set_kind(TC_CAST(filter_qdisc), "netem"); found_qdisc = (struct rtnl_qdisc *)nl_cache_find( qdisc_cache, OBJ_CAST(filter_qdisc)); if (!found_qdisc) { /* The iface probably doesn't have a netem qdisc at startup. */ goto cleanup_filter_qdisc; } if (0 > (delay = rtnl_netem_get_delay(found_qdisc))) { fprintf(stderr, "couldn't get delay for iface: %s\n", iface); goto cleanup_qdisc; } params->delay = (double)delay / 1000; if (0 > (jitter = rtnl_netem_get_jitter(found_qdisc))) { fprintf(stderr, "couldn't get jitter for iface: %s\n", iface); goto cleanup_qdisc; } params->jitter = (double)jitter / 1000; if (0 > (loss = rtnl_netem_get_loss(found_qdisc))) { fprintf(stderr, "couldn't get loss for iface: %s\n", iface); goto cleanup_qdisc; } /* loss is specified in 10ths of a percent, ie. 1 ==> 0.1% */ params->loss = (int)(loss / (UINT_MAX / 1000)); rtnl_qdisc_put(found_qdisc); rtnl_qdisc_put(filter_qdisc); rtnl_link_put(link); pthread_mutex_unlock(&nl_sock_mutex); return 0; cleanup_qdisc: rtnl_qdisc_put(found_qdisc); cleanup_filter_qdisc: rtnl_qdisc_put(filter_qdisc); cleanup_link: rtnl_link_put(link); cleanup: pthread_mutex_unlock(&nl_sock_mutex); return -1; }