121999Sdist /* 234569Skarels * Copyright (c) 1983, 1988 Regents of the University of California. 333489Sbostic * All rights reserved. 433489Sbostic * 542712Sbostic * %sccs.include.redist.c% 621999Sdist */ 721999Sdist 89020Ssam #ifndef lint 9*55907Smckusick static char sccsid[] = "@(#)tables.c 5.20 (Berkeley) 08/14/92"; 1033489Sbostic #endif /* not lint */ 119020Ssam 129020Ssam /* 139020Ssam * Routing Table Management Daemon 149020Ssam */ 1510245Ssam #include "defs.h" 169020Ssam #include <sys/ioctl.h> 179020Ssam #include <errno.h> 1834569Skarels #include <sys/syslog.h> 199020Ssam 209020Ssam #ifndef DEBUG 219020Ssam #define DEBUG 0 229020Ssam #endif 239020Ssam 2438684Ssklower #ifdef RTM_ADD 2538684Ssklower #define FIXLEN(s) {if ((s)->sa_len == 0) (s)->sa_len = sizeof *(s);} 2638684Ssklower #else 2738684Ssklower #define FIXLEN(s) { } 2838684Ssklower #endif 2938684Ssklower 309020Ssam int install = !DEBUG; /* if 1 call kernel */ 319020Ssam 329020Ssam /* 339020Ssam * Lookup dst in the tables for an exact match. 349020Ssam */ 359020Ssam struct rt_entry * 369020Ssam rtlookup(dst) 379020Ssam struct sockaddr *dst; 389020Ssam { 399020Ssam register struct rt_entry *rt; 409020Ssam register struct rthash *rh; 4116128Skarels register u_int hash; 429020Ssam struct afhash h; 439020Ssam int doinghost = 1; 449020Ssam 4526342Skarels if (dst->sa_family >= af_max) 469020Ssam return (0); 479020Ssam (*afswitch[dst->sa_family].af_hash)(dst, &h); 489020Ssam hash = h.afh_hosthash; 4916435Skarels rh = &hosthash[hash & ROUTEHASHMASK]; 509020Ssam again: 519020Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 529020Ssam if (rt->rt_hash != hash) 539020Ssam continue; 549020Ssam if (equal(&rt->rt_dst, dst)) 559020Ssam return (rt); 569020Ssam } 579020Ssam if (doinghost) { 589020Ssam doinghost = 0; 599020Ssam hash = h.afh_nethash; 6016435Skarels rh = &nethash[hash & ROUTEHASHMASK]; 619020Ssam goto again; 629020Ssam } 639020Ssam return (0); 649020Ssam } 659020Ssam 6634569Skarels struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ 6734569Skarels 689020Ssam /* 699020Ssam * Find a route to dst as the kernel would. 709020Ssam */ 719020Ssam struct rt_entry * 729020Ssam rtfind(dst) 739020Ssam struct sockaddr *dst; 749020Ssam { 759020Ssam register struct rt_entry *rt; 769020Ssam register struct rthash *rh; 7716128Skarels register u_int hash; 789020Ssam struct afhash h; 799020Ssam int af = dst->sa_family; 809020Ssam int doinghost = 1, (*match)(); 819020Ssam 8226342Skarels if (af >= af_max) 839020Ssam return (0); 849020Ssam (*afswitch[af].af_hash)(dst, &h); 859020Ssam hash = h.afh_hosthash; 8616435Skarels rh = &hosthash[hash & ROUTEHASHMASK]; 879020Ssam 889020Ssam again: 899020Ssam for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 909020Ssam if (rt->rt_hash != hash) 919020Ssam continue; 929020Ssam if (doinghost) { 939020Ssam if (equal(&rt->rt_dst, dst)) 949020Ssam return (rt); 959020Ssam } else { 969020Ssam if (rt->rt_dst.sa_family == af && 979020Ssam (*match)(&rt->rt_dst, dst)) 989020Ssam return (rt); 999020Ssam } 1009020Ssam } 1019020Ssam if (doinghost) { 1029020Ssam doinghost = 0; 1039020Ssam hash = h.afh_nethash; 10416435Skarels rh = &nethash[hash & ROUTEHASHMASK]; 1059020Ssam match = afswitch[af].af_netmatch; 1069020Ssam goto again; 1079020Ssam } 10834569Skarels #ifdef notyet 10934569Skarels /* 11034569Skarels * Check for wildcard gateway, by convention network 0. 11134569Skarels */ 11234569Skarels if (dst != &wildcard) { 11334569Skarels dst = &wildcard, hash = 0; 11434569Skarels goto again; 11534569Skarels } 11634569Skarels #endif 1179020Ssam return (0); 1189020Ssam } 1199020Ssam 1209020Ssam rtadd(dst, gate, metric, state) 1219020Ssam struct sockaddr *dst, *gate; 1229020Ssam int metric, state; 1239020Ssam { 1249020Ssam struct afhash h; 1259020Ssam register struct rt_entry *rt; 1269020Ssam struct rthash *rh; 12731220Skarels int af = dst->sa_family, flags; 12816128Skarels u_int hash; 1299020Ssam 13026342Skarels if (af >= af_max) 1319020Ssam return; 1329020Ssam (*afswitch[af].af_hash)(dst, &h); 13327232Skarels flags = (*afswitch[af].af_rtflags)(dst); 13427232Skarels /* 13527232Skarels * Subnet flag isn't visible to kernel, move to state. XXX 13627232Skarels */ 13738684Ssklower FIXLEN(dst); 13838684Ssklower FIXLEN(gate); 13927232Skarels if (flags & RTF_SUBNET) { 14027232Skarels state |= RTS_SUBNET; 14127232Skarels flags &= ~RTF_SUBNET; 14227232Skarels } 1439020Ssam if (flags & RTF_HOST) { 1449020Ssam hash = h.afh_hosthash; 14516435Skarels rh = &hosthash[hash & ROUTEHASHMASK]; 1469020Ssam } else { 1479020Ssam hash = h.afh_nethash; 14816435Skarels rh = &nethash[hash & ROUTEHASHMASK]; 1499020Ssam } 1509020Ssam rt = (struct rt_entry *)malloc(sizeof (*rt)); 1519020Ssam if (rt == 0) 1529020Ssam return; 1539020Ssam rt->rt_hash = hash; 1549020Ssam rt->rt_dst = *dst; 1559020Ssam rt->rt_router = *gate; 1569020Ssam rt->rt_timer = 0; 1579020Ssam rt->rt_flags = RTF_UP | flags; 1589020Ssam rt->rt_state = state | RTS_CHANGED; 15938684Ssklower rt->rt_ifp = if_ifwithdstaddr(&rt->rt_dst); 16027232Skarels if (rt->rt_ifp == 0) 16127232Skarels rt->rt_ifp = if_ifwithnet(&rt->rt_router); 16230745Skarels if ((state & RTS_INTERFACE) == 0) 1639020Ssam rt->rt_flags |= RTF_GATEWAY; 16431220Skarels rt->rt_metric = metric; 1659020Ssam insque(rt, rh); 16634569Skarels TRACE_ACTION("ADD", rt); 16715098Ssam /* 16815098Ssam * If the ioctl fails because the gateway is unreachable 16915098Ssam * from this host, discard the entry. This should only 17015098Ssam * occur because of an incorrect entry in /etc/gateways. 17115098Ssam */ 172*55907Smckusick if ((rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 && 17352583Ssklower rtioctl(ADD, &rt->rt_rt) < 0) { 17431220Skarels if (errno != EEXIST && gate->sa_family < af_max) 17530961Skarels syslog(LOG_ERR, 17630961Skarels "adding route to net/host %s through gateway %s: %m\n", 17730961Skarels (*afswitch[dst->sa_family].af_format)(dst), 17830961Skarels (*afswitch[gate->sa_family].af_format)(gate)); 17952583Ssklower perror("ADD ROUTE"); 18015098Ssam if (errno == ENETUNREACH) { 18134569Skarels TRACE_ACTION("DELETE", rt); 18215098Ssam remque(rt); 18315098Ssam free((char *)rt); 18415098Ssam } 18515098Ssam } 1869020Ssam } 1879020Ssam 1889020Ssam rtchange(rt, gate, metric) 1899020Ssam struct rt_entry *rt; 1909020Ssam struct sockaddr *gate; 1919020Ssam short metric; 1929020Ssam { 19336831Skarels int add = 0, delete = 0, newgateway = 0; 194*55907Smckusick struct rtuentry oldroute; 1959020Ssam 19638684Ssklower FIXLEN(gate); 19738684Ssklower FIXLEN(&(rt->rt_router)); 19838684Ssklower FIXLEN(&(rt->rt_dst)); 19936831Skarels if (!equal(&rt->rt_router, gate)) { 20036831Skarels newgateway++; 20136831Skarels TRACE_ACTION("CHANGE FROM ", rt); 20236831Skarels } else if (metric != rt->rt_metric) 20336831Skarels TRACE_NEWMETRIC(rt, metric); 20431220Skarels if ((rt->rt_state & RTS_INTERNAL) == 0) { 20531220Skarels /* 20631220Skarels * If changing to different router, we need to add 20731220Skarels * new route and delete old one if in the kernel. 20831220Skarels * If the router is the same, we need to delete 20931220Skarels * the route if has become unreachable, or re-add 21031220Skarels * it if it had been unreachable. 21131220Skarels */ 21236831Skarels if (newgateway) { 21331220Skarels add++; 21431220Skarels if (rt->rt_metric != HOPCNT_INFINITY) 21531220Skarels delete++; 21631220Skarels } else if (metric == HOPCNT_INFINITY) 21729931Skarels delete++; 21831220Skarels else if (rt->rt_metric == HOPCNT_INFINITY) 21931220Skarels add++; 22029931Skarels } 22134569Skarels if (delete) 22234569Skarels oldroute = rt->rt_rt; 22331220Skarels if ((rt->rt_state & RTS_INTERFACE) && delete) { 22431220Skarels rt->rt_state &= ~RTS_INTERFACE; 22534569Skarels rt->rt_flags |= RTF_GATEWAY; 22634569Skarels if (metric > rt->rt_metric && delete) 22734569Skarels syslog(LOG_ERR, "%s route to interface %s (timed out)", 22834569Skarels add? "changing" : "deleting", 229*55907Smckusick rt->rt_ifp ? rt->rt_ifp->int_name : "?"); 23031220Skarels } 23131220Skarels if (add) { 23231220Skarels rt->rt_router = *gate; 23331220Skarels rt->rt_ifp = if_ifwithdstaddr(&rt->rt_router); 23431220Skarels if (rt->rt_ifp == 0) 23531220Skarels rt->rt_ifp = if_ifwithnet(&rt->rt_router); 23631220Skarels } 23731220Skarels rt->rt_metric = metric; 23831220Skarels rt->rt_state |= RTS_CHANGED; 23936831Skarels if (newgateway) 24034569Skarels TRACE_ACTION("CHANGE TO ", rt); 24138684Ssklower #ifndef RTM_ADD 24252583Ssklower if (add && rtioctl(ADD, &rt->rt_rt) < 0) 24352583Ssklower perror("ADD ROUTE"); 24452583Ssklower if (delete && rtioctl(DELETE, &oldroute) < 0) 24552583Ssklower perror("DELETE ROUTE"); 24638684Ssklower #else 24752583Ssklower if (delete && !add) { 24852583Ssklower if (rtioctl(DELETE, &oldroute) < 0) 24952583Ssklower perror("DELETE ROUTE"); 25052583Ssklower } else if (!delete && add) { 25152583Ssklower if (rtioctl(ADD, &rt->rt_rt) < 0) 25252583Ssklower perror("ADD ROUTE"); 25352583Ssklower } else if (delete && add) { 25452583Ssklower if (rtioctl(CHANGE, &rt->rt_rt) < 0) 25552583Ssklower perror("CHANGE ROUTE"); 25638684Ssklower } 25738684Ssklower #endif 2589020Ssam } 2599020Ssam 2609020Ssam rtdelete(rt) 2619020Ssam struct rt_entry *rt; 2629020Ssam { 26315098Ssam 26434569Skarels TRACE_ACTION("DELETE", rt); 26538684Ssklower FIXLEN(&(rt->rt_router)); 26638684Ssklower FIXLEN(&(rt->rt_dst)); 26734569Skarels if (rt->rt_metric < HOPCNT_INFINITY) { 26834569Skarels if ((rt->rt_state & (RTS_INTERFACE|RTS_INTERNAL)) == RTS_INTERFACE) 26934569Skarels syslog(LOG_ERR, 27034569Skarels "deleting route to interface %s? (timed out?)", 27134569Skarels rt->rt_ifp->int_name); 272*55907Smckusick if ((rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 && 27352583Ssklower rtioctl(DELETE, &rt->rt_rt) < 0) 27452583Ssklower perror("rtdelete"); 27534569Skarels } 2769020Ssam remque(rt); 2779020Ssam free((char *)rt); 2789020Ssam } 2799020Ssam 28036831Skarels rtdeleteall(sig) 28136831Skarels int sig; 28231220Skarels { 28331220Skarels register struct rthash *rh; 28431220Skarels register struct rt_entry *rt; 28531220Skarels struct rthash *base = hosthash; 28631220Skarels int doinghost = 1; 28731220Skarels 28831220Skarels again: 28931220Skarels for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { 29031220Skarels rt = rh->rt_forw; 29131220Skarels for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 29234569Skarels if (rt->rt_state & RTS_INTERFACE || 29334569Skarels rt->rt_metric >= HOPCNT_INFINITY) 29431220Skarels continue; 29534569Skarels TRACE_ACTION("DELETE", rt); 29631220Skarels if ((rt->rt_state & (RTS_INTERNAL|RTS_EXTERNAL)) == 0 && 29752583Ssklower rtioctl(DELETE, &rt->rt_rt) < 0) 29852583Ssklower perror("rtdeleteall"); 29931220Skarels } 30031220Skarels } 30131220Skarels if (doinghost) { 30231220Skarels doinghost = 0; 30331220Skarels base = nethash; 30431220Skarels goto again; 30531220Skarels } 30636831Skarels exit(sig); 30731220Skarels } 30831220Skarels 30916328Skarels /* 31016328Skarels * If we have an interface to the wide, wide world, 31116328Skarels * add an entry for an Internet default route (wildcard) to the internal 31216328Skarels * tables and advertise it. This route is not added to the kernel routes, 31316328Skarels * but this entry prevents us from listening to other people's defaults 31416328Skarels * and installing them in the kernel here. 31516328Skarels */ 31616328Skarels rtdefault() 31716328Skarels { 31816328Skarels extern struct sockaddr inet_default; 31916328Skarels 32034661Skarels rtadd(&inet_default, &inet_default, 1, 32127232Skarels RTS_CHANGED | RTS_PASSIVE | RTS_INTERNAL); 32216328Skarels } 32316328Skarels 3249020Ssam rtinit() 3259020Ssam { 3269020Ssam register struct rthash *rh; 3279020Ssam 3289020Ssam for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) 3299020Ssam rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 3309020Ssam for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) 3319020Ssam rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 3329020Ssam } 33352583Ssklower 33452583Ssklower rtioctl(action, ort) 33552583Ssklower int action; 336*55907Smckusick struct rtuentry *ort; 33752583Ssklower { 33852583Ssklower #ifndef RTM_ADD 339*55907Smckusick if (install == 0) 340*55907Smckusick return (errno = 0); 341*55907Smckusick ort->rtu_rtflags = ort->rtu_flags; 34252583Ssklower switch (action) { 34352583Ssklower 34452583Ssklower case ADD: 34552583Ssklower return (ioctl(s, SIOCADDRT, (char *)ort)); 34652583Ssklower 34752583Ssklower case DELETE: 34852583Ssklower return (ioctl(s, SIOCDELRT, (char *)ort)); 34952583Ssklower 35052583Ssklower default: 35152583Ssklower return (-1); 35252583Ssklower } 35352583Ssklower #else /* RTM_ADD */ 35452583Ssklower struct { 35552583Ssklower struct rt_msghdr w_rtm; 35652583Ssklower struct sockaddr_in w_dst; 35752583Ssklower struct sockaddr w_gate; 35852583Ssklower struct sockaddr_in w_netmask; 35952583Ssklower } w; 36052583Ssklower #define rtm w.w_rtm 36152583Ssklower 36252583Ssklower bzero((char *)&w, sizeof(w)); 36352583Ssklower rtm.rtm_msglen = sizeof(w); 36452583Ssklower rtm.rtm_version = RTM_VERSION; 36552583Ssklower rtm.rtm_type = (action == ADD ? RTM_ADD : 36652583Ssklower (action == DELETE ? RTM_DELETE : RTM_CHANGE)); 36752583Ssklower #undef rt_dst 368*55907Smckusick rtm.rtm_flags = ort->rtu_flags; 36952583Ssklower rtm.rtm_seq = ++seqno; 37052583Ssklower rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; 371*55907Smckusick bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst)); 372*55907Smckusick bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate)); 37352583Ssklower w.w_dst.sin_family = AF_INET; 37452583Ssklower w.w_dst.sin_len = sizeof(w.w_dst); 37552583Ssklower w.w_gate.sa_family = AF_INET; 37652583Ssklower w.w_gate.sa_len = sizeof(w.w_gate); 37752583Ssklower if (rtm.rtm_flags & RTF_HOST) { 37852583Ssklower rtm.rtm_msglen -= sizeof(w.w_netmask); 37952583Ssklower } else { 38052620Ssklower register char *cp; 38152620Ssklower int len; 38252620Ssklower 38352583Ssklower rtm.rtm_addrs |= RTA_NETMASK; 38452583Ssklower w.w_netmask.sin_addr.s_addr = 38552583Ssklower inet_maskof(w.w_dst.sin_addr.s_addr); 38652620Ssklower for (cp = (char *)(1 + &w.w_netmask.sin_addr); 38752620Ssklower --cp > (char *) &w.w_netmask; ) 38852620Ssklower if (*cp) 38952620Ssklower break; 39052620Ssklower len = cp - (char *)&w.w_netmask; 39152620Ssklower if (len) { 39252620Ssklower len++; 39352620Ssklower w.w_netmask.sin_len = len; 39452620Ssklower len = 1 + ((len - 1) | (sizeof(long) - 1)); 39552620Ssklower } else 39652620Ssklower len = sizeof(long); 39752620Ssklower rtm.rtm_msglen -= (sizeof(w.w_netmask) - len); 39852583Ssklower } 39952583Ssklower errno = 0; 400*55907Smckusick return (install ? write(r, (char *)&w, rtm.rtm_msglen) : (errno = 0)); 40152583Ssklower #endif /* RTM_ADD */ 40252583Ssklower } 403