net/mlx5: add representor recognition on Linux 5.x
[dpdk.git] / drivers / net / mlx5 / mlx5_nl.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2018 6WIND S.A.
3  * Copyright 2018 Mellanox Technologies, Ltd
4  */
5
6 #include <errno.h>
7 #include <linux/if_link.h>
8 #include <linux/netlink.h>
9 #include <linux/rtnetlink.h>
10 #include <net/if.h>
11 #include <rdma/rdma_netlink.h>
12 #include <stdbool.h>
13 #include <stdint.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/socket.h>
17 #include <unistd.h>
18
19 #include <rte_errno.h>
20
21 #include "mlx5.h"
22 #include "mlx5_utils.h"
23
24 /* Size of the buffer to receive kernel messages */
25 #define MLX5_NL_BUF_SIZE (32 * 1024)
26 /* Send buffer size for the Netlink socket */
27 #define MLX5_SEND_BUF_SIZE 32768
28 /* Receive buffer size for the Netlink socket */
29 #define MLX5_RECV_BUF_SIZE 32768
30
31 /*
32  * Define NDA_RTA as defined in iproute2 sources.
33  *
34  * see in iproute2 sources file include/libnetlink.h
35  */
36 #ifndef MLX5_NDA_RTA
37 #define MLX5_NDA_RTA(r) \
38         ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
39 #endif
40
41 /*
42  * The following definitions are normally found in rdma/rdma_netlink.h,
43  * however they are so recent that most systems do not expose them yet.
44  */
45 #ifndef HAVE_RDMA_NL_NLDEV
46 #define RDMA_NL_NLDEV 5
47 #endif
48 #ifndef HAVE_RDMA_NLDEV_CMD_GET
49 #define RDMA_NLDEV_CMD_GET 1
50 #endif
51 #ifndef HAVE_RDMA_NLDEV_CMD_PORT_GET
52 #define RDMA_NLDEV_CMD_PORT_GET 5
53 #endif
54 #ifndef HAVE_RDMA_NLDEV_ATTR_DEV_INDEX
55 #define RDMA_NLDEV_ATTR_DEV_INDEX 1
56 #endif
57 #ifndef HAVE_RDMA_NLDEV_ATTR_DEV_NAME
58 #define RDMA_NLDEV_ATTR_DEV_NAME 2
59 #endif
60 #ifndef HAVE_RDMA_NLDEV_ATTR_PORT_INDEX
61 #define RDMA_NLDEV_ATTR_PORT_INDEX 3
62 #endif
63 #ifndef HAVE_RDMA_NLDEV_ATTR_NDEV_INDEX
64 #define RDMA_NLDEV_ATTR_NDEV_INDEX 50
65 #endif
66
67 /* These are normally found in linux/if_link.h. */
68 #ifndef HAVE_IFLA_NUM_VF
69 #define IFLA_NUM_VF 21
70 #endif
71 #ifndef HAVE_IFLA_EXT_MASK
72 #define IFLA_EXT_MASK 29
73 #endif
74 #ifndef HAVE_IFLA_PHYS_SWITCH_ID
75 #define IFLA_PHYS_SWITCH_ID 36
76 #endif
77 #ifndef HAVE_IFLA_PHYS_PORT_NAME
78 #define IFLA_PHYS_PORT_NAME 38
79 #endif
80
81 /* Add/remove MAC address through Netlink */
82 struct mlx5_nl_mac_addr {
83         struct ether_addr (*mac)[];
84         /**< MAC address handled by the device. */
85         int mac_n; /**< Number of addresses in the array. */
86 };
87
88 /** Data structure used by mlx5_nl_ifindex_cb(). */
89 struct mlx5_nl_ifindex_data {
90         const char *name; /**< IB device name (in). */
91         uint32_t ibindex; /**< IB device index (out). */
92         uint32_t ifindex; /**< Network interface index (out). */
93 };
94
95 /**
96  * Opens a Netlink socket.
97  *
98  * @param protocol
99  *   Netlink protocol (e.g. NETLINK_ROUTE, NETLINK_RDMA).
100  *
101  * @return
102  *   A file descriptor on success, a negative errno value otherwise and
103  *   rte_errno is set.
104  */
105 int
106 mlx5_nl_init(int protocol)
107 {
108         int fd;
109         int sndbuf_size = MLX5_SEND_BUF_SIZE;
110         int rcvbuf_size = MLX5_RECV_BUF_SIZE;
111         struct sockaddr_nl local = {
112                 .nl_family = AF_NETLINK,
113         };
114         int ret;
115
116         fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);
117         if (fd == -1) {
118                 rte_errno = errno;
119                 return -rte_errno;
120         }
121         ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(int));
122         if (ret == -1) {
123                 rte_errno = errno;
124                 goto error;
125         }
126         ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(int));
127         if (ret == -1) {
128                 rte_errno = errno;
129                 goto error;
130         }
131         ret = bind(fd, (struct sockaddr *)&local, sizeof(local));
132         if (ret == -1) {
133                 rte_errno = errno;
134                 goto error;
135         }
136         return fd;
137 error:
138         close(fd);
139         return -rte_errno;
140 }
141
142 /**
143  * Send a request message to the kernel on the Netlink socket.
144  *
145  * @param[in] nlsk_fd
146  *   Netlink socket file descriptor.
147  * @param[in] nh
148  *   The Netlink message send to the kernel.
149  * @param[in] ssn
150  *   Sequence number.
151  * @param[in] req
152  *   Pointer to the request structure.
153  * @param[in] len
154  *   Length of the request in bytes.
155  *
156  * @return
157  *   The number of sent bytes on success, a negative errno value otherwise and
158  *   rte_errno is set.
159  */
160 static int
161 mlx5_nl_request(int nlsk_fd, struct nlmsghdr *nh, uint32_t sn, void *req,
162                 int len)
163 {
164         struct sockaddr_nl sa = {
165                 .nl_family = AF_NETLINK,
166         };
167         struct iovec iov[2] = {
168                 { .iov_base = nh, .iov_len = sizeof(*nh), },
169                 { .iov_base = req, .iov_len = len, },
170         };
171         struct msghdr msg = {
172                 .msg_name = &sa,
173                 .msg_namelen = sizeof(sa),
174                 .msg_iov = iov,
175                 .msg_iovlen = 2,
176         };
177         int send_bytes;
178
179         nh->nlmsg_pid = 0; /* communication with the kernel uses pid 0 */
180         nh->nlmsg_seq = sn;
181         send_bytes = sendmsg(nlsk_fd, &msg, 0);
182         if (send_bytes < 0) {
183                 rte_errno = errno;
184                 return -rte_errno;
185         }
186         return send_bytes;
187 }
188
189 /**
190  * Send a message to the kernel on the Netlink socket.
191  *
192  * @param[in] nlsk_fd
193  *   The Netlink socket file descriptor used for communication.
194  * @param[in] nh
195  *   The Netlink message send to the kernel.
196  * @param[in] sn
197  *   Sequence number.
198  *
199  * @return
200  *   The number of sent bytes on success, a negative errno value otherwise and
201  *   rte_errno is set.
202  */
203 static int
204 mlx5_nl_send(int nlsk_fd, struct nlmsghdr *nh, uint32_t sn)
205 {
206         struct sockaddr_nl sa = {
207                 .nl_family = AF_NETLINK,
208         };
209         struct iovec iov = {
210                 .iov_base = nh,
211                 .iov_len = nh->nlmsg_len,
212         };
213         struct msghdr msg = {
214                 .msg_name = &sa,
215                 .msg_namelen = sizeof(sa),
216                 .msg_iov = &iov,
217                 .msg_iovlen = 1,
218         };
219         int send_bytes;
220
221         nh->nlmsg_pid = 0; /* communication with the kernel uses pid 0 */
222         nh->nlmsg_seq = sn;
223         send_bytes = sendmsg(nlsk_fd, &msg, 0);
224         if (send_bytes < 0) {
225                 rte_errno = errno;
226                 return -rte_errno;
227         }
228         return send_bytes;
229 }
230
231 /**
232  * Receive a message from the kernel on the Netlink socket, following
233  * mlx5_nl_send().
234  *
235  * @param[in] nlsk_fd
236  *   The Netlink socket file descriptor used for communication.
237  * @param[in] sn
238  *   Sequence number.
239  * @param[in] cb
240  *   The callback function to call for each Netlink message received.
241  * @param[in, out] arg
242  *   Custom arguments for the callback.
243  *
244  * @return
245  *   0 on success, a negative errno value otherwise and rte_errno is set.
246  */
247 static int
248 mlx5_nl_recv(int nlsk_fd, uint32_t sn, int (*cb)(struct nlmsghdr *, void *arg),
249              void *arg)
250 {
251         struct sockaddr_nl sa;
252         char buf[MLX5_RECV_BUF_SIZE];
253         struct iovec iov = {
254                 .iov_base = buf,
255                 .iov_len = sizeof(buf),
256         };
257         struct msghdr msg = {
258                 .msg_name = &sa,
259                 .msg_namelen = sizeof(sa),
260                 .msg_iov = &iov,
261                 /* One message at a time */
262                 .msg_iovlen = 1,
263         };
264         int multipart = 0;
265         int ret = 0;
266
267         do {
268                 struct nlmsghdr *nh;
269                 int recv_bytes = 0;
270
271                 do {
272                         recv_bytes = recvmsg(nlsk_fd, &msg, 0);
273                         if (recv_bytes == -1) {
274                                 rte_errno = errno;
275                                 return -rte_errno;
276                         }
277                         nh = (struct nlmsghdr *)buf;
278                 } while (nh->nlmsg_seq != sn);
279                 for (;
280                      NLMSG_OK(nh, (unsigned int)recv_bytes);
281                      nh = NLMSG_NEXT(nh, recv_bytes)) {
282                         if (nh->nlmsg_type == NLMSG_ERROR) {
283                                 struct nlmsgerr *err_data = NLMSG_DATA(nh);
284
285                                 if (err_data->error < 0) {
286                                         rte_errno = -err_data->error;
287                                         return -rte_errno;
288                                 }
289                                 /* Ack message. */
290                                 return 0;
291                         }
292                         /* Multi-part msgs and their trailing DONE message. */
293                         if (nh->nlmsg_flags & NLM_F_MULTI) {
294                                 if (nh->nlmsg_type == NLMSG_DONE)
295                                         return 0;
296                                 multipart = 1;
297                         }
298                         if (cb) {
299                                 ret = cb(nh, arg);
300                                 if (ret < 0)
301                                         return ret;
302                         }
303                 }
304         } while (multipart);
305         return ret;
306 }
307
308 /**
309  * Parse Netlink message to retrieve the bridge MAC address.
310  *
311  * @param nh
312  *   Pointer to Netlink Message Header.
313  * @param arg
314  *   PMD data register with this callback.
315  *
316  * @return
317  *   0 on success, a negative errno value otherwise and rte_errno is set.
318  */
319 static int
320 mlx5_nl_mac_addr_cb(struct nlmsghdr *nh, void *arg)
321 {
322         struct mlx5_nl_mac_addr *data = arg;
323         struct ndmsg *r = NLMSG_DATA(nh);
324         struct rtattr *attribute;
325         int len;
326
327         len = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
328         for (attribute = MLX5_NDA_RTA(r);
329              RTA_OK(attribute, len);
330              attribute = RTA_NEXT(attribute, len)) {
331                 if (attribute->rta_type == NDA_LLADDR) {
332                         if (data->mac_n == MLX5_MAX_MAC_ADDRESSES) {
333                                 DRV_LOG(WARNING,
334                                         "not enough room to finalize the"
335                                         " request");
336                                 rte_errno = ENOMEM;
337                                 return -rte_errno;
338                         }
339 #ifndef NDEBUG
340                         char m[18];
341
342                         ether_format_addr(m, 18, RTA_DATA(attribute));
343                         DRV_LOG(DEBUG, "bridge MAC address %s", m);
344 #endif
345                         memcpy(&(*data->mac)[data->mac_n++],
346                                RTA_DATA(attribute), ETHER_ADDR_LEN);
347                 }
348         }
349         return 0;
350 }
351
352 /**
353  * Get bridge MAC addresses.
354  *
355  * @param dev
356  *   Pointer to Ethernet device.
357  * @param mac[out]
358  *   Pointer to the array table of MAC addresses to fill.
359  *   Its size should be of MLX5_MAX_MAC_ADDRESSES.
360  * @param mac_n[out]
361  *   Number of entries filled in MAC array.
362  *
363  * @return
364  *   0 on success, a negative errno value otherwise and rte_errno is set.
365  */
366 static int
367 mlx5_nl_mac_addr_list(struct rte_eth_dev *dev, struct ether_addr (*mac)[],
368                       int *mac_n)
369 {
370         struct mlx5_priv *priv = dev->data->dev_private;
371         unsigned int iface_idx = mlx5_ifindex(dev);
372         struct {
373                 struct nlmsghdr hdr;
374                 struct ifinfomsg ifm;
375         } req = {
376                 .hdr = {
377                         .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
378                         .nlmsg_type = RTM_GETNEIGH,
379                         .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
380                 },
381                 .ifm = {
382                         .ifi_family = PF_BRIDGE,
383                         .ifi_index = iface_idx,
384                 },
385         };
386         struct mlx5_nl_mac_addr data = {
387                 .mac = mac,
388                 .mac_n = 0,
389         };
390         int fd;
391         int ret;
392         uint32_t sn = priv->nl_sn++;
393
394         if (priv->nl_socket_route == -1)
395                 return 0;
396         fd = priv->nl_socket_route;
397         ret = mlx5_nl_request(fd, &req.hdr, sn, &req.ifm,
398                               sizeof(struct ifinfomsg));
399         if (ret < 0)
400                 goto error;
401         ret = mlx5_nl_recv(fd, sn, mlx5_nl_mac_addr_cb, &data);
402         if (ret < 0)
403                 goto error;
404         *mac_n = data.mac_n;
405         return 0;
406 error:
407         DRV_LOG(DEBUG, "port %u cannot retrieve MAC address list %s",
408                 dev->data->port_id, strerror(rte_errno));
409         return -rte_errno;
410 }
411
412 /**
413  * Modify the MAC address neighbour table with Netlink.
414  *
415  * @param dev
416  *   Pointer to Ethernet device.
417  * @param mac
418  *   MAC address to consider.
419  * @param add
420  *   1 to add the MAC address, 0 to remove the MAC address.
421  *
422  * @return
423  *   0 on success, a negative errno value otherwise and rte_errno is set.
424  */
425 static int
426 mlx5_nl_mac_addr_modify(struct rte_eth_dev *dev, struct ether_addr *mac,
427                         int add)
428 {
429         struct mlx5_priv *priv = dev->data->dev_private;
430         unsigned int iface_idx = mlx5_ifindex(dev);
431         struct {
432                 struct nlmsghdr hdr;
433                 struct ndmsg ndm;
434                 struct rtattr rta;
435                 uint8_t buffer[ETHER_ADDR_LEN];
436         } req = {
437                 .hdr = {
438                         .nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
439                         .nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE |
440                                 NLM_F_EXCL | NLM_F_ACK,
441                         .nlmsg_type = add ? RTM_NEWNEIGH : RTM_DELNEIGH,
442                 },
443                 .ndm = {
444                         .ndm_family = PF_BRIDGE,
445                         .ndm_state = NUD_NOARP | NUD_PERMANENT,
446                         .ndm_ifindex = iface_idx,
447                         .ndm_flags = NTF_SELF,
448                 },
449                 .rta = {
450                         .rta_type = NDA_LLADDR,
451                         .rta_len = RTA_LENGTH(ETHER_ADDR_LEN),
452                 },
453         };
454         int fd;
455         int ret;
456         uint32_t sn = priv->nl_sn++;
457
458         if (priv->nl_socket_route == -1)
459                 return 0;
460         fd = priv->nl_socket_route;
461         memcpy(RTA_DATA(&req.rta), mac, ETHER_ADDR_LEN);
462         req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
463                 RTA_ALIGN(req.rta.rta_len);
464         ret = mlx5_nl_send(fd, &req.hdr, sn);
465         if (ret < 0)
466                 goto error;
467         ret = mlx5_nl_recv(fd, sn, NULL, NULL);
468         if (ret < 0)
469                 goto error;
470         return 0;
471 error:
472         DRV_LOG(DEBUG,
473                 "port %u cannot %s MAC address %02X:%02X:%02X:%02X:%02X:%02X"
474                 " %s",
475                 dev->data->port_id,
476                 add ? "add" : "remove",
477                 mac->addr_bytes[0], mac->addr_bytes[1],
478                 mac->addr_bytes[2], mac->addr_bytes[3],
479                 mac->addr_bytes[4], mac->addr_bytes[5],
480                 strerror(rte_errno));
481         return -rte_errno;
482 }
483
484 /**
485  * Add a MAC address.
486  *
487  * @param dev
488  *   Pointer to Ethernet device.
489  * @param mac
490  *   MAC address to register.
491  * @param index
492  *   MAC address index.
493  *
494  * @return
495  *   0 on success, a negative errno value otherwise and rte_errno is set.
496  */
497 int
498 mlx5_nl_mac_addr_add(struct rte_eth_dev *dev, struct ether_addr *mac,
499                      uint32_t index)
500 {
501         struct mlx5_priv *priv = dev->data->dev_private;
502         int ret;
503
504         ret = mlx5_nl_mac_addr_modify(dev, mac, 1);
505         if (!ret)
506                 BITFIELD_SET(priv->mac_own, index);
507         if (ret == -EEXIST)
508                 return 0;
509         return ret;
510 }
511
512 /**
513  * Remove a MAC address.
514  *
515  * @param dev
516  *   Pointer to Ethernet device.
517  * @param mac
518  *   MAC address to remove.
519  * @param index
520  *   MAC address index.
521  *
522  * @return
523  *   0 on success, a negative errno value otherwise and rte_errno is set.
524  */
525 int
526 mlx5_nl_mac_addr_remove(struct rte_eth_dev *dev, struct ether_addr *mac,
527                         uint32_t index)
528 {
529         struct mlx5_priv *priv = dev->data->dev_private;
530
531         BITFIELD_RESET(priv->mac_own, index);
532         return mlx5_nl_mac_addr_modify(dev, mac, 0);
533 }
534
535 /**
536  * Synchronize Netlink bridge table to the internal table.
537  *
538  * @param dev
539  *   Pointer to Ethernet device.
540  */
541 void
542 mlx5_nl_mac_addr_sync(struct rte_eth_dev *dev)
543 {
544         struct ether_addr macs[MLX5_MAX_MAC_ADDRESSES];
545         int macs_n = 0;
546         int i;
547         int ret;
548
549         ret = mlx5_nl_mac_addr_list(dev, &macs, &macs_n);
550         if (ret)
551                 return;
552         for (i = 0; i != macs_n; ++i) {
553                 int j;
554
555                 /* Verify the address is not in the array yet. */
556                 for (j = 0; j != MLX5_MAX_MAC_ADDRESSES; ++j)
557                         if (is_same_ether_addr(&macs[i],
558                                                &dev->data->mac_addrs[j]))
559                                 break;
560                 if (j != MLX5_MAX_MAC_ADDRESSES)
561                         continue;
562                 /* Find the first entry available. */
563                 for (j = 0; j != MLX5_MAX_MAC_ADDRESSES; ++j) {
564                         if (is_zero_ether_addr(&dev->data->mac_addrs[j])) {
565                                 dev->data->mac_addrs[j] = macs[i];
566                                 break;
567                         }
568                 }
569         }
570 }
571
572 /**
573  * Flush all added MAC addresses.
574  *
575  * @param dev
576  *   Pointer to Ethernet device.
577  */
578 void
579 mlx5_nl_mac_addr_flush(struct rte_eth_dev *dev)
580 {
581         struct mlx5_priv *priv = dev->data->dev_private;
582         int i;
583
584         for (i = MLX5_MAX_MAC_ADDRESSES - 1; i >= 0; --i) {
585                 struct ether_addr *m = &dev->data->mac_addrs[i];
586
587                 if (BITFIELD_ISSET(priv->mac_own, i))
588                         mlx5_nl_mac_addr_remove(dev, m, i);
589         }
590 }
591
592 /**
593  * Enable promiscuous / all multicast mode through Netlink.
594  *
595  * @param dev
596  *   Pointer to Ethernet device structure.
597  * @param flags
598  *   IFF_PROMISC for promiscuous, IFF_ALLMULTI for allmulti.
599  * @param enable
600  *   Nonzero to enable, disable otherwise.
601  *
602  * @return
603  *   0 on success, a negative errno value otherwise and rte_errno is set.
604  */
605 static int
606 mlx5_nl_device_flags(struct rte_eth_dev *dev, uint32_t flags, int enable)
607 {
608         struct mlx5_priv *priv = dev->data->dev_private;
609         unsigned int iface_idx = mlx5_ifindex(dev);
610         struct {
611                 struct nlmsghdr hdr;
612                 struct ifinfomsg ifi;
613         } req = {
614                 .hdr = {
615                         .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
616                         .nlmsg_type = RTM_NEWLINK,
617                         .nlmsg_flags = NLM_F_REQUEST,
618                 },
619                 .ifi = {
620                         .ifi_flags = enable ? flags : 0,
621                         .ifi_change = flags,
622                         .ifi_index = iface_idx,
623                 },
624         };
625         int fd;
626         int ret;
627
628         assert(!(flags & ~(IFF_PROMISC | IFF_ALLMULTI)));
629         if (priv->nl_socket_route < 0)
630                 return 0;
631         fd = priv->nl_socket_route;
632         ret = mlx5_nl_send(fd, &req.hdr, priv->nl_sn++);
633         if (ret < 0)
634                 return ret;
635         return 0;
636 }
637
638 /**
639  * Enable promiscuous mode through Netlink.
640  *
641  * @param dev
642  *   Pointer to Ethernet device structure.
643  * @param enable
644  *   Nonzero to enable, disable otherwise.
645  *
646  * @return
647  *   0 on success, a negative errno value otherwise and rte_errno is set.
648  */
649 int
650 mlx5_nl_promisc(struct rte_eth_dev *dev, int enable)
651 {
652         int ret = mlx5_nl_device_flags(dev, IFF_PROMISC, enable);
653
654         if (ret)
655                 DRV_LOG(DEBUG,
656                         "port %u cannot %s promisc mode: Netlink error %s",
657                         dev->data->port_id, enable ? "enable" : "disable",
658                         strerror(rte_errno));
659         return ret;
660 }
661
662 /**
663  * Enable all multicast mode through Netlink.
664  *
665  * @param dev
666  *   Pointer to Ethernet device structure.
667  * @param enable
668  *   Nonzero to enable, disable otherwise.
669  *
670  * @return
671  *   0 on success, a negative errno value otherwise and rte_errno is set.
672  */
673 int
674 mlx5_nl_allmulti(struct rte_eth_dev *dev, int enable)
675 {
676         int ret = mlx5_nl_device_flags(dev, IFF_ALLMULTI, enable);
677
678         if (ret)
679                 DRV_LOG(DEBUG,
680                         "port %u cannot %s allmulti mode: Netlink error %s",
681                         dev->data->port_id, enable ? "enable" : "disable",
682                         strerror(rte_errno));
683         return ret;
684 }
685
686 /**
687  * Process network interface information from Netlink message.
688  *
689  * @param nh
690  *   Pointer to Netlink message header.
691  * @param arg
692  *   Opaque data pointer for this callback.
693  *
694  * @return
695  *   0 on success, a negative errno value otherwise and rte_errno is set.
696  */
697 static int
698 mlx5_nl_ifindex_cb(struct nlmsghdr *nh, void *arg)
699 {
700         struct mlx5_nl_ifindex_data *data = arg;
701         size_t off = NLMSG_HDRLEN;
702         uint32_t ibindex = 0;
703         uint32_t ifindex = 0;
704         int found = 0;
705
706         if (nh->nlmsg_type !=
707             RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET) &&
708             nh->nlmsg_type !=
709             RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_PORT_GET))
710                 goto error;
711         while (off < nh->nlmsg_len) {
712                 struct nlattr *na = (void *)((uintptr_t)nh + off);
713                 void *payload = (void *)((uintptr_t)na + NLA_HDRLEN);
714
715                 if (na->nla_len > nh->nlmsg_len - off)
716                         goto error;
717                 switch (na->nla_type) {
718                 case RDMA_NLDEV_ATTR_DEV_INDEX:
719                         ibindex = *(uint32_t *)payload;
720                         break;
721                 case RDMA_NLDEV_ATTR_DEV_NAME:
722                         if (!strcmp(payload, data->name))
723                                 found = 1;
724                         break;
725                 case RDMA_NLDEV_ATTR_NDEV_INDEX:
726                         ifindex = *(uint32_t *)payload;
727                         break;
728                 default:
729                         break;
730                 }
731                 off += NLA_ALIGN(na->nla_len);
732         }
733         if (found) {
734                 data->ibindex = ibindex;
735                 data->ifindex = ifindex;
736         }
737         return 0;
738 error:
739         rte_errno = EINVAL;
740         return -rte_errno;
741 }
742
743 /**
744  * Get index of network interface associated with some IB device.
745  *
746  * This is the only somewhat safe method to avoid resorting to heuristics
747  * when faced with port representors. Unfortunately it requires at least
748  * Linux 4.17.
749  *
750  * @param nl
751  *   Netlink socket of the RDMA kind (NETLINK_RDMA).
752  * @param[in] name
753  *   IB device name.
754  *
755  * @return
756  *   A valid (nonzero) interface index on success, 0 otherwise and rte_errno
757  *   is set.
758  */
759 unsigned int
760 mlx5_nl_ifindex(int nl, const char *name)
761 {
762         static const uint32_t pindex = 1;
763         uint32_t seq = random();
764         struct mlx5_nl_ifindex_data data = {
765                 .name = name,
766                 .ibindex = 0, /* Determined during first pass. */
767                 .ifindex = 0, /* Determined during second pass. */
768         };
769         union {
770                 struct nlmsghdr nh;
771                 uint8_t buf[NLMSG_HDRLEN +
772                             NLA_HDRLEN + NLA_ALIGN(sizeof(data.ibindex)) +
773                             NLA_HDRLEN + NLA_ALIGN(sizeof(pindex))];
774         } req = {
775                 .nh = {
776                         .nlmsg_len = NLMSG_LENGTH(0),
777                         .nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
778                                                        RDMA_NLDEV_CMD_GET),
779                         .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
780                 },
781         };
782         struct nlattr *na;
783         int ret;
784
785         ret = mlx5_nl_send(nl, &req.nh, seq);
786         if (ret < 0)
787                 return 0;
788         ret = mlx5_nl_recv(nl, seq, mlx5_nl_ifindex_cb, &data);
789         if (ret < 0)
790                 return 0;
791         if (!data.ibindex)
792                 goto error;
793         ++seq;
794         req.nh.nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
795                                              RDMA_NLDEV_CMD_PORT_GET);
796         req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
797         req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.buf) - NLMSG_HDRLEN);
798         na = (void *)((uintptr_t)req.buf + NLMSG_HDRLEN);
799         na->nla_len = NLA_HDRLEN + sizeof(data.ibindex);
800         na->nla_type = RDMA_NLDEV_ATTR_DEV_INDEX;
801         memcpy((void *)((uintptr_t)na + NLA_HDRLEN),
802                &data.ibindex, sizeof(data.ibindex));
803         na = (void *)((uintptr_t)na + NLA_ALIGN(na->nla_len));
804         na->nla_len = NLA_HDRLEN + sizeof(pindex);
805         na->nla_type = RDMA_NLDEV_ATTR_PORT_INDEX;
806         memcpy((void *)((uintptr_t)na + NLA_HDRLEN),
807                &pindex, sizeof(pindex));
808         ret = mlx5_nl_send(nl, &req.nh, seq);
809         if (ret < 0)
810                 return 0;
811         ret = mlx5_nl_recv(nl, seq, mlx5_nl_ifindex_cb, &data);
812         if (ret < 0)
813                 return 0;
814         if (!data.ifindex)
815                 goto error;
816         return data.ifindex;
817 error:
818         rte_errno = ENODEV;
819         return 0;
820 }
821
822 /**
823  * Process switch information from Netlink message.
824  *
825  * @param nh
826  *   Pointer to Netlink message header.
827  * @param arg
828  *   Opaque data pointer for this callback.
829  *
830  * @return
831  *   0 on success, a negative errno value otherwise and rte_errno is set.
832  */
833 static int
834 mlx5_nl_switch_info_cb(struct nlmsghdr *nh, void *arg)
835 {
836         struct mlx5_switch_info info = {
837                 .master = 0,
838                 .representor = 0,
839                 .port_name_new = 0,
840                 .port_name = 0,
841                 .switch_id = 0,
842         };
843         size_t off = NLMSG_LENGTH(sizeof(struct ifinfomsg));
844         bool port_name_set = false;
845         bool switch_id_set = false;
846         bool num_vf_set = false;
847
848         if (nh->nlmsg_type != RTM_NEWLINK)
849                 goto error;
850         while (off < nh->nlmsg_len) {
851                 struct rtattr *ra = (void *)((uintptr_t)nh + off);
852                 void *payload = RTA_DATA(ra);
853                 unsigned int i;
854
855                 if (ra->rta_len > nh->nlmsg_len - off)
856                         goto error;
857                 switch (ra->rta_type) {
858                 case IFLA_NUM_VF:
859                         num_vf_set = true;
860                         break;
861                 case IFLA_PHYS_PORT_NAME:
862                         port_name_set =
863                                 mlx5_translate_port_name((char *)payload,
864                                                          &info);
865                         break;
866                 case IFLA_PHYS_SWITCH_ID:
867                         info.switch_id = 0;
868                         for (i = 0; i < RTA_PAYLOAD(ra); ++i) {
869                                 info.switch_id <<= 8;
870                                 info.switch_id |= ((uint8_t *)payload)[i];
871                         }
872                         switch_id_set = true;
873                         break;
874                 }
875                 off += RTA_ALIGN(ra->rta_len);
876         }
877         if (switch_id_set) {
878                 if (info.port_name_new) {
879                         /* New representors naming schema. */
880                         if (port_name_set) {
881                                 info.master = (info.port_name == -1);
882                                 info.representor = (info.port_name != -1);
883                         }
884                 } else {
885                         /* Legacy representors naming schema. */
886                         info.master = (!port_name_set || num_vf_set);
887                         info.representor = port_name_set && !num_vf_set;
888                 }
889         }
890         assert(!(info.master && info.representor));
891         memcpy(arg, &info, sizeof(info));
892         return 0;
893 error:
894         rte_errno = EINVAL;
895         return -rte_errno;
896 }
897
898 /**
899  * Get switch information associated with network interface.
900  *
901  * @param nl
902  *   Netlink socket of the ROUTE kind (NETLINK_ROUTE).
903  * @param ifindex
904  *   Network interface index.
905  * @param[out] info
906  *   Switch information object, populated in case of success.
907  *
908  * @return
909  *   0 on success, a negative errno value otherwise and rte_errno is set.
910  */
911 int
912 mlx5_nl_switch_info(int nl, unsigned int ifindex, struct mlx5_switch_info *info)
913 {
914         uint32_t seq = random();
915         struct {
916                 struct nlmsghdr nh;
917                 struct ifinfomsg info;
918                 struct rtattr rta;
919                 uint32_t extmask;
920         } req = {
921                 .nh = {
922                         .nlmsg_len = NLMSG_LENGTH
923                                         (sizeof(req.info) +
924                                          RTA_LENGTH(sizeof(uint32_t))),
925                         .nlmsg_type = RTM_GETLINK,
926                         .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
927                 },
928                 .info = {
929                         .ifi_family = AF_UNSPEC,
930                         .ifi_index = ifindex,
931                 },
932                 .rta = {
933                         .rta_type = IFLA_EXT_MASK,
934                         .rta_len = RTA_LENGTH(sizeof(int32_t)),
935                 },
936                 .extmask = RTE_LE32(1),
937         };
938         int ret;
939
940         ret = mlx5_nl_send(nl, &req.nh, seq);
941         if (ret >= 0)
942                 ret = mlx5_nl_recv(nl, seq, mlx5_nl_switch_info_cb, info);
943         if (info->master && info->representor) {
944                 DRV_LOG(ERR, "ifindex %u device is recognized as master"
945                              " and as representor", ifindex);
946                 rte_errno = ENODEV;
947                 ret = -rte_errno;
948         }
949         return ret;
950 }