Linuxその2 Advent Calendar 2020 の 9 日目です。 この記事は、普段ググりながら適当に使っている NetworkManager について、どういう仕組みなんだっけ?を調べなおすものです。
検証環境
CentOS 8 を利用する。
# cat /etc/redhat-release CentOS Linux release 8.2.2004 (Core) # rpm -q NetworkManager NetworkManager-1.22.8-5.el8_2.x86_64
NetworkManager とは
NetoworkManager はネットワーク設定をしてくれるデーモン。
# systemctl status NetworkManager ● NetworkManager.service - Network Manager Loaded: loaded (/usr/lib/systemd/system/NetworkManager.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2020-12-01 09:50:09 JST; 3 days ago Docs: man:NetworkManager(8) Main PID: 949 (NetworkManager) Tasks: 4 (limit: 49090) Memory: 10.5M CGroup: /system.slice/NetworkManager.service mq949 /usr/sbin/NetworkManager --no-daemon
nmcli は DBus(プロセス間通信の仕組み) 経由で NetworkManager と通信し、ネットワークを操作する CLI ツール。 NetworkManager を利用したツールは nmcli のほかに、 nmtui や GUI などがある。
nmcli 利用時に、 DBus 経由でネットワーク情報を取ってくる様子をモニタしてみると、インタフェース情報などがやり取りされている様子がわかる。
// eth0 の情報を表示する terminal A ]# nmcli connection show eth0 // NetworkManager から DBus 経由でネットワーク情報を取ってくる様子をモニタする terminal B ]# dbus-monitor --system ... method call time=1607402291.256118 sender=:1.8 -> destination=org.freedesktop.DBus serial=1245 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetConnectionUnixUser string ":1.115" method return time=1607402291.256138 sender=org.freedesktop.DBus -> destination=:1.8 serial=158 reply_serial=1245 uint32 0 method call time=1607402291.256490 sender=:1.115 -> destination=:1.8 serial=12 path=/org/freedesktop/NetworkManager/Settings/1; interface=org.freedesktop.NetworkManager.Settings.Connection; member=GetSettings method call time=1607402291.256503 sender=:1.115 -> destination=:1.8 serial=13 path=/org/freedesktop/NetworkManager/Settings/2; interface=org.freedesktop.NetworkManager.Settings.Connection; member=GetSettings method call time=1607402291.256512 sender=:1.115 -> destination=:1.8 serial=14 path=/org/freedesktop/NetworkManager/Settings/3; interface=org.freedesktop.NetworkManager.Settings.Connection; member=GetSettings method call time=1607402291.256520 sender=:1.8 -> destination=org.freedesktop.DBus serial=1246 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetConnectionUnixProcessID string ":1.115" method return time=1607402291.256534 sender=org.freedesktop.DBus -> destination=:1.8 serial=159 reply_serial=1246 uint32 25500 method return time=1607402291.256547 sender=:1.8 -> destination=:1.115 serial=1247 reply_serial=12 array [ dict entry( string "ipv4" array [ dict entry( string "address-data" variant array [ array [ dict entry( string "address" variant string "192.168.11.11" ) dict entry( string "prefix" variant uint32 24 ) ] ] ) ...
設定変更時の動作
NetworkManager によってネットワーク設定をすると、主に/etc/sysconfig/
配下のファイルが書き変えられる。
(一部の設定項目では /etc/NetworkManager/
が利用されるなどの例外はある)
# nmcli connection modify eth0 ipv4.dns 8.8.8.8
上記コマンド実行前後の /etc/sysconfig/network-scripts/ifcfg-eth0
ファイルの diff を取ると、次のようになっており、ファイルが書き換えられているのがわかる。
19c19 < DNS1=192.168.11.1 --- > DNS1=8.8.8.8
起動時の動作
NetworkManager 起動時は、 /etc/sysconfig/network-scripts/ifcfg-eth0
などの設定ファイルを読み込んでネットワークを設定する。
bpftrace を用いてトレースすると、NetworkManager 起動時に /etc/sysconfig/network-scripts/ifcfg-eth0
などが読み込まれている様子がわかる。
terminal A ]# systemctl restart NetworkManager // NetworkManager 再起動時に /etc/resolv.conf を読み込んでいることを確認する terminal B ]# bpftrace -e 'tracepoint:syscalls:sys_enter_openat / str(args->filename) == "/etc/resolv.conf" / { printf("%s\n", comm); } ' Attaching 1 probe... NetworkManager pool setroubleshootd rpm // NetworkManager 再起動時に /etc/sysconfig/network-scripts/ifcfg-eth0 を読み込んでいることを確認する terminal C ]# bpftrace -e 'tracepoint:syscalls:sys_enter_openat / str(args->filename) == "/etc/sysconfig/network-scripts/ifcfg-eth0" / { printf("%s\n", comm); } ' Attaching 1 probe... NetworkManager 11-dhclient
NetworkManager を経由して設定変更した場合はその更新がクライアントに通知されるが、/etc/sysconfig/network-scripts/ifcfg-eth0
などを直接編集した場合は、NetworkManager への再読み込み要求が必要になる。この場合、nmcli connection up connection_name
を実行すればよい。
iproute2 パッケージ
nmcli と違い ip や ss は、NetworkManager を停止しても失敗しない。そういえば、なんでなんだろう。
# systemctl stop NetworkManager # ip addr 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 mq state UP group default qlen 1000 link/ether 00:15:5d:0b:06:27 brd ff:ff:ff:ff:ff:ff inet 192.168.11.11/24 brd 192.168.11.255 scope global noprefixroute eth0 valid_lft forever preferred_lft forever 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:15:5d:0b:06:2c brd ff:ff:ff:ff:ff:ff inet 192.168.11.12/24 brd 192.168.11.255 scope global dynamic eth1 valid_lft 162607sec preferred_lft 162607sec 4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:15:5d:0b:06:2d brd ff:ff:ff:ff:ff:ff inet 192.168.11.13/24 brd 192.168.11.255 scope global dynamic eth2 valid_lft 162607sec preferred_lft 162607sec # ss -t State Recv-Q Send-Q Local Address:Port Peer Address:Port ESTAB 0 0 192.168.11.11:ssh 192.168.11.5:55389 ESTAB 0 36 192.168.11.11:ssh 192.168.11.5:55128 ESTAB 0 0 192.168.11.11:ssh 192.168.11.5:50120 ESTAB 0 0 192.168.11.11:ssh 192.168.11.5:51278
この理由は、 ip コマンドは NetworkManager を経由してないから。(だから ip コマンドで何かを設定したところで永続化されない。再起動すると元に戻る。)
terminal A ]# ip addr // 何も表示されない terminal B ]# dbus-monitor --system
では ip コマンドはどうしているかというと、 netlink を利用して直接カーネルとやり取りしている(nl_pid=0
)。
# strace ip addr 2>&1 | grep "^socket" -A 5 socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE) = 3 setsockopt(3, SOL_SOCKET, SO_SNDBUF, [32768], 4) = 0 setsockopt(3, SOL_SOCKET, SO_RCVBUF, [1048576], 4) = 0 setsockopt(3, SOL_NETLINK, NETLINK_EXT_ACK, [1], 4) = 0 bind(3, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, 12) = 0 getsockname(3, {sa_family=AF_NETLINK, nl_pid=8326, nl_groups=00000000}, [12]) = 0
netlinkファンのためのnlmon を参考に、netlink を流れる通信をキャプチャすると、その様子がわかる。
terminal A ]# modprobe nlmon terminal A ]# ip link add nlmon0 type nlmon terminal A ]# ip link set nlmon0 up terminal B ]# ip link terminal A ]# tshark -i nlmon0 -V Frame 1: 1328 bytes on wire (10624 bits), 1328 bytes captured (10624 bits) on interface 0 Interface id: 0 (nlmon0) Interface name: nlmon0 Encapsulation type: Linux Netlink (158) Arrival Time: Dec 8, 2020 20:27:50.798021600 JST [Time shift for this packet: 0.000000000 seconds] Epoch Time: 1607426870.798021600 seconds [Time delta from previous captured frame: 0.000000000 seconds] [Time delta from previous displayed frame: 0.000000000 seconds] [Time since reference or first frame: 0.000000000 seconds] Frame Number: 1 Frame Length: 1328 bytes (10624 bits) Capture Length: 1328 bytes (10624 bits) [Frame is marked: False] [Frame is ignored: False] [Protocols in frame: netlink:netlink-route] Linux netlink (cooked header) Link-layer address type: Netlink (824) Family: Route (0x0000) ...
なお、NetworkManager も DBus 経由でリクエストを受け付けたあとの実際のネットワーク設定には netlink を利用している。
まとめ
まとめると、NetworkManager や ip コマンドは以下のようになる。
参考
NetworkManager の詳細な利用方法については、ネットワークの設定および管理 Red Hat Enterprise Linux 8 | Red Hat Customer Portal を参照してください。