1/*	$NetBSD: trpt.c,v 1.26 2009/04/19 00:26:18 lukem Exp $	*/
2
3/*-
4 * Copyright (c) 1997, 2005, 2006 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * Copyright (c) 1983, 1988, 1993
35 *	The Regents of the University of California.  All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 *    notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 *    notice, this list of conditions and the following disclaimer in the
44 *    documentation and/or other materials provided with the distribution.
45 * 3. Neither the name of the University nor the names of its contributors
46 *    may be used to endorse or promote products derived from this software
47 *    without specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 */
61
62#include <sys/cdefs.h>
63#ifndef lint
64__COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993\
65 The Regents of the University of California.  All rights reserved.");
66#endif /* not lint */
67
68#ifndef lint
69#if 0
70static char sccsid[] = "@(#)trpt.c	8.1 (Berkeley) 6/6/93";
71#else
72__RCSID("$NetBSD: trpt.c,v 1.26 2009/04/19 00:26:18 lukem Exp $");
73#endif
74#endif /* not lint */
75
76#define _CALLOUT_PRIVATE	/* for defs in sys/callout.h */
77
78#include <sys/param.h>
79#include <sys/queue.h>
80#include <sys/socket.h>
81#include <sys/socketvar.h>
82#include <sys/sysctl.h>
83#define PRUREQUESTS
84#include <sys/protosw.h>
85#include <sys/file.h>
86
87#include <net/route.h>
88#include <net/if.h>
89
90#include <netinet/in.h>
91#include <netinet/in_systm.h>
92#include <netinet/ip.h>
93#include <netinet/in_pcb.h>
94#include <netinet/ip_var.h>
95
96#ifdef INET6
97#ifndef INET
98#include <netinet/in.h>
99#endif
100#include <netinet/ip6.h>
101#endif
102
103#include <netinet/tcp.h>
104#define TCPSTATES
105#include <netinet/tcp_fsm.h>
106#include <netinet/tcp_seq.h>
107#define	TCPTIMERS
108#include <netinet/tcp_timer.h>
109#include <netinet/tcp_var.h>
110#include <netinet/tcpip.h>
111#define	TANAMES
112#include <netinet/tcp_debug.h>
113
114#include <arpa/inet.h>
115
116#include <err.h>
117#include <stdio.h>
118#include <errno.h>
119#include <kvm.h>
120#include <nlist.h>
121#include <paths.h>
122#include <limits.h>
123#include <stdlib.h>
124#include <unistd.h>
125
126static struct nlist nl[] = {
127#define	N_HARDCLOCK_TICKS	0
128	{ "_hardclock_ticks", 0, 0, 0, 0 },
129#define	N_TCP_DEBUG		1
130	{ "_tcp_debug", 0, 0, 0, 0 },
131#define	N_TCP_DEBX		2
132	{ "_tcp_debx", 0, 0, 0, 0 },
133	{ NULL, 0, 0, 0, 0 },
134};
135
136static caddr_t tcp_pcbs[TCP_NDEBUG];
137static n_time ntime;
138static int aflag, follow, sflag, tflag;
139
140/* see sys/netinet/tcp_debug.c */
141static struct  tcp_debug tcp_debug[TCP_NDEBUG];
142static int tcp_debx;
143
144static void	dotrace(caddr_t);
145static void	tcp_trace(short, short, struct tcpcb *, struct tcpcb *,
146	    int, void *, int);
147static int	numeric(const void *, const void *);
148__dead static void	usage(void);
149
150static kvm_t	*kd;
151static int     use_sysctl;
152
153int
154main(int argc, char *argv[])
155{
156	int ch, i, jflag, npcbs;
157	char *kernel, *core, *cp, errbuf[_POSIX2_LINE_MAX];
158	unsigned long l;
159
160	jflag = npcbs = 0;
161	kernel = core = NULL;
162
163	while ((ch = getopt(argc, argv, "afjp:stN:M:")) != -1) {
164		switch (ch) {
165		case 'a':
166			++aflag;
167			break;
168		case 'f':
169			++follow;
170			setlinebuf(stdout);
171			break;
172		case 'j':
173			++jflag;
174			break;
175		case 'p':
176			if (npcbs >= TCP_NDEBUG)
177				errx(1, "too many pcbs specified");
178			errno = 0;
179			cp = NULL;
180			l = strtoul(optarg, &cp, 16);
181			tcp_pcbs[npcbs] = (caddr_t)l;
182			if (*optarg == '\0' || *cp != '\0' || errno ||
183			    (unsigned long)tcp_pcbs[npcbs] != l)
184				errx(1, "invalid address: %s", optarg);
185			npcbs++;
186			break;
187		case 's':
188			++sflag;
189			break;
190		case 't':
191			++tflag;
192			break;
193		case 'N':
194			kernel = optarg;
195			break;
196		case 'M':
197			core = optarg;
198			break;
199		default:
200			usage();
201			/* NOTREACHED */
202		}
203	}
204	argc -= optind;
205	argv += optind;
206
207	if (argc)
208		usage();
209
210	use_sysctl = (kernel == NULL && core == NULL);
211
212	if (use_sysctl) {
213		size_t lenx = sizeof(tcp_debx);
214		size_t lend = sizeof(tcp_debug);
215
216		if (sysctlbyname("net.inet.tcp.debx", &tcp_debx, &lenx,
217		    NULL, 0) == -1)
218			err(1, "net.inet.tcp.debx");
219		if (sysctlbyname("net.inet.tcp.debug", &tcp_debug, &lend,
220		    NULL, 0) == -1)
221			err(1, "net.inet.tcp.debug");
222	} else {
223		kd = kvm_openfiles(kernel, core, NULL, O_RDONLY, errbuf);
224		if (kd == NULL)
225			errx(1, "can't open kmem: %s", errbuf);
226
227		if (kvm_nlist(kd, nl))
228			errx(2, "%s: no namelist", kernel);
229
230		if (kvm_read(kd, nl[N_TCP_DEBX].n_value, (char *)&tcp_debx,
231		    sizeof(tcp_debx)) != sizeof(tcp_debx))
232			errx(3, "tcp_debx: %s", kvm_geterr(kd));
233
234		if (kvm_read(kd, nl[N_TCP_DEBUG].n_value, (char *)tcp_debug,
235		    sizeof(tcp_debug)) != sizeof(tcp_debug))
236			errx(3, "tcp_debug: %s", kvm_geterr(kd));
237	}
238
239	/*
240	 * If no control blocks have been specified, figure
241	 * out how many distinct one we have and summarize
242	 * them in tcp_pcbs for sorting the trace records
243	 * below.
244	 */
245	if (npcbs == 0) {
246		for (i = 0; i < TCP_NDEBUG; i++) {
247			struct tcp_debug *td = &tcp_debug[i];
248			int j;
249
250			if (td->td_tcb == 0)
251				continue;
252			for (j = 0; j < npcbs; j++)
253				if (tcp_pcbs[j] == td->td_tcb)
254					break;
255			if (j >= npcbs)
256				tcp_pcbs[npcbs++] = td->td_tcb;
257		}
258		if (npcbs == 0)
259			exit(0);
260	}
261	qsort(tcp_pcbs, npcbs, sizeof(caddr_t), numeric);
262	if (jflag) {
263		for (i = 0;;) {
264			printf("%lx", (long)tcp_pcbs[i]);
265			if (++i == npcbs)
266				break;
267			fputs(", ", stdout);
268		}
269		putchar('\n');
270	} else {
271		for (i = 0; i < npcbs; i++) {
272			printf("\n%lx:\n", (long)tcp_pcbs[i]);
273			dotrace(tcp_pcbs[i]);
274		}
275	}
276	exit(0);
277}
278
279static void
280dotrace(caddr_t tcpcb)
281{
282	struct tcp_debug *td;
283	int prev_debx = tcp_debx;
284	int i;
285
286 again:
287	if (--tcp_debx < 0)
288		tcp_debx = TCP_NDEBUG - 1;
289	for (i = prev_debx % TCP_NDEBUG; i < TCP_NDEBUG; i++) {
290		td = &tcp_debug[i];
291		if (tcpcb && td->td_tcb != tcpcb)
292			continue;
293		ntime = ntohl(td->td_time);
294		switch (td->td_family) {
295		case AF_INET:
296			tcp_trace(td->td_act, td->td_ostate,
297			    (struct tcpcb *)td->td_tcb, &td->td_cb,
298			    td->td_family, &td->td_ti, td->td_req);
299			break;
300#ifdef INET6
301		case AF_INET6:
302			tcp_trace(td->td_act, td->td_ostate,
303			    (struct tcpcb *)td->td_tcb, &td->td_cb,
304			    td->td_family, &td->td_ti6, td->td_req);
305			break;
306#endif
307		default:
308			tcp_trace(td->td_act, td->td_ostate,
309			    (struct tcpcb *)td->td_tcb, &td->td_cb,
310			    td->td_family, NULL, td->td_req);
311			break;
312		}
313		if (i == tcp_debx)
314			goto done;
315	}
316	for (i = 0; i <= tcp_debx % TCP_NDEBUG; i++) {
317		td = &tcp_debug[i];
318		if (tcpcb && td->td_tcb != tcpcb)
319			continue;
320		ntime = ntohl(td->td_time);
321		switch (td->td_family) {
322		case AF_INET:
323			tcp_trace(td->td_act, td->td_ostate,
324			    (struct tcpcb *)td->td_tcb, &td->td_cb,
325			    td->td_family, &td->td_ti, td->td_req);
326			break;
327#ifdef INET6
328		case AF_INET6:
329			tcp_trace(td->td_act, td->td_ostate,
330			    (struct tcpcb *)td->td_tcb, &td->td_cb,
331			    td->td_family, &td->td_ti6, td->td_req);
332			break;
333#endif
334		default:
335			tcp_trace(td->td_act, td->td_ostate,
336			    (struct tcpcb *)td->td_tcb, &td->td_cb,
337			    td->td_family, NULL, td->td_req);
338			break;
339		}
340	}
341 done:
342	if (follow) {
343		prev_debx = tcp_debx + 1;
344		if (prev_debx >= TCP_NDEBUG)
345			prev_debx = 0;
346		do {
347			sleep(1);
348			if (use_sysctl) {
349				size_t len = sizeof(tcp_debx);
350
351				if (sysctlbyname("net.inet.tcp.debx",
352				    &tcp_debx, &len, NULL, 0) == -1)
353					err(1, "net.inet.tcp.debx");
354			} else
355				if (kvm_read(kd, nl[N_TCP_DEBX].n_value,
356				    (char *)&tcp_debx, sizeof(tcp_debx)) !=
357				    sizeof(tcp_debx))
358					errx(3, "tcp_debx: %s",
359					    kvm_geterr(kd));
360		} while (tcp_debx == prev_debx);
361
362		if (use_sysctl) {
363			size_t len = sizeof(tcp_debug);
364
365			if (sysctlbyname("net.inet.tcp.debug", &tcp_debug,
366			    &len, NULL, 0) == -1)
367				err(1, "net.inet.tcp.debug");
368		} else
369			if (kvm_read(kd, nl[N_TCP_DEBUG].n_value,
370			    (char *)tcp_debug,
371			    sizeof(tcp_debug)) != sizeof(tcp_debug))
372				errx(3, "tcp_debug: %s", kvm_geterr(kd));
373
374		goto again;
375	}
376}
377
378/*
379 * Tcp debug routines
380 */
381/*ARGSUSED*/
382static void
383tcp_trace(short act, short ostate, struct tcpcb *atp, struct tcpcb *tp,
384    int family, void *packet, int req)
385{
386	tcp_seq seq, ack;
387	int flags, len, win, timer;
388	struct tcphdr *th = NULL;
389	struct ip *ip = NULL;
390#ifdef INET6
391	struct ip6_hdr *ip6 = NULL;
392#endif
393	callout_impl_t *ci;
394	char hbuf[MAXHOSTNAMELEN];
395
396	len = 0;	/* XXXGCC -Wuninitialized */
397
398	switch (family) {
399	case AF_INET:
400		if (packet) {
401			ip = (struct ip *)packet;
402			th = (struct tcphdr *)(ip + 1);
403		}
404		break;
405#ifdef INET6
406	case AF_INET6:
407		if (packet) {
408			ip6 = (struct ip6_hdr *)packet;
409			th = (struct tcphdr *)(ip6 + 1);
410		}
411		break;
412#endif
413	default:
414		return;
415	}
416
417	printf("%03d %s:%s ", (ntime/10) % 1000, tcpstates[ostate],
418	    tanames[act]);
419
420#ifndef INET6
421	if (!ip)
422#else
423	if (!(ip || ip6))
424#endif
425		goto skipact;
426
427	switch (act) {
428	case TA_INPUT:
429	case TA_OUTPUT:
430	case TA_DROP:
431		if (aflag) {
432			inet_ntop(family,
433#ifndef INET6
434				(void *)&ip->ip_src,
435#else
436				family == AF_INET ? (void *)&ip->ip_src
437						  : (void *)&ip6->ip6_src,
438#endif
439				hbuf, sizeof(hbuf));
440			printf("(src=%s,%u, ",
441			    hbuf, ntohs(th->th_sport));
442			inet_ntop(family,
443#ifndef INET6
444				(void *)&ip->ip_dst,
445#else
446				family == AF_INET ? (void *)&ip->ip_dst
447						  : (void *)&ip6->ip6_dst,
448#endif
449				hbuf, sizeof(hbuf));
450			printf("dst=%s,%u)",
451			    hbuf, ntohs(th->th_dport));
452		}
453		seq = th->th_seq;
454		ack = th->th_ack;
455		if (ip)
456			len = ip->ip_len;
457#ifdef INET6
458		else if (ip6)
459			len = ip6->ip6_plen;
460#endif
461		win = th->th_win;
462		if (act == TA_OUTPUT) {
463			NTOHL(seq);
464			NTOHL(ack);
465			NTOHS(len);
466			NTOHS(win);
467		}
468		if (act == TA_OUTPUT)
469			len -= sizeof(struct tcphdr);
470		if (len)
471			printf("[%x..%x)", seq, seq + len);
472		else
473			printf("%x", seq);
474		printf("@%x", ack);
475		if (win)
476			printf("(win=%x)", win);
477		flags = th->th_flags;
478		if (flags) {
479			const char *cp = "<";
480#define	pf(flag, string) { \
481	if (th->th_flags&flag) { \
482		(void)printf("%s%s", cp, string); \
483		cp = ","; \
484	} \
485}
486			pf(TH_SYN, "SYN");
487			pf(TH_ACK, "ACK");
488			pf(TH_FIN, "FIN");
489			pf(TH_RST, "RST");
490			pf(TH_PUSH, "PUSH");
491			pf(TH_URG, "URG");
492			pf(TH_CWR, "CWR");
493			pf(TH_ECE, "ECE");
494			printf(">");
495		}
496		break;
497	case TA_USER:
498		timer = req >> 8;
499		req &= 0xff;
500		printf("%s", prurequests[req]);
501		if (req == PRU_SLOWTIMO || req == PRU_FASTTIMO)
502			printf("<%s>", tcptimers[timer]);
503		break;
504	}
505
506skipact:
507	printf(" -> %s", tcpstates[tp->t_state]);
508	/* print out internal state of tp !?! */
509	printf("\n");
510	if (sflag) {
511		printf("\trcv_nxt %x rcv_wnd %lx snd_una %x snd_nxt %x snd_max %x\n",
512		    tp->rcv_nxt, tp->rcv_wnd, tp->snd_una, tp->snd_nxt,
513		    tp->snd_max);
514		printf("\tsnd_wl1 %x snd_wl2 %x snd_wnd %lx\n", tp->snd_wl1,
515		    tp->snd_wl2, tp->snd_wnd);
516	}
517	/* print out timers? */
518	if (tflag) {
519		const char *cp = "\t";
520		int i;
521		int hardticks;
522
523		if (use_sysctl) {
524			size_t hlen = sizeof(hardticks);
525
526			if (sysctlbyname("kern.hardclock_ticks", &hardticks,
527			    &hlen, NULL, 0) == -1)
528				err(1, "kern.hardclock_ticks");
529		} else {
530			if (kvm_read(kd, nl[N_HARDCLOCK_TICKS].n_value,
531			    (char *)&hardticks,
532			    sizeof(hardticks)) != sizeof(hardticks))
533				errx(3, "hardclock_ticks: %s", kvm_geterr(kd));
534
535			for (i = 0; i < TCPT_NTIMERS; i++) {
536				ci = (callout_impl_t *)&tp->t_timer[i];
537				if ((ci->c_flags & CALLOUT_PENDING) == 0)
538					continue;
539				printf("%s%s=%d", cp, tcptimers[i],
540				    ci->c_time - hardticks);
541				if (i == TCPT_REXMT)
542					printf(" (t_rxtshft=%d)",
543					    tp->t_rxtshift);
544				cp = ", ";
545			}
546			if (*cp != '\t')
547				putchar('\n');
548		}
549	}
550}
551
552static int
553numeric(const void *v1, const void *v2)
554{
555	const caddr_t *c1 = v1;
556	const caddr_t *c2 = v2;
557	int rv;
558
559	if (*c1 < *c2)
560		rv = -1;
561	else if (*c1 > *c2)
562		rv = 1;
563	else
564		rv = 0;
565
566	return (rv);
567}
568
569static void
570usage(void)
571{
572
573	(void) fprintf(stderr, "usage: %s [-afjst] [-p hex-address]"
574	    " [-N system] [-M core]\n", getprogname());
575	exit(1);
576}
577