+ break;
+
+ case TCP_CONNECTIONTIMEOUT:
+ error = sooptcopyin(sopt, &optval, sizeof optval,
+ sizeof optval);
+ if (error) {
+ break;
+ }
+ if (optval < 0 || optval > UINT32_MAX / TCP_RETRANSHZ) {
+ error = EINVAL;
+ } else {
+ tp->t_keepinit = optval * TCP_RETRANSHZ;
+ if (tp->t_state == TCPS_SYN_RECEIVED ||
+ tp->t_state == TCPS_SYN_SENT) {
+ tp->t_timer[TCPT_KEEP] = OFFSET_FROM_START(tp,
+ TCP_CONN_KEEPINIT(tp));
+ tcp_check_timer_state(tp);
+ }
+ }
+ break;
+
+ case TCP_KEEPINTVL:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error) {
+ break;
+ }
+ if (optval < 0 || optval > UINT32_MAX / TCP_RETRANSHZ) {
+ error = EINVAL;
+ } else {
+ tp->t_keepintvl = optval * TCP_RETRANSHZ;
+ if (tp->t_state == TCPS_FIN_WAIT_2 &&
+ TCP_CONN_MAXIDLE(tp) > 0) {
+ tp->t_timer[TCPT_2MSL] = OFFSET_FROM_START(tp,
+ TCP_CONN_MAXIDLE(tp));
+ tcp_check_timer_state(tp);
+ }
+ }
+ break;
+
+ case TCP_KEEPCNT:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error) {
+ break;
+ }
+ if (optval < 0 || optval > INT32_MAX) {
+ error = EINVAL;
+ } else {
+ tp->t_keepcnt = optval;
+ if (tp->t_state == TCPS_FIN_WAIT_2 &&
+ TCP_CONN_MAXIDLE(tp) > 0) {
+ tp->t_timer[TCPT_2MSL] = OFFSET_FROM_START(tp,
+ TCP_CONN_MAXIDLE(tp));
+ tcp_check_timer_state(tp);
+ }
+ }
+ break;
+
+ case TCP_KEEPALIVE_OFFLOAD:
+ if ((error = priv_check_cred(kauth_cred_get(),
+ PRIV_NETINET_TCP_KA_OFFLOAD, 0)) != 0) {
+ break;
+ }
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error) {
+ break;
+ }
+ if (optval < 0 || optval > INT32_MAX) {
+ error = EINVAL;
+ break;
+ }
+ if (optval != 0) {
+ error = tcp_set_keep_alive_offload(so,
+ sopt->sopt_p);
+ } else {
+ tcp_clear_keep_alive_offload(so);
+ }
+ break;
+
+ case PERSIST_TIMEOUT:
+ error = sooptcopyin(sopt, &optval, sizeof optval,
+ sizeof optval);
+ if (error) {
+ break;
+ }
+ if (optval < 0) {
+ error = EINVAL;
+ } else {
+ tp->t_persist_timeout = optval * TCP_RETRANSHZ;
+ }
+ break;
+ case TCP_RXT_CONNDROPTIME:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error) {
+ break;
+ }
+ if (optval < 0) {
+ error = EINVAL;
+ } else {
+ tp->t_rxt_conndroptime = optval * TCP_RETRANSHZ;
+ }
+ break;
+ case TCP_NOTSENT_LOWAT:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error) {
+ break;
+ }
+ if (optval < 0) {
+ error = EINVAL;
+ break;
+ } else {
+ if (optval == 0) {
+ so->so_flags &= ~(SOF_NOTSENT_LOWAT);
+ tp->t_notsent_lowat = 0;
+ } else {
+ so->so_flags |= SOF_NOTSENT_LOWAT;
+ tp->t_notsent_lowat = optval;
+ }
+ }
+ break;
+ case TCP_ADAPTIVE_READ_TIMEOUT:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error) {
+ break;
+ }
+ if (optval < 0 ||
+ optval > TCP_ADAPTIVE_TIMEOUT_MAX) {
+ error = EINVAL;
+ break;
+ } else if (optval == 0) {
+ tp->t_adaptive_rtimo = 0;
+ tcp_keepalive_reset(tp);
+
+ if (tp->t_mpsub) {
+ mptcp_reset_keepalive(tp);
+ }
+ } else {
+ tp->t_adaptive_rtimo = optval;
+ }
+ break;
+ case TCP_ADAPTIVE_WRITE_TIMEOUT:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error) {
+ break;
+ }
+ if (optval < 0 ||
+ optval > TCP_ADAPTIVE_TIMEOUT_MAX) {
+ error = EINVAL;
+ break;
+ } else {
+ tp->t_adaptive_wtimo = optval;
+ }
+ break;
+ case TCP_ENABLE_MSGS:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error) {
+ break;
+ }
+ if (optval < 0 || optval > 1) {
+ error = EINVAL;
+ } else if (optval == 1) {
+ /*
+ * Check if messages option is already
+ * enabled, if so return.
+ */
+ if (so->so_flags & SOF_ENABLE_MSGS) {
+ VERIFY(so->so_msg_state != NULL);
+ break;
+ }
+
+ /*
+ * allocate memory for storing message
+ * related state
+ */
+ VERIFY(so->so_msg_state == NULL);
+ MALLOC(so->so_msg_state,
+ struct msg_state *,
+ sizeof(struct msg_state),
+ M_TEMP, M_WAITOK | M_ZERO);
+ if (so->so_msg_state == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ /* Enable message delivery */
+ so->so_flags |= SOF_ENABLE_MSGS;
+ } else {
+ /*
+ * Can't disable message delivery on socket
+ * because of restrictions imposed by
+ * encoding/decoding
+ */
+ error = EINVAL;
+ }
+ break;
+ case TCP_SENDMOREACKS:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error) {
+ break;
+ }
+ if (optval < 0 || optval > 1) {
+ error = EINVAL;
+ } else if (optval == 0) {
+ tp->t_flagsext &= ~(TF_NOSTRETCHACK);
+ } else {
+ tp->t_flagsext |= TF_NOSTRETCHACK;
+ }
+ break;
+ case TCP_DISABLE_BLACKHOLE_DETECTION:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error) {
+ break;
+ }
+ if (optval < 0 || optval > 1) {
+ error = EINVAL;
+ } else if (optval == 0) {
+ tp->t_flagsext &= ~TF_NOBLACKHOLE_DETECTION;
+ } else {
+ tp->t_flagsext |= TF_NOBLACKHOLE_DETECTION;
+ if ((tp->t_flags & TF_BLACKHOLE) &&
+ tp->t_pmtud_saved_maxopd > 0) {
+ tcp_pmtud_revert_segment_size(tp);
+ }
+ }
+ break;
+ case TCP_FASTOPEN:
+ if (!(tcp_fastopen & TCP_FASTOPEN_SERVER)) {
+ error = ENOTSUP;
+ break;
+ }
+
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error) {
+ break;
+ }
+ if (optval < 0 || optval > 1) {
+ error = EINVAL;
+ break;
+ }
+ if (tp->t_state != TCPS_LISTEN) {
+ error = EINVAL;
+ break;
+ }
+ if (optval) {
+ tp->t_flagsext |= TF_FASTOPEN;
+ } else {
+ tcp_disable_tfo(tp);
+ }
+ break;
+ case TCP_FASTOPEN_FORCE_HEURISTICS:
+
+ break;
+ case TCP_FASTOPEN_FORCE_ENABLE:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+
+ if (error) {
+ break;
+ }
+ if (optval < 0 || optval > 1) {
+ error = EINVAL;
+ break;
+ }
+
+ if (tp->t_state != TCPS_CLOSED) {
+ error = EINVAL;
+ break;
+ }
+ if (optval) {
+ tp->t_flagsext |= TF_FASTOPEN_FORCE_ENABLE;
+ } else {
+ tp->t_flagsext &= ~TF_FASTOPEN_FORCE_ENABLE;
+ }
+
+ break;
+ case TCP_ENABLE_ECN:
+ error = sooptcopyin(sopt, &optval, sizeof optval,
+ sizeof optval);
+ if (error) {
+ break;
+ }
+ if (optval) {
+ tp->ecn_flags |= TE_ECN_MODE_ENABLE;
+ tp->ecn_flags &= ~TE_ECN_MODE_DISABLE;
+ } else {
+ tp->ecn_flags &= ~TE_ECN_MODE_ENABLE;
+ tp->ecn_flags |= TE_ECN_MODE_DISABLE;
+ }
+ break;
+ case TCP_ECN_MODE:
+ error = sooptcopyin(sopt, &optval, sizeof optval,
+ sizeof optval);
+ if (error) {
+ break;
+ }
+ if (optval == ECN_MODE_DEFAULT) {
+ tp->ecn_flags &= ~TE_ECN_MODE_ENABLE;
+ tp->ecn_flags &= ~TE_ECN_MODE_DISABLE;
+ } else if (optval == ECN_MODE_ENABLE) {
+ tp->ecn_flags |= TE_ECN_MODE_ENABLE;
+ tp->ecn_flags &= ~TE_ECN_MODE_DISABLE;
+ } else if (optval == ECN_MODE_DISABLE) {
+ tp->ecn_flags &= ~TE_ECN_MODE_ENABLE;
+ tp->ecn_flags |= TE_ECN_MODE_DISABLE;
+ } else {
+ error = EINVAL;
+ }
+ break;
+ case TCP_NOTIFY_ACKNOWLEDGEMENT:
+ error = sooptcopyin(sopt, &optval,
+ sizeof(optval), sizeof(optval));
+ if (error) {
+ break;
+ }
+ if (optval <= 0) {
+ error = EINVAL;
+ break;
+ }
+ if (tp->t_notify_ack_count >= TCP_MAX_NOTIFY_ACK) {
+ error = ETOOMANYREFS;
+ break;
+ }
+
+ /*
+ * validate that the given marker id is not
+ * a duplicate to avoid ambiguity
+ */
+ if ((error = tcp_notify_ack_id_valid(tp, so,
+ optval)) != 0) {
+ break;
+ }
+ error = tcp_add_notify_ack_marker(tp, optval);
+ break;
+ case SO_FLUSH:
+ if ((error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval))) != 0) {
+ break;
+ }
+
+ error = inp_flush(inp, optval);
+ break;
+
+ case SO_TRAFFIC_MGT_BACKGROUND:
+ if ((error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval))) != 0) {
+ break;
+ }
+
+ if (optval) {
+ socket_set_traffic_mgt_flags_locked(so,
+ TRAFFIC_MGT_SO_BACKGROUND);
+ } else {
+ socket_clear_traffic_mgt_flags_locked(so,
+ TRAFFIC_MGT_SO_BACKGROUND);
+ }
+ break;
+ case TCP_RXT_MINIMUM_TIMEOUT:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error) {
+ break;
+ }
+ if (optval < 0) {
+ error = EINVAL;
+ break;
+ }
+ if (optval == 0) {
+ tp->t_rxt_minimum_timeout = 0;
+ } else {
+ tp->t_rxt_minimum_timeout = min(optval,
+ TCP_RXT_MINIMUM_TIMEOUT_LIMIT);
+ /* convert to milliseconds */
+ tp->t_rxt_minimum_timeout *= TCP_RETRANSHZ;
+ }
+ break;