xref: /titanic_44/usr/src/lib/libipadm/common/ipadm_prop.c (revision 5fd03bc0f2e00e7ba02316c2e08f45d52aab15db)
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) 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright (c) 2013 by Delphix. All rights reserved.
24  */
25 
26 /*
27  * This file contains routines that are used to modify/retrieve protocol or
28  * interface property values. It also holds all the supported properties for
29  * both IP interface and protocols in `ipadm_prop_desc_t'. Following protocols
30  * are supported: IP, IPv4, IPv6, TCP, SCTP, UDP and ICMP.
31  *
32  * This file also contains walkers, which walks through the property table and
33  * calls the callback function, of the form `ipadm_prop_wfunc_t' , for every
34  * property in the table.
35  */
36 
37 #include <unistd.h>
38 #include <errno.h>
39 #include <ctype.h>
40 #include <fcntl.h>
41 #include <strings.h>
42 #include <stdlib.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <sys/sockio.h>
46 #include <assert.h>
47 #include <libdllink.h>
48 #include <zone.h>
49 #include "libipadm_impl.h"
50 #include <inet/tunables.h>
51 
52 #define	IPADM_NONESTR		"none"
53 #define	DEF_METRIC_VAL		0	/* default metric value */
54 
55 #define	A_CNT(arr)	(sizeof (arr) / sizeof (arr[0]))
56 
57 static ipadm_status_t	i_ipadm_validate_if(ipadm_handle_t, const char *,
58 			    uint_t, uint_t);
59 
60 /*
61  * Callback functions to retrieve property values from the kernel. These
62  * functions, when required, translate the values from the kernel to a format
63  * suitable for printing. For example: boolean values will be translated
64  * to on/off. They also retrieve DEFAULT, PERM and POSSIBLE values for
65  * a given property.
66  */
67 static ipadm_pd_getf_t	i_ipadm_get_prop, i_ipadm_get_ifprop_flags,
68 			i_ipadm_get_mtu, i_ipadm_get_metric,
69 			i_ipadm_get_usesrc, i_ipadm_get_forwarding,
70 			i_ipadm_get_ecnsack, i_ipadm_get_hostmodel;
71 
72 /*
73  * Callback function to set property values. These functions translate the
74  * values to a format suitable for kernel consumption, allocates the necessary
75  * ioctl buffers and then invokes ioctl().
76  */
77 static ipadm_pd_setf_t	i_ipadm_set_prop, i_ipadm_set_mtu,
78 			i_ipadm_set_ifprop_flags,
79 			i_ipadm_set_metric, i_ipadm_set_usesrc,
80 			i_ipadm_set_forwarding, i_ipadm_set_eprivport,
81 			i_ipadm_set_ecnsack, i_ipadm_set_hostmodel;
82 
83 /* array of protocols we support */
84 static int protocols[] = { MOD_PROTO_IP, MOD_PROTO_RAWIP,
85 			    MOD_PROTO_TCP, MOD_PROTO_UDP,
86 			    MOD_PROTO_SCTP };
87 
88 /*
89  * Supported IP protocol properties.
90  */
91 static ipadm_prop_desc_t ipadm_ip_prop_table[] = {
92 	{ "arp", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
93 	    i_ipadm_set_ifprop_flags, i_ipadm_get_onoff,
94 	    i_ipadm_get_ifprop_flags },
95 
96 	{ "forwarding", NULL, IPADMPROP_CLASS_MODIF, MOD_PROTO_IPV4, 0,
97 	    i_ipadm_set_forwarding, i_ipadm_get_onoff,
98 	    i_ipadm_get_forwarding },
99 
100 	{ "metric", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
101 	    i_ipadm_set_metric, NULL, i_ipadm_get_metric },
102 
103 	{ "mtu", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
104 	    i_ipadm_set_mtu, i_ipadm_get_mtu, i_ipadm_get_mtu },
105 
106 	{ "exchange_routes", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
107 	    i_ipadm_set_ifprop_flags, i_ipadm_get_onoff,
108 	    i_ipadm_get_ifprop_flags },
109 
110 	{ "usesrc", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
111 	    i_ipadm_set_usesrc, NULL, i_ipadm_get_usesrc },
112 
113 	{ "ttl", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV4, 0,
114 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
115 
116 	{ "forwarding", NULL, IPADMPROP_CLASS_MODIF, MOD_PROTO_IPV6, 0,
117 	    i_ipadm_set_forwarding, i_ipadm_get_onoff,
118 	    i_ipadm_get_forwarding },
119 
120 	{ "hoplimit", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV6, 0,
121 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
122 
123 	{ "metric", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
124 	    i_ipadm_set_metric, NULL, i_ipadm_get_metric },
125 
126 	{ "mtu", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
127 	    i_ipadm_set_mtu, i_ipadm_get_mtu, i_ipadm_get_mtu },
128 
129 	{ "nud", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
130 	    i_ipadm_set_ifprop_flags, i_ipadm_get_onoff,
131 	    i_ipadm_get_ifprop_flags },
132 
133 	{ "exchange_routes", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
134 	    i_ipadm_set_ifprop_flags, i_ipadm_get_onoff,
135 	    i_ipadm_get_ifprop_flags },
136 
137 	{ "usesrc", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
138 	    i_ipadm_set_usesrc, NULL, i_ipadm_get_usesrc },
139 
140 	{ "hostmodel", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV6, 0,
141 	    i_ipadm_set_hostmodel, i_ipadm_get_hostmodel,
142 	    i_ipadm_get_hostmodel },
143 
144 	{ "hostmodel", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV4, 0,
145 	    i_ipadm_set_hostmodel, i_ipadm_get_hostmodel,
146 	    i_ipadm_get_hostmodel },
147 
148 	{ NULL, NULL, 0, 0, 0, NULL, NULL, NULL }
149 };
150 
151 /* possible values for TCP properties `ecn' and `sack' */
152 static const char *ecn_sack_vals[] = {"never", "passive", "active", NULL};
153 
154 /* Supported TCP protocol properties */
155 static ipadm_prop_desc_t ipadm_tcp_prop_table[] = {
156 	{ "ecn", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
157 	    i_ipadm_set_ecnsack, i_ipadm_get_ecnsack, i_ipadm_get_ecnsack },
158 
159 	{ "extra_priv_ports", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP,
160 	    IPADMPROP_MULVAL, i_ipadm_set_eprivport, i_ipadm_get_prop,
161 	    i_ipadm_get_prop },
162 
163 	{ "largest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
164 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
165 
166 	{ "max_buf", "_max_buf", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
167 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
168 
169 	{ "recv_buf", "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
170 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
171 
172 	{ "sack", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
173 	    i_ipadm_set_ecnsack, i_ipadm_get_ecnsack, i_ipadm_get_ecnsack },
174 
175 	{ "send_buf", "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
176 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
177 
178 	{ "smallest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
179 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
180 
181 	{ "smallest_nonpriv_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP,
182 	    0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
183 
184 	{ NULL, NULL, 0, 0, 0, NULL, NULL, NULL }
185 };
186 
187 /* Supported UDP protocol properties */
188 static ipadm_prop_desc_t ipadm_udp_prop_table[] = {
189 	{ "extra_priv_ports", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP,
190 	    IPADMPROP_MULVAL, i_ipadm_set_eprivport, i_ipadm_get_prop,
191 	    i_ipadm_get_prop },
192 
193 	{ "largest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
194 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
195 
196 	{ "max_buf", "_max_buf", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
197 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
198 
199 	{ "recv_buf", "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
200 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
201 
202 	{ "send_buf", "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
203 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
204 
205 	{ "smallest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
206 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
207 
208 	{ "smallest_nonpriv_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP,
209 	    0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
210 
211 	{ NULL, NULL, 0, 0, 0, NULL, NULL, NULL }
212 };
213 
214 /* Supported SCTP protocol properties */
215 static ipadm_prop_desc_t ipadm_sctp_prop_table[] = {
216 	{ "extra_priv_ports", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP,
217 	    IPADMPROP_MULVAL, i_ipadm_set_eprivport, i_ipadm_get_prop,
218 	    i_ipadm_get_prop },
219 
220 	{ "largest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
221 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
222 
223 	{ "max_buf", "_max_buf", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
224 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
225 
226 	{ "recv_buf", "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
227 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
228 
229 	{ "send_buf", "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
230 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
231 
232 	{ "smallest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
233 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
234 
235 	{ "smallest_nonpriv_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP,
236 	    0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
237 
238 	{ NULL, NULL, 0, 0, 0, NULL, NULL, NULL }
239 };
240 
241 /* Supported ICMP protocol properties */
242 static ipadm_prop_desc_t ipadm_icmp_prop_table[] = {
243 	{ "max_buf", "_max_buf", IPADMPROP_CLASS_MODULE, MOD_PROTO_RAWIP, 0,
244 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
245 
246 	{ "recv_buf", "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_RAWIP, 0,
247 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
248 
249 	{ "send_buf", "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_RAWIP, 0,
250 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
251 
252 	{ NULL, NULL, 0, 0, 0, NULL, NULL, NULL }
253 };
254 
255 /*
256  * A dummy private property structure, used while handling private
257  * protocol properties (properties not yet supported by libipadm).
258  */
259 static ipadm_prop_desc_t ipadm_privprop =
260 	{ NULL, NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_NONE, 0,
261 	    i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop };
262 
263 /*
264  * Returns the property description table, for the given protocol
265  */
266 static ipadm_prop_desc_t *
267 i_ipadm_get_propdesc_table(uint_t proto)
268 {
269 	switch (proto) {
270 	case MOD_PROTO_IP:
271 	case MOD_PROTO_IPV4:
272 	case MOD_PROTO_IPV6:
273 		return (ipadm_ip_prop_table);
274 	case MOD_PROTO_RAWIP:
275 		return (ipadm_icmp_prop_table);
276 	case MOD_PROTO_TCP:
277 		return (ipadm_tcp_prop_table);
278 	case MOD_PROTO_UDP:
279 		return (ipadm_udp_prop_table);
280 	case MOD_PROTO_SCTP:
281 		return (ipadm_sctp_prop_table);
282 	}
283 
284 	return (NULL);
285 }
286 
287 static ipadm_prop_desc_t *
288 i_ipadm_get_prop_desc(const char *pname, uint_t proto, int *errp)
289 {
290 	int		err = 0;
291 	boolean_t	matched_name = B_FALSE;
292 	ipadm_prop_desc_t *ipdp = NULL, *ipdtbl;
293 
294 	if ((ipdtbl = i_ipadm_get_propdesc_table(proto)) == NULL) {
295 		err = EINVAL;
296 		goto ret;
297 	}
298 
299 	for (ipdp = ipdtbl; ipdp->ipd_name != NULL; ipdp++) {
300 		if (strcmp(pname, ipdp->ipd_name) == 0 ||
301 		    (ipdp->ipd_old_name != NULL &&
302 		    strcmp(pname, ipdp->ipd_old_name) == 0)) {
303 			matched_name = B_TRUE;
304 			if (ipdp->ipd_proto == proto)
305 				break;
306 		}
307 	}
308 
309 	if (ipdp->ipd_name == NULL) {
310 		err = ENOENT;
311 		/* if we matched name, but failed protocol check */
312 		if (matched_name)
313 			err = EPROTO;
314 		ipdp = NULL;
315 	}
316 ret:
317 	if (errp != NULL)
318 		*errp = err;
319 	return (ipdp);
320 }
321 
322 char *
323 ipadm_proto2str(uint_t proto)
324 {
325 	switch (proto) {
326 	case MOD_PROTO_IP:
327 		return ("ip");
328 	case MOD_PROTO_IPV4:
329 		return ("ipv4");
330 	case MOD_PROTO_IPV6:
331 		return ("ipv6");
332 	case MOD_PROTO_RAWIP:
333 		return ("icmp");
334 	case MOD_PROTO_TCP:
335 		return ("tcp");
336 	case MOD_PROTO_UDP:
337 		return ("udp");
338 	case MOD_PROTO_SCTP:
339 		return ("sctp");
340 	}
341 
342 	return (NULL);
343 }
344 
345 uint_t
346 ipadm_str2proto(const char *protostr)
347 {
348 	if (protostr == NULL)
349 		return (MOD_PROTO_NONE);
350 	if (strcmp(protostr, "tcp") == 0)
351 		return (MOD_PROTO_TCP);
352 	else if (strcmp(protostr, "udp") == 0)
353 		return (MOD_PROTO_UDP);
354 	else if (strcmp(protostr, "ip") == 0)
355 		return (MOD_PROTO_IP);
356 	else if (strcmp(protostr, "ipv4") == 0)
357 		return (MOD_PROTO_IPV4);
358 	else if (strcmp(protostr, "ipv6") == 0)
359 		return (MOD_PROTO_IPV6);
360 	else if (strcmp(protostr, "icmp") == 0)
361 		return (MOD_PROTO_RAWIP);
362 	else if (strcmp(protostr, "sctp") == 0)
363 		return (MOD_PROTO_SCTP);
364 	else if (strcmp(protostr, "arp") == 0)
365 		return (MOD_PROTO_IP);
366 
367 	return (MOD_PROTO_NONE);
368 }
369 
370 /* ARGSUSED */
371 static ipadm_status_t
372 i_ipadm_set_mtu(ipadm_handle_t iph, const void *arg,
373     ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
374 {
375 	struct lifreq	lifr;
376 	char		*endp;
377 	uint_t		mtu;
378 	int		s;
379 	const char	*ifname = arg;
380 	char		val[MAXPROPVALLEN];
381 
382 	/* to reset MTU first retrieve the default MTU and then set it */
383 	if (flags & IPADM_OPT_DEFAULT) {
384 		ipadm_status_t	status;
385 		uint_t		size = MAXPROPVALLEN;
386 
387 		status = i_ipadm_get_prop(iph, arg, pdp, val, &size,
388 		    proto, MOD_PROP_DEFAULT);
389 		if (status != IPADM_SUCCESS)
390 			return (status);
391 		pval = val;
392 	}
393 
394 	errno = 0;
395 	mtu = (uint_t)strtol(pval, &endp, 10);
396 	if (errno != 0 || *endp != '\0')
397 		return (IPADM_INVALID_ARG);
398 
399 	bzero(&lifr, sizeof (lifr));
400 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
401 	lifr.lifr_mtu = mtu;
402 
403 	s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
404 	if (ioctl(s, SIOCSLIFMTU, (caddr_t)&lifr) < 0)
405 		return (ipadm_errno2status(errno));
406 
407 	return (IPADM_SUCCESS);
408 }
409 
410 /* ARGSUSED */
411 static ipadm_status_t
412 i_ipadm_set_metric(ipadm_handle_t iph, const void *arg,
413     ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
414 {
415 	struct lifreq	lifr;
416 	char		*endp;
417 	int		metric;
418 	const char	*ifname = arg;
419 	int		s;
420 
421 	/* if we are resetting, set the value to its default value */
422 	if (flags & IPADM_OPT_DEFAULT) {
423 		metric = DEF_METRIC_VAL;
424 	} else {
425 		errno = 0;
426 		metric = (uint_t)strtol(pval, &endp, 10);
427 		if (errno != 0 || *endp != '\0')
428 			return (IPADM_INVALID_ARG);
429 	}
430 
431 	bzero(&lifr, sizeof (lifr));
432 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
433 	lifr.lifr_metric = metric;
434 
435 	s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
436 
437 	if (ioctl(s, SIOCSLIFMETRIC, (caddr_t)&lifr) < 0)
438 		return (ipadm_errno2status(errno));
439 
440 	return (IPADM_SUCCESS);
441 }
442 
443 /* ARGSUSED */
444 static ipadm_status_t
445 i_ipadm_set_usesrc(ipadm_handle_t iph, const void *arg,
446     ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
447 {
448 	struct lifreq	lifr;
449 	const char	*ifname = arg;
450 	int		s;
451 	uint_t		ifindex = 0;
452 
453 	/* if we are resetting, set the value to its default value */
454 	if (flags & IPADM_OPT_DEFAULT)
455 		pval = IPADM_NONESTR;
456 
457 	/*
458 	 * cannot specify logical interface name. We can also filter out other
459 	 * bogus interface names here itself through i_ipadm_validate_ifname().
460 	 */
461 	if (strcmp(pval, IPADM_NONESTR) != 0 &&
462 	    !i_ipadm_validate_ifname(iph, pval))
463 		return (IPADM_INVALID_ARG);
464 
465 	bzero(&lifr, sizeof (lifr));
466 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
467 
468 	s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
469 
470 	if (strcmp(pval, IPADM_NONESTR) != 0) {
471 		if ((ifindex = if_nametoindex(pval)) == 0)
472 			return (ipadm_errno2status(errno));
473 		lifr.lifr_index = ifindex;
474 	} else {
475 		if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) < 0)
476 			return (ipadm_errno2status(errno));
477 		lifr.lifr_index = 0;
478 	}
479 	if (ioctl(s, SIOCSLIFUSESRC, (caddr_t)&lifr) < 0)
480 		return (ipadm_errno2status(errno));
481 
482 	return (IPADM_SUCCESS);
483 }
484 
485 static struct hostmodel_strval {
486 	char *esm_str;
487 	ip_hostmodel_t esm_val;
488 } esm_arr[] = {
489 	{"weak", IP_WEAK_ES},
490 	{"src-priority", IP_SRC_PRI_ES},
491 	{"strong", IP_STRONG_ES},
492 	{"custom", IP_MAXVAL_ES}
493 };
494 
495 static ip_hostmodel_t
496 i_ipadm_hostmodel_str2val(const char *pval)
497 {
498 	int i;
499 
500 	for (i = 0; i < A_CNT(esm_arr); i++) {
501 		if (esm_arr[i].esm_str != NULL &&
502 		    strcmp(pval, esm_arr[i].esm_str) == 0) {
503 			return (esm_arr[i].esm_val);
504 		}
505 	}
506 	return (IP_MAXVAL_ES);
507 }
508 
509 static char *
510 i_ipadm_hostmodel_val2str(ip_hostmodel_t pval)
511 {
512 	int i;
513 
514 	for (i = 0; i < A_CNT(esm_arr); i++) {
515 		if (esm_arr[i].esm_val == pval)
516 			return (esm_arr[i].esm_str);
517 	}
518 	return (NULL);
519 }
520 
521 /* ARGSUSED */
522 static ipadm_status_t
523 i_ipadm_set_hostmodel(ipadm_handle_t iph, const void *arg,
524     ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
525 {
526 	ip_hostmodel_t hostmodel;
527 	char val[11]; /* covers uint32_max as a string */
528 
529 	if ((flags & IPADM_OPT_DEFAULT) == 0) {
530 		hostmodel = i_ipadm_hostmodel_str2val(pval);
531 		if (hostmodel == IP_MAXVAL_ES)
532 			return (IPADM_INVALID_ARG);
533 		(void) snprintf(val, sizeof (val), "%d", hostmodel);
534 		pval = val;
535 	}
536 	return (i_ipadm_set_prop(iph, NULL, pdp, pval, proto, flags));
537 }
538 
539 /* ARGSUSED */
540 static ipadm_status_t
541 i_ipadm_get_hostmodel(ipadm_handle_t iph, const void *arg,
542     ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
543     uint_t valtype)
544 {
545 	ip_hostmodel_t hostmodel;
546 	char *cp;
547 	size_t nbytes;
548 	ipadm_status_t status;
549 
550 	switch (valtype) {
551 	case MOD_PROP_PERM:
552 		nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
553 		break;
554 	case MOD_PROP_DEFAULT:
555 		nbytes = snprintf(buf, *bufsize, "weak");
556 		break;
557 	case MOD_PROP_ACTIVE:
558 		status = i_ipadm_get_prop(iph, arg, pdp, buf, bufsize, proto,
559 		    valtype);
560 		if (status != IPADM_SUCCESS)
561 			return (status);
562 		bcopy(buf, &hostmodel, sizeof (hostmodel));
563 		cp = i_ipadm_hostmodel_val2str(hostmodel);
564 		nbytes = snprintf(buf, *bufsize, "%s",
565 		    (cp != NULL ? cp : "?"));
566 		break;
567 	case MOD_PROP_POSSIBLE:
568 		nbytes = snprintf(buf, *bufsize, "strong,src-priority,weak");
569 		break;
570 	default:
571 		return (IPADM_INVALID_ARG);
572 	}
573 	if (nbytes >= *bufsize) {
574 		/* insufficient buffer space */
575 		*bufsize = nbytes + 1;
576 		return (IPADM_NO_BUFS);
577 	}
578 	return (IPADM_SUCCESS);
579 }
580 
581 /* ARGSUSED */
582 static ipadm_status_t
583 i_ipadm_set_ifprop_flags(ipadm_handle_t iph, const void *arg,
584     ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
585 {
586 	ipadm_status_t	status = IPADM_SUCCESS;
587 	const char	*ifname = arg;
588 	uint64_t	on_flags = 0, off_flags = 0;
589 	boolean_t	on = B_FALSE;
590 	sa_family_t	af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET);
591 
592 	/* if we are resetting, set the value to its default value */
593 	if (flags & IPADM_OPT_DEFAULT) {
594 		if (strcmp(pdp->ipd_name, "exchange_routes") == 0 ||
595 		    strcmp(pdp->ipd_name, "arp") == 0 ||
596 		    strcmp(pdp->ipd_name, "nud") == 0) {
597 			pval = IPADM_ONSTR;
598 		} else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
599 			pval = IPADM_OFFSTR;
600 		} else {
601 			return (IPADM_PROP_UNKNOWN);
602 		}
603 	}
604 
605 	if (strcmp(pval, IPADM_ONSTR) == 0)
606 		on = B_TRUE;
607 	else if (strcmp(pval, IPADM_OFFSTR) == 0)
608 		on = B_FALSE;
609 	else
610 		return (IPADM_INVALID_ARG);
611 
612 	if (strcmp(pdp->ipd_name, "exchange_routes") == 0) {
613 		if (on)
614 			off_flags = IFF_NORTEXCH;
615 		else
616 			on_flags = IFF_NORTEXCH;
617 	} else if (strcmp(pdp->ipd_name, "arp") == 0) {
618 		if (on)
619 			off_flags = IFF_NOARP;
620 		else
621 			on_flags = IFF_NOARP;
622 	} else if (strcmp(pdp->ipd_name, "nud") == 0) {
623 		if (on)
624 			off_flags = IFF_NONUD;
625 		else
626 			on_flags = IFF_NONUD;
627 	} else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
628 		if (on)
629 			on_flags = IFF_ROUTER;
630 		else
631 			off_flags = IFF_ROUTER;
632 	}
633 
634 	if (on_flags || off_flags)  {
635 		status = i_ipadm_set_flags(iph, ifname, af, on_flags,
636 		    off_flags);
637 	}
638 	return (status);
639 }
640 
641 /* ARGSUSED */
642 static ipadm_status_t
643 i_ipadm_set_eprivport(ipadm_handle_t iph, const void *arg,
644     ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
645 {
646 	nvlist_t	*portsnvl = NULL;
647 	nvpair_t	*nvp;
648 	ipadm_status_t	status = IPADM_SUCCESS;
649 	int		err;
650 	uint_t		count = 0;
651 
652 	if (flags & IPADM_OPT_DEFAULT) {
653 		assert(pval == NULL);
654 		return (i_ipadm_set_prop(iph, arg, pdp, pval, proto, flags));
655 	}
656 
657 	if ((err = ipadm_str2nvlist(pval, &portsnvl, IPADM_NORVAL)) != 0)
658 		return (ipadm_errno2status(err));
659 
660 	/* count the number of ports */
661 	for (nvp = nvlist_next_nvpair(portsnvl, NULL); nvp != NULL;
662 	    nvp = nvlist_next_nvpair(portsnvl, nvp)) {
663 		++count;
664 	}
665 
666 	if (iph->iph_flags & IPH_INIT) {
667 		flags |= IPADM_OPT_APPEND;
668 	} else if (count > 1) {
669 		/*
670 		 * We allow only one port to be added, removed or
671 		 * assigned at a time.
672 		 *
673 		 * However on reboot, while initializing protocol
674 		 * properties, extra_priv_ports might have multiple
675 		 * values. Only in that case we allow setting multiple
676 		 * values.
677 		 */
678 		nvlist_free(portsnvl);
679 		return (IPADM_INVALID_ARG);
680 	}
681 
682 	for (nvp = nvlist_next_nvpair(portsnvl, NULL); nvp != NULL;
683 	    nvp = nvlist_next_nvpair(portsnvl, nvp)) {
684 		status = i_ipadm_set_prop(iph, arg, pdp, nvpair_name(nvp),
685 		    proto, flags);
686 		if (status != IPADM_SUCCESS)
687 			break;
688 	}
689 	nvlist_free(portsnvl);
690 	return (status);
691 }
692 
693 /* ARGSUSED */
694 static ipadm_status_t
695 i_ipadm_set_forwarding(ipadm_handle_t iph, const void *arg,
696     ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
697 {
698 	const char	*ifname = arg;
699 	ipadm_status_t	status;
700 
701 	/*
702 	 * if interface name is provided, then set forwarding using the
703 	 * IFF_ROUTER flag
704 	 */
705 	if (ifname != NULL) {
706 		status = i_ipadm_set_ifprop_flags(iph, ifname, pdp, pval,
707 		    proto, flags);
708 	} else {
709 		char	*val = NULL;
710 
711 		/*
712 		 * if the caller is IPH_LEGACY, `pval' already contains
713 		 * numeric values.
714 		 */
715 		if (!(flags & IPADM_OPT_DEFAULT) &&
716 		    !(iph->iph_flags & IPH_LEGACY)) {
717 
718 			if (strcmp(pval, IPADM_ONSTR) == 0)
719 				val = "1";
720 			else if (strcmp(pval, IPADM_OFFSTR) == 0)
721 				val = "0";
722 			else
723 				return (IPADM_INVALID_ARG);
724 			pval = val;
725 		}
726 
727 		status = i_ipadm_set_prop(iph, ifname, pdp, pval, proto, flags);
728 	}
729 
730 	return (status);
731 }
732 
733 /* ARGSUSED */
734 static ipadm_status_t
735 i_ipadm_set_ecnsack(ipadm_handle_t iph, const void *arg,
736     ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
737 {
738 	uint_t		i;
739 	char		val[MAXPROPVALLEN];
740 
741 	/* if IPH_LEGACY is set, `pval' already contains numeric values */
742 	if (!(flags & IPADM_OPT_DEFAULT) && !(iph->iph_flags & IPH_LEGACY)) {
743 		for (i = 0; ecn_sack_vals[i] != NULL; i++) {
744 			if (strcmp(pval, ecn_sack_vals[i]) == 0)
745 				break;
746 		}
747 		if (ecn_sack_vals[i] == NULL)
748 			return (IPADM_INVALID_ARG);
749 		(void) snprintf(val, MAXPROPVALLEN, "%d", i);
750 		pval = val;
751 	}
752 
753 	return (i_ipadm_set_prop(iph, arg, pdp, pval, proto, flags));
754 }
755 
756 /* ARGSUSED */
757 ipadm_status_t
758 i_ipadm_get_ecnsack(ipadm_handle_t iph, const void *arg,
759     ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
760     uint_t valtype)
761 {
762 	ipadm_status_t	status = IPADM_SUCCESS;
763 	uint_t		i, nbytes = 0;
764 
765 	switch (valtype) {
766 	case MOD_PROP_POSSIBLE:
767 		for (i = 0; ecn_sack_vals[i] != NULL; i++) {
768 			if (i == 0)
769 				nbytes += snprintf(buf + nbytes,
770 				    *bufsize - nbytes, "%s", ecn_sack_vals[i]);
771 			else
772 				nbytes += snprintf(buf + nbytes,
773 				    *bufsize - nbytes, ",%s", ecn_sack_vals[i]);
774 			if (nbytes >= *bufsize)
775 				break;
776 		}
777 		break;
778 	case MOD_PROP_PERM:
779 	case MOD_PROP_DEFAULT:
780 	case MOD_PROP_ACTIVE:
781 		status = i_ipadm_get_prop(iph, arg, pdp, buf, bufsize, proto,
782 		    valtype);
783 
784 		/*
785 		 * If IPH_LEGACY is set, do not convert the value returned
786 		 * from kernel,
787 		 */
788 		if (iph->iph_flags & IPH_LEGACY)
789 			break;
790 
791 		/*
792 		 * For current and default value, convert the value returned
793 		 * from kernel to more discrete representation.
794 		 */
795 		if (status == IPADM_SUCCESS && (valtype == MOD_PROP_ACTIVE ||
796 		    valtype == MOD_PROP_DEFAULT)) {
797 			i = atoi(buf);
798 			assert(i < 3);
799 			nbytes = snprintf(buf, *bufsize, "%s",
800 			    ecn_sack_vals[i]);
801 		}
802 		break;
803 	default:
804 		return (IPADM_INVALID_ARG);
805 	}
806 	if (nbytes >= *bufsize) {
807 		/* insufficient buffer space */
808 		*bufsize = nbytes + 1;
809 		return (IPADM_NO_BUFS);
810 	}
811 
812 	return (status);
813 }
814 
815 /* ARGSUSED */
816 static ipadm_status_t
817 i_ipadm_get_forwarding(ipadm_handle_t iph, const void *arg,
818     ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
819     uint_t valtype)
820 {
821 	const char	*ifname = arg;
822 	ipadm_status_t	status = IPADM_SUCCESS;
823 
824 	/*
825 	 * if interface name is provided, then get forwarding status using
826 	 * SIOCGLIFFLAGS
827 	 */
828 	if (ifname != NULL) {
829 		status = i_ipadm_get_ifprop_flags(iph, ifname, pdp,
830 		    buf, bufsize, pdp->ipd_proto, valtype);
831 	} else {
832 		status = i_ipadm_get_prop(iph, ifname, pdp, buf,
833 		    bufsize, proto, valtype);
834 		/*
835 		 * If IPH_LEGACY is set, do not convert the value returned
836 		 * from kernel,
837 		 */
838 		if (iph->iph_flags & IPH_LEGACY)
839 			goto ret;
840 		if (status == IPADM_SUCCESS && (valtype == MOD_PROP_ACTIVE ||
841 		    valtype == MOD_PROP_DEFAULT)) {
842 			uint_t	val = atoi(buf);
843 
844 			(void) snprintf(buf, *bufsize,
845 			    (val == 1 ? IPADM_ONSTR : IPADM_OFFSTR));
846 		}
847 	}
848 
849 ret:
850 	return (status);
851 }
852 
853 /* ARGSUSED */
854 static ipadm_status_t
855 i_ipadm_get_mtu(ipadm_handle_t iph, const void *arg,
856     ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
857     uint_t valtype)
858 {
859 	struct lifreq	lifr;
860 	const char	*ifname = arg;
861 	size_t		nbytes;
862 	int		s;
863 
864 	switch (valtype) {
865 	case MOD_PROP_PERM:
866 		nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
867 		break;
868 	case MOD_PROP_DEFAULT:
869 	case MOD_PROP_POSSIBLE:
870 		return (i_ipadm_get_prop(iph, arg, pdp, buf, bufsize,
871 		    proto, valtype));
872 	case MOD_PROP_ACTIVE:
873 		bzero(&lifr, sizeof (lifr));
874 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
875 		s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
876 
877 		if (ioctl(s, SIOCGLIFMTU, (caddr_t)&lifr) < 0)
878 			return (ipadm_errno2status(errno));
879 		nbytes = snprintf(buf, *bufsize, "%u", lifr.lifr_mtu);
880 		break;
881 	default:
882 		return (IPADM_INVALID_ARG);
883 	}
884 	if (nbytes >= *bufsize) {
885 		/* insufficient buffer space */
886 		*bufsize = nbytes + 1;
887 		return (IPADM_NO_BUFS);
888 	}
889 	return (IPADM_SUCCESS);
890 }
891 
892 /* ARGSUSED */
893 static ipadm_status_t
894 i_ipadm_get_metric(ipadm_handle_t iph, const void *arg,
895     ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
896     uint_t valtype)
897 {
898 	struct lifreq	lifr;
899 	const char	*ifname = arg;
900 	size_t		nbytes;
901 	int		s, val;
902 
903 	switch (valtype) {
904 	case MOD_PROP_PERM:
905 		val = MOD_PROP_PERM_RW;
906 		break;
907 	case MOD_PROP_DEFAULT:
908 		val = DEF_METRIC_VAL;
909 		break;
910 	case MOD_PROP_ACTIVE:
911 		bzero(&lifr, sizeof (lifr));
912 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
913 
914 		s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
915 		if (ioctl(s, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0)
916 			return (ipadm_errno2status(errno));
917 		val = lifr.lifr_metric;
918 		break;
919 	default:
920 		return (IPADM_INVALID_ARG);
921 	}
922 	nbytes = snprintf(buf, *bufsize, "%d", val);
923 	if (nbytes >= *bufsize) {
924 		/* insufficient buffer space */
925 		*bufsize = nbytes + 1;
926 		return (IPADM_NO_BUFS);
927 	}
928 
929 	return (IPADM_SUCCESS);
930 }
931 
932 /* ARGSUSED */
933 static ipadm_status_t
934 i_ipadm_get_usesrc(ipadm_handle_t iph, const void *arg,
935     ipadm_prop_desc_t *ipd, char *buf, uint_t *bufsize, uint_t proto,
936     uint_t valtype)
937 {
938 	struct lifreq	lifr;
939 	const char	*ifname = arg;
940 	int		s;
941 	char 		if_name[IF_NAMESIZE];
942 	size_t		nbytes;
943 
944 	switch (valtype) {
945 	case MOD_PROP_PERM:
946 		nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
947 		break;
948 	case MOD_PROP_DEFAULT:
949 		nbytes = snprintf(buf, *bufsize, "%s", IPADM_NONESTR);
950 		break;
951 	case MOD_PROP_ACTIVE:
952 		bzero(&lifr, sizeof (lifr));
953 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
954 
955 		s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
956 		if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) < 0)
957 			return (ipadm_errno2status(errno));
958 		if (lifr.lifr_index == 0) {
959 			/* no src address was set, so print 'none' */
960 			(void) strlcpy(if_name, IPADM_NONESTR,
961 			    sizeof (if_name));
962 		} else if (if_indextoname(lifr.lifr_index, if_name) == NULL) {
963 			return (ipadm_errno2status(errno));
964 		}
965 		nbytes = snprintf(buf, *bufsize, "%s", if_name);
966 		break;
967 	default:
968 		return (IPADM_INVALID_ARG);
969 	}
970 	if (nbytes >= *bufsize) {
971 		/* insufficient buffer space */
972 		*bufsize = nbytes + 1;
973 		return (IPADM_NO_BUFS);
974 	}
975 	return (IPADM_SUCCESS);
976 }
977 
978 /* ARGSUSED */
979 static ipadm_status_t
980 i_ipadm_get_ifprop_flags(ipadm_handle_t iph, const void *arg,
981     ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
982     uint_t valtype)
983 {
984 	uint64_t 	intf_flags;
985 	char 		*val;
986 	size_t		nbytes;
987 	const char	*ifname = arg;
988 	sa_family_t	af;
989 	ipadm_status_t	status = IPADM_SUCCESS;
990 
991 	switch (valtype) {
992 	case MOD_PROP_PERM:
993 		nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
994 		break;
995 	case MOD_PROP_DEFAULT:
996 		if (strcmp(pdp->ipd_name, "exchange_routes") == 0 ||
997 		    strcmp(pdp->ipd_name, "arp") == 0 ||
998 		    strcmp(pdp->ipd_name, "nud") == 0) {
999 			val = IPADM_ONSTR;
1000 		} else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
1001 			val = IPADM_OFFSTR;
1002 		} else {
1003 			return (IPADM_PROP_UNKNOWN);
1004 		}
1005 		nbytes = snprintf(buf, *bufsize, "%s", val);
1006 		break;
1007 	case MOD_PROP_ACTIVE:
1008 		af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET);
1009 		status = i_ipadm_get_flags(iph, ifname, af, &intf_flags);
1010 		if (status != IPADM_SUCCESS)
1011 			return (status);
1012 
1013 		val = IPADM_OFFSTR;
1014 		if (strcmp(pdp->ipd_name, "exchange_routes") == 0) {
1015 			if (!(intf_flags & IFF_NORTEXCH))
1016 				val = IPADM_ONSTR;
1017 		} else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
1018 			if (intf_flags & IFF_ROUTER)
1019 				val = IPADM_ONSTR;
1020 		} else if (strcmp(pdp->ipd_name, "arp") == 0) {
1021 			if (!(intf_flags & IFF_NOARP))
1022 				val = IPADM_ONSTR;
1023 		} else if (strcmp(pdp->ipd_name, "nud") == 0) {
1024 			if (!(intf_flags & IFF_NONUD))
1025 				val = IPADM_ONSTR;
1026 		}
1027 		nbytes = snprintf(buf, *bufsize, "%s", val);
1028 		break;
1029 	default:
1030 		return (IPADM_INVALID_ARG);
1031 	}
1032 	if (nbytes >= *bufsize) {
1033 		/* insufficient buffer space */
1034 		*bufsize = nbytes + 1;
1035 		status = IPADM_NO_BUFS;
1036 	}
1037 
1038 	return (status);
1039 }
1040 
1041 static void
1042 i_ipadm_perm2str(char *buf, uint_t *bufsize)
1043 {
1044 	uint_t perm = atoi(buf);
1045 
1046 	(void) snprintf(buf, *bufsize, "%c%c",
1047 	    ((perm & MOD_PROP_PERM_READ) != 0) ? 'r' : '-',
1048 	    ((perm & MOD_PROP_PERM_WRITE) != 0) ? 'w' : '-');
1049 }
1050 
1051 /* ARGSUSED */
1052 static ipadm_status_t
1053 i_ipadm_get_prop(ipadm_handle_t iph, const void *arg,
1054     ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
1055     uint_t valtype)
1056 {
1057 	ipadm_status_t	status = IPADM_SUCCESS;
1058 	const char	*ifname = arg;
1059 	mod_ioc_prop_t	*mip;
1060 	char 		*pname = pdp->ipd_name;
1061 	uint_t		iocsize;
1062 
1063 	/* allocate sufficient ioctl buffer to retrieve value */
1064 	iocsize = sizeof (mod_ioc_prop_t) + *bufsize - 1;
1065 	if ((mip = calloc(1, iocsize)) == NULL)
1066 		return (IPADM_NO_BUFS);
1067 
1068 	mip->mpr_version = MOD_PROP_VERSION;
1069 	mip->mpr_flags = valtype;
1070 	mip->mpr_proto = proto;
1071 	if (ifname != NULL) {
1072 		(void) strlcpy(mip->mpr_ifname, ifname,
1073 		    sizeof (mip->mpr_ifname));
1074 	}
1075 	(void) strlcpy(mip->mpr_name, pname, sizeof (mip->mpr_name));
1076 	mip->mpr_valsize = *bufsize;
1077 
1078 	if (i_ipadm_strioctl(iph->iph_sock, SIOCGETPROP, (char *)mip,
1079 	    iocsize) < 0) {
1080 		if (errno == ENOENT)
1081 			status = IPADM_PROP_UNKNOWN;
1082 		else
1083 			status = ipadm_errno2status(errno);
1084 	} else {
1085 		bcopy(mip->mpr_val, buf, *bufsize);
1086 	}
1087 
1088 	free(mip);
1089 	return (status);
1090 }
1091 
1092 /*
1093  * Populates the ipmgmt_prop_arg_t based on the class of property.
1094  *
1095  * For private protocol properties, while persisting information in ipadm
1096  * data store, to ensure there is no collision of namespace between ipadm
1097  * private nvpair names (which also starts with '_', see ipadm_ipmgmt.h)
1098  * and private protocol property names, we will prepend IPADM_PRIV_PROP_PREFIX
1099  * to property names.
1100  */
1101 static void
1102 i_ipadm_populate_proparg(ipmgmt_prop_arg_t *pargp, ipadm_prop_desc_t *pdp,
1103     const char *pval, const void *object)
1104 {
1105 	const struct ipadm_addrobj_s *ipaddr;
1106 	uint_t		class = pdp->ipd_class;
1107 	uint_t		proto = pdp->ipd_proto;
1108 
1109 	(void) strlcpy(pargp->ia_pname, pdp->ipd_name,
1110 	    sizeof (pargp->ia_pname));
1111 	if (pval != NULL)
1112 		(void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval));
1113 
1114 	switch (class) {
1115 	case IPADMPROP_CLASS_MODULE:
1116 		/* if it's a private property then add the prefix. */
1117 		if (pdp->ipd_name[0] == '_') {
1118 			(void) snprintf(pargp->ia_pname,
1119 			    sizeof (pargp->ia_pname), "_%s", pdp->ipd_name);
1120 		}
1121 		(void) strlcpy(pargp->ia_module, object,
1122 		    sizeof (pargp->ia_module));
1123 		break;
1124 	case IPADMPROP_CLASS_MODIF:
1125 		/* check if object is protostr or an ifname */
1126 		if (ipadm_str2proto(object) != MOD_PROTO_NONE) {
1127 			(void) strlcpy(pargp->ia_module, object,
1128 			    sizeof (pargp->ia_module));
1129 			break;
1130 		}
1131 		/* it's an interface property, fall through */
1132 		/* FALLTHRU */
1133 	case IPADMPROP_CLASS_IF:
1134 		(void) strlcpy(pargp->ia_ifname, object,
1135 		    sizeof (pargp->ia_ifname));
1136 		(void) strlcpy(pargp->ia_module, ipadm_proto2str(proto),
1137 		    sizeof (pargp->ia_module));
1138 		break;
1139 	case IPADMPROP_CLASS_ADDR:
1140 		ipaddr = object;
1141 		(void) strlcpy(pargp->ia_ifname, ipaddr->ipadm_ifname,
1142 		    sizeof (pargp->ia_ifname));
1143 		(void) strlcpy(pargp->ia_aobjname, ipaddr->ipadm_aobjname,
1144 		    sizeof (pargp->ia_aobjname));
1145 		break;
1146 	}
1147 }
1148 
1149 /*
1150  * Common function to retrieve property value for a given interface `ifname' or
1151  * for a given protocol `proto'. The property name is in `pname'.
1152  *
1153  * `valtype' determines the type of value that will be retrieved.
1154  * 	IPADM_OPT_ACTIVE -	current value of the property (active config)
1155  *	IPADM_OPT_PERSIST -	value of the property from persistent store
1156  *	IPADM_OPT_DEFAULT -	default hard coded value (boot-time value)
1157  *	IPADM_OPT_PERM -	read/write permissions for the value
1158  *	IPADM_OPT_POSSIBLE -	range of values
1159  */
1160 static ipadm_status_t
1161 i_ipadm_getprop_common(ipadm_handle_t iph, const char *ifname,
1162     const char *pname, char *buf, uint_t *bufsize, uint_t proto,
1163     uint_t valtype)
1164 {
1165 	ipadm_status_t		status = IPADM_SUCCESS;
1166 	ipadm_prop_desc_t	*pdp;
1167 	char			priv_propname[MAXPROPNAMELEN];
1168 	boolean_t		is_if = (ifname != NULL);
1169 	int			err = 0;
1170 
1171 	pdp = i_ipadm_get_prop_desc(pname, proto, &err);
1172 	if (err == EPROTO)
1173 		return (IPADM_BAD_PROTOCOL);
1174 	/* there are no private interface properties */
1175 	if (is_if && err == ENOENT)
1176 		return (IPADM_PROP_UNKNOWN);
1177 
1178 	if (pdp != NULL) {
1179 		/*
1180 		 * check whether the property can be
1181 		 * applied on an interface
1182 		 */
1183 		if (is_if && !(pdp->ipd_class & IPADMPROP_CLASS_IF))
1184 			return (IPADM_INVALID_ARG);
1185 		/*
1186 		 * check whether the property can be
1187 		 * applied on a module
1188 		 */
1189 		if (!is_if && !(pdp->ipd_class & IPADMPROP_CLASS_MODULE))
1190 			return (IPADM_INVALID_ARG);
1191 
1192 	} else {
1193 		/* private protocol properties, pass it to kernel directly */
1194 		pdp = &ipadm_privprop;
1195 		(void) strlcpy(priv_propname, pname, sizeof (priv_propname));
1196 		pdp->ipd_name = priv_propname;
1197 	}
1198 
1199 	switch (valtype) {
1200 	case IPADM_OPT_PERM:
1201 		status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto,
1202 		    MOD_PROP_PERM);
1203 		if (status == IPADM_SUCCESS)
1204 			i_ipadm_perm2str(buf, bufsize);
1205 		break;
1206 	case IPADM_OPT_ACTIVE:
1207 		status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto,
1208 		    MOD_PROP_ACTIVE);
1209 		break;
1210 	case IPADM_OPT_DEFAULT:
1211 		status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto,
1212 		    MOD_PROP_DEFAULT);
1213 		break;
1214 	case IPADM_OPT_POSSIBLE:
1215 		if (pdp->ipd_get_range != NULL) {
1216 			status = pdp->ipd_get_range(iph, ifname, pdp, buf,
1217 			    bufsize, proto, MOD_PROP_POSSIBLE);
1218 			break;
1219 		}
1220 		buf[0] = '\0';
1221 		break;
1222 	case IPADM_OPT_PERSIST:
1223 		/* retrieve from database */
1224 		if (is_if)
1225 			status = i_ipadm_get_persist_propval(iph, pdp, buf,
1226 			    bufsize, ifname);
1227 		else
1228 			status = i_ipadm_get_persist_propval(iph, pdp, buf,
1229 			    bufsize, ipadm_proto2str(proto));
1230 		break;
1231 	default:
1232 		status = IPADM_INVALID_ARG;
1233 		break;
1234 	}
1235 	return (status);
1236 }
1237 
1238 /*
1239  * Get protocol property of the specified protocol.
1240  */
1241 ipadm_status_t
1242 ipadm_get_prop(ipadm_handle_t iph, const char *pname, char *buf,
1243     uint_t *bufsize, uint_t proto, uint_t valtype)
1244 {
1245 	/*
1246 	 * validate the arguments of the function.
1247 	 */
1248 	if (iph == NULL || pname == NULL || buf == NULL ||
1249 	    bufsize == NULL || *bufsize == 0) {
1250 		return (IPADM_INVALID_ARG);
1251 	}
1252 	/*
1253 	 * Do we support this proto, if not return error.
1254 	 */
1255 	if (ipadm_proto2str(proto) == NULL)
1256 		return (IPADM_NOTSUP);
1257 
1258 	return (i_ipadm_getprop_common(iph, NULL, pname, buf, bufsize,
1259 	    proto, valtype));
1260 }
1261 
1262 /*
1263  * Get interface property of the specified interface.
1264  */
1265 ipadm_status_t
1266 ipadm_get_ifprop(ipadm_handle_t iph, const char *ifname, const char *pname,
1267     char *buf, uint_t *bufsize, uint_t proto, uint_t valtype)
1268 {
1269 	/* validate the arguments of the function. */
1270 	if (iph == NULL || pname == NULL || buf == NULL ||
1271 	    bufsize == NULL || *bufsize == 0) {
1272 		return (IPADM_INVALID_ARG);
1273 	}
1274 
1275 	/* Do we support this proto, if not return error. */
1276 	if (ipadm_proto2str(proto) == NULL)
1277 		return (IPADM_NOTSUP);
1278 
1279 	/*
1280 	 * check if interface name is provided for interface property and
1281 	 * is valid.
1282 	 */
1283 	if (!i_ipadm_validate_ifname(iph, ifname))
1284 		return (IPADM_INVALID_ARG);
1285 
1286 	return (i_ipadm_getprop_common(iph, ifname, pname, buf, bufsize,
1287 	    proto, valtype));
1288 }
1289 
1290 /*
1291  * Allocates sufficient ioctl buffers and copies property name and the
1292  * value, among other things. If the flag IPADM_OPT_DEFAULT is set, then
1293  * `pval' will be NULL and it instructs the kernel to reset the current
1294  * value to property's default value.
1295  */
1296 static ipadm_status_t
1297 i_ipadm_set_prop(ipadm_handle_t iph, const void *arg,
1298     ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
1299 {
1300 	ipadm_status_t	status = IPADM_SUCCESS;
1301 	const char	*ifname = arg;
1302 	mod_ioc_prop_t 	*mip;
1303 	char 		*pname = pdp->ipd_name;
1304 	uint_t 		valsize, iocsize;
1305 	uint_t		iocflags = 0;
1306 
1307 	if (flags & IPADM_OPT_DEFAULT) {
1308 		iocflags |= MOD_PROP_DEFAULT;
1309 	} else if (flags & IPADM_OPT_ACTIVE) {
1310 		iocflags |= MOD_PROP_ACTIVE;
1311 		if (flags & IPADM_OPT_APPEND)
1312 			iocflags |= MOD_PROP_APPEND;
1313 		else if (flags & IPADM_OPT_REMOVE)
1314 			iocflags |= MOD_PROP_REMOVE;
1315 	}
1316 
1317 	if (pval != NULL) {
1318 		valsize = strlen(pval);
1319 		iocsize = sizeof (mod_ioc_prop_t) + valsize - 1;
1320 	} else {
1321 		valsize = 0;
1322 		iocsize = sizeof (mod_ioc_prop_t);
1323 	}
1324 
1325 	if ((mip = calloc(1, iocsize)) == NULL)
1326 		return (IPADM_NO_BUFS);
1327 
1328 	mip->mpr_version = MOD_PROP_VERSION;
1329 	mip->mpr_flags = iocflags;
1330 	mip->mpr_proto = proto;
1331 	if (ifname != NULL) {
1332 		(void) strlcpy(mip->mpr_ifname, ifname,
1333 		    sizeof (mip->mpr_ifname));
1334 	}
1335 
1336 	(void) strlcpy(mip->mpr_name, pname, sizeof (mip->mpr_name));
1337 	mip->mpr_valsize = valsize;
1338 	if (pval != NULL)
1339 		bcopy(pval, mip->mpr_val, valsize);
1340 
1341 	if (i_ipadm_strioctl(iph->iph_sock, SIOCSETPROP, (char *)mip,
1342 	    iocsize) < 0) {
1343 		if (errno == ENOENT)
1344 			status = IPADM_PROP_UNKNOWN;
1345 		else
1346 			status = ipadm_errno2status(errno);
1347 	}
1348 	free(mip);
1349 	return (status);
1350 }
1351 
1352 /*
1353  * Common function for modifying both protocol/interface property.
1354  *
1355  * If:
1356  *   IPADM_OPT_PERSIST is set then the value is persisted.
1357  *   IPADM_OPT_DEFAULT is set then the default value for the property will
1358  *		       be applied.
1359  */
1360 static ipadm_status_t
1361 i_ipadm_setprop_common(ipadm_handle_t iph, const char *ifname,
1362     const char *pname, const char *buf, uint_t proto, uint_t pflags)
1363 {
1364 	ipadm_status_t		status = IPADM_SUCCESS;
1365 	boolean_t 		persist = (pflags & IPADM_OPT_PERSIST);
1366 	boolean_t		reset = (pflags & IPADM_OPT_DEFAULT);
1367 	ipadm_prop_desc_t	*pdp;
1368 	boolean_t		is_if = (ifname != NULL);
1369 	char			priv_propname[MAXPROPNAMELEN];
1370 	int			err = 0;
1371 
1372 	/* Check that property value is within the allowed size */
1373 	if (!reset && strnlen(buf, MAXPROPVALLEN) >= MAXPROPVALLEN)
1374 		return (IPADM_INVALID_ARG);
1375 
1376 	pdp = i_ipadm_get_prop_desc(pname, proto, &err);
1377 	if (err == EPROTO)
1378 		return (IPADM_BAD_PROTOCOL);
1379 	/* there are no private interface properties */
1380 	if (is_if && err == ENOENT)
1381 		return (IPADM_PROP_UNKNOWN);
1382 
1383 	if (pdp != NULL) {
1384 		/* do some sanity checks */
1385 		if (is_if) {
1386 			if (!(pdp->ipd_class & IPADMPROP_CLASS_IF))
1387 				return (IPADM_INVALID_ARG);
1388 		} else {
1389 			if (!(pdp->ipd_class & IPADMPROP_CLASS_MODULE))
1390 				return (IPADM_INVALID_ARG);
1391 		}
1392 		/*
1393 		 * if the property is not multi-valued and IPADM_OPT_APPEND or
1394 		 * IPADM_OPT_REMOVE is specified, return IPADM_INVALID_ARG.
1395 		 */
1396 		if (!(pdp->ipd_flags & IPADMPROP_MULVAL) && (pflags &
1397 		    (IPADM_OPT_APPEND|IPADM_OPT_REMOVE))) {
1398 			return (IPADM_INVALID_ARG);
1399 		}
1400 	} else {
1401 		/* private protocol property, pass it to kernel directly */
1402 		pdp = &ipadm_privprop;
1403 		(void) strlcpy(priv_propname, pname, sizeof (priv_propname));
1404 		pdp->ipd_name = priv_propname;
1405 	}
1406 
1407 	status = pdp->ipd_set(iph, ifname, pdp, buf, proto, pflags);
1408 	if (status != IPADM_SUCCESS)
1409 		return (status);
1410 
1411 	if (persist) {
1412 		if (is_if)
1413 			status = i_ipadm_persist_propval(iph, pdp, buf, ifname,
1414 			    pflags);
1415 		else
1416 			status = i_ipadm_persist_propval(iph, pdp, buf,
1417 			    ipadm_proto2str(proto), pflags);
1418 	}
1419 	return (status);
1420 }
1421 
1422 /*
1423  * Sets the property value of the specified interface
1424  */
1425 ipadm_status_t
1426 ipadm_set_ifprop(ipadm_handle_t iph, const char *ifname, const char *pname,
1427     const char *buf, uint_t proto, uint_t pflags)
1428 {
1429 	boolean_t	reset = (pflags & IPADM_OPT_DEFAULT);
1430 	ipadm_status_t	status;
1431 
1432 	/* check for solaris.network.interface.config authorization */
1433 	if (!ipadm_check_auth())
1434 		return (IPADM_EAUTH);
1435 	/*
1436 	 * validate the arguments of the function.
1437 	 */
1438 	if (iph == NULL || pname == NULL || (!reset && buf == NULL) ||
1439 	    pflags == 0 || pflags == IPADM_OPT_PERSIST ||
1440 	    (pflags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_DEFAULT))) {
1441 		return (IPADM_INVALID_ARG);
1442 	}
1443 
1444 	/*
1445 	 * Do we support this protocol, if not return error.
1446 	 */
1447 	if (ipadm_proto2str(proto) == NULL)
1448 		return (IPADM_NOTSUP);
1449 
1450 	/*
1451 	 * Validate the interface and check if a persistent
1452 	 * operation is performed on a temporary object.
1453 	 */
1454 	status = i_ipadm_validate_if(iph, ifname, proto, pflags);
1455 	if (status != IPADM_SUCCESS)
1456 		return (status);
1457 
1458 	return (i_ipadm_setprop_common(iph, ifname, pname, buf, proto,
1459 	    pflags));
1460 }
1461 
1462 /*
1463  * Sets the property value of the specified protocol.
1464  */
1465 ipadm_status_t
1466 ipadm_set_prop(ipadm_handle_t iph, const char *pname, const char *buf,
1467     uint_t proto, uint_t pflags)
1468 {
1469 	boolean_t	reset = (pflags & IPADM_OPT_DEFAULT);
1470 
1471 	/* check for solaris.network.interface.config authorization */
1472 	if (!ipadm_check_auth())
1473 		return (IPADM_EAUTH);
1474 	/*
1475 	 * validate the arguments of the function.
1476 	 */
1477 	if (iph == NULL || pname == NULL ||(!reset && buf == NULL) ||
1478 	    pflags == 0 || pflags == IPADM_OPT_PERSIST ||
1479 	    (pflags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_DEFAULT|
1480 	    IPADM_OPT_APPEND|IPADM_OPT_REMOVE))) {
1481 		return (IPADM_INVALID_ARG);
1482 	}
1483 
1484 	/*
1485 	 * Do we support this proto, if not return error.
1486 	 */
1487 	if (ipadm_proto2str(proto) == NULL)
1488 		return (IPADM_NOTSUP);
1489 
1490 	return (i_ipadm_setprop_common(iph, NULL, pname, buf, proto,
1491 	    pflags));
1492 }
1493 
1494 /* helper function for ipadm_walk_proptbl */
1495 static void
1496 i_ipadm_walk_proptbl(ipadm_prop_desc_t *pdtbl, uint_t proto, uint_t class,
1497     ipadm_prop_wfunc_t *func, void *arg)
1498 {
1499 	ipadm_prop_desc_t	*pdp;
1500 
1501 	for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) {
1502 		if (!(pdp->ipd_class & class))
1503 			continue;
1504 
1505 		if (proto != MOD_PROTO_NONE && !(pdp->ipd_proto & proto))
1506 			continue;
1507 
1508 		/*
1509 		 * we found a class specific match, call the
1510 		 * user callback function.
1511 		 */
1512 		if (func(arg, pdp->ipd_name, pdp->ipd_proto) == B_FALSE)
1513 			break;
1514 	}
1515 }
1516 
1517 /*
1518  * Walks through all the properties, for a given protocol and property class
1519  * (protocol or interface).
1520  *
1521  * Further if proto == MOD_PROTO_NONE, then it walks through all the supported
1522  * protocol property tables.
1523  */
1524 ipadm_status_t
1525 ipadm_walk_proptbl(uint_t proto, uint_t class, ipadm_prop_wfunc_t *func,
1526     void *arg)
1527 {
1528 	ipadm_prop_desc_t	*pdtbl;
1529 	ipadm_status_t		status = IPADM_SUCCESS;
1530 	int			i;
1531 	int			count = A_CNT(protocols);
1532 
1533 	if (func == NULL)
1534 		return (IPADM_INVALID_ARG);
1535 
1536 	switch (class) {
1537 	case IPADMPROP_CLASS_ADDR:
1538 		pdtbl = ipadm_addrprop_table;
1539 		break;
1540 	case IPADMPROP_CLASS_IF:
1541 	case IPADMPROP_CLASS_MODULE:
1542 		pdtbl = i_ipadm_get_propdesc_table(proto);
1543 		if (pdtbl == NULL && proto != MOD_PROTO_NONE)
1544 			return (IPADM_INVALID_ARG);
1545 		break;
1546 	default:
1547 		return (IPADM_INVALID_ARG);
1548 	}
1549 
1550 	if (pdtbl != NULL) {
1551 		/*
1552 		 * proto will be MOD_PROTO_NONE in the case of
1553 		 * IPADMPROP_CLASS_ADDR.
1554 		 */
1555 		i_ipadm_walk_proptbl(pdtbl, proto, class, func, arg);
1556 	} else {
1557 		/* Walk thru all the protocol tables, we support */
1558 		for (i = 0; i < count; i++) {
1559 			pdtbl = i_ipadm_get_propdesc_table(protocols[i]);
1560 			i_ipadm_walk_proptbl(pdtbl, protocols[i], class, func,
1561 			    arg);
1562 		}
1563 	}
1564 	return (status);
1565 }
1566 
1567 /*
1568  * Given a property name, walks through all the instances of a property name.
1569  * Some properties have two instances one for v4 interfaces and another for v6
1570  * interfaces. For example: MTU. MTU can have different values for v4 and v6.
1571  * Therefore there are two properties for 'MTU'.
1572  *
1573  * This function invokes `func' for every instance of property `pname'
1574  */
1575 ipadm_status_t
1576 ipadm_walk_prop(const char *pname, uint_t proto, uint_t class,
1577     ipadm_prop_wfunc_t *func, void *arg)
1578 {
1579 	ipadm_prop_desc_t	*pdtbl, *pdp;
1580 	ipadm_status_t		status = IPADM_SUCCESS;
1581 	boolean_t		matched = B_FALSE;
1582 
1583 	if (pname == NULL || func == NULL)
1584 		return (IPADM_INVALID_ARG);
1585 
1586 	switch (class) {
1587 	case IPADMPROP_CLASS_ADDR:
1588 		pdtbl = ipadm_addrprop_table;
1589 		break;
1590 	case IPADMPROP_CLASS_IF:
1591 	case IPADMPROP_CLASS_MODULE:
1592 		pdtbl = i_ipadm_get_propdesc_table(proto);
1593 		break;
1594 	default:
1595 		return (IPADM_INVALID_ARG);
1596 	}
1597 
1598 	if (pdtbl == NULL)
1599 		return (IPADM_INVALID_ARG);
1600 
1601 	for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) {
1602 		if (strcmp(pname, pdp->ipd_name) != 0)
1603 			continue;
1604 		if (!(pdp->ipd_proto & proto))
1605 			continue;
1606 		matched = B_TRUE;
1607 		/* we found a match, call the callback function */
1608 		if (func(arg, pdp->ipd_name, pdp->ipd_proto) == B_FALSE)
1609 			break;
1610 	}
1611 	if (!matched)
1612 		status = IPADM_PROP_UNKNOWN;
1613 	return (status);
1614 }
1615 
1616 /* ARGSUSED */
1617 ipadm_status_t
1618 i_ipadm_get_onoff(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *dp,
1619     char *buf, uint_t *bufsize, uint_t proto, uint_t valtype)
1620 {
1621 	(void) snprintf(buf, *bufsize, "%s,%s", IPADM_ONSTR, IPADM_OFFSTR);
1622 	return (IPADM_SUCCESS);
1623 }
1624 
1625 /*
1626  * Makes a door call to ipmgmtd to retrieve the persisted property value
1627  */
1628 ipadm_status_t
1629 i_ipadm_get_persist_propval(ipadm_handle_t iph, ipadm_prop_desc_t *pdp,
1630     char *gbuf, uint_t *gbufsize, const void *object)
1631 {
1632 	ipmgmt_prop_arg_t	parg;
1633 	ipmgmt_getprop_rval_t	rval, *rvalp;
1634 	size_t			nbytes;
1635 	int			err = 0;
1636 
1637 	bzero(&parg, sizeof (parg));
1638 	parg.ia_cmd = IPMGMT_CMD_GETPROP;
1639 	i_ipadm_populate_proparg(&parg, pdp, NULL, object);
1640 
1641 	rvalp = &rval;
1642 	err = ipadm_door_call(iph, &parg, sizeof (parg), (void **)&rvalp,
1643 	    sizeof (rval), B_FALSE);
1644 	if (err == 0) {
1645 		/* assert that rvalp was not reallocated */
1646 		assert(rvalp == &rval);
1647 
1648 		/* `ir_pval' contains the property value */
1649 		nbytes = snprintf(gbuf, *gbufsize, "%s", rvalp->ir_pval);
1650 		if (nbytes >= *gbufsize) {
1651 			/* insufficient buffer space */
1652 			*gbufsize = nbytes + 1;
1653 			err = ENOBUFS;
1654 		}
1655 	}
1656 	return (ipadm_errno2status(err));
1657 }
1658 
1659 /*
1660  * Persists the property value for a given property in the data store
1661  */
1662 ipadm_status_t
1663 i_ipadm_persist_propval(ipadm_handle_t iph, ipadm_prop_desc_t *pdp,
1664     const char *pval, const void *object, uint_t flags)
1665 {
1666 	ipmgmt_prop_arg_t	parg;
1667 	int			err = 0;
1668 
1669 	bzero(&parg, sizeof (parg));
1670 	i_ipadm_populate_proparg(&parg, pdp, pval, object);
1671 	/*
1672 	 * Check if value to be persisted need to be appended or removed. This
1673 	 * is required for multi-valued property.
1674 	 */
1675 	if (flags & IPADM_OPT_APPEND)
1676 		parg.ia_flags |= IPMGMT_APPEND;
1677 	if (flags & IPADM_OPT_REMOVE)
1678 		parg.ia_flags |= IPMGMT_REMOVE;
1679 
1680 	if (flags & (IPADM_OPT_DEFAULT|IPADM_OPT_REMOVE))
1681 		parg.ia_cmd = IPMGMT_CMD_RESETPROP;
1682 	else
1683 		parg.ia_cmd = IPMGMT_CMD_SETPROP;
1684 
1685 	err = ipadm_door_call(iph, &parg, sizeof (parg), NULL, 0, B_FALSE);
1686 
1687 	/*
1688 	 * its fine if there were no entry in the DB to delete. The user
1689 	 * might be changing property value, which was not changed
1690 	 * persistently.
1691 	 */
1692 	if (err == ENOENT)
1693 		err = 0;
1694 	return (ipadm_errno2status(err));
1695 }
1696 
1697 /*
1698  * This is called from ipadm_set_ifprop() to validate the set operation.
1699  * It does the following steps:
1700  * 1. Validates the interface name.
1701  * 2. Fails if it is an IPMP meta-interface or an underlying interface.
1702  * 3. In case of a persistent operation, verifies that the
1703  *	interface is persistent.
1704  */
1705 static ipadm_status_t
1706 i_ipadm_validate_if(ipadm_handle_t iph, const char *ifname,
1707     uint_t proto, uint_t flags)
1708 {
1709 	sa_family_t	af, other_af;
1710 	ipadm_status_t	status;
1711 	boolean_t	p_exists;
1712 	boolean_t	af_exists, other_af_exists, a_exists;
1713 
1714 	/* Check if the interface name is valid. */
1715 	if (!i_ipadm_validate_ifname(iph, ifname))
1716 		return (IPADM_INVALID_ARG);
1717 
1718 	af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET);
1719 	/*
1720 	 * Setting properties on an IPMP meta-interface or underlying
1721 	 * interface is not supported.
1722 	 */
1723 	if (i_ipadm_is_ipmp(iph, ifname) || i_ipadm_is_under_ipmp(iph, ifname))
1724 		return (IPADM_NOTSUP);
1725 
1726 	/* Check if interface exists in the persistent configuration. */
1727 	status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
1728 	if (status != IPADM_SUCCESS)
1729 		return (status);
1730 
1731 	/* Check if interface exists in the active configuration. */
1732 	af_exists = ipadm_if_enabled(iph, ifname, af);
1733 	other_af = (af == AF_INET ? AF_INET6 : AF_INET);
1734 	other_af_exists = ipadm_if_enabled(iph, ifname, other_af);
1735 	a_exists = (af_exists || other_af_exists);
1736 	if (!a_exists && p_exists)
1737 		return (IPADM_OP_DISABLE_OBJ);
1738 	if (!af_exists)
1739 		return (IPADM_ENXIO);
1740 
1741 	/*
1742 	 * If a persistent operation is requested, check if the underlying
1743 	 * IP interface is persistent.
1744 	 */
1745 	if ((flags & IPADM_OPT_PERSIST) && !p_exists)
1746 		return (IPADM_TEMPORARY_OBJ);
1747 	return (IPADM_SUCCESS);
1748 }
1749 
1750 /*
1751  * Private protocol properties namespace scheme:
1752  *
1753  * PSARC 2010/080 identified the private protocol property names to be the
1754  * leading protocol names. For e.g. tcp_strong_iss, ip_strict_src_multihoming,
1755  * et al,. However to be consistent with private data-link property names,
1756  * which starts with '_', private protocol property names will start with '_'.
1757  * For e.g. _strong_iss, _strict_src_multihoming, et al,.
1758  */
1759 
1760 /* maps new private protocol property name to the old private property name */
1761 typedef struct ipadm_oname2nname_map {
1762 	char	*iom_oname;
1763 	char	*iom_nname;
1764 	uint_t	iom_proto;
1765 } ipadm_oname2nname_map_t;
1766 
1767 /*
1768  * IP is a special case. It isn't straight forward to derive the legacy name
1769  * from the new name and vice versa. No set standard was followed in naming
1770  * the properties and hence we need a table to capture the mapping.
1771  */
1772 static ipadm_oname2nname_map_t name_map[] = {
1773 	{ "arp_probe_delay",		"_arp_probe_delay",
1774 	    MOD_PROTO_IP },
1775 	{ "arp_fastprobe_delay",	"_arp_fastprobe_delay",
1776 	    MOD_PROTO_IP },
1777 	{ "arp_probe_interval",		"_arp_probe_interval",
1778 	    MOD_PROTO_IP },
1779 	{ "arp_fastprobe_interval",	"_arp_fastprobe_interval",
1780 	    MOD_PROTO_IP },
1781 	{ "arp_probe_count",		"_arp_probe_count",
1782 	    MOD_PROTO_IP },
1783 	{ "arp_fastprobe_count",	"_arp_fastprobe_count",
1784 	    MOD_PROTO_IP },
1785 	{ "arp_defend_interval",	"_arp_defend_interval",
1786 	    MOD_PROTO_IP },
1787 	{ "arp_defend_rate",		"_arp_defend_rate",
1788 	    MOD_PROTO_IP },
1789 	{ "arp_defend_period",		"_arp_defend_period",
1790 	    MOD_PROTO_IP },
1791 	{ "ndp_defend_interval",	"_ndp_defend_interval",
1792 	    MOD_PROTO_IP },
1793 	{ "ndp_defend_rate",		"_ndp_defend_rate",
1794 	    MOD_PROTO_IP },
1795 	{ "ndp_defend_period",		"_ndp_defend_period",
1796 	    MOD_PROTO_IP },
1797 	{ "igmp_max_version",		"_igmp_max_version",
1798 	    MOD_PROTO_IP },
1799 	{ "mld_max_version",		"_mld_max_version",
1800 	    MOD_PROTO_IP },
1801 	{ "ipsec_override_persocket_policy", "_ipsec_override_persocket_policy",
1802 	    MOD_PROTO_IP },
1803 	{ "ipsec_policy_log_interval",	"_ipsec_policy_log_interval",
1804 	    MOD_PROTO_IP },
1805 	{ "icmp_accept_clear_messages",	"_icmp_accept_clear_messages",
1806 	    MOD_PROTO_IP },
1807 	{ "igmp_accept_clear_messages",	"_igmp_accept_clear_messages",
1808 	    MOD_PROTO_IP },
1809 	{ "pim_accept_clear_messages",	"_pim_accept_clear_messages",
1810 	    MOD_PROTO_IP },
1811 	{ "ip_respond_to_echo_multicast", "_respond_to_echo_multicast",
1812 	    MOD_PROTO_IPV4 },
1813 	{ "ip_send_redirects",		"_send_redirects",
1814 	    MOD_PROTO_IPV4 },
1815 	{ "ip_forward_src_routed",	"_forward_src_routed",
1816 	    MOD_PROTO_IPV4 },
1817 	{ "ip_icmp_return_data_bytes",	"_icmp_return_data_bytes",
1818 	    MOD_PROTO_IPV4 },
1819 	{ "ip_ignore_redirect",		"_ignore_redirect",
1820 	    MOD_PROTO_IPV4 },
1821 	{ "ip_strict_dst_multihoming",	"_strict_dst_multihoming",
1822 	    MOD_PROTO_IPV4 },
1823 	{ "ip_reasm_timeout",		"_reasm_timeout",
1824 	    MOD_PROTO_IPV4 },
1825 	{ "ip_strict_src_multihoming",	"_strict_src_multihoming",
1826 	    MOD_PROTO_IPV4 },
1827 	{ "ipv4_dad_announce_interval",	"_dad_announce_interval",
1828 	    MOD_PROTO_IPV4 },
1829 	{ "ipv4_icmp_return_pmtu",	"_icmp_return_pmtu",
1830 	    MOD_PROTO_IPV4 },
1831 	{ "ipv6_dad_announce_interval",	"_dad_announce_interval",
1832 	    MOD_PROTO_IPV6 },
1833 	{ "ipv6_icmp_return_pmtu",	"_icmp_return_pmtu",
1834 	    MOD_PROTO_IPV6 },
1835 	{ NULL, NULL, MOD_PROTO_NONE }
1836 };
1837 
1838 /*
1839  * Following API returns a new property name in `nname' for the given legacy
1840  * property name in `oname'.
1841  */
1842 int
1843 ipadm_legacy2new_propname(const char *oname, char *nname, uint_t nnamelen,
1844     uint_t *proto)
1845 {
1846 	const char	*str;
1847 	ipadm_oname2nname_map_t *ionmp;
1848 
1849 	/* if it's a public property, there is nothing to return */
1850 	if (i_ipadm_get_prop_desc(oname, *proto, NULL) != NULL)
1851 		return (-1);
1852 
1853 	/*
1854 	 * we didn't find the `oname' in the table, check if the property
1855 	 * name begins with a leading protocol.
1856 	 */
1857 	str = oname;
1858 	switch (*proto) {
1859 	case MOD_PROTO_TCP:
1860 		if (strstr(oname, "tcp_") == oname)
1861 			str += strlen("tcp");
1862 		break;
1863 	case MOD_PROTO_SCTP:
1864 		if (strstr(oname, "sctp_") == oname)
1865 			str += strlen("sctp");
1866 		break;
1867 	case MOD_PROTO_UDP:
1868 		if (strstr(oname, "udp_") == oname)
1869 			str += strlen("udp");
1870 		break;
1871 	case MOD_PROTO_RAWIP:
1872 		if (strstr(oname, "icmp_") == oname)
1873 			str += strlen("icmp");
1874 		break;
1875 	case MOD_PROTO_IP:
1876 	case MOD_PROTO_IPV4:
1877 	case MOD_PROTO_IPV6:
1878 		if (strstr(oname, "ip6_") == oname) {
1879 			*proto = MOD_PROTO_IPV6;
1880 			str += strlen("ip6");
1881 		} else {
1882 			for (ionmp = name_map; ionmp->iom_oname != NULL;
1883 			    ionmp++) {
1884 				if (strcmp(oname, ionmp->iom_oname) == 0) {
1885 					str = ionmp->iom_nname;
1886 					*proto = ionmp->iom_proto;
1887 					break;
1888 				}
1889 			}
1890 			if (ionmp->iom_oname != NULL)
1891 				break;
1892 
1893 			if (strstr(oname, "ip_") == oname) {
1894 				*proto = MOD_PROTO_IP;
1895 				str += strlen("ip");
1896 			}
1897 		}
1898 		break;
1899 	default:
1900 		return (-1);
1901 	}
1902 	(void) snprintf(nname, nnamelen, "%s", str);
1903 	return (0);
1904 }
1905 
1906 /*
1907  * Following API is required for ndd.c alone. To maintain backward
1908  * compatibility with ndd output, we need to print the legacy name
1909  * for the new name.
1910  */
1911 int
1912 ipadm_new2legacy_propname(const char *oname, char *nname,
1913     uint_t nnamelen, uint_t proto)
1914 {
1915 	char	*prefix;
1916 	ipadm_oname2nname_map_t *ionmp;
1917 
1918 	/* if it's a public property, there is nothing to prepend */
1919 	if (i_ipadm_get_prop_desc(oname, proto, NULL) != NULL)
1920 		return (-1);
1921 
1922 	switch (proto) {
1923 	case MOD_PROTO_TCP:
1924 		prefix = "tcp";
1925 		break;
1926 	case MOD_PROTO_SCTP:
1927 		prefix = "sctp";
1928 		break;
1929 	case MOD_PROTO_UDP:
1930 		prefix = "udp";
1931 		break;
1932 	case MOD_PROTO_RAWIP:
1933 		prefix = "icmp";
1934 		break;
1935 	case MOD_PROTO_IP:
1936 	case MOD_PROTO_IPV4:
1937 	case MOD_PROTO_IPV6:
1938 		/* handle special case for IP */
1939 		for (ionmp = name_map; ionmp->iom_oname != NULL; ionmp++) {
1940 			if (strcmp(oname, ionmp->iom_nname) == 0 &&
1941 			    ionmp->iom_proto == proto) {
1942 				(void) strlcpy(nname, ionmp->iom_oname,
1943 				    nnamelen);
1944 				return (0);
1945 			}
1946 		}
1947 		if (proto == MOD_PROTO_IPV6)
1948 			prefix = "ip6";
1949 		else
1950 			prefix = "ip";
1951 		break;
1952 	default:
1953 		return (-1);
1954 	}
1955 	(void) snprintf(nname, nnamelen, "%s%s", prefix, oname);
1956 	return (0);
1957 }
1958