github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/rdma_link_linux.go (about) 1 package netlink 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "net" 8 9 "github.com/sagernet/netlink/nl" 10 "golang.org/x/sys/unix" 11 ) 12 13 // LinkAttrs represents data shared by most link types 14 type RdmaLinkAttrs struct { 15 Index uint32 16 Name string 17 FirmwareVersion string 18 NodeGuid string 19 SysImageGuid string 20 } 21 22 // Link represents a rdma device from netlink. 23 type RdmaLink struct { 24 Attrs RdmaLinkAttrs 25 } 26 27 func getProtoField(clientType int, op int) int { 28 return ((clientType << nl.RDMA_NL_GET_CLIENT_SHIFT) | op) 29 } 30 31 func uint64ToGuidString(guid uint64) string { 32 //Convert to byte array 33 sysGuidBytes := new(bytes.Buffer) 34 binary.Write(sysGuidBytes, binary.LittleEndian, guid) 35 36 //Convert to HardwareAddr 37 sysGuidNet := net.HardwareAddr(sysGuidBytes.Bytes()) 38 39 //Get the String 40 return sysGuidNet.String() 41 } 42 43 func executeOneGetRdmaLink(data []byte) (*RdmaLink, error) { 44 45 link := RdmaLink{} 46 47 reader := bytes.NewReader(data) 48 for reader.Len() >= 4 { 49 _, attrType, len, value := parseNfAttrTLV(reader) 50 51 switch attrType { 52 case nl.RDMA_NLDEV_ATTR_DEV_INDEX: 53 var Index uint32 54 r := bytes.NewReader(value) 55 binary.Read(r, nl.NativeEndian(), &Index) 56 link.Attrs.Index = Index 57 case nl.RDMA_NLDEV_ATTR_DEV_NAME: 58 link.Attrs.Name = string(value[0 : len-1]) 59 case nl.RDMA_NLDEV_ATTR_FW_VERSION: 60 link.Attrs.FirmwareVersion = string(value[0 : len-1]) 61 case nl.RDMA_NLDEV_ATTR_NODE_GUID: 62 var guid uint64 63 r := bytes.NewReader(value) 64 binary.Read(r, nl.NativeEndian(), &guid) 65 link.Attrs.NodeGuid = uint64ToGuidString(guid) 66 case nl.RDMA_NLDEV_ATTR_SYS_IMAGE_GUID: 67 var sysGuid uint64 68 r := bytes.NewReader(value) 69 binary.Read(r, nl.NativeEndian(), &sysGuid) 70 link.Attrs.SysImageGuid = uint64ToGuidString(sysGuid) 71 } 72 if (len % 4) != 0 { 73 // Skip pad bytes 74 reader.Seek(int64(4-(len%4)), seekCurrent) 75 } 76 } 77 return &link, nil 78 } 79 80 func execRdmaSetLink(req *nl.NetlinkRequest) error { 81 82 _, err := req.Execute(unix.NETLINK_RDMA, 0) 83 return err 84 } 85 86 // RdmaLinkList gets a list of RDMA link devices. 87 // Equivalent to: `rdma dev show` 88 func RdmaLinkList() ([]*RdmaLink, error) { 89 return pkgHandle.RdmaLinkList() 90 } 91 92 // RdmaLinkList gets a list of RDMA link devices. 93 // Equivalent to: `rdma dev show` 94 func (h *Handle) RdmaLinkList() ([]*RdmaLink, error) { 95 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_GET) 96 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_DUMP) 97 98 msgs, err := req.Execute(unix.NETLINK_RDMA, 0) 99 if err != nil { 100 return nil, err 101 } 102 103 var res []*RdmaLink 104 for _, m := range msgs { 105 link, err := executeOneGetRdmaLink(m) 106 if err != nil { 107 return nil, err 108 } 109 res = append(res, link) 110 } 111 112 return res, nil 113 } 114 115 // RdmaLinkByName finds a link by name and returns a pointer to the object if 116 // found and nil error, otherwise returns error code. 117 func RdmaLinkByName(name string) (*RdmaLink, error) { 118 return pkgHandle.RdmaLinkByName(name) 119 } 120 121 // RdmaLinkByName finds a link by name and returns a pointer to the object if 122 // found and nil error, otherwise returns error code. 123 func (h *Handle) RdmaLinkByName(name string) (*RdmaLink, error) { 124 links, err := h.RdmaLinkList() 125 if err != nil { 126 return nil, err 127 } 128 for _, link := range links { 129 if link.Attrs.Name == name { 130 return link, nil 131 } 132 } 133 return nil, fmt.Errorf("Rdma device %v not found", name) 134 } 135 136 // RdmaLinkSetName sets the name of the rdma link device. Return nil on success 137 // or error otherwise. 138 // Equivalent to: `rdma dev set $old_devname name $name` 139 func RdmaLinkSetName(link *RdmaLink, name string) error { 140 return pkgHandle.RdmaLinkSetName(link, name) 141 } 142 143 // RdmaLinkSetName sets the name of the rdma link device. Return nil on success 144 // or error otherwise. 145 // Equivalent to: `rdma dev set $old_devname name $name` 146 func (h *Handle) RdmaLinkSetName(link *RdmaLink, name string) error { 147 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SET) 148 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 149 150 b := make([]byte, 4) 151 native.PutUint32(b, uint32(link.Attrs.Index)) 152 data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, b) 153 req.AddData(data) 154 155 b = make([]byte, len(name)+1) 156 copy(b, name) 157 data = nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_NAME, b) 158 req.AddData(data) 159 160 return execRdmaSetLink(req) 161 } 162 163 func netnsModeToString(mode uint8) string { 164 switch mode { 165 case 0: 166 return "exclusive" 167 case 1: 168 return "shared" 169 default: 170 return "unknown" 171 } 172 } 173 174 func executeOneGetRdmaNetnsMode(data []byte) (string, error) { 175 reader := bytes.NewReader(data) 176 for reader.Len() >= 4 { 177 _, attrType, len, value := parseNfAttrTLV(reader) 178 179 switch attrType { 180 case nl.RDMA_NLDEV_SYS_ATTR_NETNS_MODE: 181 var mode uint8 182 r := bytes.NewReader(value) 183 binary.Read(r, nl.NativeEndian(), &mode) 184 return netnsModeToString(mode), nil 185 } 186 if (len % 4) != 0 { 187 // Skip pad bytes 188 reader.Seek(int64(4-(len%4)), seekCurrent) 189 } 190 } 191 return "", fmt.Errorf("Invalid netns mode") 192 } 193 194 // RdmaSystemGetNetnsMode gets the net namespace mode for RDMA subsystem 195 // Returns mode string and error status as nil on success or returns error 196 // otherwise. 197 // Equivalent to: `rdma system show netns' 198 func RdmaSystemGetNetnsMode() (string, error) { 199 return pkgHandle.RdmaSystemGetNetnsMode() 200 } 201 202 // RdmaSystemGetNetnsMode gets the net namespace mode for RDMA subsystem 203 // Returns mode string and error status as nil on success or returns error 204 // otherwise. 205 // Equivalent to: `rdma system show netns' 206 func (h *Handle) RdmaSystemGetNetnsMode() (string, error) { 207 208 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SYS_GET) 209 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 210 211 msgs, err := req.Execute(unix.NETLINK_RDMA, 0) 212 if err != nil { 213 return "", err 214 } 215 if len(msgs) == 0 { 216 return "", fmt.Errorf("No valid response from kernel") 217 } 218 return executeOneGetRdmaNetnsMode(msgs[0]) 219 } 220 221 func netnsModeStringToUint8(mode string) (uint8, error) { 222 switch mode { 223 case "exclusive": 224 return 0, nil 225 case "shared": 226 return 1, nil 227 default: 228 return 0, fmt.Errorf("Invalid mode; %q", mode) 229 } 230 } 231 232 // RdmaSystemSetNetnsMode sets the net namespace mode for RDMA subsystem 233 // Returns nil on success or appropriate error code. 234 // Equivalent to: `rdma system set netns { shared | exclusive }' 235 func RdmaSystemSetNetnsMode(NewMode string) error { 236 return pkgHandle.RdmaSystemSetNetnsMode(NewMode) 237 } 238 239 // RdmaSystemSetNetnsMode sets the net namespace mode for RDMA subsystem 240 // Returns nil on success or appropriate error code. 241 // Equivalent to: `rdma system set netns { shared | exclusive }' 242 func (h *Handle) RdmaSystemSetNetnsMode(NewMode string) error { 243 value, err := netnsModeStringToUint8(NewMode) 244 if err != nil { 245 return err 246 } 247 248 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SYS_SET) 249 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 250 251 data := nl.NewRtAttr(nl.RDMA_NLDEV_SYS_ATTR_NETNS_MODE, []byte{value}) 252 req.AddData(data) 253 254 _, err = req.Execute(unix.NETLINK_RDMA, 0) 255 return err 256 } 257 258 // RdmaLinkSetNsFd puts the RDMA device into a new network namespace. The 259 // fd must be an open file descriptor to a network namespace. 260 // Similar to: `rdma dev set $dev netns $ns` 261 func RdmaLinkSetNsFd(link *RdmaLink, fd uint32) error { 262 return pkgHandle.RdmaLinkSetNsFd(link, fd) 263 } 264 265 // RdmaLinkSetNsFd puts the RDMA device into a new network namespace. The 266 // fd must be an open file descriptor to a network namespace. 267 // Similar to: `rdma dev set $dev netns $ns` 268 func (h *Handle) RdmaLinkSetNsFd(link *RdmaLink, fd uint32) error { 269 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SET) 270 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 271 272 data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, 273 nl.Uint32Attr(link.Attrs.Index)) 274 req.AddData(data) 275 276 data = nl.NewRtAttr(nl.RDMA_NLDEV_NET_NS_FD, nl.Uint32Attr(fd)) 277 req.AddData(data) 278 279 return execRdmaSetLink(req) 280 } 281 282 // RdmaLinkDel deletes an rdma link 283 // 284 // Similar to: rdma link delete NAME 285 // REF: https://man7.org/linux/man-pages/man8/rdma-link.8.html 286 func RdmaLinkDel(name string) error { 287 return pkgHandle.RdmaLinkDel(name) 288 } 289 290 // RdmaLinkDel deletes an rdma link. 291 func (h *Handle) RdmaLinkDel(name string) error { 292 link, err := h.RdmaLinkByName(name) 293 if err != nil { 294 return err 295 } 296 297 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_DELLINK) 298 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 299 300 b := make([]byte, 4) 301 native.PutUint32(b, link.Attrs.Index) 302 req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, b)) 303 304 _, err = req.Execute(unix.NETLINK_RDMA, 0) 305 return err 306 } 307 308 // RdmaLinkAdd adds an rdma link for the specified type to the network device. 309 // Similar to: rdma link add NAME type TYPE netdev NETDEV 310 // 311 // NAME - specifies the new name of the rdma link to add 312 // TYPE - specifies which rdma type to use. Link types: 313 // rxe - Soft RoCE driver 314 // siw - Soft iWARP driver 315 // NETDEV - specifies the network device to which the link is bound 316 // 317 // REF: https://man7.org/linux/man-pages/man8/rdma-link.8.html 318 func RdmaLinkAdd(linkName, linkType, netdev string) error { 319 return pkgHandle.RdmaLinkAdd(linkName, linkType, netdev) 320 } 321 322 // RdmaLinkAdd adds an rdma link for the specified type to the network device. 323 func (h *Handle) RdmaLinkAdd(linkName string, linkType string, netdev string) error { 324 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_NEWLINK) 325 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 326 327 req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_NAME, nl.ZeroTerminated(linkName))) 328 req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_LINK_TYPE, nl.ZeroTerminated(linkType))) 329 req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_NDEV_NAME, nl.ZeroTerminated(netdev))) 330 _, err := req.Execute(unix.NETLINK_RDMA, 0) 331 return err 332 }