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 を参照してください。