aboutsummaryrefslogtreecommitdiff
path: root/sys/arm64
diff options
context:
space:
mode:
authorMike Karels <karels@FreeBSD.org>2020-05-30 02:09:36 +0000
committerMike Karels <karels@FreeBSD.org>2020-05-30 02:09:36 +0000
commit51cefda17055328ac1e7ec8456a81ec7600cf8dd (patch)
treeabe50d2b3fb7556c76a069ef77052f357aa980f2 /sys/arm64
parent0add2a52299c9a75600b388a1cf47996cfc76655 (diff)
downloadsrc-51cefda17055328ac1e7ec8456a81ec7600cf8dd.tar.gz
src-51cefda17055328ac1e7ec8456a81ec7600cf8dd.zip
genet: workaround for problem with ICMPv6 echo replies
The ICMPv6 echo reply is constructed with the IPv6 header too close to the beginning of a packet for an Ethernet header to be prepended, so we end up with an mbuf containing just the Ethernet header. The GENET controller doesn't seem to handle this, with or without transmit checksum offload. At least until we have chip documentation, do a pullup to satisfy the chip. Hopefully this can be fixed properly in the future.
Notes
Notes: svn path=/head/; revision=361642
Diffstat (limited to 'sys/arm64')
-rw-r--r--sys/arm64/broadcom/genet/if_genet.c34
1 files changed, 34 insertions, 0 deletions
diff --git a/sys/arm64/broadcom/genet/if_genet.c b/sys/arm64/broadcom/genet/if_genet.c
index b20cf47e0e81..3e39962f5115 100644
--- a/sys/arm64/broadcom/genet/if_genet.c
+++ b/sys/arm64/broadcom/genet/if_genet.c
@@ -76,6 +76,10 @@ __FBSDID("$FreeBSD$");
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
+#define ICMPV6_HACK /* workaround for chip issue */
+#ifdef ICMPV6_HACK
+#include <netinet/icmp6.h>
+#endif
#include "syscon_if.h"
#include "miibus_if.h"
@@ -968,6 +972,36 @@ gen_encap(struct gen_softc *sc, struct mbuf **mp)
q = &sc->tx_queue[DEF_TXQUEUE];
m = *mp;
+#ifdef ICMPV6_HACK
+ /*
+ * 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.
+ */
+#define ICMP6_LEN (sizeof(struct ether_header) + sizeof(struct ip6_hdr) + \
+ sizeof(struct icmp6_hdr))
+ 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_next->m_len >= sizeof(struct ip6_hdr)) {
+ struct ip6_hdr *ip6;
+
+ ip6 = mtod(m->m_next, struct ip6_hdr *);
+ if (ip6->ip6_nxt == IPPROTO_ICMPV6) {
+ m = m_pullup(m,
+ MIN(m->m_pkthdr.len, ICMP6_LEN));
+ if (m == NULL) {
+ if (sc->ifp->if_flags & IFF_DEBUG)
+ device_printf(sc->dev,
+ "ICMPV6 pullup fail\n");
+ *mp = NULL;
+ return (ENOMEM);
+ }
+ }
+ }
+ }
+#undef ICMP6_LEN
+#endif
if ((if_getcapenable(sc->ifp) & (IFCAP_TXCSUM | IFCAP_TXCSUM_IPV6)) !=
0) {
csum_flags = m->m_pkthdr.csum_flags;