KMC活動ブログ

京大マイコンクラブの活動の様子を紹介します!!

macOSのCLAT実装の謎

id:hanazukiです.Kaigi on Rails 2025でもRubyKaigi NOCチームに来場者向けWi-Fiの運用を任せてもらうことになって準備をしています.4月のRubyKaigi 2025と同様のIPv6-mostlyネットワークを提供する予定ですが,RubyKaigiでは相互運用性の問題に当たったので,macOSでの検証を続けていました.この記事ではmacOS 15 (Sequoia)のCLAT実装を検証する中で遭遇した謎を記録しておきます.また,ちょうど今月macOS 16 (Tahoe)がリリースされたので,MacBookを更新して挙動に差異があるか確認しました.

RFC 7050を利用するネットワークでスリープ復帰後にCLATが無効になる

RFC 7050は,NAT64の行われているネットワークに接続したクライアント端末が,DNSを使ってPREF64(IPv4からIPv6へのNATに使われるIPv6プレフィクス)を認識する仕組みです. RFC 7050を利用する環境下で,MacBookがスリープしている最中に何らかのタイマが切れると,それ以降CLATが機能しなくなる場合があることを確認しています.この状態はifconfigコマンドなどを使って,CLATが無効になっていることを見て確認できます.“ipv4only.arpa.”レコードのTTLを伸ばすなど試してみましたが,どうにも回避できないようでした.Wi-Fiを一旦切って接続しなおすと回復します.

通常時(CLATが有効になっているのが見える):

% ifconfig en0
en0: flags=89e3<UP,BROADCAST,SMART,RUNNING,NOARP,PROMISC,SIMPLEX,MULTICAST> mtu 1500
    options=6460<TSO4,TSO6,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM>
    ether f2:22:7b:94:99:c7
    inet6 fe80::41:4a0c:9034:bb5c%en0 prefixlen 64 secured scopeid 0xb
    inet6 2001:df0:8500:ca31:66:bc01:e0a2:a774 prefixlen 64 autoconf secured
    inet6 2001:df0:8500:ca31:2034:f93d:238:a003 prefixlen 64 autoconf temporary
    inet 192.0.0.2 netmask 0xffffffff broadcast 192.0.0.2
    inet6 2001:df0:8500:ca31:1863:bba2:38de:72fe prefixlen 64 clat46
    nat64 prefix 2001:df0:8500:ca64:a9:8200:: prefixlen 96
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect
    status: active

異常時(スリープ復帰後にCLATが無効になっていることがある):

% ifconfig en0
en0: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
    options=6460<TSO4,TSO6,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM>
    ether f2:22:7b:94:99:c7
    inet6 fe80::41:4a0c:9034:bb5c%en0 prefixlen 64 secured scopeid 0xb
    inet6 2001:df0:8500:ca31:66:bc01:e0a2:a774 prefixlen 64 autoconf secured
    inet6 2001:df0:8500:ca31:2034:f93d:238:a003 prefixlen 64 autoconf temporary
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect
    status: active

自宅の検証環境ではこの問題に悩まされていましたが,RubyKaigi本番のネットワークではRFC 8781対応のルータが用意されたため,特に対策をしていません.Tahoeにアップデートしてからは起こっていないので直ったのかもしれないですが,発生条件が謎なので定かではありません.

draft-ietf-v6ops-prefer8781-07でも議論されているように,RFC 7050方式には短所が多く,ルータ機器がRFC 8781に対応するまでの繋ぎの技術と考えるべきでしょう.RFC 8781はルータ広告(RA)のオプションにPREF64を記述できるようにします.知る限りでは,CLAT機能をもつコンシューマデバイスは最新版でRFC 7050とRFC 8781の両対応になっているため,IPv4/IPv6デュアルスタックに縮退可能なIPv6-mostlyネットワークにおいては,RFC 8781対応が可能であればRFC 7050を実装する必要はないと思います.また,IPv6-mostlyのためにネットワークにRFC 7050を実装する場合は,すべてのAレコードに対してDNS64を行う必要はなく,“ipv4only.arpa.”のAAAAレコードのみを合成して返せば問題ないようです.

CLATアドレス宛ユニキャストの近隣要請に応答しない

RubyKaigi 2025でIPv6-mostly Wi-Fiを提供した際に,macOSからIPv4ホスト宛の通信が,数分から十数分後に不通になる問題が発生していました.有線接続では問題が再現しなかったことと,事前の検証環境ではCisco Mobility Expressを利用していたのに対して,本番では本物のCisco WLCを使っていたことから,macOSのCLAT実装とWLCに何らかの相性があるのだろうと当たりをつけ,同型のWLCで検証を行いました.

macOS Sequoiaで検証した結果,macOSはCLATに使うIPアドレス(ifconfigの出力で“clat46”とか書かれているアドレス)宛にユニキャストで届く近隣要請(NS)に近隣広告(NA)で答えないことがわかりました.一方で,マルチキャストで届いたNSには正常に応答します.したがって,ルータからのユニキャストNSに答えられずに近隣キャッシュから消されてしまっても,その後に送られるマルチキャストNSに答えるので,通常の環境下では(わずかな遅延があるでしょうが)問題なく通信できているように見えます.

Wi-Fiの影響を取り除くためにルータ(3560-CX)とMacBookを有線接続して,NS/NAのやり取りを記録しました:

en8: flags=89e3<UP,BROADCAST,SMART,RUNNING,NOARP,PROMISC,SIMPLEX,MULTICAST> mtu 1500
    options=404<VLAN_MTU,CHANNEL_IO>
    ether c8:a3:62:00:67:74
    inet6 fe80::1e:79e8:6ce7:f060%en8 prefixlen 64 secured scopeid 0x14
    inet6 2001:df0:8500:ca31:1c20:bd02:b146:2ddc prefixlen 64 autoconf secured
    inet6 2001:df0:8500:ca31:a00d:5968:8e62:f5e8 prefixlen 64 autoconf temporary
    inet 192.0.0.2 netmask 0xffffffff broadcast 192.0.0.2
    inet6 2001:df0:8500:ca31:148a:b2a:d796:4886 prefixlen 64 clat46
    nat64 prefix 2001:df0:8500:ca64:a9:8200:: prefixlen 96
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect (1000baseT <full-duplex>)
    status: active

No. Time Source Destination Protocol Length Info
83048 13:55:05.310318 fe80::66c:9dff:fe31:41c6 2001:df0:8500:ca31:a80d:5968:8e62:f5e9 ICMPv6 86 Neighbor Solicitation for 2001:df0:8500:ca31:a80d:5968:8e62:f5e9 from 04:6c:9d:31:41:c6
83049 13:55:05.317197 fe80::de:79e8:6c9f:b968 fe80::66c:9dff:fe31:41c6 ICMPv6 78 Neighbor Advertisement 2001:df0:8500:ca31:a80d:5968:8e62:f5e9 (sol)
83087 13:55:08.371306 fe80::66c:9dff:fe31:41c6 2001:df0:8500:ca31:148a:d2a:d796:4806 ICMPv6 86 Neighbor Solicitation for 2001:df0:8500:ca31:148a:d2a:d796:4806 from 04:6c:9d:31:41:c6
83102 13:55:09.462731 fe80::66c:9dff:fe31:41c6 2001:df0:8500:ca31:148a:d2a:d796:4806 ICMPv6 86 Neighbor Solicitation for 2001:df0:8500:ca31:148a:d2a:d796:4806 from 04:6c:9d:31:41:c6
83108 13:55:10.514967 fe80::66c:9dff:fe31:41c6 2001:df0:8500:ca31:148a:d2a:d796:4806 ICMPv6 86 Neighbor Solicitation for 2001:df0:8500:ca31:148a:d2a:d796:4806 from 04:6c:9d:31:41:c6
83115 13:55:11.625881 fe80::66c:9dff:fe31:41c6 ff02::1:ff96:4806 ICMPv6 86 Neighbor Solicitation for 2001:df0:8500:ca31:148a:d2a:d796:4806 from 04:6c:9d:31:41:c6
83116 13:55:11.626185 fe80::de:79e8:6c9f:b968 fe80::66c:9dff:fe31:41c6 ICMPv6 78 Neighbor Advertisement 2001:df0:8500:ca31:148a:d2a:d796:4806 (sol, ovr) is at c8:a3:62:08:ab:e0
ルータからCLATアドレス宛に送られたユニキャストNSに応答しない様子.直前に送られた一時アドレス宛のユニキャストNSや,直後に送られたマルチキャストNSにはNAを答えている.

WLCはNS/NAをスヌーピングして代理で答えることで,無線区間を転送されるマルチキャストフレームを減らす機能を持っています.WLCはNAを観測すると,自身のNeighbor Bindingテーブルにこれを記録します.NAを観測してから(Reachable Lifetime)秒以内に合致するNSを観測した場合,NSを破棄してNAを代返します.(Reachable Lifetime)秒以降(Stale Lifetime)秒以内にユニキャストNSを観測した場合は,NSをクライアントに転送します.通常は,このユニキャストNSに対してクライアントが返したNAをWLCが観測することで,Neighbor Bindingのタイマが更新されます.マルチキャストNSは既定では転送されません.

ところが,macOSはCLATアドレス宛のユニキャストNSに答えないので,正常にWLCのNeighbor Bindingのタイマが更新されず,ネクストホップルータの近隣キャッシュからも抜け落ちてしまいます.結果として,上流方向へのパケットは届くが,下流方向へのパケットは届かない状態になります.

この問題の緩和策を2つ見つけています.いずれかを適用すれば十分で,Kaigi on Rails 2025では前者を試してみる予定です.

  1. WLCのNeighbor BindingのReachable Lifetimeを大きく設定すれば,WLCが隣接ルータからのNSに代返する期間を伸ばすことができます.これがWi-Fiクライアントが接続を持続している時間より長ければ疎通を保ち続けられます.ただし,WLCのNeighbor Bindingの最大エントリ数(Cisco 3504では36,000)を考慮して値を決める必要があると思われます.
  2. WLCで“Unknown Address Multicast NS Forwarding”を許可すると,WLCは自身のNeighbor Bindingに合致しないマルチキャストNSをクライアントに転送するようになります.macOSマルチキャストNSには応答するので,NAがWLCのNeighbor Bindingに記録され,以降はWLCが代返するようになります.しかし,マルチキャストフレームがWi-Fiのエアタイムを消費することが問題になりえます.特に,RubyKaigiネットワークではIPv6のステートフルファイアウォールを設けておらず,インターネット側からのアドレススキャンによって大量のマルチキャストNSが発生する可能性があります.

draft-ietf-v6ops-claton-08は,CLATアドレスと通常のIPv6アドレスで異なる挙動を示す実装が見つかっていることに言及しています.ここで紹介したmacOSの問題もその筋では知られているかもしれませんが,ワイヤレスアプライアンスとの組み合わせで問題になりうることを指摘する資料は見つけられませんでした.

なお,CLATアドレス宛のユニキャストNSに応答しない問題は,Tahoeで修正されたように見えます.また,同アドレス宛のICMPv6 Echo Requestに答えない問題もありましたが,同様に修正を確認しました.

Kaigi on Rails 2025 での追試と、上記とは異なる挙動について (id:sora_h)

id:sora_h です。以上を踏まえて 9/26-27 に開催された Kaigi on Rails 2025 にて追加検証を行いました。Kaigi on Rails 2025 では前述の緩和策 (1) を実装していましたが *1、いくつかの macOSiOS のバージョンで CLAT アドレス宛近隣要請の挙動を確認しました。

前述の報告では Neighbor Solicitiation への返答 (solicited Neighbor Advertisement) がないという内容でしたが、Kaigi on Rails 2025 での追加検証では異なる挙動を確認しました。 *2。今回は solicited Neighbor Advertisement は観測できましたが、その挙動は下記の通りです:

  • macOS 15.5, macOS 15.6.1, iOS 18.5: solicited Neighbor Advertisement の内容に含まれる target address は CLAT アドレス (GUA)、IPv6 ヘッダの source はそれと異なりリンクローカルアドレス
  • iOS 26, macOS 26: solicited Neighbor Advertisement の target address・IPv6 ヘッダの source はともに CLAT アドレス

前述の solicited Neighbor Advertisement が観測できないという結果にはなりませんでしたが、macOS 15, iOS 18 では当該パケットの source address がその target address とは一致せず、リンクローカルアドレスになってしまっているようでした。 RFC 4861 等をざっくり読んでもこの点に関してどうあるべきかの記述はみられませんでしたが、iOS 26, macOS 26 ではこの挙動が変更になり状況が改善したと見られることから、何らかの相互接続性観点の改善、あるいは不具合修正が入ったと考えられます。

また、30 分程度でパケットが不通になってしまうという症状も設営日に緩和策を入れずに検証していましたが、macOS 15.6.1, iOS 18.5 で再現させることはできませんでした (おそらくある程度近隣テーブルにエントリが必要で、設営日に再現しなかったという仮説を立てています)。

その他に報告されている事象

macOSには他に,CLATが有効化されたインターフェイスに与えられるダミーのIPv4アドレス“192.0.0.2”をネットワークにリークしてしまう問題や,sshコマンドに“-4”のオプションをつけるとIPv4ホストに接続できない問題が報告されています*3.これらの問題はSequoiaでは再現していましたが,Tahoeで修正されたようです.IPv4ホスト宛のtracerouteで中間ホストからのICMPエラーを受け取れない問題はTahoeにも残っていました.

Sequoiaで見つけた問題をKaigi on Railsで実地検証して報告しようと思っていたら,直前にTahoeが出て直ってしまいましたというブログでした.

*1:これと直接関係があるかは分かっていませんが、一部クライアントでDHCPv4やRAパケットだけWLCのレイヤで抜け落ちる現象が突如発生する事象は確認していて困ってます。macOS Tahoe 以後がある程度普及したと考えられる 2027 年以降はこの緩和策を解除できますが…

*2:検証方法もとい Neighbor Solicitiation を送る手段が異なるため、その関係もあるかもしれません

*3:Experimental IPv6-only network at APRICOT 2024

「LT大会@DTM練習会」を開催しました

こんにちは。ぶ(kmcid:bu4)です。 記事を書くのが何年ぶりかもわからない程度には久しぶりに記事を書きます。

タイトルの通り本日8月23日「LT大会@DTM練習会」を開催しました。

普段やってたりやってなかったりするLT大会のDTM練習会版です。DTMとか音楽とかに関する話題と関しない話題が、なされました。

発表内容は以下のようなものがありました。

  • CDを即売会に出す話
  • おいしい茶碗蒸しの作り方
  • WholeToneScaleのお話
  • 風来のシレンSFCのはなし
  • シンセサイザーって何?
  • 地元について
  • やって良かったなってこと

とても面白そうなものばかりですね。普段聞くことのできないお話が色々聞けましたし、久々に自分も資料を用意するにあたって勉強し直せた部分もあります。

ちなみに部室とオンラインとで行う、ハイブリッド形式で行われました。個人的なお話ですが、部室にしばらく行けていないので久々に部室で人々がわいわいしてる声も聞くことができて良かったです。

スライドの抜粋(bu4「やって良かったなってこと」)

……実のところ、何年ぶりかもわからんくらいの間で突然記事を書き始めたのは、最後にDTM関連の活動でブログが書かれたのが3年前とかいう事実にこの前気付いてしまったからです。

活動ブログに載ってない(書くのサボってた)だけで、KMC内でDTM活動はちゃんと行われています!めちゃくちゃやってます!!具体的には週2回とかやってます。

NF(学園祭)とかではDTM練習会参加者によるアルバムとか出るはずです!!お楽しみに!!!!

KMCM

KMCではパソコンで何かしたい人々を常時募集しています!!個人的にはDTMに興味がある人を特に募集したいです!!! (※もちろんDTM以外に興味のある人や現在特に何か興味があるわけではない人も歓迎します!!)

www.kmc.gr.jp

自分のLT「やって良かったなってこと」で話した内容として「ゲームの曲を書く」ってのも紹介したんですが、やっぱ身近にゲーム作ってる人がいて、そのゲームの曲が書けるコミュニティってのはすごいです。逆にゲーム作ってる人からしたら身近に曲を書ける人間がいるんですよね。やべ~。

RubyKaigi 2025で使用したSIITのRuby実装について

こんにちは,id:hanazukiです.松山で開催されたRubyKaigi 2025に何人かのKMC部員とともに参加し,Wi-Fiの構築・運用をやっていました.KMC活動ブログに記事を書くのは2023年のDNSリゾルバの記事ぶりです.

現地でも噂されてしまっていたようですが,今年のRubyKaigiでは15人いたNOCメンバーの過半数が会期中に体調不良でダウンするインシデントに見舞われてしまいました.私はどうやら風邪と食中毒を時間差でもらったようで,RubyKaigi本編もNOCの活動も満足に参加できず,たいへん残念でした.愛媛で買ってきたみかんを1週間くらい食べ続けていたら快復しました.

さて,RubyKaigi 2025では,今年からの新しい試みとしてIPv6-mostlyネットワークを提供していました.IPv6-mostlyとは,464XLATと呼ばれるIPv6移行技術を対応クライアントへ選択的に展開することで,漸進的にIPv4の利用を削減してゆく方式のことです.464XLATは,IPv4アプリケーションとIPv4のサーバ間の通信の中間部分をIPv6で代替する技術です.そのために必要なNAT64(IPv6からIPv4へのNAPT)の実装として,OSSではJoolというLinuxカーネルモジュールがよく利用されていますが,今回のRubyKaigiではNAT64のRubyによる代替実装を開発することを目的の一つとして,IPv6-mostlyネットワークの実装実験を行いました.

先日の東京Ruby会議12での発表でも紹介したように,1ヶ月ほど前から自宅の検証環境で種々のクライアントとのIPv6-mostlyの相互運用試験をして本番に望んだのですが,松山ではあまり安定稼働させられませんでした.わが家の検証環境は私物のネットワーク機材の余り物で組んでいる都合で,本番で利用しているCisco WLC(無線LANコントローラ)ではなくCisco Mobility Expressを使っていたところ,どうやらWLC(もしくはRubyKaigiで使っているWLCの設定)とmacOSIPv6-mostly実装に相性の問題があったようです.日本Rubyの会に予備機材として同型のWLCを1台追加調達してもらったので,それを使って原因究明をし次回へ向けて調整したいと思っています.

RubyKaigiのIPv6-mostlyネットワークの全貌についてはチームメイトのid:sora_hに譲ることにして,本稿では今回のRubyKaigiで使用するために新しく開発したソフトウェアとその際の工夫(あるいは小ネタ)を紹介します.もちろんこれらのソフトウェアの主要部分はRubyで書かれています.RubyKaigiなので.

Xlat: SIITのRuby実装

今回RubyKaigiにIPv6-mostlyを導入するにあたって,sora_hとhanazukiでSIITの実装をRubyで書きました.これをXlatと呼んでいます.

SIIT (Stateless IP/ICMP Translation)は,IPv4データグラムとIPv6データグラムの相互変換をするアルゴリズムで,RFC 7915で定義されています.IPv4IPv6ではヘッダの形式が異なるので,SIITは翻訳先のバージョンのIPヘッダをできるだけ辻褄をあわせた形で生成し,IPv4ネットワークとIPv6ネットワークの間で相互にIPデータグラムのやりとりをできるようにします.

また,IPv4IPv6ではアドレス体系も異なるので,IPv4データグラムをIPv6データグラムに翻訳するときには,送信元と宛先のIPアドレスを適当に書き換えてやる必要があります.IPv6-mostlyでは,IPv4の世界を表すIPv6プレフィクスを一つ選んで(これをPref64::/nと呼びます),IPv4アドレスをPref64::/nの末尾に埋め込むことでアドレスの対応付けを行います(詳細はRFC 6052を参照).例えば,RubyKaigiではPref64::/nとして2001:df0:8500:ca64:a9:8200::/96を使いました.このとき192.50.220.162は2001:df0:8500:ca64:a9:8200:c032:dca2に対応付けられます.

このPref64::/nは16 bit単位での1の補数和が0になるように選んであります(0x2001+0x0df0+0x8500+0xca64+0x00a9+0x8200=0xffff=0).このようなプレフィクスのことを「チェックサム中立 (checksum-neutral)」であると言います.TCPUDPチェックサムは1の補数和で計算されるので,この部分がチェックサムの値に影響を与えないということです.チェックサム中立なPref64::/nを選ぶと,翻訳時にL4ヘッダのチェックサムを更新する必要がなく少しだけ効率的になるメリットがあります.

IPv6でのNAPTであるNAPTv6(プレフィクスを取り替えるNPTv6とは異なるので注意)とSIIT(IPv6からIPv4への変換)を関数合成すると,NAT64(IPv6からIPv4へのNAPT)を作れます.LinuxカーネルのNetfilterがNAPTv6の機能を持っているので,これとユーザランドのXlatを組み合わせることでNAT64を実現できる寸法です.

RubyKaigiでは,県民文化会館の楽屋廊下で2台のN100ミニPCにインストールされたXlatが,みなさんのMacBookiPhoneAndroidなどIPv6-mostly対応クライアントからのトラフィックをNAT64していました.メトリクスを見ると,NAT64にはピークで200 Mbpsくらい流れていたようです.事前の検証では1500 Bのロングパケットで1台あたり1 Gbps(つまり,NICの限界)までは捌けることを確認していたので,パフォーマンス面では十分な余裕がありました.

オブジェクト割当の削減

今年のRubyKaigiでClass#newが遅いのだという発表がありましたね(私は裏番組を見ていました).Xlatの初期の実装でもオブジェクトを作りすぎていて遅いという問題を踏んでいて,できるだけオブジェクトを使い回せるようにAPIを変更する修正を行いました.

Xlatのパフォーマンスの問題を調べるのに,NOCメンバーでプロファイラ職人のosyoyu (id:tomo_ari)が開発しているPf2というプロファイラを使わせてもらいました.Pf2はどのRubyメソッドがどのC関数を呼び出しているのか可視化できるプロファイラです.Pf2を使うとどのメソッドでオブジェクトの割当が行われているか一目瞭然になるので便利でした.たとえば,パーサを改善する前少し改善した後のフレームグラフを比較するとXlat::Protocols::Ip#parseの部分の盛り上がりが減っているのが見てとれます.最新版ではもう少し改善しています

オブジェクトの割当がパフォーマンスを悪化させるのは,Class#newが遅いだけではなく,付随的にガーベジコレクション(GC)が実行されることも要因になっています.TCP輻輳制御アルゴリズムはネットワークのRTTを予測して適正な流量でパケットを流します.GCが走るとパケットの処理時間にゆらぎ(ジッター)が発生することになり,これはTCPのパフォーマンスに悪影響を及ぼします.したがって,オブジェクトの割当はできるだけ減らすのが望ましいです.

IO::BufferとベクタIOの利用

IPv4IPv6ではヘッダの長さが異なります.IPv4では20 Bから60 Bの可変長,IPv6では40 Bの固定長ヘッダにさまざまな拡張ヘッダを付与できて長さに上限がありません.したがって,IPv4IPv6の間でデータグラムを翻訳すると,ほとんどの場合,長さが変わります.

MRIでは,Stringの部分文字列を取ったり,String同士を連結したりすると,文字列のバイト列をコピーすることになります*1.それゆえ,SIITのように長さの変わるバイト列の操作をStringで実装すると,どうしてもデータのコピー回数が多くなり非効率です.

Xlatでは,バイト列をIO::Bufferを使って保持し,LinuxのベクタIO (writev(2))を使って出力することで,Rubyプロセス内での「ゼロコピー」を実現しています.例えばヘッダとペイロードをそれぞれIO::Bufferとして持っているときに,これらを1回のwritev(2)システムコールでまとめて出力することで,1つのIPデータグラムとして出力するという具合です.IO::BufferでベクタIOを使うAPIが今のRubyには無いので,この部分だけ拡張として実装しています.

IO::Bufferの部分バイト列(スライス)を抜き出す#sliceメソッドは,バイト列のコピーこそしないものの,IO::Bufferインスタンスを新しく割り当ててしまう問題があります.C++std::span<T>やRustの&[T]のようなファットポインタ(ポインタの長さの対)のつもりで使うとGCのコストが乗ってきて痛い目を見るということです.これの回避策は簡単で,スライスを作る代わりに(buffer, offset, length)の3つ組を持ち回って,どうしても必要な時までIO::Buffer#sliceの呼び出しを遅延させることです.

YJITとRactor

YJITを有効にすると勝手にプログラムが速くなり,たいへん偉大でした.具体的には,1スレッドのXlatで1ストリームのTCPを処理した場合のスループットが240 Mbpsから430 Mbpsに改善するくらいの効果がありました.メソッドをあまり細かく分けないコーディングスタイルが効いているのではないかと想像していますが,実際のところは詳しく計っていません.IO::BufferStringなどと比べてまだあまりYJITで最適化されていないので,たとえばIO::Buffer#get_valueなどをインライン化できるようになると嬉しいのではないか,など処理系の方からも更なる改善の余地はありそうに考えています.

また,SIITのSが“stateless”の頭文字であることからわかるように,SIITは状態を持たないアルゴリズムになっています.すなわち,ある一つのデータグラムを翻訳する際に,そのデータグラムの前後のデータグラムの情報を必要としません.これは異なるRactorインスタンス間で状態を共有できないというRactorの制約に対して非常に都合がよい性質です.翻訳ワーカーをそれぞれ個別のRactorとして起動することで,マルチコアを活かしてCPU処理を並列化できます.Ractorの“actor”の部分は何も使っていませんが,GVLがRactor毎に分割されたのが効いてくる事例でした.

RubyKaigiではRactorローカルGCの発表を聞きそびれてしまったのですが,RactorローカルGCが実現した場合,GC中のワーカーにはタスクを渡さないように制御することで,タスクの処理がGCによる停止の影響をうけないようにできるのではないかと考えました.オブジェクト割当を目の敵にしなくてよくなれば嬉しく,気になっています.

ファジング

SIITへの入力はすべてユーザ端末かインターネットから与えられるもので,データグラムが正しい形式に従っている保証はありません.Rubyスクリプトとして書いている以上,範囲外参照でメモリが壊れるような深刻な事態には陥りませんが,壊れたデータグラムを食べさせられて例外を上げてしまうのは防ぎたいです.そこでRuzzyというライブラリを使ってパーサとSIIT実装のファジングを行っていました.

Ruzzyは内部的にLLVMlibFuzzerを使っていて,コードカバレッジを拡大するようにランダムな入力を生成し続ける仕組みになっています.Rubyスクリプトをファジングする場合は,Integerの比較をフックすることで分岐をどちらに進んだかを記録しているようです.したがって,例えばIO::Buffer#<=>のようなCで実装された比較メソッドをRubyスクリプト中で分岐条件に使っている場合,これをIntegerの比較を使うようにRubyで実装し直すことによって,Rubyインタプリタ自体を計装のためにリビルドすることなくファジングを行えます.

範囲外参照や境界エラーのようなありがちなバグを発見してくれるのみならず,思いもよらないような異常なパケットを次々に生成してくれて大いにデバッグの助けになりました.

やりのこし

会期までに完成させられなかったこともたくさんあり,来年度にむけて磨いてゆきたいと思っています.TCPUDPのパケットの翻訳をするパスに関しては,オブジェクトの割当をかなり減らしたのですが,ICMPを扱う部分でまだ非効率な部分が残っているのを改善したいというのもその一つです.

ベクタIOを行う部分は拡張として実装したのですが,現状IO::Bufferの配列を引数として取るインターフェイスになっています.つまり呼び出し側でIO::Bufferの出力に必要な範囲のスライスをとる前提です.これでは先述のIO::Buffer#sliceのオーバーヘッドを避けられないことから,もうすこしよいインターフェイスを考えたいです.

また,この拡張はRustとmagnusを使って実装したものの,安全性のために微妙に余分なオーバーヘッドが乗ることがわかって,C++で書き直そうかと思っています.さいきん別のところで必要になってrcxというモダンC++Rubyの拡張を書くためのライブラリを作っているというのもあります*2.これもまた別の機会に紹介したいと思います.

conntrack_exporter

Netfilterのコネクショントラッキング(conntrack)の状態を可視化するために,conntrack_exporterRubyで書きました.同名のソフトウェアは世の中に存在するのですが,今回書いたconntrack_exporterは,conntrackエントリをL3プロトコル・L4プロトコル・ラベル(connlabel)で集約して数えられるのが特徴です.connlabelとは各conntrackエントリにある128 b長のビットセットで,各ビットが1つのラベルに対応しています.nftablesなどを使うとconnlabelに管理用の好きな値を設定できます.

RubyKaigiではNAT64のアドレスプール毎に異なるconnlabelを付与することで,各アドレスプールからL4ポートがどれだけ使われているかを識別できるようにしていました.UDPでは,NAPTの外側IPアドレス1つあたり64,512本(UDPで利用可能なすべてのポート2**16個から,システムポート2**10個を除いた数)のコネクションしか張れない制約があるため,ポートの利用率を監視することでアドレスプールのサイズを決める目安になります.

nl/ynl

conntrack_exporterを実装するにあたって,Rubyで使えるNetlinkのライブラリが必要となりnlという名のgemを書きました.NetlinkとはLinuxのプロセス間通信の仕組みの一つで,主にアプリケーションがカーネルの持っている情報を取得したり,カーネルの状態を変更したりするのに使われています.

Netlinkの通信プロトコル自体は,ソケット上でリクエスト・リプライのメッセージを順にやりとりするだけで,さほど複雑ではありません.しかし,対話したいカーネルのサブシステムごとにメッセージをエンコード・デコードするコードを書くのがたいへんに面倒という問題が知られています.メッセージは基本的にTLV (tag-length-value)のフォーマットに従っているものの,tagはC言語のヘッダで定義されていて,valueのフォーマットはCヘッダのコメントか,運がよければmanpageに自然言語で書かれているものを解読しなければなりません.このような理由から,網羅的なエンコーダ・デコーダを書くことも現実的には困難で,メンテナンスが続かなくなるプロジェクトも少なくありませんでした.

このような問題を解決するために,機械可読でCに依存しないメッセージスキーマを提供するプロジェクトがYNLです.YNLのYがYAMLのYであるように,YAML形式で各サブシステムのメッセージのフォーマットを定義しています.スキーマカーネルツリーのDocumentation/netlink/specsディレクトリ以下に含まれています.このスキーマから各言語のコードを生成することで,エンコーダ・デコーダを無料で手に入れられ,カーネルのアップデートに追従するのも簡単になるというもくろみです.Rubyのコードを生成するynl gemも作りました(nlと同じGitHubリポジトリに置いています).

YNLは始まったばかりのプロジェクトで,まだ全てのサブシステムを網羅しているわけではないですが,conntrackサブシステムのメッセージスキーマがちょうどいま開発中のLinux 6.15で追加されて,conntrack_exporterを作るのに利用できました.YNL自体はあらゆるサブシステムをカバーするために結構複雑になっていて,ynl gemではまだ全ての機能は実装できていません.これはおいおいやっていこうと思います.

ところで,nlのNetlinkメッセージをデコード・エンコードする部分もIO::Bufferをベースに書きました.packunpackのフォーマット文字列を覚えることから開放されるのもIO::Bufferの利点です.

来年に向けて

年始の東京Ruby会議12ではDNSの話をしたものの,その後IPv6-mostlyの実装にかまけていて,今年のDNSゾルバには小さな改善しか盛り込めませんでした.DNS-over-QUIC (RFC 9250)を喋るようになっていることや,RESINFO (RFC 9606)を返すようになっていることに気づいた人はいるでしょうか.IPv6-mostlyを導入するにあたって,DNSゾルバもIPv6に対応しました.これも実はXlatのNAT64を使っていて,クライアントからIPv6で送られてきたクエリをXlatで翻訳してIPv4DNSゾルバに流していました.来年こそはDNS関連でなにかコードを書けるようなことをやりたいと思っています.もっとも,秋にはKaigi on RailsWi-Fiをやるかもしれないという話もあるので,IPv6-mostlyの検証をまだもうすこしやっているのだと思いますが.

関連記事

BAKUCHIKU BANBAN #1で,id:sorahがKaigi Wi-Fiの近況を話してくれました.

*1:TruffleRubyのような他のRuby処理系では,このような文字列操作にバイト列のコピーが必要ないように最適化されているものもあります

*2:バインディングを書くのが楽しすぎてバインディングを書いていたらバインディングを書くためのバインディングが生えました

KMC入部のお誘い

こんにちは!KMC 48代のgunjouです。

若草が萌えたち春も深まってまいりました。
春から入学される新入生の方、そして新しく入るサークルをお探しの方にKMCについて知っていただくために、この記事では私たちの活動を紹介いたします!
この記事で少しでも「KMCいいな!」と思っていただけると嬉しいです。

体験入部フォームはこちら

KMCとは?

KMCは京大マイコンクラブ(Kyoto university Microcomputer Club)の略です。京都大学を中心に活動している京都大学全学公認のコンピュータサークルになります。 KMCでは

など、コンピューターを中心とした幅広い分野で活動しています。 詳しくはこちらのサイトをご覧ください。

どんな人がいるの?

大学生になってからプログラミングをはじめた人もいれば、入部前から高い専門性を持つ人まで幅広く在籍しています。京都大学の学生が多いですが、京大外の方も多数在籍しています。
開発の経験がある方はもちろん、未経験で入る方にとっても、コンピュータ好きの夢が叶う充実した環境だと思います。

2025年も新勧をやります!

今年度も新勧を実施します!KMCに興味のある方はぜひ参加してみてください。情報は公式ツイッターや公式サイトを通じて発信していくので、ぜひ確認してみてください。

活動紹介

日々の活動や一部のプロジェクトを紹介します!

日々の活動

週2回の例会で情報交換をしています。主に部室で開催されますが、参加は自由ですし、オンラインで参加する部員もいます。例会の後は希望者の持ち回りで例会講座を開催しています。色々な分野を垣間見ることができるので、初めての世界へ足を踏み入れるきっかけになるかもしれません。

例会

例会以外にSlackやDiscordといったチャットツールでも情報共有しているので、いつどこにいてもキャッチアップできるようになっています。

プロジェクト・イベント

例会以外の活動内容は個人、プロジェクトの自由に委ねられています。 部員の興味に基づいて、自由に寄り集まったメンバーで日々プロジェクトや勉強会が進行しています。部員であれば誰でもプロジェクトやイベントを立ち上げたり、参加したりすることができます。プロジェクトの成果は主に京都大学の文化祭である11月祭(NF)や 夏と冬のコミックマーケットで公開されます。勉強会に参加したり、プロジェクトに飛び込んでみたり、時にはゲームに興じたり…何をするかはあなた次第です!

KMC部室の様子

サーバーの他に、技術書、モニター、パソコン、3Dプリンタ、液タブ、VRゴーグルなどなど……様々な機材が揃っています。


みんゲー(「みんなでゲームを作る」プロジェクト)

新入生がチームを作りオリジナルゲームを作成するプロジェクトです。成果物は学祭(NF)等で公開し、実際に遊んでもらいます!
考えたゲームを形にしていく楽しさを味わいつつ、他の新入生との交流も深められます!毎年ほとんどの参加者がゲーム制作未経験なので、初心者でも安心して参加できます!
※画像は昨年度の作品になります


お絵描きプロジェクト

KMC共用の液タブを使ったお絵描きプロジェクトが進行しています! Adobeや液タブなどの豊富な機材を利用し、ゆるいお喋りありおやつありのお絵描き活動を行っています。OBにはプロイラストレーターもおり、部員は先輩の講座や部室の豊富な技術書を見ることができます! そんな普段の活動を体験していただけるゆるお絵描き会です。

部室共用の液タブ

音楽制作

DTM練習会は、パソコンを使った音楽制作を練習する会です。初心者から経験者まで参加でき、楽しくスキルを磨けます。

【KMC会員が作った曲①】
「せーので!」
わくわくするような明るい曲です!


www.youtube.com

【KMC会員が作った曲②】
「泡沫の祈り」
美しい情景が目に浮かぶ壮大な曲です!


www.youtube.com

サーバーいじり

KMCが持つサーバーの管理も部員が担っています。

インフラ勉強会

KMCが持つインフラを有効に活用すべく、最近誕生したプロジェクトです。

LT会

部員の有志が集まって、5分程度のプレゼン(ライトニングトーク)を行います。

コーディング大海

皆で部室に集まって各自で思い思いの作業をしました。

その他

Siv3D勉強会やCTF大会への参加、群論ゼミなど様々なプロジェクトやイベントが開かれました。

KMCの一年

新歓(4月,5月)

新入生のための体験会や説明会を開きます。

体験会

KMCの普段の活動を新入生に体験していただくために、気軽に参加できるテーマ別のイベントを数多く用意しています。ここでの体験が、6月以降の「春プロジェクト」へ緩やかに繋がっていきます。

過去の体験会の様子

説明会

KMCが持つ環境を、新入生にもフルに活用していただくことを目的として説明会を実施します。ぜひ参加してみてください!

その他

・新歓コンパ(確定コン) ・KMC情報縦コン

春プロジェクト (6月~)

新入生が参加しやすいように企画されたプロジェクトです。ここがKMCでの活動のスタート地点になります!

OBOG会

OB・OGの方々と交流できる機会です。

夏のコミックマーケット(8月中旬)

部誌やオリジナルゲーム、DTMといった成果物を毎年夏と冬のコミックマーケットで公開しています。

NF(11月)

教室を借りて、みんげーで作成されたオリジナルゲームやDTMといったプロジェクトの成果物を展示します。

NFの様子

冬のコミックマーケット(12月末)

夏のコミックマーケットと同様に、KMCでの活動を発信する部誌を頒布しています。

アドベントカレンダー

毎年12月には部員がリレー形式で記事を投稿する「アドベントカレンダー」を実施しています。 昨年度のものはこちら

2月 春合宿

春休みには春合宿を開催し、現役部員からOBまで幅広い年代が集まって講座を開きます。

過去の春合宿の様子

入部方法

KMCでは年間を通して部員を募集しています! 現在、学生なら大学に関わらず誰でも入部可能です(社会人の方は部員1名以上からの紹介を受ける必要があります)。 まずは体験入部で、部内チャットから活動の様子をご覧ください! 申込フォームはこちら

新歓期には特別なイベントを数多く用意しておりますので、奮ってご参加ください!

Q&A

Q: 「京大マイコンクラブ」の「マイコン」って何ですか?

A: KMCはもともとKyoto university Microcomputer Clubの略です。 マイコンという言葉は今では死語ですが、1977年の設立以来その名を引き継いで現在に至ります。

Q: 兼サー(複数のサークルに入ること)は可能ですか?

A: 可能です。

Q: プログラミングなど全くしたことが無いのですが、活動に参加できますか?

A: 入部当初は全くプログラミングをしたことが無くても、 数年後には嬉々としてプログラムを書いている人もいます。 初めからプログラムを書ける必要は全くありません。 まずは初心者向けのプロジェクトに参加するなどして上回生から教えてもらいながら力を付けて行くのが良いと思います。 このことはプログラミングだけではなく、音楽制作やイラスト制作でも同じ事が言えます。

C105で京大マイコンクラブのインフラ本を出します

こんにちは。KMC45代のsegreです。 これはKMC Advent Calender 2024 の21日目の記事です。

私が所属している「京大マイコンクラブ(KMC)」はコミックマーケット105にサークル参加します。 コミケでは毎回部誌として部員が書いた技術系コラム集を頒布していますが、今回はそれに加えてKMCのインフラについてまとめた本を頒布します。52P/B5/500円を予定しています。

新刊表紙

KMCについて

KMCとは京都大学を中心に活動しているコンピュータサークルです。1977 年に設立され、今年で48年目を迎えました。コンピュータを使ったゲーム制作、音楽制作、イラスト制作、Web サービス開発、電子工作をはじめとする創作活動や、競技プログラミング、情報分野の勉強会など様々な活動を行っていますが、インフラ関連の活動も活発で長い歴史をもっています。KMCでは2017年にAS59128の割り当てを受け運用を開始しましたが、それ以前の30年以上前からPIアドレスの割り当てを受けていました。京都の部室にはサーバーラックをはじめとして様々な機器が転がっています。

部室の様子

新刊「京大マイコンクラブのインフラ~AS59128 完全解剖~」

この度「京大マイコンクラブ」では新刊「京大マイコンクラブのインフラ~AS59128 完全解剖~」を頒布します。 内容は自分を含めた6人の有志によって書かれており、歴史、バックボーン、部室ネットワーク、仮想化基盤、サーバー、DNSに分けられています。 Circle.ms https://webcatalog-free.circle.ms/Circle/20011063 で冒頭数ページのサンプルも公開されているのでぜひご覧ください。

歴史では前節でも述べたようなPIアドレス割り当てに関することや、30年以上前のKMC部員がこのアドレスを使ってどのように遊んでいたのかを辿っています。

バックボーンではAS59128の構成や運用方法について述べられており、Linuxマシンをルーターとして使うノウハウが紹介されています。

部室ネットワークでは部室内で構築されているネットワークについて紹介します。 以下は部室の物理構成となっています。本ではモノクロになってしまっているのでカラー版をここに載せておきます。 この図は部員のcrashRTが作成したものです。

部室ネットワーク

仮想化基盤では5台のマシンで構築しているProxmoxクラスタについてその構成と行っている自動化について紹介しています。また、libvirtから移行したのでそのノウハウや手こずった点なども紹介しています。

サーバーでは構成管理ツールとして使用しているitamaeの紹介や仮想化基盤上で構築しているKubernetesクラスタについて紹介しています。

最後の章ではDNSについてその基本からKMCでの運用方法について紹介しています。

また、付録として初学者向けの解説集を掲載しています。本編でわからないことがあったらぜひご参照ください。

以上が本の内容となります。全52ページでKMCのインフラについてほぼすべてを網羅した内容になっています。主な対象者は自宅サーバーや個人ASを運用されている方、インフラに興味を持っている方です。当日は是非お立ち寄りください。

サークル座標

2日目 西2こ08aです!!

サークル「京大マイコンクラブ」で出展しています。

Circle.msも公開しているのでぜひご覧ください。

https://webcatalog-free.circle.ms/Circle/20011063

お品書きは後日掲載予定です。

申し訳ございませんが筆者(segre)はKMCのブースにはおらず、別の部員が頒布を行ってくれています。私は「Home NOC インフラ部」 2日目 西2い33aで頒布を行っています。記事について気になることなどがある場合はそちらにお立ち寄りください。「Home NOC インフラ部」でも新刊の頒布がございますので、そちらもよろしくお願いします。

TypstでC105に出す部誌を組みました

こんにちは。KMC 46thのmarimoです。 これはKMC Advent Calendar 2024 1日目の記事です。

KMCはコミケに合わせて部誌を年に二回刷っています。 C103まではLaTeXを使っていました1が、 年末のC105で頒布する予定の部誌をTypstで組んだので、その話をします2

Typstを紹介する記事はすでに多くあるので、Typst自体やその基本的な機能の紹介はそちらに譲ることとして、 実際に印刷物を作る上で必要だったことを書くことにします。

この記事はTypst v0.12.0を前提としています。

ファイルの分割

長い文書は複数のファイルに分割したくなります。 そのための機能としてincludeとimportがあります。 機能としては書いてある通りでincludeはファイルを文書として読み込み、 importはファイルをプログラムとして(moduleとして)読み込みます。

#include "foo.typ"

だとfoo.typを評価した結果(Contents型の値)が入ります。 つまり、LaTeXのincludeなどのようにテキストがそのまま展開されるのではないので、中で書いた設定が外に漏れたりはしません(うれしい)。

目次、queryの活用

Typst標準でoutlineという関数がありますが、凝ったことをするには機能が足りません。 部誌は寄稿誌の形態をとっているので、目次には題だけでなく著者も載せたいです。 今回はheadingの後にmetadataをおいて、queryで情報をとってくるという戦略でやります。

= chapter1
#metadata([alice]) <author>
= chapter2
#metadata([bob]) <author>

このような文書があったときに

#let toc() = {
  let elements = query(heading.where(level: 1, outlined: true))
  for el in elements {
    let metadatas = query(selector(<author>).after(el.location()))
    let author = metadatas.first().value
  }
  <略>
}

などとしてchapter(level-1のheading)のリストと、そのauthorを得ることができます。 実際にはコーナーケースに対応する必要があります。

TypstはLaTeXなどと同様、文書を単なる要素列として取り扱います。 したがって要素に付随する情報を得るためには列の位置に依存して頑張る必要があります。 LaTeXはauxに書き出して収束するまで繰り返しコンパイルする必要がありましたが、 Typstは外部ファイルを使わずにqueryでとってこれたり、勝手に収束するまでコンパイルしてくれたり、そもそもコンパイルが速いといったうれしさがあります。

凝ったページデザイン

縁付けたりとか背景付けたりしたいときがあります。page関数のbackground,foregroundを使いましょう。

#set page(background: image("hogehoge.jpg"))

フォント

これはそこそこ知られていますが、v0.12.0現在、和文と欧文のフォントを別に設定するのに困難があります。 正規表現をつかうと禁則処理が壊れるなどの問題があります。

#show regex("[\p{scx:Han}\p{scx:Hira}\p{scx:Kana}]"): set text(font: "Hoge Font")

フォントのフォールバックを使ったほうがいいです。

#set text(font: ("Latin Font","Japanese Font"))

段組みの注意

Typstはpage(column: 2)とすれば2段組ができます。 あとは、v0.12.0になって段抜きの浮動図を置くことができるようになりました。 しかしv0.12.0現在、最後のページでバランスすることはできません(#466)。

さいごに

京大マイコンクラブ(KMC)は、C105の2日目(月曜日)西-08aで参加します。 年2回発行の部誌に加えて、今回はKMCが管理するAS59128の本も出す予定です。 コミケに参加する方はぜひお願いします。

さいごに参考にしたドキュメントを紹介します。 Typst公式のドキュメントは今どきらしく綺麗にまとまっています。 また、技術書展17で頒布されたTypstで技術同人誌を書こう!すぐに役立つ20のトピックも大いに参考になりました3。 テンプレートの例としては公式のtemplatesを見ると良いです。 Typstは開発が活発でその辺のコードは使い物にならないことがあるので(この記事含め)、基本的には公式のものを見ることになります。

Footnotes

1 https://github.com/kmc-jp/bushi-template2

2 ちなみにC104でも私が組版を担当して、そのときはSATySFiを使いました。

3 たまたまハンドルネームが一致していますが別人です。

「Happy Elements式 ゲーム開発の基礎語ります」を開催しました!

こんにちは!KMC 45thの進捗ゼミです。

2024年6月14日に、Happy Elementsさんをお招きし、「Happy Elements式 ゲーム開発の基礎語ります」という新歓イベントを開催しました!

ゲーム開発現場で活躍しているゲームデザイナーの方に、ゲームを面白くするためのコツや、時間の無い中でゲームを完成させるための工夫、 社内でのゲーム開発事例についてご講演をいただきました。

当日の様子

15人もの新入生が参加してくれました

少人数チームでのゲーム開発のコツについて学びました

手作り感がいい感じの会場の飾りつけ

Happy Elements社内で短期間でゲームを完成させるSuper Liteプロジェクトの事例についてお話いたしました。

解説していただいたゲーム事例。Super LiteのWebページから遊べます!

実際にゲームを遊びながら学びました

KMCとゲーム開発

KMCでは、毎年新入生がチームを作ってゲームを開発する「みんなでゲームを作る」という新歓プロジェクトを開催しています。 初心者がぶっつけ本番で挑むプロジェクトなので、すんなりいくチームもあれば、苦戦するチームもいます。

2023年NFに展示されたゲーム

スムーズなゲーム開発をサポートするため、ゲームの規模感と開発のスケジュールについて現場のリアルを学んでもらいたいという思いから、今回のイベントを企画しました。

今回のイベントに参加した新入生が刺激を受け、楽しみながら面白いゲーム開発をしてくれたらうれしいです!

ご参加ありがとうございました!

Happy Elementsの皆様、参加者の皆様、本当にありがとうございました!