github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/devlink_linux.go (about) 1 package netlink 2 3 import ( 4 "fmt" 5 "net" 6 "strings" 7 "syscall" 8 9 "github.com/sagernet/netlink/nl" 10 "golang.org/x/sys/unix" 11 ) 12 13 // DevlinkDevEswitchAttr represents device's eswitch attributes 14 type DevlinkDevEswitchAttr struct { 15 Mode string 16 InlineMode string 17 EncapMode string 18 } 19 20 // DevlinkDevAttrs represents device attributes 21 type DevlinkDevAttrs struct { 22 Eswitch DevlinkDevEswitchAttr 23 } 24 25 // DevlinkDevice represents device and its attributes 26 type DevlinkDevice struct { 27 BusName string 28 DeviceName string 29 Attrs DevlinkDevAttrs 30 } 31 32 // DevlinkPortFn represents port function and its attributes 33 type DevlinkPortFn struct { 34 HwAddr net.HardwareAddr 35 State uint8 36 OpState uint8 37 } 38 39 // DevlinkPortFnSetAttrs represents attributes to set 40 type DevlinkPortFnSetAttrs struct { 41 FnAttrs DevlinkPortFn 42 HwAddrValid bool 43 StateValid bool 44 } 45 46 // DevlinkPort represents port and its attributes 47 type DevlinkPort struct { 48 BusName string 49 DeviceName string 50 PortIndex uint32 51 PortType uint16 52 NetdeviceName string 53 NetdevIfIndex uint32 54 RdmaDeviceName string 55 PortFlavour uint16 56 Fn *DevlinkPortFn 57 } 58 59 type DevLinkPortAddAttrs struct { 60 Controller uint32 61 SfNumber uint32 62 PortIndex uint32 63 PfNumber uint16 64 SfNumberValid bool 65 PortIndexValid bool 66 ControllerValid bool 67 } 68 69 // DevlinkDeviceInfo represents devlink info 70 type DevlinkDeviceInfo struct { 71 Driver string 72 SerialNumber string 73 BoardID string 74 FwApp string 75 FwAppBoundleID string 76 FwAppName string 77 FwBoundleID string 78 FwMgmt string 79 FwMgmtAPI string 80 FwMgmtBuild string 81 FwNetlist string 82 FwNetlistBuild string 83 FwPsidAPI string 84 FwUndi string 85 } 86 87 func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) { 88 devices := make([]*DevlinkDevice, 0, len(msgs)) 89 for _, m := range msgs { 90 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 91 if err != nil { 92 return nil, err 93 } 94 dev := &DevlinkDevice{} 95 if err = dev.parseAttributes(attrs); err != nil { 96 return nil, err 97 } 98 devices = append(devices, dev) 99 } 100 return devices, nil 101 } 102 103 func eswitchStringToMode(modeName string) (uint16, error) { 104 if modeName == "legacy" { 105 return nl.DEVLINK_ESWITCH_MODE_LEGACY, nil 106 } else if modeName == "switchdev" { 107 return nl.DEVLINK_ESWITCH_MODE_SWITCHDEV, nil 108 } else { 109 return 0xffff, fmt.Errorf("invalid switchdev mode") 110 } 111 } 112 113 func parseEswitchMode(mode uint16) string { 114 var eswitchMode = map[uint16]string{ 115 nl.DEVLINK_ESWITCH_MODE_LEGACY: "legacy", 116 nl.DEVLINK_ESWITCH_MODE_SWITCHDEV: "switchdev", 117 } 118 if eswitchMode[mode] == "" { 119 return "unknown" 120 } else { 121 return eswitchMode[mode] 122 } 123 } 124 125 func parseEswitchInlineMode(inlinemode uint8) string { 126 var eswitchInlineMode = map[uint8]string{ 127 nl.DEVLINK_ESWITCH_INLINE_MODE_NONE: "none", 128 nl.DEVLINK_ESWITCH_INLINE_MODE_LINK: "link", 129 nl.DEVLINK_ESWITCH_INLINE_MODE_NETWORK: "network", 130 nl.DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT: "transport", 131 } 132 if eswitchInlineMode[inlinemode] == "" { 133 return "unknown" 134 } else { 135 return eswitchInlineMode[inlinemode] 136 } 137 } 138 139 func parseEswitchEncapMode(encapmode uint8) string { 140 var eswitchEncapMode = map[uint8]string{ 141 nl.DEVLINK_ESWITCH_ENCAP_MODE_NONE: "disable", 142 nl.DEVLINK_ESWITCH_ENCAP_MODE_BASIC: "enable", 143 } 144 if eswitchEncapMode[encapmode] == "" { 145 return "unknown" 146 } else { 147 return eswitchEncapMode[encapmode] 148 } 149 } 150 151 func (d *DevlinkDevice) parseAttributes(attrs []syscall.NetlinkRouteAttr) error { 152 for _, a := range attrs { 153 switch a.Attr.Type { 154 case nl.DEVLINK_ATTR_BUS_NAME: 155 d.BusName = string(a.Value[:len(a.Value)-1]) 156 case nl.DEVLINK_ATTR_DEV_NAME: 157 d.DeviceName = string(a.Value[:len(a.Value)-1]) 158 case nl.DEVLINK_ATTR_ESWITCH_MODE: 159 d.Attrs.Eswitch.Mode = parseEswitchMode(native.Uint16(a.Value)) 160 case nl.DEVLINK_ATTR_ESWITCH_INLINE_MODE: 161 d.Attrs.Eswitch.InlineMode = parseEswitchInlineMode(uint8(a.Value[0])) 162 case nl.DEVLINK_ATTR_ESWITCH_ENCAP_MODE: 163 d.Attrs.Eswitch.EncapMode = parseEswitchEncapMode(uint8(a.Value[0])) 164 } 165 } 166 return nil 167 } 168 169 func (dev *DevlinkDevice) parseEswitchAttrs(msgs [][]byte) { 170 m := msgs[0] 171 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 172 if err != nil { 173 return 174 } 175 dev.parseAttributes(attrs) 176 } 177 178 func (h *Handle) getEswitchAttrs(family *GenlFamily, dev *DevlinkDevice) { 179 msg := &nl.Genlmsg{ 180 Command: nl.DEVLINK_CMD_ESWITCH_GET, 181 Version: nl.GENL_DEVLINK_VERSION, 182 } 183 req := h.newNetlinkRequest(int(family.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK) 184 req.AddData(msg) 185 186 b := make([]byte, len(dev.BusName)+1) 187 copy(b, dev.BusName) 188 data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b) 189 req.AddData(data) 190 191 b = make([]byte, len(dev.DeviceName)+1) 192 copy(b, dev.DeviceName) 193 data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b) 194 req.AddData(data) 195 196 msgs, err := req.Execute(unix.NETLINK_GENERIC, 0) 197 if err != nil { 198 return 199 } 200 dev.parseEswitchAttrs(msgs) 201 } 202 203 // DevLinkGetDeviceList provides a pointer to devlink devices and nil error, 204 // otherwise returns an error code. 205 func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) { 206 f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME) 207 if err != nil { 208 return nil, err 209 } 210 msg := &nl.Genlmsg{ 211 Command: nl.DEVLINK_CMD_GET, 212 Version: nl.GENL_DEVLINK_VERSION, 213 } 214 req := h.newNetlinkRequest(int(f.ID), 215 unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP) 216 req.AddData(msg) 217 msgs, err := req.Execute(unix.NETLINK_GENERIC, 0) 218 if err != nil { 219 return nil, err 220 } 221 devices, err := parseDevLinkDeviceList(msgs) 222 if err != nil { 223 return nil, err 224 } 225 for _, d := range devices { 226 h.getEswitchAttrs(f, d) 227 } 228 return devices, nil 229 } 230 231 // DevLinkGetDeviceList provides a pointer to devlink devices and nil error, 232 // otherwise returns an error code. 233 func DevLinkGetDeviceList() ([]*DevlinkDevice, error) { 234 return pkgHandle.DevLinkGetDeviceList() 235 } 236 237 func parseDevlinkDevice(msgs [][]byte) (*DevlinkDevice, error) { 238 m := msgs[0] 239 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 240 if err != nil { 241 return nil, err 242 } 243 dev := &DevlinkDevice{} 244 if err = dev.parseAttributes(attrs); err != nil { 245 return nil, err 246 } 247 return dev, nil 248 } 249 250 func (h *Handle) createCmdReq(cmd uint8, bus string, device string) (*GenlFamily, *nl.NetlinkRequest, error) { 251 f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME) 252 if err != nil { 253 return nil, nil, err 254 } 255 256 msg := &nl.Genlmsg{ 257 Command: cmd, 258 Version: nl.GENL_DEVLINK_VERSION, 259 } 260 req := h.newNetlinkRequest(int(f.ID), 261 unix.NLM_F_REQUEST|unix.NLM_F_ACK) 262 req.AddData(msg) 263 264 b := make([]byte, len(bus)+1) 265 copy(b, bus) 266 data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b) 267 req.AddData(data) 268 269 b = make([]byte, len(device)+1) 270 copy(b, device) 271 data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b) 272 req.AddData(data) 273 274 return f, req, nil 275 } 276 277 // DevlinkGetDeviceByName provides a pointer to devlink device and nil error, 278 // otherwise returns an error code. 279 func (h *Handle) DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) { 280 f, req, err := h.createCmdReq(nl.DEVLINK_CMD_GET, Bus, Device) 281 if err != nil { 282 return nil, err 283 } 284 285 respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0) 286 if err != nil { 287 return nil, err 288 } 289 dev, err := parseDevlinkDevice(respmsg) 290 if err == nil { 291 h.getEswitchAttrs(f, dev) 292 } 293 return dev, err 294 } 295 296 // DevlinkGetDeviceByName provides a pointer to devlink device and nil error, 297 // otherwise returns an error code. 298 func DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) { 299 return pkgHandle.DevLinkGetDeviceByName(Bus, Device) 300 } 301 302 // DevLinkSetEswitchMode sets eswitch mode if able to set successfully or 303 // returns an error code. 304 // Equivalent to: `devlink dev eswitch set $dev mode switchdev` 305 // Equivalent to: `devlink dev eswitch set $dev mode legacy` 306 func (h *Handle) DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error { 307 mode, err := eswitchStringToMode(NewMode) 308 if err != nil { 309 return err 310 } 311 312 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_ESWITCH_SET, Dev.BusName, Dev.DeviceName) 313 if err != nil { 314 return err 315 } 316 317 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_ESWITCH_MODE, nl.Uint16Attr(mode))) 318 319 _, err = req.Execute(unix.NETLINK_GENERIC, 0) 320 return err 321 } 322 323 // DevLinkSetEswitchMode sets eswitch mode if able to set successfully or 324 // returns an error code. 325 // Equivalent to: `devlink dev eswitch set $dev mode switchdev` 326 // Equivalent to: `devlink dev eswitch set $dev mode legacy` 327 func DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error { 328 return pkgHandle.DevLinkSetEswitchMode(Dev, NewMode) 329 } 330 331 func (port *DevlinkPort) parseAttributes(attrs []syscall.NetlinkRouteAttr) error { 332 for _, a := range attrs { 333 switch a.Attr.Type { 334 case nl.DEVLINK_ATTR_BUS_NAME: 335 port.BusName = string(a.Value[:len(a.Value)-1]) 336 case nl.DEVLINK_ATTR_DEV_NAME: 337 port.DeviceName = string(a.Value[:len(a.Value)-1]) 338 case nl.DEVLINK_ATTR_PORT_INDEX: 339 port.PortIndex = native.Uint32(a.Value) 340 case nl.DEVLINK_ATTR_PORT_TYPE: 341 port.PortType = native.Uint16(a.Value) 342 case nl.DEVLINK_ATTR_PORT_NETDEV_NAME: 343 port.NetdeviceName = string(a.Value[:len(a.Value)-1]) 344 case nl.DEVLINK_ATTR_PORT_NETDEV_IFINDEX: 345 port.NetdevIfIndex = native.Uint32(a.Value) 346 case nl.DEVLINK_ATTR_PORT_IBDEV_NAME: 347 port.RdmaDeviceName = string(a.Value[:len(a.Value)-1]) 348 case nl.DEVLINK_ATTR_PORT_FLAVOUR: 349 port.PortFlavour = native.Uint16(a.Value) 350 case nl.DEVLINK_ATTR_PORT_FUNCTION: 351 port.Fn = &DevlinkPortFn{} 352 for nested := range nl.ParseAttributes(a.Value) { 353 switch nested.Type { 354 case nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR: 355 port.Fn.HwAddr = nested.Value[:] 356 case nl.DEVLINK_PORT_FN_ATTR_STATE: 357 port.Fn.State = uint8(nested.Value[0]) 358 case nl.DEVLINK_PORT_FN_ATTR_OPSTATE: 359 port.Fn.OpState = uint8(nested.Value[0]) 360 } 361 } 362 } 363 } 364 return nil 365 } 366 367 func parseDevLinkAllPortList(msgs [][]byte) ([]*DevlinkPort, error) { 368 ports := make([]*DevlinkPort, 0, len(msgs)) 369 for _, m := range msgs { 370 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 371 if err != nil { 372 return nil, err 373 } 374 port := &DevlinkPort{} 375 if err = port.parseAttributes(attrs); err != nil { 376 return nil, err 377 } 378 ports = append(ports, port) 379 } 380 return ports, nil 381 } 382 383 // DevLinkGetPortList provides a pointer to devlink ports and nil error, 384 // otherwise returns an error code. 385 func (h *Handle) DevLinkGetAllPortList() ([]*DevlinkPort, error) { 386 f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME) 387 if err != nil { 388 return nil, err 389 } 390 msg := &nl.Genlmsg{ 391 Command: nl.DEVLINK_CMD_PORT_GET, 392 Version: nl.GENL_DEVLINK_VERSION, 393 } 394 req := h.newNetlinkRequest(int(f.ID), 395 unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP) 396 req.AddData(msg) 397 msgs, err := req.Execute(unix.NETLINK_GENERIC, 0) 398 if err != nil { 399 return nil, err 400 } 401 ports, err := parseDevLinkAllPortList(msgs) 402 if err != nil { 403 return nil, err 404 } 405 return ports, nil 406 } 407 408 // DevLinkGetPortList provides a pointer to devlink ports and nil error, 409 // otherwise returns an error code. 410 func DevLinkGetAllPortList() ([]*DevlinkPort, error) { 411 return pkgHandle.DevLinkGetAllPortList() 412 } 413 414 func parseDevlinkPortMsg(msgs [][]byte) (*DevlinkPort, error) { 415 m := msgs[0] 416 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 417 if err != nil { 418 return nil, err 419 } 420 port := &DevlinkPort{} 421 if err = port.parseAttributes(attrs); err != nil { 422 return nil, err 423 } 424 return port, nil 425 } 426 427 // DevLinkGetPortByIndexprovides a pointer to devlink device and nil error, 428 // otherwise returns an error code. 429 func (h *Handle) DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) { 430 431 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_GET, Bus, Device) 432 if err != nil { 433 return nil, err 434 } 435 436 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex))) 437 438 respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0) 439 if err != nil { 440 return nil, err 441 } 442 port, err := parseDevlinkPortMsg(respmsg) 443 return port, err 444 } 445 446 // DevLinkGetPortByIndex provides a pointer to devlink portand nil error, 447 // otherwise returns an error code. 448 func DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) { 449 return pkgHandle.DevLinkGetPortByIndex(Bus, Device, PortIndex) 450 } 451 452 // DevLinkPortAdd adds a devlink port and returns a port on success 453 // otherwise returns nil port and an error code. 454 func (h *Handle) DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) { 455 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_NEW, Bus, Device) 456 if err != nil { 457 return nil, err 458 } 459 460 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(Flavour))) 461 462 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(Attrs.PfNumber))) 463 if Flavour == nl.DEVLINK_PORT_FLAVOUR_PCI_SF && Attrs.SfNumberValid { 464 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_SF_NUMBER, nl.Uint32Attr(Attrs.SfNumber))) 465 } 466 if Attrs.PortIndexValid { 467 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(Attrs.PortIndex))) 468 } 469 if Attrs.ControllerValid { 470 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_CONTROLLER_NUMBER, nl.Uint32Attr(Attrs.Controller))) 471 } 472 respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0) 473 if err != nil { 474 return nil, err 475 } 476 port, err := parseDevlinkPortMsg(respmsg) 477 return port, err 478 } 479 480 // DevLinkPortAdd adds a devlink port and returns a port on success 481 // otherwise returns nil port and an error code. 482 func DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) { 483 return pkgHandle.DevLinkPortAdd(Bus, Device, Flavour, Attrs) 484 } 485 486 // DevLinkPortDel deletes a devlink port and returns success or error code. 487 func (h *Handle) DevLinkPortDel(Bus string, Device string, PortIndex uint32) error { 488 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_DEL, Bus, Device) 489 if err != nil { 490 return err 491 } 492 493 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex))) 494 _, err = req.Execute(unix.NETLINK_GENERIC, 0) 495 return err 496 } 497 498 // DevLinkPortDel deletes a devlink port and returns success or error code. 499 func DevLinkPortDel(Bus string, Device string, PortIndex uint32) error { 500 return pkgHandle.DevLinkPortDel(Bus, Device, PortIndex) 501 } 502 503 // DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask. 504 // It returns 0 on success or error code. 505 func (h *Handle) DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error { 506 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_SET, Bus, Device) 507 if err != nil { 508 return err 509 } 510 511 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex))) 512 513 fnAttr := nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FUNCTION|unix.NLA_F_NESTED, nil) 514 515 if FnAttrs.HwAddrValid { 516 fnAttr.AddRtAttr(nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, []byte(FnAttrs.FnAttrs.HwAddr)) 517 } 518 519 if FnAttrs.StateValid { 520 fnAttr.AddRtAttr(nl.DEVLINK_PORT_FN_ATTR_STATE, nl.Uint8Attr(FnAttrs.FnAttrs.State)) 521 } 522 req.AddData(fnAttr) 523 524 _, err = req.Execute(unix.NETLINK_GENERIC, 0) 525 return err 526 } 527 528 // DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask. 529 // It returns 0 on success or error code. 530 func DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error { 531 return pkgHandle.DevlinkPortFnSet(Bus, Device, PortIndex, FnAttrs) 532 } 533 534 // devlinkInfoGetter is function that is responsible for getting devlink info message 535 // this is introduced for test purpose 536 type devlinkInfoGetter func(bus, device string) ([]byte, error) 537 538 // DevlinkGetDeviceInfoByName returns devlink info for selected device, 539 // otherwise returns an error code. 540 // Equivalent to: `devlink dev info $dev` 541 func (h *Handle) DevlinkGetDeviceInfoByName(Bus string, Device string, getInfoMsg devlinkInfoGetter) (*DevlinkDeviceInfo, error) { 542 info, err := h.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, getInfoMsg) 543 if err != nil { 544 return nil, err 545 } 546 547 return parseInfoData(info), nil 548 } 549 550 // DevlinkGetDeviceInfoByName returns devlink info for selected device, 551 // otherwise returns an error code. 552 // Equivalent to: `devlink dev info $dev` 553 func DevlinkGetDeviceInfoByName(Bus string, Device string) (*DevlinkDeviceInfo, error) { 554 return pkgHandle.DevlinkGetDeviceInfoByName(Bus, Device, pkgHandle.getDevlinkInfoMsg) 555 } 556 557 // DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map, 558 // otherwise returns an error code. 559 // Equivalent to: `devlink dev info $dev` 560 func (h *Handle) DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string, getInfoMsg devlinkInfoGetter) (map[string]string, error) { 561 response, err := getInfoMsg(Bus, Device) 562 if err != nil { 563 return nil, err 564 } 565 566 info, err := parseInfoMsg(response) 567 if err != nil { 568 return nil, err 569 } 570 571 return info, nil 572 } 573 574 // DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map, 575 // otherwise returns an error code. 576 // Equivalent to: `devlink dev info $dev` 577 func DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string) (map[string]string, error) { 578 return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, pkgHandle.getDevlinkInfoMsg) 579 } 580 581 // GetDevlinkInfo returns devlink info for target device, 582 // otherwise returns an error code. 583 func (d *DevlinkDevice) GetDevlinkInfo() (*DevlinkDeviceInfo, error) { 584 return pkgHandle.DevlinkGetDeviceInfoByName(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg) 585 } 586 587 // GetDevlinkInfoAsMap returns devlink info for target device as a map, 588 // otherwise returns an error code. 589 func (d *DevlinkDevice) GetDevlinkInfoAsMap() (map[string]string, error) { 590 return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg) 591 } 592 593 func (h *Handle) getDevlinkInfoMsg(bus, device string) ([]byte, error) { 594 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_INFO_GET, bus, device) 595 if err != nil { 596 return nil, err 597 } 598 599 response, err := req.Execute(unix.NETLINK_GENERIC, 0) 600 if err != nil { 601 return nil, err 602 } 603 604 if len(response) < 1 { 605 return nil, fmt.Errorf("getDevlinkInfoMsg: message too short") 606 } 607 608 return response[0], nil 609 } 610 611 func parseInfoMsg(msg []byte) (map[string]string, error) { 612 if len(msg) < nl.SizeofGenlmsg { 613 return nil, fmt.Errorf("parseInfoMsg: message too short") 614 } 615 616 info := make(map[string]string) 617 err := collectInfoData(msg[nl.SizeofGenlmsg:], info) 618 619 if err != nil { 620 return nil, err 621 } 622 623 return info, nil 624 } 625 626 func collectInfoData(msg []byte, data map[string]string) error { 627 attrs, err := nl.ParseRouteAttr(msg) 628 if err != nil { 629 return err 630 } 631 632 for _, attr := range attrs { 633 switch attr.Attr.Type { 634 case nl.DEVLINK_ATTR_INFO_DRIVER_NAME: 635 data["driver"] = parseInfoValue(attr.Value) 636 case nl.DEVLINK_ATTR_INFO_SERIAL_NUMBER: 637 data["serialNumber"] = parseInfoValue(attr.Value) 638 case nl.DEVLINK_ATTR_INFO_VERSION_RUNNING, nl.DEVLINK_ATTR_INFO_VERSION_FIXED, 639 nl.DEVLINK_ATTR_INFO_VERSION_STORED: 640 key, value, err := getNestedInfoData(attr.Value) 641 if err != nil { 642 return err 643 } 644 data[key] = value 645 } 646 } 647 648 if len(data) == 0 { 649 return fmt.Errorf("collectInfoData: could not read attributes") 650 } 651 652 return nil 653 } 654 655 func getNestedInfoData(msg []byte) (string, string, error) { 656 nestedAttrs, err := nl.ParseRouteAttr(msg) 657 658 var key, value string 659 660 if err != nil { 661 return "", "", err 662 } 663 664 if len(nestedAttrs) != 2 { 665 return "", "", fmt.Errorf("getNestedInfoData: too few attributes in nested structure") 666 } 667 668 for _, nestedAttr := range nestedAttrs { 669 switch nestedAttr.Attr.Type { 670 case nl.DEVLINK_ATTR_INFO_VERSION_NAME: 671 key = parseInfoValue(nestedAttr.Value) 672 case nl.DEVLINK_ATTR_INFO_VERSION_VALUE: 673 value = parseInfoValue(nestedAttr.Value) 674 } 675 } 676 677 if key == "" { 678 return "", "", fmt.Errorf("getNestedInfoData: key not found") 679 } 680 681 if value == "" { 682 return "", "", fmt.Errorf("getNestedInfoData: value not found") 683 } 684 685 return key, value, nil 686 } 687 688 func parseInfoData(data map[string]string) *DevlinkDeviceInfo { 689 info := new(DevlinkDeviceInfo) 690 for key, value := range data { 691 switch key { 692 case "driver": 693 info.Driver = value 694 case "serialNumber": 695 info.SerialNumber = value 696 case "board.id": 697 info.BoardID = value 698 case "fw.app": 699 info.FwApp = value 700 case "fw.app.bundle_id": 701 info.FwAppBoundleID = value 702 case "fw.app.name": 703 info.FwAppName = value 704 case "fw.bundle_id": 705 info.FwBoundleID = value 706 case "fw.mgmt": 707 info.FwMgmt = value 708 case "fw.mgmt.api": 709 info.FwMgmtAPI = value 710 case "fw.mgmt.build": 711 info.FwMgmtBuild = value 712 case "fw.netlist": 713 info.FwNetlist = value 714 case "fw.netlist.build": 715 info.FwNetlistBuild = value 716 case "fw.psid.api": 717 info.FwPsidAPI = value 718 case "fw.undi": 719 info.FwUndi = value 720 } 721 } 722 return info 723 } 724 725 func parseInfoValue(value []byte) string { 726 v := strings.ReplaceAll(string(value), "\x00", "") 727 return strings.TrimSpace(v) 728 }