xref: /openbsd/sbin/ifconfig/brconfig.c (revision 17df1aa7)
1 /*	$OpenBSD: brconfig.c,v 1.3 2009/12/14 19:22:20 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #ifndef SMALL
30 
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include <sys/stdint.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <sys/param.h>
37 #include <sys/socket.h>
38 #include <sys/ioctl.h>
39 #include <net/if.h>
40 #include <net/if_dl.h>
41 #include <netinet/in.h>
42 #include <netinet/if_ether.h>
43 #include <net/if_bridge.h>
44 #include <string.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <getopt.h>
48 #include <limits.h>
49 
50 #include "brconfig.h"
51 
52 void bridge_ifsetflag(const char *, u_int32_t);
53 void bridge_ifclrflag(const char *, u_int32_t);
54 
55 void bridge_list(char *);
56 void bridge_cfg(const char *);
57 void bridge_badrule(int, char **, int);
58 void bridge_showrule(struct ifbrlreq *);
59 
60 #define	IFBAFBITS	"\020\1STATIC"
61 #define	IFBIFBITS	\
62 "\020\1LEARNING\2DISCOVER\3BLOCKNONIP\4STP\5EDGE\6AUTOEDGE\7PTP\10AUTOPTP\11SPAN"
63 
64 #define	PV2ID(pv, epri, eaddr)	do {					\
65 	epri	 = pv >> 48;						\
66 	eaddr[0] = pv >> 40;						\
67 	eaddr[1] = pv >> 32;						\
68 	eaddr[2] = pv >> 24;						\
69 	eaddr[3] = pv >> 16;						\
70 	eaddr[4] = pv >> 8;						\
71 	eaddr[5] = pv >> 0;						\
72 } while (0)
73 
74 char *stpstates[] = {
75 	"disabled",
76 	"listening",
77 	"learning",
78 	"forwarding",
79 	"blocking",
80 	"discarding"
81 };
82 char *stpproto[] = {
83 	"stp",
84 	"(none)",
85 	"rstp",
86 };
87 char *stproles[] = {
88 	"disabled",
89 	"root",
90 	"designated",
91 	"alternate",
92 	"backup"
93 };
94 
95 
96 void
97 setdiscover(const char *val, int d)
98 {
99 	bridge_ifsetflag(val, IFBIF_DISCOVER);
100 }
101 
102 void
103 unsetdiscover(const char *val, int d)
104 {
105 	bridge_ifclrflag(val, IFBIF_DISCOVER);
106 }
107 
108 void
109 setblocknonip(const char *val, int d)
110 {
111 	bridge_ifsetflag(val, IFBIF_BLOCKNONIP);
112 }
113 
114 void
115 unsetblocknonip(const char *val, int d)
116 {
117 	bridge_ifclrflag(val, IFBIF_BLOCKNONIP);
118 }
119 
120 void
121 setlearn(const char *val, int d)
122 {
123 	bridge_ifsetflag(val, IFBIF_LEARNING);
124 }
125 
126 void
127 unsetlearn(const char *val, int d)
128 {
129 	bridge_ifclrflag(val, IFBIF_LEARNING);
130 }
131 
132 void
133 setstp(const char *val, int d)
134 {
135 	bridge_ifsetflag(val, IFBIF_STP);
136 }
137 
138 void
139 unsetstp(const char *val, int d)
140 {
141 	bridge_ifclrflag(val, IFBIF_STP);
142 }
143 
144 void
145 setedge(const char *val, int d)
146 {
147 	bridge_ifsetflag(val, IFBIF_BSTP_EDGE);
148 }
149 
150 void
151 unsetedge(const char *val, int d)
152 {
153 	bridge_ifclrflag(val, IFBIF_BSTP_EDGE);
154 }
155 
156 void
157 setautoedge(const char *val, int d)
158 {
159 	bridge_ifsetflag(val, IFBIF_BSTP_AUTOEDGE);
160 }
161 
162 void
163 unsetautoedge(const char *val, int d)
164 {
165 	bridge_ifclrflag(val, IFBIF_BSTP_AUTOEDGE);
166 }
167 
168 void
169 setptp(const char *val, int d)
170 {
171 	bridge_ifsetflag(val, IFBIF_BSTP_PTP);
172 }
173 
174 void
175 unsetptp(const char *val, int d)
176 {
177 	bridge_ifclrflag(val, IFBIF_BSTP_PTP);
178 }
179 
180 void
181 setautoptp(const char *val, int d)
182 {
183 	bridge_ifsetflag(val, IFBIF_BSTP_AUTOPTP);
184 }
185 
186 void
187 unsetautoptp(const char *val, int d)
188 {
189 	bridge_ifclrflag(val, IFBIF_BSTP_AUTOPTP);
190 }
191 
192 
193 void
194 bridge_ifsetflag(const char *ifsname, u_int32_t flag)
195 {
196 	struct ifbreq req;
197 
198 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
199 	strlcpy(req.ifbr_ifsname, ifsname, sizeof(req.ifbr_ifsname));
200 	if (ioctl(s, SIOCBRDGGIFFLGS, (caddr_t)&req) < 0)
201 		err(1, "%s: ioctl SIOCBRDGGIFFLGS %s", name, ifsname);
202 
203 	req.ifbr_ifsflags |= flag & ~IFBIF_RO_MASK;
204 
205 	if (ioctl(s, SIOCBRDGSIFFLGS, (caddr_t)&req) < 0)
206 		err(1, "%s: ioctl SIOCBRDGSIFFLGS %s", name, ifsname);
207 }
208 
209 void
210 bridge_ifclrflag(const char *ifsname, u_int32_t flag)
211 {
212 	struct ifbreq req;
213 
214 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
215 	strlcpy(req.ifbr_ifsname, ifsname, sizeof(req.ifbr_ifsname));
216 
217 	if (ioctl(s, SIOCBRDGGIFFLGS, (caddr_t)&req) < 0)
218 		err(1, "%s: ioctl SIOCBRDGGIFFLGS %s", name, ifsname);
219 
220 	req.ifbr_ifsflags &= ~(flag | IFBIF_RO_MASK);
221 
222 	if (ioctl(s, SIOCBRDGSIFFLGS, (caddr_t)&req) < 0)
223 		err(1, "%s: ioctl SIOCBRDGSIFFLGS %s", name, ifsname);
224 }
225 
226 void
227 bridge_flushall(const char *val, int p)
228 {
229 	struct ifbreq req;
230 
231 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
232 	req.ifbr_ifsflags = IFBF_FLUSHALL;
233 	if (ioctl(s, SIOCBRDGFLUSH, &req) < 0)
234 		err(1, "%s", name);
235 }
236 
237 void
238 bridge_flush(const char *val, int p)
239 {
240 	struct ifbreq req;
241 
242 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
243 	req.ifbr_ifsflags = IFBF_FLUSHDYN;
244 	if (ioctl(s, SIOCBRDGFLUSH, &req) < 0)
245 		err(1, "%s", name);
246 }
247 
248 void
249 bridge_cfg(const char *delim)
250 {
251 	struct ifbropreq ifbp;
252 	u_int16_t pri;
253 	u_int8_t ht, fd, ma, hc, proto;
254 	u_int8_t lladdr[ETHER_ADDR_LEN];
255 	u_int16_t bprio;
256 
257 	strlcpy(ifbp.ifbop_name, name, sizeof(ifbp.ifbop_name));
258 	if (ioctl(s, SIOCBRDGGPARAM, (caddr_t)&ifbp))
259 		err(1, "%s", name);
260 	printf("%s", delim);
261 	pri = ifbp.ifbop_priority;
262 	ht = ifbp.ifbop_hellotime;
263 	fd = ifbp.ifbop_fwddelay;
264 	ma = ifbp.ifbop_maxage;
265 	hc = ifbp.ifbop_holdcount;
266 	proto = ifbp.ifbop_protocol;
267 
268 	printf("priority %u hellotime %u fwddelay %u maxage %u "
269 	    "holdcnt %u proto %s\n", pri, ht, fd, ma, hc, stpproto[proto]);
270 
271 	if (aflag)
272 		return;
273 
274 	PV2ID(ifbp.ifbop_desg_bridge, bprio, lladdr);
275 	printf("\tdesignated: id %s priority %u\n",
276 	    ether_ntoa((struct ether_addr *)lladdr), bprio);
277 
278 	if (ifbp.ifbop_root_bridge == ifbp.ifbop_desg_bridge)
279 		return;
280 
281 	PV2ID(ifbp.ifbop_root_bridge, bprio, lladdr);
282 	printf("\troot: id %s priority %u ifcost %u port %u\n",
283 	    ether_ntoa((struct ether_addr *)lladdr), bprio,
284 	    ifbp.ifbop_root_path_cost, ifbp.ifbop_root_port & 0xfff);
285 }
286 
287 void
288 bridge_list(char *delim)
289 {
290 	struct ifbreq *reqp;
291 	struct ifbifconf bifc;
292 	int i, len = 8192;
293 	char buf[sizeof(reqp->ifbr_ifsname) + 1], *inbuf = NULL, *inb;
294 
295 	while (1) {
296 		bifc.ifbic_len = len;
297 		inb = realloc(inbuf, len);
298 		if (inb == NULL)
299 			err(1, "malloc");
300 		bifc.ifbic_buf = inbuf = inb;
301 		strlcpy(bifc.ifbic_name, name, sizeof(bifc.ifbic_name));
302 		if (ioctl(s, SIOCBRDGIFS, &bifc) < 0)
303 			err(1, "%s", name);
304 		if (bifc.ifbic_len + sizeof(*reqp) < len)
305 			break;
306 		len *= 2;
307 	}
308 	for (i = 0; i < bifc.ifbic_len / sizeof(*reqp); i++) {
309 		reqp = bifc.ifbic_req + i;
310 		strlcpy(buf, reqp->ifbr_ifsname, sizeof(buf));
311 		printf("%s%s ", delim, buf);
312 		printb("flags", reqp->ifbr_ifsflags, IFBIFBITS);
313 		printf("\n");
314 		if (reqp->ifbr_ifsflags & IFBIF_SPAN)
315 			continue;
316 		printf("\t\t");
317 		printf("port %u ifpriority %u ifcost %u",
318 		    reqp->ifbr_portno, reqp->ifbr_priority,
319 		    reqp->ifbr_path_cost);
320 		if (reqp->ifbr_ifsflags & IFBIF_STP)
321 			printf(" %s role %s",
322 			    stpstates[reqp->ifbr_state],
323 			    stproles[reqp->ifbr_role]);
324 		printf("\n");
325 		bridge_rules(buf, 0);
326 	}
327 	free(bifc.ifbic_buf);
328 }
329 
330 void
331 bridge_add(const char *ifn, int d)
332 {
333 	struct ifbreq req;
334 
335 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
336 	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
337 	if (ioctl(s, SIOCBRDGADD, &req) < 0) {
338 		if (errno == EEXIST)
339 			return;
340 		err(1, "%s: %s", name, ifn);
341 	}
342 }
343 
344 void
345 bridge_delete(const char *ifn, int d)
346 {
347 	struct ifbreq req;
348 
349 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
350 	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
351 	if (ioctl(s, SIOCBRDGDEL, &req) < 0)
352 		err(1, "%s: %s", name, ifn);
353 }
354 
355 void
356 bridge_addspan(const char *ifn, int d)
357 {
358 	struct ifbreq req;
359 
360 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
361 	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
362 	if (ioctl(s, SIOCBRDGADDS, &req) < 0)
363 		err(1, "%s: %s", name, ifn);
364 }
365 
366 void
367 bridge_delspan(const char *ifn, int d)
368 {
369 	struct ifbreq req;
370 
371 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
372 	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
373 	if (ioctl(s, SIOCBRDGDELS, &req) < 0)
374 		err(1, "%s: %s", name, ifn);
375 }
376 
377 void
378 bridge_timeout(const char *arg, int d)
379 {
380 	struct ifbrparam bp;
381 	long newtime;
382 	char *endptr;
383 
384 	errno = 0;
385 	newtime = strtol(arg, &endptr, 0);
386 	if (arg[0] == '\0' || endptr[0] != '\0' ||
387 	    (newtime & ~INT_MAX) != 0L ||
388 	    (errno == ERANGE && newtime == LONG_MAX))
389 		errx(1, "invalid arg for timeout: %s\n", arg);
390 
391 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
392 	bp.ifbrp_ctime = newtime;
393 	if (ioctl(s, SIOCBRDGSTO, (caddr_t)&bp) < 0)
394 		err(1, "%s", name);
395 }
396 
397 void
398 bridge_maxage(const char *arg, int d)
399 {
400 	struct ifbrparam bp;
401 	unsigned long v;
402 	char *endptr;
403 
404 	errno = 0;
405 	v = strtoul(arg, &endptr, 0);
406 	if (arg[0] == '\0' || endptr[0] != '\0' || v > 0xffUL ||
407 	    (errno == ERANGE && v == ULONG_MAX))
408 		errx(1, "invalid arg for maxage: %s\n", arg);
409 
410 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
411 	bp.ifbrp_maxage = v;
412 	if (ioctl(s, SIOCBRDGSMA, (caddr_t)&bp) < 0)
413 		err(1, "%s", name);
414 }
415 
416 void
417 bridge_priority(const char *arg, int d)
418 {
419 	struct ifbrparam bp;
420 	unsigned long v;
421 	char *endptr;
422 
423 	errno = 0;
424 	v = strtoul(arg, &endptr, 0);
425 	if (arg[0] == '\0' || endptr[0] != '\0' || v > 0xffffUL ||
426 	    (errno == ERANGE && v == ULONG_MAX))
427 		errx(1, "invalid arg for spanpriority: %s\n", arg);
428 
429 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
430 	bp.ifbrp_prio = v;
431 	if (ioctl(s, SIOCBRDGSPRI, (caddr_t)&bp) < 0)
432 		err(1, "%s", name);
433 }
434 
435 void
436 bridge_proto(const char *arg, int d)
437 {
438 	struct ifbrparam bp;
439 	int i, proto = -1;
440 
441 	for (i = 0; i <= BSTP_PROTO_MAX; i++)
442 		if (strcmp(arg, stpproto[i]) == 0) {
443 			proto = i;
444 			break;
445 		}
446 	if (proto == -1)
447 		errx(1, "invalid arg for proto: %s\n", arg);
448 
449 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
450 	bp.ifbrp_prio = proto;
451 	if (ioctl(s, SIOCBRDGSPROTO, (caddr_t)&bp) < 0)
452 		err(1, "%s", name);
453 }
454 
455 void
456 bridge_fwddelay(const char *arg, int d)
457 {
458 	struct ifbrparam bp;
459 	unsigned long v;
460 	char *endptr;
461 
462 	errno = 0;
463 	v = strtoul(arg, &endptr, 0);
464 	if (arg[0] == '\0' || endptr[0] != '\0' || v > 0xffUL ||
465 	    (errno == ERANGE && v == ULONG_MAX))
466 		errx(1, "invalid arg for fwddelay: %s\n", arg);
467 
468 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
469 	bp.ifbrp_fwddelay = v;
470 	if (ioctl(s, SIOCBRDGSFD, (caddr_t)&bp) < 0)
471 		err(1, "%s", name);
472 }
473 
474 void
475 bridge_hellotime(const char *arg, int d)
476 {
477 	struct ifbrparam bp;
478 	unsigned long v;
479 	char *endptr;
480 
481 	errno = 0;
482 	v = strtoul(arg, &endptr, 0);
483 	if (arg[0] == '\0' || endptr[0] != '\0' || v > 0xffUL ||
484 	    (errno == ERANGE && v == ULONG_MAX))
485 		errx(1, "invalid arg for hellotime: %s\n", arg);
486 
487 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
488 	bp.ifbrp_hellotime = v;
489 	if (ioctl(s, SIOCBRDGSHT, (caddr_t)&bp) < 0)
490 		err(1, "%s", name);
491 }
492 
493 void
494 bridge_maxaddr(const char *arg, int d)
495 {
496 	struct ifbrparam bp;
497 	unsigned long newsize;
498 	char *endptr;
499 
500 	errno = 0;
501 	newsize = strtoul(arg, &endptr, 0);
502 	if (arg[0] == '\0' || endptr[0] != '\0' || newsize > 0xffffffffUL ||
503 	    (errno == ERANGE && newsize == ULONG_MAX))
504 		errx(1, "invalid arg for maxaddr: %s\n", arg);
505 
506 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
507 	bp.ifbrp_csize = newsize;
508 	if (ioctl(s, SIOCBRDGSCACHE, (caddr_t)&bp) < 0)
509 		err(1, "%s", name);
510 }
511 
512 void
513 bridge_deladdr(const char *addr, int d)
514 {
515 	struct ifbareq ifba;
516 	struct ether_addr *ea;
517 
518 	strlcpy(ifba.ifba_name, name, sizeof(ifba.ifba_name));
519 	ea = ether_aton(addr);
520 	if (ea == NULL)
521 		err(1, "Invalid address: %s", addr);
522 
523 	bcopy(ea, &ifba.ifba_dst, sizeof(struct ether_addr));
524 
525 	if (ioctl(s, SIOCBRDGDADDR, &ifba) < 0)
526 		err(1, "%s: %s", name, addr);
527 }
528 
529 void
530 bridge_ifprio(const char *ifname, const char *val)
531 {
532 	struct ifbreq breq;
533 	unsigned long v;
534 	char *endptr;
535 
536 	strlcpy(breq.ifbr_name, name, sizeof(breq.ifbr_name));
537 	strlcpy(breq.ifbr_ifsname, ifname, sizeof(breq.ifbr_ifsname));
538 
539 	errno = 0;
540 	v = strtoul(val, &endptr, 0);
541 	if (val[0] == '\0' || endptr[0] != '\0' || v > 0xffUL ||
542 	    (errno == ERANGE && v == ULONG_MAX))
543 		err(1, "invalid arg for ifpriority: %s\n", val);
544 	breq.ifbr_priority = v;
545 
546 	if (ioctl(s, SIOCBRDGSIFPRIO, (caddr_t)&breq) < 0)
547 		err(1, "%s: %s", name, val);
548 }
549 
550 void
551 bridge_ifcost(const char *ifname, const char *val)
552 {
553 	struct ifbreq breq;
554 	unsigned long v;
555 	char *endptr;
556 
557 	strlcpy(breq.ifbr_name, name, sizeof(breq.ifbr_name));
558 	strlcpy(breq.ifbr_ifsname, ifname, sizeof(breq.ifbr_ifsname));
559 
560 	errno = 0;
561 	v = strtoul(val, &endptr, 0);
562 	if (val[0] == '\0' || endptr[0] != '\0' ||
563 	    v < 0 || v > 0xffffffffUL ||
564 	    (errno == ERANGE && v == ULONG_MAX))
565 		errx(1, "invalid arg for ifcost: %s\n", val);
566 
567 	breq.ifbr_path_cost = v;
568 
569 	if (ioctl(s, SIOCBRDGSIFCOST, (caddr_t)&breq) < 0)
570 		err(1, "%s: %s", name, val);
571 }
572 
573 void
574 bridge_noifcost(const char *ifname, int d)
575 {
576 	struct ifbreq breq;
577 
578 	strlcpy(breq.ifbr_name, name, sizeof(breq.ifbr_name));
579 	strlcpy(breq.ifbr_ifsname, ifname, sizeof(breq.ifbr_ifsname));
580 
581 	breq.ifbr_path_cost = 0;
582 
583 	if (ioctl(s, SIOCBRDGSIFCOST, (caddr_t)&breq) < 0)
584 		err(1, "%s", name);
585 }
586 
587 void
588 bridge_addaddr(const char *ifname, const char *addr)
589 {
590 	struct ifbareq ifba;
591 	struct ether_addr *ea;
592 
593 	strlcpy(ifba.ifba_name, name, sizeof(ifba.ifba_name));
594 	strlcpy(ifba.ifba_ifsname, ifname, sizeof(ifba.ifba_ifsname));
595 
596 	ea = ether_aton(addr);
597 	if (ea == NULL)
598 		errx(1, "Invalid address: %s", addr);
599 
600 	bcopy(ea, &ifba.ifba_dst, sizeof(struct ether_addr));
601 	ifba.ifba_flags = IFBAF_STATIC;
602 
603 	if (ioctl(s, SIOCBRDGSADDR, &ifba) < 0)
604 		err(1, "%s: %s", name, addr);
605 }
606 
607 void
608 bridge_addrs(const char *delim, int d)
609 {
610 	struct ifbaconf ifbac;
611 	struct ifbareq *ifba;
612 	char *inbuf = NULL, buf[sizeof(ifba->ifba_ifsname) + 1], *inb;
613 	int i, len = 8192;
614 
615 	/* ifconfig will call us with the argv of the command */
616 	if (strcmp(delim, "addr") == 0)
617 		delim = "";
618 
619 	while (1) {
620 		ifbac.ifbac_len = len;
621 		inb = realloc(inbuf, len);
622 		if (inb == NULL)
623 			err(1, "malloc");
624 		ifbac.ifbac_buf = inbuf = inb;
625 		strlcpy(ifbac.ifbac_name, name, sizeof(ifbac.ifbac_name));
626 		if (ioctl(s, SIOCBRDGRTS, &ifbac) < 0) {
627 			if (errno == ENETDOWN)
628 				return;
629 			err(1, "%s", name);
630 		}
631 		if (ifbac.ifbac_len + sizeof(*ifba) < len)
632 			break;
633 		len *= 2;
634 	}
635 
636 	for (i = 0; i < ifbac.ifbac_len / sizeof(*ifba); i++) {
637 		ifba = ifbac.ifbac_req + i;
638 		strlcpy(buf, ifba->ifba_ifsname, sizeof(buf));
639 		printf("%s%s %s %u ", delim, ether_ntoa(&ifba->ifba_dst),
640 		    buf, ifba->ifba_age);
641 		printb("flags", ifba->ifba_flags, IFBAFBITS);
642 		printf("\n");
643 	}
644 	free(inbuf);
645 }
646 
647 void
648 bridge_holdcnt(const char *value, int d)
649 {
650 	struct ifbrparam bp;
651 	const char *errstr;
652 
653 	bp.ifbrp_txhc = strtonum(value, 0, UINT8_MAX, &errstr);
654 	if (errstr)
655 		err(1, "holdcnt %s %s", value, errstr);
656 
657 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
658 	if (ioctl(s, SIOCBRDGSTXHC, (caddr_t)&bp) < 0)
659 		err(1, "%s", name);
660 }
661 
662 /*
663  * Check to make sure 'brdg' is really a bridge interface.
664  */
665 int
666 is_bridge(char *brdg)
667 {
668 	struct ifreq ifr;
669 	struct ifbaconf ifbac;
670 
671 	strlcpy(ifr.ifr_name, brdg, sizeof(ifr.ifr_name));
672 
673 	if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0)
674 		return (0);
675 
676 	ifbac.ifbac_len = 0;
677 	strlcpy(ifbac.ifbac_name, brdg, sizeof(ifbac.ifbac_name));
678 	if (ioctl(s, SIOCBRDGRTS, (caddr_t)&ifbac) < 0) {
679 		if (errno == ENETDOWN)
680 			return (1);
681 		return (0);
682 	}
683 	return (1);
684 }
685 
686 void
687 bridge_status(void)
688 {
689 	struct ifreq ifr;
690 	struct ifbrparam bp1, bp2;
691 
692 	if (!is_bridge(name))
693 		return;
694 
695 	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
696 	if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0)
697 		return;
698 
699 	bridge_cfg("\t");
700 
701 	bridge_list("\t");
702 
703 	if (aflag && !ifaliases)
704 		return;
705 
706 	strlcpy(bp1.ifbrp_name, name, sizeof(bp1.ifbrp_name));
707 	if (ioctl(s, SIOCBRDGGCACHE, (caddr_t)&bp1) < 0)
708 		return;
709 
710 	strlcpy(bp2.ifbrp_name, name, sizeof(bp2.ifbrp_name));
711 	if (ioctl(s, SIOCBRDGGTO, (caddr_t)&bp2) < 0)
712 		return;
713 
714 	printf("\tAddresses (max cache: %u, timeout: %u):\n",
715 	    bp1.ifbrp_csize, bp2.ifbrp_ctime);
716 
717 	bridge_addrs("\t\t", 0);
718 }
719 
720 void
721 bridge_flushrule(const char *ifname, int d)
722 {
723 	struct ifbrlreq req;
724 
725 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
726 	strlcpy(req.ifbr_ifsname, ifname, sizeof(req.ifbr_ifsname));
727 	if (ioctl(s, SIOCBRDGFRL, &req) < 0)
728 		err(1, "%s: %s", name, ifname);
729 }
730 
731 void
732 bridge_rules(const char *ifname, int d)
733 {
734 	char *inbuf = NULL, *inb;
735 	struct ifbrlconf ifc;
736 	struct ifbrlreq *ifrp;
737 	int len = 8192, i;
738 
739 	while (1) {
740 		ifc.ifbrl_len = len;
741 		inb = realloc(inbuf, len);
742 		if (inb == NULL)
743 			err(1, "malloc");
744 		ifc.ifbrl_buf = inbuf = inb;
745 		strlcpy(ifc.ifbrl_name, name, sizeof(ifc.ifbrl_name));
746 		strlcpy(ifc.ifbrl_ifsname, ifname, sizeof(ifc.ifbrl_ifsname));
747 		if (ioctl(s, SIOCBRDGGRL, &ifc) < 0)
748 			err(1, "ioctl(SIOCBRDGGRL)");
749 		if (ifc.ifbrl_len + sizeof(*ifrp) < len)
750 			break;
751 		len *= 2;
752 	}
753 	ifrp = ifc.ifbrl_req;
754 	for (i = 0; i < ifc.ifbrl_len; i += sizeof(*ifrp)) {
755 		ifrp = (struct ifbrlreq *)((caddr_t)ifc.ifbrl_req + i);
756 		bridge_showrule(ifrp);
757 	}
758 }
759 
760 void
761 bridge_showrule(struct ifbrlreq *r)
762 {
763 	if (r->ifbr_action == BRL_ACTION_BLOCK)
764 		printf("block ");
765 	else if (r->ifbr_action == BRL_ACTION_PASS)
766 		printf("pass ");
767 	else
768 		printf("[neither block nor pass?]\n");
769 
770 	if ((r->ifbr_flags & (BRL_FLAG_IN | BRL_FLAG_OUT)) ==
771 	    (BRL_FLAG_IN | BRL_FLAG_OUT))
772 		printf("in/out ");
773 	else if (r->ifbr_flags & BRL_FLAG_IN)
774 		printf("in ");
775 	else if (r->ifbr_flags & BRL_FLAG_OUT)
776 		printf("out ");
777 	else
778 		printf("[neither in nor out?]\n");
779 
780 	printf("on %s", r->ifbr_ifsname);
781 
782 	if (r->ifbr_flags & BRL_FLAG_SRCVALID)
783 		printf(" src %s", ether_ntoa(&r->ifbr_src));
784 	if (r->ifbr_flags & BRL_FLAG_DSTVALID)
785 		printf(" dst %s", ether_ntoa(&r->ifbr_dst));
786 	if (r->ifbr_tagname[0])
787 		printf(" tag %s", r->ifbr_tagname);
788 
789 	printf("\n");
790 }
791 
792 /*
793  * Parse a rule definition and send it upwards.
794  *
795  * Syntax:
796  *	{block|pass} {in|out|in/out} on {ifs} [src {mac}] [dst {mac}]
797  */
798 int
799 bridge_rule(int targc, char **targv, int ln)
800 {
801 	char **argv = targv;
802 	int argc = targc;
803 	struct ifbrlreq rule;
804 	struct ether_addr *ea, *dea;
805 
806 	if (argc == 0) {
807 		warnx("invalid rule\n");
808 		return (1);
809 	}
810 	rule.ifbr_tagname[0] = 0;
811 	rule.ifbr_flags = 0;
812 	rule.ifbr_action = 0;
813 	strlcpy(rule.ifbr_name, name, sizeof(rule.ifbr_name));
814 
815 	if (strcmp(argv[0], "block") == 0)
816 		rule.ifbr_action = BRL_ACTION_BLOCK;
817 	else if (strcmp(argv[0], "pass") == 0)
818 		rule.ifbr_action = BRL_ACTION_PASS;
819 	else
820 		goto bad_rule;
821 	argc--;	argv++;
822 
823 	if (argc == 0) {
824 		bridge_badrule(targc, targv, ln);
825 		return (1);
826 	}
827 	if (strcmp(argv[0], "in") == 0)
828 		rule.ifbr_flags |= BRL_FLAG_IN;
829 	else if (strcmp(argv[0], "out") == 0)
830 		rule.ifbr_flags |= BRL_FLAG_OUT;
831 	else if (strcmp(argv[0], "in/out") == 0)
832 		rule.ifbr_flags |= BRL_FLAG_IN | BRL_FLAG_OUT;
833 	else if (strcmp(argv[0], "on") == 0) {
834 		rule.ifbr_flags |= BRL_FLAG_IN | BRL_FLAG_OUT;
835 		argc++; argv--;
836 	} else
837 		goto bad_rule;
838 	argc--; argv++;
839 
840 	if (argc == 0 || strcmp(argv[0], "on"))
841 		goto bad_rule;
842 	argc--; argv++;
843 
844 	if (argc == 0)
845 		goto bad_rule;
846 	strlcpy(rule.ifbr_ifsname, argv[0], sizeof(rule.ifbr_ifsname));
847 	argc--; argv++;
848 
849 	while (argc) {
850 		if (strcmp(argv[0], "dst") == 0) {
851 			if (rule.ifbr_flags & BRL_FLAG_DSTVALID)
852 				goto bad_rule;
853 			rule.ifbr_flags |= BRL_FLAG_DSTVALID;
854 			dea = &rule.ifbr_dst;
855 		} else if (strcmp(argv[0], "src") == 0) {
856 			if (rule.ifbr_flags & BRL_FLAG_SRCVALID)
857 				goto bad_rule;
858 			rule.ifbr_flags |= BRL_FLAG_SRCVALID;
859 			dea = &rule.ifbr_src;
860 		} else if (strcmp(argv[0], "tag") == 0) {
861 			if (argc < 2) {
862 				warnx("missing tag name\n");
863 				goto bad_rule;
864 			}
865 			if (rule.ifbr_tagname[0]) {
866 				warnx("tag already defined\n");
867 				goto bad_rule;
868 			}
869 			if (strlcpy(rule.ifbr_tagname, argv[1],
870 			    PF_TAG_NAME_SIZE) > PF_TAG_NAME_SIZE) {
871 				warnx("tag name '%s' too long\n", argv[1]);
872 				goto bad_rule;
873 			}
874 			dea = NULL;
875 		} else
876 			goto bad_rule;
877 
878 		argc--; argv++;
879 
880 		if (argc == 0)
881 			goto bad_rule;
882 		if (dea != NULL) {
883 			ea = ether_aton(argv[0]);
884 			if (ea == NULL) {
885 				warnx("invalid address: %s", argv[0]);
886 				return (1);
887 			}
888 			bcopy(ea, dea, sizeof(*dea));
889 		}
890 		argc--; argv++;
891 	}
892 
893 	if (ioctl(s, SIOCBRDGARL, &rule) < 0) {
894 		warn("%s", name);
895 		return (1);
896 	}
897 	return (0);
898 
899 bad_rule:
900 	bridge_badrule(targc, targv, ln);
901 	return (1);
902 }
903 
904 #define MAXRULEWORDS 8
905 
906 void
907 bridge_rulefile(const char *fname, int d)
908 {
909 	FILE *f;
910 	char *str, *argv[MAXRULEWORDS], buf[1024];
911 	int ln = 0, argc = 0;
912 
913 	f = fopen(fname, "r");
914 	if (f == NULL)
915 		err(1, "%s", fname);
916 
917 	while (fgets(buf, sizeof(buf), f) != NULL) {
918 		ln++;
919 		if (buf[0] == '#' || buf[0] == '\n')
920 			continue;
921 
922 		argc = 0;
923 		str = strtok(buf, "\n\t\r ");
924 		while (str != NULL && argc < MAXRULEWORDS) {
925 			argv[argc++] = str;
926 			str = strtok(NULL, "\n\t\r ");
927 		}
928 
929 		/* Rule is too long if there's more. */
930 		if (str != NULL) {
931 			warnx("invalid rule: %d: %s ...\n", ln, buf);
932 			continue;
933 		}
934 
935 		bridge_rule(argc, argv, ln);
936 	}
937 	fclose(f);
938 }
939 
940 void
941 bridge_badrule(int argc, char *argv[], int ln)
942 {
943 	extern const char *__progname;
944 	int i;
945 
946 	fprintf(stderr, "%s: invalid rule: ", __progname);
947 	if (ln != -1)
948 		fprintf(stderr, "%d: ", ln);
949 	for (i = 0; i < argc; i++)
950 		fprintf(stderr, "%s ", argv[i]);
951 	fprintf(stderr, "\n");
952 }
953 
954 #endif
955