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