add Berkeley header
[unix-history] / usr / src / sys / net / route.c
CommitLineData
cb1c44c2 1/*
1810611d 2 * Copyright (c) 1980, 1986 Regents of the University of California.
5b519e94 3 * All rights reserved.
cb1c44c2 4 *
5b519e94
KB
5 * Redistribution and use in source and binary forms are permitted
6 * provided that this notice is preserved and that due credit is given
7 * to the University of California at Berkeley. The name of the University
8 * may not be used to endorse or promote products derived from this
9 * software without specific prior written permission. This software
10 * is provided ``as is'' without express or implied warranty.
11 *
12 * @(#)route.c 7.3 (Berkeley) %G%
cb1c44c2 13 */
9d03c806 14
a0369dcf
JB
15#include "param.h"
16#include "systm.h"
17#include "mbuf.h"
18#include "protosw.h"
19#include "socket.h"
20#include "dir.h"
21#include "user.h"
22#include "ioctl.h"
23#include "errno.h"
f4d55810 24
a0369dcf
JB
25#include "if.h"
26#include "af.h"
27#include "route.h"
9d03c806 28
a13c006d 29int rttrash; /* routes not in table but not freed */
a9ea8834 30struct sockaddr wildcard; /* zero valued cookie for wildcard searches */
c50b0999 31int rthashsize = RTHASHSIZ; /* for netstat, etc. */
a9ea8834 32
9d03c806
SL
33/*
34 * Packet routing routines.
35 */
f6311fb6 36rtalloc(ro)
9d03c806
SL
37 register struct route *ro;
38{
a9ea8834 39 register struct rtentry *rt;
9d03c806 40 register struct mbuf *m;
0b33b6b5 41 register u_long hash;
9d03c806 42 struct sockaddr *dst = &ro->ro_dst;
c50b0999 43 int (*match)(), doinghost, s;
a9ea8834 44 struct afhash h;
e8de4b5e 45 u_int af = dst->sa_family;
a9ea8834 46 struct mbuf **table;
9d03c806 47
7eb1f827
MK
48 if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP))
49 return; /* XXX */
e65dcd4c
SL
50 if (af >= AF_MAX)
51 return;
9d03c806 52 (*afswitch[af].af_hash)(dst, &h);
fc74f0c9 53 match = afswitch[af].af_netmatch;
a9ea8834 54 hash = h.afh_hosthash, table = rthost, doinghost = 1;
c50b0999 55 s = splnet();
a9ea8834 56again:
b50b85d9 57 for (m = table[RTHASHMOD(hash)]; m; m = m->m_next) {
fc74f0c9
SL
58 rt = mtod(m, struct rtentry *);
59 if (rt->rt_hash != hash)
60 continue;
90137845
SL
61 if ((rt->rt_flags & RTF_UP) == 0 ||
62 (rt->rt_ifp->if_flags & IFF_UP) == 0)
63 continue;
a9ea8834
SL
64 if (doinghost) {
65 if (bcmp((caddr_t)&rt->rt_dst, (caddr_t)dst,
66 sizeof (*dst)))
67 continue;
68 } else {
69 if (rt->rt_dst.sa_family != af ||
70 !(*match)(&rt->rt_dst, dst))
71 continue;
72 }
c50b0999
MK
73 rt->rt_refcnt++;
74 splx(s);
75 if (dst == &wildcard)
76 rtstat.rts_wildcard++;
77 ro->ro_rt = rt;
78 return;
9d03c806 79 }
c50b0999 80 if (doinghost) {
a9ea8834
SL
81 doinghost = 0;
82 hash = h.afh_nethash, table = rtnet;
83 goto again;
84 }
85 /*
86 * Check for wildcard gateway, by convention network 0.
87 */
c50b0999 88 if (dst != &wildcard) {
a9ea8834
SL
89 dst = &wildcard, hash = 0;
90 goto again;
91 }
c50b0999
MK
92 splx(s);
93 rtstat.rts_unreach++;
9d03c806
SL
94}
95
f6311fb6 96rtfree(rt)
c124e997
SL
97 register struct rtentry *rt;
98{
f6311fb6 99
c124e997 100 if (rt == 0)
a1edc12b 101 panic("rtfree");
c124e997 102 rt->rt_refcnt--;
a13c006d
BJ
103 if (rt->rt_refcnt == 0 && (rt->rt_flags&RTF_UP) == 0) {
104 rttrash--;
105 (void) m_free(dtom(rt));
106 }
c124e997
SL
107}
108
464f931f
SL
109/*
110 * Force a routing table entry to the specified
111 * destination to go through the given gateway.
112 * Normally called as a result of a routing redirect
113 * message from the network layer.
114 *
115 * N.B.: must be called at splnet or higher
116 *
464f931f 117 */
92c26501
MK
118rtredirect(dst, gateway, flags, src)
119 struct sockaddr *dst, *gateway, *src;
7eb1f827 120 int flags;
464f931f
SL
121{
122 struct route ro;
123 register struct rtentry *rt;
124
125 /* verify the gateway is directly reachable */
cba69651 126 if (ifa_ifwithnet(gateway) == 0) {
a9ea8834 127 rtstat.rts_badredirect++;
464f931f 128 return;
a9ea8834 129 }
464f931f 130 ro.ro_dst = *dst;
a9ea8834 131 ro.ro_rt = 0;
464f931f
SL
132 rtalloc(&ro);
133 rt = ro.ro_rt;
92c26501
MK
134#define equal(a1, a2) \
135 (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof(struct sockaddr)) == 0)
136 /*
137 * If the redirect isn't from our current router for this dst,
2e9c4868
MK
138 * it's either old or wrong. If it redirects us to ourselves,
139 * we have a routing loop, perhaps as a result of an interface
140 * going down recently.
92c26501 141 */
2e9c4868 142 if ((rt && !equal(src, &rt->rt_gateway)) || ifa_ifwithaddr(gateway)) {
92c26501 143 rtstat.rts_badredirect++;
703b8607
MK
144 if (rt)
145 rtfree(rt);
92c26501
MK
146 return;
147 }
a9ea8834 148 /*
48afbdc5
SL
149 * Create a new entry if we just got back a wildcard entry
150 * or the the lookup failed. This is necessary for hosts
151 * which use routing redirects generated by smart gateways
152 * to dynamically build the routing tables.
a9ea8834 153 */
48afbdc5
SL
154 if (rt &&
155 (*afswitch[dst->sa_family].af_netmatch)(&wildcard, &rt->rt_dst)) {
156 rtfree(rt);
157 rt = 0;
158 }
a9ea8834 159 if (rt == 0) {
157d4f92 160 rtinit(dst, gateway, (int)SIOCADDRT,
92c26501 161 (flags & RTF_HOST) | RTF_GATEWAY | RTF_DYNAMIC);
a9ea8834
SL
162 rtstat.rts_dynamic++;
163 return;
164 }
464f931f
SL
165 /*
166 * Don't listen to the redirect if it's
167 * for a route to an interface.
464f931f 168 */
a9ea8834 169 if (rt->rt_flags & RTF_GATEWAY) {
7eb1f827
MK
170 if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) {
171 /*
66798f86 172 * Changing from route to net => route to host.
7eb1f827
MK
173 * Create new route, rather than smashing route to net.
174 */
157d4f92
MK
175 rtinit(dst, gateway, (int)SIOCADDRT,
176 flags | RTF_DYNAMIC);
3b66e456 177 rtstat.rts_dynamic++;
7eb1f827
MK
178 } else {
179 /*
180 * Smash the current notion of the gateway to
3b66e456 181 * this destination.
7eb1f827
MK
182 */
183 rt->rt_gateway = *gateway;
31150b52
MK
184 rt->rt_flags |= RTF_MODIFIED;
185 rtstat.rts_newgateway++;
7eb1f827 186 }
92c26501
MK
187 } else
188 rtstat.rts_badredirect++;
ff320836 189 rtfree(rt);
464f931f
SL
190}
191
a62dd253
SL
192/*
193 * Routing table ioctl interface.
194 */
195rtioctl(cmd, data)
196 int cmd;
197 caddr_t data;
198{
199
200 if (cmd != SIOCADDRT && cmd != SIOCDELRT)
201 return (EINVAL);
202 if (!suser())
203 return (u.u_error);
204 return (rtrequest(cmd, (struct rtentry *)data));
205}
206
9d03c806 207/*
c124e997 208 * Carry out a request to change the routing table. Called by
a9ea8834
SL
209 * interfaces at boot time to make their ``local routes'' known,
210 * for ioctl's, and as the result of routing redirects.
9d03c806 211 */
a13c006d 212rtrequest(req, entry)
9d03c806 213 int req;
a13c006d 214 register struct rtentry *entry;
9d03c806 215{
9d03c806 216 register struct mbuf *m, **mprev;
92c26501 217 struct mbuf **mfirst;
a13c006d 218 register struct rtentry *rt;
9d03c806 219 struct afhash h;
0b33b6b5 220 int s, error = 0, (*match)();
e8de4b5e 221 u_int af;
0b33b6b5 222 u_long hash;
cba69651 223 struct ifaddr *ifa;
ae674e00 224 struct ifaddr *ifa_ifwithdstaddr();
9d03c806 225
a13c006d 226 af = entry->rt_dst.sa_family;
e65dcd4c
SL
227 if (af >= AF_MAX)
228 return (EAFNOSUPPORT);
a13c006d
BJ
229 (*afswitch[af].af_hash)(&entry->rt_dst, &h);
230 if (entry->rt_flags & RTF_HOST) {
d7887ae9 231 hash = h.afh_hosthash;
b50b85d9 232 mprev = &rthost[RTHASHMOD(hash)];
d7887ae9
BJ
233 } else {
234 hash = h.afh_nethash;
b50b85d9 235 mprev = &rtnet[RTHASHMOD(hash)];
d7887ae9
BJ
236 }
237 match = afswitch[af].af_netmatch;
c124e997 238 s = splimp();
92c26501 239 for (mfirst = mprev; m = *mprev; mprev = &m->m_next) {
9d03c806 240 rt = mtod(m, struct rtentry *);
fc74f0c9 241 if (rt->rt_hash != hash)
9d03c806 242 continue;
a13c006d 243 if (entry->rt_flags & RTF_HOST) {
a13c006d 244 if (!equal(&rt->rt_dst, &entry->rt_dst))
9d03c806
SL
245 continue;
246 } else {
a13c006d
BJ
247 if (rt->rt_dst.sa_family != entry->rt_dst.sa_family ||
248 (*match)(&rt->rt_dst, &entry->rt_dst) == 0)
9d03c806
SL
249 continue;
250 }
a13c006d 251 if (equal(&rt->rt_gateway, &entry->rt_gateway))
d7887ae9 252 break;
c124e997 253 }
9d03c806
SL
254 switch (req) {
255
256 case SIOCDELRT:
d7887ae9
BJ
257 if (m == 0) {
258 error = ESRCH;
259 goto bad;
260 }
a13c006d 261 *mprev = m->m_next;
d7887ae9 262 if (rt->rt_refcnt > 0) {
a13c006d
BJ
263 rt->rt_flags &= ~RTF_UP;
264 rttrash++;
265 m->m_next = 0;
266 } else
267 (void) m_free(m);
9d03c806
SL
268 break;
269
270 case SIOCADDRT:
a13c006d 271 if (m) {
d7887ae9
BJ
272 error = EEXIST;
273 goto bad;
274 }
92c26501
MK
275 if ((entry->rt_flags & RTF_GATEWAY) == 0) {
276 /*
277 * If we are adding a route to an interface,
278 * and the interface is a pt to pt link
279 * we should search for the destination
280 * as our clue to the interface. Otherwise
281 * we can use the local address.
282 */
a25c073d 283 ifa = 0;
ae674e00
KS
284 if (entry->rt_flags & RTF_HOST)
285 ifa = ifa_ifwithdstaddr(&entry->rt_dst);
a25c073d 286 if (ifa == 0)
ae674e00
KS
287 ifa = ifa_ifwithaddr(&entry->rt_gateway);
288 } else {
92c26501
MK
289 /*
290 * If we are adding a route to a remote net
291 * or host, the gateway may still be on the
292 * other end of a pt to pt link.
293 */
ae674e00
KS
294 ifa = ifa_ifwithdstaddr(&entry->rt_gateway);
295 }
cba69651
MK
296 if (ifa == 0) {
297 ifa = ifa_ifwithnet(&entry->rt_gateway);
298 if (ifa == 0) {
a13c006d
BJ
299 error = ENETUNREACH;
300 goto bad;
301 }
302 }
cce93e4b 303 m = m_get(M_DONTWAIT, MT_RTABLE);
c124e997
SL
304 if (m == 0) {
305 error = ENOBUFS;
d7887ae9 306 goto bad;
c124e997 307 }
92c26501
MK
308 m->m_next = *mfirst;
309 *mfirst = m;
9d03c806 310 m->m_off = MMINOFF;
f6311fb6 311 m->m_len = sizeof (struct rtentry);
9d03c806 312 rt = mtod(m, struct rtentry *);
d7887ae9 313 rt->rt_hash = hash;
a13c006d
BJ
314 rt->rt_dst = entry->rt_dst;
315 rt->rt_gateway = entry->rt_gateway;
92c26501
MK
316 rt->rt_flags = RTF_UP |
317 (entry->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_DYNAMIC));
fc74f0c9 318 rt->rt_refcnt = 0;
a13c006d 319 rt->rt_use = 0;
cba69651 320 rt->rt_ifp = ifa->ifa_ifp;
9d03c806
SL
321 break;
322 }
c124e997
SL
323bad:
324 splx(s);
325 return (error);
9d03c806 326}
f6311fb6
SL
327
328/*
329 * Set up a routing table entry, normally
330 * for an interface.
331 */
2a63f7ba 332rtinit(dst, gateway, cmd, flags)
f6311fb6 333 struct sockaddr *dst, *gateway;
2a63f7ba 334 int cmd, flags;
f6311fb6
SL
335{
336 struct rtentry route;
f6311fb6 337
a13c006d 338 bzero((caddr_t)&route, sizeof (route));
f6311fb6
SL
339 route.rt_dst = *dst;
340 route.rt_gateway = *gateway;
341 route.rt_flags = flags;
0c33c832 342 (void) rtrequest(cmd, &route);
f6311fb6 343}