Index | Thread | Search

From:
Sergii Rudchenko <sergii@rudchenko.nl>
Subject:
octeon: fix cnmac corrupting VLAN packets transmitted by veb
To:
tech@openbsd.org
Date:
Mon, 13 Jul 2026 23:30:50 +0200

Download raw body.

Thread
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)