xref: /original-bsd/sys/net/route.c (revision 9c59a687)
1 /*	route.c	4.3	82/03/29	*/
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 /*
21  * With much ado about nothing...
22  * route the cars that climb halfway to the stars...
23  */
24 allocroute(ro)
25 	register struct route *ro;
26 {
27 	register struct rtentry *rt, *rtmin;
28 	register struct mbuf *m;
29 	register int key;
30 	struct afhash h;
31 	struct sockaddr *dst = &ro->ro_dst;
32 	int af = dst->sa_family, doinghost;
33 
34 COUNT(ALLOCROUTE);
35 	if (ro->ro_rt && ro->ro_rt->rt_ifp)	/* can't happen */
36 		return;
37 	(*afswitch[af].af_hash)(dst, &h);
38 	m = routehash[h.afh_hosthash % RTHASHSIZ];
39 	key = h.afh_hostkey;
40 	rtmin = 0, doinghost = 1;
41 again:
42 	for (; m; m = m->m_next) {
43 		rt = mtod(m, struct rtentry *);
44 		if (rt->rt_key != key)
45 			continue;
46 		if (doinghost) {
47 #define	equal(a1, a2) \
48 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof(struct sockaddr)) == 0)
49 			if (!equal(&rt->rt_dst, dst))
50 				continue;
51 		} else {
52 			if (rt->rt_dst.sa_family != af)
53 				continue;
54 			if ((*afswitch[af].af_netmatch)(&rt->rt_dst, dst) == 0)
55 				continue;
56 		}
57 		if (rtmin == 0 || rt->rt_use < rtmin->rt_use)
58 			rtmin = rt;
59 	}
60 	if (rtmin) {
61 		ro->ro_dst = rt->rt_dst;
62 		ro->ro_rt = rt;
63 		rt->rt_refcnt++;
64 		return (rt->rt_flags & RTF_DIRECT);
65 	}
66 	if (doinghost) {
67 		doinghost = 0;
68 		m = routehash[h.afh_nethash % RTHASHSIZ];
69 		key = h.afh_netkey;
70 		goto again;
71 	}
72 	ro->ro_rt = 0;
73 	return (0);
74 }
75 
76 freeroute(rt)
77 	register struct rtentry *rt;
78 {
79 COUNT(FREEROUTE);
80 	if (rt == 0)
81 		panic("freeroute");
82 	rt->rt_refcnt--;
83 	/* on refcnt == 0 reclaim? notify someone? */
84 }
85 
86 #ifdef notdef
87 struct rtentry *
88 reroute(sa)
89 	register struct sockaddr *sa;
90 {
91 	register struct rtentry *rt;
92 	register struct mbuf *m;
93 	register int key;
94 	struct afhash h;
95 
96 COUNT(REROUTE);
97 	(*afswitch[sa->sa_family].af_hash)(sa, &h);
98 	m = routehash[h.afh_hosthash];
99 	key = h.afh_hostkey;
100 	for (; m; m = m->m_next) {
101 		rt = mtod(m, struct rtentry *);
102 		if (rt->rt_key != key)
103 			continue;
104 		if (equal(&rt->rt_gateway, sa))
105 			return (rt);
106 	}
107 	return (0);
108 }
109 #endif
110 
111 /*
112  * Carry out a request to change the routing table.  Called by
113  * interfaces at boot time to make their ``local routes'' known
114  * and for ioctl's.
115  */
116 rtrequest(req, new)
117 	int req;
118 	register struct rtentry *new;
119 {
120 	register struct rtentry *rt;
121 	register struct mbuf *m, **mprev;
122 	register int key;
123 	struct sockaddr *sa = &new->rt_dst;
124 	struct afhash h;
125 	int af = sa->sa_family, doinghost, s, error = 0;
126 
127 COUNT(RTREQUEST);
128 	(*afswitch[af].af_hash)(sa, &h);
129 	mprev = &routehash[h.afh_hosthash % RTHASHSIZ];
130 	key = h.afh_hostkey;
131 	doinghost = 1;
132 	s = splimp();
133 again:
134 	for (; m = *mprev; mprev = &m->m_next) {
135 		rt = mtod(m, struct rtentry *);
136 		if (rt->rt_key != key)
137 			continue;
138 		if (doinghost) {
139 			if (!equal(&rt->rt_dst, &new->rt_dst))
140 				continue;
141 		} else {
142 			if (rt->rt_dst.sa_family != af)
143 				continue;
144 			if ((*afswitch[af].af_netmatch)(&rt->rt_dst, sa) == 0)
145 				continue;
146 		}
147 		/* require full match on deletions */
148 		if (req == SIOCDELRT &&
149 		    !equal(&rt->rt_gateway, &new->rt_gateway))
150 			continue;
151 		/* don't keep multiple identical entries */
152 		if (req == SIOCADDRT &&
153 		    equal(&rt->rt_gateway, &new->rt_gateway)) {
154 			error = EEXIST;
155 			goto bad;
156 		}
157 		break;
158 	}
159 	if (m == 0 && doinghost) {
160 		doinghost = 0;
161 		mprev = &routehash[h.afh_nethash % RTHASHSIZ];
162 		key = h.afh_netkey;
163 		goto again;
164 	}
165 
166 	if (m == 0 && req != SIOCADDRT) {
167 		error = ESRCH;
168 		goto bad;
169 	}
170 	switch (req) {
171 
172 	case SIOCDELRT:
173 		rt->rt_flags &= ~RTF_UP;
174 		if (rt->rt_refcnt > 0)	/* should we notify protocols? */
175 			error = EBUSY;
176 		else
177 			*mprev = m_free(m);
178 		break;
179 
180 	case SIOCCHGRT:
181 		rt->rt_flags = new->rt_flags;
182 		if (rt->rt_refcnt > 0)
183 			error = EBUSY;
184 		else if (!equal(&rt->rt_gateway, &new->rt_gateway))
185 			goto newneighbor;
186 		break;
187 
188 	case SIOCADDRT:
189 		m = m_getclr(M_DONTWAIT);
190 		if (m == 0) {
191 			error = ENOBUFS;
192 			break;
193 		}
194 		m->m_off = MMINOFF;
195 		*mprev = m;
196 		rt = mtod(m, struct rtentry *);
197 		*rt = *new;
198 		rt->rt_key = h.afh_nethash | h.afh_hosthash;
199 newneighbor:
200 		rt->rt_ifp = if_ifonnetof(&new->rt_gateway);
201 		if (rt->rt_ifp == 0)
202 			rt->rt_flags &= ~RTF_UP;
203 		rt->rt_refcnt = 0;
204 		break;
205 	}
206 bad:
207 	splx(s);
208 	return (error);
209 }
210