273 lines
7.2 KiB
Go
273 lines
7.2 KiB
Go
|
package netlink
|
||
|
|
||
|
import (
|
||
|
"syscall"
|
||
|
|
||
|
"fmt"
|
||
|
"github.com/vishvananda/netlink/nl"
|
||
|
"golang.org/x/sys/unix"
|
||
|
)
|
||
|
|
||
|
// DevlinkDevEswitchAttr represents device's eswitch attributes
|
||
|
type DevlinkDevEswitchAttr struct {
|
||
|
Mode string
|
||
|
InlineMode string
|
||
|
EncapMode string
|
||
|
}
|
||
|
|
||
|
// DevlinkDevAttrs represents device attributes
|
||
|
type DevlinkDevAttrs struct {
|
||
|
Eswitch DevlinkDevEswitchAttr
|
||
|
}
|
||
|
|
||
|
// DevlinkDevice represents device and its attributes
|
||
|
type DevlinkDevice struct {
|
||
|
BusName string
|
||
|
DeviceName string
|
||
|
Attrs DevlinkDevAttrs
|
||
|
}
|
||
|
|
||
|
func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) {
|
||
|
devices := make([]*DevlinkDevice, 0, len(msgs))
|
||
|
for _, m := range msgs {
|
||
|
attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
dev := &DevlinkDevice{}
|
||
|
if err = dev.parseAttributes(attrs); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
devices = append(devices, dev)
|
||
|
}
|
||
|
return devices, nil
|
||
|
}
|
||
|
|
||
|
func eswitchStringToMode(modeName string) (uint16, error) {
|
||
|
if modeName == "legacy" {
|
||
|
return nl.DEVLINK_ESWITCH_MODE_LEGACY, nil
|
||
|
} else if modeName == "switchdev" {
|
||
|
return nl.DEVLINK_ESWITCH_MODE_SWITCHDEV, nil
|
||
|
} else {
|
||
|
return 0xffff, fmt.Errorf("invalid switchdev mode")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func parseEswitchMode(mode uint16) string {
|
||
|
var eswitchMode = map[uint16]string{
|
||
|
nl.DEVLINK_ESWITCH_MODE_LEGACY: "legacy",
|
||
|
nl.DEVLINK_ESWITCH_MODE_SWITCHDEV: "switchdev",
|
||
|
}
|
||
|
if eswitchMode[mode] == "" {
|
||
|
return "unknown"
|
||
|
} else {
|
||
|
return eswitchMode[mode]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func parseEswitchInlineMode(inlinemode uint8) string {
|
||
|
var eswitchInlineMode = map[uint8]string{
|
||
|
nl.DEVLINK_ESWITCH_INLINE_MODE_NONE: "none",
|
||
|
nl.DEVLINK_ESWITCH_INLINE_MODE_LINK: "link",
|
||
|
nl.DEVLINK_ESWITCH_INLINE_MODE_NETWORK: "network",
|
||
|
nl.DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT: "transport",
|
||
|
}
|
||
|
if eswitchInlineMode[inlinemode] == "" {
|
||
|
return "unknown"
|
||
|
} else {
|
||
|
return eswitchInlineMode[inlinemode]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func parseEswitchEncapMode(encapmode uint8) string {
|
||
|
var eswitchEncapMode = map[uint8]string{
|
||
|
nl.DEVLINK_ESWITCH_ENCAP_MODE_NONE: "disable",
|
||
|
nl.DEVLINK_ESWITCH_ENCAP_MODE_BASIC: "enable",
|
||
|
}
|
||
|
if eswitchEncapMode[encapmode] == "" {
|
||
|
return "unknown"
|
||
|
} else {
|
||
|
return eswitchEncapMode[encapmode]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (d *DevlinkDevice) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
|
||
|
for _, a := range attrs {
|
||
|
switch a.Attr.Type {
|
||
|
case nl.DEVLINK_ATTR_BUS_NAME:
|
||
|
d.BusName = string(a.Value)
|
||
|
case nl.DEVLINK_ATTR_DEV_NAME:
|
||
|
d.DeviceName = string(a.Value)
|
||
|
case nl.DEVLINK_ATTR_ESWITCH_MODE:
|
||
|
d.Attrs.Eswitch.Mode = parseEswitchMode(native.Uint16(a.Value))
|
||
|
case nl.DEVLINK_ATTR_ESWITCH_INLINE_MODE:
|
||
|
d.Attrs.Eswitch.InlineMode = parseEswitchInlineMode(uint8(a.Value[0]))
|
||
|
case nl.DEVLINK_ATTR_ESWITCH_ENCAP_MODE:
|
||
|
d.Attrs.Eswitch.EncapMode = parseEswitchEncapMode(uint8(a.Value[0]))
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (dev *DevlinkDevice) parseEswitchAttrs(msgs [][]byte) {
|
||
|
m := msgs[0]
|
||
|
attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
dev.parseAttributes(attrs)
|
||
|
}
|
||
|
|
||
|
func (h *Handle) getEswitchAttrs(family *GenlFamily, dev *DevlinkDevice) {
|
||
|
msg := &nl.Genlmsg{
|
||
|
Command: nl.DEVLINK_CMD_ESWITCH_GET,
|
||
|
Version: nl.GENL_DEVLINK_VERSION,
|
||
|
}
|
||
|
req := h.newNetlinkRequest(int(family.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK)
|
||
|
req.AddData(msg)
|
||
|
|
||
|
b := make([]byte, len(dev.BusName))
|
||
|
copy(b, dev.BusName)
|
||
|
data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b)
|
||
|
req.AddData(data)
|
||
|
|
||
|
b = make([]byte, len(dev.DeviceName))
|
||
|
copy(b, dev.DeviceName)
|
||
|
data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b)
|
||
|
req.AddData(data)
|
||
|
|
||
|
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
dev.parseEswitchAttrs(msgs)
|
||
|
}
|
||
|
|
||
|
// DevLinkGetDeviceList provides a pointer to devlink devices and nil error,
|
||
|
// otherwise returns an error code.
|
||
|
func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
|
||
|
f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
msg := &nl.Genlmsg{
|
||
|
Command: nl.DEVLINK_CMD_GET,
|
||
|
Version: nl.GENL_DEVLINK_VERSION,
|
||
|
}
|
||
|
req := h.newNetlinkRequest(int(f.ID),
|
||
|
unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP)
|
||
|
req.AddData(msg)
|
||
|
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
devices, err := parseDevLinkDeviceList(msgs)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
for _, d := range devices {
|
||
|
h.getEswitchAttrs(f, d)
|
||
|
}
|
||
|
return devices, nil
|
||
|
}
|
||
|
|
||
|
// DevLinkGetDeviceList provides a pointer to devlink devices and nil error,
|
||
|
// otherwise returns an error code.
|
||
|
func DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
|
||
|
return pkgHandle.DevLinkGetDeviceList()
|
||
|
}
|
||
|
|
||
|
func parseDevlinkDevice(msgs [][]byte) (*DevlinkDevice, error) {
|
||
|
m := msgs[0]
|
||
|
attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
dev := &DevlinkDevice{}
|
||
|
if err = dev.parseAttributes(attrs); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return dev, nil
|
||
|
}
|
||
|
|
||
|
func (h *Handle) createCmdReq(cmd uint8, bus string, device string) (*GenlFamily, *nl.NetlinkRequest, error) {
|
||
|
f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
|
||
|
if err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
|
||
|
msg := &nl.Genlmsg{
|
||
|
Command: cmd,
|
||
|
Version: nl.GENL_DEVLINK_VERSION,
|
||
|
}
|
||
|
req := h.newNetlinkRequest(int(f.ID),
|
||
|
unix.NLM_F_REQUEST|unix.NLM_F_ACK)
|
||
|
req.AddData(msg)
|
||
|
|
||
|
b := make([]byte, len(bus)+1)
|
||
|
copy(b, bus)
|
||
|
data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b)
|
||
|
req.AddData(data)
|
||
|
|
||
|
b = make([]byte, len(device)+1)
|
||
|
copy(b, device)
|
||
|
data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b)
|
||
|
req.AddData(data)
|
||
|
|
||
|
return f, req, nil
|
||
|
}
|
||
|
|
||
|
// DevlinkGetDeviceByName provides a pointer to devlink device and nil error,
|
||
|
// otherwise returns an error code.
|
||
|
func (h *Handle) DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) {
|
||
|
f, req, err := h.createCmdReq(nl.DEVLINK_CMD_GET, Bus, Device)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
dev, err := parseDevlinkDevice(respmsg)
|
||
|
if err == nil {
|
||
|
h.getEswitchAttrs(f, dev)
|
||
|
}
|
||
|
return dev, err
|
||
|
}
|
||
|
|
||
|
// DevlinkGetDeviceByName provides a pointer to devlink device and nil error,
|
||
|
// otherwise returns an error code.
|
||
|
func DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) {
|
||
|
return pkgHandle.DevLinkGetDeviceByName(Bus, Device)
|
||
|
}
|
||
|
|
||
|
// DevLinkSetEswitchMode sets eswitch mode if able to set successfully or
|
||
|
// returns an error code.
|
||
|
// Equivalent to: `devlink dev eswitch set $dev mode switchdev`
|
||
|
// Equivalent to: `devlink dev eswitch set $dev mode legacy`
|
||
|
func (h *Handle) DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error {
|
||
|
mode, err := eswitchStringToMode(NewMode)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_ESWITCH_SET, Dev.BusName, Dev.DeviceName)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_ESWITCH_MODE, nl.Uint16Attr(mode)))
|
||
|
|
||
|
_, err = req.Execute(unix.NETLINK_GENERIC, 0)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// DevLinkSetEswitchMode sets eswitch mode if able to set successfully or
|
||
|
// returns an error code.
|
||
|
// Equivalent to: `devlink dev eswitch set $dev mode switchdev`
|
||
|
// Equivalent to: `devlink dev eswitch set $dev mode legacy`
|
||
|
func DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error {
|
||
|
return pkgHandle.DevLinkSetEswitchMode(Dev, NewMode)
|
||
|
}
|