mirror of
git://git.9front.org/plan9front/plan9front
synced 2025-01-12 11:10:06 +00:00
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.
This commit is contained in:
parent
06ba01b816
commit
216ecc5460
9 changed files with 103 additions and 66 deletions
|
@ -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 @@ for(i){
|
|||
|
||||
# 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(! ~ $new $primary){
|
||||
echo bind bypass primary 0 $primary
|
||||
echo bind ether secondary 0 $new
|
||||
}
|
||||
|
||||
# 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
|
||||
|
||||
# make switches aware of the new path
|
||||
echo flush > $net/arp
|
||||
|
||||
old=$new
|
||||
}
|
||||
old=$secondary
|
||||
sleep 1
|
||||
}
|
||||
} </dev/null &
|
||||
|
|
|
@ -67,11 +67,12 @@ argument is explained in
|
|||
.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
|
||||
|
|
|
@ -15,13 +15,13 @@ Ethermultilink takes a
|
|||
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 '#B' /net
|
|||
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.
|
||||
|
|
|
@ -44,9 +44,8 @@ fn confignet{
|
|||
# ...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 $*)
|
||||
}
|
||||
|
|
|
@ -69,11 +69,13 @@ static Logflag logflags[] = {
|
|||
|
||||
enum {
|
||||
Tether,
|
||||
Tbypass,
|
||||
Ttun,
|
||||
};
|
||||
|
||||
static char *typstr[] = {
|
||||
"ether",
|
||||
"bypass",
|
||||
"tunnel",
|
||||
};
|
||||
|
||||
|
@ -649,6 +651,7 @@ portbind(Bridge *b, int argc, char *argv[])
|
|||
default:
|
||||
error(usage);
|
||||
case Tether:
|
||||
case Tbypass:
|
||||
if(argc > 4)
|
||||
vlan = argv[4];
|
||||
break;
|
||||
|
@ -682,6 +685,7 @@ portbind(Bridge *b, int argc, char *argv[])
|
|||
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 @@ portbind(Bridge *b, int argc, char *argv[])
|
|||
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);
|
||||
|
||||
|
|
|
@ -172,6 +172,7 @@ ethermux(Ether *ether, Block *bp, Netfile **from)
|
|||
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 @@ ethermux(Ether *ether, Block *bp, Netfile **from)
|
|||
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 @@ Drop: freeb(bp);
|
|||
void
|
||||
etheriq(Ether* ether, Block* bp)
|
||||
{
|
||||
if(ether->bypass != nil){
|
||||
freeb(bp);
|
||||
return;
|
||||
}
|
||||
ether->inpackets++;
|
||||
ethermux(ether, bp, nil);
|
||||
}
|
||||
|
@ -254,13 +259,19 @@ etheriq(Ether* ether, Block* bp)
|
|||
static void
|
||||
etheroq(Ether* ether, Block* bp, Netfile **from)
|
||||
{
|
||||
Netfile *x;
|
||||
|
||||
if((*from)->bridge == 0)
|
||||
memmove(((Etherpkt*)bp->rp)->s, ether->ea, Eaddrlen);
|
||||
|
||||
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)
|
||||
|
|
|
@ -334,6 +334,11 @@ netifwrite(Netif *nif, Chan *c, void *a, long n)
|
|||
}
|
||||
} 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 @@ netifclose(Netif *nif, Chan *c)
|
|||
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)
|
||||
|
|
|
@ -38,10 +38,13 @@ struct Netfile
|
|||
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 @@ struct Netif
|
|||
char name[KNAMELEN]; /* for top level directory */
|
||||
int nfile; /* max number of Netfiles */
|
||||
Netfile **f;
|
||||
Netfile *bypass;
|
||||
|
||||
/* about net */
|
||||
int limit; /* flow control */
|
||||
|
|
|
@ -66,9 +66,11 @@ struct Conn
|
|||
|
||||
int used;
|
||||
int type;
|
||||
int prom;
|
||||
int bridge;
|
||||
int headersonly;
|
||||
|
||||
char prom;
|
||||
char bridge;
|
||||
char bypass;
|
||||
char headersonly;
|
||||
|
||||
Dq *dq;
|
||||
};
|
||||
|
@ -83,6 +85,7 @@ uchar bcast[Eaddrlen] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|||
|
||||
Stats stats;
|
||||
Conn conn[32];
|
||||
Conn *bypass;
|
||||
int nconn = 0;
|
||||
|
||||
int nprom = 0;
|
||||
|
@ -397,6 +400,13 @@ fswrite(Req *r)
|
|||
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 @@ fsopen(Req *r)
|
|||
c->type = 0;
|
||||
c->prom = 0;
|
||||
c->bridge = 0;
|
||||
c->bypass = 0;
|
||||
c->headersonly = 0;
|
||||
}
|
||||
if(d != nil){
|
||||
|
@ -581,7 +592,10 @@ fsdestroyfid(Fid *fid)
|
|||
}
|
||||
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 @@ ethermux(Block *bp, Conn *from)
|
|||
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 @@ Drop: freeb(bp);
|
|||
void
|
||||
etheriq(Block *bp)
|
||||
{
|
||||
if(bypass != nil){
|
||||
freeb(bp);
|
||||
return;
|
||||
}
|
||||
stats.in++;
|
||||
ethermux(bp, nil);
|
||||
}
|
||||
|
@ -828,11 +846,17 @@ etheriq(Block *bp)
|
|||
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);
|
||||
|
|
Loading…
Reference in a new issue