blob: e61c369f050e77b96c035ca3a0cbc64f2ca200a2 [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);
453 u8 *pathname = format (0, "/var/run/netns/%v%c", str, 0);
454 if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
455 err = clib_error_return_unix (0, "open('%s')", pathname);
456 vec_free (str);
457 vec_free (pathname);
458 if (err)
459 goto done;
460 }
461 else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT && netns_fd == -1 &&
462 strncmp (p, "netns_pid=", 10) == 0)
463 {
464 char *old_p = p = p + 10;
465 u32 pid = (u32) strtol (old_p, &p, 0);
466
467 if (p == old_p)
468 err = clib_error_return (0, "invalid pid");
469 else
470 {
471 u8 *pathname = format (0, "/proc/%u/ns/net%c", pid, 0);
472 if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
473 err = clib_error_return_unix (0, "open('%s')", pathname);
474 vec_free (pathname);
475 }
476 if (err)
477 goto done;
478 }
479#endif
480 else
481 break;
482 }
483
484 if (p[0] != 0)
485 {
486 err = clib_error_return (0, "unknown input `%s'", p);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700487 goto done;
488 }
489
Damjan Marion085757b2023-01-30 11:48:38 +0100490#if CLIB_LINUX
491 /* change netns if requested */
492 if (s->type != CLIB_SOCKET_TYPE_INET && netns_fd != -1)
493 {
494 int fd = open ("/proc/self/ns/net", O_RDONLY);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700495
Damjan Marion085757b2023-01-30 11:48:38 +0100496 if (setns (netns_fd, CLONE_NEWNET) < 0)
497 {
498 close (fd);
499 err = clib_error_return_unix (0, "setns(%d)", netns_fd);
500 goto done;
501 }
502 netns_fd = fd;
503 }
504#endif
505
506 if (s->type == CLIB_SOCKET_TYPE_INET)
507 {
508 addr_len = sizeof (si);
509 si.sin_port = htons (port);
510
511 if (name)
512 {
513 struct in_addr host_addr;
514 vec_add1 (name, 0);
515
516 /* Recognize localhost to avoid host lookup in most common cast. */
517 if (!strcmp ((char *) name, "localhost"))
518 si.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
519
520 else if (inet_aton ((char *) name, &host_addr))
521 si.sin_addr = host_addr;
522
523 else if (strlen ((char *) name) > 0)
524 {
525 struct hostent *host = gethostbyname ((char *) name);
526 if (!host)
527 err = clib_error_return (0, "unknown host `%s'", name);
528 else
529 clib_memcpy (&si.sin_addr.s_addr, host->h_addr_list[0],
530 host->h_length);
531 }
532
533 else
534 si.sin_addr.s_addr =
535 htonl (s->is_server ? INADDR_LOOPBACK : INADDR_ANY);
536
537 if (err)
538 goto done;
539 }
540 sa = (struct sockaddr *) &si;
541 }
542 else if (s->type == CLIB_SOCKET_TYPE_UNIX)
543 {
544 struct stat st = { 0 };
545 char *path = (char *) &su.sun_path;
546
547 if (vec_len (name) > sizeof (su.sun_path) - 1)
548 {
549 err = clib_error_return (0, "File path '%v' too long", name);
550 goto done;
551 }
552
553 clib_memcpy (path, s->config, vec_len (name));
554 addr_len = sizeof (su);
555 sa = (struct sockaddr *) &su;
556
557 rv = stat (path, &st);
558 if (!s->is_server && rv < 0)
559 {
560 err = clib_error_return_unix (0, "stat ('%s')", path);
561 goto done;
562 }
563
564 if (s->is_server && rv == 0)
565 {
566 if (S_ISSOCK (st.st_mode))
567 {
568 int client_fd = socket (AF_UNIX, SOCK_STREAM, 0);
569 int ret = connect (client_fd, (const struct sockaddr *) &su,
570 sizeof (su));
571 typeof (errno) connect_errno = errno;
572 close (client_fd);
573
574 if (ret == 0 || (ret < 0 && connect_errno != ECONNREFUSED))
575 {
576 err = clib_error_return (0, "Active listener on '%s'", path);
577 goto done;
578 }
579
580 if (unlink (path) < 0)
581 {
582 err = clib_error_return_unix (0, "unlink ('%s')", path);
583 goto done;
584 }
585 }
586 else
587 {
588 err = clib_error_return (0, "File '%s' already exists", path);
589 goto done;
590 }
591 }
592 }
593#if CLIB_LINUX
594 else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT)
595 {
596 if (vec_len (name) > sizeof (su.sun_path) - 2)
597 {
598 err = clib_error_return (0, "Socket name '%v' too long", name);
599 goto done;
600 }
601
602 clib_memcpy (&su.sun_path[1], name, vec_len (name));
603 addr_len = sizeof (su.sun_family) + vec_len (name);
604 sa = (struct sockaddr *) &su;
605 s->allow_group_write = 0;
606 }
607#endif
608 else
609 {
610 err = clib_error_return_unix (0, "unknown socket family");
611 goto done;
612 }
613
614 socket_init_funcs (s);
615
616 if ((s->fd = socket (sa->sa_family,
617 s->is_seqpacket ? SOCK_SEQPACKET : SOCK_STREAM, 0)) < 0)
618 {
619 err =
620 clib_error_return_unix (0, "socket (fd %d, '%s')", s->fd, s->config);
621 goto done;
622 }
623
624 if (s->is_server)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700625 {
626 uword need_bind = 1;
627
Damjan Marion085757b2023-01-30 11:48:38 +0100628 if (sa->sa_family == AF_INET && si.sin_port == 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700629 {
Damjan Marion085757b2023-01-30 11:48:38 +0100630 word port = find_free_port (s->fd);
631 if (port < 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700632 {
Damjan Marion085757b2023-01-30 11:48:38 +0100633 err = clib_error_return (0, "no free port (fd %d, '%s')", s->fd,
634 s->config);
635 goto done;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700636 }
Damjan Marion085757b2023-01-30 11:48:38 +0100637 si.sin_port = port;
638 need_bind = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700639 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700640
Damjan Marion085757b2023-01-30 11:48:38 +0100641 if (setsockopt (s->fd, SOL_SOCKET, SO_REUSEADDR, &((int){ 1 }),
642 sizeof (int)) < 0)
643 clib_unix_warning ("setsockopt SO_REUSEADDR fails");
Ed Warnickecb9cada2015-12-08 15:45:58 -0700644
Damjan Marion085757b2023-01-30 11:48:38 +0100645#if CLIB_LINUX
646 if (sa->sa_family == AF_UNIX && s->passcred)
Damjan Marionf49921f2017-09-11 16:52:11 +0200647 {
Damjan Marion085757b2023-01-30 11:48:38 +0100648 if (setsockopt (s->fd, SOL_SOCKET, SO_PASSCRED, &((int){ 1 }),
649 sizeof (int)) < 0)
Damjan Marionf49921f2017-09-11 16:52:11 +0200650 {
Damjan Marion085757b2023-01-30 11:48:38 +0100651 err = clib_error_return_unix (0,
652 "setsockopt (SO_PASSCRED, "
653 "fd %d, '%s')",
654 s->fd, s->config);
Damjan Marionf49921f2017-09-11 16:52:11 +0200655 goto done;
656 }
657 }
Damjan Marion4dffd1c2018-09-03 12:30:36 +0200658#endif
Damjan Marionf49921f2017-09-11 16:52:11 +0200659
Damjan Marion085757b2023-01-30 11:48:38 +0100660 if (need_bind && bind (s->fd, sa, addr_len) < 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700661 {
Damjan Marion085757b2023-01-30 11:48:38 +0100662 err =
663 clib_error_return_unix (0, "bind (fd %d, '%s')", s->fd, s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700664 goto done;
665 }
666
667 if (listen (s->fd, 5) < 0)
668 {
Damjan Marion085757b2023-01-30 11:48:38 +0100669 err = clib_error_return_unix (0, "listen (fd %d, '%s')", s->fd,
670 s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700671 goto done;
672 }
Damjan Marion085757b2023-01-30 11:48:38 +0100673
674 if (s->local_only && s->allow_group_write)
Damjan Marionf6616382017-06-21 12:01:37 +0200675 {
Damjan Marion085757b2023-01-30 11:48:38 +0100676 if (fchmod (s->fd, S_IWGRP) < 0)
Chris Lukeab7b8d92017-09-07 07:40:13 -0400677 {
Damjan Marion085757b2023-01-30 11:48:38 +0100678 err = clib_error_return_unix (
679 0, "fchmod (fd %d, '%s', mode S_IWGRP)", s->fd, s->config);
Chris Lukeab7b8d92017-09-07 07:40:13 -0400680 goto done;
681 }
Damjan Marionf6616382017-06-21 12:01:37 +0200682 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700683 }
684 else
685 {
Damjan Marion085757b2023-01-30 11:48:38 +0100686 if (s->non_blocking_connect && fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700687 {
Damjan Marion085757b2023-01-30 11:48:38 +0100688 err = clib_error_return_unix (0, "fcntl NONBLOCK (fd %d, '%s')",
689 s->fd, s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700690 goto done;
691 }
692
Damjan Marion085757b2023-01-30 11:48:38 +0100693 while ((rv = connect (s->fd, sa, addr_len)) < 0 && errno == EAGAIN)
Florin Coras42ddf692020-01-06 20:23:07 +0000694 ;
Damjan Marion085757b2023-01-30 11:48:38 +0100695 if (rv < 0 && !(s->non_blocking_connect && errno == EINPROGRESS))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700696 {
Damjan Marion085757b2023-01-30 11:48:38 +0100697 err = clib_error_return_unix (0, "connect (fd %d, '%s')", s->fd,
698 s->config);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700699 goto done;
700 }
Florin Coras57e0af92021-05-26 10:21:10 -0700701 /* Connect was blocking so set fd to non-blocking now unless
702 * blocking mode explicitly requested. */
Damjan Marion085757b2023-01-30 11:48:38 +0100703 if (!s->non_blocking_connect && !s->is_blocking &&
Florin Coras5fe94572021-05-24 08:58:15 -0700704 fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
705 {
Damjan Marion085757b2023-01-30 11:48:38 +0100706 err = clib_error_return_unix (0, "fcntl NONBLOCK2 (fd %d, '%s')",
707 s->fd, s->config);
Florin Coras5fe94572021-05-24 08:58:15 -0700708 goto done;
709 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700710 }
711
Dave Barachc3799992016-08-15 11:12:27 -0400712done:
Damjan Marion085757b2023-01-30 11:48:38 +0100713 if (err && s->fd > -1)
714 {
715 close (s->fd);
716 s->fd = -1;
717 }
718#if CLIB_LINUX
719 if (netns_fd != -1)
720 {
721 setns (CLONE_NEWNET, netns_fd);
722 close (netns_fd);
723 }
724#endif
725 vec_free (name);
726 return err;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700727}
728
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200729__clib_export clib_error_t *
Nathan Skrzypczak4cef6de2021-07-19 18:21:43 +0200730clib_socket_init_netns (clib_socket_t *s, u8 *namespace)
731{
732 if (namespace == NULL || namespace[0] == 0)
733 return clib_socket_init (s);
734
735 clib_error_t *error;
Nathan Skrzypczak9218c602021-07-27 19:51:27 +0200736 int old_netns_fd, nfd = -1;
Nathan Skrzypczak4cef6de2021-07-19 18:21:43 +0200737
738 old_netns_fd = clib_netns_open (NULL /* self */);
Florin Coras807868d2021-10-12 08:52:12 -0700739 if (old_netns_fd < 0)
740 return clib_error_return_unix (0, "get current netns failed");
741
Nathan Skrzypczak4cef6de2021-07-19 18:21:43 +0200742 if ((nfd = clib_netns_open (namespace)) == -1)
743 {
744 error = clib_error_return_unix (0, "clib_netns_open '%s'", namespace);
745 goto done;
746 }
747
748 if (clib_setns (nfd) == -1)
749 {
750 error = clib_error_return_unix (0, "setns '%s'", namespace);
751 goto done;
752 }
753
754 error = clib_socket_init (s);
755
756done:
757 if (clib_setns (old_netns_fd) == -1)
758 clib_warning ("Cannot set old ns");
Nathan Skrzypczak9218c602021-07-27 19:51:27 +0200759
Nathan Skrzypczak4cef6de2021-07-19 18:21:43 +0200760 close (old_netns_fd);
761
Nathan Skrzypczak9218c602021-07-27 19:51:27 +0200762 if (-1 != nfd)
763 close (nfd);
764
Nathan Skrzypczak4cef6de2021-07-19 18:21:43 +0200765 return error;
766}
767
768__clib_export clib_error_t *
Dave Barachc3799992016-08-15 11:12:27 -0400769clib_socket_accept (clib_socket_t * server, clib_socket_t * client)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700770{
Dave Barachc3799992016-08-15 11:12:27 -0400771 clib_error_t *err = 0;
772 socklen_t len = 0;
773
Dave Barachb7b92992018-10-17 10:38:51 -0400774 clib_memset (client, 0, sizeof (client[0]));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700775
776 /* Accept the new socket connection. */
777 client->fd = accept (server->fd, 0, 0);
Dave Barachc3799992016-08-15 11:12:27 -0400778 if (client->fd < 0)
Dave Wallace70ec09d2017-09-06 16:45:04 -0400779 return clib_error_return_unix (0, "accept (fd %d, '%s')",
780 server->fd, server->config);
Dave Barachc3799992016-08-15 11:12:27 -0400781
Ed Warnickecb9cada2015-12-08 15:45:58 -0700782 /* Set the new socket to be non-blocking. */
783 if (fcntl (client->fd, F_SETFL, O_NONBLOCK) < 0)
784 {
Dave Wallace70ec09d2017-09-06 16:45:04 -0400785 err = clib_error_return_unix (0, "fcntl O_NONBLOCK (fd %d)",
786 client->fd);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700787 goto close_client;
788 }
Dave Barachc3799992016-08-15 11:12:27 -0400789
Ed Warnickecb9cada2015-12-08 15:45:58 -0700790 /* Get peer info. */
791 len = sizeof (client->peer);
792 if (getpeername (client->fd, (struct sockaddr *) &client->peer, &len) < 0)
793 {
Dave Wallace70ec09d2017-09-06 16:45:04 -0400794 err = clib_error_return_unix (0, "getpeername (fd %d)", client->fd);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700795 goto close_client;
796 }
797
Damjan Marionf49921f2017-09-11 16:52:11 +0200798 client->flags = CLIB_SOCKET_F_IS_CLIENT;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700799
800 socket_init_funcs (client);
801 return 0;
802
Dave Barachc3799992016-08-15 11:12:27 -0400803close_client:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700804 close (client->fd);
805 return err;
806}
Dave Barachc3799992016-08-15 11:12:27 -0400807
808/*
809 * fd.io coding-style-patch-verification: ON
810 *
811 * Local Variables:
812 * eval: (c-set-style "gnu")
813 * End:
814 */