genet: pullup minimum header amount for IPv4

The genet driver (RPi4 Ethernet) had code to pull headers into the
first mbuf if there was only an Ethernet header there.  This was
originally needed for ICMPv6 replies, then for forwarded IPv6/TCP.
Now a situation has been found where it is needed for IPv4, when
using NAT with IPFW.  Generalize to do this for all protocols.
Rather than using an IPv6-related definition for the length, move
the length to a variable that can be set with sysctl
(hw.genet.tx_hdr_min).  Move an old tunable to a new RDTUN variable
with a better name.

PR:		25607
MFC after:	3 days
Reviewers:	emaste
Differential Revision: https://reviews.freebsd.org/D30831
This commit is contained in:
Mike Karels 2021-06-20 12:50:31 -05:00
parent 5dd84e315a
commit 184291b0a5

View File

@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/module.h>
#include <sys/taskqueue.h>
#include <sys/gpio.h>
@ -97,9 +98,27 @@ __FBSDID("$FreeBSD$");
#define TX_MAX_SEGS 20
/* Maximum number of mbufs to send to if_input */
static SYSCTL_NODE(_hw, OID_AUTO, genet, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"genet driver parameters");
/* Maximum number of mbufs to pass per call to if_input */
static int gen_rx_batch = 16 /* RX_BATCH_DEFAULT */;
TUNABLE_INT("hw.gen.rx_batch", &gen_rx_batch);
SYSCTL_INT(_hw_genet, OID_AUTO, rx_batch, CTLFLAG_RDTUN,
&gen_rx_batch, 0, "max mbufs per call to if_input");
TUNABLE_INT("hw.gen.rx_batch", &gen_rx_batch); /* old name/interface */
/*
* Transmitting packets with only an Ethernet header in the first mbuf
* fails. Examples include reflected ICMPv6 packets, e.g. echo replies;
* forwarded IPv6/TCP packets; and forwarded IPv4/TCP packets that use NAT
* with IPFW. Pulling up the sizes of ether_header + ip6_hdr + icmp6_hdr
* seems to work for both ICMPv6 and TCP over IPv6, as well as the IPv4/TCP
* case.
*/
static int gen_tx_hdr_min = 56; /* ether_header + ip6_hdr + icmp6_hdr */
SYSCTL_INT(_hw_genet, OID_AUTO, tx_hdr_min, CTLFLAG_RW,
&gen_tx_hdr_min, 0, "header to add to packets with ether header only");
static struct ofw_compat_data compat_data[] = {
{ "brcm,genet-v1", 1 },
@ -995,31 +1014,19 @@ gen_encap(struct gen_softc *sc, struct mbuf **mp)
m = *mp;
/*
* Reflected ICMPv6 packets, e.g. echo replies, tend to get laid
* out with only the Ethernet header in the first mbuf, and this
* doesn't seem to work. Forwarded TCP packets over IPv6 also
* fail if laid out with only the Ethernet header in the first mbuf.
* For now, pull up any IPv6 packet with that layout. Maybe IPv4
* needs it but we haven't run into it. Pulling up the sizes of
* ether_header + ip6_header + icmp6_hdr seems to work for both
* ICMPv6 and TCP over IPv6.
* Don't attempt to send packets with only an Ethernet header in
* first mbuf; see comment above with gen_tx_hdr_min.
*/
#define IP6_PULLUP_LEN (sizeof(struct ether_header) + \
sizeof(struct ip6_hdr) + 8)
if (m->m_len == sizeof(struct ether_header)) {
int ether_type = mtod(m, struct ether_header *)->ether_type;
if (ntohs(ether_type) == ETHERTYPE_IPV6) {
m = m_pullup(m, MIN(m->m_pkthdr.len, IP6_PULLUP_LEN));
if (m == NULL) {
if (sc->ifp->if_flags & IFF_DEBUG)
device_printf(sc->dev,
"IPV6 pullup fail\n");
*mp = NULL;
return (ENOMEM);
}
m = m_pullup(m, MIN(m->m_pkthdr.len, gen_tx_hdr_min));
if (m == NULL) {
if (sc->ifp->if_flags & IFF_DEBUG)
device_printf(sc->dev,
"header pullup fail\n");
*mp = NULL;
return (ENOMEM);
}
}
#undef IP6_PULLUP_LEN
if ((if_getcapenable(sc->ifp) & (IFCAP_TXCSUM | IFCAP_TXCSUM_IPV6)) !=
0) {