1#!/usr/sbin/dtrace -Cs
2/*
3 * tcpsnoop.d - snoop TCP network packets by process.
4 *              Written using DTrace (Solaris 10 3/05)
5 *
6 * This analyses TCP network packets and prints the responsible PID and UID,
7 * plus standard details such as IP address and port. This captures traffic
8 * of newly created TCP connections that were established while this program
9 * was running. It can help identify which processes is causing TCP traffic.
10 *
11 * 20-Apr-2006, ver 0.81        (check for newer versions)
12 *
13 * USAGE:       tcpsnoop.d
14 *
15 * FIELDS:
16 *		UID     	user ID
17 *		PID     	process ID
18 *		CMD     	command
19 *		LADDR		local IP address
20 *		RADDR		remote IP address
21 *		LPORT		local port number
22 *		RPORT		remote port number
23 *		DR      	direction
24 *		SIZE    	packet size, bytes
25 *
26 * SEE ALSO: snoop -rS
27 *
28 * COPYRIGHT: Copyright (c) 2005, 2006 Brendan Gregg.
29 *
30 * CDDL HEADER START
31 *
32 *  The contents of this file are subject to the terms of the
33 *  Common Development and Distribution License, Version 1.0 only
34 *  (the "License").  You may not use this file except in compliance
35 *  with the License.
36 *
37 *  You can obtain a copy of the license at Docs/cddl1.txt
38 *  or http://www.opensolaris.org/os/licensing.
39 *  See the License for the specific language governing permissions
40 *  and limitations under the License.
41 *
42 * CDDL HEADER END
43 *
44 * Author: Brendan Gregg  [Sydney, Australia]
45 *
46 * TODO: IPv6
47 *
48 * 09-Jul-2004  Brendan Gregg   Created this.
49 * 12-Mar-2005     "      "	Changed probes, size info now printed.
50 * 02-Jul-2005     "      "	Many more probes. Renamed "tcpsnoop.d".
51 * 03-Dec-2005	   "	  "	Fixed tcp_accept_finish bug, now 100% correct
52 *				execname. Thanks Kias Belgaied for expertise.
53 * 20-Apr-2006	   "	  "	Fixed SS_TCP_FAST_ACCEPT bug in build 31+.
54 */
55
56#pragma D option quiet
57#pragma D option switchrate=10hz
58
59#include <sys/file.h>
60#include <inet/common.h>
61#include <sys/byteorder.h>
62
63/*
64 * Print header
65 */
66dtrace:::BEGIN
67{
68	/* print main headers */
69	printf("%5s %6s %-15s %5s %2s %-15s %5s %5s %s\n",
70	    "UID", "PID", "LADDR", "LPORT", "DR", "RADDR", "RPORT",
71	    "SIZE", "CMD");
72}
73
74/*
75 * TCP Process inbound connections
76 *
77 * 0x00200000 has been hardcoded. It was SS_TCP_FAST_ACCEPT, but was
78 * renamed to SS_DIRECT around build 31.
79 */
80fbt:sockfs:sotpi_accept:entry
81/(arg1 & FREAD) && (arg1 & FWRITE) && (args[0]->so_state & 0x00200000)/
82{
83	self->sop = args[0];
84}
85
86fbt:sockfs:sotpi_create:return
87/self->sop/
88{
89	self->nsop = (struct sonode *)arg1;
90}
91
92fbt:sockfs:sotpi_accept:return
93/self->nsop/
94{
95	this->tcpp = (tcp_t *)self->nsop->so_priv;
96	self->connp = (conn_t *)this->tcpp->tcp_connp;
97	tname[(int)self->connp] = execname;
98	tpid[(int)self->connp] = pid;
99	tuid[(int)self->connp] = uid;
100}
101
102fbt:sockfs:sotpi_accept:return
103{
104	self->nsop = 0;
105	self->sop = 0;
106}
107
108/*
109 * TCP Process outbound connections
110 */
111fbt:ip:tcp_connect:entry
112{
113	this->tcpp = (tcp_t *)arg0;
114	self->connp = (conn_t *)this->tcpp->tcp_connp;
115	tname[(int)self->connp] = execname;
116	tpid[(int)self->connp] = pid;
117	tuid[(int)self->connp] = uid;
118}
119
120/*
121 * TCP Data translations
122 */
123fbt:sockfs:sotpi_accept:return,
124fbt:ip:tcp_connect:return
125/self->connp/
126{
127	/* fetch ports */
128#if defined(_BIG_ENDIAN)
129	self->lport = self->connp->u_port.tcpu_ports.tcpu_lport;
130	self->fport = self->connp->u_port.tcpu_ports.tcpu_fport;
131#else
132	self->lport = BSWAP_16(self->connp->u_port.tcpu_ports.tcpu_lport);
133	self->fport = BSWAP_16(self->connp->u_port.tcpu_ports.tcpu_fport);
134#endif
135
136	/* fetch IPv4 addresses */
137	this->fad12 =
138	    (int)self->connp->connua_v6addr.connua_faddr._S6_un._S6_u8[12];
139	this->fad13 =
140	    (int)self->connp->connua_v6addr.connua_faddr._S6_un._S6_u8[13];
141	this->fad14 =
142	    (int)self->connp->connua_v6addr.connua_faddr._S6_un._S6_u8[14];
143	this->fad15 =
144	    (int)self->connp->connua_v6addr.connua_faddr._S6_un._S6_u8[15];
145	this->lad12 =
146	    (int)self->connp->connua_v6addr.connua_laddr._S6_un._S6_u8[12];
147	this->lad13 =
148	    (int)self->connp->connua_v6addr.connua_laddr._S6_un._S6_u8[13];
149	this->lad14 =
150	    (int)self->connp->connua_v6addr.connua_laddr._S6_un._S6_u8[14];
151	this->lad15 =
152	    (int)self->connp->connua_v6addr.connua_laddr._S6_un._S6_u8[15];
153
154	/* convert type for use with lltostr() */
155	this->fad12 = this->fad12 < 0 ? 256 + this->fad12 : this->fad12;
156	this->fad13 = this->fad13 < 0 ? 256 + this->fad13 : this->fad13;
157	this->fad14 = this->fad14 < 0 ? 256 + this->fad14 : this->fad14;
158	this->fad15 = this->fad15 < 0 ? 256 + this->fad15 : this->fad15;
159	this->lad12 = this->lad12 < 0 ? 256 + this->lad12 : this->lad12;
160	this->lad13 = this->lad13 < 0 ? 256 + this->lad13 : this->lad13;
161	this->lad14 = this->lad14 < 0 ? 256 + this->lad14 : this->lad14;
162	this->lad15 = this->lad15 < 0 ? 256 + this->lad15 : this->lad15;
163
164	/* stringify addresses */
165	self->faddr = strjoin(lltostr(this->fad12), ".");
166	self->faddr = strjoin(self->faddr, strjoin(lltostr(this->fad13), "."));
167	self->faddr = strjoin(self->faddr, strjoin(lltostr(this->fad14), "."));
168	self->faddr = strjoin(self->faddr, lltostr(this->fad15 + 0));
169	self->laddr = strjoin(lltostr(this->lad12), ".");
170	self->laddr = strjoin(self->laddr, strjoin(lltostr(this->lad13), "."));
171	self->laddr = strjoin(self->laddr, strjoin(lltostr(this->lad14), "."));
172	self->laddr = strjoin(self->laddr, lltostr(this->lad15 + 0));
173
174	/* fix direction and save values */
175	tladdr[(int)self->connp] = self->laddr;
176	tfaddr[(int)self->connp] = self->faddr;
177	tlport[(int)self->connp] = self->lport;
178	tfport[(int)self->connp] = self->fport;
179
180	/* all systems go */
181	tok[(int)self->connp] = 1;
182}
183
184/*
185 * TCP Clear connp
186 */
187fbt:ip:tcp_get_conn:return
188{
189	/* Q_TO_CONN */
190	this->connp = (conn_t *)arg1;
191	tok[(int)this->connp] = 0;
192	tpid[(int)this->connp] = 0;
193	tuid[(int)this->connp] = 0;
194	tname[(int)this->connp] = 0;
195}
196
197/*
198 * TCP Process "port closed"
199 */
200fbt:ip:tcp_xmit_early_reset:entry
201{
202	this->queuep = (queue_t *)`tcp_g_q; /* ` */
203	this->connp = (conn_t *)this->queuep->q_ptr;
204	this->tcpp = (tcp_t *)this->connp->conn_tcp;
205
206	/* split addresses */
207	this->ipha = (ipha_t *)args[1]->b_rptr;
208	this->fad15 = (this->ipha->ipha_src & 0xff000000) >> 24;
209	this->fad14 = (this->ipha->ipha_src & 0x00ff0000) >> 16;
210	this->fad13 = (this->ipha->ipha_src & 0x0000ff00) >> 8;
211	this->fad12 = (this->ipha->ipha_src & 0x000000ff);
212	this->lad15 = (this->ipha->ipha_dst & 0xff000000) >> 24;
213	this->lad14 = (this->ipha->ipha_dst & 0x00ff0000) >> 16;
214	this->lad13 = (this->ipha->ipha_dst & 0x0000ff00) >> 8;
215	this->lad12 = (this->ipha->ipha_dst & 0x000000ff);
216
217	/* stringify addresses */
218	self->faddr = strjoin(lltostr(this->fad12), ".");
219	self->faddr = strjoin(self->faddr, strjoin(lltostr(this->fad13), "."));
220	self->faddr = strjoin(self->faddr, strjoin(lltostr(this->fad14), "."));
221	self->faddr = strjoin(self->faddr, lltostr(this->fad15 + 0));
222	self->laddr = strjoin(lltostr(this->lad12), ".");
223	self->laddr = strjoin(self->laddr, strjoin(lltostr(this->lad13), "."));
224	self->laddr = strjoin(self->laddr, strjoin(lltostr(this->lad14), "."));
225	self->laddr = strjoin(self->laddr, lltostr(this->lad15 + 0));
226
227	self->reset = 1;
228}
229
230/*
231 * TCP Fetch "port closed" ports
232 */
233fbt:ip:tcp_xchg:entry
234/self->reset/
235{
236#if defined(_BIG_ENDIAN)
237	self->lport = (uint16_t)arg0;
238	self->fport = (uint16_t)arg1;
239#else
240	self->lport = BSWAP_16((uint16_t)arg0);
241	self->fport = BSWAP_16((uint16_t)arg1);
242#endif
243	self->lport = BE16_TO_U16(arg0);
244	self->fport = BE16_TO_U16(arg1);
245}
246
247/*
248 * TCP Print "port closed"
249 */
250fbt:ip:tcp_xmit_early_reset:return
251{
252	self->name = "<closed>";
253	self->pid = 0;
254	self->uid = 0;
255	self->size = 54;	/* should check trailers */
256	self->dir = "<-";
257	printf("%5d %6d %-15s %5d %2s %-15s %5d %5d %s\n",
258	    self->uid, self->pid, self->laddr, self->lport, self->dir,
259	    self->faddr, self->fport, self->size, self->name);
260	self->dir = "->";
261	printf("%5d %6d %-15s %5d %2s %-15s %5d %5d %s\n",
262	    self->uid, self->pid, self->laddr, self->lport, self->dir,
263	    self->faddr, self->fport, self->size, self->name);
264	self->reset = 0;
265	self->size = 0;
266	self->name = 0;
267}
268
269/*
270 * TCP Process Write
271 */
272fbt:ip:tcp_send_data:entry
273{
274	self->conn_p = (conn_t *)args[0]->tcp_connp;
275}
276
277fbt:ip:tcp_send_data:entry
278/tok[(int)self->conn_p]/
279{
280	self->dir = "->";
281	self->size = msgdsize(args[2]) + 14;	/* should check trailers */
282	self->uid = tuid[(int)self->conn_p];
283	self->laddr = tladdr[(int)self->conn_p];
284	self->faddr = tfaddr[(int)self->conn_p];
285	self->lport = tlport[(int)self->conn_p];
286	self->fport = tfport[(int)self->conn_p];
287	self->ok = 2;
288
289	/* follow inetd -> in.* transitions */
290	self->name = pid && (tname[(int)self->conn_p] == "inetd") ?
291	    execname : tname[(int)self->conn_p];
292	self->pid = pid && (tname[(int)self->conn_p] == "inetd") ?
293	    pid : tpid[(int)self->conn_p];
294	tname[(int)self->conn_p] = self->name;
295	tpid[(int)self->conn_p] = self->pid;
296}
297
298/*
299 * TCP Process Read
300 */
301fbt:ip:tcp_rput_data:entry
302{
303	self->conn_p = (conn_t *)arg0;
304	self->size = msgdsize(args[1]) + 14;	/* should check trailers */
305}
306
307fbt:ip:tcp_rput_data:entry
308/tok[(int)self->conn_p]/
309{
310	self->dir = "<-";
311	self->uid = tuid[(int)self->conn_p];
312	self->laddr = tladdr[(int)self->conn_p];
313	self->faddr = tfaddr[(int)self->conn_p];
314	self->lport = tlport[(int)self->conn_p];
315	self->fport = tfport[(int)self->conn_p];
316	self->ok = 2;
317
318	/* follow inetd -> in.* transitions */
319	self->name = pid && (tname[(int)self->conn_p] == "inetd") ?
320	    execname : tname[(int)self->conn_p];
321	self->pid = pid && (tname[(int)self->conn_p] == "inetd") ?
322	    pid : tpid[(int)self->conn_p];
323	tname[(int)self->conn_p] = self->name;
324	tpid[(int)self->conn_p] = self->pid;
325}
326
327/*
328 * TCP Complete printing outbound handshake
329 */
330fbt:ip:tcp_connect:return
331/self->connp/
332{
333	self->name = tname[(int)self->connp];
334	self->pid = tpid[(int)self->connp];
335	self->uid = tuid[(int)self->connp];
336	self->size = 54;	/* should check trailers */
337	self->dir = "->";
338	/* this packet occured before connp was fully established */
339	printf("%5d %6d %-15s %5d %2s %-15s %5d %5d %s\n",
340	    self->uid, self->pid, self->laddr, self->lport, self->dir,
341	    self->faddr, self->fport, self->size, self->name);
342}
343
344/*
345 * TCP Complete printing inbound handshake
346 */
347fbt:sockfs:sotpi_accept:return
348/self->connp/
349{
350	self->name = tname[(int)self->connp];
351	self->pid = tpid[(int)self->connp];
352	self->uid = tuid[(int)self->connp];
353	self->size = 54;	/* should check trailers */
354	/* these packets occured before connp was fully established */
355	self->dir = "<-";
356	printf("%5d %6d %-15s %5d %2s %-15s %5d %5d %s\n",
357	    self->uid, self->pid, self->laddr, self->lport, self->dir,
358	    self->faddr, self->fport, self->size, self->name);
359	self->dir = "->";
360	printf("%5d %6d %-15s %5d %2s %-15s %5d %5d %s\n",
361	    self->uid, self->pid, self->laddr, self->lport, self->dir,
362	    self->faddr, self->fport, self->size, self->name);
363	self->dir = "<-";
364	printf("%5d %6d %-15s %5d %2s %-15s %5d %5d %s\n",
365	    self->uid, self->pid, self->laddr, self->lport, self->dir,
366	    self->faddr, self->fport, self->size, self->name);
367}
368
369/*
370 * Print output
371 */
372fbt:ip:tcp_send_data:entry,
373fbt:ip:tcp_rput_data:entry
374/self->ok == 2/
375{
376	/* print output line */
377	printf("%5d %6d %-15s %5d %2s %-15s %5d %5d %s\n",
378	    self->uid, self->pid, self->laddr, self->lport, self->dir,
379	    self->faddr, self->fport, self->size, self->name);
380}
381
382/*
383 * TCP Clear connect variables
384 */
385fbt:sockfs:sotpi_accept:return,
386fbt:ip:tcp_connect:return
387/self->connp/
388{
389	self->faddr = 0;
390	self->laddr = 0;
391	self->fport = 0;
392	self->lport = 0;
393	self->connp = 0;
394	self->name = 0;
395	self->pid = 0;
396	self->uid = 0;
397}
398
399/*
400 * TCP Clear r/w variables
401 */
402fbt:ip:tcp_send_data:entry,
403fbt:ip:tcp_rput_data:entry
404{
405	self->ok = 0;
406	self->dir = 0;
407	self->uid = 0;
408	self->pid = 0;
409	self->size = 0;
410	self->name = 0;
411	self->lport = 0;
412	self->fport = 0;
413	self->laddr = 0;
414	self->faddr = 0;
415	self->conn_p = 0;
416}
417