64220178a6ba5e2f8d69e958c8e5110dc929acfd
[dpdk.git] / drivers / net / tap / tap_netlink.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2017 6WIND S.A.
3  * Copyright 2017 Mellanox Technologies, Ltd
4  */
5
6 #include <errno.h>
7 #include <inttypes.h>
8 #include <linux/netlink.h>
9 #include <string.h>
10 #include <sys/socket.h>
11 #include <unistd.h>
12
13 #include <rte_malloc.h>
14 #include <tap_netlink.h>
15 #include <rte_random.h>
16 #include "tap_log.h"
17
18 /* Must be quite large to support dumping a huge list of QDISC or filters. */
19 #define BUF_SIZE (32 * 1024) /* Size of the buffer to receive kernel messages */
20 #define SNDBUF_SIZE 32768 /* Send buffer size for the netlink socket */
21 #define RCVBUF_SIZE 32768 /* Receive buffer size for the netlink socket */
22
23 struct nested_tail {
24         struct rtattr *tail;
25         struct nested_tail *prev;
26 };
27
28 /**
29  * Initialize a netlink socket for communicating with the kernel.
30  *
31  * @param nl_groups
32  *   Set it to a netlink group value (e.g. RTMGRP_LINK) to receive messages for
33  *   specific netlink multicast groups. Otherwise, no subscription will be made.
34  *
35  * @return
36  *   netlink socket file descriptor on success, -1 otherwise.
37  */
38 int
39 tap_nl_init(uint32_t nl_groups)
40 {
41         int fd, sndbuf_size = SNDBUF_SIZE, rcvbuf_size = RCVBUF_SIZE;
42         struct sockaddr_nl local = {
43                 .nl_family = AF_NETLINK,
44                 .nl_groups = nl_groups,
45         };
46
47         fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
48         if (fd < 0) {
49                 TAP_LOG(ERR, "Unable to create a netlink socket");
50                 return -1;
51         }
52         if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(int))) {
53                 TAP_LOG(ERR, "Unable to set socket buffer send size");
54                 close(fd);
55                 return -1;
56         }
57         if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(int))) {
58                 TAP_LOG(ERR, "Unable to set socket buffer receive size");
59                 close(fd);
60                 return -1;
61         }
62         if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
63                 TAP_LOG(ERR, "Unable to bind to the netlink socket");
64                 close(fd);
65                 return -1;
66         }
67         return fd;
68 }
69
70 /**
71  * Clean up a netlink socket once all communicating with the kernel is finished.
72  *
73  * @param[in] nlsk_fd
74  *   The netlink socket file descriptor used for communication.
75  *
76  * @return
77  *   0 on success, -1 otherwise.
78  */
79 int
80 tap_nl_final(int nlsk_fd)
81 {
82         if (close(nlsk_fd)) {
83                 TAP_LOG(ERR, "Failed to close netlink socket: %s (%d)",
84                         strerror(errno), errno);
85                 return -1;
86         }
87         return 0;
88 }
89
90 /**
91  * Send a message to the kernel on the netlink socket.
92  *
93  * @param[in] nlsk_fd
94  *   The netlink socket file descriptor used for communication.
95  * @param[in] nh
96  *   The netlink message send to the kernel.
97  *
98  * @return
99  *   the number of sent bytes on success, -1 otherwise.
100  */
101 int
102 tap_nl_send(int nlsk_fd, struct nlmsghdr *nh)
103 {
104         int send_bytes;
105
106         nh->nlmsg_pid = 0; /* communication with the kernel uses pid 0 */
107         nh->nlmsg_seq = (uint32_t)rte_rand();
108
109 retry:
110         send_bytes = send(nlsk_fd, nh, nh->nlmsg_len, 0);
111         if (send_bytes < 0) {
112                 if (errno == EINTR)
113                         goto retry;
114
115                 TAP_LOG(ERR, "Failed to send netlink message: %s (%d)",
116                         strerror(errno), errno);
117                 return -1;
118         }
119         return send_bytes;
120 }
121
122 /**
123  * Check that the kernel sends an appropriate ACK in response
124  * to an tap_nl_send().
125  *
126  * @param[in] nlsk_fd
127  *   The netlink socket file descriptor used for communication.
128  *
129  * @return
130  *   0 on success, -1 otherwise with errno set.
131  */
132 int
133 tap_nl_recv_ack(int nlsk_fd)
134 {
135         return tap_nl_recv(nlsk_fd, NULL, NULL);
136 }
137
138 /**
139  * Receive a message from the kernel on the netlink socket, following an
140  * tap_nl_send().
141  *
142  * @param[in] nlsk_fd
143  *   The netlink socket file descriptor used for communication.
144  * @param[in] cb
145  *   The callback function to call for each netlink message received.
146  * @param[in, out] arg
147  *   Custom arguments for the callback.
148  *
149  * @return
150  *   0 on success, -1 otherwise with errno set.
151  */
152 int
153 tap_nl_recv(int nlsk_fd, int (*cb)(struct nlmsghdr *, void *arg), void *arg)
154 {
155         char buf[BUF_SIZE];
156         int multipart = 0;
157         int ret = 0;
158
159         do {
160                 struct nlmsghdr *nh;
161                 int recv_bytes;
162
163 retry:
164                 recv_bytes = recv(nlsk_fd, buf, sizeof(buf), 0);
165                 if (recv_bytes < 0) {
166                         if (errno == EINTR)
167                                 goto retry;
168                         return -1;
169                 }
170
171                 for (nh = (struct nlmsghdr *)buf;
172                      NLMSG_OK(nh, (unsigned int)recv_bytes);
173                      nh = NLMSG_NEXT(nh, recv_bytes)) {
174                         if (nh->nlmsg_type == NLMSG_ERROR) {
175                                 struct nlmsgerr *err_data = NLMSG_DATA(nh);
176
177                                 if (err_data->error < 0) {
178                                         errno = -err_data->error;
179                                         return -1;
180                                 }
181                                 /* Ack message. */
182                                 return 0;
183                         }
184                         /* Multi-part msgs and their trailing DONE message. */
185                         if (nh->nlmsg_flags & NLM_F_MULTI) {
186                                 if (nh->nlmsg_type == NLMSG_DONE)
187                                         return 0;
188                                 multipart = 1;
189                         }
190                         if (cb)
191                                 ret = cb(nh, arg);
192                 }
193         } while (multipart);
194         return ret;
195 }
196
197 /**
198  * Append a netlink attribute to a message.
199  *
200  * @param[in, out] nh
201  *   The netlink message to parse, received from the kernel.
202  * @param[in] type
203  *   The type of attribute to append.
204  * @param[in] data_len
205  *   The length of the data to append.
206  * @param[in] data
207  *   The data to append.
208  */
209 void
210 tap_nlattr_add(struct nlmsghdr *nh, unsigned short type,
211            unsigned int data_len, const void *data)
212 {
213         /* see man 3 rtnetlink */
214         struct rtattr *rta;
215
216         rta = (struct rtattr *)NLMSG_TAIL(nh);
217         rta->rta_len = RTA_LENGTH(data_len);
218         rta->rta_type = type;
219         memcpy(RTA_DATA(rta), data, data_len);
220         nh->nlmsg_len = NLMSG_ALIGN(nh->nlmsg_len) + RTA_ALIGN(rta->rta_len);
221 }
222
223 /**
224  * Append a uint8_t netlink attribute to a message.
225  *
226  * @param[in, out] nh
227  *   The netlink message to parse, received from the kernel.
228  * @param[in] type
229  *   The type of attribute to append.
230  * @param[in] data
231  *   The data to append.
232  */
233 void
234 tap_nlattr_add8(struct nlmsghdr *nh, unsigned short type, uint8_t data)
235 {
236         tap_nlattr_add(nh, type, sizeof(uint8_t), &data);
237 }
238
239 /**
240  * Append a uint16_t netlink attribute to a message.
241  *
242  * @param[in, out] nh
243  *   The netlink message to parse, received from the kernel.
244  * @param[in] type
245  *   The type of attribute to append.
246  * @param[in] data
247  *   The data to append.
248  */
249 void
250 tap_nlattr_add16(struct nlmsghdr *nh, unsigned short type, uint16_t data)
251 {
252         tap_nlattr_add(nh, type, sizeof(uint16_t), &data);
253 }
254
255 /**
256  * Append a uint16_t netlink attribute to a message.
257  *
258  * @param[in, out] nh
259  *   The netlink message to parse, received from the kernel.
260  * @param[in] type
261  *   The type of attribute to append.
262  * @param[in] data
263  *   The data to append.
264  */
265 void
266 tap_nlattr_add32(struct nlmsghdr *nh, unsigned short type, uint32_t data)
267 {
268         tap_nlattr_add(nh, type, sizeof(uint32_t), &data);
269 }
270
271 /**
272  * Start a nested netlink attribute.
273  * It must be followed later by a call to tap_nlattr_nested_finish().
274  *
275  * @param[in, out] msg
276  *   The netlink message where to edit the nested_tails metadata.
277  * @param[in] type
278  *   The nested attribute type to append.
279  *
280  * @return
281  *   -1 if adding a nested netlink attribute failed, 0 otherwise.
282  */
283 int
284 tap_nlattr_nested_start(struct nlmsg *msg, uint16_t type)
285 {
286         struct nested_tail *tail;
287
288         tail = rte_zmalloc(NULL, sizeof(struct nested_tail), 0);
289         if (!tail) {
290                 TAP_LOG(ERR,
291                         "Couldn't allocate memory for nested netlink attribute");
292                 return -1;
293         }
294
295         tail->tail = (struct rtattr *)NLMSG_TAIL(&msg->nh);
296
297         tap_nlattr_add(&msg->nh, type, 0, NULL);
298
299         tail->prev = msg->nested_tails;
300
301         msg->nested_tails = tail;
302
303         return 0;
304 }
305
306 /**
307  * End a nested netlink attribute.
308  * It follows a call to tap_nlattr_nested_start().
309  * In effect, it will modify the nested attribute length to include every bytes
310  * from the nested attribute start, up to here.
311  *
312  * @param[in, out] msg
313  *   The netlink message where to edit the nested_tails metadata.
314  */
315 void
316 tap_nlattr_nested_finish(struct nlmsg *msg)
317 {
318         struct nested_tail *tail = msg->nested_tails;
319
320         tail->tail->rta_len = (char *)NLMSG_TAIL(&msg->nh) - (char *)tail->tail;
321
322         if (tail->prev)
323                 msg->nested_tails = tail->prev;
324
325         rte_free(tail);
326 }