shithub: riscv

Download patch

ref: 216ecc546034247d9281462c605b4938971e9b98
parent: 06ba01b81692aa3eea9a8bc99799d9253cb44518
author: cinap_lenrek <[email protected]>
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);