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 <christina.schoenrogge@garmin.com>
Co-authored-by: hanhui <hanhui03@163.com>
This commit is contained in:
Nate Karstens 2023-05-10 09:58:30 -05:00
parent c004029f77
commit ea53e5aa6e
5 changed files with 60 additions and 17 deletions

View File

@ -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 */
}

View File

@ -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:

View File

@ -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)

View File

@ -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 */
};

View File

@ -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