xref: /openbsd-src/sbin/ifconfig/brconfig.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: brconfig.c,v 1.11 2016/09/03 17:13:48 chl 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/socket.h>
37 #include <sys/ioctl.h>
38 #include <net/if.h>
39 #include <net/if_dl.h>
40 #include <netinet/in.h>
41 #include <netinet/if_ether.h>
42 #include <net/if_bridge.h>
43 #include <netdb.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 int is_switch(char *);
60 
61 #define	IFBAFBITS	"\020\1STATIC"
62 #define	IFBIFBITS	\
63 "\020\1LEARNING\2DISCOVER\3BLOCKNONIP\4STP\5EDGE\6AUTOEDGE\7PTP\10AUTOPTP\11SPAN\15LOCAL"
64 
65 #define	PV2ID(pv, epri, eaddr)	do {					\
66 	epri	 = pv >> 48;						\
67 	eaddr[0] = pv >> 40;						\
68 	eaddr[1] = pv >> 32;						\
69 	eaddr[2] = pv >> 24;						\
70 	eaddr[3] = pv >> 16;						\
71 	eaddr[4] = pv >> 8;						\
72 	eaddr[5] = pv >> 0;						\
73 } while (0)
74 
75 char *stpstates[] = {
76 	"disabled",
77 	"listening",
78 	"learning",
79 	"forwarding",
80 	"blocking",
81 	"discarding"
82 };
83 char *stpproto[] = {
84 	"stp",
85 	"(none)",
86 	"rstp",
87 };
88 char *stproles[] = {
89 	"disabled",
90 	"root",
91 	"designated",
92 	"alternate",
93 	"backup"
94 };
95 
96 
97 void
98 setdiscover(const char *val, int d)
99 {
100 	bridge_ifsetflag(val, IFBIF_DISCOVER);
101 }
102 
103 void
104 unsetdiscover(const char *val, int d)
105 {
106 	bridge_ifclrflag(val, IFBIF_DISCOVER);
107 }
108 
109 void
110 setblocknonip(const char *val, int d)
111 {
112 	bridge_ifsetflag(val, IFBIF_BLOCKNONIP);
113 }
114 
115 void
116 unsetblocknonip(const char *val, int d)
117 {
118 	bridge_ifclrflag(val, IFBIF_BLOCKNONIP);
119 }
120 
121 void
122 setlearn(const char *val, int d)
123 {
124 	bridge_ifsetflag(val, IFBIF_LEARNING);
125 }
126 
127 void
128 unsetlearn(const char *val, int d)
129 {
130 	bridge_ifclrflag(val, IFBIF_LEARNING);
131 }
132 
133 void
134 setstp(const char *val, int d)
135 {
136 	bridge_ifsetflag(val, IFBIF_STP);
137 }
138 
139 void
140 unsetstp(const char *val, int d)
141 {
142 	bridge_ifclrflag(val, IFBIF_STP);
143 }
144 
145 void
146 setedge(const char *val, int d)
147 {
148 	bridge_ifsetflag(val, IFBIF_BSTP_EDGE);
149 }
150 
151 void
152 unsetedge(const char *val, int d)
153 {
154 	bridge_ifclrflag(val, IFBIF_BSTP_EDGE);
155 }
156 
157 void
158 setautoedge(const char *val, int d)
159 {
160 	bridge_ifsetflag(val, IFBIF_BSTP_AUTOEDGE);
161 }
162 
163 void
164 unsetautoedge(const char *val, int d)
165 {
166 	bridge_ifclrflag(val, IFBIF_BSTP_AUTOEDGE);
167 }
168 
169 void
170 setptp(const char *val, int d)
171 {
172 	bridge_ifsetflag(val, IFBIF_BSTP_PTP);
173 }
174 
175 void
176 unsetptp(const char *val, int d)
177 {
178 	bridge_ifclrflag(val, IFBIF_BSTP_PTP);
179 }
180 
181 void
182 setautoptp(const char *val, int d)
183 {
184 	bridge_ifsetflag(val, IFBIF_BSTP_AUTOPTP);
185 }
186 
187 void
188 unsetautoptp(const char *val, int d)
189 {
190 	bridge_ifclrflag(val, IFBIF_BSTP_AUTOPTP);
191 }
192 
193 void
194 addlocal(const char *ifsname, int d)
195 {
196 	struct ifbreq breq;
197 
198 	if (strncmp(ifsname, "vether", (sizeof("vether") - 1)) != 0)
199 		errx(1, "only vether can be local interface");
200 
201 	/* Add local */
202 	strlcpy(breq.ifbr_name, name, sizeof(breq.ifbr_name));
203 	strlcpy(breq.ifbr_ifsname, ifsname, sizeof(breq.ifbr_ifsname));
204 	if (ioctl(s, SIOCBRDGADDL, (caddr_t)&breq) < 0) {
205 		if (errno == EEXIST)
206 			errx(1, "%s: local port exists already", name);
207 		else
208 			err(1, "%s: ioctl SIOCBRDGADDL %s", name, ifsname);
209 	}
210 }
211 
212 void
213 bridge_ifsetflag(const char *ifsname, u_int32_t flag)
214 {
215 	struct ifbreq req;
216 
217 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
218 	strlcpy(req.ifbr_ifsname, ifsname, sizeof(req.ifbr_ifsname));
219 	if (ioctl(s, SIOCBRDGGIFFLGS, (caddr_t)&req) < 0)
220 		err(1, "%s: ioctl SIOCBRDGGIFFLGS %s", name, ifsname);
221 
222 	req.ifbr_ifsflags |= flag & ~IFBIF_RO_MASK;
223 
224 	if (ioctl(s, SIOCBRDGSIFFLGS, (caddr_t)&req) < 0)
225 		err(1, "%s: ioctl SIOCBRDGSIFFLGS %s", name, ifsname);
226 }
227 
228 void
229 bridge_ifclrflag(const char *ifsname, u_int32_t flag)
230 {
231 	struct ifbreq req;
232 
233 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
234 	strlcpy(req.ifbr_ifsname, ifsname, sizeof(req.ifbr_ifsname));
235 
236 	if (ioctl(s, SIOCBRDGGIFFLGS, (caddr_t)&req) < 0)
237 		err(1, "%s: ioctl SIOCBRDGGIFFLGS %s", name, ifsname);
238 
239 	req.ifbr_ifsflags &= ~(flag | IFBIF_RO_MASK);
240 
241 	if (ioctl(s, SIOCBRDGSIFFLGS, (caddr_t)&req) < 0)
242 		err(1, "%s: ioctl SIOCBRDGSIFFLGS %s", name, ifsname);
243 }
244 
245 void
246 bridge_flushall(const char *val, int p)
247 {
248 	struct ifbreq req;
249 
250 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
251 	req.ifbr_ifsflags = IFBF_FLUSHALL;
252 	if (ioctl(s, SIOCBRDGFLUSH, &req) < 0)
253 		err(1, "%s", name);
254 }
255 
256 void
257 bridge_flush(const char *val, int p)
258 {
259 	struct ifbreq req;
260 
261 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
262 	req.ifbr_ifsflags = IFBF_FLUSHDYN;
263 	if (ioctl(s, SIOCBRDGFLUSH, &req) < 0)
264 		err(1, "%s", name);
265 }
266 
267 void
268 bridge_cfg(const char *delim)
269 {
270 	struct ifbropreq ifbp;
271 	u_int16_t pri;
272 	u_int8_t ht, fd, ma, hc, proto;
273 	u_int8_t lladdr[ETHER_ADDR_LEN];
274 	u_int16_t bprio;
275 
276 	strlcpy(ifbp.ifbop_name, name, sizeof(ifbp.ifbop_name));
277 	if (ioctl(s, SIOCBRDGGPARAM, (caddr_t)&ifbp))
278 		err(1, "%s", name);
279 	printf("%s", delim);
280 	pri = ifbp.ifbop_priority;
281 	ht = ifbp.ifbop_hellotime;
282 	fd = ifbp.ifbop_fwddelay;
283 	ma = ifbp.ifbop_maxage;
284 	hc = ifbp.ifbop_holdcount;
285 	proto = ifbp.ifbop_protocol;
286 
287 	printf("priority %u hellotime %u fwddelay %u maxage %u "
288 	    "holdcnt %u proto %s\n", pri, ht, fd, ma, hc, stpproto[proto]);
289 
290 	if (aflag)
291 		return;
292 
293 	PV2ID(ifbp.ifbop_desg_bridge, bprio, lladdr);
294 	printf("\tdesignated: id %s priority %u\n",
295 	    ether_ntoa((struct ether_addr *)lladdr), bprio);
296 
297 	if (ifbp.ifbop_root_bridge == ifbp.ifbop_desg_bridge)
298 		return;
299 
300 	PV2ID(ifbp.ifbop_root_bridge, bprio, lladdr);
301 	printf("\troot: id %s priority %u ifcost %u port %u\n",
302 	    ether_ntoa((struct ether_addr *)lladdr), bprio,
303 	    ifbp.ifbop_root_path_cost, ifbp.ifbop_root_port & 0xfff);
304 }
305 
306 void
307 bridge_list(char *delim)
308 {
309 	struct ifbreq *reqp;
310 	struct ifbifconf bifc;
311 	int i, len = 8192;
312 	char buf[sizeof(reqp->ifbr_ifsname) + 1], *inbuf = NULL, *inb;
313 
314 	while (1) {
315 		bifc.ifbic_len = len;
316 		inb = realloc(inbuf, len);
317 		if (inb == NULL)
318 			err(1, "malloc");
319 		bifc.ifbic_buf = inbuf = inb;
320 		strlcpy(bifc.ifbic_name, name, sizeof(bifc.ifbic_name));
321 		if (ioctl(s, SIOCBRDGIFS, &bifc) < 0)
322 			err(1, "%s", name);
323 		if (bifc.ifbic_len + sizeof(*reqp) < len)
324 			break;
325 		len *= 2;
326 	}
327 	for (i = 0; i < bifc.ifbic_len / sizeof(*reqp); i++) {
328 		reqp = bifc.ifbic_req + i;
329 		strlcpy(buf, reqp->ifbr_ifsname, sizeof(buf));
330 		printf("%s%s ", delim, buf);
331 		printb("flags", reqp->ifbr_ifsflags, IFBIFBITS);
332 		printf("\n");
333 		if (reqp->ifbr_ifsflags & IFBIF_SPAN)
334 			continue;
335 		printf("\t\t");
336 		printf("port %u ifpriority %u ifcost %u",
337 		    reqp->ifbr_portno, reqp->ifbr_priority,
338 		    reqp->ifbr_path_cost);
339 		if (reqp->ifbr_ifsflags & IFBIF_STP)
340 			printf(" %s role %s",
341 			    stpstates[reqp->ifbr_state],
342 			    stproles[reqp->ifbr_role]);
343 		printf("\n");
344 		bridge_rules(buf, 1);
345 	}
346 	free(bifc.ifbic_buf);
347 }
348 
349 void
350 bridge_add(const char *ifn, int d)
351 {
352 	struct ifbreq req;
353 
354 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
355 	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
356 	if (ioctl(s, SIOCBRDGADD, &req) < 0) {
357 		if (errno == EEXIST)
358 			return;
359 		err(1, "%s: %s", name, ifn);
360 	}
361 }
362 
363 void
364 bridge_delete(const char *ifn, int d)
365 {
366 	struct ifbreq req;
367 
368 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
369 	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
370 	if (ioctl(s, SIOCBRDGDEL, &req) < 0)
371 		err(1, "%s: %s", name, ifn);
372 }
373 
374 void
375 bridge_addspan(const char *ifn, int d)
376 {
377 	struct ifbreq req;
378 
379 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
380 	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
381 	if (ioctl(s, SIOCBRDGADDS, &req) < 0) {
382 		if (errno == EEXIST)
383 			return;
384 		err(1, "%s: %s", name, ifn);
385 	}
386 }
387 
388 void
389 bridge_delspan(const char *ifn, int d)
390 {
391 	struct ifbreq req;
392 
393 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
394 	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
395 	if (ioctl(s, SIOCBRDGDELS, &req) < 0)
396 		err(1, "%s: %s", name, ifn);
397 }
398 
399 void
400 bridge_timeout(const char *arg, int d)
401 {
402 	struct ifbrparam bp;
403 	long newtime;
404 	char *endptr;
405 
406 	errno = 0;
407 	newtime = strtol(arg, &endptr, 0);
408 	if (arg[0] == '\0' || endptr[0] != '\0' ||
409 	    (newtime & ~INT_MAX) != 0L ||
410 	    (errno == ERANGE && newtime == LONG_MAX))
411 		errx(1, "invalid arg for timeout: %s", arg);
412 
413 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
414 	bp.ifbrp_ctime = newtime;
415 	if (ioctl(s, SIOCBRDGSTO, (caddr_t)&bp) < 0)
416 		err(1, "%s", name);
417 }
418 
419 void
420 bridge_maxage(const char *arg, int d)
421 {
422 	struct ifbrparam bp;
423 	unsigned long v;
424 	char *endptr;
425 
426 	errno = 0;
427 	v = strtoul(arg, &endptr, 0);
428 	if (arg[0] == '\0' || endptr[0] != '\0' || v > 0xffUL ||
429 	    (errno == ERANGE && v == ULONG_MAX))
430 		errx(1, "invalid arg for maxage: %s", arg);
431 
432 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
433 	bp.ifbrp_maxage = v;
434 	if (ioctl(s, SIOCBRDGSMA, (caddr_t)&bp) < 0)
435 		err(1, "%s", name);
436 }
437 
438 void
439 bridge_priority(const char *arg, int d)
440 {
441 	struct ifbrparam bp;
442 	unsigned long v;
443 	char *endptr;
444 
445 	errno = 0;
446 	v = strtoul(arg, &endptr, 0);
447 	if (arg[0] == '\0' || endptr[0] != '\0' || v > 0xffffUL ||
448 	    (errno == ERANGE && v == ULONG_MAX))
449 		errx(1, "invalid arg for spanpriority: %s", arg);
450 
451 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
452 	bp.ifbrp_prio = v;
453 	if (ioctl(s, SIOCBRDGSPRI, (caddr_t)&bp) < 0)
454 		err(1, "%s", name);
455 }
456 
457 void
458 bridge_proto(const char *arg, int d)
459 {
460 	struct ifbrparam bp;
461 	int i, proto = -1;
462 
463 	for (i = 0; i <= BSTP_PROTO_MAX; i++)
464 		if (strcmp(arg, stpproto[i]) == 0) {
465 			proto = i;
466 			break;
467 		}
468 	if (proto == -1)
469 		errx(1, "invalid arg for proto: %s", arg);
470 
471 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
472 	bp.ifbrp_prio = proto;
473 	if (ioctl(s, SIOCBRDGSPROTO, (caddr_t)&bp) < 0)
474 		err(1, "%s", name);
475 }
476 
477 void
478 bridge_fwddelay(const char *arg, int d)
479 {
480 	struct ifbrparam bp;
481 	unsigned long v;
482 	char *endptr;
483 
484 	errno = 0;
485 	v = strtoul(arg, &endptr, 0);
486 	if (arg[0] == '\0' || endptr[0] != '\0' || v > 0xffUL ||
487 	    (errno == ERANGE && v == ULONG_MAX))
488 		errx(1, "invalid arg for fwddelay: %s", arg);
489 
490 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
491 	bp.ifbrp_fwddelay = v;
492 	if (ioctl(s, SIOCBRDGSFD, (caddr_t)&bp) < 0)
493 		err(1, "%s", name);
494 }
495 
496 void
497 bridge_hellotime(const char *arg, int d)
498 {
499 	struct ifbrparam bp;
500 	unsigned long v;
501 	char *endptr;
502 
503 	errno = 0;
504 	v = strtoul(arg, &endptr, 0);
505 	if (arg[0] == '\0' || endptr[0] != '\0' || v > 0xffUL ||
506 	    (errno == ERANGE && v == ULONG_MAX))
507 		errx(1, "invalid arg for hellotime: %s", arg);
508 
509 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
510 	bp.ifbrp_hellotime = v;
511 	if (ioctl(s, SIOCBRDGSHT, (caddr_t)&bp) < 0)
512 		err(1, "%s", name);
513 }
514 
515 void
516 bridge_maxaddr(const char *arg, int d)
517 {
518 	struct ifbrparam bp;
519 	unsigned long newsize;
520 	char *endptr;
521 
522 	errno = 0;
523 	newsize = strtoul(arg, &endptr, 0);
524 	if (arg[0] == '\0' || endptr[0] != '\0' || newsize > 0xffffffffUL ||
525 	    (errno == ERANGE && newsize == ULONG_MAX))
526 		errx(1, "invalid arg for maxaddr: %s", arg);
527 
528 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
529 	bp.ifbrp_csize = newsize;
530 	if (ioctl(s, SIOCBRDGSCACHE, (caddr_t)&bp) < 0)
531 		err(1, "%s", name);
532 }
533 
534 void
535 bridge_deladdr(const char *addr, int d)
536 {
537 	struct ifbareq ifba;
538 	struct ether_addr *ea;
539 
540 	strlcpy(ifba.ifba_name, name, sizeof(ifba.ifba_name));
541 	ea = ether_aton(addr);
542 	if (ea == NULL)
543 		err(1, "Invalid address: %s", addr);
544 
545 	bcopy(ea, &ifba.ifba_dst, sizeof(struct ether_addr));
546 
547 	if (ioctl(s, SIOCBRDGDADDR, &ifba) < 0)
548 		err(1, "%s: %s", name, addr);
549 }
550 
551 void
552 bridge_ifprio(const char *ifname, const char *val)
553 {
554 	struct ifbreq breq;
555 	unsigned long v;
556 	char *endptr;
557 
558 	strlcpy(breq.ifbr_name, name, sizeof(breq.ifbr_name));
559 	strlcpy(breq.ifbr_ifsname, ifname, sizeof(breq.ifbr_ifsname));
560 
561 	errno = 0;
562 	v = strtoul(val, &endptr, 0);
563 	if (val[0] == '\0' || endptr[0] != '\0' || v > 0xffUL ||
564 	    (errno == ERANGE && v == ULONG_MAX))
565 		err(1, "invalid arg for ifpriority: %s", val);
566 	breq.ifbr_priority = v;
567 
568 	if (ioctl(s, SIOCBRDGSIFPRIO, (caddr_t)&breq) < 0)
569 		err(1, "%s: %s", name, val);
570 }
571 
572 void
573 bridge_ifcost(const char *ifname, const char *val)
574 {
575 	struct ifbreq breq;
576 	unsigned long v;
577 	char *endptr;
578 
579 	strlcpy(breq.ifbr_name, name, sizeof(breq.ifbr_name));
580 	strlcpy(breq.ifbr_ifsname, ifname, sizeof(breq.ifbr_ifsname));
581 
582 	errno = 0;
583 	v = strtoul(val, &endptr, 0);
584 	if (val[0] == '\0' || endptr[0] != '\0' ||
585 	    v < 0 || v > 0xffffffffUL ||
586 	    (errno == ERANGE && v == ULONG_MAX))
587 		errx(1, "invalid arg for ifcost: %s", val);
588 
589 	breq.ifbr_path_cost = v;
590 
591 	if (ioctl(s, SIOCBRDGSIFCOST, (caddr_t)&breq) < 0)
592 		err(1, "%s: %s", name, val);
593 }
594 
595 void
596 bridge_noifcost(const char *ifname, int d)
597 {
598 	struct ifbreq breq;
599 
600 	strlcpy(breq.ifbr_name, name, sizeof(breq.ifbr_name));
601 	strlcpy(breq.ifbr_ifsname, ifname, sizeof(breq.ifbr_ifsname));
602 
603 	breq.ifbr_path_cost = 0;
604 
605 	if (ioctl(s, SIOCBRDGSIFCOST, (caddr_t)&breq) < 0)
606 		err(1, "%s", name);
607 }
608 
609 void
610 bridge_addaddr(const char *ifname, const char *addr)
611 {
612 	struct ifbareq ifba;
613 	struct ether_addr *ea;
614 
615 	strlcpy(ifba.ifba_name, name, sizeof(ifba.ifba_name));
616 	strlcpy(ifba.ifba_ifsname, ifname, sizeof(ifba.ifba_ifsname));
617 
618 	ea = ether_aton(addr);
619 	if (ea == NULL)
620 		errx(1, "Invalid address: %s", addr);
621 
622 	bcopy(ea, &ifba.ifba_dst, sizeof(struct ether_addr));
623 	ifba.ifba_flags = IFBAF_STATIC;
624 
625 	if (ioctl(s, SIOCBRDGSADDR, &ifba) < 0)
626 		err(1, "%s: %s", name, addr);
627 }
628 
629 void
630 bridge_addrs(const char *delim, int d)
631 {
632 	char dstaddr[NI_MAXHOST];
633 	char dstport[NI_MAXSERV];
634 	const int niflag = NI_NUMERICHOST|NI_DGRAM;
635 	struct ifbaconf ifbac;
636 	struct ifbareq *ifba;
637 	char *inbuf = NULL, buf[sizeof(ifba->ifba_ifsname) + 1], *inb;
638 	struct sockaddr *sa;
639 	int i, len = 8192;
640 
641 	/* ifconfig will call us with the argv of the command */
642 	if (strcmp(delim, "addr") == 0)
643 		delim = "";
644 
645 	while (1) {
646 		ifbac.ifbac_len = len;
647 		inb = realloc(inbuf, len);
648 		if (inb == NULL)
649 			err(1, "malloc");
650 		ifbac.ifbac_buf = inbuf = inb;
651 		strlcpy(ifbac.ifbac_name, name, sizeof(ifbac.ifbac_name));
652 		if (ioctl(s, SIOCBRDGRTS, &ifbac) < 0) {
653 			if (errno == ENETDOWN)
654 				return;
655 			err(1, "%s", name);
656 		}
657 		if (ifbac.ifbac_len + sizeof(*ifba) < len)
658 			break;
659 		len *= 2;
660 	}
661 
662 	for (i = 0; i < ifbac.ifbac_len / sizeof(*ifba); i++) {
663 		ifba = ifbac.ifbac_req + i;
664 		strlcpy(buf, ifba->ifba_ifsname, sizeof(buf));
665 		printf("%s%s %s %u ", delim, ether_ntoa(&ifba->ifba_dst),
666 		    buf, ifba->ifba_age);
667 		sa = (struct sockaddr *)&ifba->ifba_dstsa;
668 		printb("flags", ifba->ifba_flags, IFBAFBITS);
669 		if (sa->sa_family != AF_UNSPEC &&
670 		    getnameinfo(sa, sa->sa_len,
671 		    dstaddr, sizeof(dstaddr),
672 		    dstport, sizeof(dstport), niflag) == 0)
673 			printf(" tunnel %s:%s", dstaddr, dstport);
674 		printf("\n");
675 	}
676 	free(inbuf);
677 }
678 
679 void
680 bridge_holdcnt(const char *value, int d)
681 {
682 	struct ifbrparam bp;
683 	const char *errstr;
684 
685 	bp.ifbrp_txhc = strtonum(value, 0, UINT8_MAX, &errstr);
686 	if (errstr)
687 		err(1, "holdcnt %s %s", value, errstr);
688 
689 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
690 	if (ioctl(s, SIOCBRDGSTXHC, (caddr_t)&bp) < 0)
691 		err(1, "%s", name);
692 }
693 
694 /*
695  * Check to make sure 'brdg' is really a bridge interface.
696  */
697 int
698 is_bridge(char *brdg)
699 {
700 	struct ifreq ifr;
701 	struct ifbaconf ifbac;
702 
703 	strlcpy(ifr.ifr_name, brdg, sizeof(ifr.ifr_name));
704 
705 	if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0)
706 		return (0);
707 
708 	ifbac.ifbac_len = 0;
709 	strlcpy(ifbac.ifbac_name, brdg, sizeof(ifbac.ifbac_name));
710 	if (ioctl(s, SIOCBRDGRTS, (caddr_t)&ifbac) < 0) {
711 		if (errno == ENETDOWN)
712 			return (1);
713 		return (0);
714 	}
715 	return (1);
716 }
717 
718 void
719 bridge_status(void)
720 {
721 	struct ifreq ifr;
722 	struct ifbrparam bp1, bp2;
723 
724 	if (!is_bridge(name) || is_switch(name))
725 		return;
726 
727 	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
728 	if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0)
729 		return;
730 
731 	bridge_cfg("\t");
732 
733 	bridge_list("\t");
734 
735 	if (aflag && !ifaliases)
736 		return;
737 
738 	strlcpy(bp1.ifbrp_name, name, sizeof(bp1.ifbrp_name));
739 	if (ioctl(s, SIOCBRDGGCACHE, (caddr_t)&bp1) < 0)
740 		return;
741 
742 	strlcpy(bp2.ifbrp_name, name, sizeof(bp2.ifbrp_name));
743 	if (ioctl(s, SIOCBRDGGTO, (caddr_t)&bp2) < 0)
744 		return;
745 
746 	printf("\tAddresses (max cache: %u, timeout: %u):\n",
747 	    bp1.ifbrp_csize, bp2.ifbrp_ctime);
748 
749 	bridge_addrs("\t\t", 0);
750 }
751 
752 void
753 bridge_flushrule(const char *ifname, int d)
754 {
755 	struct ifbrlreq req;
756 
757 	strlcpy(req.ifbr_name, name, sizeof(req.ifbr_name));
758 	strlcpy(req.ifbr_ifsname, ifname, sizeof(req.ifbr_ifsname));
759 	if (ioctl(s, SIOCBRDGFRL, &req) < 0)
760 		err(1, "%s: %s", name, ifname);
761 }
762 
763 void
764 bridge_rules(const char *ifname, int usetab)
765 {
766 	char *inbuf = NULL, *inb;
767 	struct ifbrlconf ifc;
768 	struct ifbrlreq *ifrp;
769 	int len = 8192, i;
770 
771 	while (1) {
772 		ifc.ifbrl_len = len;
773 		inb = realloc(inbuf, len);
774 		if (inb == NULL)
775 			err(1, "malloc");
776 		ifc.ifbrl_buf = inbuf = inb;
777 		strlcpy(ifc.ifbrl_name, name, sizeof(ifc.ifbrl_name));
778 		strlcpy(ifc.ifbrl_ifsname, ifname, sizeof(ifc.ifbrl_ifsname));
779 		if (ioctl(s, SIOCBRDGGRL, &ifc) < 0)
780 			err(1, "ioctl(SIOCBRDGGRL)");
781 		if (ifc.ifbrl_len + sizeof(*ifrp) < len)
782 			break;
783 		len *= 2;
784 	}
785 	ifrp = ifc.ifbrl_req;
786 	for (i = 0; i < ifc.ifbrl_len; i += sizeof(*ifrp)) {
787 		ifrp = (struct ifbrlreq *)((caddr_t)ifc.ifbrl_req + i);
788 
789 		if (usetab)
790 			printf("\t");
791 
792 		bridge_showrule(ifrp);
793 	}
794 }
795 
796 void
797 bridge_showrule(struct ifbrlreq *r)
798 {
799 	if (r->ifbr_action == BRL_ACTION_BLOCK)
800 		printf("block ");
801 	else if (r->ifbr_action == BRL_ACTION_PASS)
802 		printf("pass ");
803 	else
804 		printf("[neither block nor pass?]\n");
805 
806 	if ((r->ifbr_flags & (BRL_FLAG_IN | BRL_FLAG_OUT)) ==
807 	    (BRL_FLAG_IN | BRL_FLAG_OUT))
808 		printf("in/out ");
809 	else if (r->ifbr_flags & BRL_FLAG_IN)
810 		printf("in ");
811 	else if (r->ifbr_flags & BRL_FLAG_OUT)
812 		printf("out ");
813 	else
814 		printf("[neither in nor out?]\n");
815 
816 	printf("on %s", r->ifbr_ifsname);
817 
818 	if (r->ifbr_flags & BRL_FLAG_SRCVALID)
819 		printf(" src %s", ether_ntoa(&r->ifbr_src));
820 	if (r->ifbr_flags & BRL_FLAG_DSTVALID)
821 		printf(" dst %s", ether_ntoa(&r->ifbr_dst));
822 	if (r->ifbr_tagname[0])
823 		printf(" tag %s", r->ifbr_tagname);
824 
825 	printf("\n");
826 }
827 
828 /*
829  * Parse a rule definition and send it upwards.
830  *
831  * Syntax:
832  *	{block|pass} {in|out|in/out} on {ifs} [src {mac}] [dst {mac}]
833  */
834 int
835 bridge_rule(int targc, char **targv, int ln)
836 {
837 	char **argv = targv;
838 	int argc = targc;
839 	struct ifbrlreq rule;
840 	struct ether_addr *ea, *dea;
841 
842 	if (argc == 0) {
843 		warnx("invalid rule");
844 		return (1);
845 	}
846 	rule.ifbr_tagname[0] = 0;
847 	rule.ifbr_flags = 0;
848 	rule.ifbr_action = 0;
849 	strlcpy(rule.ifbr_name, name, sizeof(rule.ifbr_name));
850 
851 	if (strcmp(argv[0], "block") == 0)
852 		rule.ifbr_action = BRL_ACTION_BLOCK;
853 	else if (strcmp(argv[0], "pass") == 0)
854 		rule.ifbr_action = BRL_ACTION_PASS;
855 	else
856 		goto bad_rule;
857 	argc--;	argv++;
858 
859 	if (argc == 0) {
860 		bridge_badrule(targc, targv, ln);
861 		return (1);
862 	}
863 	if (strcmp(argv[0], "in") == 0)
864 		rule.ifbr_flags |= BRL_FLAG_IN;
865 	else if (strcmp(argv[0], "out") == 0)
866 		rule.ifbr_flags |= BRL_FLAG_OUT;
867 	else if (strcmp(argv[0], "in/out") == 0)
868 		rule.ifbr_flags |= BRL_FLAG_IN | BRL_FLAG_OUT;
869 	else if (strcmp(argv[0], "on") == 0) {
870 		rule.ifbr_flags |= BRL_FLAG_IN | BRL_FLAG_OUT;
871 		argc++; argv--;
872 	} else
873 		goto bad_rule;
874 	argc--; argv++;
875 
876 	if (argc == 0 || strcmp(argv[0], "on"))
877 		goto bad_rule;
878 	argc--; argv++;
879 
880 	if (argc == 0)
881 		goto bad_rule;
882 	strlcpy(rule.ifbr_ifsname, argv[0], sizeof(rule.ifbr_ifsname));
883 	argc--; argv++;
884 
885 	while (argc) {
886 		if (strcmp(argv[0], "dst") == 0) {
887 			if (rule.ifbr_flags & BRL_FLAG_DSTVALID)
888 				goto bad_rule;
889 			rule.ifbr_flags |= BRL_FLAG_DSTVALID;
890 			dea = &rule.ifbr_dst;
891 		} else if (strcmp(argv[0], "src") == 0) {
892 			if (rule.ifbr_flags & BRL_FLAG_SRCVALID)
893 				goto bad_rule;
894 			rule.ifbr_flags |= BRL_FLAG_SRCVALID;
895 			dea = &rule.ifbr_src;
896 		} else if (strcmp(argv[0], "tag") == 0) {
897 			if (argc < 2) {
898 				warnx("missing tag name");
899 				goto bad_rule;
900 			}
901 			if (rule.ifbr_tagname[0]) {
902 				warnx("tag already defined");
903 				goto bad_rule;
904 			}
905 			if (strlcpy(rule.ifbr_tagname, argv[1],
906 			    PF_TAG_NAME_SIZE) > PF_TAG_NAME_SIZE) {
907 				warnx("tag name '%s' too long", argv[1]);
908 				goto bad_rule;
909 			}
910 			dea = NULL;
911 		} else
912 			goto bad_rule;
913 
914 		argc--; argv++;
915 
916 		if (argc == 0)
917 			goto bad_rule;
918 		if (dea != NULL) {
919 			ea = ether_aton(argv[0]);
920 			if (ea == NULL) {
921 				warnx("invalid address: %s", argv[0]);
922 				return (1);
923 			}
924 			bcopy(ea, dea, sizeof(*dea));
925 		}
926 		argc--; argv++;
927 	}
928 
929 	if (ioctl(s, SIOCBRDGARL, &rule) < 0) {
930 		warn("%s", name);
931 		return (1);
932 	}
933 	return (0);
934 
935 bad_rule:
936 	bridge_badrule(targc, targv, ln);
937 	return (1);
938 }
939 
940 #define MAXRULEWORDS 8
941 
942 void
943 bridge_rulefile(const char *fname, int d)
944 {
945 	FILE *f;
946 	char *str, *argv[MAXRULEWORDS], buf[1024];
947 	int ln = 0, argc = 0;
948 
949 	f = fopen(fname, "r");
950 	if (f == NULL)
951 		err(1, "%s", fname);
952 
953 	while (fgets(buf, sizeof(buf), f) != NULL) {
954 		ln++;
955 		if (buf[0] == '#' || buf[0] == '\n')
956 			continue;
957 
958 		argc = 0;
959 		str = strtok(buf, "\n\t\r ");
960 		while (str != NULL && argc < MAXRULEWORDS) {
961 			argv[argc++] = str;
962 			str = strtok(NULL, "\n\t\r ");
963 		}
964 
965 		/* Rule is too long if there's more. */
966 		if (str != NULL) {
967 			warnx("invalid rule: %d: %s ...", ln, buf);
968 			continue;
969 		}
970 
971 		bridge_rule(argc, argv, ln);
972 	}
973 	fclose(f);
974 }
975 
976 void
977 bridge_badrule(int argc, char *argv[], int ln)
978 {
979 	extern const char *__progname;
980 	int i;
981 
982 	fprintf(stderr, "%s: invalid rule: ", __progname);
983 	if (ln != -1)
984 		fprintf(stderr, "%d: ", ln);
985 	for (i = 0; i < argc; i++)
986 		fprintf(stderr, "%s ", argv[i]);
987 	fprintf(stderr, "\n");
988 }
989 
990 int
991 is_switch(char *swname)
992 {
993 	struct ifbrparam bp;
994 
995 	strlcpy(bp.ifbrp_name, swname, sizeof(bp.ifbrp_name));
996 	if (ioctl(s, SIOCSWGDPID, (caddr_t)&bp) < 0)
997 		return (0);
998 
999 	return (1);
1000 }
1001 
1002 void
1003 switch_cfg(char *delim)
1004 {
1005 	struct ifbrparam bp;
1006 
1007 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
1008 	if (ioctl(s, SIOCSWGDPID, (caddr_t)&bp) < 0)
1009 		err(1, "%s", name);
1010 
1011 	printf("%sdatapath-id 0x%016llx\n", delim, bp.ifbrp_datapath);
1012 
1013 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
1014 	if (ioctl(s, SIOCSWGFLOWMAX, (caddr_t)&bp) < 0)
1015 		err(1, "%s", name);
1016 
1017 	printf("%smax flows per table %d\n", delim, bp.ifbrp_maxflow);
1018 
1019 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
1020 	if (ioctl(s, SIOCSWGMAXGROUP, (caddr_t)&bp) < 0)
1021 		err(1, "%s", name);
1022 
1023 	printf("%smax groups %d\n", delim, bp.ifbrp_maxgroup);
1024 }
1025 
1026 void
1027 switch_status(void)
1028 {
1029 	struct ifreq ifr;
1030 
1031 	if (!is_switch(name))
1032 		return;
1033 
1034 	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
1035 	if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0)
1036 		return;
1037 
1038 	switch_cfg("\t");
1039 
1040 	bridge_list("\t");
1041 
1042 	if (aflag && !ifaliases)
1043 		return;
1044 }
1045 
1046 void
1047 switch_datapathid(const char *arg, int d)
1048 {
1049 	struct ifbrparam bp;
1050 	uint64_t newdpid;
1051 	char *endptr;
1052 
1053 	errno = 0;
1054 	newdpid = strtoll(arg, &endptr, 0);
1055 	if (arg[0] == '\0' || endptr[0] != '\0' || errno == ERANGE)
1056 		errx(1, "invalid arg for datapath-id: %s", arg);
1057 
1058 	strlcpy(bp.ifbrp_name, name, sizeof(bp.ifbrp_name));
1059 	bp.ifbrp_datapath = newdpid;
1060 	if (ioctl(s, SIOCSWSDPID, (caddr_t)&bp) < 0)
1061 		err(1, "%s", name);
1062 }
1063 
1064 void
1065 switch_portno(const char *ifname, const char *val)
1066 {
1067 	struct ifbreq breq;
1068 	uint32_t newportidx;
1069 	char *endptr;
1070 
1071 	strlcpy(breq.ifbr_name, name, sizeof(breq.ifbr_name));
1072 	strlcpy(breq.ifbr_ifsname, ifname, sizeof(breq.ifbr_ifsname));
1073 
1074 	errno = 0;
1075 	newportidx = strtol(val, &endptr, 0);
1076 	if (val[0] == '\0' || endptr[0] != '\0' || errno == ERANGE)
1077 		errx(1, "invalid arg for portidx: %s", val);
1078 
1079 	breq.ifbr_portno = newportidx;
1080 	if (ioctl(s, SIOCSWSPORTNO, (caddr_t)&breq) < 0)
1081 		err(1, "%s", name);
1082 }
1083 
1084 #endif
1085