ref: 298f2396957bea59cf0985227a6dd903813b5938
parent: b2599999be1d51eedc0e11a15cec9e7fac253250
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Thu May 10 15:31:58 EDT 2018
ip: add some primitive rate limiting knobs to counteract bufferbloat
--- a/sys/man/3/ip
+++ b/sys/man/3/ip
@@ -194,6 +194,17 @@
The mtu is the maximum size of the packet including any
medium-specific headers.
.TP
+.BI speed\ n
+Set the maximum transmit speed in bits per second.
+TP
+.BI delay\ n
+Set the maximum burst delay in milliseconds. (Default is 40ms)
+When
+.B speed
+has been set and packets in flight exceed the maximum burst
+delay then packets send on the interface are discarded until
+the load drops below the maximum.
+.TP
.BI iprouting\ n
Allow
.RI ( n
--- a/sys/src/9/ip/arp.c
+++ b/sys/src/9/ip/arp.c
@@ -303,7 +303,7 @@
freeblistchain(next);
break;
}
- ifc->m->bwrite(ifc, concatblock(bp), version, ip);
+ ipifcoput(ifc, bp, version, ip);
poperror();
}
return 1;
--- a/sys/src/9/ip/ip.c
+++ b/sys/src/9/ip/ip.c
@@ -180,6 +180,7 @@
runlock(ifc);
nexterror();
}
+
if(ifc->m == nil)
goto raise;
@@ -196,7 +197,7 @@
eh->cksum[0] = 0;
eh->cksum[1] = 0;
hnputs(eh->cksum, ipcsum(&eh->vihl));
- ifc->m->bwrite(ifc, concatblock(bp), V4, gate);
+ ipifcoput(ifc, bp, V4, gate);
runlock(ifc);
poperror();
return 0;
@@ -280,7 +281,7 @@
feh->cksum[0] = 0;
feh->cksum[1] = 0;
hnputs(feh->cksum, ipcsum(&feh->vihl));
- ifc->m->bwrite(ifc, nb, V4, gate);
+ ipifcoput(ifc, nb, V4, gate);
ip->stats[FragCreates]++;
}
ip->stats[FragOKs]++;
--- a/sys/src/9/ip/ip.h
+++ b/sys/src/9/ip/ip.h
@@ -327,6 +327,12 @@
uchar recvra6; /* flag: recv router advs on this ifc */
Routerparams rp; /* router parameters as in RFC 2461, pp.40—43.
used only if node is router */
+
+ int speed; /* link speed in bits per second */
+ int delay; /* burst delay in ms */
+ int burst; /* burst delay in bytes */
+ int load; /* bytes in flight */
+ ulong ticks;
};
/*
@@ -652,6 +658,7 @@
*/
extern Medium* ipfindmedium(char *name);
extern void addipmedium(Medium *med);
+extern void ipifcoput(Ipifc *ifc, Block *bp, int version, uchar *ip);
extern int ipforme(Fs*, uchar *addr);
extern int ipismulticast(uchar *ip);
extern Ipifc* findipifc(Fs*, uchar *local, uchar *remote, int type);
--- a/sys/src/9/ip/ipifc.c
+++ b/sys/src/9/ip/ipifc.c
@@ -250,7 +250,7 @@
char sfixedformat[] = "device %s maxtu %d sendra %d recvra %d mflag %d oflag"
" %d maxraint %d minraint %d linkmtu %d reachtime %d rxmitra %d ttl %d routerlt"
-" %d pktin %lud pktout %lud errin %lud errout %lud\n";
+" %d pktin %lud pktout %lud errin %lud errout %lud speed %d delay %d\n";
char slineformat[] = " %-40I %-10M %-40I %-12lud %-12lud\n";
@@ -267,7 +267,8 @@
ifc->rp.mflag, ifc->rp.oflag, ifc->rp.maxraint,
ifc->rp.minraint, ifc->rp.linkmtu, ifc->rp.reachtime,
ifc->rp.rxmitra, ifc->rp.ttl, ifc->rp.routerlt,
- ifc->in, ifc->out, ifc->inerr, ifc->outerr);
+ ifc->in, ifc->out, ifc->inerr, ifc->outerr,
+ ifc->speed, ifc->delay);
rlock(ifc);
for(lifc = ifc->lifc; lifc != nil && n > m; lifc = lifc->next)
@@ -309,6 +310,50 @@
return ifc->m != nil;
}
+static void
+ipifcsetdelay(Ipifc *ifc, int delay)
+{
+ if(delay < 0)
+ delay = 0;
+ else if(delay > 1000)
+ delay = 1000;
+ ifc->delay = delay;
+ ifc->burst = ((vlong)delay * ifc->speed) / 8000;
+ if(ifc->burst < ifc->maxtu)
+ ifc->burst = ifc->maxtu;
+}
+
+static void
+ipifcsetspeed(Ipifc *ifc, int speed)
+{
+ if(speed < 0)
+ speed = 0;
+ ifc->speed = speed;
+ ifc->load = 0;
+ ipifcsetdelay(ifc, ifc->delay);
+}
+
+void
+ipifcoput(Ipifc *ifc, Block *bp, int version, uchar *ip)
+{
+ if(ifc->speed){
+ ulong now = MACHP(0)->ticks;
+ int dt = TK2MS(now - ifc->ticks);
+ ifc->ticks = now;
+ ifc->load -= ((vlong)dt * ifc->speed) / 8000;
+ if(ifc->load < 0 || dt < 0 || dt > 1000)
+ ifc->load = 0;
+ else if(ifc->load > ifc->burst){
+ freeblist(bp);
+ return;
+ }
+ }
+ bp = concatblock(bp);
+ ifc->load += BLEN(bp);
+ ifc->m->bwrite(ifc, bp, version, ip);
+}
+
+
/*
* called when a process writes to an interface's 'data'
*/
@@ -358,6 +403,8 @@
ifc->m = nil;
ifc->reflect = 0;
ifc->reassemble = 0;
+ ipifcsetspeed(ifc, 0);
+ ipifcsetdelay(ifc, 40);
}
/*
@@ -772,6 +819,14 @@
return ipifcunbind(ifc);
else if(strcmp(argv[0], "mtu") == 0)
return ipifcsetmtu(ifc, argv, argc);
+ else if(strcmp(argv[0], "speed") == 0){
+ ipifcsetspeed(ifc, argc>1? atoi(argv[1]): 0);
+ return nil;
+ }
+ else if(strcmp(argv[0], "delay") == 0){
+ ipifcsetdelay(ifc, argc>1? atoi(argv[1]): 0);
+ return nil;
+ }
else if(strcmp(argv[0], "iprouting") == 0){
iprouting(c->p->f, argc>1? atoi(argv[1]): 1);
return nil;
--- a/sys/src/9/ip/ipv6.c
+++ b/sys/src/9/ip/ipv6.c
@@ -103,7 +103,7 @@
medialen = ifc->maxtu - ifc->m->hsize;
if(len <= medialen) {
hnputs(eh->ploadlen, len - IP6HDR);
- ifc->m->bwrite(ifc, concatblock(bp), V6, gate);
+ ipifcoput(ifc, bp, V6, gate);
runlock(ifc);
poperror();
return 0;
@@ -193,8 +193,7 @@
if(xp->rp == xp->wp)
xp = xp->next;
}
-
- ifc->m->bwrite(ifc, nb, V6, gate);
+ ipifcoput(ifc, nb, V6, gate);
ip->stats[FragCreates]++;
}
ip->stats[FragOKs]++;