net/mlx5: reduce Netlink commands dependencies
[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/rtnetlink.h>
9 #include <net/if.h>
10 #include <rdma/rdma_netlink.h>
11 #include <stdbool.h>
12 #include <stdint.h>
13 #include <stdlib.h>
14 #include <stdalign.h>
15 #include <string.h>
16 #include <sys/socket.h>
17 #include <unistd.h>
18
19 #include <rte_errno.h>
20 #include <rte_atomic.h>
21 #include <rte_ether.h>
22
23 #include "mlx5.h"
24 #include "mlx5_nl.h"
25 #include "mlx5_utils.h"
26
27 /* Size of the buffer to receive kernel messages */
28 #define MLX5_NL_BUF_SIZE (32 * 1024)
29 /* Send buffer size for the Netlink socket */
30 #define MLX5_SEND_BUF_SIZE 32768
31 /* Receive buffer size for the Netlink socket */
32 #define MLX5_RECV_BUF_SIZE 32768
33
34 /** Parameters of VLAN devices created by driver. */
35 #define MLX5_VMWA_VLAN_DEVICE_PFX "evmlx"
36 /*
37  * Define NDA_RTA as defined in iproute2 sources.
38  *
39  * see in iproute2 sources file include/libnetlink.h
40  */
41 #ifndef MLX5_NDA_RTA
42 #define MLX5_NDA_RTA(r) \
43         ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
44 #endif
45 /*
46  * Define NLMSG_TAIL as defined in iproute2 sources.
47  *
48  * see in iproute2 sources file include/libnetlink.h
49  */
50 #ifndef NLMSG_TAIL
51 #define NLMSG_TAIL(nmsg) \
52         ((struct rtattr *)(((char *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
53 #endif
54 /*
55  * The following definitions are normally found in rdma/rdma_netlink.h,
56  * however they are so recent that most systems do not expose them yet.
57  */
58 #ifndef HAVE_RDMA_NL_NLDEV
59 #define RDMA_NL_NLDEV 5
60 #endif
61 #ifndef HAVE_RDMA_NLDEV_CMD_GET
62 #define RDMA_NLDEV_CMD_GET 1
63 #endif
64 #ifndef HAVE_RDMA_NLDEV_CMD_PORT_GET
65 #define RDMA_NLDEV_CMD_PORT_GET 5
66 #endif
67 #ifndef HAVE_RDMA_NLDEV_ATTR_DEV_INDEX
68 #define RDMA_NLDEV_ATTR_DEV_INDEX 1
69 #endif
70 #ifndef HAVE_RDMA_NLDEV_ATTR_DEV_NAME
71 #define RDMA_NLDEV_ATTR_DEV_NAME 2
72 #endif
73 #ifndef HAVE_RDMA_NLDEV_ATTR_PORT_INDEX
74 #define RDMA_NLDEV_ATTR_PORT_INDEX 3
75 #endif
76 #ifndef HAVE_RDMA_NLDEV_ATTR_NDEV_INDEX
77 #define RDMA_NLDEV_ATTR_NDEV_INDEX 50
78 #endif
79
80 /* These are normally found in linux/if_link.h. */
81 #ifndef HAVE_IFLA_NUM_VF
82 #define IFLA_NUM_VF 21
83 #endif
84 #ifndef HAVE_IFLA_EXT_MASK
85 #define IFLA_EXT_MASK 29
86 #endif
87 #ifndef HAVE_IFLA_PHYS_SWITCH_ID
88 #define IFLA_PHYS_SWITCH_ID 36
89 #endif
90 #ifndef HAVE_IFLA_PHYS_PORT_NAME
91 #define IFLA_PHYS_PORT_NAME 38
92 #endif
93
94 /* Add/remove MAC address through Netlink */
95 struct mlx5_nl_mac_addr {
96         struct rte_ether_addr (*mac)[];
97         /**< MAC address handled by the device. */
98         int mac_n; /**< Number of addresses in the array. */
99 };
100
101 #define MLX5_NL_CMD_GET_IB_NAME (1 << 0)
102 #define MLX5_NL_CMD_GET_IB_INDEX (1 << 1)
103 #define MLX5_NL_CMD_GET_NET_INDEX (1 << 2)
104 #define MLX5_NL_CMD_GET_PORT_INDEX (1 << 3)
105
106 /** Data structure used by mlx5_nl_cmdget_cb(). */
107 struct mlx5_nl_ifindex_data {
108         const char *name; /**< IB device name (in). */
109         uint32_t flags; /**< found attribute flags (out). */
110         uint32_t ibindex; /**< IB device index (out). */
111         uint32_t ifindex; /**< Network interface index (out). */
112         uint32_t portnum; /**< IB device max port number (out). */
113 };
114
115 rte_atomic32_t atomic_sn = RTE_ATOMIC32_INIT(0);
116
117 /* Generate Netlink sequence number. */
118 #define MLX5_NL_SN_GENERATE ((uint32_t)rte_atomic32_add_return(&atomic_sn, 1))
119
120 /**
121  * Opens a Netlink socket.
122  *
123  * @param protocol
124  *   Netlink protocol (e.g. NETLINK_ROUTE, NETLINK_RDMA).
125  *
126  * @return
127  *   A file descriptor on success, a negative errno value otherwise and
128  *   rte_errno is set.
129  */
130 int
131 mlx5_nl_init(int protocol)
132 {
133         int fd;
134         int sndbuf_size = MLX5_SEND_BUF_SIZE;
135         int rcvbuf_size = MLX5_RECV_BUF_SIZE;
136         struct sockaddr_nl local = {
137                 .nl_family = AF_NETLINK,
138         };
139         int ret;
140
141         fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);
142         if (fd == -1) {
143                 rte_errno = errno;
144                 return -rte_errno;
145         }
146         ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(int));
147         if (ret == -1) {
148                 rte_errno = errno;
149                 goto error;
150         }
151         ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(int));
152         if (ret == -1) {
153                 rte_errno = errno;
154                 goto error;
155         }
156         ret = bind(fd, (struct sockaddr *)&local, sizeof(local));
157         if (ret == -1) {
158                 rte_errno = errno;
159                 goto error;
160         }
161         return fd;
162 error:
163         close(fd);
164         return -rte_errno;
165 }
166
167 /**
168  * Send a request message to the kernel on the Netlink socket.
169  *
170  * @param[in] nlsk_fd
171  *   Netlink socket file descriptor.
172  * @param[in] nh
173  *   The Netlink message send to the kernel.
174  * @param[in] ssn
175  *   Sequence number.
176  * @param[in] req
177  *   Pointer to the request structure.
178  * @param[in] len
179  *   Length of the request in bytes.
180  *
181  * @return
182  *   The number of sent bytes on success, a negative errno value otherwise and
183  *   rte_errno is set.
184  */
185 static int
186 mlx5_nl_request(int nlsk_fd, struct nlmsghdr *nh, uint32_t sn, void *req,
187                 int len)
188 {
189         struct sockaddr_nl sa = {
190                 .nl_family = AF_NETLINK,
191         };
192         struct iovec iov[2] = {
193                 { .iov_base = nh, .iov_len = sizeof(*nh), },
194                 { .iov_base = req, .iov_len = len, },
195         };
196         struct msghdr msg = {
197                 .msg_name = &sa,
198                 .msg_namelen = sizeof(sa),
199                 .msg_iov = iov,
200                 .msg_iovlen = 2,
201         };
202         int send_bytes;
203
204         nh->nlmsg_pid = 0; /* communication with the kernel uses pid 0 */
205         nh->nlmsg_seq = sn;
206         send_bytes = sendmsg(nlsk_fd, &msg, 0);
207         if (send_bytes < 0) {
208                 rte_errno = errno;
209                 return -rte_errno;
210         }
211         return send_bytes;
212 }
213
214 /**
215  * Send a message to the kernel on the Netlink socket.
216  *
217  * @param[in] nlsk_fd
218  *   The Netlink socket file descriptor used for communication.
219  * @param[in] nh
220  *   The Netlink message send to the kernel.
221  * @param[in] sn
222  *   Sequence number.
223  *
224  * @return
225  *   The number of sent bytes on success, a negative errno value otherwise and
226  *   rte_errno is set.
227  */
228 static int
229 mlx5_nl_send(int nlsk_fd, struct nlmsghdr *nh, uint32_t sn)
230 {
231         struct sockaddr_nl sa = {
232                 .nl_family = AF_NETLINK,
233         };
234         struct iovec iov = {
235                 .iov_base = nh,
236                 .iov_len = nh->nlmsg_len,
237         };
238         struct msghdr msg = {
239                 .msg_name = &sa,
240                 .msg_namelen = sizeof(sa),
241                 .msg_iov = &iov,
242                 .msg_iovlen = 1,
243         };
244         int send_bytes;
245
246         nh->nlmsg_pid = 0; /* communication with the kernel uses pid 0 */
247         nh->nlmsg_seq = sn;
248         send_bytes = sendmsg(nlsk_fd, &msg, 0);
249         if (send_bytes < 0) {
250                 rte_errno = errno;
251                 return -rte_errno;
252         }
253         return send_bytes;
254 }
255
256 /**
257  * Receive a message from the kernel on the Netlink socket, following
258  * mlx5_nl_send().
259  *
260  * @param[in] nlsk_fd
261  *   The Netlink socket file descriptor used for communication.
262  * @param[in] sn
263  *   Sequence number.
264  * @param[in] cb
265  *   The callback function to call for each Netlink message received.
266  * @param[in, out] arg
267  *   Custom arguments for the callback.
268  *
269  * @return
270  *   0 on success, a negative errno value otherwise and rte_errno is set.
271  */
272 static int
273 mlx5_nl_recv(int nlsk_fd, uint32_t sn, int (*cb)(struct nlmsghdr *, void *arg),
274              void *arg)
275 {
276         struct sockaddr_nl sa;
277         char buf[MLX5_RECV_BUF_SIZE];
278         struct iovec iov = {
279                 .iov_base = buf,
280                 .iov_len = sizeof(buf),
281         };
282         struct msghdr msg = {
283                 .msg_name = &sa,
284                 .msg_namelen = sizeof(sa),
285                 .msg_iov = &iov,
286                 /* One message at a time */
287                 .msg_iovlen = 1,
288         };
289         int multipart = 0;
290         int ret = 0;
291
292         do {
293                 struct nlmsghdr *nh;
294                 int recv_bytes = 0;
295
296                 do {
297                         recv_bytes = recvmsg(nlsk_fd, &msg, 0);
298                         if (recv_bytes == -1) {
299                                 rte_errno = errno;
300                                 return -rte_errno;
301                         }
302                         nh = (struct nlmsghdr *)buf;
303                 } while (nh->nlmsg_seq != sn);
304                 for (;
305                      NLMSG_OK(nh, (unsigned int)recv_bytes);
306                      nh = NLMSG_NEXT(nh, recv_bytes)) {
307                         if (nh->nlmsg_type == NLMSG_ERROR) {
308                                 struct nlmsgerr *err_data = NLMSG_DATA(nh);
309
310                                 if (err_data->error < 0) {
311                                         rte_errno = -err_data->error;
312                                         return -rte_errno;
313                                 }
314                                 /* Ack message. */
315                                 return 0;
316                         }
317                         /* Multi-part msgs and their trailing DONE message. */
318                         if (nh->nlmsg_flags & NLM_F_MULTI) {
319                                 if (nh->nlmsg_type == NLMSG_DONE)
320                                         return 0;
321                                 multipart = 1;
322                         }
323                         if (cb) {
324                                 ret = cb(nh, arg);
325                                 if (ret < 0)
326                                         return ret;
327                         }
328                 }
329         } while (multipart);
330         return ret;
331 }
332
333 /**
334  * Parse Netlink message to retrieve the bridge MAC address.
335  *
336  * @param nh
337  *   Pointer to Netlink Message Header.
338  * @param arg
339  *   PMD data register with this callback.
340  *
341  * @return
342  *   0 on success, a negative errno value otherwise and rte_errno is set.
343  */
344 static int
345 mlx5_nl_mac_addr_cb(struct nlmsghdr *nh, void *arg)
346 {
347         struct mlx5_nl_mac_addr *data = arg;
348         struct ndmsg *r = NLMSG_DATA(nh);
349         struct rtattr *attribute;
350         int len;
351
352         len = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
353         for (attribute = MLX5_NDA_RTA(r);
354              RTA_OK(attribute, len);
355              attribute = RTA_NEXT(attribute, len)) {
356                 if (attribute->rta_type == NDA_LLADDR) {
357                         if (data->mac_n == MLX5_MAX_MAC_ADDRESSES) {
358                                 DRV_LOG(WARNING,
359                                         "not enough room to finalize the"
360                                         " request");
361                                 rte_errno = ENOMEM;
362                                 return -rte_errno;
363                         }
364 #ifndef NDEBUG
365                         char m[18];
366
367                         rte_ether_format_addr(m, 18, RTA_DATA(attribute));
368                         DRV_LOG(DEBUG, "bridge MAC address %s", m);
369 #endif
370                         memcpy(&(*data->mac)[data->mac_n++],
371                                RTA_DATA(attribute), RTE_ETHER_ADDR_LEN);
372                 }
373         }
374         return 0;
375 }
376
377 /**
378  * Get bridge MAC addresses.
379  *
380  * @param[in] nlsk_fd
381  *   Netlink socket file descriptor.
382  * @param[in] iface_idx
383  *   Net device interface index.
384  * @param mac[out]
385  *   Pointer to the array table of MAC addresses to fill.
386  *   Its size should be of MLX5_MAX_MAC_ADDRESSES.
387  * @param mac_n[out]
388  *   Number of entries filled in MAC array.
389  *
390  * @return
391  *   0 on success, a negative errno value otherwise and rte_errno is set.
392  */
393 static int
394 mlx5_nl_mac_addr_list(int nlsk_fd, unsigned int iface_idx,
395                       struct rte_ether_addr (*mac)[], int *mac_n)
396 {
397         struct {
398                 struct nlmsghdr hdr;
399                 struct ifinfomsg ifm;
400         } req = {
401                 .hdr = {
402                         .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
403                         .nlmsg_type = RTM_GETNEIGH,
404                         .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
405                 },
406                 .ifm = {
407                         .ifi_family = PF_BRIDGE,
408                         .ifi_index = iface_idx,
409                 },
410         };
411         struct mlx5_nl_mac_addr data = {
412                 .mac = mac,
413                 .mac_n = 0,
414         };
415         uint32_t sn = MLX5_NL_SN_GENERATE;
416         int ret;
417
418         if (nlsk_fd == -1)
419                 return 0;
420         ret = mlx5_nl_request(nlsk_fd, &req.hdr, sn, &req.ifm,
421                               sizeof(struct ifinfomsg));
422         if (ret < 0)
423                 goto error;
424         ret = mlx5_nl_recv(nlsk_fd, sn, mlx5_nl_mac_addr_cb, &data);
425         if (ret < 0)
426                 goto error;
427         *mac_n = data.mac_n;
428         return 0;
429 error:
430         DRV_LOG(DEBUG, "Interface %u cannot retrieve MAC address list %s",
431                 iface_idx, strerror(rte_errno));
432         return -rte_errno;
433 }
434
435 /**
436  * Modify the MAC address neighbour table with Netlink.
437  *
438  * @param[in] nlsk_fd
439  *   Netlink socket file descriptor.
440  * @param[in] iface_idx
441  *   Net device interface index.
442  * @param mac
443  *   MAC address to consider.
444  * @param add
445  *   1 to add the MAC address, 0 to remove the MAC address.
446  *
447  * @return
448  *   0 on success, a negative errno value otherwise and rte_errno is set.
449  */
450 static int
451 mlx5_nl_mac_addr_modify(int nlsk_fd, unsigned int iface_idx,
452                         struct rte_ether_addr *mac, int add)
453 {
454         struct {
455                 struct nlmsghdr hdr;
456                 struct ndmsg ndm;
457                 struct rtattr rta;
458                 uint8_t buffer[RTE_ETHER_ADDR_LEN];
459         } req = {
460                 .hdr = {
461                         .nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
462                         .nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE |
463                                 NLM_F_EXCL | NLM_F_ACK,
464                         .nlmsg_type = add ? RTM_NEWNEIGH : RTM_DELNEIGH,
465                 },
466                 .ndm = {
467                         .ndm_family = PF_BRIDGE,
468                         .ndm_state = NUD_NOARP | NUD_PERMANENT,
469                         .ndm_ifindex = iface_idx,
470                         .ndm_flags = NTF_SELF,
471                 },
472                 .rta = {
473                         .rta_type = NDA_LLADDR,
474                         .rta_len = RTA_LENGTH(RTE_ETHER_ADDR_LEN),
475                 },
476         };
477         uint32_t sn = MLX5_NL_SN_GENERATE;
478         int ret;
479
480         if (nlsk_fd == -1)
481                 return 0;
482         memcpy(RTA_DATA(&req.rta), mac, RTE_ETHER_ADDR_LEN);
483         req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
484                 RTA_ALIGN(req.rta.rta_len);
485         ret = mlx5_nl_send(nlsk_fd, &req.hdr, sn);
486         if (ret < 0)
487                 goto error;
488         ret = mlx5_nl_recv(nlsk_fd, sn, NULL, NULL);
489         if (ret < 0)
490                 goto error;
491         return 0;
492 error:
493         DRV_LOG(DEBUG,
494                 "Interface %u cannot %s MAC address"
495                 " %02X:%02X:%02X:%02X:%02X:%02X %s",
496                 iface_idx,
497                 add ? "add" : "remove",
498                 mac->addr_bytes[0], mac->addr_bytes[1],
499                 mac->addr_bytes[2], mac->addr_bytes[3],
500                 mac->addr_bytes[4], mac->addr_bytes[5],
501                 strerror(rte_errno));
502         return -rte_errno;
503 }
504
505 /**
506  * Modify the VF MAC address neighbour table with Netlink.
507  *
508  * @param[in] nlsk_fd
509  *   Netlink socket file descriptor.
510  * @param[in] iface_idx
511  *   Net device interface index.
512  * @param mac
513  *    MAC address to consider.
514  * @param vf_index
515  *    VF index.
516  *
517  * @return
518  *    0 on success, a negative errno value otherwise and rte_errno is set.
519  */
520 int
521 mlx5_nl_vf_mac_addr_modify(int nlsk_fd, unsigned int iface_idx,
522                            struct rte_ether_addr *mac, int vf_index)
523 {
524         int ret;
525         struct {
526                 struct nlmsghdr hdr;
527                 struct ifinfomsg ifm;
528                 struct rtattr vf_list_rta;
529                 struct rtattr vf_info_rta;
530                 struct rtattr vf_mac_rta;
531                 struct ifla_vf_mac ivm;
532         } req = {
533                 .hdr = {
534                         .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
535                         .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
536                         .nlmsg_type = RTM_BASE,
537                 },
538                 .ifm = {
539                         .ifi_index = iface_idx,
540                 },
541                 .vf_list_rta = {
542                         .rta_type = IFLA_VFINFO_LIST,
543                         .rta_len = RTA_ALIGN(RTA_LENGTH(0)),
544                 },
545                 .vf_info_rta = {
546                         .rta_type = IFLA_VF_INFO,
547                         .rta_len = RTA_ALIGN(RTA_LENGTH(0)),
548                 },
549                 .vf_mac_rta = {
550                         .rta_type = IFLA_VF_MAC,
551                 },
552         };
553         struct ifla_vf_mac ivm = {
554                 .vf = vf_index,
555         };
556         uint32_t sn = MLX5_NL_SN_GENERATE;
557
558         memcpy(&ivm.mac, mac, RTE_ETHER_ADDR_LEN);
559         memcpy(RTA_DATA(&req.vf_mac_rta), &ivm, sizeof(ivm));
560
561         req.vf_mac_rta.rta_len = RTA_LENGTH(sizeof(ivm));
562         req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
563                 RTA_ALIGN(req.vf_list_rta.rta_len) +
564                 RTA_ALIGN(req.vf_info_rta.rta_len) +
565                 RTA_ALIGN(req.vf_mac_rta.rta_len);
566         req.vf_list_rta.rta_len = RTE_PTR_DIFF(NLMSG_TAIL(&req.hdr),
567                                                &req.vf_list_rta);
568         req.vf_info_rta.rta_len = RTE_PTR_DIFF(NLMSG_TAIL(&req.hdr),
569                                                &req.vf_info_rta);
570
571         if (nlsk_fd < 0)
572                 return -1;
573         ret = mlx5_nl_send(nlsk_fd, &req.hdr, sn);
574         if (ret < 0)
575                 goto error;
576         ret = mlx5_nl_recv(nlsk_fd, sn, NULL, NULL);
577         if (ret < 0)
578                 goto error;
579         return 0;
580 error:
581         DRV_LOG(ERR,
582                 "representor %u cannot set VF MAC address "
583                 "%02X:%02X:%02X:%02X:%02X:%02X : %s",
584                 vf_index,
585                 mac->addr_bytes[0], mac->addr_bytes[1],
586                 mac->addr_bytes[2], mac->addr_bytes[3],
587                 mac->addr_bytes[4], mac->addr_bytes[5],
588                 strerror(rte_errno));
589         return -rte_errno;
590 }
591
592 /**
593  * Add a MAC address.
594  *
595  * @param[in] nlsk_fd
596  *   Netlink socket file descriptor.
597  * @param[in] iface_idx
598  *   Net device interface index.
599  * @param mac_own
600  *   BITFIELD_DECLARE array to store the mac.
601  * @param mac
602  *   MAC address to register.
603  * @param index
604  *   MAC address index.
605  *
606  * @return
607  *   0 on success, a negative errno value otherwise and rte_errno is set.
608  */
609 int
610 mlx5_nl_mac_addr_add(int nlsk_fd, unsigned int iface_idx,
611                      uint64_t *mac_own, struct rte_ether_addr *mac,
612                      uint32_t index)
613 {
614         int ret;
615
616         ret = mlx5_nl_mac_addr_modify(nlsk_fd, iface_idx, mac, 1);
617         if (!ret)
618                 BITFIELD_SET(mac_own, index);
619         if (ret == -EEXIST)
620                 return 0;
621         return ret;
622 }
623
624 /**
625  * Remove a MAC address.
626  *
627  * @param[in] nlsk_fd
628  *   Netlink socket file descriptor.
629  * @param[in] iface_idx
630  *   Net device interface index.
631  * @param mac_own
632  *   BITFIELD_DECLARE array to store the mac.
633  * @param mac
634  *   MAC address to remove.
635  * @param index
636  *   MAC address index.
637  *
638  * @return
639  *   0 on success, a negative errno value otherwise and rte_errno is set.
640  */
641 int
642 mlx5_nl_mac_addr_remove(int nlsk_fd, unsigned int iface_idx, uint64_t *mac_own,
643                         struct rte_ether_addr *mac, uint32_t index)
644 {
645         BITFIELD_RESET(mac_own, index);
646         return mlx5_nl_mac_addr_modify(nlsk_fd, iface_idx, mac, 0);
647 }
648
649 /**
650  * Synchronize Netlink bridge table to the internal table.
651  *
652  * @param[in] nlsk_fd
653  *   Netlink socket file descriptor.
654  * @param[in] iface_idx
655  *   Net device interface index.
656  * @param mac_addrs
657  *   Mac addresses array to sync.
658  * @param n
659  *   @p mac_addrs array size.
660  */
661 void
662 mlx5_nl_mac_addr_sync(int nlsk_fd, unsigned int iface_idx,
663                       struct rte_ether_addr *mac_addrs, int n)
664 {
665         struct rte_ether_addr macs[n];
666         int macs_n = 0;
667         int i;
668         int ret;
669
670         ret = mlx5_nl_mac_addr_list(nlsk_fd, iface_idx, &macs, &macs_n);
671         if (ret)
672                 return;
673         for (i = 0; i != macs_n; ++i) {
674                 int j;
675
676                 /* Verify the address is not in the array yet. */
677                 for (j = 0; j != n; ++j)
678                         if (rte_is_same_ether_addr(&macs[i], &mac_addrs[j]))
679                                 break;
680                 if (j != n)
681                         continue;
682                 /* Find the first entry available. */
683                 for (j = 0; j != n; ++j) {
684                         if (rte_is_zero_ether_addr(&mac_addrs[j])) {
685                                 mac_addrs[j] = macs[i];
686                                 break;
687                         }
688                 }
689         }
690 }
691
692 /**
693  * Flush all added MAC addresses.
694  *
695  * @param[in] nlsk_fd
696  *   Netlink socket file descriptor.
697  * @param[in] iface_idx
698  *   Net device interface index.
699  * @param[in] mac_addrs
700  *   Mac addresses array to flush.
701  * @param n
702  *   @p mac_addrs array size.
703  * @param mac_own
704  *   BITFIELD_DECLARE array to store the mac.
705  */
706 void
707 mlx5_nl_mac_addr_flush(int nlsk_fd, unsigned int iface_idx,
708                        struct rte_ether_addr *mac_addrs, int n,
709                        uint64_t *mac_own)
710 {
711         int i;
712
713         for (i = n - 1; i >= 0; --i) {
714                 struct rte_ether_addr *m = &mac_addrs[i];
715
716                 if (BITFIELD_ISSET(mac_own, i))
717                         mlx5_nl_mac_addr_remove(nlsk_fd, iface_idx, mac_own, m,
718                                                 i);
719         }
720 }
721
722 /**
723  * Enable promiscuous / all multicast mode through Netlink.
724  *
725  * @param[in] nlsk_fd
726  *   Netlink socket file descriptor.
727  * @param[in] iface_idx
728  *   Net device interface index.
729  * @param flags
730  *   IFF_PROMISC for promiscuous, IFF_ALLMULTI for allmulti.
731  * @param enable
732  *   Nonzero to enable, disable otherwise.
733  *
734  * @return
735  *   0 on success, a negative errno value otherwise and rte_errno is set.
736  */
737 static int
738 mlx5_nl_device_flags(int nlsk_fd, unsigned int iface_idx, uint32_t flags,
739                      int enable)
740 {
741         struct {
742                 struct nlmsghdr hdr;
743                 struct ifinfomsg ifi;
744         } req = {
745                 .hdr = {
746                         .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
747                         .nlmsg_type = RTM_NEWLINK,
748                         .nlmsg_flags = NLM_F_REQUEST,
749                 },
750                 .ifi = {
751                         .ifi_flags = enable ? flags : 0,
752                         .ifi_change = flags,
753                         .ifi_index = iface_idx,
754                 },
755         };
756         uint32_t sn = MLX5_NL_SN_GENERATE;
757         int ret;
758
759         assert(!(flags & ~(IFF_PROMISC | IFF_ALLMULTI)));
760         if (nlsk_fd < 0)
761                 return 0;
762         ret = mlx5_nl_send(nlsk_fd, &req.hdr, sn);
763         if (ret < 0)
764                 return ret;
765         return 0;
766 }
767
768 /**
769  * Enable promiscuous mode through Netlink.
770  *
771  * @param[in] nlsk_fd
772  *   Netlink socket file descriptor.
773  * @param[in] iface_idx
774  *   Net device interface index.
775  * @param enable
776  *   Nonzero to enable, disable otherwise.
777  *
778  * @return
779  *   0 on success, a negative errno value otherwise and rte_errno is set.
780  */
781 int
782 mlx5_nl_promisc(int nlsk_fd, unsigned int iface_idx, int enable)
783 {
784         int ret = mlx5_nl_device_flags(nlsk_fd, iface_idx, IFF_PROMISC, enable);
785
786         if (ret)
787                 DRV_LOG(DEBUG,
788                         "Interface %u cannot %s promisc mode: Netlink error %s",
789                         iface_idx, enable ? "enable" : "disable",
790                         strerror(rte_errno));
791         return ret;
792 }
793
794 /**
795  * Enable all multicast mode through Netlink.
796  *
797  * @param[in] nlsk_fd
798  *   Netlink socket file descriptor.
799  * @param[in] iface_idx
800  *   Net device interface index.
801  * @param enable
802  *   Nonzero to enable, disable otherwise.
803  *
804  * @return
805  *   0 on success, a negative errno value otherwise and rte_errno is set.
806  */
807 int
808 mlx5_nl_allmulti(int nlsk_fd, unsigned int iface_idx, int enable)
809 {
810         int ret = mlx5_nl_device_flags(nlsk_fd, iface_idx, IFF_ALLMULTI,
811                                        enable);
812
813         if (ret)
814                 DRV_LOG(DEBUG,
815                         "Interface %u cannot %s allmulti : Netlink error %s",
816                         iface_idx, enable ? "enable" : "disable",
817                         strerror(rte_errno));
818         return ret;
819 }
820
821 /**
822  * Process network interface information from Netlink message.
823  *
824  * @param nh
825  *   Pointer to Netlink message header.
826  * @param arg
827  *   Opaque data pointer for this callback.
828  *
829  * @return
830  *   0 on success, a negative errno value otherwise and rte_errno is set.
831  */
832 static int
833 mlx5_nl_cmdget_cb(struct nlmsghdr *nh, void *arg)
834 {
835         struct mlx5_nl_ifindex_data *data = arg;
836         struct mlx5_nl_ifindex_data local = {
837                 .flags = 0,
838         };
839         size_t off = NLMSG_HDRLEN;
840
841         if (nh->nlmsg_type !=
842             RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET) &&
843             nh->nlmsg_type !=
844             RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_PORT_GET))
845                 goto error;
846         while (off < nh->nlmsg_len) {
847                 struct nlattr *na = (void *)((uintptr_t)nh + off);
848                 void *payload = (void *)((uintptr_t)na + NLA_HDRLEN);
849
850                 if (na->nla_len > nh->nlmsg_len - off)
851                         goto error;
852                 switch (na->nla_type) {
853                 case RDMA_NLDEV_ATTR_DEV_INDEX:
854                         local.ibindex = *(uint32_t *)payload;
855                         local.flags |= MLX5_NL_CMD_GET_IB_INDEX;
856                         break;
857                 case RDMA_NLDEV_ATTR_DEV_NAME:
858                         if (!strcmp(payload, data->name))
859                                 local.flags |= MLX5_NL_CMD_GET_IB_NAME;
860                         break;
861                 case RDMA_NLDEV_ATTR_NDEV_INDEX:
862                         local.ifindex = *(uint32_t *)payload;
863                         local.flags |= MLX5_NL_CMD_GET_NET_INDEX;
864                         break;
865                 case RDMA_NLDEV_ATTR_PORT_INDEX:
866                         local.portnum = *(uint32_t *)payload;
867                         local.flags |= MLX5_NL_CMD_GET_PORT_INDEX;
868                         break;
869                 default:
870                         break;
871                 }
872                 off += NLA_ALIGN(na->nla_len);
873         }
874         /*
875          * It is possible to have multiple messages for all
876          * Infiniband devices in the system with appropriate name.
877          * So we should gather parameters locally and copy to
878          * query context only in case of coinciding device name.
879          */
880         if (local.flags & MLX5_NL_CMD_GET_IB_NAME) {
881                 data->flags = local.flags;
882                 data->ibindex = local.ibindex;
883                 data->ifindex = local.ifindex;
884                 data->portnum = local.portnum;
885         }
886         return 0;
887 error:
888         rte_errno = EINVAL;
889         return -rte_errno;
890 }
891
892 /**
893  * Get index of network interface associated with some IB device.
894  *
895  * This is the only somewhat safe method to avoid resorting to heuristics
896  * when faced with port representors. Unfortunately it requires at least
897  * Linux 4.17.
898  *
899  * @param nl
900  *   Netlink socket of the RDMA kind (NETLINK_RDMA).
901  * @param[in] name
902  *   IB device name.
903  * @param[in] pindex
904  *   IB device port index, starting from 1
905  * @return
906  *   A valid (nonzero) interface index on success, 0 otherwise and rte_errno
907  *   is set.
908  */
909 unsigned int
910 mlx5_nl_ifindex(int nl, const char *name, uint32_t pindex)
911 {
912         struct mlx5_nl_ifindex_data data = {
913                 .name = name,
914                 .flags = 0,
915                 .ibindex = 0, /* Determined during first pass. */
916                 .ifindex = 0, /* Determined during second pass. */
917         };
918         union {
919                 struct nlmsghdr nh;
920                 uint8_t buf[NLMSG_HDRLEN +
921                             NLA_HDRLEN + NLA_ALIGN(sizeof(data.ibindex)) +
922                             NLA_HDRLEN + NLA_ALIGN(sizeof(pindex))];
923         } req = {
924                 .nh = {
925                         .nlmsg_len = NLMSG_LENGTH(0),
926                         .nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
927                                                        RDMA_NLDEV_CMD_GET),
928                         .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
929                 },
930         };
931         struct nlattr *na;
932         uint32_t sn = MLX5_NL_SN_GENERATE;
933         int ret;
934
935         ret = mlx5_nl_send(nl, &req.nh, sn);
936         if (ret < 0)
937                 return 0;
938         ret = mlx5_nl_recv(nl, sn, mlx5_nl_cmdget_cb, &data);
939         if (ret < 0)
940                 return 0;
941         if (!(data.flags & MLX5_NL_CMD_GET_IB_NAME) ||
942             !(data.flags & MLX5_NL_CMD_GET_IB_INDEX))
943                 goto error;
944         data.flags = 0;
945         sn = MLX5_NL_SN_GENERATE;
946         req.nh.nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
947                                              RDMA_NLDEV_CMD_PORT_GET);
948         req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
949         req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.buf) - NLMSG_HDRLEN);
950         na = (void *)((uintptr_t)req.buf + NLMSG_HDRLEN);
951         na->nla_len = NLA_HDRLEN + sizeof(data.ibindex);
952         na->nla_type = RDMA_NLDEV_ATTR_DEV_INDEX;
953         memcpy((void *)((uintptr_t)na + NLA_HDRLEN),
954                &data.ibindex, sizeof(data.ibindex));
955         na = (void *)((uintptr_t)na + NLA_ALIGN(na->nla_len));
956         na->nla_len = NLA_HDRLEN + sizeof(pindex);
957         na->nla_type = RDMA_NLDEV_ATTR_PORT_INDEX;
958         memcpy((void *)((uintptr_t)na + NLA_HDRLEN),
959                &pindex, sizeof(pindex));
960         ret = mlx5_nl_send(nl, &req.nh, sn);
961         if (ret < 0)
962                 return 0;
963         ret = mlx5_nl_recv(nl, sn, mlx5_nl_cmdget_cb, &data);
964         if (ret < 0)
965                 return 0;
966         if (!(data.flags & MLX5_NL_CMD_GET_IB_NAME) ||
967             !(data.flags & MLX5_NL_CMD_GET_IB_INDEX) ||
968             !(data.flags & MLX5_NL_CMD_GET_NET_INDEX) ||
969             !data.ifindex)
970                 goto error;
971         return data.ifindex;
972 error:
973         rte_errno = ENODEV;
974         return 0;
975 }
976
977 /**
978  * Get the number of physical ports of given IB device.
979  *
980  * @param nl
981  *   Netlink socket of the RDMA kind (NETLINK_RDMA).
982  * @param[in] name
983  *   IB device name.
984  *
985  * @return
986  *   A valid (nonzero) number of ports on success, 0 otherwise
987  *   and rte_errno is set.
988  */
989 unsigned int
990 mlx5_nl_portnum(int nl, const char *name)
991 {
992         struct mlx5_nl_ifindex_data data = {
993                 .flags = 0,
994                 .name = name,
995                 .ifindex = 0,
996                 .portnum = 0,
997         };
998         struct nlmsghdr req = {
999                 .nlmsg_len = NLMSG_LENGTH(0),
1000                 .nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
1001                                                RDMA_NLDEV_CMD_GET),
1002                 .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
1003         };
1004         uint32_t sn = MLX5_NL_SN_GENERATE;
1005         int ret;
1006
1007         ret = mlx5_nl_send(nl, &req, sn);
1008         if (ret < 0)
1009                 return 0;
1010         ret = mlx5_nl_recv(nl, sn, mlx5_nl_cmdget_cb, &data);
1011         if (ret < 0)
1012                 return 0;
1013         if (!(data.flags & MLX5_NL_CMD_GET_IB_NAME) ||
1014             !(data.flags & MLX5_NL_CMD_GET_IB_INDEX) ||
1015             !(data.flags & MLX5_NL_CMD_GET_PORT_INDEX)) {
1016                 rte_errno = ENODEV;
1017                 return 0;
1018         }
1019         if (!data.portnum)
1020                 rte_errno = EINVAL;
1021         return data.portnum;
1022 }
1023
1024 /**
1025  * Analyze gathered port parameters via Netlink to recognize master
1026  * and representor devices for E-Switch configuration.
1027  *
1028  * @param[in] num_vf_set
1029  *   flag of presence of number of VFs port attribute.
1030  * @param[inout] switch_info
1031  *   Port information, including port name as a number and port name
1032  *   type if recognized
1033  *
1034  * @return
1035  *   master and representor flags are set in switch_info according to
1036  *   recognized parameters (if any).
1037  */
1038 static void
1039 mlx5_nl_check_switch_info(bool num_vf_set,
1040                           struct mlx5_switch_info *switch_info)
1041 {
1042         switch (switch_info->name_type) {
1043         case MLX5_PHYS_PORT_NAME_TYPE_UNKNOWN:
1044                 /*
1045                  * Name is not recognized, assume the master,
1046                  * check the number of VFs key presence.
1047                  */
1048                 switch_info->master = num_vf_set;
1049                 break;
1050         case MLX5_PHYS_PORT_NAME_TYPE_NOTSET:
1051                 /*
1052                  * Name is not set, this assumes the legacy naming
1053                  * schema for master, just check if there is a
1054                  * number of VFs key.
1055                  */
1056                 switch_info->master = num_vf_set;
1057                 break;
1058         case MLX5_PHYS_PORT_NAME_TYPE_UPLINK:
1059                 /* New uplink naming schema recognized. */
1060                 switch_info->master = 1;
1061                 break;
1062         case MLX5_PHYS_PORT_NAME_TYPE_LEGACY:
1063                 /* Legacy representors naming schema. */
1064                 switch_info->representor = !num_vf_set;
1065                 break;
1066         case MLX5_PHYS_PORT_NAME_TYPE_PFVF:
1067                 /* New representors naming schema. */
1068                 switch_info->representor = 1;
1069                 break;
1070         }
1071 }
1072
1073 /**
1074  * Process switch information from Netlink message.
1075  *
1076  * @param nh
1077  *   Pointer to Netlink message header.
1078  * @param arg
1079  *   Opaque data pointer for this callback.
1080  *
1081  * @return
1082  *   0 on success, a negative errno value otherwise and rte_errno is set.
1083  */
1084 static int
1085 mlx5_nl_switch_info_cb(struct nlmsghdr *nh, void *arg)
1086 {
1087         struct mlx5_switch_info info = {
1088                 .master = 0,
1089                 .representor = 0,
1090                 .name_type = MLX5_PHYS_PORT_NAME_TYPE_NOTSET,
1091                 .port_name = 0,
1092                 .switch_id = 0,
1093         };
1094         size_t off = NLMSG_LENGTH(sizeof(struct ifinfomsg));
1095         bool switch_id_set = false;
1096         bool num_vf_set = false;
1097
1098         if (nh->nlmsg_type != RTM_NEWLINK)
1099                 goto error;
1100         while (off < nh->nlmsg_len) {
1101                 struct rtattr *ra = (void *)((uintptr_t)nh + off);
1102                 void *payload = RTA_DATA(ra);
1103                 unsigned int i;
1104
1105                 if (ra->rta_len > nh->nlmsg_len - off)
1106                         goto error;
1107                 switch (ra->rta_type) {
1108                 case IFLA_NUM_VF:
1109                         num_vf_set = true;
1110                         break;
1111                 case IFLA_PHYS_PORT_NAME:
1112                         mlx5_translate_port_name((char *)payload, &info);
1113                         break;
1114                 case IFLA_PHYS_SWITCH_ID:
1115                         info.switch_id = 0;
1116                         for (i = 0; i < RTA_PAYLOAD(ra); ++i) {
1117                                 info.switch_id <<= 8;
1118                                 info.switch_id |= ((uint8_t *)payload)[i];
1119                         }
1120                         switch_id_set = true;
1121                         break;
1122                 }
1123                 off += RTA_ALIGN(ra->rta_len);
1124         }
1125         if (switch_id_set) {
1126                 /* We have some E-Switch configuration. */
1127                 mlx5_nl_check_switch_info(num_vf_set, &info);
1128         }
1129         assert(!(info.master && info.representor));
1130         memcpy(arg, &info, sizeof(info));
1131         return 0;
1132 error:
1133         rte_errno = EINVAL;
1134         return -rte_errno;
1135 }
1136
1137 /**
1138  * Get switch information associated with network interface.
1139  *
1140  * @param nl
1141  *   Netlink socket of the ROUTE kind (NETLINK_ROUTE).
1142  * @param ifindex
1143  *   Network interface index.
1144  * @param[out] info
1145  *   Switch information object, populated in case of success.
1146  *
1147  * @return
1148  *   0 on success, a negative errno value otherwise and rte_errno is set.
1149  */
1150 int
1151 mlx5_nl_switch_info(int nl, unsigned int ifindex,
1152                     struct mlx5_switch_info *info)
1153 {
1154         struct {
1155                 struct nlmsghdr nh;
1156                 struct ifinfomsg info;
1157                 struct rtattr rta;
1158                 uint32_t extmask;
1159         } req = {
1160                 .nh = {
1161                         .nlmsg_len = NLMSG_LENGTH
1162                                         (sizeof(req.info) +
1163                                          RTA_LENGTH(sizeof(uint32_t))),
1164                         .nlmsg_type = RTM_GETLINK,
1165                         .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
1166                 },
1167                 .info = {
1168                         .ifi_family = AF_UNSPEC,
1169                         .ifi_index = ifindex,
1170                 },
1171                 .rta = {
1172                         .rta_type = IFLA_EXT_MASK,
1173                         .rta_len = RTA_LENGTH(sizeof(int32_t)),
1174                 },
1175                 .extmask = RTE_LE32(1),
1176         };
1177         uint32_t sn = MLX5_NL_SN_GENERATE;
1178         int ret;
1179
1180         ret = mlx5_nl_send(nl, &req.nh, sn);
1181         if (ret >= 0)
1182                 ret = mlx5_nl_recv(nl, sn, mlx5_nl_switch_info_cb, info);
1183         if (info->master && info->representor) {
1184                 DRV_LOG(ERR, "ifindex %u device is recognized as master"
1185                              " and as representor", ifindex);
1186                 rte_errno = ENODEV;
1187                 ret = -rte_errno;
1188         }
1189         return ret;
1190 }
1191
1192 /*
1193  * Delete VLAN network device by ifindex.
1194  *
1195  * @param[in] tcf
1196  *   Context object initialized by mlx5_nl_vlan_vmwa_init().
1197  * @param[in] ifindex
1198  *   Interface index of network device to delete.
1199  */
1200 void
1201 mlx5_nl_vlan_vmwa_delete(struct mlx5_nl_vlan_vmwa_context *vmwa,
1202                       uint32_t ifindex)
1203 {
1204         uint32_t sn = MLX5_NL_SN_GENERATE;
1205         int ret;
1206         struct {
1207                 struct nlmsghdr nh;
1208                 struct ifinfomsg info;
1209         } req = {
1210                 .nh = {
1211                         .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
1212                         .nlmsg_type = RTM_DELLINK,
1213                         .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
1214                 },
1215                 .info = {
1216                         .ifi_family = AF_UNSPEC,
1217                         .ifi_index = ifindex,
1218                 },
1219         };
1220
1221         if (ifindex) {
1222                 ret = mlx5_nl_send(vmwa->nl_socket, &req.nh, sn);
1223                 if (ret >= 0)
1224                         ret = mlx5_nl_recv(vmwa->nl_socket, sn, NULL, NULL);
1225                 if (ret < 0)
1226                         DRV_LOG(WARNING, "netlink: error deleting VLAN WA"
1227                                 " ifindex %u, %d", ifindex, ret);
1228         }
1229 }
1230
1231 /* Set of subroutines to build Netlink message. */
1232 static struct nlattr *
1233 nl_msg_tail(struct nlmsghdr *nlh)
1234 {
1235         return (struct nlattr *)
1236                 (((uint8_t *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len));
1237 }
1238
1239 static void
1240 nl_attr_put(struct nlmsghdr *nlh, int type, const void *data, int alen)
1241 {
1242         struct nlattr *nla = nl_msg_tail(nlh);
1243
1244         nla->nla_type = type;
1245         nla->nla_len = NLMSG_ALIGN(sizeof(struct nlattr) + alen);
1246         nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + nla->nla_len;
1247
1248         if (alen)
1249                 memcpy((uint8_t *)nla + sizeof(struct nlattr), data, alen);
1250 }
1251
1252 static struct nlattr *
1253 nl_attr_nest_start(struct nlmsghdr *nlh, int type)
1254 {
1255         struct nlattr *nest = (struct nlattr *)nl_msg_tail(nlh);
1256
1257         nl_attr_put(nlh, type, NULL, 0);
1258         return nest;
1259 }
1260
1261 static void
1262 nl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *nest)
1263 {
1264         nest->nla_len = (uint8_t *)nl_msg_tail(nlh) - (uint8_t *)nest;
1265 }
1266
1267 /*
1268  * Create network VLAN device with specified VLAN tag.
1269  *
1270  * @param[in] tcf
1271  *   Context object initialized by mlx5_nl_vlan_vmwa_init().
1272  * @param[in] ifindex
1273  *   Base network interface index.
1274  * @param[in] tag
1275  *   VLAN tag for VLAN network device to create.
1276  */
1277 uint32_t
1278 mlx5_nl_vlan_vmwa_create(struct mlx5_nl_vlan_vmwa_context *vmwa,
1279                          uint32_t ifindex, uint16_t tag)
1280 {
1281         struct nlmsghdr *nlh;
1282         struct ifinfomsg *ifm;
1283         char name[sizeof(MLX5_VMWA_VLAN_DEVICE_PFX) + 32];
1284
1285         alignas(RTE_CACHE_LINE_SIZE)
1286         uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
1287                     NLMSG_ALIGN(sizeof(struct ifinfomsg)) +
1288                     NLMSG_ALIGN(sizeof(struct nlattr)) * 8 +
1289                     NLMSG_ALIGN(sizeof(uint32_t)) +
1290                     NLMSG_ALIGN(sizeof(name)) +
1291                     NLMSG_ALIGN(sizeof("vlan")) +
1292                     NLMSG_ALIGN(sizeof(uint32_t)) +
1293                     NLMSG_ALIGN(sizeof(uint16_t)) + 16];
1294         struct nlattr *na_info;
1295         struct nlattr *na_vlan;
1296         uint32_t sn = MLX5_NL_SN_GENERATE;
1297         int ret;
1298
1299         memset(buf, 0, sizeof(buf));
1300         nlh = (struct nlmsghdr *)buf;
1301         nlh->nlmsg_len = sizeof(struct nlmsghdr);
1302         nlh->nlmsg_type = RTM_NEWLINK;
1303         nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE |
1304                            NLM_F_EXCL | NLM_F_ACK;
1305         ifm = (struct ifinfomsg *)nl_msg_tail(nlh);
1306         nlh->nlmsg_len += sizeof(struct ifinfomsg);
1307         ifm->ifi_family = AF_UNSPEC;
1308         ifm->ifi_type = 0;
1309         ifm->ifi_index = 0;
1310         ifm->ifi_flags = IFF_UP;
1311         ifm->ifi_change = 0xffffffff;
1312         nl_attr_put(nlh, IFLA_LINK, &ifindex, sizeof(ifindex));
1313         ret = snprintf(name, sizeof(name), "%s.%u.%u",
1314                        MLX5_VMWA_VLAN_DEVICE_PFX, ifindex, tag);
1315         nl_attr_put(nlh, IFLA_IFNAME, name, ret + 1);
1316         na_info = nl_attr_nest_start(nlh, IFLA_LINKINFO);
1317         nl_attr_put(nlh, IFLA_INFO_KIND, "vlan", sizeof("vlan"));
1318         na_vlan = nl_attr_nest_start(nlh, IFLA_INFO_DATA);
1319         nl_attr_put(nlh, IFLA_VLAN_ID, &tag, sizeof(tag));
1320         nl_attr_nest_end(nlh, na_vlan);
1321         nl_attr_nest_end(nlh, na_info);
1322         assert(sizeof(buf) >= nlh->nlmsg_len);
1323         ret = mlx5_nl_send(vmwa->nl_socket, nlh, sn);
1324         if (ret >= 0)
1325                 ret = mlx5_nl_recv(vmwa->nl_socket, sn, NULL, NULL);
1326         if (ret < 0) {
1327                 DRV_LOG(WARNING, "netlink: VLAN %s create failure (%d)", name,
1328                         ret);
1329         }
1330         // Try to get ifindex of created or pre-existing device.
1331         ret = if_nametoindex(name);
1332         if (!ret) {
1333                 DRV_LOG(WARNING, "VLAN %s failed to get index (%d)", name,
1334                         errno);
1335                 return 0;
1336         }
1337         return ret;
1338 }