openvswitch: add OpenvSwitch and OpenvSwitchSlave link models

Signed-off-by: Ivan Tsvetkov <ivanfromearth@gmail.com>
This commit is contained in:
Ivan Tsvetkov
2026-02-11 18:49:24 +03:00
committed by Alessandro Boch
parent c7039a4139
commit 4e69fae463
3 changed files with 68 additions and 0 deletions
+21
View File
@@ -289,6 +289,21 @@ func (bridge *Bridge) Type() string {
return "bridge"
}
// OpenvSwitch links are Open vSwitch bridge devices.
// Note: their lifecycle is typically managed by OVS (OVSDB/ovs-vsctl),
// while netlink is used to query/link them.
type OpenvSwitch struct {
LinkAttrs
}
func (ovs *OpenvSwitch) Attrs() *LinkAttrs {
return &ovs.LinkAttrs
}
func (ovs *OpenvSwitch) Type() string {
return "openvswitch"
}
// Vlan links have ParentIndex set in their Attrs()
type Vlan struct {
LinkAttrs
@@ -1066,6 +1081,12 @@ func (v *VrfSlave) SlaveType() string {
return "vrf"
}
type OpenvSwitchSlave struct{}
func (o *OpenvSwitchSlave) SlaveType() string {
return "openvswitch"
}
// Geneve devices must specify RemoteIP and ID (VNI) on create
// https://github.com/torvalds/linux/blob/47ec5303d73ea344e84f46660fff693c57641386/drivers/net/geneve.c#L1209-L1223
type Geneve struct {
+4
View File
@@ -2188,6 +2188,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
link = &Ifb{}
case "bridge":
link = &Bridge{}
case "openvswitch":
link = &OpenvSwitch{}
case "vlan":
link = &Vlan{}
case "netkit":
@@ -2308,6 +2310,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
linkSlave = &BondSlave{}
case "vrf":
linkSlave = &VrfSlave{}
case "openvswitch":
linkSlave = &OpenvSwitchSlave{}
}
case nl.IFLA_INFO_SLAVE_DATA:
+43
View File
@@ -2240,6 +2240,49 @@ func TestLinkByIndex(t *testing.T) {
}
}
func TestOpenvSwitch(t *testing.T) {
skipUnlessRoot(t)
skipUnlessKModuleLoaded(t, "openvswitch")
// This test validates host OVS state, so it must not create an isolated netns
// via setUpNetlinkTest(), otherwise host OVS links will be invisible.
link, err := LinkByName("ovs-system")
if err != nil {
if _, ok := err.(LinkNotFoundError); ok {
t.Skip("ovs-system interface not found")
}
t.Fatal("Failed getting link: ", err)
}
ovsSystem, ok := link.(*OpenvSwitch)
if !ok {
t.Fatalf("unexpected link type: %T", link)
}
links, err := LinkList()
if err != nil {
t.Fatal(err)
}
for _, l := range links {
if l.Attrs().Name == ovsSystem.Name {
if _, ok := l.(*OpenvSwitch); !ok {
t.Fatalf("unexpected link type: %T", l)
}
}
if l.Attrs().Slave != nil && l.Attrs().Slave.SlaveType() == "openvswitch" {
if _, ok := l.Attrs().Slave.(*OpenvSwitchSlave); !ok {
t.Fatalf("unexpected slave type: %T", l.Attrs().Slave)
}
if l.Attrs().MasterIndex != ovsSystem.Index {
t.Fatalf("Got unexpected master index: %d, expected: %d", l.Attrs().MasterIndex, ovsSystem.Index)
}
}
}
}
func TestLinkSet(t *testing.T) {
t.Cleanup(setUpNetlinkTest(t))