diff --git a/share/man/man4/ddb.4 b/share/man/man4/ddb.4 --- a/share/man/man4/ddb.4 +++ b/share/man/man4/ddb.4 @@ -24,7 +24,7 @@ .\" any improvements or extensions that they make and grant Carnegie Mellon .\" the rights to redistribute these changes. .\" -.Dd May 28, 2025 +.Dd June 10, 2025 .Dt DDB 4 .Os .Sh NAME @@ -604,9 +604,12 @@ modifier will print command line arguments for each process. .\" .Pp -.It Ic show Cm all tcpcbs Ns Op Li / Ns Cm l +.It Ic show Cm all tcpcbs Ns Op Li / Ns Cm b Ns Cm l Show the same output as "show tcpcb" does, but for all TCP control blocks within the system. +The +.Cm b +modifier will request BBLog entries to be printed. Using the .Cm l modifier will limit the output to TCP control blocks, which are locked. @@ -1103,7 +1106,7 @@ Not present on some platforms. .\" .Pp -.It Ic show Cm tcpcb Ar addr +.It Ic show Cm tcpcb Ns Oo Li / Ns Cm b Oc Ar addr Print TCP control block .Vt struct tcpcb lying at address @@ -1111,6 +1114,9 @@ For exact interpretation of output, visit .Pa netinet/tcp.h header file. +The +.Cm b +modifier will request BBLog entries to be printed. .\" .Pp .It Ic show Cm thread Op Ar addr | tid diff --git a/sys/netinet/tcp_log_buf.h b/sys/netinet/tcp_log_buf.h --- a/sys/netinet/tcp_log_buf.h +++ b/sys/netinet/tcp_log_buf.h @@ -585,5 +585,9 @@ } #endif /* TCP_BLACKBOX */ +#ifdef DDB +void db_print_bblog_entries(struct tcp_log_stailq *log_entries, int indent); +#endif + #endif /* _KERNEL */ #endif /* __tcp_log_buf_h__ */ diff --git a/sys/netinet/tcp_log_buf.c b/sys/netinet/tcp_log_buf.c --- a/sys/netinet/tcp_log_buf.c +++ b/sys/netinet/tcp_log_buf.c @@ -29,6 +29,7 @@ #include #include "opt_inet.h" +#include "opt_ddb.h" #include #include #include @@ -43,11 +44,18 @@ #include #include #include +#ifdef DDB +#include +#endif #include #include /* Must come after qmath.h and tree.h */ #include #include +#ifdef DDB +#include +#endif + #include #include #include @@ -2972,3 +2980,376 @@ done: INP_WUNLOCK(inp); } + +#ifdef DDB +static void +db_print_indent(int indent) +{ + int i; + + for (i = 0; i < indent; i++) + db_printf(" "); +} + +static void +db_print_tcphdr(struct tcp_log_buffer *tlm_buf) +{ + struct sackblk sack; + struct tcphdr *th; + int cnt, i, j, opt, optlen, num_sacks; + uint32_t val, ecr; + uint16_t mss; + uint16_t flags; + + if ((tlm_buf->tlb_eventflags & TLB_FLAG_HDR) == 0) { + return; + } + th = &tlm_buf->tlb_th; + flags = tcp_get_flags(th); + if (flags & TH_FIN) { + db_printf("F"); + } + if (flags & TH_SYN) { + db_printf("S"); + } + if (flags & TH_RST) { + db_printf("R"); + } + if (flags & TH_PUSH) { + db_printf("P"); + } + if (flags & TH_ACK) { + db_printf("."); + } + if (flags & TH_URG) { + db_printf("U"); + } + if (flags & TH_ECE) { + db_printf("E"); + } + if (flags & TH_FIN) { + db_printf("F"); + } + if (flags & TH_CWR) { + db_printf("W"); + } + if (flags & TH_FIN) { + db_printf("F"); + } + if (flags & TH_AE) { + db_printf("A"); + } + db_printf(" %u:%u(%u)", ntohl(th->th_seq), + ntohl(th->th_seq) + tlm_buf->tlb_len, tlm_buf->tlb_len); + if (flags & TH_ACK) { + db_printf(" ack %u", ntohl(th->th_ack)); + } + db_printf(" win %u", ntohs(th->th_win)); + if (flags & TH_URG) { + db_printf(" urg %u", ntohs(th->th_urp)); + } + cnt = (th->th_off << 2) - sizeof(struct tcphdr); + if (cnt > 0) { + db_printf(" <"); + for (i = 0; i < cnt; i += optlen) { + opt = tlm_buf->tlb_opts[i]; + if (opt == TCPOPT_EOL || opt == TCPOPT_NOP) { + optlen = 1; + } else { + if (cnt - i < 2) { + break; + } + optlen = tlm_buf->tlb_opts[i + 1]; + if (optlen < 2 || optlen > cnt - i) { + break; + } + } + if (i > 0) { + db_printf(","); + } + switch (opt) { + case TCPOPT_EOL: + db_printf("eol"); + break; + case TCPOPT_NOP: + db_printf("nop"); + break; + case TCPOPT_MAXSEG: + if (optlen != TCPOLEN_MAXSEG) { + break; + } + bcopy(tlm_buf->tlb_opts + i + 2, &mss, + sizeof(uint16_t)); + db_printf("mss %u", ntohs(mss)); + break; + case TCPOPT_WINDOW: + if (optlen != TCPOLEN_WINDOW) { + break; + } + db_printf("wscale %u", + tlm_buf->tlb_opts[i + 2]); + break; + case TCPOPT_SACK_PERMITTED: + if (optlen != TCPOLEN_SACK_PERMITTED) { + break; + } + db_printf("sackOK"); + break; + case TCPOPT_SACK: + if (optlen == TCPOLEN_SACKHDR || + (optlen - 2) % TCPOLEN_SACK != 0) { + break; + } + num_sacks = (optlen - 2) / TCPOLEN_SACK; + db_printf("sack"); + for (j = 0; j < num_sacks; j++) { + bcopy(tlm_buf->tlb_opts + i + 2 + + j * TCPOLEN_SACK, &sack, + TCPOLEN_SACK); + db_printf(" %u:%u", ntohl(sack.start), + ntohl(sack.end)); + } + break; + case TCPOPT_TIMESTAMP: + if (optlen != TCPOLEN_TIMESTAMP) { + break; + } + bcopy(tlm_buf->tlb_opts + i + 2, &val, + sizeof(uint32_t)); + bcopy(tlm_buf->tlb_opts + i + 6, &ecr, + sizeof(uint32_t)); + db_printf("TS val %u ecr %u", ntohl(val), + ntohl(ecr)); + break; + case TCPOPT_SIGNATURE: + db_printf("md5"); + if (optlen > 2) { + db_printf(" "); + } + for (j = 0; j < optlen - 2; j++) { + db_printf("%02x", + tlm_buf->tlb_opts[i + 2 + j]); + } + break; + case TCPOPT_FAST_OPEN: + db_printf("FO"); + if (optlen > 2) { + db_printf(" "); + } + for (j = 0; j < optlen - 2; j++) { + db_printf("%02x", + tlm_buf->tlb_opts[i + 2 + j]); + } + break; + default: + db_printf("opt=%u len=%u", opt, optlen); + break; + } + } + db_printf(">"); + } +} +static void +db_print_pru(struct tcp_log_buffer *tlm_buf) +{ + switch (tlm_buf->tlb_flex1) { + case PRU_ATTACH: + db_printf("ATTACH"); + break; + case PRU_DETACH: + db_printf("DETACH"); + break; + case PRU_BIND: + db_printf("BIND"); + break; + case PRU_LISTEN: + db_printf("LISTEN"); + break; + case PRU_CONNECT: + db_printf("CONNECT"); + break; + case PRU_ACCEPT: + db_printf("ACCEPT"); + break; + case PRU_DISCONNECT: + db_printf("DISCONNECT"); + break; + case PRU_SHUTDOWN: + db_printf("SHUTDOWN"); + break; + case PRU_RCVD: + db_printf("RCVD"); + break; + case PRU_SEND: + db_printf("SEND"); + break; + case PRU_ABORT: + db_printf("ABORT"); + break; + case PRU_CONTROL: + db_printf("CONTROL"); + break; + case PRU_SENSE: + db_printf("SENSE"); + break; + case PRU_RCVOOB: + db_printf("RCVOOB"); + break; + case PRU_SENDOOB: + db_printf("SENDOOB"); + break; + case PRU_SOCKADDR: + db_printf("SOCKADDR"); + break; + case PRU_PEERADDR: + db_printf("PEERADDR"); + break; + case PRU_CONNECT2: + db_printf("CONNECT2"); + break; + case PRU_FASTTIMO: + db_printf("FASTTIMO"); + break; + case PRU_SLOWTIMO: + db_printf("SLOWTIMO"); + break; + case PRU_PROTORCV: + db_printf("PROTORCV"); + break; + case PRU_PROTOSEND: + db_printf("PROTOSEND"); + break; + case PRU_SEND_EOF: + db_printf("SEND_EOF"); + break; + case PRU_SOSETLABEL: + db_printf("SOSETLABEL"); + break; + case PRU_CLOSE: + db_printf("CLOSE"); + break; + case PRU_FLUSH: + db_printf("FLUSH"); + break; + default: + db_printf("Unknown PRU (%u)", tlm_buf->tlb_flex1); + break; + } + if (tlm_buf->tlb_errno >= 0) { + db_printf(", error: %d", tlm_buf->tlb_errno); + } +} + +static void +db_print_rto(struct tcp_log_buffer *tlm_buf) +{ + tt_what what; + tt_which which; + + what = (tlm_buf->tlb_flex1 & 0xffffff00) >> 8; + which = tlm_buf->tlb_flex1 & 0x000000ff; + switch (what) { + case TT_PROCESSING: + db_printf("Processing "); + break; + case TT_PROCESSED: + db_printf("Processed "); + break; + case TT_STARTING: + db_printf("Starting "); + break; + case TT_STOPPING: + db_printf("Stopping "); + break; + default: + db_printf("Unknown operation (%u) for ", what); + break; + } + switch (which) { + case TT_REXMT: + db_printf("Retransmission "); + break; + case TT_PERSIST: + db_printf("Persist "); + break; + case TT_KEEP: + db_printf("Keepalive "); + break; + case TT_2MSL: + db_printf("2 MSL "); + break; + case TT_DELACK: + db_printf("Delayed ACK "); + break; + default: + db_printf("Unknown (%u) ", which); + break; + } + db_printf("timer"); + if (what == TT_STARTING) { + db_printf(": %u ms", tlm_buf->tlb_flex2); + } +} + +static void +db_print_usersend(struct tcp_log_buffer *tlm_buf) +{ + if ((tlm_buf->tlb_eventflags & TLB_FLAG_RXBUF) == 0) { + return; + } + if ((tlm_buf->tlb_eventflags & TLB_FLAG_TXBUF) == 0) { + return; + } + db_printf("usersend: rcv.acc: %u rcv.ccc: %u snd.acc: %u snd.ccc: %u", + tlm_buf->tlb_rxbuf.tls_sb_acc, tlm_buf->tlb_rxbuf.tls_sb_ccc, + tlm_buf->tlb_txbuf.tls_sb_acc, tlm_buf->tlb_txbuf.tls_sb_ccc); +} + +void +db_print_bblog_entries(struct tcp_log_stailq *log_entries, int indent) +{ + struct tcp_log_mem *log_entry; + struct tcp_log_buffer *tlm_buf, *prev_tlm_buf; + int64_t delta_t; + + indent += 2; + prev_tlm_buf = NULL; + STAILQ_FOREACH(log_entry, log_entries, tlm_queue) { + db_print_indent(indent); + tlm_buf = &log_entry->tlm_buf; + if (prev_tlm_buf == NULL) { + db_printf(" 0.000 "); + } else { + delta_t = sbttoms(tvtosbt(tlm_buf->tlb_tv) - + tvtosbt(prev_tlm_buf->tlb_tv)); + db_printf("+%u.%03u ", (uint32_t)(delta_t / 1000), + (uint32_t)(delta_t % 1000)); + } + switch (tlm_buf->tlb_eventid) { + case TCP_LOG_IN: + db_printf("< "); + db_print_tcphdr(tlm_buf); + break; + case TCP_LOG_OUT: + db_printf("> "); + db_print_tcphdr(tlm_buf); + break; + case TCP_LOG_RTO: + db_print_rto(tlm_buf); + break; + case TCP_LOG_PRU: + db_print_pru(tlm_buf); + break; + case TCP_LOG_USERSEND: + db_print_usersend(tlm_buf); + break; + default: + break; + } + db_printf("\n"); + prev_tlm_buf = tlm_buf; + if (db_pager_quit) + break; + } +} +#endif diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -3083,7 +3083,7 @@ } static void -db_print_tcpcb(struct tcpcb *tp, const char *name, int indent) +db_print_tcpcb(struct tcpcb *tp, const char *name, int indent, bool show_bblog) { db_print_indent(indent); @@ -3208,35 +3208,42 @@ db_print_indent(indent); db_printf("t_lognum: %d t_loglimit: %d t_logsn: %u\n", tp->t_lognum, tp->t_loglimit, tp->t_logsn); + + if (show_bblog) { + db_print_bblog_entries(&tp->t_logs, indent); + } } DB_SHOW_COMMAND(tcpcb, db_show_tcpcb) { struct tcpcb *tp; + bool show_bblog; if (!have_addr) { db_printf("usage: show tcpcb \n"); return; } + show_bblog = strchr(modif, 'b') != NULL; tp = (struct tcpcb *)addr; - db_print_tcpcb(tp, "tcpcb", 0); + db_print_tcpcb(tp, "tcpcb", 0, show_bblog); } DB_SHOW_ALL_COMMAND(tcpcbs, db_show_all_tcpcbs) { VNET_ITERATOR_DECL(vnet_iter); struct inpcb *inp; - bool only_locked; + bool only_locked, show_bblog; only_locked = strchr(modif, 'l') != NULL; + show_bblog = strchr(modif, 'b') != NULL; VNET_FOREACH(vnet_iter) { CURVNET_SET(vnet_iter); CK_LIST_FOREACH(inp, &V_tcbinfo.ipi_listhead, inp_list) { if (only_locked && inp->inp_lock.rw_lock == RW_UNLOCKED) continue; - db_print_tcpcb(intotcpcb(inp), "tcpcb", 0); + db_print_tcpcb(intotcpcb(inp), "tcpcb", 0, show_bblog); if (db_pager_quit) break; }