+/*
+ * Export TCP internal state information via a struct tcp_info
+ */
+__private_extern__ 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;
+
+ if (tp->t_state > TCPS_LISTEN) {
+ if ((tp->t_flags & TF_REQ_TSTMP) && (tp->t_flags & TF_RCVD_TSTMP))
+ ti->tcpi_options |= TCPI_OPT_TIMESTAMPS;
+ if (tp->t_flags & TF_SACK_PERMIT)
+ ti->tcpi_options |= TCPI_OPT_SACK;
+ if ((tp->t_flags & TF_REQ_SCALE) && (tp->t_flags & TF_RCVD_SCALE)) {
+ ti->tcpi_options |= TCPI_OPT_WSCALE;
+ ti->tcpi_snd_wscale = tp->snd_scale;
+ ti->tcpi_rcv_wscale = tp->rcv_scale;
+ }
+
+ /* Are we in retranmission episode */
+ if (tp->snd_max != tp->snd_nxt)
+ ti->tcpi_flags |= TCPI_FLAG_LOSSRECOVERY;
+ else
+ ti->tcpi_flags &= ~TCPI_FLAG_LOSSRECOVERY;
+
+ 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 = tp->t_inpcb->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_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;
+ }
+}
+
+__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;
+}
+
+
+__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;
+ 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),
+ sizeof(command_name)) == 0) &&
+ (escape_str(parent_name, strlen(parent_name),
+ 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);
+
+ 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;
+
+ if (inp->inp_vflag & INP_IPV6) {
+ struct in6_addr laddr6 = inp->in6p_laddr;
+ struct in6_addr faddr6 = inp->in6p_faddr;
+ socket_unlock(so, 0);
+ finp = in6_pcblookup_hash(&tcbinfo, &laddr6, lport, &faddr6, fport, 0, NULL);
+ socket_lock(so, 0);
+ } else if (inp->inp_vflag & INP_IPV4) {
+ struct in_addr laddr4 = inp->inp_laddr;
+ struct in_addr faddr4 = inp->inp_faddr;
+ socket_unlock(so, 0);
+ finp = in_pcblookup_hash(&tcbinfo, laddr4, lport, faddr4, fport, 0, NULL);
+ socket_lock(so, 0);
+ }
+
+ if (finp) {
+ *out_pid = finp->inp_socket->last_pid;
+ error = 0;
+ in_pcb_checkstate(finp, WNT_RELEASE, 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);
+}
+