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