xref: /illumos-gate/usr/src/cmd/ipdadm/ipdadm.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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