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