blob: 3bad5c644508992ef3bb2f70ab1fc5abde72ae8d [file]
/*
* Copyright (c) 2017 Linaro Limited
* Copyright (c) 2021 Nordic Semiconductor
* Copyright (c) 2023 Arm Limited (or its affiliates). All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
/* Zephyr headers */
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(net_sock, CONFIG_NET_SOCKETS_LOG_LEVEL);
#include <zephyr/kernel.h>
#include <zephyr/tracing/tracing.h>
#include <zephyr/net/net_log.h>
#include <zephyr/net/socket.h>
#include <zephyr/internal/syscall_handler.h>
#include "sockets_internal.h"
#define VTABLE_CALL(fn, sock, ...) \
({ \
const struct socket_op_vtable *vtable; \
struct k_mutex *lock; \
void *obj; \
int retval; \
\
obj = get_sock_vtable(sock, &vtable, &lock); \
if (obj == NULL) { \
errno = EBADF; \
return -1; \
} \
\
if (vtable->fn == NULL) { \
errno = EOPNOTSUPP; \
return -1; \
} \
\
(void)k_mutex_lock(lock, K_FOREVER); \
\
retval = vtable->fn(obj, __VA_ARGS__); \
\
k_mutex_unlock(lock); \
\
retval; \
})
static inline void *get_sock_vtable(int sock,
const struct socket_op_vtable **vtable,
struct k_mutex **lock)
{
void *ctx;
ctx = zvfs_get_fd_obj_and_vtable(sock,
(const struct fd_op_vtable **)vtable,
lock);
#ifdef CONFIG_USERSPACE
if (ctx != NULL && k_is_in_user_syscall()) {
if (!k_object_is_valid(ctx, K_OBJ_NET_SOCKET)) {
/* Invalidate the context, the caller doesn't have
* sufficient permission or there was some other
* problem with the net socket object
*/
ctx = NULL;
}
}
#endif /* CONFIG_USERSPACE */
if (ctx == NULL) {
NET_DBG("Invalid access on sock %d by thread %p (%s)", sock,
_current, k_thread_name_get(_current));
}
return ctx;
}
size_t msghdr_non_empty_iov_count(const struct net_msghdr *msg)
{
size_t non_empty_iov_count = 0;
for (size_t i = 0; i < msg->msg_iovlen; i++) {
if (msg->msg_iov[i].iov_len) {
non_empty_iov_count++;
}
}
return non_empty_iov_count;
}
void *z_impl_zsock_get_context_object(int sock)
{
const struct socket_op_vtable *ignored;
return get_sock_vtable(sock, &ignored, NULL);
}
#ifdef CONFIG_USERSPACE
void *z_vrfy_zsock_get_context_object(int sock)
{
/* All checking done in implementation */
return z_impl_zsock_get_context_object(sock);
}
#include <zephyr/syscalls/zsock_get_context_object_mrsh.c>
#endif
int z_impl_zsock_socket(int family, int type, int proto)
{
STRUCT_SECTION_FOREACH(net_socket_register, sock_family) {
int ret;
if (sock_family->family != family &&
sock_family->family != NET_AF_UNSPEC) {
continue;
}
NET_ASSERT(sock_family->is_supported);
if (!sock_family->is_supported(family, type, proto)) {
continue;
}
errno = 0;
ret = sock_family->handler(family, type, proto);
SYS_PORT_TRACING_OBJ_INIT(socket, ret < 0 ? -errno : ret,
family, type, proto);
(void)sock_obj_core_alloc(ret, sock_family, family, type, proto);
return ret;
}
errno = EAFNOSUPPORT;
SYS_PORT_TRACING_OBJ_INIT(socket, -errno, family, type, proto);
return -1;
}
#ifdef CONFIG_USERSPACE
static inline int z_vrfy_zsock_socket(int family, int type, int proto)
{
/* implementation call to net_context_get() should do all necessary
* checking
*/
return z_impl_zsock_socket(family, type, proto);
}
#include <zephyr/syscalls/zsock_socket_mrsh.c>
#endif /* CONFIG_USERSPACE */
extern int zvfs_close(int fd);
int z_impl_zsock_close(int sock)
{
return zvfs_close(sock);
}
#ifdef CONFIG_USERSPACE
static inline int z_vrfy_zsock_close(int sock)
{
const struct socket_op_vtable *vtable;
struct k_mutex *lock;
void *ctx;
ctx = get_sock_vtable(sock, &vtable, &lock);
if (ctx == NULL) {
errno = EBADF;
return -1;
}
return z_impl_zsock_close(sock);
}
#include <zephyr/syscalls/zsock_close_mrsh.c>
#endif /* CONFIG_USERSPACE */
int z_impl_zsock_shutdown(int sock, int how)
{
const struct socket_op_vtable *vtable;
struct k_mutex *lock;
void *ctx;
int ret;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, shutdown, sock, how);
ctx = get_sock_vtable(sock, &vtable, &lock);
if (ctx == NULL) {
errno = EBADF;
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, shutdown, sock, -errno);
return -1;
}
if (!vtable->shutdown) {
errno = ENOTSUP;
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, shutdown, sock, -errno);
return -1;
}
(void)k_mutex_lock(lock, K_FOREVER);
NET_DBG("shutdown: ctx=%p, fd=%d, how=%d", ctx, sock, how);
ret = vtable->shutdown(ctx, how);
k_mutex_unlock(lock);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, shutdown, sock, ret < 0 ? -errno : ret);
return ret;
}
#ifdef CONFIG_USERSPACE
static inline int z_vrfy_zsock_shutdown(int sock, int how)
{
return z_impl_zsock_shutdown(sock, how);
}
#include <zephyr/syscalls/zsock_shutdown_mrsh.c>
#endif /* CONFIG_USERSPACE */
int z_impl_zsock_bind(int sock, const struct net_sockaddr *addr, net_socklen_t addrlen)
{
int ret;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, bind, sock, addr, addrlen);
ret = VTABLE_CALL(bind, sock, addr, addrlen);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, bind, sock, ret < 0 ? -errno : ret);
return ret;
}
#ifdef CONFIG_USERSPACE
static inline int z_vrfy_zsock_bind(int sock, const struct net_sockaddr *addr,
net_socklen_t addrlen)
{
struct net_sockaddr_storage dest_addr_copy;
K_OOPS(K_SYSCALL_VERIFY(addrlen <= sizeof(dest_addr_copy)));
K_OOPS(k_usermode_from_copy(&dest_addr_copy, (void *)addr, addrlen));
return z_impl_zsock_bind(sock, (struct net_sockaddr *)&dest_addr_copy,
addrlen);
}
#include <zephyr/syscalls/zsock_bind_mrsh.c>
#endif /* CONFIG_USERSPACE */
int z_impl_zsock_connect(int sock, const struct net_sockaddr *addr,
net_socklen_t addrlen)
{
int ret;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, connect, sock, addr, addrlen);
ret = VTABLE_CALL(connect, sock, addr, addrlen);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, connect, sock,
ret < 0 ? -errno : ret);
return ret;
}
#ifdef CONFIG_USERSPACE
int z_vrfy_zsock_connect(int sock, const struct net_sockaddr *addr,
net_socklen_t addrlen)
{
struct net_sockaddr_storage dest_addr_copy;
K_OOPS(K_SYSCALL_VERIFY(addrlen <= sizeof(dest_addr_copy)));
K_OOPS(k_usermode_from_copy(&dest_addr_copy, (void *)addr, addrlen));
return z_impl_zsock_connect(sock, (struct net_sockaddr *)&dest_addr_copy,
addrlen);
}
#include <zephyr/syscalls/zsock_connect_mrsh.c>
#endif /* CONFIG_USERSPACE */
int z_impl_zsock_listen(int sock, int backlog)
{
int ret;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, listen, sock, backlog);
ret = VTABLE_CALL(listen, sock, backlog);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, listen, sock,
ret < 0 ? -errno : ret);
return ret;
}
#ifdef CONFIG_USERSPACE
static inline int z_vrfy_zsock_listen(int sock, int backlog)
{
return z_impl_zsock_listen(sock, backlog);
}
#include <zephyr/syscalls/zsock_listen_mrsh.c>
#endif /* CONFIG_USERSPACE */
int z_impl_zsock_accept(int sock, struct net_sockaddr *addr, net_socklen_t *addrlen)
{
int new_sock;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, accept, sock);
new_sock = VTABLE_CALL(accept, sock, addr, addrlen);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, accept, new_sock, addr, addrlen,
new_sock < 0 ? -errno : 0);
(void)sock_obj_core_alloc_find(sock, new_sock, NET_SOCK_STREAM);
return new_sock;
}
#ifdef CONFIG_USERSPACE
static inline int z_vrfy_zsock_accept(int sock, struct net_sockaddr *addr,
net_socklen_t *addrlen)
{
net_socklen_t addrlen_copy;
int ret;
K_OOPS(addrlen && k_usermode_from_copy(&addrlen_copy, addrlen,
sizeof(net_socklen_t)));
K_OOPS(addr && K_SYSCALL_MEMORY_WRITE(addr, addrlen ? addrlen_copy : 0));
ret = z_impl_zsock_accept(sock, (struct net_sockaddr *)addr,
addrlen ? &addrlen_copy : NULL);
K_OOPS(ret >= 0 && addrlen && k_usermode_to_copy(addrlen, &addrlen_copy,
sizeof(net_socklen_t)));
return ret;
}
#include <zephyr/syscalls/zsock_accept_mrsh.c>
#endif /* CONFIG_USERSPACE */
ssize_t z_impl_zsock_sendto(int sock, const void *buf, size_t len, int flags,
const struct net_sockaddr *dest_addr, net_socklen_t addrlen)
{
int bytes_sent;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, sendto, sock, len, flags,
dest_addr, addrlen);
bytes_sent = VTABLE_CALL(sendto, sock, buf, len, flags, dest_addr, addrlen);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, sendto, sock,
bytes_sent < 0 ? -errno : bytes_sent);
sock_obj_core_update_send_stats(sock, bytes_sent);
return bytes_sent;
}
#ifdef CONFIG_USERSPACE
ssize_t z_vrfy_zsock_sendto(int sock, const void *buf, size_t len, int flags,
const struct net_sockaddr *dest_addr, net_socklen_t addrlen)
{
struct net_sockaddr_storage dest_addr_copy;
K_OOPS(K_SYSCALL_MEMORY_READ(buf, len));
if (dest_addr) {
K_OOPS(K_SYSCALL_VERIFY(addrlen <= sizeof(dest_addr_copy)));
K_OOPS(k_usermode_from_copy(&dest_addr_copy, (void *)dest_addr,
addrlen));
}
return z_impl_zsock_sendto(sock, (const void *)buf, len, flags,
dest_addr ? (struct net_sockaddr *)&dest_addr_copy : NULL,
addrlen);
}
#include <zephyr/syscalls/zsock_sendto_mrsh.c>
#endif /* CONFIG_USERSPACE */
ssize_t z_impl_zsock_sendmsg(int sock, const struct net_msghdr *msg, int flags)
{
int bytes_sent;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, sendmsg, sock, msg, flags);
bytes_sent = VTABLE_CALL(sendmsg, sock, msg, flags);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, sendmsg, sock,
bytes_sent < 0 ? -errno : bytes_sent);
sock_obj_core_update_send_stats(sock, bytes_sent);
return bytes_sent;
}
#ifdef CONFIG_USERSPACE
static inline ssize_t z_vrfy_zsock_sendmsg(int sock,
const struct net_msghdr *msg,
int flags)
{
struct net_msghdr msg_copy;
struct net_iovec *user_iov;
void *user_name;
void *user_control;
size_t msg_iov_size;
size_t i;
int ret;
K_OOPS(k_usermode_from_copy(&msg_copy, (void *)msg, sizeof(msg_copy)));
if (size_mul_overflow(msg_copy.msg_iovlen, sizeof(struct net_iovec),
&msg_iov_size)) {
errno = EFAULT;
return -1;
}
user_iov = msg_copy.msg_iov;
user_name = msg_copy.msg_name;
user_control = msg_copy.msg_control;
msg_copy.msg_name = NULL;
msg_copy.msg_control = NULL;
msg_copy.msg_iov = k_usermode_alloc_from_copy(user_iov, msg_iov_size);
if (!msg_copy.msg_iov) {
errno = ENOMEM;
goto fail;
}
memset(msg_copy.msg_iov, 0, msg_iov_size);
for (i = 0; i < msg_copy.msg_iovlen; i++) {
struct net_iovec elem;
K_OOPS(k_usermode_from_copy(&elem, &user_iov[i], sizeof(elem)));
msg_copy.msg_iov[i].iov_base =
k_usermode_alloc_from_copy(elem.iov_base, elem.iov_len);
if (!msg_copy.msg_iov[i].iov_base) {
errno = ENOMEM;
goto fail;
}
msg_copy.msg_iov[i].iov_len = elem.iov_len;
}
if (msg_copy.msg_namelen > 0) {
msg_copy.msg_name = k_usermode_alloc_from_copy(user_name,
msg_copy.msg_namelen);
if (!msg_copy.msg_name) {
errno = ENOMEM;
goto fail;
}
}
if (msg_copy.msg_controllen > 0) {
msg_copy.msg_control = k_usermode_alloc_from_copy(user_control,
msg_copy.msg_controllen);
if (!msg_copy.msg_control) {
errno = ENOMEM;
goto fail;
}
}
ret = z_impl_zsock_sendmsg(sock, (const struct net_msghdr *)&msg_copy,
flags);
k_free(msg_copy.msg_name);
k_free(msg_copy.msg_control);
for (i = 0; i < msg_copy.msg_iovlen; i++) {
k_free(msg_copy.msg_iov[i].iov_base);
}
k_free(msg_copy.msg_iov);
return ret;
fail:
if (msg_copy.msg_name) {
k_free(msg_copy.msg_name);
}
if (msg_copy.msg_control) {
k_free(msg_copy.msg_control);
}
if (msg_copy.msg_iov) {
for (i = 0; i < msg_copy.msg_iovlen; i++) {
if (msg_copy.msg_iov[i].iov_base) {
k_free(msg_copy.msg_iov[i].iov_base);
}
}
k_free(msg_copy.msg_iov);
}
return -1;
}
#include <zephyr/syscalls/zsock_sendmsg_mrsh.c>
#endif /* CONFIG_USERSPACE */
ssize_t z_impl_zsock_recvfrom(int sock, void *buf, size_t max_len, int flags,
struct net_sockaddr *src_addr, net_socklen_t *addrlen)
{
int bytes_received;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, recvfrom, sock, max_len, flags, src_addr, addrlen);
bytes_received = VTABLE_CALL(recvfrom, sock, buf, max_len, flags, src_addr, addrlen);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, recvfrom, sock,
src_addr, addrlen,
bytes_received < 0 ? -errno : bytes_received);
sock_obj_core_update_recv_stats(sock, bytes_received);
return bytes_received;
}
#ifdef CONFIG_USERSPACE
ssize_t z_vrfy_zsock_recvfrom(int sock, void *buf, size_t max_len, int flags,
struct net_sockaddr *src_addr, net_socklen_t *addrlen)
{
net_socklen_t addrlen_copy;
ssize_t ret;
if (K_SYSCALL_MEMORY_WRITE(buf, max_len)) {
errno = EFAULT;
return -1;
}
if (addrlen) {
K_OOPS(k_usermode_from_copy(&addrlen_copy, addrlen,
sizeof(net_socklen_t)));
}
K_OOPS(src_addr && K_SYSCALL_MEMORY_WRITE(src_addr, addrlen_copy));
ret = z_impl_zsock_recvfrom(sock, (void *)buf, max_len, flags,
(struct net_sockaddr *)src_addr,
addrlen ? &addrlen_copy : NULL);
if (addrlen) {
K_OOPS(k_usermode_to_copy(addrlen, &addrlen_copy,
sizeof(net_socklen_t)));
}
return ret;
}
#include <zephyr/syscalls/zsock_recvfrom_mrsh.c>
#endif /* CONFIG_USERSPACE */
ssize_t z_impl_zsock_recvmsg(int sock, struct net_msghdr *msg, int flags)
{
int bytes_received;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, recvmsg, sock, msg, flags);
bytes_received = VTABLE_CALL(recvmsg, sock, msg, flags);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, recvmsg, sock, msg,
bytes_received < 0 ? -errno : bytes_received);
sock_obj_core_update_recv_stats(sock, bytes_received);
return bytes_received;
}
#ifdef CONFIG_USERSPACE
ssize_t z_vrfy_zsock_recvmsg(int sock, struct net_msghdr *msg, int flags)
{
struct net_msghdr msg_copy;
struct net_iovec *user_iov;
void *user_name;
void *user_control;
size_t msg_iov_size;
size_t iovlen;
size_t i;
int ret;
if (msg == NULL) {
errno = EINVAL;
return -1;
}
K_OOPS(k_usermode_from_copy(&msg_copy, (void *)msg, sizeof(msg_copy)));
if (msg_copy.msg_iov == NULL) {
errno = ENOMEM;
return -1;
}
if (size_mul_overflow(msg_copy.msg_iovlen, sizeof(struct net_iovec),
&msg_iov_size)) {
errno = EFAULT;
return -1;
}
user_iov = msg_copy.msg_iov;
user_name = msg_copy.msg_name;
user_control = msg_copy.msg_control;
iovlen = msg_copy.msg_iovlen;
msg_copy.msg_name = NULL;
msg_copy.msg_control = NULL;
msg_copy.msg_iov = k_usermode_alloc_from_copy(user_iov, msg_iov_size);
if (!msg_copy.msg_iov) {
errno = ENOMEM;
goto fail;
}
/* Clear the pointers in the copy so that if the allocation in the
* next loop fails, we do not try to free non allocated memory
* in fail branch.
*/
memset(msg_copy.msg_iov, 0, msg_iov_size);
for (i = 0; i < iovlen; i++) {
struct net_iovec elem;
K_OOPS(k_usermode_from_copy(&elem, &user_iov[i], sizeof(elem)));
/* TODO: In practice we do not need to copy the actual data
* in msghdr when receiving data but currently there is no
* ready made function to do just that (unless we want to call
* relevant malloc function here ourselves). So just use
* the copying variant for now.
*/
msg_copy.msg_iov[i].iov_base =
k_usermode_alloc_from_copy(elem.iov_base, elem.iov_len);
if (!msg_copy.msg_iov[i].iov_base) {
errno = ENOMEM;
goto fail;
}
msg_copy.msg_iov[i].iov_len = elem.iov_len;
}
if (msg_copy.msg_namelen > 0) {
if (user_name == NULL) {
errno = EINVAL;
goto fail;
}
msg_copy.msg_name = k_usermode_alloc_from_copy(user_name,
msg_copy.msg_namelen);
if (msg_copy.msg_name == NULL) {
errno = ENOMEM;
goto fail;
}
}
if (msg_copy.msg_controllen > 0) {
if (user_control == NULL) {
errno = EINVAL;
goto fail;
}
msg_copy.msg_control =
k_usermode_alloc_from_copy(user_control,
msg_copy.msg_controllen);
if (msg_copy.msg_control == NULL) {
errno = ENOMEM;
goto fail;
}
}
ret = z_impl_zsock_recvmsg(sock, &msg_copy, flags);
/* Do not copy anything back if there was an error or nothing was
* received.
*/
if (ret > 0) {
if (msg_copy.msg_name != NULL) {
K_OOPS(k_usermode_to_copy(user_name,
msg_copy.msg_name,
msg_copy.msg_namelen));
K_OOPS(k_usermode_to_copy(&msg->msg_namelen,
&msg_copy.msg_namelen,
sizeof(msg->msg_namelen)));
}
if (msg_copy.msg_control != NULL) {
K_OOPS(k_usermode_to_copy(user_control,
msg_copy.msg_control,
msg_copy.msg_controllen));
K_OOPS(k_usermode_to_copy(&msg->msg_controllen,
&msg_copy.msg_controllen,
sizeof(msg->msg_controllen)));
} else {
size_t zero = 0U;
K_OOPS(k_usermode_to_copy(&msg->msg_controllen,
&zero,
sizeof(msg->msg_controllen)));
}
K_OOPS(k_usermode_to_copy(&msg->msg_iovlen,
&msg_copy.msg_iovlen,
sizeof(msg->msg_iovlen)));
/* The new iovlen cannot be bigger than the original one */
NET_ASSERT(msg_copy.msg_iovlen <= iovlen);
for (i = 0; i < iovlen; i++) {
struct net_iovec dest_elem;
K_OOPS(k_usermode_from_copy(&dest_elem, &user_iov[i],
sizeof(dest_elem)));
if (i < msg_copy.msg_iovlen) {
K_OOPS(k_usermode_to_copy(dest_elem.iov_base,
msg_copy.msg_iov[i].iov_base,
msg_copy.msg_iov[i].iov_len));
K_OOPS(k_usermode_to_copy(&user_iov[i].iov_len,
&msg_copy.msg_iov[i].iov_len,
sizeof(user_iov[i].iov_len)));
} else {
/* Clear out those vectors that we could not populate */
K_OOPS(k_usermode_to_copy(&user_iov[i].iov_len, &(size_t){0},
sizeof(user_iov[i].iov_len)));
}
}
K_OOPS(k_usermode_to_copy(&msg->msg_flags,
&msg_copy.msg_flags,
sizeof(msg->msg_flags)));
}
k_free(msg_copy.msg_name);
k_free(msg_copy.msg_control);
/* Note that we need to free according to original iovlen */
for (i = 0; i < iovlen; i++) {
k_free(msg_copy.msg_iov[i].iov_base);
}
k_free(msg_copy.msg_iov);
return ret;
fail:
if (msg_copy.msg_name) {
k_free(msg_copy.msg_name);
}
if (msg_copy.msg_control) {
k_free(msg_copy.msg_control);
}
if (msg_copy.msg_iov) {
for (i = 0; i < msg_copy.msg_iovlen; i++) {
if (msg_copy.msg_iov[i].iov_base) {
k_free(msg_copy.msg_iov[i].iov_base);
}
}
k_free(msg_copy.msg_iov);
}
return -1;
}
#include <zephyr/syscalls/zsock_recvmsg_mrsh.c>
#endif /* CONFIG_USERSPACE */
/* As this is limited function, we don't follow POSIX signature, with
* "..." instead of last arg.
*/
int z_impl_zsock_fcntl_impl(int sock, int cmd, int flags)
{
const struct socket_op_vtable *vtable;
struct k_mutex *lock;
void *obj;
int ret;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, fcntl, sock, cmd, flags);
obj = get_sock_vtable(sock, &vtable, &lock);
if (obj == NULL) {
errno = EBADF;
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, fcntl, sock, -errno);
return -1;
}
(void)k_mutex_lock(lock, K_FOREVER);
ret = zvfs_fdtable_call_ioctl((const struct fd_op_vtable *)vtable,
obj, cmd, flags);
k_mutex_unlock(lock);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, fcntl, sock,
ret < 0 ? -errno : ret);
return ret;
}
#ifdef CONFIG_USERSPACE
static inline int z_vrfy_zsock_fcntl_impl(int sock, int cmd, int flags)
{
return z_impl_zsock_fcntl_impl(sock, cmd, flags);
}
#include <zephyr/syscalls/zsock_fcntl_impl_mrsh.c>
#endif
int z_impl_zsock_ioctl_impl(int sock, unsigned long request, va_list args)
{
const struct socket_op_vtable *vtable;
struct k_mutex *lock;
void *ctx;
int ret;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, ioctl, sock, request);
ctx = get_sock_vtable(sock, &vtable, &lock);
if (ctx == NULL) {
errno = EBADF;
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, ioctl, sock, -errno);
return -1;
}
(void)k_mutex_lock(lock, K_FOREVER);
NET_DBG("ioctl: ctx=%p, fd=%d, request=%lu", ctx, sock, request);
ret = vtable->fd_vtable.ioctl(ctx, request, args);
k_mutex_unlock(lock);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, ioctl, sock,
ret < 0 ? -errno : ret);
return ret;
}
#ifdef CONFIG_USERSPACE
static inline int z_vrfy_zsock_ioctl_impl(int sock, unsigned long request, va_list args)
{
switch (request) {
case ZFD_IOCTL_FIONBIO:
break;
case ZFD_IOCTL_FIONREAD:
case ZFD_IOCTL_FIONWRITE: {
int *avail;
avail = va_arg(args, int *);
K_OOPS(K_SYSCALL_MEMORY_WRITE(avail, sizeof(*avail)));
break;
}
default:
errno = EOPNOTSUPP;
return -1;
}
return z_impl_zsock_ioctl_impl(sock, request, args);
}
#include <zephyr/syscalls/zsock_ioctl_impl_mrsh.c>
#endif
int z_impl_zsock_inet_pton(net_sa_family_t family, const char *src, void *dst)
{
if (net_addr_pton(family, src, dst) == 0) {
return 1;
} else {
return 0;
}
}
#ifdef CONFIG_USERSPACE
static inline int z_vrfy_zsock_inet_pton(net_sa_family_t family,
const char *src, void *dst)
{
int dst_size;
char src_copy[NET_IPV6_ADDR_LEN];
char dst_copy[sizeof(struct net_in6_addr)];
int ret;
switch (family) {
case NET_AF_INET:
dst_size = sizeof(struct net_in_addr);
break;
case NET_AF_INET6:
dst_size = sizeof(struct net_in6_addr);
break;
default:
errno = EAFNOSUPPORT;
return -1;
}
K_OOPS(k_usermode_string_copy(src_copy, (char *)src, sizeof(src_copy)));
ret = z_impl_zsock_inet_pton(family, src_copy, dst_copy);
K_OOPS(k_usermode_to_copy(dst, dst_copy, dst_size));
return ret;
}
#include <zephyr/syscalls/zsock_inet_pton_mrsh.c>
#endif
int z_impl_zsock_getsockopt(int sock, int level, int optname,
void *optval, net_socklen_t *optlen)
{
int ret;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, getsockopt, sock, level, optname);
ret = VTABLE_CALL(getsockopt, sock, level, optname, optval, optlen);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, getsockopt, sock, level, optname,
optval, *optlen, ret < 0 ? -errno : ret);
return ret;
}
#ifdef CONFIG_USERSPACE
int z_vrfy_zsock_getsockopt(int sock, int level, int optname,
void *optval, net_socklen_t *optlen)
{
net_socklen_t kernel_optlen = *(net_socklen_t *)optlen;
void *kernel_optval;
int ret;
if (K_SYSCALL_MEMORY_WRITE(optval, kernel_optlen)) {
errno = -EPERM;
return -1;
}
kernel_optval = k_usermode_alloc_from_copy((const void *)optval,
kernel_optlen);
K_OOPS(!kernel_optval);
ret = z_impl_zsock_getsockopt(sock, level, optname,
kernel_optval, &kernel_optlen);
K_OOPS(k_usermode_to_copy((void *)optval, kernel_optval, kernel_optlen));
K_OOPS(k_usermode_to_copy((void *)optlen, &kernel_optlen,
sizeof(net_socklen_t)));
k_free(kernel_optval);
return ret;
}
#include <zephyr/syscalls/zsock_getsockopt_mrsh.c>
#endif /* CONFIG_USERSPACE */
int z_impl_zsock_setsockopt(int sock, int level, int optname,
const void *optval, net_socklen_t optlen)
{
int ret;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, setsockopt, sock,
level, optname, optval, optlen);
ret = VTABLE_CALL(setsockopt, sock, level, optname, optval, optlen);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, setsockopt, sock,
ret < 0 ? -errno : ret);
return ret;
}
#ifdef CONFIG_USERSPACE
int z_vrfy_zsock_setsockopt(int sock, int level, int optname,
const void *optval, net_socklen_t optlen)
{
void *kernel_optval;
int ret;
kernel_optval = k_usermode_alloc_from_copy((const void *)optval, optlen);
K_OOPS(!kernel_optval);
ret = z_impl_zsock_setsockopt(sock, level, optname,
kernel_optval, optlen);
k_free(kernel_optval);
return ret;
}
#include <zephyr/syscalls/zsock_setsockopt_mrsh.c>
#endif /* CONFIG_USERSPACE */
int z_impl_zsock_getpeername(int sock, struct net_sockaddr *addr,
net_socklen_t *addrlen)
{
int ret;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, getpeername, sock);
ret = VTABLE_CALL(getpeername, sock, addr, addrlen);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, getpeername, sock,
addr, addrlen,
ret < 0 ? -errno : ret);
return ret;
}
#ifdef CONFIG_USERSPACE
static inline int z_vrfy_zsock_getpeername(int sock, struct net_sockaddr *addr,
net_socklen_t *addrlen)
{
net_socklen_t addrlen_copy;
int ret;
K_OOPS(k_usermode_from_copy(&addrlen_copy, (void *)addrlen,
sizeof(net_socklen_t)));
if (K_SYSCALL_MEMORY_WRITE(addr, addrlen_copy)) {
errno = EFAULT;
return -1;
}
ret = z_impl_zsock_getpeername(sock, (struct net_sockaddr *)addr,
&addrlen_copy);
if (ret == 0 &&
k_usermode_to_copy((void *)addrlen, &addrlen_copy,
sizeof(net_socklen_t))) {
errno = EINVAL;
return -1;
}
return ret;
}
#include <zephyr/syscalls/zsock_getpeername_mrsh.c>
#endif /* CONFIG_USERSPACE */
int z_impl_zsock_getsockname(int sock, struct net_sockaddr *addr,
net_socklen_t *addrlen)
{
int ret;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, getsockname, sock);
ret = VTABLE_CALL(getsockname, sock, addr, addrlen);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, getsockname, sock,
addr, addrlen,
ret < 0 ? -errno : ret);
return ret;
}
#ifdef CONFIG_USERSPACE
static inline int z_vrfy_zsock_getsockname(int sock, struct net_sockaddr *addr,
net_socklen_t *addrlen)
{
net_socklen_t addrlen_copy;
int ret;
K_OOPS(k_usermode_from_copy(&addrlen_copy, (void *)addrlen,
sizeof(net_socklen_t)));
if (K_SYSCALL_MEMORY_WRITE(addr, addrlen_copy)) {
errno = EFAULT;
return -1;
}
ret = z_impl_zsock_getsockname(sock, (struct net_sockaddr *)addr,
&addrlen_copy);
if (ret == 0 &&
k_usermode_to_copy((void *)addrlen, &addrlen_copy,
sizeof(net_socklen_t))) {
errno = EINVAL;
return -1;
}
return ret;
}
#include <zephyr/syscalls/zsock_getsockname_mrsh.c>
#endif /* CONFIG_USERSPACE */
int zsock_send_all(int sock, const void *buf, size_t len, int flags, k_timeout_t timeout,
size_t *sent_len)
{
k_timepoint_t end;
int ret, opt;
if (sent_len != NULL) {
*sent_len = 0;
}
/* This function is only meaningful for stream sockets. */
ret = zsock_getsockopt(sock, ZSOCK_SOL_SOCKET, ZSOCK_SO_TYPE,
&opt, &(net_socklen_t){ sizeof(opt) });
if (ret < 0) {
return -errno;
}
if (opt != NET_SOCK_STREAM) {
return -EOPNOTSUPP;
}
end = sys_timepoint_calc(timeout);
while (len > 0) {
ssize_t out_len = zsock_send(sock, buf, len, ZSOCK_MSG_DONTWAIT | flags);
if ((out_len == 0) || (out_len < 0 && errno == EAGAIN)) {
k_ticks_t req_timeout_ticks = sys_timepoint_timeout(end).ticks;
int req_timeout_ms = k_ticks_to_ms_floor32(req_timeout_ticks);
struct zsock_pollfd pfd;
int pollres;
pfd.fd = sock;
pfd.events = ZSOCK_POLLOUT;
pollres = zsock_poll(&pfd, 1, req_timeout_ms);
if (pollres == 0) {
return -ETIMEDOUT;
} else if (pollres > 0) {
continue;
} else {
return -errno;
}
} else if (out_len < 0) {
return -errno;
}
__ASSERT_NO_MSG(out_len <= len);
buf = (const char *)buf + out_len;
len -= out_len;
if (sent_len != NULL) {
*sent_len += out_len;
}
}
return 0;
}
int zsock_sendmsg_all(int sock, const struct net_msghdr *message, int flags,
k_timeout_t timeout, size_t *sent_len)
{
size_t total_len = 0;
size_t offset = 0;
k_timepoint_t end;
int ret, i, opt;
if (sent_len != NULL) {
*sent_len = 0;
}
/* This function is only meaningful for stream sockets. */
ret = zsock_getsockopt(sock, ZSOCK_SOL_SOCKET, ZSOCK_SO_TYPE,
&opt, &(net_socklen_t){ sizeof(opt) });
if (ret < 0) {
return -errno;
}
if (opt != NET_SOCK_STREAM) {
return -EOPNOTSUPP;
}
for (i = 0; i < message->msg_iovlen; i++) {
total_len += message->msg_iov[i].iov_len;
}
end = sys_timepoint_calc(timeout);
while (offset < total_len) {
ret = zsock_sendmsg(sock, message, ZSOCK_MSG_DONTWAIT | flags);
if ((ret == 0) || (ret < 0 && errno == EAGAIN)) {
k_ticks_t req_timeout_ticks = sys_timepoint_timeout(end).ticks;
int req_timeout_ms = k_ticks_to_ms_floor32(req_timeout_ticks);
struct zsock_pollfd pfd;
int pollres;
pfd.fd = sock;
pfd.events = ZSOCK_POLLOUT;
pollres = zsock_poll(&pfd, 1, req_timeout_ms);
if (pollres == 0) {
return -ETIMEDOUT;
} else if (pollres > 0) {
continue;
} else {
return -errno;
}
} else if (ret < 0) {
return -errno;
}
if (sent_len != NULL) {
*sent_len += ret;
}
offset += ret;
if (offset >= total_len) {
break;
}
/* Update msghdr for the next iteration. */
for (i = 0; i < message->msg_iovlen; i++) {
if (ret < message->msg_iov[i].iov_len) {
message->msg_iov[i].iov_len -= ret;
message->msg_iov[i].iov_base =
(uint8_t *)message->msg_iov[i].iov_base + ret;
break;
}
ret -= message->msg_iov[i].iov_len;
message->msg_iov[i].iov_len = 0;
}
}
return 0;
}