1150 lines
27 KiB
C
1150 lines
27 KiB
C
/* $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
|