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