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