+ * Export TCP internal state information via a struct tcp_info
+ */
+void
+tcp_fill_info(struct tcpcb *tp, struct tcp_info *ti)
+{
+ struct inpcb *inp = tp->t_inpcb;
+
+ bzero(ti, sizeof(*ti));
+
+ ti->tcpi_state = tp->t_state;
+ ti->tcpi_flowhash = inp->inp_flowhash;
+
+ if (tp->t_state > TCPS_LISTEN) {
+ if (TSTMP_SUPPORTED(tp))
+ ti->tcpi_options |= TCPI_OPT_TIMESTAMPS;
+ if (SACK_ENABLED(tp))
+ ti->tcpi_options |= TCPI_OPT_SACK;
+ if (TCP_WINDOW_SCALE_ENABLED(tp)) {
+ ti->tcpi_options |= TCPI_OPT_WSCALE;
+ ti->tcpi_snd_wscale = tp->snd_scale;
+ ti->tcpi_rcv_wscale = tp->rcv_scale;
+ }
+ if (TCP_ECN_ENABLED(tp))
+ ti->tcpi_options |= TCPI_OPT_ECN;
+
+ /* Are we in retranmission episode */
+ if (IN_FASTRECOVERY(tp) || tp->t_rxtshift > 0)
+ ti->tcpi_flags |= TCPI_FLAG_LOSSRECOVERY;
+
+ if (tp->t_flags & TF_STREAMING_ON)
+ ti->tcpi_flags |= TCPI_FLAG_STREAMING_ON;
+
+ ti->tcpi_rto = tp->t_timer[TCPT_REXMT] ? tp->t_rxtcur : 0;
+ ti->tcpi_snd_mss = tp->t_maxseg;
+ ti->tcpi_rcv_mss = tp->t_maxseg;
+
+ ti->tcpi_rttcur = tp->t_rttcur;
+ ti->tcpi_srtt = tp->t_srtt >> TCP_RTT_SHIFT;
+ ti->tcpi_rttvar = tp->t_rttvar >> TCP_RTTVAR_SHIFT;
+ ti->tcpi_rttbest = tp->t_rttbest >> TCP_RTT_SHIFT;
+
+ ti->tcpi_snd_ssthresh = tp->snd_ssthresh;
+ ti->tcpi_snd_cwnd = tp->snd_cwnd;
+ ti->tcpi_snd_sbbytes = inp->inp_socket->so_snd.sb_cc;
+
+ ti->tcpi_rcv_space = tp->rcv_wnd;
+
+ ti->tcpi_snd_wnd = tp->snd_wnd;
+ ti->tcpi_snd_nxt = tp->snd_nxt;
+ ti->tcpi_rcv_nxt = tp->rcv_nxt;
+
+ /* convert bytes/msec to bits/sec */
+ if ((tp->t_flagsext & TF_MEASURESNDBW) != 0 &&
+ tp->t_bwmeas != NULL) {
+ ti->tcpi_snd_bw = (tp->t_bwmeas->bw_sndbw * 8000);
+ }
+
+ ti->tcpi_last_outif = (tp->t_inpcb->inp_last_outifp == NULL) ? 0 :
+ tp->t_inpcb->inp_last_outifp->if_index;
+
+ //atomic_get_64(ti->tcpi_txbytes, &inp->inp_stat->txbytes);
+ ti->tcpi_txpackets = inp->inp_stat->txpackets;
+ ti->tcpi_txbytes = inp->inp_stat->txbytes;
+ ti->tcpi_txretransmitbytes = tp->t_stat.txretransmitbytes;
+ ti->tcpi_txretransmitpackets = tp->t_stat.rxmitpkts;
+ ti->tcpi_txunacked = tp->snd_max - tp->snd_una;
+
+ //atomic_get_64(ti->tcpi_rxbytes, &inp->inp_stat->rxbytes);
+ ti->tcpi_rxpackets = inp->inp_stat->rxpackets;
+ ti->tcpi_rxbytes = inp->inp_stat->rxbytes;
+ ti->tcpi_rxduplicatebytes = tp->t_stat.rxduplicatebytes;
+ ti->tcpi_rxoutoforderbytes = tp->t_stat.rxoutoforderbytes;
+
+ if (tp->t_state > TCPS_LISTEN) {
+ ti->tcpi_synrexmits = tp->t_stat.synrxtshift;
+ }
+ ti->tcpi_cell_rxpackets = inp->inp_cstat->rxpackets;
+ ti->tcpi_cell_rxbytes = inp->inp_cstat->rxbytes;
+ ti->tcpi_cell_txpackets = inp->inp_cstat->txpackets;
+ ti->tcpi_cell_txbytes = inp->inp_cstat->txbytes;
+
+ ti->tcpi_wifi_rxpackets = inp->inp_wstat->rxpackets;
+ ti->tcpi_wifi_rxbytes = inp->inp_wstat->rxbytes;
+ ti->tcpi_wifi_txpackets = inp->inp_wstat->txpackets;
+ ti->tcpi_wifi_txbytes = inp->inp_wstat->txbytes;
+
+ ti->tcpi_wired_rxpackets = inp->inp_Wstat->rxpackets;
+ ti->tcpi_wired_rxbytes = inp->inp_Wstat->rxbytes;
+ ti->tcpi_wired_txpackets = inp->inp_Wstat->txpackets;
+ ti->tcpi_wired_txbytes = inp->inp_Wstat->txbytes;
+ tcp_get_connectivity_status(tp, &ti->tcpi_connstatus);
+
+ ti->tcpi_tfo_syn_data_rcv = !!(tp->t_tfo_stats & TFO_S_SYNDATA_RCV);
+ ti->tcpi_tfo_cookie_req_rcv = !!(tp->t_tfo_stats & TFO_S_COOKIEREQ_RECV);
+ ti->tcpi_tfo_cookie_sent = !!(tp->t_tfo_stats & TFO_S_COOKIE_SENT);
+ ti->tcpi_tfo_cookie_invalid = !!(tp->t_tfo_stats & TFO_S_COOKIE_INVALID);
+
+ ti->tcpi_tfo_cookie_req = !!(tp->t_tfo_stats & TFO_S_COOKIE_REQ);
+ ti->tcpi_tfo_cookie_rcv = !!(tp->t_tfo_stats & TFO_S_COOKIE_RCV);
+ ti->tcpi_tfo_syn_data_sent = !!(tp->t_tfo_stats & TFO_S_SYN_DATA_SENT);
+ ti->tcpi_tfo_syn_data_acked = !!(tp->t_tfo_stats & TFO_S_SYN_DATA_ACKED);
+ ti->tcpi_tfo_syn_loss = !!(tp->t_tfo_stats & TFO_S_SYN_LOSS);
+ ti->tcpi_tfo_cookie_wrong = !!(tp->t_tfo_stats & TFO_S_COOKIE_WRONG);
+ ti->tcpi_tfo_no_cookie_rcv = !!(tp->t_tfo_stats & TFO_S_NO_COOKIE_RCV);
+ ti->tcpi_tfo_heuristics_disable = !!(tp->t_tfo_stats & TFO_S_HEURISTICS_DISABLE);
+ ti->tcpi_tfo_send_blackhole = !!(tp->t_tfo_stats & TFO_S_SEND_BLACKHOLE);
+ ti->tcpi_tfo_recv_blackhole = !!(tp->t_tfo_stats & TFO_S_RECV_BLACKHOLE);
+ ti->tcpi_tfo_onebyte_proxy = !!(tp->t_tfo_stats & TFO_S_ONE_BYTE_PROXY);
+
+ ti->tcpi_ecn_client_setup = !!(tp->ecn_flags & TE_SETUPSENT);
+ ti->tcpi_ecn_server_setup = !!(tp->ecn_flags & TE_SETUPRECEIVED);
+ ti->tcpi_ecn_success = (tp->ecn_flags & TE_ECN_ON) == TE_ECN_ON ? 1 : 0;
+ ti->tcpi_ecn_lost_syn = !!(tp->ecn_flags & TE_LOST_SYN);
+ ti->tcpi_ecn_lost_synack = !!(tp->ecn_flags & TE_LOST_SYNACK);
+
+ ti->tcpi_local_peer = !!(tp->t_flags & TF_LOCAL);
+
+ if (tp->t_inpcb->inp_last_outifp != NULL) {
+ if (IFNET_IS_CELLULAR(tp->t_inpcb->inp_last_outifp))
+ ti->tcpi_if_cell = 1;
+ if (IFNET_IS_WIFI(tp->t_inpcb->inp_last_outifp))
+ ti->tcpi_if_wifi = 1;
+ if (IFNET_IS_WIRED(tp->t_inpcb->inp_last_outifp))
+ ti->tcpi_if_wired = 1;
+ if (IFNET_IS_WIFI_INFRA(tp->t_inpcb->inp_last_outifp))
+ ti->tcpi_if_wifi_infra = 1;
+ if (tp->t_inpcb->inp_last_outifp->if_eflags & IFEF_AWDL)
+ ti->tcpi_if_wifi_awdl = 1;
+ }
+ if (tp->tcp_cc_index == TCP_CC_ALGO_BACKGROUND_INDEX)
+ ti->tcpi_snd_background = 1;
+ if (tcp_recv_bg == 1 ||
+ IS_TCP_RECV_BG(tp->t_inpcb->inp_socket))
+ ti->tcpi_rcv_background = 1;
+
+ ti->tcpi_ecn_recv_ce = tp->t_ecn_recv_ce;
+ ti->tcpi_ecn_recv_cwr = tp->t_ecn_recv_cwr;
+
+ ti->tcpi_rcvoopack = tp->t_rcvoopack;
+ ti->tcpi_pawsdrop = tp->t_pawsdrop;
+ ti->tcpi_sack_recovery_episode = tp->t_sack_recovery_episode;
+ ti->tcpi_reordered_pkts = tp->t_reordered_pkts;
+ ti->tcpi_dsack_sent = tp->t_dsack_sent;
+ ti->tcpi_dsack_recvd = tp->t_dsack_recvd;
+ }
+}
+
+__private_extern__ errno_t
+tcp_fill_info_for_info_tuple(struct info_tuple *itpl, struct tcp_info *ti)
+{
+ struct inpcbinfo *pcbinfo = NULL;
+ struct inpcb *inp = NULL;
+ struct socket *so;
+ struct tcpcb *tp;
+
+ if (itpl->itpl_proto == IPPROTO_TCP)
+ pcbinfo = &tcbinfo;
+ else
+ return EINVAL;
+
+ if (itpl->itpl_local_sa.sa_family == AF_INET &&
+ itpl->itpl_remote_sa.sa_family == AF_INET) {
+ inp = in_pcblookup_hash(pcbinfo,
+ itpl->itpl_remote_sin.sin_addr,
+ itpl->itpl_remote_sin.sin_port,
+ itpl->itpl_local_sin.sin_addr,
+ itpl->itpl_local_sin.sin_port,
+ 0, NULL);
+ } else if (itpl->itpl_local_sa.sa_family == AF_INET6 &&
+ itpl->itpl_remote_sa.sa_family == AF_INET6) {
+ struct in6_addr ina6_local;
+ struct in6_addr ina6_remote;
+
+ ina6_local = itpl->itpl_local_sin6.sin6_addr;
+ if (IN6_IS_SCOPE_LINKLOCAL(&ina6_local) &&
+ itpl->itpl_local_sin6.sin6_scope_id)
+ ina6_local.s6_addr16[1] = htons(itpl->itpl_local_sin6.sin6_scope_id);
+
+ ina6_remote = itpl->itpl_remote_sin6.sin6_addr;
+ if (IN6_IS_SCOPE_LINKLOCAL(&ina6_remote) &&
+ itpl->itpl_remote_sin6.sin6_scope_id)
+ ina6_remote.s6_addr16[1] = htons(itpl->itpl_remote_sin6.sin6_scope_id);
+
+ inp = in6_pcblookup_hash(pcbinfo,
+ &ina6_remote,
+ itpl->itpl_remote_sin6.sin6_port,
+ &ina6_local,
+ itpl->itpl_local_sin6.sin6_port,
+ 0, NULL);
+ } else {
+ return EINVAL;
+ }
+ if (inp == NULL || (so = inp->inp_socket) == NULL)
+ return ENOENT;
+
+ socket_lock(so, 0);
+ if (in_pcb_checkstate(inp, WNT_RELEASE, 1) == WNT_STOPUSING) {
+ socket_unlock(so, 0);
+ return ENOENT;
+ }
+ tp = intotcpcb(inp);
+
+ tcp_fill_info(tp, ti);
+ socket_unlock(so, 0);
+
+ return 0;
+}
+
+static void
+tcp_connection_fill_info(struct tcpcb *tp, struct tcp_connection_info *tci)
+{
+ struct inpcb *inp = tp->t_inpcb;
+
+ bzero(tci, sizeof(*tci));
+ tci->tcpi_state = tp->t_state;
+ if (tp->t_state > TCPS_LISTEN) {
+ if (TSTMP_SUPPORTED(tp))
+ tci->tcpi_options |= TCPCI_OPT_TIMESTAMPS;
+ if (SACK_ENABLED(tp))
+ tci->tcpi_options |= TCPCI_OPT_SACK;
+ if (TCP_WINDOW_SCALE_ENABLED(tp)) {
+ tci->tcpi_options |= TCPCI_OPT_WSCALE;
+ tci->tcpi_snd_wscale = tp->snd_scale;
+ tci->tcpi_rcv_wscale = tp->rcv_scale;
+ }
+ if (TCP_ECN_ENABLED(tp))
+ tci->tcpi_options |= TCPCI_OPT_ECN;
+ if (IN_FASTRECOVERY(tp) || tp->t_rxtshift > 0)
+ tci->tcpi_flags |= TCPCI_FLAG_LOSSRECOVERY;
+ if (tp->t_flagsext & TF_PKTS_REORDERED)
+ tci->tcpi_flags |= TCPCI_FLAG_REORDERING_DETECTED;
+ tci->tcpi_rto = (tp->t_timer[TCPT_REXMT] > 0) ?
+ tp->t_rxtcur : 0;
+ tci->tcpi_maxseg = tp->t_maxseg;
+ tci->tcpi_snd_ssthresh = tp->snd_ssthresh;
+ tci->tcpi_snd_cwnd = tp->snd_cwnd;
+ tci->tcpi_snd_wnd = tp->snd_wnd;
+ tci->tcpi_snd_sbbytes = inp->inp_socket->so_snd.sb_cc;
+ tci->tcpi_rcv_wnd = tp->rcv_wnd;
+ tci->tcpi_rttcur = tp->t_rttcur;
+ tci->tcpi_srtt = (tp->t_srtt >> TCP_RTT_SHIFT);
+ tci->tcpi_rttvar = (tp->t_rttvar >> TCP_RTTVAR_SHIFT);
+ tci->tcpi_txpackets = inp->inp_stat->txpackets;
+ tci->tcpi_txbytes = inp->inp_stat->txbytes;
+ tci->tcpi_txretransmitbytes = tp->t_stat.txretransmitbytes;
+ tci->tcpi_txretransmitpackets = tp->t_stat.rxmitpkts;
+ tci->tcpi_rxpackets = inp->inp_stat->rxpackets;
+ tci->tcpi_rxbytes = inp->inp_stat->rxbytes;
+ tci->tcpi_rxoutoforderbytes = tp->t_stat.rxoutoforderbytes;
+
+ tci->tcpi_tfo_syn_data_rcv = !!(tp->t_tfo_stats & TFO_S_SYNDATA_RCV);
+ tci->tcpi_tfo_cookie_req_rcv = !!(tp->t_tfo_stats & TFO_S_COOKIEREQ_RECV);
+ tci->tcpi_tfo_cookie_sent = !!(tp->t_tfo_stats & TFO_S_COOKIE_SENT);
+ tci->tcpi_tfo_cookie_invalid = !!(tp->t_tfo_stats & TFO_S_COOKIE_INVALID);
+ tci->tcpi_tfo_cookie_req = !!(tp->t_tfo_stats & TFO_S_COOKIE_REQ);
+ tci->tcpi_tfo_cookie_rcv = !!(tp->t_tfo_stats & TFO_S_COOKIE_RCV);
+ tci->tcpi_tfo_syn_data_sent = !!(tp->t_tfo_stats & TFO_S_SYN_DATA_SENT);
+ tci->tcpi_tfo_syn_data_acked = !!(tp->t_tfo_stats & TFO_S_SYN_DATA_ACKED);
+ tci->tcpi_tfo_syn_loss = !!(tp->t_tfo_stats & TFO_S_SYN_LOSS);
+ tci->tcpi_tfo_cookie_wrong = !!(tp->t_tfo_stats & TFO_S_COOKIE_WRONG);
+ tci->tcpi_tfo_no_cookie_rcv = !!(tp->t_tfo_stats & TFO_S_NO_COOKIE_RCV);
+ tci->tcpi_tfo_heuristics_disable = !!(tp->t_tfo_stats & TFO_S_HEURISTICS_DISABLE);
+ tci->tcpi_tfo_send_blackhole = !!(tp->t_tfo_stats & TFO_S_SEND_BLACKHOLE);
+ tci->tcpi_tfo_recv_blackhole = !!(tp->t_tfo_stats & TFO_S_RECV_BLACKHOLE);
+ tci->tcpi_tfo_onebyte_proxy = !!(tp->t_tfo_stats & TFO_S_ONE_BYTE_PROXY);
+ }
+}
+
+
+__private_extern__ int
+tcp_sysctl_info(__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req)
+{
+ int error;
+ struct tcp_info ti = {};
+ struct info_tuple itpl;
+#if !CONFIG_EMBEDDED
+ proc_t caller = PROC_NULL;
+ proc_t caller_parent = PROC_NULL;
+ char command_name[MAXCOMLEN + 1] = "";
+ char parent_name[MAXCOMLEN + 1] = "";
+
+ if ((caller = proc_self()) != PROC_NULL) {
+ /* get process name */
+ strlcpy(command_name, caller->p_comm, sizeof(command_name));
+
+ /* get parent process name if possible */
+ if ((caller_parent = proc_find(caller->p_ppid)) != PROC_NULL) {
+ strlcpy(parent_name, caller_parent->p_comm,
+ sizeof(parent_name));
+ proc_rele(caller_parent);
+ }
+
+ if ((escape_str(command_name, strlen(command_name) + 1,
+ sizeof(command_name)) == 0) &&
+ (escape_str(parent_name, strlen(parent_name) + 1,
+ sizeof(parent_name)) == 0)) {
+ kern_asl_msg(LOG_DEBUG, "messagetracer",
+ 5,
+ "com.apple.message.domain",
+ "com.apple.kernel.tcpstat", /* 1 */
+ "com.apple.message.signature",
+ "tcpinfo", /* 2 */
+ "com.apple.message.signature2", command_name, /* 3 */
+ "com.apple.message.signature3", parent_name, /* 4 */
+ "com.apple.message.summarize", "YES", /* 5 */
+ NULL);
+ }
+ }
+
+ if (caller != PROC_NULL)
+ proc_rele(caller);
+#endif /* !CONFIG_EMBEDDED */
+
+ if (req->newptr == USER_ADDR_NULL) {
+ return EINVAL;
+ }
+ if (req->newlen < sizeof(struct info_tuple)) {
+ return EINVAL;
+ }
+ error = SYSCTL_IN(req, &itpl, sizeof(struct info_tuple));
+ if (error != 0) {
+ return error;
+ }
+ error = tcp_fill_info_for_info_tuple(&itpl, &ti);
+ if (error != 0) {
+ return error;
+ }
+ error = SYSCTL_OUT(req, &ti, sizeof(struct tcp_info));
+ if (error != 0) {
+ return error;
+ }
+
+ return 0;
+}
+
+static int
+tcp_lookup_peer_pid_locked(struct socket *so, pid_t *out_pid)
+{
+ int error = EHOSTUNREACH;
+ *out_pid = -1;
+ if ((so->so_state & SS_ISCONNECTED) == 0) return ENOTCONN;
+
+ struct inpcb *inp = (struct inpcb*)so->so_pcb;
+ uint16_t lport = inp->inp_lport;
+ uint16_t fport = inp->inp_fport;
+ struct inpcb *finp = NULL;
+ struct in6_addr laddr6, faddr6;
+ struct in_addr laddr4, faddr4;
+
+ if (inp->inp_vflag & INP_IPV6) {
+ laddr6 = inp->in6p_laddr;
+ faddr6 = inp->in6p_faddr;
+ } else if (inp->inp_vflag & INP_IPV4) {
+ laddr4 = inp->inp_laddr;
+ faddr4 = inp->inp_faddr;
+ }
+
+ socket_unlock(so, 0);
+ if (inp->inp_vflag & INP_IPV6) {
+ finp = in6_pcblookup_hash(&tcbinfo, &laddr6, lport, &faddr6, fport, 0, NULL);
+ } else if (inp->inp_vflag & INP_IPV4) {
+ finp = in_pcblookup_hash(&tcbinfo, laddr4, lport, faddr4, fport, 0, NULL);
+ }
+
+ if (finp) {
+ *out_pid = finp->inp_socket->last_pid;
+ error = 0;
+ in_pcb_checkstate(finp, WNT_RELEASE, 0);
+ }
+ socket_lock(so, 0);
+
+ return error;
+}
+
+void
+tcp_getconninfo(struct socket *so, struct conninfo_tcp *tcp_ci)
+{
+ (void) tcp_lookup_peer_pid_locked(so, &tcp_ci->tcpci_peer_pid);
+ tcp_fill_info(sototcpcb(so), &tcp_ci->tcpci_tcp_info);
+}
+
+/*
+ * The new sockopt interface makes it possible for us to block in the
+ * copyin/out step (if we take a page fault). Taking a page fault at
+ * splnet() is probably a Bad Thing. (Since sockets and pcbs both now
+ * use TSM, there probably isn't any need for this function to run at