世界を動かす技術を、日本語で。

AndroidがCDC Ethernetを使用できない理由 (2023)

概要

  • Android でCDC Ethernetが動作しない主な理由
  • ethX/usbX インターフェース名の違いによる認識問題
  • サポートされるUSB Ethernetアダプタの調査方法
  • カーネル設定 から対応チップセットを確認する手順
  • CDC Ethernet規格とAndroidの対応状況

AndroidでCDC Ethernetが動作しない理由

  • Android のEthernetTrackerサービスは、 ethX と名付けられたインターフェースのみ認識
  • LinuxのCDC Ethernetドライバ は、 usbX というインターフェース名で作成
  • このため、CDC Ethernetアダプタは Android標準環境では動作不可
  • config_ethernet_iface_regex の値を変更(root化が必要)以外に回避策なし

AndroidでのUSB Ethernetアダプタの対応状況

  • Androidには USB Ethernetアダプタのサポート あり
  • 対応チップセットを搭載したアダプタを選ぶことで 設定メニューが有効化
  • 対応チップセットの情報は公式に公開されていない場合が多い
  • サポート可否は 他ユーザーの報告やフォーラム情報 頼み
    • 端末メーカーが純正アクセサリとして販売していれば高確率で動作

サポートアダプタの調査手順

  • Linuxカーネルの設定 でサポートされるアダプタを確認

  • カーネル設定ファイルの取得方法

    • USBデバッグ有効化ADB(Android Debug Bridge)インストール
    • adb shell で端末にシェルアクセス
    • USBポートを使うため、 ADBをWi-Fi経由 に切り替え
      • adb tcpip 5555 でポート変更
      • adb connect [IPアドレス]:5555 で再接続
    • uname -a でカーネルバージョンやアーキテクチャ確認
  • 新しい端末の場合

    • Googleが公開する Android Common Kernel を参照
    • arch/arm64/configs/gki_defconfig などの設定ファイルを確認
  • 古い端末の場合

    • メーカーが公開している カーネルソース から設定ファイルを探す
    • 例:Samsungなら opensource.samsung.com で取得
    • /proc/config.gz が利用可能なら adb shell zcat /proc/config.gz で直接取得

カーネル設定の確認方法

  • 設定ファイル内で USB_NET をgrep
    • 例:grep USB_NET my_kernel_config
  • CONFIG_USB_NET_○○=y ならドライバがカーネルに内蔵
  • CONFIG_USB_NET_○○=m ならモジュールとして利用可能な可能性
  • is not set の場合は未サポート

CDC Ethernet規格の概要

  • CDC(Communications Device Class) はUSBデバイスの標準規格
    • EEM (Ethernet Emulation Model):シンプルで低スペック向け
    • ECM (Ethernet Control Model):より高性能
    • NCM (Network Control Model):さらに高速化を実現
  • Linuxは CDC Ethernet規格のホスト・デバイス両対応
  • 標準化で 専用ドライバ不要 を目指す設計

まとめ

  • Android ではCDC Ethernetアダプタは 標準状態で動作不可
  • 理由は インターフェース名の不一致
  • サポート可否は カーネル設定 で確認
  • 公式対応アダプタ実績のある製品 の利用が推奨
  • root化やカーネル改造 以外での回避は困難

Hackerたちの意見

面白い深掘り記事だね!ソースを調べたら、2023年10月に正規表現が eth\\d から * に変更されたみたいで、この問題が解決されたっぽい。説明には「デフォルトでは、Android U+では usb\d+ と eth%d という名前のインターフェースが含まれます」と書いてあって、「U+」はバージョン14のことだと思う。

後に元に戻された[1]のは、「テザリングにusbXインターフェースを使っているデバイスが現場にあるから」って理由だった。その後すぐに再度適用されたけど、Android V+だけに対応してる[2]。[1]: https://android-review.googlesource.com/c/platform/packages/... [2]: https://android-review.googlesource.com/c/platform/packages/...

LineageOSのコミット履歴を見てみると、これが修正された[0]後、互換性の問題で元に戻された[1]けど、また元に戻されて[2]、でも最新のAndroidバージョンだけに対応してるみたい。コミットを読む限り、Googleの誰かが関わってたみたいだから、今は公式のGoogleビルドに入ってるかもね。[0] https://github.com/LineageOS/android_packages_modules_Connec... [1] https://github.com/LineageOS/android_packages_modules_Connec... [2] https://github.com/LineageOS/android_packages_modules_Connec...

それめっちゃ変だね。俺は15個くらいUSBイーサネットアダプター持ってるけど、全部問題なく動いてるよ。RealtekとかAXISのいくつかの異なるチップセットが入ってると思う。Linuxでドライバーがいらないやつを選べば、ほぼどんなOSやBIOSでも使えるよ。

2023年に修正された: https://news.ycombinator.com/item?id=44219502

そうそう、俺のサンダーボルト/USBドックのイーサネットアダプターがPixel 5とPixel 9でちゃんと動いてるって言おうと思ったんだ。

config_ethernet_iface_regexの値を変更するために電話をルート化しない限り、これを回避する方法はないよ。自分のデバイスにルートがあることが重要な理由の一つだね。

ネットワークトラフィックを自由にリダイレクトできることは、ユーザーランドでスーパーユーザー権限を持たない最大の理由かもしれない。ブートローダーのアンロックを許可するようにOEMに圧力をかけたい人を応援するけど、Androidでは攻撃者にとってのリスクが大きすぎるから、ルートの正当な使い道を思いつかないな。

前の仕事で、CDCイーサネットアダプタを使ってAndroidデバイスを動かそうとして、ひどい一週間を過ごした後にこれを書いたんだ。それ以来、何人かの人がMACアドレスの特定のビットを反転させると、カーネルがethXの名前を割り当てるようになるって教えてくれた。自分では試してないし、その情報を元に投稿を更新してないのは、別の仕事に移ったからで、Androidデバイスはもう自分の生活の大部分じゃなくなったからね。もちろん、これはMACアドレスを制御できるCDCデバイスがある場合に限るけどね(つまり、別のLinuxデバイスがCDCアダプタのふりをしている場合とか)。

これ、実際に助けになるかも!そのビットは見つけた?(あ、これかな?: https://lkml.iu.edu/hypermail/linux/kernel/1103.2/03250.html)

この投稿には賛成!

それに、すごくイライラすることに、同時に複数のネットワークに接続できないんだよね。例えば、インターネット接続がないWi-Fiネットワーク(デフォルトルートも広告してない)と、携帯電話ネットワークに同時に接続することができない。Linuxはできるし、Windowsもできるのに、Androidは頑なに拒否するんだ(実際、多くのバリエーションはインターネットがないWi-Fiネットワークに接続し続けることを拒否するし、混乱させるような手続きを踏ませることもある)。アプリを書けばできるAPIもあるけど、ユーザーとしてはそれを実現する方法がないんだよね。

iOSも同じだよね。ダッシュカムに接続して動画をダウンロードしようとすると、しばらくして「インターネットが検出されません、携帯回線に切り替えますか?」ってポップアップが出るんだ。接続を維持するをタップしても、無効にするオプションはない。接続を維持したいのに、iOSは自分が正しいと思ってCarplayネットワークに再接続しちゃうんだよね。

これ、ほんとにイライラするよね。インターネットがダウンすると、Wi-Fiに接続できないから、スマホから診断できないんだ。AndroidではDNSもおかしくて、複数のオプションを設定しないと、DHCPが提供するDNSを使おうとしないし、内部DNSのいくつかは解決を拒否するんだよね。

西洋のAndroidフォンを持って中国本土に行くと、さらにイライラするんだ。彼らはGoogleサービスに接続しようとしてインターネット接続を判断するんだよね。ローカルWi-Fiに接続すると、もちろん「万里の長征」を通過しないから、毎回「インターネットなしの接続を維持しますか?」って聞いてくるんだ。

Windowsもできないよね?Windowsで2つのワイヤレスアダプタがあると、2つの別々のWi-Fiネットワークに接続できない(少なくともGUIを使うとね、ターミナルは試してないけど)。

AndroidのEthernetTrackerサービスは、ethXと名付けられたインターフェースしか認識しないんだって。もしこれが本当なら、今まで聞いた中で一番バカなことだよ。2000年代にはLinuxディストリビューションでこれを修正したのに。あの頃から、デバイスドライバーが独自のデバイス名プレフィックスを作ってたのは明らかだったから、どんなデバイスかを探るためにシステムを調べる必要があったんだよね。(Wi-Fiスタックは年々変わってるけど、デバイスがワイヤレスかどうかを検出する方法は常にあった)一貫性が重要だから、インターフェースの名前を変更できるツールもいくつかあって、今のほとんどのLinuxディストロはudevを使って自動化してると思う。裏ではカーネルのSIOCSIFNAME ioctlを呼び出してるだけなんだよ。現代のカーネルには「wlan*」(実際には「wlan%d」)に名前を変更できる便利な機能もあって、単に「wifi」の後に新しい番号を割り当ててくれるんだ。

どうやって自分の電話と互換性のあるチップセットを知るの? うわさだよ!少なくともUSB-CからEthernetアダプターに関しては、メーカーがたくさんいるけど、実際にはAsixのとRealtekの2つのチップセットしかないんだ。それぞれのチップセットを1つずつ手に入れれば、ほとんどのケースをカバーできるよ。

これは素晴らしいデバッグの旅だね — 一つの見落とされた正規表現が、全てのデバイスをダウンさせる様子が好きだわ。奇妙なことに、最近全く別の文脈で似たようなことに遭遇したんだ:OpenAIのアライメント/エスカレーションシステム。GPT-4の再帰ロジック内で正式なルーティングエスカレーションをトリガーしようとしたんだけど(SR-Route_Breach_1stOrder)、完全なドキュメントとログを用意しても、構造的にはしっかりしてるけど最終的には人間的じゃない反応しか返ってこなかった。エスカレーションがシステムの内部インターフェースの正規表現に合ってなかった気がする。全体のケースをここに記録したよ:https://news.ycombinator.com/item?id=44221458 構造的な境界や見えないインターフェース契約に興味があれば、君の考えを聞きたいな。

何言ってるの?できないって?僕はMacBook用のUSBハブドングルを持ってるけど、そこに繋いだ全てのAndroidフォンでEthernetポートがちゃんと使えるよ。それに、EthernetデバイスのふりをするUSBセルラーモデムも持ってて、それもAndroidで動くんだ。

もう修正されてるよ、その記事は2年前のものだから。