rtnetlink/driver/macvlan_test.go
Jeroen Simonetti 5c41262525
Implement additional drivers (#280)
Add drivers for bridge, macvlan, vlan, vxlan and add helpers to
LinkService to use them (SetMaster, RemoveMaster)

Signed-off-by: Jeroen Simonetti <jeroen@simonetti.nl>
2025-10-30 22:55:44 +01:00

287 lines
7.5 KiB
Go

package driver
import (
"testing"
"github.com/jsimonetti/rtnetlink/v2/internal/unix"
"github.com/mdlayher/netlink"
)
func TestMacvlanEncodeDecode(t *testing.T) {
tests := []struct {
name string
macvlan *Macvlan
}{
{
name: "minimal configuration",
macvlan: &Macvlan{
Mode: func() *MacvlanMode { m := MacvlanModeBridge; return &m }(),
},
},
{
name: "bridge mode with flags",
macvlan: &Macvlan{
Mode: func() *MacvlanMode { m := MacvlanModeBridge; return &m }(),
Flags: func() *MacvlanFlag { f := MacvlanFlagNopromisc; return &f }(),
},
},
{
name: "vepa mode",
macvlan: &Macvlan{
Mode: func() *MacvlanMode { m := MacvlanModeVEPA; return &m }(),
},
},
{
name: "private mode",
macvlan: &Macvlan{
Mode: func() *MacvlanMode { m := MacvlanModePrivate; return &m }(),
},
},
{
name: "passthru mode",
macvlan: &Macvlan{
Mode: func() *MacvlanMode { m := MacvlanModePassthru; return &m }(),
},
},
{
name: "source mode with MAC addresses",
macvlan: &Macvlan{
Mode: func() *MacvlanMode { m := MacvlanModeSource; return &m }(),
MacaddrMode: func() *MacvlanMacaddrMode { m := MacvlanMacaddrAdd; return &m }(),
MacaddrData: [][]byte{
{0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
},
MacaddrCount: func() *uint32 { c := uint32(2); return &c }(),
},
},
{
name: "full configuration",
macvlan: &Macvlan{
Mode: func() *MacvlanMode { m := MacvlanModeBridge; return &m }(),
Flags: func() *MacvlanFlag { f := MacvlanFlagNopromisc | MacvlanFlagNodst; return &f }(),
BcQueueLen: func() *uint32 { l := uint32(1000); return &l }(),
BcQueueLenUsed: func() *uint8 { u := uint8(1); return &u }(),
BcCutoff: func() *int32 { c := int32(10); return &c }(),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Encode
ae := netlink.NewAttributeEncoder()
if err := tt.macvlan.Encode(ae); err != nil {
t.Fatalf("failed to encode: %v", err)
}
b, err := ae.Encode()
if err != nil {
t.Fatalf("failed to encode attributes: %v", err)
}
// Decode
ad, err := netlink.NewAttributeDecoder(b)
if err != nil {
t.Fatalf("failed to create decoder: %v", err)
}
decoded := &Macvlan{}
if err := decoded.Decode(ad); err != nil {
t.Fatalf("failed to decode: %v", err)
}
// Verify
if tt.macvlan.Mode != nil {
if decoded.Mode == nil {
t.Errorf("expected Mode %v, got nil", *tt.macvlan.Mode)
} else if *decoded.Mode != *tt.macvlan.Mode {
t.Errorf("expected Mode %v, got %v", *tt.macvlan.Mode, *decoded.Mode)
}
}
if tt.macvlan.Flags != nil {
if decoded.Flags == nil {
t.Errorf("expected Flags %v, got nil", *tt.macvlan.Flags)
} else if *decoded.Flags != *tt.macvlan.Flags {
t.Errorf("expected Flags %v, got %v", *tt.macvlan.Flags, *decoded.Flags)
}
}
if tt.macvlan.MacaddrMode != nil {
if decoded.MacaddrMode == nil {
t.Errorf("expected MacaddrMode %v, got nil", *tt.macvlan.MacaddrMode)
} else if *decoded.MacaddrMode != *tt.macvlan.MacaddrMode {
t.Errorf("expected MacaddrMode %v, got %v", *tt.macvlan.MacaddrMode, *decoded.MacaddrMode)
}
}
if len(tt.macvlan.MacaddrData) > 0 {
if len(decoded.MacaddrData) != len(tt.macvlan.MacaddrData) {
t.Errorf("expected %d MAC addresses, got %d", len(tt.macvlan.MacaddrData), len(decoded.MacaddrData))
}
}
if tt.macvlan.MacaddrCount != nil {
if decoded.MacaddrCount == nil {
t.Errorf("expected MacaddrCount %v, got nil", *tt.macvlan.MacaddrCount)
} else if *decoded.MacaddrCount != *tt.macvlan.MacaddrCount {
t.Errorf("expected MacaddrCount %v, got %v", *tt.macvlan.MacaddrCount, *decoded.MacaddrCount)
}
}
if tt.macvlan.BcQueueLen != nil {
if decoded.BcQueueLen == nil {
t.Errorf("expected BcQueueLen %v, got nil", *tt.macvlan.BcQueueLen)
} else if *decoded.BcQueueLen != *tt.macvlan.BcQueueLen {
t.Errorf("expected BcQueueLen %v, got %v", *tt.macvlan.BcQueueLen, *decoded.BcQueueLen)
}
}
if tt.macvlan.BcQueueLenUsed != nil {
if decoded.BcQueueLenUsed == nil {
t.Errorf("expected BcQueueLenUsed %v, got nil", *tt.macvlan.BcQueueLenUsed)
} else if *decoded.BcQueueLenUsed != *tt.macvlan.BcQueueLenUsed {
t.Errorf("expected BcQueueLenUsed %v, got %v", *tt.macvlan.BcQueueLenUsed, *decoded.BcQueueLenUsed)
}
}
if tt.macvlan.BcCutoff != nil {
if decoded.BcCutoff == nil {
t.Errorf("expected BcCutoff %v, got nil", *tt.macvlan.BcCutoff)
} else if *decoded.BcCutoff != *tt.macvlan.BcCutoff {
t.Errorf("expected BcCutoff %v, got %v", *tt.macvlan.BcCutoff, *decoded.BcCutoff)
}
}
})
}
}
func TestMacvlanModeString(t *testing.T) {
tests := []struct {
name string
mode MacvlanMode
expected string
}{
{
name: "private",
mode: MacvlanModePrivate,
expected: "private",
},
{
name: "vepa",
mode: MacvlanModeVEPA,
expected: "vepa",
},
{
name: "bridge",
mode: MacvlanModeBridge,
expected: "bridge",
},
{
name: "passthru",
mode: MacvlanModePassthru,
expected: "passthru",
},
{
name: "source",
mode: MacvlanModeSource,
expected: "source",
},
{
name: "unknown",
mode: MacvlanMode(99),
expected: "unknown MacvlanMode value (99)",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.mode.String(); got != tt.expected {
t.Errorf("expected %q, got %q", tt.expected, got)
}
})
}
}
func TestMacvlanKind(t *testing.T) {
m := &Macvlan{}
if got := m.Kind(); got != "macvlan" {
t.Errorf("expected %q, got %q", "macvlan", got)
}
}
func TestMacvlanNew(t *testing.T) {
m := &Macvlan{}
n := m.New()
if _, ok := n.(*Macvlan); !ok {
t.Errorf("expected *Macvlan, got %T", n)
}
}
func TestMacvlanDecodeRaw(t *testing.T) {
// Test decoding raw netlink data
tests := []struct {
name string
data []byte
expected *Macvlan
}{
{
name: "mode only",
data: func() []byte {
ae := netlink.NewAttributeEncoder()
ae.Uint32(unix.IFLA_MACVLAN_MODE, uint32(MacvlanModeBridge))
b, _ := ae.Encode()
return b
}(),
expected: &Macvlan{
Mode: func() *MacvlanMode { m := MacvlanModeBridge; return &m }(),
},
},
{
name: "mode and flags",
data: func() []byte {
ae := netlink.NewAttributeEncoder()
ae.Uint32(unix.IFLA_MACVLAN_MODE, uint32(MacvlanModeVEPA))
ae.Uint16(unix.IFLA_MACVLAN_FLAGS, uint16(MacvlanFlagNopromisc))
b, _ := ae.Encode()
return b
}(),
expected: &Macvlan{
Mode: func() *MacvlanMode { m := MacvlanModeVEPA; return &m }(),
Flags: func() *MacvlanFlag { f := MacvlanFlagNopromisc; return &f }(),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ad, err := netlink.NewAttributeDecoder(tt.data)
if err != nil {
t.Fatalf("failed to create decoder: %v", err)
}
m := &Macvlan{}
if err := m.Decode(ad); err != nil {
t.Fatalf("failed to decode: %v", err)
}
if tt.expected.Mode != nil {
if m.Mode == nil {
t.Errorf("expected Mode %v, got nil", *tt.expected.Mode)
} else if *m.Mode != *tt.expected.Mode {
t.Errorf("expected Mode %v, got %v", *tt.expected.Mode, *m.Mode)
}
}
if tt.expected.Flags != nil {
if m.Flags == nil {
t.Errorf("expected Flags %v, got nil", *tt.expected.Flags)
} else if *m.Flags != *tt.expected.Flags {
t.Errorf("expected Flags %v, got %v", *tt.expected.Flags, *m.Flags)
}
}
})
}
}