LinuxでTCP/UDPコネクション状態にプロセス情報を紐付ける方法

Linuxでは、/proc/net/*Netlinkソケットを通じて、TCP/UDPのコネクション情報を取得できる。 しかし、ここで取得したコネクション情報は、コネクションを保有するプロセスに関する情報(pidなど)を含んでいない。 ssコマンドの--extendedオプションは、inode番号を表示することから、inode番号からプロセス情報を辿ることを考える。

当該プロセスのpidがわかっていれば、/proc/<pid>/fd以下から、次のようにsocket inode番号を知ることができる。

ubuntu@yuukidev01:~$ sudo readlink /proc/21326/fd/6
socket:[16801]

しかし、inode番号からそのinodeを保持しているpidを直接知ることはおそらくできない。 そこで、次のようなTCP/UDPのコネクションリストプロセスリストの2つのリストを突き合わせ、socket inodeをキーとして、結合する必要がある。(RDB風に表現するとNested Loop Joinする。)

TCP/UDP connections (netlink diag messages | /proc/net/*)
----------------------------------------------------------
| State | Local Address:Port | Peer Address:Port | Inode |
|--------------------------------------------------------|
| ESTAB | 10.0.0.10:80       | 10.0.0.11: 46021  | 16801 |
| ESTAB | 10.0.0.10:80       | 10.0.0.11: 48010  | 16829 |
| ...                                                    |
----------------------------------------------------------
Processes (/proc/<pid>/*)
------------------------------------------------|
| Pid  | Process Name | File Discriptor | Inode |
| 543  | nginx        |  3              | 16801 |
| 543  | nginx        |  3              | 16802 |
| ...                                           |
-------------------------------------------------

iproute2ユーティリティのssコマンドの実装でもこれと同じようになっており、具体的には次の通りである。

  1. --processesオプションが指定されると、user_ent_hash_buildが呼ばれる https://github.com/shemminger/iproute2/blob/afa588490b7e87c5adfb05d5163074e20b6ff14a/misc/ss.c#L5073 .
  2. user_ent_hash_buildでは、/proc/以下をスキャンして、inodeをキーとしたプロセス情報の連想配列を作成している、 https://github.com/shemminger/iproute2/blob/afa588490b7e87c5adfb05d5163074e20b6ff14a/misc/ss.c#L573-L674
  3. コネクションの各行を表示するときに、コネクションのinodeをキーとして2.の連想配列からプロセス情報を取得する。tcp_show_line -> inet_stats_print -> proc_ctx_print -> find_entry

ただし、TCPコネクションのステートがTIME_WAITなど、ソケットをcloseした後のステートでは、ssコマンドでinodeが0となり、プロセス情報を取得できない。 また、まれにステートがESTABであっても、inodeが0となっていることがある。

参考