vxlan: correct interface MTU when using hw offloads

Otherwise it breaks when offloading like checksum or TSO are used,
because second (encapsulated) ip_output() processing passes fragments of
the encapsulated packet down to the hardware interface.

Diagnosed by:	hselasky
Reviewed by:	np
Sponsored by:	Nvidia Networking / Mellanox Technologies
MFC after:	1 week
Differential revision:	https://reviews.freebsd.org/D29501
This commit is contained in:
Konstantin Belousov 2021-03-29 12:03:07 +03:00
parent e243367b64
commit baacf70137
2 changed files with 28 additions and 4 deletions

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd September 17, 2020
.Dd March 30, 2021
.Dt VXLAN 4
.Os
.Sh NAME
@ -175,13 +175,24 @@ The
.Nm
specification recommends the physical network MTU be configured
to use jumbo frames to accommodate the encapsulated frame size.
.Pp
By default, the
.Nm
driver sets its MTU to usual ethernet MTU of 1500 bytes, reduced by
the size of vxlan headers prepended to the encapsulated packets.
.Pp
Alternatively, the
.Xr ifconfig 8
.Cm mtu
command may be used to reduce the MTU size on the
command may be used to set the fixed MTU size on the
.Nm
interface to allow the encapsulated frame to fit in the
current MTU of the physical network.
If the
.Cm mtu
command was used, system no longer adjust the
.Nm
interface MTU on routing or address changes.
.Sh HARDWARE
The
.Nm

View File

@ -171,6 +171,7 @@ struct vxlan_softc {
#define VXLAN_FLAG_INIT 0x0001
#define VXLAN_FLAG_TEARDOWN 0x0002
#define VXLAN_FLAG_LEARN 0x0004
#define VXLAN_FLAG_USER_MTU 0x0008
uint32_t vxl_port_hash_key;
uint16_t vxl_min_port;
@ -1620,6 +1621,8 @@ vxlan_setup_interface_hdrlen(struct vxlan_softc *sc)
{
struct ifnet *ifp;
VXLAN_LOCK_WASSERT(sc);
ifp = sc->vxl_ifp;
ifp->if_hdrlen = ETHER_HDR_LEN + sizeof(struct vxlanudphdr);
@ -1627,6 +1630,9 @@ vxlan_setup_interface_hdrlen(struct vxlan_softc *sc)
ifp->if_hdrlen += sizeof(struct ip);
else if (VXLAN_SOCKADDR_IS_IPV6(&sc->vxl_dst_addr) != 0)
ifp->if_hdrlen += sizeof(struct ip6_hdr);
if ((sc->vxl_flags & VXLAN_FLAG_USER_MTU) == 0)
ifp->if_mtu = ETHERMTU - ifp->if_hdrlen;
}
static int
@ -2354,10 +2360,14 @@ vxlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
case SIOCSIFMTU:
if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > VXLAN_MAX_MTU)
if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > VXLAN_MAX_MTU) {
error = EINVAL;
else
} else {
VXLAN_WLOCK(sc);
ifp->if_mtu = ifr->ifr_mtu;
sc->vxl_flags |= VXLAN_FLAG_USER_MTU;
VXLAN_WUNLOCK(sc);
}
break;
case SIOCSIFCAP:
@ -3211,7 +3221,10 @@ vxlan_clone_create(struct if_clone *ifc, int unit, caddr_t params)
ether_ifattach(ifp, sc->vxl_hwaddr.octet);
ifp->if_baudrate = 0;
VXLAN_WLOCK(sc);
vxlan_setup_interface_hdrlen(sc);
VXLAN_WUNLOCK(sc);
return (0);