feat: add support for configuring vlan filtering on the bridge

Fixes #8941

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
This commit is contained in:
Andrey Smirnov 2024-06-30 17:33:19 +04:00
parent 2d054ad355
commit cc345c8c94
No known key found for this signature in database
GPG Key ID: FE042E3D4085A811
18 changed files with 1435 additions and 961 deletions

View File

@ -70,6 +70,7 @@ message BondSlave {
// BridgeMasterSpec describes bridge settings if Kind == "bridge".
message BridgeMasterSpec {
STPSpec stp = 1;
BridgeVLANSpec vlan = 2;
}
// BridgeSlave contains a bond's master name and slave index.
@ -77,6 +78,11 @@ message BridgeSlave {
string master_name = 1;
}
// BridgeVLANSpec describes VLAN settings of a bridge.
message BridgeVLANSpec {
bool filtering_enabled = 1;
}
// DHCP4OperatorSpec describes DHCP4 operator options.
message DHCP4OperatorSpec {
uint32 route_metric = 1;

View File

@ -78,6 +78,12 @@ A list of PCI devices can now be obtained via `PCIDevices` resource, e.g. `talos
Talos Linux now shows diagnostics information for common problems related to misconfiguration via `talosctl health` and Talos dashboard.
"""
[notes.bridge]
title = "Bridge Interface"
description = """\
Talos Linux now support configuring 'vlan_filtering' for bridge interfaces.
"""
[make_deps]

View File

@ -36,7 +36,13 @@ func (a bridgeMaster) Encode() ([]byte, error) {
stpEnabled = 1
}
vlanFiltering := 0
if bridge.VLAN.FilteringEnabled {
vlanFiltering = 1
}
encoder.Uint32(unix.IFLA_BR_STP_STATE, uint32(stpEnabled))
encoder.Uint8(unix.IFLA_BR_VLAN_FILTERING, uint8(vlanFiltering))
return encoder.Encode()
}
@ -51,8 +57,11 @@ func (a bridgeMaster) Decode(data []byte) error {
}
for decoder.Next() {
if decoder.Type() == unix.IFLA_BR_STP_STATE {
switch decoder.Type() {
case unix.IFLA_BR_STP_STATE:
bridge.STP.Enabled = decoder.Uint32() == 1
case unix.IFLA_BR_VLAN_FILTERING:
bridge.VLAN.FilteringEnabled = decoder.Uint8() == 1
}
}

View File

@ -0,0 +1,34 @@
// 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 network_test
import (
"testing"
"github.com/stretchr/testify/require"
networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network"
"github.com/siderolabs/talos/pkg/machinery/resources/network"
)
func TestBridgeMasterSpec(t *testing.T) {
spec := network.BridgeMasterSpec{
STP: network.STPSpec{
Enabled: true,
},
VLAN: network.BridgeVLANSpec{
FilteringEnabled: true,
},
}
b, err := networkadapter.BridgeMasterSpec(&spec).Encode()
require.NoError(t, err)
var decodedSpec network.BridgeMasterSpec
require.NoError(t, networkadapter.BridgeMasterSpec(&decodedSpec).Decode(b))
require.Equal(t, spec, decodedSpec)
}

View File

@ -229,6 +229,9 @@ func (suite *LinkConfigSuite) TestMachineConfiguration() {
BridgeSTP: &v1alpha1.STP{
STPEnabled: pointer.To(true),
},
BridgeVLAN: &v1alpha1.BridgeVLAN{
BridgeVLANFiltering: pointer.To(true),
},
},
},
{
@ -348,7 +351,8 @@ func (suite *LinkConfigSuite) TestMachineConfiguration() {
asrt.True(r.TypedSpec().Logical)
asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type)
asrt.Equal(network.LinkKindBridge, r.TypedSpec().Kind)
asrt.Equal(true, r.TypedSpec().BridgeMaster.STP.Enabled)
asrt.True(r.TypedSpec().BridgeMaster.STP.Enabled)
asrt.True(r.TypedSpec().BridgeMaster.VLAN.FilteringEnabled)
case "wireguard0":
asrt.True(r.TypedSpec().Up)
asrt.True(r.TypedSpec().Logical)

View File

@ -702,9 +702,10 @@ func (suite *LinkSpecSuite) TestBridge() {
),
)
// attempt to enable STP
// attempt to enable STP & VLAN filtering
ctest.UpdateWithConflicts(suite, bridge, func(r *network.LinkSpec) error {
r.TypedSpec().BridgeMaster.STP.Enabled = true
r.TypedSpec().BridgeMaster.VLAN.FilteringEnabled = true
return nil
})
@ -720,6 +721,12 @@ func (suite *LinkSpecSuite) TestBridge() {
)
}
if !r.TypedSpec().BridgeMaster.VLAN.FilteringEnabled {
return retry.ExpectedErrorf(
"vlan filtering is not enabled on bridge %s", r.Metadata().ID(),
)
}
return nil
},
)

View File

@ -132,6 +132,9 @@ func SetBridgeMaster(link *network.LinkSpecSpec, bridge talosconfig.Bridge) erro
STP: network.STPSpec{
Enabled: bridge.STP().Enabled(),
},
VLAN: network.BridgeVLANSpec{
FilteringEnabled: bridge.VLAN().FilteringEnabled(),
},
}
return nil

File diff suppressed because it is too large Load Diff

View File

@ -544,6 +544,16 @@ func (m *BridgeMasterSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if m.Vlan != nil {
size, err := m.Vlan.MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
i--
dAtA[i] = 0x12
}
if m.Stp != nil {
size, err := m.Stp.MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
@ -597,6 +607,49 @@ func (m *BridgeSlave) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *BridgeVLANSpec) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *BridgeVLANSpec) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *BridgeVLANSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if m.FilteringEnabled {
i--
if m.FilteringEnabled {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *DHCP4OperatorSpec) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
@ -3751,6 +3804,10 @@ func (m *BridgeMasterSpec) SizeVT() (n int) {
l = m.Stp.SizeVT()
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
if m.Vlan != nil {
l = m.Vlan.SizeVT()
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
n += len(m.unknownFields)
return n
}
@ -3769,6 +3826,19 @@ func (m *BridgeSlave) SizeVT() (n int) {
return n
}
func (m *BridgeVLANSpec) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.FilteringEnabled {
n += 2
}
n += len(m.unknownFields)
return n
}
func (m *DHCP4OperatorSpec) SizeVT() (n int) {
if m == nil {
return 0
@ -6161,6 +6231,42 @@ func (m *BridgeMasterSpec) UnmarshalVT(dAtA []byte) error {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Vlan", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Vlan == nil {
m.Vlan = &BridgeVLANSpec{}
}
if err := m.Vlan.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
@ -6266,6 +6372,77 @@ func (m *BridgeSlave) UnmarshalVT(dAtA []byte) error {
}
return nil
}
func (m *BridgeVLANSpec) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: BridgeVLANSpec: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: BridgeVLANSpec: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field FilteringEnabled", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.FilteringEnabled = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return protohelpers.ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *DHCP4OperatorSpec) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0

View File

@ -245,10 +245,16 @@ type STP interface {
Enabled() bool
}
// BridgeVLAN contains the VLAN settings for a bridge.
type BridgeVLAN interface {
FilteringEnabled() bool
}
// Bridge contains the options for configuring a bridged interface.
type Bridge interface {
Interfaces() []string
STP() STP
VLAN() BridgeVLAN
}
// Vlan represents vlan settings for a device.

View File

@ -734,6 +734,26 @@
"description": "A bridge option.\nPlease see the official kernel documentation.\n",
"markdownDescription": "A bridge option.\nPlease see the official kernel documentation.",
"x-intellij-html-description": "\u003cp\u003eA bridge option.\nPlease see the official kernel documentation.\u003c/p\u003e\n"
},
"vlan": {
"$ref": "#/$defs/v1alpha1.BridgeVLAN",
"title": "vlan",
"description": "A bridge option.\nPlease see the official kernel documentation.\n",
"markdownDescription": "A bridge option.\nPlease see the official kernel documentation.",
"x-intellij-html-description": "\u003cp\u003eA bridge option.\nPlease see the official kernel documentation.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object"
},
"v1alpha1.BridgeVLAN": {
"properties": {
"vlanFiltering": {
"type": "boolean",
"title": "vlanFiltering",
"description": "Whether VLAN filtering is enabled.\n",
"markdownDescription": "Whether VLAN filtering is enabled.",
"x-intellij-html-description": "\u003cp\u003eWhether VLAN filtering is enabled.\u003c/p\u003e\n"
}
},
"additionalProperties": false,

View File

@ -1039,6 +1039,15 @@ func (s *STP) Enabled() bool {
return *s.STPEnabled
}
// FilteringEnabled implements the config.BridgeVLAN interface.
func (v *BridgeVLAN) FilteringEnabled() bool {
if v == nil {
return false
}
return pointer.SafeDeref(v.BridgeVLANFiltering)
}
// Interfaces implements the config.Bridge interface.
func (b *Bridge) Interfaces() []string {
return b.BridgedInterfaces
@ -1053,6 +1062,15 @@ func (b *Bridge) STP() config.STP {
return b.BridgeSTP
}
// VLAN implements the config.Bridge interface.
func (b *Bridge) VLAN() config.BridgeVLAN {
if b.BridgeVLAN == nil {
return (*BridgeVLAN)(nil)
}
return b.BridgeVLAN
}
// Addresses implements the MachineNetwork interface.
func (v *Vlan) Addresses() []string {
switch {

View File

@ -1971,6 +1971,12 @@ type STP struct {
STPEnabled *bool `yaml:"enabled,omitempty"`
}
// BridgeVLAN contains the various options for configuring the VLAN properties of a bridge interface.
type BridgeVLAN struct {
// description: Whether VLAN filtering is enabled.
BridgeVLANFiltering *bool `yaml:"vlanFiltering,omitempty"`
}
// Bridge contains the various options for configuring a bridge interface.
type Bridge struct {
// description: The interfaces that make up the bridge.
@ -1979,6 +1985,10 @@ type Bridge struct {
// A bridge option.
// Please see the official kernel documentation.
BridgeSTP *STP `yaml:"stp,omitempty"`
// description: |
// A bridge option.
// Please see the official kernel documentation.
BridgeVLAN *BridgeVLAN `yaml:"vlan,omitempty"`
}
// VlanList is a list of *Vlan structures with overridden merge process.

View File

@ -2954,6 +2954,31 @@ func (STP) Doc() *encoder.Doc {
return doc
}
func (BridgeVLAN) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "BridgeVLAN",
Comments: [3]string{"" /* encoder.HeadComment */, "BridgeVLAN contains the various options for configuring the VLAN properties of a bridge interface." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "BridgeVLAN contains the various options for configuring the VLAN properties of a bridge interface.",
AppearsIn: []encoder.Appearance{
{
TypeName: "Bridge",
FieldName: "vlan",
},
},
Fields: []encoder.Doc{
{
Name: "vlanFiltering",
Type: "bool",
Note: "",
Description: "Whether VLAN filtering is enabled.",
Comments: [3]string{"" /* encoder.HeadComment */, "Whether VLAN filtering is enabled." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
return doc
}
func (Bridge) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "Bridge",
@ -2980,6 +3005,13 @@ func (Bridge) Doc() *encoder.Doc {
Description: "A bridge option.\nPlease see the official kernel documentation.",
Comments: [3]string{"" /* encoder.HeadComment */, "A bridge option." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "vlan",
Type: "BridgeVLAN",
Note: "",
Description: "A bridge option.\nPlease see the official kernel documentation.",
Comments: [3]string{"" /* encoder.HeadComment */, "A bridge option." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
@ -4077,6 +4109,7 @@ func GetFileDoc() *encoder.FileDoc {
VIPHCloudConfig{}.Doc(),
Bond{}.Doc(),
STP{}.Doc(),
BridgeVLAN{}.Doc(),
Bridge{}.Doc(),
Vlan{}.Doc(),
Route{}.Doc(),

View File

@ -58,7 +58,8 @@ type BondMasterSpec struct {
//
//gotagsrewrite:gen
type BridgeMasterSpec struct {
STP STPSpec `yaml:"stp,omitempty" protobuf:"1"`
STP STPSpec `yaml:"stp,omitempty" protobuf:"1"`
VLAN BridgeVLANSpec `yaml:"vlan,omitempty" protobuf:"2"`
}
// STPSpec describes Spanning Tree Protocol (STP) settings of a bridge.
@ -68,6 +69,13 @@ type STPSpec struct {
Enabled bool `yaml:"enabled" protobuf:"1"`
}
// BridgeVLANSpec describes VLAN settings of a bridge.
//
//gotagsrewrite:gen
type BridgeVLANSpec struct {
FilteringEnabled bool `yaml:"filteringEnabled" protobuf:"1"`
}
// WireguardSpec describes Wireguard settings if Kind == "wireguard".
//
//gotagsrewrite:gen

View File

@ -164,6 +164,7 @@ description: Talos gRPC API reference.
- [BondSlave](#talos.resource.definitions.network.BondSlave)
- [BridgeMasterSpec](#talos.resource.definitions.network.BridgeMasterSpec)
- [BridgeSlave](#talos.resource.definitions.network.BridgeSlave)
- [BridgeVLANSpec](#talos.resource.definitions.network.BridgeVLANSpec)
- [DHCP4OperatorSpec](#talos.resource.definitions.network.DHCP4OperatorSpec)
- [DHCP6OperatorSpec](#talos.resource.definitions.network.DHCP6OperatorSpec)
- [DNSResolveCacheSpec](#talos.resource.definitions.network.DNSResolveCacheSpec)
@ -3015,6 +3016,7 @@ BridgeMasterSpec describes bridge settings if Kind == "bridge".
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| stp | [STPSpec](#talos.resource.definitions.network.STPSpec) | | |
| vlan | [BridgeVLANSpec](#talos.resource.definitions.network.BridgeVLANSpec) | | |
@ -3036,6 +3038,21 @@ BridgeSlave contains a bond's master name and slave index.
<a name="talos.resource.definitions.network.BridgeVLANSpec"></a>
### BridgeVLANSpec
BridgeVLANSpec describes VLAN settings of a bridge.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| filtering_enabled | [bool](#bool) | | |
<a name="talos.resource.definitions.network.DHCP4OperatorSpec"></a>
### DHCP4OperatorSpec

View File

@ -1358,6 +1358,7 @@ machine:
|-------|------|-------------|----------|
|`interfaces` |[]string |The interfaces that make up the bridge. | |
|`stp` |<a href="#Config.machine.network.interfaces..bridge.stp">STP</a> |<details><summary>A bridge option.</summary>Please see the official kernel documentation.</details> | |
|`vlan` |<a href="#Config.machine.network.interfaces..bridge.vlan">BridgeVLAN</a> |<details><summary>A bridge option.</summary>Please see the official kernel documentation.</details> | |
@ -1378,6 +1379,22 @@ STP contains the various options for configuring the STP properties of a bridge
###### vlan {#Config.machine.network.interfaces..bridge.vlan}
BridgeVLAN contains the various options for configuring the VLAN properties of a bridge interface.
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`vlanFiltering` |bool |Whether VLAN filtering is enabled. | |
##### vlans[] {#Config.machine.network.interfaces..vlans.}

View File

@ -734,6 +734,26 @@
"description": "A bridge option.\nPlease see the official kernel documentation.\n",
"markdownDescription": "A bridge option.\nPlease see the official kernel documentation.",
"x-intellij-html-description": "\u003cp\u003eA bridge option.\nPlease see the official kernel documentation.\u003c/p\u003e\n"
},
"vlan": {
"$ref": "#/$defs/v1alpha1.BridgeVLAN",
"title": "vlan",
"description": "A bridge option.\nPlease see the official kernel documentation.\n",
"markdownDescription": "A bridge option.\nPlease see the official kernel documentation.",
"x-intellij-html-description": "\u003cp\u003eA bridge option.\nPlease see the official kernel documentation.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object"
},
"v1alpha1.BridgeVLAN": {
"properties": {
"vlanFiltering": {
"type": "boolean",
"title": "vlanFiltering",
"description": "Whether VLAN filtering is enabled.\n",
"markdownDescription": "Whether VLAN filtering is enabled.",
"x-intellij-html-description": "\u003cp\u003eWhether VLAN filtering is enabled.\u003c/p\u003e\n"
}
},
"additionalProperties": false,