blob: 2abf2b244cd36262d63db395467a80a45863abc2 [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{
Tom Jones93fc6922024-01-26 14:54:57 +0000229#if 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 {
Tom Jones93fc6922024-01-26 14:54:57 +0000264#if 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 },
Tom Jones93fc6922024-01-26 14:54:57 +0000317#if CLIB_LINUX
Damjan Marion085757b2023-01-30 11:48:38 +0100318 { .prefix = "abstract:",
319 .family = AF_UNIX,
320 .type = CLIB_SOCKET_TYPE_LINUX_ABSTRACT,
321 .skip_prefix = 1,
322 .is_local = 1 },
Tom Jones93fc6922024-01-26 14:54:57 +0000323#endif /* CLIB_LINUX */
Damjan Marion085757b2023-01-30 11:48:38 +0100324 { .prefix = "/",
325 .family = AF_UNIX,
326 .type = CLIB_SOCKET_TYPE_UNIX,
327 .skip_prefix = 0,
328 .is_local = 1 },
329 { .prefix = "",
330 .family = AF_INET,
331 .type = CLIB_SOCKET_TYPE_INET,
332 .skip_prefix = 0,
333 .is_local = 0 },
334 { .prefix = "",
335 .family = AF_UNIX,
336 .type = CLIB_SOCKET_TYPE_UNIX,
337 .skip_prefix = 0,
338 .is_local = 1 },
339};
Ed Warnickecb9cada2015-12-08 15:45:58 -0700340
Damjan Marion085757b2023-01-30 11:48:38 +0100341static u8 *
342_clib_socket_get_string (char **p, int is_hostname)
343{
344 u8 *s = 0;
345 while (**p)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700346 {
Damjan Marion085757b2023-01-30 11:48:38 +0100347 switch (**p)
348 {
349 case '_':
350 if (is_hostname)
351 return s;
352 case 'a' ... 'z':
353 case 'A' ... 'Z':
354 case '0' ... '9':
355 case '/':
356 case '-':
357 case '.':
358 vec_add1 (s, **p);
359 (*p)++;
360 break;
361 break;
362 default:
363 return s;
364 }
365 }
366 return s;
367}
368
369__clib_export int
370clib_socket_prefix_is_valid (char *s)
371{
372 for (typeof (clib_socket_type_data[0]) *d = clib_socket_type_data;
373 d - clib_socket_type_data < ARRAY_LEN (clib_socket_type_data); d++)
374 if (d->skip_prefix && strncmp (s, d->prefix, strlen (d->prefix)) == 0)
375 return 1;
376 return 0;
377}
378
Nathan Skrzypczak51f1b262023-04-27 12:43:46 +0200379__clib_export int
380clib_socket_prefix_get_type (char *s)
381{
382 for (typeof (clib_socket_type_data[0]) *d = clib_socket_type_data;
383 d - clib_socket_type_data < ARRAY_LEN (clib_socket_type_data); d++)
384 if (strncmp (s, d->prefix, strlen (d->prefix)) == 0)
385 return d->type;
386 return 0;
387}
388
Damjan Marion085757b2023-01-30 11:48:38 +0100389__clib_export clib_error_t *
390clib_socket_init (clib_socket_t *s)
391{
392 struct sockaddr_un su = { .sun_family = AF_UNIX };
393 struct sockaddr_in si = { .sin_family = AF_INET };
394 struct sockaddr *sa = 0;
395 typeof (clib_socket_type_data[0]) *data = 0;
396 socklen_t addr_len = 0;
397 int rv;
398 char *p;
399 clib_error_t *err = 0;
400 u8 *name = 0;
401 u16 port = 0;
402#if CLIB_LINUX
403 int netns_fd = -1;
404#endif
405
406 s->fd = -1;
407
408 if (!s->config)
409 s->config = "";
410
411 for (int i = 0; i < ARRAY_LEN (clib_socket_type_data); i++)
412 {
413 typeof (clib_socket_type_data[0]) *d = clib_socket_type_data + i;
414
415 if (d->is_local == 0 && s->local_only)
416 continue;
417
418 if (strncmp (s->config, d->prefix, strlen (d->prefix)) == 0)
419 {
420 data = d;
421 break;
422 }
423 }
424
425 if (data == 0)
426 return clib_error_return (0, "unsupported socket config '%s'", s->config);
427
428 s->type = data->type;
429 p = s->config + (data->skip_prefix ? strlen (data->prefix) : 0);
430
431 name = _clib_socket_get_string (&p, data->type == CLIB_SOCKET_TYPE_INET);
432 vec_add1 (name, 0);
433
434 /* parse port type for INET sockets */
435 if (data->type == CLIB_SOCKET_TYPE_INET && p[0] == ':')
436 {
437 char *old_p = p + 1;
438 long long ll = strtoll (old_p, &p, 0);
439
440 if (p == old_p)
441 {
442 err = clib_error_return (0, "invalid port");
443 goto done;
444 }
445
446 if (ll > CLIB_U16_MAX || ll < 1)
447 {
448 err = clib_error_return (0, "port out of range");
449 goto done;
450 }
451 port = ll;
452 }
453
454 while (p[0] == ',')
455 {
456 p++;
457 if (0)
458 ;
459#if CLIB_LINUX
460 else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT && netns_fd == -1 &&
461 strncmp (p, "netns_name=", 11) == 0)
462 {
463 p += 11;
464 u8 *str = _clib_socket_get_string (&p, 0);
Mohsin Kazmi60a107c2023-02-15 13:31:27 +0000465 u8 *pathname = 0;
466 if (str[0] == '/')
467 pathname = format (0, "%v%c", str, 0);
468 else
469 pathname = format (0, "/var/run/netns/%v%c", str, 0);
Damjan Marion085757b2023-01-30 11:48:38 +0100470 if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
471 err = clib_error_return_unix (0, "open('%s')", pathname);
472 vec_free (str);
473 vec_free (pathname);
474 if (err)
475 goto done;
476 }
477 else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT && netns_fd == -1 &&
478 strncmp (p, "netns_pid=", 10) == 0)
479 {
480 char *old_p = p = p + 10;
481 u32 pid = (u32) strtol (old_p, &p, 0);
482
483 if (p == old_p)
484 err = clib_error_return (0, "invalid pid");
485 else
486 {
487 u8 *pathname = format (0, "/proc/%u/ns/net%c", pid, 0);
488 if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
489 err = clib_error_return_unix (0, "open('%s')", pathname);
490 vec_free (pathname);
491 }
492 if (err)
493 goto done;
494 }
495#endif
496 else
497 break;
498 }
499
500 if (p[0] != 0)
501 {
502 err = clib_error_return (0, "unknown input `%s'", p);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700503 goto done;
504 }
505
Damjan Marion085757b2023-01-30 11:48:38 +0100506#if CLIB_LINUX
507 /* change netns if requested */
508 if (s->type != CLIB_SOCKET_TYPE_INET && netns_fd != -1)
509 {
510 int fd = open ("/proc/self/ns/net", O_RDONLY);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700511
Damjan Marion085757b2023-01-30 11:48:38 +0100512 if (setns (netns_fd, CLONE_NEWNET) < 0)
513 {
514 close (fd);
515 err = clib_error_return_unix (0, "setns(%d)", netns_fd);
516 goto done;
517 }
518 netns_fd = fd;
519 }
520#endif
521
522 if (s->type == CLIB_SOCKET_TYPE_INET)
523 {
524 addr_len = sizeof (si);
525 si.sin_port = htons (port);
526
527 if (name)
528 {
529 struct in_addr host_addr;
530 vec_add1 (name, 0);
531
532 /* Recognize localhost to avoid host lookup in most common cast. */
533 if (!strcmp ((char *) name, "localhost"))
534 si.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
535
536 else if (inet_aton ((char *) name, &host_addr))
537 si.sin_addr = host_addr;
538
539 else if (strlen ((char *) name) > 0)
540 {
541 struct hostent *host = gethostbyname ((char *) name);
542 if (!host)
543 err = clib_error_return (0, "unknown host `%s'", name);
544 else
545 clib_memcpy (&si.sin_addr.s_addr, host->h_addr_list[0],
546 host->h_length);
547 }
548
549 else
550 si.sin_addr.s_addr =
551 htonl (s->is_server ? INADDR_LOOPBACK : INADDR_ANY);
552
553 if (err)
554 goto done;
555 }
556 sa = (struct sockaddr *) &si;
557 }
558 else if (s->type == CLIB_SOCKET_TYPE_UNIX)
559 {
560 struct stat st = { 0 };
561 char *path = (char *) &su.sun_path;
562
563 if (vec_len (name) > sizeof (su.sun_path) - 1)
564 {
565 err = clib_error_return (0, "File path '%v' too long", name);
566 goto done;
567 }
568
569 clib_memcpy (path, s->config, vec_len (name));
570 addr_len = sizeof (su);
571 sa = (struct sockaddr *) &su;
572
573 rv = stat (path, &st);
574 if (!s->is_server && rv < 0)
575 {
576 err = clib_error_return_unix (0, "stat ('%s')", path);
577 goto done;
578 }
579
580 if (s->is_server && rv == 0)
581 {
582 if (S_ISSOCK (st.st_mode))
583 {
584 int client_fd = socket (AF_UNIX, SOCK_STREAM, 0);
585 int ret = connect (client_fd, (const struct sockaddr *) &su,
586 sizeof (su));
587 typeof (errno) connect_errno = errno;
588 close (client_fd);
589
590 if (ret == 0 || (ret < 0 && connect_errno != ECONNREFUSED))
591 {
592 err = clib_error_return (0, "Active listener on '%s'", path);
593 goto done;
594 }
595
596 if (unlink (path) < 0)
597 {
598 err = clib_error_return_unix (0, "unlink ('%s')", path);
599 goto done;
600 }
601 }
602 else
603 {
604 err = clib_error_return (0, "File '%s' already exists", path);
605 goto done;
606 }
607 }
608 }
609#if CLIB_LINUX
610 else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT)
611 {
612 if (vec_len (name) > sizeof (su.sun_path) - 2)
613 {
614 err = clib_error_return (0, "Socket name '%v' too long", name);
615 goto done;
616 }
617
618 clib_memcpy (&su.sun_path[1], name, vec_len (name));
619 addr_len = sizeof (su.sun_family) + vec_len (name);
620 sa = (struct sockaddr *) &su;
621 s->allow_group_write = 0;
622 }
623#endif
624 else
625 {
626 err = clib_error_return_unix (0, "unknown socket family");
627 goto done;
628 }
629
630 socket_init_funcs (s);
631
632 if ((s->fd = socket (sa->sa_family,
633 s->is_seqpacket ? SOCK_SEQPACKET : SOCK_STREAM, 0)) < 0)
634 {
635 err =
636 clib_error_return_unix (0, "socket (fd %d, '%s')", s->fd, s->config);
637 goto done;
638 }
639
640 if (s->is_server)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700641 {
642 uword need_bind = 1;
643
Damjan Marion085757b2023-01-30 11:48:38 +0100644 if (sa->sa_family == AF_INET && si.sin_port == 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700645 {
Damjan Marion085757b2023-01-30 11:48:38 +0100646 word port = find_free_port (s->fd);
647 if (port < 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700648 {
Damjan Marion085757b2023-01-30 11:48:38 +0100649 err = clib_error_return (0, "no free port (fd %d, '%s')", s->fd,
650 s->config);
651 goto done;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700652 }
Damjan Marion085757b2023-01-30 11:48:38 +0100653 si.sin_port = port;
654 need_bind = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700655 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700656
Damjan Marion085757b2023-01-30 11:48:38 +0100657 if (setsockopt (s->fd, SOL_SOCKET, SO_REUSEADDR, &((int){ 1 }),
658 sizeof (int)) < 0)
659 clib_unix_warning ("setsockopt SO_REUSEADDR fails");
Ed Warnickecb9cada2015-12-08 15:45:58 -0700660
Damjan Marion085757b2023-01-30 11:48:38 +0100661#if CLIB_LINUX
662 if (sa->sa_family == AF_UNIX && s->passcred)
Damjan Marionf49921f2017-09-11 16:52:11 +0200663 {
Damjan Marion085757b2023-01-30 11:48:38 +0100664 if (setsockopt (s->fd, SOL_SOCKET, SO_PASSCRED, &((int){ 1 }),
665 sizeof (int)) < 0)
Damjan Marionf49921f2017-09-11 16:52:11 +0200666 {
Damjan Marion085757b2023-01-30 11:48:38 +0100667 err = clib_error_return_unix (0,
668 "setsockopt (SO_PASSCRED, "
669 "fd %d, '%s')",
670 s->fd, s->config);
Damjan Marionf49921f2017-09-11 16:52:11 +0200671 goto done;
672 }
673 }
Damjan Marion4dffd1c2018-09-03 12:30:36 +0200674#endif
Damjan Marionf49921f2017-09-11 16:52:11 +0200675
Georgy Borodindc26d502023-11-10 16:31:09 +0100676 if (need_bind)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700677 {
Georgy Borodindc26d502023-11-10 16:31:09 +0100678 int bind_ret;
679 if (sa->sa_family == AF_UNIX && s->allow_group_write)
680 {
681 mode_t def_restrictions = umask (S_IWOTH);
682 bind_ret = bind (s->fd, sa, addr_len);
683 umask (def_restrictions);
684 }
685 else
686 bind_ret = bind (s->fd, sa, addr_len);
687
688 if (bind_ret < 0)
689 {
690 err = clib_error_return_unix (0, "bind (fd %d, '%s')", s->fd,
691 s->config);
692 goto done;
693 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700694 }
695
696 if (listen (s->fd, 5) < 0)
697 {
Damjan Marion085757b2023-01-30 11:48:38 +0100698 err = clib_error_return_unix (0, "listen (fd %d, '%s')", s->fd,
699 s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700700 goto done;
701 }
702 }
703 else
704 {
Damjan Marion085757b2023-01-30 11:48:38 +0100705 if (s->non_blocking_connect && fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700706 {
Damjan Marion085757b2023-01-30 11:48:38 +0100707 err = clib_error_return_unix (0, "fcntl NONBLOCK (fd %d, '%s')",
708 s->fd, s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700709 goto done;
710 }
711
Damjan Marion085757b2023-01-30 11:48:38 +0100712 while ((rv = connect (s->fd, sa, addr_len)) < 0 && errno == EAGAIN)
Florin Coras42ddf692020-01-06 20:23:07 +0000713 ;
Damjan Marion085757b2023-01-30 11:48:38 +0100714 if (rv < 0 && !(s->non_blocking_connect && errno == EINPROGRESS))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700715 {
Damjan Marion085757b2023-01-30 11:48:38 +0100716 err = clib_error_return_unix (0, "connect (fd %d, '%s')", s->fd,
717 s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700718 goto done;
719 }
Florin Coras57e0af92021-05-26 10:21:10 -0700720 /* Connect was blocking so set fd to non-blocking now unless
721 * blocking mode explicitly requested. */
Damjan Marion085757b2023-01-30 11:48:38 +0100722 if (!s->non_blocking_connect && !s->is_blocking &&
Florin Coras5fe94572021-05-24 08:58:15 -0700723 fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
724 {
Damjan Marion085757b2023-01-30 11:48:38 +0100725 err = clib_error_return_unix (0, "fcntl NONBLOCK2 (fd %d, '%s')",
726 s->fd, s->config);
Florin Coras5fe94572021-05-24 08:58:15 -0700727 goto done;
728 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700729 }
730
Dave Barachc3799992016-08-15 11:12:27 -0400731done:
Damjan Marion085757b2023-01-30 11:48:38 +0100732 if (err && s->fd > -1)
733 {
734 close (s->fd);
735 s->fd = -1;
736 }
737#if CLIB_LINUX
738 if (netns_fd != -1)
739 {
Artem Glazychev986ca972023-09-07 14:16:56 +0700740 setns (netns_fd, CLONE_NEWNET);
Damjan Marion085757b2023-01-30 11:48:38 +0100741 close (netns_fd);
742 }
743#endif
744 vec_free (name);
745 return err;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700746}
747
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200748__clib_export clib_error_t *
Dave Barachc3799992016-08-15 11:12:27 -0400749clib_socket_accept (clib_socket_t * server, clib_socket_t * client)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700750{
Dave Barachc3799992016-08-15 11:12:27 -0400751 clib_error_t *err = 0;
752 socklen_t len = 0;
753
Dave Barachb7b92992018-10-17 10:38:51 -0400754 clib_memset (client, 0, sizeof (client[0]));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700755
756 /* Accept the new socket connection. */
757 client->fd = accept (server->fd, 0, 0);
Dave Barachc3799992016-08-15 11:12:27 -0400758 if (client->fd < 0)
Dave Wallace70ec09d2017-09-06 16:45:04 -0400759 return clib_error_return_unix (0, "accept (fd %d, '%s')",
760 server->fd, server->config);
Dave Barachc3799992016-08-15 11:12:27 -0400761
Ed Warnickecb9cada2015-12-08 15:45:58 -0700762 /* Set the new socket to be non-blocking. */
763 if (fcntl (client->fd, F_SETFL, O_NONBLOCK) < 0)
764 {
Dave Wallace70ec09d2017-09-06 16:45:04 -0400765 err = clib_error_return_unix (0, "fcntl O_NONBLOCK (fd %d)",
766 client->fd);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700767 goto close_client;
768 }
Dave Barachc3799992016-08-15 11:12:27 -0400769
Ed Warnickecb9cada2015-12-08 15:45:58 -0700770 /* Get peer info. */
771 len = sizeof (client->peer);
772 if (getpeername (client->fd, (struct sockaddr *) &client->peer, &len) < 0)
773 {
Dave Wallace70ec09d2017-09-06 16:45:04 -0400774 err = clib_error_return_unix (0, "getpeername (fd %d)", client->fd);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700775 goto close_client;
776 }
777
Damjan Marionf49921f2017-09-11 16:52:11 +0200778 client->flags = CLIB_SOCKET_F_IS_CLIENT;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700779
780 socket_init_funcs (client);
781 return 0;
782
Dave Barachc3799992016-08-15 11:12:27 -0400783close_client:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700784 close (client->fd);
785 return err;
786}
Dave Barachc3799992016-08-15 11:12:27 -0400787
788/*
789 * fd.io coding-style-patch-verification: ON
790 *
791 * Local Variables:
792 * eval: (c-set-style "gnu")
793 * End:
794 */