From 6b367f846fd32eeab4146aa3e806431d150480a4 Mon Sep 17 00:00:00 2001
From: Nate Karstens <nate.karstens@garmin.com>
Date: Wed, 10 May 2023 09:58:30 -0500
Subject: [PATCH 01/10] Implement IPV6_UNICAST_HOPS socket option

Implements the IPV6_UNICAST_HOPS socket option, which sets
the hop limit for outgoing unicast IPv6 packets.

Based on work from https://savannah.nongnu.org/patch/?9554

Co-authored-by: Christina Schoenrogge <christina.schoenrogge@garmin.com>
Co-authored-by: Chee Bin Hoh <hohcheebin@gmail.com>
Co-authored-by: hanhui <hanhui03@163.com>
---
 src/api/sockets.c          | 12 ++++++++++++
 src/include/lwip/sockets.h |  1 +
 2 files changed, 13 insertions(+)

diff --git a/src/api/sockets.c b/src/api/sockets.c
index 929cd652..a4d09b48 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -3169,6 +3169,12 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
     /* Level: IPPROTO_IPV6 */
     case IPPROTO_IPV6:
       switch (optname) {
+        case IPV6_UNICAST_HOPS:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+          *(int *)optval = sock->conn->pcb.ip->ttl;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_UNICAST_HOPS) = %d\n",
+                                      s, *(int *)optval));
+          break;
         case IPV6_V6ONLY:
           LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
           *(int *)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
@@ -3646,6 +3652,12 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
     /* Level: IPPROTO_IPV6 */
     case IPPROTO_IPV6:
       switch (optname) {
+        case IPV6_UNICAST_HOPS:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+          sock->conn->pcb.ip->ttl = *(int *)optval;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_UNICAST_HOPS, ...) -> %d\n",
+                                      s, *(int *)optval));
+          break;
         case IPV6_V6ONLY:
           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
           if (*(const int *)optval) {
diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h
index b6f3d524..0a2d92ff 100644
--- a/src/include/lwip/sockets.h
+++ b/src/include/lwip/sockets.h
@@ -297,6 +297,7 @@ struct linger {
  * Options for level IPPROTO_IPV6
  */
 #define IPV6_CHECKSUM       7  /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */
+#define IPV6_UNICAST_HOPS   16 /* RFC3493: hop limit in outgoing unicast IPv6 packets */
 #define IPV6_V6ONLY         27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */
 #endif /* LWIP_IPV6 */
 
-- 
2.30.2.windows.1


From d4a89f5291ce28edd76bcdf11df2c0dc3fd23f78 Mon Sep 17 00:00:00 2001
From: Nate Karstens <nate.karstens@garmin.com>
Date: Wed, 10 May 2023 09:58:30 -0500
Subject: [PATCH 02/10] Implement IPV6_MULTICAST_IF socket option

Implements the IPV6_MULTICAST_IF socket option, which
specifies the interface for outgoing multicast packets.

Based on work from https://savannah.nongnu.org/patch/?9554

Co-authored-by: Christina Schoenrogge <christina.schoenrogge@garmin.com>
Co-authored-by: Chee Bin Hoh <hohcheebin@gmail.com>
Co-authored-by: hanhui <hanhui03@163.com>
---
 src/api/sockets.c          | 16 ++++++++++++++++
 src/include/lwip/sockets.h |  2 ++
 2 files changed, 18 insertions(+)

diff --git a/src/api/sockets.c b/src/api/sockets.c
index a4d09b48..ad03b903 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -3181,6 +3181,14 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
                                       s, *(int *)optval));
           break;
+#if LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
+        case IPV6_MULTICAST_IF:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP);
+          *(int *)optval = udp_get_multicast_netif_index(sock->conn->pcb.udp);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_IF) = %d\n",
+                                      s, *(int *)optval));
+          break;
+#endif /* LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
         default:
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
                                       s, optname));
@@ -3668,6 +3676,14 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
                                       s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
           break;
+#if LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
+        case IPV6_MULTICAST_IF:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
+          udp_set_multicast_netif_index(sock->conn->pcb.udp, (u8_t)(*(int *)optval));
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_IF, ..) -> %d\n",
+                                      s, *(int *)optval));
+          break;
+#endif /* LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
 #if LWIP_IPV6_MLD
         case IPV6_JOIN_GROUP:
         case IPV6_LEAVE_GROUP: {
diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h
index 0a2d92ff..c01877aa 100644
--- a/src/include/lwip/sockets.h
+++ b/src/include/lwip/sockets.h
@@ -317,6 +317,8 @@ struct linger {
 #define IP_MULTICAST_TTL   5
 #define IP_MULTICAST_IF    6
 #define IP_MULTICAST_LOOP  7
+
+#define IPV6_MULTICAST_IF    17 /* RFC3493: interface for outgoing multicast packets */
 #endif /* LWIP_MULTICAST_TX_OPTIONS */
 
 #if LWIP_IGMP
-- 
2.30.2.windows.1


From ff393b735a3177a0c6040c27a69db82a8beba166 Mon Sep 17 00:00:00 2001
From: Nate Karstens <nate.karstens@garmin.com>
Date: Wed, 10 May 2023 09:58:30 -0500
Subject: [PATCH 03/10] Use macro to test netconn_type

Changes two IPv4 multicast socket options to use the
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE macro.
---
 src/api/sockets.c | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/src/api/sockets.c b/src/api/sockets.c
index ad03b903..cd5532c9 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -3080,21 +3080,13 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
           break;
 #if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
         case IP_MULTICAST_TTL:
-          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
-          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
-            done_socket(sock);
-            return ENOPROTOOPT;
-          }
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, u8_t, NETCONN_UDP);
           *(u8_t *)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
                                       s, *(int *)optval));
           break;
         case IP_MULTICAST_IF:
-          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr);
-          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
-            done_socket(sock);
-            return ENOPROTOOPT;
-          }
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, struct in_addr, NETCONN_UDP);
           inet_addr_from_ip4addr((struct in_addr *)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
                                       s, *(u32_t *)optval));
-- 
2.30.2.windows.1


From 0fec156e275810367f44e882da6399f388191af1 Mon Sep 17 00:00:00 2001
From: Nate Karstens <nate.karstens@garmin.com>
Date: Wed, 10 May 2023 09:58:30 -0500
Subject: [PATCH 04/10] Implement IPV6_MULTICAST_HOPS socket option

Implements the IPV6_MULTICAST_HOPS socket option, which sets
the hop limit for outgoing multicast packets.

Also fixes debug message in getsockopt for IP_MULTICAST_TTL.

Based on work from https://savannah.nongnu.org/patch/?9554

Co-authored-by: Christina Schoenrogge <christina.schoenrogge@garmin.com>
Co-authored-by: Chee Bin Hoh <hohcheebin@gmail.com>
Co-authored-by: hanhui <hanhui03@163.com>
---
 src/api/sockets.c          | 12 +++++++++++-
 src/include/lwip/sockets.h |  1 +
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/api/sockets.c b/src/api/sockets.c
index cd5532c9..d919bffb 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -3083,7 +3083,7 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, u8_t, NETCONN_UDP);
           *(u8_t *)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
-                                      s, *(int *)optval));
+                                      s, *(u8_t *)optval));
           break;
         case IP_MULTICAST_IF:
           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, struct in_addr, NETCONN_UDP);
@@ -3180,6 +3180,12 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_IF) = %d\n",
                                       s, *(int *)optval));
           break;
+        case IPV6_MULTICAST_HOPS:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, u8_t, NETCONN_UDP);
+          *(u8_t *)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_HOPS) = %d\n",
+                                      s, *(u8_t *)optval));
+          break;
 #endif /* LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
         default:
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
@@ -3675,6 +3681,10 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_IF, ..) -> %d\n",
                                       s, *(int *)optval));
           break;
+        case IPV6_MULTICAST_HOPS:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+          udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t *)optval));
+          break;
 #endif /* LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
 #if LWIP_IPV6_MLD
         case IPV6_JOIN_GROUP:
diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h
index c01877aa..8d0534c2 100644
--- a/src/include/lwip/sockets.h
+++ b/src/include/lwip/sockets.h
@@ -319,6 +319,7 @@ struct linger {
 #define IP_MULTICAST_LOOP  7
 
 #define IPV6_MULTICAST_IF    17 /* RFC3493: interface for outgoing multicast packets */
+#define IPV6_MULTICAST_HOPS  18 /* RFC3493: hop limit for outgoing multicast packets */
 #endif /* LWIP_MULTICAST_TX_OPTIONS */
 
 #if LWIP_IGMP
-- 
2.30.2.windows.1


From c47e7b83914f40db7e159bd5bfc94b2e601077e6 Mon Sep 17 00:00:00 2001
From: Nate Karstens <nate.karstens@garmin.com>
Date: Wed, 10 May 2023 09:58:30 -0500
Subject: [PATCH 05/10] Improve getsockopt for IP_MULTICAST_LOOP

Changes getsockopt handling for IP_MULTICAST_LOOP to
test for UDP and use macro. Also fixes debug message.
---
 src/api/sockets.c | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/src/api/sockets.c b/src/api/sockets.c
index d919bffb..a30f26f6 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -3092,14 +3092,10 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
                                       s, *(u32_t *)optval));
           break;
         case IP_MULTICAST_LOOP:
-          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
-          if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
-            *(u8_t *)optval = 1;
-          } else {
-            *(u8_t *)optval = 0;
-          }
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, u8_t, NETCONN_UDP);
+          *(u8_t *)optval = udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP) ? 1 : 0;
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
-                                      s, *(int *)optval));
+                                      s, *(u8_t *)optval));
           break;
 #endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
         default:
-- 
2.30.2.windows.1


From ce2d815e814dd0385a5574b52968564c48182abb Mon Sep 17 00:00:00 2001
From: Nate Karstens <nate.karstens@garmin.com>
Date: Wed, 10 May 2023 09:58:30 -0500
Subject: [PATCH 06/10] Implement IPV6_MULTICAST_LOOP socket option

Implements the IPV6_MULTICAST_LOOP socket option, which controls
if a copy of the packet is looped back for local delivery.
---
 src/api/sockets.c          | 14 ++++++++++++++
 src/include/lwip/sockets.h |  1 +
 2 files changed, 15 insertions(+)

diff --git a/src/api/sockets.c b/src/api/sockets.c
index a30f26f6..4ec8d233 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -3182,6 +3182,12 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_HOPS) = %d\n",
                                       s, *(u8_t *)optval));
           break;
+        case IPV6_MULTICAST_LOOP:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, u8_t, NETCONN_UDP);
+          *(u8_t *)optval = udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP) ? 1 : 0;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_LOOP) = %d\n",
+                                      s, *(u8_t *)optval));
+          break;
 #endif /* LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
         default:
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
@@ -3681,6 +3687,14 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
           udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t *)optval));
           break;
+        case IPV6_MULTICAST_LOOP:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+          if (*(const u8_t *)optval) {
+            udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
+          } else {
+            udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
+          }
+          break;
 #endif /* LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
 #if LWIP_IPV6_MLD
         case IPV6_JOIN_GROUP:
diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h
index 8d0534c2..4ae9c38e 100644
--- a/src/include/lwip/sockets.h
+++ b/src/include/lwip/sockets.h
@@ -320,6 +320,7 @@ struct linger {
 
 #define IPV6_MULTICAST_IF    17 /* RFC3493: interface for outgoing multicast packets */
 #define IPV6_MULTICAST_HOPS  18 /* RFC3493: hop limit for outgoing multicast packets */
+#define IPV6_MULTICAST_LOOP  19 /* RFC3493: a copy of the packet is looped back for local delivery */
 #endif /* LWIP_MULTICAST_TX_OPTIONS */
 
 #if LWIP_IGMP
-- 
2.30.2.windows.1


From 044e16be69954962f73b35f05c794dfef91e89f3 Mon Sep 17 00:00:00 2001
From: Nate Karstens <nate.karstens@garmin.com>
Date: Wed, 10 May 2023 09:58:30 -0500
Subject: [PATCH 07/10] Use macros in setsockopt handling of IP_PKTINFO

Changes setsockopt handling of IP_PKTINFO to use the provided macros.
---
 src/api/sockets.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/api/sockets.c b/src/api/sockets.c
index 4ec8d233..572518de 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -3541,9 +3541,9 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
         case IP_PKTINFO:
           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
           if (*(const int *)optval) {
-            sock->conn->flags |= NETCONN_FLAG_PKTINFO;
+            netconn_set_flags(sock->conn, NETCONN_FLAG_PKTINFO);
           } else {
-            sock->conn->flags &= ~NETCONN_FLAG_PKTINFO;
+            netconn_clear_flags(sock->conn, NETCONN_FLAG_PKTINFO);
           }
           break;
 #endif /* LWIP_NETBUF_RECVINFO */
-- 
2.30.2.windows.1


From 914c3490fd708e5f5b867f66d58121b891010cd6 Mon Sep 17 00:00:00 2001
From: Nate Karstens <nate.karstens@garmin.com>
Date: Wed, 10 May 2023 09:58:30 -0500
Subject: [PATCH 08/10] Implement IPV6_RECVPKTINFO socket option

Implements the IPV6_RECVPKTINFO socket option for receiving ancillary
data about an IPv6 packet.

Also fixes an issue where the interface index was not being copied
and updates IP_PKTINFO flag to use netconn flags macros.

Based on work from https://savannah.nongnu.org/patch/?9554

Co-authored-by: Christina Schoenrogge <christina.schoenrogge@garmin.com>
Co-authored-by: Joseph Huang <joseph.huang@garmin.com>
Co-authored-by: Chee Bin Hoh <hohcheebin@gmail.com>
Co-authored-by: hanhui <hanhui03@163.com>
---
 src/api/sockets.c          | 29 +++++++++++++++++++++++++++++
 src/core/pbuf.c            |  1 +
 src/include/lwip/sockets.h |  9 +++++++++
 3 files changed, 39 insertions(+)

diff --git a/src/api/sockets.c b/src/api/sockets.c
index 572518de..cf0a8d90 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -1214,6 +1214,23 @@ lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, struct msghdr *msg, u16
         }
 #endif /* LWIP_IPV4 */
       }
+#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!! */
+          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;
+        } else {
+          msg->msg_flags |= MSG_CTRUNC;
+        }
+      }
+#endif /* LWIP_IPV6 */
     }
 #endif /* LWIP_NETBUF_RECVINFO */
 
@@ -3676,6 +3693,18 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
                                       s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
           break;
+#if LWIP_NETBUF_RECVINFO
+        case IPV6_RECVPKTINFO:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
+          if (*(const int *)optval) {
+            netconn_set_flags(sock->conn, NETCONN_FLAG_PKTINFO);
+          } else {
+            netconn_clear_flags(sock->conn, NETCONN_FLAG_PKTINFO);
+          }
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_RECVPKTINFO, ..) -> %d\n",
+                                      s, *(const int *)optval));
+          break;
+#endif /* LWIP_NETBUF_RECVINFO */
 #if LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
         case IPV6_MULTICAST_IF:
           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
diff --git a/src/core/pbuf.c b/src/core/pbuf.c
index ea5e026c..86005137 100644
--- a/src/core/pbuf.c
+++ b/src/core/pbuf.c
@@ -1343,6 +1343,7 @@ pbuf_clone(pbuf_layer layer, pbuf_type type, struct pbuf *p)
     return NULL;
   }
   err = pbuf_copy(q, p);
+  q->if_idx = p->if_idx;
   LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
   LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
   return q;
diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h
index 4ae9c38e..c00098e4 100644
--- a/src/include/lwip/sockets.h
+++ b/src/include/lwip/sockets.h
@@ -299,6 +299,8 @@ struct linger {
 #define IPV6_CHECKSUM       7  /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */
 #define IPV6_UNICAST_HOPS   16 /* RFC3493: hop limit in outgoing unicast IPv6 packets */
 #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 */
 #endif /* LWIP_IPV6 */
 
 #if LWIP_UDP && LWIP_UDPLITE
@@ -343,6 +345,13 @@ struct in_pktinfo {
 };
 #endif /* LWIP_IPV4 */
 
+#if LWIP_IPV6
+struct in6_pktinfo {
+  struct in6_addr ipi6_addr;
+  int             ipi6_ifindex;
+};
+#endif /* LWIP_IPV6 */
+
 #if LWIP_IPV6_MLD
 /*
  * Options and types related to IPv6 multicast membership
-- 
2.30.2.windows.1


From d4b8f1bc29fc8d39c878fc1995ad1fbbe71ab9c8 Mon Sep 17 00:00:00 2001
From: Nate Karstens <nate.karstens@garmin.com>
Date: Wed, 10 May 2023 09:58:30 -0500
Subject: [PATCH 09/10] Render IPv6 addresses in lower case

RFC5952 section 4.3 requires text representations
of IPv6 addresses to use lower case.

Co-authored-by: Chee Bin Hoh <hohcheebin@gmail.com>
---
 src/core/ipv6/ip6_addr.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/core/ipv6/ip6_addr.c b/src/core/ipv6/ip6_addr.c
index 6e0ac86b..d54d5cfb 100644
--- a/src/core/ipv6/ip6_addr.c
+++ b/src/core/ipv6/ip6_addr.c
@@ -57,7 +57,7 @@
 /* used by IP6_ADDR_ANY(6) in ip6_addr.h */
 const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul);
 
-#define lwip_xchar(i)        ((char)((i) < 10 ? '0' + (i) : 'A' + (i) - 10))
+#define lwip_xchar(i)        ((char)((i) < 10 ? '0' + (i) : 'a' + (i) - 10))
 
 /**
  * Check whether "cp" is a valid ascii representation
-- 
2.30.2.windows.1


From ba64cbe1bbad8e8cde1dca7f7b07be42931ed976 Mon Sep 17 00:00:00 2001
From: Nate Karstens <nate.karstens@garmin.com>
Date: Wed, 10 May 2023 09:58:30 -0500
Subject: [PATCH 10/10] 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>
---
 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 cf0a8d90..8be15288 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
-- 
2.30.2.windows.1

