blob: ef0eaaee0a32cd972edee38ec1b399f648ceec6c [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/*
16 Copyright (c) 2001, 2002, 2003, 2005 Eliot Dresselhaus
17
18 Permission is hereby granted, free of charge, to any person obtaining
19 a copy of this software and associated documentation files (the
20 "Software"), to deal in the Software without restriction, including
21 without limitation the rights to use, copy, modify, merge, publish,
22 distribute, sublicense, and/or sell copies of the Software, and to
23 permit persons to whom the Software is furnished to do so, subject to
24 the following conditions:
25
26 The above copyright notice and this permission notice shall be
27 included in all copies or substantial portions of the Software.
28
29 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36*/
37
Damjan Marionf49921f2017-09-11 16:52:11 +020038#include <stdio.h>
39#include <string.h> /* strchr */
40#define __USE_GNU
Nathan Moos323418d2021-01-15 16:45:14 -080041#define _GNU_SOURCE
Ed Warnickecb9cada2015-12-08 15:45:58 -070042#include <sys/types.h>
43#include <sys/socket.h>
Damjan Marionf49921f2017-09-11 16:52:11 +020044#include <sys/un.h>
Damjan Marionf6616382017-06-21 12:01:37 +020045#include <sys/stat.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070046#include <netinet/in.h>
47#include <arpa/inet.h>
48#include <netdb.h>
49#include <unistd.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070050#include <fcntl.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070051
52#include <vppinfra/mem.h>
53#include <vppinfra/vec.h>
54#include <vppinfra/socket.h>
Nathan Skrzypczak4cef6de2021-07-19 18:21:43 +020055#include <vppinfra/linux/netns.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070056#include <vppinfra/format.h>
57#include <vppinfra/error.h>
58
Nathan Moos323418d2021-01-15 16:45:14 -080059#ifndef __GLIBC__
60/* IPPORT_USERRESERVED is not part of musl libc. */
61#define IPPORT_USERRESERVED 5000
62#endif
63
Damjan Mariondae1c7e2020-10-17 13:32:25 +020064__clib_export void
Dave Barachc3799992016-08-15 11:12:27 -040065clib_socket_tx_add_formatted (clib_socket_t * s, char *fmt, ...)
Ed Warnickecb9cada2015-12-08 15:45:58 -070066{
67 va_list va;
68 va_start (va, fmt);
69 clib_socket_tx_add_va_formatted (s, fmt, &va);
70 va_end (va);
71}
72
73/* Return and bind to an unused port. */
Dave Barachc3799992016-08-15 11:12:27 -040074static word
75find_free_port (word sock)
Ed Warnickecb9cada2015-12-08 15:45:58 -070076{
77 word port;
78
79 for (port = IPPORT_USERRESERVED; port < 1 << 16; port++)
80 {
81 struct sockaddr_in a;
82
Dave Barachb7b92992018-10-17 10:38:51 -040083 clib_memset (&a, 0, sizeof (a)); /* Warnings be gone */
Ed Warnickecb9cada2015-12-08 15:45:58 -070084
85 a.sin_family = PF_INET;
86 a.sin_addr.s_addr = INADDR_ANY;
87 a.sin_port = htons (port);
88
89 if (bind (sock, (struct sockaddr *) &a, sizeof (a)) >= 0)
90 break;
91 }
Dave Barachc3799992016-08-15 11:12:27 -040092
Ed Warnickecb9cada2015-12-08 15:45:58 -070093 return port < 1 << 16 ? port : -1;
94}
95
Ed Warnickecb9cada2015-12-08 15:45:58 -070096static clib_error_t *
97default_socket_write (clib_socket_t * s)
98{
Dave Barachc3799992016-08-15 11:12:27 -040099 clib_error_t *err = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700100 word written = 0;
101 word fd = 0;
102 word tx_len;
103
104 fd = s->fd;
105
106 /* Map standard input to standard output.
107 Typically, fd is a socket for which read/write both work. */
108 if (fd == 0)
109 fd = 1;
110
111 tx_len = vec_len (s->tx_buffer);
112 written = write (fd, s->tx_buffer, tx_len);
113
114 /* Ignore certain errors. */
Dave Barachc3799992016-08-15 11:12:27 -0400115 if (written < 0 && !unix_error_is_fatal (errno))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700116 written = 0;
117
118 /* A "real" error occurred. */
119 if (written < 0)
120 {
Dave Wallace70ec09d2017-09-06 16:45:04 -0400121 err = clib_error_return_unix (0, "write %wd bytes (fd %d, '%s')",
122 tx_len, s->fd, s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700123 vec_free (s->tx_buffer);
124 goto done;
125 }
126
127 /* Reclaim the transmitted part of the tx buffer on successful writes. */
128 else if (written > 0)
129 {
130 if (written == tx_len)
Damjan Marion8bea5892022-04-04 22:40:45 +0200131 vec_set_len (s->tx_buffer, 0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700132 else
133 vec_delete (s->tx_buffer, written, 0);
134 }
135
136 /* If a non-fatal error occurred AND
137 the buffer is full, then we must free it. */
Dave Barachc3799992016-08-15 11:12:27 -0400138 else if (written == 0 && tx_len > 64 * 1024)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700139 {
140 vec_free (s->tx_buffer);
141 }
142
Dave Barachc3799992016-08-15 11:12:27 -0400143done:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700144 return err;
145}
146
147static clib_error_t *
148default_socket_read (clib_socket_t * sock, int n_bytes)
149{
150 word fd, n_read;
Dave Barachc3799992016-08-15 11:12:27 -0400151 u8 *buf;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700152
153 /* RX side of socket is down once end of file is reached. */
Damjan Marion085757b2023-01-30 11:48:38 +0100154 if (sock->rx_end_of_file)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700155 return 0;
156
157 fd = sock->fd;
158
159 n_bytes = clib_max (n_bytes, 4096);
160 vec_add2 (sock->rx_buffer, buf, n_bytes);
161
162 if ((n_read = read (fd, buf, n_bytes)) < 0)
163 {
164 n_read = 0;
165
166 /* Ignore certain errors. */
Dave Barachc3799992016-08-15 11:12:27 -0400167 if (!unix_error_is_fatal (errno))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700168 goto non_fatal;
Dave Barachc3799992016-08-15 11:12:27 -0400169
Dave Wallace70ec09d2017-09-06 16:45:04 -0400170 return clib_error_return_unix (0, "read %d bytes (fd %d, '%s')",
171 n_bytes, sock->fd, sock->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700172 }
Dave Barachc3799992016-08-15 11:12:27 -0400173
Ed Warnickecb9cada2015-12-08 15:45:58 -0700174 /* Other side closed the socket. */
175 if (n_read == 0)
Damjan Marion085757b2023-01-30 11:48:38 +0100176 sock->rx_end_of_file = 1;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700177
Dave Barachc3799992016-08-15 11:12:27 -0400178non_fatal:
Damjan Marion8bea5892022-04-04 22:40:45 +0200179 vec_inc_len (sock->rx_buffer, n_read - n_bytes);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700180
181 return 0;
182}
183
Dave Barachc3799992016-08-15 11:12:27 -0400184static clib_error_t *
185default_socket_close (clib_socket_t * s)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700186{
187 if (close (s->fd) < 0)
Dave Wallace70ec09d2017-09-06 16:45:04 -0400188 return clib_error_return_unix (0, "close (fd %d, %s)", s->fd, s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700189 return 0;
190}
191
Damjan Marionf49921f2017-09-11 16:52:11 +0200192static clib_error_t *
193default_socket_sendmsg (clib_socket_t * s, void *msg, int msglen,
194 int fds[], int num_fds)
195{
196 struct msghdr mh = { 0 };
197 struct iovec iov[1];
Damjan Marion0215fb42018-10-30 13:04:19 +0100198 char ctl[CMSG_SPACE (sizeof (int) * num_fds)];
Damjan Marionf49921f2017-09-11 16:52:11 +0200199 int rv;
200
201 iov[0].iov_base = msg;
202 iov[0].iov_len = msglen;
203 mh.msg_iov = iov;
204 mh.msg_iovlen = 1;
205
206 if (num_fds > 0)
207 {
208 struct cmsghdr *cmsg;
Dave Barachb7b92992018-10-17 10:38:51 -0400209 clib_memset (&ctl, 0, sizeof (ctl));
Damjan Marionf49921f2017-09-11 16:52:11 +0200210 mh.msg_control = ctl;
211 mh.msg_controllen = sizeof (ctl);
212 cmsg = CMSG_FIRSTHDR (&mh);
213 cmsg->cmsg_len = CMSG_LEN (sizeof (int) * num_fds);
214 cmsg->cmsg_level = SOL_SOCKET;
215 cmsg->cmsg_type = SCM_RIGHTS;
216 memcpy (CMSG_DATA (cmsg), fds, sizeof (int) * num_fds);
217 }
218 rv = sendmsg (s->fd, &mh, 0);
219 if (rv < 0)
220 return clib_error_return_unix (0, "sendmsg");
221 return 0;
222}
223
224
225static clib_error_t *
226default_socket_recvmsg (clib_socket_t * s, void *msg, int msglen,
227 int fds[], int num_fds)
228{
Damjan Marion085757b2023-01-30 11:48:38 +0100229#ifdef CLIB_LINUX
Damjan Marionf49921f2017-09-11 16:52:11 +0200230 char ctl[CMSG_SPACE (sizeof (int) * num_fds) +
231 CMSG_SPACE (sizeof (struct ucred))];
Damjan Marion4dffd1c2018-09-03 12:30:36 +0200232 struct ucred *cr = 0;
233#else
234 char ctl[CMSG_SPACE (sizeof (int) * num_fds)];
235#endif
Damjan Marionf49921f2017-09-11 16:52:11 +0200236 struct msghdr mh = { 0 };
237 struct iovec iov[1];
238 ssize_t size;
Damjan Marionf49921f2017-09-11 16:52:11 +0200239 struct cmsghdr *cmsg;
240
241 iov[0].iov_base = msg;
242 iov[0].iov_len = msglen;
243 mh.msg_iov = iov;
244 mh.msg_iovlen = 1;
245 mh.msg_control = ctl;
246 mh.msg_controllen = sizeof (ctl);
247
Dave Barachb7b92992018-10-17 10:38:51 -0400248 clib_memset (ctl, 0, sizeof (ctl));
Damjan Marionf49921f2017-09-11 16:52:11 +0200249
250 /* receive the incoming message */
251 size = recvmsg (s->fd, &mh, 0);
252 if (size != msglen)
253 {
254 return (size == 0) ? clib_error_return (0, "disconnected") :
255 clib_error_return_unix (0, "recvmsg: malformed message (fd %d, '%s')",
256 s->fd, s->config);
257 }
258
259 cmsg = CMSG_FIRSTHDR (&mh);
260 while (cmsg)
261 {
262 if (cmsg->cmsg_level == SOL_SOCKET)
263 {
Damjan Marion085757b2023-01-30 11:48:38 +0100264#ifdef CLIB_LINUX
Damjan Marionf49921f2017-09-11 16:52:11 +0200265 if (cmsg->cmsg_type == SCM_CREDENTIALS)
266 {
267 cr = (struct ucred *) CMSG_DATA (cmsg);
268 s->uid = cr->uid;
269 s->gid = cr->gid;
270 s->pid = cr->pid;
271 }
Damjan Marion4dffd1c2018-09-03 12:30:36 +0200272 else
273#endif
274 if (cmsg->cmsg_type == SCM_RIGHTS)
Damjan Marionf49921f2017-09-11 16:52:11 +0200275 {
Dave Barach178cf492018-11-13 16:34:13 -0500276 clib_memcpy_fast (fds, CMSG_DATA (cmsg),
277 num_fds * sizeof (int));
Damjan Marionf49921f2017-09-11 16:52:11 +0200278 }
279 }
280 cmsg = CMSG_NXTHDR (&mh, cmsg);
281 }
282 return 0;
283}
284
Dave Barachc3799992016-08-15 11:12:27 -0400285static void
286socket_init_funcs (clib_socket_t * s)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700287{
Dave Barachc3799992016-08-15 11:12:27 -0400288 if (!s->write_func)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700289 s->write_func = default_socket_write;
Dave Barachc3799992016-08-15 11:12:27 -0400290 if (!s->read_func)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700291 s->read_func = default_socket_read;
Dave Barachc3799992016-08-15 11:12:27 -0400292 if (!s->close_func)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700293 s->close_func = default_socket_close;
Damjan Marionf49921f2017-09-11 16:52:11 +0200294 if (!s->sendmsg_func)
295 s->sendmsg_func = default_socket_sendmsg;
296 if (!s->recvmsg_func)
297 s->recvmsg_func = default_socket_recvmsg;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700298}
299
Damjan Marion085757b2023-01-30 11:48:38 +0100300static const struct
Ed Warnickecb9cada2015-12-08 15:45:58 -0700301{
Damjan Marion085757b2023-01-30 11:48:38 +0100302 char *prefix;
303 sa_family_t family;
304 clib_socket_type_t type;
305 u16 skip_prefix : 1;
306 u16 is_local : 1;
307} clib_socket_type_data[] = {
308 { .prefix = "unix:",
309 .family = AF_UNIX,
310 .type = CLIB_SOCKET_TYPE_UNIX,
311 .skip_prefix = 1,
312 .is_local = 1 },
313 { .prefix = "tcp:",
314 .family = AF_INET,
315 .type = CLIB_SOCKET_TYPE_INET,
316 .skip_prefix = 1 },
317 { .prefix = "abstract:",
318 .family = AF_UNIX,
319 .type = CLIB_SOCKET_TYPE_LINUX_ABSTRACT,
320 .skip_prefix = 1,
321 .is_local = 1 },
322 { .prefix = "/",
323 .family = AF_UNIX,
324 .type = CLIB_SOCKET_TYPE_UNIX,
325 .skip_prefix = 0,
326 .is_local = 1 },
327 { .prefix = "",
328 .family = AF_INET,
329 .type = CLIB_SOCKET_TYPE_INET,
330 .skip_prefix = 0,
331 .is_local = 0 },
332 { .prefix = "",
333 .family = AF_UNIX,
334 .type = CLIB_SOCKET_TYPE_UNIX,
335 .skip_prefix = 0,
336 .is_local = 1 },
337};
Ed Warnickecb9cada2015-12-08 15:45:58 -0700338
Damjan Marion085757b2023-01-30 11:48:38 +0100339static u8 *
340_clib_socket_get_string (char **p, int is_hostname)
341{
342 u8 *s = 0;
343 while (**p)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700344 {
Damjan Marion085757b2023-01-30 11:48:38 +0100345 switch (**p)
346 {
347 case '_':
348 if (is_hostname)
349 return s;
350 case 'a' ... 'z':
351 case 'A' ... 'Z':
352 case '0' ... '9':
353 case '/':
354 case '-':
355 case '.':
356 vec_add1 (s, **p);
357 (*p)++;
358 break;
359 break;
360 default:
361 return s;
362 }
363 }
364 return s;
365}
366
367__clib_export int
368clib_socket_prefix_is_valid (char *s)
369{
370 for (typeof (clib_socket_type_data[0]) *d = clib_socket_type_data;
371 d - clib_socket_type_data < ARRAY_LEN (clib_socket_type_data); d++)
372 if (d->skip_prefix && strncmp (s, d->prefix, strlen (d->prefix)) == 0)
373 return 1;
374 return 0;
375}
376
Nathan Skrzypczak51f1b262023-04-27 12:43:46 +0200377__clib_export int
378clib_socket_prefix_get_type (char *s)
379{
380 for (typeof (clib_socket_type_data[0]) *d = clib_socket_type_data;
381 d - clib_socket_type_data < ARRAY_LEN (clib_socket_type_data); d++)
382 if (strncmp (s, d->prefix, strlen (d->prefix)) == 0)
383 return d->type;
384 return 0;
385}
386
Damjan Marion085757b2023-01-30 11:48:38 +0100387__clib_export clib_error_t *
388clib_socket_init (clib_socket_t *s)
389{
390 struct sockaddr_un su = { .sun_family = AF_UNIX };
391 struct sockaddr_in si = { .sin_family = AF_INET };
392 struct sockaddr *sa = 0;
393 typeof (clib_socket_type_data[0]) *data = 0;
394 socklen_t addr_len = 0;
395 int rv;
396 char *p;
397 clib_error_t *err = 0;
398 u8 *name = 0;
399 u16 port = 0;
400#if CLIB_LINUX
401 int netns_fd = -1;
402#endif
403
404 s->fd = -1;
405
406 if (!s->config)
407 s->config = "";
408
409 for (int i = 0; i < ARRAY_LEN (clib_socket_type_data); i++)
410 {
411 typeof (clib_socket_type_data[0]) *d = clib_socket_type_data + i;
412
413 if (d->is_local == 0 && s->local_only)
414 continue;
415
416 if (strncmp (s->config, d->prefix, strlen (d->prefix)) == 0)
417 {
418 data = d;
419 break;
420 }
421 }
422
423 if (data == 0)
424 return clib_error_return (0, "unsupported socket config '%s'", s->config);
425
426 s->type = data->type;
427 p = s->config + (data->skip_prefix ? strlen (data->prefix) : 0);
428
429 name = _clib_socket_get_string (&p, data->type == CLIB_SOCKET_TYPE_INET);
430 vec_add1 (name, 0);
431
432 /* parse port type for INET sockets */
433 if (data->type == CLIB_SOCKET_TYPE_INET && p[0] == ':')
434 {
435 char *old_p = p + 1;
436 long long ll = strtoll (old_p, &p, 0);
437
438 if (p == old_p)
439 {
440 err = clib_error_return (0, "invalid port");
441 goto done;
442 }
443
444 if (ll > CLIB_U16_MAX || ll < 1)
445 {
446 err = clib_error_return (0, "port out of range");
447 goto done;
448 }
449 port = ll;
450 }
451
452 while (p[0] == ',')
453 {
454 p++;
455 if (0)
456 ;
457#if CLIB_LINUX
458 else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT && netns_fd == -1 &&
459 strncmp (p, "netns_name=", 11) == 0)
460 {
461 p += 11;
462 u8 *str = _clib_socket_get_string (&p, 0);
Mohsin Kazmi60a107c2023-02-15 13:31:27 +0000463 u8 *pathname = 0;
464 if (str[0] == '/')
465 pathname = format (0, "%v%c", str, 0);
466 else
467 pathname = format (0, "/var/run/netns/%v%c", str, 0);
Damjan Marion085757b2023-01-30 11:48:38 +0100468 if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
469 err = clib_error_return_unix (0, "open('%s')", pathname);
470 vec_free (str);
471 vec_free (pathname);
472 if (err)
473 goto done;
474 }
475 else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT && netns_fd == -1 &&
476 strncmp (p, "netns_pid=", 10) == 0)
477 {
478 char *old_p = p = p + 10;
479 u32 pid = (u32) strtol (old_p, &p, 0);
480
481 if (p == old_p)
482 err = clib_error_return (0, "invalid pid");
483 else
484 {
485 u8 *pathname = format (0, "/proc/%u/ns/net%c", pid, 0);
486 if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
487 err = clib_error_return_unix (0, "open('%s')", pathname);
488 vec_free (pathname);
489 }
490 if (err)
491 goto done;
492 }
493#endif
494 else
495 break;
496 }
497
498 if (p[0] != 0)
499 {
500 err = clib_error_return (0, "unknown input `%s'", p);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700501 goto done;
502 }
503
Damjan Marion085757b2023-01-30 11:48:38 +0100504#if CLIB_LINUX
505 /* change netns if requested */
506 if (s->type != CLIB_SOCKET_TYPE_INET && netns_fd != -1)
507 {
508 int fd = open ("/proc/self/ns/net", O_RDONLY);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700509
Damjan Marion085757b2023-01-30 11:48:38 +0100510 if (setns (netns_fd, CLONE_NEWNET) < 0)
511 {
512 close (fd);
513 err = clib_error_return_unix (0, "setns(%d)", netns_fd);
514 goto done;
515 }
516 netns_fd = fd;
517 }
518#endif
519
520 if (s->type == CLIB_SOCKET_TYPE_INET)
521 {
522 addr_len = sizeof (si);
523 si.sin_port = htons (port);
524
525 if (name)
526 {
527 struct in_addr host_addr;
528 vec_add1 (name, 0);
529
530 /* Recognize localhost to avoid host lookup in most common cast. */
531 if (!strcmp ((char *) name, "localhost"))
532 si.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
533
534 else if (inet_aton ((char *) name, &host_addr))
535 si.sin_addr = host_addr;
536
537 else if (strlen ((char *) name) > 0)
538 {
539 struct hostent *host = gethostbyname ((char *) name);
540 if (!host)
541 err = clib_error_return (0, "unknown host `%s'", name);
542 else
543 clib_memcpy (&si.sin_addr.s_addr, host->h_addr_list[0],
544 host->h_length);
545 }
546
547 else
548 si.sin_addr.s_addr =
549 htonl (s->is_server ? INADDR_LOOPBACK : INADDR_ANY);
550
551 if (err)
552 goto done;
553 }
554 sa = (struct sockaddr *) &si;
555 }
556 else if (s->type == CLIB_SOCKET_TYPE_UNIX)
557 {
558 struct stat st = { 0 };
559 char *path = (char *) &su.sun_path;
560
561 if (vec_len (name) > sizeof (su.sun_path) - 1)
562 {
563 err = clib_error_return (0, "File path '%v' too long", name);
564 goto done;
565 }
566
567 clib_memcpy (path, s->config, vec_len (name));
568 addr_len = sizeof (su);
569 sa = (struct sockaddr *) &su;
570
571 rv = stat (path, &st);
572 if (!s->is_server && rv < 0)
573 {
574 err = clib_error_return_unix (0, "stat ('%s')", path);
575 goto done;
576 }
577
578 if (s->is_server && rv == 0)
579 {
580 if (S_ISSOCK (st.st_mode))
581 {
582 int client_fd = socket (AF_UNIX, SOCK_STREAM, 0);
583 int ret = connect (client_fd, (const struct sockaddr *) &su,
584 sizeof (su));
585 typeof (errno) connect_errno = errno;
586 close (client_fd);
587
588 if (ret == 0 || (ret < 0 && connect_errno != ECONNREFUSED))
589 {
590 err = clib_error_return (0, "Active listener on '%s'", path);
591 goto done;
592 }
593
594 if (unlink (path) < 0)
595 {
596 err = clib_error_return_unix (0, "unlink ('%s')", path);
597 goto done;
598 }
599 }
600 else
601 {
602 err = clib_error_return (0, "File '%s' already exists", path);
603 goto done;
604 }
605 }
606 }
607#if CLIB_LINUX
608 else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT)
609 {
610 if (vec_len (name) > sizeof (su.sun_path) - 2)
611 {
612 err = clib_error_return (0, "Socket name '%v' too long", name);
613 goto done;
614 }
615
616 clib_memcpy (&su.sun_path[1], name, vec_len (name));
617 addr_len = sizeof (su.sun_family) + vec_len (name);
618 sa = (struct sockaddr *) &su;
619 s->allow_group_write = 0;
620 }
621#endif
622 else
623 {
624 err = clib_error_return_unix (0, "unknown socket family");
625 goto done;
626 }
627
628 socket_init_funcs (s);
629
630 if ((s->fd = socket (sa->sa_family,
631 s->is_seqpacket ? SOCK_SEQPACKET : SOCK_STREAM, 0)) < 0)
632 {
633 err =
634 clib_error_return_unix (0, "socket (fd %d, '%s')", s->fd, s->config);
635 goto done;
636 }
637
638 if (s->is_server)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700639 {
640 uword need_bind = 1;
641
Damjan Marion085757b2023-01-30 11:48:38 +0100642 if (sa->sa_family == AF_INET && si.sin_port == 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700643 {
Damjan Marion085757b2023-01-30 11:48:38 +0100644 word port = find_free_port (s->fd);
645 if (port < 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700646 {
Damjan Marion085757b2023-01-30 11:48:38 +0100647 err = clib_error_return (0, "no free port (fd %d, '%s')", s->fd,
648 s->config);
649 goto done;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700650 }
Damjan Marion085757b2023-01-30 11:48:38 +0100651 si.sin_port = port;
652 need_bind = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700653 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700654
Damjan Marion085757b2023-01-30 11:48:38 +0100655 if (setsockopt (s->fd, SOL_SOCKET, SO_REUSEADDR, &((int){ 1 }),
656 sizeof (int)) < 0)
657 clib_unix_warning ("setsockopt SO_REUSEADDR fails");
Ed Warnickecb9cada2015-12-08 15:45:58 -0700658
Damjan Marion085757b2023-01-30 11:48:38 +0100659#if CLIB_LINUX
660 if (sa->sa_family == AF_UNIX && s->passcred)
Damjan Marionf49921f2017-09-11 16:52:11 +0200661 {
Damjan Marion085757b2023-01-30 11:48:38 +0100662 if (setsockopt (s->fd, SOL_SOCKET, SO_PASSCRED, &((int){ 1 }),
663 sizeof (int)) < 0)
Damjan Marionf49921f2017-09-11 16:52:11 +0200664 {
Damjan Marion085757b2023-01-30 11:48:38 +0100665 err = clib_error_return_unix (0,
666 "setsockopt (SO_PASSCRED, "
667 "fd %d, '%s')",
668 s->fd, s->config);
Damjan Marionf49921f2017-09-11 16:52:11 +0200669 goto done;
670 }
671 }
Damjan Marion4dffd1c2018-09-03 12:30:36 +0200672#endif
Damjan Marionf49921f2017-09-11 16:52:11 +0200673
Damjan Marion085757b2023-01-30 11:48:38 +0100674 if (need_bind && bind (s->fd, sa, addr_len) < 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700675 {
Damjan Marion085757b2023-01-30 11:48:38 +0100676 err =
677 clib_error_return_unix (0, "bind (fd %d, '%s')", s->fd, s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700678 goto done;
679 }
680
681 if (listen (s->fd, 5) < 0)
682 {
Damjan Marion085757b2023-01-30 11:48:38 +0100683 err = clib_error_return_unix (0, "listen (fd %d, '%s')", s->fd,
684 s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700685 goto done;
686 }
Damjan Marion085757b2023-01-30 11:48:38 +0100687
688 if (s->local_only && s->allow_group_write)
Damjan Marionf6616382017-06-21 12:01:37 +0200689 {
Damjan Marion085757b2023-01-30 11:48:38 +0100690 if (fchmod (s->fd, S_IWGRP) < 0)
Chris Lukeab7b8d92017-09-07 07:40:13 -0400691 {
Damjan Marion085757b2023-01-30 11:48:38 +0100692 err = clib_error_return_unix (
693 0, "fchmod (fd %d, '%s', mode S_IWGRP)", s->fd, s->config);
Chris Lukeab7b8d92017-09-07 07:40:13 -0400694 goto done;
695 }
Damjan Marionf6616382017-06-21 12:01:37 +0200696 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700697 }
698 else
699 {
Damjan Marion085757b2023-01-30 11:48:38 +0100700 if (s->non_blocking_connect && fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700701 {
Damjan Marion085757b2023-01-30 11:48:38 +0100702 err = clib_error_return_unix (0, "fcntl NONBLOCK (fd %d, '%s')",
703 s->fd, s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700704 goto done;
705 }
706
Damjan Marion085757b2023-01-30 11:48:38 +0100707 while ((rv = connect (s->fd, sa, addr_len)) < 0 && errno == EAGAIN)
Florin Coras42ddf692020-01-06 20:23:07 +0000708 ;
Damjan Marion085757b2023-01-30 11:48:38 +0100709 if (rv < 0 && !(s->non_blocking_connect && errno == EINPROGRESS))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700710 {
Damjan Marion085757b2023-01-30 11:48:38 +0100711 err = clib_error_return_unix (0, "connect (fd %d, '%s')", s->fd,
712 s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700713 goto done;
714 }
Florin Coras57e0af92021-05-26 10:21:10 -0700715 /* Connect was blocking so set fd to non-blocking now unless
716 * blocking mode explicitly requested. */
Damjan Marion085757b2023-01-30 11:48:38 +0100717 if (!s->non_blocking_connect && !s->is_blocking &&
Florin Coras5fe94572021-05-24 08:58:15 -0700718 fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
719 {
Damjan Marion085757b2023-01-30 11:48:38 +0100720 err = clib_error_return_unix (0, "fcntl NONBLOCK2 (fd %d, '%s')",
721 s->fd, s->config);
Florin Coras5fe94572021-05-24 08:58:15 -0700722 goto done;
723 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700724 }
725
Dave Barachc3799992016-08-15 11:12:27 -0400726done:
Damjan Marion085757b2023-01-30 11:48:38 +0100727 if (err && s->fd > -1)
728 {
729 close (s->fd);
730 s->fd = -1;
731 }
732#if CLIB_LINUX
733 if (netns_fd != -1)
734 {
735 setns (CLONE_NEWNET, netns_fd);
736 close (netns_fd);
737 }
738#endif
739 vec_free (name);
740 return err;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700741}
742
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200743__clib_export clib_error_t *
Dave Barachc3799992016-08-15 11:12:27 -0400744clib_socket_accept (clib_socket_t * server, clib_socket_t * client)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700745{
Dave Barachc3799992016-08-15 11:12:27 -0400746 clib_error_t *err = 0;
747 socklen_t len = 0;
748
Dave Barachb7b92992018-10-17 10:38:51 -0400749 clib_memset (client, 0, sizeof (client[0]));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700750
751 /* Accept the new socket connection. */
752 client->fd = accept (server->fd, 0, 0);
Dave Barachc3799992016-08-15 11:12:27 -0400753 if (client->fd < 0)
Dave Wallace70ec09d2017-09-06 16:45:04 -0400754 return clib_error_return_unix (0, "accept (fd %d, '%s')",
755 server->fd, server->config);
Dave Barachc3799992016-08-15 11:12:27 -0400756
Ed Warnickecb9cada2015-12-08 15:45:58 -0700757 /* Set the new socket to be non-blocking. */
758 if (fcntl (client->fd, F_SETFL, O_NONBLOCK) < 0)
759 {
Dave Wallace70ec09d2017-09-06 16:45:04 -0400760 err = clib_error_return_unix (0, "fcntl O_NONBLOCK (fd %d)",
761 client->fd);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700762 goto close_client;
763 }
Dave Barachc3799992016-08-15 11:12:27 -0400764
Ed Warnickecb9cada2015-12-08 15:45:58 -0700765 /* Get peer info. */
766 len = sizeof (client->peer);
767 if (getpeername (client->fd, (struct sockaddr *) &client->peer, &len) < 0)
768 {
Dave Wallace70ec09d2017-09-06 16:45:04 -0400769 err = clib_error_return_unix (0, "getpeername (fd %d)", client->fd);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700770 goto close_client;
771 }
772
Damjan Marionf49921f2017-09-11 16:52:11 +0200773 client->flags = CLIB_SOCKET_F_IS_CLIENT;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700774
775 socket_init_funcs (client);
776 return 0;
777
Dave Barachc3799992016-08-15 11:12:27 -0400778close_client:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700779 close (client->fd);
780 return err;
781}
Dave Barachc3799992016-08-15 11:12:27 -0400782
783/*
784 * fd.io coding-style-patch-verification: ON
785 *
786 * Local Variables:
787 * eval: (c-set-style "gnu")
788 * End:
789 */