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 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 #include <ctype.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <locale.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/socket.h>
34 #include <sys/socketvar.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37
38 #define MAXLINELEN 4096
39
40 /*
41 * Usage:
42 * soconfig -d <dir>
43 * Reads input from files in dir.
44 *
45 * soconfig -f <file>
46 * Reads input from file. The file is structured as
47 * <fam> <type> <protocol> <path|module>
48 * <fam> <type> <protocol>
49 * with the first line registering and the second line
50 * deregistering.
51 *
52 * soconfig <fam> <type> <protocol> <path|module>
53 * registers
54 *
55 * soconfig <fam> <type> <protocol>
56 * deregisters
57 *
58 * soconfig -l
59 * print the in-kernel socket configuration table
60 *
61 * Filter Operations (Consolidation Private):
62 *
63 * soconfig -F <name> <modname> {auto [top | bottom | before:filter |
64 * after:filter] | prog} <fam>:<type>:<proto>,...
65 * configure filter
66 *
67 * soconfig -F <name>
68 * unconfigures filter
69 */
70
71 static int parse_files_in_dir(const char *dir);
72
73 static int parse_file(char *filename);
74
75 static int split_line(char *line, char *argvec[], int maxargvec);
76
77 static int parse_params(char *famstr, char *typestr, char *protostr,
78 char *path, const char *file, int line);
79
80 static int parse_int(char *str);
81
82 static void usage(void);
83
84 static int parse_filter_params(int argc, char **argv);
85
86 static int print_socktable();
87
88 int
main(argc,argv)89 main(argc, argv)
90 int argc;
91 char *argv[];
92 {
93 int ret;
94
95 argc--; argv++;
96
97 (void) setlocale(LC_ALL, "");
98 #if !defined(TEXT_DOMAIN)
99 #define TEXT_DOMAIN "SYS_TEST"
100 #endif
101 (void) textdomain(TEXT_DOMAIN);
102
103 if (argc == 1 && strcmp(argv[0], "-l") == 0) {
104 ret = print_socktable();
105 exit(ret);
106 }
107
108 if (argc >= 2 && strcmp(argv[0], "-F") == 0) {
109 argc--; argv++;
110 ret = parse_filter_params(argc, argv);
111 exit(ret);
112 }
113 if (argc == 2 && strcmp(argv[0], "-d") == 0) {
114 ret = parse_files_in_dir(argv[1]);
115 exit(ret);
116 }
117 if (argc == 2 && strcmp(argv[0], "-f") == 0) {
118 ret = parse_file(argv[1]);
119 exit(ret);
120 }
121 if (argc == 3) {
122 ret = parse_params(argv[0], argv[1], argv[2], NULL, NULL, -1);
123 exit(ret);
124 }
125 if (argc == 4) {
126 ret = parse_params(argv[0], argv[1], argv[2], argv[3],
127 NULL, -1);
128 exit(ret);
129 }
130 usage();
131 exit(1);
132 /* NOTREACHED */
133 }
134
135 static void
usage(void)136 usage(void)
137 {
138 fprintf(stderr, gettext(
139 "Usage: soconfig -d <dir>\n"
140 "\tsoconfig -f <file>\n"
141 "\tsoconfig <fam> <type> <protocol> <path|module>\n"
142 "\tsoconfig <fam> <type> <protocol>\n"
143 "\tsoconfig -l\n"));
144 }
145
146 /*
147 * Parse all files in the given directory.
148 */
149 static int
parse_files_in_dir(const char * dirname)150 parse_files_in_dir(const char *dirname)
151 {
152 DIR *dp;
153 struct dirent *dirp;
154 struct stat stats;
155 char buf[MAXPATHLEN];
156
157 if ((dp = opendir(dirname)) == NULL) {
158 fprintf(stderr, gettext("failed to open directory '%s': %s\n"),
159 dirname, strerror(errno));
160 return (1);
161 }
162
163 while ((dirp = readdir(dp)) != NULL) {
164 if (dirp->d_name[0] == '.')
165 continue;
166
167 if (snprintf(buf, sizeof (buf), "%s/%s", dirname,
168 dirp->d_name) >= sizeof (buf)) {
169 fprintf(stderr,
170 gettext("path name is too long: %s/%s\n"),
171 dirname, dirp->d_name);
172 continue;
173 }
174 if (stat(buf, &stats) == -1) {
175 fprintf(stderr,
176 gettext("failed to stat '%s': %s\n"), buf,
177 strerror(errno));
178 continue;
179 }
180 if (!S_ISREG(stats.st_mode))
181 continue;
182
183 (void) parse_file(buf);
184 }
185
186 closedir(dp);
187
188 return (0);
189 }
190
191 /*
192 * Open the specified file and parse each line. Skip comments (everything
193 * after a '#'). Return 1 if at least one error was encountered; otherwise 0.
194 */
195 static int
parse_file(char * filename)196 parse_file(char *filename)
197 {
198 char line[MAXLINELEN];
199 char pline[MAXLINELEN];
200 int argcount;
201 char *argvec[20];
202 FILE *fp;
203 int linecount = 0;
204 int numerror = 0;
205
206 fp = fopen(filename, "r");
207 if (fp == NULL) {
208 perror("soconfig: open");
209 fprintf(stderr, "\n");
210 usage();
211 return (1);
212 }
213
214 while (fgets(line, sizeof (line) - 1, fp) != NULL) {
215 linecount++;
216 strcpy(pline, line);
217 argcount = split_line(pline, argvec,
218 sizeof (argvec) / sizeof (argvec[0]));
219 #ifdef DEBUG
220 {
221 int i;
222
223 printf("scanned %d args\n", argcount);
224 for (i = 0; i < argcount; i++)
225 printf("arg[%d]: %s\n", i, argvec[i]);
226 }
227 #endif /* DEBUG */
228 switch (argcount) {
229 case 0:
230 /* Empty line - or comment only line */
231 break;
232 case 3:
233 numerror += parse_params(argvec[0], argvec[1],
234 argvec[2], NULL, filename, linecount);
235 break;
236 case 4:
237 numerror += parse_params(argvec[0], argvec[1],
238 argvec[2], argvec[3], filename, linecount);
239 break;
240 default:
241 numerror++;
242 fprintf(stderr,
243 gettext("Malformed line: <%s>\n"), line);
244 fprintf(stderr,
245 gettext("\ton line %d in %s\n"), linecount,
246 filename);
247 break;
248 }
249 }
250 (void) fclose(fp);
251
252 if (numerror > 0)
253 return (1);
254 else
255 return (0);
256 }
257
258 /*
259 * Parse a line splitting it off at whitspace characters.
260 * Modifies the content of the string by inserting NULLs.
261 */
262 static int
split_line(char * line,char * argvec[],int maxargvec)263 split_line(char *line, char *argvec[], int maxargvec)
264 {
265 int i = 0;
266 char *cp;
267
268 /* Truncate at the beginning of a comment */
269 cp = strchr(line, '#');
270 if (cp != NULL)
271 *cp = NULL;
272
273 /* CONSTCOND */
274 while (1) {
275 /* Skip any whitespace */
276 while (isspace(*line) && *line != NULL)
277 line++;
278
279 if (i >= maxargvec)
280 return (i);
281
282 argvec[i] = line;
283 if (*line == NULL)
284 return (i);
285 i++;
286 /* Skip until next whitespace */
287 while (!isspace(*line) && *line != NULL)
288 line++;
289 if (*line != NULL) {
290 /* Break off argument */
291 *line++ = NULL;
292 }
293 }
294 /* NOTREACHED */
295 }
296
297 /*
298 * Parse the set of parameters and issues the sockconfig syscall.
299 * If line is not -1 it is assumed to be the line number in the file.
300 */
301 static int
parse_params(char * famstr,char * typestr,char * protostr,char * path,const char * file,int line)302 parse_params(char *famstr, char *typestr, char *protostr, char *path,
303 const char *file, int line)
304 {
305 int cmd, fam, type, protocol;
306
307 fam = parse_int(famstr);
308 if (fam == -1) {
309 fprintf(stderr, gettext("Bad family number: %s\n"), famstr);
310 if (line != -1)
311 fprintf(stderr,
312 gettext("\ton line %d in %s\n"), line, file);
313 else {
314 fprintf(stderr, "\n");
315 usage();
316 }
317 return (1);
318 }
319
320 type = parse_int(typestr);
321 if (type == -1) {
322 fprintf(stderr,
323 gettext("Bad socket type number: %s\n"), typestr);
324 if (line != -1)
325 fprintf(stderr,
326 gettext("\ton line %d in %s\n"), line, file);
327 else {
328 fprintf(stderr, "\n");
329 usage();
330 }
331 return (1);
332 }
333
334 protocol = parse_int(protostr);
335 if (protocol == -1) {
336 fprintf(stderr,
337 gettext("Bad protocol number: %s\n"), protostr);
338 if (line != -1)
339 fprintf(stderr,
340 gettext("\ton line %d in %s\n"), line, file);
341 else {
342 fprintf(stderr, "\n");
343 usage();
344 }
345 return (1);
346 }
347
348
349 if (path != NULL) {
350 struct stat stats;
351
352 if (strncmp(path, "/dev", strlen("/dev")) == 0 &&
353 stat(path, &stats) == -1) {
354 perror(path);
355 if (line != -1)
356 fprintf(stderr,
357 gettext("\ton line %d in %s\n"), line,
358 file);
359 else {
360 fprintf(stderr, "\n");
361 usage();
362 }
363 return (1);
364 }
365
366 cmd = SOCKCONFIG_ADD_SOCK;
367 } else {
368 cmd = SOCKCONFIG_REMOVE_SOCK;
369 }
370
371 #ifdef DEBUG
372 printf("not calling sockconfig(%d, %d, %d, %d, %s)\n",
373 cmd, fam, type, protocol, path == NULL ? "(null)" : path);
374 #else
375 if (_sockconfig(cmd, fam, type, protocol, path) == -1) {
376 char *s;
377
378 switch (errno) {
379 case EEXIST:
380 s = gettext("Mapping exists");
381 break;
382 default:
383 s = strerror(errno);
384 break;
385 }
386
387 fprintf(stderr,
388 gettext("warning: socket configuration failed "
389 "for family %d type %d protocol %d: %s\n"),
390 fam, type, protocol, s);
391 if (line != -1) {
392 fprintf(stderr,
393 gettext("\ton line %d in %s\n"), line, file);
394 }
395 return (1);
396 }
397 #endif
398 return (0);
399 }
400
401 static int
parse_int(char * str)402 parse_int(char *str)
403 {
404 char *end;
405 int res;
406
407 res = strtol(str, &end, 0);
408 if (end == str)
409 return (-1);
410 return (res);
411 }
412
413 /*
414 * Add and remove socket filters.
415 */
416 static int
parse_filter_params(int argc,char ** argv)417 parse_filter_params(int argc, char **argv)
418 {
419 struct sockconfig_filter_props filprop;
420 sof_socktuple_t *socktuples;
421 size_t tupcnt, nalloc;
422 char *hintarg, *socktup, *tupstr;
423 int i;
424
425 if (argc == 1) {
426 if (_sockconfig(SOCKCONFIG_REMOVE_FILTER, argv[0], 0,
427 0, 0) < 0) {
428 switch (errno) {
429 case ENXIO:
430 fprintf(stderr,
431 gettext("socket filter is not configured "
432 "'%s'\n"), argv[0]);
433 break;
434 default:
435 perror("sockconfig");
436 break;
437 }
438 return (1);
439 }
440 return (0);
441 }
442
443 if (argc < 4 || argc > 5)
444 return (1);
445
446
447 if (strlen(argv[1]) >= MODMAXNAMELEN) {
448 fprintf(stderr,
449 gettext("invalid module name '%s': name too long\n"),
450 argv[1]);
451 return (1);
452 }
453 filprop.sfp_modname = argv[1];
454
455 /* Check the attach semantics */
456 if (strcmp(argv[2], "auto") == 0) {
457 filprop.sfp_autoattach = B_TRUE;
458 if (argc == 5) {
459 /* placement hint */
460 if (strcmp(argv[3], "top") == 0) {
461 filprop.sfp_hint = SOF_HINT_TOP;
462 } else if (strcmp(argv[3], "bottom") == 0) {
463 filprop.sfp_hint = SOF_HINT_BOTTOM;
464 } else {
465 if (strncmp(argv[3], "before", 6) == 0) {
466 filprop.sfp_hint = SOF_HINT_BEFORE;
467 } else if (strncmp(argv[3], "after", 5) == 0) {
468 filprop.sfp_hint = SOF_HINT_AFTER;
469 } else {
470 fprintf(stderr,
471 gettext("invalid placement hint "
472 "'%s'\n"), argv[3]);
473 return (1);
474 }
475
476 hintarg = strchr(argv[3], ':');
477 if (hintarg == NULL ||
478 (strlen(++hintarg) == 0) ||
479 (strlen(hintarg) >= FILNAME_MAX)) {
480 fprintf(stderr,
481 gettext("invalid placement hint "
482 "argument '%s': name too long\n"),
483 argv[3]);
484 return (1);
485 }
486
487 filprop.sfp_hintarg = hintarg;
488 }
489 } else {
490 filprop.sfp_hint = SOF_HINT_NONE;
491 }
492 } else if (strcmp(argv[2], "prog") == 0) {
493 filprop.sfp_autoattach = B_FALSE;
494 filprop.sfp_hint = SOF_HINT_NONE;
495 /* cannot specify placement hint for programmatic filter */
496 if (argc == 5) {
497 fprintf(stderr,
498 gettext("placement hint specified for programmatic "
499 "filter\n"));
500 return (1);
501 }
502 } else {
503 fprintf(stderr, gettext("invalid attach semantic '%s'\n"),
504 argv[2]);
505 return (1);
506 }
507
508 /* parse the socket tuples */
509 nalloc = 4;
510 socktuples = calloc(nalloc, sizeof (sof_socktuple_t));
511 if (socktuples == NULL) {
512 perror("calloc");
513 return (1);
514 }
515
516 tupcnt = 0;
517 tupstr = argv[(argc == 4) ? 3 : 4];
518 while ((socktup = strsep(&tupstr, ",")) != NULL) {
519 int val;
520 char *valstr;
521
522 if (tupcnt == nalloc) {
523 sof_socktuple_t *new;
524
525 nalloc *= 2;
526 new = realloc(socktuples,
527 nalloc * sizeof (sof_socktuple_t));
528 if (new == NULL) {
529 perror("realloc");
530 free(socktuples);
531 return (1);
532 }
533 socktuples = new;
534 }
535 i = 0;
536 while ((valstr = strsep(&socktup, ":")) != NULL && i < 3) {
537 val = parse_int(valstr);
538 if (val == -1) {
539 fprintf(stderr, gettext("bad socket tuple\n"));
540 free(socktuples);
541 return (1);
542 }
543 switch (i) {
544 case 0: socktuples[tupcnt].sofst_family = val; break;
545 case 1: socktuples[tupcnt].sofst_type = val; break;
546 case 2: socktuples[tupcnt].sofst_protocol = val; break;
547 }
548 i++;
549 }
550 if (i != 3) {
551 fprintf(stderr, gettext("bad socket tuple\n"));
552 free(socktuples);
553 return (1);
554 }
555 tupcnt++;
556 }
557 if (tupcnt == 0) {
558 fprintf(stderr, gettext("no socket tuples specified\n"));
559 free(socktuples);
560 return (1);
561 }
562 filprop.sfp_socktuple_cnt = tupcnt;
563 filprop.sfp_socktuple = socktuples;
564
565 if (_sockconfig(SOCKCONFIG_ADD_FILTER, argv[0], &filprop, 0, 0) < 0) {
566 switch (errno) {
567 case EINVAL:
568 fprintf(stderr,
569 gettext("invalid socket filter configuration\n"));
570 break;
571 case EEXIST:
572 fprintf(stderr,
573 gettext("socket filter is already configured "
574 "'%s'\n"), argv[0]);
575 break;
576 case ENOSPC:
577 fprintf(stderr, gettext("unable to satisfy placement "
578 "constraint\n"));
579 break;
580 default:
581 perror("sockconfig");
582 break;
583 }
584 free(socktuples);
585 return (1);
586 }
587 free(socktuples);
588 return (0);
589 }
590
591 /*
592 * Print the in-kernel socket configuration table
593 */
594
595 static int
print_socktable()596 print_socktable()
597 {
598 sockconfig_socktable_t sc_table;
599 int i;
600
601 (void) memset(&sc_table, 0, sizeof (sockconfig_socktable_t));
602
603 /* get number of entries */
604 if (_sockconfig(SOCKCONFIG_GET_SOCKTABLE, &sc_table) == -1) {
605 fprintf(stderr,
606 gettext("cannot get in-kernel socket table: %s\n"),
607 strerror(errno));
608 return (-1);
609 }
610 if (sc_table.num_of_entries == 0)
611 return (0);
612
613 sc_table.st_entries = calloc(sc_table.num_of_entries,
614 sizeof (sockconfig_socktable_entry_t));
615 if (sc_table.st_entries == NULL) {
616 fprintf(stderr, gettext("out of memory\n"));
617 return (-1);
618 }
619
620 /* get socket table entries */
621 if (_sockconfig(SOCKCONFIG_GET_SOCKTABLE, &sc_table) == -1) {
622 fprintf(stderr,
623 gettext("cannot get in-kernel socket table: %s\n"),
624 strerror(errno));
625 return (-1);
626 }
627
628 printf("%6s %4s %5s %15s %15s %6s %6s\n",
629 "FAMILY", "TYPE", "PROTO", "STRDEV", "SOCKMOD",
630 "REFS", "FLAGS");
631 for (i = 0; i < sc_table.num_of_entries; i++) {
632 printf("%6u %4u %5u %15s %15s %6u %#6x\n",
633 sc_table.st_entries[i].se_family,
634 sc_table.st_entries[i].se_type,
635 sc_table.st_entries[i].se_protocol,
636 (strcmp(sc_table.st_entries[i].se_modname,
637 "socktpi") == 0) ?
638 sc_table.st_entries[i].se_strdev : "-",
639 sc_table.st_entries[i].se_modname,
640 sc_table.st_entries[i].se_refcnt,
641 sc_table.st_entries[i].se_flags);
642 }
643 free(sc_table.st_entries);
644 return (0);
645 }
646