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