121999Sdist /*
261540Sbostic * Copyright (c) 1983, 1988, 1993
361540Sbostic * The Regents of the University of California. All rights reserved.
433489Sbostic *
542712Sbostic * %sccs.include.redist.c%
621999Sdist */
721999Sdist
89020Ssam #ifndef lint
9*69003Sbostic static char sccsid[] = "@(#)tables.c 8.2 (Berkeley) 04/28/95";
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 *
rtlookup(dst)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 *
rtfind(dst)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 */
17255907Smckusick 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;
19455907Smckusick 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",
22955907Smckusick 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);
27255907Smckusick 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
rtdeleteall(sig)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 */
rtdefault()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
rtinit()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
rtioctl(action,ort)33452583Ssklower rtioctl(action, ort)
33552583Ssklower int action;
33655907Smckusick struct rtuentry *ort;
33752583Ssklower {
33852583Ssklower #ifndef RTM_ADD
33955907Smckusick if (install == 0)
34055907Smckusick return (errno = 0);
34155907Smckusick 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
362*69003Sbostic memset(&w, 0, 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
36855907Smckusick rtm.rtm_flags = ort->rtu_flags;
36952583Ssklower rtm.rtm_seq = ++seqno;
37052583Ssklower rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
371*69003Sbostic memmove(&w.w_dst, &ort->rtu_dst, sizeof(w.w_dst));
372*69003Sbostic memmove(&w.w_gate, &ort->rtu_router, 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;
40055907Smckusick return (install ? write(r, (char *)&w, rtm.rtm_msglen) : (errno = 0));
40152583Ssklower #endif /* RTM_ADD */
40252583Ssklower }
403