vlan: add support for flags and qos maps

Signed-off-by: Gwendolyn <me@gwendolyn.dev>
This commit is contained in:
Gwendolyn
2025-04-27 15:37:10 +02:00
committed by Alessandro Boch
parent b929916209
commit 17daef607c
4 changed files with 310 additions and 3 deletions

11
link.go
View File

@@ -290,8 +290,15 @@ func (bridge *Bridge) Type() string {
// Vlan links have ParentIndex set in their Attrs()
type Vlan struct {
LinkAttrs
VlanId int
VlanProtocol VlanProtocol
VlanId int
VlanProtocol VlanProtocol
IngressQosMap map[uint32]uint32
EgressQosMap map[uint32]uint32
ReorderHdr *bool
Gvrp *bool
LooseBinding *bool
Mvrp *bool
BridgeBinding *bool
}
func (vlan *Vlan) Attrs() *LinkAttrs {

View File

@@ -1683,6 +1683,73 @@ func (h *Handle) linkModify(link Link, flags int) error {
native.PutUint16(b, uint16(link.VlanId))
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
data.AddRtAttr(nl.IFLA_VLAN_ID, b)
var vlanFlags uint32
var vlanFlagsMask uint32
if link.ReorderHdr != nil {
vlanFlagsMask |= nl.VLAN_FLAG_REORDER_HDR
if *link.ReorderHdr {
vlanFlags |= nl.VLAN_FLAG_REORDER_HDR
} else {
vlanFlags &= ^uint32(nl.VLAN_FLAG_REORDER_HDR)
}
}
if link.Gvrp != nil {
vlanFlagsMask |= nl.VLAN_FLAG_GVRP
if *link.Gvrp {
vlanFlags |= nl.VLAN_FLAG_GVRP
} else {
vlanFlags &= ^uint32(nl.VLAN_FLAG_GVRP)
}
}
if link.Mvrp != nil {
vlanFlagsMask |= nl.VLAN_FLAG_MVRP
if *link.Mvrp {
vlanFlags |= nl.VLAN_FLAG_MVRP
} else {
vlanFlags &= ^uint32(nl.VLAN_FLAG_MVRP)
}
}
if link.LooseBinding != nil {
vlanFlagsMask |= nl.VLAN_FLAG_LOOSE_BINDING
if *link.LooseBinding {
vlanFlags |= nl.VLAN_FLAG_LOOSE_BINDING
} else {
vlanFlags &= ^uint32(nl.VLAN_FLAG_LOOSE_BINDING)
}
}
if link.BridgeBinding != nil {
vlanFlagsMask |= nl.VLAN_FLAG_BRIDGE_BINDING
if *link.BridgeBinding {
vlanFlags |= nl.VLAN_FLAG_BRIDGE_BINDING
} else {
vlanFlags &= ^uint32(nl.VLAN_FLAG_BRIDGE_BINDING)
}
}
buf := &bytes.Buffer{}
buf.Write(nl.Uint32Attr(vlanFlags))
buf.Write(nl.Uint32Attr(vlanFlagsMask))
data.AddRtAttr(nl.IFLA_VLAN_FLAGS, buf.Bytes())
if link.IngressQosMap != nil {
ingressMap := data.AddRtAttr(nl.IFLA_VLAN_INGRESS_QOS, nil)
for from, to := range link.IngressQosMap {
buf := &bytes.Buffer{}
buf.Write(nl.Uint32Attr(from))
buf.Write(nl.Uint32Attr(to))
ingressMap.AddRtAttr(nl.IFLA_VLAN_QOS_MAPPING, buf.Bytes())
}
}
if link.EgressQosMap != nil {
egressMap := data.AddRtAttr(nl.IFLA_VLAN_EGRESS_QOS, nil)
for from, to := range link.EgressQosMap {
buf := &bytes.Buffer{}
buf.Write(nl.Uint32Attr(from))
buf.Write(nl.Uint32Attr(to))
egressMap.AddRtAttr(nl.IFLA_VLAN_QOS_MAPPING, buf.Bytes())
}
}
if link.VlanProtocol != VLAN_PROTOCOL_UNKNOWN {
data.AddRtAttr(nl.IFLA_VLAN_PROTOCOL, htons(uint16(link.VlanProtocol)))
@@ -2801,12 +2868,65 @@ func parseNetkitData(link Link, data []syscall.NetlinkRouteAttr) {
}
}
func parseVlanQosMap(data []byte) map[uint32]uint32 {
values, err := nl.ParseRouteAttr(data)
if err != nil {
return nil
}
qosMap := make(map[uint32]uint32)
for _, value := range values {
switch value.Attr.Type {
case nl.IFLA_VLAN_QOS_MAPPING:
from := native.Uint32(value.Value[:4])
to := native.Uint32(value.Value[4:])
qosMap[from] = to
}
}
return qosMap
}
func parseVlanData(link Link, data []syscall.NetlinkRouteAttr) {
vlan := link.(*Vlan)
for _, datum := range data {
switch datum.Attr.Type {
case nl.IFLA_VLAN_ID:
vlan.VlanId = int(native.Uint16(datum.Value[0:2]))
case nl.IFLA_VLAN_FLAGS:
flags := native.Uint32(datum.Value[0:4])
trueVal := true
falseVal := false
if flags&nl.VLAN_FLAG_REORDER_HDR != 0 {
vlan.ReorderHdr = &trueVal
} else {
vlan.ReorderHdr = &falseVal
}
if flags&nl.VLAN_FLAG_GVRP != 0 {
vlan.Gvrp = &trueVal
} else {
vlan.Gvrp = &falseVal
}
if flags&nl.VLAN_FLAG_LOOSE_BINDING != 0 {
vlan.LooseBinding = &trueVal
} else {
vlan.LooseBinding = &falseVal
}
if flags&nl.VLAN_FLAG_MVRP != 0 {
vlan.Mvrp = &trueVal
} else {
vlan.Mvrp = &falseVal
}
if flags&nl.VLAN_FLAG_BRIDGE_BINDING != 0 {
vlan.BridgeBinding = &trueVal
} else {
vlan.BridgeBinding = &falseVal
}
case nl.IFLA_VLAN_EGRESS_QOS:
vlan.EgressQosMap = parseVlanQosMap(datum.Value)
case nl.IFLA_VLAN_INGRESS_QOS:
vlan.IngressQosMap = parseVlanQosMap(datum.Value)
case nl.IFLA_VLAN_PROTOCOL:
vlan.VlanProtocol = VlanProtocol(int(ntohs(datum.Value[0:2])))
}

View File

@@ -10,6 +10,7 @@ import (
"net"
"os"
"os/exec"
"reflect"
"sort"
"strings"
"syscall"
@@ -919,13 +920,178 @@ func TestLinkAddDelVlan(t *testing.T) {
t.Fatal(err)
}
testLinkAddDel(t, &Vlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, 900, VLAN_PROTOCOL_8021Q})
testLinkAddDel(t, &Vlan{
LinkAttrs: LinkAttrs{
Name: "bar",
ParentIndex: parent.Attrs().Index,
},
VlanId: 900,
VlanProtocol: VLAN_PROTOCOL_8021Q,
})
if err := LinkDel(parent); err != nil {
t.Fatal(err)
}
}
func TestLinkAddVlanWithQosMaps(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
parent := &Dummy{LinkAttrs{Name: "foo"}}
if err := LinkAdd(parent); err != nil {
t.Fatal(err)
}
ingressMap := map[uint32]uint32{
0: 2,
1: 3,
2: 5,
}
egressMap := map[uint32]uint32{
1: 3,
2: 5,
3: 7,
}
vlan := &Vlan{
LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index},
VlanId: 900,
VlanProtocol: VLAN_PROTOCOL_8021Q,
IngressQosMap: ingressMap,
EgressQosMap: egressMap,
}
if err := LinkAdd(vlan); err != nil {
t.Fatal(err)
}
link, err := LinkByName("bar")
if err != nil {
t.Fatal(err)
}
if vlan, ok := link.(*Vlan); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if !reflect.DeepEqual(vlan.IngressQosMap, ingressMap) {
t.Fatalf("expected ingress qos map to be %v, got %v", ingressMap, vlan.IngressQosMap)
}
if !reflect.DeepEqual(vlan.EgressQosMap, egressMap) {
t.Fatalf("expected egress qos map to be %v, got %v", egressMap, vlan.EgressQosMap)
}
}
}
func TestLinkAddVlanWithFlags(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
parent := &Dummy{LinkAttrs{Name: "foo"}}
if err := LinkAdd(parent); err != nil {
t.Fatal(err)
}
valueTrue := true
valueFalse := false
vlan := &Vlan{
LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index},
VlanId: 900,
VlanProtocol: VLAN_PROTOCOL_8021Q,
Gvrp: &valueTrue,
Mvrp: &valueFalse,
BridgeBinding: &valueFalse,
LooseBinding: &valueFalse,
ReorderHdr: &valueTrue,
}
if err := LinkAdd(vlan); err != nil {
t.Fatal(err)
}
link, err := LinkByName("bar")
if err != nil {
t.Fatal(err)
}
if vlan, ok := link.(*Vlan); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if vlan.Gvrp == nil || *vlan.Gvrp != true {
t.Fatalf("expected gvrp to be true, got %v", vlan.Gvrp)
}
if vlan.Mvrp == nil || *vlan.Mvrp != false {
t.Fatalf("expected mvrp to be false, got %v", vlan.Mvrp)
}
if vlan.BridgeBinding == nil || *vlan.BridgeBinding != false {
t.Fatalf("expected bridge binding to be false, got %v", vlan.BridgeBinding)
}
if vlan.LooseBinding == nil || *vlan.LooseBinding != false {
t.Fatalf("expected loose binding to be false, got %v", vlan.LooseBinding)
}
if vlan.ReorderHdr == nil || *vlan.ReorderHdr != true {
t.Fatalf("expected reorder hdr to be true, got %v", vlan.ReorderHdr)
}
}
}
func TestLinkModifyVlanFlags(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
parent := &Dummy{LinkAttrs{Name: "foo"}}
if err := LinkAdd(parent); err != nil {
t.Fatal(err)
}
valueTrue := true
valueFalse := false
vlan := &Vlan{
LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index},
VlanId: 900,
VlanProtocol: VLAN_PROTOCOL_8021Q,
Gvrp: &valueTrue,
Mvrp: &valueFalse,
BridgeBinding: &valueFalse,
LooseBinding: &valueFalse,
ReorderHdr: &valueTrue,
}
if err := LinkAdd(vlan); err != nil {
t.Fatal(err)
}
vlan = &Vlan{
LinkAttrs: LinkAttrs{Name: "bar"},
BridgeBinding: &valueTrue,
}
if err := LinkModify(vlan); err != nil {
t.Fatal(err)
}
link, err := LinkByName("bar")
if err != nil {
t.Fatal(err)
}
if vlan, ok := link.(*Vlan); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if vlan.Gvrp == nil || *vlan.Gvrp != true {
t.Fatalf("expected gvrp to be true, got %v", vlan.Gvrp)
}
if vlan.Mvrp == nil || *vlan.Mvrp != false {
t.Fatalf("expected mvrp to be false, got %v", vlan.Mvrp)
}
if vlan.BridgeBinding == nil || *vlan.BridgeBinding != true {
t.Fatalf("expected bridge binding to be true, got %v", vlan.BridgeBinding)
}
if vlan.LooseBinding == nil || *vlan.LooseBinding != false {
t.Fatalf("expected loose binding to be false, got %v", vlan.LooseBinding)
}
if vlan.ReorderHdr == nil || *vlan.ReorderHdr != true {
t.Fatalf("expected reorder hdr to be true, got %v", vlan.ReorderHdr)
}
}
}
func TestLinkAddDelMacvlan(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()

View File

@@ -31,6 +31,20 @@ const (
IFLA_VLAN_MAX = IFLA_VLAN_PROTOCOL
)
const (
IFLA_VLAN_QOS_UNSPEC = iota
IFLA_VLAN_QOS_MAPPING
IFLA_VLAN_QOS_MAX = IFLA_VLAN_QOS_MAPPING
)
const (
VLAN_FLAG_REORDER_HDR = 1 << iota
VLAN_FLAG_GVRP
VLAN_FLAG_LOOSE_BINDING
VLAN_FLAG_MVRP
VLAN_FLAG_BRIDGE_BINDING
)
const (
IFLA_NETKIT_UNSPEC = iota
IFLA_NETKIT_PEER_INFO