* Implemented a basic infrastructure for a netstat command.

* Started a netstat command.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19560 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2006-12-19 02:06:07 +00:00
parent 6f9f14ec88
commit 8dfd7ea7bf
10 changed files with 372 additions and 25 deletions

View File

@ -11,10 +11,10 @@
// level flags to pass to control() // level flags to pass to control()
#define LEVEL_SET_OPTION 0x10000000 #define LEVEL_SET_OPTION 0x10000000
#define LEVEL_GET_OPTION 0x20000000 #define LEVEL_GET_OPTION 0x20000000
#define LEVEL_DRIVER_IOCTL 0x0f000000 #define LEVEL_DRIVER_IOCTL 0x0f000000
#define LEVEL_MASK 0x0fffffff #define LEVEL_MASK 0x0fffffff
typedef struct net_protocol { typedef struct net_protocol {
struct net_protocol *next; struct net_protocol *next;

View File

@ -68,6 +68,8 @@ struct net_socket_module_info {
status_t (*receive_data)(net_socket *socket, size_t length, uint32 flags, status_t (*receive_data)(net_socket *socket, size_t length, uint32 flags,
net_buffer **_buffer); net_buffer **_buffer);
status_t (*get_next_stat)(uint32 *cookie, int family, struct net_stat *stat);
// connections // connections
status_t (*spawn_pending_socket)(net_socket *parent, net_socket **_socket); status_t (*spawn_pending_socket)(net_socket *parent, net_socket **_socket);
void (*delete_socket)(net_socket *socket); void (*delete_socket)(net_socket *socket);

View File

@ -6,10 +6,9 @@
#define NET_STACK_DRIVER_H #define NET_STACK_DRIVER_H
#include <OS.h> #include <net_stat.h>
#include <sys/select.h> #include <sys/select.h>
#include <sys/socket.h>
// Forward declaration // Forward declaration
@ -26,8 +25,8 @@ enum {
NET_STACK_SOCKET = NET_STACK_IOCTL_BASE, // socket_args * NET_STACK_SOCKET = NET_STACK_IOCTL_BASE, // socket_args *
NET_STACK_GET_COOKIE, // void ** NET_STACK_GET_COOKIE, // void **
NET_STACK_CONTROL_NET_MODULE, // control_net_module_args * NET_STACK_CONTROL_NET_MODULE, // control_net_module_args *
NET_STACK_SYSCTL, // sysctl_args * NET_STACK_GET_NEXT_STAT, // get_next_stat_args *
// ops acting on an existing socket // ops acting on an existing socket
NET_STACK_BIND, // sockaddr_args * NET_STACK_BIND, // sockaddr_args *
NET_STACK_RECVFROM, // struct msghdr * NET_STACK_RECVFROM, // struct msghdr *
@ -43,13 +42,9 @@ enum {
NET_STACK_GETSOCKNAME, // sockaddr_args * NET_STACK_GETSOCKNAME, // sockaddr_args *
NET_STACK_GETPEERNAME, // sockaddr_args * NET_STACK_GETPEERNAME, // sockaddr_args *
NET_STACK_SOCKETPAIR, // socketpair_args * NET_STACK_SOCKETPAIR, // socketpair_args *
// TODO: remove R5 select() emulation
NET_STACK_SELECT, // select_args *
NET_STACK_DESELECT, // select_args *
NET_STACK_NOTIFY_SOCKET_EVENT, // notify_socket_event_args * (userland stack only) NET_STACK_NOTIFY_SOCKET_EVENT, // notify_socket_event_args * (userland stack only)
NET_STACK_IOCTL_MAX NET_STACK_IOCTL_MAX
}; };
@ -89,13 +84,10 @@ struct accept_args { // used by NET_STACK_ACCEPT
socklen_t address_length; socklen_t address_length;
}; };
struct sysctl_args { // used by NET_STACK_SYSCTL struct get_next_stat_args { // used by NET_STACK_GET_NEXT_STAT
int *name; uint32 cookie;
uint namelen; int family;
void *oldp; struct net_stat stat;
size_t *oldlenp;
void *newp;
size_t newlen;
}; };
struct control_net_module_args { // used by NET_STACK_CONTROL_NET_MODULE struct control_net_module_args { // used by NET_STACK_CONTROL_NET_MODULE

View File

@ -0,0 +1,28 @@
/*
* Copyright 2006, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef NET_STAT_H
#define NET_STAT_H
#include <OS.h>
#include <sys/socket.h>
#define NET_STAT_SOCKET 1
#define NET_STAT_PROTOCOL 2
typedef struct net_stat {
int family;
int type;
int protocol;
char state[B_OS_NAME_LENGTH];
team_id owner;
struct sockaddr_storage address;
struct sockaddr_storage peer;
} net_stat;
#endif // NET_STAT_H

View File

@ -262,6 +262,20 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length)
// of the file descriptor to use for the new accepted socket // of the file descriptor to use for the new accepted socket
return user_memcpy(data, cookie, sizeof(void *)); return user_memcpy(data, cookie, sizeof(void *));
case NET_STACK_GET_NEXT_STAT:
{
get_next_stat_args args;
status = check_args(args, data, length);
if (status < B_OK)
return status;
status = sSocket->get_next_stat(&args.cookie, args.family, &args.stat);
if (status < B_OK)
return status;
return user_memcpy(data, &args, sizeof(get_next_stat_args));
}
default: default:
return B_BAD_VALUE; return B_BAD_VALUE;
} }

View File

@ -12,6 +12,7 @@
#include "TCPEndpoint.h" #include "TCPEndpoint.h"
#include <net_protocol.h> #include <net_protocol.h>
#include <net_stat.h>
#include <KernelExport.h> #include <KernelExport.h>
#include <util/list.h> #include <util/list.h>
@ -259,6 +260,43 @@ reply_with_reset(tcp_segment_header &segment, net_buffer *buffer)
} }
static const char *
name_for_state(tcp_state state)
{
switch (state) {
case CLOSED:
return "closed";
case LISTEN:
return "listen";
case SYNCHRONIZE_SENT:
return "syn-sent";
case SYNCHRONIZE_RECEIVED:
return "syn-received";
case ESTABLISHED:
return "established";
// peer closes the connection
case FINISH_RECEIVED:
return "close-wait";
case WAIT_FOR_FINISH_ACKNOWLEDGE:
return "last-ack";
// we close the connection
case FINISH_SENT:
return "fin-wait1";
case FINISH_ACKNOWLEDGED:
return "fin-wait2";
case CLOSING:
return "closing";
case TIME_WAIT:
return "time-wait";
}
return "-";
}
// #pragma mark - protocol API // #pragma mark - protocol API
@ -328,11 +366,29 @@ tcp_accept(net_protocol *protocol, struct net_socket **_acceptedSocket)
status_t status_t
tcp_control(net_protocol *protocol, int level, int option, void *value, tcp_control(net_protocol *_protocol, int level, int option, void *value,
size_t *_length) size_t *_length)
{ {
return protocol->next->module->control(protocol->next, level, option, TCPEndpoint *protocol = (TCPEndpoint *)_protocol;
value, _length);
switch (level & LEVEL_MASK) {
case IPPROTO_TCP:
if (option == NET_STAT_SOCKET) {
net_stat *stat = (net_stat *)value;
strlcpy(stat->state, name_for_state(protocol->State()),
sizeof(stat->state));
return B_OK;
}
break;
case SOL_SOCKET:
break;
default:
return protocol->next->module->control(protocol->next, level, option,
value, _length);
}
return B_BAD_VALUE;
} }

View File

@ -11,6 +11,7 @@
#include <net_protocol.h> #include <net_protocol.h>
#include <net_stack.h> #include <net_stack.h>
#include <net_stat.h>
#include <KernelExport.h> #include <KernelExport.h>
#include <util/AutoLock.h> #include <util/AutoLock.h>
@ -26,6 +27,9 @@
void socket_delete(net_socket *socket); void socket_delete(net_socket *socket);
int socket_bind(net_socket *socket, const struct sockaddr *address, socklen_t addressLength); int socket_bind(net_socket *socket, const struct sockaddr *address, socklen_t addressLength);
struct list sSocketList;
benaphore sSocketLock;
static status_t static status_t
create_socket(int family, int type, int protocol, net_socket **_socket) create_socket(int family, int type, int protocol, net_socket **_socket)
@ -86,6 +90,10 @@ socket_open(int family, int type, int protocol, net_socket **_socket)
return status; return status;
} }
benaphore_lock(&sSocketLock);
list_add_item(&sSocketList, socket);
benaphore_unlock(&sSocketLock);
*_socket = socket; *_socket = socket;
return B_OK; return B_OK;
} }
@ -209,6 +217,45 @@ socket_receive_data(net_socket *socket, size_t length, uint32 flags,
} }
status_t
socket_get_next_stat(uint32 *_cookie, int family, struct net_stat *stat)
{
BenaphoreLocker locker(sSocketLock);
net_socket *socket = NULL;
uint32 cookie = *_cookie;
uint32 count = 0;
while ((socket = (net_socket *)list_get_next_item(&sSocketList, socket)) != NULL) {
if (count == cookie)
break;
if (family == -1 || family == socket->family)
count++;
}
if (socket == NULL)
return B_ENTRY_NOT_FOUND;
*_cookie = count + 1;
stat->family = socket->family;
stat->type = socket->type;
stat->protocol = socket->protocol;
stat->owner = -1;
stat->state[0] = '\0';
memcpy(&stat->address, &socket->address, sizeof(struct sockaddr_storage));
memcpy(&stat->peer, &socket->peer, sizeof(struct sockaddr_storage));
// fill in protocol specific data (if supported by the protocol)
size_t length = sizeof(net_stat);
socket->first_info->control(socket->first_protocol, socket->protocol,
NET_STAT_SOCKET, stat, &length);
return B_OK;
}
// #pragma mark - connections // #pragma mark - connections
@ -252,6 +299,10 @@ socket_delete(net_socket *socket)
if (socket->parent != NULL) if (socket->parent != NULL)
panic("socket still has a parent!"); panic("socket still has a parent!");
benaphore_lock(&sSocketLock);
list_remove_item(&sSocketList, socket);
benaphore_unlock(&sSocketLock);
put_domain_protocols(socket); put_domain_protocols(socket);
benaphore_destroy(&socket->lock); benaphore_destroy(&socket->lock);
delete_select_sync_pool(socket->select_pool); delete_select_sync_pool(socket->select_pool);
@ -272,7 +323,15 @@ socket_dequeue_connected(net_socket *parent, net_socket **_socket)
} }
benaphore_unlock(&parent->lock); benaphore_unlock(&parent->lock);
return socket != NULL ? B_OK : B_ENTRY_NOT_FOUND;
if (socket == NULL)
return B_ENTRY_NOT_FOUND;
benaphore_lock(&sSocketLock);
list_add_item(&sSocketList, socket);
benaphore_unlock(&sSocketLock);
return B_OK;
} }
@ -924,9 +983,12 @@ socket_std_ops(int32 op, ...)
// initialize the main stack if not done so already // initialize the main stack if not done so already
//module_info *module; //module_info *module;
//return get_module(NET_STARTER_MODULE_NAME, &module); //return get_module(NET_STARTER_MODULE_NAME, &module);
list_init_etc(&sSocketList, offsetof(net_socket, link));
return benaphore_init(&sSocketLock, "socket list");
} }
case B_MODULE_UNINIT: case B_MODULE_UNINIT:
//return put_module(NET_STARTER_MODULE_NAME); //return put_module(NET_STARTER_MODULE_NAME);
benaphore_destroy(&sSocketLock);
return B_OK; return B_OK;
default: default:
@ -955,6 +1017,8 @@ net_socket_module_info gNetSocketModule = {
socket_send_data, socket_send_data,
socket_receive_data, socket_receive_data,
socket_get_next_stat,
// connections // connections
socket_spawn_pending, socket_spawn_pending,
socket_delete, socket_delete,

View File

@ -3,6 +3,7 @@ SubDir HAIKU_TOP src bin network ;
SubInclude HAIKU_TOP src bin network arp ; SubInclude HAIKU_TOP src bin network arp ;
SubInclude HAIKU_TOP src bin network ftp ; SubInclude HAIKU_TOP src bin network ftp ;
SubInclude HAIKU_TOP src bin network ifconfig ; SubInclude HAIKU_TOP src bin network ifconfig ;
SubInclude HAIKU_TOP src bin network netstat ;
#SubInclude HAIKU_TOP src bin network pppconfig ; #SubInclude HAIKU_TOP src bin network pppconfig ;
#SubInclude HAIKU_TOP src bin network ppp_up ; #SubInclude HAIKU_TOP src bin network ppp_up ;
SubInclude HAIKU_TOP src bin network ping ; SubInclude HAIKU_TOP src bin network ping ;

View File

@ -0,0 +1,8 @@
SubDir HAIKU_TOP src bin network netstat ;
UsePrivateHeaders net ;
BinCommand netstat :
netstat.cpp
: libnetwork.so
;

View File

@ -0,0 +1,182 @@
/*
* Copyright 2006, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
*/
#include <SupportDefs.h>
#include <net_stack_driver.h>
#include <net_stat.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
extern const char* __progname;
const char* kProgramName = __progname;
struct address_family {
int family;
const char* name;
const char* identifiers[4];
void (*print_address)(sockaddr* address);
};
// AF_INET family
static void inet_print_address(sockaddr* address);
static const address_family kFamilies[] = {
{
AF_INET,
"inet",
{"AF_INET", "inet", "ipv4", NULL},
inet_print_address
},
{ -1, NULL, {NULL}, NULL }
};
static void
inet_print_address(sockaddr* _address)
{
sockaddr_in& address = *(sockaddr_in *)_address;
if (address.sin_family != AF_INET || address.sin_len == 0) {
printf("%-28s", "-");
return;
}
hostent* host = gethostbyaddr((const char*)_address, sizeof(sockaddr_in), AF_INET);
servent* service = getservbyport(ntohs(address.sin_port), NULL);
char buffer[128];
int length = strlcpy(buffer, host != NULL
? host->h_name : inet_ntoa(address.sin_addr), sizeof(buffer));
if (service != NULL)
snprintf(buffer + length, sizeof(buffer) - length, ":%s", service->s_name);
else
snprintf(buffer + length, sizeof(buffer) - length, ":%u", ntohs(address.sin_port));
printf("%-28s", buffer);
}
// #pragma mark -
void
usage(int status)
{
printf("usage: %s\n", kProgramName);
exit(status);
}
status_t
get_next_stat(int stack, uint32& cookie, int family, net_stat& stat)
{
get_next_stat_args args;
args.cookie = cookie;
args.family = family;
if (ioctl(stack, NET_STACK_GET_NEXT_STAT, &args, sizeof(args)) < 0)
return errno;
cookie = args.cookie;
memcpy(&stat, &args.stat, sizeof(net_stat));
return B_OK;
}
bool
get_address_family(const char* argument, int32& familyIndex)
{
for (int32 i = 0; kFamilies[i].family >= 0; i++) {
for (int32 j = 0; kFamilies[i].identifiers[j]; j++) {
if (!strcmp(argument, kFamilies[i].identifiers[j])) {
// found a match
familyIndex = i;
return true;
}
}
}
// defaults to AF_INET
familyIndex = 0;
return false;
}
// #pragma mark -
int
main(int argc, char** argv)
{
if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
usage(0);
int stack = open(NET_STACK_DRIVER_PATH, O_RDWR);
if (stack < 0) {
fprintf(stderr, "%s: The networking stack doesn't seem to be available.\n",
kProgramName);
return -1;
}
bool printProgram = true;
// TODO: add some program options... :-)
printf("Proto Local Address Foreign Address State Program\n");
uint32 cookie = 0;
int family = -1;
net_stat stat;
while (get_next_stat(stack, cookie, family, stat) == B_OK) {
protoent* proto = getprotobynumber(stat.protocol);
if (proto != NULL)
printf("%-6s ", proto->p_name);
else
printf("%-6d ", stat.protocol);
inet_print_address((sockaddr*)&stat.address);
inet_print_address((sockaddr*)&stat.peer);
printf("%-12s ", stat.state);
team_info info;
if (printProgram && get_team_info(stat.owner, &info) == B_OK) {
// remove arguments
char* name = strchr(info.args, ' ');
if (name != NULL)
name[0] = '\0';
// remove path name
name = strrchr(info.args, '/');
if (name != NULL)
name++;
else
name = info.args;
printf("%ld/%s\n", stat.owner, name);
} else
printf("%ld\n", stat.owner);
}
close(stack);
return 0;
}