From ea53e5aa6e2e973a9aa6b50db3650bac93a916b2 Mon Sep 17 00:00:00 2001 From: Nate Karstens Date: Wed, 10 May 2023 09:58:30 -0500 Subject: [PATCH] Implement IPV6_RECVHOPLIMIT socket option Implements the IPV6_RECVHOPLIMIT socket option for receiving the hop limit in the IPv6 packet. Based on work from https://savannah.nongnu.org/patch/?9554 Co-authored-by: Christina Schoenrogge Co-authored-by: hanhui --- src/api/api_msg.c | 12 +++++++++- src/api/sockets.c | 48 +++++++++++++++++++++++++++----------- src/include/lwip/api.h | 10 +++++--- src/include/lwip/netbuf.h | 5 ++++ src/include/lwip/sockets.h | 2 ++ 5 files changed, 60 insertions(+), 17 deletions(-) diff --git a/src/api/api_msg.c b/src/api/api_msg.c index 8092be96..acd169ab 100644 --- a/src/api/api_msg.c +++ b/src/api/api_msg.c @@ -258,14 +258,24 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, buf->ptr = p; ip_addr_set(&buf->addr, addr); buf->port = port; + buf->flags = 0; #if LWIP_NETBUF_RECVINFO if (conn->flags & NETCONN_FLAG_PKTINFO) { /* get the UDP header - always in the first pbuf, ensured by udp_input */ const struct udp_hdr *udphdr = (const struct udp_hdr *)ip_next_header_ptr(); - buf->flags = NETBUF_FLAG_DESTADDR; + buf->flags |= NETBUF_FLAG_DESTADDR; ip_addr_set(&buf->toaddr, ip_current_dest_addr()); buf->toport_chksum = udphdr->dest; } +#if LWIP_IPV6 + if (netconn_is_flag_set(conn, NETCONN_FLAG_HOPLIMIT)) { + if (ip_current_is_v6()) { + struct ip6_hdr *ip6hdr = (struct ip6_hdr *)ip6_current_header(); + buf->flags |= NETBUF_FLAG_HOPLIMIT; + buf->hoplim = IP6H_HOPLIM(ip6hdr); + } + } +#endif /* LWIP_IPV6 */ #endif /* LWIP_NETBUF_RECVINFO */ } diff --git a/src/api/sockets.c b/src/api/sockets.c index 64c9f485..cb88b8bb 100644 --- a/src/api/sockets.c +++ b/src/api/sockets.c @@ -1193,22 +1193,22 @@ lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, struct msghdr *msg, u16 msg->msg_flags = 0; if (msg->msg_control) { - u8_t wrote_msg = 0; + socklen_t msg_controllen_in = msg->msg_controllen; + msg->msg_controllen = 0; #if LWIP_NETBUF_RECVINFO /* Check if packet info was recorded */ if (buf->flags & NETBUF_FLAG_DESTADDR) { if (IP_IS_V4(&buf->toaddr)) { #if LWIP_IPV4 - if (msg->msg_controllen >= CMSG_SPACE(sizeof(struct in_pktinfo))) { - struct cmsghdr *chdr = CMSG_FIRSTHDR(msg); /* This will always return a header!! */ + if (msg_controllen_in >= msg->msg_controllen + CMSG_SPACE(sizeof(struct in_pktinfo))) { + struct cmsghdr *chdr = ((u8_t *)msg->msg_control + msg->msg_controllen); struct in_pktinfo *pkti = (struct in_pktinfo *)CMSG_DATA(chdr); chdr->cmsg_level = IPPROTO_IP; chdr->cmsg_type = IP_PKTINFO; chdr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); pkti->ipi_ifindex = buf->p->if_idx; inet_addr_from_ip4addr(&pkti->ipi_addr, ip_2_ip4(netbuf_destaddr(buf))); - msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); - wrote_msg = 1; + msg->msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); } else { msg->msg_flags |= MSG_CTRUNC; } @@ -1216,27 +1216,39 @@ lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, struct msghdr *msg, u16 } #if LWIP_IPV6 else if (IP_IS_V6(&buf->toaddr)) { - if (msg->msg_controllen >= CMSG_SPACE(sizeof(struct in6_pktinfo))) { - struct cmsghdr *chdr = CMSG_FIRSTHDR(msg); /* This will always return a header!! */ + if (msg_controllen_in >= msg->msg_controllen + CMSG_SPACE(sizeof(struct in6_pktinfo))) { + struct cmsghdr *chdr = ((u8_t *)msg->msg_control + msg->msg_controllen); struct in6_pktinfo *pkti = (struct in6_pktinfo *)CMSG_DATA(chdr); chdr->cmsg_level = IPPROTO_IPV6; chdr->cmsg_type = IPV6_PKTINFO; chdr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); pkti->ipi6_ifindex = buf->p->if_idx; inet6_addr_from_ip6addr(&pkti->ipi6_addr, ip_2_ip6(netbuf_destaddr(buf))); - msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); - wrote_msg = 1; + msg->msg_controllen += CMSG_SPACE(sizeof(struct in6_pktinfo)); } else { msg->msg_flags |= MSG_CTRUNC; } } #endif /* LWIP_IPV6 */ } -#endif /* LWIP_NETBUF_RECVINFO */ - - if (!wrote_msg) { - msg->msg_controllen = 0; +#if LWIP_IPV6 + if (buf->flags & NETBUF_FLAG_HOPLIMIT) { + if (IP_IS_V6(&buf->toaddr)) { + if (msg_controllen_in >= msg->msg_controllen + CMSG_SPACE(sizeof(int))) { + struct cmsghdr *chdr = ((u8_t *)msg->msg_control + msg->msg_controllen); + int *hoplim = (int *)CMSG_DATA(chdr); + chdr->cmsg_level = IPPROTO_IPV6; + chdr->cmsg_type = IPV6_HOPLIMIT; + chdr->cmsg_len = CMSG_LEN(sizeof(int)); + *hoplim = buf->hoplim; + msg->msg_controllen += CMSG_SPACE(sizeof(int)); + } else { + msg->msg_flags |= MSG_CTRUNC; + } + } } +#endif /* LWIP_IPV6 */ +#endif /* LWIP_NETBUF_RECVINFO */ } /* If we don't peek the incoming message: zero lastdata pointer and free the netbuf */ @@ -3704,6 +3716,16 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_RECVPKTINFO, ..) -> %d\n", s, *(const int *)optval)); break; + case IPV6_RECVHOPLIMIT: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP); + if (*(const int *)optval) { + netconn_set_flags(sock->conn, NETCONN_FLAG_HOPLIMIT); + } else { + netconn_clear_flags(sock->conn, NETCONN_FLAG_HOPLIMIT); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, ..) -> %d\n", + s, *(const int *)optval)); + break; #endif /* LWIP_NETBUF_RECVINFO */ #if LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP case IPV6_MULTICAST_IF: diff --git a/src/include/lwip/api.h b/src/include/lwip/api.h index be8c22aa..ae64f670 100644 --- a/src/include/lwip/api.h +++ b/src/include/lwip/api.h @@ -92,6 +92,10 @@ extern "C" { #endif /* LWIP_NETBUF_RECVINFO */ /** A FIN has been received but not passed to the application yet */ #define NETCONN_FIN_RX_PENDING 0x80 +#if LWIP_NETBUF_RECVINFO +/** The hop limit will be stored for incoming packets */ +#define NETCONN_FLAG_HOPLIMIT 0x100 +#endif /* LWIP_NETBUF_RECVINFO */ /* Helpers to process several netconn_types by the same code */ #define NETCONNTYPE_GROUP(t) ((t)&0xF0) @@ -274,7 +278,7 @@ struct netconn { s16_t linger; #endif /* LWIP_SO_LINGER */ /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */ - u8_t flags; + u16_t flags; #if LWIP_TCP /** TCP: when data passed to netconn_write doesn't fit into the send buffer, this temporarily stores the message. @@ -369,8 +373,8 @@ err_t netconn_gethostbyname(const char *name, ip_addr_t *addr); err_t netconn_err(struct netconn *conn); #define netconn_recv_bufsize(conn) ((conn)->recv_bufsize) -#define netconn_set_flags(conn, set_flags) do { (conn)->flags = (u8_t)((conn)->flags | (set_flags)); } while(0) -#define netconn_clear_flags(conn, clr_flags) do { (conn)->flags = (u8_t)((conn)->flags & (u8_t)(~(clr_flags) & 0xff)); } while(0) +#define netconn_set_flags(conn, set_flags) do { (conn)->flags = (u16_t)((conn)->flags | (set_flags)); } while(0) +#define netconn_clear_flags(conn, clr_flags) do { (conn)->flags = (u16_t)((conn)->flags & (u16_t)(~(clr_flags) & 0xffff)); } while(0) #define netconn_is_flag_set(conn, flag) (((conn)->flags & (flag)) != 0) #define netconn_set_callback_arg(conn, arg) do { (conn)->callback_arg.ptr = (arg); } while(0) diff --git a/src/include/lwip/netbuf.h b/src/include/lwip/netbuf.h index 42a911bc..9d86187f 100644 --- a/src/include/lwip/netbuf.h +++ b/src/include/lwip/netbuf.h @@ -55,6 +55,8 @@ extern "C" { #define NETBUF_FLAG_DESTADDR 0x01 /** This netbuf includes a checksum */ #define NETBUF_FLAG_CHKSUM 0x02 +/** This netbuf has hop limit set */ +#define NETBUF_FLAG_HOPLIMIT 0x04 /** "Network buffer" - contains data and addressing info */ struct netbuf { @@ -66,6 +68,9 @@ struct netbuf { u16_t toport_chksum; #if LWIP_NETBUF_RECVINFO ip_addr_t toaddr; +#if LWIP_IPV6 + u8_t hoplim; +#endif /* LWIP_IPV6 */ #endif /* LWIP_NETBUF_RECVINFO */ #endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ }; diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h index c00098e4..f7424e05 100644 --- a/src/include/lwip/sockets.h +++ b/src/include/lwip/sockets.h @@ -301,6 +301,8 @@ struct linger { #define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */ #define IPV6_RECVPKTINFO 49 /* RFC3542: receive ancillary data for packet */ #define IPV6_PKTINFO 50 /* RFC3542: ancillary data for a packet */ +#define IPV6_RECVHOPLIMIT 51 /* RFC3542: receive hop limit for packet */ +#define IPV6_HOPLIMIT 52 /* RFC3542: ancillary data containing hop limit for packet */ #endif /* LWIP_IPV6 */ #if LWIP_UDP && LWIP_UDPLITE