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) 1991, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 /* Copyright (c) 1990 Mentat Inc. */ 25 26 #include <assert.h> 27 #include <stdio.h> 28 #include <errno.h> 29 #include <ctype.h> 30 #include <stdarg.h> 31 #include <fcntl.h> 32 #include <unistd.h> 33 #include <sys/types.h> 34 #include <stropts.h> 35 #include <inet/tunables.h> 36 #include <inet/nd.h> 37 #include <string.h> 38 #include <strings.h> 39 #include <stdlib.h> 40 #include <libdllink.h> 41 #include <libintl.h> 42 #include <libipadm.h> 43 44 static boolean_t do_getset(int fd, int cmd, char *buf, int buf_len); 45 static int get_value(char *msg, char *buf, int buf_len); 46 static void name_print(char *buf); 47 static void getset_interactive(int fd); 48 static int open_device(void); 49 static char *errmsg(int err); 50 static void fatal(char *fmt, ...); 51 static void printe(boolean_t print_errno, char *fmt, ...); 52 53 static char modpath[128]; /* path to module */ 54 static char gbuf[65536]; /* need large buffer to retrieve all names */ 55 static char usage_str[] = "usage: ndd -set device_name name value\n" 56 " ndd [-get] device_name name [name ...]"; 57 58 /* 59 * Maps old ndd_name to the new ipadm_name. Any ndd property that is moved to 60 * libipadm should have an entry here to ensure backward compatibility 61 */ 62 typedef struct ndd2ipadm_map { 63 char *ndd_name; 64 char *ipadm_name; 65 uint_t ipadm_proto; 66 uint_t ipadm_flags; 67 uint_t ndd_perm; 68 } ndd2ipadm_map_t; 69 70 static ndd2ipadm_map_t map[] = { 71 { "ip_def_ttl", "ttl", MOD_PROTO_IPV4, 0, 0 }, 72 { "ip6_def_hops", "hoplimit", MOD_PROTO_IPV6, 0, 0 }, 73 { "ip_forwarding", "forwarding", MOD_PROTO_IPV4, 0, 0 }, 74 { "ip6_forwarding", "forwarding", MOD_PROTO_IPV6, 0, 0 }, 75 { "icmp_recv_hiwat", "recv_maxbuf", MOD_PROTO_RAWIP, 0, 0 }, 76 { "icmp_xmit_hiwat", "send_maxbuf", MOD_PROTO_RAWIP, 0, 0 }, 77 { "tcp_ecn_permitted", "ecn", MOD_PROTO_TCP, 0, 0 }, 78 { "tcp_extra_priv_ports_add", "extra_priv_ports", MOD_PROTO_TCP, 79 IPADM_OPT_APPEND, MOD_PROP_PERM_WRITE }, 80 { "tcp_extra_priv_ports_del", "extra_priv_ports", MOD_PROTO_TCP, 81 IPADM_OPT_REMOVE, MOD_PROP_PERM_WRITE }, 82 { "tcp_extra_priv_ports", "extra_priv_ports", MOD_PROTO_TCP, 83 0, MOD_PROP_PERM_READ }, 84 { "tcp_largest_anon_port", "largest_anon_port", MOD_PROTO_TCP, 85 0, 0 }, 86 { "tcp_recv_hiwat", "recv_maxbuf", MOD_PROTO_TCP, 0, 0 }, 87 { "tcp_sack_permitted", "sack", MOD_PROTO_TCP, 0, 0 }, 88 { "tcp_xmit_hiwat", "send_maxbuf", MOD_PROTO_TCP, 0, 0 }, 89 { "tcp_smallest_anon_port", "smallest_anon_port", MOD_PROTO_TCP, 90 0, 0 }, 91 { "tcp_smallest_nonpriv_port", "smallest_nonpriv_port", MOD_PROTO_TCP, 92 0, 0 }, 93 { "udp_extra_priv_ports_add", "extra_priv_ports", MOD_PROTO_UDP, 94 IPADM_OPT_APPEND, MOD_PROP_PERM_WRITE }, 95 { "udp_extra_priv_ports_del", "extra_priv_ports", MOD_PROTO_UDP, 96 IPADM_OPT_REMOVE, MOD_PROP_PERM_WRITE }, 97 { "udp_extra_priv_ports", "extra_priv_ports", MOD_PROTO_UDP, 98 0, MOD_PROP_PERM_READ }, 99 { "udp_largest_anon_port", "largest_anon_port", MOD_PROTO_UDP, 100 0, 0 }, 101 { "udp_recv_hiwat", "recv_maxbuf", MOD_PROTO_UDP, 0, 0 }, 102 { "udp_xmit_hiwat", "send_maxbuf", MOD_PROTO_UDP, 0, 0 }, 103 { "udp_smallest_anon_port", "smallest_anon_port", MOD_PROTO_UDP, 104 0, 0 }, 105 { "udp_smallest_nonpriv_port", "smallest_nonpriv_port", MOD_PROTO_UDP, 106 0, 0 }, 107 { "sctp_extra_priv_ports_add", "extra_priv_ports", MOD_PROTO_SCTP, 108 IPADM_OPT_APPEND, MOD_PROP_PERM_WRITE }, 109 { "sctp_extra_priv_ports_del", "extra_priv_ports", MOD_PROTO_SCTP, 110 IPADM_OPT_REMOVE, MOD_PROP_PERM_WRITE }, 111 { "sctp_extra_priv_ports", "extra_priv_ports", MOD_PROTO_SCTP, 112 0, MOD_PROP_PERM_READ }, 113 { "sctp_largest_anon_port", "largest_anon_port", MOD_PROTO_SCTP, 114 0, 0 }, 115 { "sctp_recv_hiwat", "recv_maxbuf", MOD_PROTO_SCTP, 0, 0 }, 116 { "sctp_xmit_hiwat", "send_maxbuf", MOD_PROTO_SCTP, 0, 0 }, 117 { "sctp_smallest_anon_port", "smallest_anon_port", MOD_PROTO_SCTP, 118 0, 0 }, 119 { "sctp_smallest_nonpriv_port", "smallest_nonpriv_port", MOD_PROTO_SCTP, 120 0, 0 }, 121 { NULL, NULL, 0, 0, 0 } 122 }; 123 124 static uint_t 125 ndd_str2proto(const char *protostr) 126 { 127 if (strcmp(protostr, "tcp") == 0 || 128 strcmp(protostr, "tcp6") == 0) { 129 return (MOD_PROTO_TCP); 130 } else if (strcmp(protostr, "udp") == 0 || 131 strcmp(protostr, "udp6") == 0) { 132 return (MOD_PROTO_UDP); 133 } else if (strcmp(protostr, "ip") == 0 || 134 strcmp(protostr, "ip6") == 0 || 135 strcmp(protostr, "arp") == 0) { 136 return (MOD_PROTO_IP); 137 } else if (strcmp(protostr, "icmp") == 0 || 138 strcmp(protostr, "icmp6") == 0) { 139 return (MOD_PROTO_RAWIP); 140 } else if (strcmp(protostr, "sctp") == 0 || 141 strcmp(protostr, "sctp6") == 0) { 142 return (MOD_PROTO_SCTP); 143 } 144 return (MOD_PROTO_NONE); 145 } 146 147 static char * 148 ndd_perm2str(uint_t perm) 149 { 150 switch (perm) { 151 case MOD_PROP_PERM_READ: 152 return ("read only"); 153 case MOD_PROP_PERM_WRITE: 154 return ("write only"); 155 case MOD_PROP_PERM_RW: 156 return ("read and write"); 157 } 158 159 return (NULL); 160 } 161 162 /* 163 * Print all the protocol properties for the given protocol name. The kernel 164 * returns all the properties for the given protocol therefore we have to 165 * apply some filters before we print them. 166 * 167 * - convert any new ipadm name to old ndd name using the table. 168 * For example: `sack' --> `tcp_sack_permitted'. 169 * 170 * - replace leading underscores with protocol name. 171 * For example: `_strong_iss' --> `tcp_strong_iss' 172 * 173 * - don't print new public properties that are supported only by ipadm(8) 174 * For example: `hostmodel' should be supported only from ipadm(8). 175 * Such properties are identified by not having leading '_' and not 176 * being present in the mapping table. 177 */ 178 static void 179 print_ipadm2ndd(char *oldbuf, uint_t obufsize) 180 { 181 ndd2ipadm_map_t *nimap; 182 char *pname, *rwtag, *protostr; 183 uint_t proto, perm; 184 boolean_t matched; 185 186 pname = oldbuf; 187 while (pname[0] && pname < (oldbuf + obufsize - 1)) { 188 for (protostr = pname; !isspace(*protostr); protostr++) 189 ; 190 *protostr++ = '\0'; 191 /* protostr now points to protocol */ 192 193 for (rwtag = protostr; !isspace(*rwtag); rwtag++) 194 ; 195 *rwtag++ = '\0'; 196 /* rwtag now points to permissions */ 197 198 proto = atoi(protostr); 199 perm = atoi(rwtag); 200 matched = B_FALSE; 201 for (nimap = map; nimap->ndd_name != NULL; nimap++) { 202 if (strcmp(pname, nimap->ipadm_name) != 0 || 203 !(nimap->ipadm_proto & proto)) 204 continue; 205 206 matched = B_TRUE; 207 if (nimap->ndd_perm != 0) 208 perm = nimap->ndd_perm; 209 (void) printf("%-30s (%s)\n", nimap->ndd_name, 210 ndd_perm2str(perm)); 211 } 212 /* 213 * print only if it's a private property. We should 214 * not be printing any new public property in ndd(8) 215 * output. 216 */ 217 if (!matched && pname[0] == '_') { 218 char tmpstr[512]; 219 int err; 220 221 err = ipadm_new2legacy_propname(pname, tmpstr, 222 sizeof (tmpstr), proto); 223 assert(err != -1); 224 225 (void) printf("%-30s (%s)\n", tmpstr, 226 ndd_perm2str(perm)); 227 } 228 for (pname = rwtag; *pname++; ) 229 ; 230 } 231 } 232 233 /* 234 * get/set the value for a given property by calling into libipadm. The 235 * IPH_LEGACY flag is used by libipadm for special handling. For some 236 * properties, libipadm.so displays strings (for e.g., on/off, 237 * never/passive/active, et al) instead of numerals. However ndd(8) always 238 * printed numberals. This flag will help in avoiding printing strings. 239 */ 240 static boolean_t 241 do_ipadm_getset(int cmd, char *buf, int buflen) 242 { 243 ndd2ipadm_map_t *nimap; 244 ipadm_handle_t iph = NULL; 245 ipadm_status_t status; 246 char *mod; 247 uint_t proto, perm = 0, flags = 0; 248 char *pname, *pvalp, nname[512]; 249 int i; 250 251 if ((mod = strrchr(modpath, '/')) == NULL) 252 mod = modpath; 253 else 254 ++mod; 255 if ((proto = ndd_str2proto(mod)) == MOD_PROTO_NONE) 256 return (B_FALSE); 257 258 if ((status = ipadm_open(&iph, IPH_LEGACY)) != IPADM_SUCCESS) 259 goto fail; 260 261 pname = buf; 262 for (nimap = map; nimap->ndd_name != NULL; nimap++) { 263 if (strcmp(pname, nimap->ndd_name) == 0) { 264 pname = nimap->ipadm_name; 265 proto = nimap->ipadm_proto; 266 flags = nimap->ipadm_flags; 267 perm = nimap->ndd_perm; 268 break; 269 } 270 } 271 272 if (nimap->ndd_name == NULL && strcmp(pname, "?") != 0) { 273 /* do not allow set/get of public properties from ndd(8) */ 274 if (ipadm_legacy2new_propname(pname, nname, sizeof (nname), 275 &proto) != 0) { 276 status = IPADM_PROP_UNKNOWN; 277 goto fail; 278 } else { 279 pname = nname; 280 } 281 } 282 283 if (cmd == ND_GET) { 284 char propval[MAXPROPVALLEN], allprop[64536]; 285 uint_t pvalsz; 286 sa_family_t af = AF_UNSPEC; 287 int err; 288 289 if (perm == MOD_PROP_PERM_WRITE) 290 fatal("operation failed: Permission denied"); 291 292 if (strcmp(pname, "?") == 0) { 293 pvalp = allprop; 294 pvalsz = sizeof (allprop); 295 } else { 296 pvalp = propval; 297 pvalsz = sizeof (propval); 298 } 299 300 status = ipadm_get_prop(iph, pname, pvalp, &pvalsz, proto, 301 IPADM_OPT_ACTIVE); 302 if (status != IPADM_SUCCESS) 303 goto fail; 304 305 if (strcmp(pname, "?") == 0) { 306 (void) print_ipadm2ndd(pvalp, pvalsz); 307 } else { 308 char *tmp = pvalp; 309 310 /* 311 * For backward compatibility if there are multiple 312 * values print each value in it's own line. 313 */ 314 while (*tmp != '\0') { 315 if (*tmp == ',') 316 *tmp = '\n'; 317 tmp++; 318 } 319 (void) printf("%s\n", pvalp); 320 } 321 (void) fflush(stdout); 322 } else { 323 if (perm == MOD_PROP_PERM_READ) 324 fatal("operation failed: Permission denied"); 325 326 /* walk past the property name to find the property value */ 327 for (i = 0; buf[i] != '\0'; i++) 328 ; 329 330 pvalp = &buf[++i]; 331 status = ipadm_set_prop(iph, pname, pvalp, proto, 332 flags|IPADM_OPT_ACTIVE); 333 } 334 fail: 335 ipadm_close(iph); 336 if (status != IPADM_SUCCESS) 337 fatal("operation failed: %s", ipadm_status2str(status)); 338 return (B_TRUE); 339 } 340 341 /* 342 * gldv3_warning() catches the case of /sbin/ndd abuse to administer 343 * ethernet/MII props. Note that /sbin/ndd has not been abused 344 * for administration of other datalink types, which makes it permissible 345 * to test for support of the flowctrl property. 346 */ 347 static void 348 gldv3_warning(char *module) 349 { 350 datalink_id_t linkid; 351 dladm_status_t status; 352 char buf[DLADM_PROP_VAL_MAX], *cp; 353 uint_t cnt = 1; 354 char *link; 355 dladm_handle_t handle; 356 357 link = strrchr(module, '/'); 358 if (link == NULL) 359 return; 360 361 if (dladm_open(&handle) != DLADM_STATUS_OK) 362 return; 363 364 status = dladm_name2info(handle, ++link, &linkid, NULL, NULL, NULL); 365 if (status == DLADM_STATUS_OK) { 366 cp = buf; 367 status = dladm_get_linkprop(handle, linkid, 368 DLADM_PROP_VAL_CURRENT, "flowctrl", &cp, &cnt); 369 if (status == DLADM_STATUS_OK) { 370 (void) fprintf(stderr, gettext( 371 "WARNING: The ndd commands for datalink " 372 "administration are obsolete and may be " 373 "removed in a future release of Solaris. " 374 "Use dladm(8) to manage datalink tunables.\n")); 375 } 376 } 377 dladm_close(handle); 378 } 379 380 /* ARGSUSED */ 381 int 382 main(int argc, char **argv) 383 { 384 char *cp, *value, *mod; 385 int cmd; 386 int fd = 0; 387 388 if (!(cp = *++argv)) { 389 while ((fd = open_device()) != -1) { 390 getset_interactive(fd); 391 (void) close(fd); 392 } 393 return (EXIT_SUCCESS); 394 } 395 396 cmd = ND_GET; 397 if (cp[0] == '-') { 398 if (strncmp(&cp[1], "set", 3) == 0) 399 cmd = ND_SET; 400 else if (strncmp(&cp[1], "get", 3) != 0) 401 fatal(usage_str); 402 if (!(cp = *++argv)) 403 fatal(usage_str); 404 } 405 406 gldv3_warning(cp); 407 408 mod = strrchr(cp, '/'); 409 if (mod != NULL) 410 mod++; 411 else 412 mod = cp; 413 414 if (ndd_str2proto(mod) == MOD_PROTO_NONE) { 415 if ((fd = open(cp, O_RDWR)) == -1) 416 fatal("open of %s failed: %s", cp, errmsg(errno)); 417 if (!isastream(fd)) 418 fatal("%s is not a streams device", cp); 419 } 420 421 (void) strlcpy(modpath, cp, sizeof (modpath)); 422 if (!(cp = *++argv)) { 423 getset_interactive(fd); 424 (void) close(fd); 425 return (EXIT_SUCCESS); 426 } 427 428 if (cmd == ND_SET) { 429 if (!(value = *++argv)) 430 fatal(usage_str); 431 (void) snprintf(gbuf, sizeof (gbuf), "%s%c%s%c", cp, '\0', 432 value, '\0'); 433 if (!do_getset(fd, cmd, gbuf, sizeof (gbuf))) 434 return (EXIT_FAILURE); 435 } else { 436 do { 437 (void) memset(gbuf, '\0', sizeof (gbuf)); 438 (void) strlcpy(gbuf, cp, sizeof (gbuf)); 439 if (!do_getset(fd, cmd, gbuf, sizeof (gbuf))) 440 return (EXIT_FAILURE); 441 if (cp = *++argv) 442 (void) putchar('\n'); 443 } while (cp); 444 } 445 446 (void) close(fd); 447 return (EXIT_SUCCESS); 448 } 449 450 static void 451 name_print(char *buf) 452 { 453 char *cp, *rwtag; 454 455 for (cp = buf; cp[0]; ) { 456 for (rwtag = cp; !isspace(*rwtag); rwtag++) 457 ; 458 *rwtag++ = '\0'; 459 while (isspace(*rwtag)) 460 rwtag++; 461 (void) printf("%-30s%s\n", cp, rwtag); 462 for (cp = rwtag; *cp++; ) 463 ; 464 } 465 } 466 467 /* 468 * This function is vile, but it's better here than in the kernel. 469 */ 470 static boolean_t 471 is_obsolete(const char *param) 472 { 473 if (strcmp(param, "ip_enable_group_ifs") == 0 || 474 strcmp(param, "ifgrp_status") == 0) { 475 (void) fprintf(stderr, "The \"%s\" tunable has been superseded " 476 "by IP Multipathing.\nPlease see the IP Network " 477 "Multipathing Administration Guide for details.\n", param); 478 return (B_TRUE); 479 } 480 return (B_FALSE); 481 } 482 483 static boolean_t 484 do_getset(int fd, int cmd, char *buf, int buf_len) 485 { 486 char *cp; 487 struct strioctl stri; 488 boolean_t is_name_get; 489 490 if (is_obsolete(buf)) 491 return (B_TRUE); 492 493 /* 494 * See if libipadm can handle this request, i.e., properties on 495 * following modules arp, ip, ipv4, ipv6, tcp, udp and sctp 496 */ 497 if (do_ipadm_getset(cmd, buf, buf_len)) 498 return (B_TRUE); 499 500 stri.ic_cmd = cmd; 501 stri.ic_timout = 0; 502 stri.ic_len = buf_len; 503 stri.ic_dp = buf; 504 is_name_get = stri.ic_cmd == ND_GET && buf[0] == '?' && buf[1] == '\0'; 505 506 if (ioctl(fd, I_STR, &stri) == -1) { 507 if (errno == ENOENT) 508 (void) printf("name is non-existent for this module\n" 509 "for a list of valid names, use name '?'\n"); 510 else 511 (void) printf("operation failed: %s\n", errmsg(errno)); 512 return (B_FALSE); 513 } 514 if (is_name_get) 515 name_print(buf); 516 else if (stri.ic_cmd == ND_GET) { 517 for (cp = buf; *cp != '\0'; cp += strlen(cp) + 1) 518 (void) puts(cp); 519 } 520 (void) fflush(stdout); 521 return (B_TRUE); 522 } 523 524 static int 525 get_value(char *msg, char *buf, int buf_len) 526 { 527 int len; 528 529 (void) printf("%s", msg); 530 (void) fflush(stdout); 531 532 buf[buf_len-1] = '\0'; 533 if (fgets(buf, buf_len-1, stdin) == NULL) 534 exit(EXIT_SUCCESS); 535 len = strlen(buf); 536 if (buf[len-1] == '\n') 537 buf[len - 1] = '\0'; 538 else 539 len++; 540 return (len); 541 } 542 543 static void 544 getset_interactive(int fd) 545 { 546 int cmd; 547 char *cp; 548 int len, buf_len; 549 char len_buf[10]; 550 551 for (;;) { 552 (void) memset(gbuf, '\0', sizeof (gbuf)); 553 len = get_value("name to get/set ? ", gbuf, sizeof (gbuf)); 554 if (len == 1 || (gbuf[0] == 'q' && gbuf[1] == '\0')) 555 return; 556 for (cp = gbuf; cp < &gbuf[len]; cp++) { 557 if (isspace(*cp)) 558 *cp = '\0'; 559 } 560 cmd = ND_GET; 561 if (gbuf[0] != '?' && 562 get_value("value ? ", &gbuf[len], sizeof (gbuf) - len) > 1) 563 cmd = ND_SET; 564 if (cmd == ND_GET && gbuf[0] != '?' && 565 get_value("length ? ", len_buf, sizeof (len_buf)) > 1) { 566 if (!isdigit(len_buf[0])) { 567 (void) printf("invalid length\n"); 568 continue; 569 } 570 buf_len = atoi(len_buf); 571 } else 572 buf_len = sizeof (gbuf); 573 (void) do_getset(fd, cmd, gbuf, buf_len); 574 } 575 } 576 577 static void 578 printe(boolean_t print_errno, char *fmt, ...) 579 { 580 va_list ap; 581 int error = errno; 582 583 va_start(ap, fmt); 584 (void) printf("*ERROR* "); 585 (void) vprintf(fmt, ap); 586 va_end(ap); 587 588 if (print_errno) 589 (void) printf(": %s\n", errmsg(error)); 590 else 591 (void) printf("\n"); 592 } 593 594 static int 595 open_device() 596 { 597 int fd, len; 598 char *mod; 599 600 for (;;) { 601 len = get_value("module to query ? ", modpath, 602 sizeof (modpath)); 603 if (len <= 1 || 604 (len == 2 && (modpath[0] == 'q' || modpath[0] == 'Q'))) 605 return (-1); 606 607 mod = strrchr(modpath, '/'); 608 if (mod != NULL) 609 mod++; 610 else 611 mod = modpath; 612 if (ndd_str2proto(mod) == MOD_PROTO_NONE) { 613 if ((fd = open(modpath, O_RDWR)) == -1) { 614 printe(B_TRUE, "open of %s failed", modpath); 615 continue; 616 } 617 } else { 618 return (0); 619 } 620 621 gldv3_warning(modpath); 622 623 if (isastream(fd)) 624 return (fd); 625 626 (void) close(fd); 627 printe(B_FALSE, "%s is not a streams device", modpath); 628 } 629 } 630 631 static void 632 fatal(char *fmt, ...) 633 { 634 va_list ap; 635 636 va_start(ap, fmt); 637 (void) vfprintf(stderr, fmt, ap); 638 va_end(ap); 639 (void) fprintf(stderr, "\n"); 640 641 exit(EXIT_FAILURE); 642 } 643 644 static char * 645 errmsg(int error) 646 { 647 char *msg = strerror(error); 648 649 return (msg != NULL ? msg : "unknown error"); 650 } 651