b73802b51e4c6f9a59214500ef5bd886e9d8fe11
[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 #include <stdbool.h>
13
14 #include <rte_malloc.h>
15 #include <tap_netlink.h>
16 #include <rte_random.h>
17
18 #include "tap_log.h"
19
20 /* Must be quite large to support dumping a huge list of QDISC or filters. */
21 #define BUF_SIZE (32 * 1024) /* Size of the buffer to receive kernel messages */
22 #define SNDBUF_SIZE 32768 /* Send buffer size for the netlink socket */
23 #define RCVBUF_SIZE 32768 /* Receive buffer size for the netlink socket */
24
25 struct nested_tail {
26         struct rtattr *tail;
27         struct nested_tail *prev;
28 };
29
30 /**
31  * Initialize a netlink socket for communicating with the kernel.
32  *
33  * @param nl_groups
34  *   Set it to a netlink group value (e.g. RTMGRP_LINK) to receive messages for
35  *   specific netlink multicast groups. Otherwise, no subscription will be made.
36  *
37  * @return
38  *   netlink socket file descriptor on success, -1 otherwise.
39  */
40 int
41 tap_nl_init(uint32_t nl_groups)
42 {
43         int fd, sndbuf_size = SNDBUF_SIZE, rcvbuf_size = RCVBUF_SIZE;
44         struct sockaddr_nl local = {
45                 .nl_family = AF_NETLINK,
46                 .nl_groups = nl_groups,
47         };
48 #ifdef NETLINK_EXT_ACK
49         int one = 1;
50 #endif
51
52         fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
53         if (fd < 0) {
54                 TAP_LOG(ERR, "Unable to create a netlink socket");
55                 return -1;
56         }
57         if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(int))) {
58                 TAP_LOG(ERR, "Unable to set socket buffer send size");
59                 close(fd);
60                 return -1;
61         }
62         if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(int))) {
63                 TAP_LOG(ERR, "Unable to set socket buffer receive size");
64                 close(fd);
65                 return -1;
66         }
67
68 #ifdef NETLINK_EXT_ACK
69         /* Ask for extended ACK response. on older kernel will ignore request. */
70         setsockopt(fd, SOL_NETLINK, NETLINK_EXT_ACK, &one, sizeof(one));
71 #endif
72
73         if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
74                 TAP_LOG(ERR, "Unable to bind to the netlink socket");
75                 close(fd);
76                 return -1;
77         }
78         return fd;
79 }
80
81 /**
82  * Clean up a netlink socket once all communicating with the kernel is finished.
83  *
84  * @param[in] nlsk_fd
85  *   The netlink socket file descriptor used for communication.
86  *
87  * @return
88  *   0 on success, -1 otherwise.
89  */
90 int
91 tap_nl_final(int nlsk_fd)
92 {
93         if (close(nlsk_fd)) {
94                 TAP_LOG(ERR, "Failed to close netlink socket: %s (%d)",
95                         strerror(errno), errno);
96                 return -1;
97         }
98         return 0;
99 }
100
101 /**
102  * Send a message to the kernel on the netlink socket.
103  *
104  * @param[in] nlsk_fd
105  *   The netlink socket file descriptor used for communication.
106  * @param[in] nh
107  *   The netlink message send to the kernel.
108  *
109  * @return
110  *   the number of sent bytes on success, -1 otherwise.
111  */
112 int
113 tap_nl_send(int nlsk_fd, struct nlmsghdr *nh)
114 {
115         int send_bytes;
116
117         nh->nlmsg_pid = 0; /* communication with the kernel uses pid 0 */
118         nh->nlmsg_seq = (uint32_t)rte_rand();
119
120 retry:
121         send_bytes = send(nlsk_fd, nh, nh->nlmsg_len, 0);
122         if (send_bytes < 0) {
123                 if (errno == EINTR)
124                         goto retry;
125
126                 TAP_LOG(ERR, "Failed to send netlink message: %s (%d)",
127                         strerror(errno), errno);
128                 return -1;
129         }
130         return send_bytes;
131 }
132
133 #ifdef NETLINK_EXT_ACK
134 static const struct nlattr *
135 tap_nl_attr_first(const struct nlmsghdr *nh, size_t offset)
136 {
137         return (const struct nlattr *)((const char *)nh + NLMSG_SPACE(offset));
138 }
139
140 static const struct nlattr *
141 tap_nl_attr_next(const struct nlattr *attr)
142 {
143         return (const struct nlattr *)((const char *)attr
144                                        + NLMSG_ALIGN(attr->nla_len));
145 }
146
147 static bool
148 tap_nl_attr_ok(const struct nlattr *attr, int len)
149 {
150         if (len < (int)sizeof(struct nlattr))
151                 return false; /* missing header */
152         if (attr->nla_len < sizeof(struct nlattr))
153                 return false; /* attribute length should include itself */
154         if ((int)attr->nla_len  > len)
155                 return false; /* attribute is truncated */
156         return true;
157 }
158
159
160 /* Decode extended errors from kernel */
161 static void
162 tap_nl_dump_ext_ack(const struct nlmsghdr *nh, const struct nlmsgerr *err)
163 {
164         const struct nlattr *attr;
165         const char *tail = (const char *)nh + NLMSG_ALIGN(nh->nlmsg_len);
166         size_t hlen = sizeof(*err);
167
168         /* no TLVs, no extended response */
169         if (!(nh->nlmsg_flags & NLM_F_ACK_TLVS))
170                 return;
171
172         if (!(nh->nlmsg_flags & NLM_F_CAPPED))
173                 hlen += err->msg.nlmsg_len - NLMSG_HDRLEN;
174
175         for (attr = tap_nl_attr_first(nh, hlen);
176              tap_nl_attr_ok(attr, tail - (const char *)attr);
177              attr = tap_nl_attr_next(attr)) {
178                 uint16_t type = attr->nla_type & NLA_TYPE_MASK;
179
180                 if (type == NLMSGERR_ATTR_MSG) {
181                         const char *msg = (const char *)attr
182                                 + NLMSG_ALIGN(sizeof(*attr));
183
184                         if (err->error)
185                                 TAP_LOG(ERR, "%s", msg);
186                         else
187
188                                 TAP_LOG(WARNING, "%s", msg);
189                         break;
190                 }
191         }
192 }
193 #else
194 /*
195  * External ACK support was added in Linux kernel 4.17
196  * on older kernels, just ignore that part of message
197  */
198 #define tap_nl_dump_ext_ack(nh, err) do { } while (0)
199 #endif
200
201 /**
202  * Check that the kernel sends an appropriate ACK in response
203  * to an tap_nl_send().
204  *
205  * @param[in] nlsk_fd
206  *   The netlink socket file descriptor used for communication.
207  *
208  * @return
209  *   0 on success, -1 otherwise with errno set.
210  */
211 int
212 tap_nl_recv_ack(int nlsk_fd)
213 {
214         return tap_nl_recv(nlsk_fd, NULL, NULL);
215 }
216
217 /**
218  * Receive a message from the kernel on the netlink socket, following an
219  * tap_nl_send().
220  *
221  * @param[in] nlsk_fd
222  *   The netlink socket file descriptor used for communication.
223  * @param[in] cb
224  *   The callback function to call for each netlink message received.
225  * @param[in, out] arg
226  *   Custom arguments for the callback.
227  *
228  * @return
229  *   0 on success, -1 otherwise with errno set.
230  */
231 int
232 tap_nl_recv(int nlsk_fd, int (*cb)(struct nlmsghdr *, void *arg), void *arg)
233 {
234         char buf[BUF_SIZE];
235         int multipart = 0;
236         int ret = 0;
237
238         do {
239                 struct nlmsghdr *nh;
240                 int recv_bytes;
241
242 retry:
243                 recv_bytes = recv(nlsk_fd, buf, sizeof(buf), 0);
244                 if (recv_bytes < 0) {
245                         if (errno == EINTR)
246                                 goto retry;
247                         return -1;
248                 }
249
250                 for (nh = (struct nlmsghdr *)buf;
251                      NLMSG_OK(nh, (unsigned int)recv_bytes);
252                      nh = NLMSG_NEXT(nh, recv_bytes)) {
253                         if (nh->nlmsg_type == NLMSG_ERROR) {
254                                 struct nlmsgerr *err_data = NLMSG_DATA(nh);
255
256                                 tap_nl_dump_ext_ack(nh, err_data);
257                                 if (err_data->error < 0) {
258                                         errno = -err_data->error;
259                                         return -1;
260                                 }
261                                 /* Ack message. */
262                                 return 0;
263                         }
264                         /* Multi-part msgs and their trailing DONE message. */
265                         if (nh->nlmsg_flags & NLM_F_MULTI) {
266                                 if (nh->nlmsg_type == NLMSG_DONE)
267                                         return 0;
268                                 multipart = 1;
269                         }
270                         if (cb)
271                                 ret = cb(nh, arg);
272                 }
273         } while (multipart);
274         return ret;
275 }
276
277 /**
278  * Append a netlink attribute to a message.
279  *
280  * @param[in, out] nh
281  *   The netlink message to parse, received from the kernel.
282  * @param[in] type
283  *   The type of attribute to append.
284  * @param[in] data_len
285  *   The length of the data to append.
286  * @param[in] data
287  *   The data to append.
288  */
289 void
290 tap_nlattr_add(struct nlmsghdr *nh, unsigned short type,
291            unsigned int data_len, const void *data)
292 {
293         /* see man 3 rtnetlink */
294         struct rtattr *rta;
295
296         rta = (struct rtattr *)NLMSG_TAIL(nh);
297         rta->rta_len = RTA_LENGTH(data_len);
298         rta->rta_type = type;
299         memcpy(RTA_DATA(rta), data, data_len);
300         nh->nlmsg_len = NLMSG_ALIGN(nh->nlmsg_len) + RTA_ALIGN(rta->rta_len);
301 }
302
303 /**
304  * Append a uint8_t netlink attribute to a message.
305  *
306  * @param[in, out] nh
307  *   The netlink message to parse, received from the kernel.
308  * @param[in] type
309  *   The type of attribute to append.
310  * @param[in] data
311  *   The data to append.
312  */
313 void
314 tap_nlattr_add8(struct nlmsghdr *nh, unsigned short type, uint8_t data)
315 {
316         tap_nlattr_add(nh, type, sizeof(uint8_t), &data);
317 }
318
319 /**
320  * Append a uint16_t netlink attribute to a message.
321  *
322  * @param[in, out] nh
323  *   The netlink message to parse, received from the kernel.
324  * @param[in] type
325  *   The type of attribute to append.
326  * @param[in] data
327  *   The data to append.
328  */
329 void
330 tap_nlattr_add16(struct nlmsghdr *nh, unsigned short type, uint16_t data)
331 {
332         tap_nlattr_add(nh, type, sizeof(uint16_t), &data);
333 }
334
335 /**
336  * Append a uint16_t netlink attribute to a message.
337  *
338  * @param[in, out] nh
339  *   The netlink message to parse, received from the kernel.
340  * @param[in] type
341  *   The type of attribute to append.
342  * @param[in] data
343  *   The data to append.
344  */
345 void
346 tap_nlattr_add32(struct nlmsghdr *nh, unsigned short type, uint32_t data)
347 {
348         tap_nlattr_add(nh, type, sizeof(uint32_t), &data);
349 }
350
351 /**
352  * Start a nested netlink attribute.
353  * It must be followed later by a call to tap_nlattr_nested_finish().
354  *
355  * @param[in, out] msg
356  *   The netlink message where to edit the nested_tails metadata.
357  * @param[in] type
358  *   The nested attribute type to append.
359  *
360  * @return
361  *   -1 if adding a nested netlink attribute failed, 0 otherwise.
362  */
363 int
364 tap_nlattr_nested_start(struct nlmsg *msg, uint16_t type)
365 {
366         struct nested_tail *tail;
367
368         tail = rte_zmalloc(NULL, sizeof(struct nested_tail), 0);
369         if (!tail) {
370                 TAP_LOG(ERR,
371                         "Couldn't allocate memory for nested netlink attribute");
372                 return -1;
373         }
374
375         tail->tail = (struct rtattr *)NLMSG_TAIL(&msg->nh);
376
377         tap_nlattr_add(&msg->nh, type, 0, NULL);
378
379         tail->prev = msg->nested_tails;
380
381         msg->nested_tails = tail;
382
383         return 0;
384 }
385
386 /**
387  * End a nested netlink attribute.
388  * It follows a call to tap_nlattr_nested_start().
389  * In effect, it will modify the nested attribute length to include every bytes
390  * from the nested attribute start, up to here.
391  *
392  * @param[in, out] msg
393  *   The netlink message where to edit the nested_tails metadata.
394  */
395 void
396 tap_nlattr_nested_finish(struct nlmsg *msg)
397 {
398         struct nested_tail *tail = msg->nested_tails;
399
400         tail->tail->rta_len = (char *)NLMSG_TAIL(&msg->nh) - (char *)tail->tail;
401
402         if (tail->prev)
403                 msg->nested_tails = tail->prev;
404
405         rte_free(tail);
406 }