diff --git a/internal/app/machined/pkg/controllers/network/address_config.go b/internal/app/machined/pkg/controllers/network/address_config.go index 8473fd914f..c9819d6148 100644 --- a/internal/app/machined/pkg/controllers/network/address_config.go +++ b/internal/app/machined/pkg/controllers/network/address_config.go @@ -293,7 +293,7 @@ func (ctrl *AddressConfigController) processDevicesConfiguration(logger *zap.Log address := network.AddressSpecSpec{ Address: ipPrefix, Scope: nethelpers.ScopeGlobal, - LinkName: fmt.Sprintf("%s.%d", device.Interface(), vlan.ID()), + LinkName: nethelpers.VLANLinkName(device.Interface(), vlan.ID()), ConfigLayer: network.ConfigMachineConfiguration, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), } diff --git a/internal/app/machined/pkg/controllers/network/cmdline.go b/internal/app/machined/pkg/controllers/network/cmdline.go index 6f5e38112f..b16b2ffa09 100644 --- a/internal/app/machined/pkg/controllers/network/cmdline.go +++ b/internal/app/machined/pkg/controllers/network/cmdline.go @@ -351,7 +351,7 @@ func ParseCmdlineNetwork(cmdline *procfs.Cmdline) (CmdlineNetworking, error) { Protocol: nethelpers.VLANProtocol8021Q, } - vlanName = fmt.Sprintf("%s.%d", phyDevice, vlanID) + vlanName = nethelpers.VLANLinkName(phyDevice, uint16(vlanID)) linkSpecUpdated := false diff --git a/internal/app/machined/pkg/controllers/network/link_config.go b/internal/app/machined/pkg/controllers/network/link_config.go index a3d8f884b2..08b1a65d73 100644 --- a/internal/app/machined/pkg/controllers/network/link_config.go +++ b/internal/app/machined/pkg/controllers/network/link_config.go @@ -358,7 +358,7 @@ func (ctrl *LinkConfigController) processDevicesConfiguration(logger *zap.Logger } for _, vlan := range device.Vlans() { - vlanName := fmt.Sprintf("%s.%d", device.Interface(), vlan.ID()) + vlanName := nethelpers.VLANLinkName(device.Interface(), vlan.ID()) linkMap[vlanName] = &network.LinkSpecSpec{ Name: device.Interface(), @@ -403,7 +403,7 @@ type vlaner interface { } func vlanLink(link *network.LinkSpecSpec, linkName string, vlan vlaner) { - link.Name = fmt.Sprintf("%s.%d", linkName, vlan.ID()) + link.Name = nethelpers.VLANLinkName(linkName, vlan.ID()) link.Logical = true link.Up = true link.MTU = vlan.MTU() diff --git a/internal/app/machined/pkg/controllers/network/operator_config.go b/internal/app/machined/pkg/controllers/network/operator_config.go index aa8cb37f0f..506fa5f380 100644 --- a/internal/app/machined/pkg/controllers/network/operator_config.go +++ b/internal/app/machined/pkg/controllers/network/operator_config.go @@ -17,6 +17,7 @@ import ( "go.uber.org/zap" talosconfig "github.com/siderolabs/talos/pkg/machinery/config/config" + "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) @@ -176,7 +177,7 @@ func (ctrl *OperatorConfigController) Run(ctx context.Context, r controller.Runt specs = append(specs, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, - LinkName: fmt.Sprintf("%s.%d", device.Interface(), vlan.ID()), + LinkName: nethelpers.VLANLinkName(device.Interface(), vlan.ID()), RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: routeMetric, @@ -193,7 +194,7 @@ func (ctrl *OperatorConfigController) Run(ctx context.Context, r controller.Runt specs = append(specs, network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, - LinkName: fmt.Sprintf("%s.%d", device.Interface(), vlan.ID()), + LinkName: nethelpers.VLANLinkName(device.Interface(), vlan.ID()), RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: routeMetric, diff --git a/internal/app/machined/pkg/controllers/network/operator_vip_config.go b/internal/app/machined/pkg/controllers/network/operator_vip_config.go index e8503272c7..5b4c70f66d 100644 --- a/internal/app/machined/pkg/controllers/network/operator_vip_config.go +++ b/internal/app/machined/pkg/controllers/network/operator_vip_config.go @@ -19,6 +19,7 @@ import ( "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/operator/vip" talosconfig "github.com/siderolabs/talos/pkg/machinery/config/config" + "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) @@ -118,7 +119,7 @@ func (ctrl *OperatorVIPConfigController) Run(ctx context.Context, r controller.R for _, vlan := range device.Vlans() { if vlan.VIPConfig() != nil { - linkName := fmt.Sprintf("%s.%d", device.Interface(), vlan.ID()) + linkName := nethelpers.VLANLinkName(device.Interface(), vlan.ID()) if spec, specErr := handleVIP(ctx, vlan.VIPConfig(), linkName, logger); specErr != nil { specErrors = multierror.Append(specErrors, specErr) } else { diff --git a/internal/app/machined/pkg/controllers/network/route_config.go b/internal/app/machined/pkg/controllers/network/route_config.go index cd0ae824ec..4526b7d1a4 100644 --- a/internal/app/machined/pkg/controllers/network/route_config.go +++ b/internal/app/machined/pkg/controllers/network/route_config.go @@ -307,7 +307,7 @@ func (ctrl *RouteConfigController) processDevicesConfiguration(logger *zap.Logge } for _, vlan := range device.Vlans() { - vlanLinkName := fmt.Sprintf("%s.%d", device.Interface(), vlan.ID()) + vlanLinkName := nethelpers.VLANLinkName(device.Interface(), vlan.ID()) for _, route := range vlan.Routes() { routeSpec, err := convert(vlanLinkName, route) diff --git a/pkg/machinery/nethelpers/vlan.go b/pkg/machinery/nethelpers/vlan.go new file mode 100644 index 0000000000..1518571bce --- /dev/null +++ b/pkg/machinery/nethelpers/vlan.go @@ -0,0 +1,32 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package nethelpers + +import ( + "crypto/sha256" + "fmt" +) + +const maxLinkNameLength = 15 + +// VLANLinkName builds a VLAN link name out of the base device name and VLAN ID. +// +// The function takes care of the maximum length of the link name. +func VLANLinkName(base string, vlanID uint16) string { + // VLAN ID is actually 12-bit, so the allowed values are 0-4095. + // In ".%d" format, vlanID can be up to 5 characters long. + if len(base)+5 <= maxLinkNameLength { + return fmt.Sprintf("%s.%d", base, vlanID) + } + + // If the base name is too long, we need to truncate it, but simply + // truncating might lead to ambiguous link name, so take some hash of the original + // name. + prefix := base[:4] + + hash := sha256.Sum256([]byte(base)) + + return fmt.Sprintf("%s%x.%d", prefix, hash[:(maxLinkNameLength-len(prefix)-5)/2], vlanID) +} diff --git a/pkg/machinery/nethelpers/vlan_test.go b/pkg/machinery/nethelpers/vlan_test.go new file mode 100644 index 0000000000..4dfcc2a5d3 --- /dev/null +++ b/pkg/machinery/nethelpers/vlan_test.go @@ -0,0 +1,70 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package nethelpers_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/siderolabs/talos/pkg/machinery/nethelpers" +) + +func TestVLANLinkName(t *testing.T) { + t.Parallel() + + for _, test := range []struct { + base string + vlanID uint16 + + expected string + }{ + { + base: "eth0", + vlanID: 1, + + expected: "eth0.1", + }, + { + base: "en9s0", + vlanID: 4095, + + expected: "en9s0.4095", + }, + { + base: "0123456789", + vlanID: 4095, + + expected: "0123456789.4095", + }, + { + base: "enx12545f8c99cd", + vlanID: 25, + + expected: "enx1ee6413.25", + }, + { + base: "enx12545f8c99cd", + vlanID: 4095, + + expected: "enx1ee6413.4095", + }, + { + base: "enx12545f8c99ce", + vlanID: 4095, + + expected: "enx1ef972f.4095", + }, + } { + test := test + + t.Run(fmt.Sprintf("%s.%d", test.base, test.vlanID), func(t *testing.T) { + t.Parallel() + + assert.Equal(t, test.expected, nethelpers.VLANLinkName(test.base, test.vlanID)) + }) + } +}