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
ndd_str2proto(const char * protostr)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 *
ndd_perm2str(uint_t perm)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
print_ipadm2ndd(char * oldbuf,uint_t obufsize)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
do_ipadm_getset(int cmd,char * buf,int buflen)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
gldv3_warning(char * module)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
main(int argc,char ** argv)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
name_print(char * buf)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
is_obsolete(const char * param)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
do_getset(int fd,int cmd,char * buf,int buf_len)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
get_value(char * msg,char * buf,int buf_len)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
getset_interactive(int fd)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
printe(boolean_t print_errno,char * fmt,...)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
open_device()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
fatal(char * fmt,...)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 *
errmsg(int error)645 errmsg(int error)
646 {
647 char *msg = strerror(error);
648
649 return (msg != NULL ? msg : "unknown error");
650 }
651