xref: /csrg-svn/sys/net/route.c (revision 7156)
1 /*	route.c	4.10	82/06/12	*/
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 "../net/in.h"
10 #include "../net/in_systm.h"
11 #include "../net/if.h"
12 #include "../net/af.h"
13 #include "../net/route.h"
14 #include <errno.h>
15 
16 int	rttrash;		/* routes not in table but not freed */
17 /*
18  * Packet routing routines.
19  */
20 rtalloc(ro)
21 	register struct route *ro;
22 {
23 	register struct rtentry *rt, *rtmin;
24 	register struct mbuf *m;
25 	register int hash, (*match)();
26 	struct afhash h;
27 	struct sockaddr *dst = &ro->ro_dst;
28 	int af = dst->sa_family;
29 
30 COUNT(RTALLOC);
31 	if (ro->ro_rt && ro->ro_rt->rt_ifp)			/* XXX */
32 		return;
33 	if (af >= AF_MAX)
34 		return;
35 	(*afswitch[af].af_hash)(dst, &h);
36 	rtmin = 0, hash = h.afh_hosthash;
37 	for (m = rthost[hash % RTHASHSIZ]; m; m = m->m_next) {
38 		rt = mtod(m, struct rtentry *);
39 		if (rt->rt_hash != hash)
40 			continue;
41 		if ((rt->rt_flags & RTF_UP) == 0 ||
42 		    (rt->rt_ifp->if_flags & IFF_UP) == 0)
43 			continue;
44 		if (bcmp((caddr_t)&rt->rt_dst, (caddr_t)dst, sizeof (*dst)))
45 			continue;
46 		if (rtmin == 0 || rt->rt_use < rtmin->rt_use)
47 			rtmin = rt;
48 	}
49 	if (rtmin)
50 		goto found;
51 
52 	hash = h.afh_nethash;
53 	match = afswitch[af].af_netmatch;
54 	for (m = rtnet[hash % RTHASHSIZ]; m; m = m->m_next) {
55 		rt = mtod(m, struct rtentry *);
56 		if (rt->rt_hash != hash)
57 			continue;
58 		if ((rt->rt_flags & RTF_UP) == 0 ||
59 		    (rt->rt_ifp->if_flags & IFF_UP) == 0)
60 			continue;
61 		if (rt->rt_dst.sa_family != af || !(*match)(&rt->rt_dst, dst))
62 			continue;
63 		if (rtmin == 0 || rt->rt_use < rtmin->rt_use)
64 			rtmin = rt;
65 	}
66 found:
67 	ro->ro_rt = rtmin;
68 	if (rtmin)
69 		rtmin->rt_refcnt++;
70 }
71 
72 rtfree(rt)
73 	register struct rtentry *rt;
74 {
75 	register struct mbuf **mp;
76 
77 	if (rt == 0)
78 		panic("freeroute");
79 	rt->rt_refcnt--;
80 	if (rt->rt_refcnt == 0 && (rt->rt_flags&RTF_UP) == 0) {
81 		rttrash--;
82 		(void) m_free(dtom(rt));
83 	}
84 }
85 
86 /*
87  * Carry out a request to change the routing table.  Called by
88  * interfaces at boot time to make their ``local routes'' known
89  * and for ioctl's.
90  */
91 rtrequest(req, entry)
92 	int req;
93 	register struct rtentry *entry;
94 {
95 	register struct mbuf *m, **mprev;
96 	register struct rtentry *rt;
97 	struct afhash h;
98 	int af, s, error = 0, hash, (*match)();
99 	struct ifnet *ifp;
100 
101 COUNT(RTREQUEST);
102 	af = entry->rt_dst.sa_family;
103 	if (af >= AF_MAX)
104 		return (EAFNOSUPPORT);
105 	(*afswitch[af].af_hash)(&entry->rt_dst, &h);
106 	if (entry->rt_flags & RTF_HOST) {
107 		hash = h.afh_hosthash;
108 		mprev = &rthost[hash % RTHASHSIZ];
109 	} else {
110 		hash = h.afh_nethash;
111 		mprev = &rtnet[hash % RTHASHSIZ];
112 	}
113 	match = afswitch[af].af_netmatch;
114 	s = splimp();
115 	for (; m = *mprev; mprev = &m->m_next) {
116 		rt = mtod(m, struct rtentry *);
117 		if (rt->rt_hash != hash)
118 			continue;
119 		if (entry->rt_flags & RTF_HOST) {
120 #define	equal(a1, a2) \
121 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
122 			if (!equal(&rt->rt_dst, &entry->rt_dst))
123 				continue;
124 		} else {
125 			if (rt->rt_dst.sa_family != entry->rt_dst.sa_family ||
126 			    (*match)(&rt->rt_dst, &entry->rt_dst) == 0)
127 				continue;
128 		}
129 		if (equal(&rt->rt_gateway, &entry->rt_gateway))
130 			break;
131 	}
132 	switch (req) {
133 
134 	case SIOCDELRT:
135 		if (m == 0) {
136 			error = ESRCH;
137 			goto bad;
138 		}
139 		*mprev = m->m_next;
140 		if (rt->rt_refcnt > 0) {
141 			rt->rt_flags &= ~RTF_UP;
142 			rttrash++;
143 			m->m_next = 0;
144 		} else
145 			(void) m_free(m);
146 		break;
147 
148 	case SIOCADDRT:
149 		if (m) {
150 			error = EEXIST;
151 			goto bad;
152 		}
153 		ifp = if_ifwithaddr(&entry->rt_gateway);
154 		if (ifp == 0) {
155 			ifp = if_ifwithnet(&entry->rt_gateway);
156 			if (ifp == 0) {
157 				error = ENETUNREACH;
158 				goto bad;
159 			}
160 		}
161 		m = m_get(M_DONTWAIT);
162 		if (m == 0) {
163 			error = ENOBUFS;
164 			goto bad;
165 		}
166 		*mprev = m;
167 		m->m_off = MMINOFF;
168 		m->m_len = sizeof (struct rtentry);
169 		rt = mtod(m, struct rtentry *);
170 		rt->rt_hash = hash;
171 		rt->rt_dst = entry->rt_dst;
172 		rt->rt_gateway = entry->rt_gateway;
173 		rt->rt_flags =
174 		    RTF_UP | (entry->rt_flags & (RTF_HOST|RTF_GATEWAY));
175 		rt->rt_refcnt = 0;
176 		rt->rt_use = 0;
177 		rt->rt_ifp = ifp;
178 		break;
179 	}
180 bad:
181 	splx(s);
182 	return (error);
183 }
184 
185 /*
186  * Set up a routing table entry, normally
187  * for an interface.
188  */
189 rtinit(dst, gateway, flags)
190 	struct sockaddr *dst, *gateway;
191 	int flags;
192 {
193 	struct rtentry route;
194 	struct route ro;
195 
196 	bzero((caddr_t)&route, sizeof (route));
197 	route.rt_dst = *dst;
198 	route.rt_gateway = *gateway;
199 	route.rt_flags = flags;
200 	(void) rtrequest(SIOCADDRT, &route);
201 }
202