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