From 8dfd7ea7bf7714ff094a69f120771e6c6465262e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Tue, 19 Dec 2006 02:06:07 +0000 Subject: [PATCH] * 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 --- headers/private/net/net_protocol.h | 8 +- headers/private/net/net_socket.h | 2 + headers/private/net/net_stack_driver.h | 26 +-- headers/private/net/net_stat.h | 28 +++ .../drivers/network/stack/kernel_stack.cpp | 14 ++ .../kernel/network/protocols/tcp/tcp.cpp | 62 +++++- .../kernel/network/stack/net_socket.cpp | 66 ++++++- src/bin/network/Jamfile | 1 + src/bin/network/netstat/Jamfile | 8 + src/bin/network/netstat/netstat.cpp | 182 ++++++++++++++++++ 10 files changed, 372 insertions(+), 25 deletions(-) create mode 100644 headers/private/net/net_stat.h create mode 100644 src/bin/network/netstat/Jamfile create mode 100644 src/bin/network/netstat/netstat.cpp diff --git a/headers/private/net/net_protocol.h b/headers/private/net/net_protocol.h index 4f50c1f9db..d5012f5b6f 100644 --- a/headers/private/net/net_protocol.h +++ b/headers/private/net/net_protocol.h @@ -11,10 +11,10 @@ // level flags to pass to control() -#define LEVEL_SET_OPTION 0x10000000 -#define LEVEL_GET_OPTION 0x20000000 -#define LEVEL_DRIVER_IOCTL 0x0f000000 -#define LEVEL_MASK 0x0fffffff +#define LEVEL_SET_OPTION 0x10000000 +#define LEVEL_GET_OPTION 0x20000000 +#define LEVEL_DRIVER_IOCTL 0x0f000000 +#define LEVEL_MASK 0x0fffffff typedef struct net_protocol { struct net_protocol *next; diff --git a/headers/private/net/net_socket.h b/headers/private/net/net_socket.h index 314a3eccc5..66920c384b 100644 --- a/headers/private/net/net_socket.h +++ b/headers/private/net/net_socket.h @@ -68,6 +68,8 @@ struct net_socket_module_info { status_t (*receive_data)(net_socket *socket, size_t length, uint32 flags, net_buffer **_buffer); + status_t (*get_next_stat)(uint32 *cookie, int family, struct net_stat *stat); + // connections status_t (*spawn_pending_socket)(net_socket *parent, net_socket **_socket); void (*delete_socket)(net_socket *socket); diff --git a/headers/private/net/net_stack_driver.h b/headers/private/net/net_stack_driver.h index 9a53357b74..f778be1fcb 100644 --- a/headers/private/net/net_stack_driver.h +++ b/headers/private/net/net_stack_driver.h @@ -6,10 +6,9 @@ #define NET_STACK_DRIVER_H -#include +#include #include -#include // Forward declaration @@ -26,8 +25,8 @@ enum { NET_STACK_SOCKET = NET_STACK_IOCTL_BASE, // socket_args * NET_STACK_GET_COOKIE, // void ** 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 NET_STACK_BIND, // sockaddr_args * NET_STACK_RECVFROM, // struct msghdr * @@ -43,13 +42,9 @@ enum { NET_STACK_GETSOCKNAME, // sockaddr_args * NET_STACK_GETPEERNAME, // sockaddr_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_IOCTL_MAX }; @@ -89,13 +84,10 @@ struct accept_args { // used by NET_STACK_ACCEPT socklen_t address_length; }; -struct sysctl_args { // used by NET_STACK_SYSCTL - int *name; - uint namelen; - void *oldp; - size_t *oldlenp; - void *newp; - size_t newlen; +struct get_next_stat_args { // used by NET_STACK_GET_NEXT_STAT + uint32 cookie; + int family; + struct net_stat stat; }; struct control_net_module_args { // used by NET_STACK_CONTROL_NET_MODULE diff --git a/headers/private/net/net_stat.h b/headers/private/net/net_stat.h new file mode 100644 index 0000000000..c3af64054f --- /dev/null +++ b/headers/private/net/net_stat.h @@ -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 + +#include + + +#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 diff --git a/src/add-ons/kernel/drivers/network/stack/kernel_stack.cpp b/src/add-ons/kernel/drivers/network/stack/kernel_stack.cpp index eedc54399b..51d3c07ac9 100644 --- a/src/add-ons/kernel/drivers/network/stack/kernel_stack.cpp +++ b/src/add-ons/kernel/drivers/network/stack/kernel_stack.cpp @@ -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 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: return B_BAD_VALUE; } diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp index 1ed67734a9..858f767bdf 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp @@ -12,6 +12,7 @@ #include "TCPEndpoint.h" #include +#include #include #include @@ -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 @@ -328,11 +366,29 @@ tcp_accept(net_protocol *protocol, struct net_socket **_acceptedSocket) 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) { - return protocol->next->module->control(protocol->next, level, option, - value, _length); + TCPEndpoint *protocol = (TCPEndpoint *)_protocol; + + 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; } diff --git a/src/add-ons/kernel/network/stack/net_socket.cpp b/src/add-ons/kernel/network/stack/net_socket.cpp index 4a865dfa33..0b206612c3 100644 --- a/src/add-ons/kernel/network/stack/net_socket.cpp +++ b/src/add-ons/kernel/network/stack/net_socket.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -26,6 +27,9 @@ void socket_delete(net_socket *socket); int socket_bind(net_socket *socket, const struct sockaddr *address, socklen_t addressLength); +struct list sSocketList; +benaphore sSocketLock; + static status_t 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; } + benaphore_lock(&sSocketLock); + list_add_item(&sSocketList, socket); + benaphore_unlock(&sSocketLock); + *_socket = socket; 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 @@ -252,6 +299,10 @@ socket_delete(net_socket *socket) if (socket->parent != NULL) panic("socket still has a parent!"); + benaphore_lock(&sSocketLock); + list_remove_item(&sSocketList, socket); + benaphore_unlock(&sSocketLock); + put_domain_protocols(socket); benaphore_destroy(&socket->lock); delete_select_sync_pool(socket->select_pool); @@ -272,7 +323,15 @@ socket_dequeue_connected(net_socket *parent, net_socket **_socket) } 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 //module_info *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: //return put_module(NET_STARTER_MODULE_NAME); + benaphore_destroy(&sSocketLock); return B_OK; default: @@ -955,6 +1017,8 @@ net_socket_module_info gNetSocketModule = { socket_send_data, socket_receive_data, + socket_get_next_stat, + // connections socket_spawn_pending, socket_delete, diff --git a/src/bin/network/Jamfile b/src/bin/network/Jamfile index 1d70b7f16e..80238bb7e8 100644 --- a/src/bin/network/Jamfile +++ b/src/bin/network/Jamfile @@ -3,6 +3,7 @@ SubDir HAIKU_TOP src bin network ; SubInclude HAIKU_TOP src bin network arp ; SubInclude HAIKU_TOP src bin network ftp ; 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 ppp_up ; SubInclude HAIKU_TOP src bin network ping ; diff --git a/src/bin/network/netstat/Jamfile b/src/bin/network/netstat/Jamfile new file mode 100644 index 0000000000..75690893a6 --- /dev/null +++ b/src/bin/network/netstat/Jamfile @@ -0,0 +1,8 @@ +SubDir HAIKU_TOP src bin network netstat ; + +UsePrivateHeaders net ; + +BinCommand netstat : + netstat.cpp + : libnetwork.so +; diff --git a/src/bin/network/netstat/netstat.cpp b/src/bin/network/netstat/netstat.cpp new file mode 100644 index 0000000000..2945fb96f1 --- /dev/null +++ b/src/bin/network/netstat/netstat.cpp @@ -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 + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +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; +} +