さくらのクラウドで無理矢理MACVLANを生やしてみる話
さくらのクラウド上のサーバでMACVLANを用い、1つのスイッチに対して2本のNetwork interfaceを繋ぐ。
background
さくらのクラウドには「スイッチ」(ルータ+スイッチ)というファンクションが存在する。
文字通り仮想的なL2スイッチにサーバを接続して通信できるのだが、このスイッチへのトラフィックは サーバに割り当てられているMACアドレス or VRRP仮想MACアドレス(VRID 1-4)を送信元としたパケットしか許可されない という制約がある。
で、ごく稀に1台のサーバから1つのルータ+スイッチに対して複数の足を伸ばしたくなる時がある ✽。しかしさくらのクラウドでは、あるスイッチに対してサーバごとにNIC1つしか接続できない & 前述のMAC制限があるので、容易ではない。
✽ 例えばサーバ内のLXCコンテナから直接L2の足を出したいケースなど。こうした場合では、単なるセカンダリIPアドレスではなく、IF自体がもう一つ欲しいのだ。 といってもさくらのクラウドは(専用ホストを利用しない限り) Nested KVM が使えないので、こうしたケースは多くない。
しかしVRRP用の仮想MACアドレス(VMAC)は4つぶん許可されている。VMACだろうがなんだろうがMACアドレスはMACアドレスである。ということでこのVMACを流用して、ルータ+スイッチに2本目の足を伸ばしてみる。
method
PoCということで、ルータ+スイッチに接続されているNIC (eth0)に対し、MACVLANを用いて追加のIFを生やしてみる。
MACVLANを扱う方法は以下のリンク先が分かりやすい。
https://qiita.com/albatross/items/8c32615b5154acf712f2
macvlan の使い方と挙動のメモ @albatross
root:~# sysctl -w net.ipv4.conf.all.arp_ignore=1 root:~# sysctl -w net.ipv4.conf.all.rp_filter=0 # MACVLAN インターフェイスとして vmac0 を追加 root:~# ip link add link eth0 name vmac0 type macvlan mode private # vmac0のMACアドレスに(本来はVRRP用の)仮想MACを設定 root:~# ip link set vmac0 address 00:00:5e:00:01:01 # IPアドレスをアサイン root:~# ip addr add dev vmac0 192.0.2.85/28 root:~# ip link set vmac0 up root:~# ip addr : 2: eth0:mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 9c:a3:ba:32:1e:3a brd ff:ff:ff:ff:ff:ff altname enp0s3 altname ens3 inet 192.0.2.84/28 brd 192.0.2.95 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::9ea3:baff:fe32:1e3a/64 scope link valid_lft forever preferred_lft forever : 7: vmac0@eth0: mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 00:00:5e:00:01:01 brd ff:ff:ff:ff:ff:ff inet 192.0.2.85/28 scope global vmac0 valid_lft forever preferred_lft forever inet6 fe80::200:5eff:fe00:101/64 scope link valid_lft forever preferred_lft forever # # eth0 と vmac0 からデフォゲ宛にIPv4 Ping疎通テスト (OK) # root:~# ping -I eth0 192.0.2.1 PING 192.0.2.1 (192.0.2.1) from 192.0.2.84 eth0: 56(84) bytes of data. 64 bytes from 192.0.2.1: icmp_seq=1 ttl=64 time=1.10 ms 64 bytes from 192.0.2.1: icmp_seq=2 ttl=64 time=1.14 ms ^C --- 192.0.2.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 1.098/1.120/1.142/0.022 ms root:~# ping -I vmac0 192.0.2.1 PING 192.0.2.1 (192.0.2.1) from 192.0.2.85 vmac0: 56(84) bytes of data. 64 bytes from 192.0.2.1: icmp_seq=1 ttl=64 time=15.1 ms 64 bytes from 192.0.2.1: icmp_seq=2 ttl=64 time=1.11 ms ^C --- 192.0.2.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 1.105/8.121/15.137/7.016 ms # # 外部への疎通テスト (OK) # root:~# ip route add 0.0.0.0/0 via 192.0.2.1 dev eth0 src 192.0.2.84 root:~# ip route add 0.0.0.0/0 via 192.0.2.1 dev vmac0 src 192.0.2.85 RTNETLINK answers: File exists # src違くても同じCIDRはダメらしい root:~# ip route add 0.0.0.0/1 via 192.0.2.1 dev vmac0 src 192.0.2.85 root:~# curl --interface eth0 api.ipify.org 192.0.2.84 root:~# curl --interface vmac0 api.ipify.org 192.0.2.85 # # eth0 と vmac0 からデフォゲ宛にIPv6 Ping疎通テスト (NG) # なぜか vmac0 だけ通ってない、ちゃんと原因追ってないけど # root:~# ping6 fe80::1%vmac0 PING fe80::1%vmac0(fe80::1%vmac0) 56 data bytes ^C --- fe80::1%vmac0 ping statistics --- 3 packets transmitted, 0 received, 100% packet loss, time 2051ms root:~# ping6 fe80::1%eth0 PING fe80::1%eth0(fe80::1%eth0) 56 data bytes 64 bytes from fe80::1%eth0: icmp_seq=1 ttl=64 time=1.32 ms ^C --- fe80::1%eth0 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 1.316/1.316/1.316/0.000 ms # # お互いのIFに上位スイッチ経由でのPingは通らない # (※普通L2スイッチで同じポートに対するヘアピンはできないと思う) # root:~# ping -I vmac0 192.0.2.84 PING 192.0.2.84 (192.0.2.84) from 192.0.2.85 vmac0: 56(84) bytes of data. ^C --- 192.0.2.84 ping statistics --- 4 packets transmitted, 0 received, 100% packet loss, time 3053ms pipe 3 root:~# ping -I eth0 192.0.2.85 PING 192.0.2.85 (192.0.2.85) from 192.0.2.84 eth0: 56(84) bytes of data. ^C --- 192.0.2.85 ping statistics --- 2 packets transmitted, 0 received, 100% packet loss, time 1001ms root:~# ip neigh 192.0.2.85 dev eth0 FAILED 192.0.2.84 dev vmac0 FAILED 192.0.2.82 dev eth0 lladdr 40:de:ad:3b:51:3a STALE 192.0.2.1 dev eth0 lladdr 00:00:5e:00:01:ff REACHABLE 192.0.2.1 dev vmac0 lladdr 00:00:5e:00:01:ff STALE fe80::42de:ad06:ca3b:513a dev eth0 lladdr 40:de:ad:3b:51:3a router STALE fe80::1 dev eth0 lladdr 00:00:5e:00:02:ff router STALE 2001:e42:407:102b::2 dev eth0 lladdr 40:de:ad:3b:51:3a router STALE fe80::1 dev vmac0 FAILED
Appendix
もしさくらのクラウドでDocker コンテナなどから直接スイッチに足を出したい場合、
L2でのリーチャビリティに拘らなければ ipvlan を使うのがより良い方法だと思う。
https://docs.docker.com/network/drivers/ipvlan/
$ docker network create -d ipvlan \ --subnet=192.168.1.0/24 \ --gateway=192.168.1.1 \ -o ipvlan_mode=l2 \ -o parent=eth0 hoge_net $ docker run --net=hoge_net -it --rm alpine /bin/sh