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