/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*/
/*
* Copyright (c) 2012 Joyent, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <values.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stropts.h>
#include <zone.h>
#include <libgen.h>
#include <assert.h>
#include <libipd.h>
static char *g_pname;
static char g_zonename[ZONENAME_MAX];
static zoneid_t g_zid;
#define E_SUCCESS 0
#define E_ERROR 1
#define E_USAGE 2
typedef int (*idc_cmd_func_t)(int, char *[]);
typedef struct ipdadm_cmd {
const char *idc_name; /* subcommand name */
idc_cmd_func_t idc_func; /* subcommand function */
const char *idc_usage; /* subcommand help */
} ipdadm_cmd_t;
static int ipdadm_list(int, char *[]);
static int ipdadm_info(int, char *[]);
static int ipdadm_corrupt(int, char *[]);
static int ipdadm_delay(int, char *[]);
static int ipdadm_drop(int, char *[]);
static int ipdadm_remove(int, char *[]);
#define IPDADM_NCMDS 6
static ipdadm_cmd_t ipdadm_cmds[] = {
{ "list", ipdadm_list, "list [-v]" },
{ "info", ipdadm_info, "info" },
{ "corrupt", ipdadm_corrupt, "corrupt <percentage>" },
{ "delay", ipdadm_delay, "delay <microseconds>" },
{ "drop", ipdadm_drop, "drop <percentage>" },
{ "remove", ipdadm_remove, "remove [corrupt|delay|drop]" }
};
static int
usage(FILE *fp)
{
int ii;
ipdadm_cmd_t *cmd;
(void) fprintf(fp, "Usage: %s [-z zonename] subcommand "
"[subcommand opts]\n\n", g_pname);
(void) fprintf(fp, "Subcommands:\n");
for (ii = 0; ii < IPDADM_NCMDS; ii++) {
cmd = &ipdadm_cmds[ii];
(void) fprintf(fp, "\t%s\n", cmd->idc_usage);
}
return (E_USAGE);
}
static void
ipdadm_list_one(zoneid_t z, const ipd_config_t *icp, void *arg)
{
char zonename[ZONENAME_MAX];
int opt_v = (int)(intptr_t)arg;
if (getzonenamebyid(z, zonename, sizeof (zonename)) < 0)
(void) printf("%ld", z);
else
(void) printf("%s", zonename);
if (!opt_v) {
(void) printf("\n");
return;
}
(void) printf("\t%u\t%u\t%u\n", icp->ic_corrupt, icp->ic_drop,
icp->ic_delay);
}
static int
ipdadm_list(int argc, char *argv[])
{
int opt_v = 0;
int fd, rval;
ipd_stathdl_t hdl;
if (argc > 1)
return (usage(stderr));
if (argc == 1) {
if (strcmp(argv[0], "-v") == 0)
++opt_v;
else
return (usage(stderr));
}
fd = ipd_open(NULL);
if (fd < 0) {
(void) fprintf(stderr, "%s: failed to open ipd ctl node: %s\n",
g_pname, ipd_errmsg);
return (E_ERROR);
}
rval = ipd_status_read(fd, &hdl);
(void) ipd_close(fd);
if (rval != 0) {
(void) fprintf(stderr, "%s: failed to get list info: %s\n",
g_pname, ipd_errmsg);
return (E_ERROR);
}
ipd_status_foreach_zone(hdl, ipdadm_list_one, (void *)(intptr_t)opt_v);
ipd_status_free(hdl);
return (E_SUCCESS);
}
/*ARGSUSED*/
static int
ipdadm_info(int argc, char *argv[])
{
int rval, fd;
ipd_stathdl_t hdl;
ipd_config_t *icp;
if (argc != 0)
return (usage(stderr));
fd = ipd_open(NULL);
if (fd < 0) {
(void) fprintf(stderr, "%s: failed to open ipd ctl node: %s\n",
g_pname, ipd_errmsg);
return (E_ERROR);
}
rval = ipd_status_read(fd, &hdl);
(void) ipd_close(fd);
if (rval != 0) {
(void) fprintf(stderr, "%s: failed to get info: %s\n",
g_pname, ipd_errmsg);
return (E_ERROR);
}
if (ipd_status_get_config(hdl, g_zid, &icp) != 0) {
if (ipd_errno == EIPD_ZC_NOENT) {
(void) printf("zone %s does not exist or has no "
"ipd actions enabled\n", g_zonename);
return (E_SUCCESS);
}
(void) fprintf(stderr, "%s: failed to get info: %s\n",
g_pname, ipd_errmsg);
return (E_ERROR);
}
(void) printf("ipd information for zone %s:\n",
g_zonename);
(void) printf("\tcorrupt:\t%u%% chance of packet corruption\n",
icp->ic_corrupt);
(void) printf("\tdrop:\t\t%u%% chance of packet drop\n",
icp->ic_drop);
(void) printf("\tdelay:\t\t%u microsecond delay per packet\n",
icp->ic_delay);
ipd_status_free(hdl);
return (E_SUCCESS);
}
static long
ipdadm_parse_long(const char *str, const char *name, long min, long max)
{
long val;
char *end;
errno = 0;
val = strtol(str, &end, 10);
if (errno != 0) {
(void) fprintf(stderr, "%s: invalid value for %s: %s\n",
g_pname, name, str);
exit(E_ERROR);
}
/*
* We want to make sure that we got the whole string. If not that's an
* error. e.g. 23.42 should not be valid.
*/
if (*end != '\0') {
(void) fprintf(stderr, "%s: %s value must be an integer\n",
g_pname, name);
exit(E_ERROR);
}
if (val < min || val > max) {
(void) fprintf(stderr, "%s: %s value must be between %ld and "
"%ld inclusive\n", g_pname, name, min, max);
exit(E_ERROR);
}
return (val);
}
static int
ipdadm_corrupt(int argc, char *argv[])
{
int rval, fd;
long val;
ipd_config_t ic;
if (argc != 1) {
(void) fprintf(stderr, "%s: corrupt <percentage>\n",
g_pname);
return (usage(stderr));
}
val = ipdadm_parse_long(argv[0], "corrupt", 0, 100);
bzero(&ic, sizeof (ic));
ic.ic_mask = IPDM_CORRUPT;
ic.ic_corrupt = val;
fd = ipd_open(NULL);
if (fd < 0) {
(void) fprintf(stderr, "%s: failed to open ipd ctl node: %s\n",
g_pname, ipd_errmsg);
return (E_ERROR);
}
rval = ipd_ctl(fd, g_zid, &ic);
(void) ipd_close(fd);
if (rval != 0) {
(void) fprintf(stderr, "%s: failed to change corrupt "
"value: %s\n", g_pname, ipd_errmsg);
return (E_ERROR);
}
return (E_SUCCESS);
}
static int
ipdadm_delay(int argc, char *argv[])
{
long val;
int fd, rval;
ipd_config_t ic;
if (argc != 1) {
(void) fprintf(stderr, "%s: delay <microseconds>\n",
g_pname);
return (usage(stderr));
}
val = ipdadm_parse_long(argv[0], "delay", 0, MAXLONG);
bzero(&ic, sizeof (ic));
ic.ic_mask = IPDM_DELAY;
ic.ic_delay = val;
fd = ipd_open(NULL);
if (fd < 0) {
(void) fprintf(stderr, "%s: failed to open ipd ctl node: %s\n",
g_pname, ipd_errmsg);
return (E_ERROR);
}
rval = ipd_ctl(fd, g_zid, &ic);
(void) ipd_close(fd);
if (rval != 0) {
(void) fprintf(stderr, "%s: failed to change delay value: %s\n",
g_pname, ipd_errmsg);
return (E_ERROR);
}
return (E_SUCCESS);
}
static int
ipdadm_drop(int argc, char *argv[])
{
long val;
int fd, rval;
ipd_config_t ic;
if (argc != 1) {
(void) fprintf(stderr, "%s: drop <percentage>\n",
g_pname);
return (usage(stderr));
}
val = ipdadm_parse_long(argv[0], "drop", 0, 100);
bzero(&ic, sizeof (ic));
ic.ic_mask = IPDM_DROP;
ic.ic_drop = val;
fd = ipd_open(NULL);
if (fd < 0) {
(void) fprintf(stderr, "%s: failed to open ipd ctl node: %s\n",
g_pname, ipd_errmsg);
return (E_ERROR);
}
rval = ipd_ctl(fd, g_zid, &ic);
(void) ipd_close(fd);
if (rval != 0) {
(void) fprintf(stderr, "%s: failed to change drop value: %s\n",
g_pname, ipd_errmsg);
return (E_ERROR);
}
return (E_SUCCESS);
}
static int
ipdadm_remove_valid(const char *str)
{
if (strcmp(str, "corrupt") == 0) {
return (IPDM_CORRUPT);
} else if (strcmp(str, "drop") == 0) {
return (IPDM_DROP);
} else if (strcmp(str, "delay") == 0) {
return (IPDM_DELAY);
}
return (0);
}
static int
ipdadm_remove(int argc, char *argv[])
{
ipd_config_t ic;
char *cur, *res;
int rval, fd;
if (argc < 1) {
(void) fprintf(stderr, "%s: remove <arguments>\n",
g_pname);
return (usage(stderr));
}
if (argc > 1) {
(void) fprintf(stderr, "%s: remove's arguments must be "
"comma seperated\n", g_pname);
return (E_ERROR);
}
bzero(&ic, sizeof (ic));
cur = argv[0];
while ((res = strchr(cur, ',')) != NULL) {
*res = '\0';
if ((rval = ipdadm_remove_valid(cur)) == 0) {
(void) fprintf(stderr, "%s: unknown remove "
"argument: %s\n", g_pname, cur);
return (E_ERROR);
}
ic.ic_mask |= rval;
cur = res + 1;
}
if ((rval = ipdadm_remove_valid(cur)) == 0) {
(void) fprintf(stderr, "%s: unknown remove argument: %s\n",
g_pname, cur);
return (E_ERROR);
}
ic.ic_mask |= rval;
fd = ipd_open(NULL);
if (fd < 0) {
(void) fprintf(stderr, "%s: failed to open ipd ctl node: %s\n",
g_pname, ipd_errmsg);
return (E_ERROR);
}
rval = ipd_ctl(fd, g_zid, &ic);
(void) ipd_close(fd);
if (rval == -1) {
(void) fprintf(stderr, "%s: failed to remove instances: %s\n",
g_pname, ipd_errmsg);
return (E_ERROR);
}
return (E_SUCCESS);
}
int
main(int argc, char *argv[])
{
int ii;
ipdadm_cmd_t *cmd;
g_pname = basename(argv[0]);
if (argc < 2)
return (usage(stderr));
argc--;
argv++;
g_zid = getzoneid();
if (strcmp("-z", argv[0]) == 0) {
argc--;
argv++;
if (argc < 1) {
(void) fprintf(stderr, "%s: -z requires an argument\n",
g_pname);
return (usage(stderr));
}
if (g_zid != GLOBAL_ZONEID) {
(void) fprintf(stderr, "%s: -z option only permitted "
"in global zone\n", g_pname);
return (usage(stderr));
}
g_zid = getzoneidbyname(argv[0]);
if (g_zid == -1) {
(void) fprintf(stderr, "%s: %s: invalid zone\n",
g_pname, argv[0]);
return (E_ERROR);
}
argc--;
argv++;
}
if (getzonenamebyid(g_zid, g_zonename, sizeof (g_zonename)) < 0) {
(void) fprintf(stderr, "%s: failed to get zonename: %s\n",
g_pname, strerror(errno));
return (E_ERROR);
}
if (argc < 1)
return (usage(stderr));
for (ii = 0; ii < IPDADM_NCMDS; ii++) {
cmd = &ipdadm_cmds[ii];
if (strcmp(argv[0], cmd->idc_name) == 0) {
argv++;
argc--;
assert(cmd->idc_func != NULL);
return (cmd->idc_func(argc, argv));
}
}
(void) fprintf(stderr, "%s: %s: unknown command\n", g_pname, argv[0]);
return (usage(stderr));
}