仙石浩明の日記

システム構築・運用

2023年7月5日

exFAT な Ubuntu ブータブル USBメモリ (exFAT Bootable USB Flash Drive) の作り方

USBメモリや DVD などのリムーバブルメディアから起動可能な Ubuntu (以下 「Live Ubuntu」 と呼ぶ) は、 PC がトラブったときのレスキュー (障害復旧) の道具として重宝する。 最近の USBメモリは容量が大きく、 3GB 程度の Live Ubuntu を入れておいても大して邪魔にならない。 ふだん持ち歩く USBメモリにも、 それぞれ Live Ubuntu を入れておくとイザというとき便利。

ところが、 Ubuntu (公式) が公開している Live Ubuntu は exFAT からの起動に対応していない。 exFAT は FAT の後継として Microsoft が開発したファイルシステムで、 従来 4GB までのファイルしか扱うことができなかった FAT の制約が大幅に緩和されている。

昨今の動画ファイルはサイズが 4GB を超えるものも多く、 USBメモリは FAT ではなく exFAT でフォーマットしたい。 もちろん、 NTFS でフォーマットすれば大きなファイルを入れられるし、 Live Ubuntu も起動できるが、 NTFS は USBメモリには牛刀すぎる。

USBメモリは様々な機器に挿す可能性があるわけで、 NTFS にするのは躊躇してしまう。 スマホやラズパイ、 さらにはコンビニ等のプリント機など、 その全てで NTFS が問題無く使えるのだろうか? やっぱりデフォルトである exFAT のほうが安心。

Ubuntu などのインストールメディアの ISO イメージを exFAT に置いて起動する方法 (Make an exFAT Bootable USB Flash Drive) が既に公開されているが、 仕組み (Ventoy) が複雑だし、 そもそも ISO イメージを loopback デバイス経由でマウントして起動すると重くなるし、 必要なメモリ量も多いので、 利用したいとは思わなかった。 トラブった PC のスペックが低い場合など、 道具は軽ければ軽いほど好ましい。

というわけで、 exFAT でフォーマットした USBメモリに、 Live Ubuntu を入れる方法を考えてみた。 要は Live Ubuntu の initrd ファイル (cpio アーカイブ) をどう改変して exFAT に対応させるか?

基本的には Live Ubuntu の initrd を展開して exFAT に対応させる修正を行なった後、 cpio コマンドを使って initrd を作り直せばいいのだが、 initrd は複数の cpio アーカイブをつなげた形になっていて、 作り方によっては互換性の問題が起きるかも? Live Ubuntu 自身の update-initramfs コマンドを使って initrd を更新したほうが手軽だし確実。

なお、 (PC の起動に必要な) UEFI パーティションは (いまのところ) FAT でフォーマットしておいた方が無難と思われる。 ほとんど (全て?) の PC が exFAT や NTFS でフォーマットした UEFI パーティションを認識するらしいが、 最大限の互換性を求めるなら FAT にしておくべきだろう。

というわけで、 USBメモリの末尾 4MB (以下の実行例では sdc2) だけ FAT でフォーマットして UEFI パーティションとし、 残り (sdc1, ここでは 「boot パーティション」と呼ぶ) を exFAT でフォーマットする:

senri:/ # gdisk -l /dev/sdc
GPT fdisk (gdisk) version 0.8.4

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/sdc: 121098240 sectors, 57.7 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 619FAAB5-5325-4404-99C4-F0541D53B069
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 121098206
Partitions will be aligned on 2-sector boundaries
Total free space is 0 sectors (0 bytes)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048       121090047   57.7 GiB    0700  Microsoft basic data
   2       121090048       121098206   4.0 MiB     EF00  EFI system partition
   3              34            2047   1007.0 KiB  EF02  BIOS boot partition
senri:/ # mkfs -t fat /dev/sdc2
mkfs.fat 4.0 (2016-05-06)
senri:/ # mkfs -t exfat /dev/sdc1
mkexfatfs 1.2.3
Creating... done.
Flushing... done.
File system created successfully.
senri:/ # 

UEFI パーティションは Windows ユーザ (あるいはコンビニのプリント機) からは見えないので、 boot パーティションのみを、 ふつうの USBメモリとして使うことになる。

次に grub-install コマンドで UEFI パーティションに GRUB (ブートローダ) をインストールする。 ついでに UEFI に対応していない PC (は滅多にないと思うが) でも起動できるように、 「--target i386-pc」オプションを使って MBR にも GRUB をインストールしておく。

senri:/ # mount /dev/sdc1 /mnt/usb
senri:/ # mount /dev/sdc2 /mnt/efi
senri:/ # grub-install --target x86_64-efi --efi-directory /mnt/efi --boot-directory=/mnt/usb/boot --removable
Installing for x86_64-efi platform.
Installation finished. No error reported.
senri:/ # grub-install --target i386-pc --boot-directory=/mnt/usb/boot --removable /dev/sdc
Installing for i386-pc platform.
Installation finished. No error reported.
senri:/ # blkid | grep sdc1
/dev/sdc1: UUID="E7F3-77CD" BLOCK_SIZE="512" TYPE="exfat" PTTYPE="dos" PARTLABEL="Microsoft basic data" PARTUUID="23041859-cc53-4164-be6a-44af6a966e5d"
senri:/ # 

UEFI パーティションに書込まれるのは GRUB コア (/EFI/BOOT/BOOTX64.EFI) のみで 124KB しかない。 UEFI パーティションは 4MB も要らないかも? GRUB 本体 (/boot/grub/x86_64-efi ディレクトリ) は boot パーティションに書込まれる。 /EFI/BOOT/BOOTX64.EFI は /boot/grub/x86_64-efi/core.efi の内容と同じ。

GRUB の設定ファイル /boot/grub/grub.cfg は ↓ こんな感じ:

set uuid="E7F3-77CD"
insmod all_video

menuentry "ubuntu 22.04.2 desktop amd64" {
  linux /casper/vmlinuz boot=casper uuid=$uuid
  initrd /casper/initrd
}

ここで "E7F3-77CD" は USBメモリの exFAT パーティション /dev/sdc1 の UUID (Universally Unique Identifier, 汎用一意識別子)。 blkid コマンドなどで調べることができる (上記 grub-install の実行例の末尾を参照)。

/casper ディレクトリは、 Ubuntu DVD (以下の実行例では /cdrom にマウントしている) からコピーする:

senri:/ # cp -a /cdrom/casper /mnt/usb/
senri:/ # ls -la /mnt/usb/casper
total 2813952
drwxr-xr-x 2 root root      32768 Feb 23 13:13 .
drwxr-xr-x 4 root root      32768 Jul  3 18:10 ..
-rwxr-xr-x 1 root root      59931 Feb 23 13:09 filesystem.manifest
-rwxr-xr-x 1 root root       2885 Feb 23 13:09 filesystem.manifest-minimal-remove
-rwxr-xr-x 1 root root       3578 Feb 23 13:09 filesystem.manifest-remove
-rwxr-xr-x 1 root root         11 Feb 23 13:09 filesystem.size
-rwxr-xr-x 1 root root 2731876352 Feb 23 13:09 filesystem.squashfs
-rwxr-xr-x 1 root root        833 Feb 23 13:12 filesystem.squashfs.gpg
-rwxr-xr-x 1 root root  137120699 Feb 23 13:09 initrd
-rwxr-xr-x 1 root root   12186376 Feb 23 13:09 vmlinuz
senri:/ # 

filesystem.squashfs が Live Ubuntu の root ファイルシステム。 これ以外の filesystem.* は不要なので削除して構わない。 vmlinuz が Linux カーネルで、 initrd がこれから書き換える initrd ファイル。

boot パーティションが FAT 等であれば、 この状態でブータブル USBメモリとして機能するが、 exFAT だと initrd が /casper/filesystem.squashfs を見つけられず Ubuntu を起動できない。

そこで initrd の内容を書き換えて exFAT を扱えるようにする。 といっても Linux Kernel 5.4 以降は (以前の exfat-fuse ではなく) カーネルレベルで exFAT を扱うことができる。 つまり必要なのはカーネルモジュール kernel/fs/exfat/exfat.ko を initrd へ追加することだけ。

まず Ubuntu DVD を用いて Live Ubuntu を起動する (インストール DVD から起動して「Ubuntu を試す」を選択)。 /etc/initramfs-tools/modules および /usr/share/initramfs-tools/scripts/casper-helpers に以下のパッチ ubuntu.patch をあてる:

--- etc/initramfs-tools/modules~	2023-02-23 12:59:33.000000000 +0900
+++ etc/initramfs-tools/modules	2023-07-03 08:46:22.000000000 +0900
@@ -9,3 +9,4 @@
 #
 # raid1
 # sd_mod
+exfat
--- usr/share/initramfs-tools/scripts/casper-helpers~	2022-05-30 23:40:38.000000000 +0900
+++ usr/share/initramfs-tools/scripts/casper-helpers	2023-07-03 08:48:03.000000000 +0900
@@ -36,7 +36,7 @@
     # FIXME: do something better like the scan of supported filesystems
     fstype="${1}"
     case ${fstype} in
-        vfat|iso9660|udf|ext2|ext3|ext4|btrfs|ntfs)
+        vfat|exfat|iso9660|udf|ext2|ext3|ext4|btrfs|ntfs)
             return 0
             ;;
     esac
@@ -234,7 +234,7 @@
             # will cause data loss when a live CD is booted on a system
             # where filesystems are in use by hibernated operating systems.
             case "$(get_fstype ${devname})" in
-                vfat)
+                vfat|exfat)
                     :;;
                 *)
                     continue;;
@@ -337,7 +337,7 @@
         for dev in $(subdevices "${sysblock}"); do
             devname=$(sys2dev "${dev}")
             case "$(get_fstype ${devname})" in
-                vfat|ext2)
+                vfat|exfat|ext2)
                     :;;
                 *)
                     continue;;
@@ -367,7 +367,7 @@
 is_supported_fs(){
     [ -z "${1}" ] && return 1
     case ${1} in
-        ext2|ext3|ext4|xfs|jfs|reiserfs|vfat|ntfs|iso9660|btrfs)
+        ext2|ext3|ext4|xfs|jfs|reiserfs|vfat|exfat|ntfs|iso9660|btrfs)
             return 0
             ;;
     esac
@@ -388,6 +388,7 @@
     modprobe xfs
     modprobe jfs
     modprobe vfat
+    modprobe exfat
     modprobe fuse
     [ "$quiet" != "y" ] && log_end_msg "...devs loaded..."
     touch /dev/.initramfs/lupin-waited-for-devs

この ↑ パッチでは、 起動時に (つまり initramfs で) 必要なカーネルモジュールを指定するファイル /etc/initramfs-tools/modules に 「exfat」 を追記している。 また、 initramfs 内のスクリプト 「/usr/share/initramfs-tools/scripts/casper-helpers」 を boot パーティションが exFAT でもエラーにならないよう修正している。

というか、 たったこれだけの修正で exFAT から起動できるのだから、 公式の Live Ubuntu で exFAT からの起動をサポートして欲しい。 exFAT を除外する理由でもあるのだろうか?

そして update-initramfs.distrib コマンドを使って新しい initrd を生成する。 ここで (通常の Ubuntu と同じ感覚で) update-initramfs を使ってしまうと 「update-initramfs is disabled since running on read-only media」 と言われてしまうので注意。 「read-only media」 だからダメなのではなく、 Live Ubuntu の update-initramfs は、 このメッセージを出力するだけの sh スクリプトに置き換えられている (いったい何のために?)。

例えば Live Ubuntu の Terminal を使って以下のように実行する:

root@ubuntu:/# wget https://www.gcd.org/sengoku/docs/ubuntu-22.04.2-desktop-amd64.patch
--2023-07-04 01:47:50--  https://www.gcd.org/sengoku/docs/ubuntu-22.04.2-desktop-amd64.patch
Resolving www.gcd.org (www.gcd.org)... 71.19.146.203, 74.207.241.21, 219.94.252.139, ...
Connecting to www.gcd.org (www.gcd.org)|71.19.146.203|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1799 (1.8K) [text/plain]
Saving to: ‘ubuntu-22.04.2-desktop-amd64.patch’

2023-07-04 01:47:57 (38.1 MB/s) - ‘ubuntu-22.04.2-desktop-amd64.patch’ saved [1799/1799]

root@ubuntu:/# patch -p0 < ubuntu-22.04.2-desktop-amd64.patch
patching file etc/initramfs-tools/modules
patching file usr/share/initramfs-tools/scripts/casper-helpers
root@ubuntu:/# uname -a
Linux ubuntu 5.19.0-32-generic #33~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon Jan 30 17:03:34 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
root@ubuntu:/# update-initramfs.distrib -c -k 5.19.0-32-generic
update-initramfs: Generating /boot/initrd.img-5.19.0-32-generic
cryptsetup: ERROR: Couldn't resolve device /cow
cryptsetup: WARNING: Couldn't determine root device
W: Couldn't identify type of root file system for fsck hook
root@ubuntu:/# 

生成された /boot/initrd.img-5.19.0-32-generic を USBメモリの boot パーティションの /casper/initrd へコピーする。

以上で、 Ubuntu が起動できる exFAT な USBメモリができた。 ふだんは普通の USBメモリとして使える。 /casper/filesystem.squashfs は 2605MB もあるが、 いつでも Ubuntu DVD からコピーすることで元に戻せるので、 USBメモリに空きがないときは気軽に削除して構わない。

Filed under: システム構築・運用 — hiroaki_sengoku @ 07:55
2022年5月11日

WSL2 (Windows Subsystem for Linux 2) で物理ディスク上の独自 OS を動かしてみた

Windows で VMware Workstation Player を使って Linux ベースの独自 OS (以下 GCD OS と呼ぶ) を動かしていたのだけど、 WSL2 (Windows Subsystem for Linux 2) が物理ディスクをマウントできるようになったと聞き、 WSL2 が VMware の代りに使えるか試してみた。 ただし、物理ディスクをマウントするには Windows 11 ビルド 22000 以上が必要。

本当は Linux カーネルも自前でビルドしたものを使いたいが、 とりあえず WSL2 のカーネルをそのまま使い、 root ファイルシステムは物理ディスク上にインストールしてある GCD OS を使う方向で考えた。

以前、 OpenVZ な VPS サービス上で GCD OS を動かしたことがある (10年前!) ので、 その時と同様に chroot して物理ディスク上の GCD OS を起動するのが簡単そう。 つまり、 以下のシェルスクリプト gcd.sh を WSL2 上で実行するだけ:

#!/bin/sh
PATH="/mnt/c/WINDOWS/System32:/usr/bin:/bin:/usr/sbin:/sbin"
ROOT="/usr/local/GCD"

cd /mnt/c
wsl.exe --mount '\\.\PHYSICALDRIVE1' --bare

test -d $ROOT || mkdir $ROOT
mount LABEL=/ $ROOT

$ROOT/etc/init.d/chroot

このスクリプトは WSL2 の仮想ディスク (Virtual Hard Disk) 内に置いてもよいが、 私は Windows の C: ドライブに置いた。 WSL2 の内容を変更する必要がなくなるし、 WSL2 でどのディストリビューションを使っているかに依存しなくなる。 仮想ディスクが肥大化したら、インストールし直して初期状態に戻してもよい。

例えば C:\bin\gcd.sh に置いて、 次のように実行する:

C:\Windows\System32\wsl.exe -u root -- /mnt/c/bin/gcd.sh

タスクスケジューラーで Windows の起動時に自動的に実行すれば、 Windows にログインしなくても外部から ssh で GCD OS へログインできるので便利。 VMware より軽くて手軽で便利かも?

以下、このスクリプト gcd.sh を順に説明する:

まず、 「wsl.exe --mount '\\.\PHYSICALDRIVE1' --bare」 で WSL2 から物理ディスク 「PHYSICALDRIVE1」 が見えるようにする。 wsl.exe は Windows のコマンドだが、 WSL2 は Linux なのに /mnt/c にマウントされた C: ドライブ上の Windows のコマンドが実行できる (Linux カーネルの binfmt_misc を使っている)。

(Linux の) lsblk コマンドを使うと、 物理ディスクが見えることが確認できる:

Linux 5.10.16.3-microsoft-standard-WSL2.
kayano:~ $ lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sda      8:0    0   256G  0 disk
sdb      8:16   0 339.7M  1 disk
sdc      8:32   0   256G  0 disk /mnt/virtual
sdd      8:48   0   7.3T  0 disk
├─sdd5   8:53   0   200G  0 part 
├─sdd6   8:54   0    16G  0 part [SWAP]
└─sdd7   8:55   0   6.8T  0 part /

sdd が WSL2 から見えるようになった物理ディスク (8TB HDD)。 パーティションが 3つあることが分かる。 うち sdd7 が GCD OS の root ファイルシステム。

一点注意すべきなのは、 Windows のシステムドライブ 「PHYSICALDRIVE0」 は指定できないので、 システムドライブとは異なる物理ディスクを使う必要があるという点。 8TB HDD など大容量のハードディスクを、 パーティションで区切って Windows と Linux の両方をインストールしている人は多いと思うのだけど、 残念ながら Windows と同じディスク上にある Linux パーティションは、 WSL2 から使うことはできない。 VMware ならできるのに...

仕方がないので私は (わざわざ) M.2 NVMe SSD を買って Windows のシステムドライブを NVMe へ移し、 SATA0 につないだ 8TB HDD を (Windows をインストールしていたパーティション 1〜4 を削除して) Linux 専用にした。

また、WSL2 上で wsl.exe を実行するとき、 カレントディレクトリが WSL2 の仮想ディスクだと、 どんな引数でも常に 「Invalid argument」 エラーになる:

root@kayano:~# /mnt/c/Windows/System32/wsl.exe --shutdown
/mnt/c/Windows/System32/wsl.exe: Invalid argument
                                                 root@kayano:~#

エラー出力後の改行がうまくいかないあたり、 いかにもバグっぽい?

カレントディレクトリが C: ドライブだと正常に実行できるようなので、 wsl.exe を実行する前に 「cd /mnt/c」 を行っている。

物理ディスクが WSL2 で見えるようになったら、 次にこの物理ディスクをマウントする: 「mount LABEL=/ $ROOT」。 ここでは LABEL が 「/」 のパーティションを 「$ROOT」 つまり /usr/local/GCD へマウントしている。

あとは GCD OS を起動するだけ: 「$ROOT/etc/init.d/chroot」。 /etc/init.d/chrootOpenVZ 上で GCD OS を起動するときも使った、 以下のシェルスクリプト:

#!/bin/sh
root=`echo $0 | sed -e 's@/etc/init.d/chroot$@@'`
if [ ! -d $root ]; then
   echo "Can't find root: $root"
   exit 1
fi
sed -n 's/^[a-z][_a-z]* \([^ ][^ ]*\) .*/\1/p' < /proc/mounts | while read d; do
    if [ -d "$d" ]; then
	test -d "$root$d" || mkdir "$root$d"
	mount -obind "$d" "$root$d"
    elif [ -f "$d" ]; then
	test -f "$root$d" || touch "$root$d"
	mount -obind "$d" "$root$d"
    fi
done
if [ -d /lib/modules/`uname -r` ]; then
    mount -obind /lib/modules $root/boot/lib/modules
fi
chroot $root /bin/sh <<EOF
swapon -a
mount -a -t ext4
/etc/init.d/svscanboot &
/etc/rc.d/rc.M
EOF

このシェルスクリプトは、 まず WSL2 の /proc/mounts を参照して、 WSL2 がマウントしているディレクトリとファイルを、 そのまま GCD OS のルート (/usr/local/GCD) へマウントする。 これで GCD OS でも /mnt/c にマウントされた Windows コマンドを実行できるようになる。

次に 「chroot /usr/local/GCD /bin/sh」 を実行して、 chroot 環境下で svscanboot/etc/rc.d/rc.M を実行する。 svscanboot は daemontools の起動スクリプト。 GCD OS のほとんどのデーモン類は daemontools の管理下で起動される。 一方 /etc/rc.d/rc.M は、 GCD OS のブートスクリプトで、 ネットワーク等の各種設定と、 一部のデーモン類の起動を行なう。

ssh サーバや WWW サーバも /etc/rc.d/rc.M が立ち上げるが、 いままで WSL2 のネットワークは Windows 内でしか見えないバーチャルネットワーク (内部ネットワーク) だったらしい。 「WSL2 外部から接続」 などとググっても、 外部から WSL2 の ssh サーバにログインするには (netsh.exe の) portproxy を使え、という話ばかり出てくる。

Hyper-V Virtual Switch

どのバージョンから可能になったのかは知らないが、 「仮想スイッチ マネージャー」 で設定すれば VMware のようなブリッジモードが WSL2 でも使えるようになる。

まず Windows 管理ツールの 「Hyper-V マネージャー」 を実行する。 仮想マシン (この例では KAYANO) を選択し、 「操作(A)」 メニューから 「仮想スイッチ マネージャー(C)...」 を選ぶと、 「仮想スイッチ マネージャー」のウィンドウが開く。

左ペイン 「仮想スイッチ」 の中から 「WSL」 を選び、 右ペインに表示される 「接続の種類」 として 「外部ネットワーク(E):」を選択し、 適切なネットワーク アダプターを選択する。

これで WSL2 が外部ネットワークと通信できるようになる。 (いまのところ) タグVLAN が使えるようにはできていないが、 VMware ではタグVLAN が使えるので、 なんとかしてタグVLAN が使えるようにしたいところ...

他のマシン (以下の例では senri) から ssh で GCD OS (kayano) へログインしてみる:

senri:/ # ssh kayano
Last login: Wed May 11 04:16:34 2022 from senri.gcd.org
Linux 5.10.16.3-microsoft-standard-WSL2.
kayano:~ # ip addr show dev eth0
6: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    link/ether 00:15:5d:ff:11:b1 brd ff:ff:ff:ff:ff:ff
    inet 192.168.18.40/24 brd 192.168.18.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::215:5dff:feff:11b1/64 scope link
       valid_lft forever preferred_lft forever
kayano:~ # tcpspray senri
Transmitted 102400 bytes in 0.000315 seconds (317460.317 kbytes/s)
kayano:~ # free
             total       used       free     shared    buffers     cached
Mem:       8055704     573892    7481812          0      30704     178088
-/+ buffers/cache:     365100    7690604
Swap:     18874364          0   18874364
kayano:~ # chroot_escape /bin/bash
groups: cannot find name for group ID 11
groups: cannot find name for group ID 14
root@kayano:/# lsb_release -d
Description:    Ubuntu 20.04.4 LTS
root@kayano:/# exit
kayano:~ #

chroot 環境から脱出 (chroot_escape /bin/bash) すると WSL2 の Ubuntu 環境に戻る。 で、exit すると GCD OS に戻る。

この GCD OS で物理ディスクの読み書き速度を測ってみた:

kayano:/ # hdparm -t /dev/sdd7

/dev/sdd7:
 Timing buffered disk reads:  538 MB in  3.01 seconds = 178.93 MB/sec

kayano:/ # dd if=/dev/zero of=/tmp/test bs=1024k count=10240
10240+0 records in
10240+0 records out
10737418240 bytes (11 GB, 10 GiB) copied, 60.5833 s, 177 MB/s

読み書きともに 177MB/sec くらい。 Core i5-8500 マシンだとこんなもの? 同じ PC (同じ Windows) で VMware 上の同じ GCD OS でも測ってみると、 読込みが 180.62 MB/sec で書き込みが 132 MB/s だった。 簡易な測定なので、ほぼ同等と言っていいと思う。

ちなみに仮想化なしで直接このマシン上で測ると、 読込み 178.65 MB/sec 書込み 233 MB/s なので、 仮想マシンによるオーバーヘッドは多少あるようだ。 とはいえ AMD FX-4100 マシンとかだと読込み 174.82 MB/sec 書込み 95.3 MB/s なので、 実用上は 177MB/sec もあれば充分?

AMD FX とかの 10年前のマシンだと CPU がボトルネックになってる感じ。そろそろ 「Sandyで十分おじさん」 は卒業すべき?

ネットワークの速度も測ってみた:

kayano:/ # iperf3 -c esaka
Connecting to host esaka, port 5201
[  5] local 172.17.14.235 port 35504 connected to 192.168.18.20 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec   116 MBytes   970 Mbits/sec    0   3.01 MBytes       
[  5]   1.00-2.00   sec   111 MBytes   933 Mbits/sec    0   3.01 MBytes       
[  5]   2.00-3.00   sec   112 MBytes   944 Mbits/sec    0   3.01 MBytes       
[  5]   3.00-4.00   sec   112 MBytes   944 Mbits/sec    0   3.01 MBytes       
[  5]   4.00-5.00   sec   112 MBytes   944 Mbits/sec    0   3.01 MBytes       
[  5]   5.00-6.00   sec   111 MBytes   933 Mbits/sec    0   3.01 MBytes       
[  5]   6.00-7.00   sec   112 MBytes   944 Mbits/sec    0   3.01 MBytes       
[  5]   7.00-8.00   sec   112 MBytes   944 Mbits/sec    0   3.01 MBytes       
[  5]   8.00-9.00   sec   112 MBytes   944 Mbits/sec    0   3.01 MBytes       
[  5]   9.00-10.00  sec   111 MBytes   933 Mbits/sec    0   3.01 MBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  1.10 GBytes   943 Mbits/sec    0             sender
[  5]   0.00-10.05  sec  1.10 GBytes   938 Mbits/sec                  receiver

iperf Done.

1Gbits/sec の LAN なので、ほぼ上限の速度が出ている。 同条件で VMware でも測ってみると、 送信が 928 Mbits/sec で受信が 925 Mbits/sec だった。 仮想化なしでも、 送信が 943 Mbits/sec 受信が 941 Mbits/sec なのでほとんど同じ。

というわけで、 速度的にはディスクもネットワークも問題無さそう。

More...
Filed under: システム構築・運用 — hiroaki_sengoku @ 09:37
2020年9月2日

サーバの MAC アドレスを偽装 (MAC spoofing) してエッジ・スイッチの MAC アドレスと同一にしてみた 〜 プロバイダの接続台数制限を回避する

多くの機器で MACアドレス (Media Access Control address) は偽装できる。 例えば Linux サーバなら次のような感じ:

# ip link set dev eth0 down
# ip link set dev eth0 address 00:11:22:33:44:55
# ip link set dev eth0 up
# ip link show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:11:22:33:44:55 brd ff:ff:ff:ff:ff:ff

インタフェースが立ち上がった状態では変更できないので、 まず eth0 を down させているが、 サーバの起動時などインタフェースが立ち上がる前なら、もちろん不要。 そして MACアドレスを 「00:11:22:33:44:55」 に偽装している (MAC spoofing)。

     インターネット
        │
        │
   ┌────┴────┐
   │ マンション共用部│
   │   のルータ  │
   └─┬┬┬┬┬┬┬─┘
 ┌───┘│││││└───┐
 │ ┌──┘│││└──┐ │
 │ │ ┌─┘│└─┐ │ │
 : : :  │  : : :
  各戸へ   │   各戸へ
        │
     ┌──┴──┐
     |スイッチE|
     └┬─┬─┬┘
  ┌───┘ │ └───┐
  │     │     │
  :  ┌──┴──┐  :
 各部屋 |スイッチR| 各部屋
     └┬┬┬┬┬┘
  ┌───┘│││└───┐
  │ ┌──┘│└──┐ │
  ↓ ↓   ↓   ↓ ↓
  他のPC  サーバ  他のPC

私の自宅内の LAN において、 サーバ (左図下端) の MACアドレスを偽装して、 スイッチE (マンション共用部のルータにつながるエッジ・スイッチ) の MACアドレスと同一にしてみた。

なぜこんなことをするか?を、順を追って説明する:

マンション共用部のルータ (以下、「ルータM」と略記) は、 各戸に最大 5個のグローバル IPアドレスを割当てる。 つまり、 「ルータM」は各戸 (マンション専有部) ごとに接続された機器を数えていて、 接続を検知した順に先着 5台にのみグローバル IPアドレスを割当てる。

割当てられるグローバル IPアドレスが 5個なのではなく、 先着 5台の機器のみに、 DHCP リクエストがあれば IPアドレスを割当てる点に注意。 DHCP リクエストを行わない機器の MACアドレスがルータM に届いてしまえば、 それも 1台としてカウントされる。

困ったことにスイッチE (GS108E) は、 (L2スイッチのくせに) MACアドレスを持っていて、 定期的に UDP ブロードキャスト (NSDP) を行う。 このブロードキャストがルータM に届くと、 スイッチE が「接続機器」とみなされてしまい、 貴重なグローバル IPアドレス枠が一つ浪費されてしまう

NSDP のブロードキャストを止めることが可能ならそれが一番だが、 残念ながら GS108E の管理ツール (Windows アプリ) にはそういう設定項目は無い。 スイッチE の MACアドレスがルータM に届くのを阻止する術は無さそうだ。 もちろん、 スイッチE とルータM との間にブリッジを挟んでブロードキャストをフィルタリングすれば阻止できるが、 そんなボトルネックは作りたくない。

スイッチR (GS116E ポート数が異なる他は GS108E と同等) も同様に MACアドレスを持っていて NSDP のブロードキャストを行うし、 もちろん (グローバル IPアドレスを持たない) その他の PC 等も様々なパケットを送信するが、 いずれもルータM と直接つながっていないので、 VLAN を設定することでパケットがルータM へ届くのを阻止することが可能。

上記ネットワーク図には、 家庭内LAN に必ずある NATルータ (Wi-Fiルータ等) が見当たらないが、 Linux サーバが NATルータの役割を果たしている。 GS108E のようなタグVLAN 機能付スイッチを使うと、 物理的な配線にとらわれずに自在に LAN を構成できるので便利。

つまり、 MACアドレスがルータM に届くのを阻止できないのは、 ルータM と直接つながっているスイッチE のみ、 ということになる。

そこで、 スイッチE が「接続機器」と見なされるのが避けられないなら、 逆にスイッチE の MACアドレスでグローバル IPアドレスを取得してやろうと考えた次第。 サーバの MACアドレスを偽装してスイッチE の MACアドレスと同一にすることで、 このサーバは無事グローバル IPアドレスを取得できた。

もちろん、 これでは同一セグメント内に同じ MACアドレスを持つ機器が存在することになってしまう。 これはネットワークの教科書的には、 決してやってはいけないことだ。

一番の問題はスイッチの MACアドレス学習が混乱する点。 上図でいうと「スイッチR」 (部屋ごとに設置しているスイッチ) は、 上の「スイッチE」からも下の「サーバ」からも、 同じ MACアドレスを送信元とするパケットが届いてしまう。

また、2番目の問題として、 スイッチE に自身の MACアドレス宛のパケットが届いたとき、 それを正しくサーバへ中継するのではなく、 自身宛と見なして中継しない (そのまま捨ててしまう) 恐れがある。

まず 1番目の問題は、 スイッチE のブロードキャストの間隔が充分長ければ、 実用上の問題は起きないだろうと考えた。 確かに、 スイッチE のブロードキャストがスイッチR に届けば、 スイッチR で誤学習が起きる。 この状態でスイッチR にサーバ宛のパケットが届くと、 スイッチR はサーバへ送らずに、 スイッチE へ送ってしまう。

が、サーバは常時通信を行っているわけだから、 スイッチR には速やかにサーバからパケットが届いて誤学習状態は直ちに解消される。 一時的な誤学習は、 それが低頻度かつ短時間であればパフォーマンス上の問題は起きないだろう。

2番目の問題は、 スイッチの機種にも依存するが、 少なくとも私が使ってる GS108E-100JPS の場合は問題にならない。 つまり自身の MACアドレス宛のパケットが届いても、 それを自分宛とは見なさず、 他のパケットと同様に中継する。

例えばインターネットからサーバ宛に届くパケットの場合、 ルータM はサーバの MACアドレス (つまりスイッチE の MACアドレス) を宛先としたパケットを送信するが、 このパケットはスイッチE において何事もなく中継されて、 サーバに正しく届く。

なお、 GS108E-100JPS の後継機 GS108E-200JPS は、 管理ツール以外に WWW ブラウザで管理することもできる。 つまり (簡易な) Web サーバを内蔵しているわけで、 このようなスイッチの場合は、 スイッチの MACアドレスを宛先とするパケットは、 この Web サーバによって受信されてしまい、 このスイッチにおいては中継されないと予想される。

実際に GS105E-200JPS (ポート数が異なる他は GS108E-200JPS と同等と考えられる) で実験してみたところ、 スイッチの MACアドレスを宛先とするパケットは、 スイッチで中継されなかった。 したがって GS105E-200JPS や GS108E-200JPS を、 スイッチE として使うことは、 グローバル IPアドレス枠を一つ浪費することになるので適切ではない。

もちろん、 スイッチE として一番適切なのは、 無用なブロードキャストを吐かない (あるいは吐かない設定が可能な) スイッチである。 接続台数に制限があるプロバイダを利用する際は、 エッジ・スイッチの挙動の細かな違いにも気を配りたい。

Filed under: システム構築・運用 — hiroaki_sengoku @ 09:35
2019年11月28日

IFTTT に登録できないのでお蔵入りになってた Eco Plugs RC-028W & CT-065W が、UDP パケットを送るだけでコントロールできた!

IoT 機器の多くが、 専用のスマホアプリだけでなく Googleアシスタントや Amazonアレクサからコントロールできる。 しかし、 いちいち音声でコントロールするのはメンドクサイ (なぜ音声以外の方法でもコントロールできるようにしないのか?)。 出かけるときに毎回 「行ってきま〜す」 などと Googleアシスタントに呼び掛けるのは、 いかがなものかと思う。 外出を勝手に検知して家電を適切にコントロール (例えば電気ポットの電源を切る) してくれるほうがずっといい。

IoT 機器を IFTTT に登録すると、 自前のプログラムからコントロールできるようになる。 IoT 機器は Googleアシスタントでコントロールするより、 自前のプログラムでコントロールするに限る。 例えば自宅の Wi-Fi LAN にスマホが繋がっているかプログラムで監視し、 繋がってるスマホがいなくなったら留守になったと判断して、 自動的に電気ポットの電源を切れば、 電気ポットのコンセントを抜いたかどうか出先で心配せずに済む。 あるいはコンセントを抜くのを忘れて寝てしまい、 翌朝電気ポットのお湯が熱いままなのを見て愕然とするより (先月の電気使用量が 600kWh だったので驚いた)、 部屋が暗いときは自動的に電源が切れている方がいい (これはプログラムを書かなくても IFTTT だけで実現できる)。

というわけで持ってる IoT 機器を片っ端から IFTTT に登録したのだけど、 IFTTT に登録できない IoT 機器も残念ながら若干ある。 いまどき IFTTT に登録できない IoT 機器に何の意味があるのだろう? (今なら絶対に買わない) と思うのだけど、 IFTTT の便利さを知る前に買ってしまったのだから後悔先に立たず。 IFTTT の便利さを知ってからは、 お蔵入りになっていた。

EcoPlugs RC-028W

Eco Plugs もそんな「使えない」IoT 機器の一つ。 当時としては安価だった (今ならもっと安い) ので Walmart で購入してしまった。 Googleアシスタントや Amazonアレクサには登録できるのに、 肝心の IFTTT に登録できない。

といって通信プロトコルを解析しようにも、 いまどきの IoT 機器はクラウド (ベンダが運用するサーバ) と https で通信するので調べる取っ掛かりがない。 最後の手段、 分解するしかないのか?

ところがググっていると、 Eco Plugs は平文で通信しているという投稿を見つけた。 Eco Plugs はクラウドに登録しなくても、 同一 LAN セグメントならスマホアプリでコントロールできるが、 同一 LAN 内では平文の UDP パケットを飛ばしているらしい。

ありがたいことに Eco Plugs をコントロールする JavaScript プログラムが GitHub に公開されていた。 JavaScript は文法もロクに知らない (^^; のだけど、 見よう見まねで perl で書き直してみる:

#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Std;
use IO::Socket::INET;
our ($opt_v);
(getopts('v') && @ARGV == 3) || &help;
my ($ip, $id, $on) = @ARGV;

my $state = 0x0100;
$state = 0x0101 if $on eq "on";
my $buf = pack("H260", 0);
# Byte 0:3 - Command 0x16000500 = Write, 0x17000500 = Read
substr($buf, 0, 4) = pack("N", 0x16000500);
# Byte 4:7 - Command sequence num - looks random
substr($buf, 4, 4) = pack("N", rand(0xffffffff));
# Byte 8:9 - Not sure what this field is - 0x0200 = Write, 0x0000 = Read
substr($buf, 8, 2) = pack("n", 0x0200);
# Byte 16:31 - ECO Plugs ID ASCII Encoded - <ECO-xxxxxxxx>
substr($buf, 16, 16) = $id;
# Byte 116:119 - The current epoch time in Little Endian
substr($buf, 116, 4) = pack("L", time());
# Byte 124:127 - Not sure what this field is - this value works, but i've seen others 0xCDB8422A
substr($buf, 124, 4) = pack("N", 0xCDB8422A);
# Byte 128:129 - Power state (only for writes)
substr($buf, 128, 2) = pack("n", $state);

my $sock = IO::Socket::INET->new(PeerAddr => $ip, PeerPort => 80,
    Proto => 'udp', Timeout => 1) || die;
my $flags;
print unpack("H*", $buf) . "\n" if $opt_v;
print $sock $buf;
$sock->recv($buf, 1024, $flags);
print unpack("H*", $buf) . "\n" if $opt_v;

# Byte 10:14 - ASCII encoded FW Version - Set in readback only?
my $fwver = substr($buf, 10, 5);
# Byte 48:79 - ECO Plugs name as set in app
my $name = substr($buf, 48, 32);
$name =~ s/\0*$//;
printf("%s (ver %s)\n", $name, $fwver);

sub help {
    print <<EOF;
Usage: ecoplugs <opt> <IP> <ID> <on/off>
opt:   -v           ; verbose
EOF
    exit 1;
}

長さ 130 バイトの UDP パケット (変数 $buf) を作って Eco Plugs へ送信している (print $sock $buf;) だけなので、 いたってシンプル。 ユーザ認証もないので LAN 内なら誰でもコントロールできる。

Eco Plugs の IP アドレス (第1引数) と、 Eco Plugs の ID 「ECO-XXXXXXXX」(第2引数, XXXXXXXX は MACアドレスの第3〜6オクテット, ただし 16進数の A〜F は大文字限定)、 および「on」あるいは「off」の 3引数を付けて、 この perl プログラムを実行すると、 該当 Eco Plugs をオン/オフし、 Eco Plugs の名前 (スマホアプリで設定できる。以下の例では 「potplug」) と、 ファームウェアのバージョン (以下の例では 「1.6.3」) を表示する。

senri:~ $ ecoplugs -v 192.168.15.123 ECO-01234567 on
16000500940c163b020000000000000045434f2d303132333435363700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a625df5d00000000cdb8422a0101
160005000000163b0000312e362e330045434f2d30313233343536370000000000000000000000000000000000000000706f74706c7567000000000000000000000000000000000000000000000000003031323334353637000000000000000000000000000000000000000000000000a8e23b7ea625df5d00000000cdb8422a
potplug (ver 1.6.3)

「-v」オプションを付けた場合、 最初に表示される行が Eco Plugs へ送った長さ 130バイトの UDP パケット (260桁の 16進数)、 2行目が Eco Plugs から返ってきた長さ 128バイトの UDP パケット (256桁の 16進数)。 第1引数で指定した IP アドレスが Eco Plugs のものでなかった場合、 あるいは第2引数で指定した ID が間違っている場合など、 応答が返ってこない時は待ち続ける。 ID の 16進数において A〜F が小文字だと応答しないので注意。

Eco PlugsRC-028W (屋外用) および CT-065W (屋内用) で動作を確認したが、 おそらく同シリーズの他の機器でも使えるだろう。 Woods の WiON (スマホアプリが Eco Plugs そっくり) でも使えるらしい。

More...
2019年11月19日

IoT な人感センサをトリガーとした照明の点灯/消灯を IFTTT を使って行っていたけど反応が遅いので IFTTT をショートカットしてみた

さいきん流行りの IoT 機器。 多くの家電がネットからコントロールできるようになった。 IFTTT を使うと、 そういった機器を手軽に連携できるので便利。 IoT 機器同士だけでなく、 (私が管理する) WWW サーバを IFTTT がアクセスするように設定したり、 あるいは逆に私のサーバが IFTTT をアクセスする (トリガーを送る) こともできるので、 思いのままに IoT 機器を制御できる。

例えば、 人感センサで照明を点灯/消灯させる場合、 防犯用ライトなら人の動きを感知したときだけ点灯し、 人の動きが無くなれば速やかに消灯する、 といった単純なルールで充分だが、 部屋の照明となると人の動きが無くなったからと言ってすぐに消されては困る。 部屋を退出したことを確認してから消灯して欲しいし、 時間帯、あるいは在宅/不在時に応じて (さらにはその時々の天気に応じて)、 適切な点灯/消灯制御を行いたい。

つまり、 部屋の外にも人感センサを設置し、 部屋の中で人の動きが検知できなくなった後、 部屋の外で人の動きを検知すれば、 部屋を出ていったと判断し部屋の照明を消灯する。 さらに、 特定のスマホが LAN (家庭内 Wi-Fi) に接続していないときは不在とみなし、 部屋の外で人の動きを検知しただけでは照明を点灯しないけど、 そのスマホが LAN に接続した直後は帰宅したとみなし、 夜間であれば人の動きを検知したら速やかに点灯するなど。 私自身のサーバ (以下、「自サーバ」と略記) を IFTTT と連携させれば、 いくらでも複雑な制御ルールを設定できる。

IFTTT (IF This Then That) は、 その名の通り特定の条件 (This) が満たされたとき特定の動作 (That) を行わせることができる。 IoT 機器の多くは IFTTT との連携をサポートしているので、 例えば「This」として、 「人感センサが人の動きを検知」を設定し、 「That」として、 「照明をオン」を設定すれば、 単純な防犯用ライトが実現できる。

この連携に自サーバを絡めるには、 「This」および「That」を自サーバと結び付ければ良い。 それには IFTTT の 「webhooks」を用いる。

「This」は、 IFTTT の特定の URL をアクセスするだけ。 例えばこんな感じ:

senri:~ $ curl https://maker.ifttt.com/trigger/light_on/with/key/dD-v7GCx46LnWaF1AD9nwSUeA_N1ALvDHKS57cP1_Md
Congratulations! You've fired the light_on event

「light_on」の部分は任意に定めることができる。 「with/key/」以降の部分はユーザごとに IFTTT が割当てる認証用キー。 このキーが他人に漏れると勝手に操作されてしまうので適切な管理が必要。 そして、 「https://maker.ifttt.com/trigger/light_on/with/key/... へのアクセスがあった」(This) ならば、 「照明をオン」(That) を行う、 というルールを設定することで、 自サーバから照明を点灯させることが可能になる。

いっぽう 「That」 は、 IFTTT に自サーバをアクセスさせる。 例えば 「https://www.gcd.org/ifttt へ POST メソッドでアクセス」させる。 POST の body として json データを送るよう設定することができて、

{"magic": "0svikYKbcsxDbkty", "type": "Motion detected", "CreatedAt": "{{CreatedAt}}", "DeviceName": "{{DeviceName}}"}

などと設定する。 「"magic": "0svikYKbcsxDbkty"」は認証用。 https://www.gcd.org/ifttt は誰でもアクセスできるので、 "magic" の文字列が一致しないリクエストは無視する。 「"type"」は 「This」の機器の種類 (この例では人感センサ) を伝えるために設定。 「{{CreatedAt}}」と 「{{DeviceName}}」は、 「This」の機器が IFTTT へ送信したデータ。 例えば人感センサが検知 (This) すると IFTTT が次のようなアクセスを www.gcd.org へ行ってくれる (That)。

POST /ifttt HTTP/1.1
Content-type: application/json
host: www.gcd.org
content-length: 134
x-newrelic-id: ZW1uPtmAO9tRDSFGGvmp
x-newrelic-transaction: VGhpcyBpcyBmYWtlIHgtbmV3cmVsaWMtdHJhbnNhY3Rpb24uCg==
Connection: close

{"magic": "0svikYKbcsxDbkty",
 "type": "Motion detected",
 "CreatedAt": "November 19, 2019 at 09:15AM",
 "DeviceName": "廊下センサ"
}

この IFTTT からのアクセスを受信することで、 人感センサが人の動きを検知したことを自サーバが知ることができる。 そして自サーバにおいて様々な条件を加味した後、 前述した 「https://maker.ifttt.com/trigger/light_on/with/key/...」 へアクセスすれば照明を点灯することができる。

以上で、 IoT の連携に自サーバを絡ませることができるようになった。 ところがこの方法は、いかんせん遅い。 人感センサ ⇒ IFTTT ⇒ 自サーバ ⇒ IFTTT ⇒ 照明 などと IFTTT とのやりとりを 2度も行うため、 人の動きを検知してから照明が点灯するまで 6秒ほどかかってしまう。 部屋に入るまで 6秒も待てないので、 暗いままの部屋に入る羽目になる。 なお、 点灯するのは素早さが肝要だが、 消灯するのは数秒程度の遅れなら全く問題にならない。

More...
Filed under: システム構築・運用,ハードウェアの認識と制御 — hiroaki_sengoku @ 15:58
2019年9月4日

UEFI ブートでキーボードが無いと GRUB がハングするバグを修正してみた hatena_b

遅ればせながら手元の PC を MBR ブートから UEFI ブートに切り替えた。 ハードディスクの最初の 512バイト MBR (Master Boot Record) を読み込んで起動するのが MBR ブート。 Windows だと 2TB 超のディスクは MBR ブートできない (Linux ならブート可能)。 2TB では手狭になってきたのが切り替えを決意した理由だが、 ついでに Linux 専用マシンも PC のファームウェアが対応しているものは UEFI ブートに切り替えた。

UEFI (Unified Extensible Firmware Interface) だと、 普通の FAT32 ボリュームにブートローダのファイルを置いておくだけなので、 わざわざ MBR を書き換えたりするより簡単だし分かりやすい。 PC が起動しなくなった、などのトラブルはよくあるが、 トラブル発生時は気が急くし時間的余裕がないことも多いので、 トラブル時の作業は簡単であればあるほど、 分かりやすければ分かりやすいほど好ましい。

UEFI ブートに切り替えて 1ヶ月ほど経ったある日、 CPU を換装するために落としていた PC の電源を入れたら GRUB のメニュー画面でフリーズしたので私も凍り付いた。 CPU の性能をむやみに上げると、マザーボードとのミスマッチが起りがち。 せっかく買った新しい CPU が問題を起こしたのかと思った。 電源ボタンを長押しして強制的に電源を落とす。

BIOS 設定を確認するためにキーボードをつないで再度電源を入れてみる。 設定に何の問題もない。 続いて GRUB を立ち上げる。問題無く立ち上がる。 Linux を起動する。問題無い。 さっきのフリーズは何だったのだろう?

この時は因果関係に気付かなかったが、 キーボードをつないでいないと GRUB 2.04 (および最新版の 2.05 も) がメニュー画面のカウントダウンでハングする (最初の秒を表示したまま止まる)。 PC を再起動するときは、たいていキーボードをつないでいるので正常に起動し、 この症状は見たことがなかった。

実はこの時も、 なぜキーボードをつないでいなかったかというと明確な理由はない。 CPU 換装後の動作確認なのだからキーボードをつないでおくべきだと思うのだけど、 たまたまつなぐのを忘れていただけかも。 Web で 「headless GRUB hang bug UEFI without keyboard」 などを検索しても似た症例がほとんど見つからないのは、 キーボードをつながずに起動させる人がほとんどいないから?

とはいえ、 何台もあるサーバそれぞれにキーボードをつないでいては邪魔である。 意図して再起動するときはキーボードをつなぐなり KVM スイッチ (Keyboards, Video monitors, Mice Switch) を切り替えてキーボードが効く状態にするのが通常だが、 トラブルや Watchdog タイマーで勝手に再起動したとき、 あるいは遠隔から再起動させたときに、 立ち上がらずにフリーズしてしまったら非常に困る。 不測の再起動に備えて、 急遽テンキーをつないでおくことにした。

GRUB がキー入力を検知する状態であれば、 画面表示の有無自体は関係なくハングする。 逆に言うと、 GRUB がキー入力を検知しなければ、 例えば grub.cfg でキー入力を読む命令が無ければ (menuentry 等が無ければ) ハングしない。 例えば grub.cfg が次のように特定の Linux を起動するだけなら、 正常に起動できる。

insmod all_video
insmod part_gpt
insmod search_label
search --no-floppy --label --set=root /
linux /boot/linuz-4.19.69-x86_64 root=LABEL=/ resume=LABEL=swap ro
initrd /boot/initz-4.19.69-x86_64
boot

もちろんこれでは GRUB を使う意味がないが、 少なくとも問題が GRUB のキー入力関連にあることが分かった。 同じ PC、同じ GRUB でも、 UEFI ブートではなく MBR ブートなら、 キーボードをつながなくてもハングしない (MBR ブート専用の USB メモリを作って確認した。末尾の「おまけ」参照)。 また、UEFI ブートであっても UEFI ファームウェアによってはハングしない場合もあると思われる (未確認)。 が、少なくとも私の PRIMERGY MX130 S2 では確実に再現する。

というわけで、 問題の所在はおおむね絞り込めたので、 GRUB のソースコードを読み始めた。 並行して facebook に、 ことの顛末を書込んだ

すると野中尚道さんから grub-core/tem/efi/console.c が怪しそう、 とのヒントを頂いた。 ありがたい! なにぶん EFI のソースコードを読むのは初めてなので、 この時点ではまだ流れを追いきれていなかった。 efi/console.c の grub_console_getkey まわりを重点的に読んでみる。

grub-core/tem/efi/console.cのget_keyで key_exとkey_conを呼び分けている処理が失敗しているような気がします。 key_exの方はオプション機能なので実装有無を確認してるのですが

なるほど確かに grub_console_getkey_ex は、 grub_efi_open_protocol の返り値が NULL で無いとき (key_ex の実装が有るとき) に限り呼び出されているが、 grub_console_getkey_con には対応するものがない。 key_con は必ず実装されているから確認不要? でも、キーボードをつないでいない場合はどうなる? key_ex の有無を確認するコード:

text_input_ex_guid = GRUB_EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
        ...
text_input = grub_efi_open_protocol(grub_efi_system_table->console_in_handler,
                                    &text_input_ex_guid,
                                    GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);

をマネして、 key_con の有無を確認するコードをでっち上げてみる。 単に 「_EX_」 の部分を取り除いただけだが、 キーボードをつないでいるか否かを正しく検出しているようだ。

text_input_guid = GRUB_EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
text_input = grub_efi_open_protocol(grub_efi_system_table->console_in_handler,
                                    &text_input_guid,
                                    GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);

あとはキーボードの有無に応じて grub_efi_console_input_init の返り値を変えるだけ。 grub_efi_console_input_init が 1 (非ゼロ?) を返せば、 GRUB は grub_console_getkey を呼ばなくなるようだ。

以上をまとめると、 次のようなパッチになった:

diff --git a/grub-core/term/efi/console.c b/grub-core/term/efi/console.c
index 4840cc5..f2be32f 100644
--- a/grub-core/term/efi/console.c
+++ b/grub-core/term/efi/console.c
@@ -207,7 +207,12 @@ grub_efi_console_input_init (struct grub_term_input *term)
                                       GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
   term->data = (void *)text_input;
 
-  return 0;
+  if (text_input) return 0;
+  grub_efi_guid_t text_input_guid = GRUB_EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
+  text_input = grub_efi_open_protocol(grub_efi_system_table->console_in_handler,
+                                      &text_input_guid,
+                                      GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+  return text_input == NULL;
 }
 
 static int

わずか 6行だが、 これで GRUB がハングしなくなった。

More...
Filed under: システム構築・運用,プログラミングと開発環境 — hiroaki_sengoku @ 21:21
2019年8月26日

Windows 10 上の VMware Workstation Player のゲストOS でタグVLAN を使ってみる

Windows 上の仮想化ソフトウェアの定番 VMware Workstation Player で、 タグVLAN (tagged VLAN, IEEE 802.1Q) を使うことはできないと今まで思い込んでいたが、 レジストリエディタ (regedt32.exe) でレコードを一つ追加するだけで使えるようになった。 ググっても Windows 上のゲストOS でタグVLAN を使う話は見かけないのでメモしておく。

私の自宅内LAN はタグVLAN を利用している。 つまり物理的な配線は一本のイーサケーブルで、 複数のネットワーク (宅内LAN, DMZ, 対外セグメントなど) を同居させている。 タグVLAN はオフィス等で使われることが多いが、 美観上の理由からケーブルを何本も這わすわけにはいかない家庭内LAN においてこそ、 タグVLAN は有用と思う。

Linux OS などタグVLAN 対応の OS が走るマシンへは、 タグが付いたままのパケットを流し、 Windows OS などタグVLAN に非対応な OS が走るマシンへは、 スマートスイッチでタグを取り除いた (通常の) パケットを流している。

ところが、 マシンによっては Linux と Windows の両方の OS を走らせることがある (いわゆるデュアルブート)。 OS を切り替えるたびにスマートスイッチの設定を変更するのは面倒なので、 そういうマシンには Linux と Windows の両方がアクセスするネットワーク (つまり自宅内LAN) のパケットはタグ無しで流しつつ、 Linux のみがアクセスするネットワーク (DMZ や対外セグメント) のパケットはタグ付で流すことになる。

Windows が立ち上がってるときもタグ付パケットが届くが無視される。 Linux が立ち上がっているときはタグ付、タグ無し両方のパケットを受け取る。

さいきんは PC のメモリも大きくなり、 普段は Windows を使うマシンでも、 VMware Workstation Player (以下 VMware と略記) などの仮想化ソフトウェアを使って Linux をゲストOS として走らせておくことが増えてきた。 デュアルブートよりも同時に走らせておくほうが便利に決まってる。 となってくると、 ゲストOS でもタグVLAN を使いたくなるのが人情というもの。

ゲストOS でタグVLAN が扱えると、 本来 DMZ へ置くべきようなサーバ (WWW サーバとか) をゲストOS 上で動かすことが可能になる。 また、 ゲストOS でも自宅内LAN とインターネットとの両方のネットワークへアクセスできるわけで、 ルータの役割を担わせてもよい。 いずれにしても仮想マシンの応用範囲が一気に広がる。

しかし VMware を使って起動した仮想マシン上では、 (仮想)ネットワークインタフェースにタグ付パケットが上がってこないので、 ゲストOS でタグ付パケットを拾うことができない。 VMware がタグVLAN に対応していないのだろうと諦めていた。 まあタダで利用させてもらってるソフトウェアだし、 対応してなくても仕方がないなぁと。

ところが、 実は VMware 自体はタグVLAN に対応しているらしい。 Windows 10 がタグ付パケットを落としているから、 VMware までパケットが届かないということらしい。 細かく言えば Windows 10 が悪いというよりは、 ネットワークインターフェース (以下 NIC と略記) のドライバがタグ付パケットを落としているらしい。

私が使ってる Windows 10 では NIC が 「Broadcom NetLink (TM) Gigabit Ethernet」 と表示されるので、 「windows10 broadcom capture vlan」 あたりのキーワードで検索していたら、 Windows 上でタグ付パケットをキャプチャする方法について書かれたページを見つけた。

なお google では同じキーワードを使って検索しても、 このページを見つけ出すことはできなかった。 ニッチなものを見つけたい場合に、 google 検索が全く役に立たなくなったのは、 google がモバイル検索にシフトし始めた頃だったろうか? 探しているページが全く見つけられないので、 最近は google 検索を使わなくなってしまった。 google お得意の AI で賢く検索するより、 バカ正直にキーワード検索してくれたほうが (少なくとも私にとっては) 役に立つ。 Stay Foolish !

この見つけたページには、 Windows 上のパケットキャプチャソフトウェア Wireshark でタグ付パケットを見る方法が書かれている:

Display VLAN tags in Wireshark on laptops with Broadcom B57 chipsets から引用:

In order to make these tags visible to Wireshark, specialized drivers or specific NICs that support VLAN tags are usually needed. In the case of the Broadcom B57 chipset in some Dell Latitude laptops, the NIC itself supports VLAN tags (display only, it cannot actively tag outgoing traffic) with a small registry modification and a specific driver.

(意訳) Wireshark でタグ付パケットを見るには、 NIC ドライバがタグVLAN に対応している必要がある。 Broadcom B57 チップセット自体はタグVLAN に対応しているので、 ドライバのレジストリをいじればタグ付パケットを見ることができるようになる。 ただし見るだけでタグ付パケットを送信することはできない。

このページには 「送信できない」 と書いてあるが、 後述するようにゲストOS からタグ付パケットを送信できている。 Wireshark と VMware とでは違うのかも?

レジストリをいじるには、 まず NIC ドライバのインスタンスを見つけなければならない。 このページでは 「TxCoalescingTicks」 を検索せよと説いている。 Broadcom B57 チップセットのドライバのインスタンスであれば、 この名前のレコードを必ず持っているからだろう。

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{クラスID}\番号 を見つけたら、 このインスタンスに 「PreserveVlanInfoInRxPacket」 という名前で文字列値 「1」 を追加すればよい。

ちなみに、 「tx coalesce ticks」 を WWW で検索すると Broadcom 関連のページばかりが出てくる。 Broadcom 製 NIC 特有の機能なのかも? Linux の BCM5700 用ドライバ にも tx_coalesce_ticks というパラメータがあるようだ。

というわけで、 Broadcom 製 NIC でタグ付パケットを落とさないようにする方法は以下のようになる:

VLAN capture setup から引用:

  1. Run the Registry Editor (regedt32).
  2. Search for "TxCoalescingTicks" and ensure this is the only instance that you have.
  3. Right-click on the instance number (eg. 0008) and add a new string value.
  4. Enter "PreserveVlanInfoInRxPacket" and give it the value "1".

意訳:

  1. レジストリエディタ regedt32 を起動する
  2. 「TxCoalescingTicks」 を検索する。 見つけたインスタンスが (Backup 用のインスタンスを除いて) 唯一であることを確認する。
  3. 見つけたインスタンス番号 (例えば 0008) を右クリックし、 「新規 文字列値」 を追加する。
  4. 「PreserveVlanInfoInRxPacket」 を入力し、 その値を 「1」 とする。

Intel 製 NIC の場合も、 ドライバのレジストリをいじることで、 タグ付パケットを落とさないようにできるらしい。

レジストリをいじった後は、 再起動すれば NIC ドライバがタグ付パケットを捨てずに受け取るようになっている。 ドライバが捨てなければ、 タグ付パケットは Windows OS から VMware に渡され、 ゲストOS まで届く。 逆にゲストOS から発せられたタグ付パケットは、 Windows OS から NIC へ送られ、 ネットワークへ送出される。 つまりゲストOS がタグVLAN を扱えるようになった。

Filed under: システム構築・運用 — hiroaki_sengoku @ 09:06
2018年4月12日

NETGEAR のスマートスイッチ GS108E/GS116E のポートごとの通信量を取得して Cacti でグラフ化してみた 〜 通信量を見ることができない Wi-Fi中継機への対処法 〜

たまたま買った Wi-Fiアクセスポイント (以下 Wi-Fi AP と略記) が、 SNMP 非対応なのはもちろん、 Web管理画面でさえ通信量を見ることができなかったので、 この Wi-Fi AP を接続しているスイッチ (ネットワークハブ) NETGEAR GS108E (と GS116E) のポートの通信量を取得して Cacti (最近の流行りは Grafana ?) で可視化してみた。 Wi-Fi そのものの通信量ではないが、 Wi-Fi AP とスイッチとの間の通信量を測ることができる。

買ったのは NETGEAR WiFi中継機/802.11ac wave2 Nighthawk X4 EX7300-100JPS (以下 EX7300 と略記)。 たまたま amazon で税込5,980円 (送料無料) で売っていて、 中継機 (ワイヤレスエクステンダー) としてだけでなくアクセスポイントとしても使えるとのことなので (NETGEAR製ということもあって) 衝動買い。 もともとルータの機能は必要ないし、 コンセント直挿しで場所を取らないので、 リビング用としてちょうどいいかなと思った次第。

さいきん Wi-Fi中継機が流行りらしく、 各社からお手頃価格で多数の製品が発売されている。 コンセント直挿しで場所を取らないので家庭向きと言えるが、 残念なことにアクセスポイントとして使える製品は (NETGEAR や TP-Link などのごく一部の例外を除くと) ほとんど無い。
中継機も (広義の) アクセスポイントと言えるが、 親機 (Wi-Fiルータ) と無線でつながるのが中継機で、 有線でつながるのが (狭義の) アクセスポイント。 あたりまえだが、 有線でつなぐことができる (家庭内でケーブルを配線できる) なら、 有線のほうがいいに決まってる。
ちなみに私が初めて Wi-Fi中継機を使ったのは 10年前なので、 いまさら流行って少々戸惑いを覚える。

EX7300 は、 たった 6000円なのに性能的には悪くない。 ルータ機能が不要ならお買得と思う。 ノートPC Lavie HZ を Wi-Fi で EX7300 につないで iperf3 を走らせて測定してみたら 530Mbps くらい出ている。

C:\Users\sengoku>c:\bin\iperf3.exe -c esaka -P 5
Connecting to host esaka, port 5201
[  4] local 192.168.18.147 port 50119 connected to 192.168.18.20 port 5201
[  6] local 192.168.18.147 port 50120 connected to 192.168.18.20 port 5201
[  8] local 192.168.18.147 port 50121 connected to 192.168.18.20 port 5201
[ 10] local 192.168.18.147 port 50122 connected to 192.168.18.20 port 5201
[ 12] local 192.168.18.147 port 50123 connected to 192.168.18.20 port 5201
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-1.00   sec  13.4 MBytes   112 Mbits/sec
[  6]   0.00-1.00   sec  13.2 MBytes   111 Mbits/sec
        ...

[SUM]   9.00-10.01  sec  63.9 MBytes   533 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-10.01  sec   130 MBytes   109 Mbits/sec                  sender
[  4]   0.00-10.01  sec   130 MBytes   109 Mbits/sec                  receiver
[  6]   0.00-10.01  sec   128 MBytes   108 Mbits/sec                  sender
[  6]   0.00-10.01  sec   128 MBytes   108 Mbits/sec                  receiver
[  8]   0.00-10.01  sec   128 MBytes   107 Mbits/sec                  sender
[  8]   0.00-10.01  sec   128 MBytes   107 Mbits/sec                  receiver
[ 10]   0.00-10.01  sec   126 MBytes   106 Mbits/sec                  sender
[ 10]   0.00-10.01  sec   126 MBytes   106 Mbits/sec                  receiver
[ 12]   0.00-10.01  sec   124 MBytes   104 Mbits/sec                  sender
[ 12]   0.00-10.01  sec   124 MBytes   104 Mbits/sec                  receiver
[SUM]   0.00-10.01  sec   636 MBytes   534 Mbits/sec                  sender
[SUM]   0.00-10.01  sec   636 MBytes   534 Mbits/sec                  receiver

iperf Done.

たまたま手元にあった TP-Link WiFi 無線LAN ルーター Archer C3150 11ac にも Wi-Fi でつないで同様に iperf3 を走らせて比較してみたら、 ほとんど差がない。 同等と言ってもいいレベル。 値段は 3倍くらい違うのだから、 EX7300 をブッチぎって欲しかった。

しかもこの Archer C3150 は OpenVPN を使ってると腐るバグがあるように感じる (いちど電源を切らないと復旧しない)。 ヘンテコな付加機能よりも安定性こそ一番重要なのではないか? ついでに言うと勝手に外部と通信しているのが気持ち悪い。 値段が高い Wi-Fiルータにはもうちょっと頑張ってもらいたいところ。

もちろん、 このノートPC を無線LAN ではなく有線LAN につなげば 940Mbps くらいは出るので、 ノートPC の性能がボトルネックになっているわけではない。 というか Wi-Fi のパフォーマンスは周囲の電波状況に強く影響され (もちろん空いてるチャンネルを使っている)、 有線側の状況はほとんど関係がない。 電波暗室とかで測定すれば、 EX7300 と Archer C3150 の差が開くのかもしれないが、 実用上は無意味。

性能でも安定性でも申し分のない EX7300 ではあるが、 残念なことに Web管理画面を隅から隅まで調べてみても通信量を見る方法がない。 どんな安物 Wi-Fiルータ (or Wi-Fi AP) でも、 通信量 (パケット数とか) を表示させる方法はあるものだと思っていたが、 中継機は Wi-Fiルータとは製品コンセプトが違うのかも?

通信量を見ることができない通信機器があるとは思っていなかったので、 意表を突かれた感じ。 今後 Wi-Fi AP を買う際は注意したい。 やっぱり (ルータ機能が必要なくても) 素直に Wi-Fiルータを買うべきなのかも。 ほんとうは (ルータ機能がない) アクセスポイント専用機が欲しいのだけど...

幸い、 この EX7300 を接続しているスイッチ NETGEAR GS108E が「スマート」で、 スイッチの各ポートごとの通信量 (≒ Wi-Fi AP の通信量) を GS108E/GS116E 付属の Windows アプリ ProSafe Plus で見ることができる。

ProSafePlus GS116E Port Statistics

Web アプリではなく Windows アプリなので、 表示された通信量の数値を Web スクレイピングなどのお手軽な方法で取り込むことはできないが、 google で検索したら ProSafe Plus と同等のことができる Python プログラムが見つかった。 6年前に開発が終了しているが、 このスイッチを買ったのも 6年前なので無問題。

さっそくダウンロードして (ざっと内容を確認したのち) 走らせてみる:

senri $ git clone https://github.com/Z3po/Netgearizer.git ↵
Cloning into 'Netgearizer'...
remote: Counting objects: 88, done.        
remote: Total 88 (delta 0), reused 0 (delta 0), pack-reused 88        
Unpacking objects: 100% (88/88), done.
Checking connectivity... done.
senri $ Netgearizer/netgearizer.py ↵
please select one of the following switches with "selectSwitch $NR"
--> 0: 192.168.18.239
Information: 
switch-name: living
switch-dhcp: disabled
switch-type: GS108Ev2
switch-ip: 192.168.18.239
switch-mac: 4c:60:de:69:01:23
switch-firmware: 1.00.06
switch-netmask: 255.255.255.0
switch-gateway: 192.168.18.1
(Cmd) 

スイッチは 3つ (GS116E と GS108E 2台) あるのに、 なぜか 1つ (リビングに設置した GS108E) しか表示されない。 selectSwitch コマンドでスイッチを選択し (選択肢は 1つしか無いが)、 getPortStatistics コマンドでポートごとの通信量を表示できるようだ。

実行してみる:

(Cmd) selectSwitch 0 ↵
(Cmd) getPortStatistics ↵
switch-port-statistics: 
 -> Port 01: 
   >> send: 2664610542955
   >> receive: 5993649462240
   >> crcerrors: 0
 -> Port 02: 
   >> send: 6062498001
   >> receive: 328715447216
   >> crcerrors: 0
 -> Port 03: 
   >> send: 183424114643
   >> receive: 8723285000144
   >> crcerrors: 0
 -> Port 04: 
   >> send: 39935317815
   >> receive: 26550837425456
   >> crcerrors: 0
 -> Port 05: 
   >> send: 137960667631
   >> receive: 790398362288
   >> crcerrors: 0
 -> Port 06: 
   >> send: 3948410207
   >> receive: 1156872131840
   >> crcerrors: 0
 -> Port 07: 
   >> send: 2789623
   >> receive: 20875141728
   >> crcerrors: 0
 -> Port 08: 
   >> send: 1989939088
   >> receive: 2852348890112
   >> crcerrors: 0
(Cmd) 

一見、 正しく動いているように見えるが、 「send:」に表示されている数値は (ProSafe Plus で見たときの) 受信バイト数で、 「receive:」に表示されている数値は送信バイト数を 16倍した値になっている。

受信と送信が入れ替わっているのはご愛敬だが、 16倍になっているのは... netgearizer.py (Python で書かれたスクリプト) を読んでみると、 単純なバグだった。 次のパッチをあてると正常に動作した:

--- netgearizer.py.org        2018-04-07 22:04:42.292912972 +0900
+++ netgearizer.py        2018-04-09 11:34:50.124297214 +0900
@@ -284,5 +284,5 @@
             for port in hexvalue:
-                sendstats = self.__convertFromHex(port[2:18],'cipher')
-                receivestats = self.__convertFromHex(port[19:35],'cipher')
-                crcerrors = self.__convertFromHex(port[36:53],'cipher')
+                receivestats = self.__convertFromHex(port[2:18],'cipher')
+                sendstats = self.__convertFromHex(port[18:34],'cipher')
+                crcerrors = self.__convertFromHex(port[82:98],'cipher')
                 result.append(( 'Port ' + str(port[:2]), (('send', sendstats), ('receive', receivestats), ('crcerrors', crcerrors))))
@@ -345,15 +345,16 @@
                 for key in self.switches.keys():
                     self.switchList.append(key)
                     print '--> ' + str(counter) + ': ' + key
                     print 'Information: '
                     self.selectedSwitch = key
                     self.__printResult(True)
                     self.selectedSwitch = None
-                    return True
+                    counter += 1
+                return True
             else:
                 print 'please select one of the switches you get with getSwitches first.'
                 return False
 
         if 'ERROR' in resultdict:
             found = None
             for key in  self.switchattributes.keys():

送信バイト数が 16倍になるのは、 部分文字列の切り出しで「port[18:34]」 (変数「port」の文字列の 18文字目から 34文字目まで切り出す) とすべきところを、 「port[19:35]」 としてしまっていたため。 変数「port」には、 スイッチから受信したデータを 16進数に変換した文字列が格納されている。 1文字ずれて切り出したため、 16倍 (16進数で 1桁ぶん) になっていた。

16進数で 16文字つまり 64bit だが、 ProSafe Plus で表示される GS116E の送受信バイト数が小さすぎると思っていたら、 GS116E はバイト数が 32bit (4GiB) でラップアラウンドしているようだ。 つまり上位 32bit は常にゼロ。ポートによっては毎日ラップアラウンドしている。
GS108Ev2 は 40bit (1TiB) を超えるバイト数になっているポートもあるので、 どこでラップアラウンドするか確認できるまで、 まだだいぶかかりそう。 仮に 1ヶ月で 1TiB くらいの通信量だとすると、 もし 48bit (256TiB) が上限なら 20年もかかってしまう。
もし 64bit 全部使ってカウントしているなら、 上限は 16EiB (エクスビバイト) ということになり、 140万年くらいかかってしまう。 ちょっとのコスト増を惜しんでラップアラウンドするより、 64bit きっちり使って半永久にラップアラウンドしないほうがいい。

実は、 私は今まで Python でプログラミングしたことが無い。 文法もろくに知らなかったのだけど、 部分文字列を切り出す際の Python の位置の指定方法は独特だと思う。 このプログラムの作者も Python は慣れていなくて、 うっかりこのようなバグを作り込んでしまった (前の行で 18文字目まで切り出したので、 その次は 19文字目からとしたくなる気持ちは分かる) のではないか?

さらに興味深いのが、 3つあるはずのスイッチが 1つしか見つからなかったバグ。 なんと、 「return True」文のインデント (字下げ) が間違っていただけ。 インデントでブロックを表わす Python ならではのバグ?

インデントが一段階深かったので、 「return True」文が forループの中に入ってしまい、 必ずループが 1回だけで終了してしまっていた。 だからスイッチを 1つ見つけてすぐループを終了していた。 「return True」文の前の空白を削除してループの外へ出したら、 正しく 3つのスイッチを見つけるようになった。 空白の有無で挙動が変わってしまうのは、 やっぱり気持ち悪い。

この netgearizer.py は対話的にコマンドを入力するソフトウェアだが、 通信量を自動計測する際には使いにくいので、 対話せず通信量を取得するだけのプログラムに書き換えた。 不要な機能をばっさり削除したので、 プログラムの行数が半分以下の 317行になった。 この改変版は、

https://www.gcd.org/sengoku/docs/netgearizer.py

からダウンロードできる。 わずかな改変とはいえ、 なにぶん私が初めて書く Python プログラムなので、 変なところがあったらご指摘頂けると幸い。

この改変版を実行する (引数としてインタフェースを指定する) と、 以下のように LAN 内 (同一セグメント内) の全ての NETGEAR スマートスイッチ (GS116E, GS108E, GS105E など) の、 各ポートの受信バイト数、送信バイト数、エラー数を表示する。

senri:~ $ netgearizer.py eth0 ↵
IP:   192.168.18.239
MAC:  4c:60:de:69:01:23
Type: GS108Ev2
switch-port-statistics: 
 Port01 rx:2673873720246 tx:375671471563 err:0
 Port02 rx:6410023533 tx:21992185424 err:0
 Port03 rx:183424114643 tx:545205312509 err:0
 Port04 rx:40052812544 tx:1664344574414 err:1
 Port05 rx:138616484625 tx:52251007903 err:0
 Port06 rx:3954090302 tx:72431873899 err:0
 Port07 rx:2802623 tx:1308888305 err:0
 Port08 rx:1989939088 tx:178271805632 err:0
IP:   192.168.18.237
MAC:  4c:60:de:69:04:56
Type: GS108Ev2
switch-port-statistics: 
 Port01 rx:786331851 tx:1283347 err:0
 Port02 rx:0 tx:0 err:0
 Port03 rx:0 tx:0 err:0
 Port04 rx:0 tx:0 err:0
 Port05 rx:0 tx:0 err:0
 Port06 rx:474556 tx:875905 err:0
 Port07 rx:0 tx:0 err:0
 Port08 rx:0 tx:0 err:0
IP:   192.168.18.236
MAC:  84:1b:5e:90:81:72
Type: GS116E
switch-port-statistics: 
 Port01 rx:2245190893 tx:3192491053 err:0
 Port02 rx:2079058908 tx:4030809209 err:0
 Port03 rx:0 tx:0 err:0
 Port04 rx:2815311290 tx:1592352771 err:0
 Port05 rx:3838533539 tx:2573311206 err:0
 Port06 rx:1441584273 tx:1249733039 err:0
 Port07 rx:747795050 tx:97733030 err:0
 Port08 rx:889171012 tx:559797414 err:0
 Port09 rx:3757714660 tx:749158734 err:0
 Port0a rx:3210386057 tx:2724670950 err:0
 Port0b rx:441710237 tx:2183781562 err:0
 Port0c rx:0 tx:0 err:0
 Port0d rx:4837745 tx:252496639 err:0
 Port0e rx:2058650283 tx:3610878767 err:0
 Port0f rx:1435416601 tx:403439849 err:0
 Port10 rx:0 tx:0 err:0
senri:~ $ 

私の自宅の LAN ではタグVLAN を使っているため、 以上のコマンドを実行している Linux マシン「senri」の eth0 インタフェースには IP アドレスを割当てていないが、 問題なく動作している。 しかも、 この Linux マシンは GS116E の Port04 に接続していて、 2台の GS108E には GS116E 経由で間接的に繋がっているのだけど、 GS108E の通信量も取得できている。 Windows アプリの ProSafe Plus だと、 スイッチに直接繋げないと管理できないことがある。

改変版 netgearizer.py で取得した送受信バイト数を Cacti (最近の流行りは Prometheus とか Fluentd ?) に読み込ませて EX7300 の通信量をグラフ化すると、 こんな感じ:

NETGEAR EX7300 Cacti

In がスイッチのポートの送信バイト数、 つまりスイッチから EX7300 が受信したバイト数、 すなわち Wi-Fi 子機 (スマホなど) がダウンロードした合計。

Out がスイッチのポートの受信バイト数、 つまりスイッチへ EX7300 が送信したバイト数、 すなわち Wi-Fi 子機がアップロードした合計。

実は ProSafe Plus のもう一つの Python 実装である ProSafeLinux も試してみたのだが、 IP アドレスを割当てていないインタフェースを受付けないので、 先に netgearizer.py の書き換えを試みた次第。

ProSafeLinux のほうが (active ではないにせよ) 今でも開発が続いているし、 前述したようなバグも無いようなので、 試してみるべきだとは思うが、 プログラムの行数が netgearizer.py の倍以上 (私が作った改変版の 5倍以上) もある。 通信量の取得といった単純な目的であれば、 短い方がいい。

Filed under: システム構築・運用 — hiroaki_sengoku @ 10:10
2013年12月13日

nexus5 の充電をワイヤレス (Qi 給電) にしたので、バックアップもワイヤレスにしてみた 〜 ssh サーバを nexus5 上で動かす 〜 hatena_b

昨今の円安のため香港ドルが高い (>_<)。 昨年は 10円/HK$ 台だったのに、いまや 13円/HK$ を超えている。 香港 (に限らないが) の人たちが大挙して日本に買物に来る気持ちが分かる。 これだけ円安が進めば、 日本じゅうどこへ行っても、 全てのものが安く感じられるのだろう。

ARENA Scientific Icey QI Charging Pad

逆に、 日本人が香港へ行くと、全てのものが以前より 3割ほど高く見えるわけで、 物欲が萎えてしまいほとんど買物しなかった (おまけに、香港に行く直前に日本で nexus 5 を買ってしまったし)。 とはいえ、 香港までわざわざ行っておきながら何も買わないというのもアレなので、 Qi 充電器を買ってみた。 深水埗 黄金電腦商場 地下35號舖 Sunny Computer Digital Co 力生電腦數碼公司 →

HK$199 = 約2700円なので、 ちっとも安くない。 「おにぎり」 こと、 ワイヤレスチャージャー 03 なら 2200円くらいで売っているが、 「おにぎり」 の電源が専用アダプタであるのに対し、 これは汎用の micro USB ケーブルが使えるのでよしとしよう。

この Qi 充電器を USB ハブの余っているポートにつないで机の上に置き、 その上に nexus 5 を置く。 使ってみると想像以上に便利。 電話がかかってきたとき、 以前はいちいち USB ケーブルを抜いていたのだけど、 Qi 充電器ならサッと nexus 5 を手に取れるし、 電話が終わったら Qi 充電器の上に戻すだけ。 はやく全てのケータイが Qi に対応して、 喫茶店などのテーブルに Qi 充電器を標準装備して欲しいなどと思う今日このごろ。

充電がワイヤレスなのに、 PC との通信に USB ケーブルを使っていては片手落ちである。 私は普段 rsync を使って nexus 5 上のデータを丸ごと PC へバックアップしているが、 有線な adb (Android Debug Bridge) を使うのは止めて Wi-Fi を使うことにした。

ここで注意したいのは、 主導権を握る (コントロールする) のは PC 側でなければならない、 ということ。 スマホ側 (nexus 5) が主導して rsync を起動するアプリならすでにいくつか出回っているが、 PC が目の前にあるときに何が嬉しくてスマホの小さい画面をいじらなきゃならないのかと思う。 Qi 充電器の上に置いたら、 もう 1 タッチといえどスマホには触りたくない。 全ての作業は PC のキーボードで完結させたい。

つまり、 PC がクライアントとなり、 スマホをサーバとして扱いたい、 ということ。 サーバのキーボードやモニタはトラブル発生時でもなければ使わないのと同様、 家にいるときはスマホは充電器の上に置きっぱなしにしておきたい。 あるいは寝室専用になってしまっているスマホ (各部屋に一台以上、専用スマホ/タブレットが置きっぱなしにしてある) は、 寝室に置きっぱなしのままで (別の部屋の) PC から操作したい。 スマホを PC から操作できれば、 コマンド一発で、 家じゅうのスマホ (10台くらいある) をいっぺんにバックアップしたり、 相互にデータを同期させたり、 何でも思いのまま (^^)。

スマホをサーバとして扱うには、 スマホ上で ssh サーバを動かしておけばよい、 ということで早速 dropbear サーバ (軽量 ssh サーバ) と rsync を nexus 5 (だけでなく私が持っている全ての android スマホ) にインストールした。

以下、インストールのメモ:

More...
Filed under: Android,システム構築・運用 — hiroaki_sengoku @ 09:07
2013年3月30日

UCOM光電話の VoIP アダプタ (Aterm BH812V) を LAN 内に設置してみた tweets

UCOM光が導入されているマンションに住んでいるので UCOM光電話を使っている。 UCOM から貸与された VoIP (Voice Over IP, IP 電話) アダプタは、 グローバル IP アドレスが必要と説明書にあった。 UCOM光で各戸に割当てられるグローバル IP アドレスは 5個しかないのに、 うち 1個を VoIP アダプタごときに専有されるなんてトンデモナイ。 なんとか VoIP アダプタを、 次図のように LAN 内に設置することはできないか?

                    宅内 ←:→ UCOM
                        :       :
      LAN    ┌─────┐  DMZ   :       :
           │NAT 機能付│      :       :
───┬────┬──┤ルータ  ├──┬───────┬────→ インター
   │    │  └─────┘  │   :   │   :  ネット
┌──┴─┐┌─┴─┐   :   ┌─┴─┐ : ┌─┴─┐ :
│ VoIP ││ PC等 │   :   │公開 │ : │SIP  │ :
│アダプタ│└───┘   :   │サーバ│ : │Proxy │ :
└────┘        :   └───┘ : └───┘ :
              :
プライベート IP アドレス  :    グローバル IP アドレス

SIP (Session Initiation Protocol, VoIP で使われるプロトコル) は NAT と相性が悪いとはよく言われるが、 それは UA (User Agent) 同士が直接通話する場合の話。 SIP Proxy 経由で通話する場合であれば、 端末 (VoIP アダプタ等) は特定の SIP Proxy に接続すれば事足りるので、 端末が NAT の内側 (つまり LAN 内) でも何の問題もない。 だから VoIP アダプタにグローバル IP アドレスが必要と言われても納得しかねる。

私の場合、 VoIP アダプタとして Aterm BH812V (UZ) を貸与された。 このアダプタに、 フツーの (アナログ) 電話機をつなぐとフツーの (050 な電話番号ではなくフツーの市外局番な番号の) 固定電話として使える。 BH812V は VoIP アダプタなのに VoIP 関連の設定項目が全く無く、 ましてプライベート IP アドレスで使うための設定方法など望むべくもない。 仕方がないのでとりあえずそのまま BH812V を LAN につないでみた。 上図中 「NAT 機能付ルータ」 は実際は Linux マシンで、 このマシン上で DHCP (Dynamic Host Configuration Protocol) サーバを走らせている。 つまり、 この DHCP サーバから BH812V に対してプライベート IP アドレスが割当てられる。

BH812V 前面

BH812V の筐体には、 「ステータス」 「電話サービス」 「電話1」 「電話2」 「インターネット」 と書かれた 5 つのランプがあり、 正常な定常状態では全て緑色に点灯する。 ところが、 BH812V を LAN につないで電源を入れると、 「ステータス」 が橙点滅、 「インターネット」 が橙点灯したままになり、 「電話サービス」 他のランプは消灯したままになってしまった。 もちろん、 BH812V につないだ電話機は使用不能。 BH812V のログはこんな感じ:

2013/03/29 00:39:15 NAT GET 192.168.10.130 (IP-PORT=1)
2013/03/29 00:39:15 WAN  Port is UP for Local Router mode
2013/03/29 00:39:15 WAN  Connect request from 192.168.19.1 for Local Router mode
2013/03/29 00:39:15 DHCP_Client IP ADDRESS Get 192.168.10.130 (IP-PORT=1)

プライベート IP アドレス 192.168.10.130 が配布されたことは分かるが、 SIP 関連のエラーは全く出力されていないので、 このログではなぜ電話機が使えないか何も分からない (>_<)

ちなみに、 BH812V にグローバル IP アドレス 122.218.XX.XX を割当てて正常に動作したときのログはこんな感じ:

2013/03/29 00:51:09 NAT GET 122.218.XX.XX (IP-PORT=1)
2013/03/29 00:51:09 WAN  Port is UP for Local Router mode
2013/03/29 00:51:09 WAN  Connect request from 192.168.19.1 for Local Router mode
2013/03/29 00:51:09 DHCP_Client IP ADDRESS Get 122.218.XX.XXX (IP-PORT=1)

電話が使えない異常状態と、正常状態とで、 ログが (割当てられた IP アドレス以外は) 全く同じというのはいかがなものか。

そこで、 BH812V にグローバル IP アドレスを割当てたときと、 プライベート IP アドレスを割当てたときとで、 どのような挙動の違いがあるか調べてみる。 まず UCOM DHCP サーバから グローバル IP アドレスを割当てるとき、 BH812V と UCOM との間でどのような通信が行なわれるか? BH812V と UCOM との間にブリッジ (という名の Linux マシン) を挟んで、 どのようなパケットがやりとりされているか tcpdump (ネットワークを流れるパケットを表示するツール) で調べてみる。

   ┌────┐  ┌─────┐  宅内 ←:→ UCOM
   │ VoIP ├──┤ブリッジ ├──┐   :
   │アダプタ│  └─────┘  │   :       :
   └────┘  ┌─────┐  │   :       :
           │NAT 機能付│  │   :       :
────────┬──┤ルータ  ├──┼───────┬────→ インター
        │  └─────┘  │   :   │   :  ネット
      ┌─┴─┐       ┌─┴─┐ : ┌─┴─┐ :
      │ PC等 │       │公開 │ : │SIP  │ :
      └───┘       │サーバ│ : │Proxy │ :
                  └───┘ : └───┘ :

上図のように 「VoIP アダプタ」 (BH812V) を LAN から (ブリッジを介した) DMZ へつなぎ替えたわけだが、 このようなネットワーク構成の変更が、 物理的な配線変更を行なわずにできるのが、 スマートスイッチならでは (^^)v。

More...
Filed under: システム構築・運用 — hiroaki_sengoku @ 22:53
2013年3月19日

GS108E / GS116E の ProSafe Plus 設定ユーティリティが 「HTTP request error」 で使えないときの対処法

安価なスマートスイッチ GS108E / GS116E の設定ユーティリティ 「ProSafe Plus」 を起動したとき、 以下のようなエラーダイアログが表示されて使用不可能に陥ることがある:

Web サービス サーバと通信中にエラーが生じました。エラーメッセージ:HTTP request error

「サーバと通信中にエラーが生じました」 と言われても、 そもそもこの 「ProSafe Plus 設定ユーティリティ」 はサーバと通信するツールではないので途方に暮れてしまう。 「サーバ」 というのはこのツールの設定対象であるスイッチ GS108E/GS116E のことなのか? この設定ユーティリティは起動時にスイッチを探す (UDP broadcast) が、 もちろんプロトコルは HTTP ではない。

「HTTP request error」 というエラーメッセージが唯一の手がかりなので、 とりあえず google で検索してみる。 私と同様に途方に暮れている人が何人もいるようだ。

幸い、 そのうちの一人が解決方法を発見していた:

FIXED: Prosafe Plus Configuration Utility - HTTP request error

I Fixed it!!! : The problem seemed to be caused by the 'Internet Connection Sharing Service (ICS)'.
Some how I have been configuring this service on my laptop. When I stop this service, the utility works again. Because I do not need this service most of the time I changed the startup mode of this service to manual.

ICS ? そんなものを設定した覚えはないのだけど... と思いつつ、 「ネットワークと共有センター」 から 「アダプターの設定の変更」 を選び、 ネットワーク アダプターの 「プロパティ」 を開いて 「共有」タブを選択する (Windows7 あるいは Windows8 の場合)。 すると...

インターネット接続の共有 ネットワークのほかのユーザに、このコンピューターのインターネット接続をとおしての接続を許可する

がーん、 「インターネット接続の共有」(ICS) が許可されている orz.
誰が許可したんだ?

そういえば先月 Wi-Fi USBアダプタ GW-USFang300 を購入 (e-trend で 1280円で売っていたので衝動買い ^^;) した際、 この PC を Wi-Fi アクセスポイントとして使用したのだった。 アクセスポイントなので、 当然 ICS を許可することになる。 一時的に使用しただけなので、 今はこの Wi-Fi USBアダプタを外しているのだが、 ICS が許可されたままになっていたのだろう。

この 「ネットワークのほかのユーザに、このコンピューターのインターネット接続をとおしての接続を許可する」 のチェックを外して 「OK」 を押したところ、 無事 「ProSafe Plus 設定ユーティリティ」 が使えるようになった。 よかった〜

なお、 上記 google 検索で見つけた解決方法は、 「I changed the startup mode of this service to manual」 すなわち 「『Internet Connection Sharing (ICS)』 サービスの 『スタートアップの種類』 を 『手動』 にした」 とあるが、 ネットワーク アダプターの 「プロパティ」 で ICS 許可のチェックを外しても同じ効果が得られるし、 「プロパティ」 で設定変更するほうが素直な対処方法だと思う。

Filed under: システム構築・運用 — hiroaki_sengoku @ 10:41
2012年9月4日

個人でも気軽に買える安価なスマートスイッチ GS108E (IEEE 802.1Q タグVLAN 機能付) を使ってみた 〜 UCOM光 マンション全戸一括タイプの戸内配線上にプライベートネットワークを構築 hatena_b

先日、 UCOM光 マンション全戸一括タイプが導入されているマンションに引っ越した。 各部屋に LAN コンセントがあり、 UTPケーブルをつなぐだけでインターネットに接続できる。 USENのスピードテストで測定したところ、 50Mbps 以上の通信が可能であるようだ。 個人ユースであれば充分な帯域。

     UCOM
       │
       │
   ┌───┴───┐
   │ マンションの│
   │  ルータ  │
   └┬┬┬┬┬┬┬┘
┌───┘│││││└───┐
│ ┌──┘│││└──┐ │
│ │ ┌─┘│└─┐ │ │
: : :  │  : : :
 各戸へ   │   各戸へ
       │
    ┌──┴──┐
    |各戸のハブ| /29
    └┬─┬─┬┘
   ┌─┘ │ └─┐
   │   │   │
   ↓   ↓   ↓
  各部屋の LAN コンセント

各戸に半固定な /29 グローバル IP アドレス空間 (つまり 8個の IP アドレス) が割当てられていて、 LAN コンセントに PC 等をつなぐと、 マンション内に設置されたルータが DHCP でグローバル IP アドレスを配布する。 8個のアドレスのうち、 1個はこのルータが使い、 ネットワークアドレス (下位 3ビットが 000) とブロードキャストアドレス (下位 3ビットが 111) は配布されないので、 実際に配布されるのは (下位 3ビットが 010 〜 110 の) 5個のアドレスのみ (IPv4 アドレスが枯渇している昨今、 3/8 を無駄にするのはいかがなものか?)。

UCOM に問合わせたところ、 各戸のハブ (以下、ここでは 「戸ハブ」 と呼ぶ) は、 浴室の天井裏に設置されているらしい。 また、 各戸に割当てられるグローバルアドレスは、 通常は変化せず (機器構成の変更等で変わる可能性がある)、 全てのパケットはフィルタリングせずに届けられるらしい。

インターネット上の任意のホストから任意の通信が可能であるわけで、 (特に XP 以前の脆弱な) Windows マシン等を LAN コンセントに直結するのは、 セキュリティ上あまりよろしくない (有料オプションで、セキュリティ対策サービスがあるらしい)。 また、 5台以上の PC をつなぎたい場合もあるだろう。 特に UCOM光電話を使う場合は、 IP電話アダプタがアドレスを一つ使う (使わずに済ませる方法) ので、 残り 4個になる。 フツーの家庭でも 4個では足らなくなるのでは? もちろん私の場合は 12個でも足らない。;-)

普通は、 PC を LAN コンセントに直結してグローバルアドレスを使うのではなく、 NAT 機能付ルータ (無線LAN ルータ等) を LAN コンセントにつないで、 PC にはプライベートアドレスを割当てることになるだろう。 各部屋で有線LAN を使いたい場合は、 各部屋にルータ (以下、 「部屋ルータ」 と呼ぶ) を用意して PC をつなぐことになる。

UCOM によると、 マンションによっては、 戸ハブの代わりにルータを各戸に設置して、 プライベートアドレスを配布しているケースもあるとか。 セキュリティを気にするのであれば、 戸ハブをルータと交換してもらっても構わないとまで言っていたが、 私はグローバルアドレスを使いたいので、 現状の仕様のほうが嬉しい。

ところが、 この方法だと各部屋の LAN は互いに異なるプライベートネットワークになってしまい、 異なる部屋 (そういくつも部屋があるわけでもないのだが ^^;) の PC と通信したいとき困る (居間にサーバを置くのは無粋なので、 ネットワークメディアプレーヤを使う場合とか)。 各部屋ルータの間で VPN を張って、 各部屋のプライベートネットワークを一つにする方法もあるが、 VPN のオーバヘッドがモッタイナイ。

もちろん、 部屋間に (廊下などに) UTP ケーブルを這わせれば済む話だが、 美観上好ましくないのと、 私の場合は購入ではなく賃貸で借りたマンションなので、 むやみにケーブルを配線するのも憚られる。 各LAN コンセントは、 壁裏配線を介して同じ戸ハブ (天井裏の写真 ↓ ダムハブ ^^;) につながっているのだから、 一つの LAN コンセントからパケットを送れば、 他の LAN コンセントに届く。 マンションのルータは当然 IP パケットの発信元/宛先アドレスをチェックしているだろうから、 発信元/宛先アドレスがプライベートアドレスなら外部へは出さないだろう。

Baffalo Dumb Hub

したがって、 グローバルアドレスなパケットに対してはルータとして、 プライベートアドレスなパケットに対してはブリッジとして機能するブルータを、 部屋ルータとして用いればよい。 ただし、 一般に売られている安価なルータにブルータの機能を求めるのは無理がある。 Linux マシンを使えばブルータを実現するのは容易だが、 各部屋 (特に寝室) で Linux マシンを 24時間稼働させるのはいかがなものか。

もっと安易に、 各PC を LAN コンセントにそのまま (あるいはハブを介して) つなぎ、 PC にはプライベートアドレスを割当てる方法もありそう。 あらかじめ 5個のグローバルアドレス全てを使いきってしまえば (例えば各グローバルアドレスを持った PC を前もってつないでおく)、 マンションのルータが (新たにつないだ) PC にグローバルアドレスを割当てることを防ぐことができる。 そして、 プライベートアドレスを配布する DHCP サーバを動かしておけばよい。 プライベートアドレスを割当てられた PC 同士は、 異なる LAN コンセントにつながれていても、 戸ハブ経由で通信できる。

ただしこの方法は、 PC にグローバルアドレスが割当てられてしまうことを完全には防げない。 なんらかのトラブルによりグローバルアドレスを使いきれない状態が生まれると、 そのとき LAN コンセントにつないだ PC には、 グローバルアドレスが割当てられてしまう。 グローバルアドレスが割当てられても、 (当然) フツーに通信できるわけで、 ユーザはグローバル空間に晒されていることに気付かない。 セキュリティ的にかなり好ましくない事態と言える。

各部屋にブルータ (という名の Linux マシン) を配置するしかないのか? と思っていたら、 タグVLAN (IEEE 802.1Q) 機能を持った安価なスイッチングハブを見つけた。 タグVLAN は業務用のインテリジェントスイッチでないと使えないと思っていたが、 1万円以下 (6400円) で買えるとわ... 思わず e-TREND で衝動買い。

本当は 5ポートの GS105E を買いたかったが、 日本では入手するのが難しそう。 8ポートの GS108E なら、 e-TREND の他、 NTT-X Store などでも送料込 6400円で購入できる (どちらのショップも 「在庫なし」 と表示されるが、 私が e-TREND に注文したときは 1週間程度で届いた)。

タグVLAN 機能付ハブを使えば、 ブルータよりもっとスマートにプライベートネットワークが実現できる。 つまり、 部屋間のプライベート通信はタグを付けて行なう。 戸ハブは (ダムハブなので) タグの有無にかかわらずなんでも中継してくれるし、 マンションのルータはタグ付パケットを無視する。

GS108E-living

GS108E を 3個購入し、 各部屋 (便宜上、ここでは居間、寝室、書斎とする) に配置する (写真 → は、居間に設置した GS108E, その上に置いてあるのは無線LAN AP, 左は IP電話アダプタ)。

VLAN ID 1 をグローバルネットワークに、 VLAN ID 2 をプライベートネットワークに使うことにする。 各 GS108E のポート 01 には、 各部屋の LAN コンセントをつなぎ、 VLAN ID 1 をタグ無しで、 VLAN ID 2 をタグ付で流す。 ポート 04 〜 08 は、 プライベートネットワーク専用ということにして、 VLAN ID 2 のみをタグ無しで流す。

グローバルネットワークに接続する必要があるのは、 グローバルアドレスをプライベートアドレスへ変換する部屋ルータ (という名の Linux サーバ) と、 IP電話アダプタ (UCOM光電話) のみ。 前者 (部屋ルータ) は書斎にある。 部屋ルータは、 グローバルとプライベートの両方のネットワークに接続する必要があるが、 幸い Linux はタグ付パケットを扱えるので、 単一の NIC で両方のネットワークに接続できる。 書斎の GS108E のポート 02 に部屋ルータをつなぎ、 ポート 01 と同様、 VLAN ID 1 をタグ無しで、 VLAN ID 2 をタグ付で流す。 書斎に他の PC が無ければ、 部屋ルータを LAN コンセントに直接つないでもよい。

後者 (IP電話アダプタ) は居間にある。 IP電話アダプタはグローバルネットワークにだけつなげればよいので、 居間の GS108E のポート 02 にIP電話アダプタをつなぎ、 VLAN ID 1 をタグ無しで流す。 急遽グローバルネットワークに接続したい機器が増えた場合に備えて、 ポート 03 にも VLAN ID 1 をタグ無しで流す (つまりグローバルネットワーク専用) 設定にした。

GS108E-living VLAN config

タグVLAN は、 一本のケーブル上に複数のネットワークを同居させることができる便利な規格。 ケーブルを何本も這わすことが (美観上の理由で) 難しい家庭でこそ必要な機能なのに、 普及価格帯のハブでタグVLAN をサポートしているのは (私が知る限り) GS105EGS108E のみ。 タグVLAN 機能を持った安価なスマートスイッチが増えることを願ってやまない。

Filed under: システム構築・運用 — hiroaki_sengoku @ 11:40
2011年7月31日

OpenVZ な ServersMan@VPS で独自 OS を動かしてみた hatena_b

国内に (自宅以外で) IPv6 が使えるサーバが欲しかったので、 ServersMan@VPS を使ってみた。 ServersMan@VPS は仮想化プラットフォームが OpenVZ なので今まで敬遠していたのだが、 Osukini サーバ (Xen) も、 さくらの VPS (KVM) も、 いまのところネイティブな IPv6 はサポートしていない (さくらの IPv6 は 6rd) ので、 やむなく契約してみた次第。

Xen や KVM などの完全仮想化と異なり、 OpenVZ はホストOS のカーネルがゲスト OS のカーネルとしても使われる。 つまり VPS (Virtual Private Server) サービスのユーザが別のカーネルを立ち上げることができない (ServersMan@VPS Perfect は完全仮想化だが月額 3150円と高いのでここでは考えない)。 OS は CentOS, Debian, ubuntu から選ぶことになる。

しかしながら、 私が個人的に管理しているサーバは、 全て OS を独自の 「my distribution」 (便宜上ここでは GCD OS と呼ぶ) に統一していて、 全サーバでディスクの内容を同期させている。 つまりある特定のサーバ固有の設定情報も、 全サーバが共有している。 共有していないのは各サーバのホスト名と、 秘密鍵などごく一部の情報だけ。 だからサーバが壊れた場合も、 新しいマシンを用意して他のサーバからディスク内容を丸ごとコピーするだけで済む。

私個人で管理しているサーバ (もちろん会社で運用しているサーバは除く) は、 仮想環境も含めると 20台ほどになるので、 異なる OS はできれば管理したくない。 ServersMan@VPS で提供されるカーネルを使うのは (OpenVZ の仕組み上) 仕方がないとして、 ディスクの内容は GCD OS と入れ替えることにした。

稼働中のサーバをいじるとき重要なのが、 トラブった時に 「元に戻せるか?」 ということ。 元に戻せるなら多少の失敗を恐れることはない。 ほとんどの VPS サービスは、 最悪の事態に陥っても初期化すれば元通りになるので、 操作をミスっても一からやり直せばいいのだが、 OS を入れ替えようとする場合は (当然) 新しい OS 一式を送り込む必要があり、 初期化するとこれが消えてしまうので再度転送する羽目になる。 GCD OS は最小セットで 7GB 近くあるので、 何度も転送を繰り返したりすると VPS サービスの契約ネットワーク帯域を使いきってしまう。

幸い、 ServersMan@VPS に帯域制限は無いが、 一日に 何十GB も転送していたら、 きっと管理者に睨まれるはず。 そこで、 どんなに失敗しても、 再起動すれば ssh でログインできる状態に戻るような OS 入れ替え手順を考えてみた。 ssh でログインできさえすれば後は何とでも修復できる。 逆に言うと ServersMan@VPS のようなコンソールが提供されない VPS サービスでは ssh でログインできなくなると万事休す、 残された復旧手段は初期化しかない。

まず ServersMan@VPS Standard プランを契約 (月額 980円) して、 Ubuntu(64bit) の最小構成 (シンプルセット) を選択。 ssh でログインして netstat でソケットを開いているデーモンを調べ、 片っ端から dpkg --purge (アンインストール) する。 もちろん sshd (ssh サーバ) だけは purge してはいけない。 syslog や cron などフツーは決して purge しないデーモンも遠慮無く purge する。 ps したとき sshd のプロセスのみが表示されるような状態になるまで purge しまくる。 で、 一通り purge し終わったら念のため再起動してみる。 もしここで ssh でログインできなくなってしまっていたら、 初期化して振出しに戻る。

この時、 sshd が 22番ポートで listen している場合は、 GCD OS が起動する sshd と衝突するので、 22番ポート以外に変えておく。 幸い ServersMan@VPS の場合は sshd が初めから 3843番ポートで listen する設定になっていた (なぜ?) ので、 そのままにしておく。

次に、 デーモン類以外のパッケージも、 残しておくとディスクの肥やしになるだけなので、 できるだけ purge する。 とはいっても、 多くのパッケージが数MB 以下で容量的には誤差の範囲なので、 無理に purge して再起不能状態に陥るリスクを冒すより、 ディスクの肥やしにしておいた方がマシ。 で、 一通り purge し終わったら念のため再起動してみる。 もしここで ssh でログインできなかったら、 初期化して振出しに戻る。

こうして netstat -nap しても ps axf しても sshd 以外のプロセスが一切残っていない状態になったら、 いよいよ GCD OS 一式を送り込む。

senri:/ # mirror -v -r /usr/local/gcd -e 'ssh -p 3843' \
        'core64[183.181.54.38]' > /tmp/serversman &

mirror というのはサーバ間で GCD OS の同期を行なうためのスクリプト。 64bit (x86_64) の最小セットをコピーするために引数として 「core64」 を指定した。 このスクリプトは、 同期すべきファイルのリストを作成して rsync を呼び出す。 「-e 'ssh -p 3843'」 オプションは、 そのまま rsync に渡される。 このコマンド列で GCD OS 最小セット約 7GB が VPS の /usr/local/gcd ディレクトリ以下へ丸ごとコピーされる。 7GB の転送にどのくらいかかるかと思っていたら、 わずか 10分弱で終わってしまった。 100Mbps 以上の帯域ということになる。 結構すごい。

ホスト名を chiyoda.gcd.org に設定し、 このサーバ専用の秘密鍵を作成すれば GCD OS のインストールが完了。 あとは、 /usr/local/gcd 以下へ chroot して GCD OS を起動するだけ。 次のような GCD OS 起動スクリプト /etc/init.d/chroot を書いてみた。

#!/bin/sh
root=`echo $0 | sed -e 's@/etc/init.d/chroot$@@'`
if [ ! -d $root ]; then
   echo "Can't find root: $root"
   exit 1
fi
mount -obind /lib/modules $root/boot/lib/modules
chroot $root sh <<EOF
mount -a
svscanboot &
/etc/rc.d/rc.M
EOF

このスクリプトも GCD OS 一式をコピーするときに /usr/local/gcd/etc/init.d/chroot へコピーされる。 で、/usr/local/gcd/etc/init.d/chroot を、 とりあえず手動で実行。 手動で実行するところがミソで、 もしこのスクリプトにバグがあって異常事態に陥っても、 再起動すれば元に戻る。

上記 /usr/local/gcd/etc/init.d/chroot は、 chroot /usr/local/gcd sh を実行して、 chroot 環境下で svscanboot と /etc/rc.d/rc.M を実行する。 svscanboot は daemontools の起動スクリプト。 GCD OS のほとんどのデーモン類は daemontools の管理下で起動される。 一方 /etc/rc.d/rc.M は、 GCD OS のブートスクリプトで、 ファイアウォールなどネットワークまわりの設定や、 個々のサーバ特有の設定、 および一部のデーモン類の起動を行なう。

普通の Linux OS だと init から直接起動されるデーモンもあるが、 GCD OS の場合 init が起動するのは /etc/rc.d/rc.S と /etc/rc.d/rc.M および svscanboot だけ。 /etc/rc.d/rc.S は、 OpenVZ 環境下では不要な処理ばかりなので実行する必要はない。

こうして chroot 環境下で GCD OS が起動したら、 chroot /usr/local/gcd sh などと実行して GCD OS 環境へ入ることができる。 各種設定が正しく機能しているか、 デーモン類が正しく動いているか確認する。

ServersMan@VPS で使われているカーネルが、 2.6.18-194.3.1.el5.028stab069.6 と、 かなり古い (2.6.18 などという 5年も昔のカーネルを使い続けないでほしい > RHEL) ので、 GCD OS をきちんと動かすには問題があった。 特に困ったのが、 iptables の owner モジュールが使えない点:

chiyoda:/ # uname -rv
2.6.18-194.3.1.el5.028stab069.6 #1 SMP Wed May 26 18:31:05 MSD 2010
chiyoda:/ # iptables -t nat -j REDIRECT -p udp --dport 53 --to-port 2053 \
        -A dnscache.lo -s 127.0.0.1 -d 127.0.0.1 -m owner ! --uid-owner Gdnscache
iptables: No chain/target/match by that name.

このエラーは、 カーネルに CONFIG_NETFILTER_XT_MATCH_OWNER の設定がないのが原因。 NETFILTER_XT_MATCH_OWNER が導入されたのは 2.6.25 以降なので、 そもそも元から 2.6.18 には存在しない。

なぜ --uid-owner が必要かと言えば、 GCD OS では tinydns と dnscache を、 同じ IP アドレスで動かすことが基本になっているから。 つまり、 通常の名前解決に 127.0.0.1 の dnscache を利用するのだが、 dnscache (Gdnscache 権限で動作) が 127.0.0.1 に問合わせる時に限り mydns が返事をするようにしたい。

仕方がないので、 iptables に --uid-owner を指定してエラーになる場合は、 --uid-owner 抜きで iptables を再実行するように修正した:

nsredirect="-t nat -j REDIRECT --dport 53 --to-port $PORT"
chain=dnscache.lo
iptables -t nat -N $chain 2>/dev/null || iptables -t nat -F $chain
nsinner="$nsredirect -A $chain -s 127.0.0.1 -d 127.0.0.1 \
        -m owner ! --uid-owner Gdnscache"
iptables -p udp $nsinner
if [ $? -ne 0 ]; then
    nsinner="$nsredirect -A $chain -s 127.0.0.1 -d 127.0.0.1"
    iptables -p udp $nsinner
fi
iptables -p tcp $nsinner

このような修正を、 chiyoda.gcd.org だけでなく、 GCD OS をインストールしている全サーバで一斉に行なう点がミソ。 サーバごとにファイルの内容が微妙に異なっていたりしたら、 GCD OS を使う意味がない。

当然、 --uid-owner 抜きで iptables を実行した場合は、 dnscache が 127.0.0.1 の mydns に問合わせをすることができないが、 127.0.0.1 以外、 つまり 183.181.54.38 あるいは 2001:2e8:634:0:2:1:0:2a ならば mydns に問合わせることができるので問題無い。

じゃ、 なんのために、 わざわざ --uid-owner を使って 127.0.0.1 の mydns に問合わせられるようになっているかというと、 127.0.0.1 以外の IP アドレスが動的に変わるサーバ (ノートPC など) も想定しているから。 GCD OS は VirtualBox や Xen などの完全仮想化環境だけでなく、 coLinux や今回の OpenVZ など、 仮想化環境としてはやや異質なものもサポートしている。

以上のような細かい修正を行なっていって、 GCD OS が問題無く立ち上がるようになったら、 /usr/local/gcd/etc/init.d/chroot の呼び出しを (ubuntu の) /etc/rc.local に追加して、 VPS の起動時に自動的に GCD OS が起動されるようにする。

GCD OS が正しく立ち上がれば、 外部から 22番ポートに対して ssh ログインして GCD OS が使える。 22番ポートでログインできることが確認できたら、 3843番ポートの sshd は止めてもよい。 chroot 環境からはいつでも脱出できるので、 3843番ポートを止めても本来の (chroot する前の) ubuntu 環境にアクセスすることが可能:

senri:~ # ssh chiyoda.gcd.org
Enter passphrase for key '/root/.ssh/id_rsa':
Last login: Sat Jul 30 09:14:54 2011 from 2409:82:5fff:0:5542:d84e:971a:9656
Linux 2.6.18-194.3.1.el5.028stab069.6.
chiyoda:~ # ls -F /                                ↓ GCD OS
bin@   dev/  ftp/   lib/    proc/  run/   sys/  usr/
boot/  etc/  home/  lib64@  root/  sbin@  tmp/  var/
chiyoda:/ # chroot_escape /bin/bash                ← chroot から脱出
groups: cannot find name for group ID 11
groups: cannot find name for group ID 14
root@chiyoda:/# ls -F --color=never                ↓ ubuntu
aquota.group@  boot/  fastboot  lib32/  mnt/   sbin/     sys/  var/
aquota.user@   dev/   home/     lib64@  proc/  selinux/  tmp/
bin/           etc/   lib/      media/  root/  srv/      usr/
root@chiyoda:/# cat /etc/debian_version
squeeze/sid
root@chiyoda:/# lsb_release -r
Release:        10.10
root@chiyoda:/# exit
chiyoda:~ #                                         ↓ GCD OS

「chroot_escape /bin/bash」 が、 chroot 環境から脱出するコマンド。 脱出して、 「本来の」 root 環境 (この例では ubuntu) 下で、 引数のコマンド 「/bin/bash」 を実行する。 この bash を使って ubuntu の操作ができて、 exit すると元の GCD OS へ戻る。 まるで chroot 下の GCD OS の方が 「主」 で、 本来の root が 「従」 のように見える ;-)。

なお、 chroot_escape コマンド実行時の 「groups: cannot find name for group ID 〜」 というエラーは、 GCD OS の /etc/group と ubuntu の /etc/group が異なるため。 つまり GCD OS の root は ID 11 と 14 のグループに属しているが、 ubuntu には ID が 11 と 14 のグループが存在しないため、 このようなエラーが表示される。

ここまでやるなら、 chroot といわず root に GCD OS をインストールしてしまえば? という声が聞こえてきそうであるが、 ServersMan@VPS ではコンソールが利用できないため、 トラブったときのために何らかの 「バックドア」 は残しておきたい。 GCD OS がどのような状況に陥っても、 3843番ポートの sshd (init から起動されるので kill しても再起動する) にログインできれば、 ubuntu 環境で GCD OS の修復が可能。

というわけで一週間ほど ServersMan@VPS Standard プランを使っているが、 意外に (失礼!) 使えるので驚いた。 実は、 OpenVZ な 512MB ということであまり期待していなかった。 OpenVZ は swap を使えないので、 メモリ 512MB だと、 ちょっと重い処理をさせるだけですぐ OOM Killer が動き出すのだろうと思っていた。 ところが、

chiyoda:~ $ free
             total       used       free     shared    buffers     cached
Mem:       2097152     425020    1672132          0          0          0
-/+ buffers/cache:     425020    1672132
Swap:            0          0          0

512MB というのは実メモリ? の割当量のようで、 見かけ上は 2GB のメモリがある。 OpenVZ な VPS サービスによっては、 これをメモリ 2GB と言い張るところもあるんじゃないかと思うので、 ServersMan@VPS は良心的。

どのくらいのパフォーマンスなのか、 この日記 (WordPress を使用) の処理時間を測ってみる。 senri.gcd.org から ApacheBench でアクセスすると:

senri:~ $ /usr/apache2/bin/ab -n 10 http://chiyoda.gcd.org/blog/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
        ...
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        6    7   0.3      7       7
Processing:   992 1116  94.1   1139    1259
Waiting:      305  403  78.3    380     561
Total:        998 1122  94.1   1146    1265

これを、 Linode Xen 512MB プラン (fremont.gcd.org) と比較する。 fremont.gcd.org は米国西海岸にある VPS なので、 同じく西海岸にある prgmr.gcd.org から ApacheBench でアクセスすると:

prgmr:~ $ /usr/apache2/bin/ab -n 10 http://fremont.gcd.org/blog/
        ...
              min  mean[+/-sd] median   max
Connect:        2    3   0.1      3       3
Processing:   996 1081  73.8   1084    1197
Waiting:      354  391  30.5    388     450
Total:        999 1084  73.8   1086    1200

両者は、 ほとんど同等のパフォーマンスであることが分かる。 Linode Xen 512MB プランは、 ディスク 20GB で月額 $19.95 (約 1600円) なので、 ServersMan@VPS Standard プラン (ディスク 30GB, 月額 980円) の方がコストパフォーマンスが良い。

もちろん、 ServersMan@VPS Standard には、 カーネルのバージョンが古すぎ、という問題点があるし、 メモリ 512MB を超える部分のパフォーマンスは、 ホストOS 側の負荷状況に左右されると思われるので、 単純に比較すべきではない。

Filed under: IPv6,システム構築・運用 — hiroaki_sengoku @ 10:48
2011年7月24日

フレッツ 光ネクストの IPv6 IPoE 接続 (ネイティブ方式) を使ってみた tweets

IPv6 閉域網であることを今までさんざん dis られてきたフレッツ網 (NGN, 次世代ネットワーク) が、 ついに 7月21日からインターネットに接続できるようになった。 これでもう 「NGN と IPv6 インターネットは併用できない」 などとは言わせない。 私は IPv6 を既に PPPoE 接続で使っているのだが、 トンネル方式 (PPPoE) よりネイティブ方式 (IPoE) のほうがいいにきまってる、 ということでさっそく申し込んでみた

NGN のサービス情報サイト (NGN からでないとアクセスできない) のページで 「サービス申込受付」 をクリック。 「お客様ID」 と 「アクセスキー」 を入力してログインすると、 「フレッツ・v6オプション」 を申し込むことができる (このページから申し込むと無料)。

申込み後、 NGN から流れてくる IPv6 ルータ広告 (RA, Router Advertisement) を tcpdump で監視していたら、 34分経過した頃にプレフィックスが突然変わった。 NGN の契約以来、 今まで一度も変わったことがなかったプレフィックス 2408:82:5fff:86a::/64 が、 2408:282:5fff::/64 になった。 これはきっとインターネットと通信できるプレフィックスに違いないと思って、 他のサイトから ping6 を打ってみた。 が、 NGN からは RA 以外は何も流れてこない。 うーん。 ちなみに RA の送信元 (NGN のエッジ・ルータ) は fe80::21e:13ff:fec2:69c2 のまま変わらず。

その後 1時間ほど放置してみたが何も状況が変化しなかったので、 「フレッツ・v6オプション」 って何? と思い直して (サービス内容をよく確認せずに申し込んでしまっていた)、 あらためて FAQ を見ると、

 Q
「フレッツ・v6オプション」を利用してインターネットへの接続はできますか?
 A
「フレッツ・v6オプション」 は、NTT東日本が構築するNGN内での通信が可能ですが、 「フレッツ・v6オプション」 のみではインターネットへの接続はできません。 インターネットへの接続には、 別途プロバイダサービスをお申し込みいただく必要があります。

インターネットへ接続するには、 フレッツ・v6オプションとプロバイダ契約の両方が必要らしい。 では、 どのプロバイダの、 どんなサービスを申し込めばいいんだろう? と思って、 フレッツ 光ネクスト IPv6 IPoE対応プロバイダを調べてみると、 神奈川県だと現時点で対応しているのは IIJmio だけだった (神奈川県だけでなく他県も同様)。 IIJ は今まで契約したことがなかったが、 他に選択肢が無いのであれば仕方がない。 さくっとクレジットカード番号を入力して IIJmio FiberAccess/NF を契約した (月額 2100円)。 これで半固定 IPv6 アドレスによる IPoE サービスと、 動的割当て IPv4 アドレスによる PPPoE サービスが利用できる。

Ether 上に IP を流すのはごくごく普通のことなので、 あらたまって 「IPoE」 (IP over Ether) と表現されると何だか妙な感じ。 「半固定」 というのも微妙な表現だが、 注意書きには 「お客様の移転、フレッツ回線の品目変更やNTTのメンテナンスにより、 変更になる場合があります」 と書いてあるので、 普通に使ってる限りプレフィックスが変わることは無さそう。

mio FiberAccess/NFサービスは、 インターネットマルチフィード株式会社が提供する 「transix (トランジックス)」 サービスを利用して、 NTT東西の 「インターネット(IPv6 IPoE)接続」 に対応したIPv6接続を提供します。 IPv6接続に加えて、 PPPoE接続方式によるIPv4接続もあわせて提供するため、 お客様は一つのサービスでIPv6、 IPv4の両方の接続環境を利用することができます。
IIJmio FiberAccess/NF概要 から引用

IPv6 接続は全て transix が担っていて、 IIJmio はネットワーク的には何の役割も果たしていない。 IIJmio がやってるのは課金などユーザ管理関連だけ。 プロバイダである IIJmio が絡むから、 IPv4 接続も提供するなどという抱き合わせ商法になる。 動的割当の IPv4 PPPoE 接続サービスなんて要らないから、 もう少し安いプランがあればいいのにと思う。

本来、 ネイティブ方式の IPv6 接続サービスは、 NTT東西だけで提供するのが自然な形だった。 ところが、 NGN を持っていて地域独占な NTT東西が接続サービスまで提供してしまっては、 他のプロバイダの出る幕がなくなると猛反発されて、 BBIX, JPNE, インターネットマルチフィード の三社が接続サービスを提供することになった。 なぜ三社だけかといえば、 プロバイダを増やすと経路情報の処理量が膨大になって NGN の各ルータの処理能力の限界を超えてしまうから。

ところが、 三社だけでは少なすぎると他のプロバイダ達が反対したので、 その他大勢の中小プロバイダにも参入の余地を無理矢理作ったということなのだろう。 でも、 やってることは単なるユーザ管理なので、 通信事業者じゃなくてもできる簡単なお仕事。 この期に及んで業界保護みたいなことはやめてほしい。 インターネット接続サービスは既にコモディティ化しているのだから、 体力のないところは淘汰されるべき。

FiberAccess/NF を契約してから 1時間が経過したとき、 NGN から流れてくる RA のプレフィックスが 2409:82:5fff::/64 に変わった。 今度こそ疎通したかと思い、 senri.gcd.org に IPv6 アドレス 2409:82:5fff::3c20:55dc を割当てて、 他のサイトから 2409:82:5fff::3c20:55dc に対して ping6 を打ってみる。 すると無事、NGN IPoE 経由で senri.gcd.org に ICMPv6 パケットが届いた。

ただし、 まだ senri.gcd.org の routing 設定を行なっていないので、 返りパケットは PPPoE 経由で OCN 側へ行ってしまう (おそらく OCN 内部で捨てられる)。 そこで、 とりあえずの対策として NGN から届いたパケットは NGN へ返すように、 policy routing rule を設定した:

senri:/ # ip -6 rule add from 2409:82:5fff::/64 table 100 pref 25600
senri:/ # ip -6 route add default table 100 via fe80::21e:13ff:fec2:69c2 dev eth1

つまり、 送信元アドレスが 2409:82:5fff::/64 なパケットは routing table 100 を参照するようにして、 routing table 100 において default route を、 fe80::21e:13ff:fec2:69c2 (NGN のエッジ・ルータ) へ向ける。

これで ping に対して応答できるようになった。

fremont:~ $ ping6 -c 3 2409:82:5fff::3c20:55dc
PING 2409:82:5fff::3c20:55dc(2409:82:5fff::3c20:55dc) 56 data bytes
64 bytes from 2409:82:5fff::3c20:55dc: icmp_seq=1 ttl=49 time=122 ms
64 bytes from 2409:82:5fff::3c20:55dc: icmp_seq=2 ttl=49 time=122 ms
64 bytes from 2409:82:5fff::3c20:55dc: icmp_seq=3 ttl=49 time=122 ms

--- 2409:82:5fff::3c20:55dc ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 122.479/122.526/122.591/0.289 ms
fremont:~ $ tcpspray 2409:82:5fff::3c20:55dc
Transmitted 102400 bytes in 0.491628 seconds (203.406 kbytes/s)

RTT (Round Trip Time, 往復所要時間) が 122ms もかかってるのは、 fremont.gcd.org が米国の西海岸にあるため。 2400:400d:100::3c20:55dc (OCN の PPPoE 接続) 宛の場合↓ と比べると、 transix は RTT で 50ms ほど速く、 帯域 (tcpspray による簡易測定) で倍くらい広い。

fremont:~ $ ping6 -c 3 2400:400d:100::3c20:55dc
PING 2400:400d:100::3c20:55dc(2400:400d:100::3c20:55dc) 56 data bytes
64 bytes from 2400:400d:100::3c20:55dc: icmp_seq=1 ttl=49 time=174 ms
64 bytes from 2400:400d:100::3c20:55dc: icmp_seq=2 ttl=49 time=174 ms
64 bytes from 2400:400d:100::3c20:55dc: icmp_seq=3 ttl=49 time=174 ms

--- 2400:400d:100::3c20:55dc ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 174.046/174.386/174.593/0.539 ms

fremont:~ $ tcpspray 2400:400d:100::3c20:55dc
Transmitted 102400 bytes in 0.905523 seconds (110.433 kbytes/s)

参考までに IPv4 60.32.85.216 (OCN の PPPoE 接続) の場合も測ってみた。 すると RTT も帯域も transix と同程度だった。 つまり OCN の IPv6 PPPoE だけが突出して遅く、 帯域も狭い。

fremont:~ $ ping -c 3 60.32.85.216
PING 60.32.85.216 (60.32.85.216) 56(84) bytes of data.
64 bytes from 60.32.85.216: icmp_req=1 ttl=51 time=130 ms
64 bytes from 60.32.85.216: icmp_req=2 ttl=51 time=130 ms
64 bytes from 60.32.85.216: icmp_req=3 ttl=51 time=129 ms

--- 60.32.85.216 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 129.377/129.903/130.202/0.559 ms

fremont:~ $ tcpspray 60.32.85.216
Transmitted 102400 bytes in 0.518052 seconds (193.031 kbytes/s)

米国西海岸から transix までの IPv6 の経路はこんな感じ:

fremont:~ $ tracepath6 2409:82:5fff::3c20:55dc
 1?: [LOCALHOST]                        0.021ms pmtu 1500
 1:  2600:3c01:ffff:0:ca4c:75ff:fef5:d63f                  0.558ms
 1:  2600:3c01:ffff:0:ca4c:75ff:fef5:d63f                  0.480ms
 2:  10gigabitethernet2-3.core1.fmt1.he.net                3.149ms
 3:  10gigabitethernet1-2.core1.sjc2.he.net               11.042ms
 4:  equi6ix.sv.iij.com                                    1.834ms asymm  5
 5:  sjc002bf00.iij.net                                    9.384ms asymm  7
 6:  2001:48b0:bb00:8016::71                             120.588ms asymm  7
 7:  tky009bb11.IIJ.Net                                  120.730ms asymm  8
 8:  tky009ip50.IIJ.Net                                  121.048ms
 9:  2001:240:bb5c:1008::cafe                            120.365ms
10:  2404:8e00:feed:101::2                               121.852ms
11:  no reply
12:  no reply
13:  no reply
14:  no reply
15:  no reply
16:  2409:82:5fff::3c20:55dc                             127.409ms reached
     Resume: pmtu 1500 hops 16 back 49

「IIJmio はネットワーク的には何の役割も果たしていない」 が、 日米間の回線は IIJ が担っているようだ (あれっ? ^^;)。 日本に上陸後 (6 以降) はほとんど遅延していない。

OCN の PPPoE の場合だと、こんな感じ:

fremont:~ $ tracepath6 2400:400d:100::3c20:55dc
 1?: [LOCALHOST]                        0.047ms pmtu 1500
 1:  2600:3c01:ffff:0:ca4c:75ff:fef5:d63f                  3.935ms
 1:  2600:3c01:ffff:0:ca4c:75ff:fef5:d63f                  0.852ms
 2:  10gigabitethernet2-3.core1.fmt1.he.net                7.978ms
 3:  10gigabitethernet1-2.core1.sjc2.he.net                2.532ms
 4:  xe-0.equinix.snjsca04.us.bb.gin.ntt.net               2.018ms asymm  5
 5:  as-1.r21.osakjp01.jp.bb.gin.ntt.net                 168.074ms asymm 11
 6:  ae-0.ocn.osakjp01.jp.bb.gin.ntt.net                 167.792ms asymm 14
 7:  2001:380:8060:6::1                                  159.446ms asymm 13
 8:  2001:380:8170:4::1                                  167.630ms asymm 14
 9:  2001:380:8030:16::1                                 168.401ms asymm 15
10:  2001:380:8110:d::1                                  170.344ms asymm 14
11:  2001:380:8110:f::2                                  178.503ms asymm 13
12:  2001:380:8130:11::13                                178.131ms asymm 13
13:  2001:380:8270:8::2                                  172.448ms
14:  2001:380:4d:101::2                                  179.036ms
15:  2001:380:4d:182::2                                  181.317ms
16:  no reply
17:  2001:380:4d:181::2                                  173.127ms pmtu 1454
17:  senri.v6.gcd.org                                    183.023ms reached
     Resume: pmtu 1454 hops 17 back 49

日米間に 160ms もかかっている上に、 日本に上陸後 (5 以降) も 15ms ほど遅延がある。 なぜこんなに遅いのだろう? また、PPPoE なので mtu が 1454 になっている。

同じ OCN の PPPoE でも、 IPv4 だと遅くない (というか transix より若干速い) ので、 OCN の IPv6 PPPoE 接続サービスには、 なにか問題がありそう。 まあ、 追加料金無しのサービスなので、 IPv4 のオマケ的な位置づけなのかも?

fremont:~ $ tracepath 60.32.85.216
 1:  fremont.gcd.org                                       0.169ms pmtu 1500
 1:  184.105.143.85                                        1.702ms
 1:  184.105.143.85                                        0.418ms
 2:  10gigabitethernet2-3.core1.fmt1.he.net                0.665ms
 3:  10gigabitethernet1-1.core1.pao1.he.net                8.907ms
 4:  sjo-bb1-link.telia.net                                1.297ms asymm  5
 5:  verio-119529-sjo-bb1.telia.net                        4.568ms
 6:  ae-8.r20.snjsca04.us.bb.gin.ntt.net                   1.922ms
 7:  as-2.r20.tokyjp01.jp.bb.gin.ntt.net                 112.093ms asymm  8
 8:  ae-1.ocn.tokyjp01.jp.bb.gin.ntt.net                 119.692ms asymm 11
 9:  60.37.27.137                                        120.641ms asymm 10
10:  60.37.55.158                                        119.549ms asymm 11
11:  122.1.173.238                                       121.243ms
12:  118.23.5.78                                         128.853ms asymm 14
13:  no reply
14:  118.23.8.9                                          128.712ms pmtu 1454
14:  gcd.org                                             123.788ms reached
     Resume: pmtu 1454 hops 14 back 52
More...
Filed under: IPv6,システム構築・運用 — hiroaki_sengoku @ 18:33
2011年6月20日

フレッツ 光ネクストの IPv6 PPPoE 接続を OCN 光アクセスで使ってみた tweets

6月1日から NTT東日本のフレッツ 光ネクストにおいて IPv6 PPPoE 接続の提供が始まった。 私は OCN光アクセスを契約しているが、 幸い OCN (NTTコミュニケーションズ) も、 NTT東日本に合わせて順次対応を開始ということなので、 さっそく OCN へ電話で問合わせてみたら、 申込書をメールで送るので記入押印の上 FAX で送り返して欲しい、とのこと。

いまどき Web で申し込めないなんて、 と思いつつ 8ページにもわたる申込書 (いつも感じるが OCN の申込書は無駄にページ数が多い *_*) を FAX で送付。 すると翌日電話がかかってきて、 開通日は 10日後などとおっしゃる。 それじゃ World IPv6 Day に間に合わないじゃんと思ったが、 どんな変更でも申込みから 7営業日は最低でもかかるらしいので仕方がない。 なお、 工事費および月額使用料は無料。

OCN には元々月額 315円の 「OCN IPv6」 というサービスがあるが、 OCN IPv6 はフレッツ網に PPPoE によるトンネルを張って IPv4 を通し (OCN フレッツ光)、 その IPv4 上に L2TP (Layer 2 Tunneling Protocol) によるトンネルを張って PPP セッションを通し、 その PPP 上に IPv6 を通すという屋上屋を架す方式だったのに対し、 6月1日から始まった 「IPv6インターネット接続」 はフレッツ網に PPPoE によるトンネルを張って IPv6 を通す方式。

つまり OCN フレッツ光の IPv4 の部分を IPv6 でそのまま置き換えたシンプルな方式。 もちろんフレッツ網に IPv6 を直接通す 「ネイティブ方式」 が一番シンプルだが、 ネイティブ方式に関しては 「平成23年7月を目途に提供を予定」 ということなので、 どのようなサービスが始まるのか今のところ不明 (もう来週には 7月が始まってしまうのだが)。 7/24追記: ネイティブ方式サービスが始まった!

開通日の 2日前に郵便で届いた 「ご利用内容のご案内」 の欄外の注釈に、

IPv6 でインターネットに接続する際、 認証ID の @ 右側を “@bizf.ocn.ne.jp” の場合は “@bizf6.ocn.ne.jp” に、 “@bizd.ocn.ne.jp” の場合は “@bizd6.ocn.ne.jp” に変更してご利用下さい。

と書いてあった。 IPv6 での接続に関する説明はこの部分だけなので、 あとは推測するしかない (サポートに接続方法を聞いても、 単に IPv6トンネル対応ルータを購入してくれと言われる)。

とりあえず、 ふだん IPv4 で接続するときに使ってる PPPoE スクリプト (RP-PPPoE) を、 認証ID を “xxxx@bizf.ocn.ne.jp” から “xxxx@bizf6.ocn.ne.jp” に変更して走らせてみる:

20:39:13 senri pppd[16587]: Plugin /etc/ppp/plugins/rp-pppoe.so loaded.
20:39:13 senri pppd[16587]: RP-PPPoE plugin version 3.10 compiled against pppd 2.4.5
20:39:13 senri pppd[16587]: pppd 2.4.5 started by root, uid 0
20:39:13 senri pppd[16587]: PPP session is 6394 (0x18fa)
20:39:13 senri pppd[16587]: Connected to 00:1e:13:c2:69:c2 via interface eth1
20:39:13 senri pppd[16587]: Using interface ppp0
20:39:13 senri pppd[16587]: Connect: ppp0 < --> eth1
20:39:13 senri pppd[16587]: Couldn't increase MTU to 1500
20:39:13 senri pppd[16587]: Couldn't increase MRU to 1500
20:39:13 senri pppd[16587]: PAP authentication succeeded
20:39:13 senri pppd[16587]: peer from calling number 00:1E:13:C2:69:C2 authorized
20:39:13 senri pppd[16587]: local  LL address fe80::fd61:ff9b:eb97:1f93
20:39:13 senri pppd[16587]: remote LL address fe80::0090:1a00:41a3:d3f3

PPP は 1 つ以上のネットワーク層プロトコルを選択できるので、 IPCP (IP Control Protocol) と IPv6CP (IPv6 Control Protocol) の両方が流れてくるのかと予想したのだが、 流れてきたのは IPv6CP のみだった。 つまり “@bizf.ocn.ne.jp” で IPv4 用、 “@bizf6.ocn.ne.jp” で IPv6 用、 計 2本の PPPoE セッションを張ることになる。

上記ログから分かる通り Link Local とはいえ IPv6 なアドレス fe80::fd61:ff9b:eb97:1f93 が割り振られたのだから、 IPv6 で通信できるはず。 試しに PPP の対向サーバへ ping を打ってみると、 ちゃんと応答が返ってきた:

senri:~ $ ping6 -c 3 fe80::0090:1a00:41a3:d3f3%ppp0
PING fe80::90:1a00:41a3:d3f3%ppp0 (fe80::90:1a00:41a3:d3f3%ppp0): 48 data bytes
56 bytes from fe80::90:1a00:41a3:d3f3%ppp0: icmp_seq=0 ttl=255 time=3.936 ms
56 bytes from fe80::90:1a00:41a3:d3f3%ppp0: icmp_seq=1 ttl=255 time=4.050 ms
56 bytes from fe80::90:1a00:41a3:d3f3%ppp0: icmp_seq=2 ttl=255 time=4.176 ms
--- fe80::0090:1a00:41a3:d3f3%ppp0 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 3.936/4.054/4.176/0.098 ms
senri:~ $ 

じゃ、 Global なアドレス (固定アドレス契約なのでプレフィックスが 「ご利用内容のご案内」 に書かれていた) を付けて routing 設定を行なえば Global に通信できそうと思い、 試してみると...

senri:/ # ifconfig eth0 add 2400:400d:100::1/56
senri:/ # ip -6 route add default via fe80::0090:1a00:41a3:d3f3 dev ppp0
senri:/ # ping6 www.kame.net
PING www.kame.net (2001:200:dff:fff1:216:3eff:feb1:44d7): 48 data bytes
^C--- www.kame.net ping statistics ---
15 packets transmitted, 0 packets received, 100% packet loss
senri:/ # 

う〜ん、ダメか。 tcpdump を使って ppp0 に届く IPv6 パケットを監視してみたが、 OCN へ送信したパケットのみが表示され、 OCN 側からは何のパケットも届かない。

しかも、 他のサイトから 2400:400d:100::1 に対して ping6 を打ってみても何も届かない。 仮にこちらの設定に何か間違いがあったとしても、 OCN 側で 2400:400d:100::1 宛のパケットを routing していれば、 パケット自体は流れてきそうな気がするのだが... 少なくとも IPv4 の場合なら、 こちらの IP アドレス設定が間違っていても、 受け取れないだけでパケット自体は流れてくる。

ひょっとして、 「ご利用内容のご案内」に書かれていた 「2400:400d:100::」 というプレフィックスが間違っているんじゃ? と、 途方に暮れる。

More...
Filed under: IPv6,システム構築・運用 — hiroaki_sengoku @ 09:08
Older Posts »