diff --git a/sys/net/if_tuntap.c b/sys/net/if_tuntap.c --- a/sys/net/if_tuntap.c +++ b/sys/net/if_tuntap.c @@ -97,6 +97,7 @@ #endif #include #include +#include #include #include #include @@ -144,6 +145,8 @@ struct ether_addr tun_ether; /* remote address */ int tun_busy; /* busy count */ int tun_vhdrlen; /* virtio-net header length */ + struct lro_ctrl tun_lro; /* for TCP LRO */ + bool tun_lro_ready; /* TCP LRO initialized */ }; #define TUN2IFP(sc) ((sc)->tun_ifp) @@ -978,7 +981,8 @@ IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ifp->if_capabilities |= IFCAP_LINKSTATE; if ((tp->tun_flags & TUN_L2) != 0) - ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6; + ifp->if_capabilities |= + IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6 | IFCAP_LRO; ifp->if_capenable |= IFCAP_LINKSTATE; if ((tp->tun_flags & TUN_L2) != 0) { @@ -1175,6 +1179,12 @@ (l2tun && (ifp->if_flags & IFF_LINK0) != 0)) goto out; + if (l2tun && tp->tun_lro_ready) { + TUNDEBUG (ifp, "LRO disabled\n"); + tcp_lro_free(&tp->tun_lro); + tp->tun_lro_ready = false; + } + if (ifp->if_flags & IFF_UP) { TUN_UNLOCK(tp); if_down(ifp); @@ -1219,6 +1229,14 @@ getmicrotime(&ifp->if_lastchange); TUN_UNLOCK(tp); } else { + if (tcp_lro_init(&tp->tun_lro) == 0) { + TUNDEBUG(ifp, "LRO enabled\n"); + tp->tun_lro.ifp = ifp; + tp->tun_lro_ready = true; + } else { + TUNDEBUG(ifp, "Could not enable LRO\n"); + tp->tun_lro_ready = false; + } ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; TUN_UNLOCK(tp); /* attempt to start output */ @@ -1765,6 +1783,7 @@ struct epoch_tracker et; struct ether_header *eh; struct ifnet *ifp; + int result; ifp = TUN2IFP(tp); @@ -1820,7 +1839,15 @@ /* Pass packet up to parent. */ CURVNET_SET(ifp->if_vnet); NET_EPOCH_ENTER(et); - (*ifp->if_input)(ifp, m); + if (tp->tun_lro_ready && ifp->if_capenable & IFCAP_LRO) { + result = tcp_lro_rx(&tp->tun_lro, m, 0); + TUNDEBUG(ifp, "tcp_lro_rx() returned %d\n", result); + } else + result = TCP_LRO_CANNOT; + if (result == 0) + tcp_lro_flush_all(&tp->tun_lro); + else + (*ifp->if_input)(ifp, m); NET_EPOCH_EXIT(et); CURVNET_RESTORE(); /* ibytes are counted in parent */