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