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.
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.
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]
22 * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
24 /* Copyright (c) 1990 Mentat Inc. */
33 #include <sys/types.h>
35 #include <inet/tunables.h>
40 #include <libdllink.h>
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
, ...);
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 ...]";
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
62 typedef struct ndd2ipadm_map
{
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
,
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
,
91 { "tcp_smallest_nonpriv_port", "smallest_nonpriv_port", MOD_PROTO_TCP
,
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
,
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
,
105 { "udp_smallest_nonpriv_port", "smallest_nonpriv_port", MOD_PROTO_UDP
,
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
,
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
,
119 { "sctp_smallest_nonpriv_port", "smallest_nonpriv_port", MOD_PROTO_SCTP
,
121 { NULL
, NULL
, 0, 0, 0 }
125 ndd_str2proto(const char *protostr
)
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
);
144 return (MOD_PROTO_NONE
);
148 ndd_perm2str(uint_t 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");
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.
167 * - convert any new ipadm name to old ndd name using the table.
168 * For example: `sack' --> `tcp_sack_permitted'.
170 * - replace leading underscores with protocol name.
171 * For example: `_strong_iss' --> `tcp_strong_iss'
173 * - don't print new public properties that are supported only by ipadm(1M)
174 * For example: `hostmodel' should be supported only from ipadm(1M).
175 * Such properties are identified by not having leading '_' and not
176 * being present in the mapping table.
179 print_ipadm2ndd(char *oldbuf
, uint_t obufsize
)
181 ndd2ipadm_map_t
*nimap
;
182 char *pname
, *rwtag
, *protostr
;
187 while (pname
[0] && pname
< (oldbuf
+ obufsize
- 1)) {
188 for (protostr
= pname
; !isspace(*protostr
); protostr
++)
191 /* protostr now points to protocol */
193 for (rwtag
= protostr
; !isspace(*rwtag
); rwtag
++)
196 /* rwtag now points to permissions */
198 proto
= atoi(protostr
);
201 for (nimap
= map
; nimap
->ndd_name
!= NULL
; nimap
++) {
202 if (strcmp(pname
, nimap
->ipadm_name
) != 0 ||
203 !(nimap
->ipadm_proto
& proto
))
207 if (nimap
->ndd_perm
!= 0)
208 perm
= nimap
->ndd_perm
;
209 (void) printf("%-30s (%s)\n", nimap
->ndd_name
,
213 * print only if it's a private property. We should
214 * not be printing any new public property in ndd(1M)
217 if (!matched
&& pname
[0] == '_') {
221 err
= ipadm_new2legacy_propname(pname
, tmpstr
,
222 sizeof (tmpstr
), proto
);
225 (void) printf("%-30s (%s)\n", tmpstr
,
228 for (pname
= rwtag
; *pname
++; )
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(1M) always
238 * printed numberals. This flag will help in avoiding printing strings.
241 do_ipadm_getset(int cmd
, char *buf
, int buflen
)
243 ndd2ipadm_map_t
*nimap
;
244 ipadm_handle_t iph
= NULL
;
245 ipadm_status_t status
;
247 uint_t proto
, perm
= 0, flags
= 0;
248 char *pname
, *pvalp
, nname
[512];
251 if ((mod
= strrchr(modpath
, '/')) == NULL
)
255 if ((proto
= ndd_str2proto(mod
)) == MOD_PROTO_NONE
)
258 if ((status
= ipadm_open(&iph
, IPH_LEGACY
)) != IPADM_SUCCESS
)
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
;
272 if (nimap
->ndd_name
== NULL
&& strcmp(pname
, "?") != 0) {
273 /* do not allow set/get of public properties from ndd(1M) */
274 if (ipadm_legacy2new_propname(pname
, nname
, sizeof (nname
),
276 status
= IPADM_PROP_UNKNOWN
;
284 char propval
[MAXPROPVALLEN
], allprop
[64536];
286 sa_family_t af
= AF_UNSPEC
;
289 if (perm
== MOD_PROP_PERM_WRITE
)
290 fatal("operation failed: Permission denied");
292 if (strcmp(pname
, "?") == 0) {
294 pvalsz
= sizeof (allprop
);
297 pvalsz
= sizeof (propval
);
300 status
= ipadm_get_prop(iph
, pname
, pvalp
, &pvalsz
, proto
,
302 if (status
!= IPADM_SUCCESS
)
305 if (strcmp(pname
, "?") == 0) {
306 (void) print_ipadm2ndd(pvalp
, pvalsz
);
311 * For backward compatibility if there are multiple
312 * values print each value in it's own line.
314 while (*tmp
!= '\0') {
319 (void) printf("%s\n", pvalp
);
321 (void) fflush(stdout
);
323 if (perm
== MOD_PROP_PERM_READ
)
324 fatal("operation failed: Permission denied");
326 /* walk past the property name to find the property value */
327 for (i
= 0; buf
[i
] != '\0'; i
++)
331 status
= ipadm_set_prop(iph
, pname
, pvalp
, proto
,
332 flags
|IPADM_OPT_ACTIVE
);
336 if (status
!= IPADM_SUCCESS
)
337 fatal("operation failed: %s", ipadm_status2str(status
));
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.
348 gldv3_warning(char *module
)
350 datalink_id_t linkid
;
351 dladm_status_t status
;
352 char buf
[DLADM_PROP_VAL_MAX
], *cp
;
355 dladm_handle_t handle
;
357 link
= strrchr(module
, '/');
361 if (dladm_open(&handle
) != DLADM_STATUS_OK
)
364 status
= dladm_name2info(handle
, ++link
, &linkid
, NULL
, NULL
, NULL
);
365 if (status
== DLADM_STATUS_OK
) {
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(1M) to manage datalink tunables.\n"));
382 main(int argc
, char **argv
)
384 char *cp
, *value
, *mod
;
388 if (!(cp
= *++argv
)) {
389 while ((fd
= open_device()) != -1) {
390 getset_interactive(fd
);
393 return (EXIT_SUCCESS
);
398 if (strncmp(&cp
[1], "set", 3) == 0)
400 else if (strncmp(&cp
[1], "get", 3) != 0)
408 mod
= strrchr(cp
, '/');
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
));
418 fatal("%s is not a streams device", cp
);
421 (void) strlcpy(modpath
, cp
, sizeof (modpath
));
422 if (!(cp
= *++argv
)) {
423 getset_interactive(fd
);
425 return (EXIT_SUCCESS
);
429 if (!(value
= *++argv
))
431 (void) snprintf(gbuf
, sizeof (gbuf
), "%s%c%s%c", cp
, '\0',
433 if (!do_getset(fd
, cmd
, gbuf
, sizeof (gbuf
)))
434 return (EXIT_FAILURE
);
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
);
442 (void) putchar('\n');
447 return (EXIT_SUCCESS
);
451 name_print(char *buf
)
455 for (cp
= buf
; cp
[0]; ) {
456 for (rwtag
= cp
; !isspace(*rwtag
); rwtag
++)
459 while (isspace(*rwtag
))
461 (void) printf("%-30s%s\n", cp
, rwtag
);
462 for (cp
= rwtag
; *cp
++; )
468 * This function is vile, but it's better here than in the kernel.
471 is_obsolete(const char *param
)
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
);
484 do_getset(int fd
, int cmd
, char *buf
, int buf_len
)
487 struct strioctl stri
;
488 boolean_t is_name_get
;
490 if (is_obsolete(buf
))
494 * See if libipadm can handle this request, i.e., properties on
495 * following modules arp, ip, ipv4, ipv6, tcp, udp and sctp
497 if (do_ipadm_getset(cmd
, buf
, buf_len
))
502 stri
.ic_len
= buf_len
;
504 is_name_get
= stri
.ic_cmd
== ND_GET
&& buf
[0] == '?' && buf
[1] == '\0';
506 if (ioctl(fd
, I_STR
, &stri
) == -1) {
508 (void) printf("name is non-existent for this module\n"
509 "for a list of valid names, use name '?'\n");
511 (void) printf("operation failed: %s\n", errmsg(errno
));
516 else if (stri
.ic_cmd
== ND_GET
) {
517 for (cp
= buf
; *cp
!= '\0'; cp
+= strlen(cp
) + 1)
520 (void) fflush(stdout
);
525 get_value(char *msg
, char *buf
, int buf_len
)
529 (void) printf("%s", msg
);
530 (void) fflush(stdout
);
532 buf
[buf_len
-1] = '\0';
533 if (fgets(buf
, buf_len
-1, stdin
) == NULL
)
536 if (buf
[len
-1] == '\n')
544 getset_interactive(int fd
)
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'))
556 for (cp
= gbuf
; cp
< &gbuf
[len
]; cp
++) {
561 if (gbuf
[0] != '?' &&
562 get_value("value ? ", &gbuf
[len
], sizeof (gbuf
) - len
) > 1)
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");
570 buf_len
= atoi(len_buf
);
572 buf_len
= sizeof (gbuf
);
573 (void) do_getset(fd
, cmd
, gbuf
, buf_len
);
578 printe(boolean_t print_errno
, char *fmt
, ...)
584 (void) printf("*ERROR* ");
585 (void) vprintf(fmt
, ap
);
589 (void) printf(": %s\n", errmsg(error
));
601 len
= get_value("module to query ? ", modpath
,
604 (len
== 2 && (modpath
[0] == 'q' || modpath
[0] == 'Q')))
607 mod
= strrchr(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
);
621 gldv3_warning(modpath
);
627 printe(B_FALSE
, "%s is not a streams device", modpath
);
632 fatal(char *fmt
, ...)
637 (void) vfprintf(stderr
, fmt
, ap
);
639 (void) fprintf(stderr
, "\n");
647 char *msg
= strerror(error
);
649 return (msg
!= NULL
? msg
: "unknown error");