summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fw/src/fw.c155
-rwxr-xr-xfw/test/fwtest.rc51
2 files changed, 147 insertions, 59 deletions
diff --git a/fw/src/fw.c b/fw/src/fw.c
index 1fb88bd..8901c2a 100644
--- a/fw/src/fw.c
+++ b/fw/src/fw.c
@@ -1126,6 +1126,36 @@ etherout(void *a)
}
/*
+ * The first n lines of a Biobuf, copied out, since Brdline's buffer is
+ * only good until the next one. Returns how many there were.
+ */
+static int
+rdline(Biobuf *b, char **lines, int n)
+{
+ char *p;
+ int i;
+
+ for(i = 0; i < n; i++){
+ if((p = Brdline(b, '\n')) == nil)
+ break;
+ p[Blinelen(b)-1] = '\0';
+ lines[i] = estrdup(p);
+ }
+ return i;
+}
+
+static void
+freelines(char **lines, int n)
+{
+ int i;
+
+ for(i = 0; i < n; i++){
+ free(lines[i]);
+ lines[i] = nil;
+ }
+}
+
+/*
* The address, mask and gateway the stack is already using. Asking the
* administrator to repeat what ipconfig(8) was told is a way of getting
* the two out of step, so read them instead: the interface status names
@@ -1134,52 +1164,53 @@ etherout(void *a)
static int
learnaddr(char *net, char *dev, char *addr, int naddr, char *gw, int ngw)
{
- char path[128], buf[1024], *lines[8], *f[8], *p;
- int i, fd, n, nl, nf, found;
+ char path[128], *lines[2], *f[8], *p;
+ Biobuf *b;
+ int i, nf, found;
found = 0;
for(i = 0; i < 16 && !found; i++){
snprint(path, sizeof path, "%s/ipifc/%d/status", net, i);
- if((fd = open(path, OREAD)) < 0)
- continue;
- n = read(fd, buf, sizeof buf - 1);
- close(fd);
- if(n <= 0)
- continue;
- buf[n] = '\0';
- if((p = strstr(buf, dev)) == nil)
- continue;
- USED(p);
- nl = getfields(buf, lines, nelem(lines), 0, "\n");
- if(nl < 2)
+ if((b = Bopen(path, OREAD)) == nil)
continue;
- /* the address line: address, then the mask as a prefix */
- nf = tokenize(lines[1], f, nelem(f));
- if(nf < 2)
- continue;
- snprint(addr, naddr, "%s%s", f[0], f[1]);
- found = 1;
+ /*
+ * The device line, then the first address line. Read, not
+ * searched: the whole status text used to be scanned for the
+ * device name, which an address could satisfy, and it was
+ * read into a fixed buffer that a few addresses would fill.
+ */
+ if(rdline(b, lines, 2) == 2 && strstr(lines[0], dev) != nil){
+ nf = tokenize(lines[1], f, nelem(f));
+ if(nf >= 2){
+ snprint(addr, naddr, "%s%s", f[0], f[1]);
+ found = 1;
+ }
+ }
+ freelines(lines, 2);
+ Bterm(b);
}
if(!found)
return -1;
+ /*
+ * The default route names the gateway. A line at a time: a
+ * routing table is as long as it is, and reading 1024 bytes of it
+ * meant the default route could be off the end and the gateway
+ * silently unknown.
+ */
*gw = '\0';
snprint(path, sizeof path, "%s/iproute", net);
- if((fd = open(path, OREAD)) >= 0){
- n = read(fd, buf, sizeof buf - 1);
- close(fd);
- if(n > 0){
- buf[n] = '\0';
- nl = getfields(buf, lines, nelem(lines), 0, "\n");
- for(i = 0; i < nl; i++){
- nf = tokenize(lines[i], f, nelem(f));
- if(nf >= 3 && strcmp(f[0], "0.0.0.0") == 0
- && strcmp(f[1], "/96") == 0){
- snprint(gw, ngw, "%s", f[2]);
- break;
- }
+ if((b = Bopen(path, OREAD)) != nil){
+ while((p = Brdline(b, '\n')) != nil){
+ p[Blinelen(b)-1] = '\0';
+ nf = tokenize(p, f, nelem(f));
+ if(nf >= 3 && strcmp(f[0], "0.0.0.0") == 0
+ && strcmp(f[1], "/96") == 0){
+ snprint(gw, ngw, "%s", f[2]);
+ break;
}
}
+ Bterm(b);
}
return 0;
}
@@ -1207,27 +1238,31 @@ learnaddr(char *net, char *dev, char *addr, int naddr, char *gw, int ngw)
static void
reclaim(char *net, char *addr)
{
- char path[128], buf[1024], *lines[8], *f[8];
- int i, fd, n, nl, nf;
+ char path[128], *lines[2], *f[8];
+ Biobuf *b;
+ int i, fd, nf;
for(i = 0; i < 16; i++){
snprint(path, sizeof path, "%s/ipifc/%d/status", net, i);
- if((fd = open(path, OREAD)) < 0)
- continue;
- n = read(fd, buf, sizeof buf - 1);
- close(fd);
- if(n <= 0)
+ if((b = Bopen(path, OREAD)) == nil)
continue;
- buf[n] = '\0';
- nl = getfields(buf, lines, nelem(lines), 0, "\n");
- if(nl < 2)
+ nf = rdline(b, lines, 2);
+ Bterm(b);
+ if(nf != 2){
+ freelines(lines, 2);
continue;
+ }
nf = tokenize(lines[0], f, nelem(f));
- if(nf < 2 || strncmp(f[1], "pkt", 3) != 0)
+ if(nf < 2 || strncmp(f[1], "pkt", 3) != 0){
+ freelines(lines, 2);
continue;
+ }
nf = tokenize(lines[1], f, nelem(f));
- if(nf < 1 || strcmp(f[0], addr) != 0)
+ if(nf < 1 || strcmp(f[0], addr) != 0){
+ freelines(lines, 2);
continue;
+ }
+ freelines(lines, 2);
snprint(path, sizeof path, "%s/ipifc/%d/ctl", net, i);
if((fd = open(path, OWRITE)) < 0)
@@ -1249,26 +1284,30 @@ reclaim(char *net, char *addr)
static void
takecard(char *net, char *dev)
{
- char path[128], buf[512], *p;
- int i, fd, n, found;
+ char path[128], *lines[1], *f[8];
+ Biobuf *b;
+ int i, fd, nf, found;
found = 0;
for(i = 0; i < 16; i++){
snprint(path, sizeof path, "%s/ipifc/%d/status", net, i);
- if((fd = open(path, OREAD)) < 0)
- continue;
- n = read(fd, buf, sizeof buf - 1);
- close(fd);
- if(n <= 0)
+ if((b = Bopen(path, OREAD)) == nil)
continue;
- buf[n] = '\0';
- if((p = strchr(buf, ' ')) == nil)
+ nf = rdline(b, lines, 1);
+ Bterm(b);
+ if(nf != 1)
continue;
- *p = '\0';
- if(strcmp(buf, "device") != 0 && strstr(buf, dev) == nil)
- continue;
- if(strstr(p+1, dev) == nil && strstr(buf, dev) == nil)
+ /*
+ * "device <name> maxtu ...". The name, not the rest of the
+ * status: an address that happened to contain the device's
+ * name used to match it.
+ */
+ nf = tokenize(lines[0], f, nelem(f));
+ if(nf < 2 || strstr(f[1], dev) == nil){
+ freelines(lines, 1);
continue;
+ }
+ freelines(lines, 1);
snprint(path, sizeof path, "%s/ipifc/%d/ctl", net, i);
if((fd = open(path, OWRITE)) < 0)
continue;
diff --git a/fw/test/fwtest.rc b/fw/test/fwtest.rc
index 8a5a085..0325bfd 100755
--- a/fw/test/fwtest.rc
+++ b/fw/test/fwtest.rc
@@ -46,7 +46,7 @@ mkdir -p $mtpt
# The stacks the packet checks build for themselves. Fixed numbers are
# fine because they are unbound again at the end of each block, and the
# last check proves it.
-stacks=(20 21 22 23 24 25)
+stacks=(20 21 22 23 24 25 26)
# A port of our own. Conversations outlive the run that made them - a
# devip Conv is never freed - so a fixed port makes one run's leftovers
@@ -502,6 +502,55 @@ echo '== a fragmented datagram crosses'
stopfw $nA $nB
}
+echo '== a card'
+# Card mode has never been tested, because taking the machine's card
+# away is how you end up with no network. A second card that nothing
+# is using can be taken safely: bound into a stack of our own, given an
+# address there, and handed to fw. Everything up to the wire is then
+# real - reading the address off the interface, taking the card,
+# putting a pkt interface in its place, restoring the route.
+@{
+ rfork n
+ bind -a '#l1' /net >[2]/dev/null
+ spare=no
+ if(test -e /net/ether1)
+ if(~ `{grep -c ether1 /net/ipifc/*/status >[2]/dev/null | awk '{n += $1} END {print n+0}'} 0)
+ spare=yes
+ if(~ $spare no)
+ echo ' skip no spare card to take'
+ if not {
+ mkdir -p $tmp/nC
+ bind -a '#I26' $tmp/nC
+ @{
+ conv=`{cat /fd/0}
+ echo -n 'bind ether /net/ether1' >[1=0]
+ echo -n 'add 10.9.9.1 255.255.255.0' >[1=0]
+ } <>[0] $tmp/nC/ipifc/clone
+ echo -n 'add 0.0.0.0 0.0.0.0 10.9.9.254' > $tmp/nC/iproute
+
+ # no -a and no -g: both come off the interface it is taking
+ # over, which is the whole point of not having to repeat them
+ $fw -n $tmp/nC -e /net/ether1 -m $mtpt $tmp/empty.ndb >[2] $tmp/carderr
+ sleep 2
+
+ r=`{grep -c '/net/ether1 has 10.9.9.1/120, gateway 10.9.9.254' $tmp/carderr}
+ check 'the address and gateway are read off the card' 1 $"r
+ r=`{grep -c 'took /net/ether1 away' $tmp/carderr}
+ check 'and the card is taken from the stack' 1 $"r
+
+ r=`{sed 1q $tmp/nC/ipifc/0/status | awk '{print $2}'}
+ check 'what the stack has now is a pkt interface' pkt0 $"r
+ r=`{sed 1q $tmp/nC/ipifc/0/status | awk '{print $4}'}
+ check 'at the mtu of the card it replaced' 1500 $"r
+ r=`{sed -n 2p $tmp/nC/ipifc/0/status | awk '{print $1}'}
+ check 'keeping the address' 10.9.9.1 $"r
+ r=`{awk '$1 == "0.0.0.0" {print $3}' $tmp/nC/iproute | sed 1q}
+ check 'and the route that went with the card' 10.9.9.254 $"r
+
+ stopfw $tmp/nC
+ }
+}
+
echo '== fwstart'
if(test -f $lib/fwstart)
@{