github.com/vishvananda/netlink@v1.3.1/devlink_linux.go (about) 1 package netlink 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "strings" 8 "syscall" 9 10 "github.com/vishvananda/netlink/nl" 11 "golang.org/x/sys/unix" 12 ) 13 14 // DevlinkDevEswitchAttr represents device's eswitch attributes 15 type DevlinkDevEswitchAttr struct { 16 Mode string 17 InlineMode string 18 EncapMode string 19 } 20 21 // DevlinkDevAttrs represents device attributes 22 type DevlinkDevAttrs struct { 23 Eswitch DevlinkDevEswitchAttr 24 } 25 26 // DevlinkDevice represents device and its attributes 27 type DevlinkDevice struct { 28 BusName string 29 DeviceName string 30 Attrs DevlinkDevAttrs 31 } 32 33 // DevlinkPortFn represents port function and its attributes 34 type DevlinkPortFn struct { 35 HwAddr net.HardwareAddr 36 State uint8 37 OpState uint8 38 } 39 40 // DevlinkPortFnSetAttrs represents attributes to set 41 type DevlinkPortFnSetAttrs struct { 42 FnAttrs DevlinkPortFn 43 HwAddrValid bool 44 StateValid bool 45 } 46 47 // DevlinkPort represents port and its attributes 48 type DevlinkPort struct { 49 BusName string 50 DeviceName string 51 PortIndex uint32 52 PortType uint16 53 NetdeviceName string 54 NetdevIfIndex uint32 55 RdmaDeviceName string 56 PortFlavour uint16 57 Fn *DevlinkPortFn 58 } 59 60 type DevLinkPortAddAttrs struct { 61 Controller uint32 62 SfNumber uint32 63 PortIndex uint32 64 PfNumber uint16 65 SfNumberValid bool 66 PortIndexValid bool 67 ControllerValid bool 68 } 69 70 // DevlinkDeviceInfo represents devlink info 71 type DevlinkDeviceInfo struct { 72 Driver string 73 SerialNumber string 74 BoardID string 75 FwApp string 76 FwAppBoundleID string 77 FwAppName string 78 FwBoundleID string 79 FwMgmt string 80 FwMgmtAPI string 81 FwMgmtBuild string 82 FwNetlist string 83 FwNetlistBuild string 84 FwPsidAPI string 85 FwUndi string 86 } 87 88 // DevlinkResource represents a device resource 89 type DevlinkResource struct { 90 Name string 91 ID uint64 92 Size uint64 93 SizeNew uint64 94 SizeMin uint64 95 SizeMax uint64 96 SizeGranularity uint64 97 PendingChange bool 98 Unit uint8 99 SizeValid bool 100 OCCValid bool 101 OCCSize uint64 102 Parent *DevlinkResource 103 Children []DevlinkResource 104 } 105 106 // parseAttributes parses provided Netlink Attributes and populates DevlinkResource, returns error if occured 107 func (dlr *DevlinkResource) parseAttributes(attrs map[uint16]syscall.NetlinkRouteAttr) error { 108 var attr syscall.NetlinkRouteAttr 109 var ok bool 110 111 // mandatory attributes 112 attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_ID] 113 if !ok { 114 return fmt.Errorf("missing resource id") 115 } 116 dlr.ID = native.Uint64(attr.Value) 117 118 attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_NAME] 119 if !ok { 120 return fmt.Errorf("missing resource name") 121 } 122 dlr.Name = nl.BytesToString(attr.Value) 123 124 attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE] 125 if !ok { 126 return fmt.Errorf("missing resource size") 127 } 128 dlr.Size = native.Uint64(attr.Value) 129 130 attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_GRAN] 131 if !ok { 132 return fmt.Errorf("missing resource size granularity") 133 } 134 dlr.SizeGranularity = native.Uint64(attr.Value) 135 136 attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_UNIT] 137 if !ok { 138 return fmt.Errorf("missing resource unit") 139 } 140 dlr.Unit = uint8(attr.Value[0]) 141 142 attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_MIN] 143 if !ok { 144 return fmt.Errorf("missing resource size min") 145 } 146 dlr.SizeMin = native.Uint64(attr.Value) 147 148 attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_MAX] 149 if !ok { 150 return fmt.Errorf("missing resource size max") 151 } 152 dlr.SizeMax = native.Uint64(attr.Value) 153 154 // optional attributes 155 attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_OCC] 156 if ok { 157 dlr.OCCSize = native.Uint64(attr.Value) 158 dlr.OCCValid = true 159 } 160 161 attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_VALID] 162 if ok { 163 dlr.SizeValid = uint8(attr.Value[0]) != 0 164 } 165 166 dlr.SizeNew = dlr.Size 167 attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_NEW] 168 if ok { 169 dlr.SizeNew = native.Uint64(attr.Value) 170 } 171 172 dlr.PendingChange = dlr.Size != dlr.SizeNew 173 174 attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_LIST] 175 if ok { 176 // handle nested resoruces recursively 177 subResources, err := nl.ParseRouteAttr(attr.Value) 178 if err != nil { 179 return err 180 } 181 182 for _, subresource := range subResources { 183 resource := DevlinkResource{Parent: dlr} 184 attrs, err := nl.ParseRouteAttrAsMap(subresource.Value) 185 if err != nil { 186 return err 187 } 188 err = resource.parseAttributes(attrs) 189 if err != nil { 190 return fmt.Errorf("failed to parse child resource, parent:%s. %w", dlr.Name, err) 191 } 192 dlr.Children = append(dlr.Children, resource) 193 } 194 } 195 return nil 196 } 197 198 // DevlinkResources represents all devlink resources of a devlink device 199 type DevlinkResources struct { 200 Bus string 201 Device string 202 Resources []DevlinkResource 203 } 204 205 // parseAttributes parses provided Netlink Attributes and populates DevlinkResources, returns error if occured 206 func (dlrs *DevlinkResources) parseAttributes(attrs map[uint16]syscall.NetlinkRouteAttr) error { 207 var attr syscall.NetlinkRouteAttr 208 var ok bool 209 210 // Bus 211 attr, ok = attrs[nl.DEVLINK_ATTR_BUS_NAME] 212 if !ok { 213 return fmt.Errorf("missing bus name") 214 } 215 dlrs.Bus = nl.BytesToString(attr.Value) 216 217 // Device 218 attr, ok = attrs[nl.DEVLINK_ATTR_DEV_NAME] 219 if !ok { 220 return fmt.Errorf("missing device name") 221 } 222 dlrs.Device = nl.BytesToString(attr.Value) 223 224 // Resource List 225 attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_LIST] 226 if !ok { 227 return fmt.Errorf("missing resource list") 228 } 229 230 resourceAttrs, err := nl.ParseRouteAttr(attr.Value) 231 if err != nil { 232 return err 233 } 234 235 for _, resourceAttr := range resourceAttrs { 236 resource := DevlinkResource{} 237 attrs, err := nl.ParseRouteAttrAsMap(resourceAttr.Value) 238 if err != nil { 239 return err 240 } 241 err = resource.parseAttributes(attrs) 242 if err != nil { 243 return fmt.Errorf("failed to parse root resoruces, %w", err) 244 } 245 dlrs.Resources = append(dlrs.Resources, resource) 246 } 247 248 return nil 249 } 250 251 // DevlinkParam represents parameter of the device 252 type DevlinkParam struct { 253 Name string 254 IsGeneric bool 255 Type uint8 // possible values are in nl.DEVLINK_PARAM_TYPE_* constants 256 Values []DevlinkParamValue 257 } 258 259 // DevlinkParamValue contains values of the parameter 260 // Data field contains specific type which can be casted by unsing info from the DevlinkParam.Type field 261 type DevlinkParamValue struct { 262 rawData []byte 263 Data interface{} 264 CMODE uint8 // possible values are in nl.DEVLINK_PARAM_CMODE_* constants 265 } 266 267 // parseAttributes parses provided Netlink Attributes and populates DevlinkParam, returns error if occured 268 func (dlp *DevlinkParam) parseAttributes(attrs []syscall.NetlinkRouteAttr) error { 269 var valuesList [][]syscall.NetlinkRouteAttr 270 for _, attr := range attrs { 271 switch attr.Attr.Type { 272 case nl.DEVLINK_ATTR_PARAM: 273 nattrs, err := nl.ParseRouteAttr(attr.Value) 274 if err != nil { 275 return err 276 } 277 for _, nattr := range nattrs { 278 switch nattr.Attr.Type { 279 case nl.DEVLINK_ATTR_PARAM_NAME: 280 dlp.Name = nl.BytesToString(nattr.Value) 281 case nl.DEVLINK_ATTR_PARAM_GENERIC: 282 dlp.IsGeneric = true 283 case nl.DEVLINK_ATTR_PARAM_TYPE: 284 if len(nattr.Value) == 1 { 285 dlp.Type = nattr.Value[0] 286 } 287 case nl.DEVLINK_ATTR_PARAM_VALUES_LIST: 288 nnattrs, err := nl.ParseRouteAttr(nattr.Value) 289 if err != nil { 290 return err 291 } 292 valuesList = append(valuesList, nnattrs) 293 } 294 } 295 } 296 } 297 for _, valAttr := range valuesList { 298 v := DevlinkParamValue{} 299 if err := v.parseAttributes(valAttr, dlp.Type); err != nil { 300 return err 301 } 302 dlp.Values = append(dlp.Values, v) 303 } 304 return nil 305 } 306 307 func (dlpv *DevlinkParamValue) parseAttributes(attrs []syscall.NetlinkRouteAttr, paramType uint8) error { 308 for _, attr := range attrs { 309 nattrs, err := nl.ParseRouteAttr(attr.Value) 310 if err != nil { 311 return err 312 } 313 var rawData []byte 314 for _, nattr := range nattrs { 315 switch nattr.Attr.Type { 316 case nl.DEVLINK_ATTR_PARAM_VALUE_DATA: 317 rawData = nattr.Value 318 case nl.DEVLINK_ATTR_PARAM_VALUE_CMODE: 319 if len(nattr.Value) == 1 { 320 dlpv.CMODE = nattr.Value[0] 321 } 322 } 323 } 324 switch paramType { 325 case nl.DEVLINK_PARAM_TYPE_U8: 326 dlpv.Data = uint8(0) 327 if rawData != nil && len(rawData) == 1 { 328 dlpv.Data = uint8(rawData[0]) 329 } 330 case nl.DEVLINK_PARAM_TYPE_U16: 331 dlpv.Data = uint16(0) 332 if rawData != nil { 333 dlpv.Data = native.Uint16(rawData) 334 } 335 case nl.DEVLINK_PARAM_TYPE_U32: 336 dlpv.Data = uint32(0) 337 if rawData != nil { 338 dlpv.Data = native.Uint32(rawData) 339 } 340 case nl.DEVLINK_PARAM_TYPE_STRING: 341 dlpv.Data = "" 342 if rawData != nil { 343 dlpv.Data = nl.BytesToString(rawData) 344 } 345 case nl.DEVLINK_PARAM_TYPE_BOOL: 346 dlpv.Data = rawData != nil 347 } 348 } 349 return nil 350 } 351 352 func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) { 353 devices := make([]*DevlinkDevice, 0, len(msgs)) 354 for _, m := range msgs { 355 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 356 if err != nil { 357 return nil, err 358 } 359 dev := &DevlinkDevice{} 360 if err = dev.parseAttributes(attrs); err != nil { 361 return nil, err 362 } 363 devices = append(devices, dev) 364 } 365 return devices, nil 366 } 367 368 func eswitchStringToMode(modeName string) (uint16, error) { 369 if modeName == "legacy" { 370 return nl.DEVLINK_ESWITCH_MODE_LEGACY, nil 371 } else if modeName == "switchdev" { 372 return nl.DEVLINK_ESWITCH_MODE_SWITCHDEV, nil 373 } else { 374 return 0xffff, fmt.Errorf("invalid switchdev mode") 375 } 376 } 377 378 func parseEswitchMode(mode uint16) string { 379 var eswitchMode = map[uint16]string{ 380 nl.DEVLINK_ESWITCH_MODE_LEGACY: "legacy", 381 nl.DEVLINK_ESWITCH_MODE_SWITCHDEV: "switchdev", 382 } 383 if eswitchMode[mode] == "" { 384 return "unknown" 385 } else { 386 return eswitchMode[mode] 387 } 388 } 389 390 func parseEswitchInlineMode(inlinemode uint8) string { 391 var eswitchInlineMode = map[uint8]string{ 392 nl.DEVLINK_ESWITCH_INLINE_MODE_NONE: "none", 393 nl.DEVLINK_ESWITCH_INLINE_MODE_LINK: "link", 394 nl.DEVLINK_ESWITCH_INLINE_MODE_NETWORK: "network", 395 nl.DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT: "transport", 396 } 397 if eswitchInlineMode[inlinemode] == "" { 398 return "unknown" 399 } else { 400 return eswitchInlineMode[inlinemode] 401 } 402 } 403 404 func parseEswitchEncapMode(encapmode uint8) string { 405 var eswitchEncapMode = map[uint8]string{ 406 nl.DEVLINK_ESWITCH_ENCAP_MODE_NONE: "disable", 407 nl.DEVLINK_ESWITCH_ENCAP_MODE_BASIC: "enable", 408 } 409 if eswitchEncapMode[encapmode] == "" { 410 return "unknown" 411 } else { 412 return eswitchEncapMode[encapmode] 413 } 414 } 415 416 func (d *DevlinkDevice) parseAttributes(attrs []syscall.NetlinkRouteAttr) error { 417 for _, a := range attrs { 418 switch a.Attr.Type { 419 case nl.DEVLINK_ATTR_BUS_NAME: 420 d.BusName = string(a.Value[:len(a.Value)-1]) 421 case nl.DEVLINK_ATTR_DEV_NAME: 422 d.DeviceName = string(a.Value[:len(a.Value)-1]) 423 case nl.DEVLINK_ATTR_ESWITCH_MODE: 424 d.Attrs.Eswitch.Mode = parseEswitchMode(native.Uint16(a.Value)) 425 case nl.DEVLINK_ATTR_ESWITCH_INLINE_MODE: 426 d.Attrs.Eswitch.InlineMode = parseEswitchInlineMode(uint8(a.Value[0])) 427 case nl.DEVLINK_ATTR_ESWITCH_ENCAP_MODE: 428 d.Attrs.Eswitch.EncapMode = parseEswitchEncapMode(uint8(a.Value[0])) 429 } 430 } 431 return nil 432 } 433 434 func (dev *DevlinkDevice) parseEswitchAttrs(msgs [][]byte) { 435 m := msgs[0] 436 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 437 if err != nil { 438 return 439 } 440 dev.parseAttributes(attrs) 441 } 442 443 func (h *Handle) getEswitchAttrs(family *GenlFamily, dev *DevlinkDevice) { 444 msg := &nl.Genlmsg{ 445 Command: nl.DEVLINK_CMD_ESWITCH_GET, 446 Version: nl.GENL_DEVLINK_VERSION, 447 } 448 req := h.newNetlinkRequest(int(family.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK) 449 req.AddData(msg) 450 451 b := make([]byte, len(dev.BusName)+1) 452 copy(b, dev.BusName) 453 data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b) 454 req.AddData(data) 455 456 b = make([]byte, len(dev.DeviceName)+1) 457 copy(b, dev.DeviceName) 458 data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b) 459 req.AddData(data) 460 461 msgs, err := req.Execute(unix.NETLINK_GENERIC, 0) 462 if err != nil { 463 return 464 } 465 dev.parseEswitchAttrs(msgs) 466 } 467 468 // DevLinkGetDeviceList provides a pointer to devlink devices and nil error, 469 // otherwise returns an error code. 470 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 471 // or incomplete. 472 func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) { 473 f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME) 474 if err != nil { 475 return nil, err 476 } 477 msg := &nl.Genlmsg{ 478 Command: nl.DEVLINK_CMD_GET, 479 Version: nl.GENL_DEVLINK_VERSION, 480 } 481 req := h.newNetlinkRequest(int(f.ID), 482 unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP) 483 req.AddData(msg) 484 msgs, executeErr := req.Execute(unix.NETLINK_GENERIC, 0) 485 if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) { 486 return nil, executeErr 487 } 488 devices, err := parseDevLinkDeviceList(msgs) 489 if err != nil { 490 return nil, err 491 } 492 for _, d := range devices { 493 h.getEswitchAttrs(f, d) 494 } 495 return devices, executeErr 496 } 497 498 // DevLinkGetDeviceList provides a pointer to devlink devices and nil error, 499 // otherwise returns an error code. 500 // 501 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 502 // or incomplete. 503 func DevLinkGetDeviceList() ([]*DevlinkDevice, error) { 504 return pkgHandle.DevLinkGetDeviceList() 505 } 506 507 func parseDevlinkDevice(msgs [][]byte) (*DevlinkDevice, error) { 508 m := msgs[0] 509 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 510 if err != nil { 511 return nil, err 512 } 513 dev := &DevlinkDevice{} 514 if err = dev.parseAttributes(attrs); err != nil { 515 return nil, err 516 } 517 return dev, nil 518 } 519 520 func (h *Handle) createCmdReq(cmd uint8, bus string, device string) (*GenlFamily, *nl.NetlinkRequest, error) { 521 f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME) 522 if err != nil { 523 return nil, nil, err 524 } 525 526 msg := &nl.Genlmsg{ 527 Command: cmd, 528 Version: nl.GENL_DEVLINK_VERSION, 529 } 530 req := h.newNetlinkRequest(int(f.ID), 531 unix.NLM_F_REQUEST|unix.NLM_F_ACK) 532 req.AddData(msg) 533 534 b := make([]byte, len(bus)+1) 535 copy(b, bus) 536 data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b) 537 req.AddData(data) 538 539 b = make([]byte, len(device)+1) 540 copy(b, device) 541 data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b) 542 req.AddData(data) 543 544 return f, req, nil 545 } 546 547 // DevlinkGetDeviceByName provides a pointer to devlink device and nil error, 548 // otherwise returns an error code. 549 func (h *Handle) DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) { 550 f, req, err := h.createCmdReq(nl.DEVLINK_CMD_GET, Bus, Device) 551 if err != nil { 552 return nil, err 553 } 554 555 respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0) 556 if err != nil { 557 return nil, err 558 } 559 dev, err := parseDevlinkDevice(respmsg) 560 if err == nil { 561 h.getEswitchAttrs(f, dev) 562 } 563 return dev, err 564 } 565 566 // DevlinkGetDeviceByName provides a pointer to devlink device and nil error, 567 // otherwise returns an error code. 568 func DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) { 569 return pkgHandle.DevLinkGetDeviceByName(Bus, Device) 570 } 571 572 // DevLinkSetEswitchMode sets eswitch mode if able to set successfully or 573 // returns an error code. 574 // Equivalent to: `devlink dev eswitch set $dev mode switchdev` 575 // Equivalent to: `devlink dev eswitch set $dev mode legacy` 576 func (h *Handle) DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error { 577 mode, err := eswitchStringToMode(NewMode) 578 if err != nil { 579 return err 580 } 581 582 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_ESWITCH_SET, Dev.BusName, Dev.DeviceName) 583 if err != nil { 584 return err 585 } 586 587 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_ESWITCH_MODE, nl.Uint16Attr(mode))) 588 589 _, err = req.Execute(unix.NETLINK_GENERIC, 0) 590 return err 591 } 592 593 // DevLinkSetEswitchMode sets eswitch mode if able to set successfully or 594 // returns an error code. 595 // Equivalent to: `devlink dev eswitch set $dev mode switchdev` 596 // Equivalent to: `devlink dev eswitch set $dev mode legacy` 597 func DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error { 598 return pkgHandle.DevLinkSetEswitchMode(Dev, NewMode) 599 } 600 601 func (port *DevlinkPort) parseAttributes(attrs []syscall.NetlinkRouteAttr) error { 602 for _, a := range attrs { 603 switch a.Attr.Type { 604 case nl.DEVLINK_ATTR_BUS_NAME: 605 port.BusName = string(a.Value[:len(a.Value)-1]) 606 case nl.DEVLINK_ATTR_DEV_NAME: 607 port.DeviceName = string(a.Value[:len(a.Value)-1]) 608 case nl.DEVLINK_ATTR_PORT_INDEX: 609 port.PortIndex = native.Uint32(a.Value) 610 case nl.DEVLINK_ATTR_PORT_TYPE: 611 port.PortType = native.Uint16(a.Value) 612 case nl.DEVLINK_ATTR_PORT_NETDEV_NAME: 613 port.NetdeviceName = string(a.Value[:len(a.Value)-1]) 614 case nl.DEVLINK_ATTR_PORT_NETDEV_IFINDEX: 615 port.NetdevIfIndex = native.Uint32(a.Value) 616 case nl.DEVLINK_ATTR_PORT_IBDEV_NAME: 617 port.RdmaDeviceName = string(a.Value[:len(a.Value)-1]) 618 case nl.DEVLINK_ATTR_PORT_FLAVOUR: 619 port.PortFlavour = native.Uint16(a.Value) 620 case nl.DEVLINK_ATTR_PORT_FUNCTION: 621 port.Fn = &DevlinkPortFn{} 622 for nested := range nl.ParseAttributes(a.Value) { 623 switch nested.Type { 624 case nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR: 625 port.Fn.HwAddr = nested.Value[:] 626 case nl.DEVLINK_PORT_FN_ATTR_STATE: 627 port.Fn.State = uint8(nested.Value[0]) 628 case nl.DEVLINK_PORT_FN_ATTR_OPSTATE: 629 port.Fn.OpState = uint8(nested.Value[0]) 630 } 631 } 632 } 633 } 634 return nil 635 } 636 637 func parseDevLinkAllPortList(msgs [][]byte) ([]*DevlinkPort, error) { 638 ports := make([]*DevlinkPort, 0, len(msgs)) 639 for _, m := range msgs { 640 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 641 if err != nil { 642 return nil, err 643 } 644 port := &DevlinkPort{} 645 if err = port.parseAttributes(attrs); err != nil { 646 return nil, err 647 } 648 ports = append(ports, port) 649 } 650 return ports, nil 651 } 652 653 // DevLinkGetPortList provides a pointer to devlink ports and nil error, 654 // otherwise returns an error code. 655 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 656 // or incomplete. 657 func (h *Handle) DevLinkGetAllPortList() ([]*DevlinkPort, error) { 658 f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME) 659 if err != nil { 660 return nil, err 661 } 662 msg := &nl.Genlmsg{ 663 Command: nl.DEVLINK_CMD_PORT_GET, 664 Version: nl.GENL_DEVLINK_VERSION, 665 } 666 req := h.newNetlinkRequest(int(f.ID), 667 unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP) 668 req.AddData(msg) 669 msgs, executeErr := req.Execute(unix.NETLINK_GENERIC, 0) 670 if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) { 671 return nil, executeErr 672 } 673 ports, err := parseDevLinkAllPortList(msgs) 674 if err != nil { 675 return nil, err 676 } 677 return ports, executeErr 678 } 679 680 // DevLinkGetPortList provides a pointer to devlink ports and nil error, 681 // otherwise returns an error code. 682 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 683 // or incomplete. 684 func DevLinkGetAllPortList() ([]*DevlinkPort, error) { 685 return pkgHandle.DevLinkGetAllPortList() 686 } 687 688 func parseDevlinkPortMsg(msgs [][]byte) (*DevlinkPort, error) { 689 m := msgs[0] 690 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 691 if err != nil { 692 return nil, err 693 } 694 port := &DevlinkPort{} 695 if err = port.parseAttributes(attrs); err != nil { 696 return nil, err 697 } 698 return port, nil 699 } 700 701 // DevLinkGetPortByIndexprovides a pointer to devlink device and nil error, 702 // otherwise returns an error code. 703 func (h *Handle) DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) { 704 705 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_GET, Bus, Device) 706 if err != nil { 707 return nil, err 708 } 709 710 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex))) 711 712 respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0) 713 if err != nil { 714 return nil, err 715 } 716 port, err := parseDevlinkPortMsg(respmsg) 717 return port, err 718 } 719 720 // DevlinkGetDeviceResources returns devlink device resources 721 func DevlinkGetDeviceResources(bus string, device string) (*DevlinkResources, error) { 722 return pkgHandle.DevlinkGetDeviceResources(bus, device) 723 } 724 725 // DevlinkGetDeviceResources returns devlink device resources 726 func (h *Handle) DevlinkGetDeviceResources(bus string, device string) (*DevlinkResources, error) { 727 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_RESOURCE_DUMP, bus, device) 728 if err != nil { 729 return nil, err 730 } 731 732 respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0) 733 if err != nil { 734 return nil, err 735 } 736 737 var resources DevlinkResources 738 for _, m := range respmsg { 739 attrs, err := nl.ParseRouteAttrAsMap(m[nl.SizeofGenlmsg:]) 740 if err != nil { 741 return nil, err 742 } 743 resources.parseAttributes(attrs) 744 } 745 746 return &resources, nil 747 } 748 749 // DevlinkGetDeviceParams returns parameters for devlink device 750 // Equivalent to: `devlink dev param show <bus>/<device>` 751 // 752 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 753 // or incomplete. 754 func (h *Handle) DevlinkGetDeviceParams(bus string, device string) ([]*DevlinkParam, error) { 755 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PARAM_GET, bus, device) 756 if err != nil { 757 return nil, err 758 } 759 req.Flags |= unix.NLM_F_DUMP 760 respmsg, executeErr := req.Execute(unix.NETLINK_GENERIC, 0) 761 if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) { 762 return nil, executeErr 763 } 764 var params []*DevlinkParam 765 for _, m := range respmsg { 766 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 767 if err != nil { 768 return nil, err 769 } 770 p := &DevlinkParam{} 771 if err := p.parseAttributes(attrs); err != nil { 772 return nil, err 773 } 774 params = append(params, p) 775 } 776 777 return params, executeErr 778 } 779 780 // DevlinkGetDeviceParams returns parameters for devlink device 781 // Equivalent to: `devlink dev param show <bus>/<device>` 782 // 783 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 784 // or incomplete. 785 func DevlinkGetDeviceParams(bus string, device string) ([]*DevlinkParam, error) { 786 return pkgHandle.DevlinkGetDeviceParams(bus, device) 787 } 788 789 // DevlinkGetDeviceParamByName returns specific parameter for devlink device 790 // Equivalent to: `devlink dev param show <bus>/<device> name <param>` 791 func (h *Handle) DevlinkGetDeviceParamByName(bus string, device string, param string) (*DevlinkParam, error) { 792 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PARAM_GET, bus, device) 793 if err != nil { 794 return nil, err 795 } 796 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_NAME, nl.ZeroTerminated(param))) 797 respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0) 798 if err != nil { 799 return nil, err 800 } 801 if len(respmsg) == 0 { 802 return nil, fmt.Errorf("unexpected response") 803 } 804 attrs, err := nl.ParseRouteAttr(respmsg[0][nl.SizeofGenlmsg:]) 805 if err != nil { 806 return nil, err 807 } 808 p := &DevlinkParam{} 809 if err := p.parseAttributes(attrs); err != nil { 810 return nil, err 811 } 812 return p, nil 813 } 814 815 // DevlinkGetDeviceParamByName returns specific parameter for devlink device 816 // Equivalent to: `devlink dev param show <bus>/<device> name <param>` 817 func DevlinkGetDeviceParamByName(bus string, device string, param string) (*DevlinkParam, error) { 818 return pkgHandle.DevlinkGetDeviceParamByName(bus, device, param) 819 } 820 821 // DevlinkSetDeviceParam set specific parameter for devlink device 822 // Equivalent to: `devlink dev param set <bus>/<device> name <param> cmode <cmode> value <value>` 823 // cmode argument should contain valid cmode value as uint8, modes are define in nl.DEVLINK_PARAM_CMODE_* constants 824 // value argument should have one of the following types: uint8, uint16, uint32, string, bool 825 func (h *Handle) DevlinkSetDeviceParam(bus string, device string, param string, cmode uint8, value interface{}) error { 826 // retrive the param type 827 p, err := h.DevlinkGetDeviceParamByName(bus, device, param) 828 if err != nil { 829 return fmt.Errorf("failed to get device param: %v", err) 830 } 831 paramType := p.Type 832 833 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PARAM_SET, bus, device) 834 if err != nil { 835 return err 836 } 837 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_TYPE, nl.Uint8Attr(paramType))) 838 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_NAME, nl.ZeroTerminated(param))) 839 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_VALUE_CMODE, nl.Uint8Attr(cmode))) 840 841 var valueAsBytes []byte 842 switch paramType { 843 case nl.DEVLINK_PARAM_TYPE_U8: 844 v, ok := value.(uint8) 845 if !ok { 846 return fmt.Errorf("unepected value type required: uint8, actual: %T", value) 847 } 848 valueAsBytes = nl.Uint8Attr(v) 849 case nl.DEVLINK_PARAM_TYPE_U16: 850 v, ok := value.(uint16) 851 if !ok { 852 return fmt.Errorf("unepected value type required: uint16, actual: %T", value) 853 } 854 valueAsBytes = nl.Uint16Attr(v) 855 case nl.DEVLINK_PARAM_TYPE_U32: 856 v, ok := value.(uint32) 857 if !ok { 858 return fmt.Errorf("unepected value type required: uint32, actual: %T", value) 859 } 860 valueAsBytes = nl.Uint32Attr(v) 861 case nl.DEVLINK_PARAM_TYPE_STRING: 862 v, ok := value.(string) 863 if !ok { 864 return fmt.Errorf("unepected value type required: string, actual: %T", value) 865 } 866 valueAsBytes = nl.ZeroTerminated(v) 867 case nl.DEVLINK_PARAM_TYPE_BOOL: 868 v, ok := value.(bool) 869 if !ok { 870 return fmt.Errorf("unepected value type required: bool, actual: %T", value) 871 } 872 if v { 873 valueAsBytes = []byte{} 874 } 875 default: 876 return fmt.Errorf("unsupported parameter type: %d", paramType) 877 } 878 if valueAsBytes != nil { 879 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_VALUE_DATA, valueAsBytes)) 880 } 881 _, err = req.Execute(unix.NETLINK_GENERIC, 0) 882 return err 883 } 884 885 // DevlinkSetDeviceParam set specific parameter for devlink device 886 // Equivalent to: `devlink dev param set <bus>/<device> name <param> cmode <cmode> value <value>` 887 // cmode argument should contain valid cmode value as uint8, modes are define in nl.DEVLINK_PARAM_CMODE_* constants 888 // value argument should have one of the following types: uint8, uint16, uint32, string, bool 889 func DevlinkSetDeviceParam(bus string, device string, param string, cmode uint8, value interface{}) error { 890 return pkgHandle.DevlinkSetDeviceParam(bus, device, param, cmode, value) 891 } 892 893 // DevLinkGetPortByIndex provides a pointer to devlink portand nil error, 894 // otherwise returns an error code. 895 func DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) { 896 return pkgHandle.DevLinkGetPortByIndex(Bus, Device, PortIndex) 897 } 898 899 // DevLinkPortAdd adds a devlink port and returns a port on success 900 // otherwise returns nil port and an error code. 901 func (h *Handle) DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) { 902 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_NEW, Bus, Device) 903 if err != nil { 904 return nil, err 905 } 906 907 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(Flavour))) 908 909 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(Attrs.PfNumber))) 910 if Flavour == nl.DEVLINK_PORT_FLAVOUR_PCI_SF && Attrs.SfNumberValid { 911 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_SF_NUMBER, nl.Uint32Attr(Attrs.SfNumber))) 912 } 913 if Attrs.PortIndexValid { 914 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(Attrs.PortIndex))) 915 } 916 if Attrs.ControllerValid { 917 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_CONTROLLER_NUMBER, nl.Uint32Attr(Attrs.Controller))) 918 } 919 respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0) 920 if err != nil { 921 return nil, err 922 } 923 port, err := parseDevlinkPortMsg(respmsg) 924 return port, err 925 } 926 927 // DevLinkPortAdd adds a devlink port and returns a port on success 928 // otherwise returns nil port and an error code. 929 func DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) { 930 return pkgHandle.DevLinkPortAdd(Bus, Device, Flavour, Attrs) 931 } 932 933 // DevLinkPortDel deletes a devlink port and returns success or error code. 934 func (h *Handle) DevLinkPortDel(Bus string, Device string, PortIndex uint32) error { 935 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_DEL, Bus, Device) 936 if err != nil { 937 return err 938 } 939 940 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex))) 941 _, err = req.Execute(unix.NETLINK_GENERIC, 0) 942 return err 943 } 944 945 // DevLinkPortDel deletes a devlink port and returns success or error code. 946 func DevLinkPortDel(Bus string, Device string, PortIndex uint32) error { 947 return pkgHandle.DevLinkPortDel(Bus, Device, PortIndex) 948 } 949 950 // DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask. 951 // It returns 0 on success or error code. 952 func (h *Handle) DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error { 953 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_SET, Bus, Device) 954 if err != nil { 955 return err 956 } 957 958 req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex))) 959 960 fnAttr := nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FUNCTION|unix.NLA_F_NESTED, nil) 961 962 if FnAttrs.HwAddrValid { 963 fnAttr.AddRtAttr(nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, []byte(FnAttrs.FnAttrs.HwAddr)) 964 } 965 966 if FnAttrs.StateValid { 967 fnAttr.AddRtAttr(nl.DEVLINK_PORT_FN_ATTR_STATE, nl.Uint8Attr(FnAttrs.FnAttrs.State)) 968 } 969 req.AddData(fnAttr) 970 971 _, err = req.Execute(unix.NETLINK_GENERIC, 0) 972 return err 973 } 974 975 // DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask. 976 // It returns 0 on success or error code. 977 func DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error { 978 return pkgHandle.DevlinkPortFnSet(Bus, Device, PortIndex, FnAttrs) 979 } 980 981 // devlinkInfoGetter is function that is responsible for getting devlink info message 982 // this is introduced for test purpose 983 type devlinkInfoGetter func(bus, device string) ([]byte, error) 984 985 // DevlinkGetDeviceInfoByName returns devlink info for selected device, 986 // otherwise returns an error code. 987 // Equivalent to: `devlink dev info $dev` 988 func (h *Handle) DevlinkGetDeviceInfoByName(Bus string, Device string, getInfoMsg devlinkInfoGetter) (*DevlinkDeviceInfo, error) { 989 info, err := h.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, getInfoMsg) 990 if err != nil { 991 return nil, err 992 } 993 994 return parseInfoData(info), nil 995 } 996 997 // DevlinkGetDeviceInfoByName returns devlink info for selected device, 998 // otherwise returns an error code. 999 // Equivalent to: `devlink dev info $dev` 1000 func DevlinkGetDeviceInfoByName(Bus string, Device string) (*DevlinkDeviceInfo, error) { 1001 return pkgHandle.DevlinkGetDeviceInfoByName(Bus, Device, pkgHandle.getDevlinkInfoMsg) 1002 } 1003 1004 // DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map, 1005 // otherwise returns an error code. 1006 // Equivalent to: `devlink dev info $dev` 1007 func (h *Handle) DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string, getInfoMsg devlinkInfoGetter) (map[string]string, error) { 1008 response, err := getInfoMsg(Bus, Device) 1009 if err != nil { 1010 return nil, err 1011 } 1012 1013 info, err := parseInfoMsg(response) 1014 if err != nil { 1015 return nil, err 1016 } 1017 1018 return info, nil 1019 } 1020 1021 // DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map, 1022 // otherwise returns an error code. 1023 // Equivalent to: `devlink dev info $dev` 1024 func DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string) (map[string]string, error) { 1025 return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, pkgHandle.getDevlinkInfoMsg) 1026 } 1027 1028 // GetDevlinkInfo returns devlink info for target device, 1029 // otherwise returns an error code. 1030 func (d *DevlinkDevice) GetDevlinkInfo() (*DevlinkDeviceInfo, error) { 1031 return pkgHandle.DevlinkGetDeviceInfoByName(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg) 1032 } 1033 1034 // GetDevlinkInfoAsMap returns devlink info for target device as a map, 1035 // otherwise returns an error code. 1036 func (d *DevlinkDevice) GetDevlinkInfoAsMap() (map[string]string, error) { 1037 return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg) 1038 } 1039 1040 func (h *Handle) getDevlinkInfoMsg(bus, device string) ([]byte, error) { 1041 _, req, err := h.createCmdReq(nl.DEVLINK_CMD_INFO_GET, bus, device) 1042 if err != nil { 1043 return nil, err 1044 } 1045 1046 response, err := req.Execute(unix.NETLINK_GENERIC, 0) 1047 if err != nil { 1048 return nil, err 1049 } 1050 1051 if len(response) < 1 { 1052 return nil, fmt.Errorf("getDevlinkInfoMsg: message too short") 1053 } 1054 1055 return response[0], nil 1056 } 1057 1058 func parseInfoMsg(msg []byte) (map[string]string, error) { 1059 if len(msg) < nl.SizeofGenlmsg { 1060 return nil, fmt.Errorf("parseInfoMsg: message too short") 1061 } 1062 1063 info := make(map[string]string) 1064 err := collectInfoData(msg[nl.SizeofGenlmsg:], info) 1065 1066 if err != nil { 1067 return nil, err 1068 } 1069 1070 return info, nil 1071 } 1072 1073 func collectInfoData(msg []byte, data map[string]string) error { 1074 attrs, err := nl.ParseRouteAttr(msg) 1075 if err != nil { 1076 return err 1077 } 1078 1079 for _, attr := range attrs { 1080 switch attr.Attr.Type { 1081 case nl.DEVLINK_ATTR_INFO_DRIVER_NAME: 1082 data["driver"] = parseInfoValue(attr.Value) 1083 case nl.DEVLINK_ATTR_INFO_SERIAL_NUMBER: 1084 data["serialNumber"] = parseInfoValue(attr.Value) 1085 case nl.DEVLINK_ATTR_INFO_VERSION_RUNNING, nl.DEVLINK_ATTR_INFO_VERSION_FIXED, 1086 nl.DEVLINK_ATTR_INFO_VERSION_STORED: 1087 key, value, err := getNestedInfoData(attr.Value) 1088 if err != nil { 1089 return err 1090 } 1091 data[key] = value 1092 } 1093 } 1094 1095 if len(data) == 0 { 1096 return fmt.Errorf("collectInfoData: could not read attributes") 1097 } 1098 1099 return nil 1100 } 1101 1102 func getNestedInfoData(msg []byte) (string, string, error) { 1103 nestedAttrs, err := nl.ParseRouteAttr(msg) 1104 1105 var key, value string 1106 1107 if err != nil { 1108 return "", "", err 1109 } 1110 1111 if len(nestedAttrs) != 2 { 1112 return "", "", fmt.Errorf("getNestedInfoData: too few attributes in nested structure") 1113 } 1114 1115 for _, nestedAttr := range nestedAttrs { 1116 switch nestedAttr.Attr.Type { 1117 case nl.DEVLINK_ATTR_INFO_VERSION_NAME: 1118 key = parseInfoValue(nestedAttr.Value) 1119 case nl.DEVLINK_ATTR_INFO_VERSION_VALUE: 1120 value = parseInfoValue(nestedAttr.Value) 1121 } 1122 } 1123 1124 if key == "" { 1125 return "", "", fmt.Errorf("getNestedInfoData: key not found") 1126 } 1127 1128 if value == "" { 1129 return "", "", fmt.Errorf("getNestedInfoData: value not found") 1130 } 1131 1132 return key, value, nil 1133 } 1134 1135 func parseInfoData(data map[string]string) *DevlinkDeviceInfo { 1136 info := new(DevlinkDeviceInfo) 1137 for key, value := range data { 1138 switch key { 1139 case "driver": 1140 info.Driver = value 1141 case "serialNumber": 1142 info.SerialNumber = value 1143 case "board.id": 1144 info.BoardID = value 1145 case "fw.app": 1146 info.FwApp = value 1147 case "fw.app.bundle_id": 1148 info.FwAppBoundleID = value 1149 case "fw.app.name": 1150 info.FwAppName = value 1151 case "fw.bundle_id": 1152 info.FwBoundleID = value 1153 case "fw.mgmt": 1154 info.FwMgmt = value 1155 case "fw.mgmt.api": 1156 info.FwMgmtAPI = value 1157 case "fw.mgmt.build": 1158 info.FwMgmtBuild = value 1159 case "fw.netlist": 1160 info.FwNetlist = value 1161 case "fw.netlist.build": 1162 info.FwNetlistBuild = value 1163 case "fw.psid.api": 1164 info.FwPsidAPI = value 1165 case "fw.undi": 1166 info.FwUndi = value 1167 } 1168 } 1169 return info 1170 } 1171 1172 func parseInfoValue(value []byte) string { 1173 v := strings.ReplaceAll(string(value), "\x00", "") 1174 return strings.TrimSpace(v) 1175 }