xref: /titanic_50/usr/src/lib/libipadm/common/ipadm_prop.c (revision 310da939904e5e37a19c8a427209c0d4d9683ec8)
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 	uint_t		count = 0;
498 
499 	if (flags & IPADM_OPT_DEFAULT) {
500 		assert(pval == NULL);
501 		return (i_ipadm_set_prop(iph, arg, pdp, pval, proto, flags));
502 	}
503 
504 	if ((err = ipadm_str2nvlist(pval, &portsnvl, IPADM_NORVAL)) != 0)
505 		return (ipadm_errno2status(err));
506 
507 	/* count the number of ports */
508 	for (nvp = nvlist_next_nvpair(portsnvl, NULL); nvp != NULL;
509 	    nvp = nvlist_next_nvpair(portsnvl, nvp)) {
510 		++count;
511 	}
512 
513 	if (iph->iph_flags & IPH_INIT) {
514 		flags |= IPADM_OPT_APPEND;
515 	} else if (count > 1) {
516 		/*
517 		 * We allow only one port to be added, removed or
518 		 * assigned at a time.
519 		 *
520 		 * However on reboot, while initializing protocol
521 		 * properties, extra_priv_ports might have multiple
522 		 * values. Only in that case we allow setting multiple
523 		 * values.
524 		 */
525 		nvlist_free(portsnvl);
526 		return (IPADM_INVALID_ARG);
527 	}
528 
529 	for (nvp = nvlist_next_nvpair(portsnvl, NULL); nvp != NULL;
530 	    nvp = nvlist_next_nvpair(portsnvl, nvp)) {
531 		status = i_ipadm_set_prop(iph, arg, pdp, nvpair_name(nvp),
532 		    proto, flags);
533 		if (status != IPADM_SUCCESS)
534 			break;
535 	}
536 	nvlist_free(portsnvl);
537 	return (status);
538 }
539 
540 /* ARGSUSED */
541 static ipadm_status_t
542 i_ipadm_set_forwarding(ipadm_handle_t iph, const void *arg,
543     ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
544 {
545 	const char	*ifname = arg;
546 	ipadm_status_t	status;
547 
548 	/*
549 	 * if interface name is provided, then set forwarding using the
550 	 * IFF_ROUTER flag
551 	 */
552 	if (ifname != NULL) {
553 		status = i_ipadm_set_ifprop_flags(iph, ifname, pdp, pval,
554 		    proto, flags);
555 	} else {
556 		char	*val = NULL;
557 
558 		/*
559 		 * if the caller is IPH_LEGACY, `pval' already contains
560 		 * numeric values.
561 		 */
562 		if (!(flags & IPADM_OPT_DEFAULT) &&
563 		    !(iph->iph_flags & IPH_LEGACY)) {
564 
565 			if (strcmp(pval, IPADM_ONSTR) == 0)
566 				val = "1";
567 			else if (strcmp(pval, IPADM_OFFSTR) == 0)
568 				val = "0";
569 			else
570 				return (IPADM_INVALID_ARG);
571 			pval = val;
572 		}
573 
574 		status = i_ipadm_set_prop(iph, ifname, pdp, pval, proto, flags);
575 	}
576 
577 	return (status);
578 }
579 
580 /* ARGSUSED */
581 static ipadm_status_t
582 i_ipadm_set_ecnsack(ipadm_handle_t iph, const void *arg,
583     ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
584 {
585 	uint_t		i;
586 	char		val[MAXPROPVALLEN];
587 
588 	/* if IPH_LEGACY is set, `pval' already contains numeric values */
589 	if (!(flags & IPADM_OPT_DEFAULT) && !(iph->iph_flags & IPH_LEGACY)) {
590 		for (i = 0; ecn_sack_vals[i] != NULL; i++) {
591 			if (strcmp(pval, ecn_sack_vals[i]) == 0)
592 				break;
593 		}
594 		if (ecn_sack_vals[i] == NULL)
595 			return (IPADM_INVALID_ARG);
596 		(void) snprintf(val, MAXPROPVALLEN, "%d", i);
597 		pval = val;
598 	}
599 
600 	return (i_ipadm_set_prop(iph, arg, pdp, pval, proto, flags));
601 }
602 
603 /* ARGSUSED */
604 ipadm_status_t
605 i_ipadm_get_ecnsack(ipadm_handle_t iph, const void *arg,
606     ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
607     uint_t valtype)
608 {
609 	ipadm_status_t	status = IPADM_SUCCESS;
610 	uint_t		i, nbytes = 0;
611 
612 	switch (valtype) {
613 	case MOD_PROP_POSSIBLE:
614 		for (i = 0; ecn_sack_vals[i] != NULL; i++) {
615 			if (i == 0)
616 				nbytes += snprintf(buf + nbytes,
617 				    *bufsize - nbytes, "%s", ecn_sack_vals[i]);
618 			else
619 				nbytes += snprintf(buf + nbytes,
620 				    *bufsize - nbytes, ",%s", ecn_sack_vals[i]);
621 			if (nbytes >= *bufsize)
622 				break;
623 		}
624 		break;
625 	case MOD_PROP_PERM:
626 	case MOD_PROP_DEFAULT:
627 	case MOD_PROP_ACTIVE:
628 		status = i_ipadm_get_prop(iph, arg, pdp, buf, bufsize, proto,
629 		    valtype);
630 
631 		/*
632 		 * If IPH_LEGACY is set, do not convert the value returned
633 		 * from kernel,
634 		 */
635 		if (iph->iph_flags & IPH_LEGACY)
636 			break;
637 
638 		/*
639 		 * For current and default value, convert the value returned
640 		 * from kernel to more discrete representation.
641 		 */
642 		if (status == IPADM_SUCCESS && (valtype == MOD_PROP_ACTIVE ||
643 		    valtype == MOD_PROP_DEFAULT)) {
644 			i = atoi(buf);
645 			assert(i < 3);
646 			nbytes = snprintf(buf, *bufsize, "%s",
647 			    ecn_sack_vals[i]);
648 		}
649 		break;
650 	default:
651 		return (IPADM_INVALID_ARG);
652 	}
653 	if (nbytes >= *bufsize) {
654 		/* insufficient buffer space */
655 		*bufsize = nbytes + 1;
656 		return (IPADM_NO_BUFS);
657 	}
658 
659 	return (status);
660 }
661 
662 /* ARGSUSED */
663 static ipadm_status_t
664 i_ipadm_get_forwarding(ipadm_handle_t iph, const void *arg,
665     ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
666     uint_t valtype)
667 {
668 	const char	*ifname = arg;
669 	ipadm_status_t	status = IPADM_SUCCESS;
670 
671 	/*
672 	 * if interface name is provided, then get forwarding status using
673 	 * SIOCGLIFFLAGS
674 	 */
675 	if (ifname != NULL) {
676 		status = i_ipadm_get_ifprop_flags(iph, ifname, pdp,
677 		    buf, bufsize, pdp->ipd_proto, valtype);
678 	} else {
679 		status = i_ipadm_get_prop(iph, ifname, pdp, buf,
680 		    bufsize, proto, valtype);
681 		/*
682 		 * If IPH_LEGACY is set, do not convert the value returned
683 		 * from kernel,
684 		 */
685 		if (iph->iph_flags & IPH_LEGACY)
686 			goto ret;
687 		if (status == IPADM_SUCCESS && (valtype == MOD_PROP_ACTIVE ||
688 		    valtype == MOD_PROP_DEFAULT)) {
689 			uint_t	val = atoi(buf);
690 
691 			(void) snprintf(buf, *bufsize,
692 			    (val == 1 ? IPADM_ONSTR : IPADM_OFFSTR));
693 		}
694 	}
695 
696 ret:
697 	return (status);
698 }
699 
700 /* ARGSUSED */
701 static ipadm_status_t
702 i_ipadm_get_mtu(ipadm_handle_t iph, const void *arg,
703     ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
704     uint_t valtype)
705 {
706 	struct lifreq	lifr;
707 	const char	*ifname = arg;
708 	size_t		nbytes;
709 	int		s;
710 
711 	switch (valtype) {
712 	case MOD_PROP_PERM:
713 		nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
714 		break;
715 	case MOD_PROP_DEFAULT:
716 	case MOD_PROP_POSSIBLE:
717 		return (i_ipadm_get_prop(iph, arg, pdp, buf, bufsize,
718 		    proto, valtype));
719 	case MOD_PROP_ACTIVE:
720 		bzero(&lifr, sizeof (lifr));
721 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
722 		s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
723 
724 		if (ioctl(s, SIOCGLIFMTU, (caddr_t)&lifr) < 0)
725 			return (ipadm_errno2status(errno));
726 		nbytes = snprintf(buf, *bufsize, "%u", lifr.lifr_mtu);
727 		break;
728 	default:
729 		return (IPADM_INVALID_ARG);
730 	}
731 	if (nbytes >= *bufsize) {
732 		/* insufficient buffer space */
733 		*bufsize = nbytes + 1;
734 		return (IPADM_NO_BUFS);
735 	}
736 	return (IPADM_SUCCESS);
737 }
738 
739 /* ARGSUSED */
740 static ipadm_status_t
741 i_ipadm_get_metric(ipadm_handle_t iph, const void *arg,
742     ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
743     uint_t valtype)
744 {
745 	struct lifreq	lifr;
746 	const char	*ifname = arg;
747 	size_t		nbytes;
748 	int		s, val;
749 
750 	switch (valtype) {
751 	case MOD_PROP_PERM:
752 		val = MOD_PROP_PERM_RW;
753 		break;
754 	case MOD_PROP_DEFAULT:
755 		val = DEF_METRIC_VAL;
756 		break;
757 	case MOD_PROP_ACTIVE:
758 		bzero(&lifr, sizeof (lifr));
759 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
760 
761 		s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
762 		if (ioctl(s, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0)
763 			return (ipadm_errno2status(errno));
764 		val = lifr.lifr_metric;
765 		break;
766 	default:
767 		return (IPADM_INVALID_ARG);
768 	}
769 	nbytes = snprintf(buf, *bufsize, "%d", val);
770 	if (nbytes >= *bufsize) {
771 		/* insufficient buffer space */
772 		*bufsize = nbytes + 1;
773 		return (IPADM_NO_BUFS);
774 	}
775 
776 	return (IPADM_SUCCESS);
777 }
778 
779 /* ARGSUSED */
780 static ipadm_status_t
781 i_ipadm_get_usesrc(ipadm_handle_t iph, const void *arg,
782     ipadm_prop_desc_t *ipd, char *buf, uint_t *bufsize, uint_t proto,
783     uint_t valtype)
784 {
785 	struct lifreq	lifr;
786 	const char	*ifname = arg;
787 	int		s;
788 	char 		if_name[IF_NAMESIZE];
789 	size_t		nbytes;
790 
791 	switch (valtype) {
792 	case MOD_PROP_PERM:
793 		nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
794 		break;
795 	case MOD_PROP_DEFAULT:
796 		nbytes = snprintf(buf, *bufsize, "%s", IPADM_NONESTR);
797 		break;
798 	case MOD_PROP_ACTIVE:
799 		bzero(&lifr, sizeof (lifr));
800 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
801 
802 		s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
803 		if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) < 0)
804 			return (ipadm_errno2status(errno));
805 		if (lifr.lifr_index == 0) {
806 			/* no src address was set, so print 'none' */
807 			(void) strlcpy(if_name, IPADM_NONESTR,
808 			    sizeof (if_name));
809 		} else if (if_indextoname(lifr.lifr_index, if_name) == NULL) {
810 			return (ipadm_errno2status(errno));
811 		}
812 		nbytes = snprintf(buf, *bufsize, "%s", if_name);
813 		break;
814 	default:
815 		return (IPADM_INVALID_ARG);
816 	}
817 	if (nbytes >= *bufsize) {
818 		/* insufficient buffer space */
819 		*bufsize = nbytes + 1;
820 		return (IPADM_NO_BUFS);
821 	}
822 	return (IPADM_SUCCESS);
823 }
824 
825 /* ARGSUSED */
826 static ipadm_status_t
827 i_ipadm_get_ifprop_flags(ipadm_handle_t iph, const void *arg,
828     ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
829     uint_t valtype)
830 {
831 	uint64_t 	intf_flags;
832 	char 		*val;
833 	size_t		nbytes;
834 	const char	*ifname = arg;
835 	sa_family_t	af;
836 	ipadm_status_t	status = IPADM_SUCCESS;
837 
838 	switch (valtype) {
839 	case MOD_PROP_PERM:
840 		nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
841 		break;
842 	case MOD_PROP_DEFAULT:
843 		if (strcmp(pdp->ipd_name, "exchange_routes") == 0 ||
844 		    strcmp(pdp->ipd_name, "arp") == 0 ||
845 		    strcmp(pdp->ipd_name, "nud") == 0) {
846 			val = IPADM_ONSTR;
847 		} else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
848 			val = IPADM_OFFSTR;
849 		} else {
850 			return (IPADM_PROP_UNKNOWN);
851 		}
852 		nbytes = snprintf(buf, *bufsize, "%s", val);
853 		break;
854 	case MOD_PROP_ACTIVE:
855 		af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET);
856 		status = i_ipadm_get_flags(iph, ifname, af, &intf_flags);
857 		if (status != IPADM_SUCCESS)
858 			return (status);
859 
860 		val = IPADM_OFFSTR;
861 		if (strcmp(pdp->ipd_name, "exchange_routes") == 0) {
862 			if (!(intf_flags & IFF_NORTEXCH))
863 				val = IPADM_ONSTR;
864 		} else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
865 			if (intf_flags & IFF_ROUTER)
866 				val = IPADM_ONSTR;
867 		} else if (strcmp(pdp->ipd_name, "arp") == 0) {
868 			if (!(intf_flags & IFF_NOARP))
869 				val = IPADM_ONSTR;
870 		} else if (strcmp(pdp->ipd_name, "nud") == 0) {
871 			if (!(intf_flags & IFF_NONUD))
872 				val = IPADM_ONSTR;
873 		}
874 		nbytes = snprintf(buf, *bufsize, "%s", val);
875 		break;
876 	default:
877 		return (IPADM_INVALID_ARG);
878 	}
879 	if (nbytes >= *bufsize) {
880 		/* insufficient buffer space */
881 		*bufsize = nbytes + 1;
882 		status = IPADM_NO_BUFS;
883 	}
884 
885 	return (status);
886 }
887 
888 static void
889 i_ipadm_perm2str(char *buf, uint_t *bufsize)
890 {
891 	uint_t perm = atoi(buf);
892 
893 	(void) snprintf(buf, *bufsize, "%c%c",
894 	    ((perm & MOD_PROP_PERM_READ) != 0) ? 'r' : '-',
895 	    ((perm & MOD_PROP_PERM_WRITE) != 0) ? 'w' : '-');
896 }
897 
898 /* ARGSUSED */
899 static ipadm_status_t
900 i_ipadm_get_prop(ipadm_handle_t iph, const void *arg,
901     ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
902     uint_t valtype)
903 {
904 	ipadm_status_t	status = IPADM_SUCCESS;
905 	const char	*ifname = arg;
906 	mod_ioc_prop_t	*mip;
907 	char 		*pname = pdp->ipd_name;
908 	uint_t		iocsize;
909 
910 	/* allocate sufficient ioctl buffer to retrieve value */
911 	iocsize = sizeof (mod_ioc_prop_t) + *bufsize - 1;
912 	if ((mip = calloc(1, iocsize)) == NULL)
913 		return (IPADM_NO_BUFS);
914 
915 	mip->mpr_version = MOD_PROP_VERSION;
916 	mip->mpr_flags = valtype;
917 	mip->mpr_proto = proto;
918 	if (ifname != NULL) {
919 		(void) strlcpy(mip->mpr_ifname, ifname,
920 		    sizeof (mip->mpr_ifname));
921 	}
922 	(void) strlcpy(mip->mpr_name, pname, sizeof (mip->mpr_name));
923 	mip->mpr_valsize = *bufsize;
924 
925 	if (i_ipadm_strioctl(iph->iph_sock, SIOCGETPROP, (char *)mip,
926 	    iocsize) < 0) {
927 		if (errno == ENOENT)
928 			status = IPADM_PROP_UNKNOWN;
929 		else
930 			status = ipadm_errno2status(errno);
931 	} else {
932 		bcopy(mip->mpr_val, buf, *bufsize);
933 	}
934 
935 	free(mip);
936 	return (status);
937 }
938 
939 /*
940  * populates the ipmgmt_prop_arg_t based on the class of property.
941  */
942 static void
943 i_ipadm_populate_proparg(ipmgmt_prop_arg_t *pargp, ipadm_prop_desc_t *pdp,
944     const char *pval, const void *object)
945 {
946 	const struct ipadm_addrobj_s *ipaddr;
947 	uint_t		class = pdp->ipd_class;
948 	uint_t		proto = pdp->ipd_proto;
949 
950 	(void) strlcpy(pargp->ia_pname, pdp->ipd_name,
951 	    sizeof (pargp->ia_pname));
952 	if (pval != NULL)
953 		(void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval));
954 
955 	switch (class) {
956 	case IPADMPROP_CLASS_MODULE:
957 		(void) strlcpy(pargp->ia_module, object,
958 		    sizeof (pargp->ia_module));
959 		break;
960 	case IPADMPROP_CLASS_MODIF:
961 		/* check if object is protostr or an ifname */
962 		if (ipadm_str2proto(object) != MOD_PROTO_NONE) {
963 			(void) strlcpy(pargp->ia_module, object,
964 			    sizeof (pargp->ia_module));
965 			break;
966 		}
967 		/* it's an interface property, fall through */
968 		/* FALLTHRU */
969 	case IPADMPROP_CLASS_IF:
970 		(void) strlcpy(pargp->ia_ifname, object,
971 		    sizeof (pargp->ia_ifname));
972 		(void) strlcpy(pargp->ia_module, ipadm_proto2str(proto),
973 		    sizeof (pargp->ia_module));
974 		break;
975 	case IPADMPROP_CLASS_ADDR:
976 		ipaddr = object;
977 		(void) strlcpy(pargp->ia_ifname, ipaddr->ipadm_ifname,
978 		    sizeof (pargp->ia_ifname));
979 		(void) strlcpy(pargp->ia_aobjname, ipaddr->ipadm_aobjname,
980 		    sizeof (pargp->ia_aobjname));
981 		break;
982 	}
983 }
984 
985 /*
986  * Common function to retrieve property value for a given interface `ifname' or
987  * for a given protocol `proto'. The property name is in `pname'.
988  *
989  * `valtype' determines the type of value that will be retrieved.
990  * 	IPADM_OPT_ACTIVE -	current value of the property (active config)
991  *	IPADM_OPT_PERSIST -	value of the property from persistent store
992  *	IPADM_OPT_DEFAULT -	default hard coded value (boot-time value)
993  *	IPADM_OPT_PERM -	read/write permissions for the value
994  *	IPADM_OPT_POSSIBLE -	range of values
995  */
996 static ipadm_status_t
997 i_ipadm_getprop_common(ipadm_handle_t iph, const char *ifname,
998     const char *pname, char *buf, uint_t *bufsize, uint_t proto,
999     uint_t valtype)
1000 {
1001 	ipadm_status_t		status = IPADM_SUCCESS;
1002 	ipadm_prop_desc_t	*pdp, *pdtbl;
1003 	char			priv_propname[MAXPROPNAMELEN];
1004 	boolean_t		matched_name = B_FALSE;
1005 	boolean_t		is_if = (ifname != NULL);
1006 
1007 	pdtbl = i_ipadm_get_propdesc_table(proto);
1008 
1009 	/*
1010 	 * We already checked for supported protocol,
1011 	 * pdtbl better not be NULL.
1012 	 */
1013 	assert(pdtbl != NULL);
1014 
1015 	for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) {
1016 		if (strcmp(pname, pdp->ipd_name) == 0) {
1017 			matched_name = B_TRUE;
1018 			if (proto == pdp->ipd_proto)
1019 				break;
1020 		}
1021 	}
1022 
1023 	if (pdp->ipd_name != NULL) {
1024 		/*
1025 		 * check whether the property can be
1026 		 * applied on an interface
1027 		 */
1028 		if (is_if && !(pdp->ipd_class & IPADMPROP_CLASS_IF))
1029 			return (IPADM_INVALID_ARG);
1030 		/*
1031 		 * check whether the property can be
1032 		 * applied on a module
1033 		 */
1034 		if (!is_if && !(pdp->ipd_class & IPADMPROP_CLASS_MODULE))
1035 			return (IPADM_INVALID_ARG);
1036 
1037 	} else {
1038 		/*
1039 		 * if we matched name, but failed protocol check,
1040 		 * then return error
1041 		 */
1042 		if (matched_name)
1043 			return (IPADM_INVALID_ARG);
1044 
1045 		/* there are no private interface properties */
1046 		if (is_if)
1047 			return (IPADM_PROP_UNKNOWN);
1048 
1049 		/* private protocol properties, pass it to kernel directly */
1050 		pdp = &ipadm_privprop;
1051 		(void) strlcpy(priv_propname, pname, sizeof (priv_propname));
1052 		pdp->ipd_name = priv_propname;
1053 	}
1054 
1055 	switch (valtype) {
1056 	case IPADM_OPT_PERM:
1057 		status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto,
1058 		    MOD_PROP_PERM);
1059 		if (status == IPADM_SUCCESS)
1060 			i_ipadm_perm2str(buf, bufsize);
1061 		break;
1062 	case IPADM_OPT_ACTIVE:
1063 		status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto,
1064 		    MOD_PROP_ACTIVE);
1065 		break;
1066 	case IPADM_OPT_DEFAULT:
1067 		status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto,
1068 		    MOD_PROP_DEFAULT);
1069 		break;
1070 	case IPADM_OPT_POSSIBLE:
1071 		if (pdp->ipd_get_range != NULL) {
1072 			status = pdp->ipd_get_range(iph, ifname, pdp, buf,
1073 			    bufsize, proto, MOD_PROP_POSSIBLE);
1074 			break;
1075 		}
1076 		buf[0] = '\0';
1077 		break;
1078 	case IPADM_OPT_PERSIST:
1079 		/* retrieve from database */
1080 		if (is_if)
1081 			status = i_ipadm_get_persist_propval(iph, pdp, buf,
1082 			    bufsize, ifname);
1083 		else
1084 			status = i_ipadm_get_persist_propval(iph, pdp, buf,
1085 			    bufsize, ipadm_proto2str(proto));
1086 		break;
1087 	default:
1088 		status = IPADM_INVALID_ARG;
1089 		break;
1090 	}
1091 	return (status);
1092 }
1093 
1094 /*
1095  * Get protocol property of the specified protocol.
1096  */
1097 ipadm_status_t
1098 ipadm_get_prop(ipadm_handle_t iph, const char *pname, char *buf,
1099     uint_t *bufsize, uint_t proto, uint_t valtype)
1100 {
1101 	/*
1102 	 * validate the arguments of the function.
1103 	 */
1104 	if (iph == NULL || pname == NULL || buf == NULL ||
1105 	    bufsize == NULL || *bufsize == 0) {
1106 		return (IPADM_INVALID_ARG);
1107 	}
1108 	/*
1109 	 * Do we support this proto, if not return error.
1110 	 */
1111 	if (ipadm_proto2str(proto) == NULL)
1112 		return (IPADM_NOTSUP);
1113 
1114 	return (i_ipadm_getprop_common(iph, NULL, pname, buf, bufsize,
1115 	    proto, valtype));
1116 }
1117 
1118 /*
1119  * Get interface property of the specified interface.
1120  */
1121 ipadm_status_t
1122 ipadm_get_ifprop(ipadm_handle_t iph, const char *ifname, const char *pname,
1123     char *buf, uint_t *bufsize, uint_t proto, uint_t valtype)
1124 {
1125 	/* validate the arguments of the function. */
1126 	if (iph == NULL || pname == NULL || buf == NULL ||
1127 	    bufsize == NULL || *bufsize == 0) {
1128 		return (IPADM_INVALID_ARG);
1129 	}
1130 
1131 	/* Do we support this proto, if not return error. */
1132 	if (ipadm_proto2str(proto) == NULL)
1133 		return (IPADM_NOTSUP);
1134 
1135 	/*
1136 	 * check if interface name is provided for interface property and
1137 	 * is valid.
1138 	 */
1139 	if (!i_ipadm_validate_ifname(iph, ifname))
1140 		return (IPADM_INVALID_ARG);
1141 
1142 	return (i_ipadm_getprop_common(iph, ifname, pname, buf, bufsize,
1143 	    proto, valtype));
1144 }
1145 
1146 /*
1147  * Allocates sufficient ioctl buffers and copies property name and the
1148  * value, among other things. If the flag IPADM_OPT_DEFAULT is set, then
1149  * `pval' will be NULL and it instructs the kernel to reset the current
1150  * value to property's default value.
1151  */
1152 static ipadm_status_t
1153 i_ipadm_set_prop(ipadm_handle_t iph, const void *arg,
1154     ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
1155 {
1156 	ipadm_status_t	status = IPADM_SUCCESS;
1157 	const char	*ifname = arg;
1158 	mod_ioc_prop_t 	*mip;
1159 	char 		*pname = pdp->ipd_name;
1160 	uint_t 		valsize, iocsize;
1161 	uint_t		iocflags = 0;
1162 
1163 	if (flags & IPADM_OPT_DEFAULT) {
1164 		iocflags |= MOD_PROP_DEFAULT;
1165 	} else if (flags & IPADM_OPT_ACTIVE) {
1166 		iocflags |= MOD_PROP_ACTIVE;
1167 		if (flags & IPADM_OPT_APPEND)
1168 			iocflags |= MOD_PROP_APPEND;
1169 		else if (flags & IPADM_OPT_REMOVE)
1170 			iocflags |= MOD_PROP_REMOVE;
1171 	}
1172 
1173 	if (pval != NULL) {
1174 		valsize = strlen(pval);
1175 		iocsize = sizeof (mod_ioc_prop_t) + valsize - 1;
1176 	} else {
1177 		valsize = 0;
1178 		iocsize = sizeof (mod_ioc_prop_t);
1179 	}
1180 
1181 	if ((mip = calloc(1, iocsize)) == NULL)
1182 		return (IPADM_NO_BUFS);
1183 
1184 	mip->mpr_version = MOD_PROP_VERSION;
1185 	mip->mpr_flags = iocflags;
1186 	mip->mpr_proto = proto;
1187 	if (ifname != NULL) {
1188 		(void) strlcpy(mip->mpr_ifname, ifname,
1189 		    sizeof (mip->mpr_ifname));
1190 	}
1191 
1192 	(void) strlcpy(mip->mpr_name, pname, sizeof (mip->mpr_name));
1193 	mip->mpr_valsize = valsize;
1194 	if (pval != NULL)
1195 		bcopy(pval, mip->mpr_val, valsize);
1196 
1197 	if (i_ipadm_strioctl(iph->iph_sock, SIOCSETPROP, (char *)mip,
1198 	    iocsize) < 0) {
1199 		if (errno == ENOENT)
1200 			status = IPADM_PROP_UNKNOWN;
1201 		else
1202 			status = ipadm_errno2status(errno);
1203 	}
1204 	free(mip);
1205 	return (status);
1206 }
1207 
1208 /*
1209  * Common function for modifying both protocol/interface property.
1210  *
1211  * If:
1212  *   IPADM_OPT_PERSIST is set then the value is persisted.
1213  *   IPADM_OPT_DEFAULT is set then the default value for the property will
1214  *		       be applied.
1215  */
1216 static ipadm_status_t
1217 i_ipadm_setprop_common(ipadm_handle_t iph, const char *ifname,
1218     const char *pname, const char *buf, uint_t proto, uint_t pflags)
1219 {
1220 	ipadm_status_t		status = IPADM_SUCCESS;
1221 	boolean_t 		persist = (pflags & IPADM_OPT_PERSIST);
1222 	boolean_t		reset = (pflags & IPADM_OPT_DEFAULT);
1223 	ipadm_prop_desc_t	*pdp, *pdtbl;
1224 	boolean_t		is_if = (ifname != NULL);
1225 	char			priv_propname[MAXPROPNAMELEN];
1226 	boolean_t		matched_name = B_FALSE;
1227 
1228 	/* Check that property value is within the allowed size */
1229 	if (!reset && strnlen(buf, MAXPROPVALLEN) >= MAXPROPVALLEN)
1230 		return (IPADM_INVALID_ARG);
1231 
1232 	pdtbl = i_ipadm_get_propdesc_table(proto);
1233 	/*
1234 	 * We already checked for supported protocol,
1235 	 * pdtbl better not be NULL.
1236 	 */
1237 	assert(pdtbl != NULL);
1238 
1239 	/* Walk through the property table to match the given property name */
1240 	for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) {
1241 		/*
1242 		 * we find the entry which matches <pname, proto> tuple
1243 		 */
1244 		if (strcmp(pname, pdp->ipd_name) == 0) {
1245 			matched_name = B_TRUE;
1246 			if (pdp->ipd_proto == proto)
1247 				break;
1248 		}
1249 	}
1250 
1251 	if (pdp->ipd_name != NULL) {
1252 		/* do some sanity checks */
1253 		if (is_if) {
1254 			if (!(pdp->ipd_class & IPADMPROP_CLASS_IF))
1255 				return (IPADM_INVALID_ARG);
1256 		} else {
1257 			if (!(pdp->ipd_class & IPADMPROP_CLASS_MODULE))
1258 				return (IPADM_INVALID_ARG);
1259 		}
1260 		/*
1261 		 * if the property is not multi-valued and IPADM_OPT_APPEND or
1262 		 * IPADM_OPT_REMOVE is specified, return IPADM_INVALID_ARG.
1263 		 */
1264 		if (!(pdp->ipd_flags & IPADMPROP_MULVAL) && (pflags &
1265 		    (IPADM_OPT_APPEND|IPADM_OPT_REMOVE))) {
1266 			return (IPADM_INVALID_ARG);
1267 		}
1268 	} else {
1269 		/*
1270 		 * if we matched name, but failed protocol check,
1271 		 * then return error.
1272 		 */
1273 		if (matched_name)
1274 			return (IPADM_BAD_PROTOCOL);
1275 
1276 		/* Possibly a private property, pass it to kernel directly */
1277 
1278 		/* there are no private interface properties */
1279 		if (is_if)
1280 			return (IPADM_PROP_UNKNOWN);
1281 
1282 		pdp = &ipadm_privprop;
1283 		(void) strlcpy(priv_propname, pname, sizeof (priv_propname));
1284 		pdp->ipd_name = priv_propname;
1285 	}
1286 
1287 	status = pdp->ipd_set(iph, ifname, pdp, buf, proto, pflags);
1288 	if (status != IPADM_SUCCESS)
1289 		return (status);
1290 
1291 	if (persist) {
1292 		if (is_if)
1293 			status = i_ipadm_persist_propval(iph, pdp, buf, ifname,
1294 			    pflags);
1295 		else
1296 			status = i_ipadm_persist_propval(iph, pdp, buf,
1297 			    ipadm_proto2str(proto), pflags);
1298 	}
1299 	return (status);
1300 }
1301 
1302 /*
1303  * Sets the property value of the specified interface
1304  */
1305 ipadm_status_t
1306 ipadm_set_ifprop(ipadm_handle_t iph, const char *ifname, const char *pname,
1307     const char *buf, uint_t proto, uint_t pflags)
1308 {
1309 	boolean_t	reset = (pflags & IPADM_OPT_DEFAULT);
1310 	ipadm_status_t	status;
1311 
1312 	/* check for solaris.network.interface.config authorization */
1313 	if (!ipadm_check_auth())
1314 		return (IPADM_EAUTH);
1315 	/*
1316 	 * validate the arguments of the function.
1317 	 */
1318 	if (iph == NULL || pname == NULL || (!reset && buf == NULL) ||
1319 	    pflags == 0 || pflags == IPADM_OPT_PERSIST ||
1320 	    (pflags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_DEFAULT))) {
1321 		return (IPADM_INVALID_ARG);
1322 	}
1323 
1324 	/*
1325 	 * Do we support this protocol, if not return error.
1326 	 */
1327 	if (ipadm_proto2str(proto) == NULL)
1328 		return (IPADM_NOTSUP);
1329 
1330 	/*
1331 	 * Validate the interface and check if a persistent
1332 	 * operation is performed on a temporary object.
1333 	 */
1334 	status = i_ipadm_validate_if(iph, ifname, proto, pflags);
1335 	if (status != IPADM_SUCCESS)
1336 		return (status);
1337 
1338 	return (i_ipadm_setprop_common(iph, ifname, pname, buf, proto,
1339 	    pflags));
1340 }
1341 
1342 /*
1343  * Sets the property value of the specified protocol.
1344  */
1345 ipadm_status_t
1346 ipadm_set_prop(ipadm_handle_t iph, const char *pname, const char *buf,
1347     uint_t proto, uint_t pflags)
1348 {
1349 	boolean_t	reset = (pflags & IPADM_OPT_DEFAULT);
1350 
1351 	/* check for solaris.network.interface.config authorization */
1352 	if (!ipadm_check_auth())
1353 		return (IPADM_EAUTH);
1354 	/*
1355 	 * validate the arguments of the function.
1356 	 */
1357 	if (iph == NULL || pname == NULL ||(!reset && buf == NULL) ||
1358 	    pflags == 0 || pflags == IPADM_OPT_PERSIST ||
1359 	    (pflags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_DEFAULT|
1360 	    IPADM_OPT_APPEND|IPADM_OPT_REMOVE))) {
1361 		return (IPADM_INVALID_ARG);
1362 	}
1363 
1364 	/*
1365 	 * Do we support this proto, if not return error.
1366 	 */
1367 	if (ipadm_proto2str(proto) == NULL)
1368 		return (IPADM_NOTSUP);
1369 
1370 	return (i_ipadm_setprop_common(iph, NULL, pname, buf, proto,
1371 	    pflags));
1372 }
1373 
1374 /* helper function for ipadm_walk_proptbl */
1375 static void
1376 i_ipadm_walk_proptbl(ipadm_prop_desc_t *pdtbl, uint_t proto, uint_t class,
1377     ipadm_prop_wfunc_t *func, void *arg)
1378 {
1379 	ipadm_prop_desc_t	*pdp;
1380 
1381 	for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) {
1382 		if (!(pdp->ipd_class & class))
1383 			continue;
1384 
1385 		if (proto != MOD_PROTO_NONE && !(pdp->ipd_proto & proto))
1386 			continue;
1387 
1388 		/*
1389 		 * we found a class specific match, call the
1390 		 * user callback function.
1391 		 */
1392 		if (func(arg, pdp->ipd_name, pdp->ipd_proto) == B_FALSE)
1393 			break;
1394 	}
1395 }
1396 
1397 /*
1398  * Walks through all the properties, for a given protocol and property class
1399  * (protocol or interface).
1400  *
1401  * Further if proto == MOD_PROTO_NONE, then it walks through all the supported
1402  * protocol property tables.
1403  */
1404 ipadm_status_t
1405 ipadm_walk_proptbl(uint_t proto, uint_t class, ipadm_prop_wfunc_t *func,
1406     void *arg)
1407 {
1408 	ipadm_prop_desc_t	*pdtbl;
1409 	ipadm_status_t		status = IPADM_SUCCESS;
1410 	int			i;
1411 	int			count = A_CNT(protocols);
1412 
1413 	if (func == NULL)
1414 		return (IPADM_INVALID_ARG);
1415 
1416 	switch (class) {
1417 	case IPADMPROP_CLASS_ADDR:
1418 		pdtbl = ipadm_addrprop_table;
1419 		break;
1420 	case IPADMPROP_CLASS_IF:
1421 	case IPADMPROP_CLASS_MODULE:
1422 		pdtbl = i_ipadm_get_propdesc_table(proto);
1423 		if (pdtbl == NULL && proto != MOD_PROTO_NONE)
1424 			return (IPADM_INVALID_ARG);
1425 		break;
1426 	default:
1427 		return (IPADM_INVALID_ARG);
1428 	}
1429 
1430 	if (pdtbl != NULL) {
1431 		/*
1432 		 * proto will be MOD_PROTO_NONE in the case of
1433 		 * IPADMPROP_CLASS_ADDR.
1434 		 */
1435 		i_ipadm_walk_proptbl(pdtbl, proto, class, func, arg);
1436 	} else {
1437 		/* Walk thru all the protocol tables, we support */
1438 		for (i = 0; i < count; i++) {
1439 			pdtbl = i_ipadm_get_propdesc_table(protocols[i]);
1440 			i_ipadm_walk_proptbl(pdtbl, protocols[i], class, func,
1441 			    arg);
1442 		}
1443 	}
1444 	return (status);
1445 }
1446 
1447 /*
1448  * Given a property name, walks through all the instances of a property name.
1449  * Some properties have two instances one for v4 interfaces and another for v6
1450  * interfaces. For example: MTU. MTU can have different values for v4 and v6.
1451  * Therefore there are two properties for 'MTU'.
1452  *
1453  * This function invokes `func' for every instance of property `pname'
1454  */
1455 ipadm_status_t
1456 ipadm_walk_prop(const char *pname, uint_t proto, uint_t class,
1457     ipadm_prop_wfunc_t *func, void *arg)
1458 {
1459 	ipadm_prop_desc_t	*pdtbl, *pdp;
1460 	ipadm_status_t		status = IPADM_SUCCESS;
1461 	boolean_t		matched = B_FALSE;
1462 
1463 	if (pname == NULL || func == NULL)
1464 		return (IPADM_INVALID_ARG);
1465 
1466 	switch (class) {
1467 	case IPADMPROP_CLASS_ADDR:
1468 		pdtbl = ipadm_addrprop_table;
1469 		break;
1470 	case IPADMPROP_CLASS_IF:
1471 	case IPADMPROP_CLASS_MODULE:
1472 		pdtbl = i_ipadm_get_propdesc_table(proto);
1473 		break;
1474 	default:
1475 		return (IPADM_INVALID_ARG);
1476 	}
1477 
1478 	if (pdtbl == NULL)
1479 		return (IPADM_INVALID_ARG);
1480 
1481 	for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) {
1482 		if (strcmp(pname, pdp->ipd_name) != 0)
1483 			continue;
1484 		if (!(pdp->ipd_proto & proto))
1485 			continue;
1486 		matched = B_TRUE;
1487 		/* we found a match, call the callback function */
1488 		if (func(arg, pdp->ipd_name, pdp->ipd_proto) == B_FALSE)
1489 			break;
1490 	}
1491 	if (!matched)
1492 		status = IPADM_PROP_UNKNOWN;
1493 	return (status);
1494 }
1495 
1496 /* ARGSUSED */
1497 ipadm_status_t
1498 i_ipadm_get_onoff(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *dp,
1499     char *buf, uint_t *bufsize, uint_t proto, uint_t valtype)
1500 {
1501 	(void) snprintf(buf, *bufsize, "%s,%s", IPADM_ONSTR, IPADM_OFFSTR);
1502 	return (IPADM_SUCCESS);
1503 }
1504 
1505 /*
1506  * Makes a door call to ipmgmtd to retrieve the persisted property value
1507  */
1508 ipadm_status_t
1509 i_ipadm_get_persist_propval(ipadm_handle_t iph, ipadm_prop_desc_t *pdp,
1510     char *gbuf, uint_t *gbufsize, const void *object)
1511 {
1512 	ipmgmt_prop_arg_t	parg;
1513 	ipmgmt_getprop_rval_t	rval, *rvalp;
1514 	size_t			nbytes;
1515 	int			err = 0;
1516 
1517 	bzero(&parg, sizeof (parg));
1518 	parg.ia_cmd = IPMGMT_CMD_GETPROP;
1519 	i_ipadm_populate_proparg(&parg, pdp, NULL, object);
1520 
1521 	rvalp = &rval;
1522 	err = ipadm_door_call(iph, &parg, sizeof (parg), (void **)&rvalp,
1523 	    sizeof (rval), B_FALSE);
1524 	if (err == 0) {
1525 		/* assert that rvalp was not reallocated */
1526 		assert(rvalp == &rval);
1527 
1528 		/* `ir_pval' contains the property value */
1529 		nbytes = snprintf(gbuf, *gbufsize, "%s", rvalp->ir_pval);
1530 		if (nbytes >= *gbufsize) {
1531 			/* insufficient buffer space */
1532 			*gbufsize = nbytes + 1;
1533 			err = ENOBUFS;
1534 		}
1535 	}
1536 	return (ipadm_errno2status(err));
1537 }
1538 
1539 /*
1540  * Persists the property value for a given property in the data store
1541  */
1542 ipadm_status_t
1543 i_ipadm_persist_propval(ipadm_handle_t iph, ipadm_prop_desc_t *pdp,
1544     const char *pval, const void *object, uint_t flags)
1545 {
1546 	ipmgmt_prop_arg_t	parg;
1547 	int			err = 0;
1548 
1549 	bzero(&parg, sizeof (parg));
1550 	i_ipadm_populate_proparg(&parg, pdp, pval, object);
1551 
1552 	/*
1553 	 * Check if value to be persisted need to be appended or removed. This
1554 	 * is required for multi-valued property.
1555 	 */
1556 	if (flags & IPADM_OPT_APPEND)
1557 		parg.ia_flags |= IPMGMT_APPEND;
1558 	if (flags & IPADM_OPT_REMOVE)
1559 		parg.ia_flags |= IPMGMT_REMOVE;
1560 
1561 	if (flags & (IPADM_OPT_DEFAULT|IPADM_OPT_REMOVE))
1562 		parg.ia_cmd = IPMGMT_CMD_RESETPROP;
1563 	else
1564 		parg.ia_cmd = IPMGMT_CMD_SETPROP;
1565 
1566 	err = ipadm_door_call(iph, &parg, sizeof (parg), NULL, 0, B_FALSE);
1567 
1568 	/*
1569 	 * its fine if there were no entry in the DB to delete. The user
1570 	 * might be changing property value, which was not changed
1571 	 * persistently.
1572 	 */
1573 	if (err == ENOENT)
1574 		err = 0;
1575 	return (ipadm_errno2status(err));
1576 }
1577 
1578 /*
1579  * Called during boot.
1580  *
1581  * Walk through the DB and apply all the global module properties. We plow
1582  * through the DB even if we fail to apply property.
1583  */
1584 /* ARGSUSED */
1585 boolean_t
1586 ipadm_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen,
1587     int *errp)
1588 {
1589 	ipadm_handle_t	iph = cbarg;
1590 	nvpair_t	*nvp, *pnvp;
1591 	char		*strval = NULL, *name, *mod = NULL;
1592 	uint_t		proto;
1593 
1594 	/*
1595 	 * We could have used nvl_exists() directly, however we need several
1596 	 * calls to it and each call traverses the list. Since this codepath
1597 	 * is exercised during boot, let's traverse the list ourselves and do
1598 	 * the necessary checks.
1599 	 */
1600 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1601 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1602 		name = nvpair_name(nvp);
1603 		if (IPADM_PRIV_NVP(name)) {
1604 			if (strcmp(name, IPADM_NVP_IFNAME) == 0 ||
1605 			    strcmp(name, IPADM_NVP_AOBJNAME) == 0)
1606 				return (B_TRUE);
1607 			else if (strcmp(name, IPADM_NVP_PROTONAME) == 0 &&
1608 			    nvpair_value_string(nvp, &mod) != 0)
1609 				return (B_TRUE);
1610 		} else {
1611 			/* possible a property */
1612 			pnvp = nvp;
1613 		}
1614 	}
1615 
1616 	/* if we are here than we found a global property */
1617 	assert(mod != NULL);
1618 	assert(nvpair_type(pnvp) == DATA_TYPE_STRING);
1619 
1620 	proto = ipadm_str2proto(mod);
1621 	if (nvpair_value_string(pnvp, &strval) == 0) {
1622 		(void) ipadm_set_prop(iph, name, strval, proto,
1623 		    IPADM_OPT_ACTIVE);
1624 	}
1625 
1626 	return (B_TRUE);
1627 }
1628 
1629 /* initialize global module properties */
1630 ipadm_status_t
1631 ipadm_init_prop()
1632 {
1633 	ipadm_handle_t	iph = NULL;
1634 	ipadm_status_t	status;
1635 	int		err;
1636 
1637 	/* check for solaris.network.interface.config authorization */
1638 	if (!ipadm_check_auth())
1639 		return (IPADM_EAUTH);
1640 
1641 	if ((status = ipadm_open(&iph, IPH_INIT)) != IPADM_SUCCESS)
1642 		return (status);
1643 
1644 	err = ipadm_rw_db(ipadm_db_init, iph, IPADM_DB_FILE, IPADM_FILE_MODE,
1645 	    IPADM_DB_READ);
1646 
1647 	ipadm_close(iph);
1648 	return (ipadm_errno2status(err));
1649 }
1650 
1651 /*
1652  * This is called from ipadm_set_ifprop() to validate the set operation.
1653  * It does the following steps:
1654  * 1. Validates the interface name.
1655  * 2. Fails if it is an IPMP meta-interface or an underlying interface.
1656  * 3. In case of a persistent operation, verifies that the
1657  *	interface is persistent.
1658  */
1659 static ipadm_status_t
1660 i_ipadm_validate_if(ipadm_handle_t iph, const char *ifname,
1661     uint_t proto, uint_t flags)
1662 {
1663 	sa_family_t	af, other_af;
1664 	ipadm_status_t	status;
1665 	boolean_t	p_exists;
1666 	boolean_t	af_exists, other_af_exists, a_exists;
1667 
1668 	/* Check if the interface name is valid. */
1669 	if (!i_ipadm_validate_ifname(iph, ifname))
1670 		return (IPADM_INVALID_ARG);
1671 
1672 	af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET);
1673 	/*
1674 	 * Setting properties on an IPMP meta-interface or underlying
1675 	 * interface is not supported.
1676 	 */
1677 	if (i_ipadm_is_ipmp(iph, ifname) || i_ipadm_is_under_ipmp(iph, ifname))
1678 		return (IPADM_NOTSUP);
1679 
1680 	/* Check if interface exists in the persistent configuration. */
1681 	status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
1682 	if (status != IPADM_SUCCESS)
1683 		return (status);
1684 
1685 	/* Check if interface exists in the active configuration. */
1686 	af_exists = ipadm_if_enabled(iph, ifname, af);
1687 	other_af = (af == AF_INET ? AF_INET6 : AF_INET);
1688 	other_af_exists = ipadm_if_enabled(iph, ifname, other_af);
1689 	a_exists = (af_exists || other_af_exists);
1690 	if (!a_exists && p_exists)
1691 		return (IPADM_OP_DISABLE_OBJ);
1692 	if (!af_exists)
1693 		return (IPADM_ENXIO);
1694 
1695 	/*
1696 	 * If a persistent operation is requested, check if the underlying
1697 	 * IP interface is persistent.
1698 	 */
1699 	if ((flags & IPADM_OPT_PERSIST) && !p_exists)
1700 		return (IPADM_TEMPORARY_OBJ);
1701 	return (IPADM_SUCCESS);
1702 }
1703