1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2012 Joyent, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <values.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <stropts.h>
38 #include <zone.h>
39 #include <libgen.h>
40 #include <assert.h>
41
42 #include <libipd.h>
43
44 static char *g_pname;
45 static char g_zonename[ZONENAME_MAX];
46 static zoneid_t g_zid;
47
48 #define E_SUCCESS 0
49 #define E_ERROR 1
50 #define E_USAGE 2
51
52 typedef int (*idc_cmd_func_t)(int, char *[]);
53 typedef struct ipdadm_cmd {
54 const char *idc_name; /* subcommand name */
55 idc_cmd_func_t idc_func; /* subcommand function */
56 const char *idc_usage; /* subcommand help */
57 } ipdadm_cmd_t;
58
59 static int ipdadm_list(int, char *[]);
60 static int ipdadm_info(int, char *[]);
61 static int ipdadm_corrupt(int, char *[]);
62 static int ipdadm_delay(int, char *[]);
63 static int ipdadm_drop(int, char *[]);
64 static int ipdadm_remove(int, char *[]);
65
66 #define IPDADM_NCMDS 6
67 static ipdadm_cmd_t ipdadm_cmds[] = {
68 { "list", ipdadm_list, "list [-v]" },
69 { "info", ipdadm_info, "info" },
70 { "corrupt", ipdadm_corrupt, "corrupt <percentage>" },
71 { "delay", ipdadm_delay, "delay <microseconds>" },
72 { "drop", ipdadm_drop, "drop <percentage>" },
73 { "remove", ipdadm_remove, "remove [corrupt|delay|drop]" }
74 };
75
76 static int
77 usage(FILE *fp)
78 {
79 int ii;
80 ipdadm_cmd_t *cmd;
81
82 (void) fprintf(fp, "Usage: %s [-z zonename] subcommand "
83 "[subcommand opts]\n\n", g_pname);
84 (void) fprintf(fp, "Subcommands:\n");
85 for (ii = 0; ii < IPDADM_NCMDS; ii++) {
86 cmd = &ipdadm_cmds[ii];
87 (void) fprintf(fp, "\t%s\n", cmd->idc_usage);
88 }
89
90 return (E_USAGE);
91 }
92
93 static void
94 ipdadm_list_one(zoneid_t z, const ipd_config_t *icp, void *arg)
95 {
96 char zonename[ZONENAME_MAX];
97 int opt_v = (int)(intptr_t)arg;
98
99 if (getzonenamebyid(z, zonename, sizeof (zonename)) < 0)
100 (void) printf("%ld", z);
101 else
102 (void) printf("%s", zonename);
103
104 if (!opt_v) {
105 (void) printf("\n");
106 return;
107 }
108
109 (void) printf("\t%u\t%u\t%u\n", icp->ic_corrupt, icp->ic_drop,
110 icp->ic_delay);
111 }
112
113 static int
114 ipdadm_list(int argc, char *argv[])
115 {
116 int opt_v = 0;
117 int fd, rval;
118 ipd_stathdl_t hdl;
119
120 if (argc > 1)
121 return (usage(stderr));
122
123 if (argc == 1) {
124 if (strcmp(argv[0], "-v") == 0)
125 ++opt_v;
126 else
127 return (usage(stderr));
128 }
129
130 fd = ipd_open(NULL);
131 rval = ipd_status_read(fd, &hdl);
132 (void) ipd_close(fd);
133
134 if (rval != 0) {
135 (void) fprintf(stderr, "%s: failed to get list info: %s\n",
136 g_pname, ipd_errmsg);
137 return (E_ERROR);
138 }
139
140 ipd_status_foreach_zone(hdl, ipdadm_list_one, (void *)(intptr_t)opt_v);
141 ipd_status_free(hdl);
142
143 return (E_SUCCESS);
144 }
145
146 /*ARGSUSED*/
147 static int
148 ipdadm_info(int argc, char *argv[])
149 {
150 int rval, fd;
151 ipd_stathdl_t hdl;
152 ipd_config_t *icp;
153
154 if (argc != 0)
155 return (usage(stderr));
156
157 fd = ipd_open(NULL);
158 rval = ipd_status_read(fd, &hdl);
159 (void) ipd_close(fd);
160 if (rval != 0) {
161 (void) fprintf(stderr, "%s: failed to get info: %s\n",
162 g_pname, ipd_errmsg);
163 return (E_ERROR);
164 }
165
166 if (ipd_status_get_config(hdl, g_zid, &icp) != 0) {
167 if (ipd_errno == EIPD_ZC_NOENT) {
168 (void) printf("zone %s does not exist or has no "
169 "ipd actions enabled\n", g_zonename);
170 return (E_SUCCESS);
171 }
172 (void) fprintf(stderr, "%s: failed to get info: %s\n",
173 g_pname, ipd_errmsg);
174 return (E_ERROR);
175 }
176
177 (void) printf("ipd information for zone %s:\n",
178 g_zonename);
179 (void) printf("\tcorrupt:\t%u%% chance of packet corruption\n",
180 icp->ic_corrupt);
181 (void) printf("\tdrop:\t\t%u%% chance of packet drop\n",
182 icp->ic_drop);
183 (void) printf("\tdelay:\t\t%u microsecond delay per packet\n",
184 icp->ic_delay);
185
186 ipd_status_free(hdl);
187
188 return (E_SUCCESS);
189 }
190
191 static long
192 ipdadm_parse_long(const char *str, const char *name, long min, long max)
193 {
194 long val;
195 char *end;
196
197 errno = 0;
198 val = strtol(str, &end, 10);
199 if (errno != 0) {
200 (void) fprintf(stderr, "%s: invalid value for %s: %s\n",
201 g_pname, name, str);
202 exit(E_ERROR);
203 }
204
205 /*
206 * We want to make sure that we got the whole string. If not that's an
207 * error. e.g. 23.42 should not be valid.
208 */
209 if (*end != '\0') {
210 (void) fprintf(stderr, "%s: %s value must be an integer\n",
211 g_pname, name);
212 exit(E_ERROR);
213 }
214
215 if (val < min || val > max) {
216 (void) fprintf(stderr, "%s: %s value must be between %ld and "
217 "%ld inclusive\n", g_pname, name, min, max);
218 exit(E_ERROR);
219 }
220
221 return (val);
222 }
223
224 static int
225 ipdadm_corrupt(int argc, char *argv[])
226 {
227 int rval, fd;
228 long val;
229 ipd_config_t ic;
230
231 if (argc != 1) {
232 (void) fprintf(stderr, "%s: corrupt <percentage>\n",
233 g_pname);
234 return (usage(stderr));
235 }
236
237 val = ipdadm_parse_long(argv[0], "corrupt", 0, 100);
238 bzero(&ic, sizeof (ic));
239 ic.ic_mask = IPDM_CORRUPT;
240 ic.ic_corrupt = val;
241
242 fd = ipd_open(NULL);
243 rval = ipd_ctl(fd, g_zid, &ic);
244 (void) ipd_close(fd);
245
246 if (rval != 0) {
247 (void) fprintf(stderr, "%s: failed to change corrupt "
248 "value: %s\n", g_pname, ipd_errmsg);
249 return (E_ERROR);
250 }
251
252 return (E_SUCCESS);
253 }
254
255 static int
256 ipdadm_delay(int argc, char *argv[])
257 {
258 long val;
259 int fd, rval;
260 ipd_config_t ic;
261
262 if (argc != 1) {
263 (void) fprintf(stderr, "%s: delay <microseconds>\n",
264 g_pname);
265 return (usage(stderr));
266 }
267
268 val = ipdadm_parse_long(argv[0], "delay", 0, MAXLONG);
269 bzero(&ic, sizeof (ic));
270 ic.ic_mask = IPDM_DELAY;
271 ic.ic_delay = val;
272
273 fd = ipd_open(NULL);
274 rval = ipd_ctl(fd, g_zid, &ic);
275 (void) ipd_close(fd);
276
277 if (rval != 0) {
278 (void) fprintf(stderr, "%s: failed to change delay value: %s\n",
279 g_pname, ipd_errmsg);
280 return (E_ERROR);
281 }
282
283 return (E_SUCCESS);
284 }
285
286 static int
287 ipdadm_drop(int argc, char *argv[])
288 {
289 long val;
290 int fd, rval;
291 ipd_config_t ic;
292
293 if (argc != 1) {
294 (void) fprintf(stderr, "%s: drop <percentage>\n",
295 g_pname);
296 return (usage(stderr));
297 }
298
299 val = ipdadm_parse_long(argv[0], "drop", 0, 100);
300 bzero(&ic, sizeof (ic));
301 ic.ic_mask = IPDM_DROP;
302 ic.ic_drop = val;
303
304 fd = ipd_open(NULL);
305 rval = ipd_ctl(fd, g_zid, &ic);
306 (void) ipd_close(fd);
307
308 if (rval != 0) {
309 (void) fprintf(stderr, "%s: failed to change drop value: %s\n",
310 g_pname, ipd_errmsg);
311 return (E_ERROR);
312 }
313
314 return (E_SUCCESS);
315 }
316
317 static int
318 ipdadm_remove_valid(const char *str)
319 {
320 if (strcmp(str, "corrupt") == 0) {
321 return (IPDM_CORRUPT);
322 } else if (strcmp(str, "drop") == 0) {
323 return (IPDM_DROP);
324 } else if (strcmp(str, "delay") == 0) {
325 return (IPDM_DELAY);
326 }
327
328 return (0);
329 }
330
331 static int
332 ipdadm_remove(int argc, char *argv[])
333 {
334 ipd_config_t ic;
335 char *cur, *res;
336 int rval, fd;
337
338 if (argc < 1) {
339 (void) fprintf(stderr, "%s: remove <arguments>\n",
340 g_pname);
341 return (usage(stderr));
342 }
343
344 if (argc > 1) {
345 (void) fprintf(stderr, "%s: remove's arguments must be "
346 "comma seperated\n", g_pname);
347 return (E_ERROR);
348 }
349
350 bzero(&ic, sizeof (ic));
351
352 cur = argv[0];
353 while ((res = strchr(cur, ',')) != NULL) {
354 *res = '\0';
355 if ((rval = ipdadm_remove_valid(cur)) == 0) {
356 (void) fprintf(stderr, "%s: unknown remove "
357 "argument: %s\n", g_pname, cur);
358 return (E_ERROR);
359 }
360 ic.ic_mask |= rval;
361 cur = res + 1;
362 }
363
364 if ((rval = ipdadm_remove_valid(cur)) == 0) {
365 (void) fprintf(stderr, "%s: unknown remove argument: %s\n",
366 g_pname, cur);
367 return (E_ERROR);
368 }
369 ic.ic_mask |= rval;
370
371 fd = ipd_open(NULL);
372 rval = ipd_ctl(fd, g_zid, &ic);
373 (void) ipd_close(fd);
374 if (rval == -1) {
375 (void) fprintf(stderr, "%s: failed to remove instances: %s\n",
376 g_pname, ipd_errmsg);
377 return (E_ERROR);
378 }
379
380 return (E_SUCCESS);
381 }
382
383
384 int
385 main(int argc, char *argv[])
386 {
387 int ii;
388 ipdadm_cmd_t *cmd;
389
390 g_pname = basename(argv[0]);
391
392 if (argc < 2)
393 return (usage(stderr));
394 argc--;
395 argv++;
396
397 g_zid = getzoneid();
398 if (strcmp("-z", argv[0]) == 0) {
399 argc--;
400 argv++;
401 if (argc < 1) {
402 (void) fprintf(stderr, "%s: -z requires an argument\n",
403 g_pname);
404 return (usage(stderr));
405 }
406
407 if (g_zid != GLOBAL_ZONEID) {
408 (void) fprintf(stderr, "%s: -z option only permitted "
409 "in global zone\n", g_pname);
410 return (usage(stderr));
411 }
412
413 g_zid = getzoneidbyname(argv[0]);
414 if (g_zid == -1) {
415 (void) fprintf(stderr, "%s: %s: invalid zone\n",
416 g_pname, argv[0]);
417 return (E_ERROR);
418 }
419 argc--;
420 argv++;
421 }
422
423 if (getzonenamebyid(g_zid, g_zonename, sizeof (g_zonename)) < 0) {
424 (void) fprintf(stderr, "%s: failed to get zonename: %s\n",
425 g_pname, strerror(errno));
426 return (E_ERROR);
427 }
428
429 if (argc < 1)
430 return (usage(stderr));
431
432 for (ii = 0; ii < IPDADM_NCMDS; ii++) {
433 cmd = &ipdadm_cmds[ii];
434 if (strcmp(argv[0], cmd->idc_name) == 0) {
435 argv++;
436 argc--;
437 assert(cmd->idc_func != NULL);
438 return (cmd->idc_func(argc, argv));
439 }
440 }
441
442 (void) fprintf(stderr, "%s: %s: unknown command\n", g_pname, argv[0]);
443 return (usage(stderr));
444 }