/* * simple client RIP implementation * bugs to: forsyth@plan9.cs.york.ac.uk */ #include #include #include #include #include typedef struct Gateway Gateway; typedef uchar Ipaddr[4]; typedef struct Ipdest Ipdest; typedef struct Iproute Iproute; typedef struct Riphdr Riphdr; typedef struct Udphdr Udphdr; struct Ipdest { uchar iptype[2]; uchar pad[2]; uchar addr[4]; uchar zero[8]; }; #define AF_INET {0,2} /* type of Internet address */ #define Ipdestlen 16 struct Iproute { Ipdest; long metric; }; struct Riphdr { uchar op; uchar version; uchar pad[2]; /* 32-bit alignment (on the wire) */ /* followed by Iproutes or trace file name */ }; #define Riphdrlen 4 enum { Version = 1, /* protocol version */ /* operations */ OpRequest = 1, /* want route */ OpReply = 2, /* all or part of route table */ OpTraceOn = 3, /* obsolete */ OpTraceOff = 4, /* obsolete */ OpReserved = 5, /* used by Sun */ Maxop = 5, HopLimit = 16, /* defined by protocol as `infinity' */ RoutesInPkt = 25, /* limit defined by protocol */ RIPport = 520, Expired = 180, Discard = 240, OutputRate = 30, /* seconds between routing table transmissions */ NetworkCost = 1, /* assume the simple case */ Nroutes = 128, }; /* * structure used by #Iudp in `headers' mode */ struct Udphdr { Ipaddr addr; uchar port[2]; }; #define Udphdrlen 6 struct Gateway { int valid; int changed; int local; long time; long metric; Ipaddr dest; Ipaddr mask; Ipaddr gateway; Gateway *next; }; int netfd; uchar buf[8192]; int routefd; uchar iptype[] = AF_INET; Gateway routes[Nroutes]; Gateway *defroute; int nroutes; int debug; int restart; char logfile[] = "iproute"; Biobuf rbin; Ipinfo myipinfo; Ipaddr myip; void fatal(char*, ...); Gateway *lookup(Iproute*); void addroute(Gateway*); void delroute(Gateway*); void sendroutes(Udphdr*, int, int); int udplisten(char*); int udphdrconv(void*, Fconv*); void maskgen(Ipaddr, Ipaddr); int isbroadcast(Ipaddr, Ipaddr); int isnet(Ipaddr, Ipaddr); int ishost(Ipaddr, Ipaddr); int iszero(Ipaddr); void main(int argc, char **argv) { int nb; Riphdr rip; Iproute dest; Ipaddr destmask; uchar *bp; Gateway *rp; long now; Udphdr from; char *fields[6], *l, ipbuf[40]; Ndb *db; ARGBEGIN{ case 'd': debug++; break; case 'r': restart++; break; }ARGEND fmtinstall('I', eipconv); fmtinstall('U', udphdrconv); syslog(0, logfile, "started"); if(myipaddr(myip, "/net/udp") < 0) fatal("can't get my IP address from /net/udp: %r"); /* get local info from the database */ db = ndbopen(0); if(db == 0) fatal("can't open database: %r\n"); sprint(ipbuf, "%I", myip); if(ipinfo(db, 0, ipbuf, 0, &myipinfo) < 0) fatal("IP %s not in network database", ipbuf); ndbclose(db); netfd = udplisten("route"); /* * record any existing routes */ routefd = open("#P/iproute", ORDWR); Binit(&rbin, routefd, OREAD); while((l = Brdline(&rbin, '\n')) != 0) { l[Blinelen(&rbin)-1] = 0; if(getmfields(l, fields, 6) == 5) { rp = &routes[nroutes++]; parseip(rp->dest, fields[0]); parseip(rp->mask, fields[2]); parseip(rp->gateway, fields[4]); if(debug) syslog(0, logfile, "Existing: %I & %I -> %I", rp->dest, rp->mask, rp->gateway); rp->valid = 1; rp->changed = 1; rp->metric = NetworkCost; rp->local = !restart; if(iszero(rp->dest) && iszero(rp->mask)) { defroute = rp; rp->local = 1; } } } /* * broadcast request for all routes */ sendroutes(0, OpRequest, 0); /* * read routing requests */ while((nb = read(netfd, buf, sizeof(buf))) >= 0) { if(nb < Udphdrlen+Riphdrlen) continue; memmove(&from, buf, Udphdrlen); memmove(&rip, buf+Udphdrlen, Riphdrlen); if(rip.version < 1) continue; bp = buf + Udphdrlen + Riphdrlen; nb -= Riphdrlen + Udphdrlen; switch(rip.op) { case OpRequest: /* ignore it for now, since p9 can't route */ break; case OpReply: /* checking etc */ if(((from.port[0]<<8)|from.port[1]) != RIPport) continue; /* check not self ... */ now = time(0); if(debug > 1) fprint(2, "from %I:\n", from.addr); for(; nb >= Ipdestlen+4; nb -= Ipdestlen+4) { memmove(&dest, bp, Ipdestlen); bp += Ipdestlen; dest.metric = (bp[0]<<24)|(bp[1]<<16)|(bp[2]<<8)|bp[3]; bp += 4; /* * check it's an IP route, valid metric, MBZ fields zero */ if(dest.iptype[0] != iptype[0] || dest.iptype[1] != iptype[1]) { if(debug > 1) fprint(2, "\t-- unknown address type %x,%x\n", dest.iptype[0], dest.iptype[1]); continue; } if(dest.pad[0]|dest.pad[1]|dest.zero[0]|dest.zero[1]|dest.zero[2]|dest.zero[3]) { if(debug > 1) fprint(2, "\t-- non-zero MBZ\n"); continue; } if(debug > 1) fprint(2, "\t%I %ld\n", dest.addr, dest.metric); if(dest.metric <= 0 || dest.metric > HopLimit) continue; /* * ignore route if IP address is: * class D or E * net 0 (except perhaps 0.0.0.0) * net 127 * broadcast address (all 1s host part) * host routes? or ignore entry with non-zero host */ if((dest.addr[0]&0xE) == 0xE || dest.addr[0] == 0 || dest.addr[0] == 0x7F) { if(debug > 1) fprint(2, "\t%I %ld invalid addr\n", dest.addr, dest.metric); continue; } maskgen(destmask, dest.addr); if(isbroadcast(dest.addr, destmask)) { if(debug > 1) fprint(2, "\t%I & %I -> broadcast\n", dest.addr, destmask); continue; } if(ishost(dest.addr, destmask)) { if(debug > 1) fprint(2, "\t%I & %I -> host\n", dest.addr, destmask); continue; } /* * update the metric min(metric+NetworkCost, HopLimit) */ dest.metric += NetworkCost; if(dest.metric > HopLimit) dest.metric = HopLimit; /* * RFC1058 rules page 27-28, with optional replacement of expiring routes */ rp = lookup(&dest); if(rp->valid) { if(rp->local) continue; /* local, don't touch */ if(equivip(rp->gateway, from.addr)) { if(dest.metric != HopLimit) { rp->metric = dest.metric; rp->time = now; } else { if(rp->metric != HopLimit) { rp->metric = dest.metric; rp->changed = 1; rp->time = now - (Discard-120); delroute(rp); rp->valid = 1; /* keep it until finally expired */ } else if(now >= rp->time+Discard) delroute(rp); /* finally dead */ } } else if(dest.metric < rp->metric || dest.metric != HopLimit && dest.metric == rp->metric && now > rp->time+Expired/2) { delroute(rp); rp->metric = dest.metric; memmove(rp->gateway, from.addr, sizeof(rp->gateway)); rp->time = now; addroute(rp); } } else if(dest.metric < HopLimit) { /* new entry */ /* * todo: don't add route-to-host if host is on net/subnet * for which we have at least as good a route ... * currently /sys/src/9/port/stip.c supports only one interface, * and it might suffice to check for our own net */ if(!equivip(dest.addr, myipinfo.ipnet)) { rp->valid = 1; rp->changed = 1; rp->time = now; rp->metric = dest.metric; memmove(rp->gateway, from.addr, sizeof(rp->gateway)); memmove(rp->dest, dest.addr, sizeof(rp->dest)); memmove(rp->mask, destmask, sizeof(rp->mask)); addroute(rp); } } } break; default: break; } } exits(0); } void sendroutes(Udphdr *to, int op, int changes) { char buf[1024], *bp, *data; int i; Gateway *gp; Riphdr rip; bp = buf; if(to) { for(i=0; i<4; i++) *bp++ = to->addr[i]; *bp++ = to->port[0]; *bp++ = to->port[1]; } else { for(i=0; i<4; i++) *bp++ = myipinfo.ipnet[i] | ~myipinfo.ipmask[i]; /* broadcast address */ *bp++ = RIPport>>8; *bp++ = RIPport; } memset(&rip, 0, sizeof(rip)); rip.version = Version; rip.op = op; memmove(bp, &rip, Riphdrlen); bp += Riphdrlen; data = bp; if(op == OpRequest) { memset(bp, 0, Ipdestlen+4); bp += Ipdestlen; bp[3] = HopLimit; bp += 4; } else { /* send routes */ for(i=0; ichanged) continue; if(gp == defroute) continue; gp->changed = 0; if(bp+Ipdestlen+4 > buf+Udphdrlen+512) { if(write(netfd, buf, bp-buf) < 0) fprint(2, "RIP write failed: %r\n"); bp = data; } memset(bp, 0, Ipdestlen+4); bp[0] = 0; bp[1] = 2; memmove(bp+4, gp->dest, 4); bp += Ipdestlen; *bp++ = gp->metric>>24; *bp++ = gp->metric>>16; *bp++ = gp->metric>>8; *bp++ = gp->metric; } } if(bp != data && write(netfd, buf, bp-buf) < 0) fprint(2, "RIP write failed: %r\n"); } Gateway* lookup(Iproute *d) { int i; Gateway *g, *avail; avail = 0; for(i=0; ivalid) { if(avail == 0) avail = g; continue; } if(equivip(g->dest, d->addr)) return g; } if(avail == 0) { if(nroutes >= Nroutes) return 0; avail = &routes[nroutes++]; avail->valid = 0; } return avail; } void maskgen(Ipaddr mask, Ipaddr addr) { Ipaddr dnet, mynet; memmove(mask, classmask[CLASS(addr)], sizeof(mask)); maskip(myip, classmask[CLASS(myip)], mynet); maskip(addr, classmask[CLASS(addr)], dnet); if(equivip(mynet, dnet)) memmove(mask, myipinfo.ipmask, sizeof(mask)); } int isbroadcast(Ipaddr a, Ipaddr mask) { Ipaddr h, hm; int i; for(i=0; i<4; i++) hm[i] = ~mask[i]; maskip(a, hm, h); return equivip(h, hm); } int isnet(Ipaddr a, Ipaddr mask) { Ipaddr n, mc; int i; for(i=0; i<4; i++) mc[i] = ~mask[i]; maskip(a, mc, n); return (n[0]|n[1]|n[2]|n[3]) == 0; } int ishost(Ipaddr a, Ipaddr mask) { return !isnet(a, mask); } int iszero(Ipaddr a) { return (a[0]|a[1]|a[2]|a[3]) == 0; } /* * add ipdest mask gateway * add 0.0.0.0 0.0.0.0 gateway (default) * delete ipdest mask */ void addroute(Gateway *g) { if(iszero(g->mask) && iszero(g->dest)) g->valid = 0; /* don't change default route */ else if(defroute && equivip(defroute->gateway, g->gateway)) { if(debug) syslog(0, logfile, "default %I %I", g->dest, g->mask); /* don't need a new entry */ g->valid = 1; g->changed = 1; } else { if(debug) syslog(0, logfile, "add %I %I %I", g->dest, g->mask, g->gateway); if(fprint(routefd, "add %I %I %I", g->dest, g->mask, g->gateway) > 0) { g->valid = 1; g->changed = 1; } } } void delroute(Gateway *g) { if(debug) syslog(0, logfile, "delete %I %I", g->dest, g->mask); if(fprint(routefd, "delete %I %I", g->dest, g->mask) > 0) { g->valid = 0; g->changed = 1; } } int udplisten(char *port) { char data[NAMELEN*5]; char devdir[NAMELEN*4]; int fd, ctl; sprint(data, "udp!*!%s", port); ctl = announce(data, devdir); if(ctl < 0) fatal("can't announce %s: %r", data); if(fprint(ctl, "headers") < 0) fatal("can't set udp headers: %r"); sprint(data, "%s/data", devdir); fd = open(data, ORDWR); if(fd < 0) fatal("can't open %s: %r", data); close(ctl); return fd; } int udphdrconv(void *v, Fconv *f1) { Udphdr *h; char buf[4+4*4+1+20]; h = *(Udphdr **)v; sprint(buf, "udp!%I!%ud", h->addr, (h->port[0]<<8)|h->port[1]); strconv(buf, f1); return sizeof(Udphdr*); } void fatal(char *fmt, ...) { char buf[512]; doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)); syslog(0, logfile, "%s", buf); exits("fatal"); }