16920Ssam #ifndef lint 2*7029Ssam static char sccsid[] = "@(#)routed.c 4.12 06/05/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> 17*7029Ssam #include <time.h> 186920Ssam #include "rip.h" 196920Ssam #include "router.h" 206920Ssam 216920Ssam #define LOOPBACKNET 0177 226920Ssam /* casts to keep lint happy */ 236920Ssam #define insque(q,p) _insque((caddr_t)q,(caddr_t)p) 246920Ssam #define remque(q) _remque((caddr_t)q) 256920Ssam #define equal(a1, a2) \ 266920Ssam (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0) 276929Ssam #define min(a,b) ((a)>(b)?(b):(a)) 286920Ssam 296920Ssam struct nlist nl[] = { 306920Ssam #define N_IFNET 0 316920Ssam { "_ifnet" }, 326920Ssam 0, 336920Ssam }; 346920Ssam 356920Ssam struct sockaddr_in myaddr = { AF_INET, IPPORT_ROUTESERVER }; 366920Ssam 376920Ssam int s; 386929Ssam int kmem = -1; 396920Ssam int supplier; /* process should supply updates */ 406920Ssam int initializing; /* stem off broadcast() calls */ 416962Ssam int install = 1; /* if 1 call kernel */ 426929Ssam int lookforinterfaces = 1; 436929Ssam int performnlist = 1; 446920Ssam int timeval; 456920Ssam int timer(); 466920Ssam int cleanup(); 476920Ssam int trace = 0; 48*7029Ssam FILE *ftrace; 496920Ssam 506920Ssam char packet[MAXPACKETSIZE]; 516920Ssam 526934Ssam struct in_addr if_makeaddr(); 536934Ssam struct ifnet *if_ifwithaddr(), *if_ifwithnet(); 546920Ssam extern char *malloc(); 556922Ssam extern int errno, exit(); 566920Ssam 576920Ssam main(argc, argv) 586920Ssam int argc; 596920Ssam char *argv[]; 606920Ssam { 616920Ssam int cc; 626920Ssam struct sockaddr from; 636920Ssam 64*7029Ssam #ifndef DEBUG 65*7029Ssam if (fork()) 66*7029Ssam exit(0); 67*7029Ssam for (cc = 0; cc < 10; cc++) 68*7029Ssam (void) close(cc); 69*7029Ssam (void) open("/", 0); 70*7029Ssam (void) dup2(0, 1); 71*7029Ssam (void) dup2(0, 2); 72*7029Ssam { int t = open("/dev/tty", 2); 73*7029Ssam if (t >= 0) { 74*7029Ssam ioctl(t, TIOCNOTTY, (char *)0); 75*7029Ssam (void) close(t); 76*7029Ssam } 776920Ssam } 78*7029Ssam #endif 796920Ssam if (trace) { 80*7029Ssam ftrace = fopen("/etc/routerlog", "w"); 81*7029Ssam dup2(fileno(ftrace), 1); 82*7029Ssam dup2(fileno(ftrace), 2); 836920Ssam } 846937Ssam #ifdef vax || pdp11 856920Ssam myaddr.sin_port = htons(myaddr.sin_port); 866920Ssam #endif 876920Ssam again: 886920Ssam s = socket(SOCK_DGRAM, 0, &myaddr, 0); 896920Ssam if (s < 0) { 906920Ssam perror("socket"); 916920Ssam sleep(30); 926920Ssam goto again; 936920Ssam } 946920Ssam rtinit(); 956920Ssam getothers(); 966929Ssam initializing = 1; 976920Ssam getinterfaces(); 986929Ssam initializing = 0; 996920Ssam request(); 1006920Ssam 1016920Ssam argv++, argc--; 1026920Ssam while (argc > 0) { 1036920Ssam if (strcmp(*argv, "-s") == 0) 1046929Ssam supplier = 1; 1056920Ssam else if (strcmp(*argv, "-q") == 0) 1066920Ssam supplier = 0; 1076920Ssam argv++, argc--; 1086920Ssam } 1096920Ssam sigset(SIGALRM, timer); 1106929Ssam timer(); 1116920Ssam 1126920Ssam /* 1136920Ssam * Listen for routing packets 1146920Ssam */ 1156920Ssam for (;;) { 1166920Ssam cc = receive(s, &from, packet, sizeof (packet)); 1176920Ssam if (cc <= 0) { 1186920Ssam if (cc < 0 && errno != EINTR) 1196920Ssam perror("receive"); 1206920Ssam continue; 1216920Ssam } 1226920Ssam sighold(SIGALRM); 1236920Ssam rip_input(&from, cc); 1246920Ssam sigrelse(SIGALRM); 1256920Ssam } 1266920Ssam } 1276920Ssam 1286920Ssam /* 1296920Ssam * Look in a file for any gateways we should configure 1306920Ssam * outside the directly connected ones. This is a kludge, 1316920Ssam * but until we can find out about gateways on the "other side" 1326920Ssam * of the ARPANET using GGP, it's a must. 1336920Ssam * 1346920Ssam * We don't really know the distance to the gateway, so we 1356920Ssam * assume it's a neighbor. 1366920Ssam */ 1376920Ssam getothers() 1386920Ssam { 1396920Ssam struct sockaddr_in dst, gate; 1406920Ssam FILE *fp = fopen("/etc/gateways", "r"); 1416920Ssam struct rt_entry *rt; 1426920Ssam 1436920Ssam if (fp == NULL) 1446920Ssam return; 1456920Ssam bzero((char *)&dst, sizeof (dst)); 1466920Ssam bzero((char *)&gate, sizeof (gate)); 1476920Ssam dst.sin_family = AF_INET; 1486920Ssam gate.sin_family = AF_INET; 1496920Ssam while (fscanf(fp, "%x %x", &dst.sin_addr.s_addr, 1506920Ssam &gate.sin_addr.s_addr) != EOF) { 1516920Ssam rtadd((struct sockaddr *)&dst, (struct sockaddr *)&gate, 1); 1526920Ssam rt = rtlookup((struct sockaddr *)&dst); 1536920Ssam if (rt) 1546993Ssam rt->rt_state |= RTS_HIDDEN; 1556920Ssam } 1566920Ssam fclose(fp); 1576920Ssam } 1586920Ssam 1596929Ssam /* 1606929Ssam * Timer routine: 1616929Ssam * 1626929Ssam * o handle timers on table entries, 1636929Ssam * o invalidate entries which haven't been updated in a while, 1646929Ssam * o delete entries which are too old, 1656929Ssam * o retry ioctl's which weren't successful the first 1666929Ssam * time due to the kernel entry being busy 1676929Ssam * o if we're an internetwork router, supply routing updates 1686929Ssam * periodically 1696929Ssam */ 1706929Ssam timer() 1716920Ssam { 1726934Ssam register struct rthash *rh; 1736929Ssam register struct rt_entry *rt; 1746934Ssam struct rthash *base = hosthash; 1756929Ssam int doinghost = 1; 1766920Ssam 1776929Ssam if (trace) 1786929Ssam printf(">>> time %d >>>\n", timeval); 1796929Ssam again: 1806929Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { 1816929Ssam rt = rh->rt_forw; 1826929Ssam for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 1836920Ssam 1846929Ssam /* 1856929Ssam * If the host is indicated to be 1866993Ssam * "hidden" (i.e. it's one we got 1876929Ssam * from the initialization file), 1886929Ssam * don't time out it's entry. 1896929Ssam */ 1906993Ssam if ((rt->rt_state & RTS_HIDDEN) == 0) 1916933Ssam rt->rt_timer += TIMER_RATE; 1926929Ssam log("", rt); 1936920Ssam 1946929Ssam /* 1956929Ssam * If the entry should be deleted 1966929Ssam * attempt to do so and reclaim space. 1976929Ssam */ 1986929Ssam if (rt->rt_timer >= GARBAGE_TIME || 1996937Ssam (rt->rt_state & RTS_DELRT)) { 2006929Ssam rt = rt->rt_back; 2016937Ssam rtdelete(rt->rt_forw); 2026929Ssam continue; 2036929Ssam } 2046920Ssam 2056929Ssam /* 2066929Ssam * If we haven't heard from the router 2076929Ssam * in a long time indicate the route is 2086929Ssam * hard to reach. 2096929Ssam */ 2106929Ssam if (rt->rt_timer >= EXPIRE_TIME) 2116929Ssam rt->rt_metric = HOPCNT_INFINITY; 2126937Ssam if (rt->rt_state & RTS_CHGRT) 2136934Ssam if (!ioctl(s, SIOCCHGRT,(char *)&rt->rt_rt) || 2146929Ssam --rt->rt_retry == 0) 2156937Ssam rt->rt_state &= ~RTS_CHGRT; 2166920Ssam 2176929Ssam /* 2186929Ssam * Try to add the route to the kernel tables. 2196929Ssam * If this fails because the entry already exists 2206929Ssam * (perhaps because someone manually added it) 2216929Ssam * change the add to a change. If the operation 2226929Ssam * fails otherwise (likely because the entry is 2236929Ssam * in use), retry the operation a few more times. 2246929Ssam */ 2256937Ssam if (rt->rt_state & RTS_ADDRT) { 2266934Ssam if (!ioctl(s, SIOCADDRT,(char *)&rt->rt_rt)) { 2276929Ssam if (errno == EEXIST) { 2286937Ssam rt->rt_state &= ~RTS_ADDRT; 2296937Ssam rt->rt_state |= RTS_CHGRT; 2306929Ssam rt->rt_retry = 2316929Ssam (EXPIRE_TIME/TIMER_RATE); 2326929Ssam continue; 2336929Ssam } 2346929Ssam if (--rt->rt_retry) 2356929Ssam continue; 2366929Ssam } 2376937Ssam rt->rt_state &= ~RTS_ADDRT; 2386929Ssam } 2396929Ssam } 2406929Ssam } 2416929Ssam if (doinghost) { 2426929Ssam doinghost = 0; 2436929Ssam base = nethash; 2446929Ssam goto again; 2456929Ssam } 2466929Ssam timeval += TIMER_RATE; 2476929Ssam if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0) 2486929Ssam getinterfaces(); 2496929Ssam if (supplier && (timeval % SUPPLY_INTERVAL) == 0) 2506929Ssam supplyall(); 2516929Ssam if (trace) 2526929Ssam printf("<<< time %d <<<\n", timeval); 2536929Ssam alarm(TIMER_RATE); 2546920Ssam } 2556920Ssam 2566934Ssam struct ifnet *ifnet; 2576920Ssam /* 2586920Ssam * Find the network interfaces attached to this machine. 2596929Ssam * The info is used to: 2606920Ssam * 2616920Ssam * (1) initialize the routing tables, as done by the kernel. 2626920Ssam * (2) ignore incoming packets we send. 2636920Ssam * (3) figure out broadcast capability and addresses. 2646920Ssam * (4) figure out if we're an internetwork gateway. 2656920Ssam * 2666920Ssam * We don't handle anything but Internet addresses. 2676934Ssam * 2686934Ssam * Note: this routine may be called periodically to 2696934Ssam * scan for new interfaces. In fact, the timer routine 2706934Ssam * does so based on the flag lookforinterfaces. The 2716934Ssam * flag performnlist is set whenever something odd occurs 2726934Ssam * while scanning the kernel; this is likely to occur 2736934Ssam * if /vmunix isn't up to date (e.g. someone booted /ovmunix). 2746920Ssam */ 2756920Ssam getinterfaces() 2766920Ssam { 2776929Ssam struct ifnet *ifp; 2786929Ssam struct ifnet ifstruct, *next; 2796920Ssam struct sockaddr_in net; 2806929Ssam register struct sockaddr *dst; 2816920Ssam int nets; 2826920Ssam 2836929Ssam if (performnlist) { 2846929Ssam nlist("/vmunix", nl); 2856929Ssam if (nl[N_IFNET].n_value == 0) { 2866929Ssam performnlist++; 2876929Ssam printf("ifnet: not in namelist\n"); 2886929Ssam return; 2896929Ssam } 2906929Ssam performnlist = 0; 2916920Ssam } 2926920Ssam if (kmem < 0) { 2936929Ssam kmem = open("/dev/kmem", 0); 2946929Ssam if (kmem < 0) { 2956929Ssam perror("/dev/kmem"); 2966929Ssam return; 2976929Ssam } 2986920Ssam } 2996929Ssam if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 || 3006929Ssam read(kmem, (char *)&ifp, sizeof (ifp)) != sizeof (ifp)) { 3016929Ssam performnlist = 1; 3026929Ssam return; 3036929Ssam } 3046920Ssam bzero((char *)&net, sizeof (net)); 3056920Ssam net.sin_family = AF_INET; 3066961Ssam lookforinterfaces = 0; 3076920Ssam nets = 0; 3086929Ssam while (ifp) { 3096929Ssam if (lseek(kmem, (long)ifp, 0) == -1 || 3106929Ssam read(kmem, (char *)&ifstruct, sizeof (ifstruct)) != 3116929Ssam sizeof (ifstruct)) { 3126920Ssam perror("read"); 3136961Ssam lookforinterfaces = performnlist = 1; 3146920Ssam break; 3156920Ssam } 3166929Ssam ifp = &ifstruct; 3176929Ssam if ((ifp->if_flags & IFF_UP) == 0) { 3186961Ssam lookforinterfaces = 1; 3196929Ssam skip: 3206929Ssam ifp = ifp->if_next; 3216929Ssam continue; 3226929Ssam } 3236929Ssam if (ifp->if_addr.sa_family != AF_INET) 3246929Ssam goto skip; 3256929Ssam /* ignore software loopback networks. */ 3266920Ssam if (ifp->if_net == LOOPBACKNET) 3276920Ssam goto skip; 3286929Ssam /* check if we already know about this one */ 3296929Ssam if (if_ifwithaddr(&ifstruct.if_addr)) { 3306929Ssam nets++; 3316920Ssam goto skip; 3326920Ssam } 3336929Ssam ifp = (struct ifnet *)malloc(sizeof (struct ifnet)); 3346929Ssam if (ifp == 0) { 3356929Ssam printf("routed: out of memory\n"); 3366929Ssam break; 3376929Ssam } 3386929Ssam bcopy((char *)&ifstruct, (char *)ifp, sizeof (struct ifnet)); 3396920Ssam 3406920Ssam /* 3416929Ssam * Count the # of directly connected networks 3426929Ssam * and point to point links which aren't looped 3436929Ssam * back to ourself. This is used below to 3446929Ssam * decide if we should be a routing "supplier". 3456920Ssam */ 3466929Ssam if ((ifp->if_flags & IFF_POINTOPOINT) == 0 || 3476929Ssam if_ifwithaddr(&ifp->if_dstaddr) == 0) 3486929Ssam nets++; 3496920Ssam 3506929Ssam if (ifp->if_flags & IFF_POINTOPOINT) 3516929Ssam dst = &ifp->if_dstaddr; 3526929Ssam else { 3536929Ssam net.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY); 3546929Ssam dst = (struct sockaddr *)&net; 3556929Ssam } 3566929Ssam next = ifp->if_next; 3576929Ssam ifp->if_next = ifnet; 3586929Ssam ifnet = ifp; 3596929Ssam if (rtlookup(dst) == 0) 3606929Ssam rtadd(dst, &ifp->if_addr, 0); 3616929Ssam ifp = next; 3626920Ssam } 3636920Ssam supplier = nets > 1; 3646920Ssam } 3656920Ssam 3666920Ssam /* 3676920Ssam * Send a request message to all directly 3686920Ssam * connected hosts and networks. 3696920Ssam */ 3706920Ssam request() 3716920Ssam { 3726929Ssam register struct rip *msg = (struct rip *)packet; 3736920Ssam 3746929Ssam msg->rip_cmd = RIPCMD_REQUEST; 3756929Ssam msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC; 3766929Ssam msg->rip_nets[0].rip_metric = HOPCNT_INFINITY; 3776929Ssam sendall(); 3786920Ssam } 3796920Ssam 3806920Ssam /* 3816920Ssam * Broadcast a new, or modified, routing table entry 3826920Ssam * to all directly connected hosts and networks. 3836920Ssam */ 3846920Ssam broadcast(entry) 3856920Ssam struct rt_entry *entry; 3866920Ssam { 3876929Ssam struct rip *msg = (struct rip *)packet; 3886929Ssam 3896929Ssam log("broadcast", entry); 3906929Ssam msg->rip_cmd = RIPCMD_RESPONSE; 3916929Ssam msg->rip_nets[0].rip_dst = entry->rt_dst; 3926929Ssam msg->rip_nets[0].rip_metric = min(entry->rt_metric+1, HOPCNT_INFINITY); 3936929Ssam sendall(); 3946929Ssam } 3956929Ssam 3966934Ssam /* 3976934Ssam * Send "packet" to all neighbors. 3986934Ssam */ 3996929Ssam sendall() 4006929Ssam { 4016934Ssam register struct rthash *rh; 4026920Ssam register struct rt_entry *rt; 4036920Ssam register struct sockaddr *dst; 4046934Ssam struct rthash *base = hosthash; 4056920Ssam int doinghost = 1; 4066920Ssam 4076920Ssam again: 4086920Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) 4096920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 4106993Ssam if ((rt->rt_state & RTS_HIDDEN) || rt->rt_metric > 0) 4116920Ssam continue; 4126920Ssam if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST)) 4136920Ssam dst = &rt->rt_ifp->if_broadaddr; 4146920Ssam else 4156920Ssam dst = &rt->rt_gateway; 4166920Ssam (*afswitch[dst->sa_family].af_output)(dst, sizeof (struct rip)); 4176920Ssam } 4186920Ssam if (doinghost) { 4196929Ssam base = nethash; 4206920Ssam doinghost = 0; 4216920Ssam goto again; 4226920Ssam } 4236920Ssam } 4246920Ssam 4256920Ssam /* 4266920Ssam * Supply all directly connected neighbors with the 4276920Ssam * current state of the routing tables. 4286920Ssam */ 4296920Ssam supplyall() 4306920Ssam { 4316920Ssam register struct rt_entry *rt; 4326934Ssam register struct rthash *rh; 4336920Ssam register struct sockaddr *dst; 4346934Ssam struct rthash *base = hosthash; 4356920Ssam int doinghost = 1; 4366920Ssam 4376920Ssam again: 4386920Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) 4396920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 4406993Ssam if ((rt->rt_state & RTS_HIDDEN) || rt->rt_metric > 0) 4416920Ssam continue; 4426920Ssam if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST)) 4436920Ssam dst = &rt->rt_ifp->if_broadaddr; 4446920Ssam else 4456920Ssam dst = &rt->rt_gateway; 4466929Ssam log("supply", rt); 4476920Ssam supply(dst); 4486920Ssam } 4496920Ssam if (doinghost) { 4506920Ssam base = nethash; 4516920Ssam doinghost = 0; 4526920Ssam goto again; 4536920Ssam } 4546920Ssam } 4556920Ssam 4566920Ssam /* 4576920Ssam * Supply routing information to target "sa". 4586920Ssam */ 4596920Ssam supply(sa) 4606920Ssam struct sockaddr *sa; 4616920Ssam { 4626920Ssam struct rip *msg = (struct rip *)packet; 4636920Ssam struct netinfo *n = msg->rip_nets; 4646934Ssam register struct rthash *rh; 4656920Ssam register struct rt_entry *rt; 4666934Ssam struct rthash *base = hosthash; 4676929Ssam int doinghost = 1, size; 4686920Ssam int (*output)() = afswitch[sa->sa_family].af_output; 4696920Ssam 4706920Ssam msg->rip_cmd = RIPCMD_RESPONSE; 4716920Ssam again: 4726920Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) 4736920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 4746920Ssam 4756920Ssam /* 4766920Ssam * Flush packet out if not enough room for 4776920Ssam * another routing table entry. 4786920Ssam */ 4796929Ssam size = (char *)n - packet; 4806929Ssam if (size > MAXPACKETSIZE - sizeof (struct netinfo)) { 4816929Ssam (*output)(sa, size); 4826920Ssam n = msg->rip_nets; 4836920Ssam } 4846920Ssam n->rip_dst = rt->rt_dst; 4856929Ssam n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY); 4866929Ssam n++; 4876920Ssam } 4886920Ssam if (doinghost) { 4896920Ssam doinghost = 0; 4906920Ssam base = nethash; 4916920Ssam goto again; 4926920Ssam } 4936929Ssam if (n != msg->rip_nets) 4946929Ssam (*output)(sa, (char *)n - packet); 4956920Ssam } 4966920Ssam 4976920Ssam /* 4986920Ssam * Respond to a routing info request. 4996920Ssam */ 5006920Ssam rip_respond(from, size) 5016920Ssam struct sockaddr *from; 5026920Ssam int size; 5036920Ssam { 5046920Ssam register struct rip *msg = (struct rip *)packet; 5056920Ssam struct netinfo *np = msg->rip_nets; 5066920Ssam struct rt_entry *rt; 5076920Ssam int newsize = 0; 5086920Ssam 5096929Ssam size -= 4 * sizeof (char); 5106920Ssam while (size > 0) { 5116920Ssam if (size < sizeof (struct netinfo)) 5126920Ssam break; 5136920Ssam size -= sizeof (struct netinfo); 5146920Ssam if (np->rip_dst.sa_family == AF_UNSPEC && 5156920Ssam np->rip_metric == HOPCNT_INFINITY && size == 0) { 5166920Ssam supply(from); 5176920Ssam return; 5186920Ssam } 5196920Ssam rt = rtlookup(&np->rip_dst); 5206929Ssam np->rip_metric = rt == 0 ? 5216929Ssam HOPCNT_INFINITY : min(rt->rt_metric+1, HOPCNT_INFINITY); 5226920Ssam np++, newsize += sizeof (struct netinfo); 5236920Ssam } 5246920Ssam if (newsize > 0) { 5256920Ssam msg->rip_cmd = RIPCMD_RESPONSE; 5266920Ssam newsize += sizeof (int); 5276920Ssam (*afswitch[from->sa_family].af_output)(from, newsize); 5286920Ssam } 5296920Ssam } 5306920Ssam 5316920Ssam /* 5326920Ssam * Handle an incoming routing packet. 5336920Ssam */ 5346920Ssam rip_input(from, size) 5356920Ssam struct sockaddr *from; 5366920Ssam int size; 5376920Ssam { 5386920Ssam register struct rip *msg = (struct rip *)packet; 5396920Ssam struct rt_entry *rt; 5406920Ssam struct netinfo *n; 5416920Ssam 5426937Ssam switch (msg->rip_cmd) { 5436937Ssam 5446937Ssam default: 5456920Ssam return; 5466920Ssam 5476937Ssam case RIPCMD_REQUEST: 5486920Ssam rip_respond(from, size); 5496920Ssam return; 5506937Ssam 551*7029Ssam case RIPCMD_TRACEON: 552*7029Ssam case RIPCMD_TRACEOFF: 553*7029Ssam /* verify message came from a priviledged port */ 554*7029Ssam if ((*afswitch[from->sa_family].af_portcheck)(from) == 0) 555*7029Ssam return; 556*7029Ssam tracecmd(msg->rip_cmd, msg, size); 557*7029Ssam return; 558*7029Ssam 5596937Ssam case RIPCMD_RESPONSE: 560*7029Ssam /* verify message came from a router */ 5616937Ssam if ((*afswitch[from->sa_family].af_portmatch)(from) == 0) 5626937Ssam return; 5636937Ssam break; 5646920Ssam } 5656920Ssam 5666920Ssam /* 5676920Ssam * Process updates. 5686920Ssam * Extraneous information like Internet ports 5696920Ssam * must first be purged from the sender's address for 5706920Ssam * pattern matching below. 5716920Ssam */ 5726920Ssam (*afswitch[from->sa_family].af_canon)(from); 5736920Ssam if (trace) 5746934Ssam printf("input from %x\n", 5756934Ssam ((struct sockaddr_in *)from)->sin_addr); 5766920Ssam /* 5776920Ssam * If response packet is from ourselves, use it only 5786920Ssam * to reset timer on entry. Otherwise, we'd believe 5796920Ssam * it as gospel (since it comes from the router) and 5806920Ssam * unknowingly update the metric to show the outgoing 5816920Ssam * cost (higher than our real cost). I guess the protocol 5826920Ssam * spec doesn't address this because Xerox Ethernets 5836920Ssam * don't hear their own broadcasts? 5846920Ssam */ 5856920Ssam if (if_ifwithaddr(from)) { 5867011Ssam rt = rtfind(from); 5876920Ssam if (rt) 5886920Ssam rt->rt_timer = 0; 5896920Ssam return; 5906920Ssam } 5916929Ssam size -= 4 * sizeof (char); 5926920Ssam n = msg->rip_nets; 5936920Ssam for (; size > 0; size -= sizeof (struct netinfo), n++) { 5946920Ssam if (size < sizeof (struct netinfo)) 5956920Ssam break; 5966920Ssam if (trace) 5976934Ssam printf("dst %x hc %d...", 5986934Ssam ((struct sockaddr_in *)&n->rip_dst)->sin_addr, 5996920Ssam n->rip_metric); 6006920Ssam rt = rtlookup(&n->rip_dst); 6016920Ssam 6026920Ssam /* 6036920Ssam * Unknown entry, add it to the tables only if 6046920Ssam * its interesting. 6056920Ssam */ 6066920Ssam if (rt == 0) { 6076920Ssam if (n->rip_metric < HOPCNT_INFINITY) 6086920Ssam rtadd(&n->rip_dst, from, n->rip_metric); 6096920Ssam if (trace) 6106920Ssam printf("new\n"); 6116920Ssam continue; 6126920Ssam } 6136920Ssam 6146920Ssam if (trace) 6156920Ssam printf("ours: gate %x hc %d timer %d\n", 6166934Ssam ((struct sockaddr_in *)&rt->rt_gateway)->sin_addr, 6176934Ssam rt->rt_metric, rt->rt_timer); 6186920Ssam /* 6196920Ssam * Update the entry if one of the following is true: 6206920Ssam * 6216920Ssam * (1) The update came directly from the gateway. 6226920Ssam * (2) A shorter path is provided. 6236920Ssam * (3) The entry hasn't been updated in a while 6246929Ssam * and a path of equivalent cost is offered 6256929Ssam * (with the cost finite). 6266920Ssam */ 6276920Ssam if (equal(from, &rt->rt_gateway) || 6286920Ssam rt->rt_metric > n->rip_metric || 6296920Ssam (rt->rt_timer > (EXPIRE_TIME/2) && 6306929Ssam rt->rt_metric == n->rip_metric && 6316929Ssam rt->rt_metric < HOPCNT_INFINITY)) { 6326920Ssam rtchange(rt, from, n->rip_metric); 6336920Ssam rt->rt_timer = 0; 6346920Ssam } 6356920Ssam } 6366920Ssam } 6376920Ssam 638*7029Ssam /* 639*7029Ssam * Handle tracing requests. 640*7029Ssam */ 641*7029Ssam tracecmd(cmd, msg, size) 642*7029Ssam int cmd, size; 643*7029Ssam struct rip *msg; 644*7029Ssam { 645*7029Ssam time_t t; 646*7029Ssam 647*7029Ssam if (cmd == RIPCMD_TRACEOFF) { 648*7029Ssam if (!trace) 649*7029Ssam return; 650*7029Ssam t = time(0); 651*7029Ssam printf("*** Tracing turned off at %.24s ***\n", ctime(&t)); 652*7029Ssam fflush(stdout), fflush(stderr); 653*7029Ssam if (ftrace) 654*7029Ssam fclose(ftrace); 655*7029Ssam (void) close(1), (void) close(2); 656*7029Ssam trace = 0; 657*7029Ssam return; 658*7029Ssam } 659*7029Ssam if (trace) 660*7029Ssam return; 661*7029Ssam packet[size] = '\0'; 662*7029Ssam ftrace = fopen(msg->rip_tracefile, "a"); 663*7029Ssam if (ftrace == NULL) 664*7029Ssam return; 665*7029Ssam (void) dup2(fileno(ftrace), 1); 666*7029Ssam (void) dup2(fileno(ftrace), 2); 667*7029Ssam trace = 1; 668*7029Ssam t = time(0); 669*7029Ssam printf("*** Tracing turned on at %.24s ***\n", ctime(&t)); 670*7029Ssam } 671*7029Ssam 6726934Ssam rtinit() 6736934Ssam { 6746934Ssam register struct rthash *rh; 6756934Ssam 6766934Ssam for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) 6776934Ssam rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 6786934Ssam for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) 6796934Ssam rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 6806934Ssam } 6816934Ssam 6826920Ssam /* 6836920Ssam * Lookup an entry to the appropriate dstination. 6846920Ssam */ 6856920Ssam struct rt_entry * 6866920Ssam rtlookup(dst) 6876920Ssam struct sockaddr *dst; 6886920Ssam { 6896920Ssam register struct rt_entry *rt; 6906934Ssam register struct rthash *rh; 6917011Ssam register int hash; 6926920Ssam struct afhash h; 6937011Ssam int doinghost = 1; 6946920Ssam 6957011Ssam if (dst->sa_family >= AF_MAX) 6967011Ssam return (0); 6977011Ssam (*afswitch[dst->sa_family].af_hash)(dst, &h); 6987011Ssam hash = h.afh_hosthash; 6997011Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 7007011Ssam again: 7017011Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 7027011Ssam if (rt->rt_hash != hash) 7037011Ssam continue; 7047011Ssam if (equal(&rt->rt_dst, dst)) 7057011Ssam return (rt); 7067011Ssam } 7077011Ssam if (doinghost) { 7087011Ssam doinghost = 0; 7097011Ssam hash = h.afh_nethash; 7107011Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 7117011Ssam goto again; 7127011Ssam } 7137011Ssam return (0); 7147011Ssam } 7157011Ssam 7167011Ssam /* 7177011Ssam * Find an entry based on address "dst", as the kernel 7187011Ssam * does in selecting routes. This means we look first 7197011Ssam * for a point to point link, settling for a route to 7207011Ssam * the destination network if the former fails. 7217011Ssam */ 7227011Ssam struct rt_entry * 7237011Ssam rtfind(dst) 7247011Ssam struct sockaddr *dst; 7257011Ssam { 7267011Ssam register struct rt_entry *rt; 7277011Ssam register struct rthash *rh; 7287011Ssam register int hash; 7297011Ssam struct afhash h; 7307011Ssam int af = dst->sa_family; 7317011Ssam int doinghost = 1, (*match)(); 7327011Ssam 7336920Ssam if (af >= AF_MAX) 7346920Ssam return (0); 7356920Ssam (*afswitch[af].af_hash)(dst, &h); 7366920Ssam hash = h.afh_hosthash; 7376920Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 7386920Ssam 7396920Ssam again: 7406920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 7416920Ssam if (rt->rt_hash != hash) 7426920Ssam continue; 7436920Ssam if (doinghost) { 7446920Ssam if (equal(&rt->rt_dst, dst)) 7456920Ssam return (rt); 7466920Ssam } else { 7476920Ssam if (rt->rt_dst.sa_family == af && 7486920Ssam (*match)(&rt->rt_dst, dst)) 7496920Ssam return (rt); 7506920Ssam } 7516920Ssam } 7526920Ssam if (doinghost) { 7536920Ssam doinghost = 0; 7546920Ssam hash = h.afh_nethash; 7557011Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 7566920Ssam match = afswitch[af].af_netmatch; 7576920Ssam goto again; 7586920Ssam } 7596920Ssam return (0); 7606920Ssam } 7616920Ssam 7626920Ssam /* 7636920Ssam * Add a new entry. 7646920Ssam */ 7656920Ssam rtadd(dst, gate, metric) 7666920Ssam struct sockaddr *dst, *gate; 7676920Ssam short metric; 7686920Ssam { 7696920Ssam struct afhash h; 7706920Ssam register struct rt_entry *rt; 7716934Ssam struct rthash *rh; 7726920Ssam int af = dst->sa_family, flags, hash; 7736920Ssam 7746920Ssam if (af >= AF_MAX) 7756920Ssam return; 7766920Ssam (*afswitch[af].af_hash)(dst, &h); 7776920Ssam flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0; 7786920Ssam if (flags & RTF_HOST) { 7796920Ssam hash = h.afh_hosthash; 7806920Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 7816920Ssam } else { 7826920Ssam hash = h.afh_nethash; 7836920Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 7846920Ssam } 7856920Ssam rt = (struct rt_entry *)malloc(sizeof (*rt)); 7866920Ssam if (rt == 0) 7876920Ssam return; 7886920Ssam rt->rt_hash = hash; 7896920Ssam rt->rt_dst = *dst; 7906920Ssam rt->rt_gateway = *gate; 7916920Ssam rt->rt_metric = metric; 7926920Ssam rt->rt_timer = 0; 7936920Ssam rt->rt_flags = RTF_UP | flags; 7946937Ssam rt->rt_state = 0; 7956920Ssam rt->rt_ifp = if_ifwithnet(&rt->rt_gateway); 7966920Ssam if (metric == 0) 7976920Ssam rt->rt_flags |= RTF_DIRECT; 7986920Ssam insque(rt, rh); 7996929Ssam log("add", rt); 8006920Ssam if (initializing) 8016920Ssam return; 8026920Ssam if (supplier) 8036920Ssam broadcast(rt); 8046920Ssam if (install) { 8056937Ssam rt->rt_state |= RTS_ADDRT; 8066920Ssam rt->rt_retry = EXPIRE_TIME/TIMER_RATE; 8076920Ssam } 8086920Ssam } 8096920Ssam 8106920Ssam /* 8116920Ssam * Look to see if a change to an existing entry 8126920Ssam * is warranted; if so, make it. 8136920Ssam */ 8146920Ssam rtchange(rt, gate, metric) 8156920Ssam struct rt_entry *rt; 8166920Ssam struct sockaddr *gate; 8176920Ssam short metric; 8186920Ssam { 8196920Ssam int change = 0; 8206920Ssam 8216920Ssam if (!equal(&rt->rt_gateway, gate)) { 8226920Ssam rt->rt_gateway = *gate; 8236920Ssam change++; 8246920Ssam } 8256920Ssam 8266920Ssam /* 8276920Ssam * If the hop count has changed, adjust 8286920Ssam * the flags in the routing table entry accordingly. 8296920Ssam */ 8306920Ssam if (metric != rt->rt_metric) { 8316920Ssam if (rt->rt_metric == 0) 8326920Ssam rt->rt_flags &= ~RTF_DIRECT; 8336920Ssam rt->rt_metric = metric; 8346920Ssam if (metric >= HOPCNT_INFINITY) 8356920Ssam rt->rt_flags &= ~RTF_UP; 8366920Ssam else 8376920Ssam rt->rt_flags |= RTF_UP; 8386920Ssam change++; 8396920Ssam } 8406920Ssam 8416920Ssam if (!change) 8426920Ssam return; 8436920Ssam if (supplier) 8446920Ssam broadcast(rt); 8456929Ssam log("change", rt); 8466920Ssam if (install) { 8476937Ssam rt->rt_state |= RTS_CHGRT; 8486920Ssam rt->rt_retry = EXPIRE_TIME/TIMER_RATE; 8496920Ssam } 8506920Ssam } 8516920Ssam 8526920Ssam /* 8536920Ssam * Delete a routing table entry. 8546920Ssam */ 8556920Ssam rtdelete(rt) 8566920Ssam struct rt_entry *rt; 8576920Ssam { 8586929Ssam log("delete", rt); 8596920Ssam if (install) 8606934Ssam if (ioctl(s, SIOCDELRT, (char *)&rt->rt_rt) && 8616920Ssam errno == EBUSY) 8626937Ssam rt->rt_state |= RTS_DELRT; 8636920Ssam remque(rt); 8646920Ssam free((char *)rt); 8656920Ssam } 8666920Ssam 8676920Ssam log(operation, rt) 8686920Ssam char *operation; 8696920Ssam struct rt_entry *rt; 8706920Ssam { 8716920Ssam time_t t = time(0); 8726920Ssam struct sockaddr_in *dst, *gate; 8736937Ssam static struct bits { 8746920Ssam int t_bits; 8756920Ssam char *t_name; 8766937Ssam } flagbits[] = { 8776920Ssam { RTF_UP, "UP" }, 8786920Ssam { RTF_DIRECT, "DIRECT" }, 8796920Ssam { RTF_HOST, "HOST" }, 8806920Ssam { 0 } 8816937Ssam }, statebits[] = { 8826937Ssam { RTS_DELRT, "DELETE" }, 8836937Ssam { RTS_CHGRT, "CHANGE" }, 8846993Ssam { RTS_HIDDEN, "HIDDEN" }, 8856937Ssam { 0 } 8866920Ssam }; 8876937Ssam register struct bits *p; 8886920Ssam register int first; 8896920Ssam char *cp; 8906920Ssam 8916929Ssam if (trace == 0) 8926929Ssam return; 8936920Ssam printf("%s ", operation); 8946920Ssam dst = (struct sockaddr_in *)&rt->rt_dst; 8956920Ssam gate = (struct sockaddr_in *)&rt->rt_gateway; 8966937Ssam printf("dst %x, router %x, metric %d, flags", 8976920Ssam dst->sin_addr, gate->sin_addr, rt->rt_metric); 8986937Ssam cp = " %s"; 8996937Ssam for (first = 1, p = flagbits; p->t_bits > 0; p++) { 9006920Ssam if ((rt->rt_flags & p->t_bits) == 0) 9016920Ssam continue; 9026920Ssam printf(cp, p->t_name); 9036920Ssam if (first) { 9046920Ssam cp = "|%s"; 9056920Ssam first = 0; 9066920Ssam } 9076920Ssam } 9086937Ssam printf(" state"); 9096937Ssam cp = " %s"; 9106937Ssam for (first = 1, p = statebits; p->t_bits > 0; p++) { 9116937Ssam if ((rt->rt_state & p->t_bits) == 0) 9126937Ssam continue; 9136937Ssam printf(cp, p->t_name); 9146937Ssam if (first) { 9156937Ssam cp = "|%s"; 9166937Ssam first = 0; 9176937Ssam } 9186937Ssam } 9196920Ssam putchar('\n'); 9206920Ssam } 9216929Ssam 9226934Ssam /* 9236934Ssam * Find the interface with address "addr". 9246934Ssam */ 9256929Ssam struct ifnet * 9266929Ssam if_ifwithaddr(addr) 9276929Ssam struct sockaddr *addr; 9286929Ssam { 9296929Ssam register struct ifnet *ifp; 9306929Ssam 9316929Ssam #define same(a1, a2) \ 9326929Ssam (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0) 9336929Ssam for (ifp = ifnet; ifp; ifp = ifp->if_next) { 9346929Ssam if (ifp->if_addr.sa_family != addr->sa_family) 9356929Ssam continue; 9366929Ssam if (same(&ifp->if_addr, addr)) 9376929Ssam break; 9386929Ssam if ((ifp->if_flags & IFF_BROADCAST) && 9396929Ssam same(&ifp->if_broadaddr, addr)) 9406929Ssam break; 9416929Ssam } 9426929Ssam return (ifp); 9436929Ssam #undef same 9446929Ssam } 9456929Ssam 9466934Ssam /* 9476934Ssam * Find the interface with network imbedded in 9486934Ssam * the sockaddr "addr". Must use per-af routine 9496934Ssam * look for match. 9506934Ssam */ 9516929Ssam struct ifnet * 9526929Ssam if_ifwithnet(addr) 9536929Ssam register struct sockaddr *addr; 9546929Ssam { 9556929Ssam register struct ifnet *ifp; 9566929Ssam register int af = addr->sa_family; 9576929Ssam register int (*netmatch)(); 9586929Ssam 9596929Ssam if (af >= AF_MAX) 9606929Ssam return (0); 9616929Ssam netmatch = afswitch[af].af_netmatch; 9626929Ssam for (ifp = ifnet; ifp; ifp = ifp->if_next) { 9636929Ssam if (af != ifp->if_addr.sa_family) 9646929Ssam continue; 9656929Ssam if ((*netmatch)(addr, &ifp->if_addr)) 9666929Ssam break; 9676929Ssam } 9686929Ssam return (ifp); 9696929Ssam } 9706929Ssam 9716934Ssam /* 9726934Ssam * Formulate an Internet address. 9736934Ssam */ 9746929Ssam struct in_addr 9756929Ssam if_makeaddr(net, host) 9766929Ssam int net, host; 9776929Ssam { 9786929Ssam u_long addr; 9796929Ssam 9806929Ssam if (net < 128) 9816929Ssam addr = (net << 24) | host; 9826929Ssam else if (net < 65536) 9836929Ssam addr = (net << 16) | host; 9846929Ssam else 9856929Ssam addr = (net << 8) | host; 9866929Ssam #ifdef vax 9876929Ssam addr = htonl(addr); 9886929Ssam #endif 9896929Ssam return (*(struct in_addr *)&addr); 9906929Ssam } 991