* 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:
parent
6f9f14ec88
commit
8dfd7ea7bf
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -6,10 +6,9 @@
|
||||
#define NET_STACK_DRIVER_H
|
||||
|
||||
|
||||
#include <OS.h>
|
||||
#include <net_stat.h>
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
|
||||
// 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
|
||||
|
28
headers/private/net/net_stat.h
Normal file
28
headers/private/net/net_stat.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "TCPEndpoint.h"
|
||||
|
||||
#include <net_protocol.h>
|
||||
#include <net_stat.h>
|
||||
|
||||
#include <KernelExport.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
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include <net_protocol.h>
|
||||
#include <net_stack.h>
|
||||
#include <net_stat.h>
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <util/AutoLock.h>
|
||||
@ -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,
|
||||
|
@ -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 ;
|
||||
|
8
src/bin/network/netstat/Jamfile
Normal file
8
src/bin/network/netstat/Jamfile
Normal file
@ -0,0 +1,8 @@
|
||||
SubDir HAIKU_TOP src bin network netstat ;
|
||||
|
||||
UsePrivateHeaders net ;
|
||||
|
||||
BinCommand netstat :
|
||||
netstat.cpp
|
||||
: libnetwork.so
|
||||
;
|
182
src/bin/network/netstat/netstat.cpp
Normal file
182
src/bin/network/netstat/netstat.cpp
Normal 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user