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
35static char *g_pname;
36static char g_zonename[ZONENAME_MAX];
37static zoneid_t	g_zid;
38
39#define	E_SUCCESS	0
40#define	E_ERROR		1
41#define	E_USAGE		2
42
43typedef int (*idc_cmd_func_t)(int, char *[]);
44typedef 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
50static int ipdadm_list(int, char *[]);
51static int ipdadm_info(int, char *[]);
52static int ipdadm_corrupt(int, char *[]);
53static int ipdadm_delay(int, char *[]);
54static int ipdadm_drop(int, char *[]);
55static int ipdadm_remove(int, char *[]);
56
57#define	IPDADM_NCMDS	6
58static 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
67static int
68usage(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
84static void
85ipdadm_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
104static int
105ipdadm_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*/
143static int
144ipdadm_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
192static long
193ipdadm_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
225static int
226ipdadm_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
261static int
262ipdadm_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
297static int
298ipdadm_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
333static int
334ipdadm_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
347static int
348ipdadm_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
405int
406main(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