d7c9d67a9c
Changing remaining users of fcntl.h to use the include from our own POSIX file so that the values in there are consistent in all parts of the sources. Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
400 lines
8.2 KiB
C
400 lines
8.2 KiB
C
/*
|
|
* Copyright (c) 2019 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <zephyr/posix/fcntl.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(net_sock_mgmt, CONFIG_NET_SOCKETS_LOG_LEVEL);
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/sys/util.h>
|
|
#include <zephyr/net/socket.h>
|
|
#include <zephyr/internal/syscall_handler.h>
|
|
#include <zephyr/sys/fdtable.h>
|
|
#include <zephyr/net/socket_net_mgmt.h>
|
|
#include <zephyr/net/ethernet_mgmt.h>
|
|
|
|
#include "sockets_internal.h"
|
|
#include "net_private.h"
|
|
|
|
#define MSG_ALLOC_TIMEOUT K_MSEC(100)
|
|
|
|
__net_socket struct net_mgmt_socket {
|
|
/* Network interface related to this socket */
|
|
struct net_if *iface;
|
|
|
|
/* A way to separate different sockets that listen same events */
|
|
uintptr_t pid;
|
|
|
|
/* net_mgmt mask */
|
|
uint32_t mask;
|
|
|
|
/* Message allocation timeout */
|
|
k_timeout_t alloc_timeout;
|
|
|
|
/* net_mgmt event timeout */
|
|
k_timeout_t wait_timeout;
|
|
|
|
/* Socket protocol */
|
|
int proto;
|
|
|
|
/* Is this entry in use (true) or not (false) */
|
|
uint8_t is_in_use : 1;
|
|
};
|
|
|
|
static struct net_mgmt_socket
|
|
mgmt_sockets[CONFIG_NET_SOCKETS_NET_MGMT_MAX_LISTENERS];
|
|
|
|
static const struct socket_op_vtable net_mgmt_sock_fd_op_vtable;
|
|
|
|
int znet_mgmt_socket(int family, int type, int proto)
|
|
{
|
|
struct net_mgmt_socket *mgmt = NULL;
|
|
int fd, i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(mgmt_sockets); i++) {
|
|
if (mgmt_sockets[i].is_in_use) {
|
|
continue;
|
|
}
|
|
|
|
mgmt = &mgmt_sockets[i];
|
|
}
|
|
|
|
if (mgmt == NULL) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
fd = z_reserve_fd();
|
|
if (fd < 0) {
|
|
errno = ENOSPC;
|
|
return -1;
|
|
}
|
|
|
|
mgmt->is_in_use = true;
|
|
mgmt->proto = proto;
|
|
mgmt->alloc_timeout = MSG_ALLOC_TIMEOUT;
|
|
mgmt->wait_timeout = K_FOREVER;
|
|
|
|
z_finalize_fd(fd, mgmt,
|
|
(const struct fd_op_vtable *)&net_mgmt_sock_fd_op_vtable);
|
|
|
|
return fd;
|
|
}
|
|
|
|
static int znet_mgmt_bind(struct net_mgmt_socket *mgmt,
|
|
const struct sockaddr *addr,
|
|
socklen_t addrlen)
|
|
{
|
|
struct sockaddr_nm *nm_addr = (struct sockaddr_nm *)addr;
|
|
|
|
if (addrlen != sizeof(struct sockaddr_nm)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (nm_addr->nm_ifindex) {
|
|
mgmt->iface = net_if_get_by_index(nm_addr->nm_ifindex);
|
|
if (!mgmt->iface) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
} else {
|
|
mgmt->iface = NULL;
|
|
}
|
|
|
|
mgmt->pid = nm_addr->nm_pid;
|
|
|
|
if (mgmt->proto == NET_MGMT_EVENT_PROTO) {
|
|
mgmt->mask = nm_addr->nm_mask;
|
|
|
|
if (mgmt->iface) {
|
|
mgmt->mask |= NET_MGMT_IFACE_BIT;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ssize_t znet_mgmt_sendto(struct net_mgmt_socket *mgmt,
|
|
const void *buf, size_t len,
|
|
int flags, const struct sockaddr *dest_addr,
|
|
socklen_t addrlen)
|
|
{
|
|
if (mgmt->proto == NET_MGMT_EVENT_PROTO) {
|
|
/* For net_mgmt events, we only listen and never send */
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
|
|
/* Add handling of other network management operations here when
|
|
* needed.
|
|
*/
|
|
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
static ssize_t znet_mgmt_recvfrom(struct net_mgmt_socket *mgmt, void *buf,
|
|
size_t max_len, int flags,
|
|
struct sockaddr *src_addr,
|
|
socklen_t *addrlen)
|
|
{
|
|
struct sockaddr_nm *nm_addr = (struct sockaddr_nm *)src_addr;
|
|
k_timeout_t timeout = mgmt->wait_timeout;
|
|
uint32_t raised_event = 0;
|
|
uint8_t *copy_to = buf;
|
|
struct net_mgmt_msghdr hdr;
|
|
struct net_if *iface;
|
|
const uint8_t *info;
|
|
size_t info_len;
|
|
int ret;
|
|
|
|
if (flags & ZSOCK_MSG_DONTWAIT) {
|
|
timeout = K_NO_WAIT;
|
|
}
|
|
|
|
again:
|
|
if (mgmt->iface == NULL) {
|
|
ret = net_mgmt_event_wait(mgmt->mask, &raised_event,
|
|
&iface, (const void **)&info,
|
|
&info_len, timeout);
|
|
} else {
|
|
ret = net_mgmt_event_wait_on_iface(mgmt->iface,
|
|
mgmt->mask,
|
|
&raised_event,
|
|
(const void **)&info,
|
|
&info_len,
|
|
timeout);
|
|
iface = mgmt->iface;
|
|
}
|
|
|
|
if (ret == -ETIMEDOUT) {
|
|
errno = EAGAIN;
|
|
return -1;
|
|
}
|
|
|
|
if (ret < 0) {
|
|
errno = -ret;
|
|
return -1;
|
|
}
|
|
|
|
if ((mgmt->mask & raised_event) != raised_event) {
|
|
if (K_TIMEOUT_EQ(timeout, K_FOREVER)) {
|
|
goto again;
|
|
}
|
|
|
|
errno = EAGAIN;
|
|
return -1;
|
|
}
|
|
|
|
if (nm_addr) {
|
|
if (iface) {
|
|
nm_addr->nm_ifindex = net_if_get_by_iface(iface);
|
|
} else {
|
|
nm_addr->nm_ifindex = 0;
|
|
}
|
|
|
|
nm_addr->nm_pid = mgmt->pid;
|
|
nm_addr->nm_family = AF_NET_MGMT;
|
|
nm_addr->nm_mask = raised_event;
|
|
}
|
|
|
|
if (info) {
|
|
ret = info_len + sizeof(hdr);
|
|
if (ret > max_len) {
|
|
errno = EMSGSIZE;
|
|
return -1;
|
|
}
|
|
|
|
memcpy(©_to[sizeof(hdr)], info, info_len);
|
|
} else {
|
|
ret = 0;
|
|
}
|
|
|
|
hdr.nm_msg_version = NET_MGMT_SOCKET_VERSION_1;
|
|
hdr.nm_msg_len = ret;
|
|
|
|
memcpy(copy_to, &hdr, sizeof(hdr));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int znet_mgmt_getsockopt(struct net_mgmt_socket *mgmt, int level,
|
|
int optname, void *optval, socklen_t *optlen)
|
|
{
|
|
if (level != SOL_NET_MGMT_RAW || !optval || !optlen) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (mgmt->iface == NULL) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_NET_L2_ETHERNET_MGMT)) {
|
|
if (optname == NET_REQUEST_ETHERNET_GET_QAV_PARAM) {
|
|
int ret;
|
|
|
|
ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QAV_PARAM,
|
|
mgmt->iface, (void *)optval, *optlen);
|
|
if (ret < 0) {
|
|
errno = -ret;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
|
|
static int znet_mgmt_setsockopt(struct net_mgmt_socket *mgmt, int level,
|
|
int optname, const void *optval,
|
|
socklen_t optlen)
|
|
{
|
|
if (level != SOL_NET_MGMT_RAW || !optval || !optlen) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (mgmt->iface == NULL) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_NET_L2_ETHERNET_MGMT)) {
|
|
if (optname == NET_REQUEST_ETHERNET_SET_QAV_PARAM) {
|
|
int ret;
|
|
|
|
ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QAV_PARAM,
|
|
mgmt->iface, (void *)optval, optlen);
|
|
if (ret < 0) {
|
|
errno = -ret;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
} else {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
|
|
static ssize_t net_mgmt_sock_read(void *obj, void *buffer, size_t count)
|
|
{
|
|
return znet_mgmt_recvfrom(obj, buffer, count, 0, NULL, 0);
|
|
}
|
|
|
|
static ssize_t net_mgmt_sock_write(void *obj, const void *buffer,
|
|
size_t count)
|
|
{
|
|
return znet_mgmt_sendto(obj, buffer, count, 0, NULL, 0);
|
|
}
|
|
|
|
static int net_mgmt_sock_ioctl(void *obj, unsigned int request,
|
|
va_list args)
|
|
{
|
|
errno = EOPNOTSUPP;
|
|
return -1;
|
|
}
|
|
|
|
static int net_mgmt_sock_bind(void *obj, const struct sockaddr *addr,
|
|
socklen_t addrlen)
|
|
{
|
|
return znet_mgmt_bind(obj, addr, addrlen);
|
|
}
|
|
|
|
/* The connect() function is not needed */
|
|
static int net_mgmt_sock_connect(void *obj, const struct sockaddr *addr,
|
|
socklen_t addrlen)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The listen() and accept() functions are without any functionality.
|
|
*/
|
|
static int net_mgmt_sock_listen(void *obj, int backlog)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int net_mgmt_sock_accept(void *obj, struct sockaddr *addr,
|
|
socklen_t *addrlen)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t net_mgmt_sock_sendto(void *obj, const void *buf,
|
|
size_t len, int flags,
|
|
const struct sockaddr *dest_addr,
|
|
socklen_t addrlen)
|
|
{
|
|
return znet_mgmt_sendto(obj, buf, len, flags, dest_addr, addrlen);
|
|
}
|
|
|
|
static ssize_t net_mgmt_sock_recvfrom(void *obj, void *buf,
|
|
size_t max_len, int flags,
|
|
struct sockaddr *src_addr,
|
|
socklen_t *addrlen)
|
|
{
|
|
return znet_mgmt_recvfrom(obj, buf, max_len, flags,
|
|
src_addr, addrlen);
|
|
}
|
|
|
|
static int net_mgmt_sock_getsockopt(void *obj, int level, int optname,
|
|
void *optval, socklen_t *optlen)
|
|
{
|
|
return znet_mgmt_getsockopt(obj, level, optname, optval, optlen);
|
|
}
|
|
|
|
static int net_mgmt_sock_setsockopt(void *obj, int level, int optname,
|
|
const void *optval, socklen_t optlen)
|
|
{
|
|
return znet_mgmt_setsockopt(obj, level, optname, optval, optlen);
|
|
}
|
|
|
|
static const struct socket_op_vtable net_mgmt_sock_fd_op_vtable = {
|
|
.fd_vtable = {
|
|
.read = net_mgmt_sock_read,
|
|
.write = net_mgmt_sock_write,
|
|
.ioctl = net_mgmt_sock_ioctl,
|
|
},
|
|
.bind = net_mgmt_sock_bind,
|
|
.connect = net_mgmt_sock_connect,
|
|
.listen = net_mgmt_sock_listen,
|
|
.accept = net_mgmt_sock_accept,
|
|
.sendto = net_mgmt_sock_sendto,
|
|
.recvfrom = net_mgmt_sock_recvfrom,
|
|
.getsockopt = net_mgmt_sock_getsockopt,
|
|
.setsockopt = net_mgmt_sock_setsockopt,
|
|
};
|
|
|
|
static bool net_mgmt_is_supported(int family, int type, int proto)
|
|
{
|
|
if ((type != SOCK_RAW && type != SOCK_DGRAM) ||
|
|
(proto != NET_MGMT_EVENT_PROTO)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
NET_SOCKET_REGISTER(af_net_mgmt, NET_SOCKET_DEFAULT_PRIO, AF_NET_MGMT,
|
|
net_mgmt_is_supported, znet_mgmt_socket);
|