xref: /titanic_50/usr/src/lib/libdladm/common/libdlbridge.c (revision 42ed7838f131b8f58d6c95db1c7e3a6a3e6ea7e4)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <stropts.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <door.h>
36 #include <sys/mman.h>
37 #include <libscf.h>
38 #include <libscf_priv.h>
39 #include <libdllink.h>
40 #include <libdlbridge.h>
41 #include <libdladm_impl.h>
42 #include <stp_in.h>
43 #include <net/bridge.h>
44 #include <net/trill.h>
45 #include <sys/socket.h>
46 #include <sys/dld_ioc.h>
47 
48 /*
49  * Bridge Administration Library.
50  *
51  * This library is used by administration tools such as dladm(1M) to configure
52  * bridges, and by the bridge daemon to retrieve configuration information.
53  */
54 
55 #define	BRIDGE_SVC_NAME	"network/bridge"
56 #define	TRILL_SVC_NAME	"network/routing/trill"
57 
58 #define	DEFAULT_TIMEOUT	60000000
59 #define	INIT_WAIT_USECS	50000
60 #define	MAXPORTS	256
61 
62 typedef struct scf_state {
63 	scf_handle_t *ss_handle;
64 	scf_instance_t *ss_inst;
65 	scf_service_t *ss_svc;
66 	scf_snapshot_t *ss_snap;
67 	scf_propertygroup_t *ss_pg;
68 	scf_property_t *ss_prop;
69 } scf_state_t;
70 
71 static void
72 shut_down_scf(scf_state_t *sstate)
73 {
74 	scf_instance_destroy(sstate->ss_inst);
75 	(void) scf_handle_unbind(sstate->ss_handle);
76 	scf_handle_destroy(sstate->ss_handle);
77 }
78 
79 static char *
80 alloc_fmri(const char *service, const char *instance_name)
81 {
82 	ssize_t max_fmri;
83 	char *fmri;
84 
85 	/* If the limit is unknown, then use an arbitrary value */
86 	if ((max_fmri = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH)) == -1)
87 		max_fmri = 1024;
88 	if ((fmri = malloc(max_fmri)) != NULL) {
89 		(void) snprintf(fmri, max_fmri, "svc:/%s:%s", service,
90 		    instance_name);
91 	}
92 	return (fmri);
93 }
94 
95 /*
96  * Start up SCF and bind the requested instance alone.
97  */
98 static int
99 bind_instance(const char *service, const char *instance_name,
100     scf_state_t *sstate)
101 {
102 	char *fmri = NULL;
103 
104 	(void) memset(sstate, 0, sizeof (*sstate));
105 
106 	if ((sstate->ss_handle = scf_handle_create(SCF_VERSION)) == NULL)
107 		return (-1);
108 
109 	if (scf_handle_bind(sstate->ss_handle) != 0)
110 		goto failure;
111 	sstate->ss_inst = scf_instance_create(sstate->ss_handle);
112 	if (sstate->ss_inst == NULL)
113 		goto failure;
114 
115 	fmri = alloc_fmri(service, instance_name);
116 
117 	if (scf_handle_decode_fmri(sstate->ss_handle, fmri, NULL, NULL,
118 	    sstate->ss_inst, NULL, NULL,
119 	    SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0)
120 		goto failure;
121 	free(fmri);
122 	return (0);
123 
124 failure:
125 	free(fmri);
126 	shut_down_scf(sstate);
127 	return (-1);
128 }
129 
130 /*
131  * Start up SCF and an exact FMRI.  This is used for creating new instances and
132  * enable/disable actions.
133  */
134 static dladm_status_t
135 exact_instance(const char *fmri, scf_state_t *sstate)
136 {
137 	dladm_status_t status;
138 
139 	(void) memset(sstate, 0, sizeof (*sstate));
140 
141 	if ((sstate->ss_handle = scf_handle_create(SCF_VERSION)) == NULL)
142 		return (DLADM_STATUS_NOMEM);
143 
144 	status = DLADM_STATUS_FAILED;
145 	if (scf_handle_bind(sstate->ss_handle) != 0)
146 		goto failure;
147 	sstate->ss_svc = scf_service_create(sstate->ss_handle);
148 	if (sstate->ss_svc == NULL)
149 		goto failure;
150 	if (scf_handle_decode_fmri(sstate->ss_handle, fmri, NULL,
151 	    sstate->ss_svc, NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0) {
152 		if (scf_error() == SCF_ERROR_NOT_FOUND)
153 			status = DLADM_STATUS_OPTMISSING;
154 		goto failure;
155 	}
156 	sstate->ss_inst = scf_instance_create(sstate->ss_handle);
157 	if (sstate->ss_inst == NULL)
158 		goto failure;
159 	return (DLADM_STATUS_OK);
160 
161 failure:
162 	shut_down_scf(sstate);
163 	return (status);
164 }
165 
166 static void
167 drop_composed(scf_state_t *sstate)
168 {
169 	scf_property_destroy(sstate->ss_prop);
170 	scf_pg_destroy(sstate->ss_pg);
171 	scf_snapshot_destroy(sstate->ss_snap);
172 }
173 
174 /*
175  * This function sets up a composed view of the configuration information for
176  * the specified instance.  When this is done, the get_property() function
177  * should be able to return individual parameters.
178  */
179 static int
180 get_composed_properties(const char *lpg, boolean_t snap, scf_state_t *sstate)
181 {
182 	sstate->ss_snap = NULL;
183 	sstate->ss_pg = NULL;
184 	sstate->ss_prop = NULL;
185 
186 	if (snap) {
187 		sstate->ss_snap = scf_snapshot_create(sstate->ss_handle);
188 		if (sstate->ss_snap == NULL)
189 			goto failure;
190 		if (scf_instance_get_snapshot(sstate->ss_inst, "running",
191 		    sstate->ss_snap) != 0)
192 			goto failure;
193 	}
194 	if ((sstate->ss_pg = scf_pg_create(sstate->ss_handle)) == NULL)
195 		goto failure;
196 	if (scf_instance_get_pg_composed(sstate->ss_inst, sstate->ss_snap, lpg,
197 	    sstate->ss_pg) != 0)
198 		goto failure;
199 	if ((sstate->ss_prop = scf_property_create(sstate->ss_handle)) ==
200 	    NULL)
201 		goto failure;
202 	return (0);
203 
204 failure:
205 	drop_composed(sstate);
206 	return (-1);
207 }
208 
209 static int
210 get_count(const char *lprop, scf_state_t *sstate, uint64_t *answer)
211 {
212 	scf_value_t *val;
213 	int retv;
214 
215 	if (scf_pg_get_property(sstate->ss_pg, lprop, sstate->ss_prop) != 0)
216 		return (-1);
217 	if ((val = scf_value_create(sstate->ss_handle)) == NULL)
218 		return (-1);
219 
220 	if (scf_property_get_value(sstate->ss_prop, val) == 0 &&
221 	    scf_value_get_count(val, answer) == 0)
222 		retv = 0;
223 	else
224 		retv = -1;
225 	scf_value_destroy(val);
226 	return (retv);
227 }
228 
229 static int
230 get_boolean(const char *lprop, scf_state_t *sstate, boolean_t *answer)
231 {
232 	scf_value_t *val;
233 	int retv;
234 	uint8_t bval;
235 
236 	if (scf_pg_get_property(sstate->ss_pg, lprop, sstate->ss_prop) != 0)
237 		return (-1);
238 	if ((val = scf_value_create(sstate->ss_handle)) == NULL)
239 		return (-1);
240 
241 	if (scf_property_get_value(sstate->ss_prop, val) == 0 &&
242 	    scf_value_get_boolean(val, &bval) == 0) {
243 		retv = 0;
244 		*answer = bval != 0;
245 	} else {
246 		retv = -1;
247 	}
248 	scf_value_destroy(val);
249 	return (retv);
250 }
251 
252 static dladm_status_t
253 bridge_door_call(const char *instname, bridge_door_type_t dtype,
254     datalink_id_t linkid, void **bufp, size_t inlen, size_t *buflenp,
255     boolean_t is_list)
256 {
257 	char doorname[MAXPATHLEN];
258 	int did, retv, etmp;
259 	bridge_door_cmd_t *bdc;
260 	door_arg_t arg;
261 
262 	(void) snprintf(doorname, sizeof (doorname), "%s/%s", DOOR_DIRNAME,
263 	    instname);
264 
265 	/* Knock on the door */
266 	did = open(doorname, O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
267 	if (did == -1)
268 		return (dladm_errno2status(errno));
269 
270 	if ((bdc = malloc(sizeof (*bdc) + inlen)) == NULL) {
271 		(void) close(did);
272 		return (DLADM_STATUS_NOMEM);
273 	}
274 	bdc->bdc_type = dtype;
275 	bdc->bdc_linkid = linkid;
276 	if (inlen != 0)
277 		(void) memcpy(bdc + 1, *bufp, inlen);
278 
279 	(void) memset(&arg, 0, sizeof (arg));
280 	arg.data_ptr = (char *)bdc;
281 	arg.data_size = sizeof (*bdc) + inlen;
282 	arg.rbuf = *bufp;
283 	arg.rsize = *buflenp;
284 
285 	/* The door_call function doesn't restart, so take care of that */
286 	do {
287 		errno = 0;
288 		if ((retv = door_call(did, &arg)) == 0)
289 			break;
290 	} while (errno == EINTR);
291 
292 	/* If we get an unexpected response, then return an error */
293 	if (retv == 0) {
294 		/* The daemon returns a single int for errors */
295 		/* LINTED: pointer alignment */
296 		if (arg.data_size == sizeof (int) && *(int *)arg.rbuf != 0) {
297 			retv = -1;
298 			/* LINTED: pointer alignment */
299 			errno = *(int *)arg.rbuf;
300 		}
301 		/* Terminated daemon returns with zero data */
302 		if (arg.data_size == 0) {
303 			retv = -1;
304 			errno = EBADF;
305 		}
306 	}
307 
308 	if (retv == 0) {
309 		if (arg.rbuf != *bufp) {
310 			if (is_list) {
311 				void *newp;
312 
313 				newp = realloc(*bufp, arg.data_size);
314 				if (newp == NULL) {
315 					retv = -1;
316 				} else {
317 					*bufp = newp;
318 					(void) memcpy(*bufp, arg.rbuf,
319 					    arg.data_size);
320 				}
321 			}
322 			(void) munmap(arg.rbuf, arg.rsize);
323 		}
324 		if (is_list) {
325 			*buflenp = arg.data_size;
326 		} else if (arg.data_size != *buflenp || arg.rbuf != *bufp) {
327 			errno = EINVAL;
328 			retv = -1;
329 		}
330 	}
331 
332 	etmp = errno;
333 	(void) close(did);
334 
335 	/* Revoked door is the same as no door at all */
336 	if (etmp == EBADF)
337 		etmp = ENOENT;
338 
339 	return (retv == 0 ? DLADM_STATUS_OK : dladm_errno2status(etmp));
340 }
341 
342 /*
343  * Wrapper function for making per-port calls.
344  */
345 static dladm_status_t
346 port_door_call(dladm_handle_t handle, datalink_id_t linkid,
347     bridge_door_type_t dtype, void *buf, size_t inlen, size_t buflen)
348 {
349 	char bridge[MAXLINKNAMELEN];
350 	dladm_status_t status;
351 
352 	status = dladm_bridge_getlink(handle, linkid, bridge, sizeof (bridge));
353 	if (status != DLADM_STATUS_OK)
354 		return (status);
355 	return (bridge_door_call(bridge, dtype, linkid, &buf, inlen, &buflen,
356 	    B_FALSE));
357 }
358 
359 static dladm_status_t
360 bridge_refresh(const char *bridge)
361 {
362 	dladm_status_t status;
363 	int twoints[2];
364 	void *bdptr;
365 	size_t buflen;
366 	char *fmri;
367 	int refresh_count;
368 
369 	buflen = sizeof (twoints);
370 	bdptr = twoints;
371 	status = bridge_door_call(bridge, bdcBridgeGetRefreshCount,
372 	    DATALINK_INVALID_LINKID, &bdptr, 0, &buflen, B_FALSE);
373 	if (status == DLADM_STATUS_NOTFOUND)
374 		return (DLADM_STATUS_OK);
375 	if (status != DLADM_STATUS_OK)
376 		return (status);
377 	refresh_count = twoints[0];
378 	if ((fmri = alloc_fmri(BRIDGE_SVC_NAME, bridge)) == NULL)
379 		return (DLADM_STATUS_NOMEM);
380 	status = smf_refresh_instance(fmri) == 0 ?
381 	    DLADM_STATUS_OK : DLADM_STATUS_FAILED;
382 	free(fmri);
383 	if (status == DLADM_STATUS_OK) {
384 		int i = 0;
385 
386 		/*
387 		 * SMF doesn't give any synchronous behavior or dependency
388 		 * ordering for refresh operations, so we have to invent our
389 		 * own mechanism here.  Get the refresh counter from the
390 		 * daemon, and wait for it to change.  It's not pretty, but
391 		 * it's sufficient.
392 		 */
393 		while (++i <= 10) {
394 			buflen = sizeof (twoints);
395 			bdptr = twoints;
396 			status = bridge_door_call(bridge,
397 			    bdcBridgeGetRefreshCount, DATALINK_INVALID_LINKID,
398 			    &bdptr, 0, &buflen, B_FALSE);
399 			if (status != DLADM_STATUS_OK)
400 				break;
401 			if (twoints[0] != refresh_count)
402 				break;
403 			(void) usleep(100000);
404 		}
405 		fmri = alloc_fmri(TRILL_SVC_NAME, bridge);
406 		if (fmri == NULL)
407 			return (DLADM_STATUS_NOMEM);
408 		status = smf_refresh_instance(fmri) == 0 ||
409 		    scf_error() == SCF_ERROR_NOT_FOUND ?
410 		    DLADM_STATUS_OK : DLADM_STATUS_FAILED;
411 		free(fmri);
412 	}
413 	return (status);
414 }
415 
416 /*
417  * Look up bridge property values from SCF and return them.
418  */
419 dladm_status_t
420 dladm_bridge_get_properties(const char *instance_name, UID_STP_CFG_T *cfg,
421     dladm_bridge_prot_t *brprotp)
422 {
423 	scf_state_t sstate;
424 	uint64_t value;
425 	boolean_t trill_enabled;
426 
427 	cfg->field_mask = 0;
428 	cfg->bridge_priority = DEF_BR_PRIO;
429 	cfg->max_age = DEF_BR_MAXAGE;
430 	cfg->hello_time = DEF_BR_HELLOT;
431 	cfg->forward_delay = DEF_BR_FWDELAY;
432 	cfg->force_version = DEF_FORCE_VERS;
433 
434 	(void) strlcpy(cfg->vlan_name, instance_name, sizeof (cfg->vlan_name));
435 
436 	*brprotp = DLADM_BRIDGE_PROT_STP;
437 
438 	/* It's ok for this to be missing; it's installed separately */
439 	if (bind_instance(TRILL_SVC_NAME, instance_name, &sstate) == 0) {
440 		trill_enabled = B_FALSE;
441 		if (get_composed_properties(SCF_PG_GENERAL, B_FALSE, &sstate) ==
442 		    0) {
443 			(void) get_boolean(SCF_PROPERTY_ENABLED, &sstate,
444 			    &trill_enabled);
445 			if (trill_enabled)
446 				*brprotp = DLADM_BRIDGE_PROT_TRILL;
447 			drop_composed(&sstate);
448 		}
449 		if (get_composed_properties(SCF_PG_GENERAL_OVR, B_FALSE,
450 		    &sstate) == 0) {
451 			(void) get_boolean(SCF_PROPERTY_ENABLED, &sstate,
452 			    &trill_enabled);
453 			if (trill_enabled)
454 				*brprotp = DLADM_BRIDGE_PROT_TRILL;
455 			drop_composed(&sstate);
456 		}
457 		shut_down_scf(&sstate);
458 	}
459 
460 	cfg->stp_enabled = (*brprotp == DLADM_BRIDGE_PROT_STP) ?
461 	    STP_ENABLED : STP_DISABLED;
462 	cfg->field_mask |= BR_CFG_STATE;
463 
464 	if (bind_instance(BRIDGE_SVC_NAME, instance_name, &sstate) != 0)
465 		return (DLADM_STATUS_REPOSITORYINVAL);
466 
467 	if (get_composed_properties("config", B_TRUE, &sstate) != 0) {
468 		shut_down_scf(&sstate);
469 		return (DLADM_STATUS_REPOSITORYINVAL);
470 	}
471 
472 	if (get_count("priority", &sstate, &value) == 0) {
473 		cfg->bridge_priority = value;
474 		cfg->field_mask |= BR_CFG_PRIO;
475 	}
476 	if (get_count("max-age", &sstate, &value) == 0) {
477 		cfg->max_age = value / IEEE_TIMER_SCALE;
478 		cfg->field_mask |= BR_CFG_AGE;
479 	}
480 	if (get_count("hello-time", &sstate, &value) == 0) {
481 		cfg->hello_time = value / IEEE_TIMER_SCALE;
482 		cfg->field_mask |= BR_CFG_HELLO;
483 	}
484 	if (get_count("forward-delay", &sstate, &value) == 0) {
485 		cfg->forward_delay = value / IEEE_TIMER_SCALE;
486 		cfg->field_mask |= BR_CFG_DELAY;
487 	}
488 	if (get_count("force-protocol", &sstate, &value) == 0) {
489 		cfg->force_version = value;
490 		cfg->field_mask |= BR_CFG_FORCE_VER;
491 	}
492 
493 	drop_composed(&sstate);
494 	shut_down_scf(&sstate);
495 	return (DLADM_STATUS_OK);
496 }
497 
498 /*
499  * Retrieve special non-settable and undocumented parameters.
500  */
501 dladm_status_t
502 dladm_bridge_get_privprop(const char *instance_name, boolean_t *debugp,
503     uint32_t *tablemaxp)
504 {
505 	scf_state_t sstate;
506 	uint64_t value;
507 
508 	*debugp = B_FALSE;
509 	*tablemaxp = 10000;
510 
511 	if (bind_instance(BRIDGE_SVC_NAME, instance_name, &sstate) != 0)
512 		return (DLADM_STATUS_REPOSITORYINVAL);
513 
514 	if (get_composed_properties("config", B_TRUE, &sstate) != 0) {
515 		shut_down_scf(&sstate);
516 		return (DLADM_STATUS_REPOSITORYINVAL);
517 	}
518 
519 	(void) get_boolean("debug", &sstate, debugp);
520 	if (get_count("table-maximum", &sstate, &value) == 0)
521 		*tablemaxp = (uint32_t)value;
522 
523 	drop_composed(&sstate);
524 	shut_down_scf(&sstate);
525 	return (DLADM_STATUS_OK);
526 }
527 
528 static boolean_t
529 set_count_property(scf_handle_t *handle, scf_transaction_t *tran,
530     const char *propname, uint64_t propval)
531 {
532 	scf_transaction_entry_t *entry;
533 	scf_value_t *value = NULL;
534 
535 	if ((entry = scf_entry_create(handle)) == NULL)
536 		return (B_FALSE);
537 
538 	if ((value = scf_value_create(handle)) == NULL)
539 		goto out;
540 	if (scf_transaction_property_new(tran, entry, propname,
541 	    SCF_TYPE_COUNT) != 0 &&
542 	    scf_transaction_property_change(tran, entry, propname,
543 	    SCF_TYPE_COUNT) != 0)
544 		goto out;
545 	scf_value_set_count(value, propval);
546 	if (scf_entry_add_value(entry, value) == 0)
547 		return (B_TRUE);
548 
549 out:
550 	if (value != NULL)
551 		scf_value_destroy(value);
552 
553 	scf_entry_destroy_children(entry);
554 	scf_entry_destroy(entry);
555 
556 	return (B_FALSE);
557 }
558 
559 static boolean_t
560 set_string_property(scf_handle_t *handle, scf_transaction_t *tran,
561     const char *propname, const char *propval)
562 {
563 	scf_transaction_entry_t *entry;
564 	scf_value_t *value = NULL;
565 
566 	if ((entry = scf_entry_create(handle)) == NULL)
567 		return (B_FALSE);
568 
569 	if ((value = scf_value_create(handle)) == NULL)
570 		goto out;
571 	if (scf_transaction_property_new(tran, entry, propname,
572 	    SCF_TYPE_ASTRING) != 0 &&
573 	    scf_transaction_property_change(tran, entry, propname,
574 	    SCF_TYPE_ASTRING) != 0)
575 		goto out;
576 	if (scf_value_set_astring(value, propval) != 0)
577 		goto out;
578 	if (scf_entry_add_value(entry, value) == 0)
579 		return (B_TRUE);
580 
581 out:
582 	if (value != NULL)
583 		scf_value_destroy(value);
584 
585 	scf_entry_destroy_children(entry);
586 	scf_entry_destroy(entry);
587 
588 	return (B_FALSE);
589 }
590 
591 static boolean_t
592 set_fmri_property(scf_handle_t *handle, scf_transaction_t *tran,
593     const char *propname, const char *propval)
594 {
595 	scf_transaction_entry_t *entry;
596 	scf_value_t *value = NULL;
597 
598 	if ((entry = scf_entry_create(handle)) == NULL)
599 		return (B_FALSE);
600 
601 	if ((value = scf_value_create(handle)) == NULL)
602 		goto out;
603 	if (scf_transaction_property_new(tran, entry, propname,
604 	    SCF_TYPE_FMRI) != 0 &&
605 	    scf_transaction_property_change(tran, entry, propname,
606 	    SCF_TYPE_FMRI) != 0)
607 		goto out;
608 	if (scf_value_set_from_string(value, SCF_TYPE_FMRI, propval) != 0)
609 		goto out;
610 	if (scf_entry_add_value(entry, value) == 0)
611 		return (B_TRUE);
612 
613 out:
614 	if (value != NULL)
615 		scf_value_destroy(value);
616 
617 	scf_entry_destroy_children(entry);
618 	scf_entry_destroy(entry);
619 
620 	return (B_FALSE);
621 }
622 
623 static dladm_status_t
624 dladm_bridge_persist_conf(dladm_handle_t handle, const char *link,
625     datalink_id_t linkid)
626 {
627 	dladm_conf_t conf = DLADM_INVALID_CONF;
628 	dladm_status_t status;
629 
630 	status = dladm_create_conf(handle, link, linkid, DATALINK_CLASS_BRIDGE,
631 	    DL_ETHER, &conf);
632 	if (status == DLADM_STATUS_OK) {
633 		/*
634 		 * Create the datalink entry for the bridge.  Note that all of
635 		 * the real configuration information is in SMF.
636 		 */
637 		status = dladm_write_conf(handle, conf);
638 		dladm_destroy_conf(handle, conf);
639 	}
640 	return (status);
641 }
642 
643 /* Convert bridge protection option string to dladm_bridge_prot_t */
644 dladm_status_t
645 dladm_bridge_str2prot(const char *str, dladm_bridge_prot_t *brprotp)
646 {
647 	if (strcmp(str, "stp") == 0)
648 		*brprotp = DLADM_BRIDGE_PROT_STP;
649 	else if (strcmp(str, "trill") == 0)
650 		*brprotp = DLADM_BRIDGE_PROT_TRILL;
651 	else
652 		return (DLADM_STATUS_BADARG);
653 	return (DLADM_STATUS_OK);
654 }
655 
656 /* Convert bridge protection option from dladm_bridge_prot_t to string */
657 const char *
658 dladm_bridge_prot2str(dladm_bridge_prot_t brprot)
659 {
660 	switch (brprot) {
661 	case DLADM_BRIDGE_PROT_STP:
662 		return ("stp");
663 	case DLADM_BRIDGE_PROT_TRILL:
664 		return ("trill");
665 	default:
666 		return ("unknown");
667 	}
668 }
669 
670 static dladm_status_t
671 enable_instance(const char *service_name, const char *instance)
672 {
673 	dladm_status_t status;
674 	char *fmri = alloc_fmri(service_name, instance);
675 
676 	if (fmri == NULL)
677 		return (DLADM_STATUS_NOMEM);
678 	status = smf_enable_instance(fmri, 0) == 0 ?
679 	    DLADM_STATUS_OK : DLADM_STATUS_FAILED;
680 	free(fmri);
681 	return (status);
682 }
683 
684 /*
685  * Shut down a possibly-running service instance.  If this is a permanent
686  * change, then delete it from the system.
687  */
688 static dladm_status_t
689 shut_down_instance(const char *service_name, const char *instance,
690     uint32_t flags)
691 {
692 	dladm_status_t status;
693 	char *fmri = alloc_fmri(service_name, instance);
694 	char *state;
695 	scf_state_t sstate;
696 
697 	if (fmri == NULL)
698 		return (DLADM_STATUS_NOMEM);
699 
700 	if (smf_disable_instance(fmri,
701 	    flags & DLADM_OPT_PERSIST ? 0 : SMF_TEMPORARY) == 0) {
702 		useconds_t usecs, umax;
703 
704 		/* If we can disable, then wait for it to happen. */
705 		umax = DEFAULT_TIMEOUT;
706 		for (usecs = INIT_WAIT_USECS; umax != 0; umax -= usecs) {
707 			state = smf_get_state(fmri);
708 			if (state != NULL &&
709 			    strcmp(state, SCF_STATE_STRING_DISABLED) == 0)
710 				break;
711 			free(state);
712 			usecs *= 2;
713 			if (usecs > umax)
714 				usecs = umax;
715 			(void) usleep(usecs);
716 		}
717 		if (umax == 0) {
718 			state = smf_get_state(fmri);
719 			if (state != NULL &&
720 			    strcmp(state, SCF_STATE_STRING_DISABLED) == 0)
721 				umax = 1;
722 		}
723 		free(state);
724 		status = umax != 0 ? DLADM_STATUS_OK : DLADM_STATUS_FAILED;
725 	} else if (scf_error() == SCF_ERROR_NOT_FOUND) {
726 		free(fmri);
727 		return (DLADM_STATUS_OK);
728 	} else {
729 		status = DLADM_STATUS_FAILED;
730 	}
731 
732 	free(fmri);
733 	if (status == DLADM_STATUS_OK && (flags & DLADM_OPT_PERSIST) &&
734 	    bind_instance(service_name, instance, &sstate) == 0) {
735 		(void) scf_instance_delete(sstate.ss_inst);
736 		shut_down_scf(&sstate);
737 	}
738 
739 	return (status);
740 }
741 
742 static dladm_status_t
743 disable_trill(const char *instance, uint32_t flags)
744 {
745 	return (shut_down_instance(TRILL_SVC_NAME, instance, flags));
746 }
747 
748 /*
749  * To enable TRILL, we must create a new instance of the TRILL service, then
750  * add proper dependencies to it, and finally mark it as enabled.  The
751  * dependencies will keep it from going on-line until the bridge is running.
752  */
753 static dladm_status_t
754 enable_trill(const char *instance)
755 {
756 	dladm_status_t status = DLADM_STATUS_FAILED;
757 	char *fmri = NULL;
758 	scf_state_t sstate;
759 	scf_transaction_t *tran = NULL;
760 	boolean_t new_instance = B_FALSE;
761 	boolean_t new_pg = B_FALSE;
762 	int rv;
763 
764 	/*
765 	 * This check is here in case the user has installed and then removed
766 	 * the package.  SMF should remove the manifest, but currently does
767 	 * not.
768 	 */
769 	if (access("/usr/sbin/trilld", F_OK) != 0)
770 		return (DLADM_STATUS_OPTMISSING);
771 
772 	if ((status = exact_instance(TRILL_SVC_NAME, &sstate)) !=
773 	    DLADM_STATUS_OK)
774 		goto out;
775 
776 	status = DLADM_STATUS_FAILED;
777 	if (scf_service_get_instance(sstate.ss_svc, instance, sstate.ss_inst) !=
778 	    0) {
779 		if (scf_service_add_instance(sstate.ss_svc, instance,
780 		    sstate.ss_inst) != 0)
781 			goto out;
782 		new_instance = B_TRUE;
783 	}
784 
785 	if ((tran = scf_transaction_create(sstate.ss_handle)) == NULL)
786 		goto out;
787 
788 	if ((sstate.ss_pg = scf_pg_create(sstate.ss_handle)) == NULL)
789 		goto out;
790 
791 	if (scf_instance_get_pg(sstate.ss_inst, "bridging",
792 	    sstate.ss_pg) == 0) {
793 		status = DLADM_STATUS_OK;
794 		goto out;
795 	}
796 
797 	if ((fmri = alloc_fmri(BRIDGE_SVC_NAME, instance)) == NULL)
798 		goto out;
799 
800 	if (scf_instance_add_pg(sstate.ss_inst, "bridging",
801 	    SCF_GROUP_DEPENDENCY, 0, sstate.ss_pg) != 0)
802 		goto out;
803 
804 	new_pg = B_TRUE;
805 	do {
806 		if (scf_transaction_start(tran, sstate.ss_pg) != 0)
807 			goto out;
808 
809 		if (!set_string_property(sstate.ss_handle, tran,
810 		    SCF_PROPERTY_GROUPING, SCF_DEP_REQUIRE_ALL))
811 			goto out;
812 		if (!set_string_property(sstate.ss_handle, tran,
813 		    SCF_PROPERTY_RESTART_ON, SCF_DEP_RESET_ON_RESTART))
814 			goto out;
815 		if (!set_string_property(sstate.ss_handle, tran,
816 		    SCF_PROPERTY_TYPE, "service"))
817 			goto out;
818 		if (!set_fmri_property(sstate.ss_handle, tran,
819 		    SCF_PROPERTY_ENTITIES, fmri))
820 			goto out;
821 
822 		rv = scf_transaction_commit(tran);
823 		scf_transaction_reset(tran);
824 		if (rv == 0 && scf_pg_update(sstate.ss_pg) == -1)
825 			goto out;
826 	} while (rv == 0);
827 	if (rv != 1)
828 		goto out;
829 
830 	status = DLADM_STATUS_OK;
831 
832 out:
833 	free(fmri);
834 	if (tran != NULL) {
835 		scf_transaction_destroy_children(tran);
836 		scf_transaction_destroy(tran);
837 	}
838 
839 	if (status != DLADM_STATUS_OK && new_pg)
840 		(void) scf_pg_delete(sstate.ss_pg);
841 
842 	drop_composed(&sstate);
843 
844 	/*
845 	 * If we created an instance and then failed, then remove the instance
846 	 * from the system.
847 	 */
848 	if (status != DLADM_STATUS_OK && new_instance)
849 		(void) scf_instance_delete(sstate.ss_inst);
850 
851 	shut_down_scf(&sstate);
852 
853 	if (status == DLADM_STATUS_OK)
854 		status = enable_instance(TRILL_SVC_NAME, instance);
855 
856 	return (status);
857 }
858 
859 /*
860  * Create a new bridge or modify an existing one.  Update the SMF configuration
861  * and add links.
862  *
863  * Input timer values are in IEEE scaled (* 256) format.
864  */
865 dladm_status_t
866 dladm_bridge_configure(dladm_handle_t handle, const char *name,
867     const UID_STP_CFG_T *cfg, dladm_bridge_prot_t brprot, uint32_t flags)
868 {
869 	dladm_status_t status;
870 	scf_state_t sstate;
871 	scf_transaction_t *tran = NULL;
872 	boolean_t new_instance = B_FALSE;
873 	boolean_t new_pg = B_FALSE;
874 	datalink_id_t linkid = DATALINK_INVALID_LINKID;
875 	char linkname[MAXLINKNAMELEN];
876 	int rv;
877 
878 	if (!dladm_valid_bridgename(name))
879 		return (DLADM_STATUS_FAILED);
880 
881 	if (flags & DLADM_OPT_CREATE) {
882 		/*
883 		 * This check is here in case the user has installed and then
884 		 * removed the package.  SMF should remove the manifest, but
885 		 * currently does not.
886 		 */
887 		if (access("/usr/lib/bridged", F_OK) != 0)
888 			return (DLADM_STATUS_OPTMISSING);
889 
890 		(void) snprintf(linkname, sizeof (linkname), "%s0", name);
891 		status = dladm_create_datalink_id(handle, linkname,
892 		    DATALINK_CLASS_BRIDGE, DL_ETHER,
893 		    flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST), &linkid);
894 		if (status != DLADM_STATUS_OK)
895 			return (status);
896 
897 		if ((flags & DLADM_OPT_PERSIST) &&
898 		    (status = dladm_bridge_persist_conf(handle, linkname,
899 		    linkid) != DLADM_STATUS_OK))
900 			goto dladm_fail;
901 	}
902 
903 	if (brprot == DLADM_BRIDGE_PROT_TRILL)
904 		status = enable_trill(name);
905 	else
906 		status = disable_trill(name, flags);
907 	if (status != DLADM_STATUS_OK)
908 		goto dladm_fail;
909 
910 	if ((status = exact_instance(BRIDGE_SVC_NAME, &sstate)) !=
911 	    DLADM_STATUS_OK)
912 		goto out;
913 
914 	/* set up for a series of scf calls */
915 	status = DLADM_STATUS_FAILED;
916 
917 	if (scf_service_get_instance(sstate.ss_svc, name, sstate.ss_inst) ==
918 	    0) {
919 		if (flags & DLADM_OPT_CREATE) {
920 			status = DLADM_STATUS_EXIST;
921 			goto out;
922 		}
923 	} else {
924 		if (!(flags & DLADM_OPT_CREATE)) {
925 			status = DLADM_STATUS_NOTFOUND;
926 			goto out;
927 		}
928 		if (scf_service_add_instance(sstate.ss_svc, name,
929 		    sstate.ss_inst) != 0)
930 			goto out;
931 		new_instance = B_TRUE;
932 	}
933 
934 	if ((tran = scf_transaction_create(sstate.ss_handle)) == NULL)
935 		goto out;
936 
937 	if (cfg->field_mask & BR_CFG_ALL) {
938 		if ((sstate.ss_pg = scf_pg_create(sstate.ss_handle)) == NULL)
939 			goto out;
940 		if (scf_instance_add_pg(sstate.ss_inst, "config",
941 		    SCF_GROUP_APPLICATION, 0, sstate.ss_pg) == 0) {
942 			new_pg = B_TRUE;
943 		} else if (scf_instance_get_pg(sstate.ss_inst, "config",
944 		    sstate.ss_pg) != 0) {
945 			goto out;
946 		}
947 		do {
948 			if (scf_transaction_start(tran, sstate.ss_pg) != 0)
949 				goto out;
950 
951 			if ((cfg->field_mask & BR_CFG_PRIO) &&
952 			    !set_count_property(sstate.ss_handle, tran,
953 			    "priority", cfg->bridge_priority))
954 				goto out;
955 			if ((cfg->field_mask & BR_CFG_AGE) &&
956 			    !set_count_property(sstate.ss_handle, tran,
957 			    "max-age", cfg->max_age * IEEE_TIMER_SCALE))
958 				goto out;
959 			if ((cfg->field_mask & BR_CFG_HELLO) &&
960 			    !set_count_property(sstate.ss_handle, tran,
961 			    "hello-time", cfg->hello_time * IEEE_TIMER_SCALE))
962 				goto out;
963 			if ((cfg->field_mask & BR_CFG_DELAY) &&
964 			    !set_count_property(sstate.ss_handle, tran,
965 			    "forward-delay",
966 			    cfg->forward_delay * IEEE_TIMER_SCALE))
967 				goto out;
968 			if ((cfg->field_mask & BR_CFG_FORCE_VER) &&
969 			    !set_count_property(sstate.ss_handle, tran,
970 			    "force-protocol", cfg->force_version))
971 				goto out;
972 
973 			rv = scf_transaction_commit(tran);
974 			scf_transaction_reset(tran);
975 			if (rv == 0 && scf_pg_update(sstate.ss_pg) == -1)
976 				goto out;
977 		} while (rv == 0);
978 		if (rv != 1)
979 			goto out;
980 	}
981 
982 	/*
983 	 * If we're modifying an existing and running bridge, then tell the
984 	 * daemon to update the requested values.
985 	 */
986 	if ((flags & DLADM_OPT_ACTIVE) && !(flags & DLADM_OPT_CREATE))
987 		status = bridge_refresh(name);
988 	else
989 		status = DLADM_STATUS_OK;
990 
991 out:
992 	if (tran != NULL) {
993 		scf_transaction_destroy_children(tran);
994 		scf_transaction_destroy(tran);
995 	}
996 
997 	if (status != DLADM_STATUS_OK && new_pg)
998 		(void) scf_pg_delete(sstate.ss_pg);
999 
1000 	drop_composed(&sstate);
1001 
1002 	/*
1003 	 * If we created an instance and then failed, then remove the instance
1004 	 * from the system.
1005 	 */
1006 	if (status != DLADM_STATUS_OK && new_instance)
1007 		(void) scf_instance_delete(sstate.ss_inst);
1008 
1009 	shut_down_scf(&sstate);
1010 
1011 	/*
1012 	 * Remove the bridge linkid if we've allocated one in this function but
1013 	 * we've failed to set up the SMF properties.
1014 	 */
1015 dladm_fail:
1016 	if (status != DLADM_STATUS_OK && linkid != DATALINK_INVALID_LINKID) {
1017 		(void) dladm_remove_conf(handle, linkid);
1018 		(void) dladm_destroy_datalink_id(handle, linkid, flags);
1019 	}
1020 
1021 	return (status);
1022 }
1023 
1024 /*
1025  * Enable a newly-created bridge in SMF by creating "general/enabled" and
1026  * deleting any "general_ovr/enabled" (used for temporary services).
1027  */
1028 dladm_status_t
1029 dladm_bridge_enable(const char *name)
1030 {
1031 	return (enable_instance(BRIDGE_SVC_NAME, name));
1032 }
1033 
1034 /*
1035  * Set a link as a member of a bridge, or remove bridge membership.  If the
1036  * DLADM_OPT_CREATE flag is set, then we assume that the daemon isn't running.
1037  * In all other cases, we must tell the daemon to add or delete the link in
1038  * order to stay in sync.
1039  */
1040 dladm_status_t
1041 dladm_bridge_setlink(dladm_handle_t handle, datalink_id_t linkid,
1042     const char *bridge)
1043 {
1044 	dladm_status_t status;
1045 	dladm_conf_t conf;
1046 	char oldbridge[MAXLINKNAMELEN];
1047 	boolean_t has_oldbridge;
1048 	boolean_t changed = B_FALSE;
1049 
1050 	if (*bridge != '\0' && !dladm_valid_bridgename(bridge))
1051 		return (DLADM_STATUS_FAILED);
1052 
1053 	if ((status = dladm_read_conf(handle, linkid, &conf)) !=
1054 	    DLADM_STATUS_OK)
1055 		return (status);
1056 
1057 	has_oldbridge = B_FALSE;
1058 	status = dladm_get_conf_field(handle, conf, FBRIDGE, oldbridge,
1059 	    sizeof (oldbridge));
1060 	if (status == DLADM_STATUS_OK) {
1061 		/*
1062 		 * Don't allow a link to be reassigned directly from one bridge
1063 		 * to another.  It must be removed first.
1064 		 */
1065 		if (*oldbridge != '\0' && *bridge != '\0') {
1066 			status = DLADM_STATUS_EXIST;
1067 			goto out;
1068 		}
1069 		has_oldbridge = B_TRUE;
1070 	} else if (status != DLADM_STATUS_NOTFOUND) {
1071 		goto out;
1072 	}
1073 
1074 	if (*bridge != '\0') {
1075 		status = dladm_set_conf_field(handle, conf, FBRIDGE,
1076 		    DLADM_TYPE_STR, bridge);
1077 		changed = B_TRUE;
1078 	} else if (has_oldbridge) {
1079 		status = dladm_unset_conf_field(handle, conf, FBRIDGE);
1080 		changed = B_TRUE;
1081 	} else {
1082 		status = DLADM_STATUS_OK;
1083 		goto out;
1084 	}
1085 	if (status == DLADM_STATUS_OK)
1086 		status = dladm_write_conf(handle, conf);
1087 
1088 out:
1089 	dladm_destroy_conf(handle, conf);
1090 	if (changed && status == DLADM_STATUS_OK) {
1091 		if (bridge[0] == '\0')
1092 			bridge = oldbridge;
1093 		status = bridge_refresh(bridge);
1094 	}
1095 	return (status);
1096 }
1097 
1098 /*
1099  * Get the name of the bridge of which the given linkid is a member.
1100  */
1101 dladm_status_t
1102 dladm_bridge_getlink(dladm_handle_t handle, datalink_id_t linkid, char *bridge,
1103     size_t bridgelen)
1104 {
1105 	dladm_status_t status;
1106 	dladm_conf_t conf;
1107 
1108 	if ((status = dladm_read_conf(handle, linkid, &conf)) !=
1109 	    DLADM_STATUS_OK)
1110 		return (status);
1111 
1112 	*bridge = '\0';
1113 	status = dladm_get_conf_field(handle, conf, FBRIDGE, bridge, bridgelen);
1114 	if (status == DLADM_STATUS_OK && *bridge == '\0')
1115 		status = DLADM_STATUS_NOTFOUND;
1116 
1117 	dladm_destroy_conf(handle, conf);
1118 	return (status);
1119 }
1120 
1121 dladm_status_t
1122 dladm_bridge_refresh(dladm_handle_t handle, datalink_id_t linkid)
1123 {
1124 	char bridge[MAXLINKNAMELEN];
1125 	dladm_status_t status;
1126 
1127 	status = dladm_bridge_getlink(handle, linkid, bridge, sizeof (bridge));
1128 	if (status == DLADM_STATUS_NOTFOUND)
1129 		return (DLADM_STATUS_OK);
1130 	if (status == DLADM_STATUS_OK)
1131 		status = bridge_refresh(bridge);
1132 	return (status);
1133 }
1134 
1135 typedef struct bridge_held_arg_s {
1136 	const char	*bha_bridge;
1137 	boolean_t	bha_isheld;
1138 } bridge_held_arg_t;
1139 
1140 static int
1141 i_dladm_bridge_is_held(dladm_handle_t handle, datalink_id_t linkid, void *arg)
1142 {
1143 	dladm_status_t status = DLADM_STATUS_FAILED;
1144 	dladm_conf_t conf;
1145 	char bridge[MAXLINKNAMELEN];
1146 	bridge_held_arg_t	*bha = arg;
1147 
1148 	if ((status = dladm_read_conf(handle, linkid, &conf)) !=
1149 	    DLADM_STATUS_OK)
1150 		return (DLADM_WALK_CONTINUE);
1151 	status = dladm_get_conf_field(handle, conf, FBRIDGE, bridge,
1152 	    sizeof (bridge));
1153 	if (status == DLADM_STATUS_OK && strcmp(bha->bha_bridge, bridge) == 0) {
1154 		bha->bha_isheld = B_TRUE;
1155 		dladm_destroy_conf(handle, conf);
1156 		return (DLADM_WALK_TERMINATE);
1157 	} else {
1158 		dladm_destroy_conf(handle, conf);
1159 		return (DLADM_WALK_CONTINUE);
1160 	}
1161 }
1162 
1163 /*
1164  * Delete a previously created bridge.
1165  */
1166 dladm_status_t
1167 dladm_bridge_delete(dladm_handle_t handle, const char *bridge, uint32_t flags)
1168 {
1169 	datalink_id_t linkid;
1170 	datalink_class_t class;
1171 	dladm_status_t status;
1172 	char linkname[MAXLINKNAMELEN];
1173 
1174 	if (!dladm_valid_bridgename(bridge))
1175 		return (DLADM_STATUS_LINKINVAL);
1176 
1177 	/* Get the datalink ID for this bridge */
1178 	(void) snprintf(linkname, sizeof (linkname), "%s0", bridge);
1179 	if (dladm_name2info(handle, linkname, &linkid, NULL, NULL, NULL) !=
1180 	    DLADM_STATUS_OK)
1181 		linkid = DATALINK_INVALID_LINKID;
1182 	else if (dladm_datalink_id2info(handle, linkid, NULL, &class, NULL,
1183 	    NULL, 0) != DLADM_STATUS_OK)
1184 		linkid = DATALINK_INVALID_LINKID;
1185 	else if (class != DATALINK_CLASS_BRIDGE)
1186 		return (DLADM_STATUS_BADARG);
1187 
1188 	if ((flags & DLADM_OPT_ACTIVE) && linkid == DATALINK_INVALID_LINKID)
1189 		return (DLADM_STATUS_BADARG);
1190 
1191 	if (flags & DLADM_OPT_PERSIST) {
1192 		bridge_held_arg_t arg;
1193 
1194 		arg.bha_bridge = bridge;
1195 		arg.bha_isheld = B_FALSE;
1196 
1197 		/*
1198 		 * See whether there are any persistent links using this
1199 		 * bridge.  If so, we fail the operation.
1200 		 */
1201 		(void) dladm_walk_datalink_id(i_dladm_bridge_is_held, handle,
1202 		    &arg, DATALINK_CLASS_PHYS | DATALINK_CLASS_AGGR |
1203 		    DATALINK_CLASS_ETHERSTUB | DATALINK_CLASS_SIMNET,
1204 		    DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
1205 		if (arg.bha_isheld)
1206 			return (DLADM_STATUS_LINKBUSY);
1207 	}
1208 
1209 	if ((status = disable_trill(bridge, flags)) != DLADM_STATUS_OK)
1210 		goto out;
1211 
1212 	/* Disable or remove the SMF instance */
1213 	status = shut_down_instance(BRIDGE_SVC_NAME, bridge, flags);
1214 	if (status != DLADM_STATUS_OK)
1215 		goto out;
1216 
1217 	if (flags & DLADM_OPT_ACTIVE) {
1218 		/*
1219 		 * Delete ACTIVE linkprop now that daemon is gone.
1220 		 */
1221 		(void) dladm_set_linkprop(handle, linkid, NULL, NULL, 0,
1222 		    DLADM_OPT_ACTIVE);
1223 		(void) dladm_destroy_datalink_id(handle, linkid,
1224 		    DLADM_OPT_ACTIVE);
1225 	}
1226 
1227 	if (flags & DLADM_OPT_PERSIST) {
1228 		(void) dladm_remove_conf(handle, linkid);
1229 		(void) dladm_destroy_datalink_id(handle, linkid,
1230 		    DLADM_OPT_PERSIST);
1231 	}
1232 
1233 out:
1234 
1235 	return (status);
1236 }
1237 
1238 /* Check if given name is valid for bridges */
1239 boolean_t
1240 dladm_valid_bridgename(const char *bridge)
1241 {
1242 	size_t		len = strnlen(bridge, MAXLINKNAMELEN);
1243 	const char	*cp;
1244 
1245 	if (len == MAXLINKNAMELEN)
1246 		return (B_FALSE);
1247 
1248 	/*
1249 	 * The bridge name cannot start or end with a digit.
1250 	 */
1251 	if (isdigit(bridge[0]) || isdigit(bridge[len - 1]))
1252 		return (B_FALSE);
1253 
1254 	/*
1255 	 * The legal characters within a bridge name are:
1256 	 * alphanumeric (a-z,  A-Z,  0-9), and the underscore ('_').
1257 	 */
1258 	for (cp = bridge; *cp != '\0'; cp++) {
1259 		if (!isalnum(*cp) && *cp != '_')
1260 			return (B_FALSE);
1261 	}
1262 
1263 	return (B_TRUE);
1264 }
1265 
1266 /*
1267  * Convert a bridge-related observability node name back into the name of the
1268  * bridge.  Returns B_FALSE without making changes if the input name is not in
1269  * a legal format.
1270  */
1271 boolean_t
1272 dladm_observe_to_bridge(char *link)
1273 {
1274 	int llen;
1275 
1276 	llen = strnlen(link, MAXLINKNAMELEN);
1277 	if (llen < 2 || link[llen - 1] != '0' || isdigit(link[llen - 2]))
1278 		return (B_FALSE);
1279 	link[llen - 1] = '\0';
1280 	return (B_TRUE);
1281 }
1282 
1283 /*
1284  * Get bridge property values from the running daemon and return them in a
1285  * common structure.
1286  */
1287 dladm_status_t
1288 dladm_bridge_run_properties(const char *instname, UID_STP_CFG_T *smcfg,
1289     dladm_bridge_prot_t *brprotp)
1290 {
1291 	dladm_status_t status;
1292 	bridge_door_cfg_t bdcf;
1293 	bridge_door_cfg_t *bdcfp = &bdcf;
1294 	size_t buflen = sizeof (bdcf);
1295 
1296 	status = bridge_door_call(instname, bdcBridgeGetConfig,
1297 	    DATALINK_INVALID_LINKID, (void **)&bdcfp, 0, &buflen, B_FALSE);
1298 	if (status == DLADM_STATUS_OK) {
1299 		*smcfg = bdcfp->bdcf_cfg;
1300 		*brprotp = bdcfp->bdcf_prot;
1301 	} else {
1302 		smcfg->field_mask = 0;
1303 		*brprotp = DLADM_BRIDGE_PROT_STP;
1304 	}
1305 	return (status);
1306 }
1307 
1308 /*
1309  * Get bridge state from the running daemon and return in structure borrowed
1310  * from librstp.
1311  */
1312 dladm_status_t
1313 dladm_bridge_state(const char *instname, UID_STP_STATE_T *statep)
1314 {
1315 	size_t buflen = sizeof (*statep);
1316 
1317 	return (bridge_door_call(instname, bdcBridgeGetState,
1318 	    DATALINK_INVALID_LINKID, (void **)&statep, 0, &buflen, B_FALSE));
1319 }
1320 
1321 /* Returns list of ports (datalink_id_t values) assigned to a bridge instance */
1322 datalink_id_t *
1323 dladm_bridge_get_portlist(const char *instname, uint_t *nports)
1324 {
1325 	size_t buflen = sizeof (int) + MAXPORTS * sizeof (datalink_id_t);
1326 	int *rbuf;
1327 
1328 	if ((rbuf = malloc(buflen)) == NULL)
1329 		return (NULL);
1330 	if (bridge_door_call(instname, bdcBridgeGetPorts,
1331 	    DATALINK_INVALID_LINKID, (void **)&rbuf, 0, &buflen, B_TRUE) !=
1332 	    DLADM_STATUS_OK) {
1333 		free(rbuf);
1334 		return (NULL);
1335 	} else {
1336 		/*
1337 		 * Returns an array of datalink_id_t values for all the ports
1338 		 * part of the bridge instance. First entry in the array is the
1339 		 * number of ports.
1340 		 */
1341 		*nports = *rbuf;
1342 		return ((datalink_id_t *)(rbuf + 1));
1343 	}
1344 }
1345 
1346 void
1347 dladm_bridge_free_portlist(datalink_id_t *dlp)
1348 {
1349 	free((int *)dlp - 1);
1350 }
1351 
1352 /* Retrieve Bridge port configuration values */
1353 dladm_status_t
1354 dladm_bridge_get_port_cfg(dladm_handle_t handle, datalink_id_t linkid,
1355     int field, int *valuep)
1356 {
1357 	UID_STP_PORT_CFG_T portcfg;
1358 	dladm_status_t status;
1359 
1360 	status = port_door_call(handle, linkid, bdcPortGetConfig, &portcfg,
1361 	    0, sizeof (portcfg));
1362 	if (status != DLADM_STATUS_OK)
1363 		return (status);
1364 
1365 	switch (field) {
1366 	case PT_CFG_COST:
1367 		*valuep = portcfg.admin_port_path_cost;
1368 		break;
1369 	case PT_CFG_PRIO:
1370 		*valuep = portcfg.port_priority;
1371 		break;
1372 	case PT_CFG_P2P:
1373 		*valuep = portcfg.admin_point2point;
1374 		break;
1375 	case PT_CFG_EDGE:
1376 		*valuep = portcfg.admin_edge;
1377 		break;
1378 	case PT_CFG_NON_STP:
1379 		*valuep = !portcfg.admin_non_stp;
1380 		break;
1381 	case PT_CFG_MCHECK:
1382 		*valuep = (portcfg.field_mask & PT_CFG_MCHECK) ? 1 : 0;
1383 		break;
1384 	}
1385 	return (status);
1386 }
1387 
1388 /* Retreive Bridge port status (disabled, bad SDU etc.) */
1389 dladm_status_t
1390 dladm_bridge_link_state(dladm_handle_t handle, datalink_id_t linkid,
1391     UID_STP_PORT_STATE_T *spsp)
1392 {
1393 	return (port_door_call(handle, linkid, bdcPortGetState, spsp, 0,
1394 	    sizeof (*spsp)));
1395 }
1396 
1397 /* Retrieve Bridge forwarding status of the given link */
1398 dladm_status_t
1399 dladm_bridge_get_forwarding(dladm_handle_t handle, datalink_id_t linkid,
1400     uint_t *valuep)
1401 {
1402 	int twoints[2];
1403 	dladm_status_t status;
1404 
1405 	status = port_door_call(handle, linkid, bdcPortGetForwarding, twoints,
1406 	    0, sizeof (twoints));
1407 	if (status == DLADM_STATUS_OK)
1408 		*valuep = twoints[0];
1409 	return (status);
1410 }
1411 
1412 /* Retrieve Bridge forwarding table entries */
1413 bridge_listfwd_t *
1414 dladm_bridge_get_fwdtable(dladm_handle_t handle, const char *bridge,
1415     uint_t *nfwd)
1416 {
1417 	bridge_listfwd_t *blf = NULL, *newblf, blfread;
1418 	uint_t nblf = 0, maxblf = 0;
1419 	static uint8_t zero_addr[ETHERADDRL];
1420 	int rc;
1421 
1422 	(void) memset(&blfread, 0, sizeof (blfread));
1423 	(void) snprintf(blfread.blf_name, sizeof (blfread.blf_name),
1424 	    "%s0", bridge);
1425 	for (;;) {
1426 		if (nblf >= maxblf) {
1427 			maxblf = maxblf == 0 ? 64 : (maxblf << 1);
1428 			newblf = realloc(blf, maxblf * sizeof (*blf));
1429 			if (newblf == NULL) {
1430 				free(blf);
1431 				blf = NULL;
1432 				break;
1433 			}
1434 			blf = newblf;
1435 		}
1436 		rc = ioctl(dladm_dld_fd(handle), BRIDGE_IOC_LISTFWD, &blfread);
1437 		if (rc != 0) {
1438 			free(blf);
1439 			blf = NULL;
1440 			break;
1441 		}
1442 		if (memcmp(blfread.blf_dest, zero_addr, ETHERADDRL) == 0)
1443 			break;
1444 		blf[nblf++] = blfread;
1445 	}
1446 	if (blf != NULL)
1447 		*nfwd = nblf;
1448 	return (blf);
1449 }
1450 
1451 void
1452 dladm_bridge_free_fwdtable(bridge_listfwd_t *blf)
1453 {
1454 	free(blf);
1455 }
1456 
1457 /* Retrieve list of TRILL nicknames from the TRILL module */
1458 trill_listnick_t *
1459 dladm_bridge_get_trillnick(const char *bridge, uint_t *nnick)
1460 {
1461 	int fd;
1462 	char brcopy[MAXLINKNAMELEN];
1463 	trill_listnick_t *tln = NULL, *newtln, tlnread;
1464 	uint_t ntln = 0, maxtln = 0;
1465 
1466 	if ((fd = socket(PF_TRILL, SOCK_DGRAM, 0)) == -1)
1467 		return (NULL);
1468 	(void) strlcpy(brcopy, bridge, sizeof (brcopy));
1469 	if (ioctl(fd, TRILL_GETBRIDGE, &brcopy) < 0) {
1470 		(void) close(fd);
1471 		return (NULL);
1472 	}
1473 	(void) memset(&tlnread, 0, sizeof (tlnread));
1474 	for (;;) {
1475 		if (ntln >= maxtln) {
1476 			maxtln = maxtln == 0 ? 64 : (maxtln << 1);
1477 			newtln = realloc(tln, maxtln * sizeof (*tln));
1478 			if (newtln == NULL) {
1479 				free(tln);
1480 				tln = NULL;
1481 				break;
1482 			}
1483 			tln = newtln;
1484 		}
1485 		if (ioctl(fd, TRILL_LISTNICK, &tlnread) == -1) {
1486 			free(tln);
1487 			tln = NULL;
1488 			break;
1489 		}
1490 		if (tlnread.tln_nick == 0)
1491 			break;
1492 		tln[ntln++] = tlnread;
1493 	}
1494 	(void) close(fd);
1495 	if (tln != NULL)
1496 		*nnick = ntln;
1497 	return (tln);
1498 }
1499 
1500 void
1501 dladm_bridge_free_trillnick(trill_listnick_t *tln)
1502 {
1503 	free(tln);
1504 }
1505 
1506 /* Retrieve any stored TRILL nickname from TRILL SMF service */
1507 uint16_t
1508 dladm_bridge_get_nick(const char *bridge)
1509 {
1510 	scf_state_t sstate;
1511 	uint64_t value;
1512 	uint16_t nickname = RBRIDGE_NICKNAME_NONE;
1513 
1514 	if (bind_instance(TRILL_SVC_NAME, bridge, &sstate) != 0)
1515 		return (nickname);
1516 
1517 	if (get_composed_properties("config", B_TRUE, &sstate) == 0 &&
1518 	    get_count("nickname", &sstate, &value) == 0)
1519 		nickname = value;
1520 	shut_down_scf(&sstate);
1521 	return (nickname);
1522 }
1523 
1524 /* Stores TRILL nickname in SMF configuraiton for the TRILL service */
1525 void
1526 dladm_bridge_set_nick(const char *bridge, uint16_t nick)
1527 {
1528 	scf_state_t sstate;
1529 	scf_transaction_t *tran = NULL;
1530 	boolean_t new_pg = B_FALSE;
1531 	int rv = 0;
1532 	char *fmri;
1533 
1534 	if (exact_instance(TRILL_SVC_NAME, &sstate) != DLADM_STATUS_OK)
1535 		return;
1536 
1537 	if (scf_service_get_instance(sstate.ss_svc, bridge, sstate.ss_inst) !=
1538 	    0)
1539 		goto out;
1540 	if ((tran = scf_transaction_create(sstate.ss_handle)) == NULL)
1541 		goto out;
1542 	if ((sstate.ss_pg = scf_pg_create(sstate.ss_handle)) == NULL)
1543 		goto out;
1544 	if (scf_instance_add_pg(sstate.ss_inst, "config",
1545 	    SCF_GROUP_APPLICATION, 0, sstate.ss_pg) == 0) {
1546 		new_pg = B_TRUE;
1547 	} else if (scf_instance_get_pg(sstate.ss_inst, "config",
1548 	    sstate.ss_pg) != 0) {
1549 		goto out;
1550 	}
1551 	do {
1552 		if (scf_transaction_start(tran, sstate.ss_pg) != 0)
1553 			goto out;
1554 		if (!set_count_property(sstate.ss_handle, tran, "nickname",
1555 		    nick))
1556 			goto out;
1557 		rv = scf_transaction_commit(tran);
1558 		scf_transaction_reset(tran);
1559 		if (rv == 0 && scf_pg_update(sstate.ss_pg) == -1)
1560 			goto out;
1561 	} while (rv == 0);
1562 
1563 out:
1564 	if (tran != NULL) {
1565 		scf_transaction_destroy_children(tran);
1566 		scf_transaction_destroy(tran);
1567 	}
1568 
1569 	if (rv != 1 && new_pg)
1570 		(void) scf_pg_delete(sstate.ss_pg);
1571 
1572 	drop_composed(&sstate);
1573 	shut_down_scf(&sstate);
1574 	if (rv == 1 && (fmri = alloc_fmri(TRILL_SVC_NAME, bridge)) != NULL) {
1575 		(void) smf_refresh_instance(fmri);
1576 		free(fmri);
1577 	}
1578 }
1579