On Mon, Nov 02, 2026 at 04:44:23PM +0100, Samuel Thibault wrote:
> Samuel Thibault, le lun. 02 nov. 2026 16:37:20 +0100, a ecrit:
> > Stuart Henderson, le lun. 02 nov. 2026 15:23:03 +0000, a ecrit:
> > > On 2026/11/02 16:15, Samuel Thibault wrote:
> > > > Stuart Henderson, le lun. 02 nov. 2026 13:20:19 +0000, a ecrit:
> > > > > On 2026-11-01, Samuel Thibault <[email protected]> wrote:
> > > > > > With the disappearance of the trunk(4), we have lost the
> > > > > > loadbalance/roundrobin options. Since the upgrade we are seeing our
> > > > > > router send all its trafic only on one interface.
> > > > >
> > > > > hmm? trunk(4) is still there for failover/loadbalance/roundrobin.
> > > > > The only thing that was removed is trunk(4)'s LACP mode.
> > > >
> > > > And we use that, so in the immediate term, we cannot use trunk any more.
> > >
> > > In that case, I don't understand why you said "we have lost the
> > > loadbalance/roundrobin options"?
> >
> > Well, because we cannot use trunk any more with LACP?
> >
> > To recap:
> >
> > - if we use aggr(4), openbsd won't announce LACP, so the switch
> > fallsback to only one link, and thus will not enable any
> > failover/loadbalance on its side. Not having even failover is rather a
> > no-go.
> >
> > - if we use trunk(4), we won't have the loadbalance option on the
> > openbsd side.
>
> Ergl, of course I mixed the two up, sorry...
>
> To recap:
>
> - if we use trunk(4), openbsd won't announce LACP, so the switch
> fallsback to only one link, and thus will not enable any
> failover/loadbalance on its side. Not having even failover is rather a
> no-go.
>
> - if we use aggr(4), we won't have the loadbalance option on the
loadbalance, failover, and lacp are all distinct trunk operating
modes. it was never possible to set a combination of these modes
on trunk(4). aggr(4) only supported the lacp operating mode, and that
mode has now been removed from trunk(4).
the difference here is that trunk(4) parsed and hashed every packet as a
matter of course to try and pick which port to put a packet on. aggr(4)
avoids that work by relying on mbuf flowids, but mbuf flowids arent
always set.
aggr(4) is supposed to try and fake a flowid based on the port interface
indexes, but it looks like it was missing a line to set the M_FLOWID
flag when it did this.
what physical interfaces are you using as ports in aggr(4) (and
trunk(4))? generally, if an interface supports multiple rx rings, it
will set a flowid on mbufs for you.
the diff below tries to fix the M_FLOWID setting in aggr_port_input(),
and adds a link0 flag that enables packet parsing/hashing in a similar
style to what trunk(4) did. you'll have to "ifconfig aggr0 link0" to
enable it.
Index: net/if_aggr.c
===================================================================
RCS file: /cvs/src/sys/net/if_aggr.c,v
diff -u -p -r1.53 if_aggr.c
--- net/if_aggr.c 2 Dec 2025 03:50:56 -0000 1.53
+++ net/if_aggr.c 3 Nov 2026 03:50:34 -0000
@@ -78,6 +78,12 @@
#include <netinet/in.h>
#include <netinet/if_ether.h>
+#include <netinet/ip.h> /* for ether_extracted stuff */
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <net/toeplitz.h>
+
#include <crypto/siphash.h> /* if_trunk.h uses siphash bits */
#include <net/if_trunk.h>
@@ -674,12 +680,48 @@ aggr_port_moved(struct aggr_softc *sc, s
return (0);
}
+static int
+aggr_pkt_hash(struct mbuf *m)
+{
+ struct ether_extracted ext;
+ uint32_t hash = 0;
+
+ ether_extract_headers(m, &ext);
+
+ if (ext.ip4) {
+ hash ^= ext.ip4->ip_src.s_addr;
+ hash ^= ext.ip4->ip_dst.s_addr;
+ } else if (ext.ip6) {
+ hash ^= ext.ip6->ip6_src.s6_addr32[0];
+ hash ^= ext.ip6->ip6_src.s6_addr32[1];
+ hash ^= ext.ip6->ip6_src.s6_addr32[2];
+ hash ^= ext.ip6->ip6_src.s6_addr32[3];
+ hash ^= ext.ip6->ip6_dst.s6_addr32[0];
+ hash ^= ext.ip6->ip6_dst.s6_addr32[1];
+ hash ^= ext.ip6->ip6_dst.s6_addr32[2];
+ hash ^= ext.ip6->ip6_dst.s6_addr32[3];
+ } else {
+ return (-1);
+ }
+
+ if (ext.tcp) {
+ hash ^= ext.tcp->th_sport;
+ hash ^= ext.tcp->th_dport;
+ } else if (ext.udp) {
+ hash ^= ext.udp->uh_sport;
+ hash ^= ext.udp->uh_dport;
+ }
+
+ return stoeplitz_n32(hash);
+}
+
+
static void
aggr_transmit(struct aggr_softc *sc, const struct aggr_map *map, struct mbuf
*m)
{
struct ifnet *ifp = &sc->sc_if;
struct ifnet *ifp0;
- uint16_t flow = 0;
+ int flowid = -1;
#if NBPFILTER > 0
{
@@ -690,9 +732,22 @@ aggr_transmit(struct aggr_softc *sc, con
#endif
if (ISSET(m->m_pkthdr.csum_flags, M_FLOWID))
- flow = m->m_pkthdr.ph_flowid >> AGGR_FLOWID_SHIFT;
+ flowid = m->m_pkthdr.ph_flowid;
+ else if (ISSET(ifp->if_flags, IFF_LINK0)) {
+ flowid = aggr_pkt_hash(m);
+ if (flowid != -1) {
+ /* may as well let other parts of the stack use it */
+ m->m_pkthdr.ph_flowid = flowid;
+ SET(m->m_pkthdr.csum_flags, M_FLOWID);
+ }
+ }
+ if (flowid == -1) {
+ flowid = (m->m_pkthdr.ph_ifidx << AGGR_FLOWID_SHIFT) ^
+ sc->sc_mix;
+ }
- ifp0 = map->m_ifp0s[flow % AGGR_MAX_PORTS];
+ flowid >>= AGGR_FLOWID_SHIFT;
+ ifp0 = map->m_ifp0s[flowid % AGGR_MAX_PORTS];
if (if_enqueue(ifp0, m) != 0)
counters_inc(ifp->if_counters, ifc_oerrors);
@@ -884,8 +939,18 @@ aggr_port_input(struct ifnet *ifp0, stru
if (__predict_false(!p->p_collecting))
goto drop;
- if (!ISSET(m->m_pkthdr.csum_flags, M_FLOWID))
- m->m_pkthdr.ph_flowid = ifp0->if_index ^ sc->sc_mix;
+ if (!ISSET(m->m_pkthdr.csum_flags, M_FLOWID)) {
+ int flowid = -1;
+ if (ISSET(ifp->if_flags, IFF_LINK0))
+ flowid = aggr_pkt_hash(m);
+ if (flowid == -1) {
+ flowid = (ifp0->if_index << AGGR_FLOWID_SHIFT) ^
+ sc->sc_mix;
+ }
+
+ m->m_pkthdr.ph_flowid = flowid;
+ SET(m->m_pkthdr.csum_flags, M_FLOWID);
+ }
if_vinput(ifp, m, ns);
Index: conf/files
===================================================================
RCS file: /cvs/src/sys/conf/files,v
diff -u -p -r1.752 files
--- conf/files 16 Mar 2026 14:57:34 -0000 1.752
+++ conf/files 3 Nov 2026 03:50:34 -0000
@@ -603,7 +603,7 @@ pseudo-device sec: ifnet
pseudo-device gre: ifnet, ether, etherbridge
pseudo-device crypto: ifnet
pseudo-device trunk: ifnet, ether, ifmedia
-pseudo-device aggr: ifnet, ether, ifmedia
+pseudo-device aggr: ifnet, ether, ifmedia, stoeplitz
pseudo-device tpmr: ifnet, ether, ifmedia
pseudo-device mpe: ifnet, mpls
pseudo-device mpw: ifnet, mpls, ether