blob: 16ee52d9a004cd349a794878a0106cb8091015bb [file] [log] [blame]
Eric Andersen31a0ece2001-10-31 11:00:46 +00001/* vi: set sw=4 ts=4: */
2/*
3 * Mini netstat implementation(s) for busybox
4 * based in part on the netstat implementation from net-tools.
5 *
Eric Andersen51b8bd62002-07-03 11:46:38 +00006 * Copyright (C) 2002 by Bart Visscher <magick@linux-fan.com>
Eric Andersen31a0ece2001-10-31 11:00:46 +00007 *
Eric Andersen51b8bd62002-07-03 11:46:38 +00008 * 2002-04-20
9 * IPV6 support added by Bart Visscher <magick@linux-fan.com>
"Robert P. J. Day"2819f752006-07-11 11:32:31 +000010 *
11 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Eric Andersen31a0ece2001-10-31 11:00:46 +000012 */
13
Eric Andersen31a0ece2001-10-31 11:00:46 +000014#include "busybox.h"
Rob Landleyc9c1a412006-07-12 19:17:55 +000015#include "inet_common.h"
Eric Andersen31a0ece2001-10-31 11:00:46 +000016
Denis Vlasenko703e2022007-01-22 14:12:08 +000017#define NETSTAT_CONNECTED 0x01
18#define NETSTAT_LISTENING 0x02
19#define NETSTAT_NUMERIC 0x04
Denis Vlasenko67b23e62006-10-03 21:00:06 +000020/* Must match getopt32 option string */
Denis Vlasenko703e2022007-01-22 14:12:08 +000021#define NETSTAT_TCP 0x10
22#define NETSTAT_UDP 0x20
23#define NETSTAT_RAW 0x40
24#define NETSTAT_UNIX 0x80
25#define NETSTAT_ALLPROTO (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX)
Eric Andersen31a0ece2001-10-31 11:00:46 +000026
Denis Vlasenko754a88f2006-09-22 16:02:40 +000027static int flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO;
Eric Andersen31a0ece2001-10-31 11:00:46 +000028
Eric Andersen31a0ece2001-10-31 11:00:46 +000029enum {
Denis Vlasenko703e2022007-01-22 14:12:08 +000030 TCP_ESTABLISHED = 1,
31 TCP_SYN_SENT,
32 TCP_SYN_RECV,
33 TCP_FIN_WAIT1,
34 TCP_FIN_WAIT2,
35 TCP_TIME_WAIT,
36 TCP_CLOSE,
37 TCP_CLOSE_WAIT,
38 TCP_LAST_ACK,
39 TCP_LISTEN,
40 TCP_CLOSING /* now a valid state */
Eric Andersen31a0ece2001-10-31 11:00:46 +000041};
42
Eric Andersenb0c39a82002-06-22 17:32:58 +000043static const char * const tcp_state[] =
Eric Andersen31a0ece2001-10-31 11:00:46 +000044{
Denis Vlasenko703e2022007-01-22 14:12:08 +000045 "",
46 "ESTABLISHED",
47 "SYN_SENT",
48 "SYN_RECV",
49 "FIN_WAIT1",
50 "FIN_WAIT2",
51 "TIME_WAIT",
52 "CLOSE",
53 "CLOSE_WAIT",
54 "LAST_ACK",
55 "LISTEN",
56 "CLOSING"
Eric Andersen31a0ece2001-10-31 11:00:46 +000057};
58
59typedef enum {
Denis Vlasenko703e2022007-01-22 14:12:08 +000060 SS_FREE = 0, /* not allocated */
61 SS_UNCONNECTED, /* unconnected to any socket */
62 SS_CONNECTING, /* in process of connecting */
63 SS_CONNECTED, /* connected to socket */
64 SS_DISCONNECTING /* in process of disconnecting */
Eric Andersen31a0ece2001-10-31 11:00:46 +000065} socket_state;
66
Denis Vlasenko703e2022007-01-22 14:12:08 +000067#define SO_ACCEPTCON (1<<16) /* performed a listen */
68#define SO_WAITDATA (1<<17) /* wait data to read */
69#define SO_NOSPACE (1<<18) /* no space to write */
Eric Andersen31a0ece2001-10-31 11:00:46 +000070
Eric Andersencd8c4362001-11-10 11:22:46 +000071static char *get_sname(int port, const char *proto, int num)
Eric Andersen31a0ece2001-10-31 11:00:46 +000072{
Denis Vlasenko703e2022007-01-22 14:12:08 +000073 char *str = itoa(ntohs(port));
74 if (!num) {
75 struct servent *se = getservbyport(port, proto);
Eric Andersen31a0ece2001-10-31 11:00:46 +000076 if (se)
Denis Vlasenko703e2022007-01-22 14:12:08 +000077 str = se->s_name;
Eric Andersen31a0ece2001-10-31 11:00:46 +000078 }
79 if (!port) {
Denis Vlasenko703e2022007-01-22 14:12:08 +000080 str = "*";
Eric Andersen31a0ece2001-10-31 11:00:46 +000081 }
82 return str;
83}
84
Eric Andersencd8c4362001-11-10 11:22:46 +000085static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, char *proto, int numeric)
Eric Andersen31a0ece2001-10-31 11:00:46 +000086{
87 char *port_name;
Eric Andersencd8c4362001-11-10 11:22:46 +000088
Denis Vlasenko703e2022007-01-22 14:12:08 +000089#if ENABLE_FEATURE_IPV6
Eric Andersen51b8bd62002-07-03 11:46:38 +000090 if (addr->sa_family == AF_INET6) {
91 INET6_rresolve(ip_port, size, (struct sockaddr_in6 *)addr,
Denis Vlasenko703e2022007-01-22 14:12:08 +000092 (numeric & NETSTAT_NUMERIC) ? 0x0fff : 0);
Eric Andersen51b8bd62002-07-03 11:46:38 +000093 } else
94#endif
95 {
Denis Vlasenko703e2022007-01-22 14:12:08 +000096 INET_rresolve(ip_port, size, (struct sockaddr_in *)addr,
97 0x4000 | ((numeric & NETSTAT_NUMERIC) ? 0x0fff : 0),
98 0xffffffff);
Eric Andersen51b8bd62002-07-03 11:46:38 +000099 }
Denis Vlasenko703e2022007-01-22 14:12:08 +0000100 port_name = get_sname(htons(port), proto, numeric);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000101 if ((strlen(ip_port) + strlen(port_name)) > 22)
102 ip_port[22 - strlen(port_name)] = '\0';
Denis Vlasenko703e2022007-01-22 14:12:08 +0000103 ip_port += strlen(ip_port);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000104 strcat(ip_port, ":");
105 strcat(ip_port, port_name);
106}
107
108static void tcp_do_one(int lnr, const char *line)
109{
110 char local_addr[64], rem_addr[64];
Eric Andersenb0c39a82002-06-22 17:32:58 +0000111 const char *state_str;
112 char more[512];
Eric Andersen31a0ece2001-10-31 11:00:46 +0000113 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
Denis Vlasenko703e2022007-01-22 14:12:08 +0000114#if ENABLE_FEATURE_IPV6
Eric Andersen51b8bd62002-07-03 11:46:38 +0000115 struct sockaddr_in6 localaddr, remaddr;
116 char addr6[INET6_ADDRSTRLEN];
117 struct in6_addr in6;
118#else
Eric Andersen31a0ece2001-10-31 11:00:46 +0000119 struct sockaddr_in localaddr, remaddr;
Eric Andersen51b8bd62002-07-03 11:46:38 +0000120#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000121 unsigned long rxq, txq, time_len, retr, inode;
122
123 if (lnr == 0)
124 return;
125
126 more[0] = '\0';
127 num = sscanf(line,
128 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
129 &d, local_addr, &local_port,
130 rem_addr, &rem_port, &state,
131 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
132
133 if (strlen(local_addr) > 8) {
Denis Vlasenko703e2022007-01-22 14:12:08 +0000134#if ENABLE_FEATURE_IPV6
Eric Andersen51b8bd62002-07-03 11:46:38 +0000135 sscanf(local_addr, "%08X%08X%08X%08X",
136 &in6.s6_addr32[0], &in6.s6_addr32[1],
137 &in6.s6_addr32[2], &in6.s6_addr32[3]);
138 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
139 inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
140 sscanf(rem_addr, "%08X%08X%08X%08X",
141 &in6.s6_addr32[0], &in6.s6_addr32[1],
142 &in6.s6_addr32[2], &in6.s6_addr32[3]);
143 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
144 inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
145 localaddr.sin6_family = AF_INET6;
146 remaddr.sin6_family = AF_INET6;
147#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000148 } else {
149 sscanf(local_addr, "%X",
150 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
151 sscanf(rem_addr, "%X",
152 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
153 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
154 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
155 }
156
157 if (num < 10) {
Denis Vlasenko754a88f2006-09-22 16:02:40 +0000158 bb_error_msg("warning, got bogus tcp line");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000159 return;
160 }
Eric Andersenb0c39a82002-06-22 17:32:58 +0000161 state_str = tcp_state[state];
Denis Vlasenko703e2022007-01-22 14:12:08 +0000162 if ((rem_port && (flags & NETSTAT_CONNECTED))
163 || (!rem_port && (flags & NETSTAT_LISTENING))
164 ) {
Eric Andersen31a0ece2001-10-31 11:00:46 +0000165 snprint_ip_port(local_addr, sizeof(local_addr),
166 (struct sockaddr *) &localaddr, local_port,
Denis Vlasenko703e2022007-01-22 14:12:08 +0000167 "tcp", flags & NETSTAT_NUMERIC);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000168
Eric Andersen31a0ece2001-10-31 11:00:46 +0000169 snprint_ip_port(rem_addr, sizeof(rem_addr),
170 (struct sockaddr *) &remaddr, rem_port,
Denis Vlasenko703e2022007-01-22 14:12:08 +0000171 "tcp", flags & NETSTAT_NUMERIC);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000172
173 printf("tcp %6ld %6ld %-23s %-23s %-12s\n",
174 rxq, txq, local_addr, rem_addr, state_str);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000175 }
176}
177
178static void udp_do_one(int lnr, const char *line)
179{
180 char local_addr[64], rem_addr[64];
181 char *state_str, more[512];
182 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
Denis Vlasenko703e2022007-01-22 14:12:08 +0000183#if ENABLE_FEATURE_IPV6
Eric Andersen51b8bd62002-07-03 11:46:38 +0000184 struct sockaddr_in6 localaddr, remaddr;
185 char addr6[INET6_ADDRSTRLEN];
186 struct in6_addr in6;
187#else
Eric Andersen31a0ece2001-10-31 11:00:46 +0000188 struct sockaddr_in localaddr, remaddr;
Eric Andersen51b8bd62002-07-03 11:46:38 +0000189#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000190 unsigned long rxq, txq, time_len, retr, inode;
191
192 if (lnr == 0)
193 return;
194
195 more[0] = '\0';
196 num = sscanf(line,
197 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
198 &d, local_addr, &local_port,
199 rem_addr, &rem_port, &state,
200 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
201
202 if (strlen(local_addr) > 8) {
Denis Vlasenko703e2022007-01-22 14:12:08 +0000203#if ENABLE_FEATURE_IPV6
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000204 /* Demangle what the kernel gives us */
Eric Andersen51b8bd62002-07-03 11:46:38 +0000205 sscanf(local_addr, "%08X%08X%08X%08X",
206 &in6.s6_addr32[0], &in6.s6_addr32[1],
207 &in6.s6_addr32[2], &in6.s6_addr32[3]);
208 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
209 inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
210 sscanf(rem_addr, "%08X%08X%08X%08X",
211 &in6.s6_addr32[0], &in6.s6_addr32[1],
212 &in6.s6_addr32[2], &in6.s6_addr32[3]);
213 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
214 inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
215 localaddr.sin6_family = AF_INET6;
216 remaddr.sin6_family = AF_INET6;
217#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000218 } else {
219 sscanf(local_addr, "%X",
220 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
221 sscanf(rem_addr, "%X",
222 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
223 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
224 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
225 }
226
227 if (num < 10) {
Denis Vlasenko754a88f2006-09-22 16:02:40 +0000228 bb_error_msg("warning, got bogus udp line");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000229 return;
230 }
231 switch (state) {
232 case TCP_ESTABLISHED:
233 state_str = "ESTABLISHED";
234 break;
235
236 case TCP_CLOSE:
237 state_str = "";
238 break;
239
240 default:
241 state_str = "UNKNOWN";
242 break;
243 }
244
Denis Vlasenko703e2022007-01-22 14:12:08 +0000245#if ENABLE_FEATURE_IPV6
Glenn L McGrathd7fb1b32002-11-26 02:40:56 +0000246# define notnull(A) (((A.sin6_family == AF_INET6) && \
Eric Andersen51b8bd62002-07-03 11:46:38 +0000247 ((A.sin6_addr.s6_addr32[0]) || \
248 (A.sin6_addr.s6_addr32[1]) || \
249 (A.sin6_addr.s6_addr32[2]) || \
250 (A.sin6_addr.s6_addr32[3]))) || \
251 ((A.sin6_family == AF_INET) && \
252 ((struct sockaddr_in *) &A)->sin_addr.s_addr))
253#else
Glenn L McGrathd7fb1b32002-11-26 02:40:56 +0000254# define notnull(A) (A.sin_addr.s_addr)
Eric Andersen51b8bd62002-07-03 11:46:38 +0000255#endif
Denis Vlasenko703e2022007-01-22 14:12:08 +0000256 if ((notnull(remaddr) && (flags & NETSTAT_CONNECTED))
257 || (!notnull(remaddr) && (flags & NETSTAT_LISTENING))
258 ) {
Eric Andersen31a0ece2001-10-31 11:00:46 +0000259 snprint_ip_port(local_addr, sizeof(local_addr),
260 (struct sockaddr *) &localaddr, local_port,
Denis Vlasenko703e2022007-01-22 14:12:08 +0000261 "udp", flags & NETSTAT_NUMERIC);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000262
Eric Andersen31a0ece2001-10-31 11:00:46 +0000263 snprint_ip_port(rem_addr, sizeof(rem_addr),
264 (struct sockaddr *) &remaddr, rem_port,
Denis Vlasenko703e2022007-01-22 14:12:08 +0000265 "udp", flags & NETSTAT_NUMERIC);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000266
267 printf("udp %6ld %6ld %-23s %-23s %-12s\n",
268 rxq, txq, local_addr, rem_addr, state_str);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000269 }
270}
271
272static void raw_do_one(int lnr, const char *line)
273{
274 char local_addr[64], rem_addr[64];
275 char *state_str, more[512];
276 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
Denis Vlasenko703e2022007-01-22 14:12:08 +0000277#if ENABLE_FEATURE_IPV6
Eric Andersen51b8bd62002-07-03 11:46:38 +0000278 struct sockaddr_in6 localaddr, remaddr;
279 char addr6[INET6_ADDRSTRLEN];
280 struct in6_addr in6;
281#else
Eric Andersen31a0ece2001-10-31 11:00:46 +0000282 struct sockaddr_in localaddr, remaddr;
Eric Andersen51b8bd62002-07-03 11:46:38 +0000283#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000284 unsigned long rxq, txq, time_len, retr, inode;
285
286 if (lnr == 0)
287 return;
288
289 more[0] = '\0';
290 num = sscanf(line,
291 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
292 &d, local_addr, &local_port,
293 rem_addr, &rem_port, &state,
294 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
295
296 if (strlen(local_addr) > 8) {
Denis Vlasenko703e2022007-01-22 14:12:08 +0000297#if ENABLE_FEATURE_IPV6
Eric Andersen51b8bd62002-07-03 11:46:38 +0000298 sscanf(local_addr, "%08X%08X%08X%08X",
299 &in6.s6_addr32[0], &in6.s6_addr32[1],
300 &in6.s6_addr32[2], &in6.s6_addr32[3]);
301 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
302 inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
303 sscanf(rem_addr, "%08X%08X%08X%08X",
304 &in6.s6_addr32[0], &in6.s6_addr32[1],
305 &in6.s6_addr32[2], &in6.s6_addr32[3]);
306 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
307 inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
308 localaddr.sin6_family = AF_INET6;
309 remaddr.sin6_family = AF_INET6;
310#endif
Eric Andersen31a0ece2001-10-31 11:00:46 +0000311 } else {
312 sscanf(local_addr, "%X",
313 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
314 sscanf(rem_addr, "%X",
315 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
316 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
317 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
318 }
319
320 if (num < 10) {
Denis Vlasenko754a88f2006-09-22 16:02:40 +0000321 bb_error_msg("warning, got bogus raw line");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000322 return;
323 }
Denis Vlasenko703e2022007-01-22 14:12:08 +0000324 state_str = itoa(state);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000325
Denis Vlasenko703e2022007-01-22 14:12:08 +0000326#if ENABLE_FEATURE_IPV6
Glenn L McGrathd7fb1b32002-11-26 02:40:56 +0000327# define notnull(A) (((A.sin6_family == AF_INET6) && \
Eric Andersen51b8bd62002-07-03 11:46:38 +0000328 ((A.sin6_addr.s6_addr32[0]) || \
329 (A.sin6_addr.s6_addr32[1]) || \
330 (A.sin6_addr.s6_addr32[2]) || \
331 (A.sin6_addr.s6_addr32[3]))) || \
332 ((A.sin6_family == AF_INET) && \
333 ((struct sockaddr_in *) &A)->sin_addr.s_addr))
334#else
Glenn L McGrathd7fb1b32002-11-26 02:40:56 +0000335# define notnull(A) (A.sin_addr.s_addr)
Eric Andersen51b8bd62002-07-03 11:46:38 +0000336#endif
Denis Vlasenko703e2022007-01-22 14:12:08 +0000337 if ((notnull(remaddr) && (flags & NETSTAT_CONNECTED))
338 || (!notnull(remaddr) && (flags & NETSTAT_LISTENING))
339 ) {
Eric Andersen31a0ece2001-10-31 11:00:46 +0000340 snprint_ip_port(local_addr, sizeof(local_addr),
341 (struct sockaddr *) &localaddr, local_port,
Denis Vlasenko703e2022007-01-22 14:12:08 +0000342 "raw", flags & NETSTAT_NUMERIC);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000343
Eric Andersen31a0ece2001-10-31 11:00:46 +0000344 snprint_ip_port(rem_addr, sizeof(rem_addr),
345 (struct sockaddr *) &remaddr, rem_port,
Denis Vlasenko703e2022007-01-22 14:12:08 +0000346 "raw", flags & NETSTAT_NUMERIC);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000347
348 printf("raw %6ld %6ld %-23s %-23s %-12s\n",
349 rxq, txq, local_addr, rem_addr, state_str);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000350 }
351}
352
353#define HAS_INODE 1
354
355static void unix_do_one(int nr, const char *line)
356{
357 static int has = 0;
358 char path[PATH_MAX], ss_flags[32];
359 char *ss_proto, *ss_state, *ss_type;
360 int num, state, type, inode;
361 void *d;
362 unsigned long refcnt, proto, unix_flags;
363
364 if (nr == 0) {
365 if (strstr(line, "Inode"))
366 has |= HAS_INODE;
367 return;
368 }
369 path[0] = '\0';
370 num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s",
371 &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path);
372 if (num < 6) {
Denis Vlasenko754a88f2006-09-22 16:02:40 +0000373 bb_error_msg("warning, got bogus unix line");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000374 return;
375 }
376 if (!(has & HAS_INODE))
377 snprintf(path,sizeof(path),"%d",inode);
378
Denis Vlasenko703e2022007-01-22 14:12:08 +0000379 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) != (NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
Eric Andersen31a0ece2001-10-31 11:00:46 +0000380 if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
Denis Vlasenko703e2022007-01-22 14:12:08 +0000381 if (!(flags & NETSTAT_LISTENING))
Eric Andersen31a0ece2001-10-31 11:00:46 +0000382 return;
383 } else {
Denis Vlasenko703e2022007-01-22 14:12:08 +0000384 if (!(flags & NETSTAT_CONNECTED))
Eric Andersen31a0ece2001-10-31 11:00:46 +0000385 return;
386 }
387 }
388
389 switch (proto) {
390 case 0:
391 ss_proto = "unix";
392 break;
393
394 default:
395 ss_proto = "??";
396 }
397
398 switch (type) {
399 case SOCK_STREAM:
400 ss_type = "STREAM";
401 break;
402
403 case SOCK_DGRAM:
404 ss_type = "DGRAM";
405 break;
406
407 case SOCK_RAW:
408 ss_type = "RAW";
409 break;
410
411 case SOCK_RDM:
412 ss_type = "RDM";
413 break;
414
415 case SOCK_SEQPACKET:
416 ss_type = "SEQPACKET";
417 break;
418
419 default:
420 ss_type = "UNKNOWN";
421 }
422
423 switch (state) {
424 case SS_FREE:
425 ss_state = "FREE";
426 break;
427
428 case SS_UNCONNECTED:
429 /*
430 * Unconnected sockets may be listening
431 * for something.
432 */
433 if (unix_flags & SO_ACCEPTCON) {
434 ss_state = "LISTENING";
435 } else {
436 ss_state = "";
437 }
438 break;
439
440 case SS_CONNECTING:
441 ss_state = "CONNECTING";
442 break;
443
444 case SS_CONNECTED:
445 ss_state = "CONNECTED";
446 break;
447
448 case SS_DISCONNECTING:
449 ss_state = "DISCONNECTING";
450 break;
451
452 default:
453 ss_state = "UNKNOWN";
454 }
455
456 strcpy(ss_flags, "[ ");
457 if (unix_flags & SO_ACCEPTCON)
458 strcat(ss_flags, "ACC ");
459 if (unix_flags & SO_WAITDATA)
460 strcat(ss_flags, "W ");
461 if (unix_flags & SO_NOSPACE)
462 strcat(ss_flags, "N ");
463
464 strcat(ss_flags, "]");
465
466 printf("%-5s %-6ld %-11s %-10s %-13s ",
467 ss_proto, refcnt, ss_flags, ss_type, ss_state);
468 if (has & HAS_INODE)
469 printf("%-6d ",inode);
470 else
471 printf("- ");
472 puts(path);
473}
474
475#define _PATH_PROCNET_UDP "/proc/net/udp"
Eric Andersen51b8bd62002-07-03 11:46:38 +0000476#define _PATH_PROCNET_UDP6 "/proc/net/udp6"
Eric Andersen31a0ece2001-10-31 11:00:46 +0000477#define _PATH_PROCNET_TCP "/proc/net/tcp"
Eric Andersen51b8bd62002-07-03 11:46:38 +0000478#define _PATH_PROCNET_TCP6 "/proc/net/tcp6"
Eric Andersen31a0ece2001-10-31 11:00:46 +0000479#define _PATH_PROCNET_RAW "/proc/net/raw"
Eric Andersen51b8bd62002-07-03 11:46:38 +0000480#define _PATH_PROCNET_RAW6 "/proc/net/raw6"
Eric Andersen31a0ece2001-10-31 11:00:46 +0000481#define _PATH_PROCNET_UNIX "/proc/net/unix"
482
Eric Andersenb0c39a82002-06-22 17:32:58 +0000483static void do_info(const char *file, const char *name, void (*proc)(int, const char *))
Eric Andersen31a0ece2001-10-31 11:00:46 +0000484{
Eric Andersen31a0ece2001-10-31 11:00:46 +0000485 int lnr = 0;
486 FILE *procinfo;
487
Eric Andersenb0c39a82002-06-22 17:32:58 +0000488 procinfo = fopen(file, "r");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000489 if (procinfo == NULL) {
490 if (errno != ENOENT) {
Denis Vlasenko703e2022007-01-22 14:12:08 +0000491 bb_perror_msg("%s", file);
Eric Andersenb0c39a82002-06-22 17:32:58 +0000492 } else {
Denis Vlasenkoea620772006-10-14 02:23:43 +0000493 bb_error_msg("no support for '%s' on this system", name);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000494 }
Denis Vlasenko703e2022007-01-22 14:12:08 +0000495 return;
Eric Andersen31a0ece2001-10-31 11:00:46 +0000496 }
Denis Vlasenko703e2022007-01-22 14:12:08 +0000497 do {
498 char *buffer = xmalloc_fgets(procinfo);
499 if (buffer) {
500 (proc)(lnr++, buffer);
501 free(buffer);
502 }
503 } while (!feof(procinfo));
504 fclose(procinfo);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000505}
506
507/*
508 * Our main function.
509 */
510
511int netstat_main(int argc, char **argv)
512{
Denis Vlasenko754a88f2006-09-22 16:02:40 +0000513 enum {
514 OPT_extended = 0x4,
515 OPT_showroute = 0x100,
516 };
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000517 unsigned opt;
Denis Vlasenko703e2022007-01-22 14:12:08 +0000518#if ENABLE_FEATURE_IPV6
Denis Vlasenko754a88f2006-09-22 16:02:40 +0000519 int inet = 1;
520 int inet6 = 1;
Eric Andersen51b8bd62002-07-03 11:46:38 +0000521#else
Denis Vlasenko703e2022007-01-22 14:12:08 +0000522 enum { inet = 1, inet6 = 0 };
Eric Andersen51b8bd62002-07-03 11:46:38 +0000523#endif
Denis Vlasenko754a88f2006-09-22 16:02:40 +0000524
525 /* Option string must match NETSTAT_xxx constants */
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000526 opt = getopt32(argc, argv, "laentuwxr");
Denis Vlasenko754a88f2006-09-22 16:02:40 +0000527 if (opt & 0x1) { // -l
528 flags &= ~NETSTAT_CONNECTED;
529 flags |= NETSTAT_LISTENING;
530 }
531 if (opt & 0x2) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a
532 //if (opt & 0x4) // -e
533 if (opt & 0x8) flags |= NETSTAT_NUMERIC; // -n
534 //if (opt & 0x10) // -t: NETSTAT_TCP
535 //if (opt & 0x20) // -u: NETSTAT_UDP
536 //if (opt & 0x40) // -w: NETSTAT_RAW
537 //if (opt & 0x80) // -x: NETSTAT_UNIX
538 if (opt & OPT_showroute) { // -r
Denis Vlasenko703e2022007-01-22 14:12:08 +0000539#if ENABLE_ROUTE
540 bb_displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended));
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000541 return 0;
Robert Griebl820098f2002-05-14 23:03:23 +0000542#else
Denis Vlasenko754a88f2006-09-22 16:02:40 +0000543 bb_error_msg_and_die("-r (display routing table) is not compiled in");
Robert Griebl820098f2002-05-14 23:03:23 +0000544#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000545 }
546
Denis Vlasenko754a88f2006-09-22 16:02:40 +0000547 opt &= NETSTAT_ALLPROTO;
548 if (opt) {
549 flags &= ~NETSTAT_ALLPROTO;
550 flags |= opt;
Eric Andersen31a0ece2001-10-31 11:00:46 +0000551 }
Denis Vlasenko754a88f2006-09-22 16:02:40 +0000552 if (flags & (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
Eric Andersen31a0ece2001-10-31 11:00:46 +0000553 printf("Active Internet connections "); /* xxx */
554
Denis Vlasenko703e2022007-01-22 14:12:08 +0000555 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
Eric Andersen31a0ece2001-10-31 11:00:46 +0000556 printf("(servers and established)");
Denis Vlasenko703e2022007-01-22 14:12:08 +0000557 else if (flags & NETSTAT_LISTENING)
558 printf("(only servers)");
559 else
560 printf("(w/o servers)");
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +0000561 printf("\nProto Recv-Q Send-Q Local Address Foreign Address State\n");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000562 }
Denis Vlasenko703e2022007-01-22 14:12:08 +0000563 if (inet && flags & NETSTAT_TCP)
564 do_info(_PATH_PROCNET_TCP, "AF INET (tcp)", tcp_do_one);
565#if ENABLE_FEATURE_IPV6
566 if (inet6 && flags & NETSTAT_TCP)
567 do_info(_PATH_PROCNET_TCP6, "AF INET6 (tcp)", tcp_do_one);
Eric Andersen51b8bd62002-07-03 11:46:38 +0000568#endif
Denis Vlasenko703e2022007-01-22 14:12:08 +0000569 if (inet && flags & NETSTAT_UDP)
570 do_info(_PATH_PROCNET_UDP, "AF INET (udp)", udp_do_one);
571#if ENABLE_FEATURE_IPV6
572 if (inet6 && flags & NETSTAT_UDP)
573 do_info(_PATH_PROCNET_UDP6, "AF INET6 (udp)", udp_do_one);
Eric Andersen51b8bd62002-07-03 11:46:38 +0000574#endif
Denis Vlasenko703e2022007-01-22 14:12:08 +0000575 if (inet && flags & NETSTAT_RAW)
576 do_info(_PATH_PROCNET_RAW, "AF INET (raw)", raw_do_one);
577#if ENABLE_FEATURE_IPV6
578 if (inet6 && flags & NETSTAT_RAW)
579 do_info(_PATH_PROCNET_RAW6, "AF INET6 (raw)", raw_do_one);
Eric Andersen51b8bd62002-07-03 11:46:38 +0000580#endif
Denis Vlasenko703e2022007-01-22 14:12:08 +0000581 if (flags & NETSTAT_UNIX) {
Eric Andersen31a0ece2001-10-31 11:00:46 +0000582 printf("Active UNIX domain sockets ");
Denis Vlasenko703e2022007-01-22 14:12:08 +0000583 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
Eric Andersen31a0ece2001-10-31 11:00:46 +0000584 printf("(servers and established)");
Denis Vlasenko703e2022007-01-22 14:12:08 +0000585 else if (flags & NETSTAT_LISTENING)
586 printf("(only servers)");
587 else
588 printf("(w/o servers)");
Eric Andersen31a0ece2001-10-31 11:00:46 +0000589 printf("\nProto RefCnt Flags Type State I-Node Path\n");
Denis Vlasenko703e2022007-01-22 14:12:08 +0000590 do_info(_PATH_PROCNET_UNIX, "AF UNIX", unix_do_one);
Eric Andersen31a0ece2001-10-31 11:00:46 +0000591 }
592 return 0;
593}