diff --git a/sys/netinet/ip_fastfwd.c b/sys/netinet/ip_fastfwd.c --- a/sys/netinet/ip_fastfwd.c +++ b/sys/netinet/ip_fastfwd.c @@ -239,12 +239,13 @@ struct route ro; struct sockaddr_in *dst; const struct sockaddr *gw; - struct in_addr dest, odest, rtdest, osrc; + struct in_addr dest, odest, osrc; uint16_t ip_len, ip_off; int error = 0; struct m_tag *fwd_tag = NULL; struct mbuf *mcopy = NULL; struct in_addr redest; + bool rt_changed = false; /* * Are we active and forwarding packets? */ @@ -373,7 +374,9 @@ #endif /* - * Next hop forced by pfil(9) hook? + * After pfil_mbuf_in we use the changed destination no matter if it + * comes from IP headers (see right after pfil_mbuf_in) or from the + * PACKET_TAG_IPFORWARD tag. */ if ((m->m_flags & M_IP_NEXTHOP) && ((fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL)) { @@ -384,6 +387,7 @@ (fwd_tag + 1))->sin_addr.s_addr; m_tag_delete(m, fwd_tag); m->m_flags &= ~M_IP_NEXTHOP; + rt_changed = true; } /* @@ -392,17 +396,13 @@ if (ip_findroute(&nh, dest, m) != 0) return (NULL); /* icmp unreach already sent */ - /* - * Avoid second route lookup by caching destination. - */ - rtdest.s_addr = dest.s_addr; - /* * Step 5: outgoing firewall packet processing */ if (!PFIL_HOOKED_OUT(V_inet_pfil_head)) goto passout; + odest.s_addr = ip->ip_dst.s_addr; if (pfil_mbuf_out(V_inet_pfil_head, &m, nh->nh_ifp, NULL) != PFIL_PASS) goto drop; @@ -411,18 +411,21 @@ M_ASSERTPKTHDR(m); ip = mtod(m, struct ip *); - dest.s_addr = ip->ip_dst.s_addr; /* - * Destination address changed? + * After pfil_mbuf_out we might keep the destination set by pfil_mbuf_in + * if it was set via the PACKET_TAG_IPFORWARD tag. */ if (m->m_flags & M_IP_NEXTHOP) fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); else fwd_tag = NULL; - if (odest.s_addr != dest.s_addr || fwd_tag != NULL) { + + if (!rt_changed && odest.s_addr != ip->ip_dst.s_addr) { + dest.s_addr = ip->ip_dst.s_addr; /* * Is it now for a local address on this host? + * XXX Should we direct to local also by routing, not just NAT? */ if (m->m_flags & M_FASTFWD_OURS || in_localip(dest)) { forwardlocal: @@ -432,20 +435,23 @@ m->m_flags |= M_FASTFWD_OURS; return (m); } - /* - * Redo route lookup with new destination address - */ - if (fwd_tag) { - dest.s_addr = ((struct sockaddr_in *) - (fwd_tag + 1))->sin_addr.s_addr; - m_tag_delete(m, fwd_tag); - m->m_flags &= ~M_IP_NEXTHOP; - } - if (dest.s_addr != rtdest.s_addr && - ip_findroute(&nh, dest, m) != 0) - return (NULL); /* icmp unreach already sent */ + rt_changed = true; } + if (fwd_tag) { + dest.s_addr = ((struct sockaddr_in *) + (fwd_tag + 1))->sin_addr.s_addr; + m_tag_delete(m, fwd_tag); + m->m_flags &= ~M_IP_NEXTHOP; + rt_changed = true; + } + + /* + * Redo route lookup with new destination address + */ + if (rt_changed && ip_findroute(&nh, dest, m) != 0) + return (NULL); /* icmp unreach already sent */ + passout: /* * Step 6: send off the packet diff --git a/sys/netinet6/ip6_fastfwd.c b/sys/netinet6/ip6_fastfwd.c --- a/sys/netinet6/ip6_fastfwd.c +++ b/sys/netinet6/ip6_fastfwd.c @@ -92,13 +92,14 @@ struct mbuf* ip6_tryforward(struct mbuf *m) { - struct sockaddr_in6 dst; + struct sockaddr_in6 dst, odst; struct nhop_object *nh; struct m_tag *fwd_tag; struct ip6_hdr *ip6; struct ifnet *rcvif; uint32_t plen; int error; + bool rt_changed = false; /* * Fallback conditions to ip6_input for slow path processing. @@ -160,6 +161,11 @@ dst.sin6_len = sizeof(dst); dst.sin6_addr = ip6->ip6_dst; + bzero(&odst, sizeof(odst)); + odst.sin6_family = AF_INET6; + odst.sin6_len = sizeof(dst); + odst.sin6_addr = ip6->ip6_dst; + /* * Incoming packet firewall processing. */ @@ -180,6 +186,10 @@ if (m->m_flags & M_FASTFWD_OURS) return (m); + /* + * After pfil_mbuf_in we use the changed destination no matter if it + * comes from IP headers or from the PACKET_TAG_IPFORWARD tag. + */ ip6 = mtod(m, struct ip6_hdr *); if ((m->m_flags & M_IP6_NEXTHOP) && (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { @@ -189,6 +199,7 @@ bcopy((fwd_tag + 1), &dst, sizeof(dst)); m->m_flags &= ~M_IP6_NEXTHOP; m_tag_delete(m, fwd_tag); + rt_changed = true; } else { /* Update dst since pfil could change it */ dst.sin6_addr = ip6->ip6_dst; @@ -215,6 +226,7 @@ /* * Outgoing packet firewall processing. */ + odst.sin6_addr = ip6->ip6_dst; if (pfil_mbuf_out(V_inet6_pfil_head, &m, nh->nh_ifp, NULL) != PFIL_PASS) goto dropout; @@ -245,30 +257,34 @@ */ return (m); } - /* - * Again. A packet filter could change the destination address. - */ + ip6 = mtod(m, struct ip6_hdr *); + + /* + * After pfil_mbuf_out we might keep the destination set by pfil_mbuf_in + * if it was set via the PACKET_TAG_IPFORWARD tag. + */ if (m->m_flags & M_IP6_NEXTHOP) fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); else fwd_tag = NULL; - if (fwd_tag != NULL || - !IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &ip6->ip6_dst)) { - if (fwd_tag != NULL) { - bcopy((fwd_tag + 1), &dst, sizeof(dst)); - m->m_flags &= ~M_IP6_NEXTHOP; - m_tag_delete(m, fwd_tag); - } else - dst.sin6_addr = ip6->ip6_dst; - /* - * Redo route lookup with new destination address - */ - if (ip6_findroute(&nh, &dst, m) != 0) { - m = NULL; - goto dropout; - } + if (!rt_changed && !IN6_ARE_ADDR_EQUAL( + &odst.sin6_addr, &ip6->ip6_dst)) { + dst.sin6_addr = ip6->ip6_dst; + rt_changed = true; + } + + if (fwd_tag != NULL) { + bcopy((fwd_tag + 1), &dst, sizeof(dst)); + m->m_flags &= ~M_IP6_NEXTHOP; + m_tag_delete(m, fwd_tag); + rt_changed = true; + } + + if (rt_changed && ip6_findroute(&nh, &dst, m) != 0) { + m = NULL; + goto dropout; } passout: #ifdef IPSTEALTH