xref: /illumos-gate/usr/src/lib/libdladm/common/libdloverlay.c (revision 533affcbc7fc4d0c8132976ea454aaa715fe2307)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2015 Joyent, Inc.
14  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
15  */
16 
17 #include <libdladm_impl.h>
18 #include <libdllink.h>
19 #include <libdloverlay.h>
20 #include <sys/dld.h>
21 #include <sys/overlay.h>
22 #include <strings.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <limits.h>
29 #include <libscf.h>
30 #include <libvarpd_client.h>
31 
32 #define	VARPD_PROPERTY_NAME	"varpd/id"
33 #define	VARPD_SERVICE		"network/varpd:default"
34 
35 static const char *dladm_overlay_doorpath = "/var/run/varpd/varpd.door";
36 
37 static boolean_t
38 varpd_svc_isonline(void)
39 {
40 	boolean_t isonline = B_FALSE;
41 	char *s;
42 
43 	if ((s = smf_get_state(VARPD_SERVICE)) != NULL) {
44 		if (strcmp(s, SCF_STATE_STRING_ONLINE) == 0)
45 			isonline = B_TRUE;
46 		free(s);
47 	}
48 
49 	return (isonline);
50 }
51 
52 #define	MAX_WAIT_TIME	15
53 
54 static dladm_status_t
55 varpd_enable_service(void)
56 {
57 	uint_t i;
58 
59 	if (varpd_svc_isonline())
60 		return (DLADM_STATUS_OK);
61 
62 	if (smf_enable_instance(VARPD_SERVICE, 0) == -1) {
63 		if (scf_error() == SCF_ERROR_PERMISSION_DENIED)
64 			return (DLADM_STATUS_DENIED);
65 		return (DLADM_STATUS_NOTFOUND);
66 	}
67 
68 	/*
69 	 * Wait up to MAX_WAIT_TIME seconds for the service
70 	 */
71 	for (i = 0; i < MAX_WAIT_TIME; i++) {
72 		if (varpd_svc_isonline())
73 			return (DLADM_STATUS_OK);
74 		(void) sleep(1);
75 	}
76 	return (DLADM_STATUS_FAILED);
77 }
78 
79 static int
80 dladm_overlay_count_cb(dladm_handle_t handle __unused,
81     datalink_id_t linkid __unused, void *arg)
82 {
83 	(*(uint32_t *)arg)++;
84 	return (DLADM_WALK_CONTINUE);
85 }
86 
87 /*
88  * Disable the varpd service if there are no overlays left.
89  */
90 static void
91 varpd_disable_service_when_no_overlays(dladm_handle_t handle)
92 {
93 	uint32_t cnt = 0;
94 
95 	/*
96 	 * Get the number of the existing overlays. If there are no overlays
97 	 * left, disable the service.
98 	 */
99 
100 	(void) dladm_walk_datalink_id(dladm_overlay_count_cb, handle,
101 	    &cnt, DATALINK_CLASS_OVERLAY, DATALINK_ANY_MEDIATYPE,
102 	    DLADM_OPT_ACTIVE);
103 
104 	if (cnt == 0)
105 		(void) smf_disable_instance(VARPD_SERVICE, 0);
106 }
107 
108 typedef struct dladm_overlay_propinfo {
109 	boolean_t dop_isvarpd;
110 	union {
111 		overlay_ioc_propinfo_t *dop_overlay;
112 		varpd_client_prop_handle_t *dop_varpd;
113 	} dop_un;
114 } dladm_overlay_propinfo_t;
115 
116 dladm_status_t
117 dladm_overlay_prop_info(dladm_overlay_propinfo_handle_t phdl,
118     const char **namep, uint_t *typep, uint_t *protp, const void **defp,
119     uint32_t *sizep, const mac_propval_range_t **possp)
120 {
121 	dladm_overlay_propinfo_t *infop = (dladm_overlay_propinfo_t *)phdl;
122 	overlay_ioc_propinfo_t *oinfop = infop->dop_un.dop_overlay;
123 
124 	if (infop->dop_isvarpd == B_FALSE) {
125 		if (namep != NULL)
126 			*namep = oinfop->oipi_name;
127 		if (typep != NULL)
128 			*typep = oinfop->oipi_type;
129 		if (protp != NULL)
130 			*protp = oinfop->oipi_prot;
131 		if (defp != NULL)
132 			*defp = oinfop->oipi_default;
133 		if (sizep != NULL)
134 			*sizep = oinfop->oipi_defsize;
135 		if (possp != NULL) {
136 			*possp = (const mac_propval_range_t *)oinfop->oipi_poss;
137 		}
138 
139 	} else {
140 		int ret;
141 		ret = libvarpd_c_prop_info(infop->dop_un.dop_varpd, namep,
142 		    typep, protp, defp, sizep, possp);
143 		if (ret != 0)
144 			return (dladm_errno2status(ret));
145 
146 	}
147 
148 	return (DLADM_STATUS_OK);
149 }
150 
151 static dladm_status_t
152 dladm_overlay_parse_prop(overlay_prop_type_t type, void *buf, uint32_t *sizep,
153     const char *val)
154 {
155 	int ret;
156 	int64_t ival;
157 	uint64_t uval;
158 	char *eptr;
159 	struct in6_addr ipv6;
160 	struct in_addr ip;
161 
162 	switch (type) {
163 	case OVERLAY_PROP_T_INT:
164 		errno = 0;
165 		ival = strtol(val, &eptr, 10);
166 		if ((ival == 0 && errno == EINVAL) ||
167 		    ((ival == LONG_MAX || ival == LONG_MIN) &&
168 		    errno == ERANGE))
169 			return (DLADM_STATUS_BADARG);
170 		bcopy(&ival, buf, sizeof (int64_t));
171 		*sizep = sizeof (int64_t);
172 		break;
173 	case OVERLAY_PROP_T_UINT:
174 		errno = 0;
175 		uval = strtol(val, &eptr, 10);
176 		if ((uval == 0 && errno == EINVAL) ||
177 		    (uval == ULONG_MAX && errno == ERANGE))
178 			return (DLADM_STATUS_BADARG);
179 		bcopy(&uval, buf, sizeof (uint64_t));
180 		*sizep = sizeof (uint64_t);
181 		break;
182 	case OVERLAY_PROP_T_STRING:
183 		ret = strlcpy((char *)buf, val, OVERLAY_PROP_SIZEMAX);
184 		if (ret >= OVERLAY_PROP_SIZEMAX)
185 			return (DLADM_STATUS_BADARG);
186 		*sizep = ret + 1;
187 		break;
188 	case OVERLAY_PROP_T_IP:
189 		/*
190 		 * Always try to parse the IP as an IPv6 address. If that fails,
191 		 * try to interpret it as an IPv4 address and transform it into
192 		 * an IPv6 mapped IPv4 address.
193 		 */
194 		if (inet_pton(AF_INET6, val, &ipv6) != 1) {
195 			if (inet_pton(AF_INET, val, &ip) != 1)
196 				return (DLADM_STATUS_BADARG);
197 
198 			IN6_INADDR_TO_V4MAPPED(&ip, &ipv6);
199 		}
200 		bcopy(&ipv6, buf, sizeof (struct in6_addr));
201 		*sizep = sizeof (struct in6_addr);
202 		break;
203 	default:
204 		abort();
205 	}
206 
207 	return (DLADM_STATUS_OK);
208 }
209 
210 static dladm_status_t
211 i_dladm_overlay_setprop_db(dladm_handle_t handle, datalink_id_t linkid,
212     const char *name, char *const *valp, uint_t cnt)
213 {
214 	dladm_conf_t	conf;
215 	dladm_status_t	status;
216 
217 	if (linkid == DATALINK_INVALID_LINKID ||
218 	    name == NULL || valp == NULL || cnt != 1) {
219 		return (DLADM_STATUS_BADARG);
220 	}
221 
222 	status = dladm_open_conf(handle, linkid, &conf);
223 	if (status != DLADM_STATUS_OK)
224 		return (status);
225 
226 	status = dladm_set_conf_field(handle, conf, name, DLADM_TYPE_STR,
227 	    valp[0]);
228 	if (status == DLADM_STATUS_OK)
229 		status = dladm_write_conf(handle, conf);
230 
231 	dladm_destroy_conf(handle, conf);
232 	return (status);
233 }
234 
235 static dladm_status_t
236 dladm_overlay_varpd_setprop(dladm_handle_t handle __unused,
237     varpd_client_handle_t *chdl, uint64_t inst, datalink_id_t linkid __unused,
238     const char *name, char *const *valp, uint_t cnt __unused)
239 {
240 	int ret;
241 	uint32_t size;
242 	uint8_t buf[LIBVARPD_PROP_SIZEMAX];
243 	varpd_client_prop_handle_t *phdl;
244 	uint_t type;
245 	dladm_status_t status;
246 
247 	if ((ret = libvarpd_c_prop_handle_alloc(chdl, inst, &phdl)) != 0)
248 		return (dladm_errno2status(ret));
249 
250 	if ((ret = libvarpd_c_prop_info_fill_by_name(phdl, name)) != 0) {
251 		libvarpd_c_prop_handle_free(phdl);
252 		return (dladm_errno2status(ret));
253 	}
254 
255 	if ((ret = libvarpd_c_prop_info(phdl, NULL, &type, NULL, NULL, NULL,
256 	    NULL)) != 0) {
257 		libvarpd_c_prop_handle_free(phdl);
258 		return (dladm_errno2status(ret));
259 	}
260 
261 	if ((status = dladm_overlay_parse_prop(type, buf, &size, valp[0])) !=
262 	    DLADM_STATUS_OK) {
263 		libvarpd_c_prop_handle_free(phdl);
264 		return (status);
265 	}
266 
267 	status = DLADM_STATUS_OK;
268 	ret = libvarpd_c_prop_set(phdl, buf, size);
269 	libvarpd_c_prop_handle_free(phdl);
270 	if (ret != 0)
271 		status = dladm_errno2status(ret);
272 
273 	if (status != DLADM_STATUS_OK)
274 		return (status);
275 
276 	return (status);
277 }
278 
279 static dladm_status_t
280 dladm_overlay_setprop(dladm_handle_t handle, datalink_id_t linkid,
281     const char *name, char *const *valp, uint_t cnt)
282 {
283 	int			ret;
284 	dladm_status_t		status;
285 	overlay_ioc_propinfo_t	info;
286 	overlay_ioc_prop_t	prop;
287 
288 	if (linkid == DATALINK_INVALID_LINKID ||
289 	    name == NULL || valp == NULL || cnt != 1)
290 		return (DLADM_STATUS_BADARG);
291 
292 	bzero(&info, sizeof (overlay_ioc_propinfo_t));
293 	info.oipi_linkid = linkid;
294 	info.oipi_id = -1;
295 	if (strlcpy(info.oipi_name, name, OVERLAY_PROP_NAMELEN) >=
296 	    OVERLAY_PROP_NAMELEN)
297 		return (DLADM_STATUS_BADARG);
298 
299 	status = DLADM_STATUS_OK;
300 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_PROPINFO, &info);
301 	if (ret != 0)
302 		status = dladm_errno2status(errno);
303 
304 	if (status != DLADM_STATUS_OK)
305 		return (status);
306 
307 	prop.oip_linkid = linkid;
308 	prop.oip_id = info.oipi_id;
309 	prop.oip_name[0] = '\0';
310 	if ((status = dladm_overlay_parse_prop(info.oipi_type, prop.oip_value,
311 	    &prop.oip_size, valp[0])) != DLADM_STATUS_OK)
312 		return (status);
313 
314 	status = DLADM_STATUS_OK;
315 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_SETPROP, &prop);
316 	if (ret != 0)
317 		status = dladm_errno2status(errno);
318 
319 	if (status != DLADM_STATUS_OK)
320 		return (status);
321 
322 	return (status);
323 }
324 
325 /*
326  * Tell the user about any unset required properties.
327  */
328 static int
329 dladm_overlay_activate_cb(dladm_handle_t handle, datalink_id_t linkid,
330     dladm_overlay_propinfo_handle_t phdl, void *arg)
331 {
332 	dladm_status_t status;
333 	uint8_t buf[DLADM_OVERLAY_PROP_SIZEMAX];
334 	uint_t prot;
335 	size_t size = sizeof (buf);
336 	const char *name;
337 	dladm_errlist_t *errs = arg;
338 
339 	if ((status = dladm_overlay_prop_info(phdl, &name, NULL, &prot, NULL,
340 	    NULL, NULL)) != DLADM_STATUS_OK)
341 		return (status);
342 
343 	if ((prot & OVERLAY_PROP_PERM_REQ) == 0)
344 		return (DLADM_WALK_CONTINUE);
345 
346 	if (dladm_overlay_get_prop(handle, linkid, phdl, buf, &size) !=
347 	    DLADM_STATUS_OK)
348 		return (DLADM_WALK_CONTINUE);
349 
350 	if (size == 0)
351 		(void) dladm_errlist_append(errs, "unset required property: %s",
352 		    name);
353 
354 	return (DLADM_WALK_CONTINUE);
355 }
356 
357 /*
358  * We need to clean up the world here. The problem is that we may or may not
359  * actually have everything created. While in the normal case, we'd always have
360  * an overlay device, assigned datalink id, and a varpd instance, we might not
361  * have any of those, except for the datalink instance. Therefore, as long as
362  * the id refers to a valid overlay, we should try to clean up as much of the
363  * state as possible and most importantly, we need to make sure we delete the
364  * datalink id. If we fail to do that, then that name will become lost to time.
365  */
366 dladm_status_t
367 dladm_overlay_delete(dladm_handle_t handle, datalink_id_t linkid,
368     uint32_t flags)
369 {
370 	datalink_class_t class;
371 	overlay_ioc_delete_t oid;
372 	varpd_client_handle_t *chdl;
373 	int ret;
374 	uint64_t varpdid;
375 
376 	if (dladm_datalink_id2info(handle, linkid, NULL, &class, NULL,
377 	    NULL, 0) != DLADM_STATUS_OK) {
378 		return (DLADM_STATUS_BADARG);
379 	}
380 
381 	if (class != DATALINK_CLASS_OVERLAY)
382 		return (DLADM_STATUS_BADARG);
383 
384 	oid.oid_linkid = linkid;
385 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_DELETE, &oid);
386 	if (ret != 0 && errno != ENOENT) {
387 		return (dladm_errno2status(errno));
388 	}
389 
390 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0) {
391 		return (dladm_errno2status(ret));
392 	}
393 
394 	if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
395 		if (ret == ENOENT) {
396 			goto finish;
397 		}
398 		(void) libvarpd_c_destroy(chdl);
399 		return (dladm_errno2status(ret));
400 	}
401 
402 	ret = libvarpd_c_instance_destroy(chdl, varpdid);
403 finish:
404 	(void) libvarpd_c_destroy(chdl);
405 	if ((flags & DLADM_OPT_PERSIST) != 0) {
406 		(void) dladm_remove_conf(handle, linkid);
407 		(void) dladm_destroy_datalink_id(handle, linkid, flags);
408 	}
409 
410 	(void) varpd_disable_service_when_no_overlays(handle);
411 
412 	return (dladm_errno2status(ret));
413 }
414 
415 dladm_status_t
416 dladm_overlay_get_prop(dladm_handle_t handle, datalink_id_t linkid,
417     dladm_overlay_propinfo_handle_t infohdl, void *buf, size_t *sizep)
418 {
419 	int ret;
420 	overlay_ioc_prop_t oip;
421 	dladm_overlay_propinfo_t *infop = (dladm_overlay_propinfo_t *)infohdl;
422 
423 	/*
424 	 * It'd be nice if we had a better or more specific error for this. If
425 	 * this kind of error becomes common place, let's get a better dladm
426 	 * error.
427 	 */
428 	if (*sizep < DLADM_OVERLAY_PROP_SIZEMAX)
429 		return (dladm_errno2status(ERANGE));
430 
431 	if (infop->dop_isvarpd == B_FALSE) {
432 		bzero(&oip, sizeof (overlay_ioc_prop_t));
433 		oip.oip_linkid = linkid;
434 		oip.oip_id = infop->dop_un.dop_overlay->oipi_id;
435 		ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_GETPROP, &oip);
436 		if (ret != 0)
437 			return (dladm_errno2status(errno));
438 		bcopy(oip.oip_value, buf, DLADM_OVERLAY_PROP_SIZEMAX);
439 		*sizep = oip.oip_size;
440 	} else {
441 		uint32_t size = *sizep;
442 
443 		ret = libvarpd_c_prop_get(infop->dop_un.dop_varpd, buf, &size);
444 		if (ret != 0)
445 			return (dladm_errno2status(errno));
446 		*sizep = size;
447 	}
448 
449 	return (DLADM_STATUS_OK);
450 }
451 
452 static dladm_status_t
453 dladm_overlay_walk_varpd_prop(dladm_handle_t handle, datalink_id_t linkid,
454     uint64_t varpdid, dladm_overlay_prop_f func, void *arg)
455 {
456 	int ret;
457 	varpd_client_handle_t *chdl;
458 	varpd_client_prop_handle_t *phdl;
459 	uint_t i, nprops;
460 	dladm_status_t status;
461 
462 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
463 		return (dladm_errno2status(ret));
464 
465 	if ((ret = libvarpd_c_prop_handle_alloc(chdl, varpdid, &phdl)) != 0) {
466 		(void) libvarpd_c_destroy(chdl);
467 		return (dladm_errno2status(ret));
468 	}
469 
470 	if ((ret = libvarpd_c_prop_nprops(chdl, varpdid, &nprops)) != 0) {
471 		libvarpd_c_prop_handle_free(phdl);
472 		(void) libvarpd_c_destroy(chdl);
473 		return (dladm_errno2status(ret));
474 	}
475 
476 	status = DLADM_STATUS_OK;
477 	for (i = 0; i < nprops; i++) {
478 		dladm_overlay_propinfo_t dop;
479 
480 		bzero(&dop, sizeof (dop));
481 		dop.dop_isvarpd = B_TRUE;
482 		dop.dop_un.dop_varpd = phdl;
483 
484 		if ((ret = libvarpd_c_prop_info_fill(phdl, i)) != 0) {
485 			status = dladm_errno2status(ret);
486 			break;
487 		}
488 
489 		ret = func(handle, linkid,
490 		    (dladm_overlay_propinfo_handle_t)&dop, arg);
491 		if (ret == DLADM_WALK_TERMINATE)
492 			break;
493 	}
494 
495 	libvarpd_c_prop_handle_free(phdl);
496 	libvarpd_c_destroy(chdl);
497 
498 	return (status);
499 }
500 
501 dladm_status_t
502 dladm_overlay_walk_prop(dladm_handle_t handle, datalink_id_t linkid,
503     dladm_overlay_prop_f func, void *arg, dladm_errlist_t *errs)
504 {
505 	int i, ret;
506 	char buf[MAXLINKNAMELEN];
507 	char errmsg[DLADM_STRSIZE];
508 	datalink_class_t class;
509 	dladm_status_t info_status;
510 	overlay_ioc_nprops_t oin;
511 	overlay_ioc_propinfo_t oipi;
512 	dladm_overlay_propinfo_t dop;
513 	uint64_t varpdid = UINT64_MAX;
514 
515 	if ((info_status = dladm_datalink_id2info(handle, linkid, NULL, &class,
516 	    NULL, buf, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
517 		(void) dladm_errlist_append(errs, "failed to get info for "
518 		    "datalink id %u: %s",
519 		    linkid, dladm_status2str(info_status, errmsg));
520 		return (DLADM_STATUS_BADARG);
521 	}
522 
523 	if (class != DATALINK_CLASS_OVERLAY) {
524 		(void) dladm_errlist_append(errs, "%s is not an overlay", buf);
525 		return (DLADM_STATUS_BADARG);
526 	}
527 
528 	bzero(&oin, sizeof (overlay_ioc_nprops_t));
529 	oin.oipn_linkid = linkid;
530 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_NPROPS, &oin);
531 	if (ret != 0) {
532 		(void) dladm_errlist_append(errs, "failed to get "
533 		    "overlay properties for overlay %s: %s",
534 		    buf, strerror(errno));
535 		return (dladm_errno2status(errno));
536 	}
537 
538 	for (i = 0; i < oin.oipn_nprops; i++) {
539 		bzero(&dop, sizeof (dladm_overlay_propinfo_t));
540 		bzero(&oipi, sizeof (overlay_ioc_propinfo_t));
541 		oipi.oipi_linkid = linkid;
542 		oipi.oipi_id = i;
543 		ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_PROPINFO, &oipi);
544 		if (ret != 0) {
545 			(void) dladm_errlist_append(errs, "failed to get "
546 			    "propinfo for overlay %s, property %d: %s",
547 			    buf, i, strerror(errno));
548 			return (dladm_errno2status(errno));
549 		}
550 
551 		dop.dop_isvarpd = B_FALSE;
552 		dop.dop_un.dop_overlay = &oipi;
553 		ret = func(handle, linkid,
554 		    (dladm_overlay_propinfo_handle_t)&dop, arg);
555 		if (ret == DLADM_WALK_TERMINATE)
556 			break;
557 
558 		if (strcmp(oipi.oipi_name, VARPD_PROPERTY_NAME) == 0) {
559 			uint8_t buf[DLADM_OVERLAY_PROP_SIZEMAX];
560 			size_t bufsize = sizeof (buf);
561 			uint64_t *vp;
562 
563 			if (dladm_overlay_get_prop(handle, linkid,
564 			    (dladm_overlay_propinfo_handle_t)&dop, buf,
565 			    &bufsize) != DLADM_STATUS_OK)
566 				continue;
567 
568 			vp = (uint64_t *)buf;
569 			varpdid = *vp;
570 		}
571 	}
572 
573 	/* Should this really be possible? */
574 	if (varpdid == UINT64_MAX)
575 		return (DLADM_STATUS_OK);
576 
577 	ret = dladm_overlay_walk_varpd_prop(handle, linkid, varpdid, func,
578 	    arg);
579 	if (ret != DLADM_STATUS_OK) {
580 		(void) dladm_errlist_append(errs,
581 		    "failed to get varpd props for "
582 		    "overlay %s, varpd id %llu: %s",
583 		    buf, varpdid, dladm_status2str(info_status, errmsg));
584 	}
585 	return (ret);
586 }
587 
588 static dladm_status_t
589 dladm_overlay_persist_config(dladm_handle_t handle, dladm_overlay_attr_t *attr)
590 {
591 	dladm_conf_t conf;
592 	dladm_status_t status;
593 
594 	if ((status = dladm_create_conf(handle, attr->oa_name, attr->oa_linkid,
595 	    DATALINK_CLASS_OVERLAY, DL_ETHER, &conf)) != DLADM_STATUS_OK) {
596 		return (status);
597 	}
598 
599 	status = dladm_set_conf_field(handle, conf, FVNETID,
600 	    DLADM_TYPE_UINT64, &attr->oa_vid);
601 	if (status != DLADM_STATUS_OK)
602 		goto done;
603 
604 	status = dladm_set_conf_field(handle, conf, FENCAP,
605 	    DLADM_TYPE_STR, attr->oa_encap);
606 	if (status != DLADM_STATUS_OK)
607 		goto done;
608 
609 	status = dladm_set_conf_field(handle, conf, FSEARCH,
610 	    DLADM_TYPE_STR, attr->oa_search);
611 	if (status != DLADM_STATUS_OK)
612 		goto done;
613 
614 	status = dladm_write_conf(handle, conf);
615 
616 done:
617 	dladm_destroy_conf(handle, conf);
618 	return (status);
619 }
620 
621 static dladm_status_t
622 i_dladm_overlay_create_sys(dladm_handle_t handle, dladm_overlay_attr_t *attr)
623 {
624 	overlay_ioc_create_t oic;
625 	dladm_status_t status;
626 	int ret;
627 
628 	bzero(&oic, sizeof (oic));
629 	oic.oic_linkid = attr->oa_linkid;
630 	oic.oic_vnetid = attr->oa_vid;
631 	(void) strlcpy(oic.oic_encap, attr->oa_encap, MAXLINKNAMELEN);
632 
633 	status = DLADM_STATUS_OK;
634 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_CREATE, &oic);
635 	if (ret != 0) {
636 		/*
637 		 * It'd be nice if we had private errors so we could better
638 		 * distinguish between different classes of errors.
639 		 */
640 		status = dladm_errno2status(errno);
641 	}
642 
643 	return (status);
644 }
645 
646 static dladm_status_t
647 i_dladm_overlay_commit_sys(dladm_handle_t handle, dladm_overlay_attr_t *attr,
648     dladm_arg_list_t *props, dladm_errlist_t *errs)
649 {
650 	overlay_ioc_activate_t oia;
651 	varpd_client_handle_t *vch;
652 	dladm_status_t status;
653 	size_t slen;
654 	uint64_t id;
655 	int ret;
656 	uint_t i;
657 
658 	slen = strlen(attr->oa_search);
659 	for (i = 0; props != NULL && i < props->al_count; i++) {
660 		dladm_arg_info_t	*aip = &props->al_info[i];
661 
662 		/*
663 		 * If it's a property for the search plugin, eg. it has the
664 		 * prefix '<search>/', then we don't set the property on the
665 		 * overlay device and instead set it on the varpd instance.
666 		 */
667 		if (strncmp(aip->ai_name, attr->oa_search, slen) == 0 &&
668 		    aip->ai_name[slen] == '/')
669 			continue;
670 		status = dladm_overlay_setprop(handle, attr->oa_linkid,
671 		    aip->ai_name, aip->ai_val, aip->ai_count);
672 		if (status != DLADM_STATUS_OK) {
673 			(void) dladm_errlist_append(errs,
674 			    "failed to set property %s", aip->ai_name);
675 			return (status);
676 		}
677 
678 		if (attr->oa_flags & DLADM_OPT_PERSIST) {
679 			status = i_dladm_overlay_setprop_db(handle,
680 			    attr->oa_linkid, aip->ai_name, aip->ai_val,
681 			    aip->ai_count);
682 			if (status != DLADM_STATUS_OK) {
683 				(void) dladm_errlist_append(errs,
684 				    "failed to persistently set property %s",
685 				    aip->ai_name);
686 				return (status);
687 			}
688 		}
689 	}
690 
691 	if ((ret = libvarpd_c_create(&vch, dladm_overlay_doorpath)) != 0) {
692 		(void) dladm_errlist_append(errs,
693 		    "failed to create libvarpd handle: %s", strerror(ret));
694 		return (dladm_errno2status(ret));
695 	}
696 
697 	if ((ret = libvarpd_c_instance_create(vch, attr->oa_linkid,
698 	    attr->oa_search, &id)) != 0) {
699 		(void) dladm_errlist_append(errs,
700 		    "failed to create varpd instance: %s", strerror(ret));
701 		libvarpd_c_destroy(vch);
702 		return (dladm_errno2status(ret));
703 	}
704 
705 	for (i = 0; props != NULL && i < props->al_count; i++) {
706 		dladm_arg_info_t	*aip = &props->al_info[i];
707 
708 		/*
709 		 * Skip arguments we've processed already.
710 		 */
711 		if (strncmp(aip->ai_name, attr->oa_search, slen) != 0 ||
712 		    aip->ai_name[slen] != '/')
713 			continue;
714 
715 		ret = dladm_overlay_varpd_setprop(handle, vch, id,
716 		    attr->oa_linkid, aip->ai_name, aip->ai_val, aip->ai_count);
717 		if (ret != 0) {
718 			(void) dladm_errlist_append(errs,
719 			    "failed to set varpd prop: %s\n", aip->ai_name);
720 			(void) libvarpd_c_instance_destroy(vch, id);
721 			libvarpd_c_destroy(vch);
722 			return (dladm_errno2status(ret));
723 		}
724 
725 		if (attr->oa_flags & DLADM_OPT_PERSIST) {
726 			status = i_dladm_overlay_setprop_db(handle,
727 			    attr->oa_linkid, aip->ai_name, aip->ai_val,
728 			    aip->ai_count);
729 			if (status != DLADM_STATUS_OK) {
730 				(void) dladm_errlist_append(errs, "failed to "
731 				    "persistently set varpd prop: %s\n",
732 				    aip->ai_name);
733 				(void) libvarpd_c_instance_destroy(vch, id);
734 				libvarpd_c_destroy(vch);
735 				return (status);
736 			}
737 		}
738 	}
739 
740 	if ((ret = libvarpd_c_instance_activate(vch, id)) != 0) {
741 		(void) dladm_errlist_append(errs,
742 		    "failed to activate varpd instance: %s", strerror(ret));
743 		(void) dladm_overlay_walk_varpd_prop(handle, attr->oa_linkid,
744 		    id, dladm_overlay_activate_cb, errs);
745 		(void) libvarpd_c_instance_destroy(vch, id);
746 		libvarpd_c_destroy(vch);
747 		return (dladm_errno2status(ret));
748 
749 	}
750 
751 	bzero(&oia, sizeof (oia));
752 	oia.oia_linkid = attr->oa_linkid;
753 	status = DLADM_STATUS_OK;
754 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_ACTIVATE, &oia);
755 	if (ret != 0) {
756 		ret = errno;
757 		(void) dladm_errlist_append(errs, "failed to activate "
758 		    "device: %s", strerror(ret));
759 		(void) libvarpd_c_instance_destroy(vch, id);
760 		(void) dladm_overlay_walk_prop(handle, attr->oa_linkid,
761 		    dladm_overlay_activate_cb, errs, errs);
762 		status = dladm_errno2status(ret);
763 	}
764 
765 	libvarpd_c_destroy(vch);
766 
767 	return (status);
768 }
769 
770 dladm_status_t
771 dladm_overlay_create(dladm_handle_t handle, const char *name,
772     const char *encap, const char *search, uint64_t vid,
773     dladm_arg_list_t *props, dladm_errlist_t *errs, uint32_t flags)
774 {
775 	dladm_status_t status;
776 	datalink_id_t linkid;
777 	dladm_overlay_attr_t attr;
778 	char errmsg[DLADM_STRSIZE];
779 
780 	if (strlcpy(attr.oa_name, name, sizeof (attr.oa_name)) >=
781 	    sizeof (attr.oa_name)) {
782 		return (DLADM_STATUS_BADARG);
783 	}
784 	if (strlcpy(attr.oa_encap, encap, sizeof (attr.oa_encap)) >=
785 	    sizeof (attr.oa_encap)) {
786 		return (DLADM_STATUS_BADARG);
787 	}
788 	if (strlcpy(attr.oa_search, search, sizeof (attr.oa_search)) >=
789 	    sizeof (attr.oa_search)) {
790 		return (DLADM_STATUS_BADARG);
791 	}
792 
793 	status = varpd_enable_service();
794 	if (status != DLADM_STATUS_OK)
795 		return (status);
796 
797 	status = dladm_create_datalink_id(handle, name, DATALINK_CLASS_OVERLAY,
798 	    DL_ETHER, flags, &linkid);
799 	if (status != DLADM_STATUS_OK)
800 		return (status);
801 
802 	attr.oa_linkid = linkid;
803 	attr.oa_vid = vid;
804 	attr.oa_flags = flags;
805 
806 	status = i_dladm_overlay_create_sys(handle, &attr);
807 
808 	if (status != DLADM_STATUS_OK) {
809 		(void) dladm_destroy_datalink_id(handle, linkid, flags);
810 		return (status);
811 	}
812 
813 	if ((flags & DLADM_OPT_PERSIST) != 0) {
814 		status = dladm_overlay_persist_config(handle, &attr);
815 		if (status != DLADM_STATUS_OK) {
816 			(void) dladm_errlist_append(errs, "failed to create "
817 			    "persistent configuration for %s: %s",
818 			    attr.oa_name, dladm_status2str(status, errmsg));
819 		}
820 	}
821 
822 	if (status == DLADM_STATUS_OK)
823 		status = i_dladm_overlay_commit_sys(handle, &attr, props, errs);
824 
825 	if (status != DLADM_STATUS_OK) {
826 		(void) dladm_overlay_delete(handle, linkid, flags);
827 		(void) dladm_destroy_datalink_id(handle, linkid, flags);
828 	}
829 
830 	return (status);
831 }
832 
833 typedef struct overlay_walk_cb {
834 	dladm_handle_t		owc_handle;
835 	datalink_id_t		owc_linkid;
836 	void			*owc_arg;
837 	dladm_overlay_cache_f	owc_func;
838 	uint_t			owc_mode;
839 	uint_t			owc_dest;
840 } overlay_walk_cb_t;
841 
842 static int
843 dladm_overlay_walk_cache_cb(varpd_client_handle_t *chdl __unused,
844     uint64_t varpdid __unused, const struct ether_addr *key,
845     const varpd_client_cache_entry_t *entry, void *arg)
846 {
847 	overlay_walk_cb_t *owc = arg;
848 	dladm_overlay_point_t point;
849 
850 	bzero(&point, sizeof (dladm_overlay_point_t));
851 	point.dop_dest = owc->owc_dest;
852 	point.dop_mac = entry->vcp_mac;
853 	point.dop_flags = entry->vcp_flags;
854 	point.dop_ip = entry->vcp_ip;
855 	point.dop_port = entry->vcp_port;
856 
857 	if (owc->owc_mode == OVERLAY_TARGET_POINT)
858 		point.dop_flags |= DLADM_OVERLAY_F_DEFAULT;
859 
860 	if (owc->owc_func(owc->owc_handle, owc->owc_linkid, key, &point,
861 	    owc->owc_arg) == DLADM_WALK_TERMINATE)
862 		return (1);
863 	return (0);
864 }
865 
866 dladm_status_t
867 dladm_overlay_walk_cache(dladm_handle_t handle, datalink_id_t linkid,
868     dladm_overlay_cache_f func, void *arg)
869 {
870 	int ret;
871 	uint_t mode, dest;
872 	uint64_t varpdid;
873 	varpd_client_handle_t *chdl;
874 	overlay_walk_cb_t cbarg;
875 
876 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
877 		return (dladm_errno2status(ret));
878 
879 	if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
880 		libvarpd_c_destroy(chdl);
881 		return (dladm_errno2status(ret));
882 	}
883 
884 	if ((ret = libvarpd_c_instance_target_mode(chdl, varpdid,
885 	    &dest, &mode)) != 0) {
886 		libvarpd_c_destroy(chdl);
887 		return (dladm_errno2status(ret));
888 	}
889 
890 	cbarg.owc_handle = handle;
891 	cbarg.owc_linkid = linkid;
892 	cbarg.owc_arg = arg;
893 	cbarg.owc_func = func;
894 	cbarg.owc_dest = dest;
895 	cbarg.owc_mode = mode;
896 	ret = libvarpd_c_instance_cache_walk(chdl, varpdid,
897 	    dladm_overlay_walk_cache_cb, &cbarg);
898 	libvarpd_c_destroy(chdl);
899 
900 	return (dladm_errno2status(ret));
901 }
902 
903 dladm_status_t
904 dladm_overlay_cache_flush(dladm_handle_t handle __unused, datalink_id_t linkid)
905 {
906 	int ret;
907 	uint64_t varpdid;
908 	varpd_client_handle_t *chdl;
909 
910 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
911 		return (dladm_errno2status(ret));
912 
913 	if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
914 		libvarpd_c_destroy(chdl);
915 		return (dladm_errno2status(ret));
916 	}
917 
918 	ret = libvarpd_c_instance_cache_flush(chdl, varpdid);
919 	libvarpd_c_destroy(chdl);
920 
921 	return (dladm_errno2status(ret));
922 }
923 
924 dladm_status_t
925 dladm_overlay_cache_delete(dladm_handle_t handle __unused, datalink_id_t linkid,
926     const struct ether_addr *key)
927 {
928 	int ret;
929 	uint64_t varpdid;
930 	varpd_client_handle_t *chdl;
931 
932 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
933 		return (dladm_errno2status(ret));
934 
935 	if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
936 		libvarpd_c_destroy(chdl);
937 		return (dladm_errno2status(ret));
938 	}
939 
940 	ret = libvarpd_c_instance_cache_delete(chdl, varpdid, key);
941 	libvarpd_c_destroy(chdl);
942 
943 	return (dladm_errno2status(ret));
944 }
945 
946 dladm_status_t
947 dladm_overlay_cache_set(dladm_handle_t handle __unused, datalink_id_t linkid,
948     const struct ether_addr *key, char *val)
949 {
950 	int ret;
951 	uint_t dest;
952 	uint64_t varpdid;
953 	char *ip, *port = NULL;
954 	varpd_client_handle_t *chdl;
955 	varpd_client_cache_entry_t vcp;
956 
957 
958 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
959 		return (dladm_errno2status(ret));
960 
961 	if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
962 		libvarpd_c_destroy(chdl);
963 		return (dladm_errno2status(ret));
964 	}
965 
966 	if ((ret = libvarpd_c_instance_target_mode(chdl, varpdid,
967 	    &dest, NULL)) != 0) {
968 		libvarpd_c_destroy(chdl);
969 		return (dladm_errno2status(ret));
970 	}
971 
972 	/*
973 	 * Mode tells us what we should expect in val. It we have more than one
974 	 * thing listed, the canonical format of it right now is mac,ip:port.
975 	 */
976 	bzero(&vcp, sizeof (varpd_client_cache_entry_t));
977 
978 	if (strcasecmp(val, "drop") == 0) {
979 		vcp.vcp_flags = OVERLAY_TARGET_CACHE_DROP;
980 		goto send;
981 	}
982 
983 	if (dest & OVERLAY_PLUGIN_D_ETHERNET) {
984 		if (ether_aton_r(val, &vcp.vcp_mac) == NULL) {
985 			libvarpd_c_destroy(chdl);
986 			return (dladm_errno2status(EINVAL));
987 		}
988 	}
989 
990 	if (dest & OVERLAY_PLUGIN_D_IP) {
991 		if (dest & OVERLAY_PLUGIN_D_ETHERNET) {
992 			if ((ip = strchr(val, ',')) == NULL) {
993 				libvarpd_c_destroy(chdl);
994 				return (dladm_errno2status(ret));
995 			}
996 			ip++;
997 		} else {
998 			ip = val;
999 		}
1000 
1001 		if (dest & OVERLAY_PLUGIN_D_PORT) {
1002 			if ((port = strchr(val, ':')) == NULL) {
1003 				libvarpd_c_destroy(chdl);
1004 				return (dladm_errno2status(ret));
1005 			}
1006 			*port = '\0';
1007 			port++;
1008 		}
1009 
1010 		/* Try v6, then fall back to v4 */
1011 		ret = inet_pton(AF_INET6, ip, &vcp.vcp_ip);
1012 		if (ret == -1)
1013 			abort();
1014 		if (ret == 0) {
1015 			struct in_addr v4;
1016 
1017 			ret = inet_pton(AF_INET, ip, &v4);
1018 			if (ret == -1)
1019 				abort();
1020 			if (ret == 0) {
1021 				libvarpd_c_destroy(chdl);
1022 				return (dladm_errno2status(ret));
1023 			}
1024 			IN6_INADDR_TO_V4MAPPED(&v4, &vcp.vcp_ip);
1025 		}
1026 	}
1027 
1028 	if (dest & OVERLAY_PLUGIN_D_PORT) {
1029 		char *eptr;
1030 		unsigned long l;
1031 		if (port == NULL && (dest & OVERLAY_PLUGIN_D_ETHERNET)) {
1032 			if ((port = strchr(val, ',')) == NULL) {
1033 				libvarpd_c_destroy(chdl);
1034 				return (dladm_errno2status(EINVAL));
1035 			}
1036 		} else if (port == NULL)
1037 			port = val;
1038 
1039 		errno = 0;
1040 		l = strtoul(port, &eptr, 10);
1041 		if (errno != 0 || *eptr != '\0') {
1042 			libvarpd_c_destroy(chdl);
1043 			return (dladm_errno2status(EINVAL));
1044 		}
1045 		if (l == 0 || l > UINT16_MAX) {
1046 			libvarpd_c_destroy(chdl);
1047 			return (dladm_errno2status(EINVAL));
1048 		}
1049 		vcp.vcp_port = l;
1050 	}
1051 
1052 send:
1053 	ret = libvarpd_c_instance_cache_set(chdl, varpdid, key, &vcp);
1054 
1055 	libvarpd_c_destroy(chdl);
1056 	return (dladm_errno2status(ret));
1057 }
1058 
1059 dladm_status_t
1060 dladm_overlay_cache_get(dladm_handle_t handle __unused, datalink_id_t linkid,
1061     const struct ether_addr *key, dladm_overlay_point_t *point)
1062 {
1063 	int ret;
1064 	uint_t dest, mode;
1065 	uint64_t varpdid;
1066 	varpd_client_handle_t *chdl;
1067 	varpd_client_cache_entry_t entry;
1068 
1069 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
1070 		return (dladm_errno2status(ret));
1071 
1072 	if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
1073 		libvarpd_c_destroy(chdl);
1074 		return (dladm_errno2status(ret));
1075 	}
1076 
1077 	if ((ret = libvarpd_c_instance_target_mode(chdl, varpdid,
1078 	    &dest, &mode)) != 0) {
1079 		libvarpd_c_destroy(chdl);
1080 		return (dladm_errno2status(ret));
1081 	}
1082 
1083 	ret = libvarpd_c_instance_cache_get(chdl, varpdid, key, &entry);
1084 	if (ret == 0) {
1085 		point->dop_dest = dest;
1086 		point->dop_mac = entry.vcp_mac;
1087 		point->dop_flags = entry.vcp_flags;
1088 		point->dop_ip = entry.vcp_ip;
1089 		point->dop_port = entry.vcp_port;
1090 		if (mode == OVERLAY_TARGET_POINT)
1091 			point->dop_flags |= DLADM_OVERLAY_F_DEFAULT;
1092 	}
1093 
1094 	libvarpd_c_destroy(chdl);
1095 	return (dladm_errno2status(ret));
1096 }
1097 
1098 dladm_status_t
1099 dladm_overlay_status(dladm_handle_t handle, datalink_id_t linkid,
1100     dladm_overlay_status_f func, void *arg)
1101 {
1102 	int ret;
1103 	dladm_status_t status;
1104 	overlay_ioc_status_t ois;
1105 	dladm_overlay_status_t dos;
1106 
1107 	ois.ois_linkid = linkid;
1108 	status = DLADM_STATUS_OK;
1109 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_STATUS, &ois);
1110 	if (ret != 0)
1111 		status = dladm_errno2status(errno);
1112 	if (status != DLADM_STATUS_OK)
1113 		return (status);
1114 
1115 	dos.dos_degraded = ois.ois_status == OVERLAY_I_DEGRADED ? B_TRUE :
1116 	    B_FALSE;
1117 	(void) strlcpy(dos.dos_fmamsg, ois.ois_message,
1118 	    sizeof (dos.dos_fmamsg));
1119 	func(handle, linkid, &dos, arg);
1120 	return (DLADM_STATUS_OK);
1121 }
1122 
1123 /*
1124  * dladm_parse_args() usually creates a dladm_arg_list_t by tokenising a
1125  * delimited string and storing pointers to pieces of that string in the
1126  * dladm_arg_info_t structure. Those pointers do not need to be individually
1127  * freed.
1128  *
1129  * This function deals with property lists which have instead been built from
1130  * the persistent datalink configuration database, in order to bring up an
1131  * overlay at boot time. In this case, the properties have been retrieved
1132  * one-by-one, duplicated with strdup(), and added to the list. When the list
1133  * is finished with, this function takes care of freeing the memory.
1134  */
1135 static void
1136 i_dladm_overlay_props_free(dladm_handle_t handle __unused,
1137     dladm_arg_list_t *props)
1138 {
1139 	uint_t i, j;
1140 
1141 	for (i = 0; props != NULL && i < props->al_count; i++) {
1142 		dladm_arg_info_t *aip = &props->al_info[i];
1143 
1144 		/* For ai_name, we need to cast away the 'const' qualifier. */
1145 		free((char *)aip->ai_name);
1146 		for (j = 0; j < aip->ai_count; j++)
1147 			free(aip->ai_val[j]);
1148 	}
1149 	free(props);
1150 }
1151 
1152 static dladm_status_t
1153 i_dladm_overlay_fetch_persistent_config(dladm_handle_t handle,
1154     datalink_id_t linkid, dladm_overlay_attr_t *attrp,
1155     dladm_arg_list_t **props)
1156 {
1157 	dladm_conf_t conf;
1158 	dladm_status_t status;
1159 	char attr[MAXLINKATTRLEN], last_attr[MAXLINKATTRLEN];
1160 	char attrval[OVERLAY_PROP_SIZEMAX];
1161 	size_t attrsz;
1162 	dladm_arg_list_t *list = NULL;
1163 
1164 	*props = NULL;
1165 
1166 	if ((status = dladm_getsnap_conf(handle, linkid, &conf)) !=
1167 	    DLADM_STATUS_OK) {
1168 		return (status);
1169 	}
1170 
1171 	attrp->oa_linkid = linkid;
1172 
1173 	status = dladm_get_conf_field(handle, conf, FVNETID, &attrp->oa_vid,
1174 	    sizeof (attrp->oa_vid));
1175 	if (status != DLADM_STATUS_OK)
1176 		goto done;
1177 
1178 	status = dladm_get_conf_field(handle, conf, FENCAP,
1179 	    attrp->oa_encap, sizeof (attrp->oa_encap));
1180 	if (status != DLADM_STATUS_OK)
1181 		goto done;
1182 
1183 	status = dladm_get_conf_field(handle, conf, FSEARCH,
1184 	    attrp->oa_search, sizeof (attrp->oa_search));
1185 	if (status != DLADM_STATUS_OK)
1186 		goto done;
1187 
1188 	list = calloc(1, sizeof (dladm_arg_list_t));
1189 
1190 	*last_attr = '\0';
1191 	while (dladm_getnext_conf_linkprop(handle, conf, last_attr,
1192 	    attr, attrval, sizeof (attrval), &attrsz) == DLADM_STATUS_OK) {
1193 		dladm_arg_info_t *aip;
1194 
1195 		(void) strlcpy(last_attr, attr, sizeof (last_attr));
1196 		if (strchr(attr, '/') == NULL)
1197 			continue;
1198 
1199 		aip = &list->al_info[list->al_count];
1200 		bzero(aip, sizeof (dladm_arg_info_t));
1201 		if ((aip->ai_name = strdup(attr)) == NULL) {
1202 			status = dladm_errno2status(errno);
1203 			break;
1204 		}
1205 		if ((aip->ai_val[0] = strdup(attrval)) == NULL) {
1206 			status = dladm_errno2status(errno);
1207 			break;
1208 		}
1209 		aip->ai_count = 1;
1210 		list->al_count++;
1211 		if (list->al_count >= DLADM_MAX_ARG_CNT) {
1212 			status = DLADM_STATUS_TOOMANYELEMENTS;
1213 			break;
1214 		}
1215 	}
1216 
1217 done:
1218 
1219 	dladm_destroy_conf(handle, conf);
1220 
1221 	if (status != DLADM_STATUS_OK) {
1222 		if (list != NULL)
1223 			i_dladm_overlay_props_free(handle, list);
1224 		return (status);
1225 	}
1226 
1227 	*props = list;
1228 	return (DLADM_STATUS_OK);
1229 }
1230 
1231 typedef struct dladm_overlay_up_arg_s {
1232 	dladm_errlist_t	*errlist;
1233 } dladm_overlay_up_arg_t;
1234 
1235 static int
1236 i_dladm_overlay_up(dladm_handle_t handle, datalink_id_t linkid, void *arg)
1237 {
1238 	dladm_overlay_up_arg_t *argp = arg;
1239 	dladm_errlist_t *errs = argp->errlist;
1240 	datalink_class_t class;
1241 	dladm_status_t status;
1242 	dladm_overlay_attr_t attr;
1243 	dladm_arg_list_t *props;
1244 	char errmsg[DLADM_STRSIZE];
1245 
1246 	bzero(&attr, sizeof (attr));
1247 
1248 	status = dladm_datalink_id2info(handle, linkid, NULL, &class,
1249 	    NULL, attr.oa_name, sizeof (attr.oa_name));
1250 	if (status != DLADM_STATUS_OK) {
1251 		(void) dladm_errlist_append(errs, "failed to get info for "
1252 		    "datalink id %u: %s",
1253 		    linkid, dladm_status2str(status, errmsg));
1254 		return (DLADM_STATUS_BADARG);
1255 	}
1256 
1257 	if (class != DATALINK_CLASS_OVERLAY) {
1258 		(void) dladm_errlist_append(errs, "%s is not an overlay",
1259 		    attr.oa_name);
1260 		return (DLADM_STATUS_BADARG);
1261 	}
1262 
1263 	status = varpd_enable_service();
1264 	if (status != DLADM_STATUS_OK) {
1265 		(void) dladm_errlist_append(errs, "failed to enable svc:/%s",
1266 		    VARPD_SERVICE);
1267 		return (DLADM_WALK_TERMINATE);
1268 	}
1269 
1270 	status = i_dladm_overlay_fetch_persistent_config(handle, linkid,
1271 	    &attr, &props);
1272 	if (status != DLADM_STATUS_OK) {
1273 		(void) dladm_errlist_append(errs, "failed to retrieve "
1274 		    "persistent configuration for %s: %s",
1275 		    attr.oa_name, dladm_status2str(status, errmsg));
1276 		return (DLADM_WALK_CONTINUE);
1277 	}
1278 
1279 	status = i_dladm_overlay_create_sys(handle, &attr);
1280 	if (status != DLADM_STATUS_OK) {
1281 		(void) dladm_errlist_append(errs,
1282 		    "failed to create overlay device %s: %s",
1283 		    attr.oa_name, dladm_status2str(status, errmsg));
1284 		goto out;
1285 	}
1286 
1287 	status = i_dladm_overlay_commit_sys(handle, &attr, props, errs);
1288 	if (status != DLADM_STATUS_OK) {
1289 		(void) dladm_errlist_append(errs,
1290 		    "failed to set properties for overlay device %s: %s",
1291 		    attr.oa_name, dladm_status2str(status, errmsg));
1292 		dladm_overlay_delete(handle, linkid, 0);
1293 		goto out;
1294 	}
1295 
1296 	status = dladm_up_datalink_id(handle, linkid);
1297 	if (status != DLADM_STATUS_OK) {
1298 		(void) dladm_errlist_append(errs,
1299 		    "failed to bring datalink up for overlay device %s: %s",
1300 		    attr.oa_name, dladm_status2str(status, errmsg));
1301 		dladm_overlay_delete(handle, linkid, 0);
1302 		goto out;
1303 	}
1304 
1305 out:
1306 	i_dladm_overlay_props_free(handle, props);
1307 
1308 	return (DLADM_WALK_CONTINUE);
1309 }
1310 
1311 dladm_status_t
1312 dladm_overlay_up(dladm_handle_t handle, datalink_id_t linkid,
1313     dladm_errlist_t *errs)
1314 {
1315 	dladm_overlay_up_arg_t overlay_arg = {
1316 		.errlist	= errs
1317 	};
1318 
1319 	if (linkid == DATALINK_ALL_LINKID) {
1320 		(void) dladm_walk_datalink_id(i_dladm_overlay_up, handle,
1321 		    &overlay_arg, DATALINK_CLASS_OVERLAY,
1322 		    DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
1323 	} else {
1324 		(void) i_dladm_overlay_up(handle, linkid, &overlay_arg);
1325 	}
1326 
1327 	if (dladm_errlist_count(errs) == 0)
1328 		return (DLADM_STATUS_OK);
1329 
1330 	return (DLADM_STATUS_FAILED);
1331 }
1332