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