From: Sergii Rudchenko Subject: octeon: fix cnmac corrupting VLAN packets transmitted by veb To: tech@openbsd.org Date: Mon, 13 Jul 2026 23:30:50 +0200 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)