SVX日記
2024-08-14(Wed) ブリッジ難しくてわかんな~い
という成果を受けて、同じような要領で1階の書斎まで呼び線を引こうと試したのだが引けなかった。紐を割かなかったのが原因かもしれん。
DIYが好きな自分は、こういう状況はむしろ楽しみでもあるし「自前でウマいことやったった」感を味わいたいので、業者さんに頼む気にはならないのだが、ウマくいかなければ、それはそれでそれが心労となる面倒な性格である。なんだろうな、関西人の「高ければウマいのは当たり前」「ウマくても安くなきゃ不満」みたいな気持ちだろうか。知らんけど。
主力PCにイーサネットポートを追加し、ブリッジ(≒スイッチ、≠ルータ)の役割をさせ、仕事PCの通信を中継をさせればいい。手元には100MbpsのUSBイーサしかないが、ちょっと調べたらイマドキはUSB3.0の1Gbpsのモノも千円チョイで買えるようだ。普通に思考すればギガビットのスイッチに置き換えるのが普通の思考であることはわかっているが、上記の方法ならば必要のない時には電源が切れるという利点がある。まぁ、普通に置き換えるだけじゃ面白くないってのもあるんだが。
で、ここからが本題。USBイーサを挿したら、主力PC内に仮想ブリッジを設定するのであるが、以前からこのブリッジの設定について、納得のいっていない点があるのだ。なんでブリッジデバイスにIPアドレス付いちゃってんの?
# brctl show
bridge name bridge id STP enabled interfaces
bridge0 8000.e86a64573438 no enp3s0f0 ※本体のイーサポート
enp4s0f3u4u4 ※USBのイーサポート
# ip addr show
enp3s0f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master bridge0 state UP group default qlen 1000
enp4s0f3u4u4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master bridge0 state UP group default qlen 1000
bridge0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
inet 172.28.0.99/16 brd 172.28.255.255 scope global dynamic noprefixroute bridge0
んが、ネットワークの基本ではあるが、ブリッジつうのはレイヤ2であるから、それ自体にIPアドレスは付かないはずなのだ。同じ理屈でスイッチングハブにもIPアドレスは付かない。スマートスイッチだと管理用のIPアドレスが付くかもしれないが、それは意味が違う。なのに、現状を絵に描くとこう。
動いてんだから問題ないじゃん、という考え方もあろうが、サポートでメシ食っている人間としては、その指向はあまりよろしくないので、少し突き詰めてみる。んが、現状の物理デバイスをネタに突き詰めてもいいのだが、実はdockerのコンテナ環境もブリッジを使い倒している。なので、そっちの方面から深堀りしてみることにする。
ネタはこないだ導入したミニPCの上の、Fedora40のdocker(podman)環境だ。常々、コンテナを起動するとifconfigってやたら賑やかになるよなぁ、と思ってはいたが、あまり中身を意識したことはなかった。
# ip addr show
podman1: mtu 1500 qdisc noqueue state UP group default qlen 1000
inet 10.89.0.1/24 brd 10.89.0.255 scope global podman1
veth0@if2: mtu 1500 qdisc noqueue master podman1 state UP group default qlen 1000
podman2: mtu 1500 qdisc noqueue state UP group default qlen 1000
inet 10.89.1.1/24 brd 10.89.1.255 scope global podman2
veth1@if2: mtu 1500 qdisc noqueue master podman2 state UP group default qlen 1000
コンテナを立ち上げるとpodmanX, vethXが対で増える。vethXってナニよ? 試しに母艦とコンテナ両方でtcpdumpを採りながら、母艦からコンテナAにpingを打ったら理解できた。基本tcpdumpに-i anyの指定はご法度だが、最近はI/F名も出るようになって便利なんだよな。
# tcpdump -tt -nn -i any icmp
.220946 podman1 Out IP 10.89.0.1 > 10.89.0.2: ICMP echo request, id 174, seq 1, length 64
.220958 veth0 Out IP 10.89.0.1 > 10.89.0.2: ICMP echo request, id 174, seq 1, length 64
.220973 eth0 In IP 10.89.0.1 > 10.89.0.2: ICMP echo request, id 174, seq 1, length 64
.221012 eth0 Out IP 10.89.0.2 > 10.89.0.1: ICMP echo reply, id 174, seq 1, length 64
.221016 veth0 P IP 10.89.0.2 > 10.89.0.1: ICMP echo reply, id 174, seq 1, length 64
.221020 podman1 In IP 10.89.0.2 > 10.89.0.1: ICMP echo reply, id 174, seq 1, length 64
わかりやすい。行きはpodman1 veth0, eth0の順、帰りはその逆順。veth0て、内部にある外向け(?)の仮想のイーサポートってことだったんだな。Virtual-ETHer。つまり、図にするとこうだ。
$ sudo ping 10.89.0.2
# tcpdump -tt -nn -i any icmp
.724550 eth0 Out IP 10.89.0.3 > 10.89.0.2: ICMP echo request, id 175, seq 1, length 64
.724553 veth1 P IP 10.89.0.3 > 10.89.0.2: ICMP echo request, id 175, seq 1, length 64
.724554 veth0 Out IP 10.89.0.3 > 10.89.0.2: ICMP echo request, id 175, seq 1, length 64
.724570 veth0 P IP 10.89.0.2 > 10.89.0.3: ICMP echo reply, id 175, seq 1, length 64
.724571 veth1 Out IP 10.89.0.2 > 10.89.0.3: ICMP echo reply, id 175, seq 1, length 64
.724571 eth0 In IP 10.89.0.2 > 10.89.0.3: ICMP echo reply, id 175, seq 1, length 64
さっきと異なり、podman1を経由していない。いやいや、ブリッジを経由しないでパケットが届くわけないよね。逆に考えれば「podman1という名前はブリッジそのものを指しているわけではない」ということになってしまう。podman1は「ブリッジにオマケでくっついているNIC」を指している、と理解すればいいのだろうか?
# ip addr show podman1
podman1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
inet 10.89.0.1/24 brd 10.89.0.255 scope global podman1
# ip addr del 10.89.0.1/24 dev podman1
# ip addr show podman1
podman1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
今回「なんでブリッジデバイスにIPアドレス付いちゃってんの?」てのが納得のいっていない点なので、podman1にIPアドレスが付いていない状態で、母艦とコンテナ間で通信ができればなんとなく納得できる。んが、IPアドレスを付ける場所がなければどうしようもないので、仮想NICを作ってみる。
# ip link add veth101 type veth peer name veth100
# ip link show
veth100@veth101: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
veth101@veth100: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
# ip addr add 10.89.0.101/24 dev veth101
# ip link set veth100 up
# ip link set veth101 up
# ip addr show
veth100@veth101: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
veth101@veth100: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
inet 10.89.0.101/24 scope global veth101
# brctl show
bridge name bridge id STP enabled interfaces
podman1 8000.26d98dba414b no veth0
veth1
# brctl addif podman1 veth100
# brctl show
bridge name bridge id STP enabled interfaces
podman1 8000.26d98dba414b no veth0
veth1
veth100
$ sudo ping 10.89.0.101
# tcpdump -tt -nn -i any icmp
.923306 eth0 Out IP 10.89.0.3 > 10.89.0.101: ICMP echo request, id 173, seq 1, length 64
.923310 veth1 P IP 10.89.0.3 > 10.89.0.101: ICMP echo request, id 173, seq 1, length 64
.923311 veth100 Out IP 10.89.0.3 > 10.89.0.101: ICMP echo request, id 173, seq 1, length 64
.923311 veth101 In IP 10.89.0.3 > 10.89.0.101: ICMP echo request, id 173, seq 1, length 64
.923378 veth101 Out IP 10.89.0.101 > 10.89.0.3: ICMP echo reply, id 173, seq 1, length 64
.923379 veth100 P IP 10.89.0.101 > 10.89.0.3: ICMP echo reply, id 173, seq 1, length 64
.923381 veth1 Out IP 10.89.0.101 > 10.89.0.3: ICMP echo reply, id 173, seq 1, length 64
.923381 eth0 In IP 10.89.0.101 > 10.89.0.3: ICMP echo reply, id 173, seq 1, length 64
通ったー!! んが、結局ここまでやってわかったことは「そんな面倒なことしなくて済むように」「ブリッジにはオマケでNICがくっついて」いて、それが「ブリッジデバイスにIPアドレス付いちゃってる」ように見える、ってことっぽい。
ちなみに、コンテナBは、podman1のアドレスがデフォルトゲートウェイになっていて、DNSもそこを指しているので、そのIPアドレスを外してしまっている現状では、外と通信することができない。デフォルトゲートウェイをveth101にし、別のDNSを参照するようにすれば、外と通信することができるようになる。つまり、podman1のIPアドレスを外し、veth100/veth101で通信するようにした状態も、特に間違っているわけではないということだ。
$ ip route show
default via 10.89.0.1 dev eth0 proto static metric 100
10.89.0.0/24 dev eth0 proto kernel scope link src 10.89.0.3
$ sudo ip route delete default via 10.89.0.1
$ ip route show
10.89.0.0/24 dev eth0 proto kernel scope link src 10.89.0.3
$ sudo ip route add default via 10.89.0.101 dev eth0
$ ip route show
default via 10.89.0.101 dev eth0
10.89.0.0/24 dev eth0 proto kernel scope link src 10.89.0.3
$ cat /etc/resolv.conf
search dns.podman
nameserver 10.89.0.1
$ sudo vi /etc/resolv.conf
$ cat /etc/resolv.conf
search dns.podman
nameserver 172.28.0.1
$ curl www.example.com
<!doctype html>
<html>
<head>
<title>Example Domain</title>
: