各プラットフォームにおけるMACアドレスランダム化の振る舞い

これを書いている時点では、コンシューマ向け機器としては、AndroidiOSWindowsにおいてMACアドレスのランダム化を利用できる。

では具体的に、MACアドレスをランダム化したらどのように振る舞うのか? 参考文書へのポインタと現時点での振る舞いの要約を書き残しておく。

Android

要約
  • Android 8以降では、端末がネットワークに関連付けられていない場合に、新しいネットワークを探索する時にランダムなMACアドレスが使用される。
  • Android 9では、開発者向けオプション経由で、Wi-Fi接続時にランダムなMACアドレスを使用することができる。
    • デフォルトでは、Wi-Fi接続時のMACアドレスランダム化は無効化されている。
  • Android 10以降では、Wi-Fi接続時のMACアドレスランダム化が有効化されている。
    • 接続先ネットワークごとに、個別にランダム化の有効/無効を切り替えることが可能。
  • AndroidMACアドレスランダム化の振る舞いは「永続的なランダム化」と「非永続的なランダム化」の2種類がある。
    • 永続的なランダム化
      1. ネットワークプロファイル(SSIDやセキュリティタイプ)ごとに、初回接続時にランダムなMACアドレスが生成される。
      2. 2回目以降の接続時には、初回に生成されたランダムなMACアドレスが使いまわされる。
      3. 端末を工場出荷状態にリセットすると、生成済みの「ランダムなMACアドレス」は削除される。
    • 非永続的なランダム化
      1. DHCPのリース期間が期限切れとなり、かつ端末が前回切断してから4時間以上経過した状態にて、切断したネットワークに再度接続する際に、ランダムなMACアドレスが生成し直される。
      2. 使用中の「ランダムなMACアドレス」が、生成されてから24時間以上経過した後に、当該ネットワークに再度接続する際に、ランダムなMACアドレスが生成し直される。
      3. 上記以外のケースでは、前回生成された「ランダムなMACアドレス」が使いまわされる。
  • Android 10では常に「永続的なランダム化」が使用される。
  • Android 11では、デフォルトでは「永続的なランダム化」が使用される。開発者向けオプション経由で、常に「非永続的なランダム化」を使用するように変更できる。
  • Android 12では、デフォルトでは、一部のネットワークにたいして「非永続的なランダム化」が使用されて、それ以外では「永続的なランダム化」が使用される。開発者向けオプション経由で、常に「非永続的なランダム化」を使用するように変更できる。
    • 「非永続的なランダム化」が使用される条件だが、公式文書を読んでもいまいちよく分からなかった……。

iOS

要約
  • 端末がネットワークに関連付けられていない場合に、新しいネットワークを探索する時にランダムなMACアドレスが使用される。
  • 端末がネットワークに関連付けられていないか、もしくは端末のプロセッサがスリープ状態の場合に、ePNOスキャン実行時にランダムなMACアドレスが使用される。
  • iOS 14・iPadOS 14・watchOS 7以降では、デフォルトではWi-Fi接続時にランダムなMACアドレスが使用される。
    • 接続先ネットワークごとに、個別にランダム化の有効/無効を切り替えることが可能。
  • Wi-Fi接続時のMACアドレスランダム化の振る舞い:
    1. Wi-Fiネットワークごとに、初回接続時にランダムなMACアドレスが生成される。
    2. 端末を工場出荷状態にリセットすると、生成済みの「ランダムなMACアドレス」は削除される。
    3. iOS 15・iPadOS 15・watchOS 8以降では:
      1. 端末が前回切断してから6週間以上経過した状態にて、切断したネットワークに再度接続する際に、ランダムなMACアドレスが生成し直される。
      2. 端末にてネットワークの設定を削除してから2週間以上経過した状態にて、当該ネットワークに再度接続する際に、ランダムなMACアドレスが生成し直される。

Windows

要約
  • Windows 10にて「ランダムなハードウェアアドレス」などの名称で導入されたようだが、どのバージョンからなのかは不明。割と初期のころから導入済みだったようだが……。
  • 振る舞いとしては「オフ・オン・毎日変更する」の3種類を選択できる。
    • 「オフ」は文字通り「ランダムなハードウェアアドレスを使わない≒デバイスMACアドレスを使用する」ということだと思われる。
    • 「オン」や「毎日変更する」の振る舞いがよく分からない。
    • わざわざ「毎日変更する」を用意しているということは、「オン」はAndroidでいう「永続的なランダム化」みたいな振る舞いだと思うのだが、どうなのだろうか? 仮にそうだったとして、一度生成された「ランダムなMACアドレス」が削除されるパスは存在するのだろうか?
    • 「毎日変更する」にしても、律儀に「24時間経過したらMACアドレスを再生成してネットワークに再接続」みたいなことはしない気がする*1のだけど、実際にはどんな感じだろうか?
参考文書

なし。MACアドレスランダム化の挙動についてまとまった公式の文書が見つからない……。

まとめ

AndroidiOSも、徐々に「ランダムなMACアドレスを生成し直す」方向に向かっているようだ。

それはそうとして、MicrosoftMACアドレスランダム化の具体的な挙動について情報を公開してほしい(すでに公開しているなら、もうちょっと分かりやすいところに置いてくれないだろうか)。

*1:なぜならWi-Fi接続が一時的に切れることになるから。

Raspberry Pi向けのクロスコンパイル環境の整え方 2022年夏

4年ほど前にRaspberry Pi向けのC/C++ロスコンパイル環境を整えたのだけど、当時と今では環境構築の方法が異なるようだ。

ということで、現時点(2022年8月)での「Linux上でRaspberry Pi向けクロスコンパイル環境を構築する方法」についてメモを残しておく。

古い方法:GitHubに公開されているツールチェーンを使う

4年前の時は、https://github.com/raspberrypi/toolsで公開されていたツールチェーンをUbuntuにcloneして使った。

git clone https://github.com/raspberrypi/tools

最近このリポジトリを見たところ「このツールチェーンはもう古いから、別のを使え」との一文が……。

最近の方法:ホスト環境の「公式リポジトリのクロスコンパイル用ツールチェーン」を使う(※ディストロの種類やバージョンに注意)

前項のリポジトリには、代替として「Ubuntuなどの公式リポジトリのクロスコンパイル用ツールチェーン」を使う案が提示されている。

# Raspberry Pi OS 32bit版向けのツールチェーンを入れる場合:
sudo apt-get install gcc-arm-linux-gnueabihf

# Raspberry Pi OS 64bit版向けのツールチェーンを入れる場合:
sudo apt-get install gcc-aarch64-linux-gnu

ただしこの方法には「明示されていない罠」が存在する。少なくとも、ターゲット環境がRaspberry Pi OS (bullseye) であるならば、ホスト環境はUbuntu 20.04ないしDebian 11である必要がある。

例えばUbuntu 22.04にて上記の方法でクロスコンパイル環境を整えた場合、深く考えずに実行ファイルをクロスコンパイルしたならば、十中八九Raspberry Pi OS上で動作しない。

理由はglibcのバージョン違いにある。

Debian 11 (bullseye) のlibc6libc6-amd64のバージョンから推測できるが、Raspberry Pi OS (bullseye) のglibcのバージョンは2.31である。

一方でUbuntu 22.04の公式リポジトリからARM向けクロスコンパイル用ツールチェーンをインストールした場合、クロスコンパイル用のglibcは、32bit向け64bit向けも、バージョンは2.35である。

普通にビルドするとglibcは動的リンクされるので、Ubuntu 22.04でクロスコンパイルすると「実行時にglibc 2.35が動的リンクされる」想定の実行ファイルが生成される。

このファイルをRaspberry Pi OS (bullseye) に持っていって実行しても、実行時にglibc 2.35が見つからず、動的リンクに失敗してしまい、起動できない。

この問題への対策は、2通り考えられる。

1つ目は、ホスト環境として「クロスコンパイル用のglibcのバージョンがRaspberry Pi OS (bullseye) のglibcと同じ2.31となるディストリビューション」を選択する方法である。

Debian 11 (bullseye) なら確実に大丈夫だ。Ubuntuでも20.04ならglibc 2.31なので問題は生じない。

2つ目は、クロスコンパイル時にglibcを静的リンクしてしまう方法である。glibcを動的リンクするのではなく、静的リンクして丸抱えしてしまえば、実行時にバージョン違いの問題に悩まされることはない。

とはいえ静的リンクすると実行ファイルが肥大化するし、glibcにセキュリティ修正などが発生するたびにアプリを再ビルドして「修正済みのglibc」を実行ファイルに同梱しなくてはならなくなる。

そんな訳で、個人的には、静的リンクは「最終手段」として温存しておきたいところである。

ということで、Raspberry Pi OS (bullseye) で動かすソフトウェアをクロスコンパイルするならば、Debian 11 (bullseye) かUbuntu 20.04にクロスコンパイル環境を構築するのがよさそうだ。Windows使いならどちらもWSLでインストールできる。他の仮想環境ないし実機上にホスト環境を用意するなら、今ならRaspberry Pi Desktopもアリかもしれない。

最近の別解:Raspberry Pi GCC Toolchainsを使う

サードパーティ*1になるが、Raspberry Pi GCC Toolchainsという、Raspberry Pi向けに色々とチューニングされたGCCツールチェーンを配布しているプロジェクトがある。

このプロジェクトが配布しているクロスコンパイル用ツールチェーンならば、ある程度はホスト環境について自由が利くようだ。

ただしこのツールチェーンの実績や安定性については不明である。

*1:つまりRaspberry Pi公式のプロジェクトではなさそう。

書籍購入:『コーディングを支える技術』

個人的な調べ物の資料として購入。

言語機能について歴史を踏まえて書いてある本なので、この本の記述を踏み台にして歴史的文献に当たればよさそう。

――そうだよね、「コの業界は歴史が浅い」と言いつつも、高水準プログラミング言語の観点でも60年を超える積み重ねがあるんだよなあ。

書籍購入:『インサイドWindows 第7版 上』

そろそろ日本語版の下巻が出るようなので、第6版からの買い替え。

あとで読む。

Windowsも色々と変わっているので、いざという時にポインタとなる資料がないと心許ない。

書籍購入:『Linuxで動かしながら学ぶTCP/IPネットワーク入門』

LinuxのNetwork Namespaceについての参考資料として購入。

本来意図しているだろう「TCP/IPネットワークの入門書」としては……個人的に、TCP/IPまわりは独学かつ「机上の知識」ベースな人なので、「仮想環境とはいえ実際の挙動を確認できるのは便利だよね」と思いながら読み進めている。

まあ、ある程度カチッとしたカリキュラムを考えるならば、「本書+座学系のTCP/IP入門書」のセットで、チューター付きで講義と実習をする感じかなあ。

WindowsではCtrl-cでPythonのスクリプトを即時中断できないことがある

CPythonの既知の不具合に遭遇した。処理系本体ではなく、標準ライブラリの問題のようだ。

界隈では有名な話かもしれないが、忘れないように個人的なメモを残しておく。

具体的には、標準ライブラリのsocketを使用して自前でUDPパケットを受信する、以下のようなスクリプトで遭遇した。

#!/usr/bin/env python3

import socket

BIND_ADDR = '127.0.0.1'
BIND_PORT = 41214

try:
    for ai in socket.getaddrinfo(BIND_ADDR, BIND_PORT, socket.AF_UNSPEC,
                                 socket.SOCK_DGRAM):
        family, socktype, protocol, _, sockaddr = ai
        with socket.socket(family, socktype, protocol) as sock:
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.bind(sockaddr)

            print(f'listening on {sockaddr[0]}:{sockaddr[1]}')

            while True:
                data, addr = sock.recvfrom(65535)
                print(f'receive from {addr[0]}:{addr[1]} : {repr(data)}')
except KeyboardInterrupt:
    print('KeyboardInterrupt')

このスクリプトをコンソールで実行した時、macOS 11やUbuntu 20.04のPython 3.8と、Raspberry Pi OS bullseyeのPython 3.9では、Ctrl-cで終了した。しかしMicrosoft StoreからインストールしたPython 3.10 (amd64) では、Ctrl-cを入力しても即座には終了しなかった。

(1年前に、このスクリプトと同等の処理をCygwinPython 3.6で実行した際には、正しくCtrl-cで終了した記憶がある。なので、純粋なWindows版ビルドで発生する現象だと思う)

実験した感じでは、socket.recvfrom()ブロッキングしている最中にCtrl-cが入力された場合、入力された時点では何も起きない。Ctrl-c入力後、パケットを受信してsocket.recvfrom()によるブロッキングが解除された時点で、ようやくKeyboardInterruptが発生するようだ。

この挙動は既知の不具合のようで、GitHubのCPythonのリポジトリSIGINT blocked by socket operations like recv on Windows #85609というIssueがあった。Winsockの仕様(実装?)に起因する制約のようだ。*1

Winsock絡みということなので、今回はMicrosoft Store版でしか挙動を確認していないが、おそらく公式サイトで配布されているWindows版バイナリでも同様の症状が見られるのではないかと思う。

この問題の回避策の1つは、タイムアウトモードないし非ブロッキングモードのソケットを使うことだ。常にブロッキングさせるのではなく、例えば100ミリ秒から500ミリ秒ぐらいの短い周期でブロッキングが解除されるようにしておけば、解除された時にCtrl-cの入力に起因するKeyboardInterruptが発生して、スクリプトが停止する。

今回、問題に遭遇したスクリプトでは、お手軽なのはsocket.settimeout()を使用してタイムアウトモードを使う方法だった。

#!/usr/bin/env python3

import socket

BIND_ADDR = '127.0.0.1'
BIND_PORT = 41214

try:
    for ai in socket.getaddrinfo(BIND_ADDR, BIND_PORT, socket.AF_UNSPEC,
                                 socket.SOCK_DGRAM):
        family, socktype, protocol, _, sockaddr = ai
        with socket.socket(family, socktype, protocol) as sock:
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.bind(sockaddr)

            # Workaround to handle keyboard interrupt on Windows.
            sock.settimeout(0.2)

            print(f'listening on {sockaddr[0]}:{sockaddr[1]}')

            while True:
                try:
                    data, addr = sock.recvfrom(65535)
                except socket.timeout:
                    continue
                print(f'receive from {addr[0]}:{addr[1]} : {repr(data)}')
except KeyboardInterrupt:
    print('KeyboardInterrupt')

もう1つの回避策は、いきなりsocket.recvfrom()TCPの場合はsocket.accept())を呼び出すのではなく、selectselectorsを使用して、ソケットが読み込み可能になるまで待ってから処理を行う、というものだ。

読み込み可能になるまで待つ周期を100ミリ秒から500ミリ秒ぐらいの短い時間にしておけば、タイムアウト後にCtrl-cに起因するKeyboardInterruptが発生して、スクリプトが停止する。

標準ライブラリのsocketserverではこの方法が採用されている。つまりserve_forever()の引数poll_intervalは、selectorsを使用してソケットが読み込み可能になるまで待つ周期だ。この引数に妙に大きな値を指定しなければ、Ctrl-c入力によるスクリプトの停止がいい感じに機能してくれる(引数poll_intervalのデフォルト値は0.5秒(500ミリ秒)だ)。

そういう意味でも、TCP/UDP通信のサーバ側を実装する際には、最初はsocketではなくsocketserverを使うことを検討しろ、ということなのかもしれない。

*1:このIssueには記載されていないが、WindowsにはPOSIXのシグナルがない(だからSIGINTなどの一部のシグナルのみをランタイム側でシミュレートしている)という事情も絡んでいるのかもしれない。

今までどのくらいプログラミング言語を触ってきたか(3秒で挫折したものものも含む) Ver.14

2022-06-19現在のステータス。
https://eel3.hatenablog.com/entry/2021/06/20/211327 から1年経て、こうなっている。

なおCSS、HTML、XMLはひとまず除外する。人工言語ではあるけれども「プログラミング言語」という括りに含められるか否かは議論が分かれる気がする。*1

よく使っている

AWK (Gawk)
単純なテキストレコードの処理はAWKで十分間に合う。今の時代、自作ツールをnawkやGNU awk単体で実装するのは苦行すぎて*2皆無なものの、シェルスクリプトMakefileAWKのコードを埋め込むなどして他のコマンドと組み合わせて使う機会は依然として多い。シェル上でワンライナーでテキスト処理する時にも重宝している。これはこれで十分AWKらしい使い方ではないだろうか?
C++
ちょくちょくお仕事で使うが、未だに本職のC++使いではない。C++11やC++14は非常に便利で、better Cでも使う価値がある。C言語使いからすると、C++03時代よりも充実度が進んだ標準ライブラリ、ラムダ式、autoによる型推論――などなど、便利で羨ましい限りだ*3。あと、Swift時代のクロスプラットフォームC++ライブラリ作者は、どうあがいてもARCから逃れられないので、C++11以降のスマートポインタは必須だ*4正規表現とスレッドも標準ライブラリに加わったので、あとはソケット(低水準ネットワークAPI)をサポートしてくれないだろうか。C++17のstd::optionalも、KotlinやSwiftの経験者としては好ましい。低水準の処理を行いつつも便利な機能で実装時間を短縮できる点は便利で、少なくともシステムプログラム言語としての利点だと思う。だけど機能多すぎ/複雑すぎなところはなんとかならないものか。強力な反面、使い手を選ぶ言語だ。
C言語
お仕事での主力言語だった――ここ最近は使ってないなあ(C++のコードの一部にC言語寄りのコードを埋め込むことはあるけど)。シンプルかつ低水準の世界が垣間見れるところが割と好き。とはいえ最近の他の言語と比較すると、シンプルすぎて安全機構が欠けていたり標準の便利機能が少なかったりするので、入門用の言語としては薦められない。にもかかわらず、かつてはプログラミング未経験者向けのC言語の本が盛んに出版されていた――あれ、何だったのだろうか? 謎だ。クロスプラットフォームなモジュール屋としては、今までC89を採用してきたものの、いい加減そろそろC99とかC11とか次世代の言語とか使いたい。でも「まだサポート終了していないVisual Studio」のことまで考えると、C99すら2023年まで使えない*5。悲しいなぁ。
DOSバッチファイル
プログラミング言語に含まれるかどうか不明だが、含めてしまう。ちょっとした自動化や、複数ツールを組み合わせて使うときのラッパーとして、今でもよく使う。コマンドプロンプトはシバン(shebang)に対応していないので、スクリプト言語で書いたツールを起動するラッパーとしても多用している。意外と色々なコマンドが用意されているので、単純にそれらを叩く分には十分だが――言語機能がショボいので、バッチファイルでifやforのような制御構文系コマンドが必要になってきたら、何か決定的に間違えていないか、考え直すようにしている。
JavaScript(クライアントサイド)
ものすごく久しぶりにクライアントサイドJavaScriptのコード触った――いまどき珍しい、DOM直叩きスタイルだけど。収穫は、最近のECMAScriptのスタイルに触れたぐらいだろうか? フレームワークもTypeScriptも触っていないので、クライアントサイド開発のスキルは依然として賞味期限切れのままだ。
JavaScript(サーバサイド?)
初めてお仕事でNode.js向けのJavaScriptのコードを触った。Web開発の外の人からみた印象としては、ブラウザ以外のJavaScript処理系はNode.jsに収斂しちゃった感があるなあ――Denoもあるけど、エコシステム的に今後どうなるんだろう?
Kotlin
本格的にAndroidアプリ開発に関わるようになったのがGoogle I/O 2017直後の過渡期なので、JavaよりもKotlinでの経験値の方が多い。モダンな「強い静的型付け」の、割とええ感じの言語やね。ただ、使い始めが「Swift 3をつまみ食いして半年以上経ってからKotlinをつまみ食いした」みたいな経緯だったこともあり、未だに両者の概念・機能が頭の中でごった煮になっている。それと、NDK絡みの作業が多いので、C++11/14・Java 7/8・Kotlinを行ったり来たり。泣けるぜ。Swiftもそうだが、最近のメジャーな「強い静的型付け」の言語は「開発環境込み」で高い生産性とコードの安全性を両立させる方向に進んでいる気がする。
make (Makefile)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで……いやGNU Makeはそこそこプログラミング言語的か。GNU Make 4.0はさらにプログラミング言語的だな、特にGNU Guileなところが。GNU MakeとNMAKEが主力で、稀にNetBSD Make(pmake)を使うが、いずれも独自拡張アリアリだ。もう素のmakeではMakefileを書けない :)
Objective-C, Objective-C++
時代はSwiftだと言われて久しいけど、どっこいObjective-CObjective-C++は生きている。というかSwiftのコードにC++で書かれたライブラリを直接組み込むことができない以上、両者を繋げるグルー言語として生き残ることになるよね。一定以上のリアルタイム性が求められるアプリをSwiftだけで書くのは厳しくて、どうしても部分的にC言語C++を使うことになり、グルー言語としてObjective-Cが召喚されることになる。最近流行の言語と比べると良くも悪くも80年代的だが、アプリケーションプログラミング用としてはC言語よりマシだし、C++ほど複雑怪奇*6ではない。そしてC言語C++で書かれた既存のライブラリをそのまま使える。Objective-Cのハイブリッドな所は好きだが、Objective-C++はハイブリッドすぎて――C++のクラスとObjective-Cのクラスを、C++ラムダ式Objective-Cのブロック構文を同時に使うのは大変だ。便利ではあるんだけどね。
Python
Python 2.xで実装した小ツール群をPython 3.xに移植した。Pythonではlazyなスタイルでのコーディングが許されず、整然とコードを記述する必要がある。その辺は、Perl 5やRubyとは随分と雰囲気が異なる。気になるのは、インデントが必須な言語仕様であるために、シェルスクリプトに埋め込んで使うのが苦痛な点だ。Pythonだけでコードを書く分には気にならないのだけど。
Swift
コンパイラによる強力な型推論と型安全性のチェック」がお仕事用のメジャーな言語にまで降りてきたという点で、Swiftは静的型付け言語界のJavaScript*7だと思っている。でもユーザ数的には、Kotlinが「静的型付け言語界のJavaScript」ポジションなのかもしれない。割と好感が持てる言語だが、知識が中途半端にKotlinとごった煮になっているので、ついうっかりif式を書こうとしてコンパイルエラーになったり、「varval」と「varlet」の振る舞いの差異につまづいたりしてしまう*8
シェルスクリプト (/bin/sh)
プログラミング言語に含まれるかどうか不明だが……いや、私的にはシェルスクリプトは立派なプログラミング言語だ。基本的な用途は、バッチファイルと同じくちょっとした自動化や複数コマンドを組み合わせて使うときのラッパーだが、実現できる内容は遥かに多い。言語本体(?)がバッチファイルよりも高機能だし、Unixユーザランドはコマンドが充実している。その意味では、WindowsではMSYSよりもCygwinで――いやむしろWSL(Windows Subsystem for Linux)で環境構築すべきだろう。Cygwinでは、主要な処理をシェルスクリプトで記述しておき、bashからはシェルスクリプトを利用し、コマンドプロンプトではラッパーのバッチファイル経由でシェルスクリプトを叩く使い方をしている。ただWindows上では処理速度が妙に遅くなる点が不満だ。まあしかし、Unixのシェルは言語設計もシステム開発技法も未成熟だった大昔に「プアな環境でも問題なく動作する、プログラマブルな対話型コマンドインタプリタ」として開発された代物なので、言語設計の研究が進んでから作られたプログラミング言語と比較してはならない。

あまり使っていない

Perl 5
時々、やむをえない事情で触ることがある。だが基本的によく分からない。何というか、あの記号の羅列っぽさに中々慣れないというか、自分は余りに自由度が高すぎる言語は苦手だと気づいたというか。(言語仕様に慣れているなら)半ば使い捨てなテキストフィルタとかをさっと書くに分には悪くない言語だと思うのだけど。
Ruby
自作ツール実装にて、AWKの代替言語の最有力候補だった*9。テキスト処理でも割と重宝するが、バイナリデータへの変換が絡んでくるとAWKよりもRubyを使った方が効果的だ*10。そろそろirbを電卓代わりに使うスタイルも板に付いてきた気がする。to_s(16)やto_s(2)で基数変換して表示できるところが好き。
sed
プログラミング言語に含まれるかどうか不明だが、DSL扱いで*11。テキスト処理用。シェルスクリプトMakefileにて他のコマンドと組み合わせて使う。というか正規表現でのテキスト置換以外の機能を使った記憶が……あったな、dとiとpと=とブレースによるグループ化ぐらいだが。私の技術レベルではsedFizzBuzzを書けないので、sedで難しい処理を記述しないようにしている。
Windows PowerShell
時代はPowerShell Coreらしいが、現行のWindows 10でデフォルトで利用できるv5.1に留まったままである。スクリプト言語としてのPowerShellは、オブジェクト指向.NET Frameworkを叩けてダイナミックスコープでスクリプトブロック(という名の無名関数)と、無茶でピーキーで完全にプログラマ向けな代物だ。Microsoftもよくもこんなエライ代物を出したものだ。残念なことに、コマンドプロンプトの代替という観点では、外部ツールとの親和性が微妙にイマイチだ(特に文字コードとか)。でもPowerShell内で閉じている分には問題ないので、私の手元では「Windows専用のGUI付き小ツールを作るためのスクリプト言語」か「Excel COMとか叩く用のスクリプト言語」か「Windows Serverの管理スクリプトを書くためのスクリプト言語」扱いしている。ところで、いい加減『Windows PowerShell イン アクション』並みの言語解説書の最新バージョン対応版を出版してくれないだろうか。

最近使ってないが、縁は切れてない

bash
最近はデフォルトシェルがbashな環境も多いので、自分用のツールぐらいは素の/bin/shではなくbashで書いても大丈夫な気がしてきた。shよりbashの方が遥かに便利だからなあ――PerlRuby等には負けるけど。bashスクリプトを書くときの唯一の欠点は、メジャーバージョンごとの差異や各ディストリでのビルドオプションの違いにより、同じbashという名前でも実は千差万別なところだと思う。PerlRubyのバージョンは気にするけど、これがシェルになると意外とバージョンに無頓着になってしまう。なんでだろう?
C#
かつて、勉強を兼ねてC# 2.0を少し触ろうとするも未完に終わり、数年後にあらためてVisual Studio 2013をインストールして少しだけ触った*12けどほんの少しだけで終わった過去をもつ私。変数の型推論ラムダ式LINQ・デフォルト引数は便利だなあと思っていたら、いつの間にかC# 8.0になってKotlinやSwiftに見られる流行を取り入れてますな。おっちゃん、付いてくのが大変だよ。.NET Frameworkの機能数は反則ものだが、所々に微妙に抽象化が行き過ぎたAPIが見られるのは気のせいだろうか? それにしても、クラスが必須ではないC言語C++に慣れてしまった弊害か、アプリケーション・メインエントリすらclass内に定義しなくてはならないC#には、なかなか慣れない。
Free Pascal
お試しで触っているのだが、微妙にDelphi/Free Pascal初心者(ただし他言語の経験者)向けの良い資料が少なくて難儀している。玉石混交なのだ。いっそのこと『OBJECT PASCAL HANDBOOK―マルチデバイス開発ツールDelphiのためのプログラミング言語完全ガイド』を買ってしまおうかしら……と思っていたら絶版っぽい。
Go
寡作ながらもいくつか小ツールを書いてみたが、標準ライブラリが充実しているコンパイラ型言語っていいっすね。C言語に比べればC++の標準ライブラリも充実しているが、どちらかといえばプリミティブな機能が中心だ。PythonRubyばりの標準ライブラリを抱えているGoには及ばない。その辺は、やはりCプログラマ(特にCでフィルタやデーモンの類を書く層)には受けそうな言語だと思う。並列処理周り(goroutines)とかARM対応とかが気になる。ソフトリアルタイム限定だが「組み込みLinux + Goで書いたデーモン」とかどうだろう? ただメモリを食うらしいという噂がどうなったか気になる――64bit環境では解消されるという話だったようだが、32bit環境でも解消されるようになったのだろうか? 組み込みでは現時点では逆立ちしたって64bit CPUはありえないからなあ、スマホタブレット以外では。
Java
生まれて初めて触れたプログラミング言語その2。実のところ、職業プログラマとして本格的に使用することは一生ないと思っていた。Androidアプリ開発も、Kotlin採用後に本腰入れて関わるようになったので、Kotlinメインだ。だが、なぜかぬるい感じに時々Javaのコードを触っている。先にコレクションの操作方法が充実した他の言語を学んでからJavaを本格的に触るようになったので、Java 8以降のStream APIが使えないと身体が拒否反応を示す。少なくとも、構文の見た目こそ保守的なオブジェクト指向プログラミング・スタイルで書かれたC++に似ているけど、中身はC++とは似ても似つかない代物だということは体感している。
Lua
Wiresharkのパケット解析スクリプトを書いたことも、C言語で書かれたUnixデーモンの設定ファイル用に処理系を組み込んだこともあった*13。あれから数年経ったが、今はどんな感じなんだろう?
QML
宣伝文句のとおり、QMLはGUIの記述に非常に向いている。それも、単に標準のUI部品(エレメント)を使うだけでなく、少し改造して使うとか、オリジナルのUI部品を作って使うとか、それらを別のアプリケーションに使いまわすとか、そういう時に威力を発揮する。あと、プロパティバインディングやレイアウトのアンカー指定など、画面サイズの変更に追随するUIを作りやすい機能も揃っている。JavaScriptでちょっとした処理も記述できる――とはいえ、やりすぎるとパフォーマンスの罠が……。少なくとも、JavaScriptでゴリゴリコードを書くのはQML的ではない。QMLは宣言的に、シンプルに書くものだ。力技でロジックでゴリ押しすると、色々と罠に嵌る言語だ。
Scheme
GaucheWindowsネイティブ環境用バイナリは実験版だが、私が触る分には何の支障もない*14ことに気づいて久しい今日この頃。『Scheme手習い』と『Scheme修行』を購入したので、とりあえずCommon LispではなくGaucheScheme)の勉強をする方向に転換しようか検討*15しているうちに何年たったのやら。Gaucheはフィルタ・ライクな小ツールの実装用としても良い感じだ。しかし最も多い利用方法はREPLを電卓代わりにすることだ*16。うーん、作業環境がmacOSLinuxに移ったなら、大手を振ってGaucheでフィルタを書くのだが。
SQL
生まれて初めて触れたプログラミング言語その3ぐらいに位置する。組み込みの人なのでSQLとは無縁だと思っていたが、まさかTransact-SQLを少しだけ触ることになるとは。最近はAndroidアプリ絡みでSQLiteに触れることもあるが、AndroidXのRoom経由だったり、ContentResolverのqueryだったりと、フルセットのSQL文ではなく局所局所でDSL的に使う感じである。
Tcl/Tk
Tclは書き方を忘れた頃にテキスト処理ツールを書いている気がする。Tclは結構独特な言語だ。構文がシェルスクリプトばりに全てコマンドだったり、値が全て文字列だったり、実はリスト構造だったり、意外とTCPソケット通信が得意だったり……。それでも慣れれば結構使いやすい。意外とプロトタイピングに向いている気がする。8.6以降ではオブジェクト指向プログラミングもOKだが、それよりも例外処理用のtry末尾呼び出しの最適化用のtailcallの方が興味深い。しかし、これからメジャーになる可能性は低そうだ。Tkは……小規模なGUIツールをさくっと構築できるところは便利だが、Webアプリ全盛の時代にどれだけ訴求力があるのやら。
Visual Basic .NET
Visual Basic .NET 2003で書かれたコードを時々メンテ中。流石に開発環境はVisual Studio 2013移行したけど。
XSLT
よく考えてみたら生まれて初めて触れたプログラミング言語その4ぐらいに位置する言語だった。縁が切れたと思いきや、仕事でXHTMLから特定要素を抜き出す作業に使うことがあったり……。XMLからテキストレコードに変換してしまえば、後はUnix流テキストフィルタの世界が待っている。餅は餅屋というもので、定型的なXMLの変換はXSLTで記述するべきか。唯一気に入らないのは、xsl:sortでアルファベットの大文字と小文字を区別してソートすることができないこと。ぐぬぬぬ。

これから(また)使うかもしれない

Alloy
形式手法の中では比較的カジュアルに使えそうなので期待中。入門書も処理系も入手した。私の場合、先に何か論理型の言語をかじった方がよいのかも。
bison (yacc)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで。やっぱり構文解析系統のコードを自作するのは割に合わない――だなんてうそぶきつつ、LALR法とか全く知らないままに、既存のyaccのコードを切り貼りして遊んでみた。簡易電卓レベルだが便利さを体感しつつ、さっそくtypo 1文字で痛い目(shift/reduce)に遭った。とりあえず、flexと組み合わせた上でのエラー処理(エラーメッセージの改善)が課題だ。
Common Lisp
2009年に勉強しようと思い立ったものの、未だに進んでいない。階乗とかハノイの塔とかiotaぐらいは書いたが、目標は「ちょっとしたツールを自作する」だ。まだ道は遠い。最近は時々CLISPを簡易電卓代わりにしている。
Coq
ソフトウェアの基礎が気になるので、処理系だけ入手。
F#
OCamlは「Windows上で日本語を扱う」という視点では処理系がちょっと微妙なので、いっそのことF#に乗り換えようかと……。『実践F#』が積読状態になっている。
flex (lex)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで。字句解析用のツールという印象が強かったのだが、よく考えてみたら、flexは「sed(1)のよくある使い方」と同様に「正規表現でパターンマッチング --> 何らかのアクション」という内容を記述するためのツールだった。ただ単に、「何らかのアクション」をC言語で書けることと、flex自体ではパターンマッチングをせずに「パターンマッチングするC言語のコード」を生成することが少々風変わりなだけ。grep(1)やsed(1)その他で小ツールを実装して運用しつつ、性能が求められたらflexで専用ツール化する――とか考えたけど、普通にgrep(1)やsed(1)を使う方が高速だった。
Forth
pForthをMinGWでビルドしたので処理系は手元にある。スタック指向の言語はいつか勉強したい。
Io
プロトタイプベースである点を除けば、何となくSmalltalk的であるような――公式ドキュメントらしきIo Programming Guideでも影響を受けた言語として真っ先にSmalltalkが挙げられているから、あながち思い違いでもないだろう。今更ながら『7つの言語 7つの世界』のIoの章を読み終えたので、ちょっとしたコード片を書いているが……Windows版のバイナリが古いためか、リファレンス通りなのに動作しないコードに直面している。
LOGO
そういえばLOGOを触ったことがない。とりあえずUCBLogo(Berkeley Logo)だろうか? Windows上でUCBLogoばりにGUI無しで動作する処理系はないだろうか?
Object REXX
思うところがあって処理系とIBM謹製のドキュメントを入手したものの、そこから先の進展は無いまま。ReginaでClassic REXXっぽい感じで触っているからなあ。
OCaml
Common Lispを勉強するはずが、いつの間にか触っていた言語。一応、階乗ぐらいは書いた。時間が取れたらもうちょっとしっかりと勉強したいが、面倒なのでF#に移行しようか検討中。
Oz
ふと思い立ってUbuntuにMozartを入れた。『Scheme手習い』の次はCTMCP片手にOzで勉強かなあ。道は遠いな……。
PostScript
これかForthか、どちらに手を出すべきか? 悩ましい。
Processing
入門書も処理系も入手して、あとは弄る時間をつくるだけ。
Prolog
『7つの言語、7つの世界』の地図の色分けプログラムには衝撃を受けた。何というか「正しい問い」を見つけられるか否かが肝なのか。この辺は、根底の部分でAlloyに通じる何かがあるように思う。ひとまず、Prologで論理プログラミングと宣言的なスタイルに慣れておけば、形式手法にて「論理で宣言的に」記述するときに戸惑いが減るのではないかと期待している。
Rust
仕事柄「C/C++の次のシステムプログラミング言語」はそれなりに興味の対象で、Go言語やD言語ほどではないが、Rustも……まあ、気にならなくはない。ちなみに、これら3言語と同列にObjective-CやSwiftが挙げられることもあるようだが、個人的見解としては、システムプログラミング言語としてのこの2言語には全く期待していない。あれは、Appleというしがらみからは逃れられないでしょうな。
VBA (Visual Basic for Applications)
今までVBAから逃げ回っていたのだが、ついに使うことになりそうな予感。たぶん、Access VBA 8割にExcel VBA 2割ぐらい。

今は全く使っていない

Active Basic
VBScripを触りだした影響で、時々思い出しては弄くっていた。ほんの少しだけ触って放置して、すっかり忘れてからまた触る――これを繰り返していた気がする。なので毎度初めて触るのと同じ状態だった。String型をバシバシ使用 :)
bc
その昔、Windows標準の電卓アプリの代わりに使おうとして色々あって挫折した。今はirbclisp/goshで計算しているからなあ。
CASL II
生まれて初めて触れたプログラミング言語その1。何だかんだで、後でCプログラマになってからも低水準での思考ツールとして微妙に役に立っている。まあ考えるための言語であって実用言語ではない。仮に実用的な処理系*17があったとしても余りに命令がシンプル過ぎて悶絶するなあ、なんてFizzBuzzしてみて思った。
Clojure, Scala
JDKがなくてもJava APIを叩くスクリプトを書けるので非常に便利。Scala型推論とか、便利っすね。言語仕様はJavaよりも好みだ。とはいえ、IoT時代にJava VMベースでどこまでメインストリームに居残ることができるのか? ちょっと興味深い。サーバサイドに活路を見出すのだろうか?
COBOL
FizzBuzzするためだけにOpenCOBOL 1.0をWindows上に用意して触ってみた。なんというか、COBOLの名前と生まれた時代が示すように基幹業務(というかお金や帳簿が絡んでくるところ)向けの言語だよなあ、といった感じ。COBOL 2002のフリーフォーマットを採用するだけでも使い勝手が変わる気がしたが、世の中にはまだ広まらないのだろうか。
CoffeeScript
仕事で使う予定はない。RubyPythonその他の影響を受けているだけあり、その手のスクリプト言語っぽい感じでコードを書けるので、慣れれば素のJavaScriptで直接コーディングするよりは楽だ。しかし標準ライブラリ回りや処理系絡みの機能やサードパーティのライブラリなど、結局はJavaScriptを知らないとCoffeeScriptでコードを書けないと思う。それに生成されたJavaScriptのコードを見て「うわぁ、これあまり効率的でないなあ」と感じる時もあって、高速化が必要な部分では生成されるコードを気にしながら記述したりCoffeeScriptを諦めてJavaScriptで書くことになるので、やはりJavaScriptを知らないとマズイ。とはいえ便利なのは確かだ。CoffeeScriptのコードは即Node.jsで実行できるので、その辺りから「CoffeeScriptでテキストフィルタ」的な文化が生まれると面白いかも。気になるのはECMAScript 6の存在で、今までCoffeeScript独自の機能だった部分の多くがES6で取り込まれるので、今後ES6対応が進むにつれてCoffeeScriptの立場がどうなっていくのか、少々興味深い。
D言語 2.x
仕事柄「C/C++の次のシステムプログラミング言語」はそれなりに興味の対象で、Go言語ほどではないが、D言語も気になる存在だ。D言語シンタックスがC・C++に近いだけでなく、コーディングしている時のアプローチ・判断についても、CやC++での流儀がそこそこ通用しやすい気がする。少なくとも、Go言語でコーディングするよりは、文化的背景の違いによるモヤモヤは感じにくい。あと、標準ライブラリを使ってテキストフィルタを書いたところ、エラー処理を1~2ヶ所のtry - catchにスッキリまとめることができて、ちょっと驚いた。throwされる例外のメッセージ文字列が、ちょうどよい塩梅の内容だったため、メッセージを変更する(いったんcatchして、再throwする)必要がなかった。ちょっと残念なのは、マルチバイト対応だが……。
Emacs Lisp
.emacsにコピペ」限定で。Common LispSchemeを触ったためか、何となく内容を追えるようになってきた気がしていたが、勘違いだった。
Fortran
Fortran 90やFortran 95あたりは結構近代的な言語だと思う。用途次第ではC言語よりもFortranの方が遥かにマシな選択だろう。配列がらみの処理はFortranの方が得意だし、言語機能としてのモジュール化の方法はC言語には存在しない。可変長な文字列の扱いに微妙な制限がある点はマイナスな気もするが、まあ基本的に数値計算プログラム用の言語だからなあ。
GDB (GNU Debugger)
……いやGDBはデバッガとして使っているが、GDBスクリプトを書く機会は(FizzBuzz以外に)ない。勉強不足なだけかもしれない。
Groovy
JDKがなくてもJava APIを叩くスクリプトを書けるので非常に便利。動的型付け言語っぽくいくもよし、@CompileStaticや@TypeCheckedで型推論するもよし。言語仕様はJavaよりも好みだ。コンソールアプリを書く人としては、オプション引数解析用の機能を標準で持っている点で、GroovyはClojureScalaよりもポイントが高い*18。個人的には、IoT時代に「Java VMベース」の言語としてどこに活路を見出すのが、興味深く見守りたいところ。やはりサーバサイドだろうか?
HSP (Hot Soup Processor)
FizzBuzzで楽しんでみたが、何というか他言語経験者には受けが悪そうな命令体系だと思う。もっとも初心者がプログラミングという行為に深入りせずにWindows用のGUIな何かを作る分には、あの命令体系でも十分な気がしないでもない。ところで元々は「HSPで職業プログラマ的な良いコードを書くと、どんな感じになるか?」というネタを思いついて処理系を用意したのだけど、そちらは全く進展がないまま。
JScript on WSH
他人が使うテキスト処理ツールの実装に使って以来、時々触ってきた。Windows用の配布可能な小ツールを実装する時の定番言語だった。でもそろそろ潮時だろう。HTAと組み合わせてクライアントサイドJavaScriptなノリで簡易なGUIツールを実装できる点も、PowerShell + WPF + XAMLで代替できそうだ。他のメリットは「JavaScriptECMAScript)でフィルタを書ける」だったが、WSHのなかなか目的にたどり着けないオブジェクト階層にイライラするよりも、Node.jsやPhantomJSを使ったほうが精神衛生的にマシだ。
m4
その昔テキスト処理用に触ろうとして、Windows用のどの処理系も日本語の置換に何かしらの問題を抱えていたので泣く泣く諦めた。思うところがあって改めて少し触ってみたが――なるほど、確かに中毒性のある言語*19だ。
Smalltalk (Squeak, Pharo)
Smalltalkは有名な古典的プログラミング言語だというのに、触ったことがない。ということでSqueakとPharoの処理系のみ準備完了。うーん、「環境」付きなのが気になる――言語を弄くる基準が「コンソール上でテキストフィルタ」という変な人種な私だからなあ。
Smalltalk (GNU Smalltalk)
個人の思想信条による理由よりSqueakとPharoにわだかまりを感じてしまう変人なので、邪道だと思いつつもコンソールでテキスト処理もOKなGNU Smalltalkも用意してみた。これで言語としてのSmalltalkの勉強に集中できる……か?
REXX
Open Object REXXの処理系を入手したのに、何故かReginaを入れてClassic REXXっぽい方向に走っていた。何というか、COMコンポーネント.NET Frameworkと無関係でいられるのなら、バッチファイルの代替としてはREXXあたりがほどよい塩梅だと感じる。しかし最近流行の言語とは随分と勝手が違うし、日本語の情報も少ない。メインフレーム以外の世界で流行る可能性は少ないだろう。
T4 Text Template
「へえ、こんなものがVisual Studioに入っていたのか。機能多すぎで色々と便利なツールを見逃しているんだな、やっぱり」と思いつつ触ってみた。テンプレート変換の用途ではピカ一だと思う。ただ処理系を手に入れる方法が「Visual Studioをインストールする」or「MonoDevelopをインストールする」なので、何となく「単体で手軽に使えるツール」ではないというイメージが……。まあC#VBで処理を記述するので、それらの処理系が必要だという面での制約なのだろう。
VBScript on WSH
JScriptほどではないが「Windows上で他人も使えるツールを書くためのLL」扱いしていた言語。Windows Server管理の関係で触っていた。というかWebで入手可能なWSHのサンプルの大半がVBScriptで書かれていたり、ADSI関連のコレクションをJScriptで舐めれなかったりして、結局は必要に駆られて使用することに。明快に記述できる文法は評価に値するが、スクリプト言語としては少々冗長だ。配列は自動拡張しないし、組み込み関数はプリミティブ気味だし、冗長気味な文法との合わせ技でコードがさらに冗長になっていく……。文法や言語仕様の詳細なドキュメントが見つからないのだが、どこにあるのだろうか?*20
Vim script
少し触ってみた分には、exコマンドの拡張(=コマンドの羅列)とは気づかない程度にはプログラミング言語らしいと思う。とはいえ妙なところで嵌ったり微妙に一貫性のない部分があったりするので、その辺りで好き嫌いが別れる気がする。
秀丸マクロ
7年ほど秀丸エディタを使っていたが、マクロを書く機会はなかった。一念発起してFizzBuzzしてみて感じたのは、最近の便利な言語に慣れた身としては色々とモヤモヤ感がある言語仕様だということ(歴史的経緯的に仕方ないのだが)。とはいえちょっとした拡張ツール的なものを手軽に作れそうではあった。

*1:HTML5 + CSS」の組み合わせなら、チューリング完全の疑惑があったり、JavaScript使わずにCSSでWebチャットを作った猛者がいたりと、色々と怪しいのだけど。

*2:「独立性の高い単体のツールを実装する」という視点では、現代ではAWKよりも便利な言語が山ほどある。

*3:しかし標準ライブラリの充実度をJavaC#.NET Framework含む)と比較したり、型推論まわりをKotlinやSwiftと比較してはいけない。以前よりも随分と便利になったのだけど、だけど、隣の芝生を見てしまうと、うーん……。

*4:SwiftではC++の機能を直接呼び出すことができないので、Objective-Cでラッピングして利用することになる(インタフェースはObjective-Cで、内部実装はObjective-C++)。この時、Objective-Cクラスのインスタンス変数として「C++クラスのインスタンスを保持するスマートポインタ」を持つ構成にしておくと、Objective-Cクラスのインスタンスがdeallocされる時に、スマートポインタ経由でC++クラスのインスタンスもdeleteされる。

*5:本格的にC99がサポートされ始めたのはVisual Studio 2013以降だ。それよりも前のバージョンのEOLだが、Visual Studio 2012が2023年1月となっている。

*6:少なくともC++の言語仕様は私の手には余る。自分が把握している範囲の機能でコードを書くのは好きなのだけど。

*7:私の認識では、JavaScriptは、第一級関数やクロージャがお仕事用のメジャーな言語に組み込まれて、少なくない人が使う契機となった言語だ。

*8:Kotlinのvalは「再代入不可の変数」だ(定数はconstで定義する)。Kotlinのプリミティブ型以外のデータ型はclass(つまり参照型)なので、valで定義した変数を破壊的操作する行為は割と普通だと思う。一方でSwiftのletは定数であるし、値型が中心の言語仕様である影響かstructやenum(つまり値型として振る舞うデータ型)が多用されるので、letで定義した変数を破壊的操作できるケースとできないケースが生じる。

*9:でも最近はPythonが多いんだな、これが。

*10:とはいえ、ついついC++を使ってしまうのだよなあ。

*11:これでもsedチューリング完全な言語だと証明されているらしい。

*12:言語仕様的にはC# 5.0の環境だが、ライブラビまわりはC# 4.0相当だったはず。

*13:Windowsのことを考えなければ、自前でライブラリをビルドしてアプリに組み込むのは結構簡単だった。

*14:支障がある部分を触るほど深入りするには、あと20年ぐらい掛かるのではないか?

*15:Schemeの勉強というよりも、再帰の勉強なのか?

*16:現状はirbclispかgoshの3択だ。

*17:――といってもシミュレータだけど。

*18:ClojureScalaに関しては、同様の機能を私が見逃している可能性も高いのだが。

*19:m4はマクロプロセッサなのでプログラミング言語ではないはずだけど……。

*20:MSDNの資料では物足りない。もうちょっと掘り下げた内容のものが欲しい。

Debian stretchからbusterにアップグレードした環境でiptablesからnftablesに乗り換える

Debian 10ではパケットフィルタリングまわりの設定に使用するフレームワークがnftablesに変更されたのだが、この変更が「既に運用中の『iptablesを使用しているDebian 9』」をアップグレードする際にどのような影響を及ぼすのか、メモを残しておく。

Debian LTSでシステムを運用している場合、2022-06-30にDebian 9 LTSのサポートが終了するので、切羽詰まってこれからDebian 10に移行する私みたいな人もいるだろう)

先に要約を書いておく:

  • 前提条件:
    • /etc/iptables直下にrules.v4などの名前でiptablesのルールファイルを置き、netfilter-persistentを使用して起動時にパケットフィルタリングの設定を適用する構成の、Debian 9のシステムを運用している。
    • このシステムにDebian 10をアップグレード・インストールする。
  • 設定ファイルまわりを極力変更したくない人向け:
    • アップグレード時に特に何もする必要はない。
  • アップグレードを機に、パケットフィルタリングの設定管理をiptablesからnftablesに移行したい人向け:
    • おそらくアップグレード完了の時点でnftablesのパッケージはインストール済みのはず。
    • おそらくルールファイルの移行にiptables-restore-translateコマンドは不要。
    • systemdのサービス(netfilter-persistent.servicenftables.service)の切り替えに留意すること。

本題。

Debian 10のリリースノートの記述には、以下のようなことが書かれている。

  • パケットフィルタリングの既定のフレームワークがnftablesに変更された。
  • iptables系のコマンドは、既定では「内部的にnftablesを使用する版」になっている。
    • update-alternativesを使用して「旧来のx_tablesを使用する版」に変更することが可能。

今までiptablesを使用してきた環境をDebian 10にアップグレードした場合、「起動時にnetfilter-persistentiptablesのルールファイルが読み込まれる」という挙動は今まで通りとなる。ただし、使用されるのはiptables系のコマンドだが、内部ではnftablesのフレームワークが用いられている。

つまりパケットフィルタリング設定のフロントエンドはiptablesのまま、内部の処理だけ変更されている訳だ。

だから実用的には、実のところパケットフィルタリングまわりに手を付ける必要はない。今まで通りにiptablesでフィルタリング・ルールを書いておけば、ちゃんと機能する。

一方で、この機会にパケットフィルタリング設定の操作までnftables用のインタフェースに置き換えたいと考える人もいるだろう。

Debian 10にアップグレードして再起動した際、起動時にnetfilter-persistent経由で「内部的にnftablesを使用する版」のiptablesのコマンドにてパケットフィルタリングの設定が適用された状態になっている。

なので、この状態で深く考えずにいきなり以下のような手順で作業することで、nftables用のパケットフィルタリングのルールファイルを生成できる。何しろ元データは起動時にnetfilter-persistent経由で読み込み済みな訳なので……。

# 念のため元のルールファイルを残しておく。
sudo mv /etc/nftables.conf /etc/nftables.conf.old
# nftables用のルールファイルを生成。
printf '%s\n\n%s\n\n' '#!/usr/sbin/nft -f' 'flush ruleset' | sudo tee /etc/nftables.conf >/dev/null
sudo /usr/sbin/nft -s list ruleset | sudo tee -a /etc/nftables.conf >/dev/null
sudo chmod 755 /etc/nftables.conf

Debian管理者ハンドブックnftables wikiのルールファイル移行に関する記述を参考にすると、以下のような手順になりそうな気がするが、これは間違いである。

# iptablesのルールファイルをnft用のコマンドファイルに変換。
sudo /usr/sbin/iptables-restore-translate -f /etc/iptables/rules.v4 >ruleset.nft
# コマンドファイルを適用。
sudo /usr/sbin/nft -f ruleset.nft
# nftables用のルールファイルを保存しておく。
sudo /usr/sbin/nft -s list ruleset | sudo tee /etc/nftables.conf >/dev/null

起動時にnftablesに設定が読み込まれた状態でnft -f ruleset.nftすることになるので、パケットフィルタリングのルールが二重に登録された変な状態のルールファイルになってしまう。

そんなことをしなくても、すでにフィルタリング・ルールは読み込まれているのだから、それをそのままnft -s list rulesetで出力すればよいのだ。

さて、ドキュメント類では微妙に書かれていないが、ルールファイルの移行以外でやるべきことが1つある。それはsystemdのサービスをnetfilter-persistent.serviceからnftables.serviceに切り替えることだ。

切り替えをしないと、せっかくルールファイルを/etc/nftables.confに移行しても、起動時の読み込み元が/etc/iptables/rules.v4のままになってしまう。後日、/etc/nftables.confの内容を変更した際に「再起動するとフィルタリングのルールが元に戻ってしまう」という謎の現象に悩まされることになるかもしれない。

切り替えの手順はこんな感じだろうか?

# netfilter-persistentを停止して無効化。
sudo systemctl stop netfilter-persistent.service
sudo systemctl disable netfilter-persistent.service
# 念のため手動起動も禁止しておく。
sudo systemctl mask netfilter-persistent.service

# nftables.serviceを有効化
sudo systemctl enable nftables.service

これで起動時に/etc/nftables.confのルールが読み込まれるようになる。

余談:iptablesのルールはフラットなので、機械的にnftablesのルールファイルに変換した結果もフラットになる。もしかしたら「よりnftablesらしい」ルールファイルに書き換えたくなるかもしれない。この時、IPアドレスの代わりにFQDNを使ってはならない。手元の環境では、システム起動後にnft -f /etc/nftables.confと手動で読み込ませればFQDNからIPアドレスに変換されるのだが、起動時だとルールの読み込み自体に失敗してしまい、ルール未設定の状態になってしまうようだ。

河豚板のi386版でfiupdateを使いたい

先日Super-microDXにインストールした河豚板をfiupdateでアップデートしようとしたところ、すこしつまづいてしまったので、メモを残しておく。

前提というか背景を書いておくと、Super-microDXにはコンパクトフラッシュのドライブ(リーダ?)が内蔵されている。OSはコンパクトフラッシュにインストールして運用することが多い*1。なお河豚板というかOpenBSDコンパクトフラッシュIDE(パラレルATA)として認識していて、転送モードとしてPIOモード4とUltra DMAモード2が選択されている。

「Super-microDXにインストールした河豚板」と書いているが、これはSuper-microDX上でUSBメモリから河豚板を起動して、usbfadmnewdriveコマンドを使用してコンパクトフラッシュ上に河豚板のLiveUSB版*2を書き込んだ後、コンパクトフラッシュからブートして使用しているのだ。

さて本題。河豚板はOpenBSDのパッチ適用などに合わせてアップデート版がリリースされる。LiveDVD版の場合はメディアを作り直すしかないのだが、LiveUSB版の場合はfiupdateというコマンドを使用して中身を直接更新することができる。

で、このfiupdate、少なくとも河豚板 7.1のi386版では、少しやり方を工夫しないと失敗する。私の環境ではgzip(1)に「No space left on device」と言われてしまった。

河豚板ではRAMディスク上にホームディレクトリ等が展開される。河豚板 7.1ではRAMディスクを実現するためにmfs(Memory File System)を使用しているのだが、i386でのmfsの最大サイズは約1GBである。

つまりホームディレクトリでfiupdateする場合、どう頑張っても1GBのディスクスペース上で作業することになってしまう。で、fiupdateするには1GBでは足りない。

mfsの最大サイズはアーキテクチャごとに異なるようで、amd64では32GB、arm64では16GBと河豚板ガイドに記載されている。だからamd64とarm64では、mfsの割り当てを工夫すればfiupdateがディスクフルで失敗する問題は回避できる。でもi386ではmfsの割り当ての調整ではどうにもならない。

なので、河豚板 i386版でfiupdateする場合は、別途書き込み可能な作業用パーティションを用意して、その中でfiupdateの手順を実行する必要がある。

手元の環境の場合、16GBのコンパクトフラッシュの大半をデータ保存用領域(noasksファイルとかが置いてあるパーティション)に割り当てている。そこで今回はデータ保存用領域を作業用パーティション代わりにしてfiupdateを実行した。

# rootで作業していると仮定:
mount /dev/wd0d /mnt
cd /mnt
ftp https://jp2.dl.fuguita.org/{MD5,FuguIta-7.1-i386-202205061.iso.gz}
fiupdate 202205061

更新はうまくいったようだ。

ただしSuper-microDXの個体の問題なのか、アップデートの最後にデータ保存用領域がうまくアンマウントされなかったようだ。そのため後で「fsck /dev/wd0d」とチェックを走らせることになった。この件はハードウェア側の問題だと思う。

2022-05-31追記:アンマウントの件は、河豚板 7.1 202205161まで存在していた「fiupdateの最後に実行されるrebootが失敗することがある問題」が原因だった模様。この問題は202205161と202205241の2回に分けて修正されたようだ。

*1:一応、USB2.0のポートが2つ付いているので、USBブートも可能である。しかしポート間の物理的な間隔が狭いため、ブート用のUSBデバイスの選択がちょっと面倒である。

*2:コンパクトフラッシュはUSBデバイスではないのだが、河豚板が公式で「LiveDVD版」と「LiveUSB版」という書き方をしているので、それに合わせている。

お願いお助けOpenBSD

10年前に新品で購入したPCにインストールできるメジャーなOSが減ってきた。思案した結果、ひとまずOpenBSDベースの河豚板を使ってみることにした。

件のPCは、ピノーが販売していたSizka Super-microDXという、少し癖のある代物だ。*1

何しろCPUがVortex86DXというx86互換の32bit SoCである。動作周波数は800MHz。メモリはオンボードのDDR2メモリが512MBだ*2

猛者ならFreeDOSを使ったり*3他のPCでクロスコンパイルしたOSを導入したりするかもしれないが、私はめんどくさがりなので、出来合いのビルド済み汎用OSをインストールして使用したい。したいのだが、セキュリティを気にして現行のOSから選択しようとすると、候補が少ないのだ。

インストール可能なOSが減ってきた理由は、Vortex86DXがサポートする命令セットの古さにある。Vortex86DXはi586*4の命令セットに概ね対応している*5。このSoCでOSを動かすためには、OSのバイナリはi586以前の命令で構成されている必要がある。i686*6以降の命令が含まれていると、実行時に該当する命令にたどり着いた時点でCPU例外が発生してストールする。

最近のLinuxx86-64のみのディストリビューションも多いのだが、x86(32bit)版をリリースしているものであっても、その中身は「i686以降をサポートする」だったりする。多種多様なアーキテクチャをサポートしているDebianでも、Debian 9 (stretch) にてi586はサポート対象外となっている*7

FreeBSD i386も、13.0-RELEASE以降ではi686が最低ラインだ。一方で12.x系はi586でも動作する……はずなのだが、なぜか12.2-RELEASE以降のGENERIC kernelのバイナリにはi686命令であるCMOVが含まれているため、i586では動作しない。この問題はFreeBSDのBugzillaに挙がっているようだが、先行きは不透明だ。

どうしたものかと思案していたところで、Vortex86DXを搭載したPCにてNetBSD 9.2とOpenBSD 7.0が動作したとの情報を発見した。

NetBSDOpenBSDなら、最近のビルドでも問題なく使えるようだ。

ということで勉強もかねて触ってみようと思ったのだが、誤解かもしれないが素のNetBSDOpenBSDはどちらもインストールが(他のOSと比較して)面倒くさい印象があるのでパス。NetBSDておくれLive Imageは良さそうだが、Vortex86DXで動かすには重そうなので*8、今回はパス。河豚板は起動時にコンソールを指定できるだけでなく、設定で永続化できるので、丁度よさそうだった。

今回は、あまり手間をかけずに環境を構築できる河豚板を使用して、OpenBSDCLI環境を構築することにした。まだ河豚板 7.1 202205011を入れただけの状態だが、今のところ問題なく動作している。

後で気づいたのだが、個体の問題なのか私のSuper-microDXは熱が原因らしきバスエラーが発生することがあるので、システムの利用と設定等の保存(ファイルシステムへの書き込み)を分離できる河豚板は割とマッチしているかもしれない……設定保存中にバスエラーが起きませんように。

*1:メディアなどで「煙草箱サイズ」と書かれていたが、個人的には、サイズ感はAppleの60W MagSafe電源アダプタに近いと思う。アレを1.3倍ぐらい厚くした感じだ。

*2:メモリは333MHz駆動らしいのだが、DDR2のメモリチップ規格に333MHzなんて存在しないので、意味を図りかねている。Vortex86DXのDDR2コントローラはDDR2-667までサポートしているようだが、DDR2-667(つまりメモリチップが667MHz駆動)の時のバスクロックは333MHzなので、このことを指し示しているのだろうか? それともクロックを下げてDDR2-400(400MHz駆動)よりも低い333MHzでメモリチップを動作させているのだろうか?

*3:CPU内蔵のフラッシュメモリがフロッピディスクドライブとして認識できるようになっていて、しかも出荷時にFreeDOSがインストールされている。

*4:初代PentiumMMX Pentiumの世代。

*5:噂によれば、実は一部のあまり使われない命令には対応していないらしいが……。

*6:Pentium ProPentium II~4の世代。

*7:正確に言えば「NOPL以外の全てのi686命令に対応しているi586 CPU」はサポートしている。でもそれって「ほぼi686」だよね。

*8:GUI前提っぽいので。