mirror of
git://git.9front.org/plan9front/plan9front
synced 2025-01-12 11:10:06 +00:00
ethermultilink: switch between different physical interfaces based on link status
This adds the capability of specifying multiple interfaces on bootargs like: bootargs=tls!ether /net/ether1 ether /net/ether0 ... which will be combined into a bridge and the new ethermultilink script can dynamically add/remove the interfaces based on link status. a ethersink interface is used as the primary, taking the mac address of the first secondary interface. this required some changes in how ethernets and bridges interact, as bridge mode on a ethernet interface would only forward frames not desinted to the interfaces mac address. we make promisc mode ethernet connections never loop-back the frames written them and we add a new "ethermac" type to devbridge that uses promisc mode only without setting bridge flag. that way, we can attach a ethernet to a bridge and get all its frames. the result is that we can specify the wifi interface as the first interface and ethernet as the second interface and the system will roam to ethernet transparently when the ethernet cable is plugged in and switch back to wifi when ethernet cable is disconnected.
This commit is contained in:
parent
2273121e1c
commit
c690c0b9f3
7 changed files with 139 additions and 24 deletions
75
rc/bin/ethermultilink
Executable file
75
rc/bin/ethermultilink
Executable file
|
@ -0,0 +1,75 @@
|
|||
#!/bin/rc
|
||||
|
||||
# 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
|
||||
|
||||
fn usage {
|
||||
echo 'Usage: ' $0 'primaryether secondaryether1 [secondaryether2 ....] > /net/bridgeX/ctl' >[1=2]
|
||||
exit 'usage'
|
||||
}
|
||||
fn missing {
|
||||
echo 'missing: ' $1 >[1=2]
|
||||
exit 'missing'
|
||||
}
|
||||
|
||||
|
||||
~ $#* 0 1 && usage
|
||||
|
||||
# make sure arguments are ethernets
|
||||
for(i){
|
||||
test -r $i/stats || missing $i/stats
|
||||
}
|
||||
|
||||
# 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
|
||||
while(){
|
||||
# interfaces are in increasing priority order
|
||||
for(i){
|
||||
if(! ~ $i $primary && grep -s 'link: 1' $i/stats)
|
||||
secondary=$i
|
||||
}
|
||||
if(! ~ $secondary $old){
|
||||
echo $primary is switching from $old to $secondary >[1=2]
|
||||
|
||||
if(! ~ $type none){
|
||||
echo unbind $type 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
|
||||
|
||||
# make switches aware of the new path
|
||||
echo flush > $net/arp
|
||||
}
|
||||
old=$secondary
|
||||
sleep 1
|
||||
}
|
||||
} </dev/null &
|
||||
|
||||
exit ''
|
|
@ -67,6 +67,12 @@ argument is explained in
|
|||
.B vlan
|
||||
command below.
|
||||
.TP
|
||||
.BI "bind ethermac " "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.
|
||||
.TP
|
||||
.BI "bind tunnel " "name ownhash path path2 [pvid[#prio][,vlans...]]"
|
||||
Treat the device mounted at
|
||||
.I path
|
||||
|
|
|
@ -54,6 +54,7 @@ rc
|
|||
bin
|
||||
fstype
|
||||
diskparts
|
||||
ethermultilink
|
||||
srvtls
|
||||
nusbrc 555 sys sys ../boot/nusbrc
|
||||
bootrc 555 sys sys ../boot/bootrc
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
#!/bin/rc
|
||||
|
||||
fn wifi{
|
||||
if(grep -s '^essid: ' $1/ifstats){
|
||||
if(~ $#essid 0)
|
||||
essid=`{grep '^essid: ' $1/ifstats >[2]/dev/null | sed 's/^essid: //; q'}
|
||||
if(! ~ $#essid 0){
|
||||
x=(aux/wpa -s $"essid)
|
||||
if(! ~ $#wpapsk 0){
|
||||
echo 'key proto=wpapsk' `{!password=$"wpapsk whatis essid !password} > /mnt/factotum/ctl
|
||||
wpapsk=()
|
||||
}
|
||||
if not {
|
||||
x=($x -p)
|
||||
}
|
||||
$x $1
|
||||
}
|
||||
essid=()
|
||||
rm -f /env/^(essid wpapsk)
|
||||
}
|
||||
}
|
||||
|
||||
fn confignet{
|
||||
# get primary default interface if not specified
|
||||
if(~ $#* 0){
|
||||
|
@ -8,24 +28,27 @@ fn confignet{
|
|||
*=(ether $e(1))
|
||||
}
|
||||
|
||||
# setup wifi encryption if any
|
||||
if(~ $1 ether && test -x /bin/aux/wpa){
|
||||
essid=`{grep '^essid: ' $2/ifstats >[2]/dev/null | sed 's/^essid: //; q'}
|
||||
if(! ~ $#essid 0){
|
||||
if(! ~ $#wpapsk 0 || grep -s '^status: need authentication' $2/ifstats >[2]/dev/null){
|
||||
x=(aux/wpa -s $"essid)
|
||||
if(! ~ $#wpapsk 0){
|
||||
echo 'key proto=wpapsk' `{!password=$"wpapsk whatis essid !password} > /mnt/factotum/ctl
|
||||
wpapsk=()
|
||||
}
|
||||
if not {
|
||||
x=($x -p)
|
||||
}
|
||||
$x $2
|
||||
}
|
||||
essid=()
|
||||
# if ethernet, handle wifi and multilink
|
||||
if( ~ $1 ether gbe){
|
||||
t=$1
|
||||
shift
|
||||
wifi $1
|
||||
e=$1
|
||||
shift
|
||||
# if multiple ethernets specified...
|
||||
while(~ $1 ether gbe){
|
||||
shift
|
||||
e=($e $1)
|
||||
shift
|
||||
}
|
||||
rm -f /env/^(essid wpapsk)
|
||||
# ...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
|
||||
}
|
||||
*=($t $e $*)
|
||||
}
|
||||
|
||||
if(~ $1 ether gbe && ~ $#* 2) @{
|
||||
|
|
|
@ -69,11 +69,13 @@ static Logflag logflags[] = {
|
|||
|
||||
enum {
|
||||
Tether,
|
||||
Tethermac,
|
||||
Ttun,
|
||||
};
|
||||
|
||||
static char *typstr[] = {
|
||||
"ether",
|
||||
"ethermac",
|
||||
"tunnel",
|
||||
};
|
||||
|
||||
|
@ -649,6 +651,7 @@ portbind(Bridge *b, int argc, char *argv[])
|
|||
default:
|
||||
error(usage);
|
||||
case Tether:
|
||||
case Tethermac:
|
||||
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 Tethermac:
|
||||
snprint(path, sizeof(path), "%s/clone", dev);
|
||||
ctl = namec(path, Aopen, ORDWR, 0);
|
||||
if(waserror()) {
|
||||
|
@ -699,10 +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);
|
||||
snprint(buf, sizeof(buf), "bridge");
|
||||
devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
|
||||
|
||||
if(port->type != Tethermac){
|
||||
snprint(buf, sizeof(buf), "bridge");
|
||||
devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
|
||||
}
|
||||
|
||||
/* open data port */
|
||||
port->data[0] = namec(path, Aopen, ORDWR, 0);
|
||||
|
|
|
@ -204,6 +204,8 @@ ethermux(Ether *ether, Block *bp, Netfile **from)
|
|||
dispose = tome || from == nil || port > 0;
|
||||
|
||||
for(fp = ether->f; fp < ðer->f[Ntypes]; fp++){
|
||||
if(fp == from)
|
||||
continue;
|
||||
if((f = *fp) == nil)
|
||||
continue;
|
||||
if(f->type != type && f->type >= 0)
|
||||
|
@ -211,7 +213,7 @@ ethermux(Ether *ether, Block *bp, Netfile **from)
|
|||
if(!tome && !multi && !f->prom)
|
||||
continue;
|
||||
if(f->bridge){
|
||||
if(tome || fp == from)
|
||||
if(tome)
|
||||
continue;
|
||||
if(port >= 0 && port != 1+(fp - ether->f))
|
||||
continue;
|
||||
|
@ -254,7 +256,7 @@ etheriq(Ether* ether, Block* bp)
|
|||
static void
|
||||
etheroq(Ether* ether, Block* bp, Netfile **from)
|
||||
{
|
||||
if((*from)->bridge == 0)
|
||||
if((*from)->prom == 0)
|
||||
memmove(((Etherpkt*)bp->rp)->s, ether->ea, Eaddrlen);
|
||||
|
||||
bp = ethermux(ether, bp, from);
|
||||
|
|
|
@ -785,14 +785,14 @@ ethermux(Block *bp, Conn *from)
|
|||
dispose = tome || from == nil || port > 0;
|
||||
|
||||
for(c = conn; c < &conn[nconn]; c++){
|
||||
if(!c->used)
|
||||
if(!c->used || c == from)
|
||||
continue;
|
||||
if(c->type != type && c->type >= 0)
|
||||
continue;
|
||||
if(!tome && !multi && !c->prom)
|
||||
continue;
|
||||
if(c->bridge){
|
||||
if(tome || c == from)
|
||||
if(tome)
|
||||
continue;
|
||||
if(port >= 0 && port != 1+(c - conn))
|
||||
continue;
|
||||
|
@ -828,7 +828,7 @@ etheriq(Block *bp)
|
|||
static void
|
||||
etheroq(Block *bp, Conn *from)
|
||||
{
|
||||
if(!from->bridge)
|
||||
if(!from->prom)
|
||||
memmove(((Etherpkt*)bp->rp)->s, macaddr, Eaddrlen);
|
||||
bp = ethermux(bp, from);
|
||||
if(bp == nil)
|
||||
|
|
Loading…
Reference in a new issue