- register struct mbuf *m, **mprev;
- register int hash, (*match)();
- register struct sockaddr *sa = &new->rt_dst;
- register struct sockaddr *gate = &new->rt_gateway;
- struct afhash h;
- struct mbuf **oldmprev;
- int af = sa->sa_family, doinghost, s, error = 0;
-
-COUNT(RTREQUEST);
- if (af >= AF_MAX)
- return (EAFNOSUPPORT);
- (*afswitch[af].af_hash)(sa, &h);
- hash = h.afh_hosthash;
- mprev = &rthost[hash % RTHASHSIZ];
- doinghost = 1;
- s = splimp();
-again:
- for (; m = *mprev; mprev = &m->m_next) {
- rt = mtod(m, struct rtentry *);
- if (rt->rt_hash != hash)
- continue;
- if (doinghost) {
- if (!equal(&rt->rt_dst, sa))
- continue;
+
+ /* verify the gateway is directly reachable */
+ if (ifa_ifwithnet(gateway) == 0) {
+ rtstat.rts_badredirect++;
+ return;
+ }
+ ro.ro_dst = *dst;
+ ro.ro_rt = 0;
+ rtalloc(&ro);
+ rt = ro.ro_rt;
+#define equal(a1, a2) \
+ (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0)
+ /*
+ * If the redirect isn't from our current router for this dst,
+ * it's either old or wrong. If it redirects us to ourselves,
+ * we have a routing loop, perhaps as a result of an interface
+ * going down recently.
+ */
+ if ((rt && !equal(src, &rt->rt_gateway)) || ifa_ifwithaddr(gateway)) {
+ rtstat.rts_badredirect++;
+ if (rt)
+ rtfree(rt);
+ return;
+ }
+ /*
+ * Old comment:
+ * Create a new entry if we just got back a wildcard entry
+ * or the the lookup failed. This is necessary for hosts
+ * which use routing redirects generated by smart gateways
+ * to dynamically build the routing tables.
+ *
+ * New comment:
+ * If we survived the previous tests, it doesn't matter
+ * what sort of entry we got when we looked it up;
+ * we should just go ahead and free the reference to
+ * the route we created. rtalloc will not give a
+ * pointer to the root node. And if we got a pointer
+ * to a default gateway, we should free the reference
+ * in any case.
+ if (rt) {
+ rtfree(rt);
+ rt = 0;
+ }
+ */
+ if (rt == 0) {
+ rtinit(dst, gateway, (int)SIOCADDRT,
+ (flags & RTF_HOST) | RTF_GATEWAY | RTF_DYNAMIC);
+ rtstat.rts_dynamic++;
+ return;
+ }
+ /*
+ * Don't listen to the redirect if it's
+ * for a route to an interface.
+ */
+ if (rt->rt_flags & RTF_GATEWAY) {
+ if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) {
+ /*
+ * Changing from route to net => route to host.
+ * Create new route, rather than smashing route to net.
+ */
+ rtinit(dst, gateway, (int)SIOCADDRT,
+ flags | RTF_DYNAMIC);
+ rtstat.rts_dynamic++;