協力、強力

ワクチンソフトの無料期間が極僅かになってきた。契約しろ、契約しろと五月蝿い。

たまたま、メニューの右側に隠れているアイコンをクリックしたら、ご契約情報と 言う画面が現れて、そこには、

ご契約のキーカードをお送りいたしますので、送付先をお知らせください。
このカードには、製品のシリアル番号と、インストールまたは再インストールに
必要になるライセンス登録コードが記録されています。
 メアドを教えてちょって書かれていた。

これだけ読むと、誤解しちゃうなーー。メアドを教えるとコードを送ってくれるって 一瞬思っちゃうぞ。こうやって、未契約ユーザーと、太いパイプを築くわけね。

そして、毎日、メール洪水が押し寄せてくるとな。

あんたが、一番迷惑者だわい。

OpenBSD in FreeBSD

前回は、OpenBSD内の仮想PCにOpenBSDを入れようとして、ネットからのkernel等の 取得で詰まってしまった。別の手を考えた。そんなのFreeBSDに強力なbhyveが居る んだから、協力して貰え。今風に言うと、BSD兄弟のコラボレーションです。

という事で、FreeBSDで、OpenBSD入りのDISKを作成して、それをscpな何かで OpenBSDに持って行こうと言う作戦発動です。

[sakae@fb11 ~/OB60]$ truncate -s 2G ob60.img

こんな具合に空DISKを作成。入れるのは、srcだけを予定してるんで、これだけ有れば 多分大丈夫(のはず)。

[sakae@fb11 ~/OB60]$ cat d.map
(hd0) ./ob60.img
(cd0) ./install60.iso

これ、grub-bhyveが使うデバイスマップ。そして起動。

# grub-bhyve -m d.map -r cd0 -M 256M OB60
grub> kopenbsd -h com0 (cd0)/6.0/amd64/bsd.rd
grub> boot

インストール用bhyveスクリプト

bhyve \
    -H -P -A \
    -W -c 1 \
    -m 256M \
    -l com1,stdio \
    -s 0:0,hostbridge \
    -s 1:0,lpc \
    -s 2:0,virtio-net,tap0 \
    -s 3,ahci-cd,./install60.iso \
    -s 4,virtio-blk,./ob60.img \
    OB60

後は、普通にOpenBSDをインストールすればよい。但し、入れる物のうち、 X関係とゲームは今回は使う予定が無いので、 -x* -g* として、除外した。

インストールが終わったら、rebootすると、仮想PCが停止する。

次は、常時使う起動スクリプト

sudo bhyvectl --destroy --vm=OB60;
 printf "kopenbsd -h com0 -r sd0a (hd0,openbsd1)/bsd\nboot\n" |
sudo grub-bhyve -m d.map -M 256M OB60;
sudo bhyve -W -c 1 -m 256M -H -P -A -l com1,stdio \
  -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0 \
  -s 3,virtio-blk,./ob60.img OB60;
sudo bhyvectl --destroy --vm=OB60

これで、無事に起動してきた。入れる物をけちったせいで、dfでのdisk占有容量は、 411M になった。

カーネルとユーザーランドのソースを入れて、下記のようになった。

# df -k
Filesystem  1K-blocks      Used     Avail Capacity  Mounted on
/dev/sd0a     1769710   1147894    533332    68%    /

気のせいか、VMWARE上に入れた普通のOpenBSDよりも、きびきび動いているような気がする。 その後、いらないデーモンを殺して、

# ps awx
  PID TT  STAT       TIME COMMAND
    1 ??  Is      0:01.02 /sbin/init
96486 ??  Is      0:00.00 dhclient: vio0 [priv] (dhclient)
59842 ??  Isp     0:00.03 dhclient: vio0 (dhclient)
58689 ??  Sp      0:00.03 /usr/sbin/syslogd
91516 ??  Isp     0:00.00 syslogd: [priv] (syslogd)
63822 ??  Is      0:00.00 /usr/sbin/sshd
36703 ??  Ssp     0:00.02 /usr/sbin/cron
96020 00  Ssp     0:00.23 -ksh (ksh)
58574 00  R+p     0:00.01 ps -awx

こんな状態にしたので、このDISKをOpenBSDに送っておいた。

ntpdが居なくなったせいか、時刻が9時間ずれてしまった。NetBSDではこういう時、 rc.confに補正値を設定しておけるんだけど、OpenBSDはそこまで親切に出来ていない。 下記のように、カーネルを直接書き換えてからrebootしろとな。

# config -ef /bsd
OpenBSD 6.0 (GENERIC) #2148: Tue Jul 26 12:55:20 MDT 2016
    deraadt@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC
Enter 'help' for information
ukc> timezone
timezone = 0, dst = 0
ukc> timezone -540
timezone = -540, dst = 0
ukc> quit
Saving modified kernel.

bhyveによるオーバーヘッドがどれぐらいあるか、カーネルコンパイルで確認してみた。

text    data    bss     dec     hex
9093180 266328  663552  10023060        98f094
    6m00.06s real     3m28.85s user     0m31.71s system

下記は、VMWARE上のOpenBSDでやった以前の結果

text    data    bss     dec     hex
9093180 266328  663552  10023060        98f094
    3m57.33s real     1m01.10s user     2m50.59s system

体感的には、2/3ぐらいなパフォーマンスになったけど、その内訳は大幅に異なっていて 興味深い。コンパイル中にモニターしたTopの結果。100%を超えているのが面白い。

  PID USERNAME    THR PRI NICE   SIZE    RES STATE   C   TIME    WCPU COMMAND
 1279 root         11  20    0   288M   124M kqread  0   7:54 104.17% bhyve

また、OpenBSDの仮想PCで出てた、RTCエラーは、bhyve上のそれでは、発生していなかった。 BIOSも内蔵しているのだろうか? 時刻がどれぐらいズレるか、標準器に問い合わせて おいて、暫くしてから、もう一度、確認してみよう。

# rdate -ncv ntp.nict.jp
fri Nov  4 16:31:45 JST 2016
rdate: adjust local clock by -0.019824 seconds
# rdate -ncv ntp.nict.jp
Fri Nov  4 17:01:39 JST 2016
rdate: adjust local clock by -2.052748 seconds

30分で、2秒のずれか、親にしてるFreeBSDも全く時刻同期してないので、こんなもの なのだろうな。って、rdateのコードでも読んで、時刻の元を探っておけよ。

NICの2枚刺し

さて、新しい仮想Diskの受け入れ先であるOpenBSDで、準備をしておこう。 その前に、OpenBSDの時計のずれを計測しておく。

# rdate -ncv ntp.nict.jp
Sat Nov  5 05:45:52 JST 2016
rdate: adjust local clock by 0.006544 seconds
# rdate -ncv ntp.nict.jp
Sat Nov  5 06:16:05 JST 2016
rdate: adjust local clock by 0.042131 seconds

無負荷だと、時計が狂う事は無いのかな。まあ、参考値と言う事です。

じゃ、準備を始める。

[ob: vm]$ cat /etc/hostname.vether0
inet 10.0.0.1/24

これが、2枚目のNICの設定。ゲートウェイを担当させる。もう一枚のNICであるem0は 元々有ったもので、DHCPでアドレスを貰ってきている。

NATを構成する為、/etc/pf.conf の最後に、下記の1行を追加する。このファイルは 行の定義の順番が非常に重要。間違ったら、パケットフィルターの用を為さなかったり、 sshでログインすら出来なくなってしまう。

そんな時は、焦らず、コンソールから、pfctl -d として、パケットフィルターの 機能を殺せばよい。

match out on egress from !(egress) nat-to (egress)

egressって何かと思って調べてみたら、インターネットに面してるNICの事らしい。 単語の意味は、出入り口とか。オイラーの環境だと、em0が相当するんだな。

もう一つ、忘れてはならないのが、2枚のNIC間にパケットが行き来出来るように 設定しておく事だ。

[ob: vm]$ cat /etc/sysctl.conf
net.inet.ip.forwarding=1

これで、host側の準備は整ったんで、取り合えずゲストが起動するか確認。 例によって、コンソールが上手く起動せずにめげたけど、何度かの失敗の後 ログイン出来た。

仮想PCで遊ぶ

NATになってるはずなんで、FreeBSD側の設定をきっぱりと忘れて、OpenBSD側に 馴染むようにしていく。

# cat /etc/hostname.vio0
inet 10.0.0.2/24

# cat /etc/mygate
10.0.0.1

こんな設定にする。仮想側のNICは1枚だけね。それから、resolv.confは、そのまま 流用出来る。

# cat /etc/resolv.conf
# Generated by vio0 dhclient
search localdomain
nameserver xxx.xxx.xxx.2
lookup file bind

この仮想PCの起動に何度も失敗するので、一連の流れをスクリプトにまとめておいた。

# cat boot
#!/bin/sh
vmctl stop OB60
pkill vmd
vmd
vmctl start OB60 -k /bsd -m 256M -i 1 -d ob60.img
sleep 5
vmctl status
ifconfig bridge0 add vether0 add tap0 up

最初、vmd関係者を全部殺す。それからだえもん君に来てもらい、仮想PC名 OB60を起動。 起動がトロいので、暫く待って、ステータスを表示、続いて仮想HUB(bridge0)に、 ホスト側の端子tap0とゲスト側の端子vether0を差し込む。 ああ、端子なんて一般人の使う用語だな。専門家なら、RJ45って、言え!

じゃ、ついでに端子が付いているケーブルはどんなカテゴリーの物を使えばいいんでしょうか? そんなの、1000baseT full-duplex ですがな。そこまでスピード出るか知らないけど。

起動した頃を見計らって、sshでログイン(ログインまで結構待たされる)

[ob: vm]$ ssh 10.0.0.2
sakae@10.0.0.2's password:
OpenBSD 6.0 (GENERIC.MP) #0: Wed Oct 19 06:38:40 JST 2016
  :

$

どうしてもssh出来ない時は、vmctl consloe OB60 で、コンソールからログイン出来る。 ただ、コンソールから入った場合、画面のrowsが24行になってるので、stty rows 42とか やって、一度viを起動すると、望みの行数になる。ちょっと面倒だ。

で、気になる時計の精度

# rdate -ncv ntp.nict.jp
Sat Nov  5 07:10:49 JST 2016
rdate: adjust local clock by -3.742078 seconds
# rdate -ncv ntp.nict.jp
Sat Nov  5 07:10:52 JST 2016
rdate: adjust local clock by -3.213282 seconds
# rdate -ncv ntp.nict.jp
Sat Nov  5 07:10:57 JST 2016
rdate: adjust local clock by -3.698113 seconds

1秒間隔ぐらいで実行してみたんだけど、もうメタメタです。お子様の持ってる おもちゃの時計でも、ここまでは狂わないぞ。

仮想PCの中から、ftpを使って外部にファイルを取りに行った時、パケットフィルターの 状況をモニターしてみた。

bash-4.3# pfctl -s state
all tcp xxx.xxx.xxx.130:29294 <- xxx.xxx.xxx.1:50043       ESTABLISHED:ESTABLISHED
all tcp 10.0.0.1:22775 -> 10.0.0.2:22       ESTABLISHED:ESTABLISHED
all tcp 202.232.140.70:21 <- 10.0.0.2:14760       ESTABLISHED:ESTABLISHED
all tcp xxx.xxx.xxx.130:57663 (10.0.0.2:14760) -> 202.232.140.70:21       ESTABLISHED:ESTABLISHED
all tcp 202.232.140.70:36618 <- 10.0.0.2:2704       ESTABLISHED:ESTABLISHED
all tcp xxx.xxx.xxx.130:58299 (10.0.0.2:2704) -> 202.232.140.70:36618       ESTABLISHED:ESTABLISHED

こちらは、rdateを使って、日本時間製造所に時刻を請求した時のもの。どうやらntpって udpで素早くやり取りするようだな。

bash-4.3# pfctl -s state
all tcp xxx.xxx.xxx.130:29294 <- xxx.xxx.xxx.1:50043       ESTABLISHED:ESTABLISHED
all tcp 10.0.0.1:22775 -> 10.0.0.2:22       ESTABLISHED:ESTABLISHED
all udp xxx.xxx.xxx.2:53 <- 10.0.0.2:29446       SINGLE:MULTIPLE
all udp xxx.xxx.xxx.130:62450 (10.0.0.2:29446) -> xxx.xxx.xxx.2:53       MULTIPLE:SINGLE
all udp xxx.xxx.xxx.2:53 <- 10.0.0.2:46758       SINGLE:MULTIPLE
all udp xxx.xxx.xxx.130:52229 (10.0.0.2:46758) -> xxx.xxx.xxx.2:53       MULTIPLE:SINGLE
all udp 133.243.238.163:123 <- 10.0.0.2:6743       SINGLE:MULTIPLE
all udp xxx.xxx.xxx.130:53517 (10.0.0.2:6743) -> 133.243.238.163:123       MULTIPLE:SINGLE

どうも、オペレーションがスムーズに出来ない(pingで、あらぬ時間待たされるとか)のは、 時計関係がめちゃめちゃになってると思われる。

OSの時計機構

ゲストOSの時計を使わない、host側からのping

bash-4.3# ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2): 56 data bytes
64 bytes from 10.0.0.2: icmp_seq=0 ttl=255 time=0.678 ms
64 bytes from 10.0.0.2: icmp_seq=6 ttl=255 time=0.432 ms
  :
64 bytes from 10.0.0.2: icmp_seq=7 ttl=255 time=0.419 ms
64 bytes from 10.0.0.2: icmp_seq=8 ttl=255 time=0.429 ms
--- 10.0.0.2 ping statistics ---
9 packets transmitted, 9 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 0.419/0.462/0.678/0.077 ms

今度は、その逆パターン。計時はゲスト側になってる仮想PCが担当

# ping xxx.xxx.xxx.130
PING xxx.xxx.xxx.130 (xxx.xxx.xxx.130): 56 data bytes
64 bytes from xxx.xxx.xxx.130: icmp_seq=0 ttl=255 time=22860.223 ms
64 bytes from xxx.xxx.xxx.130: icmp_seq=1 ttl=255 time=1.508 ms
64 bytes from xxx.xxx.xxx.130: icmp_seq=2 ttl=255 time=641.645 ms
64 bytes from xxx.xxx.xxx.130: icmp_seq=3 ttl=255 time=1.508 ms
--- xxx.xxx.xxx.130 ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 1.508/5876.221/22860.223/9809.200 ms

もう、めためたですよ。(毎度、こればっかりです)

代表例として、rdateを見る。コードは、/usr/src/usr.sbin/rdate/ 以下に置いてある。

rdate.cを見ると、ntpなり、隣のマシンから時刻を貰ってきて、

                if (!slidetime) {
                        logwtmp("|", "date", "");
                        if (settimeofday(&pdata.new, NULL) == -1)
                                err(1, "Could not set time of day");
                        logwtmp("{", "date", "");
                } else {
                        if (adjtime(&pdata.adjust, NULL) == -1)
                                err(1, "Could not adjust time of day");
                }

一気に合わせるなら、settimeofdayを、ゆっくり合わせるなら、adjtimeを使うとな。 一気に合わせると、lastに記録が残るとな。見たら、本当に残っていた。そんな事は 今まで知らなかったぞ。

で、両手続き共、システムコールでしたよ。次はそれを探してみる。

[ob: sys]$ find . -name '*.[ch]' | xargs grep settimeofday
./arch/sparc64/sparc64/machdep.c:                * to resettodr() (e.g. from settimeofday()).
./kern/init_sysent.c:   { 2, s(struct sys_settimeofday_args), 0,
./kern/init_sysent.c:       sys_settimeofday },                 /* 68 = settimeofday */
./kern/kern_pledge.c:   [SYS_settimeofday] = PLEDGE_SETTIME,
./kern/kern_time.c:/* This function is used by clock_settime and settimeofday */
./kern/kern_time.c:sys_settimeofday(struct proc *p, void *v, register_t *retval)
./kern/kern_time.c:     struct sys_settimeofday_args /* {
./kern/syscalls.c:      "settimeofday",                 /* 68 = settimeofday */
./sys/syscall.h:/* syscall: "settimeofday" ret: "int" args: "const struct timeval *" "const struct timezone *" */
./sys/syscall.h:#define SYS_settimeofday        68
./sys/syscallargs.h:struct sys_settimeofday_args {
./sys/syscallargs.h:int sys_settimeofday(struct proc *, void *, register_t *);
./sys/time.h:int        settimeofday(const struct timeval *, const struct timezone *);

kern_time.c あたりを見ると、settimeofdayの中からsettimeが呼ばれ、核心部分は、

        nanotime(&now);
        if (securelevel > 1 && timespeccmp(ts, &now, <)) {
                printf("denied attempt to set clock back %lld seconds\n",
                    (long long)now.tv_sec - ts->tv_sec);
                return (EPERM);
        }

        tc_setrealtimeclock(ts);
        resettodr();

このあたりのようだ。

tc_setrealtimeclock()は、kern_tc.c に有った。どうやら、CPUが持ってる タイマーを利用している模様。

resettodr()の方は、RTCをリセットするようだ。場所は、arch/amd64/isa/clock.cに 有った。ここで繋がったな。無いハードに対してアクセスしてて、無駄な時間を過ごすの だろう。

ちょっと、消化不良だな。 下記が参考になるかな。

時刻と時間の管理、プロセスのスケジューリング

Ticklessカーネルとクロックソースに関するお話