16920Ssam #ifndef lint 2*7130Swnj static char sccsid[] = "@(#)routed.c 4.13 06/08/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> 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; 38*7130Swnj int snoroute; /* socket with no routing */ 396929Ssam int kmem = -1; 406920Ssam int supplier; /* process should supply updates */ 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; 487029Ssam 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 647029Ssam #ifndef DEBUG 657029Ssam if (fork()) 667029Ssam exit(0); 677029Ssam for (cc = 0; cc < 10; cc++) 687029Ssam (void) close(cc); 697029Ssam (void) open("/", 0); 707029Ssam (void) dup2(0, 1); 717029Ssam (void) dup2(0, 2); 727029Ssam { int t = open("/dev/tty", 2); 737029Ssam if (t >= 0) { 747029Ssam ioctl(t, TIOCNOTTY, (char *)0); 757029Ssam (void) close(t); 767029Ssam } 776920Ssam } 787029Ssam #endif 796920Ssam if (trace) { 807029Ssam ftrace = fopen("/etc/routerlog", "w"); 817029Ssam dup2(fileno(ftrace), 1); 827029Ssam 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 } 94*7130Swnj again2: 95*7130Swnj snoroute = socket(SOCK_DGRAM, 0, 0, SO_DONTROUTE); 96*7130Swnj if (snoroute < 0) { 97*7130Swnj perror("socket"); 98*7130Swnj sleep(30); 99*7130Swnj goto again2; 100*7130Swnj } 1016920Ssam rtinit(); 1026920Ssam getinterfaces(); 1036920Ssam request(); 1046920Ssam 1056920Ssam argv++, argc--; 1066920Ssam while (argc > 0) { 1076920Ssam if (strcmp(*argv, "-s") == 0) 1086929Ssam supplier = 1; 1096920Ssam else if (strcmp(*argv, "-q") == 0) 1106920Ssam supplier = 0; 1116920Ssam argv++, argc--; 1126920Ssam } 1136920Ssam sigset(SIGALRM, timer); 1146929Ssam timer(); 1156920Ssam 1166920Ssam /* 1176920Ssam * Listen for routing packets 1186920Ssam */ 1196920Ssam for (;;) { 1206920Ssam cc = receive(s, &from, packet, sizeof (packet)); 1216920Ssam if (cc <= 0) { 1226920Ssam if (cc < 0 && errno != EINTR) 1236920Ssam perror("receive"); 1246920Ssam continue; 1256920Ssam } 1266920Ssam sighold(SIGALRM); 1276920Ssam rip_input(&from, cc); 1286920Ssam sigrelse(SIGALRM); 1296920Ssam } 1306920Ssam } 1316920Ssam 1326920Ssam /* 1336929Ssam * Timer routine: 1346929Ssam * 1356929Ssam * o handle timers on table entries, 1366929Ssam * o invalidate entries which haven't been updated in a while, 1376929Ssam * o delete entries which are too old, 1386929Ssam * o if we're an internetwork router, supply routing updates 1396929Ssam * periodically 1406929Ssam */ 1416929Ssam timer() 1426920Ssam { 1436934Ssam register struct rthash *rh; 1446929Ssam register struct rt_entry *rt; 1456934Ssam struct rthash *base = hosthash; 1466929Ssam int doinghost = 1; 1476920Ssam 1486929Ssam if (trace) 1496929Ssam printf(">>> time %d >>>\n", timeval); 1506929Ssam again: 1516929Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { 1526929Ssam rt = rh->rt_forw; 1536929Ssam for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 1546920Ssam 1556929Ssam /* 1566929Ssam * If the host is indicated to be 1576993Ssam * "hidden" (i.e. it's one we got 1586929Ssam * from the initialization file), 1596929Ssam * don't time out it's entry. 1606929Ssam */ 161*7130Swnj if ((rt->rt_state & RTS_PASSIVE) == 0) 1626933Ssam rt->rt_timer += TIMER_RATE; 1636929Ssam log("", rt); 1646920Ssam 1656929Ssam /* 1666929Ssam * If the entry should be deleted 1676929Ssam * attempt to do so and reclaim space. 1686929Ssam */ 1696929Ssam if (rt->rt_timer >= GARBAGE_TIME || 1706937Ssam (rt->rt_state & RTS_DELRT)) { 1716929Ssam rt = rt->rt_back; 1726937Ssam rtdelete(rt->rt_forw); 1736929Ssam continue; 1746929Ssam } 1756920Ssam 1766929Ssam /* 1776929Ssam * If we haven't heard from the router 1786929Ssam * in a long time indicate the route is 1796929Ssam * hard to reach. 1806929Ssam */ 1816929Ssam if (rt->rt_timer >= EXPIRE_TIME) 1826929Ssam rt->rt_metric = HOPCNT_INFINITY; 1836920Ssam 1846929Ssam /* 185*7130Swnj * If a change or addition is to be made 186*7130Swnj * and this isn't time to broadcast an 187*7130Swnj * update, then broadcast the change. 1886929Ssam */ 189*7130Swnj if ((rt->rt_state & (RTS_CHGRT|RTS_ADDRT)) && 190*7130Swnj supplier && 191*7130Swnj (timeval + TIMER_RATE) % SUPPLY_INTERVAL) 192*7130Swnj broadcast(rt); 193*7130Swnj 194*7130Swnj if (rt->rt_state & RTS_CHGRT) { 195*7130Swnj struct rtentry oldroute; 196*7130Swnj 197*7130Swnj oldroute = rt->rt_rt; 198*7130Swnj rt->rt_router = rt->rt_newrouter; 199*7130Swnj if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) 200*7130Swnj perror("SIOCADDRT"); 201*7130Swnj if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0) 202*7130Swnj perror("SIOCDELRT"); 203*7130Swnj rt->rt_state &= ~RTS_CHGRT; 204*7130Swnj } 2056937Ssam if (rt->rt_state & RTS_ADDRT) { 206*7130Swnj if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) 207*7130Swnj perror("SIOCADDRT"); 2086937Ssam rt->rt_state &= ~RTS_ADDRT; 2096929Ssam } 2106929Ssam } 2116929Ssam } 2126929Ssam if (doinghost) { 2136929Ssam doinghost = 0; 2146929Ssam base = nethash; 2156929Ssam goto again; 2166929Ssam } 2176929Ssam timeval += TIMER_RATE; 2186929Ssam if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0) 2196929Ssam getinterfaces(); 2206929Ssam if (supplier && (timeval % SUPPLY_INTERVAL) == 0) 2216929Ssam supplyall(); 2226929Ssam if (trace) 2236929Ssam printf("<<< time %d <<<\n", timeval); 2246929Ssam alarm(TIMER_RATE); 2256920Ssam } 2266920Ssam 2276934Ssam struct ifnet *ifnet; 2286920Ssam /* 2296920Ssam * Find the network interfaces attached to this machine. 2306929Ssam * The info is used to: 2316920Ssam * 2326920Ssam * (1) initialize the routing tables, as done by the kernel. 2336920Ssam * (2) ignore incoming packets we send. 2346920Ssam * (3) figure out broadcast capability and addresses. 2356920Ssam * (4) figure out if we're an internetwork gateway. 2366920Ssam * 2376920Ssam * We don't handle anything but Internet addresses. 2386934Ssam * 2396934Ssam * Note: this routine may be called periodically to 2406934Ssam * scan for new interfaces. In fact, the timer routine 2416934Ssam * does so based on the flag lookforinterfaces. The 2426934Ssam * flag performnlist is set whenever something odd occurs 2436934Ssam * while scanning the kernel; this is likely to occur 2446934Ssam * if /vmunix isn't up to date (e.g. someone booted /ovmunix). 2456920Ssam */ 2466920Ssam getinterfaces() 2476920Ssam { 2486929Ssam struct ifnet *ifp; 2496929Ssam struct ifnet ifstruct, *next; 2506920Ssam struct sockaddr_in net; 2516929Ssam register struct sockaddr *dst; 2526920Ssam int nets; 2536920Ssam 2546929Ssam if (performnlist) { 2556929Ssam nlist("/vmunix", nl); 2566929Ssam if (nl[N_IFNET].n_value == 0) { 2576929Ssam performnlist++; 2586929Ssam printf("ifnet: not in namelist\n"); 2596929Ssam return; 2606929Ssam } 2616929Ssam performnlist = 0; 2626920Ssam } 2636920Ssam if (kmem < 0) { 2646929Ssam kmem = open("/dev/kmem", 0); 2656929Ssam if (kmem < 0) { 2666929Ssam perror("/dev/kmem"); 2676929Ssam return; 2686929Ssam } 2696920Ssam } 2706929Ssam if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 || 2716929Ssam read(kmem, (char *)&ifp, sizeof (ifp)) != sizeof (ifp)) { 2726929Ssam performnlist = 1; 2736929Ssam return; 2746929Ssam } 2756920Ssam bzero((char *)&net, sizeof (net)); 2766920Ssam net.sin_family = AF_INET; 2776961Ssam lookforinterfaces = 0; 2786920Ssam nets = 0; 2796929Ssam while (ifp) { 2806929Ssam if (lseek(kmem, (long)ifp, 0) == -1 || 2816929Ssam read(kmem, (char *)&ifstruct, sizeof (ifstruct)) != 2826929Ssam sizeof (ifstruct)) { 2836920Ssam perror("read"); 2846961Ssam lookforinterfaces = performnlist = 1; 2856920Ssam break; 2866920Ssam } 2876929Ssam ifp = &ifstruct; 2886929Ssam if ((ifp->if_flags & IFF_UP) == 0) { 2896961Ssam lookforinterfaces = 1; 2906929Ssam skip: 2916929Ssam ifp = ifp->if_next; 2926929Ssam continue; 2936929Ssam } 2946929Ssam if (ifp->if_addr.sa_family != AF_INET) 2956929Ssam goto skip; 2966929Ssam /* ignore software loopback networks. */ 2976920Ssam if (ifp->if_net == LOOPBACKNET) 2986920Ssam goto skip; 2996929Ssam /* check if we already know about this one */ 3006929Ssam if (if_ifwithaddr(&ifstruct.if_addr)) { 3016929Ssam nets++; 3026920Ssam goto skip; 3036920Ssam } 3046929Ssam ifp = (struct ifnet *)malloc(sizeof (struct ifnet)); 3056929Ssam if (ifp == 0) { 3066929Ssam printf("routed: out of memory\n"); 3076929Ssam break; 3086929Ssam } 3096929Ssam bcopy((char *)&ifstruct, (char *)ifp, sizeof (struct ifnet)); 3106920Ssam 3116920Ssam /* 3126929Ssam * Count the # of directly connected networks 3136929Ssam * and point to point links which aren't looped 3146929Ssam * back to ourself. This is used below to 3156929Ssam * decide if we should be a routing "supplier". 3166920Ssam */ 3176929Ssam if ((ifp->if_flags & IFF_POINTOPOINT) == 0 || 3186929Ssam if_ifwithaddr(&ifp->if_dstaddr) == 0) 3196929Ssam nets++; 3206920Ssam 3216929Ssam if (ifp->if_flags & IFF_POINTOPOINT) 3226929Ssam dst = &ifp->if_dstaddr; 3236929Ssam else { 3246929Ssam net.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY); 3256929Ssam dst = (struct sockaddr *)&net; 3266929Ssam } 3276929Ssam next = ifp->if_next; 3286929Ssam ifp->if_next = ifnet; 3296929Ssam ifnet = ifp; 330*7130Swnj if (rtlookup(dst) == 0) { 331*7130Swnj struct rt_entry *rt; 332*7130Swnj 3336929Ssam rtadd(dst, &ifp->if_addr, 0); 334*7130Swnj rt = rtlookup(dst); 335*7130Swnj if (rt) 336*7130Swnj rt->rt_state |= RTS_INTERFACE; 337*7130Swnj } 3386929Ssam ifp = next; 3396920Ssam } 3406920Ssam supplier = nets > 1; 341*7130Swnj getothers(); 3426920Ssam } 3436920Ssam 3446920Ssam /* 345*7130Swnj * Look in a file for any gateways we should configure 346*7130Swnj * outside the directly connected ones. This is a kludge, 347*7130Swnj * but until we can find out about gateways on the "other side" 348*7130Swnj * of the ARPANET using GGP, it's a must. 349*7130Swnj * 350*7130Swnj * File format is one entry per line, 351*7130Swnj * destination gateway flags (all hex #'s) 352*7130Swnj * 353*7130Swnj * We don't really know the distance to the gateway, so we 354*7130Swnj * assume it's a neighbor. 355*7130Swnj */ 356*7130Swnj getothers() 357*7130Swnj { 358*7130Swnj struct sockaddr_in dst, gate; 359*7130Swnj FILE *fp; 360*7130Swnj struct rt_entry *rt; 361*7130Swnj char flags[132]; 362*7130Swnj 363*7130Swnj fp = fopen("/etc/gateways", "r"); 364*7130Swnj if (fp == NULL) 365*7130Swnj return; 366*7130Swnj bzero((char *)&dst, sizeof (dst)); 367*7130Swnj bzero((char *)&gate, sizeof (gate)); 368*7130Swnj dst.sin_family = AF_INET; 369*7130Swnj gate.sin_family = AF_INET; 370*7130Swnj flags[0] = '\0'; 371*7130Swnj while (fscanf(fp, "dst %x gateway %x %s\n", &dst.sin_addr.s_addr, 372*7130Swnj &gate.sin_addr.s_addr, flags) != EOF) { 373*7130Swnj if (rt = rtlookup((struct sockaddr *)&dst)) { 374*7130Swnj flags[0] = '\0'; 375*7130Swnj continue; 376*7130Swnj } 377*7130Swnj rtadd((struct sockaddr *)&dst,(struct sockaddr *)&gate,1); 378*7130Swnj rt = rtlookup(&dst); 379*7130Swnj if (strcmp(flags, "passive") == 0) 380*7130Swnj rt->rt_state |= RTS_PASSIVE; 381*7130Swnj flags[0] = '\0'; 382*7130Swnj } 383*7130Swnj fclose(fp); 384*7130Swnj } 385*7130Swnj 386*7130Swnj /* 3876920Ssam * Send a request message to all directly 3886920Ssam * connected hosts and networks. 3896920Ssam */ 3906920Ssam request() 3916920Ssam { 3926929Ssam register struct rip *msg = (struct rip *)packet; 3936920Ssam 3946929Ssam msg->rip_cmd = RIPCMD_REQUEST; 3956929Ssam msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC; 3966929Ssam msg->rip_nets[0].rip_metric = HOPCNT_INFINITY; 3976929Ssam sendall(); 3986920Ssam } 3996920Ssam 4006920Ssam /* 4016920Ssam * Broadcast a new, or modified, routing table entry 4026920Ssam * to all directly connected hosts and networks. 4036920Ssam */ 4046920Ssam broadcast(entry) 4056920Ssam struct rt_entry *entry; 4066920Ssam { 4076929Ssam struct rip *msg = (struct rip *)packet; 4086929Ssam 4096929Ssam log("broadcast", entry); 4106929Ssam msg->rip_cmd = RIPCMD_RESPONSE; 4116929Ssam msg->rip_nets[0].rip_dst = entry->rt_dst; 4126929Ssam msg->rip_nets[0].rip_metric = min(entry->rt_metric+1, HOPCNT_INFINITY); 4136929Ssam sendall(); 4146929Ssam } 4156929Ssam 4166934Ssam /* 4176934Ssam * Send "packet" to all neighbors. 4186934Ssam */ 4196929Ssam sendall() 4206929Ssam { 4216934Ssam register struct rthash *rh; 4226920Ssam register struct rt_entry *rt; 4236920Ssam register struct sockaddr *dst; 4246934Ssam struct rthash *base = hosthash; 4256920Ssam int doinghost = 1; 4266920Ssam 4276920Ssam again: 4286920Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) 4296920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 430*7130Swnj if ((rt->rt_state&RTS_PASSIVE) || rt->rt_metric > 0) 4316920Ssam continue; 4326920Ssam if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST)) 4336920Ssam dst = &rt->rt_ifp->if_broadaddr; 4346920Ssam else 435*7130Swnj dst = &rt->rt_router; 436*7130Swnj (*afswitch[dst->sa_family].af_output) 437*7130Swnj (s, dst, sizeof (struct rip)); 4386920Ssam } 4396920Ssam if (doinghost) { 4406929Ssam base = nethash; 4416920Ssam doinghost = 0; 4426920Ssam goto again; 4436920Ssam } 4446920Ssam } 4456920Ssam 4466920Ssam /* 4476920Ssam * Supply all directly connected neighbors with the 4486920Ssam * current state of the routing tables. 4496920Ssam */ 4506920Ssam supplyall() 4516920Ssam { 4526920Ssam register struct rt_entry *rt; 4536934Ssam register struct rthash *rh; 4546920Ssam register struct sockaddr *dst; 4556934Ssam struct rthash *base = hosthash; 4566920Ssam int doinghost = 1; 4576920Ssam 4586920Ssam again: 4596920Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) 4606920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 461*7130Swnj if ((rt->rt_state&RTS_PASSIVE) || 462*7130Swnj (!(rt->rt_state&RTS_INTERFACE) && rt->rt_metric > 0)) 4636920Ssam continue; 4646920Ssam if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST)) 4656920Ssam dst = &rt->rt_ifp->if_broadaddr; 4666920Ssam else 467*7130Swnj dst = &rt->rt_router; 4686929Ssam log("supply", rt); 469*7130Swnj supply(rt->rt_state&RTS_INTERFACE ? snoroute : s, dst); 4706920Ssam } 4716920Ssam if (doinghost) { 4726920Ssam base = nethash; 4736920Ssam doinghost = 0; 4746920Ssam goto again; 4756920Ssam } 4766920Ssam } 4776920Ssam 4786920Ssam /* 4796920Ssam * Supply routing information to target "sa". 4806920Ssam */ 481*7130Swnj supply(s, sa) 482*7130Swnj int s; 4836920Ssam struct sockaddr *sa; 4846920Ssam { 4856920Ssam struct rip *msg = (struct rip *)packet; 4866920Ssam struct netinfo *n = msg->rip_nets; 4876934Ssam register struct rthash *rh; 4886920Ssam register struct rt_entry *rt; 4896934Ssam struct rthash *base = hosthash; 4906929Ssam int doinghost = 1, size; 4916920Ssam int (*output)() = afswitch[sa->sa_family].af_output; 4926920Ssam 4936920Ssam msg->rip_cmd = RIPCMD_RESPONSE; 4946920Ssam again: 4956920Ssam for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) 4966920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 4976920Ssam 4986920Ssam /* 4996920Ssam * Flush packet out if not enough room for 5006920Ssam * another routing table entry. 5016920Ssam */ 5026929Ssam size = (char *)n - packet; 5036929Ssam if (size > MAXPACKETSIZE - sizeof (struct netinfo)) { 504*7130Swnj (*output)(s, sa, size); 5056920Ssam n = msg->rip_nets; 5066920Ssam } 5076920Ssam n->rip_dst = rt->rt_dst; 5086929Ssam n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY); 5096929Ssam n++; 5106920Ssam } 5116920Ssam if (doinghost) { 5126920Ssam doinghost = 0; 5136920Ssam base = nethash; 5146920Ssam goto again; 5156920Ssam } 5166929Ssam if (n != msg->rip_nets) 517*7130Swnj (*output)(s, sa, (char *)n - packet); 5186920Ssam } 5196920Ssam 5206920Ssam /* 5216920Ssam * Respond to a routing info request. 5226920Ssam */ 5236920Ssam rip_respond(from, size) 5246920Ssam struct sockaddr *from; 5256920Ssam int size; 5266920Ssam { 5276920Ssam register struct rip *msg = (struct rip *)packet; 5286920Ssam struct netinfo *np = msg->rip_nets; 5296920Ssam struct rt_entry *rt; 5306920Ssam int newsize = 0; 5316920Ssam 5326929Ssam size -= 4 * sizeof (char); 5336920Ssam while (size > 0) { 5346920Ssam if (size < sizeof (struct netinfo)) 5356920Ssam break; 5366920Ssam size -= sizeof (struct netinfo); 5376920Ssam if (np->rip_dst.sa_family == AF_UNSPEC && 5386920Ssam np->rip_metric == HOPCNT_INFINITY && size == 0) { 539*7130Swnj supply(s, from); 5406920Ssam return; 5416920Ssam } 5426920Ssam rt = rtlookup(&np->rip_dst); 5436929Ssam np->rip_metric = rt == 0 ? 5446929Ssam HOPCNT_INFINITY : min(rt->rt_metric+1, HOPCNT_INFINITY); 5456920Ssam np++, newsize += sizeof (struct netinfo); 5466920Ssam } 5476920Ssam if (newsize > 0) { 5486920Ssam msg->rip_cmd = RIPCMD_RESPONSE; 5496920Ssam newsize += sizeof (int); 550*7130Swnj (*afswitch[from->sa_family].af_output)(s, from, newsize); 5516920Ssam } 5526920Ssam } 5536920Ssam 5546920Ssam /* 5556920Ssam * Handle an incoming routing packet. 5566920Ssam */ 5576920Ssam rip_input(from, size) 5586920Ssam struct sockaddr *from; 5596920Ssam int size; 5606920Ssam { 5616920Ssam register struct rip *msg = (struct rip *)packet; 5626920Ssam struct rt_entry *rt; 5636920Ssam struct netinfo *n; 5646920Ssam 5656937Ssam switch (msg->rip_cmd) { 5666937Ssam 5676937Ssam default: 5686920Ssam return; 5696920Ssam 5706937Ssam case RIPCMD_REQUEST: 5716920Ssam rip_respond(from, size); 5726920Ssam return; 5736937Ssam 5747029Ssam case RIPCMD_TRACEON: 5757029Ssam case RIPCMD_TRACEOFF: 5767029Ssam /* verify message came from a priviledged port */ 5777029Ssam if ((*afswitch[from->sa_family].af_portcheck)(from) == 0) 5787029Ssam return; 5797029Ssam tracecmd(msg->rip_cmd, msg, size); 5807029Ssam return; 5817029Ssam 5826937Ssam case RIPCMD_RESPONSE: 5837029Ssam /* verify message came from a router */ 5846937Ssam if ((*afswitch[from->sa_family].af_portmatch)(from) == 0) 5856937Ssam return; 5866937Ssam break; 5876920Ssam } 5886920Ssam 5896920Ssam /* 5906920Ssam * Process updates. 5916920Ssam * Extraneous information like Internet ports 5926920Ssam * must first be purged from the sender's address for 5936920Ssam * pattern matching below. 5946920Ssam */ 5956920Ssam (*afswitch[from->sa_family].af_canon)(from); 5966920Ssam if (trace) 5976934Ssam printf("input from %x\n", 5986934Ssam ((struct sockaddr_in *)from)->sin_addr); 5996920Ssam /* 6006920Ssam * If response packet is from ourselves, use it only 6016920Ssam * to reset timer on entry. Otherwise, we'd believe 6026920Ssam * it as gospel (since it comes from the router) and 6036920Ssam * unknowingly update the metric to show the outgoing 6046920Ssam * cost (higher than our real cost). I guess the protocol 6056920Ssam * spec doesn't address this because Xerox Ethernets 6066920Ssam * don't hear their own broadcasts? 6076920Ssam */ 6086920Ssam if (if_ifwithaddr(from)) { 6097011Ssam rt = rtfind(from); 6106920Ssam if (rt) 6116920Ssam rt->rt_timer = 0; 6126920Ssam return; 6136920Ssam } 6146929Ssam size -= 4 * sizeof (char); 6156920Ssam n = msg->rip_nets; 6166920Ssam for (; size > 0; size -= sizeof (struct netinfo), n++) { 6176920Ssam if (size < sizeof (struct netinfo)) 6186920Ssam break; 6196920Ssam if (trace) 6206934Ssam printf("dst %x hc %d...", 6216934Ssam ((struct sockaddr_in *)&n->rip_dst)->sin_addr, 6226920Ssam n->rip_metric); 6236920Ssam rt = rtlookup(&n->rip_dst); 6246920Ssam 6256920Ssam /* 6266920Ssam * Unknown entry, add it to the tables only if 6276920Ssam * its interesting. 6286920Ssam */ 6296920Ssam if (rt == 0) { 6306920Ssam if (n->rip_metric < HOPCNT_INFINITY) 6316920Ssam rtadd(&n->rip_dst, from, n->rip_metric); 6326920Ssam if (trace) 6336920Ssam printf("new\n"); 6346920Ssam continue; 6356920Ssam } 6366920Ssam 6376920Ssam if (trace) 6386920Ssam printf("ours: gate %x hc %d timer %d\n", 639*7130Swnj ((struct sockaddr_in *)&rt->rt_router)->sin_addr, 6406934Ssam rt->rt_metric, rt->rt_timer); 6416920Ssam /* 6426920Ssam * Update the entry if one of the following is true: 6436920Ssam * 6446920Ssam * (1) The update came directly from the gateway. 6456920Ssam * (2) A shorter path is provided. 6466920Ssam * (3) The entry hasn't been updated in a while 6476929Ssam * and a path of equivalent cost is offered 6486929Ssam * (with the cost finite). 6496920Ssam */ 650*7130Swnj if (equal(from, &rt->rt_router) || 6516920Ssam rt->rt_metric > n->rip_metric || 6526920Ssam (rt->rt_timer > (EXPIRE_TIME/2) && 6536929Ssam rt->rt_metric == n->rip_metric && 6546929Ssam rt->rt_metric < HOPCNT_INFINITY)) { 6556920Ssam rtchange(rt, from, n->rip_metric); 6566920Ssam rt->rt_timer = 0; 6576920Ssam } 6586920Ssam } 6596920Ssam } 6606920Ssam 6617029Ssam /* 6627029Ssam * Handle tracing requests. 6637029Ssam */ 6647029Ssam tracecmd(cmd, msg, size) 6657029Ssam int cmd, size; 6667029Ssam struct rip *msg; 6677029Ssam { 6687029Ssam time_t t; 6697029Ssam 6707029Ssam if (cmd == RIPCMD_TRACEOFF) { 6717029Ssam if (!trace) 6727029Ssam return; 6737029Ssam t = time(0); 6747029Ssam printf("*** Tracing turned off at %.24s ***\n", ctime(&t)); 6757029Ssam fflush(stdout), fflush(stderr); 6767029Ssam if (ftrace) 6777029Ssam fclose(ftrace); 6787029Ssam (void) close(1), (void) close(2); 6797029Ssam trace = 0; 6807029Ssam return; 6817029Ssam } 6827029Ssam if (trace) 6837029Ssam return; 6847029Ssam packet[size] = '\0'; 6857029Ssam ftrace = fopen(msg->rip_tracefile, "a"); 6867029Ssam if (ftrace == NULL) 6877029Ssam return; 6887029Ssam (void) dup2(fileno(ftrace), 1); 6897029Ssam (void) dup2(fileno(ftrace), 2); 6907029Ssam trace = 1; 6917029Ssam t = time(0); 6927029Ssam printf("*** Tracing turned on at %.24s ***\n", ctime(&t)); 6937029Ssam } 6947029Ssam 6956934Ssam rtinit() 6966934Ssam { 6976934Ssam register struct rthash *rh; 6986934Ssam 6996934Ssam for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) 7006934Ssam rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 7016934Ssam for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) 7026934Ssam rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 7036934Ssam } 7046934Ssam 7056920Ssam /* 7066920Ssam * Lookup an entry to the appropriate dstination. 7076920Ssam */ 7086920Ssam struct rt_entry * 7096920Ssam rtlookup(dst) 7106920Ssam struct sockaddr *dst; 7116920Ssam { 7126920Ssam register struct rt_entry *rt; 7136934Ssam register struct rthash *rh; 7147011Ssam register int hash; 7156920Ssam struct afhash h; 7167011Ssam int doinghost = 1; 7176920Ssam 7187011Ssam if (dst->sa_family >= AF_MAX) 7197011Ssam return (0); 7207011Ssam (*afswitch[dst->sa_family].af_hash)(dst, &h); 7217011Ssam hash = h.afh_hosthash; 7227011Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 7237011Ssam again: 7247011Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 7257011Ssam if (rt->rt_hash != hash) 7267011Ssam continue; 7277011Ssam if (equal(&rt->rt_dst, dst)) 7287011Ssam return (rt); 7297011Ssam } 7307011Ssam if (doinghost) { 7317011Ssam doinghost = 0; 7327011Ssam hash = h.afh_nethash; 7337011Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 7347011Ssam goto again; 7357011Ssam } 7367011Ssam return (0); 7377011Ssam } 7387011Ssam 7397011Ssam /* 7407011Ssam * Find an entry based on address "dst", as the kernel 7417011Ssam * does in selecting routes. This means we look first 7427011Ssam * for a point to point link, settling for a route to 7437011Ssam * the destination network if the former fails. 7447011Ssam */ 7457011Ssam struct rt_entry * 7467011Ssam rtfind(dst) 7477011Ssam struct sockaddr *dst; 7487011Ssam { 7497011Ssam register struct rt_entry *rt; 7507011Ssam register struct rthash *rh; 7517011Ssam register int hash; 7527011Ssam struct afhash h; 7537011Ssam int af = dst->sa_family; 7547011Ssam int doinghost = 1, (*match)(); 7557011Ssam 7566920Ssam if (af >= AF_MAX) 7576920Ssam return (0); 7586920Ssam (*afswitch[af].af_hash)(dst, &h); 7596920Ssam hash = h.afh_hosthash; 7606920Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 7616920Ssam 7626920Ssam again: 7636920Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 7646920Ssam if (rt->rt_hash != hash) 7656920Ssam continue; 7666920Ssam if (doinghost) { 7676920Ssam if (equal(&rt->rt_dst, dst)) 7686920Ssam return (rt); 7696920Ssam } else { 7706920Ssam if (rt->rt_dst.sa_family == af && 7716920Ssam (*match)(&rt->rt_dst, dst)) 7726920Ssam return (rt); 7736920Ssam } 7746920Ssam } 7756920Ssam if (doinghost) { 7766920Ssam doinghost = 0; 7776920Ssam hash = h.afh_nethash; 7787011Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 7796920Ssam match = afswitch[af].af_netmatch; 7806920Ssam goto again; 7816920Ssam } 7826920Ssam return (0); 7836920Ssam } 7846920Ssam 7856920Ssam /* 7866920Ssam * Add a new entry. 7876920Ssam */ 7886920Ssam rtadd(dst, gate, metric) 7896920Ssam struct sockaddr *dst, *gate; 7906920Ssam short metric; 7916920Ssam { 7926920Ssam struct afhash h; 7936920Ssam register struct rt_entry *rt; 7946934Ssam struct rthash *rh; 7956920Ssam int af = dst->sa_family, flags, hash; 7966920Ssam 7976920Ssam if (af >= AF_MAX) 7986920Ssam return; 7996920Ssam (*afswitch[af].af_hash)(dst, &h); 8006920Ssam flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0; 8016920Ssam if (flags & RTF_HOST) { 8026920Ssam hash = h.afh_hosthash; 8036920Ssam rh = &hosthash[hash % ROUTEHASHSIZ]; 8046920Ssam } else { 8056920Ssam hash = h.afh_nethash; 8066920Ssam rh = &nethash[hash % ROUTEHASHSIZ]; 8076920Ssam } 8086920Ssam rt = (struct rt_entry *)malloc(sizeof (*rt)); 8096920Ssam if (rt == 0) 8106920Ssam return; 8116920Ssam rt->rt_hash = hash; 8126920Ssam rt->rt_dst = *dst; 813*7130Swnj rt->rt_router = *gate; 8146920Ssam rt->rt_metric = metric; 8156920Ssam rt->rt_timer = 0; 8166920Ssam rt->rt_flags = RTF_UP | flags; 8176937Ssam rt->rt_state = 0; 818*7130Swnj rt->rt_ifp = if_ifwithnet(&rt->rt_router); 819*7130Swnj if (metric) 820*7130Swnj rt->rt_flags |= RTF_GATEWAY; 8216920Ssam insque(rt, rh); 8226929Ssam log("add", rt); 823*7130Swnj if (install) 8246937Ssam rt->rt_state |= RTS_ADDRT; 8256920Ssam } 8266920Ssam 8276920Ssam /* 8286920Ssam * Look to see if a change to an existing entry 8296920Ssam * is warranted; if so, make it. 8306920Ssam */ 8316920Ssam rtchange(rt, gate, metric) 8326920Ssam struct rt_entry *rt; 8336920Ssam struct sockaddr *gate; 8346920Ssam short metric; 8356920Ssam { 8366920Ssam int change = 0; 8376920Ssam 838*7130Swnj if (!equal(&rt->rt_router, gate)) { 839*7130Swnj rt->rt_newrouter = *gate; 8406920Ssam change++; 8416920Ssam } 8426920Ssam if (metric != rt->rt_metric) { 843*7130Swnj if (metric == 0) 844*7130Swnj rt->rt_flags |= RTF_GATEWAY; 8456920Ssam rt->rt_metric = metric; 8466920Ssam change++; 8476920Ssam } 8486920Ssam if (!change) 8496920Ssam return; 8506929Ssam log("change", rt); 851*7130Swnj if (install) 8526937Ssam rt->rt_state |= RTS_CHGRT; 8536920Ssam } 8546920Ssam 8556920Ssam /* 8566920Ssam * Delete a routing table entry. 8576920Ssam */ 8586920Ssam rtdelete(rt) 8596920Ssam struct rt_entry *rt; 8606920Ssam { 8616929Ssam log("delete", rt); 862*7130Swnj if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt)) 863*7130Swnj perror("SIOCDELRT"); 864*7130Swnj /* don't delete interface entries so we can poll them later */ 865*7130Swnj if (rt->rt_state & RTS_INTERFACE) 866*7130Swnj return; 8676920Ssam remque(rt); 8686920Ssam free((char *)rt); 8696920Ssam } 8706920Ssam 8716920Ssam log(operation, rt) 8726920Ssam char *operation; 8736920Ssam struct rt_entry *rt; 8746920Ssam { 8756920Ssam time_t t = time(0); 8766920Ssam struct sockaddr_in *dst, *gate; 8776937Ssam static struct bits { 8786920Ssam int t_bits; 8796920Ssam char *t_name; 8806937Ssam } flagbits[] = { 8816920Ssam { RTF_UP, "UP" }, 882*7130Swnj { RTF_GATEWAY, "GATEWAY" }, 8836920Ssam { RTF_HOST, "HOST" }, 8846920Ssam { 0 } 8856937Ssam }, statebits[] = { 8866937Ssam { RTS_DELRT, "DELETE" }, 8876937Ssam { RTS_CHGRT, "CHANGE" }, 888*7130Swnj { RTS_PASSIVE, "PASSIVE" }, 889*7130Swnj { RTS_INTERFACE,"INTERFACE" }, 8906937Ssam { 0 } 8916920Ssam }; 8926937Ssam register struct bits *p; 8936920Ssam register int first; 8946920Ssam char *cp; 8956920Ssam 8966929Ssam if (trace == 0) 8976929Ssam return; 8986920Ssam printf("%s ", operation); 8996920Ssam dst = (struct sockaddr_in *)&rt->rt_dst; 900*7130Swnj gate = (struct sockaddr_in *)&rt->rt_router; 9016937Ssam printf("dst %x, router %x, metric %d, flags", 9026920Ssam dst->sin_addr, gate->sin_addr, rt->rt_metric); 9036937Ssam cp = " %s"; 9046937Ssam for (first = 1, p = flagbits; p->t_bits > 0; p++) { 9056920Ssam if ((rt->rt_flags & p->t_bits) == 0) 9066920Ssam continue; 9076920Ssam printf(cp, p->t_name); 9086920Ssam if (first) { 9096920Ssam cp = "|%s"; 9106920Ssam first = 0; 9116920Ssam } 9126920Ssam } 9136937Ssam printf(" state"); 9146937Ssam cp = " %s"; 9156937Ssam for (first = 1, p = statebits; p->t_bits > 0; p++) { 9166937Ssam if ((rt->rt_state & p->t_bits) == 0) 9176937Ssam continue; 9186937Ssam printf(cp, p->t_name); 9196937Ssam if (first) { 9206937Ssam cp = "|%s"; 9216937Ssam first = 0; 9226937Ssam } 9236937Ssam } 9246920Ssam putchar('\n'); 9256920Ssam } 9266929Ssam 9276934Ssam /* 9286934Ssam * Find the interface with address "addr". 9296934Ssam */ 9306929Ssam struct ifnet * 9316929Ssam if_ifwithaddr(addr) 9326929Ssam struct sockaddr *addr; 9336929Ssam { 9346929Ssam register struct ifnet *ifp; 9356929Ssam 9366929Ssam #define same(a1, a2) \ 9376929Ssam (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0) 9386929Ssam for (ifp = ifnet; ifp; ifp = ifp->if_next) { 9396929Ssam if (ifp->if_addr.sa_family != addr->sa_family) 9406929Ssam continue; 9416929Ssam if (same(&ifp->if_addr, addr)) 9426929Ssam break; 9436929Ssam if ((ifp->if_flags & IFF_BROADCAST) && 9446929Ssam same(&ifp->if_broadaddr, addr)) 9456929Ssam break; 9466929Ssam } 9476929Ssam return (ifp); 9486929Ssam #undef same 9496929Ssam } 9506929Ssam 9516934Ssam /* 9526934Ssam * Find the interface with network imbedded in 9536934Ssam * the sockaddr "addr". Must use per-af routine 9546934Ssam * look for match. 9556934Ssam */ 9566929Ssam struct ifnet * 9576929Ssam if_ifwithnet(addr) 9586929Ssam register struct sockaddr *addr; 9596929Ssam { 9606929Ssam register struct ifnet *ifp; 9616929Ssam register int af = addr->sa_family; 9626929Ssam register int (*netmatch)(); 9636929Ssam 9646929Ssam if (af >= AF_MAX) 9656929Ssam return (0); 9666929Ssam netmatch = afswitch[af].af_netmatch; 9676929Ssam for (ifp = ifnet; ifp; ifp = ifp->if_next) { 9686929Ssam if (af != ifp->if_addr.sa_family) 9696929Ssam continue; 9706929Ssam if ((*netmatch)(addr, &ifp->if_addr)) 9716929Ssam break; 9726929Ssam } 9736929Ssam return (ifp); 9746929Ssam } 9756929Ssam 9766934Ssam /* 9776934Ssam * Formulate an Internet address. 9786934Ssam */ 9796929Ssam struct in_addr 9806929Ssam if_makeaddr(net, host) 9816929Ssam int net, host; 9826929Ssam { 9836929Ssam u_long addr; 9846929Ssam 9856929Ssam if (net < 128) 9866929Ssam addr = (net << 24) | host; 9876929Ssam else if (net < 65536) 9886929Ssam addr = (net << 16) | host; 9896929Ssam else 9906929Ssam addr = (net << 8) | host; 9916929Ssam #ifdef vax 9926929Ssam addr = htonl(addr); 9936929Ssam #endif 9946929Ssam return (*(struct in_addr *)&addr); 9956929Ssam } 996