Alter IP_TOS socket call for ECN based on platform. Fixes guitar failure. Respect existing DSCP configuration when setting ECN.

Protected by FLAGS_quic_restart_flag_quic_gfe_socket_factory_ecn_sockets, --quic_restart_flag_quic_gfe_socket_factory_ecn_sockets.

PiperOrigin-RevId: 515378267
diff --git a/quiche/common/platform/api/quiche_udp_socket_platform_api.h b/quiche/common/platform/api/quiche_udp_socket_platform_api.h
index 30ae2d5..a426c14 100644
--- a/quiche/common/platform/api/quiche_udp_socket_platform_api.h
+++ b/quiche/common/platform/api/quiche_udp_socket_platform_api.h
@@ -6,6 +6,8 @@
 #define QUICHE_COMMON_PLATFORM_API_QUICHE_UDP_SOCKET_PLATFORM_API_H_
 
 #include "quiche_platform_impl/quiche_udp_socket_platform_impl.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/common/quiche_ip_address_family.h"
 
 namespace quiche {
 
@@ -20,6 +22,25 @@
 
 inline void SetGoogleSocketOptions(int fd) { SetGoogleSocketOptionsImpl(fd); }
 
+// Retrieves the IP TOS byte for |fd| and |address_family|, based on the correct
+// sockopt for the platform, replaces the two ECN bits of that byte with the
+// value in |ecn_codepoint|.
+// The result is stored in |value| in the proper format to set the TOS byte
+// using a cmsg. |value| must point to memory of size |value_len|. Stores the
+// correct cmsg type to use in |type|.
+// Returns 0 on success. Returns EINVAL if |address_family| is neither IP_V4 nor
+// IP_V6, or if |value_len| is not large enough to store the appropriately
+// formatted argument. If getting the socket option fails, returns the
+// associated error code.
+inline int GetEcnCmsgArgsPreserveDscp(
+    const int fd, const quiche::IpAddressFamily address_family,
+    quic::QuicEcnCodepoint ecn_codepoint, int& type, void* value,
+    socklen_t& value_len) {
+  return GetEcnCmsgArgsPreserveDscpImpl(
+      fd, ToPlatformAddressFamily(address_family),
+      static_cast<uint8_t>(ecn_codepoint), type, value, value_len);
+}
+
 }  // namespace quiche
 
 #endif  // QUICHE_COMMON_PLATFORM_API_QUICHE_UDP_SOCKET_PLATFORM_API_H_
diff --git a/quiche/quic/core/quic_flags_list.h b/quiche/quic/core/quic_flags_list.h
index ced3670..7a96366 100644
--- a/quiche/quic/core/quic_flags_list.h
+++ b/quiche/quic/core/quic_flags_list.h
@@ -83,6 +83,8 @@
 QUIC_FLAG(quic_reloadable_flag_quic_conservative_cwnd_and_pacing_gains, false)
 // If true, when TicketCrypter fails to encrypt a session ticket, quic::TlsServerHandshaker will send a placeholder ticket, instead of an empty one, to the client.
 QUIC_FLAG(quic_reloadable_flag_quic_send_placeholder_ticket_when_encrypt_ticket_fails, true)
+// When true, check what sockopt is used to set the IP TOS byte on the platform.
+QUIC_FLAG(quic_restart_flag_quic_platform_tos_sockopt, false)
 // When true, defaults to BBR congestion control instead of Cubic.
 QUIC_FLAG(quic_reloadable_flag_quic_default_to_bbr, false)
 // When true, quiche UDP sockets report Explicit Congestion Notification (ECN) [RFC3168, RFC9330] results.
diff --git a/quiche/quic/core/quic_packet_writer.h b/quiche/quic/core/quic_packet_writer.h
index 5ebbfe0..3e6cb21 100644
--- a/quiche/quic/core/quic_packet_writer.h
+++ b/quiche/quic/core/quic_packet_writer.h
@@ -35,7 +35,7 @@
   // Whether it is allowed to send this packet without |release_time_delay|.
   bool allow_burst = false;
   // ECN codepoint to use when sending this packet.
-  QuicEcnCodepoint ecn_codepoint;
+  QuicEcnCodepoint ecn_codepoint = ECN_NOT_ECT;
 };
 
 // An interface between writers and the entity managing the
diff --git a/quiche/quic/core/quic_udp_socket_posix.cc b/quiche/quic/core/quic_udp_socket_posix.cc
index d1d5177..26d4ef4 100644
--- a/quiche/quic/core/quic_udp_socket_posix.cc
+++ b/quiche/quic/core/quic_udp_socket_posix.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "quiche/quic/core/quic_types.h"
 #if defined(__APPLE__) && !defined(__APPLE_USE_RFC_3542)
 // This must be defined before including any system headers.
 #define __APPLE_USE_RFC_3542
@@ -20,7 +21,6 @@
 #include "quiche/quic/core/quic_udp_socket.h"
 #include "quiche/quic/platform/api/quic_bug_tracker.h"
 #include "quiche/quic/platform/api/quic_flag_utils.h"
-#include "quiche/quic/platform/api/quic_ip_address_family.h"
 #include "quiche/quic/platform/api/quic_udp_socket_platform_api.h"
 
 #if defined(__APPLE__) && !defined(__APPLE_USE_RFC_3542)
@@ -664,18 +664,35 @@
   }
 #endif
 
+  // TODO(b/270584616): This code block might go away when full support for
+  // marking ECN is implemented.
   if (packet_info.HasValue(QuicUdpPacketInfoBit::ECN)) {
     int cmsg_level =
         packet_info.peer_address().host().IsIPv4() ? IPPROTO_IP : IPPROTO_IPV6;
-    int cmsg_type =
-        packet_info.peer_address().host().IsIPv4() ? IP_TOS : IPV6_TCLASS;
+    int cmsg_type;
+    unsigned char value_buf[20];
+    socklen_t value_len = sizeof(value_buf);
+    if (GetQuicRestartFlag(quic_platform_tos_sockopt)) {
+      QUIC_RESTART_FLAG_COUNT(quic_platform_tos_sockopt);
+      if (GetEcnCmsgArgsPreserveDscp(
+              fd, packet_info.peer_address().host().address_family(),
+              packet_info.ecn_codepoint(), cmsg_type, value_buf,
+              value_len) != 0) {
+        QUIC_LOG_FIRST_N(ERROR, 100)
+            << "Could not get ECN msg type for this platform.";
+        return WriteResult(WRITE_STATUS_ERROR, EINVAL);
+      }
+    } else {
+      cmsg_type = (cmsg_level == IPPROTO_IP) ? IP_TOS : IPV6_TCLASS;
+      *(int*)value_buf = static_cast<int>(packet_info.ecn_codepoint());
+      value_len = sizeof(int);
+    }
     if (!NextCmsg(&hdr, control_buffer, sizeof(control_buffer), cmsg_level,
-                  cmsg_type, sizeof(int), &cmsg)) {
+                  cmsg_type, value_len, &cmsg)) {
       QUIC_LOG_FIRST_N(ERROR, 100) << "Not enough buffer to set ECN.";
       return WriteResult(WRITE_STATUS_ERROR, EINVAL);
     }
-    *reinterpret_cast<int*>(CMSG_DATA(cmsg)) =
-        static_cast<int>(packet_info.ecn_codepoint());
+    memcpy(CMSG_DATA(cmsg), value_buf, value_len);
   }
 
   int rc;