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