xref: /original-bsd/sys/net/route.c (revision 7a4e9f34)
1 /*	route.c	4.8	82/05/11	*/
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 /*
17  * Packet routing routines.
18  */
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 
76 	if (rt == 0)
77 		panic("freeroute");
78 	rt->rt_refcnt--;
79 	/* on refcnt == 0 reclaim? notify someone? */
80 }
81 
82 #define	equal(a1, a2) \
83 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
84 /*
85  * Carry out a request to change the routing table.  Called by
86  * interfaces at boot time to make their ``local routes'' known
87  * and for ioctl's.
88  */
89 rtrequest(req, new)
90 	int req;
91 	register struct rtentry *new;
92 {
93 	register struct rtentry *rt;
94 	register struct mbuf *m, **mprev;
95 	register int hash, (*match)();
96 	register struct sockaddr *sa = &new->rt_dst;
97 	register struct sockaddr *gate = &new->rt_gateway;
98 	struct afhash h;
99 	struct mbuf **oldmprev;
100 	int af = sa->sa_family, doinghost, s, error = 0;
101 
102 COUNT(RTREQUEST);
103 	if (af >= AF_MAX)
104 		return (EAFNOSUPPORT);
105 	(*afswitch[af].af_hash)(sa, &h);
106 	hash = h.afh_hosthash;
107 	mprev = &rthost[hash % RTHASHSIZ];
108 	doinghost = 1;
109 	s = splimp();
110 again:
111 	for (; m = *mprev; mprev = &m->m_next) {
112 		rt = mtod(m, struct rtentry *);
113 		if (rt->rt_hash != hash)
114 			continue;
115 		if (doinghost) {
116 			if (!equal(&rt->rt_dst, sa))
117 				continue;
118 		} else {
119 			if (rt->rt_dst.sa_family != sa->sa_family ||
120 			    (*match)(&rt->rt_dst, sa) == 0)
121 				continue;
122 		}
123 		/* require full match on deletions */
124 		if (req == SIOCDELRT && !equal(&rt->rt_gateway, gate))
125 			continue;
126 		/* don't keep multiple identical entries */
127 		if (req == SIOCADDRT && equal(&rt->rt_gateway, gate)) {
128 			error = EEXIST;
129 			goto bad;
130 		}
131 		break;
132 	}
133 	if (m == 0 && doinghost) {
134 		hash = h.afh_nethash;
135 		oldmprev = mprev;
136 		mprev = &rtnet[hash % RTHASHSIZ];
137 		match = afswitch[af].af_netmatch;
138 		doinghost = 0;
139 		goto again;
140 	}
141 
142 	if (m == 0 && req != SIOCADDRT) {
143 		error = ESRCH;
144 		goto bad;
145 	}
146 found:
147 	switch (req) {
148 
149 	case SIOCDELRT:
150 		rt->rt_flags &= ~RTF_UP;
151 		if (rt->rt_refcnt > 0)	/* should we notify protocols? */
152 			error = EBUSY;
153 		else
154 			*mprev = m_free(m);
155 		break;
156 
157 	case SIOCCHGRT:
158 		rt->rt_flags = new->rt_flags;
159 		if (rt->rt_refcnt > 0)
160 			error = EBUSY;
161 		else if (!equal(&rt->rt_gateway, gate))
162 			goto newneighbor;
163 		break;
164 
165 	case SIOCADDRT:
166 		m = m_get(M_DONTWAIT);
167 		if (m == 0) {
168 			error = ENOBUFS;
169 			break;
170 		}
171 		m->m_off = MMINOFF;
172 		m->m_len = sizeof (struct rtentry);
173 		rt = mtod(m, struct rtentry *);
174 		*rt = *new;
175 		if (new->rt_flags & RTF_HOST) {
176 			rt->rt_hash = h.afh_hosthash;
177 			*oldmprev = m;
178 		} else {
179 			rt->rt_hash = h.afh_nethash;
180 			*mprev = m;
181 		}
182 		rt->rt_use = 0;
183 		rt->rt_refcnt = 0;
184 newneighbor:
185 		rt->rt_ifp = if_ifwithnet(gate);
186 		if (rt->rt_ifp == 0)
187 			rt->rt_flags &= ~RTF_UP;
188 		break;
189 	}
190 bad:
191 	splx(s);
192 	return (error);
193 }
194 
195 /*
196  * Set up a routing table entry, normally
197  * for an interface.
198  */
199 rtinit(dst, gateway, flags)
200 	struct sockaddr *dst, *gateway;
201 	int flags;
202 {
203 	struct rtentry route;
204 	struct route ro;
205 
206 	route.rt_dst = *dst;
207 	route.rt_gateway = *gateway;
208 	route.rt_flags = flags;
209 	route.rt_use = 0;
210 	(void) rtrequest(SIOCADDRT, &route);
211 	ro.ro_rt = 0;
212 	ro.ro_dst = *dst;
213 	rtalloc(&ro);
214 }
215