mirror of https://github.com/crash-utility/crash
1659 lines
45 KiB
C
1659 lines
45 KiB
C
/* net.c - core analysis suite
|
|
*
|
|
* Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
|
|
* Copyright (C) 2002-2015 David Anderson
|
|
* Copyright (C) 2002-2015 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 void get_device_address(ulong, char *);
|
|
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))
|
|
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))
|
|
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[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;
|
|
|
|
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);
|
|
|
|
get_device_address(next, buf);
|
|
fprintf(fp, "%s\n", buf);
|
|
|
|
readmem(next+net->dev_next, KVADDR, &next,
|
|
sizeof(void *), "(net_)device.next", FAULT_ON_ERROR);
|
|
} while (next);
|
|
}
|
|
|
|
static void
|
|
show_net_devices_v2(ulong task)
|
|
{
|
|
struct list_data list_data, *ld;
|
|
char *net_device_buf;
|
|
char buf[BUFSIZE];
|
|
int ndevcnt, i;
|
|
long flen;
|
|
|
|
if (!net->netdevice) /* initialized in net_init() */
|
|
return;
|
|
|
|
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);
|
|
|
|
get_device_address(ld->list_ptr[i], buf);
|
|
fprintf(fp, "%s\n", buf);
|
|
}
|
|
|
|
FREEBUF(ld->list_ptr);
|
|
FREEBUF(net_device_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[BUFSIZE];
|
|
int ndevcnt, i;
|
|
long flen;
|
|
|
|
if (!net->netdevice) /* initialized in net_init() */
|
|
return;
|
|
|
|
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);
|
|
|
|
get_device_address(ld->list_ptr[i], buf);
|
|
fprintf(fp, "%s\n", buf);
|
|
}
|
|
|
|
FREEBUF(ld->list_ptr);
|
|
FREEBUF(net_device_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 void
|
|
get_device_address(ulong devaddr, char *buf)
|
|
{
|
|
ulong ip_ptr, ifa_list;
|
|
struct in_addr ifa_address;
|
|
|
|
BZERO(buf, BUFSIZE);
|
|
|
|
readmem(devaddr + net->dev_ip_ptr, KVADDR,
|
|
&ip_ptr, sizeof(ulong), "ip_ptr", FAULT_ON_ERROR);
|
|
|
|
if (!ip_ptr)
|
|
return;
|
|
|
|
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(&buf[strlen(buf)], "%s%s",
|
|
strlen(buf) ? ", " : "",
|
|
inet_ntoa(ifa_address));
|
|
|
|
readmem(ifa_list + OFFSET(in_ifaddr_ifa_next), KVADDR,
|
|
&ifa_list, sizeof(ulong), "ifa_next", FAULT_ON_ERROR);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|