xref: /illumos-gate/usr/src/cmd/ipdadm/ipdadm.c (revision e1d3217b9afde782c4d3e946fda0e6ef36a61306)
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 	rval = ipd_status_read(fd, &hdl);
123 	(void) ipd_close(fd);
124 
125 	if (rval != 0) {
126 		(void) fprintf(stderr, "%s: failed to get list info: %s\n",
127 		    g_pname, ipd_errmsg);
128 		return (E_ERROR);
129 	}
130 
131 	ipd_status_foreach_zone(hdl, ipdadm_list_one, (void *)(intptr_t)opt_v);
132 	ipd_status_free(hdl);
133 
134 	return (E_SUCCESS);
135 }
136 
137 /*ARGSUSED*/
138 static int
139 ipdadm_info(int argc, char *argv[])
140 {
141 	int rval, fd;
142 	ipd_stathdl_t hdl;
143 	ipd_config_t *icp;
144 
145 	if (argc != 0)
146 		return (usage(stderr));
147 
148 	fd = ipd_open(NULL);
149 	rval = ipd_status_read(fd, &hdl);
150 	(void) ipd_close(fd);
151 	if (rval != 0) {
152 		(void) fprintf(stderr, "%s: failed to get info: %s\n",
153 		    g_pname, ipd_errmsg);
154 		return (E_ERROR);
155 	}
156 
157 	if (ipd_status_get_config(hdl, g_zid, &icp) != 0) {
158 		if (ipd_errno == EIPD_ZC_NOENT) {
159 			(void) printf("zone %s does not exist or has no "
160 			    "ipd actions enabled\n", g_zonename);
161 			return (E_SUCCESS);
162 		}
163 		(void) fprintf(stderr, "%s: failed to get info: %s\n",
164 		    g_pname, ipd_errmsg);
165 		return (E_ERROR);
166 	}
167 
168 	(void) printf("ipd information for zone %s:\n",
169 	    g_zonename);
170 	(void) printf("\tcorrupt:\t%u%% chance of packet corruption\n",
171 	    icp->ic_corrupt);
172 	(void) printf("\tdrop:\t\t%u%% chance of packet drop\n",
173 	    icp->ic_drop);
174 	(void) printf("\tdelay:\t\t%u microsecond delay per packet\n",
175 	    icp->ic_delay);
176 
177 	ipd_status_free(hdl);
178 
179 	return (E_SUCCESS);
180 }
181 
182 static long
183 ipdadm_parse_long(const char *str, const char *name, long min, long max)
184 {
185 	long val;
186 	char *end;
187 
188 	errno = 0;
189 	val = strtol(str, &end, 10);
190 	if (errno != 0) {
191 		(void) fprintf(stderr, "%s: invalid value for %s: %s\n",
192 		    g_pname, name, str);
193 		exit(E_ERROR);
194 	}
195 
196 	/*
197 	 * We want to make sure that we got the whole string. If not that's an
198 	 * error. e.g. 23.42 should not be valid.
199 	 */
200 	if (*end != '\0') {
201 		(void) fprintf(stderr, "%s: %s value must be an integer\n",
202 		    g_pname, name);
203 		exit(E_ERROR);
204 	}
205 
206 	if (val < min || val > max) {
207 		(void) fprintf(stderr, "%s: %s value must be between %ld and "
208 		    "%ld inclusive\n", g_pname, name, min, max);
209 		exit(E_ERROR);
210 	}
211 
212 	return (val);
213 }
214 
215 static int
216 ipdadm_corrupt(int argc, char *argv[])
217 {
218 	int rval, fd;
219 	long val;
220 	ipd_config_t ic;
221 
222 	if (argc != 1) {
223 		(void) fprintf(stderr, "%s: corrupt <percentage>\n",
224 		    g_pname);
225 		return (usage(stderr));
226 	}
227 
228 	val = ipdadm_parse_long(argv[0], "corrupt", 0, 100);
229 	bzero(&ic, sizeof (ic));
230 	ic.ic_mask = IPDM_CORRUPT;
231 	ic.ic_corrupt = val;
232 
233 	fd = ipd_open(NULL);
234 	rval = ipd_ctl(fd, g_zid, &ic);
235 	(void) ipd_close(fd);
236 
237 	if (rval != 0) {
238 		(void) fprintf(stderr, "%s: failed to change corrupt "
239 		    "value: %s\n", g_pname, ipd_errmsg);
240 		return (E_ERROR);
241 	}
242 
243 	return (E_SUCCESS);
244 }
245 
246 static int
247 ipdadm_delay(int argc, char *argv[])
248 {
249 	long val;
250 	int fd, rval;
251 	ipd_config_t ic;
252 
253 	if (argc != 1) {
254 		(void) fprintf(stderr, "%s: delay <microseconds>\n",
255 		    g_pname);
256 		return (usage(stderr));
257 	}
258 
259 	val = ipdadm_parse_long(argv[0], "delay", 0, MAXLONG);
260 	bzero(&ic, sizeof (ic));
261 	ic.ic_mask = IPDM_DELAY;
262 	ic.ic_delay = val;
263 
264 	fd = ipd_open(NULL);
265 	rval = ipd_ctl(fd, g_zid, &ic);
266 	(void) ipd_close(fd);
267 
268 	if (rval != 0) {
269 		(void) fprintf(stderr, "%s: failed to change delay value: %s\n",
270 		    g_pname, ipd_errmsg);
271 		return (E_ERROR);
272 	}
273 
274 	return (E_SUCCESS);
275 }
276 
277 static int
278 ipdadm_drop(int argc, char *argv[])
279 {
280 	long val;
281 	int fd, rval;
282 	ipd_config_t ic;
283 
284 	if (argc != 1) {
285 		(void) fprintf(stderr, "%s: drop <percentage>\n",
286 		    g_pname);
287 		return (usage(stderr));
288 	}
289 
290 	val = ipdadm_parse_long(argv[0], "drop", 0, 100);
291 	bzero(&ic, sizeof (ic));
292 	ic.ic_mask = IPDM_DROP;
293 	ic.ic_drop = val;
294 
295 	fd = ipd_open(NULL);
296 	rval = ipd_ctl(fd, g_zid, &ic);
297 	(void) ipd_close(fd);
298 
299 	if (rval != 0) {
300 		(void) fprintf(stderr, "%s: failed to change drop value: %s\n",
301 		    g_pname, ipd_errmsg);
302 		return (E_ERROR);
303 	}
304 
305 	return (E_SUCCESS);
306 }
307 
308 static int
309 ipdadm_remove_valid(const char *str)
310 {
311 	if (strcmp(str, "corrupt") == 0) {
312 		return (IPDM_CORRUPT);
313 	} else if (strcmp(str, "drop") == 0) {
314 		return (IPDM_DROP);
315 	} else if (strcmp(str, "delay") == 0) {
316 		return (IPDM_DELAY);
317 	}
318 
319 	return (0);
320 }
321 
322 static int
323 ipdadm_remove(int argc, char *argv[])
324 {
325 	ipd_config_t ic;
326 	char *cur, *res;
327 	int rval, fd;
328 
329 	if (argc < 1) {
330 		(void) fprintf(stderr, "%s: remove <arguments>\n",
331 		    g_pname);
332 		return (usage(stderr));
333 	}
334 
335 	if (argc > 1) {
336 		(void) fprintf(stderr, "%s: remove's arguments must be "
337 		    "comma seperated\n", g_pname);
338 		return (E_ERROR);
339 	}
340 
341 	bzero(&ic, sizeof (ic));
342 
343 	cur = argv[0];
344 	while ((res = strchr(cur, ',')) != NULL) {
345 		*res = '\0';
346 		if ((rval = ipdadm_remove_valid(cur)) == 0) {
347 			(void) fprintf(stderr, "%s: unknown remove "
348 			    "argument: %s\n", g_pname, cur);
349 			return (E_ERROR);
350 		}
351 		ic.ic_mask |= rval;
352 		cur = res + 1;
353 	}
354 
355 	if ((rval = ipdadm_remove_valid(cur)) == 0) {
356 		(void) fprintf(stderr, "%s: unknown remove argument: %s\n",
357 		    g_pname, cur);
358 		return (E_ERROR);
359 	}
360 	ic.ic_mask |= rval;
361 
362 	fd = ipd_open(NULL);
363 	rval = ipd_ctl(fd, g_zid, &ic);
364 	(void) ipd_close(fd);
365 	if (rval == -1) {
366 		(void) fprintf(stderr, "%s: failed to remove instances: %s\n",
367 		    g_pname, ipd_errmsg);
368 		return (E_ERROR);
369 	}
370 
371 	return (E_SUCCESS);
372 }
373 
374 
375 int
376 main(int argc, char *argv[])
377 {
378 	int ii;
379 	ipdadm_cmd_t *cmd;
380 
381 	g_pname = basename(argv[0]);
382 
383 	if (argc < 2)
384 		return (usage(stderr));
385 	argc--;
386 	argv++;
387 
388 	g_zid = getzoneid();
389 	if (strcmp("-z", argv[0]) == 0) {
390 		argc--;
391 		argv++;
392 		if (argc < 1) {
393 			(void) fprintf(stderr, "%s: -z requires an argument\n",
394 			    g_pname);
395 			return (usage(stderr));
396 		}
397 
398 		if (g_zid != GLOBAL_ZONEID) {
399 			(void) fprintf(stderr, "%s: -z option only permitted "
400 			    "in global zone\n", g_pname);
401 			return (usage(stderr));
402 		}
403 
404 		g_zid = getzoneidbyname(argv[0]);
405 		if (g_zid == -1) {
406 			(void) fprintf(stderr, "%s: %s: invalid zone\n",
407 			    g_pname, argv[0]);
408 			return (E_ERROR);
409 		}
410 		argc--;
411 		argv++;
412 	}
413 
414 	if (getzonenamebyid(g_zid, g_zonename, sizeof (g_zonename)) < 0) {
415 		(void) fprintf(stderr, "%s: failed to get zonename: %s\n",
416 		    g_pname, strerror(errno));
417 		return (E_ERROR);
418 	}
419 
420 	if (argc < 1)
421 		return (usage(stderr));
422 
423 	for (ii = 0; ii < IPDADM_NCMDS; ii++) {
424 		cmd = &ipdadm_cmds[ii];
425 		if (strcmp(argv[0], cmd->idc_name) == 0) {
426 			argv++;
427 			argc--;
428 			assert(cmd->idc_func != NULL);
429 			return (cmd->idc_func(argc, argv));
430 		}
431 	}
432 
433 	(void) fprintf(stderr, "%s: %s: unknown command\n", g_pname, argv[0]);
434 	return (usage(stderr));
435 }
436