xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/soconfig.c (revision 3e95bd4ab92abca814bd28e854607d1975c7dc88)
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  */
24 
25 #include <stdio.h>
26 #include <sys/stat.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <locale.h>
32 #include <sys/socket.h>
33 #include <sys/socketvar.h>
34 #include <errno.h>
35 
36 #define	MAXLINELEN	4096
37 
38 /*
39  * Usage:
40  *	sonconfig -f <file>
41  *		Reads input from file. The file is structured as
42  *			 <fam> <type> <protocol> <path|module>
43  *			 <fam> <type> <protocol>
44  *		with the first line registering and the second line
45  *		deregistering.
46  *
47  *	soconfig <fam> <type> <protocol> <path|module>
48  *		registers
49  *
50  *	soconfig <fam> <type> <protocol>
51  *		deregisters
52  *
53  * Filter Operations (Consolidation Private):
54  *
55  *	soconfig -F <name> <modname> {auto [top | bottom | before:filter |
56  *		after:filter] | prog} <fam>:<type>:<proto>,...
57  *		configure filter
58  *
59  *	soconfig -F <name>
60  *		unconfigures filter
61  */
62 
63 static int	parse_file(char *filename);
64 
65 static int	split_line(char *line, char *argvec[], int maxargvec);
66 
67 static int	parse_params(char *famstr, char *typestr, char *protostr,
68 				char *path, int line);
69 
70 static int	parse_int(char *str);
71 
72 static void	usage(void);
73 
74 static int	parse_filter_params(int argc, char **argv);
75 
76 int
77 main(argc, argv)
78 	int argc;
79 	char *argv[];
80 {
81 	int ret;
82 
83 	argc--; argv++;
84 
85 	(void) setlocale(LC_ALL, "");
86 #if !defined(TEXT_DOMAIN)
87 #define	TEXT_DOMAIN "SYS_TEST"
88 #endif
89 	(void) textdomain(TEXT_DOMAIN);
90 
91 	if (argc >= 2 && strcmp(argv[0], "-F") == 0) {
92 		argc--; argv++;
93 		ret = parse_filter_params(argc, argv);
94 		exit(ret);
95 	}
96 	if (argc == 2 && strcmp(argv[0], "-f") == 0) {
97 		ret = parse_file(argv[1]);
98 		exit(ret);
99 	}
100 	if (argc == 3) {
101 		ret = parse_params(argv[0], argv[1], argv[2], NULL, -1);
102 		exit(ret);
103 	}
104 	if (argc == 4) {
105 		ret = parse_params(argv[0], argv[1], argv[2], argv[3], -1);
106 		exit(ret);
107 	}
108 	usage();
109 	exit(1);
110 	/* NOTREACHED */
111 }
112 
113 static void
114 usage(void)
115 {
116 	fprintf(stderr, gettext(
117 	    "Usage:	soconfig -f <file>\n"
118 	    "\tsoconfig <fam> <type> <protocol> <path|module>\n"
119 	    "\tsoconfig <fam> <type> <protocol>\n"));
120 }
121 
122 /*
123  * Open the specified file and parse each line. Skip comments (everything
124  * after a '#'). Return 1 if at least one error was encountered; otherwise 0.
125  */
126 static int
127 parse_file(char *filename)
128 {
129 	char line[MAXLINELEN];
130 	char pline[MAXLINELEN];
131 	int argcount;
132 	char *argvec[20];
133 	FILE *fp;
134 	int linecount = 0;
135 	int numerror = 0;
136 
137 	fp = fopen(filename, "r");
138 	if (fp == NULL) {
139 		perror("soconfig: open");
140 		fprintf(stderr, "\n");
141 		usage();
142 		return (1);
143 	}
144 
145 	while (fgets(line, sizeof (line) - 1, fp) != NULL) {
146 		linecount++;
147 		strcpy(pline, line);
148 		argcount = split_line(pline, argvec,
149 		    sizeof (argvec) / sizeof (argvec[0]));
150 #ifdef DEBUG
151 		{
152 			int i;
153 
154 			printf("scanned %d args\n", argcount);
155 			for (i = 0; i < argcount; i++)
156 				printf("arg[%d]: %s\n", i, argvec[i]);
157 		}
158 #endif /* DEBUG */
159 		switch (argcount) {
160 		case 0:
161 			/* Empty line - or comment only line */
162 			break;
163 		case 3:
164 			numerror += parse_params(argvec[0], argvec[1],
165 			    argvec[2], NULL, linecount);
166 			break;
167 		case 4:
168 			numerror += parse_params(argvec[0], argvec[1],
169 			    argvec[2], argvec[3], linecount);
170 			break;
171 		default:
172 			numerror++;
173 			fprintf(stderr,
174 			    gettext("Malformed line: <%s>\n"), line);
175 			fprintf(stderr,
176 			    gettext("\ton line %d\n"), linecount);
177 			break;
178 		}
179 	}
180 	(void) fclose(fp);
181 
182 	if (numerror > 0)
183 		return (1);
184 	else
185 		return (0);
186 }
187 
188 /*
189  * Parse a line splitting it off at whitspace characters.
190  * Modifies the content of the string by inserting NULLs.
191  */
192 static int
193 split_line(char *line, char *argvec[], int maxargvec)
194 {
195 	int i = 0;
196 	char *cp;
197 
198 	/* Truncate at the beginning of a comment */
199 	cp = strchr(line, '#');
200 	if (cp != NULL)
201 		*cp = NULL;
202 
203 	/* CONSTCOND */
204 	while (1) {
205 		/* Skip any whitespace */
206 		while (isspace(*line) && *line != NULL)
207 			line++;
208 
209 		if (i >= maxargvec)
210 			return (i);
211 
212 		argvec[i] = line;
213 		if (*line == NULL)
214 			return (i);
215 		i++;
216 		/* Skip until next whitespace */
217 		while (!isspace(*line) && *line != NULL)
218 			line++;
219 		if (*line != NULL) {
220 			/* Break off argument */
221 			*line++ = NULL;
222 		}
223 	}
224 	/* NOTREACHED */
225 }
226 
227 /*
228  * Parse the set of parameters and issues the sockconfig syscall.
229  * If line is not -1 it is assumed to be the line number in the file.
230  */
231 static int
232 parse_params(char *famstr, char *typestr, char *protostr, char *path, int line)
233 {
234 	int cmd, fam, type, protocol;
235 
236 	fam = parse_int(famstr);
237 	if (fam == -1) {
238 		fprintf(stderr, gettext("Bad family number: %s\n"), famstr);
239 		if (line != -1)
240 			fprintf(stderr,
241 			    gettext("\ton line %d\n"), line);
242 		else {
243 			fprintf(stderr, "\n");
244 			usage();
245 		}
246 		return (1);
247 	}
248 
249 	type = parse_int(typestr);
250 	if (type == -1) {
251 		fprintf(stderr,
252 		    gettext("Bad socket type number: %s\n"), typestr);
253 		if (line != -1)
254 			fprintf(stderr,
255 			    gettext("\ton line %d\n"), line);
256 		else {
257 			fprintf(stderr, "\n");
258 			usage();
259 		}
260 		return (1);
261 	}
262 
263 	protocol = parse_int(protostr);
264 	if (protocol == -1) {
265 		fprintf(stderr,
266 		    gettext("Bad protocol number: %s\n"), protostr);
267 		if (line != -1)
268 			fprintf(stderr,
269 			    gettext("\ton line %d\n"), line);
270 		else {
271 			fprintf(stderr, "\n");
272 			usage();
273 		}
274 		return (1);
275 	}
276 
277 
278 	if (path != NULL) {
279 		struct stat stats;
280 
281 		if (strncmp(path, "/dev", strlen("/dev")) == 0 &&
282 		    stat(path, &stats) == -1) {
283 			perror(path);
284 			if (line != -1)
285 				fprintf(stderr,
286 				    gettext("\ton line %d\n"), line);
287 			else {
288 				fprintf(stderr, "\n");
289 				usage();
290 			}
291 			return (1);
292 		}
293 
294 		cmd = SOCKCONFIG_ADD_SOCK;
295 	} else {
296 		cmd = SOCKCONFIG_REMOVE_SOCK;
297 	}
298 
299 #ifdef DEBUG
300 	printf("not calling sockconfig(%d, %d, %d, %d, %s)\n",
301 	    cmd, fam, type, protocol, path == NULL ? "(null)" : path);
302 #else
303 	if (_sockconfig(cmd, fam, type, protocol, path) == -1) {
304 		perror("sockconfig");
305 		return (1);
306 	}
307 #endif
308 	return (0);
309 }
310 
311 static int
312 parse_int(char *str)
313 {
314 	char *end;
315 	int res;
316 
317 	res = strtol(str, &end, 0);
318 	if (end == str)
319 		return (-1);
320 	return (res);
321 }
322 
323 /*
324  * Add and remove socket filters.
325  */
326 static int
327 parse_filter_params(int argc, char **argv)
328 {
329 	struct sockconfig_filter_props filprop;
330 	sof_socktuple_t *socktuples;
331 	size_t tupcnt, nalloc;
332 	char *hintarg, *socktup, *tupstr;
333 	int i;
334 
335 	if (argc == 1) {
336 		if (_sockconfig(SOCKCONFIG_REMOVE_FILTER, argv[0], 0,
337 		    0, 0) < 0) {
338 			switch (errno) {
339 			case ENXIO:
340 				fprintf(stderr,
341 				    gettext("socket filter is not configured "
342 				    "'%s'\n"), argv[0]);
343 				break;
344 			default:
345 				perror("sockconfig");
346 				break;
347 			}
348 			return (1);
349 		}
350 		return (0);
351 	}
352 
353 	if (argc < 4 || argc > 5)
354 		return (1);
355 
356 
357 	if (strlen(argv[1]) >= MODMAXNAMELEN) {
358 		fprintf(stderr,
359 		    gettext("invalid module name '%s': name too long\n"),
360 		    argv[1]);
361 		return (1);
362 	}
363 	filprop.sfp_modname = argv[1];
364 
365 	/* Check the attach semantics */
366 	if (strcmp(argv[2], "auto") == 0) {
367 		filprop.sfp_autoattach = B_TRUE;
368 		if (argc == 5) {
369 			/* placement hint */
370 			if (strcmp(argv[3], "top") == 0) {
371 				filprop.sfp_hint = SOF_HINT_TOP;
372 			} else if (strcmp(argv[3], "bottom") == 0) {
373 				filprop.sfp_hint = SOF_HINT_BOTTOM;
374 			} else {
375 				if (strncmp(argv[3], "before", 6) == 0) {
376 					filprop.sfp_hint = SOF_HINT_BEFORE;
377 				} else if (strncmp(argv[3], "after", 5) == 0) {
378 					filprop.sfp_hint = SOF_HINT_AFTER;
379 				} else {
380 					fprintf(stderr,
381 					    gettext("invalid placement hint "
382 					    "'%s'\n"), argv[3]);
383 					return (1);
384 				}
385 
386 				hintarg = strchr(argv[3], ':');
387 				if (hintarg == NULL ||
388 				    (strlen(++hintarg) == 0) ||
389 				    (strlen(hintarg) >= FILNAME_MAX)) {
390 					fprintf(stderr,
391 					    gettext("invalid placement hint "
392 					    "argument '%s': name too long\n"),
393 					    argv[3]);
394 					return (1);
395 				}
396 
397 				filprop.sfp_hintarg = hintarg;
398 			}
399 		} else {
400 			filprop.sfp_hint = SOF_HINT_NONE;
401 		}
402 	} else if (strcmp(argv[2], "prog") == 0) {
403 		filprop.sfp_autoattach = B_FALSE;
404 		filprop.sfp_hint = SOF_HINT_NONE;
405 		/* cannot specify placement hint for programmatic filter */
406 		if (argc == 5) {
407 			fprintf(stderr,
408 			    gettext("placement hint specified for programmatic "
409 			    "filter\n"));
410 			return (1);
411 		}
412 	} else {
413 		fprintf(stderr, gettext("invalid attach semantic '%s'\n"),
414 		    argv[2]);
415 		return (1);
416 	}
417 
418 	/* parse the socket tuples */
419 	nalloc = 4;
420 	socktuples = calloc(nalloc, sizeof (sof_socktuple_t));
421 	if (socktuples == NULL) {
422 		perror("calloc");
423 		return (1);
424 	}
425 
426 	tupcnt = 0;
427 	tupstr = argv[(argc == 4) ? 3 : 4];
428 	while ((socktup = strsep(&tupstr, ",")) != NULL) {
429 		int val;
430 		char *valstr;
431 
432 		if (tupcnt == nalloc) {
433 			sof_socktuple_t *new;
434 
435 			nalloc *= 2;
436 			new = realloc(socktuples,
437 			    nalloc * sizeof (sof_socktuple_t));
438 			if (new == NULL) {
439 				perror("realloc");
440 				free(socktuples);
441 				return (1);
442 			}
443 			socktuples = new;
444 		}
445 		i = 0;
446 		while ((valstr = strsep(&socktup, ":")) != NULL && i < 3) {
447 			val = parse_int(valstr);
448 			if (val == -1) {
449 				fprintf(stderr, gettext("bad socket tuple\n"));
450 				free(socktuples);
451 				return (1);
452 			}
453 			switch (i) {
454 			case 0:	socktuples[tupcnt].sofst_family = val; break;
455 			case 1:	socktuples[tupcnt].sofst_type = val; break;
456 			case 2:	socktuples[tupcnt].sofst_protocol = val; break;
457 			}
458 			i++;
459 		}
460 		if (i != 3) {
461 			fprintf(stderr, gettext("bad socket tuple\n"));
462 			free(socktuples);
463 			return (1);
464 		}
465 		tupcnt++;
466 	}
467 	if (tupcnt == 0) {
468 		fprintf(stderr, gettext("no socket tuples specified\n"));
469 		free(socktuples);
470 		return (1);
471 	}
472 	filprop.sfp_socktuple_cnt = tupcnt;
473 	filprop.sfp_socktuple = socktuples;
474 
475 	if (_sockconfig(SOCKCONFIG_ADD_FILTER, argv[0], &filprop, 0, 0) < 0) {
476 		switch (errno) {
477 		case EINVAL:
478 			fprintf(stderr,
479 			    gettext("invalid socket filter configuration\n"));
480 			break;
481 		case EEXIST:
482 			fprintf(stderr,
483 			    gettext("socket filter is already configured "
484 			    "'%s'\n"), argv[0]);
485 			break;
486 		case ENOSPC:
487 			fprintf(stderr, gettext("unable to satisfy placement "
488 			    "constraint\n"));
489 			break;
490 		default:
491 			perror("sockconfig");
492 			break;
493 		}
494 		free(socktuples);
495 		return (1);
496 	}
497 	free(socktuples);
498 	return (0);
499 }
500