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 25 Jun 2024 16:50:38 -0000 @@ -89,8 +89,16 @@ 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_int8_t loopcnt = m->m_pkthdr.ph_loopcnt; + 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 +125,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 +144,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 +199,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 +213,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 +228,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 +268,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 +288,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 +342,41 @@ 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.ph_loopcnt = loopcnt; + 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 +414,21 @@ 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.ph_loopcnt = loopcnt; + mcopy->m_pkthdr.pf.flags |= (pfflags & PF_TAG_GENERATED); + memcpy(mcopy->m_data, icmp_buf, icmp_len); + } + if (mcopy != NULL) + icmp6_error(mcopy, type, code, destmtu); goto done; freecopy: