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