/* 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 #include #include #include /* * 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 : 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 { * * } * * 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; ulong *open_fds; int open_fds_size; 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); } open_fds_size = MAX(max_fdset, max_fds) / BITS_PER_BYTE; open_fds = (ulong *)GETBUF(open_fds_size); if (!open_fds) return; if (open_fds_addr) readmem(open_fds_addr, KVADDR, open_fds, open_fds_size, "files_struct open_fds", FAULT_ON_ERROR); if (!open_fds_addr || !fd) { if (!NET_REFERENCE_CHECK(ref)) fprintf(fp, "No open sockets.\n"); FREEBUF(open_fds); 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 * BITS_PER_LONG; if (((max_fdset >= 0) && (i >= max_fdset)) || (i >= max_fds)) break; set = open_fds[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"); FREEBUF(open_fds); } /* * 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; }