16920Ssam #ifndef lint 2*7011Ssam static char sccsid[] = "@(#)routed.c 4.11 05/31/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> 176920Ssam #include "rip.h" 186920Ssam #include "router.h" 196920Ssam 206920Ssam #define LOOPBACKNET 0177 216920Ssam /* casts to keep lint happy */ 226920Ssam #define insque(q,p) _insque((caddr_t)q,(caddr_t)p) 236920Ssam #define remque(q) _remque((caddr_t)q) 246920Ssam #define equal(a1, a2) \ 256920Ssam (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0) 266929Ssam #define min(a,b) ((a)>(b)?(b):(a)) 276920Ssam 286920Ssam struct nlist nl[] = { 296920Ssam #define N_IFNET 0 306920Ssam { "_ifnet" }, 316920Ssam 0, 326920Ssam }; 336920Ssam 346920Ssam struct sockaddr_in myaddr = { AF_INET, IPPORT_ROUTESERVER }; 356920Ssam 366920Ssam int s; 376929Ssam int kmem = -1; 386920Ssam int supplier; /* process should supply updates */ 396920Ssam int initializing; /* stem off broadcast() calls */ 406962Ssam int install = 1; /* if 1 call kernel */ 416929Ssam int lookforinterfaces = 1; 426929Ssam int performnlist = 1; 436920Ssam int timeval; 446920Ssam int timer(); 456920Ssam int cleanup(); 466920Ssam int trace = 0; 476920Ssam 486920Ssam char packet[MAXPACKETSIZE]; 496920Ssam 506934Ssam struct in_addr if_makeaddr(); 516934Ssam struct ifnet *if_ifwithaddr(), *if_ifwithnet(); 526920Ssam extern char *malloc(); 536922Ssam extern int errno, exit(); 546920Ssam 556920Ssam main(argc, argv) 566920Ssam int argc; 576920Ssam char *argv[]; 586920Ssam { 596920Ssam int cc; 606920Ssam struct sockaddr from; 616920Ssam 626920Ssam { int t = open("/dev/tty", 2); 636920Ssam if (t >= 0) { 646920Ssam ioctl(t, TIOCNOTTY, 0); 656920Ssam close(t); 666920Ssam } 676920Ssam } 686920Ssam if (trace) { 696929Ssam (void) freopen("/etc/routerlog", "a", stdout); 706929Ssam (void) dup2(fileno(stdout), 2); 716920Ssam setbuf(stdout, NULL); 726920Ssam } 736937Ssam #ifdef vax || pdp11 746920Ssam myaddr.sin_port = htons(myaddr.sin_port); 756920Ssam #endif 766920Ssam again: 776920Ssam s = socket(SOCK_DGRAM, 0, &myaddr, 0); 786920Ssam if (s < 0) { 796920Ssam perror("socket"); 806920Ssam sleep(30); 816920Ssam goto again; 826920Ssam } 836920Ssam rtinit(); 846920Ssam getothers(); 856929Ssam initializing = 1; 866920Ssam getinterfaces(); 876929Ssam initializing = 0; 886920Ssam request(); 896920Ssam 906920Ssam argv++, argc--; 916920Ssam while (argc > 0) { 926920Ssam if (strcmp(*argv, "-s") == 0) 936929Ssam supplier = 1; 946920Ssam else if (strcmp(*argv, "-q") == 0) 956920Ssam supplier = 0; 966920Ssam argv++, argc--; 976920Ssam } 986920Ssam sigset(SIGALRM, timer); 996929Ssam timer(); 1006920Ssam 1016920Ssam /* 1026920Ssam * Listen for routing packets 1036920Ssam */ 1046920Ssam for (;;) { 1056920Ssam cc = receive(s, &from, packet, sizeof (packet)); 1066920Ssam if (cc <= 0) { 1076920Ssam if (cc < 0 && errno != EINTR) 1086920Ssam perror("receive"); 1096920Ssam continue; 1106920Ssam } 1116920Ssam sighold(SIGALRM); 1126920Ssam rip_input(&from, cc); 1136920Ssam sigrelse(SIGALRM); 1146920Ssam } 1156920Ssam } 1166920Ssam 1176920Ssam /* 1186920Ssam * Look in a file for any gateways we should configure 1196920Ssam * outside the directly connected ones. This is a kludge, 1206920Ssam * but until we can find out about gateways on the "other side" 1216920Ssam * of the ARPANET using GGP, it's a must. 1226920Ssam * 1236920Ssam * We don't really know the distance to the gateway, so we 1246920Ssam * assume it's a neighbor. 1256920Ssam */ 1266920Ssam getothers() 1276920Ssam { 1286920Ssam struct sockaddr_in dst, gate; 1296920Ssam FILE *fp = fopen("/etc/gateways", "r"); 1306920Ssam struct rt_entry *rt; 1316920Ssam 1326920Ssam if (fp == NULL) 1336920Ssam return; 1346920Ssam bzero((char *)&dst, sizeof (dst)); 1356920Ssam bzero((char *)&gate, sizeof (gate)); 1366920Ssam dst.sin_family = AF_INET; 1376920Ssam gate.sin_family = AF_INET; 1386920Ssam while (fscanf(fp, "%x %x", &dst.sin_addr.s_addr, 1396920Ssam &gate.sin_addr.s_addr) != EOF) { 1406920Ssam rtadd((struct sockaddr *)&dst, (struct sockaddr *)&gate, 1); 1416920Ssam rt = rtlookup((struct sockaddr *)&dst); 1426920Ssam if (rt) 1436993Ssam rt->rt_state |= RTS_HIDDEN; 1446920Ssam } 1456920Ssam fclose(fp); 1466920Ssam } 1476920Ssam 1486929Ssam /* 1496929Ssam * Timer routine: 1506929Ssam * 1516929Ssam * o handle timers on table entries, 1526929Ssam * o invalidate entries which haven't been updated in a while, 1536929Ssam * o delete entries which are too old, 1546929Ssam * o retry ioctl's which weren't successful the first 1556929Ssam * time due to the kernel entry being busy 1566929Ssam * o if we're an internetwork router, supply routing updates 1576929Ssam * periodically 1586929Ssam */ 1596929Ssam timer() 1606920Ssam { 1616934Ssam register struct rthash *rh; 1626929Ssam register struct rt_entry *rt; 1636934Ssam struct rthash *base = hosthash; 1646929Ssam int doinghost = 1; 1656920Ssam 1666929Ssam if (trace) 1676929Ssam printf(">>> time %d >>>\n", timeval); 1686929Ssam again: 1696929Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { 1706929Ssam rt = rh->rt_forw; 1716929Ssam for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 1726920Ssam 1736929Ssam /* 1746929Ssam * If the host is indicated to be 1756993Ssam * "hidden" (i.e. it's one we got 1766929Ssam * from the initialization file), 1776929Ssam * don't time out it's entry. 1786929Ssam */ 1796993Ssam if ((rt->rt_state & RTS_HIDDEN) == 0) 1806933Ssam rt->rt_timer += TIMER_RATE; 1816929Ssam log("", rt); 1826920Ssam 1836929Ssam /* 1846929Ssam * If the entry should be deleted 1856929Ssam * attempt to do so and reclaim space. 1866929Ssam */ 1876929Ssam if (rt->rt_timer >= GARBAGE_TIME || 1886937Ssam (rt->rt_state & RTS_DELRT)) { 1896929Ssam rt = rt->rt_back; 1906937Ssam rtdelete(rt->rt_forw); 1916929Ssam continue; 1926929Ssam } 1936920Ssam 1946929Ssam /* 1956929Ssam * If we haven't heard from the router 1966929Ssam * in a long time indicate the route is 1976929Ssam * hard to reach. 1986929Ssam */ 1996929Ssam if (rt->rt_timer >= EXPIRE_TIME) 2006929Ssam rt->rt_metric = HOPCNT_INFINITY; 2016937Ssam if (rt->rt_state & RTS_CHGRT) 2026934Ssam if (!ioctl(s, SIOCCHGRT,(char *)&rt->rt_rt) || 2036929Ssam --rt->rt_retry == 0) 2046937Ssam rt->rt_state &= ~RTS_CHGRT; 2056920Ssam 2066929Ssam /* 2076929Ssam * Try to add the route to the kernel tables. 2086929Ssam * If this fails because the entry already exists 2096929Ssam * (perhaps because someone manually added it) 2106929Ssam * change the add to a change. If the operation 2116929Ssam * fails otherwise (likely because the entry is 2126929Ssam * in use), retry the operation a few more times. 2136929Ssam */ 2146937Ssam if (rt->rt_state & RTS_ADDRT) { 2156934Ssam if (!ioctl(s, SIOCADDRT,(char *)&rt->rt_rt)) { 2166929Ssam if (errno == EEXIST) { 2176937Ssam rt->rt_state &= ~RTS_ADDRT; 2186937Ssam rt->rt_state |= RTS_CHGRT; 2196929Ssam rt->rt_retry = 2206929Ssam (EXPIRE_TIME/TIMER_RATE); 2216929Ssam continue; 2226929Ssam } 2236929Ssam if (--rt->rt_retry) 2246929Ssam continue; 2256929Ssam } 2266937Ssam rt->rt_state &= ~RTS_ADDRT; 2276929Ssam } 2286929Ssam } 2296929Ssam } 2306929Ssam if (doinghost) { 2316929Ssam doinghost = 0; 2326929Ssam base = nethash; 2336929Ssam goto again; 2346929Ssam } 2356929Ssam timeval += TIMER_RATE; 2366929Ssam if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0) 2376929Ssam getinterfaces(); 2386929Ssam if (supplier && (timeval % SUPPLY_INTERVAL) == 0) 2396929Ssam supplyall(); 2406929Ssam if (trace) 2416929Ssam printf("<<< time %d <<<\n", timeval); 2426929Ssam alarm(TIMER_RATE); 2436920Ssam } 2446920Ssam 2456934Ssam struct ifnet *ifnet; 2466920Ssam /* 2476920Ssam * Find the network interfaces attached to this machine. 2486929Ssam * The info is used to: 2496920Ssam * 2506920Ssam * (1) initialize the routing tables, as done by the kernel. 2516920Ssam * (2) ignore incoming packets we send. 2526920Ssam * (3) figure out broadcast capability and addresses. 2536920Ssam * (4) figure out if we're an internetwork gateway. 2546920Ssam * 2556920Ssam * We don't handle anything but Internet addresses. 2566934Ssam * 2576934Ssam * Note: this routine may be called periodically to 2586934Ssam * scan for new interfaces. In fact, the timer routine 2596934Ssam * does so based on the flag lookforinterfaces. The 2606934Ssam * flag performnlist is set whenever something odd occurs 2616934Ssam * while scanning the kernel; this is likely to occur 2626934Ssam * if /vmunix isn't up to date (e.g. someone booted /ovmunix). 2636920Ssam */ 2646920Ssam getinterfaces() 2656920Ssam { 2666929Ssam struct ifnet *ifp; 2676929Ssam struct ifnet ifstruct, *next; 2686920Ssam struct sockaddr_in net; 2696929Ssam register struct sockaddr *dst; 2706920Ssam int nets; 2716920Ssam 2726929Ssam if (performnlist) { 2736929Ssam nlist("/vmunix", nl); 2746929Ssam if (nl[N_IFNET].n_value == 0) { 2756929Ssam performnlist++; 2766929Ssam printf("ifnet: not in namelist\n"); 2776929Ssam return; 2786929Ssam } 2796929Ssam performnlist = 0; 2806920Ssam } 2816920Ssam if (kmem < 0) { 2826929Ssam kmem = open("/dev/kmem", 0); 2836929Ssam if (kmem < 0) { 2846929Ssam perror("/dev/kmem"); 2856929Ssam return; 2866929Ssam } 2876920Ssam } 2886929Ssam if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 || 2896929Ssam read(kmem, (char *)&ifp, sizeof (ifp)) != sizeof (ifp)) { 2906929Ssam performnlist = 1; 2916929Ssam return; 2926929Ssam } 2936920Ssam bzero((char *)&net, sizeof (net)); 2946920Ssam net.sin_family = AF_INET; 2956961Ssam lookforinterfaces = 0; 2966920Ssam nets = 0; 2976929Ssam while (ifp) { 2986929Ssam if (lseek(kmem, (long)ifp, 0) == -1 || 2996929Ssam read(kmem, (char *)&ifstruct, sizeof (ifstruct)) != 3006929Ssam sizeof (ifstruct)) { 3016920Ssam perror("read"); 3026961Ssam lookforinterfaces = performnlist = 1; 3036920Ssam break; 3046920Ssam } 3056929Ssam ifp = &ifstruct; 3066929Ssam if ((ifp->if_flags & IFF_UP) == 0) { 3076961Ssam lookforinterfaces = 1; 3086929Ssam skip: 3096929Ssam ifp = ifp->if_next; 3106929Ssam continue; 3116929Ssam } 3126929Ssam if (ifp->if_addr.sa_family != AF_INET) 3136929Ssam goto skip; 3146929Ssam /* ignore software loopback networks. */ 3156920Ssam if (ifp->if_net == LOOPBACKNET) 3166920Ssam goto skip; 3176929Ssam /* check if we already know about this one */ 3186929Ssam if (if_ifwithaddr(&ifstruct.if_addr)) { 3196929Ssam nets++; 3206920Ssam goto skip; 3216920Ssam } 3226929Ssam ifp = (struct ifnet *)malloc(sizeof (struct ifnet)); 3236929Ssam if (ifp == 0) { 3246929Ssam printf("routed: out of memory\n"); 3256929Ssam break; 3266929Ssam } 3276929Ssam bcopy((char *)&ifstruct, (char *)ifp, sizeof (struct ifnet)); 3286920Ssam 3296920Ssam /* 3306929Ssam * Count the # of directly connected networks 3316929Ssam * and point to point links which aren't looped 3326929Ssam * back to ourself. This is used below to 3336929Ssam * decide if we should be a routing "supplier". 3346920Ssam */ 3356929Ssam if ((ifp->if_flags & IFF_POINTOPOINT) == 0 || 3366929Ssam if_ifwithaddr(&ifp->if_dstaddr) == 0) 3376929Ssam nets++; 3386920Ssam 3396929Ssam if (ifp->if_flags & IFF_POINTOPOINT) 3406929Ssam dst = &ifp->if_dstaddr; 3416929Ssam else { 3426929Ssam net.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY); 3436929Ssam dst = (struct sockaddr *)&net; 3446929Ssam } 3456929Ssam next = ifp->if_next; 3466929Ssam ifp->if_next = ifnet; 3476929Ssam ifnet = ifp; 3486929Ssam if (rtlookup(dst) == 0) 3496929Ssam rtadd(dst, &ifp->if_addr, 0); 3506929Ssam ifp = next; 3516920Ssam } 3526920Ssam supplier = nets > 1; 3536920Ssam } 3546920Ssam 3556920Ssam /* 3566920Ssam * Send a request message to all directly 3576920Ssam * connected hosts and networks. 3586920Ssam */ 3596920Ssam request() 3606920Ssam { 3616929Ssam register struct rip *msg = (struct rip *)packet; 3626920Ssam 3636929Ssam msg->rip_cmd = RIPCMD_REQUEST; 3646929Ssam msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC; 3656929Ssam msg->rip_nets[0].rip_metric = HOPCNT_INFINITY; 3666929Ssam sendall(); 3676920Ssam } 3686920Ssam 3696920Ssam /* 3706920Ssam * Broadcast a new, or modified, routing table entry 3716920Ssam * to all directly connected hosts and networks. 3726920Ssam */ 3736920Ssam broadcast(entry) 3746920Ssam struct rt_entry *entry; 3756920Ssam { 3766929Ssam struct rip *msg = (struct rip *)packet; 3776929Ssam 3786929Ssam log("broadcast", entry); 3796929Ssam msg->rip_cmd = RIPCMD_RESPONSE; 3806929Ssam msg->rip_nets[0].rip_dst = entry->rt_dst; 3816929Ssam msg->rip_nets[0].rip_metric = min(entry->rt_metric+1, HOPCNT_INFINITY); 3826929Ssam sendall(); 3836929Ssam } 3846929Ssam 3856934Ssam /* 3866934Ssam * Send "packet" to all neighbors. 3876934Ssam */ 3886929Ssam sendall() 3896929Ssam { 3906934Ssam register struct rthash *rh; 3916920Ssam register struct rt_entry *rt; 3926920Ssam register struct sockaddr *dst; 3936934Ssam struct rthash *base = hosthash; 3946920Ssam int doinghost = 1; 3956920Ssam 3966920Ssam again: 3976920Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) 3986920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 3996993Ssam if ((rt->rt_state & RTS_HIDDEN) || rt->rt_metric > 0) 4006920Ssam continue; 4016920Ssam if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST)) 4026920Ssam dst = &rt->rt_ifp->if_broadaddr; 4036920Ssam else 4046920Ssam dst = &rt->rt_gateway; 4056920Ssam (*afswitch[dst->sa_family].af_output)(dst, sizeof (struct rip)); 4066920Ssam } 4076920Ssam if (doinghost) { 4086929Ssam base = nethash; 4096920Ssam doinghost = 0; 4106920Ssam goto again; 4116920Ssam } 4126920Ssam } 4136920Ssam 4146920Ssam /* 4156920Ssam * Supply all directly connected neighbors with the 4166920Ssam * current state of the routing tables. 4176920Ssam */ 4186920Ssam supplyall() 4196920Ssam { 4206920Ssam register struct rt_entry *rt; 4216934Ssam register struct rthash *rh; 4226920Ssam register struct sockaddr *dst; 4236934Ssam struct rthash *base = hosthash; 4246920Ssam int doinghost = 1; 4256920Ssam 4266920Ssam again: 4276920Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) 4286920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 4296993Ssam if ((rt->rt_state & RTS_HIDDEN) || rt->rt_metric > 0) 4306920Ssam continue; 4316920Ssam if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST)) 4326920Ssam dst = &rt->rt_ifp->if_broadaddr; 4336920Ssam else 4346920Ssam dst = &rt->rt_gateway; 4356929Ssam log("supply", rt); 4366920Ssam supply(dst); 4376920Ssam } 4386920Ssam if (doinghost) { 4396920Ssam base = nethash; 4406920Ssam doinghost = 0; 4416920Ssam goto again; 4426920Ssam } 4436920Ssam } 4446920Ssam 4456920Ssam /* 4466920Ssam * Supply routing information to target "sa". 4476920Ssam */ 4486920Ssam supply(sa) 4496920Ssam struct sockaddr *sa; 4506920Ssam { 4516920Ssam struct rip *msg = (struct rip *)packet; 4526920Ssam struct netinfo *n = msg->rip_nets; 4536934Ssam register struct rthash *rh; 4546920Ssam register struct rt_entry *rt; 4556934Ssam struct rthash *base = hosthash; 4566929Ssam int doinghost = 1, size; 4576920Ssam int (*output)() = afswitch[sa->sa_family].af_output; 4586920Ssam 4596920Ssam msg->rip_cmd = RIPCMD_RESPONSE; 4606920Ssam again: 4616920Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) 4626920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 4636920Ssam 4646920Ssam /* 4656920Ssam * Flush packet out if not enough room for 4666920Ssam * another routing table entry. 4676920Ssam */ 4686929Ssam size = (char *)n - packet; 4696929Ssam if (size > MAXPACKETSIZE - sizeof (struct netinfo)) { 4706929Ssam (*output)(sa, size); 4716920Ssam n = msg->rip_nets; 4726920Ssam } 4736920Ssam n->rip_dst = rt->rt_dst; 4746929Ssam n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY); 4756929Ssam n++; 4766920Ssam } 4776920Ssam if (doinghost) { 4786920Ssam doinghost = 0; 4796920Ssam base = nethash; 4806920Ssam goto again; 4816920Ssam } 4826929Ssam if (n != msg->rip_nets) 4836929Ssam (*output)(sa, (char *)n - packet); 4846920Ssam } 4856920Ssam 4866920Ssam /* 4876920Ssam * Respond to a routing info request. 4886920Ssam */ 4896920Ssam rip_respond(from, size) 4906920Ssam struct sockaddr *from; 4916920Ssam int size; 4926920Ssam { 4936920Ssam register struct rip *msg = (struct rip *)packet; 4946920Ssam struct netinfo *np = msg->rip_nets; 4956920Ssam struct rt_entry *rt; 4966920Ssam int newsize = 0; 4976920Ssam 4986929Ssam size -= 4 * sizeof (char); 4996920Ssam while (size > 0) { 5006920Ssam if (size < sizeof (struct netinfo)) 5016920Ssam break; 5026920Ssam size -= sizeof (struct netinfo); 5036920Ssam if (np->rip_dst.sa_family == AF_UNSPEC && 5046920Ssam np->rip_metric == HOPCNT_INFINITY && size == 0) { 5056920Ssam supply(from); 5066920Ssam return; 5076920Ssam } 5086920Ssam rt = rtlookup(&np->rip_dst); 5096929Ssam np->rip_metric = rt == 0 ? 5106929Ssam HOPCNT_INFINITY : min(rt->rt_metric+1, HOPCNT_INFINITY); 5116920Ssam np++, newsize += sizeof (struct netinfo); 5126920Ssam } 5136920Ssam if (newsize > 0) { 5146920Ssam msg->rip_cmd = RIPCMD_RESPONSE; 5156920Ssam newsize += sizeof (int); 5166920Ssam (*afswitch[from->sa_family].af_output)(from, newsize); 5176920Ssam } 5186920Ssam } 5196920Ssam 5206920Ssam /* 5216920Ssam * Handle an incoming routing packet. 5226920Ssam */ 5236920Ssam rip_input(from, size) 5246920Ssam struct sockaddr *from; 5256920Ssam int size; 5266920Ssam { 5276920Ssam register struct rip *msg = (struct rip *)packet; 5286920Ssam struct rt_entry *rt; 5296920Ssam struct netinfo *n; 5306920Ssam 5316937Ssam switch (msg->rip_cmd) { 5326937Ssam 5336937Ssam default: 5346920Ssam return; 5356920Ssam 5366937Ssam case RIPCMD_REQUEST: 5376920Ssam rip_respond(from, size); 5386920Ssam return; 5396937Ssam 5406937Ssam case RIPCMD_RESPONSE: 5416937Ssam /* verify message came from a priviledged port */ 5426937Ssam if ((*afswitch[from->sa_family].af_portmatch)(from) == 0) 5436937Ssam return; 5446937Ssam break; 5456920Ssam } 5466920Ssam 5476920Ssam /* 5486920Ssam * Process updates. 5496920Ssam * Extraneous information like Internet ports 5506920Ssam * must first be purged from the sender's address for 5516920Ssam * pattern matching below. 5526920Ssam */ 5536920Ssam (*afswitch[from->sa_family].af_canon)(from); 5546920Ssam if (trace) 5556934Ssam printf("input from %x\n", 5566934Ssam ((struct sockaddr_in *)from)->sin_addr); 5576920Ssam /* 5586920Ssam * If response packet is from ourselves, use it only 5596920Ssam * to reset timer on entry. Otherwise, we'd believe 5606920Ssam * it as gospel (since it comes from the router) and 5616920Ssam * unknowingly update the metric to show the outgoing 5626920Ssam * cost (higher than our real cost). I guess the protocol 5636920Ssam * spec doesn't address this because Xerox Ethernets 5646920Ssam * don't hear their own broadcasts? 5656920Ssam */ 5666920Ssam if (if_ifwithaddr(from)) { 567*7011Ssam rt = rtfind(from); 5686920Ssam if (rt) 5696920Ssam rt->rt_timer = 0; 5706920Ssam return; 5716920Ssam } 5726929Ssam size -= 4 * sizeof (char); 5736920Ssam n = msg->rip_nets; 5746920Ssam for (; size > 0; size -= sizeof (struct netinfo), n++) { 5756920Ssam if (size < sizeof (struct netinfo)) 5766920Ssam break; 5776920Ssam if (trace) 5786934Ssam printf("dst %x hc %d...", 5796934Ssam ((struct sockaddr_in *)&n->rip_dst)->sin_addr, 5806920Ssam n->rip_metric); 5816920Ssam rt = rtlookup(&n->rip_dst); 5826920Ssam 5836920Ssam /* 5846920Ssam * Unknown entry, add it to the tables only if 5856920Ssam * its interesting. 5866920Ssam */ 5876920Ssam if (rt == 0) { 5886920Ssam if (n->rip_metric < HOPCNT_INFINITY) 5896920Ssam rtadd(&n->rip_dst, from, n->rip_metric); 5906920Ssam if (trace) 5916920Ssam printf("new\n"); 5926920Ssam continue; 5936920Ssam } 5946920Ssam 5956920Ssam if (trace) 5966920Ssam printf("ours: gate %x hc %d timer %d\n", 5976934Ssam ((struct sockaddr_in *)&rt->rt_gateway)->sin_addr, 5986934Ssam rt->rt_metric, rt->rt_timer); 5996920Ssam /* 6006920Ssam * Update the entry if one of the following is true: 6016920Ssam * 6026920Ssam * (1) The update came directly from the gateway. 6036920Ssam * (2) A shorter path is provided. 6046920Ssam * (3) The entry hasn't been updated in a while 6056929Ssam * and a path of equivalent cost is offered 6066929Ssam * (with the cost finite). 6076920Ssam */ 6086920Ssam if (equal(from, &rt->rt_gateway) || 6096920Ssam rt->rt_metric > n->rip_metric || 6106920Ssam (rt->rt_timer > (EXPIRE_TIME/2) && 6116929Ssam rt->rt_metric == n->rip_metric && 6126929Ssam rt->rt_metric < HOPCNT_INFINITY)) { 6136920Ssam rtchange(rt, from, n->rip_metric); 6146920Ssam rt->rt_timer = 0; 6156920Ssam } 6166920Ssam } 6176920Ssam } 6186920Ssam 6196934Ssam rtinit() 6206934Ssam { 6216934Ssam register struct rthash *rh; 6226934Ssam 6236934Ssam for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) 6246934Ssam rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 6256934Ssam for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) 6266934Ssam rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 6276934Ssam } 6286934Ssam 6296920Ssam /* 6306920Ssam * Lookup an entry to the appropriate dstination. 6316920Ssam */ 6326920Ssam struct rt_entry * 6336920Ssam rtlookup(dst) 6346920Ssam struct sockaddr *dst; 6356920Ssam { 6366920Ssam register struct rt_entry *rt; 6376934Ssam register struct rthash *rh; 638*7011Ssam register int hash; 6396920Ssam struct afhash h; 640*7011Ssam int doinghost = 1; 6416920Ssam 642*7011Ssam if (dst->sa_family >= AF_MAX) 643*7011Ssam return (0); 644*7011Ssam (*afswitch[dst->sa_family].af_hash)(dst, &h); 645*7011Ssam hash = h.afh_hosthash; 646*7011Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 647*7011Ssam again: 648*7011Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 649*7011Ssam if (rt->rt_hash != hash) 650*7011Ssam continue; 651*7011Ssam if (equal(&rt->rt_dst, dst)) 652*7011Ssam return (rt); 653*7011Ssam } 654*7011Ssam if (doinghost) { 655*7011Ssam doinghost = 0; 656*7011Ssam hash = h.afh_nethash; 657*7011Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 658*7011Ssam goto again; 659*7011Ssam } 660*7011Ssam return (0); 661*7011Ssam } 662*7011Ssam 663*7011Ssam /* 664*7011Ssam * Find an entry based on address "dst", as the kernel 665*7011Ssam * does in selecting routes. This means we look first 666*7011Ssam * for a point to point link, settling for a route to 667*7011Ssam * the destination network if the former fails. 668*7011Ssam */ 669*7011Ssam struct rt_entry * 670*7011Ssam rtfind(dst) 671*7011Ssam struct sockaddr *dst; 672*7011Ssam { 673*7011Ssam register struct rt_entry *rt; 674*7011Ssam register struct rthash *rh; 675*7011Ssam register int hash; 676*7011Ssam struct afhash h; 677*7011Ssam int af = dst->sa_family; 678*7011Ssam int doinghost = 1, (*match)(); 679*7011Ssam 6806920Ssam if (af >= AF_MAX) 6816920Ssam return (0); 6826920Ssam (*afswitch[af].af_hash)(dst, &h); 6836920Ssam hash = h.afh_hosthash; 6846920Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 6856920Ssam 6866920Ssam again: 6876920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 6886920Ssam if (rt->rt_hash != hash) 6896920Ssam continue; 6906920Ssam if (doinghost) { 6916920Ssam if (equal(&rt->rt_dst, dst)) 6926920Ssam return (rt); 6936920Ssam } else { 6946920Ssam if (rt->rt_dst.sa_family == af && 6956920Ssam (*match)(&rt->rt_dst, dst)) 6966920Ssam return (rt); 6976920Ssam } 6986920Ssam } 6996920Ssam if (doinghost) { 7006920Ssam doinghost = 0; 7016920Ssam hash = h.afh_nethash; 702*7011Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 7036920Ssam match = afswitch[af].af_netmatch; 7046920Ssam goto again; 7056920Ssam } 7066920Ssam return (0); 7076920Ssam } 7086920Ssam 7096920Ssam /* 7106920Ssam * Add a new entry. 7116920Ssam */ 7126920Ssam rtadd(dst, gate, metric) 7136920Ssam struct sockaddr *dst, *gate; 7146920Ssam short metric; 7156920Ssam { 7166920Ssam struct afhash h; 7176920Ssam register struct rt_entry *rt; 7186934Ssam struct rthash *rh; 7196920Ssam int af = dst->sa_family, flags, hash; 7206920Ssam 7216920Ssam if (af >= AF_MAX) 7226920Ssam return; 7236920Ssam (*afswitch[af].af_hash)(dst, &h); 7246920Ssam flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0; 7256920Ssam if (flags & RTF_HOST) { 7266920Ssam hash = h.afh_hosthash; 7276920Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 7286920Ssam } else { 7296920Ssam hash = h.afh_nethash; 7306920Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 7316920Ssam } 7326920Ssam rt = (struct rt_entry *)malloc(sizeof (*rt)); 7336920Ssam if (rt == 0) 7346920Ssam return; 7356920Ssam rt->rt_hash = hash; 7366920Ssam rt->rt_dst = *dst; 7376920Ssam rt->rt_gateway = *gate; 7386920Ssam rt->rt_metric = metric; 7396920Ssam rt->rt_timer = 0; 7406920Ssam rt->rt_flags = RTF_UP | flags; 7416937Ssam rt->rt_state = 0; 7426920Ssam rt->rt_ifp = if_ifwithnet(&rt->rt_gateway); 7436920Ssam if (metric == 0) 7446920Ssam rt->rt_flags |= RTF_DIRECT; 7456920Ssam insque(rt, rh); 7466929Ssam log("add", rt); 7476920Ssam if (initializing) 7486920Ssam return; 7496920Ssam if (supplier) 7506920Ssam broadcast(rt); 7516920Ssam if (install) { 7526937Ssam rt->rt_state |= RTS_ADDRT; 7536920Ssam rt->rt_retry = EXPIRE_TIME/TIMER_RATE; 7546920Ssam } 7556920Ssam } 7566920Ssam 7576920Ssam /* 7586920Ssam * Look to see if a change to an existing entry 7596920Ssam * is warranted; if so, make it. 7606920Ssam */ 7616920Ssam rtchange(rt, gate, metric) 7626920Ssam struct rt_entry *rt; 7636920Ssam struct sockaddr *gate; 7646920Ssam short metric; 7656920Ssam { 7666920Ssam int change = 0; 7676920Ssam 7686920Ssam if (!equal(&rt->rt_gateway, gate)) { 7696920Ssam rt->rt_gateway = *gate; 7706920Ssam change++; 7716920Ssam } 7726920Ssam 7736920Ssam /* 7746920Ssam * If the hop count has changed, adjust 7756920Ssam * the flags in the routing table entry accordingly. 7766920Ssam */ 7776920Ssam if (metric != rt->rt_metric) { 7786920Ssam if (rt->rt_metric == 0) 7796920Ssam rt->rt_flags &= ~RTF_DIRECT; 7806920Ssam rt->rt_metric = metric; 7816920Ssam if (metric >= HOPCNT_INFINITY) 7826920Ssam rt->rt_flags &= ~RTF_UP; 7836920Ssam else 7846920Ssam rt->rt_flags |= RTF_UP; 7856920Ssam change++; 7866920Ssam } 7876920Ssam 7886920Ssam if (!change) 7896920Ssam return; 7906920Ssam if (supplier) 7916920Ssam broadcast(rt); 7926929Ssam log("change", rt); 7936920Ssam if (install) { 7946937Ssam rt->rt_state |= RTS_CHGRT; 7956920Ssam rt->rt_retry = EXPIRE_TIME/TIMER_RATE; 7966920Ssam } 7976920Ssam } 7986920Ssam 7996920Ssam /* 8006920Ssam * Delete a routing table entry. 8016920Ssam */ 8026920Ssam rtdelete(rt) 8036920Ssam struct rt_entry *rt; 8046920Ssam { 8056929Ssam log("delete", rt); 8066920Ssam if (install) 8076934Ssam if (ioctl(s, SIOCDELRT, (char *)&rt->rt_rt) && 8086920Ssam errno == EBUSY) 8096937Ssam rt->rt_state |= RTS_DELRT; 8106920Ssam remque(rt); 8116920Ssam free((char *)rt); 8126920Ssam } 8136920Ssam 8146920Ssam log(operation, rt) 8156920Ssam char *operation; 8166920Ssam struct rt_entry *rt; 8176920Ssam { 8186920Ssam time_t t = time(0); 8196920Ssam struct sockaddr_in *dst, *gate; 8206937Ssam static struct bits { 8216920Ssam int t_bits; 8226920Ssam char *t_name; 8236937Ssam } flagbits[] = { 8246920Ssam { RTF_UP, "UP" }, 8256920Ssam { RTF_DIRECT, "DIRECT" }, 8266920Ssam { RTF_HOST, "HOST" }, 8276920Ssam { 0 } 8286937Ssam }, statebits[] = { 8296937Ssam { RTS_DELRT, "DELETE" }, 8306937Ssam { RTS_CHGRT, "CHANGE" }, 8316993Ssam { RTS_HIDDEN, "HIDDEN" }, 8326937Ssam { 0 } 8336920Ssam }; 8346937Ssam register struct bits *p; 8356920Ssam register int first; 8366920Ssam char *cp; 8376920Ssam 8386929Ssam if (trace == 0) 8396929Ssam return; 8406920Ssam printf("%s ", operation); 8416920Ssam dst = (struct sockaddr_in *)&rt->rt_dst; 8426920Ssam gate = (struct sockaddr_in *)&rt->rt_gateway; 8436937Ssam printf("dst %x, router %x, metric %d, flags", 8446920Ssam dst->sin_addr, gate->sin_addr, rt->rt_metric); 8456937Ssam cp = " %s"; 8466937Ssam for (first = 1, p = flagbits; p->t_bits > 0; p++) { 8476920Ssam if ((rt->rt_flags & p->t_bits) == 0) 8486920Ssam continue; 8496920Ssam printf(cp, p->t_name); 8506920Ssam if (first) { 8516920Ssam cp = "|%s"; 8526920Ssam first = 0; 8536920Ssam } 8546920Ssam } 8556937Ssam printf(" state"); 8566937Ssam cp = " %s"; 8576937Ssam for (first = 1, p = statebits; p->t_bits > 0; p++) { 8586937Ssam if ((rt->rt_state & p->t_bits) == 0) 8596937Ssam continue; 8606937Ssam printf(cp, p->t_name); 8616937Ssam if (first) { 8626937Ssam cp = "|%s"; 8636937Ssam first = 0; 8646937Ssam } 8656937Ssam } 8666920Ssam putchar('\n'); 8676920Ssam } 8686929Ssam 8696934Ssam /* 8706934Ssam * Find the interface with address "addr". 8716934Ssam */ 8726929Ssam struct ifnet * 8736929Ssam if_ifwithaddr(addr) 8746929Ssam struct sockaddr *addr; 8756929Ssam { 8766929Ssam register struct ifnet *ifp; 8776929Ssam 8786929Ssam #define same(a1, a2) \ 8796929Ssam (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0) 8806929Ssam for (ifp = ifnet; ifp; ifp = ifp->if_next) { 8816929Ssam if (ifp->if_addr.sa_family != addr->sa_family) 8826929Ssam continue; 8836929Ssam if (same(&ifp->if_addr, addr)) 8846929Ssam break; 8856929Ssam if ((ifp->if_flags & IFF_BROADCAST) && 8866929Ssam same(&ifp->if_broadaddr, addr)) 8876929Ssam break; 8886929Ssam } 8896929Ssam return (ifp); 8906929Ssam #undef same 8916929Ssam } 8926929Ssam 8936934Ssam /* 8946934Ssam * Find the interface with network imbedded in 8956934Ssam * the sockaddr "addr". Must use per-af routine 8966934Ssam * look for match. 8976934Ssam */ 8986929Ssam struct ifnet * 8996929Ssam if_ifwithnet(addr) 9006929Ssam register struct sockaddr *addr; 9016929Ssam { 9026929Ssam register struct ifnet *ifp; 9036929Ssam register int af = addr->sa_family; 9046929Ssam register int (*netmatch)(); 9056929Ssam 9066929Ssam if (af >= AF_MAX) 9076929Ssam return (0); 9086929Ssam netmatch = afswitch[af].af_netmatch; 9096929Ssam for (ifp = ifnet; ifp; ifp = ifp->if_next) { 9106929Ssam if (af != ifp->if_addr.sa_family) 9116929Ssam continue; 9126929Ssam if ((*netmatch)(addr, &ifp->if_addr)) 9136929Ssam break; 9146929Ssam } 9156929Ssam return (ifp); 9166929Ssam } 9176929Ssam 9186934Ssam /* 9196934Ssam * Formulate an Internet address. 9206934Ssam */ 9216929Ssam struct in_addr 9226929Ssam if_makeaddr(net, host) 9236929Ssam int net, host; 9246929Ssam { 9256929Ssam u_long addr; 9266929Ssam 9276929Ssam if (net < 128) 9286929Ssam addr = (net << 24) | host; 9296929Ssam else if (net < 65536) 9306929Ssam addr = (net << 16) | host; 9316929Ssam else 9326929Ssam addr = (net << 8) | host; 9336929Ssam #ifdef vax 9346929Ssam addr = htonl(addr); 9356929Ssam #endif 9366929Ssam return (*(struct in_addr *)&addr); 9376929Ssam } 938