概要
Linuxのクラッシュダンプからネットワークデバイスの管理情報であるnet_device構造体の内容を確認してみます。
事前準備
以下の記事を参考にして事前準備を行います。
カーネルのデバッグ情報をインストールします。
# yum install --enablerepo=base-debuginfo kernel-debuginfo-`uname -r`
クラッシュコマンドをインストールします。
# yum install crash
OSを強制的にパニックさせてテスト用のクラッシュダンプを採取します。
# echo c > /proc/sysrq-trigger
OS再起動後に/var/crash配下を確認し、クラッシュダンプが採取されていることを確認します。
ipコマンドによる構成の確認
あらかじめ、ipコマンドにより対象サーバのNIC名やIPアドレス等を確認しておきます。
# ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 52:54:00:20:1d:56 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.62/24 brd 192.168.0.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet6 240f:a1:2044:1:cb15:e4dc:e642:6dbc/64 scope global noprefixroute dynamic
valid_lft 278sec preferred_lft 278sec
inet6 fe80::b52b:b451:8c26:4b40/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: br-201008366ec8: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:81:87:19:69 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 scope global br-201008366ec8
valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:fc:76:39:b9 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
以降ではeth0の制御情報についてcrashコマンドで確認していきます。
項目 | 値 |
---|---|
NIC名 | eth0 |
Index | 2 |
IPアドレス | 192.168.0.62 |
ネットマスク | 255.255.255.0 |
MTU | 1500 |
FLAGS | BROADCAST,MULTICAST,UP,LOWER_UP |
crashコマンドの起動
採取したクラッシュダンプを指定してcrashコマンドを起動します。
[root@cent7-vm 127.0.0.1-2022-02-03-15:11:50]# cd /var/crash/127.0.0.1-2022-02-03-15\:11\:50/
[root@cent7-vm 127.0.0.1-2022-02-03-15:11:50]# crash /usr/lib/debug/usr/lib/modules/`uname -r`/vmlinux vmcore
crash 7.2.3-11.el7_9.1
Copyright (C) 2002-2017 Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation
Copyright (C) 1999-2006 Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012 Fujitsu Limited
Copyright (C) 2006, 2007 VA Linux Systems Japan K.K.
Copyright (C) 2005, 2011 NEC Corporation
Copyright (C) 1999, 2002, 2007 Silicon Graphics, Inc.
Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
This program is free software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it under
certain conditions. Enter "help copying" to see the conditions.
This program has absolutely no warranty. Enter "help warranty" for details.
GNU gdb (GDB) 7.6
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu"...
WARNING: kernel relocated [172MB]: patching 87388 gdb minimal_symbol values
KERNEL: /usr/lib/debug/usr/lib/modules/3.10.0-1160.53.1.el7.x86_64/vmlinux
DUMPFILE: vmcore [PARTIAL DUMP]
CPUS: 2
DATE: Thu Feb 3 15:11:48 2022
UPTIME: 00:23:00
LOAD AVERAGE: 0.01, 0.14, 0.14
TASKS: 161
NODENAME: cent7-vm
RELEASE: 3.10.0-1160.53.1.el7.x86_64
VERSION: #1 SMP Fri Jan 14 13:59:45 UTC 2022
MACHINE: x86_64 (2095 Mhz)
MEMORY: 2 GB
PANIC: "SysRq : Trigger a crash"
PID: 1994
COMMAND: "bash"
TASK: ffff8d8db8ede300 [THREAD_INFO: ffff8d8db9c7c000]
CPU: 1
STATE: TASK_RUNNING (SYSRQ)
crash>
ネットワーク情報の表示
netコマンドでネットワークデバイスの一覧を表示します。
crash> net
NET_DEVICE NAME IP ADDRESS(ES)
ffff8d8dbbbb2000 lo 127.0.0.1
ffff8d8d75ccd000 eth0 192.168.0.62
ffff8d8dba7fa000 br-201008366ec8 172.18.0.1
ffff8d8dba7f8000 docker0 172.17.0.1
出力される項目は以下のとおりです。
項目 | 説明 |
---|---|
NET_DEVICE | net_device構造体のポインタ |
NAME | NIC名 |
IP_ADDRESS(ES) | IPアドレス |
net_device構造体のダンプ
structコマンドでeth0のnet_device構造体 ffff8d8d75ccd000 の内容をダンプします。
crash> struct net_device ffff8d8d75ccd000
struct net_device {
name = "eth0\000\000\000\000\000\000\000\000\000\000\000",
name_hlist = {
next = 0x0,
pprev = 0xffff8d8dbbbc8440
},
ifalias = 0x0,
mem_end = 0,
mem_start = 0,
base_addr = 0,
irq = 0,
state = 3,
dev_list = {
next = 0xffff8d8dba7fa050,
prev = 0xffff8d8dbbbb2050
},
napi_list = {
next = 0xffff8d8d75c8c450,
prev = 0xffff8d8d75c8c450
},
unreg_list = {
next = 0xffff8d8d75ccd070,
prev = 0xffff8d8d75ccd070
},
upper_dev_list = {
next = 0xffff8d8d75ccd080,
prev = 0xffff8d8d75ccd080
},
features = 4296755753,
hw_features = 35192963811337,
wanted_features = 1787913,
vlan_features = 35188668825641,
hw_enc_features = 8796093022209,
mpls_features = 1,
ifindex = 2,
iflink = 2,
...
以降省略
net_device構造体のメンバーを個別にダンプ
structコマンドでeth0のnet_device構造体 ffff8d8d75ccd000 のメンバーを個別にダンプします。
デバイス名を表示する場合、net_device.nameを指定します。
crash> struct net_device.name ffff8d8d75ccd000
name = "eth0\000\000\000\000\000\000\000\000\000\000\000"
crash>
デバイスのインデックス番号を表示する場合、net_device.ifindexを指定します。
crash> struct net_device.ifindex ffff8d8d75ccd000
ifindex = 2
crash>
ifindexの値はip addrコマンドで表示されたものと同一の値です。
デバイスのインタフェースフラグを表示する、net_device.flagsを指定します。
crash> struct net_device.flags ffff8d8d75ccd000
flags = 4099
crash>
flagsの値4096を16進数に変換すると0x1003となります。これは以下のフラグのORをとった値です。
フラグ | 値 |
---|---|
IFF_UP | 0x01 |
IFF_BROADCAST | 0x02 |
IFF_MULTICAST | 0x1000 |
デバイスのMTUを表示する、net_device.mtuを指定します。
crash> struct net_device.mtu ffff8d8d75ccd000
mtu = 1500
crash>
MTUの値はip addrコマンドで表示されたものと同一の値です。
IPv4情報のダンプ
eth0のnet_device構造体からIPv4の制御情報ip_ptrのポインタffff8d8d7614ec00をin_device構造体にマッピングして内容をダンプします。
crash> struct net_device.ip_ptr ffff8d8d75ccd000
ip_ptr = 0xffff8d8d7614ec00
crash> struct in_device ffff8d8d7614ec00
struct in_device {
dev = 0xffff8d8d75ccd000,
refcnt = {
counter = 3
},
dead = 0,
ifa_list = 0xffff8d8db8423100,
mc_list = 0xffff8d8dba818f00,
mc_count = 1,
mc_tomb_lock = {
{
rlock = {
raw_lock = {
val = {
counter = 0
}
}
}
}
},
mc_tomb = 0x0,
mr_v1_seen = 0,
mr_v2_seen = 0,
mr_maxdelay = 0,
mr_qrv = 2 '\002',
mr_gq_running = 0 '\000',
mr_ifc_count = 0 '\000',
mr_gq_timer = {
entry = {
next = 0x0,
prev = 0x0
},
expires = 0,
base = 0xffffffff8cd03bc0,
function = 0xffffffff8c2e30f0,
data = 18446618237443828736,
slack = -1,
start_pid = -1,
start_site = 0x0,
start_comm = "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
},
mr_ifc_timer = {
entry = {
next = 0x0,
prev = 0x0
},
expires = 0,
base = 0xffffffff8cd03bc0,
function = 0xffffffff8c2e3120,
data = 18446618237443828736,
slack = -1,
start_pid = -1,
start_site = 0x0,
start_comm = "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
},
arp_parms = 0xffff8d8d766c2840,
cnf = {
sysctl = 0xffff8d8d75ef1800,
data = {1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
state = {67108863},
extra_data = {10000, 1000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
extra_state = {65535}
},
callback_head = {
next = 0x0,
func = 0x0
}
}
eth0のin_device構造体のifa_listの内容ffff8d8db8423100をin_ifaddr構造体にマッピングしてIPアドレスの情報を読み取ります。
crash> struct in_device.ifa_list ffff8d8d7614ec00
ifa_list = 0xffff8d8db8423100
crash> list ffff8d8db8423100
ffff8d8db8423100
crash> struct in_addr ffff8d8db8423100
struct in_addr {
s_addr = 0
}
crash> struct in_ifaddr ffff8d8db8423100
struct in_ifaddr {
hash = {
next = 0x0,
pprev = 0xffffffff8ce6a390
},
ifa_next = 0x0,
ifa_dev = 0xffff8d8d7614ec00,
callback_head = {
next = 0x0,
func = 0x0
},
ifa_local = 1040230592,
ifa_address = 1040230592,
ifa_mask = 16777215,
ifa_broadcast = 4278233280,
ifa_scope = 0 '\000',
ifa_prefixlen = 24 '\030',
ifa_flags = 640,
ifa_label = "eth0\000\000\000\000\000\000\000\000\000\000\000",
ifa_valid_lft = 0,
ifa_preferred_lft = 0,
ifa_cstamp = 4294674072,
ifa_tstamp = 4294674128
}
in_ifaddr構造体のifa_address/ifa_mask の値1040230592/16777215をそれぞれ16進数に変換すると0x3E00A8C0/0xffffffとなります。
これらのバイトオーダーをひっくり返すと 0xc0A8003E/0xffffff00となり、10進数ドット表記に変換すると、IPアドレス/ネットマスクは192.168.0.62/255.255.255.0であることが判ります。これらはip addrコマンドの出力結果と一致しています。
ドライバの情報のダンプ
eth0のnet_device構造体からnetdev_opsのポインタffff8d8d7614ec00をnet_device_ops構造体にマッピングして内容をダンプします。
crash> struct net_device.netdev_ops ffff8d8d75ccd000
netdev_ops = 0xffffffffc02d4520
crash> struct net_device_ops ffffffffc02d4520
struct net_device_ops {
ndo_init = 0x0,
ndo_uninit = 0x0,
ndo_open = 0xffffffffc02d29e0,
ndo_stop = 0xffffffffc02d0550,
ndo_start_xmit = 0xffffffffc02d1260,
{
ndo_select_queue = 0x0,
__UNIQUE_ID_rh_kabi_hide51 = {
ndo_select_queue = 0x0
},
{<No data fields>}
},
ndo_change_rx_flags = 0x0,
ndo_set_rx_mode = 0xffffffffc02d2aa0,
ndo_set_mac_address = 0xffffffffc02d0cf0,
ndo_validate_addr = 0xffffffff8c282b50,
ndo_do_ioctl = 0x0,
ndo_set_config = 0x0,
ndo_change_mtu_rh74 = 0x0,
ndo_neigh_setup = 0x0,
ndo_tx_timeout = 0x0,
{
ndo_get_stats64 = 0xffffffffc02d03d0,
__UNIQUE_ID_rh_kabi_hide52 = {
ndo_get_stats64 = 0xffffffffc02d03d0
},
{<No data fields>}
},
ndo_get_stats = 0x0,
ndo_vlan_rx_add_vid = 0xffffffffc02d0e60,
ndo_vlan_rx_kill_vid = 0xffffffffc02d0ef0,
ndo_poll_controller = 0xffffffffc02d0140,
ndo_netpoll_setup = 0x0,
ndo_netpoll_cleanup = 0x0,
ndo_busy_poll = 0x0,
ndo_set_vf_mac = 0x0,
ndo_set_vf_vlan_rh73 = 0x0,
ndo_set_vf_tx_rate = 0x0,
ndo_set_vf_spoofchk = 0x0,
ndo_get_vf_config = 0x0,
ndo_set_vf_link_state = 0x0,
ndo_set_vf_port = 0x0,
ndo_get_vf_port = 0x0,
ndo_setup_tc_rh72 = 0x0,
ndo_fcoe_enable = 0x0,
ndo_fcoe_disable = 0x0,
ndo_fcoe_ddp_setup = 0x0,
ndo_fcoe_ddp_done = 0x0,
ndo_fcoe_ddp_target = 0x0,
ndo_fcoe_get_hbainfo = 0x0,
ndo_fcoe_get_wwn = 0x0,
ndo_rx_flow_steer = 0x0,
...
以降省略
試しにイーサネットドライバのopenハンドラ ndo_openを逆アセンブルしてみます。
crash> dis ffffffffc02d29e0 10
0xffffffffc02d29e0 <virtnet_open>: nopl 0x0(%rax,%rax,1) [FTRACE NOP]
0xffffffffc02d29e5 <virtnet_open+5>: push %rbp
0xffffffffc02d29e6 <virtnet_open+6>: mov %rsp,%rbp
0xffffffffc02d29e9 <virtnet_open+9>: push %r15
0xffffffffc02d29eb <virtnet_open+11>: lea 0x900(%rdi),%r15
0xffffffffc02d29f2 <virtnet_open+18>: push %r14
0xffffffffc02d29f4 <virtnet_open+20>: lea 0x8c0(%rdi),%r14
0xffffffffc02d29fb <virtnet_open+27>: push %r13
0xffffffffc02d29fd <virtnet_open+29>: push %r12
0xffffffffc02d29ff <virtnet_open+31>: mov %rdi,%r12
crash>
シンボルがvirtnet_openとなっていることから、イーサネットドライバはvirtnetであることが判ります。
その他の情報のダンプ
まず、カーネルソースを調査して出力すべき制御情報としてどのようなものがあるかを確認します。次にそれらをリンクしているポインタやテーブル管理リストが格納されているグローバル変数を特定し、順番にたどっていくことで、最終的に確認したい情報が表示できるはずです。