124323Ssklower /* 235593Sbostic * Copyright (c) 1985 The Regents of the University of California. 335593Sbostic * All rights reserved. 424323Ssklower * 535593Sbostic * Redistribution and use in source and binary forms are permitted 635593Sbostic * provided that the above copyright notice and this paragraph are 735593Sbostic * duplicated in all such forms and that any documentation, 835593Sbostic * advertising materials, and other materials related to such 935593Sbostic * distribution and use acknowledge that the software was developed 1035593Sbostic * by the University of California, Berkeley. The name of the 1135593Sbostic * University may not be used to endorse or promote products derived 1235593Sbostic * from this software without specific prior written permission. 1335593Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1435593Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1535593Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1624323Ssklower */ 1724323Ssklower 1824313Ssklower #ifndef lint 19*38688Skarels static char sccsid[] = "@(#)tables.c 5.8 (Berkeley) 08/21/89"; 2035593Sbostic #endif /* not lint */ 2124313Ssklower 2224313Ssklower /* 2324313Ssklower * Routing Table Management Daemon 2424313Ssklower */ 2524313Ssklower #include "defs.h" 2624313Ssklower #include <sys/ioctl.h> 2724313Ssklower #include <errno.h> 2824313Ssklower 2924313Ssklower #ifndef DEBUG 3024313Ssklower #define DEBUG 0 3124313Ssklower #endif 3224313Ssklower 3330350Skarels extern char *xns_ntoa(); 34*38688Skarels #define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));} 3530350Skarels 3624313Ssklower int install = !DEBUG; /* if 1 call kernel */ 3724318Ssklower int delete = 1; 3824313Ssklower /* 3924313Ssklower * Lookup dst in the tables for an exact match. 4024313Ssklower */ 4124313Ssklower struct rt_entry * 4224313Ssklower rtlookup(dst) 4324313Ssklower struct sockaddr *dst; 4424313Ssklower { 4524313Ssklower register struct rt_entry *rt; 4624313Ssklower register struct rthash *rh; 4724313Ssklower register u_int hash; 4824313Ssklower struct afhash h; 4924313Ssklower int doinghost = 1; 5024313Ssklower 5124313Ssklower if (dst->sa_family >= AF_MAX) 5224313Ssklower return (0); 5324313Ssklower (*afswitch[dst->sa_family].af_hash)(dst, &h); 5424313Ssklower hash = h.afh_hosthash; 5524313Ssklower rh = &hosthash[hash & ROUTEHASHMASK]; 5624313Ssklower again: 5724313Ssklower for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 5824313Ssklower if (rt->rt_hash != hash) 5924313Ssklower continue; 6024313Ssklower if (equal(&rt->rt_dst, dst)) 6124313Ssklower return (rt); 6224313Ssklower } 6324313Ssklower if (doinghost) { 6424313Ssklower doinghost = 0; 6524313Ssklower hash = h.afh_nethash; 6624313Ssklower rh = &nethash[hash & ROUTEHASHMASK]; 6724313Ssklower goto again; 6824313Ssklower } 6924313Ssklower return (0); 7024313Ssklower } 7124313Ssklower 7224313Ssklower /* 7324313Ssklower * Find a route to dst as the kernel would. 7424313Ssklower */ 7524313Ssklower struct rt_entry * 7624313Ssklower rtfind(dst) 7724313Ssklower struct sockaddr *dst; 7824313Ssklower { 7924313Ssklower register struct rt_entry *rt; 8024313Ssklower register struct rthash *rh; 8124313Ssklower register u_int hash; 8224313Ssklower struct afhash h; 8324313Ssklower int af = dst->sa_family; 8424313Ssklower int doinghost = 1, (*match)(); 8524313Ssklower 8624313Ssklower if (af >= AF_MAX) 8724313Ssklower return (0); 8824313Ssklower (*afswitch[af].af_hash)(dst, &h); 8924313Ssklower hash = h.afh_hosthash; 9024313Ssklower rh = &hosthash[hash & ROUTEHASHMASK]; 9124313Ssklower 9224313Ssklower again: 9324313Ssklower for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 9424313Ssklower if (rt->rt_hash != hash) 9524313Ssklower continue; 9624313Ssklower if (doinghost) { 9724313Ssklower if (equal(&rt->rt_dst, dst)) 9824313Ssklower return (rt); 9924323Ssklower } else { 10024323Ssklower if (rt->rt_dst.sa_family == af && 10124323Ssklower (*match)(&rt->rt_dst, dst)) 10224323Ssklower return (rt); 10324313Ssklower } 10424313Ssklower } 10524313Ssklower if (doinghost) { 10624313Ssklower doinghost = 0; 10724313Ssklower hash = h.afh_nethash; 10824313Ssklower rh = &nethash[hash & ROUTEHASHMASK]; 10924313Ssklower match = afswitch[af].af_netmatch; 11024313Ssklower goto again; 11124313Ssklower } 11224313Ssklower return (0); 11324313Ssklower } 11424313Ssklower 11524313Ssklower rtadd(dst, gate, metric, state) 11624313Ssklower struct sockaddr *dst, *gate; 11724313Ssklower int metric, state; 11824313Ssklower { 11924313Ssklower struct afhash h; 12024313Ssklower register struct rt_entry *rt; 12124313Ssklower struct rthash *rh; 12224313Ssklower int af = dst->sa_family, flags; 12324313Ssklower u_int hash; 12424313Ssklower 125*38688Skarels FIXLEN(dst); 126*38688Skarels FIXLEN(gate); 12724313Ssklower if (af >= AF_MAX) 12824313Ssklower return; 12924313Ssklower (*afswitch[af].af_hash)(dst, &h); 13024313Ssklower flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0; 13124313Ssklower if (flags & RTF_HOST) { 13224313Ssklower hash = h.afh_hosthash; 13324313Ssklower rh = &hosthash[hash & ROUTEHASHMASK]; 13424313Ssklower } else { 13524313Ssklower hash = h.afh_nethash; 13624313Ssklower rh = &nethash[hash & ROUTEHASHMASK]; 13724313Ssklower } 13824313Ssklower rt = (struct rt_entry *)malloc(sizeof (*rt)); 13924313Ssklower if (rt == 0) 14024313Ssklower return; 14124313Ssklower rt->rt_hash = hash; 14224313Ssklower rt->rt_dst = *dst; 14324313Ssklower rt->rt_router = *gate; 14424313Ssklower rt->rt_metric = metric; 14524313Ssklower rt->rt_timer = 0; 14624313Ssklower rt->rt_flags = RTF_UP | flags; 14724313Ssklower rt->rt_state = state | RTS_CHANGED; 14824313Ssklower rt->rt_ifp = if_ifwithnet(&rt->rt_router); 14924313Ssklower if (metric) 15024313Ssklower rt->rt_flags |= RTF_GATEWAY; 15124313Ssklower insque(rt, rh); 15224313Ssklower TRACE_ACTION(ADD, rt); 15324313Ssklower /* 15424313Ssklower * If the ioctl fails because the gateway is unreachable 15524313Ssklower * from this host, discard the entry. This should only 15624313Ssklower * occur because of an incorrect entry in /etc/gateways. 15724313Ssklower */ 15824313Ssklower if (install && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) { 15930350Skarels if (errno != EEXIST) 16030350Skarels perror("SIOCADDRT"); 16124313Ssklower if (errno == ENETUNREACH) { 16224313Ssklower TRACE_ACTION(DELETE, rt); 16324313Ssklower remque(rt); 16424313Ssklower free((char *)rt); 16524313Ssklower } 16624313Ssklower } 16724313Ssklower } 16824313Ssklower 16924313Ssklower rtchange(rt, gate, metric) 17024313Ssklower struct rt_entry *rt; 17124313Ssklower struct sockaddr *gate; 17224313Ssklower short metric; 17324313Ssklower { 17424313Ssklower int doioctl = 0, metricchanged = 0; 17524313Ssklower struct rtentry oldroute; 17624313Ssklower 177*38688Skarels FIXLEN(gate); 17824313Ssklower if (!equal(&rt->rt_router, gate)) 17924313Ssklower doioctl++; 18024313Ssklower if (metric != rt->rt_metric) 18124313Ssklower metricchanged++; 18224313Ssklower if (doioctl || metricchanged) { 18324313Ssklower TRACE_ACTION(CHANGE FROM, rt); 18424313Ssklower if (doioctl) { 18524313Ssklower oldroute = rt->rt_rt; 18624313Ssklower rt->rt_router = *gate; 18724313Ssklower } 18824313Ssklower rt->rt_metric = metric; 18926171Ssklower if ((rt->rt_state & RTS_INTERFACE) && metric) { 19026171Ssklower rt->rt_state &= ~RTS_INTERFACE; 19126171Ssklower syslog(LOG_ERR, 19226171Ssklower "changing route from interface %s (timed out)", 19326171Ssklower rt->rt_ifp->int_name); 19426171Ssklower } 19524313Ssklower if (metric) 19626171Ssklower rt->rt_flags |= RTF_GATEWAY; 19726171Ssklower else 19826171Ssklower rt->rt_flags &= ~RTF_GATEWAY; 19924313Ssklower rt->rt_state |= RTS_CHANGED; 20024313Ssklower TRACE_ACTION(CHANGE TO, rt); 20124313Ssklower } 20224313Ssklower if (doioctl && install) { 203*38688Skarels #ifndef RTM_ADD 20424313Ssklower if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) 20530350Skarels syslog(LOG_ERR, "SIOCADDRT dst %s, gw %s: %m", 20630350Skarels xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr), 20730350Skarels xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr)); 20830350Skarels if (delete && ioctl(s, SIOCDELRT, (char *)&oldroute) < 0) 20930350Skarels perror("SIOCDELRT"); 210*38688Skarels #else 211*38688Skarels if (delete && ioctl(s, SIOCDELRT, (char *)&oldroute) < 0) 212*38688Skarels perror("SIOCDELRT"); 213*38688Skarels if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) 214*38688Skarels syslog(LOG_ERR, "SIOCADDRT dst %s, gw %s: %m", 215*38688Skarels xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr), 216*38688Skarels xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr)); 217*38688Skarels #endif 21824313Ssklower } 21924313Ssklower } 22024313Ssklower 22124313Ssklower rtdelete(rt) 22224313Ssklower struct rt_entry *rt; 22324313Ssklower { 22424313Ssklower 225*38688Skarels struct sockaddr *sa = &(rt->rt_rt.rt_gateway); 226*38688Skarels FIXLEN(sa); 227*38688Skarels #undef rt_dst 228*38688Skarels sa = &(rt->rt_rt.rt_dst); 229*38688Skarels FIXLEN(sa); 23026171Ssklower if (rt->rt_state & RTS_INTERFACE) { 23126171Ssklower syslog(LOG_ERR, "deleting route to interface %s (timed out)", 23226171Ssklower rt->rt_ifp->int_name); 23326171Ssklower } 23424313Ssklower TRACE_ACTION(DELETE, rt); 23526171Ssklower if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt)) 23626171Ssklower perror("SIOCDELRT"); 23724313Ssklower remque(rt); 23824313Ssklower free((char *)rt); 23924313Ssklower } 24024313Ssklower 24124313Ssklower rtinit() 24224313Ssklower { 24324313Ssklower register struct rthash *rh; 24424313Ssklower 24524313Ssklower for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) 24624313Ssklower rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 24724313Ssklower for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) 24824313Ssklower rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 24924313Ssklower } 250