From cf045b768e762ce9ffc97a1dfe52a7bfd3e0f5cc Mon Sep 17 00:00:00 2001 From: bluhm Date: Sat, 13 Jul 2024 09:34:26 +0000 Subject: [PATCH] Do not store full IPv6 packet in common forwarding case. Forwarding IPv6 packets is slower than IPv4. Reason is that m_copym() is done for every packet. Just in case we may have to send an ICMP6 packet, ip6_forward() creates a mbuf copy. After that mbuf cluster is read only, so for the ethernet header another mbuf is allocated. pf NAT and RDR ignores readonly clusters, so it also modifies the potential ICMP6 packet. IPv4 ip_forward() avoids all these problems by copying the leading 68 bytes of the original packets onto the stack. More is not need for ICMP. IPv6 RFC 4443 2.4. (c) requires up to 1232 bytes in the ICMP6 packet. This cannot be copied to the stack. The reason for the difference in the standard seems to be that the ICMP6 packet has to contain the full header chain. If we have a simple TCP, UDP or ESP packet without chain, do a shortcut and just preserve the header for the ICMP6 packet. Small packets already use stack memory, large packets need extra mbuf allocation. Now truncate ICMP6 packet to a reasonable length if the original packets has a final protocol header directly after the IPv6 header. List of suitable protocols contains TCP, UDP, ESP as they cover the common cases and anything behind the header should not be needed for path MTU discovery. OK deraadt@ florian@ mvs@ --- sys/netinet6/ip6_forward.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c index 14c29569099..790fb842e56 100644 --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_forward.c,v 1.121 2024/07/09 09:33:13 bluhm Exp $ */ +/* $OpenBSD: ip6_forward.c,v 1.122 2024/07/13 09:34:26 bluhm Exp $ */ /* $KAME: ip6_forward.c,v 1.75 2001/06/29 12:42:13 jinmei Exp $ */ /* @@ -145,10 +145,33 @@ ip6_forward(struct mbuf *m, struct route *ro, int flags) * Thanks to M_EXT, in most cases copy will not occur. * For small packets copy original onto stack instead of mbuf. * + * 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 4443 2.4. (c), 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. */ - icmp_len = min(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN); + switch (ip6->ip6_nxt) { + case IPPROTO_TCP: + icmp_len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr) + + MAX_TCPOPTLEN; + break; + case IPPROTO_UDP: + icmp_len = sizeof(struct ip6_hdr) + sizeof(struct udphdr); + break; + case IPPROTO_ESP: + icmp_len = sizeof(struct ip6_hdr) + 2 * sizeof(u_int32_t); + break; + default: + icmp_len = ICMPV6_PLD_MAXLEN; + break; + } + if (icmp_len > m->m_pkthdr.len) + icmp_len = m->m_pkthdr.len; if (icmp_len <= sizeof(icmp_buf)) { mflags = m->m_flags; pfflags = m->m_pkthdr.pf.flags; -- 2.20.1