Add DSCP support for POSIX platforms.

This CL only includes the necessary changes in PhysicalSocketServer,
and doesn't include the Java or Objective C API.

Note that this is doing exactly the same thing as UDPSocketPosix
in chromium.

BUG=webrtc:5658

Change-Id: I295455eaccba2a83cdd1bc55848f325c310f8d32
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168260
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Qingsi Wang <qingsi@webrtc.org>
Commit-Queue: Taylor <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30478}
diff --git a/rtc_base/physical_socket_server.cc b/rtc_base/physical_socket_server.cc
index bd6a324..08947e1 100644
--- a/rtc_base/physical_socket_server.cc
+++ b/rtc_base/physical_socket_server.cc
@@ -140,6 +140,7 @@
   Close();
   s_ = ::socket(family, type, 0);
   udp_ = (SOCK_DGRAM == type);
+  family_ = family;
   UpdateLastError();
   if (udp_) {
     SetEnabledEvents(DE_READ | DE_WRITE);
@@ -289,10 +290,18 @@
     return -1;
   socklen_t optlen = sizeof(*value);
   int ret = ::getsockopt(s_, slevel, sopt, (SockOptArg)value, &optlen);
-  if (ret != -1 && opt == OPT_DONTFRAGMENT) {
+  if (ret == -1) {
+    return -1;
+  }
+  if (opt == OPT_DONTFRAGMENT) {
 #if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
     *value = (*value != IP_PMTUDISC_DONT) ? 1 : 0;
 #endif
+  } else if (opt == OPT_DSCP) {
+#if defined(WEBRTC_POSIX)
+    // unshift DSCP value to get six most significant bits of IP DiffServ field
+    *value >>= 2;
+#endif
   }
   return ret;
 }
@@ -306,7 +315,18 @@
 #if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
     value = (value) ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
 #endif
+  } else if (opt == OPT_DSCP) {
+#if defined(WEBRTC_POSIX)
+    // shift DSCP value to fit six most significant bits of IP DiffServ field
+    value <<= 2;
+#endif
   }
+#if defined(WEBRTC_POSIX)
+  if (sopt == IPV6_TCLASS) {
+    // Set the IPv4 option in all cases to support dual-stack sockets.
+    ::setsockopt(s_, IPPROTO_IP, IP_TOS, (SockOptArg)&value, sizeof(value));
+  }
+#endif
   return ::setsockopt(s_, slevel, sopt, (SockOptArg)&value, sizeof(value));
 }
 
@@ -554,8 +574,19 @@
       *sopt = TCP_NODELAY;
       break;
     case OPT_DSCP:
+#if defined(WEBRTC_POSIX)
+      if (family_ == AF_INET6) {
+        *slevel = IPPROTO_IPV6;
+        *sopt = IPV6_TCLASS;
+      } else {
+        *slevel = IPPROTO_IP;
+        *sopt = IP_TOS;
+      }
+      break;
+#else
       RTC_LOG(LS_WARNING) << "Socket::OPT_DSCP not supported.";
       return -1;
+#endif
     case OPT_RTP_SENDTIME_EXTN_ID:
       return -1;  // No logging is necessary as this not a OS socket option.
     default:
diff --git a/rtc_base/physical_socket_server.h b/rtc_base/physical_socket_server.h
index e85b2b0..a71810f 100644
--- a/rtc_base/physical_socket_server.h
+++ b/rtc_base/physical_socket_server.h
@@ -199,11 +199,12 @@
   virtual void EnableEvents(uint8_t events);
   virtual void DisableEvents(uint8_t events);
 
-  static int TranslateOption(Option opt, int* slevel, int* sopt);
+  int TranslateOption(Option opt, int* slevel, int* sopt);
 
   PhysicalSocketServer* ss_;
   SOCKET s_;
   bool udp_;
+  int family_ = 0;
   CriticalSection crit_;
   int error_ RTC_GUARDED_BY(crit_);
   ConnState state_;
diff --git a/rtc_base/socket_unittest.cc b/rtc_base/socket_unittest.cc
index 2af3a8e..6ea4b47 100644
--- a/rtc_base/socket_unittest.cc
+++ b/rtc_base/socket_unittest.cc
@@ -1027,6 +1027,15 @@
   int current_nd, desired_nd = 1;
   ASSERT_EQ(-1, socket->GetOption(Socket::OPT_NODELAY, &current_nd));
   ASSERT_EQ(-1, socket->SetOption(Socket::OPT_NODELAY, desired_nd));
+
+#if defined(WEBRTC_POSIX)
+  // Check DSCP.
+  int current_dscp, desired_dscp = 1;
+  ASSERT_NE(-1, socket->GetOption(Socket::OPT_DSCP, &current_dscp));
+  ASSERT_NE(-1, socket->SetOption(Socket::OPT_DSCP, desired_dscp));
+  ASSERT_NE(-1, socket->GetOption(Socket::OPT_DSCP, &current_dscp));
+  ASSERT_EQ(desired_dscp, current_dscp);
+#endif
 }
 
 void SocketTest::SocketRecvTimestamp(const IPAddress& loopback) {