+static int
+get_int64(uint64_t *i, char const *s)
+{
+ char *cp;
+ *i = strtol(s, &cp, 10);
+ if (cp == s || errno != 0) {
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+get_int32(uint32_t *i, char const *s)
+{
+ char *cp;
+ *i = strtol(s, &cp, 10);
+ if (cp == s || errno != 0) {
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+get_percent(double *d, const char *s)
+{
+ char *cp;
+ *d = strtod(s, &cp) / (double)100;
+ if (*d == HUGE_VALF || *d == HUGE_VALL) {
+ return (-1);
+ }
+ if (*d == 0.0 || (*cp != '\0' && strcmp(cp, "%") != 0)) {
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+get_percent_fixed_point(uint32_t *i, const char *s)
+{
+ double p;
+
+ if (get_percent(&p, s) != 0){
+ return (-1);
+ }
+
+ *i = p * IF_NETEM_PARAMS_PSCALE;
+ return (0);
+}
+
+static int
+netem_parse_args(struct if_netem_params *p, int argc, char *const *argv)
+{
+ int argc_saved = argc;
+ uint64_t bandwitdh = 0;
+ uint32_t latency = 0, jitter = 0;
+ uint32_t corruption = 0;
+ uint32_t duplication = 0;
+ uint32_t loss_p_gr_gl = 0, loss_p_gr_bl = 0, loss_p_bl_br = 0,
+ loss_p_bl_gr = 0, loss_p_br_bl = 0;
+ uint32_t reordering = 0;
+
+ bzero(p, sizeof (*p));
+
+ /* take out "input"/"output" */
+ argc--, argv++;
+
+ for ( ; argc > 0; ) {
+ if (strcmp(*argv, "bandwidth") == 0) {
+ argc--, argv++;
+ if (argc <= 0 || get_int64(&bandwitdh, *argv) != 0) {
+ err(1, "Invalid value '%s'", *argv);
+ }
+ argc--, argv++;
+ } else if (strcmp(*argv, "corruption") == 0) {
+ argc--, argv++;
+ if (argc <= 0 ||
+ get_percent_fixed_point(&corruption, *argv) != 0) {
+ err(1, "Invalid value '%s'", *argv);
+ }
+ argc--, argv++;
+ } else if (strcmp(*argv, "delay") == 0) {
+ argc--, argv++;
+ if (argc <= 0 || get_int32(&latency, *argv) != 0) {
+ err(1, "Invalid value '%s'", *argv);
+ }
+ argc--, argv++;
+ if (argc > 0 && get_int32(&jitter, *argv) == 0) {
+ argc--, argv++;
+ }
+ } else if (strcmp(*argv, "duplication") == 0) {
+ argc--, argv++;
+ if (argc <= 0 ||
+ get_percent_fixed_point(&duplication, *argv) != 0) {
+ err(1, "Invalid value '%s'", *argv);
+ return (-1);
+ }
+ argc--, argv++;
+ } else if (strcmp(*argv, "loss") == 0) {
+ argc--, argv++;
+ if (argc <= 0 || get_percent_fixed_point(&loss_p_gr_gl, *argv) != 0) {
+ err(1, "Invalid value '%s'", *argv);
+ }
+ /* we may have all 5 probs, use naive model if not */
+ argc--, argv++;
+ if (argc <= 0 || get_percent_fixed_point(&loss_p_gr_bl, *argv) != 0) {
+ continue;
+ }
+ /* if more than p_gr_gl, then should have all probs */
+ argc--, argv++;
+ if (argc <= 0 || get_percent_fixed_point(&loss_p_bl_br, *argv) != 0) {
+ err(1, "Invalid value '%s' for p_bl_br", *argv);
+ }
+ argc--, argv++;
+ if (argc <= 0 || get_percent_fixed_point(&loss_p_bl_gr, *argv) != 0) {
+ err(1, "Invalid value '%s' for p_bl_gr", *argv);
+ }
+ argc--, argv++;
+ if (argc <= 0 || get_percent_fixed_point(&loss_p_br_bl, *argv) != 0) {
+ err(1, "Invalid value '%s' for p_br_bl", *argv);
+ }
+ argc--, argv++;
+ } else if (strcmp(*argv, "reordering") == 0) {
+ argc--, argv++;
+ if (argc <= 0 || get_percent_fixed_point(&reordering, *argv) != 0) {
+ err(1, "Invalid value '%s'", *argv);
+ }
+ argc--, argv++;
+ } else {
+ return (-1);
+ }
+ }
+
+ if (corruption > IF_NETEM_PARAMS_PSCALE) {
+ err(1, "corruption percentage > 100%%");
+ }
+
+ if (duplication > IF_NETEM_PARAMS_PSCALE) {
+ err(1, "duplication percentage > 100%%");
+ }
+
+ if (duplication > 0 && latency == 0) {
+ /* we need to insert dup'ed packet with latency */
+ err(1, "duplication needs latency param");
+ }
+
+ if (latency > 1000) {
+ err(1, "latency %dms too big (> 1 sec)", latency);
+ }
+
+ if (jitter * 3 > latency) {
+ err(1, "jitter %dms too big (latency %dms)", jitter, latency);
+ }
+
+ /* if gr_gl == 0 (no loss), other prob should all be zero */
+ if (loss_p_gr_gl == 0 &&
+ (loss_p_gr_bl != 0 || loss_p_bl_br != 0 || loss_p_bl_gr != 0 ||
+ loss_p_br_bl != 0)) {
+ err(1, "loss params not all zero when gr_gl is zero");
+ }
+
+ /* check state machine transition prob integrity */
+ if (loss_p_gr_gl > IF_NETEM_PARAMS_PSCALE ||
+ /* gr_gl = IF_NETEM_PARAMS_PSCALE for total loss */
+ loss_p_gr_bl > IF_NETEM_PARAMS_PSCALE ||
+ loss_p_bl_br > IF_NETEM_PARAMS_PSCALE ||
+ loss_p_bl_gr > IF_NETEM_PARAMS_PSCALE ||
+ loss_p_br_bl > IF_NETEM_PARAMS_PSCALE ||
+ loss_p_gr_gl + loss_p_gr_bl > IF_NETEM_PARAMS_PSCALE ||
+ loss_p_bl_br + loss_p_bl_gr > IF_NETEM_PARAMS_PSCALE) {
+ err(1, "loss params too big");
+ }
+
+ if (reordering > IF_NETEM_PARAMS_PSCALE) {
+ err(1, "reordering percentage > 100%%");
+ }
+
+ p->ifnetem_bandwidth_bps = bandwitdh;
+ p->ifnetem_latency_ms = latency;
+ p->ifnetem_jitter_ms = jitter;
+ p->ifnetem_corruption_p = corruption;
+ p->ifnetem_duplication_p = duplication;
+ p->ifnetem_loss_p_gr_gl = loss_p_gr_gl;
+ p->ifnetem_loss_p_gr_bl = loss_p_gr_bl;
+ p->ifnetem_loss_p_bl_br = loss_p_bl_br;
+ p->ifnetem_loss_p_bl_gr = loss_p_bl_gr;
+ p->ifnetem_loss_p_br_bl = loss_p_br_bl;
+ p->ifnetem_reordering_p = reordering;
+
+ return (argc_saved - argc);
+}
+
+void
+print_netem_params(struct if_netem_params *p, const char *desc)
+{
+ struct if_netem_params zero_params;
+ double pscale = IF_NETEM_PARAMS_PSCALE / 100;
+ bzero(&zero_params, sizeof (zero_params));
+
+ if (memcmp(p, &zero_params, sizeof (zero_params)) == 0) {
+ printf("%s NetEm Disabled\n\n", desc);
+ } else {
+ printf(
+ "%s NetEm Parameters\n"
+ "\tbandwidth rate %llubps\n"
+ "\tdelay latency %dms\n"
+ "\t jitter %dms\n",
+ desc, p->ifnetem_bandwidth_bps,
+ p->ifnetem_latency_ms, p->ifnetem_jitter_ms);
+ if (p->ifnetem_loss_p_gr_bl == 0 &&
+ p->ifnetem_loss_p_bl_br == 0 &&
+ p->ifnetem_loss_p_bl_gr == 0 &&
+ p->ifnetem_loss_p_br_bl == 0) {
+ printf(
+ "\tloss %.3f%%\n",
+ (double) p->ifnetem_loss_p_gr_gl / pscale);
+ } else {
+ printf(
+ "\tloss GAP_RECV -> GAP_LOSS %.3f%%\n"
+ "\t GAP_RECV -> BURST_LOSS %.3f%%\n"
+ "\t BURST_LOSS -> BURST_RECV %.3f%%\n"
+ "\t BURST_LOSS -> GAP_RECV %.3f%%\n"
+ "\t BURST_RECV -> BURST_LOSS %.3f%%\n",
+ (double) p->ifnetem_loss_p_gr_gl / pscale,
+ (double) p->ifnetem_loss_p_gr_bl / pscale,
+ (double) p->ifnetem_loss_p_bl_br / pscale,
+ (double) p->ifnetem_loss_p_bl_gr / pscale,
+ (double) p->ifnetem_loss_p_br_bl / pscale);
+ }
+ printf(
+ "\tcorruption %.3f%%\n"
+ "\treordering %.3f%%\n\n",
+ (double) p->ifnetem_corruption_p / pscale,
+ (double) p->ifnetem_reordering_p / pscale);
+ }
+}
+
+static int
+setnetem(int argc, char *const *argv, int s, const struct afswtch *afp)
+{
+ struct if_linkparamsreq iflpr;
+ struct if_netem_params input_params, output_params;
+ int ret = 0, error = 0;
+
+ bzero(&iflpr, sizeof (iflpr));
+ bzero(&input_params, sizeof (input_params));
+ bzero(&output_params, sizeof (output_params));
+
+ if (argc > 1) {
+ if (strcmp(argv[0], "input") == 0) {
+ ret = netem_parse_args(&input_params, argc, argv);
+ } else if (strcmp(argv[0], "output") == 0) {
+ ret = netem_parse_args(&output_params, argc, argv);
+ } else if (strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0) {
+ goto bad_args;
+ } else {
+ fprintf(stderr, "uknown option %s\n", argv[0]);
+ goto bad_args;
+ }
+ if (ret < 0) {
+ goto bad_args;
+ }
+ }
+
+ errno = 0;
+ strlcpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name));
+ error = ioctl(s, SIOCGIFLINKPARAMS, &iflpr);
+ if (error < 0) {
+ warn("ioctl (get link params)");
+ }
+
+ if (argc == 0) {
+ print_netem_params(&iflpr.iflpr_input_netem, "Input");
+ print_netem_params(&iflpr.iflpr_output_netem, "Output");
+ return (0);
+ } else if (argc == 1) {
+ if (strcmp(argv[0], "input") == 0) {
+ bzero(&iflpr.iflpr_input_netem,
+ sizeof (iflpr.iflpr_input_netem));
+ } else if (strcmp(argv[0], "output") == 0) {
+ bzero(&iflpr.iflpr_output_netem,
+ sizeof (iflpr.iflpr_output_netem));
+ } else {
+ fprintf(stderr, "uknown option %s\n", argv[0]);
+ goto bad_args;
+ }
+ printf("%s: netem is now disabled for %s\n", name, argv[0]);
+ ret = 1;
+ } else {
+ if (strcmp(argv[0], "input") == 0) {
+ iflpr.iflpr_input_netem = input_params;
+ } else if (strcmp(argv[0], "output") == 0) {
+ iflpr.iflpr_output_netem = output_params;
+ }
+ }
+
+ error = ioctl(s, SIOCSIFLINKPARAMS, &iflpr);
+ if (error < 0 && errno != ENOENT && errno != ENXIO && errno != ENODEV) {
+ warn("ioctl (set link params)");
+ } else if (errno == ENXIO) {
+ printf("netem cannot be set on %s\n", name);
+ } else {
+ printf("%s: netem configured\n", name);
+ }
+
+ return (ret);
+bad_args:
+ fprintf(stderr, "Usage:\n"
+ "\tTo enable/set netem params\n"
+ "\t\tnetem <input|output>\n"
+ "\t\t [ bandwidth BIT_PER_SEC ]\n"
+ "\t\t [ delay DELAY_MSEC [ JITTER_MSEC ] ]\n"
+ "\t\t [ loss PERCENTAGE ]\n"
+ "\t\t [ duplication PERCENTAGE ]\n"
+ "\t\t [ reordering PERCENTAGE ]\n\n"
+ "\tTo disable <input|output> netem\n"
+ "\t\tnetem <input|output>\n\n"
+ "\tTo show current settings\n"
+ "\t\tnetem\n\n");
+ return (-1);
+}
+