rtnetlink/driver/bridge_live_test.go
Daniel Swarbrick 2a35d85b9d
Use generic function for creating pointers (#291)
Replace type-specific ptrFoo helper functions with a generic one.

Signed-off-by: Daniel Swarbrick <daniel.swarbrick@gmail.com>
2026-01-02 11:54:34 +01:00

278 lines
6.5 KiB
Go

//go:build integration
// +build integration
package driver
import (
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/jsimonetti/rtnetlink/v2"
"github.com/jsimonetti/rtnetlink/v2/internal/testutils"
"github.com/mdlayher/netlink"
)
func bridgeT(d rtnetlink.LinkDriver) *Bridge {
b := d.(*Bridge)
return &Bridge{
StpState: b.StpState,
ForwardDelay: b.ForwardDelay,
HelloTime: b.HelloTime,
MaxAge: b.MaxAge,
VlanFiltering: b.VlanFiltering,
VlanProtocol: b.VlanProtocol,
McastSnooping: b.McastSnooping,
Priority: b.Priority,
}
}
func bridgePortT(d rtnetlink.LinkDriver) *BridgePort {
b := d.(*BridgePort)
return &BridgePort{
Priority: b.Priority,
Cost: b.Cost,
Mode: b.Mode,
Learning: b.Learning,
UnicastFlood: b.UnicastFlood,
BcastFlood: b.BcastFlood,
}
}
func TestBridge(t *testing.T) {
conn, err := rtnetlink.Dial(nil)
if err != nil {
t.Fatalf("failed to establish netlink socket: %v", err)
}
defer conn.Close()
connNS, err := rtnetlink.Dial(&netlink.Config{NetNS: testutils.NetNS(t)})
if err != nil {
t.Fatalf("failed to establish netlink socket to netns: %v", err)
}
defer connNS.Close()
var (
stpEnabled = BridgeStpStateEnabled
stpDisabled = BridgeStpStateDisabled
vlanEnabled = BridgeEnableEnabled
vlanDisabled = BridgeEnableDisabled
mcastEnabled = BridgeEnableEnabled
proto8021Q = VlanProtocol8021Q
u321500 = uint32(1500)
u32200 = uint32(200)
u322000 = uint32(2000)
u1632768 = uint16(32768) // Default bridge priority
)
tests := []struct {
name string
conn *rtnetlink.Conn
driver *Bridge
bridge *Bridge
}{
{
name: "bridge with STP enabled",
conn: conn,
driver: &Bridge{
StpState: &stpEnabled,
ForwardDelay: &u321500,
HelloTime: &u32200,
MaxAge: &u322000,
},
bridge: &Bridge{
StpState: &stpEnabled,
ForwardDelay: &u321500,
HelloTime: &u32200,
MaxAge: &u322000,
Priority: &u1632768,
VlanFiltering: &vlanDisabled,
VlanProtocol: &proto8021Q,
McastSnooping: &mcastEnabled,
},
},
{
name: "bridge with STP disabled",
conn: connNS,
driver: &Bridge{
StpState: &stpDisabled,
},
bridge: &Bridge{
StpState: &stpDisabled,
ForwardDelay: &u321500,
HelloTime: &u32200,
MaxAge: &u322000,
Priority: &u1632768,
VlanFiltering: &vlanDisabled,
VlanProtocol: &proto8021Q,
McastSnooping: &mcastEnabled,
},
},
{
name: "VLAN filtering enabled with 802.1Q",
conn: connNS,
driver: &Bridge{
VlanFiltering: &vlanEnabled,
VlanProtocol: &proto8021Q,
},
bridge: &Bridge{
StpState: &stpDisabled,
ForwardDelay: &u321500,
HelloTime: &u32200,
MaxAge: &u322000,
Priority: &u1632768,
VlanFiltering: &vlanEnabled,
VlanProtocol: &proto8021Q,
McastSnooping: &mcastEnabled,
},
},
}
for i, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bridgeID := 1200 + uint32(i*10)
if err := setupInterface(tt.conn, fmt.Sprintf("br%d", bridgeID), bridgeID, 0, tt.driver); err != nil {
t.Fatalf("failed to setup bridge interface: %v", err)
}
defer tt.conn.Link.Delete(bridgeID)
msg, err := getInterface(tt.conn, bridgeID)
if err != nil {
t.Fatalf("failed to get bridge interface: %v", err)
}
if diff := cmp.Diff(tt.bridge, bridgeT(msg.Attributes.Info.Data)); diff != "" {
t.Error(diff)
}
})
}
}
func TestBridgePort(t *testing.T) {
conn, err := rtnetlink.Dial(nil)
if err != nil {
t.Fatalf("failed to establish netlink socket: %v", err)
}
defer conn.Close()
connNS, err := rtnetlink.Dial(&netlink.Config{NetNS: testutils.NetNS(t)})
if err != nil {
t.Fatalf("failed to establish netlink socket to netns: %v", err)
}
defer connNS.Close()
var (
enabled = BridgeEnableEnabled
disabled = BridgeEnableDisabled
u1632 = uint16(32)
u32100 = uint32(100)
)
tests := []struct {
name string
conn *rtnetlink.Conn
driver *Bridge
portCfg *BridgePort
expected *BridgePort
}{
{
name: "port with cost and priority",
conn: conn,
driver: &Bridge{
StpState: ptr(BridgeStpStateEnabled),
},
portCfg: &BridgePort{
Priority: &u1632,
Cost: &u32100,
},
expected: &BridgePort{
Priority: &u1632,
Cost: &u32100,
Mode: &disabled,
Learning: &enabled,
UnicastFlood: &enabled,
BcastFlood: &enabled,
},
},
{
name: "port with hairpin mode",
conn: connNS,
driver: &Bridge{
StpState: ptr(BridgeStpStateEnabled),
},
portCfg: &BridgePort{
Mode: &enabled,
},
expected: &BridgePort{
Mode: &enabled,
Cost: &u32100,
Priority: &u1632,
Learning: &enabled,
UnicastFlood: &enabled,
BcastFlood: &enabled,
},
},
{
name: "port with learning and flooding",
conn: connNS,
driver: &Bridge{
StpState: ptr(BridgeStpStateEnabled),
},
portCfg: &BridgePort{
Learning: &enabled,
UnicastFlood: &enabled,
BcastFlood: &enabled,
},
expected: &BridgePort{
Learning: &enabled,
UnicastFlood: &enabled,
BcastFlood: &enabled,
Cost: &u32100,
Priority: &u1632,
Mode: &disabled,
},
},
}
for i, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bridgeID := 1300 + uint32(i*10)
if err := setupInterface(tt.conn, fmt.Sprintf("br%d", bridgeID), bridgeID, 0, tt.driver); err != nil {
t.Fatalf("failed to setup bridge interface: %v", err)
}
defer tt.conn.Link.Delete(bridgeID)
// Create dummy interface as bridge port
dummyID := bridgeID + 1
if err := setupInterface(tt.conn, fmt.Sprintf("d%d", dummyID), dummyID, bridgeID, &rtnetlink.LinkData{Name: "dummy"}); err != nil {
t.Fatalf("failed to setup dummy interface: %v", err)
}
defer tt.conn.Link.Delete(dummyID)
// Configure the bridge port
if tt.portCfg != nil {
err := tt.conn.Link.Set(&rtnetlink.LinkMessage{
Index: dummyID,
Attributes: &rtnetlink.LinkAttributes{
Info: &rtnetlink.LinkInfo{
SlaveKind: "bridge",
SlaveData: tt.portCfg,
},
},
})
if err != nil {
t.Fatalf("failed to configure bridge port: %v", err)
}
}
// Verify bridge port configuration
msg, err := getInterface(tt.conn, dummyID)
if err != nil {
t.Fatalf("failed to get dummy interface: %v", err)
}
if diff := cmp.Diff(tt.expected, bridgePortT(msg.Attributes.Info.SlaveData)); diff != "" {
t.Error(diff)
}
})
}
}