+/*
+ * Export TCP internal state information via a struct tcp_info
+ */
+__private_extern__ void
+tcp_fill_info(struct tcpcb *tp, struct tcp_info *ti)
+{
+ 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;
+ }
+
+ ti->tcpi_snd_mss = tp->t_maxseg;
+ ti->tcpi_rcv_mss = tp->t_maxseg;
+
+ ti->tcpi_snd_ssthresh = tp->snd_ssthresh;
+ ti->tcpi_snd_cwnd = tp->snd_cwnd;
+
+ ti->tcpi_rcv_space = tp->rcv_wnd;
+
+ ti->tcpi_snd_wnd = tp->snd_wnd;
+ ti->tcpi_snd_bwnd = tp->snd_bwnd;
+ ti->tcpi_snd_nxt = tp->snd_nxt;
+ ti->tcpi_rcv_nxt = tp->rcv_nxt;
+
+ ti->tcpi_last_outif = tp->t_inpcb->inp_last_outif;
+ }
+}
+
+__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;
+
+ 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;
+}
+