+ // Mark that the port registration is held by NECP
+ inp->inp_flags2 |= INP2_EXTERNAL_PORT;
+
+ break;
+ }
+#endif /* NECP */
+
+ case SO_EXTENDED_BK_IDLE:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error == 0) {
+ error = so_set_extended_bk_idle(so, optval);
+ }
+ break;
+
+ case SO_MARK_CELLFALLBACK:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error != 0) {
+ goto out;
+ }
+ if (optval < 0) {
+ error = EINVAL;
+ goto out;
+ }
+ if (optval == 0) {
+ so->so_flags1 &= ~SOF1_CELLFALLBACK;
+ } else {
+ so->so_flags1 |= SOF1_CELLFALLBACK;
+ }
+ break;
+
+ case SO_STATISTICS_EVENT:
+ error = sooptcopyin(sopt, &long_optval,
+ sizeof(long_optval), sizeof(long_optval));
+ if (error != 0) {
+ goto out;
+ }
+ u_int64_t nstat_event = 0;
+ error = so_statistics_event_to_nstat_event(
+ &long_optval, &nstat_event);
+ if (error != 0) {
+ goto out;
+ }
+ nstat_pcb_event(sotoinpcb(so), nstat_event);
+ break;
+
+ case SO_NET_SERVICE_TYPE: {
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error != 0) {
+ goto out;
+ }
+ error = so_set_net_service_type(so, optval);
+ break;
+ }
+
+ case SO_QOSMARKING_POLICY_OVERRIDE:
+ error = priv_check_cred(kauth_cred_get(),
+ PRIV_NET_QOSMARKING_POLICY_OVERRIDE, 0);
+ if (error != 0) {
+ goto out;
+ }
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error != 0) {
+ goto out;
+ }
+ if (optval == 0) {
+ so->so_flags1 &= ~SOF1_QOSMARKING_POLICY_OVERRIDE;
+ } else {
+ so->so_flags1 |= SOF1_QOSMARKING_POLICY_OVERRIDE;
+ }
+ break;
+
+ case SO_MPKL_SEND_INFO: {
+ struct so_mpkl_send_info so_mpkl_send_info;
+
+ error = sooptcopyin(sopt, &so_mpkl_send_info,
+ sizeof(struct so_mpkl_send_info), sizeof(struct so_mpkl_send_info));
+ if (error != 0) {
+ goto out;
+ }
+ uuid_copy(so->so_mpkl_send_uuid, so_mpkl_send_info.mpkl_uuid);
+ so->so_mpkl_send_proto = so_mpkl_send_info.mpkl_proto;
+
+ if (uuid_is_null(so->so_mpkl_send_uuid) && so->so_mpkl_send_proto == 0) {
+ so->so_flags1 &= ~SOF1_MPKL_SEND_INFO;
+ } else {
+ so->so_flags1 |= SOF1_MPKL_SEND_INFO;
+ }
+ break;
+ }
+ case SO_WANT_KEV_SOCKET_CLOSED: {
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error != 0) {
+ goto out;
+ }
+ if (optval == 0) {
+ so->so_flags1 &= ~SOF1_WANT_KEV_SOCK_CLOSED;
+ } else {
+ so->so_flags1 |= SOF1_WANT_KEV_SOCK_CLOSED;
+ }
+ break;
+ }
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ if (error == 0 && so->so_proto != NULL &&
+ so->so_proto->pr_ctloutput != NULL) {
+ (void) so->so_proto->pr_ctloutput(so, sopt);
+ }
+ }
+out:
+ if (dolock) {
+ socket_unlock(so, 1);
+ }
+ return error;
+}
+
+/* Helper routines for getsockopt */
+int
+sooptcopyout(struct sockopt *sopt, void *buf, size_t len)
+{
+ int error;
+ size_t valsize;
+
+ error = 0;
+
+ /*
+ * Documented get behavior is that we always return a value,
+ * possibly truncated to fit in the user's buffer.
+ * Traditional behavior is that we always tell the user
+ * precisely how much we copied, rather than something useful
+ * like the total amount we had available for her.
+ * Note that this interface is not idempotent; the entire answer must
+ * generated ahead of time.
+ */
+ valsize = min(len, sopt->sopt_valsize);
+ sopt->sopt_valsize = valsize;
+ if (sopt->sopt_val != USER_ADDR_NULL) {
+ if (sopt->sopt_p != kernproc) {
+ error = copyout(buf, sopt->sopt_val, valsize);
+ } else {
+ bcopy(buf, CAST_DOWN(caddr_t, sopt->sopt_val), valsize);
+ }
+ }
+ return error;
+}
+
+static int
+sooptcopyout_timeval(struct sockopt *sopt, const struct timeval *tv_p)
+{
+ int error;
+ size_t len;
+ struct user64_timeval tv64 = {};
+ struct user32_timeval tv32 = {};
+ const void * val;
+ size_t valsize;
+
+ error = 0;