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  */
15 
16 #include <libdladm_impl.h>
17 #include <libdllink.h>
18 #include <libdloverlay.h>
19 #include <sys/dld.h>
20 #include <sys/overlay.h>
21 #include <strings.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <limits.h>
28 #include <libvarpd_client.h>
29 
30 #define	VARPD_PROPERTY_NAME	"varpd/id"
31 
32 static const char *dladm_overlay_doorpath = "/var/run/varpd/varpd.door";
33 
34 typedef struct dladm_overlay_propinfo {
35 	boolean_t dop_isvarpd;
36 	union {
37 		overlay_ioc_propinfo_t *dop_overlay;
38 		varpd_client_prop_handle_t *dop_varpd;
39 	} dop_un;
40 } dladm_overlay_propinfo_t;
41 
42 dladm_status_t
43 dladm_overlay_prop_info(dladm_overlay_propinfo_handle_t phdl,
44     const char **namep, uint_t *typep, uint_t *protp, const void **defp,
45     uint32_t *sizep, const mac_propval_range_t **possp)
46 {
47 	dladm_overlay_propinfo_t *infop = (dladm_overlay_propinfo_t *)phdl;
48 	overlay_ioc_propinfo_t *oinfop = infop->dop_un.dop_overlay;
49 
50 	if (infop->dop_isvarpd == B_FALSE) {
51 		if (namep != NULL)
52 			*namep = oinfop->oipi_name;
53 		if (typep != NULL)
54 			*typep = oinfop->oipi_type;
55 		if (protp != NULL)
56 			*protp = oinfop->oipi_prot;
57 		if (defp != NULL)
58 			*defp = oinfop->oipi_default;
59 		if (sizep != NULL)
60 			*sizep = oinfop->oipi_defsize;
61 		if (possp != NULL) {
62 			*possp = (const mac_propval_range_t *)oinfop->oipi_poss;
63 		}
64 
65 	} else {
66 		int ret;
67 		ret = libvarpd_c_prop_info(infop->dop_un.dop_varpd, namep,
68 		    typep, protp, defp, sizep, possp);
69 		if (ret != 0)
70 			return (dladm_errno2status(ret));
71 
72 	}
73 
74 	return (DLADM_STATUS_OK);
75 }
76 
77 static dladm_status_t
78 dladm_overlay_parse_prop(overlay_prop_type_t type, void *buf, uint32_t *sizep,
79     const char *val)
80 {
81 	int ret;
82 	int64_t ival;
83 	uint64_t uval;
84 	char *eptr;
85 	struct in6_addr ipv6;
86 	struct in_addr ip;
87 
88 	switch (type) {
89 	case OVERLAY_PROP_T_INT:
90 		errno = 0;
91 		ival = strtol(val, &eptr, 10);
92 		if ((ival == 0 && errno == EINVAL) ||
93 		    ((ival == LONG_MAX || ival == LONG_MIN) &&
94 		    errno == ERANGE))
95 			return (DLADM_STATUS_BADARG);
96 		bcopy(&ival, buf, sizeof (int64_t));
97 		*sizep = sizeof (int64_t);
98 		break;
99 	case OVERLAY_PROP_T_UINT:
100 		errno = 0;
101 		uval = strtol(val, &eptr, 10);
102 		if ((uval == 0 && errno == EINVAL) ||
103 		    (uval == ULONG_MAX && errno == ERANGE))
104 			return (DLADM_STATUS_BADARG);
105 		bcopy(&uval, buf, sizeof (uint64_t));
106 		*sizep = sizeof (uint64_t);
107 		break;
108 	case OVERLAY_PROP_T_STRING:
109 		ret = strlcpy((char *)buf, val, OVERLAY_PROP_SIZEMAX);
110 		if (ret >= OVERLAY_PROP_SIZEMAX)
111 			return (DLADM_STATUS_BADARG);
112 		*sizep = ret + 1;
113 		break;
114 	case OVERLAY_PROP_T_IP:
115 		/*
116 		 * Always try to parse the IP as an IPv6 address. If that fails,
117 		 * try to interpret it as an IPv4 address and transform it into
118 		 * an IPv6 mapped IPv4 address.
119 		 */
120 		if (inet_pton(AF_INET6, val, &ipv6) != 1) {
121 			if (inet_pton(AF_INET, val, &ip) != 1)
122 				return (DLADM_STATUS_BADARG);
123 
124 			IN6_INADDR_TO_V4MAPPED(&ip, &ipv6);
125 		}
126 		bcopy(&ipv6, buf, sizeof (struct in6_addr));
127 		*sizep = sizeof (struct in6_addr);
128 		break;
129 	default:
130 		abort();
131 	}
132 
133 	return (DLADM_STATUS_OK);
134 }
135 
136 /* ARGSUSED */
137 static dladm_status_t
138 dladm_overlay_varpd_setprop(dladm_handle_t handle, varpd_client_handle_t *chdl,
139     uint64_t inst, const char *name, char *const *valp, uint_t cnt)
140 {
141 	int ret;
142 	uint32_t size;
143 	uint8_t buf[LIBVARPD_PROP_SIZEMAX];
144 	varpd_client_prop_handle_t *phdl;
145 	uint_t type;
146 	dladm_status_t status;
147 
148 	if ((ret = libvarpd_c_prop_handle_alloc(chdl, inst, &phdl)) != 0)
149 		return (dladm_errno2status(ret));
150 
151 	if ((ret = libvarpd_c_prop_info_fill_by_name(phdl, name)) != 0) {
152 		libvarpd_c_prop_handle_free(phdl);
153 		return (dladm_errno2status(ret));
154 	}
155 
156 	if ((ret = libvarpd_c_prop_info(phdl, NULL, &type, NULL, NULL, NULL,
157 	    NULL)) != 0) {
158 		libvarpd_c_prop_handle_free(phdl);
159 		return (dladm_errno2status(ret));
160 	}
161 
162 	if ((status = dladm_overlay_parse_prop(type, buf, &size, valp[0])) !=
163 	    DLADM_STATUS_OK) {
164 		libvarpd_c_prop_handle_free(phdl);
165 		return (status);
166 	}
167 
168 	ret = libvarpd_c_prop_set(phdl, buf, size);
169 	libvarpd_c_prop_handle_free(phdl);
170 
171 	return (dladm_errno2status(ret));
172 }
173 
174 dladm_status_t
175 dladm_overlay_setprop(dladm_handle_t handle, datalink_id_t linkid,
176     const char *name, char *const *valp, uint_t cnt)
177 {
178 	int			ret;
179 	dladm_status_t		status;
180 	overlay_ioc_propinfo_t	info;
181 	overlay_ioc_prop_t	prop;
182 
183 	if (linkid == DATALINK_INVALID_LINKID ||
184 	    name == NULL || valp == NULL || cnt != 1)
185 		return (DLADM_STATUS_BADARG);
186 
187 	bzero(&info, sizeof (overlay_ioc_propinfo_t));
188 	info.oipi_linkid = linkid;
189 	info.oipi_id = -1;
190 	if (strlcpy(info.oipi_name, name, OVERLAY_PROP_NAMELEN) >=
191 	    OVERLAY_PROP_NAMELEN)
192 		return (DLADM_STATUS_BADARG);
193 
194 	status = DLADM_STATUS_OK;
195 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_PROPINFO, &info);
196 	if (ret != 0)
197 		status = dladm_errno2status(errno);
198 
199 	if (status != DLADM_STATUS_OK)
200 		return (status);
201 
202 	prop.oip_linkid = linkid;
203 	prop.oip_id = info.oipi_id;
204 	prop.oip_name[0] = '\0';
205 	if ((ret = dladm_overlay_parse_prop(info.oipi_type, prop.oip_value,
206 	    &prop.oip_size, valp[0])) != DLADM_STATUS_OK)
207 		return (ret);
208 
209 	status = DLADM_STATUS_OK;
210 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_SETPROP, &prop);
211 	if (ret != 0)
212 		status = dladm_errno2status(errno);
213 
214 	return (ret);
215 }
216 
217 /*
218  * Tell the user about any unset required properties.
219  */
220 static int
221 dladm_overlay_activate_cb(dladm_handle_t handle, datalink_id_t linkid,
222     dladm_overlay_propinfo_handle_t phdl, void *arg)
223 {
224 	dladm_status_t status;
225 	uint8_t buf[DLADM_OVERLAY_PROP_SIZEMAX];
226 	uint_t prot;
227 	size_t size = sizeof (buf);
228 	const char *name;
229 	dladm_errlist_t *errs = arg;
230 
231 	if ((status = dladm_overlay_prop_info(phdl, &name, NULL, &prot, NULL,
232 	    NULL, NULL)) != DLADM_STATUS_OK)
233 		return (status);
234 
235 	if ((prot & OVERLAY_PROP_PERM_REQ) == 0)
236 		return (DLADM_WALK_CONTINUE);
237 
238 	if (dladm_overlay_get_prop(handle, linkid, phdl, buf, &size) !=
239 	    DLADM_STATUS_OK)
240 		return (DLADM_WALK_CONTINUE);
241 
242 	if (size == 0)
243 		(void) dladm_errlist_append(errs, "unset required property: %s",
244 		    name);
245 
246 	return (DLADM_WALK_CONTINUE);
247 }
248 
249 /*
250  * We need to clean up the world here. The problem is that we may or may not
251  * actually have everything created. While in the normal case, we'd always have
252  * an overlay device, assigned datalink id, and a varpd instance, we might not
253  * have any of those, except for the datalink instance. Therefore, as long as
254  * the id refers to a valid overlay, we should try to clean up as much of the
255  * state as possible and most importantly, we need to make sure we delete the
256  * datalink id. If we fail to do that, then that name will become lost to time.
257  */
258 dladm_status_t
259 dladm_overlay_delete(dladm_handle_t handle, datalink_id_t linkid)
260 {
261 	datalink_class_t class;
262 	overlay_ioc_delete_t oid;
263 	varpd_client_handle_t *chdl;
264 	int ret;
265 	uint32_t flags;
266 	uint64_t varpdid;
267 
268 	if (dladm_datalink_id2info(handle, linkid, &flags, &class, NULL,
269 	    NULL, 0) != DLADM_STATUS_OK)
270 		return (DLADM_STATUS_BADARG);
271 
272 	if (class != DATALINK_CLASS_OVERLAY)
273 		return (DLADM_STATUS_BADARG);
274 
275 	oid.oid_linkid = linkid;
276 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_DELETE, &oid);
277 	if (ret != 0 && errno != ENOENT) {
278 		return (dladm_errno2status(errno));
279 	}
280 
281 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0) {
282 		return (dladm_errno2status(ret));
283 	}
284 
285 	if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
286 		if (ret == ENOENT) {
287 			goto finish;
288 		}
289 		(void) libvarpd_c_destroy(chdl);
290 		return (dladm_errno2status(ret));
291 	}
292 
293 	ret = libvarpd_c_instance_destroy(chdl, varpdid);
294 finish:
295 	(void) libvarpd_c_destroy(chdl);
296 	(void) dladm_destroy_datalink_id(handle, linkid, flags);
297 
298 	return (dladm_errno2status(ret));
299 }
300 
301 dladm_status_t
302 dladm_overlay_get_prop(dladm_handle_t handle, datalink_id_t linkid,
303     dladm_overlay_propinfo_handle_t infohdl, void *buf, size_t *sizep)
304 {
305 	int ret;
306 	overlay_ioc_prop_t oip;
307 	dladm_overlay_propinfo_t *infop = (dladm_overlay_propinfo_t *)infohdl;
308 
309 	/*
310 	 * It'd be nice if we had a better or more specific error for this. If
311 	 * this kind of error becomes common place, let's get a better dladm
312 	 * error.
313 	 */
314 	if (*sizep < DLADM_OVERLAY_PROP_SIZEMAX)
315 		return (dladm_errno2status(ERANGE));
316 
317 	if (infop->dop_isvarpd == B_FALSE) {
318 		bzero(&oip, sizeof (overlay_ioc_prop_t));
319 		oip.oip_linkid = linkid;
320 		oip.oip_id = infop->dop_un.dop_overlay->oipi_id;
321 		ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_GETPROP, &oip);
322 		if (ret != 0)
323 			return (dladm_errno2status(errno));
324 		bcopy(oip.oip_value, buf, DLADM_OVERLAY_PROP_SIZEMAX);
325 		*sizep = oip.oip_size;
326 	} else {
327 		uint32_t size = *sizep;
328 
329 		ret = libvarpd_c_prop_get(infop->dop_un.dop_varpd, buf, &size);
330 		if (ret != 0)
331 			return (dladm_errno2status(errno));
332 		*sizep = size;
333 	}
334 
335 	return (DLADM_STATUS_OK);
336 }
337 
338 static dladm_status_t
339 dladm_overlay_walk_varpd_prop(dladm_handle_t handle, datalink_id_t linkid,
340     uint64_t varpdid, dladm_overlay_prop_f func, void *arg)
341 {
342 	int ret, i;
343 	varpd_client_handle_t *chdl;
344 	varpd_client_prop_handle_t *phdl;
345 	uint_t nprops;
346 	dladm_status_t status;
347 
348 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
349 		return (dladm_errno2status(ret));
350 
351 	if ((ret = libvarpd_c_prop_handle_alloc(chdl, varpdid, &phdl)) != 0) {
352 		(void) libvarpd_c_destroy(chdl);
353 		return (dladm_errno2status(ret));
354 	}
355 
356 	if ((ret = libvarpd_c_prop_nprops(chdl, varpdid, &nprops)) != 0) {
357 		libvarpd_c_prop_handle_free(phdl);
358 		(void) libvarpd_c_destroy(chdl);
359 		return (dladm_errno2status(ret));
360 	}
361 
362 	status = DLADM_STATUS_OK;
363 	for (i = 0; i < nprops; i++) {
364 		dladm_overlay_propinfo_t dop;
365 
366 		bzero(&dop, sizeof (dop));
367 		dop.dop_isvarpd = B_TRUE;
368 		dop.dop_un.dop_varpd = phdl;
369 
370 		if ((ret = libvarpd_c_prop_info_fill(phdl, i)) != 0) {
371 			status = dladm_errno2status(ret);
372 			break;
373 		}
374 
375 		ret = func(handle, linkid,
376 		    (dladm_overlay_propinfo_handle_t)&dop, arg);
377 		if (ret == DLADM_WALK_TERMINATE)
378 			break;
379 	}
380 
381 	libvarpd_c_prop_handle_free(phdl);
382 	libvarpd_c_destroy(chdl);
383 
384 	return (status);
385 }
386 
387 dladm_status_t
388 dladm_overlay_walk_prop(dladm_handle_t handle, datalink_id_t linkid,
389     dladm_overlay_prop_f func, void *arg, dladm_errlist_t *errs)
390 {
391 	int i, ret;
392 	char buf[MAXLINKNAMELEN];
393 	char errmsg[DLADM_STRSIZE];
394 	datalink_class_t class;
395 	dladm_status_t info_status;
396 	overlay_ioc_nprops_t oin;
397 	overlay_ioc_propinfo_t oipi;
398 	dladm_overlay_propinfo_t dop;
399 	uint64_t varpdid = UINT64_MAX;
400 
401 	if ((info_status = dladm_datalink_id2info(handle, linkid, NULL, &class,
402 	    NULL, buf, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
403 		(void) dladm_errlist_append(errs, "failed to get info for "
404 		    "datalink id %u: %s",
405 		    linkid, dladm_status2str(info_status, errmsg));
406 		return (DLADM_STATUS_BADARG);
407 	}
408 
409 	if (class != DATALINK_CLASS_OVERLAY) {
410 		(void) dladm_errlist_append(errs, "%s is not an overlay", buf);
411 		return (DLADM_STATUS_BADARG);
412 	}
413 
414 	bzero(&oin, sizeof (overlay_ioc_nprops_t));
415 	oin.oipn_linkid = linkid;
416 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_NPROPS, &oin);
417 	if (ret != 0) {
418 		(void) dladm_errlist_append(errs, "failed to get "
419 		    "overlay properties for overlay %s: %s",
420 		    buf, strerror(errno));
421 		return (dladm_errno2status(errno));
422 	}
423 
424 	for (i = 0; i < oin.oipn_nprops; i++) {
425 		bzero(&dop, sizeof (dladm_overlay_propinfo_t));
426 		bzero(&oipi, sizeof (overlay_ioc_propinfo_t));
427 		oipi.oipi_linkid = linkid;
428 		oipi.oipi_id = i;
429 		ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_PROPINFO, &oipi);
430 		if (ret != 0) {
431 			(void) dladm_errlist_append(errs, "failed to get "
432 			    "propinfo for overlay %s, property %d: %s",
433 			    buf, i, strerror(errno));
434 			return (dladm_errno2status(errno));
435 		}
436 
437 		dop.dop_isvarpd = B_FALSE;
438 		dop.dop_un.dop_overlay = &oipi;
439 		ret = func(handle, linkid,
440 		    (dladm_overlay_propinfo_handle_t)&dop, arg);
441 		if (ret == DLADM_WALK_TERMINATE)
442 			break;
443 
444 		if (strcmp(oipi.oipi_name, VARPD_PROPERTY_NAME) == 0) {
445 			uint8_t buf[DLADM_OVERLAY_PROP_SIZEMAX];
446 			size_t bufsize = sizeof (buf);
447 			uint64_t *vp;
448 
449 			if (dladm_overlay_get_prop(handle, linkid,
450 			    (dladm_overlay_propinfo_handle_t)&dop, buf,
451 			    &bufsize) != DLADM_STATUS_OK)
452 				continue;
453 
454 			vp = (uint64_t *)buf;
455 			varpdid = *vp;
456 		}
457 	}
458 
459 	/* Should this really be possible? */
460 	if (varpdid == UINT64_MAX)
461 		return (DLADM_STATUS_OK);
462 
463 	ret = dladm_overlay_walk_varpd_prop(handle, linkid, varpdid, func,
464 	    arg);
465 	if (ret != DLADM_STATUS_OK) {
466 		(void) dladm_errlist_append(errs,
467 		    "failed to get varpd props for "
468 		    "overlay %s, varpd id %llu: %s",
469 		    buf, varpdid, dladm_status2str(info_status, errmsg));
470 	}
471 	return (ret);
472 }
473 
474 dladm_status_t
475 dladm_overlay_create(dladm_handle_t handle, const char *name,
476     const char *encap, const char *search, uint64_t vid,
477     dladm_arg_list_t *props, dladm_errlist_t *errs, uint32_t flags)
478 {
479 	int ret, i;
480 	dladm_status_t status;
481 	datalink_id_t linkid;
482 	overlay_ioc_create_t oic;
483 	overlay_ioc_activate_t oia;
484 	size_t slen;
485 	varpd_client_handle_t *vch;
486 	uint64_t id;
487 
488 	status = dladm_create_datalink_id(handle, name, DATALINK_CLASS_OVERLAY,
489 	    DL_ETHER, flags, &linkid);
490 	if (status != DLADM_STATUS_OK)
491 		return (status);
492 
493 	bzero(&oic, sizeof (oic));
494 	oic.oic_linkid = linkid;
495 	oic.oic_vnetid = vid;
496 	(void) strlcpy(oic.oic_encap, encap, MAXLINKNAMELEN);
497 
498 	status = DLADM_STATUS_OK;
499 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_CREATE, &oic);
500 	if (ret != 0) {
501 		/*
502 		 * It'd be nice if we had private errors so we could better
503 		 * distinguish between different classes of errors.
504 		 */
505 		status = dladm_errno2status(errno);
506 	}
507 
508 	if (status != DLADM_STATUS_OK) {
509 		(void) dladm_destroy_datalink_id(handle, linkid, flags);
510 		return (status);
511 	}
512 
513 	slen = strlen(search);
514 	for (i = 0; props != NULL && i < props->al_count; i++) {
515 		dladm_arg_info_t	*aip = &props->al_info[i];
516 
517 		/*
518 		 * If it's a property for the search plugin, eg. it has the
519 		 * prefix '<search>/', then we don't set the property on the
520 		 * overlay device and instead set it on the varpd instance.
521 		 */
522 		if (strncmp(aip->ai_name, search, slen) == 0 &&
523 		    aip->ai_name[slen] == '/')
524 			continue;
525 		status = dladm_overlay_setprop(handle, linkid, aip->ai_name,
526 		    aip->ai_val, aip->ai_count);
527 		if (status != DLADM_STATUS_OK) {
528 			(void) dladm_errlist_append(errs,
529 			    "failed to set property %s",
530 			    aip->ai_name);
531 			(void) dladm_overlay_delete(handle, linkid);
532 			return (status);
533 		}
534 	}
535 
536 	if ((ret = libvarpd_c_create(&vch, dladm_overlay_doorpath)) != 0) {
537 		(void) dladm_errlist_append(errs,
538 		    "failed to create libvarpd handle: %s", strerror(ret));
539 		(void) dladm_overlay_delete(handle, linkid);
540 		return (dladm_errno2status(ret));
541 	}
542 
543 	if ((ret = libvarpd_c_instance_create(vch, linkid, search,
544 	    &id)) != 0) {
545 		(void) dladm_errlist_append(errs,
546 		    "failed to create varpd instance: %s", strerror(ret));
547 		libvarpd_c_destroy(vch);
548 		(void) dladm_overlay_delete(handle, linkid);
549 		return (dladm_errno2status(ret));
550 	}
551 
552 	for (i = 0; props != NULL && i < props->al_count; i++) {
553 		dladm_arg_info_t	*aip = &props->al_info[i];
554 
555 		/*
556 		 * Skip arguments we've processed already.
557 		 */
558 		if (strncmp(aip->ai_name, search, slen) != 0)
559 			continue;
560 
561 		if (aip->ai_name[slen] != '/')
562 			continue;
563 
564 		ret = dladm_overlay_varpd_setprop(handle, vch, id, aip->ai_name,
565 		    aip->ai_val, aip->ai_count);
566 		if (ret != 0) {
567 			(void) dladm_errlist_append(errs,
568 			    "failed to set varpd prop: %s\n",
569 			    aip->ai_name);
570 			(void) libvarpd_c_instance_destroy(vch, id);
571 			libvarpd_c_destroy(vch);
572 			(void) dladm_overlay_delete(handle, linkid);
573 			return (dladm_errno2status(ret));
574 		}
575 	}
576 
577 	if ((ret = libvarpd_c_instance_activate(vch, id)) != 0) {
578 		(void) dladm_errlist_append(errs,
579 		    "failed to activate varpd instance: %s", strerror(ret));
580 		(void) dladm_overlay_walk_varpd_prop(handle, linkid, id,
581 		    dladm_overlay_activate_cb, errs);
582 		(void) libvarpd_c_instance_destroy(vch, id);
583 		libvarpd_c_destroy(vch);
584 		(void) dladm_overlay_delete(handle, linkid);
585 		return (dladm_errno2status(ret));
586 
587 	}
588 
589 	bzero(&oia, sizeof (oia));
590 	oia.oia_linkid = linkid;
591 	status = DLADM_STATUS_OK;
592 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_ACTIVATE, &oia);
593 	if (ret != 0) {
594 		ret = errno;
595 		(void) dladm_errlist_append(errs, "failed to activate "
596 		    "device: %s", strerror(ret));
597 		(void) libvarpd_c_instance_destroy(vch, id);
598 		(void) dladm_overlay_walk_prop(handle, linkid,
599 		    dladm_overlay_activate_cb, errs, errs);
600 		status = dladm_errno2status(ret);
601 		(void) libvarpd_c_instance_destroy(vch, id);
602 	}
603 
604 	libvarpd_c_destroy(vch);
605 	if (status != DLADM_STATUS_OK)
606 		(void) dladm_overlay_delete(handle, linkid);
607 
608 	return (status);
609 }
610 
611 
612 
613 typedef struct overlay_walk_cb {
614 	dladm_handle_t		owc_handle;
615 	datalink_id_t		owc_linkid;
616 	void			*owc_arg;
617 	dladm_overlay_cache_f	owc_func;
618 	uint_t			owc_mode;
619 	uint_t			owc_dest;
620 } overlay_walk_cb_t;
621 
622 /* ARGSUSED */
623 static int
624 dladm_overlay_walk_cache_cb(varpd_client_handle_t *chdl, uint64_t varpdid,
625     const struct ether_addr *key, const varpd_client_cache_entry_t *entry,
626     void *arg)
627 {
628 	overlay_walk_cb_t *owc = arg;
629 	dladm_overlay_point_t point;
630 
631 	bzero(&point, sizeof (dladm_overlay_point_t));
632 	point.dop_dest = owc->owc_dest;
633 	point.dop_mac = entry->vcp_mac;
634 	point.dop_flags = entry->vcp_flags;
635 	point.dop_ip = entry->vcp_ip;
636 	point.dop_port = entry->vcp_port;
637 
638 	if (owc->owc_mode == OVERLAY_TARGET_POINT)
639 		point.dop_flags |= DLADM_OVERLAY_F_DEFAULT;
640 
641 	if (owc->owc_func(owc->owc_handle, owc->owc_linkid, key, &point,
642 	    owc->owc_arg) == DLADM_WALK_TERMINATE)
643 		return (1);
644 	return (0);
645 }
646 
647 dladm_status_t
648 dladm_overlay_walk_cache(dladm_handle_t handle, datalink_id_t linkid,
649     dladm_overlay_cache_f func, void *arg)
650 {
651 	int ret;
652 	uint_t mode, dest;
653 	uint64_t varpdid;
654 	varpd_client_handle_t *chdl;
655 	overlay_walk_cb_t cbarg;
656 
657 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
658 		return (dladm_errno2status(ret));
659 
660 	if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
661 		libvarpd_c_destroy(chdl);
662 		return (dladm_errno2status(ret));
663 	}
664 
665 	if ((ret = libvarpd_c_instance_target_mode(chdl, varpdid,
666 	    &dest, &mode)) != 0) {
667 		libvarpd_c_destroy(chdl);
668 		return (dladm_errno2status(ret));
669 	}
670 
671 	cbarg.owc_handle = handle;
672 	cbarg.owc_linkid = linkid;
673 	cbarg.owc_arg = arg;
674 	cbarg.owc_func = func;
675 	cbarg.owc_dest = dest;
676 	cbarg.owc_mode = mode;
677 	ret = libvarpd_c_instance_cache_walk(chdl, varpdid,
678 	    dladm_overlay_walk_cache_cb, &cbarg);
679 	libvarpd_c_destroy(chdl);
680 
681 	return (dladm_errno2status(ret));
682 }
683 
684 /* ARGSUSED */
685 dladm_status_t
686 dladm_overlay_cache_flush(dladm_handle_t handle, datalink_id_t linkid)
687 {
688 	int ret;
689 	uint64_t varpdid;
690 	varpd_client_handle_t *chdl;
691 
692 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
693 		return (dladm_errno2status(ret));
694 
695 	if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
696 		libvarpd_c_destroy(chdl);
697 		return (dladm_errno2status(ret));
698 	}
699 
700 	ret = libvarpd_c_instance_cache_flush(chdl, varpdid);
701 	libvarpd_c_destroy(chdl);
702 
703 	return (dladm_errno2status(ret));
704 }
705 
706 /* ARGSUSED */
707 dladm_status_t
708 dladm_overlay_cache_delete(dladm_handle_t handle, datalink_id_t linkid,
709     const struct ether_addr *key)
710 {
711 	int ret;
712 	uint64_t varpdid;
713 	varpd_client_handle_t *chdl;
714 
715 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
716 		return (dladm_errno2status(ret));
717 
718 	if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
719 		libvarpd_c_destroy(chdl);
720 		return (dladm_errno2status(ret));
721 	}
722 
723 	ret = libvarpd_c_instance_cache_delete(chdl, varpdid, key);
724 	libvarpd_c_destroy(chdl);
725 
726 	return (dladm_errno2status(ret));
727 }
728 
729 /* ARGSUSED */
730 dladm_status_t
731 dladm_overlay_cache_set(dladm_handle_t handle, datalink_id_t linkid,
732     const struct ether_addr *key, char *val)
733 {
734 	int ret;
735 	uint_t dest;
736 	uint64_t varpdid;
737 	char *ip, *port = NULL;
738 	varpd_client_handle_t *chdl;
739 	varpd_client_cache_entry_t vcp;
740 
741 
742 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
743 		return (dladm_errno2status(ret));
744 
745 	if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
746 		libvarpd_c_destroy(chdl);
747 		return (dladm_errno2status(ret));
748 	}
749 
750 	if ((ret = libvarpd_c_instance_target_mode(chdl, varpdid,
751 	    &dest, NULL)) != 0) {
752 		libvarpd_c_destroy(chdl);
753 		return (dladm_errno2status(ret));
754 	}
755 
756 	/*
757 	 * Mode tells us what we should expect in val. It we have more than one
758 	 * thing listed, the canonical format of it right now is mac,ip:port.
759 	 */
760 	bzero(&vcp, sizeof (varpd_client_cache_entry_t));
761 
762 	if (strcasecmp(val, "drop") == 0) {
763 		vcp.vcp_flags = OVERLAY_TARGET_CACHE_DROP;
764 		goto send;
765 	}
766 
767 	if (dest & OVERLAY_PLUGIN_D_ETHERNET) {
768 		if (ether_aton_r(val, &vcp.vcp_mac) == NULL) {
769 			libvarpd_c_destroy(chdl);
770 			return (dladm_errno2status(EINVAL));
771 		}
772 	}
773 
774 	if (dest & OVERLAY_PLUGIN_D_IP) {
775 		if (dest & OVERLAY_PLUGIN_D_ETHERNET) {
776 			if ((ip = strchr(val, ',')) == NULL) {
777 				libvarpd_c_destroy(chdl);
778 				return (dladm_errno2status(ret));
779 			}
780 			ip++;
781 		} else {
782 			ip = val;
783 		}
784 
785 		if (dest & OVERLAY_PLUGIN_D_PORT) {
786 			if ((port = strchr(val, ':')) == NULL) {
787 				libvarpd_c_destroy(chdl);
788 				return (dladm_errno2status(ret));
789 			}
790 			*port = '\0';
791 			port++;
792 		}
793 
794 		/* Try v6, then fall back to v4 */
795 		ret = inet_pton(AF_INET6, ip, &vcp.vcp_ip);
796 		if (ret == -1)
797 			abort();
798 		if (ret == 0) {
799 			struct in_addr v4;
800 
801 			ret = inet_pton(AF_INET, ip, &v4);
802 			if (ret == -1)
803 				abort();
804 			if (ret == 0) {
805 				libvarpd_c_destroy(chdl);
806 				return (dladm_errno2status(ret));
807 			}
808 			IN6_INADDR_TO_V4MAPPED(&v4, &vcp.vcp_ip);
809 		}
810 	}
811 
812 	if (dest & OVERLAY_PLUGIN_D_PORT) {
813 		char *eptr;
814 		unsigned long l;
815 		if (port == NULL && (dest & OVERLAY_PLUGIN_D_ETHERNET)) {
816 			if ((port = strchr(val, ',')) == NULL) {
817 				libvarpd_c_destroy(chdl);
818 				return (dladm_errno2status(EINVAL));
819 			}
820 		} else if (port == NULL)
821 			port = val;
822 
823 		errno = 0;
824 		l = strtoul(port, &eptr, 10);
825 		if (errno != 0 || *eptr != '\0') {
826 			libvarpd_c_destroy(chdl);
827 			return (dladm_errno2status(EINVAL));
828 		}
829 		if (l == 0 || l > UINT16_MAX) {
830 			libvarpd_c_destroy(chdl);
831 			return (dladm_errno2status(EINVAL));
832 		}
833 		vcp.vcp_port = l;
834 	}
835 
836 send:
837 	ret = libvarpd_c_instance_cache_set(chdl, varpdid, key, &vcp);
838 
839 	libvarpd_c_destroy(chdl);
840 	return (dladm_errno2status(ret));
841 }
842 
843 /* ARGSUSED */
844 dladm_status_t
845 dladm_overlay_cache_get(dladm_handle_t handle, datalink_id_t linkid,
846     const struct ether_addr *key, dladm_overlay_point_t *point)
847 {
848 	int ret;
849 	uint_t dest, mode;
850 	uint64_t varpdid;
851 	varpd_client_handle_t *chdl;
852 	varpd_client_cache_entry_t entry;
853 
854 	if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
855 		return (dladm_errno2status(ret));
856 
857 	if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
858 		libvarpd_c_destroy(chdl);
859 		return (dladm_errno2status(ret));
860 	}
861 
862 	if ((ret = libvarpd_c_instance_target_mode(chdl, varpdid,
863 	    &dest, &mode)) != 0) {
864 		libvarpd_c_destroy(chdl);
865 		return (dladm_errno2status(ret));
866 	}
867 
868 	ret = libvarpd_c_instance_cache_get(chdl, varpdid, key, &entry);
869 	if (ret == 0) {
870 		point->dop_dest = dest;
871 		point->dop_mac = entry.vcp_mac;
872 		point->dop_flags = entry.vcp_flags;
873 		point->dop_ip = entry.vcp_ip;
874 		point->dop_port = entry.vcp_port;
875 		if (mode == OVERLAY_TARGET_POINT)
876 			point->dop_flags |= DLADM_OVERLAY_F_DEFAULT;
877 	}
878 
879 	libvarpd_c_destroy(chdl);
880 	return (dladm_errno2status(ret));
881 }
882 
883 dladm_status_t
884 dladm_overlay_status(dladm_handle_t handle, datalink_id_t linkid,
885     dladm_overlay_status_f func, void *arg)
886 {
887 	int ret;
888 	dladm_status_t status;
889 	overlay_ioc_status_t ois;
890 	dladm_overlay_status_t dos;
891 
892 	ois.ois_linkid = linkid;
893 	status = DLADM_STATUS_OK;
894 	ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_STATUS, &ois);
895 	if (ret != 0)
896 		status = dladm_errno2status(errno);
897 	if (status != DLADM_STATUS_OK)
898 		return (status);
899 
900 	dos.dos_degraded = ois.ois_status == OVERLAY_I_DEGRADED ? B_TRUE :
901 	    B_FALSE;
902 	(void) strlcpy(dos.dos_fmamsg, ois.ois_message,
903 	    sizeof (dos.dos_fmamsg));
904 	func(handle, linkid, &dos, arg);
905 	return (DLADM_STATUS_OK);
906 }
907