ref: 216ecc546034247d9281462c605b4938971e9b98
parent: 06ba01b81692aa3eea9a8bc99799d9253cb44518
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Jul 22 13:23:06 EDT 2023
ethermultilink: add "bypass" ctl for ethernet Instead of having a ethersink with the same mac, implement a "bypass" mechanism that lets us get access to the interfaces frames and bypass the physical link. Now no extra ethersink is required and a pre-existing interface can be bridged transparently.
--- a/rc/bin/ethermultilink
+++ b/rc/bin/ethermultilink
@@ -3,11 +3,6 @@
# ethermultilink outpus bridge(3) commands to switch
# between multiple ethernet (or wifi) interfaces
# depending on their link status.
-# the first argument is the primary interface,
-# which is permanently added to the bridge
-# while the following arguments are for secondary
-# interfaces in increasing priority order.
-# only the highest priority active interface is bound.
rfork e
@@ -30,44 +25,36 @@
# first interface is the primary
primary=$1
-shift
-ea=`{cat $primary/addr} || missing $primary/addr
net=`{echo $primary | sed 's!/*[^/]*$!!g'}
test -r $net/arp || missing $net/arp
-# insert the primary to bridge
-echo bind ether primary 0 $primary || exit
-
# now select secondary from the list depending on link status
@{
-type=none
old=/dev/null
+new=$old
while(){
# interfaces are in increasing priority order
for(i){
- if(! ~ $i $primary && grep -s 'link: 1' $i/stats)
- secondary=$i
+ if(grep -s 'link: 1' $i/stats)
+ new=$i
}
- if(! ~ $secondary $old){
- echo $primary is switching from $old to $secondary >[1=2]
-
- if(! ~ $type none){
- echo unbind $type secondary 0
+ if(! ~ $new $old){
+ if(! ~ $old /dev/null){
+ if(! ~ $old $primary) {
+ echo unbind bypass primary 0
+ echo unbind ether secondary 0
+ }
}
-
- # if the secondary has the same ea as the primary,
- # we need to bind it in non-bridge mode
- type=ether
- if(~ $ea `{cat $secondary/addr})
- type=ethermac
-
- echo bind $type secondary 0 $secondary
-
+ if(! ~ $new $primary){
+ echo bind bypass primary 0 $primary
+ echo bind ether secondary 0 $new
+ }
# make switches aware of the new path
echo flush > $net/arp
+
+ old=$new
}
- old=$secondary
sleep 1
}
} </dev/null &
--- a/sys/man/3/bridge
+++ b/sys/man/3/bridge
@@ -67,11 +67,12 @@
.B vlan
command below.
.TP
-.BI "bind ethermac " "name ownhash path [pvid[#prio][,vlans...]]"
+.BI "bind bypass " "name ownhash path [pvid[#prio][,vlans...]]"
This is the same as
.I ether
-above, but forwards all frames, including frames destined to the
-interfaces mac address.
+above, but bypasses the physical interface, ignoring all received
+frames from the interface and diverts all frames
+transmitted on that interface to the bridge.
.TP
.BI "bind tunnel " "name ownhash path path2 [pvid[#prio][,vlans...]]"
Treat the device mounted at
--- a/sys/man/8/ethermultilink
+++ b/sys/man/8/ethermultilink
@@ -15,13 +15,13 @@
ethernet interface and a list of
.I secondary
interfaces in increasing priority order.
-It outputs
+It checks the link status of the
+secondary interfaces and if any are
+link-active, bridges the primary
+and the highest priority secondary
+interface together by outputting
.IR bridge (4)
-commands that add the
-.I primary
-and the highest priority link-active
-.I secondary
-interface to the bridge.
+commands on standard output.
The link status of all the secondary interfaces
is checked once per second and the commands to
change the active secondary are output as needed.
@@ -33,22 +33,12 @@
bind -a '#l0' /net
# mount the wifi
bind -a '#l1' /net
-# create the new virtual interface with same mac as wifi
-bind -a '#l2:sink ea='^`{cat /net/ether1/addr} /net
# roam between wifi and ethernet
-ethermultilink /net/ether2 /net/ether1 /net/ether0 > /net/bridge0/ctl
-# use the new virtual interface...
-ip/ipconfig ether /net/ether2
+ethermultilink /net/ether1 /net/ether0 > /net/bridge0/ctl
+# setup ip on the primary
+ip/ipconfig ether /net/ether1
.EE
.SH SOURCE
.B /rc/bin/ethermultilink
.SH "SEE ALSO"
.IR bridge (3).
-.SH BUGS
-The
-.I secondary
-interfaces should not
-be bound to an IP stack,
-only the
-.I primary
-interface should be used.
--- a/sys/src/9/boot/net.rc
+++ b/sys/src/9/boot/net.rc
@@ -44,9 +44,8 @@
# ...make them into a multilink bridge
if(! ~ $#e 1){
bind -a '#B15' /net
- bind -a '#l15:sink ea='^`{cat $e(1)^/addr} /net
- ethermultilink /net/ether15 $e > /net/bridge15/ctl
- e=/net/ether15
+ ethermultilink $e > /net/bridge15/ctl
+ e=$e(1)
}
*=($t $e $*)
}
--- a/sys/src/9/port/devbridge.c
+++ b/sys/src/9/port/devbridge.c
@@ -69,11 +69,13 @@
enum {
Tether,
+ Tbypass,
Ttun,
};
static char *typstr[] = {
"ether",
+ "bypass",
"tunnel",
};
@@ -649,6 +651,7 @@
default:
error(usage);
case Tether:
+ case Tbypass:
if(argc > 4)
vlan = argv[4];
break;
@@ -682,6 +685,7 @@
default:
panic("portbind: unknown port type: %d", type);
case Tether:
+ case Tbypass:
snprint(path, sizeof(path), "%s/clone", dev);
ctl = namec(path, Aopen, ORDWR, 0);
if(waserror()) {
@@ -699,8 +703,14 @@
devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
snprint(buf, sizeof(buf), "nonblocking");
devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
- snprint(buf, sizeof(buf), "promiscuous");
- devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
+
+ if(type == Tbypass){
+ snprint(buf, sizeof(buf), "bypass");
+ devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
+ } else {
+ snprint(buf, sizeof(buf), "promiscuous");
+ devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
+ }
snprint(buf, sizeof(buf), "bridge");
devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
--- a/sys/src/9/port/devether.c
+++ b/sys/src/9/port/devether.c
@@ -172,6 +172,7 @@
len = BLEN(bp);
if(len < ETHERHDRSIZE)
goto Drop;
+
pkt = (Etherpkt*)bp->rp;
if(!(multi = pkt->d[0] & 1)){
tome = memcmp(pkt->d, ether->ea, Eaddrlen) == 0;
@@ -210,7 +211,7 @@
continue;
if(!tome && !multi && !f->prom)
continue;
- if(f->bridge){
+ if(f->bridge || f->bypass){
if(tome || fp == from)
continue;
if(port >= 0 && port != 1+(fp - ether->f))
@@ -247,6 +248,10 @@
void
etheriq(Ether* ether, Block* bp)
{
+ if(ether->bypass != nil){
+ freeb(bp);
+ return;
+ }
ether->inpackets++;
ethermux(ether, bp, nil);
}
@@ -254,6 +259,8 @@
static void
etheroq(Ether* ether, Block* bp, Netfile **from)
{
+ Netfile *x;
+
if((*from)->bridge == 0)
memmove(((Etherpkt*)bp->rp)->s, ether->ea, Eaddrlen);
@@ -260,7 +267,11 @@
bp = ethermux(ether, bp, from);
if(bp == nil)
return;
-
+ if((x = ether->bypass) != nil){
+ if(qpass(x->in, bp) < 0)
+ ether->soverflows++;
+ return;
+ }
ether->outpackets++;
qbwrite(ether->oq, bp);
if(ether->transmit != nil)
--- a/sys/src/9/port/netif.c
+++ b/sys/src/9/port/netif.c
@@ -334,6 +334,11 @@
}
} else if(matchtoken(buf, "bridge")){
f->bridge = 1;
+ } else if(matchtoken(buf, "bypass")){
+ if(nif->bypass != nil)
+ error(Einuse);
+ f->bypass = 1;
+ nif->bypass = f;
} else if(matchtoken(buf, "headersonly")){
f->headersonly = 1;
} else if((p = matchtoken(buf, "addmulti")) != 0){
@@ -408,6 +413,12 @@
f = nif->f[NETID(c->qid.path)];
qlock(f);
if(--(f->inuse) == 0){
+ if(f->bypass){
+ qlock(nif);
+ nif->bypass = nil;
+ qunlock(nif);
+ f->bypass = 0;
+ }
if(f->prom){
qlock(nif);
if(--(nif->prom) == 0 && nif->promiscuous != nil)
--- a/sys/src/9/port/netif.h
+++ b/sys/src/9/port/netif.h
@@ -38,10 +38,13 @@
char owner[KNAMELEN];
int type; /* multiplexor type */
- int prom; /* promiscuous mode */
- int scan; /* base station scanning interval */
- int bridge; /* bridge mode */
- int headersonly; /* headers only - no data */
+
+ char prom; /* promiscuous mode */
+ char scan; /* base station scanning interval */
+ char bridge; /* bridge mode */
+ char bypass; /* bypass transmission */
+ char headersonly; /* headers only - no data */
+
uchar maddr[8]; /* bitmask of multicast addresses requested */
int nmaddr; /* number of multicast addresses */
@@ -70,6 +73,7 @@
char name[KNAMELEN]; /* for top level directory */
int nfile; /* max number of Netfiles */
Netfile **f;
+ Netfile *bypass;
/* about net */
int limit; /* flow control */
--- a/sys/src/cmd/nusb/ether/ether.c
+++ b/sys/src/cmd/nusb/ether/ether.c
@@ -66,10 +66,12 @@
int used;
int type;
- int prom;
- int bridge;
- int headersonly;
+ char prom;
+ char bridge;
+ char bypass;
+ char headersonly;
+
Dq *dq;
};
@@ -83,6 +85,7 @@
Stats stats;
Conn conn[32];
+Conn *bypass;
int nconn = 0;
int nprom = 0;
@@ -397,6 +400,13 @@
p = (char*)r->ifcall.data;
if(n >= 6 && memcmp(p, "bridge", 6)==0){
conn[NUM(path)].bridge = 1;
+ } else if(n >= 6 && memcmp(p, "bypass", 6)==0){
+ if(bypass != nil){
+ respond(r, "bypass in use");
+ return;
+ }
+ conn[NUM(path)].bypass = 1;
+ bypass = &conn[NUM(path)];
} else if(n >= 11 && memcmp(p, "headersonly", 11)==0){
conn[NUM(path)].headersonly = 1;
} else if(n >= 11 && memcmp(p, "promiscuous", 11)==0){
@@ -506,6 +516,7 @@
c->type = 0;
c->prom = 0;
c->bridge = 0;
+ c->bypass = 0;
c->headersonly = 0;
}
if(d != nil){
@@ -581,7 +592,10 @@
}
if(TYPE(fid->qid.path) == Qdata && c->bridge)
memset(mactab, 0, sizeof(mactab));
- c->used--;
+ if(--c->used == 0){
+ if(c->bypass)
+ bypass = nil;
+ }
qunlock(c);
}
}
@@ -791,7 +805,7 @@
continue;
if(!tome && !multi && !c->prom)
continue;
- if(c->bridge){
+ if(c->bridge || c->bypass){
if(tome || c == from)
continue;
if(port >= 0 && port != 1+(c - conn))
@@ -821,6 +835,10 @@
void
etheriq(Block *bp)
{
+ if(bypass != nil){
+ freeb(bp);
+ return;
+ }
stats.in++;
ethermux(bp, nil);
}
@@ -828,11 +846,17 @@
static void
etheroq(Block *bp, Conn *from)
{
+ Conn *x;
+
if(!from->bridge)
memmove(((Etherpkt*)bp->rp)->s, macaddr, Eaddrlen);
bp = ethermux(bp, from);
if(bp == nil)
return;
+ if((x = bypass) != nil){
+ cpass(x, bp);
+ return;
+ }
stats.out++;
/* transmit frees buffer */
(*eptransmit)(epout, bp);