1/*-
2 * Copyright (c) 1982, 1986, 1993
3 *	The Regents of the University of California.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 4. Neither the name of the University nor the names of its contributors
15 *    may be used to endorse or promote products derived from this software
16 *    without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 *	@(#)tcp_debug.c	8.1 (Berkeley) 6/10/93
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD$");
35
36#include "opt_inet.h"
37#include "opt_inet6.h"
38#include "opt_tcpdebug.h"
39
40#ifdef TCPDEBUG
41/* load symbolic names */
42#define PRUREQUESTS
43#define TCPSTATES
44#define	TCPTIMERS
45#define	TANAMES
46#endif
47
48#include <sys/param.h>
49#include <sys/systm.h>
50#include <sys/kernel.h>
51#include <sys/lock.h>
52#include <sys/mbuf.h>
53#include <sys/mutex.h>
54#include <sys/protosw.h>
55#include <sys/socket.h>
56
57#include <netinet/in.h>
58#include <netinet/in_systm.h>
59#include <netinet/ip.h>
60#ifdef INET6
61#include <netinet/ip6.h>
62#endif
63#include <netinet/ip_var.h>
64#include <netinet/tcp.h>
65#include <netinet/tcp_fsm.h>
66#include <netinet/tcp_timer.h>
67#include <netinet/tcp_var.h>
68#include <netinet/tcpip.h>
69#include <netinet/tcp_debug.h>
70
71#ifdef TCPDEBUG
72static int		tcpconsdebug = 0;
73#endif
74
75/*
76 * Global ring buffer of TCP debugging state.  Each entry captures a snapshot
77 * of TCP connection state at any given moment.  tcp_debx addresses at the
78 * next available slot.  There is no explicit export of this data structure;
79 * it will be read via /dev/kmem by debugging tools.
80 */
81static struct tcp_debug	tcp_debug[TCP_NDEBUG];
82static int		tcp_debx;
83
84/*
85 * All global state is protected by tcp_debug_mtx; tcp_trace() is split into
86 * two parts, one of which saves connection and other state into the global
87 * array (locked by tcp_debug_mtx).
88 */
89struct mtx		tcp_debug_mtx;
90MTX_SYSINIT(tcp_debug_mtx, &tcp_debug_mtx, "tcp_debug_mtx", MTX_DEF);
91
92/*
93 * Save TCP state at a given moment; optionally, both tcpcb and TCP packet
94 * header state will be saved.
95 */
96void
97tcp_trace(short act, short ostate, struct tcpcb *tp, void *ipgen,
98    struct tcphdr *th, int req)
99{
100#ifdef INET6
101	int isipv6;
102#endif /* INET6 */
103	tcp_seq seq, ack;
104	int len, flags;
105	struct tcp_debug *td;
106
107	mtx_lock(&tcp_debug_mtx);
108	td = &tcp_debug[tcp_debx++];
109	if (tcp_debx == TCP_NDEBUG)
110		tcp_debx = 0;
111	bzero(td, sizeof(*td));
112#ifdef INET6
113	isipv6 = (ipgen != NULL && ((struct ip *)ipgen)->ip_v == 6) ? 1 : 0;
114#endif /* INET6 */
115	td->td_family =
116#ifdef INET6
117	    (isipv6 != 0) ? AF_INET6 :
118#endif
119	    AF_INET;
120#ifdef INET
121	td->td_time = iptime();
122#endif
123	td->td_act = act;
124	td->td_ostate = ostate;
125	td->td_tcb = (caddr_t)tp;
126	if (tp != NULL)
127		td->td_cb = *tp;
128	if (ipgen != NULL) {
129		switch (td->td_family) {
130#ifdef INET
131		case AF_INET:
132			bcopy(ipgen, &td->td_ti.ti_i, sizeof(td->td_ti.ti_i));
133			break;
134#endif
135#ifdef INET6
136		case AF_INET6:
137			bcopy(ipgen, td->td_ip6buf, sizeof(td->td_ip6buf));
138			break;
139#endif
140		}
141	}
142	if (th != NULL) {
143		switch (td->td_family) {
144#ifdef INET
145		case AF_INET:
146			td->td_ti.ti_t = *th;
147			break;
148#endif
149#ifdef INET6
150		case AF_INET6:
151			td->td_ti6.th = *th;
152			break;
153#endif
154		}
155	}
156	td->td_req = req;
157	mtx_unlock(&tcp_debug_mtx);
158#ifdef TCPDEBUG
159	if (tcpconsdebug == 0)
160		return;
161	if (tp != NULL)
162		printf("%p %s:", tp, tcpstates[ostate]);
163	else
164		printf("???????? ");
165	printf("%s ", tanames[act]);
166	switch (act) {
167	case TA_INPUT:
168	case TA_OUTPUT:
169	case TA_DROP:
170		if (ipgen == NULL || th == NULL)
171			break;
172		seq = th->th_seq;
173		ack = th->th_ack;
174		len =
175#ifdef INET6
176		    isipv6 ? ntohs(((struct ip6_hdr *)ipgen)->ip6_plen) :
177#endif
178		    ((struct ip *)ipgen)->ip_len;
179		if (act == TA_OUTPUT) {
180			seq = ntohl(seq);
181			ack = ntohl(ack);
182			len = ntohs((u_short)len);
183		}
184		if (act == TA_OUTPUT)
185			len -= sizeof (struct tcphdr);
186		if (len)
187			printf("[%x..%x)", seq, seq+len);
188		else
189			printf("%x", seq);
190		printf("@%x, urp=%x", ack, th->th_urp);
191		flags = th->th_flags;
192		if (flags) {
193			char *cp = "<";
194#define pf(f) {					\
195	if (th->th_flags & TH_##f) {		\
196		printf("%s%s", cp, #f);		\
197		cp = ",";			\
198	}					\
199}
200			pf(SYN); pf(ACK); pf(FIN); pf(RST); pf(PUSH); pf(URG);
201			printf(">");
202		}
203		break;
204
205	case TA_USER:
206		printf("%s", prurequests[req&0xff]);
207		if ((req & 0xff) == PRU_SLOWTIMO)
208			printf("<%s>", tcptimers[req>>8]);
209		break;
210	}
211	if (tp != NULL)
212		printf(" -> %s", tcpstates[tp->t_state]);
213	/* print out internal state of tp !?! */
214	printf("\n");
215	if (tp == NULL)
216		return;
217	printf(
218	"\trcv_(nxt,wnd,up) (%lx,%lx,%lx) snd_(una,nxt,max) (%lx,%lx,%lx)\n",
219	    (u_long)tp->rcv_nxt, tp->rcv_wnd, (u_long)tp->rcv_up,
220	    (u_long)tp->snd_una, (u_long)tp->snd_nxt, (u_long)tp->snd_max);
221	printf("\tsnd_(wl1,wl2,wnd) (%lx,%lx,%lx)\n",
222	    (u_long)tp->snd_wl1, (u_long)tp->snd_wl2, tp->snd_wnd);
223#endif /* TCPDEBUG */
224}
225