crash/net.c
Dave Anderson a94b86e9eb Fix for the "net" command when the network device listing has an
unusually large number of IP addresses.  In that case, without the
patch, the command may generate a segmentation violation.
(k-hagio@ab.jp.nec.com)
2017-11-28 12:12:50 -05:00

1688 lines
46 KiB
C

/* net.c - core analysis suite
*
* Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
* Copyright (C) 2002-2016 David Anderson
* Copyright (C) 2002-2016 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "defs.h"
#include <netinet/in.h>
#include <netdb.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
/*
* Cache values we need that can change based on OS version, or any other
* variables static to this file. These are setup in net_init(). Dump
* the table during runtime via "help -n".
*/
struct net_table {
ulong flags;
char *netdevice; /* name of net device */
char *dev_name_t; /* readmem ID's */
char *dev_type_t;
char *dev_addr_t;
long dev_name;
long dev_next;
long dev_type;
long dev_addr_len;
long dev_ip_ptr;
long in_device_ifa_list;
long in_ifaddr_ifa_next;
long in_ifaddr_ifa_address;
int net_device_name_index;
} net_table = { 0 };
struct net_table *net = &net_table;
#define NETDEV_INIT (0x1)
#define STRUCT_DEVICE (0x2)
#define STRUCT_NET_DEVICE (0x4)
#define SOCK_V1 (0x8)
#define SOCK_V2 (0x10)
#define NO_INET_SOCK (0x20)
#define DEV_NAME_MAX 100
struct devinfo {
char dev_name[DEV_NAME_MAX];
unsigned char dev_addr_len;
short dev_type;
};
#define BYTES_IP_ADDR 15 /* bytes to print IP addr (xxx.xxx.xxx.xxx) */
#define BYTES_PORT_NUM 5 /* bytes to print port number */
/* bytes needed for <ip address>:<port> notation */
#define BYTES_IP_TUPLE (BYTES_IP_ADDR + BYTES_PORT_NUM + 1)
static void show_net_devices(ulong);
static void show_net_devices_v2(ulong);
static void show_net_devices_v3(ulong);
static void print_neighbour_q(ulong, int);
static void get_netdev_info(ulong, struct devinfo *);
static void get_device_name(ulong, char *);
static long get_device_address(ulong, char **, long);
static void get_sock_info(ulong, char *);
static void dump_arp(void);
static void arp_state_to_flags(unsigned char);
static void dump_ether_hw(unsigned char *, int);
static void dump_sockets(ulong, struct reference *);
static int sym_socket_dump(ulong, int, int, ulong, struct reference *);
static void dump_hw_addr(unsigned char *, int);
static char *dump_in6_addr_port(uint16_t *, uint16_t, char *, int *);
#define MK_TYPE_T(f,s,m) \
do { \
(f) = malloc(strlen(s) + strlen(m) + 2); \
if ((f) == NULL) { \
error(WARNING, "malloc fail for type %s.%s", (s), (m)); \
} else { \
sprintf((f), "%s %s", (s), (m)); \
} \
} while(0)
void
net_init(void)
{
/*
* Note the order of the following checks. The device struct was
* renamed to net_device in 2.3, but there may be another struct
* called 'device' so we check for the new one first.
*/
STRUCT_SIZE_INIT(net_device, "net_device");
if (VALID_STRUCT(net_device)) {
net->netdevice = "net_device";
net->dev_next = MEMBER_OFFSET_INIT(net_device_next,
"net_device", "next");
net->dev_name = MEMBER_OFFSET_INIT(net_device_name,
"net_device", "name");
net->dev_type = MEMBER_OFFSET_INIT(net_device_type,
"net_device", "type");
net->dev_addr_len = MEMBER_OFFSET_INIT(net_device_addr_len,
"net_device", "addr_len");
net->dev_ip_ptr = MEMBER_OFFSET_INIT(net_device_ip_ptr,
"net_device", "ip_ptr");
MEMBER_OFFSET_INIT(net_device_dev_list, "net_device", "dev_list");
MEMBER_OFFSET_INIT(net_dev_base_head, "net", "dev_base_head");
ARRAY_LENGTH_INIT(net->net_device_name_index,
net_device_name, "net_device.name", NULL, sizeof(char));
net->flags |= (NETDEV_INIT|STRUCT_NET_DEVICE);
} else {
STRUCT_SIZE_INIT(device, "device");
if (VALID_STRUCT(device)) {
net->netdevice = "device";
net->dev_next = MEMBER_OFFSET_INIT(device_next,
"device", "next");
net->dev_name = MEMBER_OFFSET_INIT(device_name,
"device", "name");
net->dev_type = MEMBER_OFFSET_INIT(device_type,
"device", "type");
net->dev_ip_ptr = MEMBER_OFFSET_INIT(device_ip_ptr,
"device", "ip_ptr");
net->dev_addr_len = MEMBER_OFFSET_INIT(device_addr_len,
"device", "addr_len");
net->flags |= (NETDEV_INIT|STRUCT_DEVICE);
} else
error(WARNING,
"net_init: unknown device type for net device");
}
if (VALID_MEMBER(task_struct_nsproxy))
MEMBER_OFFSET_INIT(nsproxy_net_ns, "nsproxy", "net_ns");
if (net->flags & NETDEV_INIT) {
MK_TYPE_T(net->dev_name_t, net->netdevice, "name");
MK_TYPE_T(net->dev_type_t, net->netdevice, "type");
MK_TYPE_T(net->dev_addr_t, net->netdevice, "addr_len");
MEMBER_OFFSET_INIT(socket_sk, "socket", "sk");
MEMBER_OFFSET_INIT(neighbour_next, "neighbour", "next");
MEMBER_OFFSET_INIT(neighbour_primary_key,
"neighbour", "primary_key");
MEMBER_OFFSET_INIT(neighbour_ha, "neighbour", "ha");
MEMBER_OFFSET_INIT(neighbour_dev, "neighbour", "dev");
MEMBER_OFFSET_INIT(neighbour_nud_state,
"neighbour", "nud_state");
MEMBER_OFFSET_INIT(neigh_table_nht_ptr, "neigh_table", "nht");
if (VALID_MEMBER(neigh_table_nht_ptr)) {
MEMBER_OFFSET_INIT(neigh_table_hash_mask,
"neigh_hash_table", "hash_mask");
MEMBER_OFFSET_INIT(neigh_table_hash_shift,
"neigh_hash_table", "hash_shift");
MEMBER_OFFSET_INIT(neigh_table_hash_buckets,
"neigh_hash_table", "hash_buckets");
} else {
MEMBER_OFFSET_INIT(neigh_table_hash_buckets,
"neigh_table", "hash_buckets");
MEMBER_OFFSET_INIT(neigh_table_hash_mask,
"neigh_table", "hash_mask");
}
MEMBER_OFFSET_INIT(neigh_table_key_len,
"neigh_table", "key_len");
MEMBER_OFFSET_INIT(in_device_ifa_list,
"in_device", "ifa_list");
MEMBER_OFFSET_INIT(in_ifaddr_ifa_next,
"in_ifaddr", "ifa_next");
MEMBER_OFFSET_INIT(in_ifaddr_ifa_address,
"in_ifaddr", "ifa_address");
STRUCT_SIZE_INIT(sock, "sock");
MEMBER_OFFSET_INIT(sock_family, "sock", "family");
if (VALID_MEMBER(sock_family)) {
MEMBER_OFFSET_INIT(sock_daddr, "sock", "daddr");
MEMBER_OFFSET_INIT(sock_rcv_saddr, "sock", "rcv_saddr");
MEMBER_OFFSET_INIT(sock_dport, "sock", "dport");
MEMBER_OFFSET_INIT(sock_sport, "sock", "sport");
MEMBER_OFFSET_INIT(sock_num, "sock", "num");
MEMBER_OFFSET_INIT(sock_type, "sock", "type");
net->flags |= SOCK_V1;
} else {
/*
* struct sock {
* struct sock_common __sk_common;
* #define sk_family __sk_common.skc_family
* ...
*/
MEMBER_OFFSET_INIT(sock_common_skc_family,
"sock_common", "skc_family");
MEMBER_OFFSET_INIT(sock_sk_type, "sock", "sk_type");
/*
* struct inet_sock {
* struct sock sk;
* struct ipv6_pinfo *pinet6;
* struct inet_opt inet;
* };
*/
STRUCT_SIZE_INIT(inet_sock, "inet_sock");
STRUCT_SIZE_INIT(socket, "socket");
if (STRUCT_EXISTS("inet_opt")) {
MEMBER_OFFSET_INIT(inet_sock_inet, "inet_sock", "inet");
MEMBER_OFFSET_INIT(inet_opt_daddr, "inet_opt", "daddr");
MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "inet_opt", "rcv_saddr");
MEMBER_OFFSET_INIT(inet_opt_dport, "inet_opt", "dport");
MEMBER_OFFSET_INIT(inet_opt_sport, "inet_opt", "sport");
MEMBER_OFFSET_INIT(inet_opt_num, "inet_opt", "num");
} else { /* inet_opt moved to inet_sock */
ASSIGN_OFFSET(inet_sock_inet) = 0;
if (MEMBER_EXISTS("inet_sock", "daddr")) {
MEMBER_OFFSET_INIT(inet_opt_daddr, "inet_sock", "daddr");
MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "inet_sock", "rcv_saddr");
MEMBER_OFFSET_INIT(inet_opt_dport, "inet_sock", "dport");
MEMBER_OFFSET_INIT(inet_opt_sport, "inet_sock", "sport");
MEMBER_OFFSET_INIT(inet_opt_num, "inet_sock", "num");
} else if (MEMBER_EXISTS("inet_sock", "inet_daddr")) {
MEMBER_OFFSET_INIT(inet_opt_daddr, "inet_sock", "inet_daddr");
MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "inet_sock", "inet_rcv_saddr");
MEMBER_OFFSET_INIT(inet_opt_dport, "inet_sock", "inet_dport");
MEMBER_OFFSET_INIT(inet_opt_sport, "inet_sock", "inet_sport");
MEMBER_OFFSET_INIT(inet_opt_num, "inet_sock", "inet_num");
} else if ((MEMBER_OFFSET("inet_sock", "sk") == 0) &&
(MEMBER_OFFSET("sock", "__sk_common") == 0)) {
MEMBER_OFFSET_INIT(inet_opt_daddr, "sock_common", "skc_daddr");
if (INVALID_MEMBER(inet_opt_daddr))
ANON_MEMBER_OFFSET_INIT(inet_opt_daddr, "sock_common",
"skc_daddr");
MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "sock_common", "skc_rcv_saddr");
if (INVALID_MEMBER(inet_opt_rcv_saddr))
ANON_MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "sock_common",
"skc_rcv_saddr");
MEMBER_OFFSET_INIT(inet_opt_dport, "inet_sock", "inet_dport");
if (INVALID_MEMBER(inet_opt_dport)) {
MEMBER_OFFSET_INIT(inet_opt_dport, "sock_common",
"skc_dport");
if (INVALID_MEMBER(inet_opt_dport))
ANON_MEMBER_OFFSET_INIT(inet_opt_dport, "sock_common",
"skc_dport");
}
MEMBER_OFFSET_INIT(inet_opt_sport, "inet_sock", "inet_sport");
MEMBER_OFFSET_INIT(inet_opt_num, "inet_sock", "inet_num");
if (INVALID_MEMBER(inet_opt_num)) {
MEMBER_OFFSET_INIT(inet_opt_num, "sock_common", "skc_num");
if (INVALID_MEMBER(inet_opt_num))
ANON_MEMBER_OFFSET_INIT(inet_opt_num, "sock_common",
"skc_num");
}
}
}
if (VALID_STRUCT(inet_sock) &&
INVALID_MEMBER(inet_sock_inet)) {
/*
* gdb can't seem to figure out the inet_sock
* in later 2.6 kernels, returning this:
*
* struct inet_sock {
* <no data fields>
* }
*
* It does know the struct size, so kludge it
* to subtract the size of the inet_opt struct
* from the size of the containing inet_sock.
*/
net->flags |= NO_INET_SOCK;
ASSIGN_OFFSET(inet_sock_inet) =
SIZE(inet_sock) - STRUCT_SIZE("inet_opt");
}
/*
* If necessary, set inet_sock size and inet_sock_inet offset,
* accounting for the configuration-dependent, intervening,
* struct ipv6_pinfo pointer located in between the sock and
* inet_opt members of the inet_sock.
*/
if (!VALID_STRUCT(inet_sock))
{
if (symbol_exists("tcpv6_protocol") &&
symbol_exists("udpv6_protocol")) {
ASSIGN_SIZE(inet_sock) = SIZE(sock) +
sizeof(void *) + STRUCT_SIZE("inet_opt");
ASSIGN_OFFSET(inet_sock_inet) = SIZE(sock) +
sizeof(void *);
} else {
ASSIGN_SIZE(inet_sock) = SIZE(sock) +
STRUCT_SIZE("inet_opt");
ASSIGN_OFFSET(inet_sock_inet) = SIZE(sock);
}
}
MEMBER_OFFSET_INIT(ipv6_pinfo_rcv_saddr, "ipv6_pinfo", "rcv_saddr");
MEMBER_OFFSET_INIT(ipv6_pinfo_daddr, "ipv6_pinfo", "daddr");
STRUCT_SIZE_INIT(in6_addr, "in6_addr");
MEMBER_OFFSET_INIT(socket_alloc_vfs_inode, "socket_alloc", "vfs_inode");
net->flags |= SOCK_V2;
}
}
}
/*
* The net command...
*/
#define NETOPTS "N:asSR:xdn"
#define s_FLAG FOREACH_s_FLAG
#define S_FLAG FOREACH_S_FLAG
#define x_FLAG FOREACH_x_FLAG
#define d_FLAG FOREACH_d_FLAG
#define NET_REF_FOUND (0x1)
#define NET_REF_HEXNUM (0x2)
#define NET_REF_DECNUM (0x4)
#define NET_TASK_HEADER_PRINTED (0x8)
#define NET_SOCK_HEADER_PRINTED (0x10)
#define NET_REF_FOUND_ITEM (0x20)
#define NET_REFERENCE_CHECK(X) (X)
#define NET_REFERENCE_FOUND(X) ((X) && ((X)->cmdflags & NET_REF_FOUND))
void
cmd_net(void)
{
int c;
ulong sflag, nflag, aflag;
ulong value;
ulong task;
struct task_context *tc = NULL;
struct in_addr in_addr;
struct reference reference, *ref;
if (!(net->flags & NETDEV_INIT))
error(FATAL, "net subsystem not initialized!");
ref = NULL;
sflag = nflag = aflag = 0;
task = pid_to_task(0);
while ((c = getopt(argcnt, args, NETOPTS)) != EOF) {
switch (c) {
case 'R':
if (ref)
error(INFO, "only one -R option allowed\n");
else {
ref = &reference;
BZERO(ref, sizeof(struct reference));
ref->str = optarg;
}
break;
case 'a':
dump_arp();
aflag++;
break;
case 'N':
value = stol(optarg, FAULT_ON_ERROR, NULL);
in_addr.s_addr = (in_addr_t)value;
fprintf(fp, "%s\n", inet_ntoa(in_addr));
return;
case 's':
if (sflag & S_FLAG)
error(INFO,
"only one -s or -S option allowed\n");
else
sflag |= s_FLAG;
break;
case 'S':
if (sflag & s_FLAG)
error(INFO,
"only one -s or -S option allowed\n");
else
sflag |= S_FLAG;
break;
case 'x':
if (sflag & d_FLAG)
error(FATAL,
"-d and -x are mutually exclusive\n");
sflag |= x_FLAG;
break;
case 'd':
if (sflag & x_FLAG)
error(FATAL,
"-d and -x are mutually exclusive\n");
sflag |= d_FLAG;
break;
case 'n':
nflag = 1;
task = CURRENT_TASK();
if (args[optind]) {
switch (str_to_context(args[optind],
&value, &tc)) {
case STR_PID:
case STR_TASK:
task = tc->task;
}
}
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (sflag & (s_FLAG|S_FLAG))
dump_sockets(sflag, ref);
else {
if ((argcnt == 1) || nflag)
show_net_devices(task);
else if (!aflag)
cmd_usage(pc->curcmd, SYNOPSIS);
}
}
/*
* Just display the address and name of each net device.
*/
static void
show_net_devices(ulong task)
{
ulong next;
long flen;
char *buf;
long buflen = BUFSIZE;
if (symbol_exists("dev_base_head")) {
show_net_devices_v2(task);
return;
} else if (symbol_exists("init_net")) {
show_net_devices_v3(task);
return;
}
if (!symbol_exists("dev_base"))
error(FATAL, "dev_base, dev_base_head or init_net do not exist!\n");
get_symbol_data("dev_base", sizeof(void *), &next);
if (!net->netdevice || !next)
return;
buf = GETBUF(buflen);
flen = MAX(VADDR_PRLEN, strlen(net->netdevice));
fprintf(fp, "%s NAME IP ADDRESS(ES)\n",
mkstring(upper_case(net->netdevice, buf),
flen, CENTER|LJUST, NULL));
do {
fprintf(fp, "%s ",
mkstring(buf, flen, CENTER|RJUST|LONG_HEX, MKSTR(next)));
get_device_name(next, buf);
fprintf(fp, "%-6s ", buf);
buflen = get_device_address(next, &buf, buflen);
fprintf(fp, "%s\n", buf);
readmem(next+net->dev_next, KVADDR, &next,
sizeof(void *), "(net_)device.next", FAULT_ON_ERROR);
} while (next);
FREEBUF(buf);
}
static void
show_net_devices_v2(ulong task)
{
struct list_data list_data, *ld;
char *net_device_buf;
char *buf;
long buflen = BUFSIZE;
int ndevcnt, i;
long flen;
if (!net->netdevice) /* initialized in net_init() */
return;
buf = GETBUF(buflen);
flen = MAX(VADDR_PRLEN, strlen(net->netdevice));
fprintf(fp, "%s NAME IP ADDRESS(ES)\n",
mkstring(upper_case(net->netdevice, buf),
flen, CENTER|LJUST, NULL));
net_device_buf = GETBUF(SIZE(net_device));
ld = &list_data;
BZERO(ld, sizeof(struct list_data));
ld->flags |= LIST_ALLOCATE;
get_symbol_data("dev_base_head", sizeof(void *), &ld->start);
ld->end = symbol_value("dev_base_head");
ld->list_head_offset = OFFSET(net_device_dev_list);
ndevcnt = do_list(ld);
for (i = 0; i < ndevcnt; ++i) {
readmem(ld->list_ptr[i], KVADDR, net_device_buf,
SIZE(net_device), "net_device buffer",
FAULT_ON_ERROR);
fprintf(fp, "%s ",
mkstring(buf, flen, CENTER|RJUST|LONG_HEX,
MKSTR(ld->list_ptr[i])));
get_device_name(ld->list_ptr[i], buf);
fprintf(fp, "%-6s ", buf);
buflen = get_device_address(ld->list_ptr[i], &buf, buflen);
fprintf(fp, "%s\n", buf);
}
FREEBUF(ld->list_ptr);
FREEBUF(net_device_buf);
FREEBUF(buf);
}
static void
show_net_devices_v3(ulong task)
{
ulong nsproxy_p, net_ns_p;
struct list_data list_data, *ld;
char *net_device_buf;
char *buf;
long buflen = BUFSIZE;
int ndevcnt, i;
long flen;
if (!net->netdevice) /* initialized in net_init() */
return;
buf = GETBUF(buflen);
flen = MAX(VADDR_PRLEN, strlen(net->netdevice));
fprintf(fp, "%s NAME IP ADDRESS(ES)\n",
mkstring(upper_case(net->netdevice, buf),
flen, CENTER|LJUST, NULL));
net_device_buf = GETBUF(SIZE(net_device));
ld = &list_data;
BZERO(ld, sizeof(struct list_data));
ld->flags |= LIST_ALLOCATE;
if (VALID_MEMBER(nsproxy_net_ns)) {
readmem(task + OFFSET(task_struct_nsproxy), KVADDR, &nsproxy_p,
sizeof(ulong), "task_struct.nsproxy", FAULT_ON_ERROR);
if (!readmem(nsproxy_p + OFFSET(nsproxy_net_ns), KVADDR, &net_ns_p,
sizeof(ulong), "nsproxy.net_ns", RETURN_ON_ERROR|QUIET))
error(FATAL, "cannot determine net_namespace location!\n");
} else
net_ns_p = symbol_value("init_net");
ld->start = ld->end = net_ns_p + OFFSET(net_dev_base_head);
ld->list_head_offset = OFFSET(net_device_dev_list);
ndevcnt = do_list(ld);
/*
* Skip the first entry (init_net).
*/
for (i = 1; i < ndevcnt; ++i) {
readmem(ld->list_ptr[i], KVADDR, net_device_buf,
SIZE(net_device), "net_device buffer",
FAULT_ON_ERROR);
fprintf(fp, "%s ",
mkstring(buf, flen, CENTER|RJUST|LONG_HEX,
MKSTR(ld->list_ptr[i])));
get_device_name(ld->list_ptr[i], buf);
fprintf(fp, "%-6s ", buf);
buflen = get_device_address(ld->list_ptr[i], &buf, buflen);
fprintf(fp, "%s\n", buf);
}
FREEBUF(ld->list_ptr);
FREEBUF(net_device_buf);
FREEBUF(buf);
}
/*
* Perform the actual work of dumping the ARP table...
*/
#define ARP_HEADING \
"NEIGHBOUR IP ADDRESS HW TYPE HW ADDRESS DEVICE STATE"
static void
dump_arp(void)
{
ulong arp_tbl; /* address of arp_tbl */
ulong *hash_buckets;
ulong hash;
long hash_bytes;
int nhash_buckets = 0;
int key_len;
int i;
int header_printed = 0;
int hash_mask = 0;
ulong nht;
if (!symbol_exists("arp_tbl"))
error(FATAL, "arp_tbl does not exist in this kernel\n");
arp_tbl = symbol_value("arp_tbl");
/*
* NOTE: 2.6.8 -> 2.6.9 neigh_table struct changed from:
*
* struct neighbour *hash_buckets[32];
* to
* struct neighbour **hash_buckets;
*
* Use 'hash_mask' as indicator to decide if we're dealing
* with an array or a pointer.
*
* Around 2.6.37 neigh_hash_table struct has been introduced
* and pointer to it has been added to neigh_table.
*/
if (VALID_MEMBER(neigh_table_nht_ptr)) {
readmem(arp_tbl + OFFSET(neigh_table_nht_ptr),
KVADDR, &nht, sizeof(nht),
"neigh_table nht", FAULT_ON_ERROR);
/* NB! Re-use of offsets like neigh_table_hash_mask
* with neigh_hash_table structure */
if (VALID_MEMBER(neigh_table_hash_mask)) {
readmem(nht + OFFSET(neigh_table_hash_mask),
KVADDR, &hash_mask, sizeof(hash_mask),
"neigh_hash_table hash_mask", FAULT_ON_ERROR);
nhash_buckets = hash_mask + 1;
} else if (VALID_MEMBER(neigh_table_hash_shift)) {
readmem(nht + OFFSET(neigh_table_hash_shift),
KVADDR, &hash_mask, sizeof(hash_mask),
"neigh_hash_table hash_shift", FAULT_ON_ERROR);
nhash_buckets = 1U << hash_mask;
}
} else if (VALID_MEMBER(neigh_table_hash_mask)) {
readmem(arp_tbl + OFFSET(neigh_table_hash_mask),
KVADDR, &hash_mask, sizeof(hash_mask),
"neigh_table hash_mask", FAULT_ON_ERROR);
nhash_buckets = hash_mask + 1;
} else
nhash_buckets = (i = ARRAY_LENGTH(neigh_table_hash_buckets)) ?
i : get_array_length("neigh_table.hash_buckets",
NULL, sizeof(void *));
if (nhash_buckets == 0) {
option_not_supported('a');
return;
}
hash_bytes = nhash_buckets * sizeof(*hash_buckets);
hash_buckets = (ulong *)GETBUF(hash_bytes);
readmem(arp_tbl + OFFSET(neigh_table_key_len),
KVADDR, &key_len, sizeof(key_len),
"neigh_table key_len", FAULT_ON_ERROR);
if (VALID_MEMBER(neigh_table_nht_ptr)) {
readmem(nht + OFFSET(neigh_table_hash_buckets),
KVADDR, &hash, sizeof(hash),
"neigh_hash_table hash_buckets ptr", FAULT_ON_ERROR);
readmem(hash, KVADDR, hash_buckets, hash_bytes,
"neigh_hash_table hash_buckets", FAULT_ON_ERROR);
} else if (hash_mask) {
readmem(arp_tbl + OFFSET(neigh_table_hash_buckets),
KVADDR, &hash, sizeof(hash),
"neigh_table hash_buckets pointer", FAULT_ON_ERROR);
readmem(hash,
KVADDR, hash_buckets, hash_bytes,
"neigh_table hash_buckets", FAULT_ON_ERROR);
} else
readmem(arp_tbl + OFFSET(neigh_table_hash_buckets),
KVADDR, hash_buckets, hash_bytes,
"neigh_table hash_buckets", FAULT_ON_ERROR);
for (i = 0; i < nhash_buckets; i++) {
if (hash_buckets[i] != (ulong)NULL) {
if (!header_printed) {
fprintf(fp, "%s\n", ARP_HEADING);
header_printed = 1;
}
print_neighbour_q(hash_buckets[i], key_len);
}
}
fflush(fp);
FREEBUF(hash_buckets);
}
/*
* Dump out the relevant information of a neighbour structure for the
* ARP table.
*/
static void
print_neighbour_q(ulong addr, int key_len)
{
int i;
ulong dev; /* dev address of this struct */
unsigned char *ha_buf; /* buffer for hardware address */
uint ha_size; /* size of HW address */
uint ipaddr; /* hold ipaddr (aka primary_key) */
struct devinfo dinfo;
unsigned char state; /* state of ARP entry */
struct in_addr in_addr;
ha_size = (i = ARRAY_LENGTH(neighbour_ha)) ?
i : get_array_length("neighbour.ha", NULL, sizeof(char));
ha_buf = (unsigned char *)GETBUF(ha_size);
while (addr) {
readmem(addr + OFFSET(neighbour_primary_key), KVADDR,
&ipaddr, sizeof(ipaddr), "neighbour primary_key",
FAULT_ON_ERROR);
readmem(addr + OFFSET(neighbour_ha), KVADDR, ha_buf, ha_size,
"neighbour ha", FAULT_ON_ERROR);
readmem(addr + OFFSET(neighbour_dev), KVADDR, &dev, sizeof(dev),
"neighbour dev", FAULT_ON_ERROR);
get_netdev_info(dev, &dinfo);
readmem(addr + OFFSET(neighbour_nud_state), KVADDR,
&state, sizeof(state), "neighbour nud_state",
FAULT_ON_ERROR);
in_addr.s_addr = ipaddr;
fprintf(fp, "%-16lx %-16s", addr, inet_ntoa(in_addr));
switch (dinfo.dev_type) {
case ARPHRD_ETHER:
/*
* Use the actual HW address size in the device struct
* rather than the max size of the array (as was done
* during the readmem() call above....
*/
fprintf(fp, "%-10s ", "ETHER");
dump_ether_hw(ha_buf, dinfo.dev_addr_len);
break;
case ARPHRD_NETROM:
fprintf(fp, "%-10s ", "NETROM");
dump_hw_addr(ha_buf, dinfo.dev_addr_len);
break;
case ARPHRD_EETHER:
fprintf(fp, "%-10s ", "EETHER");
dump_hw_addr(ha_buf, dinfo.dev_addr_len);
break;
case ARPHRD_AX25:
fprintf(fp, "%-10s ", "AX25");
dump_hw_addr(ha_buf, dinfo.dev_addr_len);
break;
case ARPHRD_PRONET:
fprintf(fp, "%-10s ", "PRONET");
dump_hw_addr(ha_buf, dinfo.dev_addr_len);
break;
case ARPHRD_CHAOS:
fprintf(fp, "%-10s ", "CHAOS");
dump_hw_addr(ha_buf, dinfo.dev_addr_len);
break;
case ARPHRD_IEEE802:
fprintf(fp, "%-10s ", "IEEE802");
dump_hw_addr(ha_buf, dinfo.dev_addr_len);
break;
case ARPHRD_ARCNET:
fprintf(fp, "%-10s ", "ARCNET");
dump_hw_addr(ha_buf, dinfo.dev_addr_len);
break;
case ARPHRD_APPLETLK:
fprintf(fp, "%-10s ", "APPLETLK");
dump_hw_addr(ha_buf, dinfo.dev_addr_len);
break;
case ARPHRD_DLCI:
fprintf(fp, "%-10s ", "DLCI");
dump_hw_addr(ha_buf, dinfo.dev_addr_len);
break;
case ARPHRD_METRICOM:
fprintf(fp, "%-10s ", "METRICOM");
dump_hw_addr(ha_buf, dinfo.dev_addr_len);
break;
default:
fprintf(fp, "%-10s ", "UNKNOWN");
dump_hw_addr(ha_buf, dinfo.dev_addr_len);
break;
}
fprintf(fp, " %-6s ", dinfo.dev_name);
arp_state_to_flags(state);
readmem(addr + OFFSET(neighbour_next), KVADDR,
&addr, sizeof(addr), "neighbour next", FAULT_ON_ERROR);
}
FREEBUF(ha_buf);
}
/*
* read netdevice info....
*/
static void
get_netdev_info(ulong devaddr, struct devinfo *dip)
{
short dev_type;
get_device_name(devaddr, dip->dev_name);
readmem(devaddr + net->dev_type, KVADDR,
&dev_type, sizeof(dev_type), net->dev_type_t, FAULT_ON_ERROR);
dip->dev_type = dev_type;
readmem(devaddr + net->dev_addr_len, KVADDR,
&dip->dev_addr_len, sizeof(dip->dev_addr_len), net->dev_addr_t,
FAULT_ON_ERROR);
}
/*
* Get the device name.
*/
static void
get_device_name(ulong devaddr, char *buf)
{
ulong name_addr;
switch (net->flags & (STRUCT_DEVICE|STRUCT_NET_DEVICE))
{
case STRUCT_NET_DEVICE:
if (net->net_device_name_index > 0) {
readmem(devaddr + net->dev_name, KVADDR,
buf, net->net_device_name_index,
net->dev_name_t, FAULT_ON_ERROR);
return;
}
/* fallthrough */
case STRUCT_DEVICE:
readmem(devaddr + net->dev_name, KVADDR,
&name_addr, sizeof(name_addr), net->dev_name_t,
FAULT_ON_ERROR);
read_string(name_addr, buf, DEV_NAME_MAX);
break;
}
}
/*
* Get the device address.
*
* {net_}device->ip_ptr points to in_device.
* in_device->in_ifaddr points to in_ifaddr list.
* in_ifaddr->ifa_address contains the address.
* in_ifaddr->ifa_next points to the next in_ifaddr in the list (if any).
*
*/
static long
get_device_address(ulong devaddr, char **bufp, long buflen)
{
ulong ip_ptr, ifa_list;
struct in_addr ifa_address;
char *buf;
char buf2[BUFSIZE];
long pos = 0;
buf = *bufp;
BZERO(buf, buflen);
BZERO(buf2, BUFSIZE);
readmem(devaddr + net->dev_ip_ptr, KVADDR,
&ip_ptr, sizeof(ulong), "ip_ptr", FAULT_ON_ERROR);
if (!ip_ptr)
return buflen;
readmem(ip_ptr + OFFSET(in_device_ifa_list), KVADDR,
&ifa_list, sizeof(ulong), "ifa_list", FAULT_ON_ERROR);
while (ifa_list) {
readmem(ifa_list + OFFSET(in_ifaddr_ifa_address), KVADDR,
&ifa_address, sizeof(struct in_addr), "ifa_address",
FAULT_ON_ERROR);
sprintf(buf2, "%s%s", pos ? ", " : "", inet_ntoa(ifa_address));
if (pos + strlen(buf2) >= buflen) {
RESIZEBUF(*bufp, buflen, buflen * 2);
buf = *bufp;
BZERO(buf + buflen, buflen);
buflen *= 2;
}
BCOPY(buf2, &buf[pos], strlen(buf2));
pos += strlen(buf2);
readmem(ifa_list + OFFSET(in_ifaddr_ifa_next), KVADDR,
&ifa_list, sizeof(ulong), "ifa_next", FAULT_ON_ERROR);
}
return buflen;
}
/*
* Get the family, type, local and destination address/port pairs.
*/
static void
get_sock_info(ulong sock, char *buf)
{
uint32_t daddr, rcv_saddr;
uint16_t dport, sport;
ushort family, type;
ushort num ATTRIBUTE_UNUSED;
char *sockbuf, *inet_sockbuf;
ulong ipv6_pinfo, ipv6_rcv_saddr, ipv6_daddr;
uint16_t u6_addr16_src[8];
uint16_t u6_addr16_dest[8];
char buf2[BUFSIZE];
struct in_addr in_addr;
int len;
BZERO(buf, BUFSIZE);
BZERO(buf2, BUFSIZE);
sockbuf = inet_sockbuf = NULL;
rcv_saddr = daddr = 0;
dport = sport = 0;
family = type = 0;
ipv6_pinfo = 0;
switch (net->flags & (SOCK_V1|SOCK_V2))
{
case SOCK_V1:
sockbuf = GETBUF(SIZE(sock));
readmem(sock, KVADDR, sockbuf, SIZE(sock),
"sock buffer", FAULT_ON_ERROR);
daddr = UINT(sockbuf + OFFSET(sock_daddr));
rcv_saddr = UINT(sockbuf + OFFSET(sock_rcv_saddr));
dport = USHORT(sockbuf + OFFSET(sock_dport));
sport = USHORT(sockbuf + OFFSET(sock_sport));
num = USHORT(sockbuf + OFFSET(sock_num));
family = USHORT(sockbuf + OFFSET(sock_family));
type = USHORT(sockbuf + OFFSET(sock_type));
break;
case SOCK_V2:
inet_sockbuf = GETBUF(SIZE(inet_sock));
readmem(sock, KVADDR, inet_sockbuf, SIZE(inet_sock),
"inet_sock buffer", FAULT_ON_ERROR);
daddr = UINT(inet_sockbuf + OFFSET(inet_sock_inet) +
OFFSET(inet_opt_daddr));
rcv_saddr = UINT(inet_sockbuf + OFFSET(inet_sock_inet) +
OFFSET(inet_opt_rcv_saddr));
dport = USHORT(inet_sockbuf + OFFSET(inet_sock_inet) +
OFFSET(inet_opt_dport));
sport = USHORT(inet_sockbuf + OFFSET(inet_sock_inet) +
OFFSET(inet_opt_sport));
num = USHORT(inet_sockbuf + OFFSET(inet_sock_inet) +
OFFSET(inet_opt_num));
family = USHORT(inet_sockbuf + OFFSET(sock_common_skc_family));
type = USHORT(inet_sockbuf + OFFSET(sock_sk_type));
ipv6_pinfo = ULONG(inet_sockbuf + SIZE(sock));
break;
}
switch (family)
{
case AF_UNSPEC:
sprintf(buf, "UNSPEC:"); break;
case AF_UNIX:
sprintf(buf, "UNIX:"); break;
case AF_INET:
sprintf(buf, "INET:"); break;
case AF_AX25:
sprintf(buf, "AX25:"); break;
case AF_IPX:
sprintf(buf, "IPX:"); break;
case AF_APPLETALK:
sprintf(buf, "APPLETALK:"); break;
case AF_NETROM:
sprintf(buf, "NETROM:"); break;
case AF_BRIDGE:
sprintf(buf, "BRIDGE:"); break;
case AF_ATMPVC:
sprintf(buf, "ATMPVC:"); break;
case AF_X25:
sprintf(buf, "X25:"); break;
case AF_INET6:
sprintf(buf, "INET6:"); break;
case AF_ROSE:
sprintf(buf, "ROSE:"); break;
case AF_DECnet:
sprintf(buf, "DECnet:"); break;
case AF_NETBEUI:
sprintf(buf, "NETBEUI:"); break;
case AF_SECURITY:
sprintf(buf, "SECURITY/KEY:"); break;
case AF_NETLINK:
sprintf(buf, "NETLINK/ROUTE:"); break;
case AF_PACKET:
sprintf(buf, "PACKET:"); break;
case AF_ASH:
sprintf(buf, "ASH:"); break;
case AF_ECONET:
sprintf(buf, "ECONET:"); break;
case AF_ATMSVC:
sprintf(buf, "ATMSVC:"); break;
case AF_SNA:
sprintf(buf, "SNA:"); break;
case AF_IRDA:
sprintf(buf, "IRDA:"); break;
#ifndef AF_PPPOX
#define AF_PPPOX 24
#endif
case AF_PPPOX:
sprintf(buf, "PPPOX:"); break;
default:
sprintf(buf, "%d:", family); break;
}
switch (type)
{
case SOCK_STREAM:
sprintf(&buf[strlen(buf)], "STREAM"); break;
case SOCK_DGRAM:
sprintf(&buf[strlen(buf)], "DGRAM "); break;
case SOCK_RAW:
sprintf(&buf[strlen(buf)], "RAW"); break;
case SOCK_RDM:
sprintf(&buf[strlen(buf)], "RDM"); break;
case SOCK_SEQPACKET:
sprintf(&buf[strlen(buf)], "SEQPACKET"); break;
case SOCK_PACKET:
sprintf(&buf[strlen(buf)], "PACKET"); break;
default:
sprintf(&buf[strlen(buf)], "%d", type); break;
}
/* make sure we have room at the end... */
// sprintf(&buf[strlen(buf)], "%s", space(MINSPACE-1));
sprintf(&buf[strlen(buf)], " ");
if (family == AF_INET) {
if (BITS32()) {
in_addr.s_addr = rcv_saddr;
sprintf(&buf[strlen(buf)], "%*s-%-*d%s",
BYTES_IP_ADDR,
inet_ntoa(in_addr),
BYTES_PORT_NUM,
ntohs(sport),
space(1));
in_addr.s_addr = daddr;
sprintf(&buf[strlen(buf)], "%*s-%-*d%s",
BYTES_IP_ADDR,
inet_ntoa(in_addr),
BYTES_PORT_NUM,
ntohs(dport),
space(1));
} else {
in_addr.s_addr = rcv_saddr;
sprintf(&buf[strlen(buf)], " %s-%d ",
inet_ntoa(in_addr),
ntohs(sport));
in_addr.s_addr = daddr;
sprintf(&buf[strlen(buf)], "%s-%d",
inet_ntoa(in_addr),
ntohs(dport));
}
}
if (sockbuf)
FREEBUF(sockbuf);
if (inet_sockbuf)
FREEBUF(inet_sockbuf);
if (family != AF_INET6)
return;
switch (net->flags & (SOCK_V1|SOCK_V2))
{
case SOCK_V1:
break;
case SOCK_V2:
if (INVALID_MEMBER(ipv6_pinfo_rcv_saddr) ||
INVALID_MEMBER(ipv6_pinfo_daddr))
break;
ipv6_rcv_saddr = ipv6_pinfo + OFFSET(ipv6_pinfo_rcv_saddr);
ipv6_daddr = ipv6_pinfo + OFFSET(ipv6_pinfo_daddr);
if (!readmem(ipv6_rcv_saddr, KVADDR, u6_addr16_src, SIZE(in6_addr),
"ipv6_rcv_saddr buffer", QUIET|RETURN_ON_ERROR))
break;
if (!readmem(ipv6_daddr, KVADDR, u6_addr16_dest, SIZE(in6_addr),
"ipv6_daddr buffer", QUIET|RETURN_ON_ERROR))
break;
sprintf(&buf[strlen(buf)], "%*s ", BITS32() ? 22 : 12,
dump_in6_addr_port(u6_addr16_src, sport, buf2, &len));
if (BITS32() && (len > 22))
len = 1;
mkstring(dump_in6_addr_port(u6_addr16_dest, dport, buf2, NULL),
len, CENTER, NULL);
sprintf(&buf[strlen(buf)], "%s", buf2);
break;
}
}
static char *
dump_in6_addr_port(uint16_t *addr, uint16_t port, char *buf, int *len)
{
sprintf(buf, "%x:%x:%x:%x:%x:%x:%x:%x-%d",
ntohs(addr[0]),
ntohs(addr[1]),
ntohs(addr[2]),
ntohs(addr[3]),
ntohs(addr[4]),
ntohs(addr[5]),
ntohs(addr[6]),
ntohs(addr[7]),
ntohs(port));
if (len)
*len = strlen(buf);
return buf;
}
/*
* XXX - copied from neighbour.h !!!!!!
*
* Neighbor Cache Entry States.
*/
#define NUD_INCOMPLETE 0x01
#define NUD_REACHABLE 0x02
#define NUD_STALE 0x04
#define NUD_DELAY 0x08
#define NUD_PROBE 0x10
#define NUD_FAILED 0x20
#define NUD_NOARP 0x40
#define NUD_PERMANENT 0x80
#define FLAGBUF_SIZE 100
#define FILLBUF(s) \
do { \
char *bp; \
int blen; \
blen=strlen(flag_buffer); \
if ((blen + strlen(s)) < FLAGBUF_SIZE-2) { \
bp = &flag_buffer[blen]; \
if (blen != 0) { \
sprintf(bp, "|%s", (s)); \
} else { \
sprintf(bp, "%s", (s)); \
} \
} \
} while(0)
/*
* Take the state of the ARP entry and print it out the flag associated
* with the binary state...
*/
static void
arp_state_to_flags(unsigned char state)
{
char flag_buffer[FLAGBUF_SIZE];
int had_flags = 0;
if (!state) {
fprintf(fp, "\n");
return;
}
bzero(flag_buffer, FLAGBUF_SIZE);
if (state & NUD_INCOMPLETE) {
FILLBUF("INCOMPLETE");
had_flags = 1;
}
if (state & NUD_REACHABLE) {
FILLBUF("REACHABLE");
had_flags = 1;
}
if (state & NUD_STALE) {
FILLBUF("STALE");
had_flags = 1;
}
if (state & NUD_DELAY) {
FILLBUF("DELAY");
had_flags = 1;
}
if (state & NUD_PROBE) {
FILLBUF("PROBE");
had_flags = 1;
}
if (state & NUD_FAILED) {
FILLBUF("FAILED");
had_flags = 1;
}
if (state & NUD_NOARP) {
FILLBUF("NOARP");
had_flags = 1;
}
if (state & NUD_PERMANENT) {
FILLBUF("PERMANENT");
had_flags = 1;
}
if (had_flags) {
fprintf(fp, "%s\n", flag_buffer);
/* fprintf(fp, "%29.29s%s)\n", " ", flag_buffer); */
}
}
#undef FILLBUF
/*
* Print out a formatted ethernet HW address....
*/
static void
dump_ether_hw(unsigned char *ha, int len)
{
int i;
for (i = 0; i < len; i++) {
char sep = ':';
if (i == (len - 1)) {
sep = ' ';
}
fprintf(fp, "%02x%c", ha[i], sep);
}
}
/*
* Catchall routine for dumping out a HA address whose format we
* don't know about...
*/
static void
dump_hw_addr(unsigned char *ha, int len)
{
int i;
for (i = 0; i < len; i++) {
fprintf(fp, "%02x ", ha[i]);
}
}
/*
* help -n output
*/
void
dump_net_table(void)
{
int others;
others = 0;
fprintf(fp, " flags: %lx (", net->flags);
if (net->flags & NETDEV_INIT)
fprintf(fp, "%sNETDEV_INIT", others++ ? "|" : "");
if (net->flags & STRUCT_DEVICE)
fprintf(fp, "%sSTRUCT_DEVICE", others++ ? "|" : "");
if (net->flags & STRUCT_NET_DEVICE)
fprintf(fp, "%sSTRUCT_NET_DEVICE", others++ ? "|" : "");
if (net->flags & NO_INET_SOCK)
fprintf(fp, "%sNO_INET_SOCK", others++ ? "|" : "");
if (net->flags & SOCK_V1)
fprintf(fp, "%sSOCK_V1", others++ ? "|" : "");
if (net->flags & SOCK_V2)
fprintf(fp, "%sSOCK_V2", others++ ? "|" : "");
fprintf(fp, ")\n");
fprintf(fp, " netdevice: \"%s\"\n", net->netdevice);
fprintf(fp, " dev_name_t: \"%s\"\n", net->dev_name_t);
fprintf(fp, " dev_type_t: \"%s\"\n", net->dev_type_t);
fprintf(fp, " dev_addr_t: \"%s\"\n", net->dev_addr_t);
fprintf(fp, " dev_name: %ld\n", net->dev_name);
fprintf(fp, " dev_next: %ld\n", net->dev_next);
fprintf(fp, " dev_type: %ld\n", net->dev_type);
fprintf(fp, " dev_ip_ptr: %ld\n", net->dev_ip_ptr);
fprintf(fp, " dev_addr_len: %ld\n", net->dev_addr_len);
fprintf(fp, "net_device_name_index: %d\n", net->net_device_name_index);
}
/*
* Dump the open sockets for a given PID.
*/
static void
dump_sockets(ulong flag, struct reference *ref)
{
struct task_context *tc;
ulong value;
int subsequent;
if (!args[optind]) {
if (!NET_REFERENCE_CHECK(ref))
print_task_header(fp, CURRENT_CONTEXT(), 0);
dump_sockets_workhorse(CURRENT_TASK(), flag, ref);
return;
}
subsequent = 0;
while (args[optind]) {
switch (str_to_context(args[optind], &value, &tc))
{
case STR_PID:
for (tc = pid_to_context(value); tc; tc = tc->tc_next) {
if (!NET_REFERENCE_CHECK(ref))
print_task_header(fp, tc, subsequent++);
dump_sockets_workhorse(tc->task, flag, ref);
}
break;
case STR_TASK:
if (!NET_REFERENCE_CHECK(ref))
print_task_header(fp, tc, subsequent++);
dump_sockets_workhorse(tc->task, flag, ref);
break;
case STR_INVALID:
error(INFO, "%sinvalid task or pid value: %s\n",
subsequent++ ? "\n" : "", args[optind]);
break;
}
optind++;
}
}
/*
* Find all sockets in the designated task and call sym_socket_dump()
* to display them.
*/
void
dump_sockets_workhorse(ulong task, ulong flag, struct reference *ref)
{
ulong files_struct_addr = 0, fdtable_addr = 0;
int max_fdset = 0;
int max_fds = 0;
ulong open_fds_addr = 0;
fd_set open_fds;
ulong fd;
ulong file;
int i, j;
int sockets_found = 0;
ulong value;
/*
* Steps to getting open sockets:
*
* 1) task->files (struct files_struct)
* 2) files->fd (struct file **)
* 3) cycle through from 0 to files->open_fds offset from *fd
* i.e. fd[0], fd[1], fd[2] are pointers to the first three
* open file descriptors. Thus, we have:
* struct file *fd[0], *fd[1], *fd[2],...
*
* 4) file->f_dentry (struct dentry)
* 5) dentry->d_inode (struct inode)
* 6) S_ISSOCK(inode.mode)
* Assuming it _is_ a socket:
* 7) inode.u (struct socket) -- offset 0xdc from inode pointer
*/
readmem(task + OFFSET(task_struct_files), KVADDR, &files_struct_addr,
sizeof(void *), "task files contents", FAULT_ON_ERROR);
if (files_struct_addr) {
if (VALID_MEMBER(files_struct_max_fdset)) {
readmem(files_struct_addr + OFFSET(files_struct_max_fdset),
KVADDR, &max_fdset, sizeof(int),
"files_struct max_fdset", FAULT_ON_ERROR);
readmem(files_struct_addr + OFFSET(files_struct_max_fds),
KVADDR, &max_fds, sizeof(int), "files_struct max_fds",
FAULT_ON_ERROR);
}
else if (VALID_MEMBER(files_struct_fdt)) {
readmem(files_struct_addr + OFFSET(files_struct_fdt), KVADDR,
&fdtable_addr, sizeof(void *), "fdtable buffer",
FAULT_ON_ERROR);
if (VALID_MEMBER(fdtable_max_fdset))
readmem(fdtable_addr + OFFSET(fdtable_max_fdset),
KVADDR, &max_fdset, sizeof(int),
"fdtable_struct max_fdset", FAULT_ON_ERROR);
else
max_fdset = -1;
readmem(fdtable_addr + OFFSET(fdtable_max_fds),
KVADDR, &max_fds, sizeof(int), "fdtable_struct max_fds",
FAULT_ON_ERROR);
}
}
if ((VALID_MEMBER(files_struct_fdt) && !fdtable_addr) ||
!files_struct_addr || (max_fdset == 0) || (max_fds == 0)) {
if (!NET_REFERENCE_CHECK(ref))
fprintf(fp, "No open sockets.\n");
return;
}
if (VALID_MEMBER(fdtable_open_fds)){
readmem(fdtable_addr + OFFSET(fdtable_open_fds), KVADDR,
&open_fds_addr, sizeof(void *), "files_struct open_fds addr",
FAULT_ON_ERROR);
readmem(fdtable_addr + OFFSET(fdtable_fd), KVADDR, &fd,
sizeof(void *), "files_struct fd addr", FAULT_ON_ERROR);
} else {
readmem(files_struct_addr + OFFSET(files_struct_open_fds), KVADDR,
&open_fds_addr, sizeof(void *), "files_struct open_fds addr",
FAULT_ON_ERROR);
readmem(files_struct_addr + OFFSET(files_struct_fd), KVADDR, &fd,
sizeof(void *), "files_struct fd addr", FAULT_ON_ERROR);
}
if (open_fds_addr)
readmem(open_fds_addr, KVADDR, &open_fds, sizeof(fd_set),
"files_struct open_fds", FAULT_ON_ERROR);
if (!open_fds_addr || !fd) {
if (!NET_REFERENCE_CHECK(ref))
fprintf(fp, "No open sockets.\n");
return;
}
if (NET_REFERENCE_CHECK(ref)) {
if (IS_A_NUMBER(ref->str)) {
if (hexadecimal_only(ref->str, 0)) {
ref->hexval = htol(ref->str,
FAULT_ON_ERROR, NULL);
ref->cmdflags |= NET_REF_HEXNUM;
} else {
value = dtol(ref->str, FAULT_ON_ERROR, NULL);
if (value <= MAX(max_fdset, max_fds)) {
ref->decval = value;
ref->cmdflags |= NET_REF_DECNUM;
} else {
ref->hexval = htol(ref->str,
FAULT_ON_ERROR, NULL);
ref->cmdflags |= NET_REF_HEXNUM;
}
}
}
ref->ref1 = task;
}
j = 0;
for (;;) {
unsigned long set;
i = j * __NFDBITS;
if (((max_fdset >= 0) && (i >= max_fdset)) || (i >= max_fds))
break;
set = open_fds.__fds_bits[j++];
while (set) {
if (set & 1) {
readmem(fd + i*sizeof(struct file *), KVADDR,
&file, sizeof(struct file *),
"fd file", FAULT_ON_ERROR);
if (file) {
if (sym_socket_dump(file, i,
sockets_found, flag, ref)) {
sockets_found++;
}
}
}
i++;
set >>= 1;
}
}
if (!sockets_found && !NET_REFERENCE_CHECK(ref))
fprintf(fp, "No open sockets.\n");
if (NET_REFERENCE_FOUND(ref))
fprintf(fp, "\n");
}
/*
* Dump a struct socket symbolically. Dave makes this _very_ easy.
*
* Return TRUE if we found a socket, FALSE otherwise.
*/
static char *socket_hdr_32 =
"FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT";
static char *socket_hdr_64 =
"FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT";
static int
sym_socket_dump(ulong file,
int fd,
int sockets_found,
ulong flag,
struct reference *ref)
{
uint16_t umode16 = 0;
uint32_t umode32 = 0;
uint mode = 0;
ulong dentry = 0, inode = 0,
struct_socket = 0;
ulong sock = 0;
char *file_buf, *dentry_buf, *inode_buf, *socket_buf;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char *socket_hdr = BITS32() ? socket_hdr_32 : socket_hdr_64;
unsigned int radix;
file_buf = fill_file_cache(file);
dentry = ULONG(file_buf + OFFSET(file_f_dentry));
if (flag & d_FLAG)
radix = 10;
else if (flag & x_FLAG)
radix = 16;
else
radix = 0;
if (!dentry)
return FALSE;
dentry_buf = fill_dentry_cache(dentry);
inode = ULONG(dentry_buf + OFFSET(dentry_d_inode));
if (!inode)
return FALSE;
inode_buf = fill_inode_cache(inode);
switch (SIZE(umode_t))
{
case SIZEOF_32BIT:
umode32 = UINT(inode_buf + OFFSET(inode_i_mode));
break;
case SIZEOF_16BIT:
umode16 = USHORT(inode_buf + OFFSET(inode_i_mode));
break;
}
if (SIZE(umode_t) == SIZEOF_32BIT)
mode = umode32;
else
mode = (uint)umode16;
if (!S_ISSOCK(mode))
return FALSE;
/*
* 2.6 (SOCK_V2) -- socket is inode addr minus sizeof(struct socket)
*/
switch (net->flags & (SOCK_V1|SOCK_V2))
{
case SOCK_V1:
struct_socket = inode + OFFSET(inode_u);
sock = ULONG(inode_buf + OFFSET(inode_u) + OFFSET(socket_sk));
break;
case SOCK_V2:
if (!VALID_SIZE(inet_sock))
error(FATAL,
"cannot determine what an inet_sock structure is\n");
struct_socket = inode - OFFSET(socket_alloc_vfs_inode);
socket_buf = GETBUF(SIZE(socket));
readmem(struct_socket, KVADDR, socket_buf,
SIZE(socket), "socket buffer", FAULT_ON_ERROR);
sock = ULONG(socket_buf + OFFSET(socket_sk));
FREEBUF(socket_buf);
break;
}
if (NET_REFERENCE_CHECK(ref)) {
if ((ref->cmdflags & NET_REF_HEXNUM) &&
((ref->hexval == sock) || (ref->hexval == struct_socket)))
ref->cmdflags |= NET_REF_FOUND_ITEM;
else if ((ref->cmdflags & NET_REF_DECNUM) &&
(ref->decval == (ulong)fd))
ref->cmdflags |= NET_REF_FOUND_ITEM;
else if ((ref->cmdflags & NET_REF_HEXNUM) &&
(ref->hexval == (ulong)fd))
ref->cmdflags |= NET_REF_FOUND_ITEM;
if (!(ref->cmdflags & NET_REF_FOUND_ITEM))
return FALSE;
ref->cmdflags &= ~NET_REF_FOUND_ITEM;
ref->cmdflags |= NET_REF_FOUND;
if (!(ref->cmdflags & NET_TASK_HEADER_PRINTED)) {
print_task_header(fp, task_to_context(ref->ref1), 0);
ref->cmdflags |= NET_TASK_HEADER_PRINTED;
}
if (!(ref->cmdflags & NET_SOCK_HEADER_PRINTED)) {
sockets_found = 0;
ref->cmdflags |= NET_SOCK_HEADER_PRINTED;
}
}
switch (flag & (S_FLAG|s_FLAG))
{
case S_FLAG:
fprintf(fp, "%sFD %s %s\n", sockets_found ? "\n" : "",
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "SOCKET"),
mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "SOCK"));
fprintf(fp, "%2d %s %s\n\n",
fd,
mkstring(buf1, VADDR_PRLEN, RJUST|LONG_HEX,
MKSTR(struct_socket)),
mkstring(buf2, VADDR_PRLEN, RJUST|LONG_HEX,
MKSTR(sock)));
dump_struct("socket", struct_socket, radix);
switch (net->flags & (SOCK_V1|SOCK_V2))
{
case SOCK_V1:
dump_struct("sock", sock, radix);
break;
case SOCK_V2:
if (STRUCT_EXISTS("inet_sock") && !(net->flags & NO_INET_SOCK))
dump_struct("inet_sock", sock, radix);
else if (STRUCT_EXISTS("sock"))
dump_struct("sock", sock, radix);
else
fprintf(fp, "\nunable to display inet_sock structure\n");
break;
}
break;
case s_FLAG:
if (!sockets_found) {
fprintf(fp, "%s\n", socket_hdr);
}
fprintf(fp, "%2d%s%s%s%s%s",
fd, space(MINSPACE),
mkstring(buf1, VADDR_PRLEN, RJUST|LONG_HEX,
MKSTR(struct_socket)),
space(MINSPACE),
mkstring(buf2, VADDR_PRLEN, RJUST|LONG_HEX,
MKSTR(sock)),
space(MINSPACE));
buf1[0] = NULLCHAR;
get_sock_info(sock, buf1);
fprintf(fp, "%s\n", buf1);
return TRUE;
default:
error(FATAL, "illegal flag: %lx\n", flag);
}
return TRUE;
}