#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 <time.h>
#include "ifconfig.h"
#define VID_SEP '@'
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 ***);
void bridge_vidmap(const char *);
void bridge_pvlans(const char *);
#define IFBAFBITS "\020\1STATIC"
#define IFBIFBITS \
"\020\1LEARNING\2DISCOVER\3BLOCKNONIP\4STP\5EDGE\6AUTOEDGE\7PTP\10AUTOPTP\11SPAN\15LOCAL\16LOCKED\17PVPTAGS"
#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
setlocked(const char *val, int d)
{
bridge_ifsetflag(val, IFBIF_LOCKED);
}
void
unsetlocked(const char *val, int d)
{
bridge_ifclrflag(val, IFBIF_LOCKED);
}
void
setpvptags(const char *val, int d)
{
bridge_ifsetflag(val, IFBIF_PVLAN_PTAGS);
}
void
unsetpvptags(const char *val, int d)
{
bridge_ifclrflag(val, IFBIF_PVLAN_PTAGS);
}
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");
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);
switch (reqp->ifbr_pvid) {
case IFBR_PVID_NULL:
break;
case IFBR_PVID_NONE:
printf(" -untagged");
break;
case IFBR_PVID_DECLINE:
printf(" untagged passthrough");
break;
default:
printf(" untagged %u", reqp->ifbr_pvid);
break;
}
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_vidmap(buf);
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)
errx(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;
optlist = strdup(val);
if (optlist == NULL)
err(1, "strdup");
str = strtok(optlist, ",");
while (str != NULL) {
v = strtonum(str, 1, 31, &errstr);
if (errstr)
errx(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_pvid(const char *ifsname, const char *val)
{
struct ifbreq breq;
strlcpy(breq.ifbr_name, ifname, sizeof(breq.ifbr_name));
strlcpy(breq.ifbr_ifsname, ifsname, sizeof(breq.ifbr_ifsname));
if (strcmp(val, "default") == 0)
breq.ifbr_pvid = IFBR_PVID_NULL;
else if (strcmp(val, "none") == 0)
breq.ifbr_pvid = IFBR_PVID_NONE;
else if (strcmp(val, "passthrough") == 0 ||
strcmp(val, "passthru") == 0)
breq.ifbr_pvid = IFBR_PVID_DECLINE;
else {
const char *errstr;
breq.ifbr_pvid = strtonum(val,
IFBR_PVID_MIN, IFBR_PVID_MAX, &errstr);
if (errstr != NULL) {
errx(1, "%s untagged %s: %s is %s",
ifname, ifsname, val, errstr);
}
}
if (ioctl(sock, SIOCBRDGSPVID, &breq) == -1)
err(1, "%s untagged %s %s", ifname, ifsname, val);
}
void
bridge_unpvid(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_pvid = IFBR_PVID_NONE;
if (ioctl(sock, SIOCBRDGSPVID, &breq) == -1)
err(1, "%s -untagged %s", ifname, ifsname);
}
static void
bridge_set_vidmap_bit(struct ifbrvidmap *ifbrvm, unsigned int vid)
{
unsigned int voff = vid / 8;
unsigned int vbit = vid % 8;
ifbrvm->ifbrvm_map[voff] |= 1U << vbit;
}
static void
bridge_set_vidmap_range(struct ifbrvidmap *ifbrvm, uint16_t from, uint16_t to)
{
unsigned int vid;
for (vid = from; vid <= to; vid++)
bridge_set_vidmap_bit(ifbrvm, vid);
}
static void
bridge_set_vidmap_parse(struct ifbrvidmap *ifbrvm, const char *ifsname,
char *str)
{
char *s;
while ((s = strsep(&str, ",")) != NULL) {
char *t;
uint16_t fvid, tvid;
const char *errstr;
t = strsep(&s, "-");
fvid = strtonum(t, EVL_VLID_MIN, EVL_VLID_MAX, &errstr);
if (errstr != NULL) {
errx(1, "%s tagged %s: %s is %s", ifname, ifsname,
t, errstr);
}
if (s == NULL) {
bridge_set_vidmap_bit(ifbrvm, fvid);
continue;
}
tvid = strtonum(s, fvid + 1, EVL_VLID_MAX, &errstr);
if (errstr != NULL) {
errx(1, "%s tagged %s: %s is %s", ifname, ifsname,
s, errstr);
}
bridge_set_vidmap_range(ifbrvm, fvid, tvid);
}
}
void
bridge_set_vidmap(const char *ifsname, const char *arg)
{
struct ifbrvidmap ifbrvm;
memset(&ifbrvm, 0, sizeof(ifbrvm));
strlcpy(ifbrvm.ifbrvm_name, ifname, sizeof(ifbrvm.ifbrvm_name));
strlcpy(ifbrvm.ifbrvm_ifsname, ifsname, sizeof(ifbrvm.ifbrvm_ifsname));
if (strcmp(arg, "all") == 0) {
ifbrvm.ifbrvm_op = IFBRVM_OP_SET;
bridge_set_vidmap_range(&ifbrvm, EVL_VLID_MIN, EVL_VLID_MAX);
} else if (strcmp(arg, "none") == 0) {
ifbrvm.ifbrvm_op = IFBRVM_OP_SET;
} else {
const char *val;
char *str;
switch (arg[0]) {
case '+':
ifbrvm.ifbrvm_op = IFBRVM_OP_OR;
val = arg + 1;
break;
case '-':
ifbrvm.ifbrvm_op = IFBRVM_OP_ANDNOT;
val = arg + 1;
break;
case '=':
ifbrvm.ifbrvm_op = IFBRVM_OP_SET;
val = arg + 1;
break;
default:
ifbrvm.ifbrvm_op = IFBRVM_OP_OR;
val = arg;
break;
}
str = strdup(val);
if (str == NULL)
err(1, "%s strdup", __func__);
bridge_set_vidmap_parse(&ifbrvm, ifsname, str);
free(str);
}
if (ioctl(sock, SIOCBRDGSVMAP, &ifbrvm) == -1)
err(1, "%s tagged %s %s", ifname, ifsname, arg);
}
void
bridge_unset_vidmap(const char *ifsname, int d)
{
struct ifbrvidmap ifbrvm;
memset(&ifbrvm, 0, sizeof(ifbrvm));
strlcpy(ifbrvm.ifbrvm_name, ifname, sizeof(ifbrvm.ifbrvm_name));
strlcpy(ifbrvm.ifbrvm_ifsname, ifsname, sizeof(ifbrvm.ifbrvm_ifsname));
ifbrvm.ifbrvm_op = IFBRVM_OP_SET;
if (ioctl(sock, SIOCBRDGSVMAP, &ifbrvm) == -1)
err(1, "%s -tagged %s", ifname, ifsname);
}
static void
bridge_pvlan_primary_op(const char *primary, const char *op, long cmd)
{
struct ifbrpvlan ifbrpv;
uint16_t vid;
const char *errstr;
vid = strtonum(primary, EVL_VLID_MIN, EVL_VLID_MAX, &errstr);
if (errstr != NULL)
errx(1, "primary vid: %s", errstr);
memset(&ifbrpv, 0, sizeof(ifbrpv));
strlcpy(ifbrpv.ifbrpv_name, ifname, sizeof(ifbrpv.ifbrpv_name));
ifbrpv.ifbrpv_primary = vid;
ifbrpv.ifbrpv_type = IFBRPV_T_PRIMARY;
if (ioctl(sock, cmd, &ifbrpv) == -1)
err(1, "%s %s %s", ifname, op, primary);
}
void
bridge_pvlan_primary(const char *primary, int d)
{
bridge_pvlan_primary_op(primary, "pvlan", SIOCBRDGADDPV);
}
void
bridge_unpvlan_primary(const char *primary, int d)
{
bridge_pvlan_primary_op(primary, "-pvlan", SIOCBRDGDELPV);
}
static void
bridge_pvlan_secondary_op(const char *primary, const char *secondary,
int type, const char *op, long cmd)
{
struct ifbrpvlan ifbrpv;
uint16_t vp, vs;
const char *errstr;
vp = strtonum(primary, EVL_VLID_MIN, EVL_VLID_MAX, &errstr);
if (errstr != NULL)
errx(1, "primary vid: %s", errstr);
vs = strtonum(secondary, EVL_VLID_MIN, EVL_VLID_MAX, &errstr);
if (errstr != NULL)
errx(1, "secondary vid: %s", errstr);
memset(&ifbrpv, 0, sizeof(ifbrpv));
strlcpy(ifbrpv.ifbrpv_name, ifname, sizeof(ifbrpv.ifbrpv_name));
ifbrpv.ifbrpv_primary = vp;
ifbrpv.ifbrpv_secondary = vs;
ifbrpv.ifbrpv_type = type;
if (ioctl(sock, cmd, &ifbrpv) == -1)
err(1, "%s %s %s %s", ifname, op, primary, secondary);
}
void
bridge_pvlan_isolated(const char *primary, const char *secondary)
{
bridge_pvlan_secondary_op(primary, secondary, IFBRPV_T_ISOLATED,
"pvlan-isolated", SIOCBRDGADDPV);
}
void
bridge_unpvlan_isolated(const char *primary, const char *secondary)
{
bridge_pvlan_secondary_op(primary, secondary, IFBRPV_T_ISOLATED,
"-pvlan-isolated", SIOCBRDGDELPV);
}
void
bridge_pvlan_community(const char *primary, const char *secondary)
{
bridge_pvlan_secondary_op(primary, secondary, IFBRPV_T_COMMUNITY,
"pvlan-community", SIOCBRDGADDPV);
}
void
bridge_unpvlan_community(const char *primary, const char *secondary)
{
bridge_pvlan_secondary_op(primary, secondary, IFBRPV_T_COMMUNITY,
"-pvlan-community", SIOCBRDGDELPV);
}
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_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);
}
static int
bridge_addr_vid_parse(struct ifbvareq *ifbva,
const char *addr, const char *vid)
{
struct ether_addr *ea;
const char *errstr;
memset(ifbva, 0, sizeof(*ifbva));
strlcpy(ifbva->ifbva_name, ifname, sizeof(ifbva->ifbva_name));
ea = ether_aton(addr);
if (ea == NULL)
errx(1, "Invalid address: %s", addr);
ifbva->ifbva_dst = *ea;
ifbva->ifbva_vid = strtonum(vid, EVL_VLID_MIN, EVL_VLID_MAX, &errstr);
if (errstr != NULL)
errx(1, "Invalid vid %s: %s", vid, errstr);
return (0);
}
static int
bridge_addr_vid(struct ifbvareq *ifbva, const char *addr, size_t sep)
{
char *buf;
int rv;
buf = strdup(addr);
if (buf == NULL)
err(1, NULL);
buf[sep] = '\0';
rv = bridge_addr_vid_parse(ifbva, buf, buf + sep + 1);
free(buf);
return (rv);
}
void
bridge_addvaddr(const char *ifsname, const char *addr, size_t sep)
{
struct ifbvareq ifbva;
if (bridge_addr_vid(&ifbva, addr, sep) == -1)
errx(1, "unable to parse address%cvid", VID_SEP);
strlcpy(ifbva.ifbva_ifsname, ifsname, sizeof(ifbva.ifbva_ifsname));
ifbva.ifbva_flags = IFBAF_STATIC;
if (ioctl(sock, SIOCBRDGSVADDR, &ifbva) == -1)
err(1, "%s static %s %s", ifname, ifsname, addr);
}
void
bridge_addaddr(const char *ifsname, const char *addr)
{
char *chr;
struct ifbareq ifba;
struct ether_addr *ea;
chr = strchr(addr, VID_SEP);
if (chr != NULL) {
bridge_addvaddr(ifsname, addr, chr - addr);
return;
}
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);
}
static void
bridge_delvaddr(const char *addr, size_t sep)
{
struct ifbvareq ifbva;
if (bridge_addr_vid(&ifbva, addr, sep) == -1)
errx(1, "unable to parse address%cvid", VID_SEP);
if (ioctl(sock, SIOCBRDGDVADDR, &ifbva) == -1)
err(1, "%s deladdr %s", ifname, addr);
}
void
bridge_deladdr(const char *addr, int d)
{
char *chr;
struct ifbareq ifba;
struct ether_addr *ea;
chr = strchr(addr, VID_SEP);
if (chr != NULL) {
bridge_delvaddr(addr, chr - addr);
return;
}
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_addendpoint(const char *endpoint, const char *addr)
{
struct ifbareq ifba;
struct ether_addr *ea;
struct addrinfo *res;
int ecode;
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_delendpoint(const char *addr, int d)
{
struct ifbareq ifba;
struct ether_addr *ea;
ea = ether_aton(addr);
if (ea == NULL) {
errx(1, "%s -endpoint %s: invalid Ethernet address",
ifname, 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));
ifba.ifba_flags = IFBAF_STATIC;
if (ioctl(sock, SIOCBRDGDADDR, &ifba) == -1)
err(1, "%s -endpoint %s", ifname, addr);
}
static int
bridge_vaddrs_try(const char *delim)
{
char dstaddr[NI_MAXHOST];
char dstport[NI_MAXSERV];
const int niflag = NI_NUMERICHOST|NI_DGRAM;
struct ifbaconf ifbac;
struct ifbvareq *ifbva;
char *inbuf = NULL, buf[sizeof(ifbva->ifbva_ifsname) + 1], *inb;
struct sockaddr *sa;
int i, len = 8192;
struct timespec now;
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, SIOCBRDGVRTS, &ifbac) == -1) {
switch (errno) {
case ENETDOWN:
return (0);
case ENOTTY:
return (-1);
default:
err(1, "%s", ifname);
}
}
if (ifbac.ifbac_len + sizeof(*ifbva) < len)
break;
len *= 2;
}
clock_gettime(CLOCK_MONOTONIC, &now);
for (i = 0; i < ifbac.ifbac_len / sizeof(*ifbva); i++) {
ifbva = ifbac.ifbac_vreq + i;
strlcpy(buf, ifbva->ifbva_ifsname, sizeof(buf));
printf("%s%s", delim, ether_ntoa(&ifbva->ifbva_dst));
if (ifbva->ifbva_vid != EVL_VLID_NULL)
printf("%c%u", VID_SEP, ifbva->ifbva_vid);
if (buf[0] != '\0')
printf(" %s", buf);
printf(" %lld", now.tv_sec - ifbva->ifbva_used);
printb(" flags", ifbva->ifbva_flags, IFBAFBITS);
sa = (struct sockaddr *)&ifbva->ifbva_dstsa;
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);
return (0);
}
void
bridge_vaddrs(const char *cmd, int d)
{
if (bridge_vaddrs_try("") == -1)
err(1, "%s %s", ifname, cmd);
}
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;
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)
errx(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);
}
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);
}
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_pvlans("\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);
if (bridge_vaddrs_try("\t\t") == 0)
return;
bridge_addrs("\t\t", 0);
}
void
bridge_vidmap(const char *ifsname)
{
struct ifbrvidmap ifbrvm;
char sep = ' ';
int vid;
unsigned int voff, vbit, vmax;
int rangelen = 0;
strlcpy(ifbrvm.ifbrvm_name, ifname, sizeof(ifbrvm.ifbrvm_name));
strlcpy(ifbrvm.ifbrvm_ifsname, ifsname, sizeof(ifbrvm.ifbrvm_ifsname));
if (ioctl(sock, SIOCBRDGGVMAP, &ifbrvm) == -1) {
switch (errno) {
case ENOTTY:
case ENOENT:
break;
default:
warn("%s port %s get tagged", ifname, ifsname);
break;
}
return;
}
printf("\t\t" "tagged:");
vmax = sizeof(ifbrvm.ifbrvm_map) * 8;
vid = vmax - 1;
voff = vid / 8;
vbit = vid % 8;
ifbrvm.ifbrvm_map[voff] &= ~(1U << vbit);
for (vid = EVL_VLID_MIN; vid < vmax; vid++) {
voff = vid / 8;
vbit = vid % 8;
if (ifbrvm.ifbrvm_map[voff] & (1U << vbit)) {
if (rangelen++ == 0) {
printf("%c%d", sep, vid);
sep = ',';
}
} else {
if (rangelen > 1)
printf("-%d", vid - 1);
rangelen = 0;
}
}
if (sep == ' ')
printf(" none");
printf("\n");
}
void
bridge_pvlans(const char *delim)
{
uint16_t vp = 0, vs;
struct ifbrpvlan ifbrpv;
for (;;) {
const char *sep = " community ";
memset(&ifbrpv, 0, sizeof(ifbrpv));
strlcpy(ifbrpv.ifbrpv_name, ifname, sizeof(ifbrpv.ifbrpv_name));
ifbrpv.ifbrpv_primary = ++vp;
ifbrpv.ifbrpv_type = IFBRPV_T_PRIMARY;
if (ioctl(sock, SIOCBRDGNFINDPV, &ifbrpv) == -1) {
switch (errno) {
case ENOTTY:
case ENOENT:
break;
default:
warn("%s SIOCBRDGNFINDPV %u", ifname, vp);
break;
}
return;
}
printf("%spvlan %u isolated", delim, ifbrpv.ifbrpv_primary);
if (ifbrpv.ifbrpv_secondary != EVL_VLID_NULL)
printf(" %u", ifbrpv.ifbrpv_secondary);
else
printf(" none");
vp = ifbrpv.ifbrpv_primary;
vs = 0;
for (;;) {
strlcpy(ifbrpv.ifbrpv_name, ifname,
sizeof(ifbrpv.ifbrpv_name));
ifbrpv.ifbrpv_primary = vp;
ifbrpv.ifbrpv_secondary = ++vs;
ifbrpv.ifbrpv_type = IFBRPV_T_COMMUNITY;
if (ioctl(sock, SIOCBRDGNFINDPV, &ifbrpv) == -1) {
if (errno == ENOENT) {
break;
}
warn("%s SIOCBRDGNFINDPV %u %u", ifname,
vp, vs);
break;
}
printf("%s%u", sep, ifbrpv.ifbrpv_secondary);
vs = ifbrpv.ifbrpv_secondary;
sep = ",";
}
printf("\n");
}
}
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");
}
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 ");
}
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