1package netlink 2 3import ( 4 "syscall" 5 6 "fmt" 7 "github.com/vishvananda/netlink/nl" 8 "golang.org/x/sys/unix" 9) 10 11// DevlinkDevEswitchAttr represents device's eswitch attributes 12type DevlinkDevEswitchAttr struct { 13 Mode string 14 InlineMode string 15 EncapMode string 16} 17 18// DevlinkDevAttrs represents device attributes 19type DevlinkDevAttrs struct { 20 Eswitch DevlinkDevEswitchAttr 21} 22 23// DevlinkDevice represents device and its attributes 24type DevlinkDevice struct { 25 BusName string 26 DeviceName string 27 Attrs DevlinkDevAttrs 28} 29 30func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) { 31 devices := make([]*DevlinkDevice, 0, len(msgs)) 32 for _, m := range msgs { 33 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 34 if err != nil { 35 return nil, err 36 } 37 dev := &DevlinkDevice{} 38 if err = dev.parseAttributes(attrs); err != nil { 39 return nil, err 40 } 41 devices = append(devices, dev) 42 } 43 return devices, nil 44} 45 46func eswitchStringToMode(modeName string) (uint16, error) { 47 if modeName == "legacy" { 48 return nl.DEVLINK_ESWITCH_MODE_LEGACY, nil 49 } else if modeName == "switchdev" { 50 return nl.DEVLINK_ESWITCH_MODE_SWITCHDEV, nil 51 } else { 52 return 0xffff, fmt.Errorf("invalid switchdev mode") 53 } 54} 55 56func parseEswitchMode(mode uint16) string { 57 var eswitchMode = map[uint16]string{ 58 nl.DEVLINK_ESWITCH_MODE_LEGACY: "legacy", 59 nl.DEVLINK_ESWITCH_MODE_SWITCHDEV: "switchdev", 60 } 61 if eswitchMode[mode] == "" { 62 return "unknown" 63 } else { 64 return eswitchMode[mode] 65 } 66} 67 68func parseEswitchInlineMode(inlinemode uint8) string { 69 var eswitchInlineMode = map[uint8]string{ 70 nl.DEVLINK_ESWITCH_INLINE_MODE_NONE: "none", 71 nl.DEVLINK_ESWITCH_INLINE_MODE_LINK: "link", 72 nl.DEVLINK_ESWITCH_INLINE_MODE_NETWORK: "network", 73 nl.DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT: "transport", 74 } 75 if eswitchInlineMode[inlinemode] == "" { 76 return "unknown" 77 } else { 78 return eswitchInlineMode[inlinemode] 79 } 80} 81 82func parseEswitchEncapMode(encapmode uint8) string { 83 var eswitchEncapMode = map[uint8]string{ 84 nl.DEVLINK_ESWITCH_ENCAP_MODE_NONE: "disable", 85 nl.DEVLINK_ESWITCH_ENCAP_MODE_BASIC: "enable", 86 } 87 if eswitchEncapMode[encapmode] == "" { 88 return "unknown" 89 } else { 90 return eswitchEncapMode[encapmode] 91 } 92} 93 94func (d *DevlinkDevice) parseAttributes(attrs []syscall.NetlinkRouteAttr) error { 95 for _, a := range attrs { 96 switch a.Attr.Type { 97 case nl.DEVLINK_ATTR_BUS_NAME: 98 d.BusName = string(a.Value) 99 case nl.DEVLINK_ATTR_DEV_NAME: 100 d.DeviceName = string(a.Value) 101 case nl.DEVLINK_ATTR_ESWITCH_MODE: 102 d.Attrs.Eswitch.Mode = parseEswitchMode(native.Uint16(a.Value)) 103 case nl.DEVLINK_ATTR_ESWITCH_INLINE_MODE: 104 d.Attrs.Eswitch.InlineMode = parseEswitchInlineMode(uint8(a.Value[0])) 105 case nl.DEVLINK_ATTR_ESWITCH_ENCAP_MODE: 106 d.Attrs.Eswitch.EncapMode = parseEswitchEncapMode(uint8(a.Value[0])) 107 } 108 } 109 return nil 110} 111 112func (dev *DevlinkDevice) parseEswitchAttrs(msgs [][]byte) { 113 m := msgs[0] 114 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 115 if err != nil { 116 return 117 } 118 dev.parseAttributes(attrs) 119} 120 121func (h *Handle) getEswitchAttrs(family *GenlFamily, dev *DevlinkDevice) { 122 msg := &nl.Genlmsg{ 123 Command: nl.DEVLINK_CMD_ESWITCH_GET, 124 Version: nl.GENL_DEVLINK_VERSION, 125 } 126 req := h.newNetlinkRequest(int(family.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK) 127 req.AddData(msg) 128 129 b := make([]byte, len(dev.BusName)) 130 copy(b, dev.BusName) 131 data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b) 132 req.AddData(data) 133 134 b = make([]byte, len(dev.DeviceName)) 135 copy(b, dev.DeviceName) 136 data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b) 137 req.AddData(data) 138 139 msgs, err := req.Execute(unix.NETLINK_GENERIC, 0) 140 if err != nil { 141 return 142 } 143 dev.parseEswitchAttrs(msgs) 144} 145 146// DevLinkGetDeviceList provides a pointer to devlink devices and nil error, 147// otherwise returns an error code. 148func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) { 149 f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME) 150 if err != nil { 151 return nil, err 152 } 153 msg := &nl.Genlmsg{ 154 Command: nl.DEVLINK_CMD_GET, 155 Version: nl.GENL_DEVLINK_VERSION, 156 } 157 req := h.newNetlinkRequest(int(f.ID), 158 unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP) 159 req.AddData(msg) 160 msgs, err := req.Execute(unix.NETLINK_GENERIC, 0) 161 if err != nil { 162 return nil, err 163 } 164 devices, err := parseDevLinkDeviceList(msgs) 165 if err != nil { 166 return nil, err 167 } 168 for _, d := range devices { 169 h.getEswitchAttrs(f, d) 170 } 171 return devices, nil 172} 173 174// DevLinkGetDeviceList provides a pointer to devlink devices and nil error, 175// otherwise returns an error code. 176func DevLinkGetDeviceList() ([]*DevlinkDevice, error) { 177 return pkgHandle.DevLinkGetDeviceList() 178} 179 180func parseDevlinkDevice(msgs [][]byte) (*DevlinkDevice, error) { 181 m := msgs[0] 182 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 183 if err != nil { 184 return nil, err 185 } 186 dev := &DevlinkDevice{} 187 if err = dev.parseAttributes(attrs); err != nil { 188 return nil, err 189 } 190 return dev, nil 191} 192 193func (h *Handle) createCmdReq(cmd uint8, bus string, device string) (*GenlFamily, *nl.NetlinkRequest, error) { 194 f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME) 195 if err != nil { 196 return nil, nil, err 197 } 198 199 msg := &nl.Genlmsg{ 200 Command: cmd, 201 Version: nl.GENL_DEVLINK_VERSION, 202 } 203 req := h.newNetlinkRequest(int(f.ID), 204 unix.NLM_F_REQUEST|unix.NLM_F_ACK) 205 req.AddData(msg) 206 207 b := make([]byte, len(bus)+1) 208 copy(b, bus) 209 data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b) 210 req.AddData(data) 211 212 b = make([]byte, len(device)+1) 213 copy(b, device) 214 data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b) 215 req.AddData(data) 216 217 return f, req, nil 218} 219 220// DevlinkGetDeviceByName provides a pointer to devlink device and nil error, 221// otherwise returns an error code. 222func (h *Handle) DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) { 223 f, req, err := h.createCmdReq(nl.DEVLINK_CMD_GET, Bus, Device) 224 if err != nil { 225 return nil, err 226 } 227 228 respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0) 229 if err != nil { 230 return nil, err 231 } 232 dev, err := parseDevlinkDevice(respmsg) 233 if err == nil { 234 h.getEswitchAttrs(f, dev) 235 } 236 return dev, err 237} 238 239// DevlinkGetDeviceByName provides a pointer to devlink device and nil error, 240// otherwise returns an error code. 241func DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) { 242 return pkgHandle.DevLinkGetDeviceByName(Bus, Device) 243} 244 245// DevLinkSetEswitchMode sets eswitch mode if able to set successfully or 246// returns an error code. 247// Equivalent to: `devlink dev eswitch set $dev mode switchdev` 248// Equivalent to: `devlink dev eswitch set $dev mode legacy` 249func (h *Handle) DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error { 250 mode, err := eswitchStringToMode(NewMode) 251 if err != nil { 252 return err 253 } 254 255 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_ESWITCH_SET, Dev.BusName, Dev.DeviceName) 256 if err != nil { 257 return err 258 } 259 260 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_ESWITCH_MODE, nl.Uint16Attr(mode))) 261 262 _, err = req.Execute(unix.NETLINK_GENERIC, 0) 263 return err 264} 265 266// DevLinkSetEswitchMode sets eswitch mode if able to set successfully or 267// returns an error code. 268// Equivalent to: `devlink dev eswitch set $dev mode switchdev` 269// Equivalent to: `devlink dev eswitch set $dev mode legacy` 270func DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error { 271 return pkgHandle.DevLinkSetEswitchMode(Dev, NewMode) 272} 273