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