xref: /haiku/src/bin/network/netstat/netstat.cpp (revision a2efc1d6e0db60ae3c6ae29f04012c2d96f59347)
18dfd7ea7SAxel Dörfler /*
220a31c45SAxel Dörfler  * Copyright 2006-2019, Haiku, Inc. All Rights Reserved.
38dfd7ea7SAxel Dörfler  * Distributed under the terms of the MIT License.
48dfd7ea7SAxel Dörfler  *
58dfd7ea7SAxel Dörfler  * Authors:
68dfd7ea7SAxel Dörfler  *		Axel Dörfler, axeld@pinc-software.de
7bcdcff76SAxel Dörfler  *		James Woodcock
88dfd7ea7SAxel Dörfler  */
98dfd7ea7SAxel Dörfler 
1020a31c45SAxel Dörfler 
118dfd7ea7SAxel Dörfler #include <arpa/inet.h>
128dfd7ea7SAxel Dörfler #include <errno.h>
13df50f7a9SIngo Weinhold #include <getopt.h>
14df50f7a9SIngo Weinhold #include <net/if.h>
15df50f7a9SIngo Weinhold #include <netdb.h>
16df50f7a9SIngo Weinhold #include <netinet/in.h>
178dfd7ea7SAxel Dörfler #include <stdio.h>
188dfd7ea7SAxel Dörfler #include <stdlib.h>
198dfd7ea7SAxel Dörfler #include <string.h>
208dfd7ea7SAxel Dörfler #include <unistd.h>
21df50f7a9SIngo Weinhold 
22df50f7a9SIngo Weinhold #include <SupportDefs.h>
23df50f7a9SIngo Weinhold 
24df50f7a9SIngo Weinhold #include <net_stat.h>
25df50f7a9SIngo Weinhold #include <syscalls.h>
268dfd7ea7SAxel Dörfler 
278dfd7ea7SAxel Dörfler 
288dfd7ea7SAxel Dörfler extern const char* __progname;
298dfd7ea7SAxel Dörfler const char* kProgramName = __progname;
308dfd7ea7SAxel Dörfler 
31bcdcff76SAxel Dörfler static int sResolveNames = 1;
328dfd7ea7SAxel Dörfler 
338dfd7ea7SAxel Dörfler struct address_family {
348dfd7ea7SAxel Dörfler 	int			family;
358dfd7ea7SAxel Dörfler 	const char*	name;
368dfd7ea7SAxel Dörfler 	const char*	identifiers[4];
378dfd7ea7SAxel Dörfler 	void		(*print_address)(sockaddr* address);
388dfd7ea7SAxel Dörfler };
398dfd7ea7SAxel Dörfler 
4020a31c45SAxel Dörfler enum filter_flags {
4120a31c45SAxel Dörfler 	FILTER_FAMILY_MASK		= 0x0000ff,
4220a31c45SAxel Dörfler 	FILTER_PROTOCOL_MASK	= 0x00ff00,
4320a31c45SAxel Dörfler 	FILTER_STATE_MASK		= 0xff0000,
4420a31c45SAxel Dörfler 
4520a31c45SAxel Dörfler 	// Families
4620a31c45SAxel Dörfler 	FILTER_AF_INET			= 0x000001,
4720a31c45SAxel Dörfler 	FILTER_AF_INET6			= 0x000002,
4820a31c45SAxel Dörfler 	FILTER_AF_UNIX			= 0x000004,
4920a31c45SAxel Dörfler 
5020a31c45SAxel Dörfler 	// Protocols
5120a31c45SAxel Dörfler 	FILTER_IPPROTO_TCP		= 0x000100,
5220a31c45SAxel Dörfler 	FILTER_IPPROTO_UDP		= 0x000200,
5320a31c45SAxel Dörfler 
5420a31c45SAxel Dörfler 	// States
5520a31c45SAxel Dörfler 	FILTER_STATE_LISTEN		= 0x010000,
5620a31c45SAxel Dörfler };
5720a31c45SAxel Dörfler 
588dfd7ea7SAxel Dörfler // AF_INET family
598dfd7ea7SAxel Dörfler static void inet_print_address(sockaddr* address);
608dfd7ea7SAxel Dörfler 
618dfd7ea7SAxel Dörfler static const address_family kFamilies[] = {
628dfd7ea7SAxel Dörfler 	{
638dfd7ea7SAxel Dörfler 		AF_INET,
648dfd7ea7SAxel Dörfler 		"inet",
658dfd7ea7SAxel Dörfler 		{"AF_INET", "inet", "ipv4", NULL},
668dfd7ea7SAxel Dörfler 		inet_print_address
678dfd7ea7SAxel Dörfler 	},
688dfd7ea7SAxel Dörfler 	{ -1, NULL, {NULL}, NULL }
698dfd7ea7SAxel Dörfler };
708dfd7ea7SAxel Dörfler 
718dfd7ea7SAxel Dörfler 
728dfd7ea7SAxel Dörfler static void
inet_print_address(sockaddr * _address)738dfd7ea7SAxel Dörfler inet_print_address(sockaddr* _address)
748dfd7ea7SAxel Dörfler {
758dfd7ea7SAxel Dörfler 	sockaddr_in& address = *(sockaddr_in *)_address;
768dfd7ea7SAxel Dörfler 
778dfd7ea7SAxel Dörfler 	if (address.sin_family != AF_INET || address.sin_len == 0) {
7866a4a428SHugo Santos 		printf("%-22s", "-");
798dfd7ea7SAxel Dörfler 		return;
808dfd7ea7SAxel Dörfler 	}
818dfd7ea7SAxel Dörfler 
82bcdcff76SAxel Dörfler 	hostent* host = NULL;
83bcdcff76SAxel Dörfler 	servent* service = NULL;
84bcdcff76SAxel Dörfler 	if (sResolveNames) {
85bcdcff76SAxel Dörfler 		host = gethostbyaddr((const char*)&address.sin_addr, sizeof(in_addr),
86bcdcff76SAxel Dörfler 			AF_INET);
87eab09848SAxel Dörfler 		service = getservbyport(address.sin_port, NULL);
88bcdcff76SAxel Dörfler 	}
898dfd7ea7SAxel Dörfler 
9079608a2dSAxel Dörfler 	const char *hostName;
9179608a2dSAxel Dörfler 	if (host != NULL)
9279608a2dSAxel Dörfler 		hostName = host->h_name;
9379608a2dSAxel Dörfler 	else if (address.sin_addr.s_addr == INADDR_ANY)
9479608a2dSAxel Dörfler 		hostName = "*";
958dfd7ea7SAxel Dörfler 	else
9679608a2dSAxel Dörfler 		hostName = inet_ntoa(address.sin_addr);
978dfd7ea7SAxel Dörfler 
9879608a2dSAxel Dörfler 	char buffer[128];
9979608a2dSAxel Dörfler 	int length = strlcpy(buffer, hostName, sizeof(buffer));
10079608a2dSAxel Dörfler 
10179608a2dSAxel Dörfler 	char port[64];
10279608a2dSAxel Dörfler 	if (service != NULL)
10379608a2dSAxel Dörfler 		strlcpy(port, service->s_name, sizeof(port));
10479608a2dSAxel Dörfler 	else if (address.sin_port == 0)
10579608a2dSAxel Dörfler 		strcpy(port, "*");
10679608a2dSAxel Dörfler 	else
10779608a2dSAxel Dörfler 		snprintf(port, sizeof(port), "%u", ntohs(address.sin_port));
10879608a2dSAxel Dörfler 
10979608a2dSAxel Dörfler 	snprintf(buffer + length, sizeof(buffer) - length, ":%s", port);
11079608a2dSAxel Dörfler 
11166a4a428SHugo Santos 	printf("%-22s", buffer);
1128dfd7ea7SAxel Dörfler }
1138dfd7ea7SAxel Dörfler 
1148dfd7ea7SAxel Dörfler 
1158dfd7ea7SAxel Dörfler //	#pragma mark -
1168dfd7ea7SAxel Dörfler 
1178dfd7ea7SAxel Dörfler 
1188dfd7ea7SAxel Dörfler void
usage(int status)1198dfd7ea7SAxel Dörfler usage(int status)
1208dfd7ea7SAxel Dörfler {
12120a31c45SAxel Dörfler 	printf("Usage: %s [-nh]\n", kProgramName);
12220a31c45SAxel Dörfler 	printf("Options:\n");
123bcdcff76SAxel Dörfler 	printf("	-n	don't resolve names\n");
124bcdcff76SAxel Dörfler 	printf("	-h	this help\n");
12520a31c45SAxel Dörfler 	printf("Filter options:\n");
12620a31c45SAxel Dörfler 	printf("	-4	IPv4\n");
12720a31c45SAxel Dörfler 	printf("	-6	IPv6\n");
12820a31c45SAxel Dörfler 	printf("	-x	Unix\n");
12920a31c45SAxel Dörfler 	printf("	-t	TCP\n");
13020a31c45SAxel Dörfler 	printf("	-u	UDP\n");
13120a31c45SAxel Dörfler 	printf("	-l	listen state\n");
1328dfd7ea7SAxel Dörfler 
1338dfd7ea7SAxel Dörfler 	exit(status);
1348dfd7ea7SAxel Dörfler }
1358dfd7ea7SAxel Dörfler 
1368dfd7ea7SAxel Dörfler 
1378dfd7ea7SAxel Dörfler bool
get_address_family(const char * argument,int32 & familyIndex)1388dfd7ea7SAxel Dörfler get_address_family(const char* argument, int32& familyIndex)
1398dfd7ea7SAxel Dörfler {
1408dfd7ea7SAxel Dörfler 	for (int32 i = 0; kFamilies[i].family >= 0; i++) {
1418dfd7ea7SAxel Dörfler 		for (int32 j = 0; kFamilies[i].identifiers[j]; j++) {
1428dfd7ea7SAxel Dörfler 			if (!strcmp(argument, kFamilies[i].identifiers[j])) {
1438dfd7ea7SAxel Dörfler 				// found a match
1448dfd7ea7SAxel Dörfler 				familyIndex = i;
1458dfd7ea7SAxel Dörfler 				return true;
1468dfd7ea7SAxel Dörfler 			}
1478dfd7ea7SAxel Dörfler 		}
1488dfd7ea7SAxel Dörfler 	}
1498dfd7ea7SAxel Dörfler 
1508dfd7ea7SAxel Dörfler 	// defaults to AF_INET
1518dfd7ea7SAxel Dörfler 	familyIndex = 0;
1528dfd7ea7SAxel Dörfler 	return false;
1538dfd7ea7SAxel Dörfler }
1548dfd7ea7SAxel Dörfler 
1558dfd7ea7SAxel Dörfler 
1568dfd7ea7SAxel Dörfler //	#pragma mark -
1578dfd7ea7SAxel Dörfler 
1588dfd7ea7SAxel Dörfler 
1598dfd7ea7SAxel Dörfler int
main(int argc,char ** argv)1608dfd7ea7SAxel Dörfler main(int argc, char** argv)
1618dfd7ea7SAxel Dörfler {
162bcdcff76SAxel Dörfler 	int optionIndex = 0;
163bcdcff76SAxel Dörfler 	int opt;
16420a31c45SAxel Dörfler 	int filter = 0;
16520a31c45SAxel Dörfler 
16620a31c45SAxel Dörfler 	const static struct option kLongOptions[] = {
167bcdcff76SAxel Dörfler 		{"help", no_argument, 0, 'h'},
168bcdcff76SAxel Dörfler 		{"numeric", no_argument, 0, 'n'},
16920a31c45SAxel Dörfler 
17020a31c45SAxel Dörfler 		{"inet", no_argument, 0, '4'},
17120a31c45SAxel Dörfler 		{"inet6", no_argument, 0, '6'},
17220a31c45SAxel Dörfler 		{"unix", no_argument, 0, 'x'},
17320a31c45SAxel Dörfler 
17420a31c45SAxel Dörfler 		{"tcp", no_argument, 0, 't'},
17520a31c45SAxel Dörfler 		{"udp", no_argument, 0, 'u'},
17620a31c45SAxel Dörfler 
17720a31c45SAxel Dörfler 		{"listen", no_argument, 0, 'l'},
17820a31c45SAxel Dörfler 
179bcdcff76SAxel Dörfler 		{0, 0, 0, 0}
180bcdcff76SAxel Dörfler 	};
181bcdcff76SAxel Dörfler 
182bcdcff76SAxel Dörfler 	do {
18320a31c45SAxel Dörfler 		opt = getopt_long(argc, argv, "hn46xtul", kLongOptions,
18420a31c45SAxel Dörfler 			&optionIndex);
185bcdcff76SAxel Dörfler 		switch (opt) {
186bcdcff76SAxel Dörfler 			case -1:
187bcdcff76SAxel Dörfler 				// end of arguments, do nothing
188bcdcff76SAxel Dörfler 				break;
189bcdcff76SAxel Dörfler 
190bcdcff76SAxel Dörfler 			case 'n':
191bcdcff76SAxel Dörfler 				sResolveNames = 0;
192bcdcff76SAxel Dörfler 				break;
193bcdcff76SAxel Dörfler 
19420a31c45SAxel Dörfler 			// Family filter
19520a31c45SAxel Dörfler 			case '4':
19620a31c45SAxel Dörfler 				filter |= FILTER_AF_INET;
19720a31c45SAxel Dörfler 				break;
19820a31c45SAxel Dörfler 			case '6':
19920a31c45SAxel Dörfler 				filter |= FILTER_AF_INET6;
20020a31c45SAxel Dörfler 				break;
20120a31c45SAxel Dörfler 			case 'x':
20220a31c45SAxel Dörfler 				filter |= FILTER_AF_UNIX;
20320a31c45SAxel Dörfler 				break;
20420a31c45SAxel Dörfler 			// Protocol filter
20520a31c45SAxel Dörfler 			case 't':
20620a31c45SAxel Dörfler 				filter |= FILTER_IPPROTO_TCP;
20720a31c45SAxel Dörfler 				break;
20820a31c45SAxel Dörfler 			case 'u':
20920a31c45SAxel Dörfler 				filter |= FILTER_IPPROTO_UDP;
21020a31c45SAxel Dörfler 				break;
21120a31c45SAxel Dörfler 			// State filter
21220a31c45SAxel Dörfler 			case 'l':
21320a31c45SAxel Dörfler 				filter |= FILTER_STATE_LISTEN;
21420a31c45SAxel Dörfler 				break;
21520a31c45SAxel Dörfler 
216bcdcff76SAxel Dörfler 			case 'h':
217bcdcff76SAxel Dörfler 			default:
2188dfd7ea7SAxel Dörfler 				usage(0);
219bcdcff76SAxel Dörfler 				break;
220bcdcff76SAxel Dörfler 		}
221bcdcff76SAxel Dörfler 	} while (opt != -1);
2228dfd7ea7SAxel Dörfler 
2238dfd7ea7SAxel Dörfler 	bool printProgram = true;
224bcdcff76SAxel Dörfler 		// TODO: add some more program options... :-)
2258dfd7ea7SAxel Dörfler 
226bcdcff76SAxel Dörfler 	printf("Proto  Recv-Q Send-Q Local Address         Foreign Address       "
227bcdcff76SAxel Dörfler 		"State        Program\n");
2288dfd7ea7SAxel Dörfler 
2298dfd7ea7SAxel Dörfler 	uint32 cookie = 0;
2308dfd7ea7SAxel Dörfler 	int family = -1;
2318dfd7ea7SAxel Dörfler 	net_stat stat;
232df50f7a9SIngo Weinhold 	while (_kern_get_next_socket_stat(family, &cookie, &stat) == B_OK) {
23320a31c45SAxel Dörfler 		// Filter families
23420a31c45SAxel Dörfler 		if ((filter & FILTER_FAMILY_MASK) != 0) {
23520a31c45SAxel Dörfler 			if (((filter & FILTER_AF_INET) == 0 || family != AF_INET)
23620a31c45SAxel Dörfler 				&& ((filter & FILTER_AF_INET6) == 0 || family != AF_INET6)
23720a31c45SAxel Dörfler 				&& ((filter & FILTER_AF_UNIX) == 0 || family != AF_UNIX))
23820a31c45SAxel Dörfler 				continue;
23920a31c45SAxel Dörfler 		}
24020a31c45SAxel Dörfler 		// Filter protocols
24120a31c45SAxel Dörfler 		if ((filter & FILTER_PROTOCOL_MASK) != 0) {
24220a31c45SAxel Dörfler 			if (((filter & FILTER_IPPROTO_TCP) == 0
24320a31c45SAxel Dörfler 					|| stat.protocol != IPPROTO_TCP)
24420a31c45SAxel Dörfler 				&& ((filter & FILTER_IPPROTO_UDP) == 0
24520a31c45SAxel Dörfler 					|| stat.protocol != IPPROTO_UDP))
24620a31c45SAxel Dörfler 				continue;
24720a31c45SAxel Dörfler 		}
24820a31c45SAxel Dörfler 		if ((filter & FILTER_STATE_MASK) != 0) {
24920a31c45SAxel Dörfler 			if ((filter & FILTER_STATE_LISTEN) == 0
25020a31c45SAxel Dörfler 				|| strcmp(stat.state, "listen") != 0)
25120a31c45SAxel Dörfler 				continue;
25220a31c45SAxel Dörfler 		}
25320a31c45SAxel Dörfler 
2548dfd7ea7SAxel Dörfler 		protoent* proto = getprotobynumber(stat.protocol);
2558dfd7ea7SAxel Dörfler 		if (proto != NULL)
2568dfd7ea7SAxel Dörfler 			printf("%-6s ", proto->p_name);
2578dfd7ea7SAxel Dörfler 		else
2588dfd7ea7SAxel Dörfler 			printf("%-6d ", stat.protocol);
2598dfd7ea7SAxel Dörfler 
26066a4a428SHugo Santos 		printf("%6lu ", stat.receive_queue_size);
26166a4a428SHugo Santos 		printf("%6lu ", stat.send_queue_size);
26266a4a428SHugo Santos 
2638dfd7ea7SAxel Dörfler 		inet_print_address((sockaddr*)&stat.address);
2648dfd7ea7SAxel Dörfler 		inet_print_address((sockaddr*)&stat.peer);
2658dfd7ea7SAxel Dörfler 		printf("%-12s ", stat.state);
2668dfd7ea7SAxel Dörfler 
2678dfd7ea7SAxel Dörfler 		team_info info;
2688dfd7ea7SAxel Dörfler 		if (printProgram && get_team_info(stat.owner, &info) == B_OK) {
2698dfd7ea7SAxel Dörfler 			// remove arguments
2708dfd7ea7SAxel Dörfler 			char* name = strchr(info.args, ' ');
2718dfd7ea7SAxel Dörfler 			if (name != NULL)
2728dfd7ea7SAxel Dörfler 				name[0] = '\0';
2738dfd7ea7SAxel Dörfler 
2748dfd7ea7SAxel Dörfler 			// remove path name
2758dfd7ea7SAxel Dörfler 			name = strrchr(info.args, '/');
2768dfd7ea7SAxel Dörfler 			if (name != NULL)
2778dfd7ea7SAxel Dörfler 				name++;
2788dfd7ea7SAxel Dörfler 			else
2798dfd7ea7SAxel Dörfler 				name = info.args;
2808dfd7ea7SAxel Dörfler 
281*a2efc1d6SMurai Takashi 			printf("%" B_PRId32 "/%s\n", stat.owner, name);
2828dfd7ea7SAxel Dörfler 		} else
283*a2efc1d6SMurai Takashi 			printf("%" B_PRId32 "\n", stat.owner);
2848dfd7ea7SAxel Dörfler 	}
2858dfd7ea7SAxel Dörfler 
2868dfd7ea7SAxel Dörfler 	return 0;
2878dfd7ea7SAxel Dörfler }
2888dfd7ea7SAxel Dörfler 
289