summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDamjan Marion <dmarion@me.com>2023-01-30 11:48:38 +0100
committerOle Tr�an <otroan@employees.org>2023-02-06 10:17:40 +0000
commit085757bb4930511928daa97f972cdca021e7a813 (patch)
tree5edf076e28156d9834fa37029028f2f5c5f568b9 /src
parent0df06b6e95b6a3261c8e9c261c7a4a661d6ea25a (diff)
vppinfra: refactor clib_socket_init, add linux netns support
Type: improvement Change-Id: Ida2d044bccf0bc8914b4fe7d383f827400fa6a52 Signed-off-by: Damjan Marion <dmarion@me.com>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/memif/cli.c2
-rw-r--r--src/plugins/memif/memif.c194
-rw-r--r--src/plugins/memif/memif_api.c15
-rw-r--r--src/plugins/memif/private.h2
-rw-r--r--src/plugins/snort/main.c6
-rw-r--r--src/vppinfra/clib.h6
-rw-r--r--src/vppinfra/socket.c567
-rw-r--r--src/vppinfra/socket.h49
8 files changed, 494 insertions, 347 deletions
diff --git a/src/plugins/memif/cli.c b/src/plugins/memif/cli.c
index 9a0ded81a1a..3d3a681f18d 100644
--- a/src/plugins/memif/cli.c
+++ b/src/plugins/memif/cli.c
@@ -73,7 +73,7 @@ memif_socket_filename_create_command_fn (vlib_main_t * vm,
return clib_error_return (0, "Invalid socket filename");
}
- err = memif_socket_filename_add_del (1, socket_id, socket_filename);
+ err = memif_socket_filename_add_del (1, socket_id, (char *) socket_filename);
vec_free (socket_filename);
diff --git a/src/plugins/memif/memif.c b/src/plugins/memif/memif.c
index 12d81ee7975..eee38f09a5b 100644
--- a/src/plugins/memif/memif.c
+++ b/src/plugins/memif/memif.c
@@ -641,8 +641,8 @@ memif_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
{
clib_memset (sock, 0, sizeof(clib_socket_t));
sock->config = (char *) msf->filename;
- sock->flags = CLIB_SOCKET_F_IS_CLIENT | CLIB_SOCKET_F_SEQPACKET |
- CLIB_SOCKET_F_BLOCKING;
+ sock->is_seqpacket = 1;
+ sock->is_blocking = 1;
if ((err = clib_socket_init (sock)))
{
@@ -675,79 +675,22 @@ memif_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
return 0;
}
-/* *INDENT-OFF* */
VLIB_REGISTER_NODE (memif_process_node,static) = {
.function = memif_process,
.type = VLIB_NODE_TYPE_PROCESS,
.name = "memif-process",
};
-/* *INDENT-ON* */
-
-static clib_error_t *
-memif_add_socket_file (u32 sock_id, u8 *socket_filename)
-{
- memif_main_t *mm = &memif_main;
- uword *p;
- memif_socket_file_t *msf;
-
- p = hash_get (mm->socket_file_index_by_sock_id, sock_id);
- if (p)
- {
- msf = pool_elt_at_index (mm->socket_files, *p);
- if (strcmp ((char *) msf->filename, (char *) socket_filename) == 0)
- {
- /* Silently accept identical "add". */
- return 0;
- }
-
- /* But don't allow a direct add of a different filename. */
- return vnet_error (VNET_ERR_ENTRY_ALREADY_EXISTS,
- "entry already exists");
- }
-
- pool_get (mm->socket_files, msf);
- clib_memset (msf, 0, sizeof (memif_socket_file_t));
-
- msf->filename = socket_filename;
- msf->socket_id = sock_id;
- hash_set (mm->socket_file_index_by_sock_id, sock_id,
- msf - mm->socket_files);
-
- return 0;
-}
-
-static clib_error_t *
-memif_delete_socket_file (u32 sock_id)
+clib_error_t *
+memif_socket_filename_add_del (u8 is_add, u32 sock_id, char *sock_filename)
{
memif_main_t *mm = &memif_main;
uword *p;
memif_socket_file_t *msf;
-
- p = hash_get (mm->socket_file_index_by_sock_id, sock_id);
- if (!p)
- /* Don't delete non-existent entries. */
- return vnet_error (VNET_ERR_INVALID_ARGUMENT,
- "socket file with id %u does not exist", sock_id);
-
- msf = pool_elt_at_index (mm->socket_files, *p);
- if (msf->ref_cnt > 0)
- return vnet_error (VNET_ERR_UNEXPECTED_INTF_STATE,
- "socket file '%s' is in use", msf->filename);
-
- vec_free (msf->filename);
- pool_put (mm->socket_files, msf);
-
- hash_unset (mm->socket_file_index_by_sock_id, sock_id);
-
- return 0;
-}
-
-clib_error_t *
-memif_socket_filename_add_del (u8 is_add, u32 sock_id, u8 *sock_filename)
-{
+ clib_error_t *err = 0;
char *dir = 0, *tmp;
u32 idx = 0;
+ u8 *name = 0;
/* allow adding socket id 0 */
if (sock_id == 0 && is_add == 0)
@@ -758,70 +701,95 @@ memif_socket_filename_add_del (u8 is_add, u32 sock_id, u8 *sock_filename)
"socked id is not specified");
if (is_add == 0)
- return memif_delete_socket_file (sock_id);
+ {
+ p = hash_get (mm->socket_file_index_by_sock_id, sock_id);
+ if (!p)
+ /* Don't delete non-existent entries. */
+ return vnet_error (VNET_ERR_INVALID_ARGUMENT,
+ "socket file with id %u does not exist", sock_id);
+
+ msf = pool_elt_at_index (mm->socket_files, *p);
+ if (msf->ref_cnt > 0)
+ return vnet_error (VNET_ERR_UNEXPECTED_INTF_STATE,
+ "socket file '%s' is in use", msf->filename);
+
+ vec_free (msf->filename);
+ pool_put (mm->socket_files, msf);
+
+ hash_unset (mm->socket_file_index_by_sock_id, sock_id);
+
+ return 0;
+ }
if (sock_filename == 0 || sock_filename[0] == 0)
return vnet_error (VNET_ERR_INVALID_ARGUMENT,
"socket filename not specified");
- if (sock_filename[0] != '/')
+ if (clib_socket_prefix_is_valid (sock_filename))
+ {
+ name = format (0, "%s%c", sock_filename, 0);
+ }
+ else if (sock_filename[0] == '/')
+ {
+ name = format (0, "%s%c", sock_filename, 0);
+ }
+ else
{
- clib_error_t *error;
-
/* copy runtime dir path */
vec_add (dir, vlib_unix_get_runtime_dir (),
strlen (vlib_unix_get_runtime_dir ()));
vec_add1 (dir, '/');
/* if sock_filename contains dirs, add them to path */
- tmp = strrchr ((char *) sock_filename, '/');
+ tmp = strrchr (sock_filename, '/');
if (tmp)
{
- idx = tmp - (char *) sock_filename;
+ idx = tmp - sock_filename;
vec_add (dir, sock_filename, idx);
}
vec_add1 (dir, '\0');
/* create socket dir */
- error = vlib_unix_recursive_mkdir (dir);
- if (error)
+ if ((err = vlib_unix_recursive_mkdir (dir)))
{
- clib_error_free (error);
- return vnet_error (VNET_ERR_SYSCALL_ERROR_1,
- "unable to create socket dir");
+ clib_error_free (err);
+ err = vnet_error (VNET_ERR_SYSCALL_ERROR_1,
+ "unable to create socket dir");
+ goto done;
}
- sock_filename = format (0, "%s/%s%c", vlib_unix_get_runtime_dir (),
- sock_filename, 0);
+ name =
+ format (0, "%s/%s%c", vlib_unix_get_runtime_dir (), sock_filename, 0);
}
- else
- {
- sock_filename = vec_dup (sock_filename);
- /* check if directory exists */
- tmp = strrchr ((char *) sock_filename, '/');
- if (tmp)
+ p = hash_get (mm->socket_file_index_by_sock_id, sock_id);
+ if (p)
+ {
+ msf = pool_elt_at_index (mm->socket_files, *p);
+ if (strcmp ((char *) msf->filename, (char *) name) == 0)
{
- idx = tmp - (char *) sock_filename;
- vec_add (dir, sock_filename, idx);
- vec_add1 (dir, '\0');
+ /* Silently accept identical "add". */
+ goto done;
}
- /* check dir existance and access rights for effective user/group IDs */
- if ((dir == NULL)
- ||
- (faccessat ( /* ignored */ -1, dir, F_OK | R_OK | W_OK, AT_EACCESS)
- < 0))
- {
- vec_free (dir);
- return vnet_error (
- VNET_ERR_INVALID_ARGUMENT,
- "directory doesn't exist or no access permissions");
- }
+ /* But don't allow a direct add of a different filename. */
+ err = vnet_error (VNET_ERR_ENTRY_ALREADY_EXISTS, "entry already exists");
+ goto done;
}
- vec_free (dir);
- return memif_add_socket_file (sock_id, sock_filename);
+ pool_get (mm->socket_files, msf);
+ clib_memset (msf, 0, sizeof (memif_socket_file_t));
+
+ msf->filename = name;
+ msf->socket_id = sock_id;
+ name = 0;
+
+ hash_set (mm->socket_file_index_by_sock_id, sock_id, msf - mm->socket_files);
+
+done:
+ vec_free (name);
+ vec_free (dir);
+ return err;
}
clib_error_t *
@@ -949,24 +917,6 @@ memif_create_if (vlib_main_t *vm, memif_create_if_args_t *args)
/* Create new socket file */
if (msf->ref_cnt == 0)
{
- struct stat file_stat;
-
- /* If we are creating listener make sure file doesn't exist or if it
- * exists thn delete it if it is old socket file */
- if (args->is_master && (stat ((char *) msf->filename, &file_stat) == 0))
- {
- if (S_ISSOCK (file_stat.st_mode))
- {
- unlink ((char *) msf->filename);
- }
- else
- {
- err = vnet_error (VNET_ERR_VALUE_EXIST, "File exists for %s",
- msf->filename);
- goto done;
- }
- }
-
mhash_init (&msf->dev_instance_by_id, sizeof (uword),
sizeof (memif_interface_id_t));
msf->dev_instance_by_fd = hash_create (0, sizeof (uword));
@@ -1068,9 +1018,11 @@ memif_create_if (vlib_main_t *vm, memif_create_if_args_t *args)
clib_memset (s, 0, sizeof (clib_socket_t));
s->config = (char *) msf->filename;
- s->flags = CLIB_SOCKET_F_IS_SERVER |
- CLIB_SOCKET_F_ALLOW_GROUP_WRITE |
- CLIB_SOCKET_F_SEQPACKET | CLIB_SOCKET_F_PASSCRED;
+ s->local_only = 1;
+ s->is_server = 1;
+ s->allow_group_write = 1;
+ s->is_seqpacket = 1;
+ s->passcred = 1;
if ((err = clib_socket_init (s)))
{
@@ -1159,9 +1111,7 @@ memif_init (vlib_main_t * vm)
* for socket-id 0 to MEMIF_DEFAULT_SOCKET_FILENAME in the
* default run-time directory.
*/
- memif_socket_filename_add_del (1, 0, (u8 *) MEMIF_DEFAULT_SOCKET_FILENAME);
-
- return 0;
+ return memif_socket_filename_add_del (1, 0, MEMIF_DEFAULT_SOCKET_FILENAME);
}
VLIB_INIT_FUNCTION (memif_init);
diff --git a/src/plugins/memif/memif_api.c b/src/plugins/memif/memif_api.c
index 04c2c8daab2..9a2f42a5bc7 100644
--- a/src/plugins/memif/memif_api.c
+++ b/src/plugins/memif/memif_api.c
@@ -48,8 +48,6 @@ void
memif_main_t *mm = &memif_main;
u8 is_add;
u32 socket_id;
- u32 len;
- u8 *socket_filename;
vl_api_memif_socket_filename_add_del_reply_t *rmp;
int rv;
@@ -65,19 +63,10 @@ void
}
/* socket filename */
- socket_filename = 0;
mp->socket_filename[ARRAY_LEN (mp->socket_filename) - 1] = 0;
- len = strlen ((char *) mp->socket_filename);
- if (mp->is_add)
- {
- vec_validate (socket_filename, len);
- memcpy (socket_filename, mp->socket_filename, len);
- }
-
- rv = vnet_api_error (
- memif_socket_filename_add_del (is_add, socket_id, socket_filename));
- vec_free (socket_filename);
+ rv = vnet_api_error (memif_socket_filename_add_del (
+ is_add, socket_id, (char *) mp->socket_filename));
reply:
REPLY_MACRO (VL_API_MEMIF_SOCKET_FILENAME_ADD_DEL_REPLY);
diff --git a/src/plugins/memif/private.h b/src/plugins/memif/private.h
index 559062ef3cd..ba77cc1c5dd 100644
--- a/src/plugins/memif/private.h
+++ b/src/plugins/memif/private.h
@@ -322,7 +322,7 @@ typedef struct
} memif_create_if_args_t;
clib_error_t *memif_socket_filename_add_del (u8 is_add, u32 sock_id,
- u8 *sock_filename);
+ char *sock_filename);
clib_error_t *memif_create_if (vlib_main_t *vm, memif_create_if_args_t *args);
clib_error_t *memif_delete_if (vlib_main_t *vm, memif_if_t *mif);
clib_error_t *memif_plugin_api_hookup (vlib_main_t * vm);
diff --git a/src/plugins/snort/main.c b/src/plugins/snort/main.c
index 39c13a8f237..2430fcdc5c2 100644
--- a/src/plugins/snort/main.c
+++ b/src/plugins/snort/main.c
@@ -259,8 +259,10 @@ snort_listener_init (vlib_main_t *vm)
s = clib_mem_alloc (sizeof (clib_socket_t));
clib_memset (s, 0, sizeof (clib_socket_t));
s->config = (char *) sm->socket_name;
- s->flags = CLIB_SOCKET_F_IS_SERVER | CLIB_SOCKET_F_ALLOW_GROUP_WRITE |
- CLIB_SOCKET_F_SEQPACKET | CLIB_SOCKET_F_PASSCRED;
+ s->is_server = 1;
+ s->allow_group_write = 1;
+ s->is_seqpacket = 1;
+ s->passcred = 1;
if ((err = clib_socket_init (s)))
{
diff --git a/src/vppinfra/clib.h b/src/vppinfra/clib.h
index 746cb511bbe..8d1e412db62 100644
--- a/src/vppinfra/clib.h
+++ b/src/vppinfra/clib.h
@@ -53,6 +53,12 @@
#define CLIB_UNIX
#endif
+#ifdef __linux__
+#define CLIB_LINUX 1
+#else
+#define CLIB_LINUX 0
+#endif
+
#include <vppinfra/types.h>
#include <vppinfra/atomics.h>
diff --git a/src/vppinfra/socket.c b/src/vppinfra/socket.c
index a3024ae7a8c..e61c369f050 100644
--- a/src/vppinfra/socket.c
+++ b/src/vppinfra/socket.c
@@ -93,108 +93,6 @@ find_free_port (word sock)
return port < 1 << 16 ? port : -1;
}
-/* Convert a config string to a struct sockaddr and length for use
- with bind or connect. */
-static clib_error_t *
-socket_config (char *config,
- void *addr, socklen_t * addr_len, u32 ip4_default_address)
-{
- clib_error_t *error = 0;
-
- if (!config)
- config = "";
-
- /* Anything that begins with a / is a local PF_LOCAL socket. */
- 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[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
- {
- char *host_name;
- int port = -1;
- struct sockaddr_in *sa = addr;
-
- host_name = 0;
- port = -1;
- if (config[0] != 0)
- {
- unformat_input_t i;
-
- unformat_init_string (&i, config, strlen (config));
- if (unformat (&i, "%s:%d", &host_name, &port)
- || unformat (&i, "%s:0x%x", &host_name, &port))
- ;
- else if (unformat (&i, "%s", &host_name))
- ;
- else
- error = clib_error_return (0, "unknown input `%U'",
- format_unformat_error, &i);
- unformat_free (&i);
-
- if (error)
- goto done;
- }
-
- sa->sin_family = PF_INET;
- *addr_len = sizeof (sa[0]);
- if (port != -1)
- sa->sin_port = htons (port);
- else
- sa->sin_port = 0;
-
- if (host_name)
- {
- struct in_addr host_addr;
-
- /* Recognize localhost to avoid host lookup in most common cast. */
- if (!strcmp (host_name, "localhost"))
- sa->sin_addr.s_addr = htonl (INADDR_LOOPBACK);
-
- else if (inet_aton (host_name, &host_addr))
- sa->sin_addr = host_addr;
-
- else if (host_name && strlen (host_name) > 0)
- {
- struct hostent *host = gethostbyname (host_name);
- if (!host)
- error = clib_error_return (0, "unknown host `%s'", config);
- else
- clib_memcpy (&sa->sin_addr.s_addr, host->h_addr_list[0],
- host->h_length);
- }
-
- else
- sa->sin_addr.s_addr = htonl (ip4_default_address);
-
- vec_free (host_name);
- if (error)
- goto done;
- }
- }
-
-done:
- return error;
-}
-
static clib_error_t *
default_socket_write (clib_socket_t * s)
{
@@ -253,7 +151,7 @@ default_socket_read (clib_socket_t * sock, int n_bytes)
u8 *buf;
/* RX side of socket is down once end of file is reached. */
- if (sock->flags & CLIB_SOCKET_F_RX_END_OF_FILE)
+ if (sock->rx_end_of_file)
return 0;
fd = sock->fd;
@@ -275,7 +173,7 @@ default_socket_read (clib_socket_t * sock, int n_bytes)
/* Other side closed the socket. */
if (n_read == 0)
- sock->flags |= CLIB_SOCKET_F_RX_END_OF_FILE;
+ sock->rx_end_of_file = 1;
non_fatal:
vec_inc_len (sock->rx_buffer, n_read - n_bytes);
@@ -328,7 +226,7 @@ static clib_error_t *
default_socket_recvmsg (clib_socket_t * s, void *msg, int msglen,
int fds[], int num_fds)
{
-#ifdef __linux__
+#ifdef CLIB_LINUX
char ctl[CMSG_SPACE (sizeof (int) * num_fds) +
CMSG_SPACE (sizeof (struct ucred))];
struct ucred *cr = 0;
@@ -363,7 +261,7 @@ default_socket_recvmsg (clib_socket_t * s, void *msg, int msglen,
{
if (cmsg->cmsg_level == SOL_SOCKET)
{
-#ifdef __linux__
+#ifdef CLIB_LINUX
if (cmsg->cmsg_type == SCM_CREDENTIALS)
{
cr = (struct ucred *) CMSG_DATA (cmsg);
@@ -399,158 +297,433 @@ socket_init_funcs (clib_socket_t * s)
s->recvmsg_func = default_socket_recvmsg;
}
+static const struct
+{
+ char *prefix;
+ sa_family_t family;
+ clib_socket_type_t type;
+ u16 skip_prefix : 1;
+ u16 is_local : 1;
+} clib_socket_type_data[] = {
+ { .prefix = "unix:",
+ .family = AF_UNIX,
+ .type = CLIB_SOCKET_TYPE_UNIX,
+ .skip_prefix = 1,
+ .is_local = 1 },
+ { .prefix = "tcp:",
+ .family = AF_INET,
+ .type = CLIB_SOCKET_TYPE_INET,
+ .skip_prefix = 1 },
+ { .prefix = "abstract:",
+ .family = AF_UNIX,
+ .type = CLIB_SOCKET_TYPE_LINUX_ABSTRACT,
+ .skip_prefix = 1,
+ .is_local = 1 },
+ { .prefix = "/",
+ .family = AF_UNIX,
+ .type = CLIB_SOCKET_TYPE_UNIX,
+ .skip_prefix = 0,
+ .is_local = 1 },
+ { .prefix = "",
+ .family = AF_INET,
+ .type = CLIB_SOCKET_TYPE_INET,
+ .skip_prefix = 0,
+ .is_local = 0 },
+ { .prefix = "",
+ .family = AF_UNIX,
+ .type = CLIB_SOCKET_TYPE_UNIX,
+ .skip_prefix = 0,
+ .is_local = 1 },
+};
+
+static u8 *
+_clib_socket_get_string (char **p, int is_hostname)
+{
+ u8 *s = 0;
+ while (**p)
+ {
+ switch (**p)
+ {
+ case '_':
+ if (is_hostname)
+ return s;
+ case 'a' ... 'z':
+ case 'A' ... 'Z':
+ case '0' ... '9':
+ case '/':
+ case '-':
+ case '.':
+ vec_add1 (s, **p);
+ (*p)++;
+ break;
+ break;
+ default:
+ return s;
+ }
+ }
+ return s;
+}
+
+__clib_export int
+clib_socket_prefix_is_valid (char *s)
+{
+ for (typeof (clib_socket_type_data[0]) *d = clib_socket_type_data;
+ d - clib_socket_type_data < ARRAY_LEN (clib_socket_type_data); d++)
+ if (d->skip_prefix && strncmp (s, d->prefix, strlen (d->prefix)) == 0)
+ return 1;
+ return 0;
+}
+
__clib_export clib_error_t *
-clib_socket_init (clib_socket_t * s)
+clib_socket_init (clib_socket_t *s)
{
- union
- {
- struct sockaddr sa;
- struct sockaddr_un su;
- } addr;
+ struct sockaddr_un su = { .sun_family = AF_UNIX };
+ struct sockaddr_in si = { .sin_family = AF_INET };
+ struct sockaddr *sa = 0;
+ typeof (clib_socket_type_data[0]) *data = 0;
socklen_t addr_len = 0;
- int socket_type, rv;
- clib_error_t *error = 0;
- word port;
+ int rv;
+ char *p;
+ clib_error_t *err = 0;
+ u8 *name = 0;
+ u16 port = 0;
+#if CLIB_LINUX
+ int netns_fd = -1;
+#endif
- error = socket_config (s->config, &addr.sa, &addr_len,
- (s->flags & CLIB_SOCKET_F_IS_SERVER
- ? INADDR_LOOPBACK : INADDR_ANY));
- if (error)
- goto done;
+ s->fd = -1;
- socket_init_funcs (s);
+ if (!s->config)
+ s->config = "";
+
+ for (int i = 0; i < ARRAY_LEN (clib_socket_type_data); i++)
+ {
+ typeof (clib_socket_type_data[0]) *d = clib_socket_type_data + i;
+
+ if (d->is_local == 0 && s->local_only)
+ continue;
+
+ if (strncmp (s->config, d->prefix, strlen (d->prefix)) == 0)
+ {
+ data = d;
+ break;
+ }
+ }
+
+ if (data == 0)
+ return clib_error_return (0, "unsupported socket config '%s'", s->config);
+
+ s->type = data->type;
+ p = s->config + (data->skip_prefix ? strlen (data->prefix) : 0);
- socket_type = s->flags & CLIB_SOCKET_F_SEQPACKET ?
- SOCK_SEQPACKET : SOCK_STREAM;
+ name = _clib_socket_get_string (&p, data->type == CLIB_SOCKET_TYPE_INET);
+ vec_add1 (name, 0);
- s->fd = socket (addr.sa.sa_family, socket_type, 0);
- if (s->fd < 0)
+ /* parse port type for INET sockets */
+ if (data->type == CLIB_SOCKET_TYPE_INET && p[0] == ':')
{
- error = clib_error_return_unix (0, "socket (fd %d, '%s')",
- s->fd, s->config);
+ char *old_p = p + 1;
+ long long ll = strtoll (old_p, &p, 0);
+
+ if (p == old_p)
+ {
+ err = clib_error_return (0, "invalid port");
+ goto done;
+ }
+
+ if (ll > CLIB_U16_MAX || ll < 1)
+ {
+ err = clib_error_return (0, "port out of range");
+ goto done;
+ }
+ port = ll;
+ }
+
+ while (p[0] == ',')
+ {
+ p++;
+ if (0)
+ ;
+#if CLIB_LINUX
+ else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT && netns_fd == -1 &&
+ strncmp (p, "netns_name=", 11) == 0)
+ {
+ p += 11;
+ u8 *str = _clib_socket_get_string (&p, 0);
+ u8 *pathname = format (0, "/var/run/netns/%v%c", str, 0);
+ if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
+ err = clib_error_return_unix (0, "open('%s')", pathname);
+ vec_free (str);
+ vec_free (pathname);
+ if (err)
+ goto done;
+ }
+ else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT && netns_fd == -1 &&
+ strncmp (p, "netns_pid=", 10) == 0)
+ {
+ char *old_p = p = p + 10;
+ u32 pid = (u32) strtol (old_p, &p, 0);
+
+ if (p == old_p)
+ err = clib_error_return (0, "invalid pid");
+ else
+ {
+ u8 *pathname = format (0, "/proc/%u/ns/net%c", pid, 0);
+ if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
+ err = clib_error_return_unix (0, "open('%s')", pathname);
+ vec_free (pathname);
+ }
+ if (err)
+ goto done;
+ }
+#endif
+ else
+ break;
+ }
+
+ if (p[0] != 0)
+ {
+ err = clib_error_return (0, "unknown input `%s'", p);
goto done;
}
- port = 0;
- if (addr.sa.sa_family == PF_INET)
- port = ((struct sockaddr_in *) &addr)->sin_port;
+#if CLIB_LINUX
+ /* change netns if requested */
+ if (s->type != CLIB_SOCKET_TYPE_INET && netns_fd != -1)
+ {
+ int fd = open ("/proc/self/ns/net", O_RDONLY);
+
+ if (setns (netns_fd, CLONE_NEWNET) < 0)
+ {
+ close (fd);
+ err = clib_error_return_unix (0, "setns(%d)", netns_fd);
+ goto done;
+ }
+ netns_fd = fd;
+ }
+#endif
- if (s->flags & CLIB_SOCKET_F_IS_SERVER)
+ if (s->type == CLIB_SOCKET_TYPE_INET)
{
- uword need_bind = 1;
+ addr_len = sizeof (si);
+ si.sin_port = htons (port);
+
+ if (name)
+ {
+ struct in_addr host_addr;
+ vec_add1 (name, 0);
+
+ /* Recognize localhost to avoid host lookup in most common cast. */
+ if (!strcmp ((char *) name, "localhost"))
+ si.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+ else if (inet_aton ((char *) name, &host_addr))
+ si.sin_addr = host_addr;
+
+ else if (strlen ((char *) name) > 0)
+ {
+ struct hostent *host = gethostbyname ((char *) name);
+ if (!host)
+ err = clib_error_return (0, "unknown host `%s'", name);
+ else
+ clib_memcpy (&si.sin_addr.s_addr, host->h_addr_list[0],
+ host->h_length);
+ }
+
+ else
+ si.sin_addr.s_addr =
+ htonl (s->is_server ? INADDR_LOOPBACK : INADDR_ANY);
+
+ if (err)
+ goto done;
+ }
+ sa = (struct sockaddr *) &si;
+ }
+ else if (s->type == CLIB_SOCKET_TYPE_UNIX)
+ {
+ struct stat st = { 0 };
+ char *path = (char *) &su.sun_path;
- if (addr.sa.sa_family == PF_INET)
+ if (vec_len (name) > sizeof (su.sun_path) - 1)
{
- if (port == 0)
+ err = clib_error_return (0, "File path '%v' too long", name);
+ goto done;
+ }
+
+ clib_memcpy (path, s->config, vec_len (name));
+ addr_len = sizeof (su);
+ sa = (struct sockaddr *) &su;
+
+ rv = stat (path, &st);
+ if (!s->is_server && rv < 0)
+ {
+ err = clib_error_return_unix (0, "stat ('%s')", path);
+ goto done;
+ }
+
+ if (s->is_server && rv == 0)
+ {
+ if (S_ISSOCK (st.st_mode))
{
- port = find_free_port (s->fd);
- if (port < 0)
+ int client_fd = socket (AF_UNIX, SOCK_STREAM, 0);
+ int ret = connect (client_fd, (const struct sockaddr *) &su,
+ sizeof (su));
+ typeof (errno) connect_errno = errno;
+ close (client_fd);
+
+ if (ret == 0 || (ret < 0 && connect_errno != ECONNREFUSED))
+ {
+ err = clib_error_return (0, "Active listener on '%s'", path);
+ goto done;
+ }
+
+ if (unlink (path) < 0)
{
- error = clib_error_return (0, "no free port (fd %d, '%s')",
- s->fd, s->config);
+ err = clib_error_return_unix (0, "unlink ('%s')", path);
goto done;
}
- need_bind = 0;
}
+ else
+ {
+ err = clib_error_return (0, "File '%s' already exists", path);
+ goto done;
+ }
+ }
+ }
+#if CLIB_LINUX
+ else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT)
+ {
+ if (vec_len (name) > sizeof (su.sun_path) - 2)
+ {
+ err = clib_error_return (0, "Socket name '%v' too long", name);
+ goto done;
+ }
+
+ clib_memcpy (&su.sun_path[1], name, vec_len (name));
+ addr_len = sizeof (su.sun_family) + vec_len (name);
+ sa = (struct sockaddr *) &su;
+ s->allow_group_write = 0;
+ }
+#endif
+ else
+ {
+ err = clib_error_return_unix (0, "unknown socket family");
+ goto done;
+ }
+
+ socket_init_funcs (s);
+
+ if ((s->fd = socket (sa->sa_family,
+ s->is_seqpacket ? SOCK_SEQPACKET : SOCK_STREAM, 0)) < 0)
+ {
+ err =
+ clib_error_return_unix (0, "socket (fd %d, '%s')", s->fd, s->config);
+ goto done;
+ }
+
+ if (s->is_server)
+ {
+ uword need_bind = 1;
+
+ if (sa->sa_family == AF_INET && si.sin_port == 0)
+ {
+ word port = find_free_port (s->fd);
+ if (port < 0)
+ {
+ err = clib_error_return (0, "no free port (fd %d, '%s')", s->fd,
+ s->config);
+ goto done;
+ }
+ si.sin_port = port;
+ need_bind = 0;
}
- 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. */
- {
- int v = 1;
- if (setsockopt (s->fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof (v)) < 0)
- clib_unix_warning ("setsockopt SO_REUSEADDR fails");
- }
-
-#if __linux__
- if (addr.sa.sa_family == PF_LOCAL && s->flags & CLIB_SOCKET_F_PASSCRED)
+
+ if (setsockopt (s->fd, SOL_SOCKET, SO_REUSEADDR, &((int){ 1 }),
+ sizeof (int)) < 0)
+ clib_unix_warning ("setsockopt SO_REUSEADDR fails");
+
+#if CLIB_LINUX
+ if (sa->sa_family == AF_UNIX && s->passcred)
{
- int x = 1;
- if (setsockopt (s->fd, SOL_SOCKET, SO_PASSCRED, &x, sizeof (x)) < 0)
+ if (setsockopt (s->fd, SOL_SOCKET, SO_PASSCRED, &((int){ 1 }),
+ sizeof (int)) < 0)
{
- error = clib_error_return_unix (0, "setsockopt (SO_PASSCRED, "
- "fd %d, '%s')", s->fd,
- s->config);
+ err = clib_error_return_unix (0,
+ "setsockopt (SO_PASSCRED, "
+ "fd %d, '%s')",
+ s->fd, s->config);
goto done;
}
}
#endif
- if (need_bind && bind (s->fd, &addr.sa, addr_len) < 0)
+ if (need_bind && bind (s->fd, sa, addr_len) < 0)
{
- error = clib_error_return_unix (0, "bind (fd %d, '%s')",
- s->fd, s->config);
+ err =
+ clib_error_return_unix (0, "bind (fd %d, '%s')", s->fd, s->config);
goto done;
}
if (listen (s->fd, 5) < 0)
{
- error = clib_error_return_unix (0, "listen (fd %d, '%s')",
- s->fd, s->config);
+ err = clib_error_return_unix (0, "listen (fd %d, '%s')", s->fd,
+ s->config);
goto done;
}
- if (addr.sa.sa_family == PF_LOCAL &&
- s->flags & CLIB_SOCKET_F_ALLOW_GROUP_WRITE &&
- ((struct sockaddr_un *) &addr)->sun_path[0] != 0)
+
+ if (s->local_only && s->allow_group_write)
{
- struct stat st = { 0 };
- if (stat (((struct sockaddr_un *) &addr)->sun_path, &st) < 0)
+ if (fchmod (s->fd, S_IWGRP) < 0)
{
- error = clib_error_return_unix (0, "stat (fd %d, '%s')",
- s->fd, s->config);
- goto done;
- }
- st.st_mode |= S_IWGRP;
- if (chmod (((struct sockaddr_un *) &addr)->sun_path, st.st_mode) <
- 0)
- {
- error =
- clib_error_return_unix (0, "chmod (fd %d, '%s', mode %o)",
- s->fd, s->config, st.st_mode);
+ err = clib_error_return_unix (
+ 0, "fchmod (fd %d, '%s', mode S_IWGRP)", s->fd, s->config);
goto done;
}
}
}
else
{
- if ((s->flags & CLIB_SOCKET_F_NON_BLOCKING_CONNECT)
- && fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
+ if (s->non_blocking_connect && fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
{
- error = clib_error_return_unix (0, "fcntl NONBLOCK (fd %d, '%s')",
- s->fd, s->config);
+ err = clib_error_return_unix (0, "fcntl NONBLOCK (fd %d, '%s')",
+ s->fd, s->config);
goto done;
}
- while ((rv = connect (s->fd, &addr.sa, addr_len)) < 0
- && errno == EAGAIN)
+ while ((rv = connect (s->fd, sa, addr_len)) < 0 && errno == EAGAIN)
;
- if (rv < 0 && !((s->flags & CLIB_SOCKET_F_NON_BLOCKING_CONNECT) &&
- errno == EINPROGRESS))
+ if (rv < 0 && !(s->non_blocking_connect && errno == EINPROGRESS))
{
- error = clib_error_return_unix (0, "connect (fd %d, '%s')",
- s->fd, s->config);
+ err = 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) &&
+ if (!s->non_blocking_connect && !s->is_blocking &&
fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
{
- error = clib_error_return_unix (0, "fcntl NONBLOCK2 (fd %d, '%s')",
- s->fd, s->config);
+ err = clib_error_return_unix (0, "fcntl NONBLOCK2 (fd %d, '%s')",
+ s->fd, s->config);
goto done;
}
}
- return error;
-
done:
- if (s->fd > 0)
- close (s->fd);
- return error;
+ if (err && s->fd > -1)
+ {
+ close (s->fd);
+ s->fd = -1;
+ }
+#if CLIB_LINUX
+ if (netns_fd != -1)
+ {
+ setns (CLONE_NEWNET, netns_fd);
+ close (netns_fd);
+ }
+#endif
+ vec_free (name);
+ return err;
}
__clib_export clib_error_t *
diff --git a/src/vppinfra/socket.h b/src/vppinfra/socket.h
index fa5ef1efced..132e89f2871 100644
--- a/src/vppinfra/socket.h
+++ b/src/vppinfra/socket.h
@@ -46,6 +46,16 @@
#include <vppinfra/error.h>
#include <vppinfra/format.h>
+typedef enum
+{
+ CLIB_SOCKET_TYPE_UNKNOWN = 0,
+ CLIB_SOCKET_TYPE_INET,
+ CLIB_SOCKET_TYPE_UNIX,
+#if CLIB_LINUX
+ CLIB_SOCKET_TYPE_LINUX_ABSTRACT,
+#endif
+} clib_socket_type_t;
+
typedef struct _socket_t
{
/* File descriptor. */
@@ -54,15 +64,21 @@ typedef struct _socket_t
/* Config string for socket HOST:PORT or just HOST. */
char *config;
- u32 flags;
-#define CLIB_SOCKET_F_IS_SERVER (1 << 0)
-#define CLIB_SOCKET_F_IS_CLIENT (0 << 0)
-#define CLIB_SOCKET_F_RX_END_OF_FILE (1 << 2)
-#define CLIB_SOCKET_F_NON_BLOCKING_CONNECT (1 << 3)
-#define CLIB_SOCKET_F_ALLOW_GROUP_WRITE (1 << 4)
-#define CLIB_SOCKET_F_SEQPACKET (1 << 5)
-#define CLIB_SOCKET_F_PASSCRED (1 << 6)
-#define CLIB_SOCKET_F_BLOCKING (1 << 7)
+ union
+ {
+ struct
+ {
+ u32 is_server : 1;
+ u32 rx_end_of_file : 1;
+ u32 non_blocking_connect : 1;
+ u32 allow_group_write : 1;
+ u32 is_seqpacket : 1;
+ u32 passcred : 1;
+ u32 is_blocking : 1;
+ u32 local_only : 1;
+ };
+ u32 flags;
+ };
/* Transmit buffer. Holds data waiting to be written. */
u8 *tx_buffer;
@@ -85,9 +101,18 @@ typedef struct _socket_t
int fds[], int num_fds);
clib_error_t *(*sendmsg_func) (struct _socket_t * s, void *msg, int msglen,
int fds[], int num_fds);
+ clib_socket_type_t type;
uword private_data;
} clib_socket_t;
+#define CLIB_SOCKET_FLAG(f) (((clib_socket_t){ .f = 1 }).flags)
+#define CLIB_SOCKET_F_IS_CLIENT 0
+#define CLIB_SOCKET_F_IS_SERVER CLIB_SOCKET_FLAG (is_server)
+#define CLIB_SOCKET_F_ALLOW_GROUP_WRITE CLIB_SOCKET_FLAG (allow_group_write)
+#define CLIB_SOCKET_F_SEQPACKET CLIB_SOCKET_FLAG (is_seqpacket)
+#define CLIB_SOCKET_F_PASSCRED CLIB_SOCKET_FLAG (passcred)
+#define CLIB_SOCKET_F_BLOCKING CLIB_SOCKET_FLAG (is_blocking)
+
/* socket config format is host:port.
Unspecified port causes a free one to be chosen starting
from IPPORT_USERRESERVED (5000). */
@@ -98,10 +123,12 @@ clib_error_t *clib_socket_init_netns (clib_socket_t *socket, u8 *namespace);
clib_error_t *clib_socket_accept (clib_socket_t * server,
clib_socket_t * client);
+int clib_socket_prefix_is_valid (char *s);
+
always_inline uword
clib_socket_is_server (clib_socket_t * sock)
{
- return (sock->flags & CLIB_SOCKET_F_IS_SERVER) != 0;
+ return sock->is_server;
}
always_inline uword
@@ -120,7 +147,7 @@ clib_socket_is_connected (clib_socket_t * sock)
always_inline int
clib_socket_rx_end_of_file (clib_socket_t * s)
{
- return s->flags & CLIB_SOCKET_F_RX_END_OF_FILE;
+ return s->rx_end_of_file;
}
always_inline void *