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