Commit | Line | Data |
---|---|---|
cb1c44c2 KM |
1 | /* |
2 | * Copyright (c) 1980 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 | * @(#)route.c 6.10 (Berkeley) %G% | |
7 | */ | |
9d03c806 | 8 | |
a0369dcf JB |
9 | #include "param.h" |
10 | #include "systm.h" | |
11 | #include "mbuf.h" | |
12 | #include "protosw.h" | |
13 | #include "socket.h" | |
14 | #include "dir.h" | |
15 | #include "user.h" | |
16 | #include "ioctl.h" | |
17 | #include "errno.h" | |
f4d55810 | 18 | |
a0369dcf JB |
19 | #include "if.h" |
20 | #include "af.h" | |
21 | #include "route.h" | |
9d03c806 | 22 | |
a13c006d | 23 | int rttrash; /* routes not in table but not freed */ |
a9ea8834 | 24 | struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ |
c50b0999 | 25 | int rthashsize = RTHASHSIZ; /* for netstat, etc. */ |
a9ea8834 | 26 | |
9d03c806 SL |
27 | /* |
28 | * Packet routing routines. | |
29 | */ | |
f6311fb6 | 30 | rtalloc(ro) |
9d03c806 SL |
31 | register struct route *ro; |
32 | { | |
a9ea8834 | 33 | register struct rtentry *rt; |
9d03c806 | 34 | register struct mbuf *m; |
0b33b6b5 | 35 | register u_long hash; |
9d03c806 | 36 | struct sockaddr *dst = &ro->ro_dst; |
c50b0999 | 37 | int (*match)(), doinghost, s; |
a9ea8834 | 38 | struct afhash h; |
e8de4b5e | 39 | u_int af = dst->sa_family; |
a9ea8834 | 40 | struct mbuf **table; |
9d03c806 | 41 | |
7eb1f827 MK |
42 | if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) |
43 | return; /* XXX */ | |
e65dcd4c SL |
44 | if (af >= AF_MAX) |
45 | return; | |
9d03c806 | 46 | (*afswitch[af].af_hash)(dst, &h); |
fc74f0c9 | 47 | match = afswitch[af].af_netmatch; |
a9ea8834 | 48 | hash = h.afh_hosthash, table = rthost, doinghost = 1; |
c50b0999 | 49 | s = splnet(); |
a9ea8834 | 50 | again: |
b50b85d9 | 51 | for (m = table[RTHASHMOD(hash)]; m; m = m->m_next) { |
fc74f0c9 SL |
52 | rt = mtod(m, struct rtentry *); |
53 | if (rt->rt_hash != hash) | |
54 | continue; | |
90137845 SL |
55 | if ((rt->rt_flags & RTF_UP) == 0 || |
56 | (rt->rt_ifp->if_flags & IFF_UP) == 0) | |
57 | continue; | |
a9ea8834 SL |
58 | if (doinghost) { |
59 | if (bcmp((caddr_t)&rt->rt_dst, (caddr_t)dst, | |
60 | sizeof (*dst))) | |
61 | continue; | |
62 | } else { | |
63 | if (rt->rt_dst.sa_family != af || | |
64 | !(*match)(&rt->rt_dst, dst)) | |
65 | continue; | |
66 | } | |
c50b0999 MK |
67 | rt->rt_refcnt++; |
68 | splx(s); | |
69 | if (dst == &wildcard) | |
70 | rtstat.rts_wildcard++; | |
71 | ro->ro_rt = rt; | |
72 | return; | |
9d03c806 | 73 | } |
c50b0999 | 74 | if (doinghost) { |
a9ea8834 SL |
75 | doinghost = 0; |
76 | hash = h.afh_nethash, table = rtnet; | |
77 | goto again; | |
78 | } | |
79 | /* | |
80 | * Check for wildcard gateway, by convention network 0. | |
81 | */ | |
c50b0999 | 82 | if (dst != &wildcard) { |
a9ea8834 SL |
83 | dst = &wildcard, hash = 0; |
84 | goto again; | |
85 | } | |
c50b0999 MK |
86 | splx(s); |
87 | rtstat.rts_unreach++; | |
9d03c806 SL |
88 | } |
89 | ||
f6311fb6 | 90 | rtfree(rt) |
c124e997 SL |
91 | register struct rtentry *rt; |
92 | { | |
f6311fb6 | 93 | |
c124e997 | 94 | if (rt == 0) |
a1edc12b | 95 | panic("rtfree"); |
c124e997 | 96 | rt->rt_refcnt--; |
a13c006d BJ |
97 | if (rt->rt_refcnt == 0 && (rt->rt_flags&RTF_UP) == 0) { |
98 | rttrash--; | |
99 | (void) m_free(dtom(rt)); | |
100 | } | |
c124e997 SL |
101 | } |
102 | ||
464f931f SL |
103 | /* |
104 | * Force a routing table entry to the specified | |
105 | * destination to go through the given gateway. | |
106 | * Normally called as a result of a routing redirect | |
107 | * message from the network layer. | |
108 | * | |
109 | * N.B.: must be called at splnet or higher | |
110 | * | |
111 | * Should notify all parties with a reference to | |
112 | * the route that it's changed (so, for instance, | |
a9ea8834 | 113 | * current round trip time estimates could be flushed), |
464f931f SL |
114 | * but we have no back pointers at the moment. |
115 | */ | |
7eb1f827 | 116 | rtredirect(dst, gateway, flags) |
464f931f | 117 | struct sockaddr *dst, *gateway; |
7eb1f827 | 118 | int flags; |
464f931f SL |
119 | { |
120 | struct route ro; | |
121 | register struct rtentry *rt; | |
122 | ||
123 | /* verify the gateway is directly reachable */ | |
cba69651 | 124 | if (ifa_ifwithnet(gateway) == 0) { |
a9ea8834 | 125 | rtstat.rts_badredirect++; |
464f931f | 126 | return; |
a9ea8834 | 127 | } |
464f931f | 128 | ro.ro_dst = *dst; |
a9ea8834 | 129 | ro.ro_rt = 0; |
464f931f SL |
130 | rtalloc(&ro); |
131 | rt = ro.ro_rt; | |
a9ea8834 | 132 | /* |
48afbdc5 SL |
133 | * Create a new entry if we just got back a wildcard entry |
134 | * or the the lookup failed. This is necessary for hosts | |
135 | * which use routing redirects generated by smart gateways | |
136 | * to dynamically build the routing tables. | |
a9ea8834 | 137 | */ |
48afbdc5 SL |
138 | if (rt && |
139 | (*afswitch[dst->sa_family].af_netmatch)(&wildcard, &rt->rt_dst)) { | |
140 | rtfree(rt); | |
141 | rt = 0; | |
142 | } | |
a9ea8834 | 143 | if (rt == 0) { |
66798f86 | 144 | rtinit(dst, gateway, (flags & RTF_HOST) | RTF_GATEWAY); |
a9ea8834 SL |
145 | rtstat.rts_dynamic++; |
146 | return; | |
147 | } | |
464f931f SL |
148 | /* |
149 | * Don't listen to the redirect if it's | |
150 | * for a route to an interface. | |
464f931f | 151 | */ |
a9ea8834 | 152 | if (rt->rt_flags & RTF_GATEWAY) { |
7eb1f827 MK |
153 | if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { |
154 | /* | |
66798f86 | 155 | * Changing from route to net => route to host. |
7eb1f827 MK |
156 | * Create new route, rather than smashing route to net. |
157 | */ | |
7eb1f827 | 158 | rtinit(dst, gateway, flags); |
7eb1f827 MK |
159 | } else { |
160 | /* | |
161 | * Smash the current notion of the gateway to | |
162 | * this destination. This is probably not right, | |
163 | * as it's conceivable a flurry of redirects could | |
164 | * cause the gateway value to fluctuate wildly during | |
165 | * dynamic routing reconfiguration. | |
166 | */ | |
167 | rt->rt_gateway = *gateway; | |
7eb1f827 | 168 | } |
66798f86 | 169 | rtstat.rts_newgateway++; |
a9ea8834 | 170 | } |
ff320836 | 171 | rtfree(rt); |
464f931f SL |
172 | } |
173 | ||
a62dd253 SL |
174 | /* |
175 | * Routing table ioctl interface. | |
176 | */ | |
177 | rtioctl(cmd, data) | |
178 | int cmd; | |
179 | caddr_t data; | |
180 | { | |
181 | ||
182 | if (cmd != SIOCADDRT && cmd != SIOCDELRT) | |
183 | return (EINVAL); | |
184 | if (!suser()) | |
185 | return (u.u_error); | |
186 | return (rtrequest(cmd, (struct rtentry *)data)); | |
187 | } | |
188 | ||
9d03c806 | 189 | /* |
c124e997 | 190 | * Carry out a request to change the routing table. Called by |
a9ea8834 SL |
191 | * interfaces at boot time to make their ``local routes'' known, |
192 | * for ioctl's, and as the result of routing redirects. | |
9d03c806 | 193 | */ |
a13c006d | 194 | rtrequest(req, entry) |
9d03c806 | 195 | int req; |
a13c006d | 196 | register struct rtentry *entry; |
9d03c806 | 197 | { |
9d03c806 | 198 | register struct mbuf *m, **mprev; |
a13c006d | 199 | register struct rtentry *rt; |
9d03c806 | 200 | struct afhash h; |
0b33b6b5 | 201 | int s, error = 0, (*match)(); |
e8de4b5e | 202 | u_int af; |
0b33b6b5 | 203 | u_long hash; |
cba69651 | 204 | struct ifaddr *ifa; |
9d03c806 | 205 | |
a13c006d | 206 | af = entry->rt_dst.sa_family; |
e65dcd4c SL |
207 | if (af >= AF_MAX) |
208 | return (EAFNOSUPPORT); | |
a13c006d BJ |
209 | (*afswitch[af].af_hash)(&entry->rt_dst, &h); |
210 | if (entry->rt_flags & RTF_HOST) { | |
d7887ae9 | 211 | hash = h.afh_hosthash; |
b50b85d9 | 212 | mprev = &rthost[RTHASHMOD(hash)]; |
d7887ae9 BJ |
213 | } else { |
214 | hash = h.afh_nethash; | |
b50b85d9 | 215 | mprev = &rtnet[RTHASHMOD(hash)]; |
d7887ae9 BJ |
216 | } |
217 | match = afswitch[af].af_netmatch; | |
c124e997 | 218 | s = splimp(); |
9d03c806 SL |
219 | for (; m = *mprev; mprev = &m->m_next) { |
220 | rt = mtod(m, struct rtentry *); | |
fc74f0c9 | 221 | if (rt->rt_hash != hash) |
9d03c806 | 222 | continue; |
a13c006d BJ |
223 | if (entry->rt_flags & RTF_HOST) { |
224 | #define equal(a1, a2) \ | |
225 | (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0) | |
226 | if (!equal(&rt->rt_dst, &entry->rt_dst)) | |
9d03c806 SL |
227 | continue; |
228 | } else { | |
a13c006d BJ |
229 | if (rt->rt_dst.sa_family != entry->rt_dst.sa_family || |
230 | (*match)(&rt->rt_dst, &entry->rt_dst) == 0) | |
9d03c806 SL |
231 | continue; |
232 | } | |
a13c006d | 233 | if (equal(&rt->rt_gateway, &entry->rt_gateway)) |
d7887ae9 | 234 | break; |
c124e997 | 235 | } |
9d03c806 SL |
236 | switch (req) { |
237 | ||
238 | case SIOCDELRT: | |
d7887ae9 BJ |
239 | if (m == 0) { |
240 | error = ESRCH; | |
241 | goto bad; | |
242 | } | |
a13c006d | 243 | *mprev = m->m_next; |
d7887ae9 | 244 | if (rt->rt_refcnt > 0) { |
a13c006d BJ |
245 | rt->rt_flags &= ~RTF_UP; |
246 | rttrash++; | |
247 | m->m_next = 0; | |
248 | } else | |
249 | (void) m_free(m); | |
9d03c806 SL |
250 | break; |
251 | ||
252 | case SIOCADDRT: | |
a13c006d | 253 | if (m) { |
d7887ae9 BJ |
254 | error = EEXIST; |
255 | goto bad; | |
256 | } | |
cba69651 MK |
257 | ifa = ifa_ifwithaddr(&entry->rt_gateway); |
258 | if (ifa == 0) { | |
259 | ifa = ifa_ifwithnet(&entry->rt_gateway); | |
260 | if (ifa == 0) { | |
a13c006d BJ |
261 | error = ENETUNREACH; |
262 | goto bad; | |
263 | } | |
264 | } | |
cce93e4b | 265 | m = m_get(M_DONTWAIT, MT_RTABLE); |
c124e997 SL |
266 | if (m == 0) { |
267 | error = ENOBUFS; | |
d7887ae9 | 268 | goto bad; |
c124e997 | 269 | } |
a13c006d | 270 | *mprev = m; |
9d03c806 | 271 | m->m_off = MMINOFF; |
f6311fb6 | 272 | m->m_len = sizeof (struct rtentry); |
9d03c806 | 273 | rt = mtod(m, struct rtentry *); |
d7887ae9 | 274 | rt->rt_hash = hash; |
a13c006d BJ |
275 | rt->rt_dst = entry->rt_dst; |
276 | rt->rt_gateway = entry->rt_gateway; | |
277 | rt->rt_flags = | |
278 | RTF_UP | (entry->rt_flags & (RTF_HOST|RTF_GATEWAY)); | |
fc74f0c9 | 279 | rt->rt_refcnt = 0; |
a13c006d | 280 | rt->rt_use = 0; |
cba69651 | 281 | rt->rt_ifp = ifa->ifa_ifp; |
9d03c806 SL |
282 | break; |
283 | } | |
c124e997 SL |
284 | bad: |
285 | splx(s); | |
286 | return (error); | |
9d03c806 | 287 | } |
f6311fb6 SL |
288 | |
289 | /* | |
290 | * Set up a routing table entry, normally | |
291 | * for an interface. | |
292 | */ | |
293 | rtinit(dst, gateway, flags) | |
294 | struct sockaddr *dst, *gateway; | |
295 | int flags; | |
296 | { | |
297 | struct rtentry route; | |
0c33c832 | 298 | int cmd; |
f6311fb6 | 299 | |
0c33c832 SL |
300 | if (flags == -1) { |
301 | cmd = (int)SIOCDELRT; | |
302 | flags = 0; | |
303 | } else { | |
304 | cmd = (int)SIOCADDRT; | |
305 | } | |
a13c006d | 306 | bzero((caddr_t)&route, sizeof (route)); |
f6311fb6 SL |
307 | route.rt_dst = *dst; |
308 | route.rt_gateway = *gateway; | |
309 | route.rt_flags = flags; | |
0c33c832 | 310 | (void) rtrequest(cmd, &route); |
f6311fb6 | 311 | } |