[BACK]Return to brconfig.c CVS log [TXT][DIR] Up to [local] / src / sbin / ifconfig

File: [local] / src / sbin / ifconfig / brconfig.c (download)

Revision 1.32, Thu Nov 23 03:38:34 2023 UTC (6 months, 1 week ago) by dlg
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, HEAD
Changes since 1.31: +37 -1 lines

add an endpoint command for "bridges" that use addresses as endpoints.

this can be used to add static entries on interfaces like vxlan(4).

/*	$OpenBSD: brconfig.c,v 1.32 2023/11/23 03:38:34 dlg Exp $	*/

/*
 * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef SMALL

#include <stdio.h>
#include <sys/types.h>
#include <sys/stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/if_bridge.h>
#include <netdb.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <arpa/inet.h>

#include "ifconfig.h"

void bridge_ifsetflag(const char *, u_int32_t);
void bridge_ifclrflag(const char *, u_int32_t);

void bridge_list(char *);
void bridge_cfg(const char *);
void bridge_badrule(int, char **, int);
void bridge_showrule(struct ifbrlreq *);
int bridge_arprule(struct ifbrlreq *, int *, char ***);

#define	IFBAFBITS	"\020\1STATIC"
#define	IFBIFBITS	\
"\020\1LEARNING\2DISCOVER\3BLOCKNONIP\4STP\5EDGE\6AUTOEDGE\7PTP\10AUTOPTP\11SPAN\15LOCAL"

#define	PV2ID(pv, epri, eaddr)	do {					\
	epri	 = pv >> 48;						\
	eaddr[0] = pv >> 40;						\
	eaddr[1] = pv >> 32;						\
	eaddr[2] = pv >> 24;						\
	eaddr[3] = pv >> 16;						\
	eaddr[4] = pv >> 8;						\
	eaddr[5] = pv >> 0;						\
} while (0)

char *stpstates[] = {
	"disabled",
	"listening",
	"learning",
	"forwarding",
	"blocking",
	"discarding"
};
char *stpproto[] = {
	"stp",
	"(none)",
	"rstp",
};
char *stproles[] = {
	"disabled",
	"root",
	"designated",
	"alternate",
	"backup"
};


void
setdiscover(const char *val, int d)
{
	bridge_ifsetflag(val, IFBIF_DISCOVER);
}

void
unsetdiscover(const char *val, int d)
{
	bridge_ifclrflag(val, IFBIF_DISCOVER);
}

void
setblocknonip(const char *val, int d)
{
	bridge_ifsetflag(val, IFBIF_BLOCKNONIP);
}

void
unsetblocknonip(const char *val, int d)
{
	bridge_ifclrflag(val, IFBIF_BLOCKNONIP);
}

void
setlearn(const char *val, int d)
{
	bridge_ifsetflag(val, IFBIF_LEARNING);
}

void
unsetlearn(const char *val, int d)
{
	bridge_ifclrflag(val, IFBIF_LEARNING);
}

void
setstp(const char *val, int d)
{
	bridge_ifsetflag(val, IFBIF_STP);
}

void
unsetstp(const char *val, int d)
{
	bridge_ifclrflag(val, IFBIF_STP);
}

void
setedge(const char *val, int d)
{
	bridge_ifsetflag(val, IFBIF_BSTP_EDGE);
}

void
unsetedge(const char *val, int d)
{
	bridge_ifclrflag(val, IFBIF_BSTP_EDGE);
}

void
setautoedge(const char *val, int d)
{
	bridge_ifsetflag(val, IFBIF_BSTP_AUTOEDGE);
}

void
unsetautoedge(const char *val, int d)
{
	bridge_ifclrflag(val, IFBIF_BSTP_AUTOEDGE);
}

void
setptp(const char *val, int d)
{
	bridge_ifsetflag(val, IFBIF_BSTP_PTP);
}

void
unsetptp(const char *val, int d)
{
	bridge_ifclrflag(val, IFBIF_BSTP_PTP);
}

void
setautoptp(const char *val, int d)
{
	bridge_ifsetflag(val, IFBIF_BSTP_AUTOPTP);
}

void
unsetautoptp(const char *val, int d)
{
	bridge_ifclrflag(val, IFBIF_BSTP_AUTOPTP);
}

void
addlocal(const char *ifsname, int d)
{
	struct ifbreq breq;

	if (strncmp(ifsname, "vether", (sizeof("vether") - 1)) != 0)
		errx(1, "only vether can be local interface");

	/* Add local */
	strlcpy(breq.ifbr_name, ifname, sizeof(breq.ifbr_name));
	strlcpy(breq.ifbr_ifsname, ifsname, sizeof(breq.ifbr_ifsname));
	if (ioctl(sock, SIOCBRDGADDL, (caddr_t)&breq) == -1) {
		if (errno == EEXIST)
			return;
		else
			err(1, "%s: ioctl SIOCBRDGADDL %s", ifname, ifsname);
	}
}

void
bridge_ifsetflag(const char *ifsname, u_int32_t flag)
{
	struct ifbreq req;

	strlcpy(req.ifbr_name, ifname, sizeof(req.ifbr_name));
	strlcpy(req.ifbr_ifsname, ifsname, sizeof(req.ifbr_ifsname));
	if (ioctl(sock, SIOCBRDGGIFFLGS, (caddr_t)&req) == -1)
		err(1, "%s: ioctl SIOCBRDGGIFFLGS %s", ifname, ifsname);

	req.ifbr_ifsflags |= flag & ~IFBIF_RO_MASK;

	if (ioctl(sock, SIOCBRDGSIFFLGS, (caddr_t)&req) == -1)
		err(1, "%s: ioctl SIOCBRDGSIFFLGS %s", ifname, ifsname);
}

void
bridge_ifclrflag(const char *ifsname, u_int32_t flag)
{
	struct ifbreq req;

	strlcpy(req.ifbr_name, ifname, sizeof(req.ifbr_name));
	strlcpy(req.ifbr_ifsname, ifsname, sizeof(req.ifbr_ifsname));

	if (ioctl(sock, SIOCBRDGGIFFLGS, (caddr_t)&req) == -1)
		err(1, "%s: ioctl SIOCBRDGGIFFLGS %s", ifname, ifsname);

	req.ifbr_ifsflags &= ~(flag | IFBIF_RO_MASK);

	if (ioctl(sock, SIOCBRDGSIFFLGS, (caddr_t)&req) == -1)
		err(1, "%s: ioctl SIOCBRDGSIFFLGS %s", ifname, ifsname);
}

void
bridge_flushall(const char *val, int p)
{
	struct ifbreq req;

	strlcpy(req.ifbr_name, ifname, sizeof(req.ifbr_name));
	req.ifbr_ifsflags = IFBF_FLUSHALL;
	if (ioctl(sock, SIOCBRDGFLUSH, &req) == -1)
		err(1, "%s", ifname);
}

void
bridge_flush(const char *val, int p)
{
	struct ifbreq req;

	strlcpy(req.ifbr_name, ifname, sizeof(req.ifbr_name));
	req.ifbr_ifsflags = IFBF_FLUSHDYN;
	if (ioctl(sock, SIOCBRDGFLUSH, &req) == -1)
		err(1, "%s", ifname);
}

void
bridge_cfg(const char *delim)
{
	struct ifbropreq ifbp;
	u_int16_t pri;
	u_int8_t ht, fd, ma, hc, proto;
	u_int8_t lladdr[ETHER_ADDR_LEN];
	u_int16_t bprio;

	strlcpy(ifbp.ifbop_name, ifname, sizeof(ifbp.ifbop_name));
	if (ioctl(sock, SIOCBRDGGPARAM, (caddr_t)&ifbp) == -1) {
		if (errno == ENOTTY)
			return;
		err(1, "%s SIOCBRDGGPARAM", ifname);
	}

	printf("%s", delim);
	pri = ifbp.ifbop_priority;
	ht = ifbp.ifbop_hellotime;
	fd = ifbp.ifbop_fwddelay;
	ma = ifbp.ifbop_maxage;
	hc = ifbp.ifbop_holdcount;
	proto = ifbp.ifbop_protocol;

	printf("priority %u hellotime %u fwddelay %u maxage %u "
	    "holdcnt %u proto %s\n", pri, ht, fd, ma, hc, stpproto[proto]);

	if (aflag)
		return;

	PV2ID(ifbp.ifbop_desg_bridge, bprio, lladdr);
	printf("\tdesignated: id %s priority %u\n",
	    ether_ntoa((struct ether_addr *)lladdr), bprio);

	if (ifbp.ifbop_root_bridge == ifbp.ifbop_desg_bridge)
		return;

	PV2ID(ifbp.ifbop_root_bridge, bprio, lladdr);
	printf("\troot: id %s priority %u ifcost %u port %u\n",
	    ether_ntoa((struct ether_addr *)lladdr), bprio,
	    ifbp.ifbop_root_path_cost, ifbp.ifbop_root_port & 0xfff);
}

void
bridge_list(char *delim)
{
	struct ifbreq *reqp;
	struct ifbifconf bifc;
	int i, len = 8192;
	char buf[sizeof(reqp->ifbr_ifsname) + 1], *inbuf = NULL, *inb;

	while (1) {
		bifc.ifbic_len = len;
		inb = realloc(inbuf, len);
		if (inb == NULL)
			err(1, "malloc");
		bifc.ifbic_buf = inbuf = inb;
		strlcpy(bifc.ifbic_name, ifname, sizeof(bifc.ifbic_name));
		if (ioctl(sock, SIOCBRDGIFS, &bifc) == -1) {
			if (errno == ENOTTY)
				return;
			err(1, "%s SIOCBRDGIFS", ifname);
		}
		if (bifc.ifbic_len + sizeof(*reqp) < len)
			break;
		len *= 2;
	}
	for (i = 0; i < bifc.ifbic_len / sizeof(*reqp); i++) {
		reqp = bifc.ifbic_req + i;
		strlcpy(buf, reqp->ifbr_ifsname, sizeof(buf));
		printf("%s%s ", delim, buf);
		printb("flags", reqp->ifbr_ifsflags, IFBIFBITS);
		printf("\n");
		if (reqp->ifbr_ifsflags & IFBIF_SPAN)
			continue;
		printf("\t\t");
		printf("port %u ifpriority %u ifcost %u",
		    reqp->ifbr_portno, reqp->ifbr_priority,
		    reqp->ifbr_path_cost);
		if (reqp->ifbr_protected) {
			int v;

			v = ffs(reqp->ifbr_protected);
			printf(" protected %u", v);
			while (++v < 32) {
				if ((1 << (v - 1)) & reqp->ifbr_protected)
					printf(",%u", v);
			}
		}
		if (reqp->ifbr_ifsflags & IFBIF_STP)
			printf(" %s role %s",
			    stpstates[reqp->ifbr_state],
			    stproles[reqp->ifbr_role]);
		printf("\n");
		bridge_rules(buf, 1);
	}
	free(bifc.ifbic_buf);
}

void
bridge_add(const char *ifn, int d)
{
	struct ifbreq req;

	strlcpy(req.ifbr_name, ifname, sizeof(req.ifbr_name));
	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
	if (ioctl(sock, SIOCBRDGADD, &req) == -1) {
		if (errno == EEXIST)
			return;
		err(1, "%s: %s", ifname, ifn);
	}
}

void
bridge_delete(const char *ifn, int d)
{
	struct ifbreq req;

	strlcpy(req.ifbr_name, ifname, sizeof(req.ifbr_name));
	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
	if (ioctl(sock, SIOCBRDGDEL, &req) == -1)
		err(1, "%s: %s", ifname, ifn);
}

void
bridge_addspan(const char *ifn, int d)
{
	struct ifbreq req;

	strlcpy(req.ifbr_name, ifname, sizeof(req.ifbr_name));
	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
	if (ioctl(sock, SIOCBRDGADDS, &req) == -1) {
		if (errno == EEXIST)
			return;
		err(1, "%s: %s", ifname, ifn);
	}
}

void
bridge_delspan(const char *ifn, int d)
{
	struct ifbreq req;

	strlcpy(req.ifbr_name, ifname, sizeof(req.ifbr_name));
	strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
	if (ioctl(sock, SIOCBRDGDELS, &req) == -1)
		err(1, "%s: %s", ifname, ifn);
}

void
bridge_timeout(const char *arg, int d)
{
	struct ifbrparam bp;
	const char *errstr;

	bp.ifbrp_ctime = strtonum(arg, 0, UINT32_MAX, &errstr);
	if (errstr)
		err(1, "timeout %s is: %s", arg, errstr);

	strlcpy(bp.ifbrp_name, ifname, sizeof(bp.ifbrp_name));
	if (ioctl(sock, SIOCBRDGSTO, (caddr_t)&bp) == -1)
		err(1, "%s", ifname);
}

void
bridge_maxage(const char *arg, int d)
{
	struct ifbrparam bp;
	const char *errstr;

	bp.ifbrp_maxage = strtonum(arg, 0, UINT8_MAX, &errstr);
	if (errstr)
		errx(1, "maxage %s is: %s", arg, errstr);

	strlcpy(bp.ifbrp_name, ifname, sizeof(bp.ifbrp_name));
	if (ioctl(sock, SIOCBRDGSMA, (caddr_t)&bp) == -1)
		err(1, "%s", ifname);
}

void
bridge_priority(const char *arg, int d)
{
	struct ifbrparam bp;
	const char *errstr;

	bp.ifbrp_prio  = strtonum(arg, 0, UINT16_MAX, &errstr);
	if (errstr)
		errx(1, "spanpriority %s is: %s", arg, errstr);

	strlcpy(bp.ifbrp_name, ifname, sizeof(bp.ifbrp_name));
	if (ioctl(sock, SIOCBRDGSPRI, (caddr_t)&bp) == -1)
		err(1, "%s", ifname);
}

void
bridge_protect(const char *ifsname, const char *val)
{
	struct ifbreq breq;
	unsigned long v;
	char *optlist, *str;
	const char *errstr;

	strlcpy(breq.ifbr_name, ifname, sizeof(breq.ifbr_name));
	strlcpy(breq.ifbr_ifsname, ifsname, sizeof(breq.ifbr_ifsname));
	breq.ifbr_protected = 0;

	/* We muck with the string, so copy it. */
	optlist = strdup(val);
	if (optlist == NULL)
		err(1, "strdup");

	str = strtok(optlist, ",");
	while (str != NULL) {
		v = strtonum(str, 1, 31, &errstr);
		if (errstr)
			err(1, "protected domain %s is: %s", str, errstr);
		breq.ifbr_protected |= (1 << (v - 1));
		str = strtok(NULL, ",");
	}

	if (ioctl(sock, SIOCBRDGSIFPROT, (caddr_t)&breq) == -1)
		err(1, "%s: %s", ifname, val);

	free(optlist);
}

void
bridge_unprotect(const char *ifsname, int d)
{
	struct ifbreq breq;

	strlcpy(breq.ifbr_name, ifname, sizeof(breq.ifbr_name));
	strlcpy(breq.ifbr_ifsname, ifsname, sizeof(breq.ifbr_ifsname));

	breq.ifbr_protected = 0;

	if (ioctl(sock, SIOCBRDGSIFPROT, (caddr_t)&breq) == -1)
		err(1, "%s: %d", ifname, 0);
}

void
bridge_proto(const char *arg, int d)
{
	struct ifbrparam bp;
	int i, proto = -1;

	for (i = 0; i <= BSTP_PROTO_MAX; i++)
		if (strcmp(arg, stpproto[i]) == 0) {
			proto = i;
			break;
		}
	if (proto == -1)
		errx(1, "invalid arg for proto: %s", arg);

	strlcpy(bp.ifbrp_name, ifname, sizeof(bp.ifbrp_name));
	bp.ifbrp_prio = proto;
	if (ioctl(sock, SIOCBRDGSPROTO, (caddr_t)&bp) == -1)
		err(1, "%s", ifname);
}

void
bridge_fwddelay(const char *arg, int d)
{
	struct ifbrparam bp;
	const char *errstr;

	bp.ifbrp_fwddelay = strtonum(arg, 0, UINT8_MAX, &errstr);
	if (errstr)
		errx(1, "fwddelay %s is: %s", arg, errstr);

	strlcpy(bp.ifbrp_name, ifname, sizeof(bp.ifbrp_name));

	if (ioctl(sock, SIOCBRDGSFD, (caddr_t)&bp) == -1)
		err(1, "%s", ifname);
}

void
bridge_hellotime(const char *arg, int d)
{
	struct ifbrparam bp;
	const char *errstr;

	bp.ifbrp_hellotime = strtonum(arg, 0, UINT8_MAX, &errstr);
	if (errstr)
		errx(1, "hellotime %s is: %s", arg, errstr);

	strlcpy(bp.ifbrp_name, ifname, sizeof(bp.ifbrp_name));

	if (ioctl(sock, SIOCBRDGSHT, (caddr_t)&bp) == -1)
		err(1, "%s", ifname);
}

void
bridge_maxaddr(const char *arg, int d)
{
	struct ifbrparam bp;
	const char *errstr;

	bp.ifbrp_csize = strtonum(arg, 0, UINT32_MAX, &errstr);
	if (errstr)
		errx(1, "maxaddr %s is: %s", arg, errstr);

	strlcpy(bp.ifbrp_name, ifname, sizeof(bp.ifbrp_name));
	if (ioctl(sock, SIOCBRDGSCACHE, (caddr_t)&bp) == -1)
		err(1, "%s", ifname);
}

void
bridge_deladdr(const char *addr, int d)
{
	struct ifbareq ifba;
	struct ether_addr *ea;

	strlcpy(ifba.ifba_name, ifname, sizeof(ifba.ifba_name));
	ea = ether_aton(addr);
	if (ea == NULL)
		err(1, "Invalid address: %s", addr);

	bcopy(ea, &ifba.ifba_dst, sizeof(struct ether_addr));

	if (ioctl(sock, SIOCBRDGDADDR, &ifba) == -1)
		err(1, "%s: %s", ifname, addr);
}

void
bridge_ifprio(const char *ifsname, const char *val)
{
	struct ifbreq breq;
	const char *errstr;

	breq.ifbr_priority = strtonum(val, 0, UINT8_MAX, &errstr);
	if (errstr)
		errx(1, "ifpriority %s is: %s", val, errstr);

	strlcpy(breq.ifbr_name, ifname, sizeof(breq.ifbr_name));
	strlcpy(breq.ifbr_ifsname, ifsname, sizeof(breq.ifbr_ifsname));

	if (ioctl(sock, SIOCBRDGSIFPRIO, (caddr_t)&breq) == -1)
		err(1, "%s: %s", ifname, val);
}

void
bridge_ifcost(const char *ifsname, const char *val)
{
	struct ifbreq breq;
	const char *errstr;

	breq.ifbr_path_cost = strtonum(val, 0, UINT32_MAX, &errstr);
	if (errstr)
		errx(1, "ifcost %s is: %s", val, errstr);

	strlcpy(breq.ifbr_name, ifname, sizeof(breq.ifbr_name));
	strlcpy(breq.ifbr_ifsname, ifsname, sizeof(breq.ifbr_ifsname));

	if (ioctl(sock, SIOCBRDGSIFCOST, (caddr_t)&breq) == -1)
		err(1, "%s: %s", ifname, val);
}

void
bridge_noifcost(const char *ifsname, int d)
{
	struct ifbreq breq;

	strlcpy(breq.ifbr_name, ifname, sizeof(breq.ifbr_name));
	strlcpy(breq.ifbr_ifsname, ifsname, sizeof(breq.ifbr_ifsname));

	breq.ifbr_path_cost = 0;

	if (ioctl(sock, SIOCBRDGSIFCOST, (caddr_t)&breq) == -1)
		err(1, "%s", ifname);
}

void
bridge_addaddr(const char *ifsname, const char *addr)
{
	struct ifbareq ifba;
	struct ether_addr *ea;

	strlcpy(ifba.ifba_name, ifname, sizeof(ifba.ifba_name));
	strlcpy(ifba.ifba_ifsname, ifsname, sizeof(ifba.ifba_ifsname));

	ea = ether_aton(addr);
	if (ea == NULL)
		errx(1, "Invalid address: %s", addr);

	bcopy(ea, &ifba.ifba_dst, sizeof(struct ether_addr));
	ifba.ifba_flags = IFBAF_STATIC;

	if (ioctl(sock, SIOCBRDGSADDR, &ifba) == -1)
		err(1, "%s: %s", ifname, addr);
}

void
bridge_addendpoint(const char *endpoint, const char *addr)
{
	struct ifbareq ifba;
	struct ether_addr *ea;
	struct addrinfo *res;
	int ecode;

	/* should we handle ports? */
	ecode = getaddrinfo(endpoint, NULL, NULL, &res);
	if (ecode != 0) {
                errx(1, "%s endpoint %s: %s", ifname, endpoint,
                    gai_strerror(ecode));
	}
	if (res->ai_addrlen > sizeof(ifba.ifba_dstsa))
		errx(1, "%s: addrlen > dstsa", __func__);

	ea = ether_aton(addr);
	if (ea == NULL) {
		errx(1, "%s endpoint %s %s: invalid Ethernet address",
		    ifname, endpoint, addr);
	}

	memset(&ifba, 0, sizeof(ifba));
	strlcpy(ifba.ifba_name, ifname, sizeof(ifba.ifba_name));
	strlcpy(ifba.ifba_ifsname, ifname, sizeof(ifba.ifba_ifsname));
	memcpy(&ifba.ifba_dst, ea, sizeof(struct ether_addr));
	memcpy(&ifba.ifba_dstsa, res->ai_addr, res->ai_addrlen);
	ifba.ifba_flags = IFBAF_STATIC;

	freeaddrinfo(res);

	if (ioctl(sock, SIOCBRDGSADDR, &ifba) == -1)
		err(1, "%s endpoint %s %s", ifname, endpoint, addr);
}

void
bridge_addrs(const char *delim, int d)
{
	char dstaddr[NI_MAXHOST];
	char dstport[NI_MAXSERV];
	const int niflag = NI_NUMERICHOST|NI_DGRAM;
	struct ifbaconf ifbac;
	struct ifbareq *ifba;
	char *inbuf = NULL, buf[sizeof(ifba->ifba_ifsname) + 1], *inb;
	struct sockaddr *sa;
	int i, len = 8192;

	/* ifconfig will call us with the argv of the command */
	if (strcmp(delim, "addr") == 0)
		delim = "";

	while (1) {
		ifbac.ifbac_len = len;
		inb = realloc(inbuf, len);
		if (inb == NULL)
			err(1, "malloc");
		ifbac.ifbac_buf = inbuf = inb;
		strlcpy(ifbac.ifbac_name, ifname, sizeof(ifbac.ifbac_name));
		if (ioctl(sock, SIOCBRDGRTS, &ifbac) == -1) {
			if (errno == ENETDOWN)
				return;
			err(1, "%s", ifname);
		}
		if (ifbac.ifbac_len + sizeof(*ifba) < len)
			break;
		len *= 2;
	}

	for (i = 0; i < ifbac.ifbac_len / sizeof(*ifba); i++) {
		ifba = ifbac.ifbac_req + i;
		strlcpy(buf, ifba->ifba_ifsname, sizeof(buf));
		printf("%s%s %s %u ", delim, ether_ntoa(&ifba->ifba_dst),
		    buf, ifba->ifba_age);
		sa = (struct sockaddr *)&ifba->ifba_dstsa;
		printb("flags", ifba->ifba_flags, IFBAFBITS);
		if (sa->sa_family != AF_UNSPEC &&
		    getnameinfo(sa, sa->sa_len,
		    dstaddr, sizeof(dstaddr),
		    dstport, sizeof(dstport), niflag) == 0)
			printf(" tunnel %s:%s", dstaddr, dstport);
		printf("\n");
	}
	free(inbuf);
}

void
bridge_holdcnt(const char *value, int d)
{
	struct ifbrparam bp;
	const char *errstr;

	bp.ifbrp_txhc = strtonum(value, 0, UINT8_MAX, &errstr);
	if (errstr)
		err(1, "holdcnt %s is: %s", value, errstr);

	strlcpy(bp.ifbrp_name, ifname, sizeof(bp.ifbrp_name));
	if (ioctl(sock, SIOCBRDGSTXHC, (caddr_t)&bp) == -1)
		err(1, "%s", ifname);
}

/*
 * Check to make sure interface is really a bridge interface.
 */
int
is_bridge()
{
	struct ifbaconf ifbac;

	ifbac.ifbac_len = 0;
	strlcpy(ifbac.ifbac_name, ifname, sizeof(ifbac.ifbac_name));
	if (ioctl(sock, SIOCBRDGRTS, (caddr_t)&ifbac) == -1) {
		if (errno == ENETDOWN)
			return (1);
		return (0);
	}
	return (1);
}

/* no tpmr(4) specific ioctls, name is enough if ifconfig.c:printif() passed */
int
is_tpmr(void)
{
	return (strncmp(ifname, "tpmr", sizeof("tpmr") - 1) == 0);
}

void
bridge_status(void)
{
	struct ifbrparam bp1, bp2;

	if (is_tpmr()) {
		bridge_list("\t");
		return;
	}

	if (!is_bridge())
		return;

	bridge_cfg("\t");
	bridge_list("\t");

	if (aflag && !ifaliases)
		return;

	strlcpy(bp1.ifbrp_name, ifname, sizeof(bp1.ifbrp_name));
	if (ioctl(sock, SIOCBRDGGCACHE, (caddr_t)&bp1) == -1)
		return;

	strlcpy(bp2.ifbrp_name, ifname, sizeof(bp2.ifbrp_name));
	if (ioctl(sock, SIOCBRDGGTO, (caddr_t)&bp2) == -1)
		return;

	printf("\tAddresses (max cache: %u, timeout: %u):\n",
	    bp1.ifbrp_csize, bp2.ifbrp_ctime);

	bridge_addrs("\t\t", 0);
}

void
bridge_flushrule(const char *ifsname, int d)
{
	struct ifbrlreq req;

	strlcpy(req.ifbr_name, ifname, sizeof(req.ifbr_name));
	strlcpy(req.ifbr_ifsname, ifsname, sizeof(req.ifbr_ifsname));
	if (ioctl(sock, SIOCBRDGFRL, &req) == -1)
		err(1, "%s: %s", ifname, ifsname);
}

void
bridge_rules(const char *ifsname, int usetab)
{
	char *inbuf = NULL, *inb;
	struct ifbrlconf ifc;
	struct ifbrlreq *ifrp;
	int len = 8192, i;

	while (1) {
		ifc.ifbrl_len = len;
		inb = realloc(inbuf, len);
		if (inb == NULL)
			err(1, "malloc");
		ifc.ifbrl_buf = inbuf = inb;
		strlcpy(ifc.ifbrl_name, ifname, sizeof(ifc.ifbrl_name));
		strlcpy(ifc.ifbrl_ifsname, ifsname, sizeof(ifc.ifbrl_ifsname));
		if (ioctl(sock, SIOCBRDGGRL, &ifc) == -1)
			err(1, "ioctl(SIOCBRDGGRL)");
		if (ifc.ifbrl_len + sizeof(*ifrp) < len)
			break;
		len *= 2;
	}
	ifrp = ifc.ifbrl_req;
	for (i = 0; i < ifc.ifbrl_len; i += sizeof(*ifrp)) {
		ifrp = (struct ifbrlreq *)((caddr_t)ifc.ifbrl_req + i);

		if (usetab)
			printf("\t");

		bridge_showrule(ifrp);
	}
}

void
bridge_showrule(struct ifbrlreq *r)
{
	if (r->ifbr_action == BRL_ACTION_BLOCK)
		printf("block ");
	else if (r->ifbr_action == BRL_ACTION_PASS)
		printf("pass ");
	else
		printf("[neither block nor pass?]\n");

	if ((r->ifbr_flags & (BRL_FLAG_IN | BRL_FLAG_OUT)) ==
	    (BRL_FLAG_IN | BRL_FLAG_OUT))
		printf("in/out ");
	else if (r->ifbr_flags & BRL_FLAG_IN)
		printf("in ");
	else if (r->ifbr_flags & BRL_FLAG_OUT)
		printf("out ");
	else
		printf("[neither in nor out?]\n");

	printf("on %s", r->ifbr_ifsname);

	if (r->ifbr_flags & BRL_FLAG_SRCVALID)
		printf(" src %s", ether_ntoa(&r->ifbr_src));
	if (r->ifbr_flags & BRL_FLAG_DSTVALID)
		printf(" dst %s", ether_ntoa(&r->ifbr_dst));
	if (r->ifbr_tagname[0])
		printf(" tag %s", r->ifbr_tagname);

	if (r->ifbr_arpf.brla_flags & BRLA_ARP)
		printf(" arp");
	if (r->ifbr_arpf.brla_flags & BRLA_RARP)
		printf(" rarp");
	if (r->ifbr_arpf.brla_op == ARPOP_REQUEST ||
	    r->ifbr_arpf.brla_op == ARPOP_REVREQUEST)
		printf(" request");
	if (r->ifbr_arpf.brla_op == ARPOP_REPLY ||
	    r->ifbr_arpf.brla_op == ARPOP_REVREPLY)
		printf(" reply");
	if (r->ifbr_arpf.brla_flags & BRLA_SHA)
		printf(" sha %s", ether_ntoa(&r->ifbr_arpf.brla_sha));
	if (r->ifbr_arpf.brla_flags & BRLA_THA)
		printf(" tha %s", ether_ntoa(&r->ifbr_arpf.brla_tha));
	if (r->ifbr_arpf.brla_flags & BRLA_SPA)
		printf(" spa %s", inet_ntoa(r->ifbr_arpf.brla_spa));
	if (r->ifbr_arpf.brla_flags & BRLA_TPA)
		printf(" tpa %s", inet_ntoa(r->ifbr_arpf.brla_tpa));

	printf("\n");
}

/*
 * Parse a rule definition and send it upwards.
 *
 * Syntax:
 *	{block|pass} {in|out|in/out} on {ifs} [src {mac}] [dst {mac}]
 */
int
bridge_rule(int targc, char **targv, int ln)
{
	char **argv = targv;
	int argc = targc;
	struct ifbrlreq rule;
	struct ether_addr *ea, *dea;

	if (argc == 0) {
		warnx("invalid rule");
		return (1);
	}
	bzero(&rule, sizeof(rule));
	strlcpy(rule.ifbr_name, ifname, sizeof(rule.ifbr_name));

	if (strcmp(argv[0], "block") == 0)
		rule.ifbr_action = BRL_ACTION_BLOCK;
	else if (strcmp(argv[0], "pass") == 0)
		rule.ifbr_action = BRL_ACTION_PASS;
	else
		goto bad_rule;
	argc--;	argv++;

	if (argc == 0) {
		bridge_badrule(targc, targv, ln);
		return (1);
	}
	if (strcmp(argv[0], "in") == 0)
		rule.ifbr_flags |= BRL_FLAG_IN;
	else if (strcmp(argv[0], "out") == 0)
		rule.ifbr_flags |= BRL_FLAG_OUT;
	else if (strcmp(argv[0], "in/out") == 0)
		rule.ifbr_flags |= BRL_FLAG_IN | BRL_FLAG_OUT;
	else if (strcmp(argv[0], "on") == 0) {
		rule.ifbr_flags |= BRL_FLAG_IN | BRL_FLAG_OUT;
		argc++; argv--;
	} else
		goto bad_rule;
	argc--; argv++;

	if (argc == 0 || strcmp(argv[0], "on"))
		goto bad_rule;
	argc--; argv++;

	if (argc == 0)
		goto bad_rule;
	strlcpy(rule.ifbr_ifsname, argv[0], sizeof(rule.ifbr_ifsname));
	argc--; argv++;

	while (argc) {
		dea = NULL;
		if (strcmp(argv[0], "dst") == 0) {
			if (rule.ifbr_flags & BRL_FLAG_DSTVALID)
				goto bad_rule;
			rule.ifbr_flags |= BRL_FLAG_DSTVALID;
			dea = &rule.ifbr_dst;
			argc--; argv++;
		} else if (strcmp(argv[0], "src") == 0) {
			if (rule.ifbr_flags & BRL_FLAG_SRCVALID)
				goto bad_rule;
			rule.ifbr_flags |= BRL_FLAG_SRCVALID;
			dea = &rule.ifbr_src;
			argc--; argv++;
		} else if (strcmp(argv[0], "tag") == 0) {
			if (argc < 2) {
				warnx("missing tag name");
				goto bad_rule;
			}
			if (rule.ifbr_tagname[0]) {
				warnx("tag already defined");
				goto bad_rule;
			}
			argc--; argv++;
			if (strlcpy(rule.ifbr_tagname, argv[0],
			    PF_TAG_NAME_SIZE) > PF_TAG_NAME_SIZE) {
				warnx("tag name '%s' too long", argv[0]);
				goto bad_rule;
			}
			argc--; argv++;
		} else if (strcmp(argv[0], "arp") == 0) {
			rule.ifbr_arpf.brla_flags |= BRLA_ARP;
			argc--; argv++;
			if (bridge_arprule(&rule, &argc, &argv) == -1)
				goto bad_rule;
		} else if (strcmp(argv[0], "rarp") == 0) {
			rule.ifbr_arpf.brla_flags |= BRLA_RARP;
			argc--; argv++;
			if (bridge_arprule(&rule, &argc, &argv) == -1)
				goto bad_rule;
		} else
			goto bad_rule;

		if (dea != NULL) {
			if (argc == 0)
				goto bad_rule;
			ea = ether_aton(argv[0]);
			if (ea == NULL) {
				warnx("invalid address: %s", argv[0]);
				return (1);
			}
			bcopy(ea, dea, sizeof(*dea));
			argc--; argv++;
		}
	}

	if (ioctl(sock, SIOCBRDGARL, &rule) == -1) {
		warn("%s", ifname);
		return (1);
	}
	return (0);

bad_rule:
	bridge_badrule(targc, targv, ln);
	return (1);
}

int
bridge_arprule(struct ifbrlreq *rule, int *argc, char ***argv)
{
	while (*argc) {
		struct ether_addr	*ea, *dea = NULL;
		struct in_addr		 ia, *dia = NULL;

		if (strcmp((*argv)[0], "request") == 0) {
			if (rule->ifbr_arpf.brla_flags & BRLA_ARP)
				rule->ifbr_arpf.brla_op = ARPOP_REQUEST;
			else if (rule->ifbr_arpf.brla_flags & BRLA_RARP)
				rule->ifbr_arpf.brla_op = ARPOP_REVREQUEST;
			else
				errx(1, "bridge_arprule: arp/rarp undefined");
		} else if (strcmp((*argv)[0], "reply") == 0) {
			if (rule->ifbr_arpf.brla_flags & BRLA_ARP)
				rule->ifbr_arpf.brla_op = ARPOP_REPLY;
			else if (rule->ifbr_arpf.brla_flags & BRLA_RARP)
				rule->ifbr_arpf.brla_op = ARPOP_REVREPLY;
			else
				errx(1, "bridge_arprule: arp/rarp undefined");
		} else if (strcmp((*argv)[0], "sha") == 0) {
			rule->ifbr_arpf.brla_flags |= BRLA_SHA;
			dea = &rule->ifbr_arpf.brla_sha;
		} else if (strcmp((*argv)[0], "tha") == 0) {
			rule->ifbr_arpf.brla_flags |= BRLA_THA;
			dea = &rule->ifbr_arpf.brla_tha;
		} else if (strcmp((*argv)[0], "spa") == 0) {
			rule->ifbr_arpf.brla_flags |= BRLA_SPA;
			dia = &rule->ifbr_arpf.brla_spa;
		} else if (strcmp((*argv)[0], "tpa") == 0) {
			rule->ifbr_arpf.brla_flags |= BRLA_TPA;
			dia = &rule->ifbr_arpf.brla_tpa;
		} else
			return (0);

		(*argc)--; (*argv)++;
		if (dea != NULL) {
			if (*argc == 0)
				return (-1);
			ea = ether_aton((*argv)[0]);
			if (ea == NULL) {
				warnx("invalid address: %s", (*argv)[0]);
				return (-1);
			}
			bcopy(ea, dea, sizeof(*dea));
			(*argc)--; (*argv)++;
		}
		if (dia != NULL) {
			if (*argc == 0)
				return (-1);
			ia.s_addr = inet_addr((*argv)[0]);
			if (ia.s_addr == INADDR_NONE) {
				warnx("invalid address: %s", (*argv)[0]);
				return (-1);
			}
			bcopy(&ia, dia, sizeof(*dia));
			(*argc)--; (*argv)++;
		}
	}
	return (0);
}


#define MAXRULEWORDS 32

void
bridge_rulefile(const char *fname, int d)
{
	FILE *f;
	char *str, *argv[MAXRULEWORDS], buf[1024];
	int ln = 0, argc = 0;

	f = fopen(fname, "r");
	if (f == NULL)
		err(1, "%s", fname);

	while (fgets(buf, sizeof(buf), f) != NULL) {
		ln++;
		if (buf[0] == '#' || buf[0] == '\n')
			continue;

		argc = 0;
		str = strtok(buf, "\n\t\r ");
		while (str != NULL && argc < MAXRULEWORDS) {
			argv[argc++] = str;
			str = strtok(NULL, "\n\t\r ");
		}

		/* Rule is too long if there's more. */
		if (str != NULL) {
			warnx("invalid rule: %d: %s ...", ln, buf);
			continue;
		}

		bridge_rule(argc, argv, ln);
	}
	fclose(f);
}

void
bridge_badrule(int argc, char *argv[], int ln)
{
	extern const char *__progname;
	int i;

	fprintf(stderr, "%s: invalid rule: ", __progname);
	if (ln != -1)
		fprintf(stderr, "%d: ", ln);
	for (i = 0; i < argc; i++)
		fprintf(stderr, "%s ", argv[i]);
	fprintf(stderr, "\n");
}

#endif