finish up interface metric code: store metric from our perspective,
[unix-history] / usr / src / sbin / routed / tables.c
CommitLineData
5ff67f98
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
b9b7b06a 7#ifndef lint
88709531 8static char sccsid[] = "@(#)tables.c 5.7 (Berkeley) %G%";
5ff67f98 9#endif not lint
b9b7b06a
SL
10
11/*
12 * Routing Table Management Daemon
13 */
7fe7fe74 14#include "defs.h"
b9b7b06a
SL
15#include <sys/ioctl.h>
16#include <errno.h>
69b7ef61 17#include <syslog.h>
b9b7b06a
SL
18
19#ifndef DEBUG
20#define DEBUG 0
21#endif
22
23int install = !DEBUG; /* if 1 call kernel */
24
25/*
26 * Lookup dst in the tables for an exact match.
27 */
28struct rt_entry *
29rtlookup(dst)
30 struct sockaddr *dst;
31{
32 register struct rt_entry *rt;
33 register struct rthash *rh;
d5568f13 34 register u_int hash;
b9b7b06a
SL
35 struct afhash h;
36 int doinghost = 1;
37
17fe297f 38 if (dst->sa_family >= af_max)
b9b7b06a
SL
39 return (0);
40 (*afswitch[dst->sa_family].af_hash)(dst, &h);
41 hash = h.afh_hosthash;
49139b80 42 rh = &hosthash[hash & ROUTEHASHMASK];
b9b7b06a
SL
43again:
44 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
45 if (rt->rt_hash != hash)
46 continue;
47 if (equal(&rt->rt_dst, dst))
48 return (rt);
49 }
50 if (doinghost) {
51 doinghost = 0;
52 hash = h.afh_nethash;
49139b80 53 rh = &nethash[hash & ROUTEHASHMASK];
b9b7b06a
SL
54 goto again;
55 }
56 return (0);
57}
58
59/*
60 * Find a route to dst as the kernel would.
61 */
62struct rt_entry *
63rtfind(dst)
64 struct sockaddr *dst;
65{
66 register struct rt_entry *rt;
67 register struct rthash *rh;
d5568f13 68 register u_int hash;
b9b7b06a
SL
69 struct afhash h;
70 int af = dst->sa_family;
71 int doinghost = 1, (*match)();
72
17fe297f 73 if (af >= af_max)
b9b7b06a
SL
74 return (0);
75 (*afswitch[af].af_hash)(dst, &h);
76 hash = h.afh_hosthash;
49139b80 77 rh = &hosthash[hash & ROUTEHASHMASK];
b9b7b06a
SL
78
79again:
80 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
81 if (rt->rt_hash != hash)
82 continue;
83 if (doinghost) {
84 if (equal(&rt->rt_dst, dst))
85 return (rt);
86 } else {
87 if (rt->rt_dst.sa_family == af &&
88 (*match)(&rt->rt_dst, dst))
89 return (rt);
90 }
91 }
92 if (doinghost) {
93 doinghost = 0;
94 hash = h.afh_nethash;
49139b80 95 rh = &nethash[hash & ROUTEHASHMASK];
b9b7b06a
SL
96 match = afswitch[af].af_netmatch;
97 goto again;
98 }
99 return (0);
100}
101
102rtadd(dst, gate, metric, state)
103 struct sockaddr *dst, *gate;
104 int metric, state;
105{
106 struct afhash h;
107 register struct rt_entry *rt;
108 struct rthash *rh;
d5568f13
MK
109 int af = dst->sa_family, flags;
110 u_int hash;
b9b7b06a 111
17fe297f 112 if (af >= af_max)
b9b7b06a
SL
113 return;
114 (*afswitch[af].af_hash)(dst, &h);
4fad5a6e
MK
115 flags = (*afswitch[af].af_rtflags)(dst);
116 /*
117 * Subnet flag isn't visible to kernel, move to state. XXX
118 */
119 if (flags & RTF_SUBNET) {
120 state |= RTS_SUBNET;
121 flags &= ~RTF_SUBNET;
122 }
b9b7b06a
SL
123 if (flags & RTF_HOST) {
124 hash = h.afh_hosthash;
49139b80 125 rh = &hosthash[hash & ROUTEHASHMASK];
b9b7b06a
SL
126 } else {
127 hash = h.afh_nethash;
49139b80 128 rh = &nethash[hash & ROUTEHASHMASK];
b9b7b06a
SL
129 }
130 rt = (struct rt_entry *)malloc(sizeof (*rt));
131 if (rt == 0)
132 return;
133 rt->rt_hash = hash;
134 rt->rt_dst = *dst;
135 rt->rt_router = *gate;
b9b7b06a
SL
136 rt->rt_timer = 0;
137 rt->rt_flags = RTF_UP | flags;
138 rt->rt_state = state | RTS_CHANGED;
4fad5a6e
MK
139 rt->rt_ifp = if_ifwithdstaddr(&rt->rt_router);
140 if (rt->rt_ifp == 0)
141 rt->rt_ifp = if_ifwithnet(&rt->rt_router);
88709531
MK
142 if (rt->rt_ifp == 0) {
143 if (dst->sa_family < af_max && gate->sa_family < af_max)
144 syslog(LOG_ERR,
145 "route to net/host %s goes through unreachable gateway %s\n",
146 (*afswitch[dst->sa_family].af_format)(dst),
147 (*afswitch[gate->sa_family].af_format)(gate));
148 free((char *)rt);
149 return;
150 }
151 if ((state & RTS_INTERFACE) == 0)
b9b7b06a 152 rt->rt_flags |= RTF_GATEWAY;
88709531
MK
153 /*
154 * Set rt_ifmetric to the amount by which we
155 * increment the route when sending it to others.
156 */
157 if (state & RTS_INTERFACE) {
158 rt->rt_metric = 0;
159 rt->rt_ifmetric = metric + 1;
160 } else {
161 rt->rt_metric = metric;
162 rt->rt_ifmetric = rt->rt_ifp->int_metric + 1;
163 }
b9b7b06a
SL
164 insque(rt, rh);
165 TRACE_ACTION(ADD, rt);
a8d9e287
SL
166 /*
167 * If the ioctl fails because the gateway is unreachable
168 * from this host, discard the entry. This should only
169 * occur because of an incorrect entry in /etc/gateways.
170 */
4fad5a6e
MK
171 if (install && (rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
172 ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) {
b9b7b06a 173 perror("SIOCADDRT");
a8d9e287
SL
174 if (errno == ENETUNREACH) {
175 TRACE_ACTION(DELETE, rt);
176 remque(rt);
177 free((char *)rt);
178 }
179 }
b9b7b06a
SL
180}
181
182rtchange(rt, gate, metric)
183 struct rt_entry *rt;
184 struct sockaddr *gate;
185 short metric;
186{
eb39c032 187 int doioctl = 0, metricchanged = 0, delete = 0;
b9b7b06a
SL
188 struct rtentry oldroute;
189
4fad5a6e 190 if (!equal(&rt->rt_router, gate) && (rt->rt_state & RTS_INTERNAL) == 0)
b9b7b06a 191 doioctl++;
eb39c032 192 if (metric != rt->rt_metric) {
b9b7b06a 193 metricchanged++;
eb39c032
MK
194 if (metric == HOPCNT_INFINITY)
195 delete++;
196 }
b9b7b06a 197 if (doioctl || metricchanged) {
b7e4f8be 198 TRACE_ACTION(CHANGE FROM, rt);
88709531
MK
199 if ((rt->rt_state & RTS_INTERFACE) &&
200 metric > rt->rt_ifp->int_metric) {
aa7d0b61 201 rt->rt_state &= ~RTS_INTERFACE;
88709531 202 rt->rt_flags |= RTF_GATEWAY;
aa7d0b61
MK
203 syslog(LOG_ERR,
204 "changing route from interface %s (timed out)",
205 rt->rt_ifp->int_name);
206 }
88709531 207 if (doioctl || delete) {
b7e4f8be
MK
208 oldroute = rt->rt_rt;
209 rt->rt_router = *gate;
4fad5a6e
MK
210 rt->rt_ifp = if_ifwithdstaddr(&rt->rt_router);
211 if (rt->rt_ifp == 0)
212 rt->rt_ifp = if_ifwithnet(&rt->rt_router);
b7e4f8be
MK
213 }
214 rt->rt_metric = metric;
b9b7b06a 215 rt->rt_state |= RTS_CHANGED;
b7e4f8be 216 TRACE_ACTION(CHANGE TO, rt);
b9b7b06a 217 }
eb39c032 218 if (doioctl && install)
b7e4f8be
MK
219 if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
220 perror("SIOCADDRT");
eb39c032 221 if ((doioctl || delete) && install)
b7e4f8be
MK
222 if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
223 perror("SIOCDELRT");
b9b7b06a
SL
224}
225
226rtdelete(rt)
227 struct rt_entry *rt;
228{
a8d9e287 229
69b7ef61
MK
230 if (rt->rt_state & RTS_INTERFACE)
231 syslog(LOG_ERR, "deleting route to interface %s (timed out)",
232 rt->rt_ifp->int_name);
b9b7b06a 233 TRACE_ACTION(DELETE, rt);
b1d43e6c 234 if (install && (rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
4fad5a6e 235 ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
b9b7b06a
SL
236 perror("SIOCDELRT");
237 remque(rt);
238 free((char *)rt);
239}
240
ad34083f
MK
241/*
242 * If we have an interface to the wide, wide world,
243 * add an entry for an Internet default route (wildcard) to the internal
244 * tables and advertise it. This route is not added to the kernel routes,
245 * but this entry prevents us from listening to other people's defaults
246 * and installing them in the kernel here.
247 */
248rtdefault()
249{
ad34083f
MK
250 extern struct sockaddr inet_default;
251
4fad5a6e
MK
252 rtadd(&inet_default, &inet_default, 0,
253 RTS_CHANGED | RTS_PASSIVE | RTS_INTERNAL);
ad34083f
MK
254}
255
b9b7b06a
SL
256rtinit()
257{
258 register struct rthash *rh;
259
260 for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
261 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
262 for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
263 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
264}