Commit | Line | Data |
---|---|---|
0b33b6b5 | 1 | /* route.c 6.2 83/10/20 */ |
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; |
0b33b6b5 | 28 | register u_long 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; |
0b33b6b5 | 189 | int s, error = 0, (*match)(); |
e8de4b5e | 190 | u_int af; |
0b33b6b5 | 191 | u_long hash; |
a13c006d | 192 | struct ifnet *ifp; |
9d03c806 | 193 | |
a13c006d | 194 | af = entry->rt_dst.sa_family; |
e65dcd4c SL |
195 | if (af >= AF_MAX) |
196 | return (EAFNOSUPPORT); | |
a13c006d BJ |
197 | (*afswitch[af].af_hash)(&entry->rt_dst, &h); |
198 | if (entry->rt_flags & RTF_HOST) { | |
d7887ae9 BJ |
199 | hash = h.afh_hosthash; |
200 | mprev = &rthost[hash % RTHASHSIZ]; | |
201 | } else { | |
202 | hash = h.afh_nethash; | |
203 | mprev = &rtnet[hash % RTHASHSIZ]; | |
204 | } | |
205 | match = afswitch[af].af_netmatch; | |
c124e997 | 206 | s = splimp(); |
9d03c806 SL |
207 | for (; m = *mprev; mprev = &m->m_next) { |
208 | rt = mtod(m, struct rtentry *); | |
fc74f0c9 | 209 | if (rt->rt_hash != hash) |
9d03c806 | 210 | continue; |
a13c006d BJ |
211 | if (entry->rt_flags & RTF_HOST) { |
212 | #define equal(a1, a2) \ | |
213 | (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0) | |
214 | if (!equal(&rt->rt_dst, &entry->rt_dst)) | |
9d03c806 SL |
215 | continue; |
216 | } else { | |
a13c006d BJ |
217 | if (rt->rt_dst.sa_family != entry->rt_dst.sa_family || |
218 | (*match)(&rt->rt_dst, &entry->rt_dst) == 0) | |
9d03c806 SL |
219 | continue; |
220 | } | |
a13c006d | 221 | if (equal(&rt->rt_gateway, &entry->rt_gateway)) |
d7887ae9 | 222 | break; |
c124e997 | 223 | } |
9d03c806 SL |
224 | switch (req) { |
225 | ||
226 | case SIOCDELRT: | |
d7887ae9 BJ |
227 | if (m == 0) { |
228 | error = ESRCH; | |
229 | goto bad; | |
230 | } | |
a13c006d | 231 | *mprev = m->m_next; |
d7887ae9 | 232 | if (rt->rt_refcnt > 0) { |
a13c006d BJ |
233 | rt->rt_flags &= ~RTF_UP; |
234 | rttrash++; | |
235 | m->m_next = 0; | |
236 | } else | |
237 | (void) m_free(m); | |
9d03c806 SL |
238 | break; |
239 | ||
240 | case SIOCADDRT: | |
a13c006d | 241 | if (m) { |
d7887ae9 BJ |
242 | error = EEXIST; |
243 | goto bad; | |
244 | } | |
a13c006d BJ |
245 | ifp = if_ifwithaddr(&entry->rt_gateway); |
246 | if (ifp == 0) { | |
247 | ifp = if_ifwithnet(&entry->rt_gateway); | |
248 | if (ifp == 0) { | |
249 | error = ENETUNREACH; | |
250 | goto bad; | |
251 | } | |
252 | } | |
cce93e4b | 253 | m = m_get(M_DONTWAIT, MT_RTABLE); |
c124e997 SL |
254 | if (m == 0) { |
255 | error = ENOBUFS; | |
d7887ae9 | 256 | goto bad; |
c124e997 | 257 | } |
a13c006d | 258 | *mprev = m; |
9d03c806 | 259 | m->m_off = MMINOFF; |
f6311fb6 | 260 | m->m_len = sizeof (struct rtentry); |
9d03c806 | 261 | rt = mtod(m, struct rtentry *); |
d7887ae9 | 262 | rt->rt_hash = hash; |
a13c006d BJ |
263 | rt->rt_dst = entry->rt_dst; |
264 | rt->rt_gateway = entry->rt_gateway; | |
265 | rt->rt_flags = | |
266 | RTF_UP | (entry->rt_flags & (RTF_HOST|RTF_GATEWAY)); | |
fc74f0c9 | 267 | rt->rt_refcnt = 0; |
a13c006d BJ |
268 | rt->rt_use = 0; |
269 | rt->rt_ifp = ifp; | |
9d03c806 SL |
270 | break; |
271 | } | |
c124e997 SL |
272 | bad: |
273 | splx(s); | |
274 | return (error); | |
9d03c806 | 275 | } |
f6311fb6 SL |
276 | |
277 | /* | |
278 | * Set up a routing table entry, normally | |
279 | * for an interface. | |
280 | */ | |
281 | rtinit(dst, gateway, flags) | |
282 | struct sockaddr *dst, *gateway; | |
283 | int flags; | |
284 | { | |
285 | struct rtentry route; | |
0c33c832 | 286 | int cmd; |
f6311fb6 | 287 | |
0c33c832 SL |
288 | if (flags == -1) { |
289 | cmd = (int)SIOCDELRT; | |
290 | flags = 0; | |
291 | } else { | |
292 | cmd = (int)SIOCADDRT; | |
293 | } | |
a13c006d | 294 | bzero((caddr_t)&route, sizeof (route)); |
f6311fb6 SL |
295 | route.rt_dst = *dst; |
296 | route.rt_gateway = *gateway; | |
297 | route.rt_flags = flags; | |
0c33c832 | 298 | (void) rtrequest(cmd, &route); |
f6311fb6 | 299 | } |