minix (2)

この国の「通信格差」。学生の通信環境問題を考える

なる程、コロナウィルスの影響でオンライン授業ですか。足を引っ張るのは通信環境。スマホしか無い学生とかは確かに辛いはなあ。で、ルーターを無料で貸し出して、通信費を稼ごうという、3社がいるわけね。何処まで足元見るんだよ。安倍ちゃん、何とかしてやれよ。ああ、担当は、あのおばちゃんか。

で、授業にはZoomが使われる。これって新種のスカイプか、私設のユーチューブか。名前を変えて、新しいものが出てくるのね。

Zoomがズームインして、色々と取り沙汰されています。セキュリティ的にヤバいんで使うなとか、ニューヨーク市は学校でのZoom禁止、セキュリティ上の懸念からMS Teamsに移行へ

こちらの方もその口で、パッケージを削除しましょ作戦発動。 Linux用zoomパッケージ:お粗末なメンテナスクリプトというのを発見されたみたい。

変化が有る時は、色々とチャンスが有りますなあ(ヲイ)。

ftp

VirtualBoxに入れているminix 3.2.1には、netconfって言う設定プログラムが用意されている。ちょっと起動してみると

# netconf
MINIX 3 currently supports the following Ethernet cards. PCI cards detected
by MINIX are marked with *. Please choose:

 0.   No Ethernet card (no networking)
 1.   3Com 501 or 3Com 509 based card
 2.   Realtek 8029 based card (also emulated by Qemu)
 3.   NE2000, 3com 503 or WD based card (also emulated by Bochs)
 4.   Attansic/Atheros L2 FastEthernet
 5.   DEC Tulip 21140A in VirtualPC
 6.   Intel PRO/1000 Gigabit
 7.   Intel PRO/100
 8. * AMD LANCE (also emulated by VMWare and VirtualBox)
 9.   Realtek 8139 based card
10.   Realtek 8169 based card
11.   Virtio network device
12.   Different Ethernet card (no networking)

Ethernet card? [8] ^C

こんな具合に、設定が進んで行く。それはいいんだけど、このプログラムがシェルスクリプトって所が特典だ(簡単に読める)。

つらつら見て行くと、

    echo "ifconfig -I /dev/ip0 -n $netmask -h $ip" > $RCNET
    test ! -z $gateway && echo "add_route -g $gateway" >> $RCNET

こんなのが出て来た。デフォルトルーターの設定方法が分かったぞ。長年DHCPに毒されているとすっかり忘れてしまうものだな。昔よくやったじゃん。route add -gateway とかね。

qemuの入れてるminix 3.1.0では、dhcpで設定を反映してくれないので、最低限を手動で設定してみる。

# ifconfig -I /dev/ip -h 10.0.2.15 -n 255.255.255.0
# add_route -g 10.0.2.2
# pr_routes
ent #   if               dest         gateway dist  pref  mtu flags
    0  ip0         0.0.0.0/0         10.0.2.2    1     0    0 static

そして、起動してるftpサーバーに接続。

$ ftp 10.0.2.15
220 FTP service (Ftpd 2.00) ready on minix at Mon, 06 Apr 2020 06:22:43 GMT
Username: sakae
331 Password required for sakae.
Password:
230 User sakae logged in, directory /home/sakae.
ftp>ls
200 Port command okay.
150 File NLST okay.  Opening data connection.
.ashrc
.ellepro.b1
.ellepro.e
.emacs.d
.emacs.el
.exrc
.profile
a.out
date.c
226 Transfer finished successfully. 0.08 KB/s

ちゃんと動いているっぽい。ならば、外と接続出来るか?

$ ftp xxx.xxx.xxx.xxx
ftp: ioctl error on NWIOTCPCONN: Destination not reachable

だめって言われた。telnetも動いているんで、外に行けるか試したけどやはり駄目。ならば、外側からminixに接続出来るかやってみたけど、これも駄目。qemuの壁が立ちはだかっていた。残念至極。

いじけて、懐かしくなってOpenBSDでrouteを確認。

ob$ route show
Routing tables

Internet:
Destination        Gateway            Flags   Refs      Use   Mtu  Prio Iface
default            xxx.xxx.xxx.2      UGS        4       23     -     8 em0
 :

at VirtualBox

ふとqemuが駄目ならvboxが有るじゃんと閃いてしまった。入れてみる鹿。 結論を書いてしまうと、vboxのの方が悲惨だった。

NICがfxpでは認識しない。e100ってデバイスなはずなんだけど駄目。ならばlanceと思った。実績あるからね。boot時に認識するも、手動でアドレスをセット出来ない。 それに、dhcpdも起動してないし。

/usr/src/servers/inet/inet_config.cあたりを見ると、NICデバイスを使えるように設定してるはずなんだけど、無しのつぶて。諦めムードで、NIC回りがどうなっているか調査。

# ls -l /dev | grep 7,
crw------- 1 root    operator  17,   0 Oct 18  2005 cmos
crw------- 2 root    operator   7,   0 Oct 18  2005 eth
crw------- 2 root    operator   7,   0 Oct 18  2005 eth0
crw------- 2 root    operator   7,   1 Oct 18  2005 ip
crw------- 2 root    operator   7,   1 Oct 18  2005 ip0
crw-rw-rw- 2 root    operator   7,   2 Oct 18  2005 tcp
crw-rw-rw- 2 root    operator   7,   2 Oct 18  2005 tcp0
crw-rw-rw- 2 root    operator   7,   3 Oct 18  2005 udp
crw-rw-rw- 2 root    operator   7,   3 Oct 18  2005 udp0

これを見ると、tcp/udpが普通に扱える事が分かる。eth/ipはユーザーが触る術は無し。icmpはサポート外?

メジャー番号が7番って事は、fxpとかlanceとかのドライバー相当なんだな。そのエイリアスと言うかマイナー番号が、それぞれのプロトコルを担当するとな。

ifconfig した時にデバイスが見つからんと言われたので、ifconfigソースを見る。

                ip_fd= open (device_s, O_RDWR);
                if (ip_fd<0)
                {
                        if (a_flag && (errno == ENOENT || errno == ENXIO))
                                continue;

                        fprintf(stderr, "%s: Unable to open '%s': %s\n",
                                prog_name, device_s, strerror(errno));
                        exit(1);
                }

デバイスが無い、デバイスファイルが無いって、んな馬鹿な! 騙されてるっぽいぞ。

ついでなんで、ipaddressをどうやってセットしてるか見ておく。

        ipconf.nwic_flags= NWIC_IPADDR_SET;
        ipconf.nwic_ipaddr= ipaddr;

        result= ioctl(ip_fd, NWIOSIPCONF, &ipconf);

ioconfな構造体は、/usr/src/include/net/gen/ip_io.hに有ったぞ。

typedef struct nwio_ipconf
{
        u32_t   nwic_flags;
        ipaddr_t nwic_ipaddr;
        ipaddr_t nwic_netmask;
        u16_t nwic_mtu;
} nwio_ipconf_t;
#define NWIC_NOFLAGS            0x0
#define NWIC_FLAGS              0x7
#       define NWIC_IPADDR_SET          0x1
#       define NWIC_NETMASK_SET         0x2
#       define NWIC_MTU_SET             0x4

デバイス内部のデータになってる訳ね。

プログラムの作りが単純だから、簡単に追えたけど、OpenBSDのソースを紐解いたらどうなるんだろう? 興味津々で追ってみるか。

4000行近い、鬼のようなコードになってた。何から何まで詰め込んでしまったって感じ。ブリッジの設定はかろうじて別ファイルになってるけど、CARP、WiFi、INET6、group、mpls、PPPOE、VLANまで有るよ。

includeしてるヘッダーに、sys/ioctl.hが有るんで、察してくださいが正解かな。 流し読みしてると、コマンドテーブルが表れた。そこを見て、望む所へ飛んで行けっていう、シュミレーター風に追いかけるのが正解かな。

dhcp

dhcp問題を考える。ARPのお世話にならなかったっけ?

vbox上のFreeBSD

[sakae@fb ~]$ arp -a
? (10.0.2.15) at 08:00:27:xx:xx:xx on em0 permanent [ethernet]
? (10.0.2.2) at 52:54:00:yy:yy:yy on em0 expires in 1197 seconds [ethernet]

vbox上のminix 3.2.1

$ arp -a
10.0.2.2 is at 52:54:00:xx:xx:xx

minix 3.1.0には、そもそもarpなんてコマンドが無いなあ。 Dynamic Host Configuration Protocolを見ると、色々書いてある。設定を追加しないと駄目なのかなあ。

でも、その先に待っているのは、telnetとftpと言う非常に古風な接続方式。もう少しすると著名なブラウザーはftpのサポートを止めるって言うしね。

3.2.1の方で一応確認(悪あがきモード炸裂)

$ dhcpd -q
DHCP data for network 0:
        op = 2 (REPLY)
        xid = 1586323594
        ciaddr = 10.0.2.15
        yiaddr = 10.0.2.15
        siaddr = 10.0.2.4
        chaddr = 080027ABCDEF
        file = "Minix3.pxe"
        netmask = 255.255.255.0
        gateway = 10.0.2.2
        DNSserver = XXX.XXX.XXX.XXX       ;; from NTT
        T15 = 666C6574732D656173742E6A70
        T51 = 00015180
        T53 = 05
        T54 = 0A000202

これの意味する所は、DHCPのメッセージフォーマットを参照。親のDHCPサーバーは、10.0.2.4になってる。/etc/dhcp.confが無くても、自動割り当てしてくれるのか。知らんかったわい。

minixではIPアドレスの割り当てにdhcpdが使われていた。これ、同時にdhclientの役目も兼ねているんだな。OpenBSDとかだと、割り付けにはdhclientって言う機能縮小版が使われている。 ならば、上のような割り当て情報はどうやって調べるん?

そんなユーザーへの回答が

ob$ cat /var/db/dhclient.leases.em0
lease {
  fixed-address xxx.xxx.xxx.138;
  next-server xxx.xxx.xxx.254;
  option subnet-mask 255.255.255.0;
  option routers xxx.xxx.xxx.2;
  option domain-name-servers xxx.xxx.xxx.2;
  option domain-name "localdomain";
  option broadcast-address xxx.xxx.xxx.255;
  option dhcp-lease-time 1800;
  option dhcp-message-type 5;
  option dhcp-server-identifier xxx.xxx.xxx.254;
  option dhcp-client-identifier 1:0:c:29:ab:cd:ef;
  epoch 1586412637;
  renew 4 2020/04/09 06:25:37 UTC;
  rebind 4 2020/04/09 06:36:52 UTC;
  expire 4 2020/04/09 06:40:37 UTC;
}

これ、VMwarePlayerの所業になるな。リース時間は30分更新なのね。

mount

前からmountってどういう操作が行われるか疑問に思っていたんだ。minix本を読んでいたら、ファイルシステムの所に解説が載っていた。

核心は、mount.c/do_mountの最後の所。その間に有る省略しちゃった部分は、諸々の検査を行っている(例えば、ブロックデバイスかとかマジック番号はOKか等)。minixでは、システムコールがdo_xxxと表される事になっている。

/* Perform the mount(name, mfile, rd_only) system call. */
   :
/* Nothing else can go wrong.  Perform the mount. */
  rip->i_mount = I_MOUNT;       /* this bit says the inode is mounted on */
  sp->s_imount = rip;
  sp->s_isup = root_ip;
  sp->s_rd_only = rd_only;

構造体spは、デバイス側(name)のスーパーブロック(但し、メモリー上にのみ存在)を表している。rip構造体は、マウントポイント側(mfile)になる。

super_block構造体の一部

  /* The following items are only used when the super_block is in memory. */
  struct buf *s_imap[I_MAP_SLOTS]; /* pointers to the in-core inode bit map */
  struct buf *s_zmap[ZMAP_SLOTS]; /* pointers to the in-core zone bit map */
  dev_nr s_dev;                 /* whose super block is this? */
  struct inode *s_isup;         /* inode for root dir of mounted file sys */
  struct inode *s_imount;       /* inode mounted on */
  real_time s_time;             /* time of last update */
  char s_rd_only;               /* set to 1 iff file sys mounted read only */
  char s_dirt;                  /* CLEAN or DIRTY */
} super_block[NR_SUPERS];

inode構造体

  /* The following items are only used when the super_block is in memory. */
    :
  struct inode *s_imount;       /* inode mounted on */

マウントポイント側では、マウントに使われてますよって印を付けているんだな。この印に出会うと、デバイス側のスーパーブロックを参照するとな。だから、マウントが解除されるまで、 マウントポイントのdirは見えない。

これを利用して、マルサの女や、悪い奴から保護する秘密の場所にしておけるぞ。

eat_path

mountでファイルツリーの途中に接ぎ木するのは分かった。今を盛りと咲き乱れている桜みたいに、接ぎ木で成長させてるんだ。何処かのOSみたいに、C:\Windows みたいな不格好さは無い訳ね。

で、この接ぎ木した木をどうやって辿って行くか? 辿り方をまとめたファイルが有った。 path.c

 *  The entry points into this file are
 *   eat_path:   the 'main' routine of the path-to-inode conversion mechanism
 *   last_dir:   find the final directory on a given path
 *   advance:    parse one component of a path name
 *   search_dir: search a directory for a string and return its inode number

冒頭にある目安。ファイルパスを喰って、ファイルの在処を教えてくれるとな。これと同じ機能がOpenBSDでは、nameiって関数になってたなと思い出した。調べてみると

HISTORY
     The namei() function first appeared in Version 4 AT&T UNIX.  Its name is
     an abbreviation for the name-to-inode conversion which it performed
     before the appearance of vfs(9) in 4.3BSD-Reno.

伝統的な関数名である。minixでは、教育的見地から、eat_pathって命名したんだな。eat wellって標語でオリンピック選手を支援してるメーカーがあったな。ふと、思い出したぞ。

話が逸れた。軌道修正して、mountの際に設定された情報の使い方を見る。advance内で使われていた。該当部分

  /* See if the inode is mounted on.  If so, switch to root directory of the
   * mounted file system.  The super_block provides the linkage between the
   * inode mounted on and the root directory of the mounted file system.
   */
  while (rip->i_mount == I_MOUNT) {
        /* The inode is indeed mounted on. */
        for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) {
                if (sp->s_imount == rip) {
                        /* Release the inode mounted on.  Replace by the
                         * inode of the root inode of the mounted device.
                         */
                        put_inode(rip);
                        rip = get_inode(sp->s_dev, ROOT_INODE);
                        break;
                }
        }
  }
  return(rip);          /* return pointer to inode's component */

dirに印が付いていたら、スーパーブロックテーブル(最大5個)をスキャンして目的のinodeをマウントしたデバイスのROOT(頂点と言うか根)から探す。 接ぎ木された枝に栄養が移動してくような仕組みだ。

染井吉野の花は飽きた。何年か先を見越して、新しい品種を育てないかねぇ。オイラーは色の濃いやつが好きだ。花桃みたいなやつも、艶やかでいいぞ。

umount

mountの仕組みを見たなら、解放するumountを直交性を考えて見ておく。

mount.c/do_umount

  /* See if the mounted device is busy.  Only 1 inode using it should be
   * open -- the root inode -- and that inode only 1 time.
   */
  count = 0;
  for (rip = &inode[0]; rip< &inode[NR_INODES]; rip++)
        if (rip->i_count > 0 && rip->i_dev == dev) count += rip->i_count;
  if (count > 1) return(EBUSY); /* can't umount a busy file system */

umountしようとするツリー上に居ると、コメントにも有るようなエラーを喰らう。 使ってる時に勝手に切断されては堪りませんからね。

そのチェックを無事に通過すると、念の為にと言うか義務で、syncシステムコールを実行して、 まだ書き込まれていないファイルを書き出す。そして、mount時に設定した情報をクリアと言うかrelease(解放)して、終了となる。

etc

Windows で Raspbian システムを起動(QEMU, qemu-rpi-kernel を使用)