#include <stdio.h>
#include <string.h> /* strchr */
#define __USE_GNU
+#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <vppinfra/mem.h>
#include <vppinfra/vec.h>
#include <vppinfra/socket.h>
+#include <vppinfra/linux/netns.h>
#include <vppinfra/format.h>
#include <vppinfra/error.h>
-void
+#ifndef __GLIBC__
+/* IPPORT_USERRESERVED is not part of musl libc. */
+#define IPPORT_USERRESERVED 5000
+#endif
+
+__clib_export void
clib_socket_tx_add_formatted (clib_socket_t * s, char *fmt, ...)
{
va_list va;
{
struct sockaddr_in a;
- memset (&a, 0, sizeof (a)); /* Warnings be gone */
+ clib_memset (&a, 0, sizeof (a)); /* Warnings be gone */
a.sin_family = PF_INET;
a.sin_addr.s_addr = INADDR_ANY;
*addr_len = sizeof (su[0]);
}
+ /* Treat everything that starts with @ as an abstract socket. */
+ else if (config[0] == '@')
+ {
+ struct sockaddr_un *su = addr;
+ su->sun_family = PF_LOCAL;
+ clib_memcpy (&su->sun_path, config,
+ clib_min (sizeof (su->sun_path), 1 + strlen (config)));
+
+ *addr_len = sizeof (su->sun_family) + strlen (config);
+ su->sun_path[0] = '\0';
+ }
+
/* Hostname or hostname:port or port. */
else
{
{
struct msghdr mh = { 0 };
struct iovec iov[1];
- char ctl[CMSG_SPACE (sizeof (int)) * num_fds];
+ char ctl[CMSG_SPACE (sizeof (int) * num_fds)];
int rv;
iov[0].iov_base = msg;
if (num_fds > 0)
{
struct cmsghdr *cmsg;
- memset (&ctl, 0, sizeof (ctl));
+ clib_memset (&ctl, 0, sizeof (ctl));
mh.msg_control = ctl;
mh.msg_controllen = sizeof (ctl);
cmsg = CMSG_FIRSTHDR (&mh);
default_socket_recvmsg (clib_socket_t * s, void *msg, int msglen,
int fds[], int num_fds)
{
+#ifdef __linux__
char ctl[CMSG_SPACE (sizeof (int) * num_fds) +
CMSG_SPACE (sizeof (struct ucred))];
+ struct ucred *cr = 0;
+#else
+ char ctl[CMSG_SPACE (sizeof (int) * num_fds)];
+#endif
struct msghdr mh = { 0 };
struct iovec iov[1];
ssize_t size;
- struct ucred *cr = 0;
struct cmsghdr *cmsg;
iov[0].iov_base = msg;
mh.msg_control = ctl;
mh.msg_controllen = sizeof (ctl);
- memset (ctl, 0, sizeof (ctl));
+ clib_memset (ctl, 0, sizeof (ctl));
/* receive the incoming message */
size = recvmsg (s->fd, &mh, 0);
{
if (cmsg->cmsg_level == SOL_SOCKET)
{
+#ifdef __linux__
if (cmsg->cmsg_type == SCM_CREDENTIALS)
{
cr = (struct ucred *) CMSG_DATA (cmsg);
s->gid = cr->gid;
s->pid = cr->pid;
}
- else if (cmsg->cmsg_type == SCM_RIGHTS)
+ else
+#endif
+ if (cmsg->cmsg_type == SCM_RIGHTS)
{
- clib_memcpy (fds, CMSG_DATA (cmsg), num_fds * sizeof (int));
+ clib_memcpy_fast (fds, CMSG_DATA (cmsg),
+ num_fds * sizeof (int));
}
}
cmsg = CMSG_NXTHDR (&mh, cmsg);
s->recvmsg_func = default_socket_recvmsg;
}
-clib_error_t *
+__clib_export clib_error_t *
clib_socket_init (clib_socket_t * s)
{
union
struct sockaddr_un su;
} addr;
socklen_t addr_len = 0;
- int socket_type;
+ int socket_type, rv;
clib_error_t *error = 0;
word port;
need_bind = 0;
}
}
- if (addr.sa.sa_family == PF_LOCAL)
+ if (addr.sa.sa_family == PF_LOCAL &&
+ ((struct sockaddr_un *) &addr)->sun_path[0] != 0)
unlink (((struct sockaddr_un *) &addr)->sun_path);
/* Make address available for multiple users. */
clib_unix_warning ("setsockopt SO_REUSEADDR fails");
}
+#if __linux__
if (addr.sa.sa_family == PF_LOCAL && s->flags & CLIB_SOCKET_F_PASSCRED)
{
int x = 1;
goto done;
}
}
+#endif
if (need_bind && bind (s->fd, &addr.sa, addr_len) < 0)
{
s->fd, s->config);
goto done;
}
- if (addr.sa.sa_family == PF_LOCAL
- && s->flags & CLIB_SOCKET_F_ALLOW_GROUP_WRITE)
+ if (addr.sa.sa_family == PF_LOCAL &&
+ s->flags & CLIB_SOCKET_F_ALLOW_GROUP_WRITE &&
+ ((struct sockaddr_un *) &addr)->sun_path[0] != 0)
{
struct stat st = { 0 };
if (stat (((struct sockaddr_un *) &addr)->sun_path, &st) < 0)
goto done;
}
- if (connect (s->fd, &addr.sa, addr_len) < 0
- && !((s->flags & CLIB_SOCKET_F_NON_BLOCKING_CONNECT) &&
- errno == EINPROGRESS))
+ while ((rv = connect (s->fd, &addr.sa, addr_len)) < 0
+ && errno == EAGAIN)
+ ;
+ if (rv < 0 && !((s->flags & CLIB_SOCKET_F_NON_BLOCKING_CONNECT) &&
+ errno == EINPROGRESS))
{
error = clib_error_return_unix (0, "connect (fd %d, '%s')",
s->fd, s->config);
goto done;
}
+ /* Connect was blocking so set fd to non-blocking now unless
+ * blocking mode explicitly requested. */
+ if (!(s->flags & CLIB_SOCKET_F_NON_BLOCKING_CONNECT) &&
+ !(s->flags & CLIB_SOCKET_F_BLOCKING) &&
+ fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ error = clib_error_return_unix (0, "fcntl NONBLOCK2 (fd %d, '%s')",
+ s->fd, s->config);
+ goto done;
+ }
}
return error;
return error;
}
-clib_error_t *
+__clib_export clib_error_t *
+clib_socket_init_netns (clib_socket_t *s, u8 *namespace)
+{
+ if (namespace == NULL || namespace[0] == 0)
+ return clib_socket_init (s);
+
+ clib_error_t *error;
+ int old_netns_fd, nfd = -1;
+
+ old_netns_fd = clib_netns_open (NULL /* self */);
+ if ((nfd = clib_netns_open (namespace)) == -1)
+ {
+ error = clib_error_return_unix (0, "clib_netns_open '%s'", namespace);
+ goto done;
+ }
+
+ if (clib_setns (nfd) == -1)
+ {
+ error = clib_error_return_unix (0, "setns '%s'", namespace);
+ goto done;
+ }
+
+ error = clib_socket_init (s);
+
+done:
+ if (clib_setns (old_netns_fd) == -1)
+ clib_warning ("Cannot set old ns");
+
+ close (old_netns_fd);
+
+ if (-1 != nfd)
+ close (nfd);
+
+ return error;
+}
+
+__clib_export clib_error_t *
clib_socket_accept (clib_socket_t * server, clib_socket_t * client)
{
clib_error_t *err = 0;
socklen_t len = 0;
- memset (client, 0, sizeof (client[0]));
+ clib_memset (client, 0, sizeof (client[0]));
/* Accept the new socket connection. */
client->fd = accept (server->fd, 0, 0);