diff options
-rw-r--r-- | src/plugins/memif/cli.c | 2 | ||||
-rw-r--r-- | src/plugins/memif/memif.c | 194 | ||||
-rw-r--r-- | src/plugins/memif/memif_api.c | 15 | ||||
-rw-r--r-- | src/plugins/memif/private.h | 2 | ||||
-rw-r--r-- | src/plugins/snort/main.c | 6 | ||||
-rw-r--r-- | src/vppinfra/clib.h | 6 | ||||
-rw-r--r-- | src/vppinfra/socket.c | 567 | ||||
-rw-r--r-- | src/vppinfra/socket.h | 49 |
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 * |