rtnetlink/driver/vlan.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

206 lines
4.7 KiB
Go

package driver
import (
"fmt"
"github.com/jsimonetti/rtnetlink/v2"
"github.com/jsimonetti/rtnetlink/v2/internal/unix"
"github.com/mdlayher/netlink"
)
// VlanProtocol represents the VLAN protocol type.
type VlanProtocol uint16
// VLAN protocols.
const (
// VlanProtocol8021Q represents 802.1Q VLAN tagging (standard VLAN).
VlanProtocol8021Q VlanProtocol = 0x0081
// VlanProtocol8021AD represents 802.1ad QinQ (VLAN stacking).
VlanProtocol8021AD VlanProtocol = 0xA888
)
// String returns a string representation of the VlanProtocol.
func (p VlanProtocol) String() string {
switch p {
case VlanProtocol8021Q:
return "802.1Q"
case VlanProtocol8021AD:
return "802.1ad"
default:
return fmt.Sprintf("unknown VlanProtocol value (0x%x)", uint16(p))
}
}
// VlanFlag represents VLAN flags.
type VlanFlag uint32
// VLAN flags.
const (
VlanFlagReorderHdr VlanFlag = 0x1
VlanFlagGVRP VlanFlag = 0x2
VlanFlagLooseBinding VlanFlag = 0x4
VlanFlagMVRP VlanFlag = 0x8
VlanFlagBridgeBinding VlanFlag = 0x10
)
// VlanQosMapping represents a QoS priority mapping.
type VlanQosMapping struct {
From uint32
To uint32
}
// Vlan represents a VLAN device configuration.
type Vlan struct {
// ID specifies the VLAN ID (1-4094).
ID *uint16
// Protocol specifies the VLAN protocol (802.1Q or 802.1ad).
Protocol *VlanProtocol
// Flags specifies VLAN flags.
Flags *VlanFlag
// EgressQos specifies egress QoS mappings.
EgressQos []VlanQosMapping
// IngressQos specifies ingress QoS mappings.
IngressQos []VlanQosMapping
}
var _ rtnetlink.LinkDriver = &Vlan{}
// New creates a new Vlan instance.
func (v *Vlan) New() rtnetlink.LinkDriver {
return &Vlan{}
}
// Kind returns the VLAN interface kind.
func (v *Vlan) Kind() string {
return "vlan"
}
// Encode encodes a Vlan into netlink attributes.
func (v *Vlan) Encode(ae *netlink.AttributeEncoder) error {
if v.ID != nil {
ae.Uint16(unix.IFLA_VLAN_ID, *v.ID)
}
if v.Protocol != nil {
ae.Uint16(unix.IFLA_VLAN_PROTOCOL, uint16(*v.Protocol))
}
if v.Flags != nil {
// VLAN flags are encoded as a nested attribute with flags and mask
ae.Nested(unix.IFLA_VLAN_FLAGS, func(nae *netlink.AttributeEncoder) error {
nae.Uint32(1, uint32(*v.Flags)) // flags
nae.Uint32(2, uint32(*v.Flags)) // mask (same as flags)
return nil
})
}
if len(v.EgressQos) > 0 {
ae.Nested(unix.IFLA_VLAN_EGRESS_QOS, func(nae *netlink.AttributeEncoder) error {
for _, mapping := range v.EgressQos {
nae.Nested(unix.IFLA_VLAN_QOS_MAPPING, func(mae *netlink.AttributeEncoder) error {
mae.Uint32(1, mapping.From)
mae.Uint32(2, mapping.To)
return nil
})
}
return nil
})
}
if len(v.IngressQos) > 0 {
ae.Nested(unix.IFLA_VLAN_INGRESS_QOS, func(nae *netlink.AttributeEncoder) error {
for _, mapping := range v.IngressQos {
nae.Nested(unix.IFLA_VLAN_QOS_MAPPING, func(mae *netlink.AttributeEncoder) error {
mae.Uint32(1, mapping.From)
mae.Uint32(2, mapping.To)
return nil
})
}
return nil
})
}
return nil
}
// Decode decodes netlink attributes into a Vlan.
func (v *Vlan) Decode(ad *netlink.AttributeDecoder) error {
for ad.Next() {
switch ad.Type() {
case unix.IFLA_VLAN_ID:
id := ad.Uint16()
v.ID = &id
case unix.IFLA_VLAN_PROTOCOL:
protocol := VlanProtocol(ad.Uint16())
v.Protocol = &protocol
case unix.IFLA_VLAN_FLAGS:
ad.Nested(func(nad *netlink.AttributeDecoder) error {
for nad.Next() {
switch nad.Type() {
case 1: // flags
flags := VlanFlag(nad.Uint32())
v.Flags = &flags
case 2: // mask (we don't need to store this)
nad.Uint32()
}
}
return nil
})
case unix.IFLA_VLAN_EGRESS_QOS:
ad.Nested(func(nad *netlink.AttributeDecoder) error {
for nad.Next() {
if nad.Type() == unix.IFLA_VLAN_QOS_MAPPING {
var mapping VlanQosMapping
nad.Nested(func(mad *netlink.AttributeDecoder) error {
for mad.Next() {
switch mad.Type() {
case 1: // from
mapping.From = mad.Uint32()
case 2: // to
mapping.To = mad.Uint32()
}
}
return nil
})
v.EgressQos = append(v.EgressQos, mapping)
}
}
return nil
})
case unix.IFLA_VLAN_INGRESS_QOS:
ad.Nested(func(nad *netlink.AttributeDecoder) error {
for nad.Next() {
if nad.Type() == unix.IFLA_VLAN_QOS_MAPPING {
var mapping VlanQosMapping
nad.Nested(func(mad *netlink.AttributeDecoder) error {
for mad.Next() {
switch mad.Type() {
case 1: // from
mapping.From = mad.Uint32()
case 2: // to
mapping.To = mad.Uint32()
}
}
return nil
})
v.IngressQos = append(v.IngressQos, mapping)
}
}
return nil
})
}
}
return ad.Err()
}