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 @@ -237,12 +237,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? */ @@ -371,7 +372,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)) { @@ -382,6 +385,7 @@ (fwd_tag + 1))->sin_addr.s_addr; m_tag_delete(m, fwd_tag); m->m_flags &= ~M_IP_NEXTHOP; + rt_changed = true; } /* @@ -390,17 +394,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; @@ -409,18 +409,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: @@ -430,20 +433,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/netinet/ip_input.c b/sys/netinet/ip_input.c --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -921,6 +921,7 @@ struct sockaddr_in *sin; struct in_addr dest; struct route ro; + struct m_tag *fwd_tag; uint32_t flowid; int error, type = 0, code = 0, mtu = 0; @@ -942,11 +943,20 @@ bzero(&ro, sizeof(ro)); sin = (struct sockaddr_in *)&ro.ro_dst; - sin->sin_family = AF_INET; - sin->sin_len = sizeof(*sin); - sin->sin_addr = ip->ip_dst; + + if (m->m_flags & M_IP_NEXTHOP && ((fwd_tag = m_tag_find(m, + PACKET_TAG_IPFORWARD, NULL)) != NULL)) { + bcopy((fwd_tag + 1), sin, sizeof(struct sockaddr_in)); + m->m_flags &= ~M_IP_NEXTHOP; + m_tag_delete(m, fwd_tag); + } else { + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + sin->sin_addr = ip->ip_dst; + } + flowid = m->m_pkthdr.flowid; - ro.ro_nh = fib4_lookup(M_GETFIB(m), ip->ip_dst, 0, NHR_REF, flowid); + ro.ro_nh = fib4_lookup(M_GETFIB(m), sin->sin_addr, 0, NHR_REF, flowid); if (ro.ro_nh != NULL) { ia = ifatoia(ro.ro_nh->nh_ifa); } else 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 @@ -90,13 +90,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. @@ -158,6 +159,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. */ @@ -178,6 +184,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) { @@ -187,6 +197,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; @@ -213,6 +224,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; @@ -243,30 +255,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 diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -177,9 +177,17 @@ * 3) packet filters attaching PACKET_TAG_IPFORWARD would embed * scope for the nexthop. */ - bzero(&dst, sizeof(struct sockaddr_in6)); - dst.sin6_family = AF_INET6; - dst.sin6_addr = ip6->ip6_dst; + if (m->m_flags & M_IP6_NEXTHOP && ((fwd_tag = m_tag_find(m, + PACKET_TAG_IPFORWARD, NULL)) != NULL)) { + bcopy((fwd_tag + 1), &dst, sizeof(dst)); + m->m_flags &= ~M_IP6_NEXTHOP; + m_tag_delete(m, fwd_tag); + } else { + bzero(&dst, sizeof(struct sockaddr_in6)); + dst.sin6_family = AF_INET6; + dst.sin6_addr = ip6->ip6_dst; + } + dst.sin6_scope_id = in6_get_unicast_scopeid(&ip6->ip6_dst, m->m_pkthdr.rcvif); again: nh = fib6_lookup(M_GETFIB(m), &dst.sin6_addr, dst.sin6_scope_id,