Download raw body.
octeon: fix cnmac corrupting VLAN packets transmitted by veb
On an EdgeRouter 6P, cnmac(4) corrupts VLAN tagged TCP and UDP packets
transmitted by veb(4).
It happens because the TCP/UDP checksum offloading setup in cnmac(4) assumes
that IP packet starts right after ethernet header:
ipoffp1 = (m->m_pkthdr.csum_flags & (M_TCP_CSUM_OUT |
M_UDP_CSUM_OUT))
? (ETHER_HDR_LEN + 1) : 0;
For VLAN-encapsulated packets it produces a wrong checksum written by a
wrong
offset, resulting in corrupt TCP and UDP packets.
vlan(4) are not affected by this problem because unline veb(4) it does not
declare IFCAP_CSUM_* capabilities and all checksums are calculated
before the
packet reaches cnmac(4).
Test setup (on the EdgeRouter):
# ifconfig vport0 inet 192.168.0.1/24
# ifconfig veb0 add cnmac1 tagged cnmac1 1 -untagged cnmac1
# ifconfig veb0 add vport0 tagged vport0 1 untagged vport0 1 up
# ifconfig cnmac1 up
# nc -l 8000
On a peer machine with 192.168.0.2/24 assigned to a VLAN trunk interface
connected to EdgeRouter:
$ echo Test | nc 192.168.0.1 8000
Expected result: nc(1) on the client completes immediately and nc(1) the
EdgeRouter prints "Test" and completes.
Actual result: nc(1) on the client hangs, nc(1) on on the EdgeRouter
does not
print anything. Examining the traffic on the client with Wireshark shows
that
TCP SYN,SYN_ACK packets from EdgeRouter have an invalid TCP option.
Comparing
to tcpdump on cnmac1 reveals two altered bytes at offsets 0x46 and 0x47.
My take on fixing it:
Index: arch/octeon/dev/if_cnmac.c
===================================================================
RCS file: /cvs/src/sys/arch/octeon/dev/if_cnmac.c,v
diff -u -p -u -r1.91 if_cnmac.c
--- arch/octeon/dev/if_cnmac.c 19 Jun 2026 15:12:10 -0000 1.91
+++ arch/octeon/dev/if_cnmac.c 13 Jul 2026 21:24:23 -0000
@@ -868,10 +868,13 @@ int
cnmac_send_makecmd(struct cnmac_softc *sc, struct mbuf *m,
uint64_t *gbuf, uint64_t *rpko_cmd_w0, uint64_t *rpko_cmd_w1)
{
+ struct ether_header* eh;
+ struct ether_vlan_header* evh;
uint64_t pko_cmd_w0, pko_cmd_w1;
- int ipoffp1;
+ int ipoffp1 = 0;
int segs;
int result = 0;
+ u_int16_t ether_type;
if (cnmac_send_makecmd_gbuf(sc, m, gbuf, &segs)) {
log(LOG_WARNING, "%s: large number of transmission"
@@ -881,8 +884,19 @@ cnmac_send_makecmd(struct cnmac_softc *s
}
/* Get the IP packet offset for TCP/UDP checksum offloading. */
- ipoffp1 = (m->m_pkthdr.csum_flags & (M_TCP_CSUM_OUT |
M_UDP_CSUM_OUT))
- ? (ETHER_HDR_LEN + 1) : 0;
+ if (m->m_pkthdr.csum_flags & (M_TCP_CSUM_OUT | M_UDP_CSUM_OUT)) {
+ eh = mtod(m, struct ether_header *);
+ ether_type = ntohs(eh->ether_type);
+
+ if (ether_type == ETHERTYPE_IP) {
+ ipoffp1 = ETHER_HDR_LEN + 1;
+ } else if (ether_type == ETHERTYPE_VLAN) {
+ evh = mtod(m, struct ether_vlan_header*);
+ if (ntohs(evh->evl_proto) == ETHERTYPE_IP) {
+ ipoffp1 = sizeof(*evh) + 1;
+ }
+ }
+ }
/*
* segs == 1 -> link mode (single continuous buffer)
octeon: fix cnmac corrupting VLAN packets transmitted by veb