xref: /original-bsd/sys/net/route.c (revision b64b0d54)
1 /*	route.c	4.18	83/03/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/ioctl.h"
9 #include "../h/errno.h"
10 
11 #include "../net/if.h"
12 #include "../net/af.h"
13 #include "../net/route.h"
14 
15 int	rttrash;		/* routes not in table but not freed */
16 /*
17  * Packet routing routines.
18  */
19 rtalloc(ro)
20 	register struct route *ro;
21 {
22 	register struct rtentry *rt, *rtmin;
23 	register struct mbuf *m;
24 	register unsigned hash;
25 	register int (*match)();
26 	struct afhash h;
27 	struct sockaddr *dst = &ro->ro_dst;
28 	int af = dst->sa_family;
29 
30 	if (ro->ro_rt && ro->ro_rt->rt_ifp)			/* XXX */
31 		return;
32 	if (af >= AF_MAX)
33 		return;
34 	(*afswitch[af].af_hash)(dst, &h);
35 	rtmin = 0, hash = h.afh_hosthash;
36 	for (m = rthost[hash % RTHASHSIZ]; m; m = m->m_next) {
37 		rt = mtod(m, struct rtentry *);
38 		if (rt->rt_hash != hash)
39 			continue;
40 		if ((rt->rt_flags & RTF_UP) == 0 ||
41 		    (rt->rt_ifp->if_flags & IFF_UP) == 0)
42 			continue;
43 		if (bcmp((caddr_t)&rt->rt_dst, (caddr_t)dst, sizeof (*dst)))
44 			continue;
45 		if (rtmin == 0 || rt->rt_use < rtmin->rt_use)
46 			rtmin = rt;
47 	}
48 	if (rtmin)
49 		goto found;
50 
51 	hash = h.afh_nethash;
52 	match = afswitch[af].af_netmatch;
53 	for (m = rtnet[hash % RTHASHSIZ]; m; m = m->m_next) {
54 		rt = mtod(m, struct rtentry *);
55 		if (rt->rt_hash != hash)
56 			continue;
57 		if ((rt->rt_flags & RTF_UP) == 0 ||
58 		    (rt->rt_ifp->if_flags & IFF_UP) == 0)
59 			continue;
60 		if (rt->rt_dst.sa_family != af || !(*match)(&rt->rt_dst, dst))
61 			continue;
62 		if (rtmin == 0 || rt->rt_use < rtmin->rt_use)
63 			rtmin = rt;
64 	}
65 found:
66 	ro->ro_rt = rtmin;
67 	if (rtmin)
68 		rtmin->rt_refcnt++;
69 }
70 
71 rtfree(rt)
72 	register struct rtentry *rt;
73 {
74 
75 	if (rt == 0)
76 		panic("rtfree");
77 	rt->rt_refcnt--;
78 	if (rt->rt_refcnt == 0 && (rt->rt_flags&RTF_UP) == 0) {
79 		rttrash--;
80 		(void) m_free(dtom(rt));
81 	}
82 }
83 
84 /*
85  * Force a routing table entry to the specified
86  * destination to go through the given gateway.
87  * Normally called as a result of a routing redirect
88  * message from the network layer.
89  *
90  * N.B.: must be called at splnet or higher
91  *
92  * Should notify all parties with a reference to
93  * the route that it's changed (so, for instance,
94  * round trip time estimates may be recalculated),
95  * but we have no back pointers at the moment.
96  */
97 rtredirect(dst, gateway)
98 	struct sockaddr *dst, *gateway;
99 {
100 	struct route ro;
101 	register struct rtentry *rt;
102 
103 	/* verify the gateway is directly reachable */
104 	if (if_ifwithnet(gateway) == 0)
105 		return;
106 	ro.ro_dst = *dst;
107 	ro.ro_rt = NULL;
108 	rtalloc(&ro);
109 	rt = ro.ro_rt;
110 	/*
111 	 * Don't listen to the redirect if it's
112 	 * for a route to an interface.
113 	 *
114 	 * Should probably create a new entry when
115 	 * the lookup fails.  This will be necessary
116 	 * when wildcard routes are added.
117 	 */
118 	if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) == 0)
119 		return;
120 	rt->rt_gateway = *gateway;
121 	rtfree(rt);
122 }
123 
124 /*
125  * Carry out a request to change the routing table.  Called by
126  * interfaces at boot time to make their ``local routes'' known
127  * and for ioctl's.
128  */
129 rtrequest(req, entry)
130 	int req;
131 	register struct rtentry *entry;
132 {
133 	register struct mbuf *m, **mprev;
134 	register struct rtentry *rt;
135 	struct afhash h;
136 	int af, s, error = 0, hash, (*match)();
137 	struct ifnet *ifp;
138 
139 	af = entry->rt_dst.sa_family;
140 	if (af >= AF_MAX)
141 		return (EAFNOSUPPORT);
142 	(*afswitch[af].af_hash)(&entry->rt_dst, &h);
143 	if (entry->rt_flags & RTF_HOST) {
144 		hash = h.afh_hosthash;
145 		mprev = &rthost[hash % RTHASHSIZ];
146 	} else {
147 		hash = h.afh_nethash;
148 		mprev = &rtnet[hash % RTHASHSIZ];
149 	}
150 	match = afswitch[af].af_netmatch;
151 	s = splimp();
152 	for (; m = *mprev; mprev = &m->m_next) {
153 		rt = mtod(m, struct rtentry *);
154 		if (rt->rt_hash != hash)
155 			continue;
156 		if (entry->rt_flags & RTF_HOST) {
157 #define	equal(a1, a2) \
158 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
159 			if (!equal(&rt->rt_dst, &entry->rt_dst))
160 				continue;
161 		} else {
162 			if (rt->rt_dst.sa_family != entry->rt_dst.sa_family ||
163 			    (*match)(&rt->rt_dst, &entry->rt_dst) == 0)
164 				continue;
165 		}
166 		if (equal(&rt->rt_gateway, &entry->rt_gateway))
167 			break;
168 	}
169 	switch (req) {
170 
171 	case SIOCDELRT:
172 		if (m == 0) {
173 			error = ESRCH;
174 			goto bad;
175 		}
176 		*mprev = m->m_next;
177 		if (rt->rt_refcnt > 0) {
178 			rt->rt_flags &= ~RTF_UP;
179 			rttrash++;
180 			m->m_next = 0;
181 		} else
182 			(void) m_free(m);
183 		break;
184 
185 	case SIOCADDRT:
186 		if (m) {
187 			error = EEXIST;
188 			goto bad;
189 		}
190 		ifp = if_ifwithaddr(&entry->rt_gateway);
191 		if (ifp == 0) {
192 			ifp = if_ifwithnet(&entry->rt_gateway);
193 			if (ifp == 0) {
194 				error = ENETUNREACH;
195 				goto bad;
196 			}
197 		}
198 		m = m_get(M_DONTWAIT, MT_RTABLE);
199 		if (m == 0) {
200 			error = ENOBUFS;
201 			goto bad;
202 		}
203 		*mprev = m;
204 		m->m_off = MMINOFF;
205 		m->m_len = sizeof (struct rtentry);
206 		rt = mtod(m, struct rtentry *);
207 		rt->rt_hash = hash;
208 		rt->rt_dst = entry->rt_dst;
209 		rt->rt_gateway = entry->rt_gateway;
210 		rt->rt_flags =
211 		    RTF_UP | (entry->rt_flags & (RTF_HOST|RTF_GATEWAY));
212 		rt->rt_refcnt = 0;
213 		rt->rt_use = 0;
214 		rt->rt_ifp = ifp;
215 		break;
216 	}
217 bad:
218 	splx(s);
219 	return (error);
220 }
221 
222 /*
223  * Set up a routing table entry, normally
224  * for an interface.
225  */
226 rtinit(dst, gateway, flags)
227 	struct sockaddr *dst, *gateway;
228 	int flags;
229 {
230 	struct rtentry route;
231 	int cmd;
232 
233 	if (flags == -1) {
234 		cmd = (int)SIOCDELRT;
235 		flags = 0;
236 	} else {
237 		cmd = (int)SIOCADDRT;
238 	}
239 	bzero((caddr_t)&route, sizeof (route));
240 	route.rt_dst = *dst;
241 	route.rt_gateway = *gateway;
242 	route.rt_flags = flags;
243 	(void) rtrequest(cmd, &route);
244 }
245