blob: 40374d736c56567158c33c3853ee5b1cc5e4c6fc [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
377__clib_export clib_error_t *
378clib_socket_init (clib_socket_t *s)
379{
380 struct sockaddr_un su = { .sun_family = AF_UNIX };
381 struct sockaddr_in si = { .sin_family = AF_INET };
382 struct sockaddr *sa = 0;
383 typeof (clib_socket_type_data[0]) *data = 0;
384 socklen_t addr_len = 0;
385 int rv;
386 char *p;
387 clib_error_t *err = 0;
388 u8 *name = 0;
389 u16 port = 0;
390#if CLIB_LINUX
391 int netns_fd = -1;
392#endif
393
394 s->fd = -1;
395
396 if (!s->config)
397 s->config = "";
398
399 for (int i = 0; i < ARRAY_LEN (clib_socket_type_data); i++)
400 {
401 typeof (clib_socket_type_data[0]) *d = clib_socket_type_data + i;
402
403 if (d->is_local == 0 && s->local_only)
404 continue;
405
406 if (strncmp (s->config, d->prefix, strlen (d->prefix)) == 0)
407 {
408 data = d;
409 break;
410 }
411 }
412
413 if (data == 0)
414 return clib_error_return (0, "unsupported socket config '%s'", s->config);
415
416 s->type = data->type;
417 p = s->config + (data->skip_prefix ? strlen (data->prefix) : 0);
418
419 name = _clib_socket_get_string (&p, data->type == CLIB_SOCKET_TYPE_INET);
420 vec_add1 (name, 0);
421
422 /* parse port type for INET sockets */
423 if (data->type == CLIB_SOCKET_TYPE_INET && p[0] == ':')
424 {
425 char *old_p = p + 1;
426 long long ll = strtoll (old_p, &p, 0);
427
428 if (p == old_p)
429 {
430 err = clib_error_return (0, "invalid port");
431 goto done;
432 }
433
434 if (ll > CLIB_U16_MAX || ll < 1)
435 {
436 err = clib_error_return (0, "port out of range");
437 goto done;
438 }
439 port = ll;
440 }
441
442 while (p[0] == ',')
443 {
444 p++;
445 if (0)
446 ;
447#if CLIB_LINUX
448 else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT && netns_fd == -1 &&
449 strncmp (p, "netns_name=", 11) == 0)
450 {
451 p += 11;
452 u8 *str = _clib_socket_get_string (&p, 0);
Mohsin Kazmi60a107c2023-02-15 13:31:27 +0000453 u8 *pathname = 0;
454 if (str[0] == '/')
455 pathname = format (0, "%v%c", str, 0);
456 else
457 pathname = format (0, "/var/run/netns/%v%c", str, 0);
Damjan Marion085757b2023-01-30 11:48:38 +0100458 if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
459 err = clib_error_return_unix (0, "open('%s')", pathname);
460 vec_free (str);
461 vec_free (pathname);
462 if (err)
463 goto done;
464 }
465 else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT && netns_fd == -1 &&
466 strncmp (p, "netns_pid=", 10) == 0)
467 {
468 char *old_p = p = p + 10;
469 u32 pid = (u32) strtol (old_p, &p, 0);
470
471 if (p == old_p)
472 err = clib_error_return (0, "invalid pid");
473 else
474 {
475 u8 *pathname = format (0, "/proc/%u/ns/net%c", pid, 0);
476 if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
477 err = clib_error_return_unix (0, "open('%s')", pathname);
478 vec_free (pathname);
479 }
480 if (err)
481 goto done;
482 }
483#endif
484 else
485 break;
486 }
487
488 if (p[0] != 0)
489 {
490 err = clib_error_return (0, "unknown input `%s'", p);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700491 goto done;
492 }
493
Damjan Marion085757b2023-01-30 11:48:38 +0100494#if CLIB_LINUX
495 /* change netns if requested */
496 if (s->type != CLIB_SOCKET_TYPE_INET && netns_fd != -1)
497 {
498 int fd = open ("/proc/self/ns/net", O_RDONLY);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700499
Damjan Marion085757b2023-01-30 11:48:38 +0100500 if (setns (netns_fd, CLONE_NEWNET) < 0)
501 {
502 close (fd);
503 err = clib_error_return_unix (0, "setns(%d)", netns_fd);
504 goto done;
505 }
506 netns_fd = fd;
507 }
508#endif
509
510 if (s->type == CLIB_SOCKET_TYPE_INET)
511 {
512 addr_len = sizeof (si);
513 si.sin_port = htons (port);
514
515 if (name)
516 {
517 struct in_addr host_addr;
518 vec_add1 (name, 0);
519
520 /* Recognize localhost to avoid host lookup in most common cast. */
521 if (!strcmp ((char *) name, "localhost"))
522 si.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
523
524 else if (inet_aton ((char *) name, &host_addr))
525 si.sin_addr = host_addr;
526
527 else if (strlen ((char *) name) > 0)
528 {
529 struct hostent *host = gethostbyname ((char *) name);
530 if (!host)
531 err = clib_error_return (0, "unknown host `%s'", name);
532 else
533 clib_memcpy (&si.sin_addr.s_addr, host->h_addr_list[0],
534 host->h_length);
535 }
536
537 else
538 si.sin_addr.s_addr =
539 htonl (s->is_server ? INADDR_LOOPBACK : INADDR_ANY);
540
541 if (err)
542 goto done;
543 }
544 sa = (struct sockaddr *) &si;
545 }
546 else if (s->type == CLIB_SOCKET_TYPE_UNIX)
547 {
548 struct stat st = { 0 };
549 char *path = (char *) &su.sun_path;
550
551 if (vec_len (name) > sizeof (su.sun_path) - 1)
552 {
553 err = clib_error_return (0, "File path '%v' too long", name);
554 goto done;
555 }
556
557 clib_memcpy (path, s->config, vec_len (name));
558 addr_len = sizeof (su);
559 sa = (struct sockaddr *) &su;
560
561 rv = stat (path, &st);
562 if (!s->is_server && rv < 0)
563 {
564 err = clib_error_return_unix (0, "stat ('%s')", path);
565 goto done;
566 }
567
568 if (s->is_server && rv == 0)
569 {
570 if (S_ISSOCK (st.st_mode))
571 {
572 int client_fd = socket (AF_UNIX, SOCK_STREAM, 0);
573 int ret = connect (client_fd, (const struct sockaddr *) &su,
574 sizeof (su));
575 typeof (errno) connect_errno = errno;
576 close (client_fd);
577
578 if (ret == 0 || (ret < 0 && connect_errno != ECONNREFUSED))
579 {
580 err = clib_error_return (0, "Active listener on '%s'", path);
581 goto done;
582 }
583
584 if (unlink (path) < 0)
585 {
586 err = clib_error_return_unix (0, "unlink ('%s')", path);
587 goto done;
588 }
589 }
590 else
591 {
592 err = clib_error_return (0, "File '%s' already exists", path);
593 goto done;
594 }
595 }
596 }
597#if CLIB_LINUX
598 else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT)
599 {
600 if (vec_len (name) > sizeof (su.sun_path) - 2)
601 {
602 err = clib_error_return (0, "Socket name '%v' too long", name);
603 goto done;
604 }
605
606 clib_memcpy (&su.sun_path[1], name, vec_len (name));
607 addr_len = sizeof (su.sun_family) + vec_len (name);
608 sa = (struct sockaddr *) &su;
609 s->allow_group_write = 0;
610 }
611#endif
612 else
613 {
614 err = clib_error_return_unix (0, "unknown socket family");
615 goto done;
616 }
617
618 socket_init_funcs (s);
619
620 if ((s->fd = socket (sa->sa_family,
621 s->is_seqpacket ? SOCK_SEQPACKET : SOCK_STREAM, 0)) < 0)
622 {
623 err =
624 clib_error_return_unix (0, "socket (fd %d, '%s')", s->fd, s->config);
625 goto done;
626 }
627
628 if (s->is_server)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700629 {
630 uword need_bind = 1;
631
Damjan Marion085757b2023-01-30 11:48:38 +0100632 if (sa->sa_family == AF_INET && si.sin_port == 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700633 {
Damjan Marion085757b2023-01-30 11:48:38 +0100634 word port = find_free_port (s->fd);
635 if (port < 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700636 {
Damjan Marion085757b2023-01-30 11:48:38 +0100637 err = clib_error_return (0, "no free port (fd %d, '%s')", s->fd,
638 s->config);
639 goto done;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700640 }
Damjan Marion085757b2023-01-30 11:48:38 +0100641 si.sin_port = port;
642 need_bind = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700643 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700644
Damjan Marion085757b2023-01-30 11:48:38 +0100645 if (setsockopt (s->fd, SOL_SOCKET, SO_REUSEADDR, &((int){ 1 }),
646 sizeof (int)) < 0)
647 clib_unix_warning ("setsockopt SO_REUSEADDR fails");
Ed Warnickecb9cada2015-12-08 15:45:58 -0700648
Damjan Marion085757b2023-01-30 11:48:38 +0100649#if CLIB_LINUX
650 if (sa->sa_family == AF_UNIX && s->passcred)
Damjan Marionf49921f2017-09-11 16:52:11 +0200651 {
Damjan Marion085757b2023-01-30 11:48:38 +0100652 if (setsockopt (s->fd, SOL_SOCKET, SO_PASSCRED, &((int){ 1 }),
653 sizeof (int)) < 0)
Damjan Marionf49921f2017-09-11 16:52:11 +0200654 {
Damjan Marion085757b2023-01-30 11:48:38 +0100655 err = clib_error_return_unix (0,
656 "setsockopt (SO_PASSCRED, "
657 "fd %d, '%s')",
658 s->fd, s->config);
Damjan Marionf49921f2017-09-11 16:52:11 +0200659 goto done;
660 }
661 }
Damjan Marion4dffd1c2018-09-03 12:30:36 +0200662#endif
Damjan Marionf49921f2017-09-11 16:52:11 +0200663
Damjan Marion085757b2023-01-30 11:48:38 +0100664 if (need_bind && bind (s->fd, sa, addr_len) < 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700665 {
Damjan Marion085757b2023-01-30 11:48:38 +0100666 err =
667 clib_error_return_unix (0, "bind (fd %d, '%s')", s->fd, s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700668 goto done;
669 }
670
671 if (listen (s->fd, 5) < 0)
672 {
Damjan Marion085757b2023-01-30 11:48:38 +0100673 err = clib_error_return_unix (0, "listen (fd %d, '%s')", s->fd,
674 s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700675 goto done;
676 }
Damjan Marion085757b2023-01-30 11:48:38 +0100677
678 if (s->local_only && s->allow_group_write)
Damjan Marionf6616382017-06-21 12:01:37 +0200679 {
Damjan Marion085757b2023-01-30 11:48:38 +0100680 if (fchmod (s->fd, S_IWGRP) < 0)
Chris Lukeab7b8d92017-09-07 07:40:13 -0400681 {
Damjan Marion085757b2023-01-30 11:48:38 +0100682 err = clib_error_return_unix (
683 0, "fchmod (fd %d, '%s', mode S_IWGRP)", s->fd, s->config);
Chris Lukeab7b8d92017-09-07 07:40:13 -0400684 goto done;
685 }
Damjan Marionf6616382017-06-21 12:01:37 +0200686 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700687 }
688 else
689 {
Damjan Marion085757b2023-01-30 11:48:38 +0100690 if (s->non_blocking_connect && fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700691 {
Damjan Marion085757b2023-01-30 11:48:38 +0100692 err = clib_error_return_unix (0, "fcntl NONBLOCK (fd %d, '%s')",
693 s->fd, s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700694 goto done;
695 }
696
Damjan Marion085757b2023-01-30 11:48:38 +0100697 while ((rv = connect (s->fd, sa, addr_len)) < 0 && errno == EAGAIN)
Florin Coras42ddf692020-01-06 20:23:07 +0000698 ;
Damjan Marion085757b2023-01-30 11:48:38 +0100699 if (rv < 0 && !(s->non_blocking_connect && errno == EINPROGRESS))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700700 {
Damjan Marion085757b2023-01-30 11:48:38 +0100701 err = clib_error_return_unix (0, "connect (fd %d, '%s')", s->fd,
702 s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700703 goto done;
704 }
Florin Coras57e0af92021-05-26 10:21:10 -0700705 /* Connect was blocking so set fd to non-blocking now unless
706 * blocking mode explicitly requested. */
Damjan Marion085757b2023-01-30 11:48:38 +0100707 if (!s->non_blocking_connect && !s->is_blocking &&
Florin Coras5fe94572021-05-24 08:58:15 -0700708 fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
709 {
Damjan Marion085757b2023-01-30 11:48:38 +0100710 err = clib_error_return_unix (0, "fcntl NONBLOCK2 (fd %d, '%s')",
711 s->fd, s->config);
Florin Coras5fe94572021-05-24 08:58:15 -0700712 goto done;
713 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700714 }
715
Dave Barachc3799992016-08-15 11:12:27 -0400716done:
Damjan Marion085757b2023-01-30 11:48:38 +0100717 if (err && s->fd > -1)
718 {
719 close (s->fd);
720 s->fd = -1;
721 }
722#if CLIB_LINUX
723 if (netns_fd != -1)
724 {
725 setns (CLONE_NEWNET, netns_fd);
726 close (netns_fd);
727 }
728#endif
729 vec_free (name);
730 return err;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700731}
732
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200733__clib_export clib_error_t *
Nathan Skrzypczak4cef6de2021-07-19 18:21:43 +0200734clib_socket_init_netns (clib_socket_t *s, u8 *namespace)
735{
736 if (namespace == NULL || namespace[0] == 0)
737 return clib_socket_init (s);
738
739 clib_error_t *error;
Nathan Skrzypczak9218c602021-07-27 19:51:27 +0200740 int old_netns_fd, nfd = -1;
Nathan Skrzypczak4cef6de2021-07-19 18:21:43 +0200741
742 old_netns_fd = clib_netns_open (NULL /* self */);
Florin Coras807868d2021-10-12 08:52:12 -0700743 if (old_netns_fd < 0)
744 return clib_error_return_unix (0, "get current netns failed");
745
Nathan Skrzypczak4cef6de2021-07-19 18:21:43 +0200746 if ((nfd = clib_netns_open (namespace)) == -1)
747 {
748 error = clib_error_return_unix (0, "clib_netns_open '%s'", namespace);
749 goto done;
750 }
751
752 if (clib_setns (nfd) == -1)
753 {
754 error = clib_error_return_unix (0, "setns '%s'", namespace);
755 goto done;
756 }
757
758 error = clib_socket_init (s);
759
760done:
761 if (clib_setns (old_netns_fd) == -1)
762 clib_warning ("Cannot set old ns");
Nathan Skrzypczak9218c602021-07-27 19:51:27 +0200763
Nathan Skrzypczak4cef6de2021-07-19 18:21:43 +0200764 close (old_netns_fd);
765
Nathan Skrzypczak9218c602021-07-27 19:51:27 +0200766 if (-1 != nfd)
767 close (nfd);
768
Nathan Skrzypczak4cef6de2021-07-19 18:21:43 +0200769 return error;
770}
771
772__clib_export clib_error_t *
Dave Barachc3799992016-08-15 11:12:27 -0400773clib_socket_accept (clib_socket_t * server, clib_socket_t * client)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700774{
Dave Barachc3799992016-08-15 11:12:27 -0400775 clib_error_t *err = 0;
776 socklen_t len = 0;
777
Dave Barachb7b92992018-10-17 10:38:51 -0400778 clib_memset (client, 0, sizeof (client[0]));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700779
780 /* Accept the new socket connection. */
781 client->fd = accept (server->fd, 0, 0);
Dave Barachc3799992016-08-15 11:12:27 -0400782 if (client->fd < 0)
Dave Wallace70ec09d2017-09-06 16:45:04 -0400783 return clib_error_return_unix (0, "accept (fd %d, '%s')",
784 server->fd, server->config);
Dave Barachc3799992016-08-15 11:12:27 -0400785
Ed Warnickecb9cada2015-12-08 15:45:58 -0700786 /* Set the new socket to be non-blocking. */
787 if (fcntl (client->fd, F_SETFL, O_NONBLOCK) < 0)
788 {
Dave Wallace70ec09d2017-09-06 16:45:04 -0400789 err = clib_error_return_unix (0, "fcntl O_NONBLOCK (fd %d)",
790 client->fd);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700791 goto close_client;
792 }
Dave Barachc3799992016-08-15 11:12:27 -0400793
Ed Warnickecb9cada2015-12-08 15:45:58 -0700794 /* Get peer info. */
795 len = sizeof (client->peer);
796 if (getpeername (client->fd, (struct sockaddr *) &client->peer, &len) < 0)
797 {
Dave Wallace70ec09d2017-09-06 16:45:04 -0400798 err = clib_error_return_unix (0, "getpeername (fd %d)", client->fd);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700799 goto close_client;
800 }
801
Damjan Marionf49921f2017-09-11 16:52:11 +0200802 client->flags = CLIB_SOCKET_F_IS_CLIENT;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700803
804 socket_init_funcs (client);
805 return 0;
806
Dave Barachc3799992016-08-15 11:12:27 -0400807close_client:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700808 close (client->fd);
809 return err;
810}
Dave Barachc3799992016-08-15 11:12:27 -0400811
812/*
813 * fd.io coding-style-patch-verification: ON
814 *
815 * Local Variables:
816 * eval: (c-set-style "gnu")
817 * End:
818 */