net/mlx5: fix socket connection return value
[dpdk.git] / drivers / net / mlx5 / mlx5_socket.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016 6WIND S.A.
3  * Copyright 2016 Mellanox Technologies, Ltd
4  */
5
6 #define _GNU_SOURCE
7
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <sys/un.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <sys/stat.h>
15
16 #include "mlx5.h"
17 #include "mlx5_utils.h"
18
19 /**
20  * Initialise the socket to communicate with the secondary process
21  *
22  * @param[in] dev
23  *   Pointer to Ethernet device.
24  *
25  * @return
26  *   0 on success, a negative errno value otherwise and rte_errno is set.
27  */
28 int
29 mlx5_socket_init(struct rte_eth_dev *dev)
30 {
31         struct priv *priv = dev->data->dev_private;
32         struct sockaddr_un sun = {
33                 .sun_family = AF_UNIX,
34         };
35         int ret;
36         int flags;
37
38         /*
39          * Initialise the socket to communicate with the secondary
40          * process.
41          */
42         ret = socket(AF_UNIX, SOCK_STREAM, 0);
43         if (ret < 0) {
44                 rte_errno = errno;
45                 DRV_LOG(WARNING, "port %u secondary process not supported: %s",
46                         dev->data->port_id, strerror(errno));
47                 goto error;
48         }
49         priv->primary_socket = ret;
50         flags = fcntl(priv->primary_socket, F_GETFL, 0);
51         if (flags == -1) {
52                 rte_errno = errno;
53                 goto error;
54         }
55         ret = fcntl(priv->primary_socket, F_SETFL, flags | O_NONBLOCK);
56         if (ret < 0) {
57                 rte_errno = errno;
58                 goto error;
59         }
60         snprintf(sun.sun_path, sizeof(sun.sun_path), "/var/tmp/%s_%d",
61                  MLX5_DRIVER_NAME, priv->primary_socket);
62         remove(sun.sun_path);
63         ret = bind(priv->primary_socket, (const struct sockaddr *)&sun,
64                    sizeof(sun));
65         if (ret < 0) {
66                 rte_errno = errno;
67                 DRV_LOG(WARNING,
68                         "port %u cannot bind socket, secondary process not"
69                         " supported: %s",
70                         dev->data->port_id, strerror(errno));
71                 goto close;
72         }
73         ret = listen(priv->primary_socket, 0);
74         if (ret < 0) {
75                 rte_errno = errno;
76                 DRV_LOG(WARNING, "port %u secondary process not supported: %s",
77                         dev->data->port_id, strerror(errno));
78                 goto close;
79         }
80         return 0;
81 close:
82         remove(sun.sun_path);
83 error:
84         claim_zero(close(priv->primary_socket));
85         priv->primary_socket = 0;
86         return -rte_errno;
87 }
88
89 /**
90  * Un-Initialise the socket to communicate with the secondary process
91  *
92  * @param[in] dev
93  */
94 void
95 mlx5_socket_uninit(struct rte_eth_dev *dev)
96 {
97         struct priv *priv = dev->data->dev_private;
98
99         MKSTR(path, "/var/tmp/%s_%d", MLX5_DRIVER_NAME, priv->primary_socket);
100         claim_zero(close(priv->primary_socket));
101         priv->primary_socket = 0;
102         claim_zero(remove(path));
103 }
104
105 /**
106  * Handle socket interrupts.
107  *
108  * @param dev
109  *   Pointer to Ethernet device.
110  */
111 void
112 mlx5_socket_handle(struct rte_eth_dev *dev)
113 {
114         struct priv *priv = dev->data->dev_private;
115         int conn_sock;
116         int ret = 0;
117         struct cmsghdr *cmsg = NULL;
118         struct ucred *cred = NULL;
119         char buf[CMSG_SPACE(sizeof(struct ucred))] = { 0 };
120         char vbuf[1024] = { 0 };
121         struct iovec io = {
122                 .iov_base = vbuf,
123                 .iov_len = sizeof(*vbuf),
124         };
125         struct msghdr msg = {
126                 .msg_iov = &io,
127                 .msg_iovlen = 1,
128                 .msg_control = buf,
129                 .msg_controllen = sizeof(buf),
130         };
131         int *fd;
132
133         /* Accept the connection from the client. */
134         conn_sock = accept(priv->primary_socket, NULL, NULL);
135         if (conn_sock < 0) {
136                 DRV_LOG(WARNING, "port %u connection failed: %s",
137                         dev->data->port_id, strerror(errno));
138                 return;
139         }
140         ret = setsockopt(conn_sock, SOL_SOCKET, SO_PASSCRED, &(int){1},
141                                          sizeof(int));
142         if (ret < 0) {
143                 ret = errno;
144                 DRV_LOG(WARNING, "port %u cannot change socket options: %s",
145                         dev->data->port_id, strerror(rte_errno));
146                 goto error;
147         }
148         ret = recvmsg(conn_sock, &msg, MSG_WAITALL);
149         if (ret < 0) {
150                 ret = errno;
151                 DRV_LOG(WARNING, "port %u received an empty message: %s",
152                         dev->data->port_id, strerror(rte_errno));
153                 goto error;
154         }
155         /* Expect to receive credentials only. */
156         cmsg = CMSG_FIRSTHDR(&msg);
157         if (cmsg == NULL) {
158                 DRV_LOG(WARNING, "port %u no message", dev->data->port_id);
159                 goto error;
160         }
161         if ((cmsg->cmsg_type == SCM_CREDENTIALS) &&
162                 (cmsg->cmsg_len >= sizeof(*cred))) {
163                 cred = (struct ucred *)CMSG_DATA(cmsg);
164                 assert(cred != NULL);
165         }
166         cmsg = CMSG_NXTHDR(&msg, cmsg);
167         if (cmsg != NULL) {
168                 DRV_LOG(WARNING, "port %u message wrongly formatted",
169                         dev->data->port_id);
170                 goto error;
171         }
172         /* Make sure all the ancillary data was received and valid. */
173         if ((cred == NULL) || (cred->uid != getuid()) ||
174             (cred->gid != getgid())) {
175                 DRV_LOG(WARNING, "port %u wrong credentials",
176                         dev->data->port_id);
177                 goto error;
178         }
179         /* Set-up the ancillary data. */
180         cmsg = CMSG_FIRSTHDR(&msg);
181         assert(cmsg != NULL);
182         cmsg->cmsg_level = SOL_SOCKET;
183         cmsg->cmsg_type = SCM_RIGHTS;
184         cmsg->cmsg_len = CMSG_LEN(sizeof(priv->ctx->cmd_fd));
185         fd = (int *)CMSG_DATA(cmsg);
186         *fd = priv->ctx->cmd_fd;
187         ret = sendmsg(conn_sock, &msg, 0);
188         if (ret < 0)
189                 DRV_LOG(WARNING, "port %u cannot send response",
190                         dev->data->port_id);
191 error:
192         close(conn_sock);
193 }
194
195 /**
196  * Connect to the primary process.
197  *
198  * @param[in] dev
199  *   Pointer to Ethernet structure.
200  *
201  * @return
202  *   fd on success, negative errno value otherwise and rte_errno is set.
203  */
204 int
205 mlx5_socket_connect(struct rte_eth_dev *dev)
206 {
207         struct priv *priv = dev->data->dev_private;
208         struct sockaddr_un sun = {
209                 .sun_family = AF_UNIX,
210         };
211         int socket_fd = -1;
212         int *fd = NULL;
213         int ret;
214         struct ucred *cred;
215         char buf[CMSG_SPACE(sizeof(*cred))] = { 0 };
216         char vbuf[1024] = { 0 };
217         struct iovec io = {
218                 .iov_base = vbuf,
219                 .iov_len = sizeof(*vbuf),
220         };
221         struct msghdr msg = {
222                 .msg_control = buf,
223                 .msg_controllen = sizeof(buf),
224                 .msg_iov = &io,
225                 .msg_iovlen = 1,
226         };
227         struct cmsghdr *cmsg;
228
229         ret = socket(AF_UNIX, SOCK_STREAM, 0);
230         if (ret < 0) {
231                 rte_errno = errno;
232                 DRV_LOG(WARNING, "port %u cannot connect to primary",
233                         dev->data->port_id);
234                 goto error;
235         }
236         socket_fd = ret;
237         snprintf(sun.sun_path, sizeof(sun.sun_path), "/var/tmp/%s_%d",
238                  MLX5_DRIVER_NAME, priv->primary_socket);
239         ret = connect(socket_fd, (const struct sockaddr *)&sun, sizeof(sun));
240         if (ret < 0) {
241                 rte_errno = errno;
242                 DRV_LOG(WARNING, "port %u cannot connect to primary",
243                         dev->data->port_id);
244                 goto error;
245         }
246         cmsg = CMSG_FIRSTHDR(&msg);
247         if (cmsg == NULL) {
248                 rte_errno = EINVAL;
249                 DRV_LOG(DEBUG, "port %u cannot get first message",
250                         dev->data->port_id);
251                 goto error;
252         }
253         cmsg->cmsg_level = SOL_SOCKET;
254         cmsg->cmsg_type = SCM_CREDENTIALS;
255         cmsg->cmsg_len = CMSG_LEN(sizeof(*cred));
256         cred = (struct ucred *)CMSG_DATA(cmsg);
257         if (cred == NULL) {
258                 rte_errno = EINVAL;
259                 DRV_LOG(DEBUG, "port %u no credentials received",
260                         dev->data->port_id);
261                 goto error;
262         }
263         cred->pid = getpid();
264         cred->uid = getuid();
265         cred->gid = getgid();
266         ret = sendmsg(socket_fd, &msg, MSG_DONTWAIT);
267         if (ret < 0) {
268                 rte_errno = errno;
269                 DRV_LOG(WARNING,
270                         "port %u cannot send credentials to primary: %s",
271                         dev->data->port_id, strerror(errno));
272                 goto error;
273         }
274         ret = recvmsg(socket_fd, &msg, MSG_WAITALL);
275         if (ret <= 0) {
276                 rte_errno = errno;
277                 DRV_LOG(WARNING, "port %u no message from primary: %s",
278                         dev->data->port_id, strerror(errno));
279                 goto error;
280         }
281         cmsg = CMSG_FIRSTHDR(&msg);
282         if (cmsg == NULL) {
283                 rte_errno = EINVAL;
284                 DRV_LOG(WARNING, "port %u no file descriptor received",
285                         dev->data->port_id);
286                 goto error;
287         }
288         fd = (int *)CMSG_DATA(cmsg);
289         if (*fd < 0) {
290                 DRV_LOG(WARNING, "port %u no file descriptor received: %s",
291                         dev->data->port_id, strerror(errno));
292                 rte_errno = *fd;
293                 goto error;
294         }
295         ret = *fd;
296         close(socket_fd);
297         return ret;
298 error:
299         if (socket_fd != -1)
300                 close(socket_fd);
301         return -rte_errno;
302 }