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 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 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 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 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 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 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 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 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 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 }