124323Ssklower /*
2*61474Sbostic * Copyright (c) 1985, 1993
3*61474Sbostic * The Regents of the University of California. All rights reserved.
424323Ssklower *
542698Sbostic * %sccs.include.redist.c%
624323Ssklower */
724323Ssklower
824313Ssklower #ifndef lint
9*61474Sbostic static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 06/05/93";
1035593Sbostic #endif /* not lint */
1124313Ssklower
1224313Ssklower /*
1324313Ssklower * Routing Table Management Daemon
1424313Ssklower */
1524313Ssklower #include "defs.h"
1624313Ssklower #include <sys/ioctl.h>
1724313Ssklower #include <errno.h>
1824313Ssklower
1924313Ssklower #ifndef DEBUG
2024313Ssklower #define DEBUG 0
2124313Ssklower #endif
2224313Ssklower
2330350Skarels extern char *xns_ntoa();
2438688Skarels #define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
2530350Skarels
2624313Ssklower int install = !DEBUG; /* if 1 call kernel */
2724318Ssklower int delete = 1;
2824313Ssklower /*
2924313Ssklower * Lookup dst in the tables for an exact match.
3024313Ssklower */
3124313Ssklower struct rt_entry *
rtlookup(dst)3224313Ssklower rtlookup(dst)
3324313Ssklower struct sockaddr *dst;
3424313Ssklower {
3524313Ssklower register struct rt_entry *rt;
3624313Ssklower register struct rthash *rh;
3724313Ssklower register u_int hash;
3824313Ssklower struct afhash h;
3924313Ssklower int doinghost = 1;
4024313Ssklower
4124313Ssklower if (dst->sa_family >= AF_MAX)
4224313Ssklower return (0);
4324313Ssklower (*afswitch[dst->sa_family].af_hash)(dst, &h);
4424313Ssklower hash = h.afh_hosthash;
4524313Ssklower rh = &hosthash[hash & ROUTEHASHMASK];
4624313Ssklower again:
4724313Ssklower for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4824313Ssklower if (rt->rt_hash != hash)
4924313Ssklower continue;
5024313Ssklower if (equal(&rt->rt_dst, dst))
5124313Ssklower return (rt);
5224313Ssklower }
5324313Ssklower if (doinghost) {
5424313Ssklower doinghost = 0;
5524313Ssklower hash = h.afh_nethash;
5624313Ssklower rh = &nethash[hash & ROUTEHASHMASK];
5724313Ssklower goto again;
5824313Ssklower }
5924313Ssklower return (0);
6024313Ssklower }
6124313Ssklower
6224313Ssklower /*
6324313Ssklower * Find a route to dst as the kernel would.
6424313Ssklower */
6524313Ssklower struct rt_entry *
rtfind(dst)6624313Ssklower rtfind(dst)
6724313Ssklower struct sockaddr *dst;
6824313Ssklower {
6924313Ssklower register struct rt_entry *rt;
7024313Ssklower register struct rthash *rh;
7124313Ssklower register u_int hash;
7224313Ssklower struct afhash h;
7324313Ssklower int af = dst->sa_family;
7424313Ssklower int doinghost = 1, (*match)();
7524313Ssklower
7624313Ssklower if (af >= AF_MAX)
7724313Ssklower return (0);
7824313Ssklower (*afswitch[af].af_hash)(dst, &h);
7924313Ssklower hash = h.afh_hosthash;
8024313Ssklower rh = &hosthash[hash & ROUTEHASHMASK];
8124313Ssklower
8224313Ssklower again:
8324313Ssklower for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
8424313Ssklower if (rt->rt_hash != hash)
8524313Ssklower continue;
8624313Ssklower if (doinghost) {
8724313Ssklower if (equal(&rt->rt_dst, dst))
8824313Ssklower return (rt);
8924323Ssklower } else {
9024323Ssklower if (rt->rt_dst.sa_family == af &&
9124323Ssklower (*match)(&rt->rt_dst, dst))
9224323Ssklower return (rt);
9324313Ssklower }
9424313Ssklower }
9524313Ssklower if (doinghost) {
9624313Ssklower doinghost = 0;
9724313Ssklower hash = h.afh_nethash;
9824313Ssklower rh = &nethash[hash & ROUTEHASHMASK];
9924313Ssklower match = afswitch[af].af_netmatch;
10024313Ssklower goto again;
10124313Ssklower }
10224313Ssklower return (0);
10324313Ssklower }
10424313Ssklower
10524313Ssklower rtadd(dst, gate, metric, state)
10624313Ssklower struct sockaddr *dst, *gate;
10724313Ssklower int metric, state;
10824313Ssklower {
10924313Ssklower struct afhash h;
11024313Ssklower register struct rt_entry *rt;
11124313Ssklower struct rthash *rh;
11224313Ssklower int af = dst->sa_family, flags;
11324313Ssklower u_int hash;
11424313Ssklower
11538688Skarels FIXLEN(dst);
11638688Skarels FIXLEN(gate);
11724313Ssklower if (af >= AF_MAX)
11824313Ssklower return;
11924313Ssklower (*afswitch[af].af_hash)(dst, &h);
12024313Ssklower flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
12124313Ssklower if (flags & RTF_HOST) {
12224313Ssklower hash = h.afh_hosthash;
12324313Ssklower rh = &hosthash[hash & ROUTEHASHMASK];
12424313Ssklower } else {
12524313Ssklower hash = h.afh_nethash;
12624313Ssklower rh = &nethash[hash & ROUTEHASHMASK];
12724313Ssklower }
12824313Ssklower rt = (struct rt_entry *)malloc(sizeof (*rt));
12924313Ssklower if (rt == 0)
13024313Ssklower return;
13124313Ssklower rt->rt_hash = hash;
13224313Ssklower rt->rt_dst = *dst;
13324313Ssklower rt->rt_router = *gate;
13424313Ssklower rt->rt_metric = metric;
13524313Ssklower rt->rt_timer = 0;
13624313Ssklower rt->rt_flags = RTF_UP | flags;
13724313Ssklower rt->rt_state = state | RTS_CHANGED;
13824313Ssklower rt->rt_ifp = if_ifwithnet(&rt->rt_router);
13924313Ssklower if (metric)
14024313Ssklower rt->rt_flags |= RTF_GATEWAY;
14124313Ssklower insque(rt, rh);
14224313Ssklower TRACE_ACTION(ADD, rt);
14324313Ssklower /*
14424313Ssklower * If the ioctl fails because the gateway is unreachable
14524313Ssklower * from this host, discard the entry. This should only
14624313Ssklower * occur because of an incorrect entry in /etc/gateways.
14724313Ssklower */
14852623Ssklower if (install && rtioctl(ADD, &rt->rt_rt) < 0) {
14930350Skarels if (errno != EEXIST)
15030350Skarels perror("SIOCADDRT");
15124313Ssklower if (errno == ENETUNREACH) {
15224313Ssklower TRACE_ACTION(DELETE, rt);
15324313Ssklower remque(rt);
15424313Ssklower free((char *)rt);
15524313Ssklower }
15624313Ssklower }
15724313Ssklower }
15824313Ssklower
15924313Ssklower rtchange(rt, gate, metric)
16024313Ssklower struct rt_entry *rt;
16124313Ssklower struct sockaddr *gate;
16224313Ssklower short metric;
16324313Ssklower {
16424313Ssklower int doioctl = 0, metricchanged = 0;
16524313Ssklower struct rtentry oldroute;
16624313Ssklower
16738688Skarels FIXLEN(gate);
16824313Ssklower if (!equal(&rt->rt_router, gate))
16924313Ssklower doioctl++;
17024313Ssklower if (metric != rt->rt_metric)
17124313Ssklower metricchanged++;
17224313Ssklower if (doioctl || metricchanged) {
17324313Ssklower TRACE_ACTION(CHANGE FROM, rt);
17424313Ssklower if (doioctl) {
17524313Ssklower oldroute = rt->rt_rt;
17624313Ssklower rt->rt_router = *gate;
17724313Ssklower }
17824313Ssklower rt->rt_metric = metric;
17926171Ssklower if ((rt->rt_state & RTS_INTERFACE) && metric) {
18026171Ssklower rt->rt_state &= ~RTS_INTERFACE;
18126171Ssklower syslog(LOG_ERR,
18226171Ssklower "changing route from interface %s (timed out)",
18326171Ssklower rt->rt_ifp->int_name);
18426171Ssklower }
18524313Ssklower if (metric)
18626171Ssklower rt->rt_flags |= RTF_GATEWAY;
18726171Ssklower else
18826171Ssklower rt->rt_flags &= ~RTF_GATEWAY;
18924313Ssklower rt->rt_state |= RTS_CHANGED;
19024313Ssklower TRACE_ACTION(CHANGE TO, rt);
19124313Ssklower }
19224313Ssklower if (doioctl && install) {
19338688Skarels #ifndef RTM_ADD
19452623Ssklower if (rtioctl(ADD, &rt->rt_rt) < 0)
19552623Ssklower syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
19630350Skarels xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr),
19730350Skarels xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr));
19852623Ssklower if (delete && rtioctl(DELETE, &oldroute) < 0)
19952623Ssklower perror("rtioctl DELETE");
20038688Skarels #else
20152623Ssklower if (delete == 0) {
20252623Ssklower if (rtioctl(ADD, &rt->rt_rt) >= 0)
20352623Ssklower return;
20452623Ssklower } else {
20552623Ssklower if (rtioctl(CHANGE, &rt->rt_rt) >= 0)
20652623Ssklower return;
20752623Ssklower }
20852623Ssklower syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
20938688Skarels xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr),
21038688Skarels xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr));
21138688Skarels #endif
21224313Ssklower }
21324313Ssklower }
21424313Ssklower
21524313Ssklower rtdelete(rt)
21624313Ssklower struct rt_entry *rt;
21724313Ssklower {
21824313Ssklower
21938688Skarels struct sockaddr *sa = &(rt->rt_rt.rt_gateway);
22038688Skarels FIXLEN(sa);
22138688Skarels #undef rt_dst
22238688Skarels sa = &(rt->rt_rt.rt_dst);
22338688Skarels FIXLEN(sa);
22426171Ssklower if (rt->rt_state & RTS_INTERFACE) {
22526171Ssklower syslog(LOG_ERR, "deleting route to interface %s (timed out)",
22626171Ssklower rt->rt_ifp->int_name);
22726171Ssklower }
22824313Ssklower TRACE_ACTION(DELETE, rt);
22952623Ssklower if (install && rtioctl(DELETE, &rt->rt_rt) < 0)
23052623Ssklower perror("rtioctl DELETE");
23124313Ssklower remque(rt);
23224313Ssklower free((char *)rt);
23324313Ssklower }
23424313Ssklower
rtinit()23524313Ssklower rtinit()
23624313Ssklower {
23724313Ssklower register struct rthash *rh;
23824313Ssklower
23924313Ssklower for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
24024313Ssklower rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
24124313Ssklower for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
24224313Ssklower rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
24324313Ssklower }
24452623Ssklower int seqno;
24552623Ssklower
rtioctl(action,ort)24652623Ssklower rtioctl(action, ort)
24752623Ssklower int action;
24852623Ssklower struct ortentry *ort;
24952623Ssklower {
25052623Ssklower #ifndef RTM_ADD
25152623Ssklower switch (action) {
25252623Ssklower
25352623Ssklower case ADD:
25452623Ssklower return (ioctl(s, SIOCADDRT, (char *)ort));
25552623Ssklower
25652623Ssklower case DELETE:
25752623Ssklower return (ioctl(s, SIOCDELRT, (char *)ort));
25852623Ssklower
25952623Ssklower default:
26052623Ssklower return (-1);
26152623Ssklower }
26252623Ssklower #else /* RTM_ADD */
26352623Ssklower struct {
26452623Ssklower struct rt_msghdr w_rtm;
26552623Ssklower struct sockaddr w_dst;
26652623Ssklower struct sockaddr w_gate;
26752623Ssklower struct sockaddr_ns w_netmask;
26852623Ssklower } w;
26952623Ssklower #define rtm w.w_rtm
27052623Ssklower
27152623Ssklower bzero((char *)&w, sizeof(w));
27252623Ssklower rtm.rtm_msglen = sizeof(w);
27352623Ssklower rtm.rtm_version = RTM_VERSION;
27452623Ssklower rtm.rtm_type = (action == ADD ? RTM_ADD :
27552623Ssklower (action == DELETE ? RTM_DELETE : RTM_CHANGE));
27652623Ssklower #undef rt_flags
27752623Ssklower rtm.rtm_flags = ort->rt_flags;
27852623Ssklower rtm.rtm_seq = ++seqno;
27952623Ssklower rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
28052623Ssklower bcopy((char *)&ort->rt_dst, (char *)&w.w_dst, sizeof(w.w_dst));
28152623Ssklower bcopy((char *)&ort->rt_gateway, (char *)&w.w_gate, sizeof(w.w_gate));
28252623Ssklower w.w_gate.sa_family = w.w_dst.sa_family = AF_NS;
28352623Ssklower w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst);
28452623Ssklower if (rtm.rtm_flags & RTF_HOST) {
28552623Ssklower rtm.rtm_msglen -= sizeof(w.w_netmask);
28652623Ssklower } else {
28752623Ssklower w.w_netmask = ns_netmask;
28852623Ssklower rtm.rtm_msglen -= 8;
28952623Ssklower }
29052623Ssklower errno = 0;
29152623Ssklower return write(r, (char *)&w, rtm.rtm_msglen);
29252623Ssklower #endif /* RTM_ADD */
29352623Ssklower }
294