libnet: bridge: ignore EINVAL when configuring bridge MTU

Since 964ab7158c, we explicitly set the bridge MTU if it was specified.
Unfortunately, kernel <v4.17 have a check preventing us to manually set
the MTU to anything greater than 1500 if no links is attached to the
bridge, which is how we do things -- create the bridge, set its MTU and
later on, attach veths to it.

Relevant kernel commit: 804b854d37

As we still have to support CentOS/RHEL 7 (and their old v3.10 kernels)
for a few more months, we need to ignore EINVAL if the MTU is > 1500
(but <= 65535).

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
(cherry picked from commit 89470a7114)
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
This commit is contained in:
Albin Kerouanton 2024-02-02 18:01:54 +01:00
parent 5b5a58b2cd
commit 7a659049b8
2 changed files with 44 additions and 0 deletions

View file

@ -2,9 +2,11 @@ package bridge
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"syscall"
"github.com/containerd/log"
"github.com/docker/docker/libnetwork/netutils"
@ -47,6 +49,14 @@ func setupDevice(config *networkConfiguration, i *bridgeInterface) error {
func setupMTU(config *networkConfiguration, i *bridgeInterface) error {
if err := i.nlh.LinkSetMTU(i.Link, config.Mtu); err != nil {
// Before Linux v4.17, bridges couldn't be configured "manually" with an MTU greater than 1500, although it
// could be autoconfigured with such a value when interfaces were added to the bridge. In that case, the
// bridge MTU would be set automatically by the kernel to the lowest MTU of all interfaces attached. To keep
// compatibility with older kernels, we need to discard -EINVAL.
// TODO(aker): remove this once we drop support for CentOS/RHEL 7.
if config.Mtu > 1500 && config.Mtu <= 0xFFFF && errors.Is(err, syscall.EINVAL) {
return nil
}
log.G(context.TODO()).WithError(err).Errorf("Failed to set bridge MTU %s via netlink", config.BridgeName)
return err
}

View file

@ -3,11 +3,13 @@ package bridge
import (
"bytes"
"net"
"syscall"
"testing"
"github.com/docker/docker/internal/testutils/netnsutils"
"github.com/docker/docker/libnetwork/netutils"
"github.com/vishvananda/netlink"
"gotest.tools/v3/assert"
)
func TestSetupNewBridge(t *testing.T) {
@ -92,3 +94,35 @@ func TestGenerateRandomMAC(t *testing.T) {
t.Fatalf("Generated twice the same MAC address %v", mac1)
}
}
func TestMTUBiggerThan1500(t *testing.T) {
defer netnsutils.SetupTestOSContext(t)()
nh, err := netlink.NewHandle()
if err != nil {
t.Fatal(err)
}
defer nh.Close()
config := &networkConfiguration{BridgeName: DefaultBridgeName, Mtu: 9000}
br := &bridgeInterface{nlh: nh}
assert.NilError(t, setupDevice(config, br))
assert.NilError(t, setupMTU(config, br))
}
func TestMTUBiggerThan64K(t *testing.T) {
defer netnsutils.SetupTestOSContext(t)()
nh, err := netlink.NewHandle()
if err != nil {
t.Fatal(err)
}
defer nh.Close()
config := &networkConfiguration{BridgeName: DefaultBridgeName, Mtu: 65536}
br := &bridgeInterface{nlh: nh}
assert.NilError(t, setupDevice(config, br))
assert.ErrorIs(t, setupMTU(config, br), syscall.EINVAL)
}