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