+/*
+ * This follows the recommended mappings between DSCP code points and WMM access classes
+ */
+static u_int32_t so_tc_from_dscp(u_int8_t dscp);
+static u_int32_t
+so_tc_from_dscp(u_int8_t dscp)
+{
+ u_int32_t tc;
+
+ if (dscp >= 0x30 && dscp <= 0x3f)
+ tc = SO_TC_VO;
+ else if (dscp >= 0x20 && dscp <= 0x2f)
+ tc = SO_TC_VI;
+ else if (dscp >= 0x08 && dscp <= 0x17)
+ tc = SO_TC_BK;
+ else
+ tc = SO_TC_BE;
+
+ return (tc);
+}
+
+errno_t
+sock_settclassopt(
+ socket_t sock,
+ const void *optval,
+ size_t optlen) {
+
+ errno_t error = 0;
+ struct sockopt sopt;
+ int sotc;
+
+ if (sock == NULL || optval == NULL || optlen != sizeof(int)) return EINVAL;
+
+ socket_lock(sock, 1);
+ if (!(sock->so_state & SS_ISCONNECTED)) {
+ /* If the socket is not connected then we don't know
+ * if the destination is on LAN or not. Skip
+ * setting traffic class in this case
+ */
+ error = ENOTCONN;
+ goto out;
+ }
+
+ if (sock->so_proto == NULL || sock->so_proto->pr_domain == NULL || sock->so_pcb == NULL) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * Set the socket traffic class based on the passed DSCP code point
+ * regardless of the scope of the destination
+ */
+ sotc = so_tc_from_dscp((*(const int *)optval) >> 2);
+
+ sopt.sopt_dir = SOPT_SET;
+ sopt.sopt_val = CAST_USER_ADDR_T(&sotc);
+ sopt.sopt_valsize = sizeof(sotc);
+ sopt.sopt_p = kernproc;
+ sopt.sopt_level = SOL_SOCKET;
+ sopt.sopt_name = SO_TRAFFIC_CLASS;
+
+ socket_unlock(sock, 0);
+ error = sosetopt(sock, &sopt);
+ socket_lock(sock, 0);
+
+ if (error != 0) {
+ printf("sock_settclassopt: sosetopt SO_TRAFFIC_CLASS failed %d\n", error);
+ goto out;
+ }
+
+ /* Check if the destination address is LAN or link local address.
+ * We do not want to set traffic class bits if the destination
+ * is not local
+ */
+ if (!so_isdstlocal(sock)) {
+ goto out;
+ }
+
+ sopt.sopt_dir = SOPT_SET;
+ sopt.sopt_val = CAST_USER_ADDR_T(optval);
+ sopt.sopt_valsize = optlen;
+ sopt.sopt_p = kernproc;
+
+ switch (sock->so_proto->pr_domain->dom_family) {
+ case AF_INET:
+ sopt.sopt_level = IPPROTO_IP;
+ sopt.sopt_name = IP_TOS;
+ break;
+ case AF_INET6:
+ sopt.sopt_level = IPPROTO_IPV6;
+ sopt.sopt_name = IPV6_TCLASS;
+ break;
+ default:
+ error = EINVAL;
+ goto out;
+ }
+
+ socket_unlock(sock, 1);
+ return sosetopt(sock, &sopt);
+out:
+ socket_unlock(sock, 1);
+ return error;
+}
+
+errno_t
+sock_gettclassopt(
+ socket_t sock,
+ void *optval,
+ size_t *optlen) {
+
+ errno_t error = 0;
+ struct sockopt sopt;
+
+ if (sock == NULL || optval == NULL || optlen == NULL) return EINVAL;
+
+ sopt.sopt_dir = SOPT_GET;
+ sopt.sopt_val = CAST_USER_ADDR_T(optval);
+ sopt.sopt_valsize = *optlen;
+ sopt.sopt_p = kernproc;
+
+ socket_lock(sock, 1);
+ if (sock->so_proto == NULL || sock->so_proto->pr_domain == NULL) {
+ socket_unlock(sock, 1);
+ return EINVAL;
+ }
+
+ switch (sock->so_proto->pr_domain->dom_family) {
+ case AF_INET:
+ sopt.sopt_level = IPPROTO_IP;
+ sopt.sopt_name = IP_TOS;
+ break;
+ case AF_INET6:
+ sopt.sopt_level = IPPROTO_IPV6;
+ sopt.sopt_name = IPV6_TCLASS;
+ break;
+ default:
+ socket_unlock(sock, 1);
+ return EINVAL;
+
+ }
+ socket_unlock(sock, 1);
+ error = sogetopt(sock, &sopt); /* will lock socket */
+ if (error == 0) *optlen = sopt.sopt_valsize;
+ return error;
+}
+