xref: /original-bsd/sys/net/route.c (revision 145b48a8)
1 /*	route.c	4.2	82/03/28	*/
2 
3 #include "../h/param.h"
4 #include "../h/systm.h"
5 #include "../h/dir.h"
6 #include "../h/user.h"
7 #include "../h/proc.h"
8 #include "../h/file.h"
9 #include "../h/inode.h"
10 #include "../h/buf.h"
11 #include "../h/mbuf.h"
12 #include "../h/protosw.h"
13 #include "../h/socket.h"
14 #include "../h/socketvar.h"
15 #include "../h/ioctl.h"
16 #include "../net/in.h"
17 #include "../net/in_systm.h"
18 #include "../net/if.h"
19 #include "../net/af.h"
20 #include "../net/route.h"
21 
22 /*
23  * Packet routing routines.
24  */
25 
26 /*
27  * With much ado about nothing...
28  * route the cars that climb halfway to the stars...
29  */
30 route(ro)
31 	register struct route *ro;
32 {
33 	register struct rtentry *rt, *rtmin;
34 	register struct mbuf *m;
35 	register int key;
36 	struct afhash h;
37 	struct sockaddr *dst = &ro->ro_dst;
38 	int af = dst->sa_family, doinghost;
39 
40 COUNT(ROUTE);
41 	if (ro && ro->ro_rt && ro->ro_rt->rt_ifp)	/* ??? */
42 		return;
43 	(*afswitch[af].af_hash)(dst, &h);
44 	m = routehash[h.afh_hosthash % RTHASHSIZ];
45 	key = h.afh_hostkey;
46 	rtmin = 0, doinghost = 1;
47 again:
48 	for (; m; m = m->m_next) {
49 		rt = mtod(m, struct rtentry *);
50 #define	equal(a1, a2) \
51 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof(struct sockaddr)) == 0)
52 		if (rt->rt_key != key)
53 			continue;
54 		if (doinghost) {
55 			if (!equal(&rt->rt_dst, dst))
56 				continue;
57 		} else {
58 			if (rt->rt_dst.sa_family != af)
59 				continue;
60 			if ((*afswitch[af].af_netmatch)(&rt->rt_dst, dst) == 0)
61 				continue;
62 		}
63 		if (rtmin == 0 || rt->rt_use < rtmin->rt_use)
64 			rtmin = rt;
65 	}
66 	if (rtmin) {
67 		ro->ro_dst = rt->rt_dst;
68 		ro->ro_rt = rt;
69 		rt->rt_refcnt++;
70 		return;
71 	}
72 	if (doinghost) {
73 		doinghost = 0;
74 		m = routehash[h.afh_nethash % RTHASHSIZ];
75 		key = h.afh_netkey;
76 		goto again;
77 	}
78 	ro->ro_rt = 0;
79 }
80 
81 struct rtentry *
82 reroute(sa)
83 	register struct sockaddr *sa;
84 {
85 	register struct rtentry *rt;
86 	register struct mbuf *m;
87 	register int key;
88 	struct afhash h;
89 
90 COUNT(REROUTE);
91 	(*afswitch[sa->sa_family].af_hash)(sa, &h);
92 	m = routehash[h.afh_hosthash];
93 	key = h.afh_hostkey;
94 	for (; m; m = m->m_next) {
95 		rt = mtod(m, struct rtentry *);
96 		if (rt->rt_key != key)
97 			continue;
98 		if (equal(&rt->rt_gateway, sa))
99 			return (rt);
100 	}
101 	return (0);
102 }
103 
104 /*
105  * Routing control calls allow a routing daemon
106  * to consistenly access the routing data base for updates.
107  */
108 rtcontrol(req, addr)
109 	caddr_t addr;
110 {
111 	register struct rtentry rq;
112 	int x = splimp(), err = 0;
113 
114 COUNT(RTCONTROL);
115 	if (suser())
116 		goto bad;
117 	if (copyin(addr, (caddr_t)&rq, sizeof(struct rtentry))) {
118 		u.u_error = EFAULT;
119 		goto bad;
120 	}
121 	err = rtrequest(req, &rq);
122 bad:
123 	splx(x);
124 	return (err);
125 }
126 
127 /*
128  * Carry out a user request to modify the data base.
129  */
130 rtrequest(req, new)
131 	int req;
132 	register struct rtentry *new;
133 {
134 	register struct rtentry *rt;
135 	register struct mbuf *m, **mprev;
136 	register int key;
137 	struct sockaddr *sa = &new->rt_dst;
138 	struct afhash h;
139 	int af = sa->sa_family, doinghost;
140 
141 	(*afswitch[af].af_hash)(sa, &h);
142 	mprev = &routehash[h.afh_hosthash % RTHASHSIZ];
143 	key = h.afh_hostkey;
144 	doinghost = 1;
145 again:
146 	for (; m = *mprev; mprev = &m->m_next) {
147 		rt = mtod(m, struct rtentry *);
148 		if (rt->rt_key != key)
149 			continue;
150 		if (doinghost) {
151 			if (!equal(&rt->rt_dst, &new->rt_dst))
152 				continue;
153 		} else {
154 			if (rt->rt_dst.sa_family != af)
155 				continue;
156 			if ((*afswitch[af].af_netmatch)(&rt->rt_dst, sa) == 0)
157 				continue;
158 		}
159 		break;
160 	}
161 	if (m == 0 && doinghost) {
162 		doinghost = 0;
163 		mprev = &routehash[h.afh_nethash % RTHASHSIZ];
164 		key = h.afh_netkey;
165 		goto again;
166 	}
167 
168 	if (m == 0 && req != SIOCADDRT)
169 		return (ESRCH);
170 	switch (req) {
171 
172 	case SIOCDELRT:
173 		rt->rt_flags &= ~RTF_UP;
174 		if (rt->rt_refcnt > 0)	/* should we notify protocols? */
175 			break;
176 		*mprev = m_free(m);
177 		break;
178 
179 	case SIOCCHGRT:
180 		rt->rt_flags = new->rt_flags;
181 		if (rt->rt_refcnt > 0)
182 			return (EBUSY);
183 		if (!equal(&rt->rt_gateway, &new->rt_gateway))
184 			goto newneighbor;
185 		break;
186 
187 	case SIOCADDRT:
188 		m = m_getclr(M_DONTWAIT);
189 		if (m == 0)
190 			return (ENOBUFS);
191 		m->m_off = MMINOFF;
192 		*mprev = m;
193 		rt = mtod(m, struct rtentry *);
194 		*rt = *new;
195 		rt->rt_key = h.afh_nethash | h.afh_hosthash;
196 newneighbor:
197 		rt->rt_ifp = if_ifonnetof(&new->rt_gateway);
198 		if (rt->rt_ifp == 0)
199 			rt->rt_flags &= ~RTF_UP;
200 		rt->rt_refcnt = 0;
201 		break;
202 	}
203 	return (0);
204 }
205