16920Ssam #ifndef lint 2*7794Ssam static char sccsid[] = "@(#)routed.c 4.22 08/19/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> 187139Ssam #define RIPCMDS 196920Ssam #include "rip.h" 206920Ssam #include "router.h" 216920Ssam 226920Ssam #define LOOPBACKNET 0177 236920Ssam /* casts to keep lint happy */ 246920Ssam #define insque(q,p) _insque((caddr_t)q,(caddr_t)p) 256920Ssam #define remque(q) _remque((caddr_t)q) 266920Ssam #define equal(a1, a2) \ 276920Ssam (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0) 286929Ssam #define min(a,b) ((a)>(b)?(b):(a)) 296920Ssam 306920Ssam struct nlist nl[] = { 316920Ssam #define N_IFNET 0 326920Ssam { "_ifnet" }, 336920Ssam 0, 346920Ssam }; 356920Ssam 367139Ssam struct sockaddr_in routingaddr = { AF_INET, IPPORT_ROUTESERVER }; 377139Ssam struct sockaddr_in noroutingaddr = { AF_INET, IPPORT_ROUTESERVER+1 }; 386920Ssam 396920Ssam int s; 407130Swnj int snoroute; /* socket with no routing */ 416929Ssam int kmem = -1; 427133Swnj int supplier = -1; /* process should supply updates */ 436962Ssam int install = 1; /* if 1 call kernel */ 447145Ssam int lookforinterfaces = 1; 457145Ssam int performnlist = 1; 467250Ssam int externalinterfaces = 0; /* # of remote and local interfaces */ 477133Swnj int timeval = -TIMER_RATE; 486920Ssam int timer(); 496920Ssam int cleanup(); 507133Swnj 517133Swnj #define tprintf if (trace) printf 526920Ssam int trace = 0; 537029Ssam FILE *ftrace; 546920Ssam 557133Swnj char packet[MAXPACKETSIZE+1]; 567133Swnj struct rip *msg = (struct rip *)packet; 576920Ssam 586934Ssam struct in_addr if_makeaddr(); 597145Ssam struct interface *if_ifwithaddr(), *if_ifwithnet(); 607145Ssam extern char *malloc(), *sys_errlist[]; 616922Ssam extern int errno, exit(); 627133Swnj char **argv0; 636920Ssam 647139Ssam int sendmsg(), supply(); 657133Swnj 666920Ssam main(argc, argv) 676920Ssam int argc; 686920Ssam char *argv[]; 696920Ssam { 706920Ssam int cc; 716920Ssam struct sockaddr from; 726920Ssam 737133Swnj argv0 = argv; 747029Ssam #ifndef DEBUG 757029Ssam if (fork()) 767029Ssam exit(0); 777029Ssam for (cc = 0; cc < 10; cc++) 787029Ssam (void) close(cc); 797029Ssam (void) open("/", 0); 807029Ssam (void) dup2(0, 1); 817029Ssam (void) dup2(0, 2); 827029Ssam { int t = open("/dev/tty", 2); 837029Ssam if (t >= 0) { 847029Ssam ioctl(t, TIOCNOTTY, (char *)0); 857029Ssam (void) close(t); 867029Ssam } 876920Ssam } 887029Ssam #endif 896920Ssam if (trace) { 907029Ssam ftrace = fopen("/etc/routerlog", "w"); 917029Ssam dup2(fileno(ftrace), 1); 927029Ssam dup2(fileno(ftrace), 2); 936920Ssam } 947250Ssam 957250Ssam /* 967250Ssam * We use two sockets. One for which outgoing 977250Ssam * packets are routed and for which they're not. 987250Ssam * The latter allows us to delete routing table 997250Ssam * entries in the kernel for network interfaces 1007250Ssam * attached to our host which we believe are down 1017250Ssam * while still polling it to see when/if it comes 1027250Ssam * back up. With the new ipc interface we'll be 1037250Ssam * able to specify ``don't route'' as an option 1047250Ssam * to send, but until then we utilize a second port. 1057250Ssam */ 1066937Ssam #ifdef vax || pdp11 1077139Ssam routingaddr.sin_port = htons(routingaddr.sin_port); 1087139Ssam noroutingaddr.sin_port = htons(noroutingaddr.sin_port); 1096920Ssam #endif 1106920Ssam again: 1117139Ssam s = socket(SOCK_DGRAM, 0, &routingaddr, 0); 1126920Ssam if (s < 0) { 1136920Ssam perror("socket"); 1146920Ssam sleep(30); 1156920Ssam goto again; 1166920Ssam } 1177130Swnj again2: 1187139Ssam snoroute = socket(SOCK_DGRAM, 0, &noroutingaddr, SO_DONTROUTE); 1197130Swnj if (snoroute < 0) { 1207130Swnj perror("socket"); 1217130Swnj sleep(30); 1227130Swnj goto again2; 1237130Swnj } 1246920Ssam argv++, argc--; 1257133Swnj while (argc > 0 && **argv == '-') { 1267133Swnj if (!strcmp(*argv, "-s") == 0) { 1276929Ssam supplier = 1; 1287133Swnj argv++, argc--; 1297133Swnj continue; 1307133Swnj } 1317133Swnj if (!strcmp(*argv, "-q") == 0) { 1326920Ssam supplier = 0; 1337133Swnj argv++, argc--; 1347133Swnj continue; 1357133Swnj } 1367133Swnj goto usage; 1376920Ssam } 1387133Swnj if (argc > 0) { 1397133Swnj usage: 1407145Ssam fprintf(stderr, "usage: routed [ -sq ]\n"); 1417133Swnj exit(1); 1427133Swnj } 1437250Ssam /* 1447250Ssam * Collect an initial view of the world by 1457250Ssam * snooping in the kernel and the gateway kludge 1467250Ssam * file. Then, send a request packet on all 1477250Ssam * directly connected networks to find out what 1487250Ssam * everyone else thinks. 1497250Ssam */ 1507133Swnj rtinit(); 1517250Ssam gwkludge(); 1527133Swnj ifinit(); 1537133Swnj if (supplier < 0) 1547133Swnj supplier = 0; 1557133Swnj msg->rip_cmd = RIPCMD_REQUEST; 1567133Swnj msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC; 1577133Swnj msg->rip_nets[0].rip_metric = HOPCNT_INFINITY; 1587133Swnj toall(sendmsg); 1596920Ssam sigset(SIGALRM, timer); 1606929Ssam timer(); 1616920Ssam 1626920Ssam for (;;) { 1636920Ssam cc = receive(s, &from, packet, sizeof (packet)); 1646920Ssam if (cc <= 0) { 1656920Ssam if (cc < 0 && errno != EINTR) 1666920Ssam perror("receive"); 1676920Ssam continue; 1686920Ssam } 1696920Ssam sighold(SIGALRM); 1706920Ssam rip_input(&from, cc); 1716920Ssam sigrelse(SIGALRM); 1726920Ssam } 1736920Ssam } 1746920Ssam 1757133Swnj rtinit() 1766920Ssam { 1776934Ssam register struct rthash *rh; 1786920Ssam 1797133Swnj for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) 1807133Swnj rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 1817133Swnj for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) 1827133Swnj rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 1836920Ssam } 1846920Ssam 1857145Ssam struct interface *ifnet; 1867133Swnj 1877250Ssam /* 1887250Ssam * Probe the kernel through /dev/kmem to find the network 1897250Ssam * interfaces which have configured themselves. If the 1907250Ssam * interface is present but not yet up (for example an 1917250Ssam * ARPANET IMP), set the lookforinterfaces flag so we'll 1927250Ssam * come back later and look again. 1937250Ssam */ 1947133Swnj ifinit() 1956920Ssam { 1967145Ssam struct interface *ifp; 1977145Ssam struct ifnet ifs, *next; 1986920Ssam 1997145Ssam if (performnlist) { 2007145Ssam nlist("/vmunix", nl); 2017145Ssam if (nl[N_IFNET].n_value == 0) { 2027145Ssam printf("ifnet: not in namelist\n"); 2037145Ssam goto bad; 2047145Ssam } 2057145Ssam performnlist = 0; 2066920Ssam } 2076920Ssam if (kmem < 0) { 2087145Ssam kmem = open("/dev/kmem", 0); 2097145Ssam if (kmem < 0) { 2107145Ssam perror("/dev/kmem"); 2117145Ssam goto bad; 2127145Ssam } 2136920Ssam } 2146929Ssam if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 || 2157133Swnj read(kmem, (char *)&next, sizeof (next)) != sizeof (next)) { 2167133Swnj printf("ifnet: error reading kmem\n"); 2177133Swnj goto bad; 2186929Ssam } 2197145Ssam lookforinterfaces = 0; 2207133Swnj while (next) { 2217133Swnj if (lseek(kmem, (long)next, 0) == -1 || 2227145Ssam read(kmem, (char *)&ifs, sizeof (ifs)) != sizeof (ifs)) { 2237133Swnj perror("read"); 2247133Swnj goto bad; 2256929Ssam } 2267145Ssam next = ifs.if_next; 2277145Ssam if ((ifs.if_flags & IFF_UP) == 0) { 2287145Ssam lookforinterfaces = 1; 2297133Swnj continue; 2307145Ssam } 2317250Ssam /* already known to us? */ 2327145Ssam if (if_ifwithaddr(&ifs.if_addr)) 2337133Swnj continue; 2347250Ssam /* argh, this'll have to change sometime */ 2357145Ssam if (ifs.if_addr.sa_family != AF_INET) 2367145Ssam continue; 2377250Ssam /* no one cares about software loopback interfaces */ 2387145Ssam if (ifs.if_net == LOOPBACKNET) 2397145Ssam continue; 2407145Ssam ifp = (struct interface *)malloc(sizeof (struct interface)); 2417145Ssam if (ifp == 0) { 2427145Ssam printf("routed: out of memory\n"); 2437145Ssam break; 2447145Ssam } 2457145Ssam /* 2467145Ssam * Count the # of directly connected networks 2477145Ssam * and point to point links which aren't looped 2487145Ssam * back to ourself. This is used below to 2497250Ssam * decide if we should be a routing ``supplier''. 2507145Ssam */ 2517145Ssam if ((ifs.if_flags & IFF_POINTOPOINT) == 0 || 2527145Ssam if_ifwithaddr(&ifs.if_dstaddr) == 0) 2537145Ssam externalinterfaces++; 2547145Ssam ifp->int_addr = ifs.if_addr; 2557145Ssam ifp->int_flags = ifs.if_flags | IFF_INTERFACE; 2567145Ssam /* this works because broadaddr overlaps dstaddr */ 2577145Ssam ifp->int_broadaddr = ifs.if_broadaddr; 2587145Ssam ifp->int_net = ifs.if_net; 2597145Ssam ifp->int_metric = 0; 2607145Ssam ifp->int_next = ifnet; 2616929Ssam ifnet = ifp; 2627133Swnj addrouteforif(ifp); 2637133Swnj } 2647145Ssam if (externalinterfaces > 1 && supplier < 0) 2657133Swnj supplier = 1; 2667133Swnj return; 2677133Swnj bad: 2687133Swnj sleep(60); 2697145Ssam close(kmem), close(s), close(snoroute); 2707133Swnj execv("/etc/routed", argv0); 2717133Swnj _exit(0177); 2727133Swnj } 2737130Swnj 2747133Swnj addrouteforif(ifp) 2757145Ssam struct interface *ifp; 2767133Swnj { 2777133Swnj struct sockaddr_in net; 2787133Swnj struct sockaddr *dst; 2797145Ssam int state, metric; 2807258Ssam struct rt_entry *rt; 2817133Swnj 2827145Ssam if (ifp->int_flags & IFF_POINTOPOINT) 2837145Ssam dst = &ifp->int_dstaddr; 2847133Swnj else { 2857133Swnj bzero((char *)&net, sizeof (net)); 2867133Swnj net.sin_family = AF_INET; 2877145Ssam net.sin_addr = if_makeaddr(ifp->int_net, INADDR_ANY); 2887133Swnj dst = (struct sockaddr *)&net; 2896920Ssam } 2907258Ssam rt = rtlookup(dst); 2917258Ssam rtadd(dst, &ifp->int_addr, ifp->int_metric, 2927258Ssam ifp->int_flags & (IFF_INTERFACE|IFF_PASSIVE|IFF_REMOTE)); 2937258Ssam if (rt) 2947258Ssam rtdelete(rt); 2956920Ssam } 2966920Ssam 2977250Ssam /* 2987250Ssam * As a concession to the ARPANET we read a list of gateways 2997250Ssam * from /etc/gateways and add them to our tables. This file 3007250Ssam * exists at each ARPANET gateway and indicates a set of ``remote'' 3017250Ssam * gateways (i.e. a gateway which we can't immediately determine 3027250Ssam * if it's present or not as we can do for those directly connected 3037250Ssam * at the hardware level). If a gateway is marked ``passive'' 3047250Ssam * in the file, then we assume it doesn't have a routing process 3057250Ssam * of our design and simply assume it's always present. Those 3067250Ssam * not marked passive are treated as if they were directly 3077250Ssam * connected -- they're added into the interface list so we'll 3087250Ssam * send them routing updates. 3097250Ssam */ 3107133Swnj gwkludge() 3117130Swnj { 3127130Swnj struct sockaddr_in dst, gate; 3137130Swnj FILE *fp; 3147792Ssam char *dname, *gname, *qual, buf[BUFSIZ]; 3157145Ssam struct interface *ifp; 3167145Ssam int metric; 3177130Swnj 3187130Swnj fp = fopen("/etc/gateways", "r"); 3197130Swnj if (fp == NULL) 3207130Swnj return; 3217792Ssam qual = buf; dname = buf + 64; gname = buf + ((BUFSIZ - 64) / 2); 3227130Swnj bzero((char *)&dst, sizeof (dst)); 3237130Swnj bzero((char *)&gate, sizeof (gate)); 3247145Ssam dst.sin_family = gate.sin_family = AF_INET; 3257145Ssam /* format: dst XX gateway XX metric DD [passive]\n */ 3267145Ssam #define readentry(fp) \ 3277792Ssam fscanf((fp), "dst %s gateway %s metric %d %s\n", \ 3287792Ssam dname, gname, &metric, qual) 3297133Swnj for (;;) { 3307145Ssam if (readentry(fp) == EOF) 3317133Swnj break; 3327792Ssam dst.sin_addr.s_addr = rhost(&dname); 3337792Ssam if (dst.sin_addr.s_addr == -1) 3347792Ssam continue; 3357792Ssam gate.sin_addr.s_addr = rhost(&gname); 3367792Ssam if (gate.sin_addr.s_addr == -1) 3377792Ssam continue; 3387145Ssam ifp = (struct interface *)malloc(sizeof (*ifp)); 3397145Ssam bzero((char *)ifp, sizeof (*ifp)); 3407145Ssam ifp->int_flags = IFF_REMOTE; 3417145Ssam /* can't identify broadcast capability */ 342*7794Ssam ifp->int_net = in_netof(dst.sin_addr); 3437145Ssam if ((*afswitch[dst.sin_family].af_checkhost)(&dst)) { 3447145Ssam ifp->int_flags |= IFF_POINTOPOINT; 3457145Ssam ifp->int_dstaddr = *((struct sockaddr *)&dst); 3467212Ssam } 3477792Ssam if (strcmp(qual, "passive") == 0) 3487145Ssam ifp->int_flags |= IFF_PASSIVE; 3497250Ssam else 3507250Ssam /* assume no duplicate entries */ 3517250Ssam externalinterfaces++; 3527145Ssam ifp->int_addr = *((struct sockaddr *)&gate); 3537145Ssam ifp->int_metric = metric; 3547145Ssam ifp->int_next = ifnet; 3557145Ssam ifnet = ifp; 3567145Ssam addrouteforif(ifp); 3577130Swnj } 3587130Swnj fclose(fp); 3597130Swnj } 3607130Swnj 3617250Ssam /* 3627250Ssam * Timer routine. Performs routing information supply 3637250Ssam * duties and manages timers on routing table entries. 3647250Ssam */ 3657133Swnj timer() 3666920Ssam { 3676934Ssam register struct rthash *rh; 3686920Ssam register struct rt_entry *rt; 3696934Ssam struct rthash *base = hosthash; 3707250Ssam int doinghost = 1, timetobroadcast; 3716920Ssam 3727133Swnj timeval += TIMER_RATE; 3737145Ssam if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0) 3747145Ssam ifinit(); 3757250Ssam timetobroadcast = supplier && (timeval % SUPPLY_INTERVAL) == 0; 3767133Swnj tprintf(">>> time %d >>>\n", timeval); 3776920Ssam again: 3787133Swnj for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { 3797133Swnj rt = rh->rt_forw; 3807133Swnj for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 3817250Ssam /* 3827250Ssam * We don't advance time on a routing entry for 3837250Ssam * a passive gateway or that for our only interface. 3847250Ssam * The latter is excused because we don't act as 3857250Ssam * a routing information supplier and hence would 3867250Ssam * time it out. This is fair as if it's down 3877250Ssam * we're cut off from the world anyway and it's 3887250Ssam * not likely we'll grow any new hardware in 3897250Ssam * the mean time. 3907250Ssam */ 3917250Ssam if (!(rt->rt_state & RTS_PASSIVE) && 3927250Ssam (supplier || !(rt->rt_state & RTS_INTERFACE))) 3937133Swnj rt->rt_timer += TIMER_RATE; 3947133Swnj if (rt->rt_timer >= EXPIRE_TIME) 3957133Swnj rt->rt_metric = HOPCNT_INFINITY; 3967145Ssam log("", rt); 3977145Ssam if (rt->rt_timer >= GARBAGE_TIME) { 3987133Swnj rt = rt->rt_back; 3997133Swnj rtdelete(rt->rt_forw); 4007133Swnj continue; 4017133Swnj } 4027145Ssam if (rt->rt_state & RTS_CHANGED) { 4037145Ssam rt->rt_state &= ~RTS_CHANGED; 4047145Ssam /* don't send extraneous packets */ 4057250Ssam if (!supplier || timetobroadcast) 4067145Ssam continue; 4077133Swnj log("broadcast", rt); 4087133Swnj msg->rip_cmd = RIPCMD_RESPONSE; 4097133Swnj msg->rip_nets[0].rip_dst = rt->rt_dst; 4107133Swnj msg->rip_nets[0].rip_metric = 4117133Swnj min(rt->rt_metric+1, HOPCNT_INFINITY); 4127139Ssam toall(sendmsg); 4137133Swnj } 4147133Swnj } 4156920Ssam } 4166920Ssam if (doinghost) { 4177133Swnj doinghost = 0; 4186929Ssam base = nethash; 4196920Ssam goto again; 4206920Ssam } 4217250Ssam if (timetobroadcast) 4227133Swnj toall(supply); 4237133Swnj tprintf("<<< time %d <<<\n", timeval); 4247133Swnj alarm(TIMER_RATE); 4256920Ssam } 4266920Ssam 4277133Swnj toall(f) 4287133Swnj int (*f)(); 4296920Ssam { 4307145Ssam register struct interface *ifp; 4316920Ssam register struct sockaddr *dst; 4326920Ssam 4337145Ssam for (ifp = ifnet; ifp; ifp = ifp->int_next) { 4347145Ssam if (ifp->int_flags & IFF_PASSIVE) 4356920Ssam continue; 4367145Ssam dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr : 4377145Ssam ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr : 4387145Ssam &ifp->int_addr; 4397145Ssam (*f)(dst, ifp->int_flags & IFF_INTERFACE); 4406920Ssam } 4416920Ssam } 4426920Ssam 4437139Ssam /*ARGSUSED*/ 4447139Ssam sendmsg(dst, dontroute) 4457133Swnj struct sockaddr *dst; 4467139Ssam int dontroute; 4477133Swnj { 4487133Swnj (*afswitch[dst->sa_family].af_output)(s, dst, sizeof (struct rip)); 4497133Swnj } 4507133Swnj 4517250Ssam /* 4527250Ssam * Supply dst with the contents of the routing tables. 4537250Ssam * If this won't fit in one packet, chop it up into several. 4547250Ssam */ 4557139Ssam supply(dst, dontroute) 4567139Ssam struct sockaddr *dst; 4577139Ssam int dontroute; 4587139Ssam { 4597133Swnj register struct rt_entry *rt; 4606920Ssam struct netinfo *n = msg->rip_nets; 4616934Ssam register struct rthash *rh; 4626934Ssam struct rthash *base = hosthash; 4636929Ssam int doinghost = 1, size; 4647139Ssam int (*output)() = afswitch[dst->sa_family].af_output; 4657139Ssam int sto = dontroute ? snoroute : s; 4666920Ssam 4677212Ssam msg->rip_cmd = RIPCMD_RESPONSE; 4686920Ssam again: 4696920Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) 4706920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 4716929Ssam size = (char *)n - packet; 4726929Ssam if (size > MAXPACKETSIZE - sizeof (struct netinfo)) { 4737139Ssam (*output)(sto, dst, size); 4746920Ssam n = msg->rip_nets; 4756920Ssam } 4766920Ssam n->rip_dst = rt->rt_dst; 4776929Ssam n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY); 4786929Ssam n++; 4796920Ssam } 4806920Ssam if (doinghost) { 4816920Ssam doinghost = 0; 4826920Ssam base = nethash; 4836920Ssam goto again; 4846920Ssam } 4856929Ssam if (n != msg->rip_nets) 4867139Ssam (*output)(sto, dst, (char *)n - packet); 4876920Ssam } 4886920Ssam 4896920Ssam /* 4906920Ssam * Handle an incoming routing packet. 4916920Ssam */ 4926920Ssam rip_input(from, size) 4936920Ssam struct sockaddr *from; 4946920Ssam int size; 4956920Ssam { 4966920Ssam struct rt_entry *rt; 4976920Ssam struct netinfo *n; 4987145Ssam struct interface *ifp; 4997133Swnj time_t t; 5007139Ssam int newsize; 5017139Ssam struct afswitch *afp; 5026920Ssam 5037139Ssam if (trace) { 5047139Ssam if (msg->rip_cmd < RIPCMD_MAX) 5057139Ssam printf("%s from %x\n", ripcmds[msg->rip_cmd], 5067139Ssam ((struct sockaddr_in *)from)->sin_addr); 5077139Ssam else 5087139Ssam printf("%x from %x\n", msg->rip_cmd, 5097139Ssam ((struct sockaddr_in *)from)->sin_addr); 5107139Ssam } 5117139Ssam if (from->sa_family >= AF_MAX) 5127139Ssam return; 5137139Ssam afp = &afswitch[from->sa_family]; 5146937Ssam switch (msg->rip_cmd) { 5156937Ssam 5167139Ssam case RIPCMD_REQUEST: 5177139Ssam newsize = 0; 5187139Ssam size -= 4 * sizeof (char); 5197139Ssam n = msg->rip_nets; 5207139Ssam while (size > 0) { 5217139Ssam if (size < sizeof (struct netinfo)) 5227139Ssam break; 5237139Ssam size -= sizeof (struct netinfo); 5246920Ssam 5257139Ssam /* 5267139Ssam * A single entry with sa_family == AF_UNSPEC and 5277139Ssam * metric ``infinity'' means ``all routes''. 5287139Ssam */ 5297139Ssam if (n->rip_dst.sa_family == AF_UNSPEC && 5307139Ssam n->rip_metric == HOPCNT_INFINITY && size == 0) { 5317212Ssam supply(from, 0); 5327139Ssam return; 5337139Ssam } 5347139Ssam rt = rtlookup(&n->rip_dst); 5357139Ssam n->rip_metric = rt == 0 ? HOPCNT_INFINITY : 5367139Ssam min(rt->rt_metric+1, HOPCNT_INFINITY); 5377139Ssam n++, newsize += sizeof (struct netinfo); 5387139Ssam } 5397139Ssam if (newsize > 0) { 5407139Ssam msg->rip_cmd = RIPCMD_RESPONSE; 5417139Ssam newsize += sizeof (int); 5427139Ssam (*afp->af_output)(s, from, newsize); 5437139Ssam } 5446920Ssam return; 5456937Ssam 5467029Ssam case RIPCMD_TRACEON: 5477139Ssam if ((*afp->af_portcheck)(from) == 0) 5487029Ssam return; 5497133Swnj if (trace) 5507133Swnj return; 5517133Swnj packet[size] = '\0'; 5527133Swnj ftrace = fopen(msg->rip_tracefile, "a"); 5537133Swnj if (ftrace == NULL) 5547133Swnj return; 5557133Swnj (void) dup2(fileno(ftrace), 1); 5567133Swnj (void) dup2(fileno(ftrace), 2); 5577133Swnj trace = 1; 5587133Swnj t = time(0); 5597133Swnj printf("*** Tracing turned on at %.24s ***\n", ctime(&t)); 5607029Ssam return; 5617029Ssam 5627133Swnj case RIPCMD_TRACEOFF: 5637133Swnj /* verify message came from a priviledged port */ 5647139Ssam if ((*afp->af_portcheck)(from) == 0) 5656937Ssam return; 5667029Ssam if (!trace) 5677029Ssam return; 5687029Ssam t = time(0); 5697029Ssam printf("*** Tracing turned off at %.24s ***\n", ctime(&t)); 5707029Ssam fflush(stdout), fflush(stderr); 5717029Ssam if (ftrace) 5727029Ssam fclose(ftrace); 5737029Ssam (void) close(1), (void) close(2); 5747029Ssam trace = 0; 5757029Ssam return; 5767133Swnj 5777133Swnj case RIPCMD_RESPONSE: 5787133Swnj /* verify message came from a router */ 5797139Ssam if ((*afp->af_portmatch)(from) == 0) 5807133Swnj return; 5817139Ssam (*afp->af_canon)(from); 5827133Swnj /* are we talking to ourselves? */ 5837133Swnj ifp = if_ifwithaddr(from); 5847133Swnj if (ifp) { 5857145Ssam rt = rtfind(from); 5867145Ssam if (rt == 0) 5877133Swnj addrouteforif(ifp); 5887145Ssam else 5897145Ssam rt->rt_timer = 0; 5907133Swnj return; 5917133Swnj } 5927133Swnj size -= 4 * sizeof (char); 5937133Swnj n = msg->rip_nets; 5947133Swnj for (; size > 0; size -= sizeof (struct netinfo), n++) { 5957133Swnj if (size < sizeof (struct netinfo)) 5967133Swnj break; 5977133Swnj if (n->rip_metric >= HOPCNT_INFINITY) 5987133Swnj continue; 5997133Swnj tprintf("dst %x hc %d...", 6007133Swnj ((struct sockaddr_in *)&n->rip_dst)->sin_addr, 6017133Swnj n->rip_metric); 6027133Swnj rt = rtlookup(&n->rip_dst); 6037133Swnj if (rt == 0) { 6047133Swnj rtadd(&n->rip_dst, from, n->rip_metric, 0); 6057133Swnj continue; 6067133Swnj } 6077133Swnj tprintf("ours: gate %x hc %d timer %d\n", 6087133Swnj ((struct sockaddr_in *)&rt->rt_router)->sin_addr, 6097133Swnj rt->rt_metric, rt->rt_timer); 6107139Ssam 6117133Swnj /* 6127250Ssam * Update if from gateway, shorter, or getting 6137139Ssam * stale and equivalent. 6147133Swnj */ 6157133Swnj if (equal(from, &rt->rt_router) || 6167133Swnj n->rip_metric < rt->rt_metric || 6177133Swnj (rt->rt_timer > (EXPIRE_TIME/2) && 6187133Swnj rt->rt_metric == n->rip_metric)) { 6197133Swnj rtchange(rt, from, n->rip_metric); 6207133Swnj rt->rt_timer = 0; 6217133Swnj } 6227133Swnj } 6237133Swnj return; 6247029Ssam } 6257145Ssam tprintf("bad packet, cmd=%x\n", msg->rip_cmd); 6267029Ssam } 6277029Ssam 6287250Ssam /* 6297250Ssam * Lookup dst in the tables for an exact match. 6307250Ssam */ 6316920Ssam struct rt_entry * 6326920Ssam rtlookup(dst) 6336920Ssam struct sockaddr *dst; 6346920Ssam { 6356920Ssam register struct rt_entry *rt; 6366934Ssam register struct rthash *rh; 6377011Ssam register int hash; 6386920Ssam struct afhash h; 6397011Ssam int doinghost = 1; 6406920Ssam 6417011Ssam if (dst->sa_family >= AF_MAX) 6427011Ssam return (0); 6437011Ssam (*afswitch[dst->sa_family].af_hash)(dst, &h); 6447011Ssam hash = h.afh_hosthash; 6457011Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 6467011Ssam again: 6477011Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 6487145Ssam if (rt->rt_hash != hash) 6497011Ssam continue; 6507011Ssam if (equal(&rt->rt_dst, dst)) 6517011Ssam return (rt); 6527011Ssam } 6537011Ssam if (doinghost) { 6547011Ssam doinghost = 0; 6557011Ssam hash = h.afh_nethash; 6567011Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 6577011Ssam goto again; 6587011Ssam } 6597011Ssam return (0); 6607011Ssam } 6617011Ssam 6627250Ssam /* 6637250Ssam * Find a route to dst as the kernel would. 6647250Ssam */ 6657011Ssam struct rt_entry * 6667145Ssam rtfind(dst) 6677011Ssam struct sockaddr *dst; 6687011Ssam { 6697011Ssam register struct rt_entry *rt; 6707011Ssam register struct rthash *rh; 6717011Ssam register int hash; 6727011Ssam struct afhash h; 6737011Ssam int af = dst->sa_family; 6747011Ssam int doinghost = 1, (*match)(); 6757011Ssam 6766920Ssam if (af >= AF_MAX) 6776920Ssam return (0); 6786920Ssam (*afswitch[af].af_hash)(dst, &h); 6796920Ssam hash = h.afh_hosthash; 6806920Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 6816920Ssam 6826920Ssam again: 6836920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 6846920Ssam if (rt->rt_hash != hash) 6856920Ssam continue; 6866920Ssam if (doinghost) { 6876920Ssam if (equal(&rt->rt_dst, dst)) 6886920Ssam return (rt); 6896920Ssam } else { 6906920Ssam if (rt->rt_dst.sa_family == af && 6916920Ssam (*match)(&rt->rt_dst, dst)) 6926920Ssam return (rt); 6936920Ssam } 6946920Ssam } 6956920Ssam if (doinghost) { 6966920Ssam doinghost = 0; 6976920Ssam hash = h.afh_nethash; 6987011Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 6996920Ssam match = afswitch[af].af_netmatch; 7006920Ssam goto again; 7016920Ssam } 7026920Ssam return (0); 7036920Ssam } 7046920Ssam 7057139Ssam rtadd(dst, gate, metric, state) 7066920Ssam struct sockaddr *dst, *gate; 7077139Ssam int metric, state; 7086920Ssam { 7096920Ssam struct afhash h; 7106920Ssam register struct rt_entry *rt; 7116934Ssam struct rthash *rh; 7126920Ssam int af = dst->sa_family, flags, hash; 7136920Ssam 7146920Ssam if (af >= AF_MAX) 7156920Ssam return; 7166920Ssam (*afswitch[af].af_hash)(dst, &h); 7176920Ssam flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0; 7186920Ssam if (flags & RTF_HOST) { 7196920Ssam hash = h.afh_hosthash; 7206920Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 7216920Ssam } else { 7226920Ssam hash = h.afh_nethash; 7236920Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 7246920Ssam } 7256920Ssam rt = (struct rt_entry *)malloc(sizeof (*rt)); 7266920Ssam if (rt == 0) 7276920Ssam return; 7286920Ssam rt->rt_hash = hash; 7296920Ssam rt->rt_dst = *dst; 7307130Swnj rt->rt_router = *gate; 7316920Ssam rt->rt_metric = metric; 7326920Ssam rt->rt_timer = 0; 7337139Ssam rt->rt_flags = RTF_UP | flags; 7347145Ssam rt->rt_state = state | RTS_CHANGED; 7357130Swnj rt->rt_ifp = if_ifwithnet(&rt->rt_router); 7367130Swnj if (metric) 7377130Swnj rt->rt_flags |= RTF_GATEWAY; 7386920Ssam insque(rt, rh); 7396929Ssam log("add", rt); 7407145Ssam if (install && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) 7417145Ssam tprintf("SIOCADDRT: %s\n", sys_errlist[errno]); 7426920Ssam } 7436920Ssam 7446920Ssam rtchange(rt, gate, metric) 7456920Ssam struct rt_entry *rt; 7466920Ssam struct sockaddr *gate; 7476920Ssam short metric; 7486920Ssam { 7497139Ssam int doioctl = 0, metricchanged = 0; 7507145Ssam struct rtentry oldroute; 7516920Ssam 7527145Ssam if (!equal(&rt->rt_router, gate)) 7537139Ssam doioctl++; 7546920Ssam if (metric != rt->rt_metric) { 7557139Ssam metricchanged++; 7566920Ssam rt->rt_metric = metric; 7576920Ssam } 7587145Ssam if (doioctl || metricchanged) { 7597145Ssam log("change", rt); 7607145Ssam rt->rt_state |= RTS_CHANGED; 7617145Ssam } 7627139Ssam if (doioctl) { 7637145Ssam oldroute = rt->rt_rt; 7647139Ssam rt->rt_router = *gate; 7657145Ssam if (install) { 7667145Ssam if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) 7677145Ssam tprintf("SIOCADDRT: %s\n", sys_errlist[errno]); 7687145Ssam if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0) 7697145Ssam tprintf("SIOCDELRT: %s\n", sys_errlist[errno]); 7707145Ssam } 7717139Ssam } 7726920Ssam } 7736920Ssam 7746920Ssam rtdelete(rt) 7756920Ssam struct rt_entry *rt; 7766920Ssam { 7776929Ssam log("delete", rt); 7787130Swnj if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt)) 7797145Ssam tprintf("SIOCDELRT: %s\n", sys_errlist[errno]); 7806920Ssam remque(rt); 7816920Ssam free((char *)rt); 7826920Ssam } 7836920Ssam 7846920Ssam log(operation, rt) 7856920Ssam char *operation; 7866920Ssam struct rt_entry *rt; 7876920Ssam { 7886920Ssam struct sockaddr_in *dst, *gate; 7896937Ssam static struct bits { 7906920Ssam int t_bits; 7916920Ssam char *t_name; 7926937Ssam } flagbits[] = { 7936920Ssam { RTF_UP, "UP" }, 7947130Swnj { RTF_GATEWAY, "GATEWAY" }, 7956920Ssam { RTF_HOST, "HOST" }, 7966920Ssam { 0 } 7976937Ssam }, statebits[] = { 7987130Swnj { RTS_PASSIVE, "PASSIVE" }, 7997145Ssam { RTS_REMOTE, "REMOTE" }, 8007145Ssam { RTS_INTERFACE,"INTERFACE" }, 8017145Ssam { RTS_CHANGED, "CHANGED" }, 8026937Ssam { 0 } 8036920Ssam }; 8046937Ssam register struct bits *p; 8056920Ssam register int first; 8066920Ssam char *cp; 8076920Ssam 8086929Ssam if (trace == 0) 8096929Ssam return; 8106920Ssam printf("%s ", operation); 8116920Ssam dst = (struct sockaddr_in *)&rt->rt_dst; 8127130Swnj gate = (struct sockaddr_in *)&rt->rt_router; 8137145Ssam printf("dst %x, router %x, metric %d, flags", dst->sin_addr, 8147145Ssam gate->sin_addr, rt->rt_metric); 8156937Ssam cp = " %s"; 8166937Ssam for (first = 1, p = flagbits; p->t_bits > 0; p++) { 8176920Ssam if ((rt->rt_flags & p->t_bits) == 0) 8186920Ssam continue; 8196920Ssam printf(cp, p->t_name); 8206920Ssam if (first) { 8216920Ssam cp = "|%s"; 8226920Ssam first = 0; 8236920Ssam } 8246920Ssam } 8256937Ssam printf(" state"); 8266937Ssam cp = " %s"; 8276937Ssam for (first = 1, p = statebits; p->t_bits > 0; p++) { 8286937Ssam if ((rt->rt_state & p->t_bits) == 0) 8296937Ssam continue; 8306937Ssam printf(cp, p->t_name); 8316937Ssam if (first) { 8326937Ssam cp = "|%s"; 8336937Ssam first = 0; 8346937Ssam } 8356937Ssam } 8366920Ssam putchar('\n'); 8376920Ssam } 8386929Ssam 8397145Ssam struct interface * 8406929Ssam if_ifwithaddr(addr) 8416929Ssam struct sockaddr *addr; 8426929Ssam { 8437145Ssam register struct interface *ifp; 8446929Ssam 8456929Ssam #define same(a1, a2) \ 8466929Ssam (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0) 8477145Ssam for (ifp = ifnet; ifp; ifp = ifp->int_next) { 8487145Ssam if (ifp->int_flags & IFF_REMOTE) 8496929Ssam continue; 8507145Ssam if (ifp->int_addr.sa_family != addr->sa_family) 8517145Ssam continue; 8527145Ssam if (same(&ifp->int_addr, addr)) 8536929Ssam break; 8547145Ssam if ((ifp->int_flags & IFF_BROADCAST) && 8557145Ssam same(&ifp->int_broadaddr, addr)) 8566929Ssam break; 8576929Ssam } 8586929Ssam return (ifp); 8596929Ssam #undef same 8606929Ssam } 8616929Ssam 8627145Ssam struct interface * 8636929Ssam if_ifwithnet(addr) 8646929Ssam register struct sockaddr *addr; 8656929Ssam { 8667145Ssam register struct interface *ifp; 8676929Ssam register int af = addr->sa_family; 8686929Ssam register int (*netmatch)(); 8696929Ssam 8706929Ssam if (af >= AF_MAX) 8716929Ssam return (0); 8726929Ssam netmatch = afswitch[af].af_netmatch; 8737145Ssam for (ifp = ifnet; ifp; ifp = ifp->int_next) { 8747145Ssam if (ifp->int_flags & IFF_REMOTE) 8756929Ssam continue; 8767145Ssam if (af != ifp->int_addr.sa_family) 8777145Ssam continue; 8787145Ssam if ((*netmatch)(addr, &ifp->int_addr)) 8796929Ssam break; 8806929Ssam } 8816929Ssam return (ifp); 8826929Ssam } 8836929Ssam 8846929Ssam struct in_addr 8856929Ssam if_makeaddr(net, host) 8866929Ssam int net, host; 8876929Ssam { 8886929Ssam u_long addr; 8896929Ssam 8906929Ssam if (net < 128) 8916929Ssam addr = (net << 24) | host; 8926929Ssam else if (net < 65536) 8936929Ssam addr = (net << 16) | host; 8946929Ssam else 8956929Ssam addr = (net << 8) | host; 896*7794Ssam #if vax || pdp11 8976929Ssam addr = htonl(addr); 8986929Ssam #endif 8996929Ssam return (*(struct in_addr *)&addr); 9006929Ssam } 901*7794Ssam 902*7794Ssam /* 903*7794Ssam * Return the network number from an internet 904*7794Ssam * address; handles class a/b/c network #'s. 905*7794Ssam */ 906*7794Ssam in_netof(in) 907*7794Ssam struct in_addr in; 908*7794Ssam { 909*7794Ssam #if vax || pdp11 910*7794Ssam register u_long net; 911*7794Ssam 912*7794Ssam if ((in.s_addr&IN_CLASSA) == 0) 913*7794Ssam return (in.s_addr & IN_CLASSA_NET); 914*7794Ssam if ((in.s_addr&IN_CLASSB) == 0) 915*7794Ssam return ((int)htons((u_short)(in.s_addr & IN_CLASSB_NET))); 916*7794Ssam net = htonl((u_long)(in.s_addr & IN_CLASSC_NET)); 917*7794Ssam net >>= 8; 918*7794Ssam return ((int)net); 919*7794Ssam #else 920*7794Ssam return (IN_NETOF(in)); 921*7794Ssam #endif 922*7794Ssam } 923*7794Ssam 924*7794Ssam /* 925*7794Ssam * Return the local network address portion of an 926*7794Ssam * internet address; handles class a/b/c network 927*7794Ssam * number formats. 928*7794Ssam */ 929*7794Ssam in_lnaof(in) 930*7794Ssam struct in_addr in; 931*7794Ssam { 932*7794Ssam #if vax || pdp11 933*7794Ssam #define IN_LNAOF(in) \ 934*7794Ssam (((in).s_addr&IN_CLASSA) == 0 ? (in).s_addr&IN_CLASSA_LNA : \ 935*7794Ssam ((in).s_addr&IN_CLASSB) == 0 ? (in).s_addr&IN_CLASSB_LNA : \ 936*7794Ssam (in).s_addr&IN_CLASSC_LNA) 937*7794Ssam return ((int)htonl((u_long)IN_LNAOF(in))); 938*7794Ssam #else 939*7794Ssam return (IN_LNAOF(in)); 940*7794Ssam #endif 941*7794Ssam } 942