16920Ssam #ifndef lint 2*8339Ssam static char sccsid[] = "@(#)routed.c 4.23 10/06/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> 18*8339Ssam #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 377139Ssam struct sockaddr_in routingaddr = { AF_INET, IPPORT_ROUTESERVER }; 387139Ssam struct sockaddr_in noroutingaddr = { AF_INET, IPPORT_ROUTESERVER+1 }; 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 596934Ssam struct in_addr if_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; 736920Ssam 747133Swnj argv0 = argv; 757029Ssam #ifndef DEBUG 767029Ssam if (fork()) 777029Ssam exit(0); 787029Ssam for (cc = 0; cc < 10; cc++) 797029Ssam (void) close(cc); 807029Ssam (void) open("/", 0); 817029Ssam (void) dup2(0, 1); 827029Ssam (void) dup2(0, 2); 837029Ssam { int t = open("/dev/tty", 2); 847029Ssam if (t >= 0) { 857029Ssam ioctl(t, TIOCNOTTY, (char *)0); 867029Ssam (void) close(t); 877029Ssam } 886920Ssam } 897029Ssam #endif 906920Ssam if (trace) { 917029Ssam ftrace = fopen("/etc/routerlog", "w"); 927029Ssam dup2(fileno(ftrace), 1); 937029Ssam dup2(fileno(ftrace), 2); 946920Ssam } 957250Ssam 967250Ssam /* 977250Ssam * We use two sockets. One for which outgoing 987250Ssam * packets are routed and for which they're not. 997250Ssam * The latter allows us to delete routing table 1007250Ssam * entries in the kernel for network interfaces 1017250Ssam * attached to our host which we believe are down 1027250Ssam * while still polling it to see when/if it comes 1037250Ssam * back up. With the new ipc interface we'll be 1047250Ssam * able to specify ``don't route'' as an option 1057250Ssam * to send, but until then we utilize a second port. 1067250Ssam */ 1076937Ssam #ifdef vax || pdp11 1087139Ssam routingaddr.sin_port = htons(routingaddr.sin_port); 1097139Ssam noroutingaddr.sin_port = htons(noroutingaddr.sin_port); 1106920Ssam #endif 1116920Ssam again: 1127139Ssam s = socket(SOCK_DGRAM, 0, &routingaddr, 0); 1136920Ssam if (s < 0) { 1146920Ssam perror("socket"); 1156920Ssam sleep(30); 1166920Ssam goto again; 1176920Ssam } 1187130Swnj again2: 1197139Ssam snoroute = socket(SOCK_DGRAM, 0, &noroutingaddr, SO_DONTROUTE); 1207130Swnj if (snoroute < 0) { 1217130Swnj perror("socket"); 1227130Swnj sleep(30); 1237130Swnj goto again2; 1247130Swnj } 1256920Ssam argv++, argc--; 1267133Swnj while (argc > 0 && **argv == '-') { 1277133Swnj if (!strcmp(*argv, "-s") == 0) { 1286929Ssam supplier = 1; 1297133Swnj argv++, argc--; 1307133Swnj continue; 1317133Swnj } 1327133Swnj if (!strcmp(*argv, "-q") == 0) { 1336920Ssam supplier = 0; 1347133Swnj argv++, argc--; 1357133Swnj continue; 1367133Swnj } 1377133Swnj goto usage; 1386920Ssam } 1397133Swnj if (argc > 0) { 1407133Swnj usage: 1417145Ssam fprintf(stderr, "usage: routed [ -sq ]\n"); 1427133Swnj exit(1); 1437133Swnj } 1447250Ssam /* 1457250Ssam * Collect an initial view of the world by 1467250Ssam * snooping in the kernel and the gateway kludge 1477250Ssam * file. Then, send a request packet on all 1487250Ssam * directly connected networks to find out what 1497250Ssam * everyone else thinks. 1507250Ssam */ 1517133Swnj rtinit(); 1527250Ssam gwkludge(); 1537133Swnj ifinit(); 1547133Swnj if (supplier < 0) 1557133Swnj supplier = 0; 1567133Swnj msg->rip_cmd = RIPCMD_REQUEST; 1577133Swnj msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC; 1587133Swnj msg->rip_nets[0].rip_metric = HOPCNT_INFINITY; 1597133Swnj toall(sendmsg); 1606920Ssam sigset(SIGALRM, timer); 1616929Ssam timer(); 1626920Ssam 1636920Ssam for (;;) { 1646920Ssam cc = receive(s, &from, packet, sizeof (packet)); 1656920Ssam if (cc <= 0) { 1666920Ssam if (cc < 0 && errno != EINTR) 1676920Ssam perror("receive"); 1686920Ssam continue; 1696920Ssam } 1706920Ssam sighold(SIGALRM); 1716920Ssam rip_input(&from, cc); 1726920Ssam sigrelse(SIGALRM); 1736920Ssam } 1746920Ssam } 1756920Ssam 1767133Swnj rtinit() 1776920Ssam { 1786934Ssam register struct rthash *rh; 1796920Ssam 1807133Swnj for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) 1817133Swnj rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 1827133Swnj for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) 1837133Swnj rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 1846920Ssam } 1856920Ssam 1867145Ssam struct interface *ifnet; 1877133Swnj 1887250Ssam /* 1897250Ssam * Probe the kernel through /dev/kmem to find the network 1907250Ssam * interfaces which have configured themselves. If the 1917250Ssam * interface is present but not yet up (for example an 1927250Ssam * ARPANET IMP), set the lookforinterfaces flag so we'll 1937250Ssam * come back later and look again. 1947250Ssam */ 1957133Swnj ifinit() 1966920Ssam { 1977145Ssam struct interface *ifp; 1987145Ssam struct ifnet ifs, *next; 1996920Ssam 2007145Ssam if (performnlist) { 2017145Ssam nlist("/vmunix", nl); 2027145Ssam if (nl[N_IFNET].n_value == 0) { 2037145Ssam printf("ifnet: not in namelist\n"); 2047145Ssam goto bad; 2057145Ssam } 2067145Ssam performnlist = 0; 2076920Ssam } 2086920Ssam if (kmem < 0) { 2097145Ssam kmem = open("/dev/kmem", 0); 2107145Ssam if (kmem < 0) { 2117145Ssam perror("/dev/kmem"); 2127145Ssam goto bad; 2137145Ssam } 2146920Ssam } 2156929Ssam if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 || 2167133Swnj read(kmem, (char *)&next, sizeof (next)) != sizeof (next)) { 2177133Swnj printf("ifnet: error reading kmem\n"); 2187133Swnj goto bad; 2196929Ssam } 2207145Ssam lookforinterfaces = 0; 2217133Swnj while (next) { 2227133Swnj if (lseek(kmem, (long)next, 0) == -1 || 2237145Ssam read(kmem, (char *)&ifs, sizeof (ifs)) != sizeof (ifs)) { 2247133Swnj perror("read"); 2257133Swnj goto bad; 2266929Ssam } 2277145Ssam next = ifs.if_next; 2287145Ssam if ((ifs.if_flags & IFF_UP) == 0) { 2297145Ssam lookforinterfaces = 1; 2307133Swnj continue; 2317145Ssam } 2327250Ssam /* already known to us? */ 2337145Ssam if (if_ifwithaddr(&ifs.if_addr)) 2347133Swnj continue; 2357250Ssam /* argh, this'll have to change sometime */ 2367145Ssam if (ifs.if_addr.sa_family != AF_INET) 2377145Ssam continue; 2387250Ssam /* no one cares about software loopback interfaces */ 2397145Ssam if (ifs.if_net == LOOPBACKNET) 2407145Ssam continue; 2417145Ssam ifp = (struct interface *)malloc(sizeof (struct interface)); 2427145Ssam if (ifp == 0) { 2437145Ssam printf("routed: out of memory\n"); 2447145Ssam break; 2457145Ssam } 2467145Ssam /* 2477145Ssam * Count the # of directly connected networks 2487145Ssam * and point to point links which aren't looped 2497145Ssam * back to ourself. This is used below to 2507250Ssam * decide if we should be a routing ``supplier''. 2517145Ssam */ 2527145Ssam if ((ifs.if_flags & IFF_POINTOPOINT) == 0 || 2537145Ssam if_ifwithaddr(&ifs.if_dstaddr) == 0) 2547145Ssam externalinterfaces++; 2557145Ssam ifp->int_addr = ifs.if_addr; 2567145Ssam ifp->int_flags = ifs.if_flags | IFF_INTERFACE; 2577145Ssam /* this works because broadaddr overlaps dstaddr */ 2587145Ssam ifp->int_broadaddr = ifs.if_broadaddr; 2597145Ssam ifp->int_net = ifs.if_net; 2607145Ssam ifp->int_metric = 0; 2617145Ssam ifp->int_next = ifnet; 2626929Ssam ifnet = ifp; 2637133Swnj addrouteforif(ifp); 2647133Swnj } 2657145Ssam if (externalinterfaces > 1 && supplier < 0) 2667133Swnj supplier = 1; 2677133Swnj return; 2687133Swnj bad: 2697133Swnj sleep(60); 2707145Ssam close(kmem), close(s), close(snoroute); 2717133Swnj execv("/etc/routed", argv0); 2727133Swnj _exit(0177); 2737133Swnj } 2747130Swnj 2757133Swnj addrouteforif(ifp) 2767145Ssam struct interface *ifp; 2777133Swnj { 2787133Swnj struct sockaddr_in net; 2797133Swnj struct sockaddr *dst; 2807145Ssam int state, metric; 2817258Ssam struct rt_entry *rt; 2827133Swnj 2837145Ssam if (ifp->int_flags & IFF_POINTOPOINT) 2847145Ssam dst = &ifp->int_dstaddr; 2857133Swnj else { 2867133Swnj bzero((char *)&net, sizeof (net)); 2877133Swnj net.sin_family = AF_INET; 2887145Ssam net.sin_addr = if_makeaddr(ifp->int_net, INADDR_ANY); 2897133Swnj dst = (struct sockaddr *)&net; 2906920Ssam } 2917258Ssam rt = rtlookup(dst); 2927258Ssam rtadd(dst, &ifp->int_addr, ifp->int_metric, 2937258Ssam ifp->int_flags & (IFF_INTERFACE|IFF_PASSIVE|IFF_REMOTE)); 2947258Ssam if (rt) 2957258Ssam rtdelete(rt); 2966920Ssam } 2976920Ssam 2987250Ssam /* 2997250Ssam * As a concession to the ARPANET we read a list of gateways 3007250Ssam * from /etc/gateways and add them to our tables. This file 3017250Ssam * exists at each ARPANET gateway and indicates a set of ``remote'' 3027250Ssam * gateways (i.e. a gateway which we can't immediately determine 3037250Ssam * if it's present or not as we can do for those directly connected 3047250Ssam * at the hardware level). If a gateway is marked ``passive'' 3057250Ssam * in the file, then we assume it doesn't have a routing process 3067250Ssam * of our design and simply assume it's always present. Those 3077250Ssam * not marked passive are treated as if they were directly 3087250Ssam * connected -- they're added into the interface list so we'll 3097250Ssam * send them routing updates. 3107250Ssam */ 3117133Swnj gwkludge() 3127130Swnj { 3137130Swnj struct sockaddr_in dst, gate; 3147130Swnj FILE *fp; 3157792Ssam char *dname, *gname, *qual, buf[BUFSIZ]; 3167145Ssam struct interface *ifp; 3177145Ssam int metric; 3187130Swnj 3197130Swnj fp = fopen("/etc/gateways", "r"); 3207130Swnj if (fp == NULL) 3217130Swnj return; 3227792Ssam qual = buf; dname = buf + 64; gname = buf + ((BUFSIZ - 64) / 2); 3237130Swnj bzero((char *)&dst, sizeof (dst)); 3247130Swnj bzero((char *)&gate, sizeof (gate)); 3257145Ssam dst.sin_family = gate.sin_family = AF_INET; 3267145Ssam /* format: dst XX gateway XX metric DD [passive]\n */ 3277145Ssam #define readentry(fp) \ 3287792Ssam fscanf((fp), "dst %s gateway %s metric %d %s\n", \ 3297792Ssam dname, gname, &metric, qual) 3307133Swnj for (;;) { 331*8339Ssam struct hostent *host; 332*8339Ssam 3337145Ssam if (readentry(fp) == EOF) 3347133Swnj break; 335*8339Ssam host = gethostbyname(dname); 336*8339Ssam if (host == 0) 3377792Ssam continue; 338*8339Ssam bcopy(host->h_addr, &dst.sin_addr, host->h_length); 339*8339Ssam host = gethostbyname(gname); 340*8339Ssam if (host == 0) 3417792Ssam continue; 342*8339Ssam bcopy(host->h_addr, &gate.sin_addr, host->h_length); 3437145Ssam ifp = (struct interface *)malloc(sizeof (*ifp)); 3447145Ssam bzero((char *)ifp, sizeof (*ifp)); 3457145Ssam ifp->int_flags = IFF_REMOTE; 3467145Ssam /* can't identify broadcast capability */ 3477794Ssam ifp->int_net = in_netof(dst.sin_addr); 3487145Ssam if ((*afswitch[dst.sin_family].af_checkhost)(&dst)) { 3497145Ssam ifp->int_flags |= IFF_POINTOPOINT; 3507145Ssam ifp->int_dstaddr = *((struct sockaddr *)&dst); 3517212Ssam } 3527792Ssam if (strcmp(qual, "passive") == 0) 3537145Ssam ifp->int_flags |= IFF_PASSIVE; 3547250Ssam else 3557250Ssam /* assume no duplicate entries */ 3567250Ssam externalinterfaces++; 3577145Ssam ifp->int_addr = *((struct sockaddr *)&gate); 3587145Ssam ifp->int_metric = metric; 3597145Ssam ifp->int_next = ifnet; 3607145Ssam ifnet = ifp; 3617145Ssam addrouteforif(ifp); 3627130Swnj } 3637130Swnj fclose(fp); 3647130Swnj } 3657130Swnj 3667250Ssam /* 3677250Ssam * Timer routine. Performs routing information supply 3687250Ssam * duties and manages timers on routing table entries. 3697250Ssam */ 3707133Swnj timer() 3716920Ssam { 3726934Ssam register struct rthash *rh; 3736920Ssam register struct rt_entry *rt; 3746934Ssam struct rthash *base = hosthash; 3757250Ssam int doinghost = 1, timetobroadcast; 3766920Ssam 3777133Swnj timeval += TIMER_RATE; 3787145Ssam if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0) 3797145Ssam ifinit(); 3807250Ssam timetobroadcast = supplier && (timeval % SUPPLY_INTERVAL) == 0; 3817133Swnj tprintf(">>> time %d >>>\n", timeval); 3826920Ssam again: 3837133Swnj for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { 3847133Swnj rt = rh->rt_forw; 3857133Swnj for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 3867250Ssam /* 3877250Ssam * We don't advance time on a routing entry for 3887250Ssam * a passive gateway or that for our only interface. 3897250Ssam * The latter is excused because we don't act as 3907250Ssam * a routing information supplier and hence would 3917250Ssam * time it out. This is fair as if it's down 3927250Ssam * we're cut off from the world anyway and it's 3937250Ssam * not likely we'll grow any new hardware in 3947250Ssam * the mean time. 3957250Ssam */ 3967250Ssam if (!(rt->rt_state & RTS_PASSIVE) && 3977250Ssam (supplier || !(rt->rt_state & RTS_INTERFACE))) 3987133Swnj rt->rt_timer += TIMER_RATE; 3997133Swnj if (rt->rt_timer >= EXPIRE_TIME) 4007133Swnj rt->rt_metric = HOPCNT_INFINITY; 4017145Ssam log("", rt); 4027145Ssam if (rt->rt_timer >= GARBAGE_TIME) { 4037133Swnj rt = rt->rt_back; 4047133Swnj rtdelete(rt->rt_forw); 4057133Swnj continue; 4067133Swnj } 4077145Ssam if (rt->rt_state & RTS_CHANGED) { 4087145Ssam rt->rt_state &= ~RTS_CHANGED; 4097145Ssam /* don't send extraneous packets */ 4107250Ssam if (!supplier || timetobroadcast) 4117145Ssam continue; 4127133Swnj log("broadcast", rt); 4137133Swnj msg->rip_cmd = RIPCMD_RESPONSE; 4147133Swnj msg->rip_nets[0].rip_dst = rt->rt_dst; 4157133Swnj msg->rip_nets[0].rip_metric = 4167133Swnj min(rt->rt_metric+1, HOPCNT_INFINITY); 4177139Ssam toall(sendmsg); 4187133Swnj } 4197133Swnj } 4206920Ssam } 4216920Ssam if (doinghost) { 4227133Swnj doinghost = 0; 4236929Ssam base = nethash; 4246920Ssam goto again; 4256920Ssam } 4267250Ssam if (timetobroadcast) 4277133Swnj toall(supply); 4287133Swnj tprintf("<<< time %d <<<\n", timeval); 4297133Swnj alarm(TIMER_RATE); 4306920Ssam } 4316920Ssam 4327133Swnj toall(f) 4337133Swnj int (*f)(); 4346920Ssam { 4357145Ssam register struct interface *ifp; 4366920Ssam register struct sockaddr *dst; 4376920Ssam 4387145Ssam for (ifp = ifnet; ifp; ifp = ifp->int_next) { 4397145Ssam if (ifp->int_flags & IFF_PASSIVE) 4406920Ssam continue; 4417145Ssam dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr : 4427145Ssam ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr : 4437145Ssam &ifp->int_addr; 4447145Ssam (*f)(dst, ifp->int_flags & IFF_INTERFACE); 4456920Ssam } 4466920Ssam } 4476920Ssam 4487139Ssam /*ARGSUSED*/ 4497139Ssam sendmsg(dst, dontroute) 4507133Swnj struct sockaddr *dst; 4517139Ssam int dontroute; 4527133Swnj { 4537133Swnj (*afswitch[dst->sa_family].af_output)(s, dst, sizeof (struct rip)); 4547133Swnj } 4557133Swnj 4567250Ssam /* 4577250Ssam * Supply dst with the contents of the routing tables. 4587250Ssam * If this won't fit in one packet, chop it up into several. 4597250Ssam */ 4607139Ssam supply(dst, dontroute) 4617139Ssam struct sockaddr *dst; 4627139Ssam int dontroute; 4637139Ssam { 4647133Swnj register struct rt_entry *rt; 4656920Ssam struct netinfo *n = msg->rip_nets; 4666934Ssam register struct rthash *rh; 4676934Ssam struct rthash *base = hosthash; 4686929Ssam int doinghost = 1, size; 4697139Ssam int (*output)() = afswitch[dst->sa_family].af_output; 4707139Ssam int sto = dontroute ? snoroute : s; 4716920Ssam 4727212Ssam msg->rip_cmd = RIPCMD_RESPONSE; 4736920Ssam again: 4746920Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) 4756920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 4766929Ssam size = (char *)n - packet; 4776929Ssam if (size > MAXPACKETSIZE - sizeof (struct netinfo)) { 4787139Ssam (*output)(sto, dst, size); 4796920Ssam n = msg->rip_nets; 4806920Ssam } 4816920Ssam n->rip_dst = rt->rt_dst; 4826929Ssam n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY); 4836929Ssam n++; 4846920Ssam } 4856920Ssam if (doinghost) { 4866920Ssam doinghost = 0; 4876920Ssam base = nethash; 4886920Ssam goto again; 4896920Ssam } 4906929Ssam if (n != msg->rip_nets) 4917139Ssam (*output)(sto, dst, (char *)n - packet); 4926920Ssam } 4936920Ssam 4946920Ssam /* 4956920Ssam * Handle an incoming routing packet. 4966920Ssam */ 4976920Ssam rip_input(from, size) 4986920Ssam struct sockaddr *from; 4996920Ssam int size; 5006920Ssam { 5016920Ssam struct rt_entry *rt; 5026920Ssam struct netinfo *n; 5037145Ssam struct interface *ifp; 5047133Swnj time_t t; 5057139Ssam int newsize; 5067139Ssam struct afswitch *afp; 5076920Ssam 5087139Ssam if (trace) { 5097139Ssam if (msg->rip_cmd < RIPCMD_MAX) 5107139Ssam printf("%s from %x\n", ripcmds[msg->rip_cmd], 5117139Ssam ((struct sockaddr_in *)from)->sin_addr); 5127139Ssam else 5137139Ssam printf("%x from %x\n", msg->rip_cmd, 5147139Ssam ((struct sockaddr_in *)from)->sin_addr); 5157139Ssam } 5167139Ssam if (from->sa_family >= AF_MAX) 5177139Ssam return; 5187139Ssam afp = &afswitch[from->sa_family]; 5196937Ssam switch (msg->rip_cmd) { 5206937Ssam 5217139Ssam case RIPCMD_REQUEST: 5227139Ssam newsize = 0; 5237139Ssam size -= 4 * sizeof (char); 5247139Ssam n = msg->rip_nets; 5257139Ssam while (size > 0) { 5267139Ssam if (size < sizeof (struct netinfo)) 5277139Ssam break; 5287139Ssam size -= sizeof (struct netinfo); 5296920Ssam 5307139Ssam /* 5317139Ssam * A single entry with sa_family == AF_UNSPEC and 5327139Ssam * metric ``infinity'' means ``all routes''. 5337139Ssam */ 5347139Ssam if (n->rip_dst.sa_family == AF_UNSPEC && 5357139Ssam n->rip_metric == HOPCNT_INFINITY && size == 0) { 5367212Ssam supply(from, 0); 5377139Ssam return; 5387139Ssam } 5397139Ssam rt = rtlookup(&n->rip_dst); 5407139Ssam n->rip_metric = rt == 0 ? HOPCNT_INFINITY : 5417139Ssam min(rt->rt_metric+1, HOPCNT_INFINITY); 5427139Ssam n++, newsize += sizeof (struct netinfo); 5437139Ssam } 5447139Ssam if (newsize > 0) { 5457139Ssam msg->rip_cmd = RIPCMD_RESPONSE; 5467139Ssam newsize += sizeof (int); 5477139Ssam (*afp->af_output)(s, from, newsize); 5487139Ssam } 5496920Ssam return; 5506937Ssam 5517029Ssam case RIPCMD_TRACEON: 5527139Ssam if ((*afp->af_portcheck)(from) == 0) 5537029Ssam return; 5547133Swnj if (trace) 5557133Swnj return; 5567133Swnj packet[size] = '\0'; 5577133Swnj ftrace = fopen(msg->rip_tracefile, "a"); 5587133Swnj if (ftrace == NULL) 5597133Swnj return; 5607133Swnj (void) dup2(fileno(ftrace), 1); 5617133Swnj (void) dup2(fileno(ftrace), 2); 5627133Swnj trace = 1; 5637133Swnj t = time(0); 5647133Swnj printf("*** Tracing turned on at %.24s ***\n", ctime(&t)); 5657029Ssam return; 5667029Ssam 5677133Swnj case RIPCMD_TRACEOFF: 5687133Swnj /* verify message came from a priviledged port */ 5697139Ssam if ((*afp->af_portcheck)(from) == 0) 5706937Ssam return; 5717029Ssam if (!trace) 5727029Ssam return; 5737029Ssam t = time(0); 5747029Ssam printf("*** Tracing turned off at %.24s ***\n", ctime(&t)); 5757029Ssam fflush(stdout), fflush(stderr); 5767029Ssam if (ftrace) 5777029Ssam fclose(ftrace); 5787029Ssam (void) close(1), (void) close(2); 5797029Ssam trace = 0; 5807029Ssam return; 5817133Swnj 5827133Swnj case RIPCMD_RESPONSE: 5837133Swnj /* verify message came from a router */ 5847139Ssam if ((*afp->af_portmatch)(from) == 0) 5857133Swnj return; 5867139Ssam (*afp->af_canon)(from); 5877133Swnj /* are we talking to ourselves? */ 5887133Swnj ifp = if_ifwithaddr(from); 5897133Swnj if (ifp) { 5907145Ssam rt = rtfind(from); 5917145Ssam if (rt == 0) 5927133Swnj addrouteforif(ifp); 5937145Ssam else 5947145Ssam rt->rt_timer = 0; 5957133Swnj return; 5967133Swnj } 5977133Swnj size -= 4 * sizeof (char); 5987133Swnj n = msg->rip_nets; 5997133Swnj for (; size > 0; size -= sizeof (struct netinfo), n++) { 6007133Swnj if (size < sizeof (struct netinfo)) 6017133Swnj break; 6027133Swnj if (n->rip_metric >= HOPCNT_INFINITY) 6037133Swnj continue; 6047133Swnj tprintf("dst %x hc %d...", 6057133Swnj ((struct sockaddr_in *)&n->rip_dst)->sin_addr, 6067133Swnj n->rip_metric); 6077133Swnj rt = rtlookup(&n->rip_dst); 6087133Swnj if (rt == 0) { 6097133Swnj rtadd(&n->rip_dst, from, n->rip_metric, 0); 6107133Swnj continue; 6117133Swnj } 6127133Swnj tprintf("ours: gate %x hc %d timer %d\n", 6137133Swnj ((struct sockaddr_in *)&rt->rt_router)->sin_addr, 6147133Swnj rt->rt_metric, rt->rt_timer); 6157139Ssam 6167133Swnj /* 6177250Ssam * Update if from gateway, shorter, or getting 6187139Ssam * stale and equivalent. 6197133Swnj */ 6207133Swnj if (equal(from, &rt->rt_router) || 6217133Swnj n->rip_metric < rt->rt_metric || 6227133Swnj (rt->rt_timer > (EXPIRE_TIME/2) && 6237133Swnj rt->rt_metric == n->rip_metric)) { 6247133Swnj rtchange(rt, from, n->rip_metric); 6257133Swnj rt->rt_timer = 0; 6267133Swnj } 6277133Swnj } 6287133Swnj return; 6297029Ssam } 6307145Ssam tprintf("bad packet, cmd=%x\n", msg->rip_cmd); 6317029Ssam } 6327029Ssam 6337250Ssam /* 6347250Ssam * Lookup dst in the tables for an exact match. 6357250Ssam */ 6366920Ssam struct rt_entry * 6376920Ssam rtlookup(dst) 6386920Ssam struct sockaddr *dst; 6396920Ssam { 6406920Ssam register struct rt_entry *rt; 6416934Ssam register struct rthash *rh; 6427011Ssam register int hash; 6436920Ssam struct afhash h; 6447011Ssam int doinghost = 1; 6456920Ssam 6467011Ssam if (dst->sa_family >= AF_MAX) 6477011Ssam return (0); 6487011Ssam (*afswitch[dst->sa_family].af_hash)(dst, &h); 6497011Ssam hash = h.afh_hosthash; 6507011Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 6517011Ssam again: 6527011Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 6537145Ssam if (rt->rt_hash != hash) 6547011Ssam continue; 6557011Ssam if (equal(&rt->rt_dst, dst)) 6567011Ssam return (rt); 6577011Ssam } 6587011Ssam if (doinghost) { 6597011Ssam doinghost = 0; 6607011Ssam hash = h.afh_nethash; 6617011Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 6627011Ssam goto again; 6637011Ssam } 6647011Ssam return (0); 6657011Ssam } 6667011Ssam 6677250Ssam /* 6687250Ssam * Find a route to dst as the kernel would. 6697250Ssam */ 6707011Ssam struct rt_entry * 6717145Ssam rtfind(dst) 6727011Ssam struct sockaddr *dst; 6737011Ssam { 6747011Ssam register struct rt_entry *rt; 6757011Ssam register struct rthash *rh; 6767011Ssam register int hash; 6777011Ssam struct afhash h; 6787011Ssam int af = dst->sa_family; 6797011Ssam int doinghost = 1, (*match)(); 6807011Ssam 6816920Ssam if (af >= AF_MAX) 6826920Ssam return (0); 6836920Ssam (*afswitch[af].af_hash)(dst, &h); 6846920Ssam hash = h.afh_hosthash; 6856920Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 6866920Ssam 6876920Ssam again: 6886920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 6896920Ssam if (rt->rt_hash != hash) 6906920Ssam continue; 6916920Ssam if (doinghost) { 6926920Ssam if (equal(&rt->rt_dst, dst)) 6936920Ssam return (rt); 6946920Ssam } else { 6956920Ssam if (rt->rt_dst.sa_family == af && 6966920Ssam (*match)(&rt->rt_dst, dst)) 6976920Ssam return (rt); 6986920Ssam } 6996920Ssam } 7006920Ssam if (doinghost) { 7016920Ssam doinghost = 0; 7026920Ssam hash = h.afh_nethash; 7037011Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 7046920Ssam match = afswitch[af].af_netmatch; 7056920Ssam goto again; 7066920Ssam } 7076920Ssam return (0); 7086920Ssam } 7096920Ssam 7107139Ssam rtadd(dst, gate, metric, state) 7116920Ssam struct sockaddr *dst, *gate; 7127139Ssam int metric, state; 7136920Ssam { 7146920Ssam struct afhash h; 7156920Ssam register struct rt_entry *rt; 7166934Ssam struct rthash *rh; 7176920Ssam int af = dst->sa_family, flags, hash; 7186920Ssam 7196920Ssam if (af >= AF_MAX) 7206920Ssam return; 7216920Ssam (*afswitch[af].af_hash)(dst, &h); 7226920Ssam flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0; 7236920Ssam if (flags & RTF_HOST) { 7246920Ssam hash = h.afh_hosthash; 7256920Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 7266920Ssam } else { 7276920Ssam hash = h.afh_nethash; 7286920Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 7296920Ssam } 7306920Ssam rt = (struct rt_entry *)malloc(sizeof (*rt)); 7316920Ssam if (rt == 0) 7326920Ssam return; 7336920Ssam rt->rt_hash = hash; 7346920Ssam rt->rt_dst = *dst; 7357130Swnj rt->rt_router = *gate; 7366920Ssam rt->rt_metric = metric; 7376920Ssam rt->rt_timer = 0; 7387139Ssam rt->rt_flags = RTF_UP | flags; 7397145Ssam rt->rt_state = state | RTS_CHANGED; 7407130Swnj rt->rt_ifp = if_ifwithnet(&rt->rt_router); 7417130Swnj if (metric) 7427130Swnj rt->rt_flags |= RTF_GATEWAY; 7436920Ssam insque(rt, rh); 7446929Ssam log("add", rt); 7457145Ssam if (install && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) 7467145Ssam tprintf("SIOCADDRT: %s\n", sys_errlist[errno]); 7476920Ssam } 7486920Ssam 7496920Ssam rtchange(rt, gate, metric) 7506920Ssam struct rt_entry *rt; 7516920Ssam struct sockaddr *gate; 7526920Ssam short metric; 7536920Ssam { 7547139Ssam int doioctl = 0, metricchanged = 0; 7557145Ssam struct rtentry oldroute; 7566920Ssam 7577145Ssam if (!equal(&rt->rt_router, gate)) 7587139Ssam doioctl++; 7596920Ssam if (metric != rt->rt_metric) { 7607139Ssam metricchanged++; 7616920Ssam rt->rt_metric = metric; 7626920Ssam } 7637145Ssam if (doioctl || metricchanged) { 7647145Ssam log("change", rt); 7657145Ssam rt->rt_state |= RTS_CHANGED; 7667145Ssam } 7677139Ssam if (doioctl) { 7687145Ssam oldroute = rt->rt_rt; 7697139Ssam rt->rt_router = *gate; 7707145Ssam if (install) { 7717145Ssam if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) 7727145Ssam tprintf("SIOCADDRT: %s\n", sys_errlist[errno]); 7737145Ssam if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0) 7747145Ssam tprintf("SIOCDELRT: %s\n", sys_errlist[errno]); 7757145Ssam } 7767139Ssam } 7776920Ssam } 7786920Ssam 7796920Ssam rtdelete(rt) 7806920Ssam struct rt_entry *rt; 7816920Ssam { 7826929Ssam log("delete", rt); 7837130Swnj if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt)) 7847145Ssam tprintf("SIOCDELRT: %s\n", sys_errlist[errno]); 7856920Ssam remque(rt); 7866920Ssam free((char *)rt); 7876920Ssam } 7886920Ssam 7896920Ssam log(operation, rt) 7906920Ssam char *operation; 7916920Ssam struct rt_entry *rt; 7926920Ssam { 7936920Ssam struct sockaddr_in *dst, *gate; 7946937Ssam static struct bits { 7956920Ssam int t_bits; 7966920Ssam char *t_name; 7976937Ssam } flagbits[] = { 7986920Ssam { RTF_UP, "UP" }, 7997130Swnj { RTF_GATEWAY, "GATEWAY" }, 8006920Ssam { RTF_HOST, "HOST" }, 8016920Ssam { 0 } 8026937Ssam }, statebits[] = { 8037130Swnj { RTS_PASSIVE, "PASSIVE" }, 8047145Ssam { RTS_REMOTE, "REMOTE" }, 8057145Ssam { RTS_INTERFACE,"INTERFACE" }, 8067145Ssam { RTS_CHANGED, "CHANGED" }, 8076937Ssam { 0 } 8086920Ssam }; 8096937Ssam register struct bits *p; 8106920Ssam register int first; 8116920Ssam char *cp; 8126920Ssam 8136929Ssam if (trace == 0) 8146929Ssam return; 8156920Ssam printf("%s ", operation); 8166920Ssam dst = (struct sockaddr_in *)&rt->rt_dst; 8177130Swnj gate = (struct sockaddr_in *)&rt->rt_router; 8187145Ssam printf("dst %x, router %x, metric %d, flags", dst->sin_addr, 8197145Ssam gate->sin_addr, rt->rt_metric); 8206937Ssam cp = " %s"; 8216937Ssam for (first = 1, p = flagbits; p->t_bits > 0; p++) { 8226920Ssam if ((rt->rt_flags & p->t_bits) == 0) 8236920Ssam continue; 8246920Ssam printf(cp, p->t_name); 8256920Ssam if (first) { 8266920Ssam cp = "|%s"; 8276920Ssam first = 0; 8286920Ssam } 8296920Ssam } 8306937Ssam printf(" state"); 8316937Ssam cp = " %s"; 8326937Ssam for (first = 1, p = statebits; p->t_bits > 0; p++) { 8336937Ssam if ((rt->rt_state & p->t_bits) == 0) 8346937Ssam continue; 8356937Ssam printf(cp, p->t_name); 8366937Ssam if (first) { 8376937Ssam cp = "|%s"; 8386937Ssam first = 0; 8396937Ssam } 8406937Ssam } 8416920Ssam putchar('\n'); 8426920Ssam } 8436929Ssam 8447145Ssam struct interface * 8456929Ssam if_ifwithaddr(addr) 8466929Ssam struct sockaddr *addr; 8476929Ssam { 8487145Ssam register struct interface *ifp; 8496929Ssam 8506929Ssam #define same(a1, a2) \ 8516929Ssam (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0) 8527145Ssam for (ifp = ifnet; ifp; ifp = ifp->int_next) { 8537145Ssam if (ifp->int_flags & IFF_REMOTE) 8546929Ssam continue; 8557145Ssam if (ifp->int_addr.sa_family != addr->sa_family) 8567145Ssam continue; 8577145Ssam if (same(&ifp->int_addr, addr)) 8586929Ssam break; 8597145Ssam if ((ifp->int_flags & IFF_BROADCAST) && 8607145Ssam same(&ifp->int_broadaddr, addr)) 8616929Ssam break; 8626929Ssam } 8636929Ssam return (ifp); 8646929Ssam #undef same 8656929Ssam } 8666929Ssam 8677145Ssam struct interface * 8686929Ssam if_ifwithnet(addr) 8696929Ssam register struct sockaddr *addr; 8706929Ssam { 8717145Ssam register struct interface *ifp; 8726929Ssam register int af = addr->sa_family; 8736929Ssam register int (*netmatch)(); 8746929Ssam 8756929Ssam if (af >= AF_MAX) 8766929Ssam return (0); 8776929Ssam netmatch = afswitch[af].af_netmatch; 8787145Ssam for (ifp = ifnet; ifp; ifp = ifp->int_next) { 8797145Ssam if (ifp->int_flags & IFF_REMOTE) 8806929Ssam continue; 8817145Ssam if (af != ifp->int_addr.sa_family) 8827145Ssam continue; 8837145Ssam if ((*netmatch)(addr, &ifp->int_addr)) 8846929Ssam break; 8856929Ssam } 8866929Ssam return (ifp); 8876929Ssam } 8886929Ssam 8896929Ssam struct in_addr 8906929Ssam if_makeaddr(net, host) 8916929Ssam int net, host; 8926929Ssam { 8936929Ssam u_long addr; 8946929Ssam 8956929Ssam if (net < 128) 8966929Ssam addr = (net << 24) | host; 8976929Ssam else if (net < 65536) 8986929Ssam addr = (net << 16) | host; 8996929Ssam else 9006929Ssam addr = (net << 8) | host; 9017794Ssam #if vax || pdp11 9026929Ssam addr = htonl(addr); 9036929Ssam #endif 9046929Ssam return (*(struct in_addr *)&addr); 9056929Ssam } 9067794Ssam 9077794Ssam /* 9087794Ssam * Return the network number from an internet 9097794Ssam * address; handles class a/b/c network #'s. 9107794Ssam */ 9117794Ssam in_netof(in) 9127794Ssam struct in_addr in; 9137794Ssam { 9147794Ssam #if vax || pdp11 9157794Ssam register u_long net; 9167794Ssam 9177794Ssam if ((in.s_addr&IN_CLASSA) == 0) 9187794Ssam return (in.s_addr & IN_CLASSA_NET); 9197794Ssam if ((in.s_addr&IN_CLASSB) == 0) 9207794Ssam return ((int)htons((u_short)(in.s_addr & IN_CLASSB_NET))); 9217794Ssam net = htonl((u_long)(in.s_addr & IN_CLASSC_NET)); 9227794Ssam net >>= 8; 9237794Ssam return ((int)net); 9247794Ssam #else 9257794Ssam return (IN_NETOF(in)); 9267794Ssam #endif 9277794Ssam } 9287794Ssam 9297794Ssam /* 9307794Ssam * Return the local network address portion of an 9317794Ssam * internet address; handles class a/b/c network 9327794Ssam * number formats. 9337794Ssam */ 9347794Ssam in_lnaof(in) 9357794Ssam struct in_addr in; 9367794Ssam { 9377794Ssam #if vax || pdp11 9387794Ssam #define IN_LNAOF(in) \ 9397794Ssam (((in).s_addr&IN_CLASSA) == 0 ? (in).s_addr&IN_CLASSA_LNA : \ 9407794Ssam ((in).s_addr&IN_CLASSB) == 0 ? (in).s_addr&IN_CLASSB_LNA : \ 9417794Ssam (in).s_addr&IN_CLASSC_LNA) 9427794Ssam return ((int)htonl((u_long)IN_LNAOF(in))); 9437794Ssam #else 9447794Ssam return (IN_LNAOF(in)); 9457794Ssam #endif 9467794Ssam } 947