5cd1ab80f0e09572d70b5edf3c9f631e780f9f11
[dpdk.git] / drivers / net / mlx5 / mlx5_socket.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright 2016 6WIND S.A.
5  *
6  *   Redistribution and use in source and binary forms, with or without
7  *   modification, are permitted provided that the following conditions
8  *   are met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above copyright
13  *       notice, this list of conditions and the following disclaimer in
14  *       the documentation and/or other materials provided with the
15  *       distribution.
16  *     * Neither the name of 6WIND S.A. nor the names of its
17  *       contributors may be used to endorse or promote products derived
18  *       from this software without specific prior written permission.
19  *
20  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 #define _GNU_SOURCE
33
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <unistd.h>
40 #include <sys/stat.h>
41
42 #include "mlx5.h"
43 #include "mlx5_utils.h"
44
45 /**
46  * Initialise the socket to communicate with the secondary process
47  *
48  * @param[in] priv
49  *   Pointer to private structure.
50  *
51  * @return
52  *   0 on success, errno value on failure.
53  */
54 int
55 priv_socket_init(struct priv *priv)
56 {
57         struct sockaddr_un sun = {
58                 .sun_family = AF_UNIX,
59         };
60         int ret;
61         int flags;
62         struct stat file_stat;
63
64         /*
65          * Initialise the socket to communicate with the secondary
66          * process.
67          */
68         ret = socket(AF_UNIX, SOCK_STREAM, 0);
69         if (ret < 0) {
70                 WARN("secondary process not supported: %s", strerror(errno));
71                 return ret;
72         }
73         priv->primary_socket = ret;
74         flags = fcntl(priv->primary_socket, F_GETFL, 0);
75         if (flags == -1)
76                 goto out;
77         ret = fcntl(priv->primary_socket, F_SETFL, flags | O_NONBLOCK);
78         if (ret < 0)
79                 goto out;
80         snprintf(sun.sun_path, sizeof(sun.sun_path), "/var/tmp/%s_%d",
81                  MLX5_DRIVER_NAME, priv->primary_socket);
82         ret = stat(sun.sun_path, &file_stat);
83         if (!ret)
84                 claim_zero(remove(sun.sun_path));
85         ret = bind(priv->primary_socket, (const struct sockaddr *)&sun,
86                    sizeof(sun));
87         if (ret < 0) {
88                 WARN("cannot bind socket, secondary process not supported: %s",
89                      strerror(errno));
90                 goto close;
91         }
92         ret = listen(priv->primary_socket, 0);
93         if (ret < 0) {
94                 WARN("Secondary process not supported: %s", strerror(errno));
95                 goto close;
96         }
97         return ret;
98 close:
99         remove(sun.sun_path);
100 out:
101         claim_zero(close(priv->primary_socket));
102         priv->primary_socket = 0;
103         return -(ret);
104 }
105
106 /**
107  * Un-Initialise the socket to communicate with the secondary process
108  *
109  * @param[in] priv
110  *   Pointer to private structure.
111  *
112  * @return
113  *   0 on success, errno value on failure.
114  */
115 int
116 priv_socket_uninit(struct priv *priv)
117 {
118         MKSTR(path, "/var/tmp/%s_%d", MLX5_DRIVER_NAME, priv->primary_socket);
119         claim_zero(close(priv->primary_socket));
120         priv->primary_socket = 0;
121         claim_zero(remove(path));
122         return 0;
123 }
124
125 /**
126  * Handle socket interrupts.
127  *
128  * @param priv
129  *   Pointer to private structure.
130  */
131 void
132 priv_socket_handle(struct priv *priv)
133 {
134         int conn_sock;
135         int ret = 0;
136         struct cmsghdr *cmsg = NULL;
137         struct ucred *cred = NULL;
138         char buf[CMSG_SPACE(sizeof(struct ucred))] = { 0 };
139         char vbuf[1024] = { 0 };
140         struct iovec io = {
141                 .iov_base = vbuf,
142                 .iov_len = sizeof(*vbuf),
143         };
144         struct msghdr msg = {
145                 .msg_iov = &io,
146                 .msg_iovlen = 1,
147                 .msg_control = buf,
148                 .msg_controllen = sizeof(buf),
149         };
150         int *fd;
151
152         /* Accept the connection from the client. */
153         conn_sock = accept(priv->primary_socket, NULL, NULL);
154         if (conn_sock < 0) {
155                 WARN("connection failed: %s", strerror(errno));
156                 return;
157         }
158         ret = setsockopt(conn_sock, SOL_SOCKET, SO_PASSCRED, &(int){1},
159                                          sizeof(int));
160         if (ret < 0) {
161                 WARN("cannot change socket options");
162                 goto out;
163         }
164         ret = recvmsg(conn_sock, &msg, MSG_WAITALL);
165         if (ret < 0) {
166                 WARN("received an empty message: %s", strerror(errno));
167                 goto out;
168         }
169         /* Expect to receive credentials only. */
170         cmsg = CMSG_FIRSTHDR(&msg);
171         if (cmsg == NULL) {
172                 WARN("no message");
173                 goto out;
174         }
175         if ((cmsg->cmsg_type == SCM_CREDENTIALS) &&
176                 (cmsg->cmsg_len >= sizeof(*cred))) {
177                 cred = (struct ucred *)CMSG_DATA(cmsg);
178                 assert(cred != NULL);
179         }
180         cmsg = CMSG_NXTHDR(&msg, cmsg);
181         if (cmsg != NULL) {
182                 WARN("Message wrongly formatted");
183                 goto out;
184         }
185         /* Make sure all the ancillary data was received and valid. */
186         if ((cred == NULL) || (cred->uid != getuid()) ||
187             (cred->gid != getgid())) {
188                 WARN("wrong credentials");
189                 goto out;
190         }
191         /* Set-up the ancillary data. */
192         cmsg = CMSG_FIRSTHDR(&msg);
193         assert(cmsg != NULL);
194         cmsg->cmsg_level = SOL_SOCKET;
195         cmsg->cmsg_type = SCM_RIGHTS;
196         cmsg->cmsg_len = CMSG_LEN(sizeof(priv->ctx->cmd_fd));
197         fd = (int *)CMSG_DATA(cmsg);
198         *fd = priv->ctx->cmd_fd;
199         ret = sendmsg(conn_sock, &msg, 0);
200         if (ret < 0)
201                 WARN("cannot send response");
202 out:
203         close(conn_sock);
204 }
205
206 /**
207  * Connect to the primary process.
208  *
209  * @param[in] priv
210  *   Pointer to private structure.
211  *
212  * @return
213  *   fd on success, negative errno value on failure.
214  */
215 int
216 priv_socket_connect(struct priv *priv)
217 {
218         struct sockaddr_un sun = {
219                 .sun_family = AF_UNIX,
220         };
221         int socket_fd;
222         int *fd = NULL;
223         int ret;
224         struct ucred *cred;
225         char buf[CMSG_SPACE(sizeof(*cred))] = { 0 };
226         char vbuf[1024] = { 0 };
227         struct iovec io = {
228                 .iov_base = vbuf,
229                 .iov_len = sizeof(*vbuf),
230         };
231         struct msghdr msg = {
232                 .msg_control = buf,
233                 .msg_controllen = sizeof(buf),
234                 .msg_iov = &io,
235                 .msg_iovlen = 1,
236         };
237         struct cmsghdr *cmsg;
238
239         ret = socket(AF_UNIX, SOCK_STREAM, 0);
240         if (ret < 0) {
241                 WARN("cannot connect to primary");
242                 return ret;
243         }
244         socket_fd = ret;
245         snprintf(sun.sun_path, sizeof(sun.sun_path), "/var/tmp/%s_%d",
246                  MLX5_DRIVER_NAME, priv->primary_socket);
247         ret = connect(socket_fd, (const struct sockaddr *)&sun, sizeof(sun));
248         if (ret < 0) {
249                 WARN("cannot connect to primary");
250                 goto out;
251         }
252         cmsg = CMSG_FIRSTHDR(&msg);
253         if (cmsg == NULL) {
254                 DEBUG("cannot get first message");
255                 goto out;
256         }
257         cmsg->cmsg_level = SOL_SOCKET;
258         cmsg->cmsg_type = SCM_CREDENTIALS;
259         cmsg->cmsg_len = CMSG_LEN(sizeof(*cred));
260         cred = (struct ucred *)CMSG_DATA(cmsg);
261         if (cred == NULL) {
262                 DEBUG("no credentials received");
263                 goto out;
264         }
265         cred->pid = getpid();
266         cred->uid = getuid();
267         cred->gid = getgid();
268         ret = sendmsg(socket_fd, &msg, MSG_DONTWAIT);
269         if (ret < 0) {
270                 WARN("cannot send credentials to primary: %s",
271                      strerror(errno));
272                 goto out;
273         }
274         ret = recvmsg(socket_fd, &msg, MSG_WAITALL);
275         if (ret <= 0) {
276                 WARN("no message from primary: %s", strerror(errno));
277                 goto out;
278         }
279         cmsg = CMSG_FIRSTHDR(&msg);
280         if (cmsg == NULL) {
281                 WARN("No file descriptor received");
282                 goto out;
283         }
284         fd = (int *)CMSG_DATA(cmsg);
285         if (*fd <= 0) {
286                 WARN("no file descriptor received: %s", strerror(errno));
287                 ret = *fd;
288                 goto out;
289         }
290         ret = *fd;
291 out:
292         close(socket_fd);
293         return ret;
294 }