New upstream version 18.08
[deb_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                 return -1;
55         }
56         if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(int))) {
57                 TAP_LOG(ERR, "Unable to set socket buffer receive size");
58                 return -1;
59         }
60         if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
61                 TAP_LOG(ERR, "Unable to bind to the netlink socket");
62                 return -1;
63         }
64         return fd;
65 }
66
67 /**
68  * Clean up a netlink socket once all communicating with the kernel is finished.
69  *
70  * @param[in] nlsk_fd
71  *   The netlink socket file descriptor used for communication.
72  *
73  * @return
74  *   0 on success, -1 otherwise.
75  */
76 int
77 tap_nl_final(int nlsk_fd)
78 {
79         if (close(nlsk_fd)) {
80                 TAP_LOG(ERR, "Failed to close netlink socket: %s (%d)",
81                         strerror(errno), errno);
82                 return -1;
83         }
84         return 0;
85 }
86
87 /**
88  * Send a message to the kernel on the netlink socket.
89  *
90  * @param[in] nlsk_fd
91  *   The netlink socket file descriptor used for communication.
92  * @param[in] nh
93  *   The netlink message send to the kernel.
94  *
95  * @return
96  *   the number of sent bytes on success, -1 otherwise.
97  */
98 int
99 tap_nl_send(int nlsk_fd, struct nlmsghdr *nh)
100 {
101         /* man 7 netlink EXAMPLE */
102         struct sockaddr_nl sa = {
103                 .nl_family = AF_NETLINK,
104         };
105         struct iovec iov = {
106                 .iov_base = nh,
107                 .iov_len = nh->nlmsg_len,
108         };
109         struct msghdr msg = {
110                 .msg_name = &sa,
111                 .msg_namelen = sizeof(sa),
112                 .msg_iov = &iov,
113                 .msg_iovlen = 1,
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         send_bytes = sendmsg(nlsk_fd, &msg, 0);
120         if (send_bytes < 0) {
121                 TAP_LOG(ERR, "Failed to send netlink message: %s (%d)",
122                         strerror(errno), errno);
123                 return -1;
124         }
125         return send_bytes;
126 }
127
128 /**
129  * Check that the kernel sends an appropriate ACK in response
130  * to an tap_nl_send().
131  *
132  * @param[in] nlsk_fd
133  *   The netlink socket file descriptor used for communication.
134  *
135  * @return
136  *   0 on success, -1 otherwise with errno set.
137  */
138 int
139 tap_nl_recv_ack(int nlsk_fd)
140 {
141         return tap_nl_recv(nlsk_fd, NULL, NULL);
142 }
143
144 /**
145  * Receive a message from the kernel on the netlink socket, following an
146  * tap_nl_send().
147  *
148  * @param[in] nlsk_fd
149  *   The netlink socket file descriptor used for communication.
150  * @param[in] cb
151  *   The callback function to call for each netlink message received.
152  * @param[in, out] arg
153  *   Custom arguments for the callback.
154  *
155  * @return
156  *   0 on success, -1 otherwise with errno set.
157  */
158 int
159 tap_nl_recv(int nlsk_fd, int (*cb)(struct nlmsghdr *, void *arg), void *arg)
160 {
161         /* man 7 netlink EXAMPLE */
162         struct sockaddr_nl sa;
163         char buf[BUF_SIZE];
164         struct iovec iov = {
165                 .iov_base = buf,
166                 .iov_len = sizeof(buf),
167         };
168         struct msghdr msg = {
169                 .msg_name = &sa,
170                 .msg_namelen = sizeof(sa),
171                 .msg_iov = &iov,
172                 /* One message at a time */
173                 .msg_iovlen = 1,
174         };
175         int multipart = 0;
176         int ret = 0;
177
178         do {
179                 struct nlmsghdr *nh;
180                 int recv_bytes = 0;
181
182                 recv_bytes = recvmsg(nlsk_fd, &msg, 0);
183                 if (recv_bytes < 0)
184                         return -1;
185                 for (nh = (struct nlmsghdr *)buf;
186                      NLMSG_OK(nh, (unsigned int)recv_bytes);
187                      nh = NLMSG_NEXT(nh, recv_bytes)) {
188                         if (nh->nlmsg_type == NLMSG_ERROR) {
189                                 struct nlmsgerr *err_data = NLMSG_DATA(nh);
190
191                                 if (err_data->error < 0) {
192                                         errno = -err_data->error;
193                                         return -1;
194                                 }
195                                 /* Ack message. */
196                                 return 0;
197                         }
198                         /* Multi-part msgs and their trailing DONE message. */
199                         if (nh->nlmsg_flags & NLM_F_MULTI) {
200                                 if (nh->nlmsg_type == NLMSG_DONE)
201                                         return 0;
202                                 multipart = 1;
203                         }
204                         if (cb)
205                                 ret = cb(nh, arg);
206                 }
207         } while (multipart);
208         return ret;
209 }
210
211 /**
212  * Append a netlink attribute to a message.
213  *
214  * @param[in, out] nh
215  *   The netlink message to parse, received from the kernel.
216  * @param[in] type
217  *   The type of attribute to append.
218  * @param[in] data_len
219  *   The length of the data to append.
220  * @param[in] data
221  *   The data to append.
222  */
223 void
224 tap_nlattr_add(struct nlmsghdr *nh, unsigned short type,
225            unsigned int data_len, const void *data)
226 {
227         /* see man 3 rtnetlink */
228         struct rtattr *rta;
229
230         rta = (struct rtattr *)NLMSG_TAIL(nh);
231         rta->rta_len = RTA_LENGTH(data_len);
232         rta->rta_type = type;
233         memcpy(RTA_DATA(rta), data, data_len);
234         nh->nlmsg_len = NLMSG_ALIGN(nh->nlmsg_len) + RTA_ALIGN(rta->rta_len);
235 }
236
237 /**
238  * Append a uint8_t netlink attribute to a message.
239  *
240  * @param[in, out] nh
241  *   The netlink message to parse, received from the kernel.
242  * @param[in] type
243  *   The type of attribute to append.
244  * @param[in] data
245  *   The data to append.
246  */
247 void
248 tap_nlattr_add8(struct nlmsghdr *nh, unsigned short type, uint8_t data)
249 {
250         tap_nlattr_add(nh, type, sizeof(uint8_t), &data);
251 }
252
253 /**
254  * Append a uint16_t netlink attribute to a message.
255  *
256  * @param[in, out] nh
257  *   The netlink message to parse, received from the kernel.
258  * @param[in] type
259  *   The type of attribute to append.
260  * @param[in] data
261  *   The data to append.
262  */
263 void
264 tap_nlattr_add16(struct nlmsghdr *nh, unsigned short type, uint16_t data)
265 {
266         tap_nlattr_add(nh, type, sizeof(uint16_t), &data);
267 }
268
269 /**
270  * Append a uint16_t netlink attribute to a message.
271  *
272  * @param[in, out] nh
273  *   The netlink message to parse, received from the kernel.
274  * @param[in] type
275  *   The type of attribute to append.
276  * @param[in] data
277  *   The data to append.
278  */
279 void
280 tap_nlattr_add32(struct nlmsghdr *nh, unsigned short type, uint32_t data)
281 {
282         tap_nlattr_add(nh, type, sizeof(uint32_t), &data);
283 }
284
285 /**
286  * Start a nested netlink attribute.
287  * It must be followed later by a call to tap_nlattr_nested_finish().
288  *
289  * @param[in, out] msg
290  *   The netlink message where to edit the nested_tails metadata.
291  * @param[in] type
292  *   The nested attribute type to append.
293  *
294  * @return
295  *   -1 if adding a nested netlink attribute failed, 0 otherwise.
296  */
297 int
298 tap_nlattr_nested_start(struct nlmsg *msg, uint16_t type)
299 {
300         struct nested_tail *tail;
301
302         tail = rte_zmalloc(NULL, sizeof(struct nested_tail), 0);
303         if (!tail) {
304                 TAP_LOG(ERR,
305                         "Couldn't allocate memory for nested netlink attribute");
306                 return -1;
307         }
308
309         tail->tail = (struct rtattr *)NLMSG_TAIL(&msg->nh);
310
311         tap_nlattr_add(&msg->nh, type, 0, NULL);
312
313         tail->prev = msg->nested_tails;
314
315         msg->nested_tails = tail;
316
317         return 0;
318 }
319
320 /**
321  * End a nested netlink attribute.
322  * It follows a call to tap_nlattr_nested_start().
323  * In effect, it will modify the nested attribute length to include every bytes
324  * from the nested attribute start, up to here.
325  *
326  * @param[in, out] msg
327  *   The netlink message where to edit the nested_tails metadata.
328  */
329 void
330 tap_nlattr_nested_finish(struct nlmsg *msg)
331 {
332         struct nested_tail *tail = msg->nested_tails;
333
334         tail->tail->rta_len = (char *)NLMSG_TAIL(&msg->nh) - (char *)tail->tail;
335
336         if (tail->prev)
337                 msg->nested_tails = tail->prev;
338
339         rte_free(tail);
340 }