net/mlx5: use Netlink to enable promisc/allmulti mode
[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 <linux/netlink.h>
7 #include <linux/rtnetlink.h>
8 #include <unistd.h>
9
10 #include "mlx5.h"
11 #include "mlx5_utils.h"
12
13 /* Size of the buffer to receive kernel messages */
14 #define MLX5_NL_BUF_SIZE (32 * 1024)
15 /* Send buffer size for the Netlink socket */
16 #define MLX5_SEND_BUF_SIZE 32768
17 /* Receive buffer size for the Netlink socket */
18 #define MLX5_RECV_BUF_SIZE 32768
19
20 /*
21  * Define NDA_RTA as defined in iproute2 sources.
22  *
23  * see in iproute2 sources file include/libnetlink.h
24  */
25 #ifndef MLX5_NDA_RTA
26 #define MLX5_NDA_RTA(r) \
27         ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
28 #endif
29
30 /* Add/remove MAC address through Netlink */
31 struct mlx5_nl_mac_addr {
32         struct ether_addr (*mac)[];
33         /**< MAC address handled by the device. */
34         int mac_n; /**< Number of addresses in the array. */
35 };
36
37 /**
38  * Opens a Netlink socket.
39  *
40  * @param nl_groups
41  *   Netlink group value (e.g. RTMGRP_LINK).
42  *
43  * @return
44  *   A file descriptor on success, a negative errno value otherwise and
45  *   rte_errno is set.
46  */
47 int
48 mlx5_nl_init(uint32_t nl_groups)
49 {
50         int fd;
51         int sndbuf_size = MLX5_SEND_BUF_SIZE;
52         int rcvbuf_size = MLX5_RECV_BUF_SIZE;
53         struct sockaddr_nl local = {
54                 .nl_family = AF_NETLINK,
55                 .nl_groups = nl_groups,
56         };
57         int ret;
58
59         fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
60         if (fd == -1) {
61                 rte_errno = errno;
62                 return -rte_errno;
63         }
64         ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(int));
65         if (ret == -1) {
66                 rte_errno = errno;
67                 goto error;
68         }
69         ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(int));
70         if (ret == -1) {
71                 rte_errno = errno;
72                 goto error;
73         }
74         ret = bind(fd, (struct sockaddr *)&local, sizeof(local));
75         if (ret == -1) {
76                 rte_errno = errno;
77                 goto error;
78         }
79         return fd;
80 error:
81         close(fd);
82         return -rte_errno;
83 }
84
85 /**
86  * Send a request message to the kernel on the Netlink socket.
87  *
88  * @param[in] nlsk_fd
89  *   Netlink socket file descriptor.
90  * @param[in] nh
91  *   The Netlink message send to the kernel.
92  * @param[in] ssn
93  *   Sequence number.
94  * @param[in] req
95  *   Pointer to the request structure.
96  * @param[in] len
97  *   Length of the request in bytes.
98  *
99  * @return
100  *   The number of sent bytes on success, a negative errno value otherwise and
101  *   rte_errno is set.
102  */
103 static int
104 mlx5_nl_request(int nlsk_fd, struct nlmsghdr *nh, uint32_t sn, void *req,
105                 int len)
106 {
107         struct sockaddr_nl sa = {
108                 .nl_family = AF_NETLINK,
109         };
110         struct iovec iov[2] = {
111                 { .iov_base = nh, .iov_len = sizeof(*nh), },
112                 { .iov_base = req, .iov_len = len, },
113         };
114         struct msghdr msg = {
115                 .msg_name = &sa,
116                 .msg_namelen = sizeof(sa),
117                 .msg_iov = iov,
118                 .msg_iovlen = 2,
119         };
120         int send_bytes;
121
122         nh->nlmsg_pid = 0; /* communication with the kernel uses pid 0 */
123         nh->nlmsg_seq = sn;
124         send_bytes = sendmsg(nlsk_fd, &msg, 0);
125         if (send_bytes < 0) {
126                 rte_errno = errno;
127                 return -rte_errno;
128         }
129         return send_bytes;
130 }
131
132 /**
133  * Send a message to the kernel on the Netlink socket.
134  *
135  * @param[in] nlsk_fd
136  *   The Netlink socket file descriptor used for communication.
137  * @param[in] nh
138  *   The Netlink message send to the kernel.
139  * @param[in] sn
140  *   Sequence number.
141  *
142  * @return
143  *   The number of sent bytes on success, a negative errno value otherwise and
144  *   rte_errno is set.
145  */
146 static int
147 mlx5_nl_send(int nlsk_fd, struct nlmsghdr *nh, uint32_t sn)
148 {
149         struct sockaddr_nl sa = {
150                 .nl_family = AF_NETLINK,
151         };
152         struct iovec iov = {
153                 .iov_base = nh,
154                 .iov_len = nh->nlmsg_len,
155         };
156         struct msghdr msg = {
157                 .msg_name = &sa,
158                 .msg_namelen = sizeof(sa),
159                 .msg_iov = &iov,
160                 .msg_iovlen = 1,
161         };
162         int send_bytes;
163
164         nh->nlmsg_pid = 0; /* communication with the kernel uses pid 0 */
165         nh->nlmsg_seq = sn;
166         send_bytes = sendmsg(nlsk_fd, &msg, 0);
167         if (send_bytes < 0) {
168                 rte_errno = errno;
169                 return -rte_errno;
170         }
171         return send_bytes;
172 }
173
174 /**
175  * Receive a message from the kernel on the Netlink socket, following
176  * mlx5_nl_send().
177  *
178  * @param[in] nlsk_fd
179  *   The Netlink socket file descriptor used for communication.
180  * @param[in] sn
181  *   Sequence number.
182  * @param[in] cb
183  *   The callback function to call for each Netlink message received.
184  * @param[in, out] arg
185  *   Custom arguments for the callback.
186  *
187  * @return
188  *   0 on success, a negative errno value otherwise and rte_errno is set.
189  */
190 static int
191 mlx5_nl_recv(int nlsk_fd, uint32_t sn, int (*cb)(struct nlmsghdr *, void *arg),
192              void *arg)
193 {
194         struct sockaddr_nl sa;
195         char buf[MLX5_RECV_BUF_SIZE];
196         struct iovec iov = {
197                 .iov_base = buf,
198                 .iov_len = sizeof(buf),
199         };
200         struct msghdr msg = {
201                 .msg_name = &sa,
202                 .msg_namelen = sizeof(sa),
203                 .msg_iov = &iov,
204                 /* One message at a time */
205                 .msg_iovlen = 1,
206         };
207         int multipart = 0;
208         int ret = 0;
209
210         do {
211                 struct nlmsghdr *nh;
212                 int recv_bytes = 0;
213
214                 do {
215                         recv_bytes = recvmsg(nlsk_fd, &msg, 0);
216                         if (recv_bytes == -1) {
217                                 rte_errno = errno;
218                                 return -rte_errno;
219                         }
220                         nh = (struct nlmsghdr *)buf;
221                 } while (nh->nlmsg_seq != sn);
222                 for (;
223                      NLMSG_OK(nh, (unsigned int)recv_bytes);
224                      nh = NLMSG_NEXT(nh, recv_bytes)) {
225                         if (nh->nlmsg_type == NLMSG_ERROR) {
226                                 struct nlmsgerr *err_data = NLMSG_DATA(nh);
227
228                                 if (err_data->error < 0) {
229                                         rte_errno = -err_data->error;
230                                         return -rte_errno;
231                                 }
232                                 /* Ack message. */
233                                 return 0;
234                         }
235                         /* Multi-part msgs and their trailing DONE message. */
236                         if (nh->nlmsg_flags & NLM_F_MULTI) {
237                                 if (nh->nlmsg_type == NLMSG_DONE)
238                                         return 0;
239                                 multipart = 1;
240                         }
241                         if (cb) {
242                                 ret = cb(nh, arg);
243                                 if (ret < 0)
244                                         return ret;
245                         }
246                 }
247         } while (multipart);
248         return ret;
249 }
250
251 /**
252  * Parse Netlink message to retrieve the bridge MAC address.
253  *
254  * @param nh
255  *   Pointer to Netlink Message Header.
256  * @param arg
257  *   PMD data register with this callback.
258  *
259  * @return
260  *   0 on success, a negative errno value otherwise and rte_errno is set.
261  */
262 static int
263 mlx5_nl_mac_addr_cb(struct nlmsghdr *nh, void *arg)
264 {
265         struct mlx5_nl_mac_addr *data = arg;
266         struct ndmsg *r = NLMSG_DATA(nh);
267         struct rtattr *attribute;
268         int len;
269
270         len = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
271         for (attribute = MLX5_NDA_RTA(r);
272              RTA_OK(attribute, len);
273              attribute = RTA_NEXT(attribute, len)) {
274                 if (attribute->rta_type == NDA_LLADDR) {
275                         if (data->mac_n == MLX5_MAX_MAC_ADDRESSES) {
276                                 DRV_LOG(WARNING,
277                                         "not enough room to finalize the"
278                                         " request");
279                                 rte_errno = ENOMEM;
280                                 return -rte_errno;
281                         }
282 #ifndef NDEBUG
283                         char m[18];
284
285                         ether_format_addr(m, 18, RTA_DATA(attribute));
286                         DRV_LOG(DEBUG, "bridge MAC address %s", m);
287 #endif
288                         memcpy(&(*data->mac)[data->mac_n++],
289                                RTA_DATA(attribute), ETHER_ADDR_LEN);
290                 }
291         }
292         return 0;
293 }
294
295 /**
296  * Get bridge MAC addresses.
297  *
298  * @param dev
299  *   Pointer to Ethernet device.
300  * @param mac[out]
301  *   Pointer to the array table of MAC addresses to fill.
302  *   Its size should be of MLX5_MAX_MAC_ADDRESSES.
303  * @param mac_n[out]
304  *   Number of entries filled in MAC array.
305  *
306  * @return
307  *   0 on success, a negative errno value otherwise and rte_errno is set.
308  */
309 static int
310 mlx5_nl_mac_addr_list(struct rte_eth_dev *dev, struct ether_addr (*mac)[],
311                       int *mac_n)
312 {
313         struct priv *priv = dev->data->dev_private;
314         int iface_idx = mlx5_ifindex(dev);
315         struct {
316                 struct nlmsghdr hdr;
317                 struct ifinfomsg ifm;
318         } req = {
319                 .hdr = {
320                         .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
321                         .nlmsg_type = RTM_GETNEIGH,
322                         .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
323                 },
324                 .ifm = {
325                         .ifi_family = PF_BRIDGE,
326                         .ifi_index = iface_idx,
327                 },
328         };
329         struct mlx5_nl_mac_addr data = {
330                 .mac = mac,
331                 .mac_n = 0,
332         };
333         int fd;
334         int ret;
335         uint32_t sn = priv->nl_sn++;
336
337         if (priv->nl_socket == -1)
338                 return 0;
339         fd = priv->nl_socket;
340         ret = mlx5_nl_request(fd, &req.hdr, sn, &req.ifm,
341                               sizeof(struct ifinfomsg));
342         if (ret < 0)
343                 goto error;
344         ret = mlx5_nl_recv(fd, sn, mlx5_nl_mac_addr_cb, &data);
345         if (ret < 0)
346                 goto error;
347         *mac_n = data.mac_n;
348         return 0;
349 error:
350         DRV_LOG(DEBUG, "port %u cannot retrieve MAC address list %s",
351                 dev->data->port_id, strerror(rte_errno));
352         return -rte_errno;
353 }
354
355 /**
356  * Modify the MAC address neighbour table with Netlink.
357  *
358  * @param dev
359  *   Pointer to Ethernet device.
360  * @param mac
361  *   MAC address to consider.
362  * @param add
363  *   1 to add the MAC address, 0 to remove the MAC address.
364  *
365  * @return
366  *   0 on success, a negative errno value otherwise and rte_errno is set.
367  */
368 static int
369 mlx5_nl_mac_addr_modify(struct rte_eth_dev *dev, struct ether_addr *mac,
370                         int add)
371 {
372         struct priv *priv = dev->data->dev_private;
373         int iface_idx = mlx5_ifindex(dev);
374         struct {
375                 struct nlmsghdr hdr;
376                 struct ndmsg ndm;
377                 struct rtattr rta;
378                 uint8_t buffer[ETHER_ADDR_LEN];
379         } req = {
380                 .hdr = {
381                         .nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
382                         .nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE |
383                                 NLM_F_EXCL | NLM_F_ACK,
384                         .nlmsg_type = add ? RTM_NEWNEIGH : RTM_DELNEIGH,
385                 },
386                 .ndm = {
387                         .ndm_family = PF_BRIDGE,
388                         .ndm_state = NUD_NOARP | NUD_PERMANENT,
389                         .ndm_ifindex = iface_idx,
390                         .ndm_flags = NTF_SELF,
391                 },
392                 .rta = {
393                         .rta_type = NDA_LLADDR,
394                         .rta_len = RTA_LENGTH(ETHER_ADDR_LEN),
395                 },
396         };
397         int fd;
398         int ret;
399         uint32_t sn = priv->nl_sn++;
400
401         if (priv->nl_socket == -1)
402                 return 0;
403         fd = priv->nl_socket;
404         memcpy(RTA_DATA(&req.rta), mac, ETHER_ADDR_LEN);
405         req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
406                 RTA_ALIGN(req.rta.rta_len);
407         ret = mlx5_nl_send(fd, &req.hdr, sn);
408         if (ret < 0)
409                 goto error;
410         ret = mlx5_nl_recv(fd, sn, NULL, NULL);
411         if (ret < 0)
412                 goto error;
413         return 0;
414 error:
415         DRV_LOG(DEBUG,
416                 "port %u cannot %s MAC address %02X:%02X:%02X:%02X:%02X:%02X"
417                 " %s",
418                 dev->data->port_id,
419                 add ? "add" : "remove",
420                 mac->addr_bytes[0], mac->addr_bytes[1],
421                 mac->addr_bytes[2], mac->addr_bytes[3],
422                 mac->addr_bytes[4], mac->addr_bytes[5],
423                 strerror(rte_errno));
424         return -rte_errno;
425 }
426
427 /**
428  * Add a MAC address.
429  *
430  * @param dev
431  *   Pointer to Ethernet device.
432  * @param mac
433  *   MAC address to register.
434  * @param index
435  *   MAC address index.
436  *
437  * @return
438  *   0 on success, a negative errno value otherwise and rte_errno is set.
439  */
440 int
441 mlx5_nl_mac_addr_add(struct rte_eth_dev *dev, struct ether_addr *mac,
442                      uint32_t index)
443 {
444         struct priv *priv = dev->data->dev_private;
445         int ret;
446
447         ret = mlx5_nl_mac_addr_modify(dev, mac, 1);
448         if (!ret)
449                 BITFIELD_SET(priv->mac_own, index);
450         if (ret == -EEXIST)
451                 return 0;
452         return ret;
453 }
454
455 /**
456  * Remove a MAC address.
457  *
458  * @param dev
459  *   Pointer to Ethernet device.
460  * @param mac
461  *   MAC address to remove.
462  * @param index
463  *   MAC address index.
464  *
465  * @return
466  *   0 on success, a negative errno value otherwise and rte_errno is set.
467  */
468 int
469 mlx5_nl_mac_addr_remove(struct rte_eth_dev *dev, struct ether_addr *mac,
470                         uint32_t index)
471 {
472         struct priv *priv = dev->data->dev_private;
473
474         BITFIELD_RESET(priv->mac_own, index);
475         return mlx5_nl_mac_addr_modify(dev, mac, 0);
476 }
477
478 /**
479  * Synchronize Netlink bridge table to the internal table.
480  *
481  * @param dev
482  *   Pointer to Ethernet device.
483  */
484 void
485 mlx5_nl_mac_addr_sync(struct rte_eth_dev *dev)
486 {
487         struct ether_addr macs[MLX5_MAX_MAC_ADDRESSES];
488         int macs_n = 0;
489         int i;
490         int ret;
491
492         ret = mlx5_nl_mac_addr_list(dev, &macs, &macs_n);
493         if (ret)
494                 return;
495         for (i = 0; i != macs_n; ++i) {
496                 int j;
497
498                 /* Verify the address is not in the array yet. */
499                 for (j = 0; j != MLX5_MAX_MAC_ADDRESSES; ++j)
500                         if (is_same_ether_addr(&macs[i],
501                                                &dev->data->mac_addrs[j]))
502                                 break;
503                 if (j != MLX5_MAX_MAC_ADDRESSES)
504                         continue;
505                 /* Find the first entry available. */
506                 for (j = 0; j != MLX5_MAX_MAC_ADDRESSES; ++j) {
507                         if (is_zero_ether_addr(&dev->data->mac_addrs[j])) {
508                                 dev->data->mac_addrs[j] = macs[i];
509                                 break;
510                         }
511                 }
512         }
513 }
514
515 /**
516  * Flush all added MAC addresses.
517  *
518  * @param dev
519  *   Pointer to Ethernet device.
520  */
521 void
522 mlx5_nl_mac_addr_flush(struct rte_eth_dev *dev)
523 {
524         struct priv *priv = dev->data->dev_private;
525         int i;
526
527         for (i = MLX5_MAX_MAC_ADDRESSES - 1; i >= 0; --i) {
528                 struct ether_addr *m = &dev->data->mac_addrs[i];
529
530                 if (BITFIELD_ISSET(priv->mac_own, i))
531                         mlx5_nl_mac_addr_remove(dev, m, i);
532         }
533 }
534
535 /**
536  * Enable promiscuous / all multicast mode through Netlink.
537  *
538  * @param dev
539  *   Pointer to Ethernet device structure.
540  * @param flags
541  *   IFF_PROMISC for promiscuous, IFF_ALLMULTI for allmulti.
542  * @param enable
543  *   Nonzero to enable, disable otherwise.
544  *
545  * @return
546  *   0 on success, a negative errno value otherwise and rte_errno is set.
547  */
548 static int
549 mlx5_nl_device_flags(struct rte_eth_dev *dev, uint32_t flags, int enable)
550 {
551         struct priv *priv = dev->data->dev_private;
552         int iface_idx = mlx5_ifindex(dev);
553         struct {
554                 struct nlmsghdr hdr;
555                 struct ifinfomsg ifi;
556         } req = {
557                 .hdr = {
558                         .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
559                         .nlmsg_type = RTM_NEWLINK,
560                         .nlmsg_flags = NLM_F_REQUEST,
561                 },
562                 .ifi = {
563                         .ifi_flags = enable ? flags : 0,
564                         .ifi_change = flags,
565                         .ifi_index = iface_idx,
566                 },
567         };
568         int fd;
569         int ret;
570
571         assert(!(flags & ~(IFF_PROMISC | IFF_ALLMULTI)));
572         if (priv->nl_socket < 0)
573                 return 0;
574         fd = priv->nl_socket;
575         ret = mlx5_nl_send(fd, &req.hdr, priv->nl_sn++);
576         if (ret < 0)
577                 return ret;
578         return 0;
579 }
580
581 /**
582  * Enable promiscuous mode through Netlink.
583  *
584  * @param dev
585  *   Pointer to Ethernet device structure.
586  * @param enable
587  *   Nonzero to enable, disable otherwise.
588  *
589  * @return
590  *   0 on success, a negative errno value otherwise and rte_errno is set.
591  */
592 int
593 mlx5_nl_promisc(struct rte_eth_dev *dev, int enable)
594 {
595         int ret = mlx5_nl_device_flags(dev, IFF_PROMISC, enable);
596
597         if (ret)
598                 DRV_LOG(DEBUG,
599                         "port %u cannot %s promisc mode: Netlink error %s",
600                         dev->data->port_id, enable ? "enable" : "disable",
601                         strerror(rte_errno));
602         return ret;
603 }
604
605 /**
606  * Enable all multicast mode through Netlink.
607  *
608  * @param dev
609  *   Pointer to Ethernet device structure.
610  * @param enable
611  *   Nonzero to enable, disable otherwise.
612  *
613  * @return
614  *   0 on success, a negative errno value otherwise and rte_errno is set.
615  */
616 int
617 mlx5_nl_allmulti(struct rte_eth_dev *dev, int enable)
618 {
619         int ret = mlx5_nl_device_flags(dev, IFF_ALLMULTI, enable);
620
621         if (ret)
622                 DRV_LOG(DEBUG,
623                         "port %u cannot %s allmulti mode: Netlink error %s",
624                         dev->data->port_id, enable ? "enable" : "disable",
625                         strerror(rte_errno));
626         return ret;
627 }