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
usage(FILE * fp)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
ipdadm_list_one(zoneid_t z,const ipd_config_t * icp,void * arg)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
ipdadm_list(int argc,char * argv[])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
ipdadm_info(int argc,char * argv[])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
ipdadm_parse_long(const char * str,const char * name,long min,long max)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
ipdadm_corrupt(int argc,char * argv[])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
ipdadm_delay(int argc,char * argv[])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
ipdadm_drop(int argc,char * argv[])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
ipdadm_remove_valid(const char * str)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
ipdadm_remove(int argc,char * argv[])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
main(int argc,char * argv[])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