1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2012 Joyent, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <sys/types.h>
  28 #include <sys/stat.h>
  29 #include <values.h>
  30 #include <fcntl.h>
  31 #include <errno.h>
  32 #include <string.h>
  33 #include <strings.h>
  34 #include <stdio.h>
  35 #include <stdlib.h>
  36 #include <unistd.h>
  37 #include <stropts.h>
  38 #include <zone.h>
  39 #include <libgen.h>
  40 #include <assert.h>
  41 
  42 #include <libipd.h>
  43 
  44 static char *g_pname;
  45 static char g_zonename[ZONENAME_MAX];
  46 static zoneid_t g_zid;
  47 
  48 #define E_SUCCESS       0
  49 #define E_ERROR         1
  50 #define E_USAGE         2
  51 
  52 typedef int (*idc_cmd_func_t)(int, char *[]);
  53 typedef struct ipdadm_cmd {
  54         const char      *idc_name;      /* subcommand name */
  55         idc_cmd_func_t  idc_func;       /* subcommand function */
  56         const char      *idc_usage;     /* subcommand help */
  57 } ipdadm_cmd_t;
  58 
  59 static int ipdadm_list(int, char *[]);
  60 static int ipdadm_info(int, char *[]);
  61 static int ipdadm_corrupt(int, char *[]);
  62 static int ipdadm_delay(int, char *[]);
  63 static int ipdadm_drop(int, char *[]);
  64 static int ipdadm_remove(int, char *[]);
  65 
  66 #define IPDADM_NCMDS    6
  67 static ipdadm_cmd_t ipdadm_cmds[] = {
  68         { "list", ipdadm_list, "list [-v]" },
  69         { "info", ipdadm_info, "info" },
  70         { "corrupt", ipdadm_corrupt, "corrupt <percentage>" },
  71         { "delay", ipdadm_delay, "delay <microseconds>" },
  72         { "drop", ipdadm_drop, "drop <percentage>" },
  73         { "remove", ipdadm_remove, "remove [corrupt|delay|drop]" }
  74 };
  75 
  76 static int
  77 usage(FILE *fp)
  78 {
  79         int ii;
  80         ipdadm_cmd_t *cmd;
  81 
  82         (void) fprintf(fp, "Usage: %s [-z zonename] subcommand "
  83             "[subcommand opts]\n\n", g_pname);
  84         (void) fprintf(fp, "Subcommands:\n");
  85         for (ii = 0; ii < IPDADM_NCMDS; ii++) {
  86                 cmd = &ipdadm_cmds[ii];
  87                 (void) fprintf(fp, "\t%s\n", cmd->idc_usage);
  88         }
  89 
  90         return (E_USAGE);
  91 }
  92 
  93 static void
  94 ipdadm_list_one(zoneid_t z, const ipd_config_t *icp, void *arg)
  95 {
  96         char zonename[ZONENAME_MAX];
  97         int opt_v = (int)(intptr_t)arg;
  98 
  99         if (getzonenamebyid(z, zonename, sizeof (zonename)) < 0)
 100                 (void) printf("%ld", z);
 101         else
 102                 (void) printf("%s", zonename);
 103 
 104         if (!opt_v) {
 105                 (void) printf("\n");
 106                 return;
 107         }
 108 
 109         (void) printf("\t%u\t%u\t%u\n", icp->ic_corrupt, icp->ic_drop,
 110             icp->ic_delay);
 111 }
 112 
 113 static int
 114 ipdadm_list(int argc, char *argv[])
 115 {
 116         int opt_v = 0;
 117         int fd, rval;
 118         ipd_stathdl_t hdl;
 119 
 120         if (argc > 1)
 121                 return (usage(stderr));
 122 
 123         if (argc == 1) {
 124                 if (strcmp(argv[0], "-v") == 0)
 125                         ++opt_v;
 126                 else
 127                         return (usage(stderr));
 128         }
 129 
 130         fd = ipd_open(NULL);
 131         rval = ipd_status_read(fd, &hdl);
 132         (void) ipd_close(fd);
 133 
 134         if (rval != 0) {
 135                 (void) fprintf(stderr, "%s: failed to get list info: %s\n",
 136                     g_pname, ipd_errmsg);
 137                 return (E_ERROR);
 138         }
 139 
 140         ipd_status_foreach_zone(hdl, ipdadm_list_one, (void *)(intptr_t)opt_v);
 141         ipd_status_free(hdl);
 142 
 143         return (E_SUCCESS);
 144 }
 145 
 146 /*ARGSUSED*/
 147 static int
 148 ipdadm_info(int argc, char *argv[])
 149 {
 150         int rval, fd;
 151         ipd_stathdl_t hdl;
 152         ipd_config_t *icp;
 153 
 154         if (argc != 0)
 155                 return (usage(stderr));
 156 
 157         fd = ipd_open(NULL);
 158         rval = ipd_status_read(fd, &hdl);
 159         (void) ipd_close(fd);
 160         if (rval != 0) {
 161                 (void) fprintf(stderr, "%s: failed to get info: %s\n",
 162                     g_pname, ipd_errmsg);
 163                 return (E_ERROR);
 164         }
 165 
 166         if (ipd_status_get_config(hdl, g_zid, &icp) != 0) {
 167                 if (ipd_errno == EIPD_ZC_NOENT) {
 168                         (void) printf("zone %s does not exist or has no "
 169                             "ipd actions enabled", g_zonename);
 170                         return (E_SUCCESS);
 171                 }
 172                 (void) fprintf(stderr, "%s: failed to get info: %s\n",
 173                     g_pname, ipd_errmsg);
 174                 return (E_ERROR);
 175         }
 176 
 177         (void) printf("ipd information for zone %s:\n",
 178             g_zonename);
 179         (void) printf("\tcorrupt:\t%u%% chance of packet corruption\n",
 180             icp->ic_corrupt);
 181         (void) printf("\tdrop:\t\t%u%% chance of packet drop\n",
 182             icp->ic_drop);
 183         (void) printf("\tdelay:\t\t%u microsecond delay per packet\n",
 184             icp->ic_delay);
 185 
 186         ipd_status_free(hdl);
 187 
 188         return (E_SUCCESS);
 189 }
 190 
 191 static long
 192 ipdadm_parse_long(const char *str, const char *name, long min, long max)
 193 {
 194         long val;
 195         char *end;
 196 
 197         errno = 0;
 198         val = strtol(str, &end, 10);
 199         if (errno != 0) {
 200                 (void) fprintf(stderr, "%s: invalid value for %s: %s\n",
 201                     g_pname, name, str);
 202                 exit(E_ERROR);
 203         }
 204 
 205         /*
 206          * We want to make sure that we got the whole string. If not that's an
 207          * error. e.g. 23.42 should not be valid.
 208          */
 209         if (*end != '\0') {
 210                 (void) fprintf(stderr, "%s: %s value must be an integer\n",
 211                     g_pname, name);
 212                 exit(E_ERROR);
 213         }
 214 
 215         if (val < min || val > max) {
 216                 (void) fprintf(stderr, "%s: %s value must be between %ld and "
 217                     "%ld inclusive\n", g_pname, name, min, max);
 218                 exit(E_ERROR);
 219         }
 220 
 221         return (val);
 222 }
 223 
 224 static int
 225 ipdadm_corrupt(int argc, char *argv[])
 226 {
 227         int rval, fd;
 228         long val;
 229         ipd_config_t ic;
 230 
 231         if (argc != 1) {
 232                 (void) fprintf(stderr, "%s: corrupt <percentage>\n",
 233                     g_pname);
 234                 return (usage(stderr));
 235         }
 236 
 237         val = ipdadm_parse_long(argv[0], "corrupt", 0, 100);
 238         bzero(&ic, sizeof (ic));
 239         ic.ic_mask = IPDM_CORRUPT;
 240         ic.ic_corrupt = val;
 241 
 242         fd = ipd_open(NULL);
 243         rval = ipd_ctl(fd, g_zid, &ic);
 244         (void) ipd_close(fd);
 245 
 246         if (rval != 0) {
 247                 (void) fprintf(stderr, "%s: failed to change corrupt "
 248                     "value: %s\n", g_pname, ipd_errmsg);
 249                 return (E_ERROR);
 250         }
 251 
 252         return (E_SUCCESS);
 253 }
 254 
 255 static int
 256 ipdadm_delay(int argc, char *argv[])
 257 {
 258         long val;
 259         int fd, rval;
 260         ipd_config_t ic;
 261 
 262         if (argc != 1) {
 263                 (void) fprintf(stderr, "%s: delay <microseconds>\n",
 264                     g_pname);
 265                 return (usage(stderr));
 266         }
 267 
 268         val = ipdadm_parse_long(argv[0], "delay", 0, MAXLONG);
 269         bzero(&ic, sizeof (ic));
 270         ic.ic_mask = IPDM_DELAY;
 271         ic.ic_delay = val;
 272 
 273         fd = ipd_open(NULL);
 274         rval = ipd_ctl(fd, g_zid, &ic);
 275         (void) ipd_close(fd);
 276 
 277         if (rval != 0) {
 278                 (void) fprintf(stderr, "%s: failed to change delay value: %s\n",
 279                     g_pname, ipd_errmsg);
 280                 return (E_ERROR);
 281         }
 282 
 283         return (E_SUCCESS);
 284 }
 285 
 286 static int
 287 ipdadm_drop(int argc, char *argv[])
 288 {
 289         long val;
 290         int fd, rval;
 291         ipd_config_t ic;
 292 
 293         if (argc != 1) {
 294                 (void) fprintf(stderr, "%s: drop <percentage>\n",
 295                     g_pname);
 296                 return (usage(stderr));
 297         }
 298 
 299         val = ipdadm_parse_long(argv[0], "drop", 0, 100);
 300         bzero(&ic, sizeof (ic));
 301         ic.ic_mask = IPDM_DROP;
 302         ic.ic_drop = val;
 303 
 304         fd = ipd_open(NULL);
 305         rval = ipd_ctl(fd, g_zid, &ic);
 306         (void) ipd_close(fd);
 307 
 308         if (rval != 0) {
 309                 (void) fprintf(stderr, "%s: failed to change drop value: %s\n",
 310                     g_pname, ipd_errmsg);
 311                 return (E_ERROR);
 312         }
 313 
 314         return (E_SUCCESS);
 315 }
 316 
 317 static int
 318 ipdadm_remove_valid(const char *str)
 319 {
 320         if (strcmp(str, "corrupt") == 0) {
 321                 return (IPDM_CORRUPT);
 322         } else if (strcmp(str, "drop") == 0) {
 323                 return (IPDM_DROP);
 324         } else if (strcmp(str, "delay") == 0) {
 325                 return (IPDM_DELAY);
 326         }
 327 
 328         return (0);
 329 }
 330 
 331 static int
 332 ipdadm_remove(int argc, char *argv[])
 333 {
 334         ipd_config_t ic;
 335         char *cur, *res;
 336         int rval, fd;
 337 
 338         if (argc < 1) {
 339                 (void) fprintf(stderr, "%s: remove <arguments>\n",
 340                     g_pname);
 341                 return (usage(stderr));
 342         }
 343 
 344         if (argc > 1) {
 345                 (void) fprintf(stderr, "%s: remove's arguments must be "
 346                     "comma seperated\n", g_pname);
 347                 return (E_ERROR);
 348         }
 349 
 350         bzero(&ic, sizeof (ic));
 351 
 352         cur = argv[0];
 353         while ((res = strchr(cur, ',')) != NULL) {
 354                 *res = '\0';
 355                 if ((rval = ipdadm_remove_valid(cur)) == 0) {
 356                         (void) fprintf(stderr, "%s: unknown remove "
 357                             "argument: %s\n", g_pname, cur);
 358                         return (E_ERROR);
 359                 }
 360                 ic.ic_mask |= rval;
 361                 cur = res + 1;
 362         }
 363 
 364         if ((rval = ipdadm_remove_valid(cur)) == 0) {
 365                 (void) fprintf(stderr, "%s: unknown remove argument: %s\n",
 366                     g_pname, cur);
 367                 return (E_ERROR);
 368         }
 369         ic.ic_mask |= rval;
 370 
 371         fd = ipd_open(NULL);
 372         rval = ipd_ctl(fd, g_zid, &ic);
 373         (void) ipd_close(fd);
 374         if (rval == -1) {
 375                 (void) fprintf(stderr, "%s: failed to remove instances: %s\n",
 376                     g_pname, ipd_errmsg);
 377                 return (E_ERROR);
 378         }
 379 
 380         return (E_SUCCESS);
 381 }
 382 
 383 
 384 int
 385 main(int argc, char *argv[])
 386 {
 387         int ii;
 388         ipdadm_cmd_t *cmd;
 389 
 390         g_pname = basename(argv[0]);
 391 
 392         if (argc < 2)
 393                 return (usage(stderr));
 394         argc--;
 395         argv++;
 396 
 397         g_zid = getzoneid();
 398         if (strcmp("-z", argv[0]) == 0) {
 399                 argc--;
 400                 argv++;
 401                 if (argc < 1) {
 402                         (void) fprintf(stderr, "%s: -z requires an argument\n",
 403                             g_pname);
 404                         return (usage(stderr));
 405                 }
 406 
 407                 if (g_zid != GLOBAL_ZONEID) {
 408                         (void) fprintf(stderr, "%s: -z option only permitted "
 409                             "in global zone\n", g_pname);
 410                         return (usage(stderr));
 411                 }
 412 
 413                 g_zid = getzoneidbyname(argv[0]);
 414                 if (g_zid == -1) {
 415                         (void) fprintf(stderr, "%s: %s: invalid zone\n",
 416                             g_pname, argv[0]);
 417                         return (E_ERROR);
 418                 }
 419                 argc--;
 420                 argv++;
 421         }
 422 
 423         if (getzonenamebyid(g_zid, g_zonename, sizeof (g_zonename)) < 0) {
 424                 (void) fprintf(stderr, "%s: failed to get zonename: %s\n",
 425                     g_pname, strerror(errno));
 426                 return (E_ERROR);
 427         }
 428 
 429         if (argc < 1)
 430                 return (usage(stderr));
 431 
 432         for (ii = 0; ii < IPDADM_NCMDS; ii++) {
 433                 cmd = &ipdadm_cmds[ii];
 434                 if (strcmp(argv[0], cmd->idc_name) == 0) {
 435                         argv++;
 436                         argc--;
 437                         assert(cmd->idc_func != NULL);
 438                         return (cmd->idc_func(argc, argv));
 439                 }
 440         }
 441 
 442         (void) fprintf(stderr, "%s: %s: unknown command\n", g_pname, argv[0]);
 443         return (usage(stderr));
 444 }