1package netlink 2 3import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "net" 8 9 "github.com/vishvananda/netlink/nl" 10 "golang.org/x/sys/unix" 11) 12 13// LinkAttrs represents data shared by most link types 14type 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. 23type RdmaLink struct { 24 Attrs RdmaLinkAttrs 25} 26 27func getProtoField(clientType int, op int) int { 28 return ((clientType << nl.RDMA_NL_GET_CLIENT_SHIFT) | op) 29} 30 31func 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 43func 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 80func execRdmaGetLink(req *nl.NetlinkRequest, name string) (*RdmaLink, error) { 81 82 msgs, err := req.Execute(unix.NETLINK_RDMA, 0) 83 if err != nil { 84 return nil, err 85 } 86 for _, m := range msgs { 87 link, err := executeOneGetRdmaLink(m) 88 if err != nil { 89 return nil, err 90 } 91 if link.Attrs.Name == name { 92 return link, nil 93 } 94 } 95 return nil, fmt.Errorf("Rdma device %v not found", name) 96} 97 98func execRdmaSetLink(req *nl.NetlinkRequest) error { 99 100 _, err := req.Execute(unix.NETLINK_RDMA, 0) 101 return err 102} 103 104// RdmaLinkByName finds a link by name and returns a pointer to the object if 105// found and nil error, otherwise returns error code. 106func RdmaLinkByName(name string) (*RdmaLink, error) { 107 return pkgHandle.RdmaLinkByName(name) 108} 109 110// RdmaLinkByName finds a link by name and returns a pointer to the object if 111// found and nil error, otherwise returns error code. 112func (h *Handle) RdmaLinkByName(name string) (*RdmaLink, error) { 113 114 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_GET) 115 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_DUMP) 116 117 return execRdmaGetLink(req, name) 118} 119 120// RdmaLinkSetName sets the name of the rdma link device. Return nil on success 121// or error otherwise. 122// Equivalent to: `rdma dev set $old_devname name $name` 123func RdmaLinkSetName(link *RdmaLink, name string) error { 124 return pkgHandle.RdmaLinkSetName(link, name) 125} 126 127// RdmaLinkSetName sets the name of the rdma link device. Return nil on success 128// or error otherwise. 129// Equivalent to: `rdma dev set $old_devname name $name` 130func (h *Handle) RdmaLinkSetName(link *RdmaLink, name string) error { 131 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SET) 132 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 133 134 b := make([]byte, 4) 135 native.PutUint32(b, uint32(link.Attrs.Index)) 136 data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, b) 137 req.AddData(data) 138 139 b = make([]byte, len(name)+1) 140 copy(b, name) 141 data = nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_NAME, b) 142 req.AddData(data) 143 144 return execRdmaSetLink(req) 145} 146 147func netnsModeToString(mode uint8) string { 148 switch mode { 149 case 0: 150 return "exclusive" 151 case 1: 152 return "shared" 153 default: 154 return "unknown" 155 } 156} 157 158func executeOneGetRdmaNetnsMode(data []byte) (string, error) { 159 reader := bytes.NewReader(data) 160 for reader.Len() >= 4 { 161 _, attrType, len, value := parseNfAttrTLV(reader) 162 163 switch attrType { 164 case nl.RDMA_NLDEV_SYS_ATTR_NETNS_MODE: 165 var mode uint8 166 r := bytes.NewReader(value) 167 binary.Read(r, nl.NativeEndian(), &mode) 168 return netnsModeToString(mode), nil 169 } 170 if (len % 4) != 0 { 171 // Skip pad bytes 172 reader.Seek(int64(4-(len%4)), seekCurrent) 173 } 174 } 175 return "", fmt.Errorf("Invalid netns mode") 176} 177 178// RdmaSystemGetNetnsMode gets the net namespace mode for RDMA subsystem 179// Returns mode string and error status as nil on success or returns error 180// otherwise. 181// Equivalent to: `rdma system show netns' 182func RdmaSystemGetNetnsMode() (string, error) { 183 return pkgHandle.RdmaSystemGetNetnsMode() 184} 185 186// RdmaSystemGetNetnsMode gets the net namespace mode for RDMA subsystem 187// Returns mode string and error status as nil on success or returns error 188// otherwise. 189// Equivalent to: `rdma system show netns' 190func (h *Handle) RdmaSystemGetNetnsMode() (string, error) { 191 192 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SYS_GET) 193 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 194 195 msgs, err := req.Execute(unix.NETLINK_RDMA, 0) 196 if err != nil { 197 return "", err 198 } 199 if len(msgs) == 0 { 200 return "", fmt.Errorf("No valid response from kernel") 201 } 202 return executeOneGetRdmaNetnsMode(msgs[0]) 203} 204 205func netnsModeStringToUint8(mode string) (uint8, error) { 206 switch mode { 207 case "exclusive": 208 return 0, nil 209 case "shared": 210 return 1, nil 211 default: 212 return 0, fmt.Errorf("Invalid mode; %q", mode) 213 } 214} 215 216// RdmaSystemSetNetnsMode sets the net namespace mode for RDMA subsystem 217// Returns nil on success or appropriate error code. 218// Equivalent to: `rdma system set netns { shared | exclusive }' 219func RdmaSystemSetNetnsMode(NewMode string) error { 220 return pkgHandle.RdmaSystemSetNetnsMode(NewMode) 221} 222 223// RdmaSystemSetNetnsMode sets the net namespace mode for RDMA subsystem 224// Returns nil on success or appropriate error code. 225// Equivalent to: `rdma system set netns { shared | exclusive }' 226func (h *Handle) RdmaSystemSetNetnsMode(NewMode string) error { 227 value, err := netnsModeStringToUint8(NewMode) 228 if err != nil { 229 return err 230 } 231 232 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SYS_SET) 233 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 234 235 data := nl.NewRtAttr(nl.RDMA_NLDEV_SYS_ATTR_NETNS_MODE, []byte{value}) 236 req.AddData(data) 237 238 _, err = req.Execute(unix.NETLINK_RDMA, 0) 239 return err 240} 241 242// RdmaLinkSetNsFd puts the RDMA device into a new network namespace. The 243// fd must be an open file descriptor to a network namespace. 244// Similar to: `rdma dev set $dev netns $ns` 245func RdmaLinkSetNsFd(link *RdmaLink, fd uint32) error { 246 return pkgHandle.RdmaLinkSetNsFd(link, fd) 247} 248 249// RdmaLinkSetNsFd puts the RDMA device into a new network namespace. The 250// fd must be an open file descriptor to a network namespace. 251// Similar to: `rdma dev set $dev netns $ns` 252func (h *Handle) RdmaLinkSetNsFd(link *RdmaLink, fd uint32) error { 253 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SET) 254 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 255 256 data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, 257 nl.Uint32Attr(link.Attrs.Index)) 258 req.AddData(data) 259 260 data = nl.NewRtAttr(nl.RDMA_NLDEV_NET_NS_FD, nl.Uint32Attr(fd)) 261 req.AddData(data) 262 263 return execRdmaSetLink(req) 264} 265