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