xref: /dragonfly/usr.sbin/trpt/trpt.c (revision 984263bc)
1 /*
2  * Copyright (c) 1983, 1988, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1983, 1988, 1993\n\
37 	The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)trpt.c	8.1 (Berkeley) 6/6/93";
43 #endif
44 static const char rcsid[] =
45   "$FreeBSD: src/usr.sbin/trpt/trpt.c,v 1.12 2000/01/29 11:49:07 shin Exp $";
46 #endif /* not lint */
47 
48 #include <sys/param.h>
49 #include <sys/queue.h>
50 #include <sys/socket.h>
51 #include <sys/socketvar.h>
52 #define PRUREQUESTS
53 #include <sys/protosw.h>
54 #include <sys/file.h>
55 #include <sys/time.h>
56 
57 #include <net/route.h>
58 #include <net/if.h>
59 
60 #include <netinet/in.h>
61 #include <netinet/in_systm.h>
62 #include <netinet/ip.h>
63 #ifdef INET6
64 #include <netinet/ip6.h>
65 #endif
66 #include <netinet/ip_var.h>
67 #include <netinet/tcp.h>
68 #define TCPSTATES
69 #include <netinet/tcp_fsm.h>
70 #include <netinet/tcp_seq.h>
71 #define	TCPTIMERS
72 #include <netinet/tcp_timer.h>
73 #include <netinet/tcp_var.h>
74 #include <netinet/tcpip.h>
75 #define	TANAMES
76 #include <netinet/tcp_debug.h>
77 
78 #include <arpa/inet.h>
79 
80 #include <err.h>
81 #include <nlist.h>
82 #include <paths.h>
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <unistd.h>
86 
87 struct nlist nl[] = {
88 #define	N_TCP_DEBUG	0
89 	{ "_tcp_debug" },
90 #define	N_TCP_DEBX	1
91 	{ "_tcp_debx" },
92 	{ "" },
93 };
94 
95 static caddr_t tcp_pcbs[TCP_NDEBUG];
96 static n_time ntime;
97 static int aflag, kflag, memf, follow, sflag, tflag;
98 
99 void dotrace __P((caddr_t));
100 void klseek __P((int, off_t, int));
101 int numeric __P((caddr_t *, caddr_t *));
102 void tcp_trace __P((short, short, struct tcpcb *, struct tcpcb *,
103 			int, void *, struct tcphdr *, int));
104 static void usage __P((void));
105 
106 int
107 main(argc, argv)
108 	int argc;
109 	char **argv;
110 {
111 	int ch, i, jflag, npcbs;
112 	char *system, *core;
113 
114 	jflag = npcbs = 0;
115 	while ((ch = getopt(argc, argv, "afjp:st")) != -1)
116 		switch (ch) {
117 		case 'a':
118 			++aflag;
119 			break;
120 		case 'f':
121 			++follow;
122 			setlinebuf(stdout);
123 			break;
124 		case 'j':
125 			++jflag;
126 			break;
127 		case 'p':
128 			if (npcbs >= TCP_NDEBUG)
129 				errx(1, "too many pcb's specified");
130 			(void)sscanf(optarg, "%x", (int *)&tcp_pcbs[npcbs++]);
131 			break;
132 		case 's':
133 			++sflag;
134 			break;
135 		case 't':
136 			++tflag;
137 			break;
138 		case '?':
139 		default:
140 			usage();
141 		}
142 	argc -= optind;
143 	argv += optind;
144 
145 	core = _PATH_KMEM;
146 	if (argc > 0) {
147 		system = *argv;
148 		argc--, argv++;
149 		if (argc > 0) {
150 			core = *argv;
151 			argc--, argv++;
152 			++kflag;
153 		}
154 		/*
155 		 * Discard setgid privileges if not the running kernel so that
156 		 * bad guys can't print interesting stuff from kernel memory.
157 		 */
158 		setgid(getgid());
159 	}
160 	else
161 		system = (char *)getbootfile();
162 
163 	if (nlist(system, nl) < 0 || !nl[0].n_value)
164 		errx(1, "%s: no namelist", system);
165 	if ((memf = open(core, O_RDONLY)) < 0)
166 		err(2, "%s", core);
167 	if (kflag)
168 		errx(1, "can't do core files yet");
169 	(void)klseek(memf, (off_t)nl[N_TCP_DEBX].n_value, L_SET);
170 	if (read(memf, (char *)&tcp_debx, sizeof(tcp_debx)) !=
171 	    sizeof(tcp_debx))
172 		err(3, "tcp_debx");
173 	(void)klseek(memf, (off_t)nl[N_TCP_DEBUG].n_value, L_SET);
174 	if (read(memf, (char *)tcp_debug, sizeof(tcp_debug)) !=
175 	    sizeof(tcp_debug))
176 		err(3, "tcp_debug");
177 	/*
178 	 * If no control blocks have been specified, figure
179 	 * out how many distinct one we have and summarize
180 	 * them in tcp_pcbs for sorting the trace records
181 	 * below.
182 	 */
183 	if (!npcbs) {
184 		for (i = 0; i < TCP_NDEBUG; i++) {
185 			register struct tcp_debug *td = &tcp_debug[i];
186 			register int j;
187 
188 			if (td->td_tcb == 0)
189 				continue;
190 			for (j = 0; j < npcbs; j++)
191 				if (tcp_pcbs[j] == td->td_tcb)
192 					break;
193 			if (j >= npcbs)
194 				tcp_pcbs[npcbs++] = td->td_tcb;
195 		}
196 		if (!npcbs)
197 			exit(0);
198 	}
199 	qsort(tcp_pcbs, npcbs, sizeof(caddr_t), numeric);
200 	if (jflag) {
201 		for (i = 0;;) {
202 			printf("%x", (int)tcp_pcbs[i]);
203 			if (++i == npcbs)
204 				break;
205 			fputs(", ", stdout);
206 		}
207 		putchar('\n');
208 	}
209 	else for (i = 0; i < npcbs; i++) {
210 		printf("\n%x:\n", (int)tcp_pcbs[i]);
211 		dotrace(tcp_pcbs[i]);
212 	}
213 	exit(0);
214 }
215 
216 static void
217 usage()
218 {
219 	(void)fprintf(stderr,
220 		"usage: trpt [-afjst] [-p hex-address] [system [core]]\n");
221 	exit(1);
222 }
223 
224 void
225 dotrace(tcpcb)
226 	register caddr_t tcpcb;
227 {
228 	register struct tcp_debug *td;
229 	register int i;
230 	int prev_debx = tcp_debx, family;
231 
232 again:	if (--tcp_debx < 0)
233 		tcp_debx = TCP_NDEBUG - 1;
234 	for (i = prev_debx % TCP_NDEBUG; i < TCP_NDEBUG; i++) {
235 		td = &tcp_debug[i];
236 		if (tcpcb && td->td_tcb != tcpcb)
237 			continue;
238 		ntime = ntohl(td->td_time);
239 #ifdef INET6
240 		family = td->td_family;
241 #else
242 		family = AF_INET;
243 #endif
244 		switch(family) {
245 		case AF_INET:
246 			tcp_trace(td->td_act, td->td_ostate,
247 				  (struct tcpcb *)td->td_tcb,
248 				  &td->td_cb, td->td_family, &td->td_ti.ti_i,
249 				  &td->td_ti.ti_t, td->td_req);
250 			break;
251 #ifdef INET6
252 		case AF_INET6:
253 			tcp_trace(td->td_act, td->td_ostate,
254 				  (struct tcpcb *)td->td_tcb,
255 				  &td->td_cb, td->td_family, &td->td_ti6.ip6,
256 				  &td->td_ti6.th, td->td_req);
257 			break;
258 #endif
259 		}
260 		if (i == tcp_debx)
261 			goto done;
262 	}
263 	for (i = 0; i <= tcp_debx % TCP_NDEBUG; i++) {
264 		td = &tcp_debug[i];
265 		if (tcpcb && td->td_tcb != tcpcb)
266 			continue;
267 		ntime = ntohl(td->td_time);
268 #ifdef INET6
269 		family = td->td_family;
270 #else
271 		family = AF_INET;
272 #endif
273 		switch(family) {
274 		case AF_INET:
275 			tcp_trace(td->td_act, td->td_ostate,
276 				  (struct tcpcb *)td->td_tcb,
277 				  &td->td_cb, td->td_family, &td->td_ti.ti_i,
278 				  &td->td_ti.ti_t, td->td_req);
279 			break;
280 #ifdef INET6
281 		case AF_INET6:
282 			tcp_trace(td->td_act, td->td_ostate,
283 				  (struct tcpcb *)td->td_tcb,
284 				  &td->td_cb, td->td_family, &td->td_ti6.ip6,
285 				  &td->td_ti6.th, td->td_req);
286 			break;
287 #endif
288 		}
289 	}
290 done:	if (follow) {
291 		prev_debx = tcp_debx + 1;
292 		if (prev_debx >= TCP_NDEBUG)
293 			prev_debx = 0;
294 		do {
295 			sleep(1);
296 			(void)klseek(memf, (off_t)nl[N_TCP_DEBX].n_value, L_SET);
297 			if (read(memf, (char *)&tcp_debx, sizeof(tcp_debx)) !=
298 			    sizeof(tcp_debx))
299 				err(3, "tcp_debx");
300 		} while (tcp_debx == prev_debx);
301 		(void)klseek(memf, (off_t)nl[N_TCP_DEBUG].n_value, L_SET);
302 		if (read(memf, (char *)tcp_debug, sizeof(tcp_debug)) !=
303 		    sizeof(tcp_debug))
304 			err(3, "tcp_debug");
305 		goto again;
306 	}
307 }
308 
309 /*
310  * Tcp debug routines
311  */
312 /*ARGSUSED*/
313 void
314 tcp_trace(act, ostate, atp, tp, family, ip, th, req)
315 	short act, ostate;
316 	struct tcpcb *atp, *tp;
317 	int family;
318 	void *ip;
319 	struct tcphdr *th;
320 	int req;
321 {
322 	tcp_seq seq, ack;
323 	int flags, len, win, timer;
324 	struct ip *ip4;
325 #ifdef INET6
326 	int isipv6, nopkt = 1;
327 	struct ip6_hdr *ip6;
328 	char ntop_buf[INET6_ADDRSTRLEN];
329 #endif
330 
331 #ifdef INET6
332 	switch (family) {
333 	case AF_INET:
334 		nopkt = 0;
335 		isipv6 = 0;
336 		ip4 = (struct ip *)ip;
337 		break;
338 	case AF_INET6:
339 		nopkt = 0;
340 		isipv6 = 1;
341 		ip6 = (struct ip6_hdr *)ip;
342 	case 0:
343 	default:
344 		break;
345 	}
346 #else
347 	ip4 = (struct ip *)ip;
348 #endif
349 	printf("%03ld %s:%s ",(ntime/10) % 1000, tcpstates[ostate],
350 	    tanames[act]);
351 	switch (act) {
352 	case TA_INPUT:
353 	case TA_OUTPUT:
354 	case TA_DROP:
355 #ifdef INET6
356 		if (nopkt != 0)
357 			break;
358 #endif
359 		if (aflag) {
360 			printf("(src=%s,%u, ",
361 
362 #ifdef INET6
363 			       isipv6
364 			       ? inet_ntop(AF_INET6, &ip6->ip6_src, ntop_buf,
365 					   sizeof(ntop_buf)) :
366 #endif
367 			       inet_ntoa(ip4->ip_src),
368 			       ntohs(th->th_sport));
369 			printf("dst=%s,%u)",
370 #ifdef INET6
371 			       isipv6
372 			       ? inet_ntop(AF_INET6, &ip6->ip6_dst, ntop_buf,
373 					   sizeof(ntop_buf)) :
374 #endif
375 			       inet_ntoa(ip4->ip_dst),
376 			       ntohs(th->th_dport));
377 		}
378 		seq = th->th_seq;
379 		ack = th->th_ack;
380 
381 		len =
382 #ifdef INET6
383 			isipv6 ? ip6->ip6_plen :
384 #endif
385 			ip4->ip_len;
386 		win = th->th_win;
387 		if (act == TA_OUTPUT) {
388 			seq = ntohl(seq);
389 			ack = ntohl(ack);
390 			len = ntohs(len);
391 			win = ntohs(win);
392 		}
393 		if (act == TA_OUTPUT)
394 			len -= sizeof(struct tcphdr);
395 		if (len)
396 			printf("[%lx..%lx)", seq, seq + len);
397 		else
398 			printf("%lx", seq);
399 		printf("@%lx", ack);
400 		if (win)
401 			printf("(win=%x)", win);
402 		flags = th->th_flags;
403 		if (flags) {
404 			register char *cp = "<";
405 #define	pf(flag, string) { \
406 	if (th->th_flags&flag) { \
407 		(void)printf("%s%s", cp, string); \
408 		cp = ","; \
409 	} \
410 }
411 			pf(TH_SYN, "SYN");
412 			pf(TH_ACK, "ACK");
413 			pf(TH_FIN, "FIN");
414 			pf(TH_RST, "RST");
415 			pf(TH_PUSH, "PUSH");
416 			pf(TH_URG, "URG");
417 			printf(">");
418 		}
419 		break;
420 	case TA_USER:
421 		timer = req >> 8;
422 		req &= 0xff;
423 		printf("%s", prurequests[req]);
424 		if (req == PRU_SLOWTIMO || req == PRU_FASTTIMO)
425 			printf("<%s>", tcptimers[timer]);
426 		break;
427 	}
428 	printf(" -> %s", tcpstates[tp->t_state]);
429 	/* print out internal state of tp !?! */
430 	printf("\n");
431 	if (sflag) {
432 		printf("\trcv_nxt %lx rcv_wnd %x snd_una %lx snd_nxt %lx snd_max %lx\n",
433 		    tp->rcv_nxt, tp->rcv_wnd, tp->snd_una, tp->snd_nxt,
434 		    tp->snd_max);
435 		printf("\tsnd_wl1 %lx snd_wl2 %lx snd_wnd %x\n", tp->snd_wl1,
436 		    tp->snd_wl2, tp->snd_wnd);
437 	}
438 	/* print out timers? */
439 #if 0
440 	/*
441 	 * XXX
442 	 * kernel now uses callouts, not integer time values.
443 	 */
444 	if (tflag) {
445 		register char *cp = "\t";
446 		register int i;
447 
448 		for (i = 0; i < TCPT_NTIMERS; i++) {
449 			if (tp->t_timer[i] == 0)
450 				continue;
451 			printf("%s%s=%d", cp, tcptimers[i], tp->t_timer[i]);
452 			if (i == TCPT_REXMT)
453 				printf(" (t_rxtshft=%d)", tp->t_rxtshift);
454 			cp = ", ";
455 		}
456 		if (*cp != '\t')
457 			putchar('\n');
458 	}
459 #endif
460 }
461 
462 int
463 numeric(c1, c2)
464 	caddr_t *c1, *c2;
465 {
466 	return(*c1 - *c2);
467 }
468 
469 void
470 klseek(fd, base, off)
471 	int fd, off;
472 	off_t base;
473 {
474 	(void)lseek(fd, base, off);
475 }
476