16920Ssam #ifndef lint
2*8386Ssam static char sccsid[] = "@(#)routed.c 4.25 10/08/82";
36920Ssam #endif
46920Ssam
56934Ssam /*
66934Ssam * Routing Table Management Daemon
76934Ssam */
86934Ssam #include <sys/types.h>
96920Ssam #include <sys/ioctl.h>
106920Ssam #include <sys/socket.h>
116920Ssam #include <net/in.h>
126920Ssam #include <net/if.h>
136920Ssam #include <errno.h>
146920Ssam #include <stdio.h>
156920Ssam #include <nlist.h>
166920Ssam #include <signal.h>
177029Ssam #include <time.h>
188339Ssam #include <netdb.h>
197139Ssam #define RIPCMDS
206920Ssam #include "rip.h"
216920Ssam #include "router.h"
226920Ssam
236920Ssam #define LOOPBACKNET 0177
246920Ssam /* casts to keep lint happy */
256920Ssam #define insque(q,p) _insque((caddr_t)q,(caddr_t)p)
266920Ssam #define remque(q) _remque((caddr_t)q)
276920Ssam #define equal(a1, a2) \
286920Ssam (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
296929Ssam #define min(a,b) ((a)>(b)?(b):(a))
306920Ssam
316920Ssam struct nlist nl[] = {
326920Ssam #define N_IFNET 0
336920Ssam { "_ifnet" },
346920Ssam 0,
356920Ssam };
366920Ssam
378376Ssam struct sockaddr_in routingaddr = { AF_INET };
388376Ssam struct sockaddr_in noroutingaddr = { AF_INET };
396920Ssam
406920Ssam int s;
417130Swnj int snoroute; /* socket with no routing */
426929Ssam int kmem = -1;
437133Swnj int supplier = -1; /* process should supply updates */
446962Ssam int install = 1; /* if 1 call kernel */
457145Ssam int lookforinterfaces = 1;
467145Ssam int performnlist = 1;
477250Ssam int externalinterfaces = 0; /* # of remote and local interfaces */
487133Swnj int timeval = -TIMER_RATE;
496920Ssam int timer();
506920Ssam int cleanup();
517133Swnj
527133Swnj #define tprintf if (trace) printf
536920Ssam int trace = 0;
547029Ssam FILE *ftrace;
556920Ssam
567133Swnj char packet[MAXPACKETSIZE+1];
577133Swnj struct rip *msg = (struct rip *)packet;
586920Ssam
598376Ssam struct in_addr inet_makeaddr();
607145Ssam struct interface *if_ifwithaddr(), *if_ifwithnet();
617145Ssam extern char *malloc(), *sys_errlist[];
626922Ssam extern int errno, exit();
637133Swnj char **argv0;
646920Ssam
657139Ssam int sendmsg(), supply();
667133Swnj
main(argc,argv)676920Ssam main(argc, argv)
686920Ssam int argc;
696920Ssam char *argv[];
706920Ssam {
716920Ssam int cc;
726920Ssam struct sockaddr from;
738376Ssam struct servent *sp;
746920Ssam
757133Swnj argv0 = argv;
767029Ssam #ifndef DEBUG
777029Ssam if (fork())
787029Ssam exit(0);
797029Ssam for (cc = 0; cc < 10; cc++)
807029Ssam (void) close(cc);
817029Ssam (void) open("/", 0);
827029Ssam (void) dup2(0, 1);
837029Ssam (void) dup2(0, 2);
847029Ssam { int t = open("/dev/tty", 2);
857029Ssam if (t >= 0) {
867029Ssam ioctl(t, TIOCNOTTY, (char *)0);
877029Ssam (void) close(t);
887029Ssam }
896920Ssam }
907029Ssam #endif
916920Ssam if (trace) {
927029Ssam ftrace = fopen("/etc/routerlog", "w");
937029Ssam dup2(fileno(ftrace), 1);
947029Ssam dup2(fileno(ftrace), 2);
956920Ssam }
967250Ssam
977250Ssam /*
987250Ssam * We use two sockets. One for which outgoing
997250Ssam * packets are routed and for which they're not.
1007250Ssam * The latter allows us to delete routing table
1017250Ssam * entries in the kernel for network interfaces
1027250Ssam * attached to our host which we believe are down
1037250Ssam * while still polling it to see when/if it comes
1047250Ssam * back up. With the new ipc interface we'll be
1057250Ssam * able to specify ``don't route'' as an option
1067250Ssam * to send, but until then we utilize a second port.
1077250Ssam */
1088376Ssam sp = getservbyname("router", "udp");
1098376Ssam if (sp == 0) {
1108376Ssam fprintf(stderr, "routed: udp/router: unknown service\n");
1118376Ssam exit(1);
1128376Ssam }
1138376Ssam routingaddr.sin_port = htons(sp->s_port);
1148376Ssam noroutingaddr.sin_port = htons(sp->s_port + 1);
1156920Ssam again:
1167139Ssam s = socket(SOCK_DGRAM, 0, &routingaddr, 0);
1176920Ssam if (s < 0) {
1186920Ssam perror("socket");
1196920Ssam sleep(30);
1206920Ssam goto again;
1216920Ssam }
1227130Swnj again2:
1237139Ssam snoroute = socket(SOCK_DGRAM, 0, &noroutingaddr, SO_DONTROUTE);
1247130Swnj if (snoroute < 0) {
1257130Swnj perror("socket");
1267130Swnj sleep(30);
1277130Swnj goto again2;
1287130Swnj }
1296920Ssam argv++, argc--;
1307133Swnj while (argc > 0 && **argv == '-') {
1317133Swnj if (!strcmp(*argv, "-s") == 0) {
1326929Ssam supplier = 1;
1337133Swnj argv++, argc--;
1347133Swnj continue;
1357133Swnj }
1367133Swnj if (!strcmp(*argv, "-q") == 0) {
1376920Ssam supplier = 0;
1387133Swnj argv++, argc--;
1397133Swnj continue;
1407133Swnj }
1417133Swnj goto usage;
1426920Ssam }
1437133Swnj if (argc > 0) {
1447133Swnj usage:
1457145Ssam fprintf(stderr, "usage: routed [ -sq ]\n");
1467133Swnj exit(1);
1477133Swnj }
1487250Ssam /*
1497250Ssam * Collect an initial view of the world by
1507250Ssam * snooping in the kernel and the gateway kludge
1517250Ssam * file. Then, send a request packet on all
1527250Ssam * directly connected networks to find out what
1537250Ssam * everyone else thinks.
1547250Ssam */
1557133Swnj rtinit();
1567250Ssam gwkludge();
1577133Swnj ifinit();
1587133Swnj if (supplier < 0)
1597133Swnj supplier = 0;
1607133Swnj msg->rip_cmd = RIPCMD_REQUEST;
1617133Swnj msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
1627133Swnj msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
1637133Swnj toall(sendmsg);
1646920Ssam sigset(SIGALRM, timer);
1656929Ssam timer();
1666920Ssam
1676920Ssam for (;;) {
1686920Ssam cc = receive(s, &from, packet, sizeof (packet));
1696920Ssam if (cc <= 0) {
1706920Ssam if (cc < 0 && errno != EINTR)
1716920Ssam perror("receive");
1726920Ssam continue;
1736920Ssam }
1746920Ssam sighold(SIGALRM);
1756920Ssam rip_input(&from, cc);
1766920Ssam sigrelse(SIGALRM);
1776920Ssam }
1786920Ssam }
1796920Ssam
rtinit()1807133Swnj rtinit()
1816920Ssam {
1826934Ssam register struct rthash *rh;
1836920Ssam
1847133Swnj for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
1857133Swnj rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
1867133Swnj for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
1877133Swnj rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
1886920Ssam }
1896920Ssam
1907145Ssam struct interface *ifnet;
1917133Swnj
1927250Ssam /*
1937250Ssam * Probe the kernel through /dev/kmem to find the network
1947250Ssam * interfaces which have configured themselves. If the
1957250Ssam * interface is present but not yet up (for example an
1967250Ssam * ARPANET IMP), set the lookforinterfaces flag so we'll
1977250Ssam * come back later and look again.
1987250Ssam */
ifinit()1997133Swnj ifinit()
2006920Ssam {
2017145Ssam struct interface *ifp;
2027145Ssam struct ifnet ifs, *next;
2036920Ssam
2047145Ssam if (performnlist) {
2057145Ssam nlist("/vmunix", nl);
2067145Ssam if (nl[N_IFNET].n_value == 0) {
2077145Ssam printf("ifnet: not in namelist\n");
2087145Ssam goto bad;
2097145Ssam }
2107145Ssam performnlist = 0;
2116920Ssam }
2126920Ssam if (kmem < 0) {
2137145Ssam kmem = open("/dev/kmem", 0);
2147145Ssam if (kmem < 0) {
2157145Ssam perror("/dev/kmem");
2167145Ssam goto bad;
2177145Ssam }
2186920Ssam }
2196929Ssam if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 ||
2207133Swnj read(kmem, (char *)&next, sizeof (next)) != sizeof (next)) {
2217133Swnj printf("ifnet: error reading kmem\n");
2227133Swnj goto bad;
2236929Ssam }
2247145Ssam lookforinterfaces = 0;
2257133Swnj while (next) {
2267133Swnj if (lseek(kmem, (long)next, 0) == -1 ||
2277145Ssam read(kmem, (char *)&ifs, sizeof (ifs)) != sizeof (ifs)) {
2287133Swnj perror("read");
2297133Swnj goto bad;
2306929Ssam }
2317145Ssam next = ifs.if_next;
2327145Ssam if ((ifs.if_flags & IFF_UP) == 0) {
2337145Ssam lookforinterfaces = 1;
2347133Swnj continue;
2357145Ssam }
2367250Ssam /* already known to us? */
2377145Ssam if (if_ifwithaddr(&ifs.if_addr))
2387133Swnj continue;
2397250Ssam /* argh, this'll have to change sometime */
2407145Ssam if (ifs.if_addr.sa_family != AF_INET)
2417145Ssam continue;
2427250Ssam /* no one cares about software loopback interfaces */
2437145Ssam if (ifs.if_net == LOOPBACKNET)
2447145Ssam continue;
2457145Ssam ifp = (struct interface *)malloc(sizeof (struct interface));
2467145Ssam if (ifp == 0) {
2477145Ssam printf("routed: out of memory\n");
2487145Ssam break;
2497145Ssam }
2507145Ssam /*
2517145Ssam * Count the # of directly connected networks
2527145Ssam * and point to point links which aren't looped
2537145Ssam * back to ourself. This is used below to
2547250Ssam * decide if we should be a routing ``supplier''.
2557145Ssam */
2567145Ssam if ((ifs.if_flags & IFF_POINTOPOINT) == 0 ||
2577145Ssam if_ifwithaddr(&ifs.if_dstaddr) == 0)
2587145Ssam externalinterfaces++;
2597145Ssam ifp->int_addr = ifs.if_addr;
2607145Ssam ifp->int_flags = ifs.if_flags | IFF_INTERFACE;
2617145Ssam /* this works because broadaddr overlaps dstaddr */
2627145Ssam ifp->int_broadaddr = ifs.if_broadaddr;
2637145Ssam ifp->int_net = ifs.if_net;
2647145Ssam ifp->int_metric = 0;
2657145Ssam ifp->int_next = ifnet;
2666929Ssam ifnet = ifp;
2677133Swnj addrouteforif(ifp);
2687133Swnj }
2697145Ssam if (externalinterfaces > 1 && supplier < 0)
2707133Swnj supplier = 1;
2717133Swnj return;
2727133Swnj bad:
2737133Swnj sleep(60);
2747145Ssam close(kmem), close(s), close(snoroute);
2757133Swnj execv("/etc/routed", argv0);
2767133Swnj _exit(0177);
2777133Swnj }
2787130Swnj
2797133Swnj addrouteforif(ifp)
2807145Ssam struct interface *ifp;
2817133Swnj {
2827133Swnj struct sockaddr_in net;
2837133Swnj struct sockaddr *dst;
2847145Ssam int state, metric;
2857258Ssam struct rt_entry *rt;
2867133Swnj
2877145Ssam if (ifp->int_flags & IFF_POINTOPOINT)
2887145Ssam dst = &ifp->int_dstaddr;
2897133Swnj else {
2907133Swnj bzero((char *)&net, sizeof (net));
2917133Swnj net.sin_family = AF_INET;
2928376Ssam net.sin_addr = inet_makeaddr(ifp->int_net, INADDR_ANY);
2937133Swnj dst = (struct sockaddr *)&net;
2946920Ssam }
2957258Ssam rt = rtlookup(dst);
2967258Ssam rtadd(dst, &ifp->int_addr, ifp->int_metric,
2977258Ssam ifp->int_flags & (IFF_INTERFACE|IFF_PASSIVE|IFF_REMOTE));
2987258Ssam if (rt)
2997258Ssam rtdelete(rt);
3006920Ssam }
3016920Ssam
3027250Ssam /*
3037250Ssam * As a concession to the ARPANET we read a list of gateways
3047250Ssam * from /etc/gateways and add them to our tables. This file
3057250Ssam * exists at each ARPANET gateway and indicates a set of ``remote''
3067250Ssam * gateways (i.e. a gateway which we can't immediately determine
3077250Ssam * if it's present or not as we can do for those directly connected
3087250Ssam * at the hardware level). If a gateway is marked ``passive''
3097250Ssam * in the file, then we assume it doesn't have a routing process
3107250Ssam * of our design and simply assume it's always present. Those
3117250Ssam * not marked passive are treated as if they were directly
3127250Ssam * connected -- they're added into the interface list so we'll
3137250Ssam * send them routing updates.
3147250Ssam */
gwkludge()3157133Swnj gwkludge()
3167130Swnj {
3177130Swnj struct sockaddr_in dst, gate;
3187130Swnj FILE *fp;
3198376Ssam char *type, *dname, *gname, *qual, buf[BUFSIZ];
3207145Ssam struct interface *ifp;
3217145Ssam int metric;
3227130Swnj
3237130Swnj fp = fopen("/etc/gateways", "r");
3247130Swnj if (fp == NULL)
3257130Swnj return;
3268376Ssam qual = buf;
3278376Ssam dname = buf + 64;
3288376Ssam gname = buf + ((BUFSIZ - 64) / 3);
3298376Ssam type = buf + (((BUFSIZ - 64) * 2) / 3);
3307130Swnj bzero((char *)&dst, sizeof (dst));
3317130Swnj bzero((char *)&gate, sizeof (gate));
3327145Ssam dst.sin_family = gate.sin_family = AF_INET;
333*8386Ssam /* format: {net | host} XX gateway XX metric DD [passive]\n */
3347145Ssam #define readentry(fp) \
335*8386Ssam fscanf((fp), "%s %s gateway %s metric %d %s\n", \
3368376Ssam type, dname, gname, &metric, qual)
3377133Swnj for (;;) {
3388339Ssam struct hostent *host;
3398376Ssam struct netent *net;
3408339Ssam
3417145Ssam if (readentry(fp) == EOF)
3427133Swnj break;
3438376Ssam if (strcmp(type, "net") == 0) {
3448376Ssam net = getnetbyname(dname);
3458376Ssam if (net == 0 || net->n_addrtype != AF_INET)
3468376Ssam continue;
3478376Ssam dst.sin_addr = inet_makeaddr(net->n_net, INADDR_ANY);
3488376Ssam } else if (strcmp(type, "host") == 0) {
3498376Ssam host = gethostbyname(dname);
3508376Ssam if (host == 0)
3518376Ssam continue;
3528376Ssam bcopy(host->h_addr, &dst.sin_addr, host->h_length);
3538376Ssam } else
3547792Ssam continue;
3558339Ssam host = gethostbyname(gname);
3568339Ssam if (host == 0)
3577792Ssam continue;
3588339Ssam bcopy(host->h_addr, &gate.sin_addr, host->h_length);
3597145Ssam ifp = (struct interface *)malloc(sizeof (*ifp));
3607145Ssam bzero((char *)ifp, sizeof (*ifp));
3617145Ssam ifp->int_flags = IFF_REMOTE;
3627145Ssam /* can't identify broadcast capability */
3638376Ssam ifp->int_net = inet_netof(dst.sin_addr);
3648376Ssam if (strcmp(type, "host") == 0) {
3657145Ssam ifp->int_flags |= IFF_POINTOPOINT;
3667145Ssam ifp->int_dstaddr = *((struct sockaddr *)&dst);
3677212Ssam }
3687792Ssam if (strcmp(qual, "passive") == 0)
3697145Ssam ifp->int_flags |= IFF_PASSIVE;
3707250Ssam else
3717250Ssam /* assume no duplicate entries */
3727250Ssam externalinterfaces++;
3737145Ssam ifp->int_addr = *((struct sockaddr *)&gate);
3747145Ssam ifp->int_metric = metric;
3757145Ssam ifp->int_next = ifnet;
3767145Ssam ifnet = ifp;
3777145Ssam addrouteforif(ifp);
3787130Swnj }
3797130Swnj fclose(fp);
3807130Swnj }
3817130Swnj
3827250Ssam /*
3837250Ssam * Timer routine. Performs routing information supply
3847250Ssam * duties and manages timers on routing table entries.
3857250Ssam */
timer()3867133Swnj timer()
3876920Ssam {
3886934Ssam register struct rthash *rh;
3896920Ssam register struct rt_entry *rt;
3906934Ssam struct rthash *base = hosthash;
3917250Ssam int doinghost = 1, timetobroadcast;
3926920Ssam
3937133Swnj timeval += TIMER_RATE;
3947145Ssam if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
3957145Ssam ifinit();
3967250Ssam timetobroadcast = supplier && (timeval % SUPPLY_INTERVAL) == 0;
3977133Swnj tprintf(">>> time %d >>>\n", timeval);
3986920Ssam again:
3997133Swnj for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
4007133Swnj rt = rh->rt_forw;
4017133Swnj for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4027250Ssam /*
4037250Ssam * We don't advance time on a routing entry for
4047250Ssam * a passive gateway or that for our only interface.
4057250Ssam * The latter is excused because we don't act as
4067250Ssam * a routing information supplier and hence would
4077250Ssam * time it out. This is fair as if it's down
4087250Ssam * we're cut off from the world anyway and it's
4097250Ssam * not likely we'll grow any new hardware in
4107250Ssam * the mean time.
4117250Ssam */
4127250Ssam if (!(rt->rt_state & RTS_PASSIVE) &&
4137250Ssam (supplier || !(rt->rt_state & RTS_INTERFACE)))
4147133Swnj rt->rt_timer += TIMER_RATE;
4157133Swnj if (rt->rt_timer >= EXPIRE_TIME)
4167133Swnj rt->rt_metric = HOPCNT_INFINITY;
4177145Ssam log("", rt);
4187145Ssam if (rt->rt_timer >= GARBAGE_TIME) {
4197133Swnj rt = rt->rt_back;
4207133Swnj rtdelete(rt->rt_forw);
4217133Swnj continue;
4227133Swnj }
4237145Ssam if (rt->rt_state & RTS_CHANGED) {
4247145Ssam rt->rt_state &= ~RTS_CHANGED;
4257145Ssam /* don't send extraneous packets */
4267250Ssam if (!supplier || timetobroadcast)
4277145Ssam continue;
4287133Swnj log("broadcast", rt);
4297133Swnj msg->rip_cmd = RIPCMD_RESPONSE;
4307133Swnj msg->rip_nets[0].rip_dst = rt->rt_dst;
4317133Swnj msg->rip_nets[0].rip_metric =
4327133Swnj min(rt->rt_metric+1, HOPCNT_INFINITY);
4337139Ssam toall(sendmsg);
4347133Swnj }
4357133Swnj }
4366920Ssam }
4376920Ssam if (doinghost) {
4387133Swnj doinghost = 0;
4396929Ssam base = nethash;
4406920Ssam goto again;
4416920Ssam }
4427250Ssam if (timetobroadcast)
4437133Swnj toall(supply);
4447133Swnj tprintf("<<< time %d <<<\n", timeval);
4457133Swnj alarm(TIMER_RATE);
4466920Ssam }
4476920Ssam
4487133Swnj toall(f)
4497133Swnj int (*f)();
4506920Ssam {
4517145Ssam register struct interface *ifp;
4526920Ssam register struct sockaddr *dst;
4536920Ssam
4547145Ssam for (ifp = ifnet; ifp; ifp = ifp->int_next) {
4557145Ssam if (ifp->int_flags & IFF_PASSIVE)
4566920Ssam continue;
4577145Ssam dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr :
4587145Ssam ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr :
4597145Ssam &ifp->int_addr;
4607145Ssam (*f)(dst, ifp->int_flags & IFF_INTERFACE);
4616920Ssam }
4626920Ssam }
4636920Ssam
4647139Ssam /*ARGSUSED*/
4657139Ssam sendmsg(dst, dontroute)
4667133Swnj struct sockaddr *dst;
4677139Ssam int dontroute;
4687133Swnj {
4697133Swnj (*afswitch[dst->sa_family].af_output)(s, dst, sizeof (struct rip));
4707133Swnj }
4717133Swnj
4727250Ssam /*
4737250Ssam * Supply dst with the contents of the routing tables.
4747250Ssam * If this won't fit in one packet, chop it up into several.
4757250Ssam */
4767139Ssam supply(dst, dontroute)
4777139Ssam struct sockaddr *dst;
4787139Ssam int dontroute;
4797139Ssam {
4807133Swnj register struct rt_entry *rt;
4816920Ssam struct netinfo *n = msg->rip_nets;
4826934Ssam register struct rthash *rh;
4836934Ssam struct rthash *base = hosthash;
4846929Ssam int doinghost = 1, size;
4857139Ssam int (*output)() = afswitch[dst->sa_family].af_output;
4867139Ssam int sto = dontroute ? snoroute : s;
4876920Ssam
4887212Ssam msg->rip_cmd = RIPCMD_RESPONSE;
4896920Ssam again:
4906920Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4916920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4926929Ssam size = (char *)n - packet;
4936929Ssam if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
4947139Ssam (*output)(sto, dst, size);
4956920Ssam n = msg->rip_nets;
4966920Ssam }
4976920Ssam n->rip_dst = rt->rt_dst;
4986929Ssam n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY);
4996929Ssam n++;
5006920Ssam }
5016920Ssam if (doinghost) {
5026920Ssam doinghost = 0;
5036920Ssam base = nethash;
5046920Ssam goto again;
5056920Ssam }
5066929Ssam if (n != msg->rip_nets)
5077139Ssam (*output)(sto, dst, (char *)n - packet);
5086920Ssam }
5096920Ssam
5106920Ssam /*
5116920Ssam * Handle an incoming routing packet.
5126920Ssam */
5136920Ssam rip_input(from, size)
5146920Ssam struct sockaddr *from;
5156920Ssam int size;
5166920Ssam {
5176920Ssam struct rt_entry *rt;
5186920Ssam struct netinfo *n;
5197145Ssam struct interface *ifp;
5207133Swnj time_t t;
5217139Ssam int newsize;
5227139Ssam struct afswitch *afp;
5236920Ssam
5247139Ssam if (trace) {
5257139Ssam if (msg->rip_cmd < RIPCMD_MAX)
5267139Ssam printf("%s from %x\n", ripcmds[msg->rip_cmd],
5277139Ssam ((struct sockaddr_in *)from)->sin_addr);
5287139Ssam else
5297139Ssam printf("%x from %x\n", msg->rip_cmd,
5307139Ssam ((struct sockaddr_in *)from)->sin_addr);
5317139Ssam }
5327139Ssam if (from->sa_family >= AF_MAX)
5337139Ssam return;
5347139Ssam afp = &afswitch[from->sa_family];
5356937Ssam switch (msg->rip_cmd) {
5366937Ssam
5377139Ssam case RIPCMD_REQUEST:
5387139Ssam newsize = 0;
5397139Ssam size -= 4 * sizeof (char);
5407139Ssam n = msg->rip_nets;
5417139Ssam while (size > 0) {
5427139Ssam if (size < sizeof (struct netinfo))
5437139Ssam break;
5447139Ssam size -= sizeof (struct netinfo);
5456920Ssam
5467139Ssam /*
5477139Ssam * A single entry with sa_family == AF_UNSPEC and
5487139Ssam * metric ``infinity'' means ``all routes''.
5497139Ssam */
5507139Ssam if (n->rip_dst.sa_family == AF_UNSPEC &&
5517139Ssam n->rip_metric == HOPCNT_INFINITY && size == 0) {
5527212Ssam supply(from, 0);
5537139Ssam return;
5547139Ssam }
5557139Ssam rt = rtlookup(&n->rip_dst);
5567139Ssam n->rip_metric = rt == 0 ? HOPCNT_INFINITY :
5577139Ssam min(rt->rt_metric+1, HOPCNT_INFINITY);
5587139Ssam n++, newsize += sizeof (struct netinfo);
5597139Ssam }
5607139Ssam if (newsize > 0) {
5617139Ssam msg->rip_cmd = RIPCMD_RESPONSE;
5627139Ssam newsize += sizeof (int);
5637139Ssam (*afp->af_output)(s, from, newsize);
5647139Ssam }
5656920Ssam return;
5666937Ssam
5677029Ssam case RIPCMD_TRACEON:
5687139Ssam if ((*afp->af_portcheck)(from) == 0)
5697029Ssam return;
5707133Swnj if (trace)
5717133Swnj return;
5727133Swnj packet[size] = '\0';
5737133Swnj ftrace = fopen(msg->rip_tracefile, "a");
5747133Swnj if (ftrace == NULL)
5757133Swnj return;
5767133Swnj (void) dup2(fileno(ftrace), 1);
5777133Swnj (void) dup2(fileno(ftrace), 2);
5787133Swnj trace = 1;
5797133Swnj t = time(0);
5807133Swnj printf("*** Tracing turned on at %.24s ***\n", ctime(&t));
5817029Ssam return;
5827029Ssam
5837133Swnj case RIPCMD_TRACEOFF:
5847133Swnj /* verify message came from a priviledged port */
5857139Ssam if ((*afp->af_portcheck)(from) == 0)
5866937Ssam return;
5877029Ssam if (!trace)
5887029Ssam return;
5897029Ssam t = time(0);
5907029Ssam printf("*** Tracing turned off at %.24s ***\n", ctime(&t));
5917029Ssam fflush(stdout), fflush(stderr);
5927029Ssam if (ftrace)
5937029Ssam fclose(ftrace);
5947029Ssam (void) close(1), (void) close(2);
5957029Ssam trace = 0;
5967029Ssam return;
5977133Swnj
5987133Swnj case RIPCMD_RESPONSE:
5997133Swnj /* verify message came from a router */
6007139Ssam if ((*afp->af_portmatch)(from) == 0)
6017133Swnj return;
6027139Ssam (*afp->af_canon)(from);
6037133Swnj /* are we talking to ourselves? */
6047133Swnj ifp = if_ifwithaddr(from);
6057133Swnj if (ifp) {
6067145Ssam rt = rtfind(from);
6077145Ssam if (rt == 0)
6087133Swnj addrouteforif(ifp);
6097145Ssam else
6107145Ssam rt->rt_timer = 0;
6117133Swnj return;
6127133Swnj }
6137133Swnj size -= 4 * sizeof (char);
6147133Swnj n = msg->rip_nets;
6157133Swnj for (; size > 0; size -= sizeof (struct netinfo), n++) {
6167133Swnj if (size < sizeof (struct netinfo))
6177133Swnj break;
6187133Swnj if (n->rip_metric >= HOPCNT_INFINITY)
6197133Swnj continue;
6207133Swnj tprintf("dst %x hc %d...",
6217133Swnj ((struct sockaddr_in *)&n->rip_dst)->sin_addr,
6227133Swnj n->rip_metric);
6237133Swnj rt = rtlookup(&n->rip_dst);
6247133Swnj if (rt == 0) {
6257133Swnj rtadd(&n->rip_dst, from, n->rip_metric, 0);
6267133Swnj continue;
6277133Swnj }
6287133Swnj tprintf("ours: gate %x hc %d timer %d\n",
6297133Swnj ((struct sockaddr_in *)&rt->rt_router)->sin_addr,
6307133Swnj rt->rt_metric, rt->rt_timer);
6317139Ssam
6327133Swnj /*
6337250Ssam * Update if from gateway, shorter, or getting
6347139Ssam * stale and equivalent.
6357133Swnj */
6367133Swnj if (equal(from, &rt->rt_router) ||
6377133Swnj n->rip_metric < rt->rt_metric ||
6387133Swnj (rt->rt_timer > (EXPIRE_TIME/2) &&
6397133Swnj rt->rt_metric == n->rip_metric)) {
6407133Swnj rtchange(rt, from, n->rip_metric);
6417133Swnj rt->rt_timer = 0;
6427133Swnj }
6437133Swnj }
6447133Swnj return;
6457029Ssam }
6467145Ssam tprintf("bad packet, cmd=%x\n", msg->rip_cmd);
6477029Ssam }
6487029Ssam
6497250Ssam /*
6507250Ssam * Lookup dst in the tables for an exact match.
6517250Ssam */
6526920Ssam struct rt_entry *
rtlookup(dst)6536920Ssam rtlookup(dst)
6546920Ssam struct sockaddr *dst;
6556920Ssam {
6566920Ssam register struct rt_entry *rt;
6576934Ssam register struct rthash *rh;
6587011Ssam register int hash;
6596920Ssam struct afhash h;
6607011Ssam int doinghost = 1;
6616920Ssam
6627011Ssam if (dst->sa_family >= AF_MAX)
6637011Ssam return (0);
6647011Ssam (*afswitch[dst->sa_family].af_hash)(dst, &h);
6657011Ssam hash = h.afh_hosthash;
6667011Ssam rh = &hosthash[hash % ROUTEHASHSIZ];
6677011Ssam again:
6687011Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6697145Ssam if (rt->rt_hash != hash)
6707011Ssam continue;
6717011Ssam if (equal(&rt->rt_dst, dst))
6727011Ssam return (rt);
6737011Ssam }
6747011Ssam if (doinghost) {
6757011Ssam doinghost = 0;
6767011Ssam hash = h.afh_nethash;
6777011Ssam rh = &nethash[hash % ROUTEHASHSIZ];
6787011Ssam goto again;
6797011Ssam }
6807011Ssam return (0);
6817011Ssam }
6827011Ssam
6837250Ssam /*
6847250Ssam * Find a route to dst as the kernel would.
6857250Ssam */
6867011Ssam struct rt_entry *
rtfind(dst)6877145Ssam rtfind(dst)
6887011Ssam struct sockaddr *dst;
6897011Ssam {
6907011Ssam register struct rt_entry *rt;
6917011Ssam register struct rthash *rh;
6927011Ssam register int hash;
6937011Ssam struct afhash h;
6947011Ssam int af = dst->sa_family;
6957011Ssam int doinghost = 1, (*match)();
6967011Ssam
6976920Ssam if (af >= AF_MAX)
6986920Ssam return (0);
6996920Ssam (*afswitch[af].af_hash)(dst, &h);
7006920Ssam hash = h.afh_hosthash;
7016920Ssam rh = &hosthash[hash % ROUTEHASHSIZ];
7026920Ssam
7036920Ssam again:
7046920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
7056920Ssam if (rt->rt_hash != hash)
7066920Ssam continue;
7076920Ssam if (doinghost) {
7086920Ssam if (equal(&rt->rt_dst, dst))
7096920Ssam return (rt);
7106920Ssam } else {
7116920Ssam if (rt->rt_dst.sa_family == af &&
7126920Ssam (*match)(&rt->rt_dst, dst))
7136920Ssam return (rt);
7146920Ssam }
7156920Ssam }
7166920Ssam if (doinghost) {
7176920Ssam doinghost = 0;
7186920Ssam hash = h.afh_nethash;
7197011Ssam rh = &nethash[hash % ROUTEHASHSIZ];
7206920Ssam match = afswitch[af].af_netmatch;
7216920Ssam goto again;
7226920Ssam }
7236920Ssam return (0);
7246920Ssam }
7256920Ssam
7267139Ssam rtadd(dst, gate, metric, state)
7276920Ssam struct sockaddr *dst, *gate;
7287139Ssam int metric, state;
7296920Ssam {
7306920Ssam struct afhash h;
7316920Ssam register struct rt_entry *rt;
7326934Ssam struct rthash *rh;
7336920Ssam int af = dst->sa_family, flags, hash;
7346920Ssam
7356920Ssam if (af >= AF_MAX)
7366920Ssam return;
7376920Ssam (*afswitch[af].af_hash)(dst, &h);
7386920Ssam flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
7396920Ssam if (flags & RTF_HOST) {
7406920Ssam hash = h.afh_hosthash;
7416920Ssam rh = &hosthash[hash % ROUTEHASHSIZ];
7426920Ssam } else {
7436920Ssam hash = h.afh_nethash;
7446920Ssam rh = &nethash[hash % ROUTEHASHSIZ];
7456920Ssam }
7466920Ssam rt = (struct rt_entry *)malloc(sizeof (*rt));
7476920Ssam if (rt == 0)
7486920Ssam return;
7496920Ssam rt->rt_hash = hash;
7506920Ssam rt->rt_dst = *dst;
7517130Swnj rt->rt_router = *gate;
7526920Ssam rt->rt_metric = metric;
7536920Ssam rt->rt_timer = 0;
7547139Ssam rt->rt_flags = RTF_UP | flags;
7557145Ssam rt->rt_state = state | RTS_CHANGED;
7567130Swnj rt->rt_ifp = if_ifwithnet(&rt->rt_router);
7577130Swnj if (metric)
7587130Swnj rt->rt_flags |= RTF_GATEWAY;
7596920Ssam insque(rt, rh);
7606929Ssam log("add", rt);
7617145Ssam if (install && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
7627145Ssam tprintf("SIOCADDRT: %s\n", sys_errlist[errno]);
7636920Ssam }
7646920Ssam
7656920Ssam rtchange(rt, gate, metric)
7666920Ssam struct rt_entry *rt;
7676920Ssam struct sockaddr *gate;
7686920Ssam short metric;
7696920Ssam {
7707139Ssam int doioctl = 0, metricchanged = 0;
7717145Ssam struct rtentry oldroute;
7726920Ssam
7737145Ssam if (!equal(&rt->rt_router, gate))
7747139Ssam doioctl++;
7756920Ssam if (metric != rt->rt_metric) {
7767139Ssam metricchanged++;
7776920Ssam rt->rt_metric = metric;
7786920Ssam }
7797145Ssam if (doioctl || metricchanged) {
7807145Ssam log("change", rt);
7817145Ssam rt->rt_state |= RTS_CHANGED;
7827145Ssam }
7837139Ssam if (doioctl) {
7847145Ssam oldroute = rt->rt_rt;
7857139Ssam rt->rt_router = *gate;
7867145Ssam if (install) {
7877145Ssam if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
7887145Ssam tprintf("SIOCADDRT: %s\n", sys_errlist[errno]);
7897145Ssam if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
7907145Ssam tprintf("SIOCDELRT: %s\n", sys_errlist[errno]);
7917145Ssam }
7927139Ssam }
7936920Ssam }
7946920Ssam
7956920Ssam rtdelete(rt)
7966920Ssam struct rt_entry *rt;
7976920Ssam {
7986929Ssam log("delete", rt);
7997130Swnj if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
8007145Ssam tprintf("SIOCDELRT: %s\n", sys_errlist[errno]);
8016920Ssam remque(rt);
8026920Ssam free((char *)rt);
8036920Ssam }
8046920Ssam
log(operation,rt)8056920Ssam log(operation, rt)
8066920Ssam char *operation;
8076920Ssam struct rt_entry *rt;
8086920Ssam {
8096920Ssam struct sockaddr_in *dst, *gate;
8106937Ssam static struct bits {
8116920Ssam int t_bits;
8126920Ssam char *t_name;
8136937Ssam } flagbits[] = {
8146920Ssam { RTF_UP, "UP" },
8157130Swnj { RTF_GATEWAY, "GATEWAY" },
8166920Ssam { RTF_HOST, "HOST" },
8176920Ssam { 0 }
8186937Ssam }, statebits[] = {
8197130Swnj { RTS_PASSIVE, "PASSIVE" },
8207145Ssam { RTS_REMOTE, "REMOTE" },
8217145Ssam { RTS_INTERFACE,"INTERFACE" },
8227145Ssam { RTS_CHANGED, "CHANGED" },
8236937Ssam { 0 }
8246920Ssam };
8256937Ssam register struct bits *p;
8266920Ssam register int first;
8276920Ssam char *cp;
8286920Ssam
8296929Ssam if (trace == 0)
8306929Ssam return;
8316920Ssam printf("%s ", operation);
8326920Ssam dst = (struct sockaddr_in *)&rt->rt_dst;
8337130Swnj gate = (struct sockaddr_in *)&rt->rt_router;
8347145Ssam printf("dst %x, router %x, metric %d, flags", dst->sin_addr,
8357145Ssam gate->sin_addr, rt->rt_metric);
8366937Ssam cp = " %s";
8376937Ssam for (first = 1, p = flagbits; p->t_bits > 0; p++) {
8386920Ssam if ((rt->rt_flags & p->t_bits) == 0)
8396920Ssam continue;
8406920Ssam printf(cp, p->t_name);
8416920Ssam if (first) {
8426920Ssam cp = "|%s";
8436920Ssam first = 0;
8446920Ssam }
8456920Ssam }
8466937Ssam printf(" state");
8476937Ssam cp = " %s";
8486937Ssam for (first = 1, p = statebits; p->t_bits > 0; p++) {
8496937Ssam if ((rt->rt_state & p->t_bits) == 0)
8506937Ssam continue;
8516937Ssam printf(cp, p->t_name);
8526937Ssam if (first) {
8536937Ssam cp = "|%s";
8546937Ssam first = 0;
8556937Ssam }
8566937Ssam }
8576920Ssam putchar('\n');
8586920Ssam }
8596929Ssam
8607145Ssam struct interface *
if_ifwithaddr(addr)8616929Ssam if_ifwithaddr(addr)
8626929Ssam struct sockaddr *addr;
8636929Ssam {
8647145Ssam register struct interface *ifp;
8656929Ssam
8666929Ssam #define same(a1, a2) \
8676929Ssam (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
8687145Ssam for (ifp = ifnet; ifp; ifp = ifp->int_next) {
8697145Ssam if (ifp->int_flags & IFF_REMOTE)
8706929Ssam continue;
8717145Ssam if (ifp->int_addr.sa_family != addr->sa_family)
8727145Ssam continue;
8737145Ssam if (same(&ifp->int_addr, addr))
8746929Ssam break;
8757145Ssam if ((ifp->int_flags & IFF_BROADCAST) &&
8767145Ssam same(&ifp->int_broadaddr, addr))
8776929Ssam break;
8786929Ssam }
8796929Ssam return (ifp);
8806929Ssam #undef same
8816929Ssam }
8826929Ssam
8837145Ssam struct interface *
if_ifwithnet(addr)8846929Ssam if_ifwithnet(addr)
8856929Ssam register struct sockaddr *addr;
8866929Ssam {
8877145Ssam register struct interface *ifp;
8886929Ssam register int af = addr->sa_family;
8896929Ssam register int (*netmatch)();
8906929Ssam
8916929Ssam if (af >= AF_MAX)
8926929Ssam return (0);
8936929Ssam netmatch = afswitch[af].af_netmatch;
8947145Ssam for (ifp = ifnet; ifp; ifp = ifp->int_next) {
8957145Ssam if (ifp->int_flags & IFF_REMOTE)
8966929Ssam continue;
8977145Ssam if (af != ifp->int_addr.sa_family)
8987145Ssam continue;
8997145Ssam if ((*netmatch)(addr, &ifp->int_addr))
9006929Ssam break;
9016929Ssam }
9026929Ssam return (ifp);
9036929Ssam }
904