sockets: fix bug #63898: allow socket option IPV6_CHECKSUM for both IPPROTO_IPV6 and IPPROTO_RAW

See bug #63898
This commit is contained in:
Simon Goldschmidt 2023-10-10 22:27:03 +02:00
parent c8d9adde0f
commit 7fd1350802

View File

@ -2920,6 +2920,49 @@ lwip_sockopt_to_ipopt(int optname)
}
}
#if LWIP_IPV6 && LWIP_RAW
static void
lwip_getsockopt_impl_ipv6_checksum(int s, struct lwip_sock* sock, void* optval)
{
if (sock->conn->pcb.raw->chksum_reqd == 0) {
*(int*)optval = -1;
}
else {
*(int*)optval = sock->conn->pcb.raw->chksum_offset;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n",
s, (*(int*)optval)));
}
static int
lwip_setsockopt_impl_ipv6_checksum(int s, struct lwip_sock* sock, const void* optval, socklen_t optlen)
{
/* It should not be possible to disable the checksum generation with ICMPv6
* as per RFC 3542 chapter 3.1 */
if (sock->conn->pcb.raw->protocol == IPPROTO_ICMPV6) {
done_socket(sock);
return EINVAL;
}
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW);
if (*(const int*)optval < 0) {
sock->conn->pcb.raw->chksum_reqd = 0;
}
else if (*(const int*)optval & 1) {
/* Per RFC3542, odd offsets are not allowed */
done_socket(sock);
return EINVAL;
}
else {
sock->conn->pcb.raw->chksum_reqd = 1;
sock->conn->pcb.raw->chksum_offset = (u16_t) * (const int*)optval;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n",
s, sock->conn->pcb.raw->chksum_reqd));
return 0;
}
#endif
/** lwip_getsockopt_impl: the actual implementation of getsockopt:
* same argument as lwip_getsockopt, either called directly or through callback
*/
@ -3169,6 +3212,12 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
/* Level: IPPROTO_IPV6 */
case IPPROTO_IPV6:
switch (optname) {
#if LWIP_IPV6 && LWIP_RAW
case IPV6_CHECKSUM:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW);
lwip_getsockopt_impl_ipv6_checksum(s, sock, optval);
break;
#endif /* LWIP_IPV6 && LWIP_RAW */
case IPV6_V6ONLY:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
*(int *)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
@ -3219,13 +3268,7 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
#if LWIP_IPV6 && LWIP_RAW
case IPV6_CHECKSUM:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW);
if (sock->conn->pcb.raw->chksum_reqd == 0) {
*(int *)optval = -1;
} else {
*(int *)optval = sock->conn->pcb.raw->chksum_offset;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n",
s, (*(int *)optval)) );
lwip_getsockopt_impl_ipv6_checksum(s, sock, optval);
break;
#endif /* LWIP_IPV6 && LWIP_RAW */
default:
@ -3646,7 +3689,15 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
/* Level: IPPROTO_IPV6 */
case IPPROTO_IPV6:
switch (optname) {
case IPV6_V6ONLY:
#if LWIP_IPV6 && LWIP_RAW
case IPV6_CHECKSUM:
err = lwip_setsockopt_impl_ipv6_checksum(s, sock, optval, optlen);
if (err) {
return err;
}
break;
#endif /* LWIP_IPV6 && LWIP_RAW */
case IPV6_V6ONLY:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
if (*(const int *)optval) {
netconn_set_ipv6only(sock->conn, 1);
@ -3744,26 +3795,10 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
switch (optname) {
#if LWIP_IPV6 && LWIP_RAW
case IPV6_CHECKSUM:
/* It should not be possible to disable the checksum generation with ICMPv6
* as per RFC 3542 chapter 3.1 */
if (sock->conn->pcb.raw->protocol == IPPROTO_ICMPV6) {
done_socket(sock);
return EINVAL;
err = lwip_setsockopt_impl_ipv6_checksum(s, sock, optval, optlen);
if (err) {
return err;
}
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW);
if (*(const int *)optval < 0) {
sock->conn->pcb.raw->chksum_reqd = 0;
} else if (*(const int *)optval & 1) {
/* Per RFC3542, odd offsets are not allowed */
done_socket(sock);
return EINVAL;
} else {
sock->conn->pcb.raw->chksum_reqd = 1;
sock->conn->pcb.raw->chksum_offset = (u16_t) * (const int *)optval;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n",
s, sock->conn->pcb.raw->chksum_reqd));
break;
#endif /* LWIP_IPV6 && LWIP_RAW */
default: