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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <door.h>
27 #include <errno.h>
28 #include <assert.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/aggr.h>
37 #include <fcntl.h>
38 #include <libdladm.h>
39 #include <libdladm_impl.h>
40 #include <libdllink.h>
41 #include <libdlmgmt.h>
42 
43 /*
44  * Table of data type sizes indexed by dladm_datatype_t.
45  */
46 static size_t dladm_datatype_size[] = {
47 	0,				/* DLADM_TYPE_STR, use strnlen() */
48 	sizeof (boolean_t),		/* DLADM_TYPE_BOOLEAN */
49 	sizeof (uint64_t)		/* DLADM_TYPE_UINT64 */
50 };
51 
52 static dladm_status_t
53 dladm_door_call(dladm_handle_t handle, void *arg, size_t asize, void *rbuf,
54     size_t rsize)
55 {
56 	door_arg_t	darg;
57 	int		door_fd;
58 	dladm_status_t	status = DLADM_STATUS_OK;
59 
60 	darg.data_ptr	= arg;
61 	darg.data_size	= asize;
62 	darg.desc_ptr	= NULL;
63 	darg.desc_num	= 0;
64 	darg.rbuf	= rbuf;
65 	darg.rsize	= rsize;
66 
67 	/* The door descriptor is opened if it isn't already */
68 	if ((status = dladm_door_fd(handle, &door_fd)) != DLADM_STATUS_OK)
69 		return (status);
70 	if (door_call(door_fd, &darg) == -1)
71 		status = dladm_errno2status(errno);
72 	if (status != DLADM_STATUS_OK)
73 		return (status);
74 
75 	if (darg.rbuf != rbuf) {
76 		/*
77 		 * The size of the input rbuf is not big enough so that
78 		 * the door allocate the rbuf itself. In this case, simply
79 		 * think something wrong with the door call.
80 		 */
81 		(void) munmap(darg.rbuf, darg.rsize);
82 		return (DLADM_STATUS_TOOSMALL);
83 	}
84 	if (darg.rsize != rsize)
85 		return (DLADM_STATUS_FAILED);
86 
87 	return (dladm_errno2status(((dlmgmt_retval_t *)rbuf)->lr_err));
88 }
89 
90 /*
91  * Allocate a new linkid with the given name. Return the new linkid.
92  */
93 dladm_status_t
94 dladm_create_datalink_id(dladm_handle_t handle, const char *link,
95     datalink_class_t class, uint32_t media, uint32_t flags,
96     datalink_id_t *linkidp)
97 {
98 	dlmgmt_door_createid_t	createid;
99 	dlmgmt_createid_retval_t retval;
100 	uint32_t		dlmgmt_flags;
101 	dladm_status_t		status;
102 
103 	if (link == NULL || class == DATALINK_CLASS_ALL ||
104 	    !(flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST)) ||
105 	    linkidp == NULL) {
106 		return (DLADM_STATUS_BADARG);
107 	}
108 
109 	dlmgmt_flags = (flags & DLADM_OPT_ACTIVE) ? DLMGMT_ACTIVE : 0;
110 	dlmgmt_flags |= (flags & DLADM_OPT_PERSIST) ? DLMGMT_PERSIST : 0;
111 
112 	(void) strlcpy(createid.ld_link, link, MAXLINKNAMELEN);
113 	createid.ld_class = class;
114 	createid.ld_media = media;
115 	createid.ld_flags = dlmgmt_flags;
116 	createid.ld_cmd = DLMGMT_CMD_CREATE_LINKID;
117 	createid.ld_prefix = (flags & DLADM_OPT_PREFIX);
118 
119 	if ((status = dladm_door_call(handle, &createid, sizeof (createid),
120 	    &retval, sizeof (retval))) == DLADM_STATUS_OK) {
121 		*linkidp = retval.lr_linkid;
122 	}
123 	return (status);
124 }
125 
126 /*
127  * Destroy the given link ID.
128  */
129 dladm_status_t
130 dladm_destroy_datalink_id(dladm_handle_t handle, datalink_id_t linkid,
131     uint32_t flags)
132 {
133 	dlmgmt_door_destroyid_t		destroyid;
134 	dlmgmt_destroyid_retval_t	retval;
135 	uint32_t			dlmgmt_flags;
136 
137 	dlmgmt_flags = (flags & DLADM_OPT_ACTIVE) ? DLMGMT_ACTIVE : 0;
138 	dlmgmt_flags |= ((flags & DLADM_OPT_PERSIST) ? DLMGMT_PERSIST : 0);
139 
140 	destroyid.ld_cmd = DLMGMT_CMD_DESTROY_LINKID;
141 	destroyid.ld_linkid = linkid;
142 	destroyid.ld_flags = dlmgmt_flags;
143 
144 	return (dladm_door_call(handle, &destroyid, sizeof (destroyid), &retval,
145 	    sizeof (retval)));
146 }
147 
148 /*
149  * Remap a given link ID to a new name.
150  */
151 dladm_status_t
152 dladm_remap_datalink_id(dladm_handle_t handle, datalink_id_t linkid,
153     const char *link)
154 {
155 	dlmgmt_door_remapid_t	remapid;
156 	dlmgmt_remapid_retval_t	retval;
157 
158 	remapid.ld_cmd = DLMGMT_CMD_REMAP_LINKID;
159 	remapid.ld_linkid = linkid;
160 	(void) strlcpy(remapid.ld_link, link, MAXLINKNAMELEN);
161 
162 	return (dladm_door_call(handle, &remapid, sizeof (remapid), &retval,
163 	    sizeof (retval)));
164 }
165 
166 /*
167  * Make a given link ID active.
168  */
169 dladm_status_t
170 dladm_up_datalink_id(dladm_handle_t handle, datalink_id_t linkid)
171 {
172 	dlmgmt_door_upid_t	upid;
173 	dlmgmt_upid_retval_t	retval;
174 
175 	upid.ld_cmd = DLMGMT_CMD_UP_LINKID;
176 	upid.ld_linkid = linkid;
177 
178 	return (dladm_door_call(handle, &upid, sizeof (upid), &retval,
179 	    sizeof (retval)));
180 }
181 
182 /*
183  * Create a new link with the given name.  Return the new link's handle
184  */
185 dladm_status_t
186 dladm_create_conf(dladm_handle_t handle, const char *link, datalink_id_t linkid,
187     datalink_class_t class, uint32_t media, dladm_conf_t *confp)
188 {
189 	dlmgmt_door_createconf_t	createconf;
190 	dlmgmt_createconf_retval_t	retval;
191 	dladm_status_t			status;
192 
193 	if (link == NULL || confp == NULL)
194 		return (DLADM_STATUS_BADARG);
195 
196 	(void) strlcpy(createconf.ld_link, link, MAXLINKNAMELEN);
197 	createconf.ld_class = class;
198 	createconf.ld_media = media;
199 	createconf.ld_linkid = linkid;
200 	createconf.ld_cmd = DLMGMT_CMD_CREATECONF;
201 
202 	if ((status = dladm_door_call(handle, &createconf, sizeof (createconf),
203 	    &retval, sizeof (retval))) == DLADM_STATUS_OK) {
204 		*confp = retval.lr_conf;
205 	}
206 	return (status);
207 }
208 
209 /*
210  * An active physical link reported by the dlmgmtd daemon might not be active
211  * anymore as this link might be removed during system shutdown. Check its
212  * real status by calling dladm_phys_info().
213  */
214 dladm_status_t
215 i_dladm_phys_status(dladm_handle_t handle, datalink_id_t linkid,
216     uint32_t *flagsp)
217 {
218 	dladm_phys_attr_t	dpa;
219 	dladm_status_t		status;
220 
221 	assert((*flagsp) & DLMGMT_ACTIVE);
222 
223 	status = dladm_phys_info(handle, linkid, &dpa, DLADM_OPT_ACTIVE);
224 	if (status == DLADM_STATUS_NOTFOUND) {
225 		/*
226 		 * No active status, this link was removed. Update its status
227 		 * in the daemon and delete all active linkprops.
228 		 *
229 		 * Note that the operation could fail. If it does, return
230 		 * failure now since otherwise dladm_set_linkprop() might
231 		 * call back to i_dladm_phys_status() recursively.
232 		 */
233 		if ((status = dladm_destroy_datalink_id(handle, linkid,
234 		    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK)
235 			return (status);
236 
237 		(void) dladm_set_linkprop(handle, linkid, NULL, NULL, 0,
238 		    DLADM_OPT_ACTIVE);
239 
240 		(*flagsp) &= ~DLMGMT_ACTIVE;
241 		status = DLADM_STATUS_OK;
242 	}
243 	return (status);
244 }
245 
246 /*
247  * Walk each entry in the data link configuration repository and
248  * call fn on the linkid and arg.
249  */
250 dladm_status_t
251 dladm_walk_datalink_id(int (*fn)(dladm_handle_t, datalink_id_t, void *),
252     dladm_handle_t handle, void *argp, datalink_class_t class,
253     datalink_media_t dmedia, uint32_t flags)
254 {
255 	dlmgmt_door_getnext_t	getnext;
256 	dlmgmt_getnext_retval_t	retval;
257 	uint32_t 		dlmgmt_flags;
258 	datalink_id_t		linkid = DATALINK_INVALID_LINKID;
259 	dladm_status_t		status = DLADM_STATUS_OK;
260 
261 	if (fn == NULL)
262 		return (DLADM_STATUS_BADARG);
263 
264 	dlmgmt_flags = (flags & DLADM_OPT_ACTIVE) ? DLMGMT_ACTIVE : 0;
265 	dlmgmt_flags |= ((flags & DLADM_OPT_PERSIST) ? DLMGMT_PERSIST : 0);
266 
267 	getnext.ld_cmd = DLMGMT_CMD_GETNEXT;
268 	getnext.ld_class = class;
269 	getnext.ld_dmedia = dmedia;
270 	getnext.ld_flags = dlmgmt_flags;
271 
272 	do {
273 		getnext.ld_linkid = linkid;
274 		if ((status = dladm_door_call(handle, &getnext,
275 		    sizeof (getnext), &retval, sizeof (retval))) !=
276 		    DLADM_STATUS_OK) {
277 			/*
278 			 * done with walking
279 			 */
280 			break;
281 		}
282 
283 		linkid = retval.lr_linkid;
284 		if ((retval.lr_class == DATALINK_CLASS_PHYS) &&
285 		    (retval.lr_flags & DLMGMT_ACTIVE)) {
286 			/*
287 			 * An active physical link reported by the dlmgmtd
288 			 * daemon might not be active anymore. Check its
289 			 * real status.
290 			 */
291 			if (i_dladm_phys_status(handle, linkid,
292 			    &retval.lr_flags) != DLADM_STATUS_OK) {
293 				continue;
294 			}
295 
296 			if (!(dlmgmt_flags & retval.lr_flags))
297 				continue;
298 		}
299 
300 		if (fn(handle, linkid, argp) == DLADM_WALK_TERMINATE)
301 			break;
302 	} while (linkid != DATALINK_INVALID_LINKID);
303 
304 	return (status);
305 }
306 
307 /*
308  * Get the link properties structure for the given link.
309  */
310 dladm_status_t
311 dladm_read_conf(dladm_handle_t handle, datalink_id_t linkid,
312     dladm_conf_t *confp)
313 {
314 	dlmgmt_door_readconf_t		readconf;
315 	dlmgmt_readconf_retval_t	retval;
316 	dladm_status_t			status;
317 
318 	if (linkid == DATALINK_INVALID_LINKID || confp == NULL)
319 		return (DLADM_STATUS_BADARG);
320 
321 	readconf.ld_linkid = linkid;
322 	readconf.ld_cmd = DLMGMT_CMD_READCONF;
323 
324 	if ((status = dladm_door_call(handle, &readconf, sizeof (readconf),
325 	    &retval, sizeof (retval))) == DLADM_STATUS_OK) {
326 		*confp = retval.lr_conf;
327 	}
328 	return (status);
329 }
330 
331 /*
332  * Commit the given link to the data link configuration repository so
333  * that it will persist across reboots.
334  */
335 dladm_status_t
336 dladm_write_conf(dladm_handle_t handle, dladm_conf_t conf)
337 {
338 	dlmgmt_door_writeconf_t		writeconf;
339 	dlmgmt_writeconf_retval_t	retval;
340 
341 	if (conf == DLADM_INVALID_CONF)
342 		return (DLADM_STATUS_BADARG);
343 
344 	writeconf.ld_cmd = DLMGMT_CMD_WRITECONF;
345 	writeconf.ld_conf = conf;
346 
347 	return (dladm_door_call(handle, &writeconf, sizeof (writeconf), &retval,
348 	    sizeof (retval)));
349 }
350 
351 /*
352  * Given a link ID and a key, get the matching information from
353  * data link configuration repository.
354  */
355 dladm_status_t
356 dladm_get_conf_field(dladm_handle_t handle, dladm_conf_t conf, const char *attr,
357     void *attrval, size_t attrsz)
358 {
359 	dlmgmt_door_getattr_t	getattr;
360 	dlmgmt_getattr_retval_t	retval;
361 	dladm_status_t		status;
362 
363 	if (conf == DLADM_INVALID_CONF || attrval == NULL ||
364 	    attrsz == 0 || attr == NULL) {
365 		return (DLADM_STATUS_BADARG);
366 	}
367 
368 	getattr.ld_cmd = DLMGMT_CMD_GETATTR;
369 	getattr.ld_conf = conf;
370 	(void) strlcpy(getattr.ld_attr, attr, MAXLINKATTRLEN);
371 
372 	if ((status = dladm_door_call(handle, &getattr, sizeof (getattr),
373 	    &retval, sizeof (retval))) != DLADM_STATUS_OK) {
374 		return (status);
375 	}
376 
377 	if (retval.lr_attrsz > attrsz)
378 		return (DLADM_STATUS_TOOSMALL);
379 
380 	bcopy(retval.lr_attrval, attrval, retval.lr_attrsz);
381 	return (DLADM_STATUS_OK);
382 }
383 
384 /*
385  * Get next property attribute from data link configuration repository.
386  */
387 dladm_status_t
388 dladm_getnext_conf_linkprop(dladm_handle_t handle, dladm_conf_t conf,
389     const char *last_attr, char *attr, void *attrval, size_t attrsz,
390     size_t *attrszp)
391 {
392 	dlmgmt_door_linkprop_getnext_t		getnext;
393 	dlmgmt_linkprop_getnext_retval_t	retval;
394 	dladm_status_t				status;
395 
396 	if (conf == DLADM_INVALID_CONF || attrval == NULL ||
397 	    attrsz == 0 || attr == NULL) {
398 		return (DLADM_STATUS_BADARG);
399 	}
400 
401 	getnext.ld_cmd = DLMGMT_CMD_LINKPROP_GETNEXT;
402 	getnext.ld_conf = conf;
403 	(void) strlcpy(getnext.ld_last_attr, last_attr, MAXLINKATTRLEN);
404 
405 	if ((status = dladm_door_call(handle, &getnext, sizeof (getnext),
406 	    &retval, sizeof (retval))) != DLADM_STATUS_OK) {
407 		return (status);
408 	}
409 
410 	*attrszp = retval.lr_attrsz;
411 	if (retval.lr_attrsz > attrsz) {
412 		return (DLADM_STATUS_TOOSMALL);
413 	}
414 
415 	(void) strlcpy(attr, retval.lr_attr, MAXLINKATTRLEN);
416 	bcopy(retval.lr_attrval, attrval, retval.lr_attrsz);
417 	return (DLADM_STATUS_OK);
418 }
419 
420 /*
421  * Get the link ID that is associated with the given name.
422  */
423 dladm_status_t
424 dladm_name2info(dladm_handle_t handle, const char *link, datalink_id_t *linkidp,
425     uint32_t *flagp, datalink_class_t *classp, uint32_t *mediap)
426 {
427 	dlmgmt_door_getlinkid_t		getlinkid;
428 	dlmgmt_getlinkid_retval_t	retval;
429 	datalink_id_t			linkid;
430 	dladm_status_t			status;
431 
432 	getlinkid.ld_cmd = DLMGMT_CMD_GETLINKID;
433 	(void) strlcpy(getlinkid.ld_link, link, MAXLINKNAMELEN);
434 
435 	if ((status = dladm_door_call(handle, &getlinkid, sizeof (getlinkid),
436 	    &retval, sizeof (retval))) != DLADM_STATUS_OK) {
437 		return (status);
438 	}
439 
440 	linkid = retval.lr_linkid;
441 	if (retval.lr_class == DATALINK_CLASS_PHYS &&
442 	    retval.lr_flags & DLMGMT_ACTIVE) {
443 		/*
444 		 * An active physical link reported by the dlmgmtd daemon
445 		 * might not be active anymore. Check and set its real status.
446 		 */
447 		status = i_dladm_phys_status(handle, linkid, &retval.lr_flags);
448 		if (status != DLADM_STATUS_OK)
449 			return (status);
450 	}
451 
452 	if (linkidp != NULL)
453 		*linkidp = linkid;
454 	if (flagp != NULL) {
455 		*flagp = retval.lr_flags & DLMGMT_ACTIVE ? DLADM_OPT_ACTIVE : 0;
456 		*flagp |= (retval.lr_flags & DLMGMT_PERSIST) ?
457 		    DLADM_OPT_PERSIST : 0;
458 	}
459 	if (classp != NULL)
460 		*classp = retval.lr_class;
461 	if (mediap != NULL)
462 		*mediap = retval.lr_media;
463 
464 	return (DLADM_STATUS_OK);
465 }
466 
467 /*
468  * Get the link name that is associated with the given id.
469  */
470 dladm_status_t
471 dladm_datalink_id2info(dladm_handle_t handle, datalink_id_t linkid,
472     uint32_t *flagp, datalink_class_t *classp, uint32_t *mediap, char *link,
473     size_t len)
474 {
475 	dlmgmt_door_getname_t	getname;
476 	dlmgmt_getname_retval_t	retval;
477 	dladm_status_t		status;
478 
479 	if ((linkid == DATALINK_INVALID_LINKID) || (link != NULL && len == 0) ||
480 	    (link == NULL && len != 0)) {
481 		return (DLADM_STATUS_BADARG);
482 	}
483 
484 	getname.ld_cmd = DLMGMT_CMD_GETNAME;
485 	getname.ld_linkid = linkid;
486 	if ((status = dladm_door_call(handle, &getname, sizeof (getname),
487 	    &retval, sizeof (retval))) != DLADM_STATUS_OK) {
488 		return (status);
489 	}
490 
491 	if (len != 0 && (strlen(retval.lr_link) + 1 > len))
492 		return (DLADM_STATUS_TOOSMALL);
493 
494 	if (retval.lr_class == DATALINK_CLASS_PHYS &&
495 	    retval.lr_flags & DLMGMT_ACTIVE) {
496 		/*
497 		 * An active physical link reported by the dlmgmtd daemon
498 		 * might not be active anymore. Check and set its real status.
499 		 */
500 		status = i_dladm_phys_status(handle, linkid, &retval.lr_flags);
501 		if (status != DLADM_STATUS_OK)
502 			return (status);
503 	}
504 
505 	if (link != NULL)
506 		(void) strlcpy(link, retval.lr_link, len);
507 	if (classp != NULL)
508 		*classp = retval.lr_class;
509 	if (mediap != NULL)
510 		*mediap = retval.lr_media;
511 	if (flagp != NULL) {
512 		*flagp = retval.lr_flags & DLMGMT_ACTIVE ?
513 		    DLADM_OPT_ACTIVE : 0;
514 		*flagp |= (retval.lr_flags & DLMGMT_PERSIST) ?
515 		    DLADM_OPT_PERSIST : 0;
516 	}
517 	return (DLADM_STATUS_OK);
518 }
519 
520 /*
521  * Set the given attr with the given attrval for the given link.
522  */
523 dladm_status_t
524 dladm_set_conf_field(dladm_handle_t handle, dladm_conf_t conf, const char *attr,
525     dladm_datatype_t type, const void *attrval)
526 {
527 	dlmgmt_door_setattr_t	setattr;
528 	dlmgmt_setattr_retval_t	retval;
529 	size_t			attrsz;
530 
531 	if (attr == NULL || attrval == NULL)
532 		return (DLADM_STATUS_BADARG);
533 
534 	if (type == DLADM_TYPE_STR)
535 		attrsz = strlen(attrval) + 1;
536 	else
537 		attrsz = dladm_datatype_size[type];
538 
539 	if (attrsz > MAXLINKATTRVALLEN)
540 		return (DLADM_STATUS_TOOSMALL);
541 
542 	setattr.ld_cmd = DLMGMT_CMD_SETATTR;
543 	setattr.ld_conf = conf;
544 	(void) strlcpy(setattr.ld_attr, attr, MAXLINKATTRLEN);
545 	setattr.ld_attrsz = attrsz;
546 	setattr.ld_type = type;
547 	bcopy(attrval, &setattr.ld_attrval, attrsz);
548 
549 	return (dladm_door_call(handle, &setattr, sizeof (setattr), &retval,
550 	    sizeof (retval)));
551 }
552 
553 /*
554  * Unset the given attr the given link.
555  */
556 dladm_status_t
557 dladm_unset_conf_field(dladm_handle_t handle, dladm_conf_t conf,
558     const char *attr)
559 {
560 	dlmgmt_door_unsetattr_t		unsetattr;
561 	dlmgmt_unsetattr_retval_t	retval;
562 
563 	if (attr == NULL)
564 		return (DLADM_STATUS_BADARG);
565 
566 	unsetattr.ld_cmd = DLMGMT_CMD_UNSETATTR;
567 	unsetattr.ld_conf = conf;
568 	(void) strlcpy(unsetattr.ld_attr, attr, MAXLINKATTRLEN);
569 
570 	return (dladm_door_call(handle, &unsetattr, sizeof (unsetattr), &retval,
571 	    sizeof (retval)));
572 }
573 
574 /*
575  * Remove the given link ID and its entry from the data link configuration
576  * repository.
577  */
578 dladm_status_t
579 dladm_remove_conf(dladm_handle_t handle, datalink_id_t linkid)
580 {
581 	dlmgmt_door_removeconf_t	removeconf;
582 	dlmgmt_removeconf_retval_t	retval;
583 
584 	removeconf.ld_cmd = DLMGMT_CMD_REMOVECONF;
585 	removeconf.ld_linkid = linkid;
586 
587 	return (dladm_door_call(handle, &removeconf, sizeof (removeconf),
588 	    &retval, sizeof (retval)));
589 }
590 
591 /*
592  * Free the contents of the link structure.
593  */
594 void
595 dladm_destroy_conf(dladm_handle_t handle, dladm_conf_t conf)
596 {
597 	dlmgmt_door_destroyconf_t	destroyconf;
598 	dlmgmt_destroyconf_retval_t	retval;
599 
600 	if (conf == DLADM_INVALID_CONF)
601 		return;
602 
603 	destroyconf.ld_cmd = DLMGMT_CMD_DESTROYCONF;
604 	destroyconf.ld_conf = conf;
605 
606 	(void) dladm_door_call(handle, &destroyconf, sizeof (destroyconf),
607 	    &retval, sizeof (retval));
608 }
609