16920Ssam #ifndef lint 2*8376Ssam static char sccsid[] = "@(#)routed.c 4.24 10/07/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 37*8376Ssam struct sockaddr_in routingaddr = { AF_INET }; 38*8376Ssam 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 59*8376Ssam 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 676920Ssam main(argc, argv) 686920Ssam int argc; 696920Ssam char *argv[]; 706920Ssam { 716920Ssam int cc; 726920Ssam struct sockaddr from; 73*8376Ssam 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 */ 108*8376Ssam sp = getservbyname("router", "udp"); 109*8376Ssam if (sp == 0) { 110*8376Ssam fprintf(stderr, "routed: udp/router: unknown service\n"); 111*8376Ssam exit(1); 112*8376Ssam } 113*8376Ssam routingaddr.sin_port = htons(sp->s_port); 114*8376Ssam 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 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 */ 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; 292*8376Ssam 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 */ 3157133Swnj gwkludge() 3167130Swnj { 3177130Swnj struct sockaddr_in dst, gate; 3187130Swnj FILE *fp; 319*8376Ssam 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; 326*8376Ssam qual = buf; 327*8376Ssam dname = buf + 64; 328*8376Ssam gname = buf + ((BUFSIZ - 64) / 3); 329*8376Ssam 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*8376Ssam /* format: dst {net | host} XX gateway XX metric DD [passive]\n */ 3347145Ssam #define readentry(fp) \ 335*8376Ssam fscanf((fp), "dst %s %s gateway %s metric %d %s\n", \ 336*8376Ssam type, dname, gname, &metric, qual) 3377133Swnj for (;;) { 3388339Ssam struct hostent *host; 339*8376Ssam struct netent *net; 3408339Ssam 3417145Ssam if (readentry(fp) == EOF) 3427133Swnj break; 343*8376Ssam if (strcmp(type, "net") == 0) { 344*8376Ssam net = getnetbyname(dname); 345*8376Ssam if (net == 0 || net->n_addrtype != AF_INET) 346*8376Ssam continue; 347*8376Ssam dst.sin_addr = inet_makeaddr(net->n_net, INADDR_ANY); 348*8376Ssam } else if (strcmp(type, "host") == 0) { 349*8376Ssam host = gethostbyname(dname); 350*8376Ssam if (host == 0) 351*8376Ssam continue; 352*8376Ssam bcopy(host->h_addr, &dst.sin_addr, host->h_length); 353*8376Ssam } 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 */ 363*8376Ssam ifp->int_net = inet_netof(dst.sin_addr); 364*8376Ssam 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 */ 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 * 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 * 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 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 * 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 * 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