xref: /freebsd/sbin/ifconfig/ifbridge.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
1 /*-
2  * SPDX-License-Identifier: BSD-4-Clause
3  *
4  * Copyright 2001 Wasabi Systems, Inc.
5  * All rights reserved.
6  *
7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed for the NetBSD Project by
20  *	Wasabi Systems, Inc.
21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22  *    or promote products derived from this software without specific prior
23  *    written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 #ifndef lint
39 static const char rcsid[] =
40   "$FreeBSD$";
41 #endif /* not lint */
42 
43 #include <sys/param.h>
44 #include <sys/ioctl.h>
45 #include <sys/socket.h>
46 #include <sys/sockio.h>
47 
48 #include <stdlib.h>
49 #include <unistd.h>
50 
51 #include <net/ethernet.h>
52 #include <net/if.h>
53 #include <net/if_bridgevar.h>
54 #include <net/route.h>
55 
56 #include <ctype.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <stdlib.h>
60 #include <unistd.h>
61 #include <err.h>
62 #include <errno.h>
63 
64 #include <libifconfig.h>
65 
66 #include "ifconfig.h"
67 
68 static const char *stpstates[] = { STP_STATES };
69 static const char *stpproto[] = { STP_PROTOS };
70 static const char *stproles[] = { STP_ROLES };
71 
72 static int
73 get_val(const char *cp, u_long *valp)
74 {
75 	char *endptr;
76 	u_long val;
77 
78 	errno = 0;
79 	val = strtoul(cp, &endptr, 0);
80 	if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE)
81 		return (-1);
82 
83 	*valp = val;
84 	return (0);
85 }
86 
87 static int
88 do_cmd(if_ctx *ctx, u_long op, void *arg, size_t argsize, int set)
89 {
90 	struct ifdrv ifd = {};
91 
92 	strlcpy(ifd.ifd_name, ctx->ifname, sizeof(ifd.ifd_name));
93 	ifd.ifd_cmd = op;
94 	ifd.ifd_len = argsize;
95 	ifd.ifd_data = arg;
96 
97 	return (ioctl_ctx(ctx, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd));
98 }
99 
100 static void
101 do_bridgeflag(if_ctx *ctx, const char *ifs, int flag, int set)
102 {
103 	struct ifbreq req;
104 
105 	strlcpy(req.ifbr_ifsname, ifs, sizeof(req.ifbr_ifsname));
106 
107 	if (do_cmd(ctx, BRDGGIFFLGS, &req, sizeof(req), 0) < 0)
108 		err(1, "unable to get bridge flags");
109 
110 	if (set)
111 		req.ifbr_ifsflags |= flag;
112 	else
113 		req.ifbr_ifsflags &= ~flag;
114 
115 	if (do_cmd(ctx, BRDGSIFFLGS, &req, sizeof(req), 1) < 0)
116 		err(1, "unable to set bridge flags");
117 }
118 
119 static void
120 bridge_addresses(if_ctx *ctx, const char *prefix)
121 {
122 	struct ifbaconf ifbac;
123 	struct ifbareq *ifba;
124 	char *inbuf = NULL, *ninbuf;
125 	size_t len = 8192;
126 	struct ether_addr ea;
127 
128 	for (;;) {
129 		ninbuf = realloc(inbuf, len);
130 		if (ninbuf == NULL)
131 			err(1, "unable to allocate address buffer");
132 		ifbac.ifbac_len = len;
133 		ifbac.ifbac_buf = inbuf = ninbuf;
134 		if (do_cmd(ctx, BRDGRTS, &ifbac, sizeof(ifbac), 0) < 0)
135 			err(1, "unable to get address cache");
136 		if ((ifbac.ifbac_len + sizeof(*ifba)) < len)
137 			break;
138 		len *= 2;
139 	}
140 
141 	for (unsigned long i = 0; i < ifbac.ifbac_len / sizeof(*ifba); i++) {
142 		ifba = ifbac.ifbac_req + i;
143 		memcpy(ea.octet, ifba->ifba_dst,
144 		    sizeof(ea.octet));
145 		printf("%s%s Vlan%d %s %lu ", prefix, ether_ntoa(&ea),
146 		    ifba->ifba_vlan, ifba->ifba_ifsname, ifba->ifba_expire);
147 		printb("flags", ifba->ifba_flags, IFBAFBITS);
148 		printf("\n");
149 	}
150 
151 	free(inbuf);
152 }
153 
154 static void
155 bridge_status(if_ctx *ctx)
156 {
157 	struct ifconfig_bridge_status *bridge;
158 	struct ifbropreq *params;
159 	const char *pad, *prefix;
160 	uint8_t lladdr[ETHER_ADDR_LEN];
161 	uint16_t bprio;
162 
163 	if (ifconfig_bridge_get_bridge_status(lifh, ctx->ifname, &bridge) == -1)
164 		return;
165 
166 	params = bridge->params;
167 
168 	PV2ID(params->ifbop_bridgeid, bprio, lladdr);
169 	printf("\tid %s priority %u hellotime %u fwddelay %u\n",
170 	    ether_ntoa((struct ether_addr *)lladdr),
171 	    params->ifbop_priority,
172 	    params->ifbop_hellotime,
173 	    params->ifbop_fwddelay);
174 	printf("\tmaxage %u holdcnt %u proto %s maxaddr %u timeout %u\n",
175 	    params->ifbop_maxage,
176 	    params->ifbop_holdcount,
177 	    stpproto[params->ifbop_protocol],
178 	    bridge->cache_size,
179 	    bridge->cache_lifetime);
180 	PV2ID(params->ifbop_designated_root, bprio, lladdr);
181 	printf("\troot id %s priority %d ifcost %u port %u\n",
182 	    ether_ntoa((struct ether_addr *)lladdr),
183 	    bprio,
184 	    params->ifbop_root_path_cost,
185 	    params->ifbop_root_port & 0xfff);
186 
187 	prefix = "\tmember: ";
188 	pad    = "\t        ";
189 	for (size_t i = 0; i < bridge->members_count; ++i) {
190 		struct ifbreq *member = &bridge->members[i];
191 
192 		printf("%s%s ", prefix, member->ifbr_ifsname);
193 		printb("flags", member->ifbr_ifsflags, IFBIFBITS);
194 		printf("\n%s", pad);
195 		printf("ifmaxaddr %u port %u priority %u path cost %u",
196 		    member->ifbr_addrmax,
197 		    member->ifbr_portno,
198 		    member->ifbr_priority,
199 		    member->ifbr_path_cost);
200 		if (member->ifbr_ifsflags & IFBIF_STP) {
201 			uint8_t proto = member->ifbr_proto;
202 			uint8_t role = member->ifbr_role;
203 			uint8_t state = member->ifbr_state;
204 
205 			if (proto < nitems(stpproto))
206 				printf(" proto %s", stpproto[proto]);
207 			else
208 				printf(" <unknown proto %d>", proto);
209 			printf("\n%s", pad);
210 			if (role < nitems(stproles))
211 				printf("role %s", stproles[role]);
212 			else
213 				printf("<unknown role %d>", role);
214 			if (state < nitems(stpstates))
215 				printf(" state %s", stpstates[state]);
216 			else
217 				printf(" <unknown state %d>", state);
218 		}
219 		printf("\n");
220 	}
221 
222 	ifconfig_bridge_free_bridge_status(bridge);
223 }
224 
225 static void
226 setbridge_add(if_ctx *ctx, const char *val, int dummy __unused)
227 {
228 	struct ifbreq req;
229 
230 	memset(&req, 0, sizeof(req));
231 	strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname));
232 	if (do_cmd(ctx, BRDGADD, &req, sizeof(req), 1) < 0)
233 		err(1, "BRDGADD %s",  val);
234 }
235 
236 static void
237 setbridge_delete(if_ctx *ctx, const char *val, int dummy __unused)
238 {
239 	struct ifbreq req;
240 
241 	memset(&req, 0, sizeof(req));
242 	strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname));
243 	if (do_cmd(ctx, BRDGDEL, &req, sizeof(req), 1) < 0)
244 		err(1, "BRDGDEL %s",  val);
245 }
246 
247 static void
248 setbridge_discover(if_ctx *ctx, const char *val, int dummy __unused)
249 {
250 
251 	do_bridgeflag(ctx, val, IFBIF_DISCOVER, 1);
252 }
253 
254 static void
255 unsetbridge_discover(if_ctx *ctx, const char *val, int dummy __unused)
256 {
257 
258 	do_bridgeflag(ctx, val, IFBIF_DISCOVER, 0);
259 }
260 
261 static void
262 setbridge_learn(if_ctx *ctx, const char *val, int dummy __unused)
263 {
264 
265 	do_bridgeflag(ctx, val, IFBIF_LEARNING,  1);
266 }
267 
268 static void
269 unsetbridge_learn(if_ctx *ctx, const char *val, int dummy __unused)
270 {
271 
272 	do_bridgeflag(ctx, val, IFBIF_LEARNING,  0);
273 }
274 
275 static void
276 setbridge_sticky(if_ctx *ctx, const char *val, int dummy __unused)
277 {
278 
279 	do_bridgeflag(ctx, val, IFBIF_STICKY,  1);
280 }
281 
282 static void
283 unsetbridge_sticky(if_ctx *ctx, const char *val, int dummy __unused)
284 {
285 
286 	do_bridgeflag(ctx, val, IFBIF_STICKY,  0);
287 }
288 
289 static void
290 setbridge_span(if_ctx *ctx, const char *val, int dummy __unused)
291 {
292 	struct ifbreq req;
293 
294 	memset(&req, 0, sizeof(req));
295 	strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname));
296 	if (do_cmd(ctx, BRDGADDS, &req, sizeof(req), 1) < 0)
297 		err(1, "BRDGADDS %s",  val);
298 }
299 
300 static void
301 unsetbridge_span(if_ctx *ctx, const char *val, int dummy __unused)
302 {
303 	struct ifbreq req;
304 
305 	memset(&req, 0, sizeof(req));
306 	strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname));
307 	if (do_cmd(ctx, BRDGDELS, &req, sizeof(req), 1) < 0)
308 		err(1, "BRDGDELS %s",  val);
309 }
310 
311 static void
312 setbridge_stp(if_ctx *ctx, const char *val, int dummy __unused)
313 {
314 
315 	do_bridgeflag(ctx, val, IFBIF_STP, 1);
316 }
317 
318 static void
319 unsetbridge_stp(if_ctx *ctx, const char *val, int dummy __unused)
320 {
321 
322 	do_bridgeflag(ctx, val, IFBIF_STP, 0);
323 }
324 
325 static void
326 setbridge_edge(if_ctx *ctx, const char *val, int dummy __unused)
327 {
328 	do_bridgeflag(ctx, val, IFBIF_BSTP_EDGE, 1);
329 }
330 
331 static void
332 unsetbridge_edge(if_ctx *ctx, const char *val, int dummy __unused)
333 {
334 	do_bridgeflag(ctx, val, IFBIF_BSTP_EDGE, 0);
335 }
336 
337 static void
338 setbridge_autoedge(if_ctx *ctx, const char *val, int dummy __unused)
339 {
340 	do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOEDGE, 1);
341 }
342 
343 static void
344 unsetbridge_autoedge(if_ctx *ctx, const char *val, int dummy __unused)
345 {
346 	do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOEDGE, 0);
347 }
348 
349 static void
350 setbridge_ptp(if_ctx *ctx, const char *val, int dummy __unused)
351 {
352 	do_bridgeflag(ctx, val, IFBIF_BSTP_PTP, 1);
353 }
354 
355 static void
356 unsetbridge_ptp(if_ctx *ctx, const char *val, int dummy __unused)
357 {
358 	do_bridgeflag(ctx, val, IFBIF_BSTP_PTP, 0);
359 }
360 
361 static void
362 setbridge_autoptp(if_ctx *ctx, const char *val, int dummy __unused)
363 {
364 	do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOPTP, 1);
365 }
366 
367 static void
368 unsetbridge_autoptp(if_ctx *ctx, const char *val, int dummy __unused)
369 {
370 	do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOPTP, 0);
371 }
372 
373 static void
374 setbridge_flush(if_ctx *ctx, const char *val __unused, int dummy __unused)
375 {
376 	struct ifbreq req;
377 
378 	memset(&req, 0, sizeof(req));
379 	req.ifbr_ifsflags = IFBF_FLUSHDYN;
380 	if (do_cmd(ctx, BRDGFLUSH, &req, sizeof(req), 1) < 0)
381 		err(1, "BRDGFLUSH");
382 }
383 
384 static void
385 setbridge_flushall(if_ctx *ctx, const char *val __unused, int dummy __unused)
386 {
387 	struct ifbreq req;
388 
389 	memset(&req, 0, sizeof(req));
390 	req.ifbr_ifsflags = IFBF_FLUSHALL;
391 	if (do_cmd(ctx, BRDGFLUSH, &req, sizeof(req), 1) < 0)
392 		err(1, "BRDGFLUSH");
393 }
394 
395 static void
396 setbridge_static(if_ctx *ctx, const char *val, const char *mac)
397 {
398 	struct ifbareq req;
399 	struct ether_addr *ea;
400 
401 	memset(&req, 0, sizeof(req));
402 	strlcpy(req.ifba_ifsname, val, sizeof(req.ifba_ifsname));
403 
404 	ea = ether_aton(mac);
405 	if (ea == NULL)
406 		errx(1, "%s: invalid address: %s", val, mac);
407 
408 	memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst));
409 	req.ifba_flags = IFBAF_STATIC;
410 	req.ifba_vlan = 1; /* XXX allow user to specify */
411 
412 	if (do_cmd(ctx, BRDGSADDR, &req, sizeof(req), 1) < 0)
413 		err(1, "BRDGSADDR %s",  val);
414 }
415 
416 static void
417 setbridge_deladdr(if_ctx *ctx, const char *val, int dummy __unused)
418 {
419 	struct ifbareq req;
420 	struct ether_addr *ea;
421 
422 	memset(&req, 0, sizeof(req));
423 
424 	ea = ether_aton(val);
425 	if (ea == NULL)
426 		errx(1, "invalid address: %s",  val);
427 
428 	memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst));
429 
430 	if (do_cmd(ctx, BRDGDADDR, &req, sizeof(req), 1) < 0)
431 		err(1, "BRDGDADDR %s",  val);
432 }
433 
434 static void
435 setbridge_addr(if_ctx *ctx, const char *val __unused, int dummy __unused)
436 {
437 
438 	bridge_addresses(ctx, "");
439 }
440 
441 static void
442 setbridge_maxaddr(if_ctx *ctx, const char *arg, int dummy __unused)
443 {
444 	struct ifbrparam param;
445 	u_long val;
446 
447 	if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0)
448 		errx(1, "invalid value: %s",  arg);
449 
450 	param.ifbrp_csize = val & 0xffffffff;
451 
452 	if (do_cmd(ctx, BRDGSCACHE, &param, sizeof(param), 1) < 0)
453 		err(1, "BRDGSCACHE %s",  arg);
454 }
455 
456 static void
457 setbridge_hellotime(if_ctx *ctx, const char *arg, int dummy __unused)
458 {
459 	struct ifbrparam param;
460 	u_long val;
461 
462 	if (get_val(arg, &val) < 0 || (val & ~0xff) != 0)
463 		errx(1, "invalid value: %s",  arg);
464 
465 	param.ifbrp_hellotime = val & 0xff;
466 
467 	if (do_cmd(ctx, BRDGSHT, &param, sizeof(param), 1) < 0)
468 		err(1, "BRDGSHT %s",  arg);
469 }
470 
471 static void
472 setbridge_fwddelay(if_ctx *ctx, const char *arg, int dummy __unused)
473 {
474 	struct ifbrparam param;
475 	u_long val;
476 
477 	if (get_val(arg, &val) < 0 || (val & ~0xff) != 0)
478 		errx(1, "invalid value: %s",  arg);
479 
480 	param.ifbrp_fwddelay = val & 0xff;
481 
482 	if (do_cmd(ctx, BRDGSFD, &param, sizeof(param), 1) < 0)
483 		err(1, "BRDGSFD %s",  arg);
484 }
485 
486 static void
487 setbridge_maxage(if_ctx *ctx, const char *arg, int dummy __unused)
488 {
489 	struct ifbrparam param;
490 	u_long val;
491 
492 	if (get_val(arg, &val) < 0 || (val & ~0xff) != 0)
493 		errx(1, "invalid value: %s",  arg);
494 
495 	param.ifbrp_maxage = val & 0xff;
496 
497 	if (do_cmd(ctx, BRDGSMA, &param, sizeof(param), 1) < 0)
498 		err(1, "BRDGSMA %s",  arg);
499 }
500 
501 static void
502 setbridge_priority(if_ctx *ctx, const char *arg, int dummy __unused)
503 {
504 	struct ifbrparam param;
505 	u_long val;
506 
507 	if (get_val(arg, &val) < 0 || (val & ~0xffff) != 0)
508 		errx(1, "invalid value: %s",  arg);
509 
510 	param.ifbrp_prio = val & 0xffff;
511 
512 	if (do_cmd(ctx, BRDGSPRI, &param, sizeof(param), 1) < 0)
513 		err(1, "BRDGSPRI %s",  arg);
514 }
515 
516 static void
517 setbridge_protocol(if_ctx *ctx, const char *arg, int dummy __unused)
518 {
519 	struct ifbrparam param;
520 
521 	if (strcasecmp(arg, "stp") == 0) {
522 		param.ifbrp_proto = 0;
523 	} else if (strcasecmp(arg, "rstp") == 0) {
524 		param.ifbrp_proto = 2;
525 	} else {
526 		errx(1, "unknown stp protocol");
527 	}
528 
529 	if (do_cmd(ctx, BRDGSPROTO, &param, sizeof(param), 1) < 0)
530 		err(1, "BRDGSPROTO %s",  arg);
531 }
532 
533 static void
534 setbridge_holdcount(if_ctx *ctx, const char *arg, int dummy __unused)
535 {
536 	struct ifbrparam param;
537 	u_long val;
538 
539 	if (get_val(arg, &val) < 0 || (val & ~0xff) != 0)
540 		errx(1, "invalid value: %s",  arg);
541 
542 	param.ifbrp_txhc = val & 0xff;
543 
544 	if (do_cmd(ctx, BRDGSTXHC, &param, sizeof(param), 1) < 0)
545 		err(1, "BRDGSTXHC %s",  arg);
546 }
547 
548 static void
549 setbridge_ifpriority(if_ctx *ctx, const char *ifn, const char *pri)
550 {
551 	struct ifbreq req;
552 	u_long val;
553 
554 	memset(&req, 0, sizeof(req));
555 
556 	if (get_val(pri, &val) < 0 || (val & ~0xff) != 0)
557 		errx(1, "invalid value: %s",  pri);
558 
559 	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
560 	req.ifbr_priority = val & 0xff;
561 
562 	if (do_cmd(ctx, BRDGSIFPRIO, &req, sizeof(req), 1) < 0)
563 		err(1, "BRDGSIFPRIO %s",  pri);
564 }
565 
566 static void
567 setbridge_ifpathcost(if_ctx *ctx, const char *ifn, const char *cost)
568 {
569 	struct ifbreq req;
570 	u_long val;
571 
572 	memset(&req, 0, sizeof(req));
573 
574 	if (get_val(cost, &val) < 0)
575 		errx(1, "invalid value: %s",  cost);
576 
577 	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
578 	req.ifbr_path_cost = val;
579 
580 	if (do_cmd(ctx, BRDGSIFCOST, &req, sizeof(req), 1) < 0)
581 		err(1, "BRDGSIFCOST %s",  cost);
582 }
583 
584 static void
585 setbridge_ifmaxaddr(if_ctx *ctx, const char *ifn, const char *arg)
586 {
587 	struct ifbreq req;
588 	u_long val;
589 
590 	memset(&req, 0, sizeof(req));
591 
592 	if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0)
593 		errx(1, "invalid value: %s",  arg);
594 
595 	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
596 	req.ifbr_addrmax = val & 0xffffffff;
597 
598 	if (do_cmd(ctx, BRDGSIFAMAX, &req, sizeof(req), 1) < 0)
599 		err(1, "BRDGSIFAMAX %s",  arg);
600 }
601 
602 static void
603 setbridge_timeout(if_ctx *ctx, const char *arg, int dummy __unused)
604 {
605 	struct ifbrparam param;
606 	u_long val;
607 
608 	if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0)
609 		errx(1, "invalid value: %s",  arg);
610 
611 	param.ifbrp_ctime = val & 0xffffffff;
612 
613 	if (do_cmd(ctx, BRDGSTO, &param, sizeof(param), 1) < 0)
614 		err(1, "BRDGSTO %s",  arg);
615 }
616 
617 static void
618 setbridge_private(if_ctx *ctx, const char *val, int dummy __unused)
619 {
620 
621 	do_bridgeflag(ctx, val, IFBIF_PRIVATE, 1);
622 }
623 
624 static void
625 unsetbridge_private(if_ctx *ctx, const char *val, int dummy __unused)
626 {
627 
628 	do_bridgeflag(ctx, val, IFBIF_PRIVATE, 0);
629 }
630 
631 static struct cmd bridge_cmds[] = {
632 	DEF_CMD_ARG("addm",		setbridge_add),
633 	DEF_CMD_ARG("deletem",		setbridge_delete),
634 	DEF_CMD_ARG("discover",		setbridge_discover),
635 	DEF_CMD_ARG("-discover",	unsetbridge_discover),
636 	DEF_CMD_ARG("learn",		setbridge_learn),
637 	DEF_CMD_ARG("-learn",		unsetbridge_learn),
638 	DEF_CMD_ARG("sticky",		setbridge_sticky),
639 	DEF_CMD_ARG("-sticky",		unsetbridge_sticky),
640 	DEF_CMD_ARG("span",		setbridge_span),
641 	DEF_CMD_ARG("-span",		unsetbridge_span),
642 	DEF_CMD_ARG("stp",		setbridge_stp),
643 	DEF_CMD_ARG("-stp",		unsetbridge_stp),
644 	DEF_CMD_ARG("edge",		setbridge_edge),
645 	DEF_CMD_ARG("-edge",		unsetbridge_edge),
646 	DEF_CMD_ARG("autoedge",		setbridge_autoedge),
647 	DEF_CMD_ARG("-autoedge",	unsetbridge_autoedge),
648 	DEF_CMD_ARG("ptp",		setbridge_ptp),
649 	DEF_CMD_ARG("-ptp",		unsetbridge_ptp),
650 	DEF_CMD_ARG("autoptp",		setbridge_autoptp),
651 	DEF_CMD_ARG("-autoptp",		unsetbridge_autoptp),
652 	DEF_CMD("flush", 0,		setbridge_flush),
653 	DEF_CMD("flushall", 0,		setbridge_flushall),
654 	DEF_CMD_ARG2("static",		setbridge_static),
655 	DEF_CMD_ARG("deladdr",		setbridge_deladdr),
656 	DEF_CMD("addr",	 1,		setbridge_addr),
657 	DEF_CMD_ARG("maxaddr",		setbridge_maxaddr),
658 	DEF_CMD_ARG("hellotime",	setbridge_hellotime),
659 	DEF_CMD_ARG("fwddelay",		setbridge_fwddelay),
660 	DEF_CMD_ARG("maxage",		setbridge_maxage),
661 	DEF_CMD_ARG("priority",		setbridge_priority),
662 	DEF_CMD_ARG("proto",		setbridge_protocol),
663 	DEF_CMD_ARG("holdcnt",		setbridge_holdcount),
664 	DEF_CMD_ARG2("ifpriority",	setbridge_ifpriority),
665 	DEF_CMD_ARG2("ifpathcost",	setbridge_ifpathcost),
666 	DEF_CMD_ARG2("ifmaxaddr",	setbridge_ifmaxaddr),
667 	DEF_CMD_ARG("timeout",		setbridge_timeout),
668 	DEF_CMD_ARG("private",		setbridge_private),
669 	DEF_CMD_ARG("-private",		unsetbridge_private),
670 };
671 static struct afswtch af_bridge = {
672 	.af_name	= "af_bridge",
673 	.af_af		= AF_UNSPEC,
674 	.af_other_status = bridge_status,
675 };
676 
677 static __constructor void
678 bridge_ctor(void)
679 {
680 	for (size_t i = 0; i < nitems(bridge_cmds);  i++)
681 		cmd_register(&bridge_cmds[i]);
682 	af_register(&af_bridge);
683 }
684