Kaynağa Gözat

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

See bug #63898
Simon Goldschmidt 2 yıl önce
ebeveyn
işleme
7fd1350802
1 değiştirilmiş dosya ile 62 ekleme ve 27 silme
  1. 62 27
      src/api/sockets.c

+ 62 - 27
src/api/sockets.c

@@ -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;
-          }
-
-          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;
+          err = lwip_setsockopt_impl_ipv6_checksum(s, sock, optval, optlen);
+          if (err) {
+            return err;
           }
-          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: