Files
nuttx/net/netlink/netlink_sockif.c
guoshichao 57637e5927 net: make the sendmsg param type consistent with posix specification
To ensure consistency, in all places where the "sendmsg" function is used
either directly or indirectly, the type of the "struct msghdr *msg" parameter
needs to be modified to "const struct msghdr *msg".

Signed-off-by: guoshichao <guoshichao@xiaomi.com>
2026-01-16 21:11:39 +08:00

777 lines
22 KiB
C

/****************************************************************************
* net/netlink/netlink_sockif.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdbool.h>
#include <string.h>
#include <poll.h>
#include <sched.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/sched.h>
#include <nuttx/kmalloc.h>
#include <nuttx/semaphore.h>
#include <nuttx/wqueue.h>
#include <nuttx/net/net.h>
#include "netlink/netlink.h"
#ifdef CONFIG_NET_NETLINK
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int netlink_setup(FAR struct socket *psock);
static sockcaps_t netlink_sockcaps(FAR struct socket *psock);
static void netlink_addref(FAR struct socket *psock);
static int netlink_bind(FAR struct socket *psock,
FAR const struct sockaddr *addr, socklen_t addrlen);
static int netlink_getsockname(FAR struct socket *psock,
FAR struct sockaddr *addr,
FAR socklen_t *addrlen);
static int netlink_getpeername(FAR struct socket *psock,
FAR struct sockaddr *addr,
FAR socklen_t *addrlen);
static int netlink_connect(FAR struct socket *psock,
FAR const struct sockaddr *addr,
socklen_t addrlen);
static int netlink_poll(FAR struct socket *psock, FAR struct pollfd *fds,
bool setup);
static ssize_t netlink_sendmsg(FAR struct socket *psock,
FAR const struct msghdr *msg, int flags);
static ssize_t netlink_recvmsg(FAR struct socket *psock,
FAR struct msghdr *msg, int flags);
static int netlink_close(FAR struct socket *psock);
/****************************************************************************
* Public Data
****************************************************************************/
const struct sock_intf_s g_netlink_sockif =
{
netlink_setup, /* si_setup */
netlink_sockcaps, /* si_sockcaps */
netlink_addref, /* si_addref */
netlink_bind, /* si_bind */
netlink_getsockname, /* si_getsockname */
netlink_getpeername, /* si_getpeername */
NULL, /* si_listen */
netlink_connect, /* si_connect */
NULL, /* si_accept */
netlink_poll, /* si_poll */
netlink_sendmsg, /* si_sendmsg */
netlink_recvmsg, /* si_recvmsg */
netlink_close /* si_close */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: netlink_setup
*
* Description:
* Called for socket() to verify that the provided socket type and
* protocol are usable by this address family. Perform any family-
* specific socket fields.
*
* Input Parameters:
* psock - A pointer to a user allocated socket structure to be
* initialized.
*
* Returned Value:
* Zero (OK) is returned on success. Otherwise, a negated errno value is
* returned.
*
****************************************************************************/
static int netlink_setup(FAR struct socket *psock)
{
int domain = psock->s_domain;
int type = psock->s_type;
int proto = psock->s_proto;
/* Verify that the protocol is supported */
DEBUGASSERT((unsigned int)proto <= UINT8_MAX);
switch (proto)
{
#ifdef CONFIG_NETLINK_ROUTE
case NETLINK_ROUTE:
break;
#endif
#ifdef CONFIG_NETLINK_NETFILTER
case NETLINK_NETFILTER:
break;
#endif
default:
return -EPROTONOSUPPORT;
}
/* Verify the socket type (domain should always be PF_NETLINK here) */
if (domain == PF_NETLINK && (type == SOCK_RAW || type == SOCK_DGRAM))
{
/* Allocate the NetLink socket connection structure and save it in the
* new socket instance.
*/
FAR struct netlink_conn_s *conn = netlink_alloc();
if (conn == NULL)
{
/* Failed to reserve a connection structure */
return -ENOMEM;
}
/* Set the reference count on the connection structure. This
* reference count will be incremented only if the socket is
* dup'ed
*/
conn->crefs = 1;
/* Attach the connection instance to the socket */
psock->s_conn = conn;
return OK;
}
return -EPROTONOSUPPORT;
}
/****************************************************************************
* Name: netlink_sockcaps
*
* Description:
* Return the bit encoded capabilities of this socket.
*
* Input Parameters:
* psock - Socket structure of the socket whose capabilities are being
* queried.
*
* Returned Value:
* The non-negative set of socket capabilities is returned.
*
****************************************************************************/
static sockcaps_t netlink_sockcaps(FAR struct socket *psock)
{
/* Permit vfcntl to set socket to non-blocking */
return SOCKCAP_NONBLOCKING;
}
/****************************************************************************
* Name: netlink_addref
*
* Description:
* Increment the reference count on the underlying connection structure.
*
* Input Parameters:
* psock - Socket structure of the socket whose reference count will be
* incremented.
*
* Returned Value:
* None
*
****************************************************************************/
static void netlink_addref(FAR struct socket *psock)
{
FAR struct netlink_conn_s *conn;
conn = psock->s_conn;
DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255);
conn->crefs++;
}
/****************************************************************************
* Name: netlink_bind
*
* Description:
* netlink_bind() gives the socket 'conn' the local address 'addr'. 'addr'
* is 'addrlen' bytes long. Traditionally, this is called "assigning a name
* to a socket." When a socket is created with socket, it exists in a name
* space (address family) but has no name assigned.
*
* Input Parameters:
* conn NetLink socket connection structure
* addr Socket local address
* addrlen Length of 'addr'
*
* Returned Value:
* 0 on success; -1 on error with errno set appropriately
*
* EACCES
* The address is protected, and the user is not the superuser.
* EADDRINUSE
* The given address is already in use.
* EINVAL
* The socket is already bound to an address.
* ENOTSOCK
* psock is a descriptor for a file, not a socket.
*
* Assumptions:
*
****************************************************************************/
static int netlink_bind(FAR struct socket *psock,
FAR const struct sockaddr *addr, socklen_t addrlen)
{
FAR struct sockaddr_nl *nladdr;
FAR struct netlink_conn_s *conn;
DEBUGASSERT(addrlen >= sizeof(struct sockaddr_nl));
/* Save the address information in the connection structure */
nladdr = (FAR struct sockaddr_nl *)addr;
conn = psock->s_conn;
conn->pid = nladdr->nl_pid ? nladdr->nl_pid : nxsched_gettid();
conn->groups = nladdr->nl_groups;
return OK;
}
/****************************************************************************
* Name: netlink_getsockname
*
* Description:
* The getsockname() function retrieves the locally-bound name of the
* specified socket, stores this address in the sockaddr structure pointed
* to by the 'addr' argument, and stores the length of this address in the
* object pointed to by the 'addrlen' argument.
*
* If the actual length of the address is greater than the length of the
* supplied sockaddr structure, the stored address will be truncated.
*
* If the socket has not been bound to a local name, the value stored in
* the object pointed to by address is unspecified.
*
* Input Parameters:
* conn NetLink socket connection structure
* addr sockaddr structure to receive data [out]
* addrlen Length of sockaddr structure [in/out]
*
****************************************************************************/
static int netlink_getsockname(FAR struct socket *psock,
FAR struct sockaddr *addr,
FAR socklen_t *addrlen)
{
FAR struct sockaddr_nl *nladdr;
FAR struct netlink_conn_s *conn;
DEBUGASSERT(*addrlen >= sizeof(struct sockaddr_nl));
conn = psock->s_conn;
/* Return the address information in the address structure */
nladdr = (FAR struct sockaddr_nl *)addr;
memset(nladdr, 0, sizeof(struct sockaddr_nl));
nladdr->nl_family = AF_NETLINK;
nladdr->nl_pid = conn->pid;
nladdr->nl_groups = conn->groups;
*addrlen = sizeof(struct sockaddr_nl);
return OK;
}
/****************************************************************************
* Name: netlink_getpeername
*
* Description:
* The netlink_getpeername() function retrieves the remote-connected name
* of the specified packet socket, stores this address in the sockaddr
* structure pointed to by the 'addr' argument, and stores the length of
* this address in the object pointed to by the 'addrlen' argument.
*
* If the actual length of the address is greater than the length of the
* supplied sockaddr structure, the stored address will be truncated.
*
* If the socket has not been bound to a local name, the value stored in
* the object pointed to by address is unspecified.
*
* Parameters:
* psock Socket structure of the socket to be queried
* addr sockaddr structure to receive data [out]
* addrlen Length of sockaddr structure [in/out]
*
* Returned Value:
* On success, 0 is returned, the 'addr' argument points to the address
* of the socket, and the 'addrlen' argument points to the length of the
* address. Otherwise, a negated errno value is returned. See
* getpeername() for the list of appropriate error numbers.
*
****************************************************************************/
static int netlink_getpeername(FAR struct socket *psock,
FAR struct sockaddr *addr,
FAR socklen_t *addrlen)
{
FAR struct sockaddr_nl *nladdr;
FAR struct netlink_conn_s *conn;
DEBUGASSERT(*addrlen >= sizeof(struct sockaddr_nl));
conn = psock->s_conn;
/* Return the address information in the address structure */
nladdr = (FAR struct sockaddr_nl *)addr;
memset(nladdr, 0, sizeof(struct sockaddr_nl));
nladdr->nl_family = AF_NETLINK;
nladdr->nl_pid = conn->dst_pid;
nladdr->nl_groups = conn->dst_groups;
*addrlen = sizeof(struct sockaddr_nl);
return OK;
}
/****************************************************************************
* Name: netlink_connect
*
* Description:
* Perform a netlink connection
*
* Input Parameters:
* psock A reference to the structure of the socket to be connected
* addr The address of the remote server to connect to
* addrlen Length of address buffer
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static int netlink_connect(FAR struct socket *psock,
FAR const struct sockaddr *addr,
socklen_t addrlen)
{
FAR struct sockaddr_nl *nladdr;
FAR struct netlink_conn_s *conn;
DEBUGASSERT(addrlen >= sizeof(struct sockaddr_nl));
/* Save the address information in the connection structure */
nladdr = (FAR struct sockaddr_nl *)addr;
conn = psock->s_conn;
conn->dst_pid = nladdr->nl_pid;
conn->dst_groups = nladdr->nl_groups;
return OK;
}
/****************************************************************************
* Name: netlink_response_available
*
* Description:
* Handle a Netlink response available notification.
*
* Input Parameters:
* Standard work handler parameters
*
* Returned Value:
* None
*
****************************************************************************/
static void netlink_response_available(FAR void *arg)
{
FAR struct netlink_conn_s *conn = arg;
DEBUGASSERT(conn != NULL);
/* The following should always be true ... but maybe not in some race
* condition?
*/
netlink_lock();
if (conn->fds != NULL)
{
/* Wake up the poll() with POLLIN */
poll_notify(&conn->fds, 1, POLLIN);
}
else
{
nwarn("WARNING: Missing references in connection.\n");
}
/* Allow another poll() */
conn->fds = NULL;
netlink_unlock();
}
/****************************************************************************
* Name: netlink_poll
*
* Description:
* The standard poll() operation redirects operations on socket descriptors
* to this function.
*
* POLLUP: Will never be reported
* POLLERR: Reported in the event of any failure.
* POLLOUT: Always reported if requested.
* POLLIN: Reported if requested but only when pending response data is
* available
*
* Input Parameters:
* psock - An instance of the internal socket structure.
* fds - The structure describing the events to be monitored.
* setup - true: Setup up the poll; false: Tear down the poll
*
* Returned Value:
* 0: Success; Negated errno on failure
*
****************************************************************************/
static int netlink_poll(FAR struct socket *psock, FAR struct pollfd *fds,
bool setup)
{
FAR struct netlink_conn_s *conn;
int ret = OK;
conn = psock->s_conn;
/* Check if we are setting up or tearing down the poll */
if (setup)
{
/* If POLLOUT is selected, return immediately (maybe) */
pollevent_t revents = POLLOUT;
/* If POLLIN is selected and a response is available, return
* immediately (maybe).
*/
netlink_lock();
if (netlink_check_response(conn))
{
revents |= POLLIN;
}
/* But return ONLY if POLLIN and/or POLLIN are included in the
* requested event set.
*/
poll_notify(&fds, 1, revents);
if (fds->revents != 0)
{
netlink_unlock();
return OK;
}
/* Set up to be notified when a response is available if POLLIN is
* requested.
*/
if ((fds->events & POLLIN) != 0)
{
/* Some limitations: There can be only a single outstanding POLLIN
* on the Netlink connection.
*/
if (conn->fds != NULL)
{
nerr("ERROR: Multiple polls() on socket not supported.\n");
netlink_unlock();
return -EBUSY;
}
/* Set up the notification */
conn->fds = fds;
ret = netlink_notifier_setup(netlink_response_available,
conn, conn);
if (ret < 0)
{
nerr("ERROR: netlink_notifier_setup() failed: %d\n", ret);
conn->fds = NULL;
}
}
netlink_unlock();
}
else
{
/* Cancel any response notifications */
netlink_notifier_teardown(conn);
conn->fds = NULL;
}
return ret;
}
/****************************************************************************
* Name: netlink_sendmsg
*
* Description:
* If sendmsg() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET)
* socket, the parameters 'msg_name' and 'msg_namelen' are ignored (and the
* error EISCONN may be returned when they are not NULL and 0), and the
* error ENOTCONN is returned when the socket was not actually connected.
*
* Input Parameters:
* psock A reference to the structure of the socket to be connected
* msg msg to send
* flags Send flags (ignored)
*
* Returned Value:
* On success, returns the number of characters sent. On error, a negated
* errno value is returned (see sendmsg() for the list of appropriate error
* values.
*
* Assumptions:
*
****************************************************************************/
static ssize_t netlink_sendmsg(FAR struct socket *psock,
FAR const struct msghdr *msg, int flags)
{
FAR const void *buf = msg->msg_iov->iov_base;
FAR const struct sockaddr *to = msg->msg_name;
socklen_t tolen = msg->msg_namelen;
FAR struct netlink_conn_s *conn;
FAR struct nlmsghdr *nlmsg;
struct sockaddr_nl nladdr;
int ret;
/* Validity check, only single iov supported */
if (msg->msg_iovlen != 1)
{
return -ENOTSUP;
}
/* Get the underlying connection structure */
conn = psock->s_conn;
if (to == NULL)
{
/* netlink_send() */
/* Format the address */
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pad = 0;
nladdr.nl_pid = conn->dst_pid;
nladdr.nl_groups = conn->dst_groups;
to = (FAR const struct sockaddr *)&nladdr;
tolen = sizeof(struct sockaddr_nl);
}
DEBUGASSERT(tolen >= sizeof(struct sockaddr_nl));
/* Get a reference to the netlink message */
nlmsg = (FAR struct nlmsghdr *)buf;
DEBUGASSERT(nlmsg->nlmsg_len >= sizeof(struct nlmsghdr));
switch (psock->s_proto)
{
#ifdef CONFIG_NETLINK_ROUTE
case NETLINK_ROUTE:
ret = netlink_route_sendto(conn, nlmsg,
msg->msg_iov->iov_len, flags,
(FAR const struct sockaddr_nl *)to,
tolen);
break;
#endif
#ifdef CONFIG_NETLINK_NETFILTER
case NETLINK_NETFILTER:
ret = netlink_netfilter_sendto(conn, nlmsg,
msg->msg_iov->iov_len, flags,
(FAR const struct sockaddr_nl *)to,
tolen);
break;
#endif
default:
ret = -EOPNOTSUPP;
break;
}
return ret;
}
/****************************************************************************
* Name: netlink_recvmsg
*
* Description:
* recvmsg() receives messages from a socket, and may be used to receive
* data on a socket whether or not it is connection-oriented.
*
* If msg_name is not NULL, and the underlying protocol provides the source
* address, this source address is filled in. The argument 'msg_namelen' is
* initialized to the size of the buffer associated with msg_name, and
* modified on return to indicate the actual size of the address stored
* there.
*
* Input Parameters:
* psock A pointer to a NuttX-specific, internal socket structure
* msg Buffer to receive the message
* flags Receive flags (ignored)
*
****************************************************************************/
static ssize_t netlink_recvmsg(FAR struct socket *psock,
FAR struct msghdr *msg, int flags)
{
FAR void *buf = msg->msg_iov->iov_base;
size_t len = msg->msg_iov->iov_len;
FAR struct sockaddr *from = msg->msg_name;
FAR socklen_t *fromlen = &msg->msg_namelen;
FAR struct netlink_response_s *entry;
FAR struct socket_conn_s *conn;
int ret = OK;
DEBUGASSERT(from == NULL ||
(fromlen != NULL && *fromlen >= sizeof(struct sockaddr_nl)));
if (msg->msg_iovlen != 1)
{
return -ENOTSUP;
}
/* Find the response to this message. The return value */
entry = netlink_tryget_response(psock->s_conn);
if (entry == NULL)
{
conn = psock->s_conn;
/* No response is available, but presumably, one is expected. Check
* if the socket has been configured for non-blocking operation.
*/
if (_SS_ISNONBLOCK(conn->s_flags) || (flags & MSG_DONTWAIT) != 0)
{
return -EAGAIN;
}
/* Wait for the response. */
ret = netlink_get_response(psock->s_conn, &entry);
/* If interrupted by signals, return errno */
if (entry == NULL)
{
return ret;
}
}
if (len > entry->msg.nlmsg_len)
{
len = entry->msg.nlmsg_len;
}
/* Copy the payload to the user buffer */
memcpy(buf, &entry->msg, len);
kmm_free(entry);
if (from != NULL)
{
netlink_getpeername(psock, from, fromlen);
}
return len;
}
/****************************************************************************
* Name: netlink_close
*
* Description:
* Performs the close operation on a NetLink socket instance
*
* Input Parameters:
* psock Socket instance
*
* Returned Value:
* 0 on success; -1 on error with errno set appropriately.
*
* Assumptions:
*
****************************************************************************/
static int netlink_close(FAR struct socket *psock)
{
FAR struct netlink_conn_s *conn = psock->s_conn;
/* Perform some pre-close operations for the NETLINK socket type. */
/* Is this the last reference to the connection structure (there
* could be more if the socket was dup'ed).
*/
if (conn->crefs <= 1)
{
/* Free the connection structure */
conn->crefs = 0;
netlink_free(psock->s_conn);
}
else
{
/* No.. Just decrement the reference count */
conn->crefs--;
}
return OK;
}
#endif /* CONFIG_NET_NETLINK */