xref: /csrg-svn/sys/net/route.c (revision 7243)
1 /*	route.c	4.11	82/06/20	*/
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 	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 	register struct mbuf **mp;
75 
76 	if (rt == 0)
77 		panic("freeroute");
78 	rt->rt_refcnt--;
79 	if (rt->rt_refcnt == 0 && (rt->rt_flags&RTF_UP) == 0) {
80 		rttrash--;
81 		(void) m_free(dtom(rt));
82 	}
83 }
84 
85 /*
86  * Carry out a request to change the routing table.  Called by
87  * interfaces at boot time to make their ``local routes'' known
88  * and for ioctl's.
89  */
90 rtrequest(req, entry)
91 	int req;
92 	register struct rtentry *entry;
93 {
94 	register struct mbuf *m, **mprev;
95 	register struct rtentry *rt;
96 	struct afhash h;
97 	int af, s, error = 0, hash, (*match)();
98 	struct ifnet *ifp;
99 
100 	af = entry->rt_dst.sa_family;
101 	if (af >= AF_MAX)
102 		return (EAFNOSUPPORT);
103 	(*afswitch[af].af_hash)(&entry->rt_dst, &h);
104 	if (entry->rt_flags & RTF_HOST) {
105 		hash = h.afh_hosthash;
106 		mprev = &rthost[hash % RTHASHSIZ];
107 	} else {
108 		hash = h.afh_nethash;
109 		mprev = &rtnet[hash % RTHASHSIZ];
110 	}
111 	match = afswitch[af].af_netmatch;
112 	s = splimp();
113 	for (; m = *mprev; mprev = &m->m_next) {
114 		rt = mtod(m, struct rtentry *);
115 		if (rt->rt_hash != hash)
116 			continue;
117 		if (entry->rt_flags & RTF_HOST) {
118 #define	equal(a1, a2) \
119 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
120 			if (!equal(&rt->rt_dst, &entry->rt_dst))
121 				continue;
122 		} else {
123 			if (rt->rt_dst.sa_family != entry->rt_dst.sa_family ||
124 			    (*match)(&rt->rt_dst, &entry->rt_dst) == 0)
125 				continue;
126 		}
127 		if (equal(&rt->rt_gateway, &entry->rt_gateway))
128 			break;
129 	}
130 	switch (req) {
131 
132 	case SIOCDELRT:
133 		if (m == 0) {
134 			error = ESRCH;
135 			goto bad;
136 		}
137 		*mprev = m->m_next;
138 		if (rt->rt_refcnt > 0) {
139 			rt->rt_flags &= ~RTF_UP;
140 			rttrash++;
141 			m->m_next = 0;
142 		} else
143 			(void) m_free(m);
144 		break;
145 
146 	case SIOCADDRT:
147 		if (m) {
148 			error = EEXIST;
149 			goto bad;
150 		}
151 		ifp = if_ifwithaddr(&entry->rt_gateway);
152 		if (ifp == 0) {
153 			ifp = if_ifwithnet(&entry->rt_gateway);
154 			if (ifp == 0) {
155 				error = ENETUNREACH;
156 				goto bad;
157 			}
158 		}
159 		m = m_get(M_DONTWAIT);
160 		if (m == 0) {
161 			error = ENOBUFS;
162 			goto bad;
163 		}
164 		*mprev = m;
165 		m->m_off = MMINOFF;
166 		m->m_len = sizeof (struct rtentry);
167 		rt = mtod(m, struct rtentry *);
168 		rt->rt_hash = hash;
169 		rt->rt_dst = entry->rt_dst;
170 		rt->rt_gateway = entry->rt_gateway;
171 		rt->rt_flags =
172 		    RTF_UP | (entry->rt_flags & (RTF_HOST|RTF_GATEWAY));
173 		rt->rt_refcnt = 0;
174 		rt->rt_use = 0;
175 		rt->rt_ifp = ifp;
176 		break;
177 	}
178 bad:
179 	splx(s);
180 	return (error);
181 }
182 
183 /*
184  * Set up a routing table entry, normally
185  * for an interface.
186  */
187 rtinit(dst, gateway, flags)
188 	struct sockaddr *dst, *gateway;
189 	int flags;
190 {
191 	struct rtentry route;
192 	struct route ro;
193 
194 	bzero((caddr_t)&route, sizeof (route));
195 	route.rt_dst = *dst;
196 	route.rt_gateway = *gateway;
197 	route.rt_flags = flags;
198 	(void) rtrequest(SIOCADDRT, &route);
199 }
200