Index: sys/netinet/ip_input.c =================================================================== RCS file: /mount/openbsd/cvs/src/sys/netinet/ip_input.c,v diff -u -p -u -p -r1.395 ip_input.c --- sys/netinet/ip_input.c 7 Jun 2024 18:24:16 -0000 1.395 +++ sys/netinet/ip_input.c 22 Jun 2024 02:14:28 -0000 @@ -1532,11 +1532,16 @@ const u_char inetctlerrmap[PRC_NCMDS] = void ip_forward(struct mbuf *m, struct ifnet *ifp, struct route *ro, int flags) { - struct mbuf mfake, *mcopy; struct ip *ip = mtod(m, struct ip *); struct route iproute; struct rtentry *rt; - int error = 0, type = 0, code = 0, destmtu = 0, fake = 0, len; + u_int rtableid = m->m_pkthdr.ph_rtableid; + u_int icmp_len; + char icmp_buf[68]; + CTASSERT(sizeof(icmp_buf) <= MHLEN); + u_short mflags, pfflags; + struct mbuf *mcopy; + int error = 0, type = 0, code = 0, destmtu = 0; u_int32_t dest; dest = 0; @@ -1554,7 +1559,7 @@ ip_forward(struct mbuf *m, struct ifnet ro = &iproute; ro->ro_rt = NULL; } - rt = route_mpath(ro, &ip->ip_dst, &ip->ip_src, m->m_pkthdr.ph_rtableid); + rt = route_mpath(ro, &ip->ip_dst, &ip->ip_src, rtableid); if (rt == NULL) { ipstat_inc(ips_noroute); icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0); @@ -1562,24 +1567,14 @@ ip_forward(struct mbuf *m, struct ifnet } /* - * Save at most 68 bytes of the packet in case - * we need to generate an ICMP message to the src. - * The data is saved in the mbuf on the stack that - * acts as a temporary storage not intended to be - * passed down the IP stack or to the mfree. + * Save at most 68 bytes of the packet in case we need to generate + * an ICMP message to the src. The data is saved on the stack. + * A new mbuf is only allocated when ICMP is actually created. */ - memset(&mfake.m_hdr, 0, sizeof(mfake.m_hdr)); - mfake.m_type = m->m_type; - if (m_dup_pkthdr(&mfake, m, M_DONTWAIT) == 0) { - mfake.m_data = mfake.m_pktdat; - len = min(ntohs(ip->ip_len), 68); - m_copydata(m, 0, len, mfake.m_pktdat); - mfake.m_pkthdr.len = mfake.m_len = len; -#if NPF > 0 - pf_pkt_addr_changed(&mfake); -#endif /* NPF > 0 */ - fake = 1; - } + icmp_len = min(sizeof(icmp_buf), ntohs(ip->ip_len)); + mflags = m->m_flags; + pfflags = m->m_pkthdr.pf.flags; + m_copydata(m, 0, icmp_len, icmp_buf); ip->ip_ttl -= IPTTLDEC; @@ -1597,7 +1592,7 @@ ip_forward(struct mbuf *m, struct ifnet (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 && satosin(rt_key(rt))->sin_addr.s_addr != 0 && ip_sendredirects && !ISSET(flags, IP_REDIRECT) && - !arpproxy(satosin(rt_key(rt))->sin_addr, m->m_pkthdr.ph_rtableid)) { + !arpproxy(satosin(rt_key(rt))->sin_addr, rtableid)) { if ((ip->ip_src.s_addr & ifatoia(rt->rt_ifa)->ia_netmask) == ifatoia(rt->rt_ifa)->ia_net) { if (rt->rt_flags & RTF_GATEWAY) @@ -1621,9 +1616,6 @@ ip_forward(struct mbuf *m, struct ifnet else goto done; } - if (!fake) - goto done; - switch (error) { case 0: /* forwarded, but need redirect */ /* type, code set above */ @@ -1674,15 +1666,21 @@ ip_forward(struct mbuf *m, struct ifnet code = ICMP_UNREACH_HOST; break; } - mcopy = m_copym(&mfake, 0, len, M_DONTWAIT); - if (mcopy != NULL) - icmp_error(mcopy, type, code, dest, destmtu); + + mcopy = m_gethdr(M_DONTWAIT, MT_DATA); + if (mcopy == NULL) + goto done; + mcopy->m_flags |= (mflags & M_COPYFLAGS); + mcopy->m_len = mcopy->m_pkthdr.len = icmp_len; + mcopy->m_pkthdr.ph_rtableid = rtableid; + mcopy->m_pkthdr.ph_ifidx = ifp->if_index; + mcopy->m_pkthdr.pf.flags |= (pfflags & PF_TAG_GENERATED); + memcpy(mcopy->m_data, icmp_buf, icmp_len); + icmp_error(mcopy, type, code, dest, destmtu); done: if (ro == &iproute) rtfree(ro->ro_rt); - if (fake) - m_tag_delete_chain(&mfake); } int Index: sys/netinet6/ip6_forward.c =================================================================== RCS file: /mount/openbsd/cvs/src/sys/netinet6/ip6_forward.c,v diff -u -p -u -p -r1.119 ip6_forward.c --- sys/netinet6/ip6_forward.c 20 Jun 2024 19:25:42 -0000 1.119 +++ sys/netinet6/ip6_forward.c 22 Jun 2024 02:14:28 -0000 @@ -89,8 +89,15 @@ ip6_forward(struct mbuf *m, struct route struct rtentry *rt; struct sockaddr *dst; struct ifnet *ifp = NULL; - int error = 0, type = 0, code = 0, destmtu = 0; + u_int rtableid = m->m_pkthdr.ph_rtableid; + u_int ifidx = m->m_pkthdr.ph_ifidx; + u_int icmp_len; + char icmp_buf[sizeof(struct ip6_hdr) + sizeof(struct tcphdr) + + MAX_TCPOPTLEN]; + CTASSERT(sizeof(icmp_buf) <= MHLEN); + u_short mflags, pfflags; struct mbuf *mcopy; + int error = 0, type = 0, code = 0, destmtu = 0; #ifdef IPSEC struct tdb *tdb = NULL; #endif /* IPSEC */ @@ -117,9 +124,7 @@ ip6_forward(struct mbuf *m, struct route log(LOG_DEBUG, "cannot forward " "from %s to %s nxt %d received on interface %u\n", - src6, dst6, - ip6->ip6_nxt, - m->m_pkthdr.ph_ifidx); + src6, dst6, ip6->ip6_nxt, ifidx); } m_freem(m); goto done; @@ -138,11 +143,30 @@ ip6_forward(struct mbuf *m, struct route * we need to generate an ICMP6 message to the src. * Thanks to M_EXT, in most cases copy will not occur. * + * For final protocol header like TCP or UDP, full header chain in + * ICMP6 packet is not necessary. In this case only copy small + * part of original packet and save it on stack instead of mbuf. + * Although this violates RFC, it avoids additional mbuf allocations. + * Also pf nat and rdr do not affect the shared mbuf cluster. + * * It is important to save it before IPsec processing as IPsec * processing may modify the mbuf. */ - mcopy = m_copym(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN), - M_NOWAIT); + switch (ip6->ip6_nxt) { + case IPPROTO_TCP: + case IPPROTO_UDP: + icmp_len = min(m->m_pkthdr.len, sizeof(icmp_buf)); + mflags = m->m_flags; + pfflags = m->m_pkthdr.pf.flags; + m_copydata(m, 0, icmp_len, icmp_buf); + mcopy = NULL; + break; + default: + icmp_len = min(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN); + mcopy = m_copym(m, 0, icmp_len, M_NOWAIT); + icmp_len = 0; + break; + } #if NPF > 0 reroute: @@ -174,12 +198,10 @@ reroute: m->m_pkthdr.ph_rtableid); if (rt == NULL) { ip6stat_inc(ip6s_noroute); - if (mcopy != NULL) { - icmp6_error(mcopy, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_NOROUTE, 0); - } + type = ICMP6_DST_UNREACH; + code = ICMP6_DST_UNREACH_NOROUTE; m_freem(m); - goto done; + goto icmperror; } dst = &ro->ro_dstsa; @@ -190,7 +212,7 @@ reroute: * unreachable error with Code 2 (beyond scope of source address). * [draft-ietf-ipngwg-icmp-v3-00.txt, Section 3.1] */ - if (in6_addr2scopeid(m->m_pkthdr.ph_ifidx, &ip6->ip6_src) != + if (in6_addr2scopeid(ifidx, &ip6->ip6_src) != in6_addr2scopeid(rt->rt_ifidx, &ip6->ip6_src)) { time_t uptime; @@ -205,15 +227,12 @@ reroute: log(LOG_DEBUG, "cannot forward " "src %s, dst %s, nxt %d, rcvif %u, outif %u\n", - src6, dst6, - ip6->ip6_nxt, - m->m_pkthdr.ph_ifidx, rt->rt_ifidx); + src6, dst6, ip6->ip6_nxt, ifidx, rt->rt_ifidx); } - if (mcopy != NULL) - icmp6_error(mcopy, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_BEYONDSCOPE, 0); + type = ICMP6_DST_UNREACH; + code = ICMP6_DST_UNREACH_BEYONDSCOPE; m_freem(m); - goto done; + goto icmperror; } #ifdef IPSEC @@ -248,7 +267,7 @@ reroute: m_freem(m); goto freecopy; } - if (rt->rt_ifidx == m->m_pkthdr.ph_ifidx && + if (rt->rt_ifidx == ifidx && ip6_sendredirects && !ISSET(flags, IPV6_REDIRECT) && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) { if ((ifp->if_flags & IFF_POINTOPOINT) && @@ -268,11 +287,10 @@ reroute: * type/code is based on suggestion by Rich Draves. * not sure if it is the best pick. */ - if (mcopy != NULL) - icmp6_error(mcopy, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_ADDR, 0); + type = ICMP6_DST_UNREACH; + code = ICMP6_DST_UNREACH_ADDR; m_freem(m); - goto done; + goto icmperror; } type = ND_REDIRECT; } @@ -323,27 +341,40 @@ reroute: if (error || m == NULL) goto senderr; - if (mcopy != NULL) - icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); + type = ICMP6_PACKET_TOO_BIG; + destmtu = ifp->if_mtu; m_freem(m); - goto done; + goto icmperror; senderr: - if (mcopy == NULL) + if (mcopy == NULL && icmp_len == 0) goto done; switch (error) { case 0: if (type == ND_REDIRECT) { - icmp6_redirect_output(mcopy, rt); - ip6stat_inc(ip6s_redirectsent); + if (icmp_len != 0) { + mcopy = m_gethdr(M_DONTWAIT, MT_DATA); + if (mcopy == NULL) + goto done; + mcopy->m_len = mcopy->m_pkthdr.len = icmp_len; + mcopy->m_flags |= (mflags & M_COPYFLAGS); + mcopy->m_pkthdr.ph_rtableid = rtableid; + mcopy->m_pkthdr.ph_ifidx = ifidx; + mcopy->m_pkthdr.pf.flags |= + (pfflags & PF_TAG_GENERATED); + memcpy(mcopy->m_data, icmp_buf, icmp_len); + } + if (mcopy != NULL) { + icmp6_redirect_output(mcopy, rt); + ip6stat_inc(ip6s_redirectsent); + } goto done; } goto freecopy; case EMSGSIZE: type = ICMP6_PACKET_TOO_BIG; - code = 0; if (rt != NULL) { if (rt->rt_mtu) { destmtu = rt->rt_mtu; @@ -381,7 +412,20 @@ senderr: code = ICMP6_DST_UNREACH_ADDR; break; } - icmp6_error(mcopy, type, code, destmtu); + icmperror: + if (icmp_len != 0) { + mcopy = m_gethdr(M_DONTWAIT, MT_DATA); + if (mcopy == NULL) + goto done; + mcopy->m_len = mcopy->m_pkthdr.len = icmp_len; + mcopy->m_flags |= (mflags & M_COPYFLAGS); + mcopy->m_pkthdr.ph_rtableid = rtableid; + mcopy->m_pkthdr.ph_ifidx = ifidx; + mcopy->m_pkthdr.pf.flags |= (pfflags & PF_TAG_GENERATED); + memcpy(mcopy->m_data, icmp_buf, icmp_len); + } + if (mcopy != NULL) + icmp6_error(m, type, code, destmtu); goto done; freecopy: