Line data Source code
1 : /*
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 :
38 : #include <stdio.h>
39 : #include <string.h> /* strchr */
40 : #define __USE_GNU
41 : #define _GNU_SOURCE
42 : #include <sys/types.h>
43 : #include <sys/socket.h>
44 : #include <sys/un.h>
45 : #include <sys/stat.h>
46 : #include <netinet/in.h>
47 : #include <arpa/inet.h>
48 : #include <netdb.h>
49 : #include <unistd.h>
50 : #include <fcntl.h>
51 :
52 : #include <vppinfra/mem.h>
53 : #include <vppinfra/vec.h>
54 : #include <vppinfra/socket.h>
55 : #include <vppinfra/linux/netns.h>
56 : #include <vppinfra/format.h>
57 : #include <vppinfra/error.h>
58 :
59 : #ifndef __GLIBC__
60 : /* IPPORT_USERRESERVED is not part of musl libc. */
61 : #define IPPORT_USERRESERVED 5000
62 : #endif
63 :
64 : __clib_export void
65 0 : clib_socket_tx_add_formatted (clib_socket_t * s, char *fmt, ...)
66 : {
67 : va_list va;
68 0 : va_start (va, fmt);
69 0 : clib_socket_tx_add_va_formatted (s, fmt, &va);
70 0 : va_end (va);
71 0 : }
72 :
73 : /* Return and bind to an unused port. */
74 : static word
75 0 : find_free_port (word sock)
76 : {
77 : word port;
78 :
79 0 : for (port = IPPORT_USERRESERVED; port < 1 << 16; port++)
80 : {
81 : struct sockaddr_in a;
82 :
83 0 : clib_memset (&a, 0, sizeof (a)); /* Warnings be gone */
84 :
85 0 : a.sin_family = PF_INET;
86 0 : a.sin_addr.s_addr = INADDR_ANY;
87 0 : a.sin_port = htons (port);
88 :
89 0 : if (bind (sock, (struct sockaddr *) &a, sizeof (a)) >= 0)
90 0 : break;
91 : }
92 :
93 0 : return port < 1 << 16 ? port : -1;
94 : }
95 :
96 : static clib_error_t *
97 0 : default_socket_write (clib_socket_t * s)
98 : {
99 0 : clib_error_t *err = 0;
100 0 : word written = 0;
101 0 : word fd = 0;
102 : word tx_len;
103 :
104 0 : fd = s->fd;
105 :
106 : /* Map standard input to standard output.
107 : Typically, fd is a socket for which read/write both work. */
108 0 : if (fd == 0)
109 0 : fd = 1;
110 :
111 0 : tx_len = vec_len (s->tx_buffer);
112 0 : written = write (fd, s->tx_buffer, tx_len);
113 :
114 : /* Ignore certain errors. */
115 0 : if (written < 0 && !unix_error_is_fatal (errno))
116 0 : written = 0;
117 :
118 : /* A "real" error occurred. */
119 0 : if (written < 0)
120 : {
121 0 : err = clib_error_return_unix (0, "write %wd bytes (fd %d, '%s')",
122 : tx_len, s->fd, s->config);
123 0 : vec_free (s->tx_buffer);
124 0 : goto done;
125 : }
126 :
127 : /* Reclaim the transmitted part of the tx buffer on successful writes. */
128 0 : else if (written > 0)
129 : {
130 0 : if (written == tx_len)
131 0 : vec_set_len (s->tx_buffer, 0);
132 : else
133 0 : 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. */
138 0 : else if (written == 0 && tx_len > 64 * 1024)
139 : {
140 0 : vec_free (s->tx_buffer);
141 : }
142 :
143 0 : done:
144 0 : return err;
145 : }
146 :
147 : static clib_error_t *
148 0 : default_socket_read (clib_socket_t * sock, int n_bytes)
149 : {
150 : word fd, n_read;
151 : u8 *buf;
152 :
153 : /* RX side of socket is down once end of file is reached. */
154 0 : if (sock->rx_end_of_file)
155 0 : return 0;
156 :
157 0 : fd = sock->fd;
158 :
159 0 : n_bytes = clib_max (n_bytes, 4096);
160 0 : vec_add2 (sock->rx_buffer, buf, n_bytes);
161 :
162 0 : if ((n_read = read (fd, buf, n_bytes)) < 0)
163 : {
164 0 : n_read = 0;
165 :
166 : /* Ignore certain errors. */
167 0 : if (!unix_error_is_fatal (errno))
168 0 : goto non_fatal;
169 :
170 0 : return clib_error_return_unix (0, "read %d bytes (fd %d, '%s')",
171 : n_bytes, sock->fd, sock->config);
172 : }
173 :
174 : /* Other side closed the socket. */
175 0 : if (n_read == 0)
176 0 : sock->rx_end_of_file = 1;
177 :
178 0 : non_fatal:
179 0 : vec_inc_len (sock->rx_buffer, n_read - n_bytes);
180 :
181 0 : return 0;
182 : }
183 :
184 : static clib_error_t *
185 575 : default_socket_close (clib_socket_t * s)
186 : {
187 575 : if (close (s->fd) < 0)
188 14 : return clib_error_return_unix (0, "close (fd %d, %s)", s->fd, s->config);
189 561 : return 0;
190 : }
191 :
192 : static clib_error_t *
193 719 : default_socket_sendmsg (clib_socket_t * s, void *msg, int msglen,
194 : int fds[], int num_fds)
195 719 : {
196 719 : struct msghdr mh = { 0 };
197 : struct iovec iov[1];
198 719 : char ctl[CMSG_SPACE (sizeof (int) * num_fds)];
199 : int rv;
200 :
201 719 : iov[0].iov_base = msg;
202 719 : iov[0].iov_len = msglen;
203 719 : mh.msg_iov = iov;
204 719 : mh.msg_iovlen = 1;
205 :
206 719 : if (num_fds > 0)
207 : {
208 : struct cmsghdr *cmsg;
209 600 : clib_memset (&ctl, 0, sizeof (ctl));
210 600 : mh.msg_control = ctl;
211 600 : mh.msg_controllen = sizeof (ctl);
212 600 : cmsg = CMSG_FIRSTHDR (&mh);
213 600 : cmsg->cmsg_len = CMSG_LEN (sizeof (int) * num_fds);
214 600 : cmsg->cmsg_level = SOL_SOCKET;
215 600 : cmsg->cmsg_type = SCM_RIGHTS;
216 600 : memcpy (CMSG_DATA (cmsg), fds, sizeof (int) * num_fds);
217 : }
218 719 : rv = sendmsg (s->fd, &mh, 0);
219 719 : if (rv < 0)
220 6 : return clib_error_return_unix (0, "sendmsg");
221 713 : return 0;
222 : }
223 :
224 :
225 : static clib_error_t *
226 172 : default_socket_recvmsg (clib_socket_t * s, void *msg, int msglen,
227 : int fds[], int num_fds)
228 172 : {
229 : #ifdef CLIB_LINUX
230 172 : char ctl[CMSG_SPACE (sizeof (int) * num_fds) +
231 : CMSG_SPACE (sizeof (struct ucred))];
232 172 : struct ucred *cr = 0;
233 : #else
234 : char ctl[CMSG_SPACE (sizeof (int) * num_fds)];
235 : #endif
236 172 : struct msghdr mh = { 0 };
237 : struct iovec iov[1];
238 : ssize_t size;
239 : struct cmsghdr *cmsg;
240 :
241 172 : iov[0].iov_base = msg;
242 172 : iov[0].iov_len = msglen;
243 172 : mh.msg_iov = iov;
244 172 : mh.msg_iovlen = 1;
245 172 : mh.msg_control = ctl;
246 172 : mh.msg_controllen = sizeof (ctl);
247 :
248 172 : clib_memset (ctl, 0, sizeof (ctl));
249 :
250 : /* receive the incoming message */
251 172 : size = recvmsg (s->fd, &mh, 0);
252 172 : if (size != msglen)
253 : {
254 8 : return (size == 0) ? clib_error_return (0, "disconnected") :
255 0 : clib_error_return_unix (0, "recvmsg: malformed message (fd %d, '%s')",
256 : s->fd, s->config);
257 : }
258 :
259 164 : cmsg = CMSG_FIRSTHDR (&mh);
260 294 : while (cmsg)
261 : {
262 130 : if (cmsg->cmsg_level == SOL_SOCKET)
263 : {
264 : #ifdef CLIB_LINUX
265 130 : if (cmsg->cmsg_type == SCM_CREDENTIALS)
266 : {
267 81 : cr = (struct ucred *) CMSG_DATA (cmsg);
268 81 : s->uid = cr->uid;
269 81 : s->gid = cr->gid;
270 81 : s->pid = cr->pid;
271 : }
272 : else
273 : #endif
274 49 : if (cmsg->cmsg_type == SCM_RIGHTS)
275 : {
276 49 : clib_memcpy_fast (fds, CMSG_DATA (cmsg),
277 : num_fds * sizeof (int));
278 : }
279 : }
280 130 : cmsg = CMSG_NXTHDR (&mh, cmsg);
281 : }
282 164 : return 0;
283 : }
284 :
285 : static void
286 3008 : socket_init_funcs (clib_socket_t * s)
287 : {
288 3008 : if (!s->write_func)
289 3008 : s->write_func = default_socket_write;
290 3008 : if (!s->read_func)
291 3008 : s->read_func = default_socket_read;
292 3008 : if (!s->close_func)
293 3008 : s->close_func = default_socket_close;
294 3008 : if (!s->sendmsg_func)
295 3008 : s->sendmsg_func = default_socket_sendmsg;
296 3008 : if (!s->recvmsg_func)
297 3008 : s->recvmsg_func = default_socket_recvmsg;
298 3008 : }
299 :
300 : static const struct
301 : {
302 : 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 : };
338 :
339 : static u8 *
340 1820 : _clib_socket_get_string (char **p, int is_hostname)
341 : {
342 1820 : u8 *s = 0;
343 98464 : while (**p)
344 : {
345 96644 : switch (**p)
346 : {
347 3892 : case '_':
348 3892 : if (is_hostname)
349 0 : return s;
350 : case 'a' ... 'z':
351 : case 'A' ... 'Z':
352 : case '0' ... '9':
353 : case '/':
354 : case '-':
355 : case '.':
356 96644 : vec_add1 (s, **p);
357 96644 : (*p)++;
358 96644 : break;
359 : break;
360 0 : default:
361 0 : return s;
362 : }
363 : }
364 1820 : return s;
365 : }
366 :
367 : __clib_export int
368 590 : clib_socket_prefix_is_valid (char *s)
369 : {
370 590 : for (typeof (clib_socket_type_data[0]) *d = clib_socket_type_data;
371 4130 : d - clib_socket_type_data < ARRAY_LEN (clib_socket_type_data); d++)
372 3540 : if (d->skip_prefix && strncmp (s, d->prefix, strlen (d->prefix)) == 0)
373 0 : return 1;
374 590 : return 0;
375 : }
376 :
377 : __clib_export int
378 70 : clib_socket_prefix_get_type (char *s)
379 : {
380 70 : for (typeof (clib_socket_type_data[0]) *d = clib_socket_type_data;
381 280 : d - clib_socket_type_data < ARRAY_LEN (clib_socket_type_data); d++)
382 280 : if (strncmp (s, d->prefix, strlen (d->prefix)) == 0)
383 70 : return d->type;
384 0 : return 0;
385 : }
386 :
387 : __clib_export clib_error_t *
388 1820 : clib_socket_init (clib_socket_t *s)
389 : {
390 1820 : struct sockaddr_un su = { .sun_family = AF_UNIX };
391 1820 : struct sockaddr_in si = { .sin_family = AF_INET };
392 1820 : struct sockaddr *sa = 0;
393 1820 : typeof (clib_socket_type_data[0]) *data = 0;
394 1820 : socklen_t addr_len = 0;
395 : int rv;
396 : char *p;
397 1820 : clib_error_t *err = 0;
398 1820 : u8 *name = 0;
399 1820 : u16 port = 0;
400 : #if CLIB_LINUX
401 1820 : int netns_fd = -1;
402 : #endif
403 :
404 1820 : s->fd = -1;
405 :
406 1820 : if (!s->config)
407 0 : s->config = "";
408 :
409 7280 : for (int i = 0; i < ARRAY_LEN (clib_socket_type_data); i++)
410 : {
411 7280 : typeof (clib_socket_type_data[0]) *d = clib_socket_type_data + i;
412 :
413 7280 : if (d->is_local == 0 && s->local_only)
414 11 : continue;
415 :
416 7269 : if (strncmp (s->config, d->prefix, strlen (d->prefix)) == 0)
417 : {
418 1820 : data = d;
419 1820 : break;
420 : }
421 : }
422 :
423 1820 : if (data == 0)
424 0 : return clib_error_return (0, "unsupported socket config '%s'", s->config);
425 :
426 1820 : s->type = data->type;
427 1820 : p = s->config + (data->skip_prefix ? strlen (data->prefix) : 0);
428 :
429 1820 : name = _clib_socket_get_string (&p, data->type == CLIB_SOCKET_TYPE_INET);
430 1820 : vec_add1 (name, 0);
431 :
432 : /* parse port type for INET sockets */
433 1820 : if (data->type == CLIB_SOCKET_TYPE_INET && p[0] == ':')
434 : {
435 0 : char *old_p = p + 1;
436 0 : long long ll = strtoll (old_p, &p, 0);
437 :
438 0 : if (p == old_p)
439 : {
440 0 : err = clib_error_return (0, "invalid port");
441 0 : goto done;
442 : }
443 :
444 0 : if (ll > CLIB_U16_MAX || ll < 1)
445 : {
446 0 : err = clib_error_return (0, "port out of range");
447 0 : goto done;
448 : }
449 0 : port = ll;
450 : }
451 :
452 1820 : while (p[0] == ',')
453 : {
454 0 : p++;
455 : if (0)
456 : ;
457 : #if CLIB_LINUX
458 0 : else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT && netns_fd == -1 &&
459 0 : strncmp (p, "netns_name=", 11) == 0)
460 0 : {
461 0 : p += 11;
462 0 : u8 *str = _clib_socket_get_string (&p, 0);
463 0 : u8 *pathname = 0;
464 0 : if (str[0] == '/')
465 0 : pathname = format (0, "%v%c", str, 0);
466 : else
467 0 : pathname = format (0, "/var/run/netns/%v%c", str, 0);
468 0 : if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
469 0 : err = clib_error_return_unix (0, "open('%s')", pathname);
470 0 : vec_free (str);
471 0 : vec_free (pathname);
472 0 : if (err)
473 0 : goto done;
474 : }
475 0 : else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT && netns_fd == -1 &&
476 0 : strncmp (p, "netns_pid=", 10) == 0)
477 : {
478 0 : char *old_p = p = p + 10;
479 0 : u32 pid = (u32) strtol (old_p, &p, 0);
480 :
481 0 : if (p == old_p)
482 0 : err = clib_error_return (0, "invalid pid");
483 : else
484 : {
485 0 : u8 *pathname = format (0, "/proc/%u/ns/net%c", pid, 0);
486 0 : if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
487 0 : err = clib_error_return_unix (0, "open('%s')", pathname);
488 0 : vec_free (pathname);
489 : }
490 0 : if (err)
491 0 : goto done;
492 : }
493 : #endif
494 : else
495 : break;
496 : }
497 :
498 1820 : if (p[0] != 0)
499 : {
500 0 : err = clib_error_return (0, "unknown input `%s'", p);
501 0 : goto done;
502 : }
503 :
504 : #if CLIB_LINUX
505 : /* change netns if requested */
506 1820 : if (s->type != CLIB_SOCKET_TYPE_INET && netns_fd != -1)
507 : {
508 0 : int fd = open ("/proc/self/ns/net", O_RDONLY);
509 :
510 0 : if (setns (netns_fd, CLONE_NEWNET) < 0)
511 : {
512 0 : close (fd);
513 0 : err = clib_error_return_unix (0, "setns(%d)", netns_fd);
514 0 : goto done;
515 : }
516 0 : netns_fd = fd;
517 : }
518 : #endif
519 :
520 1820 : if (s->type == CLIB_SOCKET_TYPE_INET)
521 : {
522 0 : addr_len = sizeof (si);
523 0 : si.sin_port = htons (port);
524 :
525 0 : if (name)
526 : {
527 : struct in_addr host_addr;
528 0 : vec_add1 (name, 0);
529 :
530 : /* Recognize localhost to avoid host lookup in most common cast. */
531 0 : if (!strcmp ((char *) name, "localhost"))
532 0 : si.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
533 :
534 0 : else if (inet_aton ((char *) name, &host_addr))
535 0 : si.sin_addr = host_addr;
536 :
537 0 : else if (strlen ((char *) name) > 0)
538 : {
539 0 : struct hostent *host = gethostbyname ((char *) name);
540 0 : if (!host)
541 0 : err = clib_error_return (0, "unknown host `%s'", name);
542 : else
543 0 : clib_memcpy (&si.sin_addr.s_addr, host->h_addr_list[0],
544 : host->h_length);
545 : }
546 :
547 : else
548 0 : si.sin_addr.s_addr =
549 0 : htonl (s->is_server ? INADDR_LOOPBACK : INADDR_ANY);
550 :
551 0 : if (err)
552 0 : goto done;
553 : }
554 0 : sa = (struct sockaddr *) &si;
555 : }
556 1820 : else if (s->type == CLIB_SOCKET_TYPE_UNIX)
557 : {
558 1820 : struct stat st = { 0 };
559 1820 : char *path = (char *) &su.sun_path;
560 :
561 1820 : if (vec_len (name) > sizeof (su.sun_path) - 1)
562 : {
563 0 : err = clib_error_return (0, "File path '%v' too long", name);
564 0 : goto done;
565 : }
566 :
567 1820 : clib_memcpy (path, s->config, vec_len (name));
568 1820 : addr_len = sizeof (su);
569 1820 : sa = (struct sockaddr *) &su;
570 :
571 1820 : rv = stat (path, &st);
572 1820 : if (!s->is_server && rv < 0)
573 : {
574 0 : err = clib_error_return_unix (0, "stat ('%s')", path);
575 0 : goto done;
576 : }
577 :
578 1820 : if (s->is_server && rv == 0)
579 : {
580 4 : if (S_ISSOCK (st.st_mode))
581 : {
582 4 : int client_fd = socket (AF_UNIX, SOCK_STREAM, 0);
583 4 : int ret = connect (client_fd, (const struct sockaddr *) &su,
584 : sizeof (su));
585 4 : typeof (errno) connect_errno = errno;
586 4 : close (client_fd);
587 :
588 4 : if (ret == 0 || (ret < 0 && connect_errno != ECONNREFUSED))
589 : {
590 0 : err = clib_error_return (0, "Active listener on '%s'", path);
591 0 : goto done;
592 : }
593 :
594 4 : if (unlink (path) < 0)
595 : {
596 0 : err = clib_error_return_unix (0, "unlink ('%s')", path);
597 0 : goto done;
598 : }
599 : }
600 : else
601 : {
602 0 : err = clib_error_return (0, "File '%s' already exists", path);
603 0 : goto done;
604 : }
605 : }
606 : }
607 : #if CLIB_LINUX
608 0 : else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT)
609 : {
610 0 : if (vec_len (name) > sizeof (su.sun_path) - 2)
611 : {
612 0 : err = clib_error_return (0, "Socket name '%v' too long", name);
613 0 : goto done;
614 : }
615 :
616 0 : clib_memcpy (&su.sun_path[1], name, vec_len (name));
617 0 : addr_len = sizeof (su.sun_family) + vec_len (name);
618 0 : sa = (struct sockaddr *) &su;
619 0 : s->allow_group_write = 0;
620 : }
621 : #endif
622 : else
623 : {
624 0 : err = clib_error_return_unix (0, "unknown socket family");
625 0 : goto done;
626 : }
627 :
628 1820 : socket_init_funcs (s);
629 :
630 1820 : if ((s->fd = socket (sa->sa_family,
631 1820 : s->is_seqpacket ? SOCK_SEQPACKET : SOCK_STREAM, 0)) < 0)
632 : {
633 : err =
634 0 : clib_error_return_unix (0, "socket (fd %d, '%s')", s->fd, s->config);
635 0 : goto done;
636 : }
637 :
638 1820 : if (s->is_server)
639 : {
640 1771 : uword need_bind = 1;
641 :
642 1771 : if (sa->sa_family == AF_INET && si.sin_port == 0)
643 : {
644 0 : word port = find_free_port (s->fd);
645 0 : if (port < 0)
646 : {
647 0 : err = clib_error_return (0, "no free port (fd %d, '%s')", s->fd,
648 : s->config);
649 0 : goto done;
650 : }
651 0 : si.sin_port = port;
652 0 : need_bind = 0;
653 : }
654 :
655 1771 : if (setsockopt (s->fd, SOL_SOCKET, SO_REUSEADDR, &((int){ 1 }),
656 : sizeof (int)) < 0)
657 0 : clib_unix_warning ("setsockopt SO_REUSEADDR fails");
658 :
659 : #if CLIB_LINUX
660 1771 : if (sa->sa_family == AF_UNIX && s->passcred)
661 : {
662 1196 : if (setsockopt (s->fd, SOL_SOCKET, SO_PASSCRED, &((int){ 1 }),
663 : sizeof (int)) < 0)
664 : {
665 0 : err = clib_error_return_unix (0,
666 : "setsockopt (SO_PASSCRED, "
667 : "fd %d, '%s')",
668 : s->fd, s->config);
669 0 : goto done;
670 : }
671 : }
672 : #endif
673 :
674 1771 : if (need_bind && bind (s->fd, sa, addr_len) < 0)
675 : {
676 : err =
677 0 : clib_error_return_unix (0, "bind (fd %d, '%s')", s->fd, s->config);
678 0 : goto done;
679 : }
680 :
681 1771 : if (listen (s->fd, 5) < 0)
682 : {
683 0 : err = clib_error_return_unix (0, "listen (fd %d, '%s')", s->fd,
684 : s->config);
685 0 : goto done;
686 : }
687 :
688 1771 : if (s->local_only && s->allow_group_write)
689 : {
690 11 : if (fchmod (s->fd, S_IWGRP) < 0)
691 : {
692 0 : err = clib_error_return_unix (
693 : 0, "fchmod (fd %d, '%s', mode S_IWGRP)", s->fd, s->config);
694 0 : goto done;
695 : }
696 : }
697 : }
698 : else
699 : {
700 49 : if (s->non_blocking_connect && fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
701 : {
702 0 : err = clib_error_return_unix (0, "fcntl NONBLOCK (fd %d, '%s')",
703 : s->fd, s->config);
704 0 : goto done;
705 : }
706 :
707 49 : while ((rv = connect (s->fd, sa, addr_len)) < 0 && errno == EAGAIN)
708 : ;
709 49 : if (rv < 0 && !(s->non_blocking_connect && errno == EINPROGRESS))
710 : {
711 1 : err = clib_error_return_unix (0, "connect (fd %d, '%s')", s->fd,
712 : s->config);
713 1 : goto done;
714 : }
715 : /* Connect was blocking so set fd to non-blocking now unless
716 : * blocking mode explicitly requested. */
717 77 : if (!s->non_blocking_connect && !s->is_blocking &&
718 29 : fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
719 : {
720 0 : err = clib_error_return_unix (0, "fcntl NONBLOCK2 (fd %d, '%s')",
721 : s->fd, s->config);
722 0 : goto done;
723 : }
724 : }
725 :
726 48 : done:
727 1820 : if (err && s->fd > -1)
728 : {
729 1 : close (s->fd);
730 1 : s->fd = -1;
731 : }
732 : #if CLIB_LINUX
733 1820 : if (netns_fd != -1)
734 : {
735 0 : setns (netns_fd, CLONE_NEWNET);
736 0 : close (netns_fd);
737 : }
738 : #endif
739 1820 : vec_free (name);
740 1820 : return err;
741 : }
742 :
743 : __clib_export clib_error_t *
744 1188 : clib_socket_accept (clib_socket_t * server, clib_socket_t * client)
745 : {
746 1188 : clib_error_t *err = 0;
747 1188 : socklen_t len = 0;
748 :
749 1188 : clib_memset (client, 0, sizeof (client[0]));
750 :
751 : /* Accept the new socket connection. */
752 1188 : client->fd = accept (server->fd, 0, 0);
753 1188 : if (client->fd < 0)
754 0 : return clib_error_return_unix (0, "accept (fd %d, '%s')",
755 : server->fd, server->config);
756 :
757 : /* Set the new socket to be non-blocking. */
758 1188 : if (fcntl (client->fd, F_SETFL, O_NONBLOCK) < 0)
759 : {
760 0 : err = clib_error_return_unix (0, "fcntl O_NONBLOCK (fd %d)",
761 : client->fd);
762 0 : goto close_client;
763 : }
764 :
765 : /* Get peer info. */
766 1188 : len = sizeof (client->peer);
767 1188 : if (getpeername (client->fd, (struct sockaddr *) &client->peer, &len) < 0)
768 : {
769 0 : err = clib_error_return_unix (0, "getpeername (fd %d)", client->fd);
770 0 : goto close_client;
771 : }
772 :
773 1188 : client->flags = CLIB_SOCKET_F_IS_CLIENT;
774 :
775 1188 : socket_init_funcs (client);
776 1188 : return 0;
777 :
778 0 : close_client:
779 0 : close (client->fd);
780 0 : return err;
781 : }
782 :
783 : /*
784 : * fd.io coding-style-patch-verification: ON
785 : *
786 : * Local Variables:
787 : * eval: (c-set-style "gnu")
788 : * End:
789 : */
|