From c288752befb6a68c753b6cf14d502efc5ffbee91 Mon Sep 17 00:00:00 2001 From: zhanghongyu Date: Thu, 20 Apr 2023 12:15:24 +0800 Subject: [PATCH] recvmsg: control msg support multi-attribute return adapts to third-party code compilation. in the process of porting ConnMan, multiple control message options are enabled, such as IPV6_RECVPKTINFO and IPV6_RECVHOPLIMIT, so I changed the Filling implementation of the control message. Signed-off-by: zhanghongyu --- net/can/can_recvmsg.c | 25 ++++-------- net/local/local_recvmsg.c | 25 ++++-------- net/socket/recvmsg.c | 18 +++++++- net/udp/udp_recvfrom.c | 49 ++++++++-------------- net/utils/Make.defs | 1 + net/utils/net_cmsg.c | 86 +++++++++++++++++++++++++++++++++++++++ net/utils/utils.h | 28 +++++++++++++ 7 files changed, 164 insertions(+), 68 deletions(-) create mode 100644 net/utils/net_cmsg.c diff --git a/net/can/can_recvmsg.c b/net/can/can_recvmsg.c index c622c14902..95baa34dcf 100644 --- a/net/can/can_recvmsg.c +++ b/net/can/can_recvmsg.c @@ -48,6 +48,7 @@ #ifdef CONFIG_NET_TIMESTAMP #include +#include #endif /**************************************************************************** @@ -490,24 +491,14 @@ ssize_t can_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, state.pr_buffer = msg->msg_iov->iov_base; #ifdef CONFIG_NET_TIMESTAMP - if (conn->timestamp && msg->msg_controllen >= - (sizeof(struct cmsghdr) + sizeof(struct timeval))) + if (conn->timestamp) { - struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); - state.pr_msglen = sizeof(struct timeval); - state.pr_msgbuf = CMSG_DATA(cmsg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SO_TIMESTAMP; - cmsg->cmsg_len = state.pr_msglen; - msg->msg_controllen = sizeof(struct cmsghdr) + sizeof(struct timeval); - } - else - { - /* Expected behavior is that the msg_controllen becomes 0, - * otherwise CMSG_NXTHDR will go into a infinite loop - */ - - msg->msg_controllen = 0; + state.pr_msgbuf = cmsg_append(msg, SOL_SOCKET, SO_TIMESTAMP, + NULL, sizeof(struct timeval)); + if (state.pr_msgbuf != NULL) + { + state.pr_msglen = sizeof(struct timeval); + } } #endif diff --git a/net/local/local_recvmsg.c b/net/local/local_recvmsg.c index a104ad6063..df1dbc8e26 100644 --- a/net/local/local_recvmsg.c +++ b/net/local/local_recvmsg.c @@ -40,6 +40,7 @@ #include "socket/socket.h" #include "local/local.h" +#include "utils/utils.h" /**************************************************************************** * Private Functions @@ -117,22 +118,12 @@ static void local_recvctl(FAR struct local_conn_s *conn, FAR struct msghdr *msg, int flags) { FAR struct local_conn_s *peer; - struct cmsghdr *cmsg; int count; int *fds; int i; net_lock(); - cmsg = CMSG_FIRSTHDR(msg); - count = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int); - cmsg->cmsg_len = 0; - - if (count == 0) - { - goto out; - } - if (conn->lc_peer == NULL) { peer = local_peerconn(conn); @@ -151,10 +142,14 @@ static void local_recvctl(FAR struct local_conn_s *conn, goto out; } - fds = (int *)CMSG_DATA(cmsg); + fds = cmsg_append(msg, SOL_SOCKET, SCM_RIGHTS, NULL, + sizeof(int) * peer->lc_cfpcount); + if (fds == NULL) + { + goto out; + } - count = count > peer->lc_cfpcount ? - peer->lc_cfpcount : count; + count = peer->lc_cfpcount; for (i = 0; i < count; i++) { fds[i] = file_dup(peer->lc_cfps[i], 0, !!(flags & MSG_CMSG_CLOEXEC)); @@ -176,10 +171,6 @@ static void local_recvctl(FAR struct local_conn_s *conn, memmove(peer->lc_cfps[0], peer->lc_cfps[i], sizeof(FAR void *) * peer->lc_cfpcount); } - - cmsg->cmsg_len = CMSG_LEN(sizeof(int) * i); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; } out: diff --git a/net/socket/recvmsg.c b/net/socket/recvmsg.c index 877f6f80ea..833d4d7cb0 100644 --- a/net/socket/recvmsg.c +++ b/net/socket/recvmsg.c @@ -69,6 +69,10 @@ ssize_t psock_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, int flags) { + unsigned long msg_controllen; + FAR void *msg_control; + int ret; + /* Verify that non-NULL pointers were passed */ if (msg == NULL || msg->msg_iov == NULL || msg->msg_iov->iov_base == NULL) @@ -100,7 +104,19 @@ ssize_t psock_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_recvmsg != NULL); - return psock->s_sockif->si_recvmsg(psock, msg, flags); + /* Save the original cmsg information */ + + msg_control = msg->msg_control; + msg_controllen = msg->msg_controllen; + + ret = psock->s_sockif->si_recvmsg(psock, msg, flags); + + /* Recover the pointer and calculate the cmsg's true data length */ + + msg->msg_control = msg_control; + msg->msg_controllen = msg_controllen - msg->msg_controllen; + + return ret; } /**************************************************************************** diff --git a/net/udp/udp_recvfrom.c b/net/udp/udp_recvfrom.c index 358dd425f2..c31690b68b 100644 --- a/net/udp/udp_recvfrom.c +++ b/net/udp/udp_recvfrom.c @@ -42,6 +42,7 @@ #include "devif/devif.h" #include "udp/udp.h" #include "socket/socket.h" +#include "utils/utils.h" /**************************************************************************** * Private Types @@ -64,59 +65,41 @@ struct udp_recvfrom_s static void udp_recvpktinfo(FAR struct udp_recvfrom_s *pstate, FAR void *srcaddr, uint8_t ifindex) { - FAR struct msghdr *msg = pstate->ir_msg; - FAR struct udp_conn_s *conn = pstate->ir_conn; - FAR struct cmsghdr *control = msg->msg_control; - size_t cmsg_len = 0; + FAR struct msghdr *msg = pstate->ir_msg; + FAR struct udp_conn_s *conn = pstate->ir_conn; if (!(conn->flags & _UDP_FLAG_PKTINFO)) { - goto out; + return; } #ifdef CONFIG_NET_IPv4 if (conn->domain == PF_INET) { - FAR struct sockaddr_in *infrom = srcaddr; - FAR struct in_pktinfo *pkt_info = CMSG_DATA(control); + FAR struct sockaddr_in *infrom = srcaddr; + FAR struct in_pktinfo pktinfo; - if (msg->msg_controllen < CMSG_LEN(sizeof(struct in_pktinfo))) - { - goto out; - } + pktinfo.ipi_ifindex = ifindex; + pktinfo.ipi_addr.s_addr = infrom->sin_addr.s_addr; + pktinfo.ipi_spec_dst.s_addr = conn->u.ipv4.laddr; - cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - control->cmsg_level = IPPROTO_IP; - control->cmsg_type = IP_PKTINFO; - control->cmsg_len = cmsg_len; - pkt_info->ipi_ifindex = ifindex; - pkt_info->ipi_addr.s_addr = infrom->sin_addr.s_addr; - pkt_info->ipi_spec_dst.s_addr = conn->u.ipv4.laddr; + cmsg_append(msg, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo)); } #endif #ifdef CONFIG_NET_IPv6 if (conn->domain == PF_INET6) { - FAR struct sockaddr_in6 *infrom = srcaddr; - FAR struct in6_pktinfo *pkt_info = CMSG_DATA(control); + FAR struct sockaddr_in6 *infrom = srcaddr; + FAR struct in6_pktinfo pktinfo; - if (msg->msg_controllen < CMSG_LEN(sizeof(struct in6_pktinfo))) - { - goto out; - } + pktinfo.ipi6_ifindex = ifindex; + net_ipv6addr_copy(&pktinfo.ipi6_addr, infrom->sin6_addr.s6_addr); - cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); - control->cmsg_level = IPPROTO_IPV6; - control->cmsg_type = IPV6_PKTINFO; - control->cmsg_len = cmsg_len; - pkt_info->ipi6_ifindex = ifindex; - net_ipv6addr_copy(&pkt_info->ipi6_addr, infrom->sin6_addr.s6_addr); + cmsg_append(msg, IPPROTO_IPV6, IPV6_PKTINFO, &pktinfo, + sizeof(pktinfo)); } #endif - -out: - msg->msg_controllen = cmsg_len; } /**************************************************************************** diff --git a/net/utils/Make.defs b/net/utils/Make.defs index 03ddb8820a..3782881c60 100644 --- a/net/utils/Make.defs +++ b/net/utils/Make.defs @@ -22,6 +22,7 @@ NET_CSRCS += net_dsec2tick.c net_dsec2timeval.c net_timeval2dsec.c NET_CSRCS += net_chksum.c net_ipchksum.c net_incr32.c net_lock.c net_snoop.c +NET_CSRCS += net_cmsg.c # IPv6 utilities diff --git a/net/utils/net_cmsg.c b/net/utils/net_cmsg.c new file mode 100644 index 0000000000..25d3fc1c4a --- /dev/null +++ b/net/utils/net_cmsg.c @@ -0,0 +1,86 @@ +/**************************************************************************** + * net/utils/net_cmsg.c + * + * 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 + +#include + +#include "utils/utils.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cmsg_append + * + * Description: + * Append specified data into the control message, msg_control and + * msg_controllen will be changed to the appropriate value when success + * + * Input Parameters: + * msg - Buffer to receive the message. + * level - The level of control message. + * type - The type of control message. + * value - If the value is not NULL, this interface copies the data + * to the appropriate location in msg_control, and modify + * msg_control and msg_controllen. + * If the value is NULL, just modify the corresponding value + * of msg. + * value_len - The value length of control message. + * + * Returned Value: + * On success, a pointer to the start address of control message data, + * the caller can copy the data in. + * On failure, return NULL, because of msg_controllen is not enough + * + ****************************************************************************/ + +FAR void *cmsg_append(FAR struct msghdr *msg, int level, int type, + FAR void *value, int value_len) +{ + FAR struct cmsghdr *cmsg; + unsigned long cmsgspace = CMSG_SPACE(value_len); + FAR void *cmsgdata; + + if (msg->msg_controllen < cmsgspace) + { + return NULL; + } + + cmsg = CMSG_FIRSTHDR(msg); + cmsg->cmsg_level = level; + cmsg->cmsg_type = type; + cmsg->cmsg_len = CMSG_LEN(value_len); + cmsgdata = CMSG_DATA(cmsg); + if (value) + { + memcpy(cmsgdata, value, value_len); + } + + msg->msg_control += cmsgspace; + msg->msg_controllen -= cmsgspace; + + return cmsgdata; +} diff --git a/net/utils/utils.h b/net/utils/utils.h index 4a9b529a13..b08efad7ce 100644 --- a/net/utils/utils.h +++ b/net/utils/utils.h @@ -311,6 +311,34 @@ uint16_t icmp_chksum_iob(FAR struct iob_s *iob); uint16_t icmpv6_chksum(FAR struct net_driver_s *dev, unsigned int iplen); #endif +/**************************************************************************** + * Name: cmsg_append + * + * Description: + * Append specified data into the control message, msg_control and + * msg_controllen will be changed to the appropriate value when success + * + * Input Parameters: + * msg - Buffer to receive the message. + * level - The level of control message. + * type - The type of control message. + * value - If the value is not NULL, this interface copies the data + * to the appropriate location in msg_control, and modify + * msg_control and msg_controllen. + * If the value is NULL, just modify the corresponding value + * of msg. + * value_len - The value length of control message. + * + * Returned Value: + * On success, a pointer to the start address of control message data, + * the caller can copy the data in. + * On failure, return NULL, because of msg_controllen is not enough + * + ****************************************************************************/ + +FAR void *cmsg_append(FAR struct msghdr *msg, int level, int type, + FAR void *value, int value_len); + #undef EXTERN #ifdef __cplusplus }