SSTエンジニアブログ

SSTのエンジニアによるWebセキュリティの技術を中心としたエンジニアブログです。

Raspberry Pi 4 を使ったスマホ向け透過型Proxy環境の構築(その2 : 有線LAN無しパターン)

前回の記事では Raspberry Pi 4 Model B Starter Kit (以下「ラズパイ」) をWiFi AP化し、Burp の Invisible(透過型) Proxy にHTTP(S)通信を転送した。 その時、自宅NWとラズパイの間は有線LAN(eth0)で接続し、WiFi APはラズパイ内蔵の無線LAN(wlan0) を使った。

f:id:msakamoto-sf:20210812164740p:plain
前回構築したWiFi AP構成

この構成のメリットとして、eth0に静的IPを設定すれば、PC上からeth0経由でラズパイにSSHやVNCでログインして運用管理が行える点がある。 ラズパイにディスプレイ/キーボード/マウスを接続しなくても操作できるので、省スペースで場所を取らない。

一方で有線LANを使う都合上、自宅NWのルータ/スイッチからLANケーブルで接続できる範囲にしかラズパイを設定できない制約も生じる。 コロナ下で在宅作業が一般化している弊社の社員においては、自宅環境の都合上、有線LANケーブルで接続するのが物理的に不可能なケースが発生している。 そのようなケースでは有線LANを使ったラズパイの運用は不可能となり、WiFiのみでの運用が必須となる。

そこで本記事では無線LAN USB dongle をラズパイのUSBポートに挿入し、有線LANが無い状態でも以下の構成でWiFi APを運用できないか挑戦した。

  • wlan0
    • ラズパイ内蔵の無線LAN, 自宅NWに接続
    • 同じく自宅NWに接続している他のPCからSSH/VNC接続を受け付けるため静的IPを設定(オプション)
  • wlan1
    • 無線LAN USB dongle, WiFi AP として運用
    • 動作実績についてネット上で検索したところ、ELECOM製の「小型無線LAN子機 WDC-150SU2MBK」が Raspberry Pi 3, 4 等で動作確認成功しているようだったので、これを採用した。
  • eth0
    • 未接続状態

f:id:msakamoto-sf:20210812164803p:plain
本記事で目指す WiFi AP 構成

最終的に上記構成で WiFi AP を構築し、スマホ(iPad)からのHTTP(S)通信をPC上のBurpの Invisible(透過型) Proxy でキャプチャすることに成功した。 これにより有線LANでの接続が不可能な環境においてもWiFi APを構築し、HTTP Proxy 未対応のスマホアプリについてもBurp でHTTP(S)通信をキャプチャすることが可能となる。

本記事で使用した機器・ソフト一覧:

  • Raspberry Pi 4 Model B
  • 無線LAN USB dongle:
    • ELECOM 小型無線LAN子機 WDC-150SU2MBK
    • https://www.amazon.co.jp/dp/B00JTSF7DY
    • 2.4GHz専用, IEEE 802.11n/g/b, 150Mbps
    • ※本記事で以下「無線LAN USB dongle」と表記するのは全てこれを指す。
  • Burp Suite Community Edition v2021.5.1
  • iPad (iOS 12.5.4)*1

本記事を読む際の注意事項

  • 本記事は執筆時点(2021-08-09 ~ 2021-08-12)の情報に基づいて、上記機器とソフトウェアのバージョンを対象として書かれている。
  • 上記以外の機器(Rasberry Pi 2/3/Zero等) やOS(Debian 9, Stretchベースなど)、無線LAN USB dongle を使った場合は本記事の対象外となる。
  • 本記事中で参照しているURLについては、執筆時点のURLとなる。URLの所有者により、変更/閉鎖される可能性があるのでご注意いただきたい。

最後に:本記事作成では udev/systemd や Linux のドライバ問題等で沼にハマりかなり時間を取られ、「~です/ます」スタイルで記事を書く余裕が無かった。 手元の「~だ/である」スタイルで残したメモをベースとして記事に整理しており、他の記事とこの点でスタイルが異なり違和感を覚えた方には、ご容赦のほどお願い申し上げる。

0. ベースとなる参考資料と前提知識

本記事では以下の参考資料をベースとしており、それぞれのトピックの詳細について本記事では割愛している。 不明点などあれば随時これらの参考資料を確認すること。

ラズパイのセットアップ, WiFi AP化:

iOSのProxy設定, Burp のInvisible(透過型) Proxy 関連:

前提知識:

  • 本記事ではラズパイにSSHログインしてlinuxのコマンド操作ができることを前提としている。
  • vi または nano でファイルの読み書きができることを前提としている。
  • TCP/IPに関する基礎的な知識があり、自宅NWのIPアドレスやルーティングをある程度自分で把握・管理できることを前提としている。

1. 最初から設定する人向けの手順

筆者自身は最初、ラズパイ内蔵無線LAN(wlan0)をWiFi APとして使う形で構築した。 本記事執筆にあたってはその状態から一度設定を巻き戻し、無線LAN USB dongle をwlan1として追加し、wlan1のWiFi AP構築を行っている。 そのため、最初から無線LANのみでゼロから WiFi APをセットアップした状況ではない。

そのうえで、「仮に最初から wlan0 - wlan1 の形でセットアップするならこの手順になるだろう」という想定で手順を整理してみた。 筆者としては未検証なため、誤りが含まれている可能性はある。 しかしながら設定ポイントの把握や、全体の流れの参考にはなると思われる。 最初から無線LANのみで構築する人は参考にしてみていただきたい。

なお WiFi AP 化関連の設定ファイルについて、実際にどう書けば良いのか分かりづらい時は 公式ドキュメント の解説を参照するとよい。 ほとんどのケースで eth0 を wlan0 に、wlan0 を wlan1 に読み替えると、今回の目的に合致する。

1-1. 設定のポイント

詳細な設定手順は長くなるため、先に設定のポイントを示す。

  1. wlan0, wlan1 を正しく認識させること。
    1. 動作実績のある無線LAN USB dongle を使う。
      • 今回は WDC-150SU2MBK を使用した。
    2. 無線LAN USB dongle にあわせたデバイスドライバをインストールする。
      • kernel組み込みのドライバでもインターフェイス名までは認識できるが、動作しないことがある。
    3. MACアドレスでインターフェイス名を固定する。
      • 再起動後にズレることがあるのでそれを防止する。
  2. wlan0 でのみ自宅NWにWiFi接続すること。(= eth0 には有線LANケーブルを接続しない)
    • IPアドレスの設定は /etc/dhcpcd.conf, wlan0のWiFi 接続は /etc/wpa_supplicant/wpa_supplicant-wlan0.conf で行う。
    • eth0, wlan0 両方で同じ自宅NWに接続していると、本記事で紹介している iptables の設定がうまく動作しないことがある。
  3. wlan1 を WiFi APとして動作させるのは、ほぼ公式ドキュメントの通り。
    • 公式ドキュメントの手順で "wlan0" となっている箇所を "wlan1" に入れ替えるだけでほぼOK。

1-2. 詳細

詳細な手順を以下に示す。

  1. ラズパイのセットアップ
  2. 内蔵無線LAN(wlan0) のインターフェイス名を固定する。
    1. ip a で wlan0 のMACアドレスを確認する。
    2. sudo vi /etc/udev/rules.d/72-static-name.rules で以下の内容を追記する。
      • ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="(内蔵無線LAN のMACアドレス)", NAME="wlan0"
    3. 再起動後にwlan0が認識できているか、WiFi接続できているか ip a で確認する。
  3. 自宅NWへのWiFi接続を、wlan0 に限定する。
    • sudo mv /etc/wpa_supplicant/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant-wlan0.conf
    • 再起動後にwlan0から自宅NWにWiFi接続できているか ip a で確認する。
    • デフォルトの設定ファイル名 /etc/wpa_supplicant/wpa_supplicant.conf のままだと dhcpcd により wlan0/wlan1 両方でWiFi接続が設定されてしまう。dhcpcd の wpa_supplicant hook スクリプトでは wpa_supplicant-(インターフェイス名).conf でインターフェイス毎にWiFi接続を分離できるので、wlan0のみ設定ファイルを用意してWiFi接続をし、wlan1はAPとして動かすため設定ファイルを用意せずWiFi接続も行わせない。
  4. 無線LAN USB dongle を適当なUSBポートに挿し、MACアドレスを確認して wlan1 のインターフェイス名に固定する。
    1. 無線LAN USB dongle を適当なUSBポートに挿入する。
    2. dmesg | less で kernel のデバッグメッセージを確認し、kernel組み込みのドライバで認識できたか確認する。
      • うまく認識できていない場合は、後述のドライバインストールを先行して試みる。
    3. ip a で新しい "wlanX" が認識されたか確認し、新しいMACアドレスをメモしておく。
    4. sudo vi /etc/udev/rules.d/72-static-name.rules で以下の内容を追記する。
      • ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="(無線LAN USB dongle のMACアドレス)", NAME="wlan1"
    5. 再起動後に ip a で以下の状態になっていることを確認する。
      • wlan0 : 内蔵無線LANのMACアドレスに対応、自宅NWへのWiFi接続成功
      • wlan1 : 無線LAN USB dongle のMACアドレスに対応、未接続状態
  5. 無線LAN USB dongle のドライバをダウンロードしてインストールする。
    1. http://downloads.fars-robotics.net/wifi-drivers/8188eu-drivers/ からラズパイの uname -a 結果に対応する .tar.gz をDLする。
    2. tar zxf (ダウンロードした tar.gz) して展開後、install.sh を実行する。
    3. 再起動後に ip a で引き続き無線LAN USB dongle が wlan1 として認識できていることを確認する。
      • 正常に認識できると 無線LAN USB dongleのLEDが青色に点滅する。
  6. (オプション) 内蔵無線LAN wlan0 に静的IPを設定する。
    • 以下のようにWiFi接続だけで運用するケースであれば、wlan0 に静的IPアドレスを設定してSSH/VNCログインで運用できると便利。
      • ディスプレイ/キーボード/マウスは繋げない。
      • 有線LANケーブルは接続しない。
      • 自宅NWに据え置きで、他のNWに移動すること (= wlan0 のIPアドレスが変わること) は滅多にない。
      • = ラズパイを自宅NWに据え置きして WiFi APとして使うケース。
    • sudo vi /etc/dhcpcd.conf で以下の行を追加する。
      • interface wlan0
      • static ip_address=(自宅NW内の静的IP)/24
      • static routers=(自宅NWのルータIP)
      • static domain_name_servers=(自宅NWのルータIP) 8.8.8.8 (←は例なので、任意のDNSサーバを指定)
    • 再起動後、自宅NW内の他のPCから静的IPに対してSSH/VNC接続できることを確認する。
  7. WiFi AP に必要なパッケージのインストール
    1. sudo apt install hostapd
    2. sudo systemctl unmask hostapd
    3. sudo systemctl enable hostapd
    4. sudo apt install dnsmasq
    5. sudo DEBIAN_FRONTEND=noninteractive apt install -y netfilter-persistent iptables-persistent
  8. 無線LAN USB dongle (wlan1) に WiFi AP としての静的IP(例: 192.168.4.1)を設定
    • sudo vi /etc/dhcpcd.conf で以下の行を追加する。
      • interface wlan1
      • static ip_address=192.168.4.1/24
      • ※ラズパイ公式ドキュメントでは wlan0 をWiFi APとするため "nohook wpa_supplicant" も含めている。本記事では wlan0 は自宅NWへのWiFi接続を維持するため wpa_supplicant が必要であり、 wpa_supplicant 全体を無効化するこの設定は使えない。wpa_supplicant の dhcpcd 用のhookは /lib/dhcpcd/dhcpcd-hooks/10-wpa_supplicant に実体があり、このスクリプトを確認すると /etc/wpa_supplicant/ 以下の設定ファイル名で wpa_supplicant-(インターフェイス名).conf により特定のインターフェイスのみに対して wpa_supplicant を実行できるよう限定することが可能であることが分かる。本記事ではこれを活用し、wpa_supplicant の hook は有効なまま、wlan0 のみ /etc/wpa_supplicant/wpa_supplicant-wlan0.conf によるWiFi接続を行い、wlan1 については /etc/wpa_supplicant/wpa_supplicant-wlan1.conf/etc/wpa_supplicant/wpa_supplicant.conf を用意しないことで wpa_supplicant の hook を実行しない、というアプローチを採用している。
  9. WiFi AP としてのルーティング設定
    • sudo vi /etc/sysctl.d/routed-ap.conf で以下の行を追記, IPv4の転送機能(forward)を有効化する。
      • net.ipv4.ip_forward=1
    • sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
      • NATによる wlan0 へのマスカレード設定をしている。
    • sudo netfilter-persistent save
      • iptablesの設定を保存して、再起動後も反映されるようにしている。
  10. WiFi AP が提供するDHCPとDNSサーバの設定
    • sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf.orig
      • パッケージ提供のデフォルト設定ファイルをバックアップしている。
    • sudo vi /etc/dnsmasq.conf → 新規に以下の内容だけで設定ファイルを作成
      • interface=wlan1 (# 無線LAN USB dongle で動かす)
      • dhcp-range=192.168.4.2,192.168.4.20,255.255.255.0,24h (# DHCPレンジとリース時間を設定)
      • domain=wlan
      • address=/gw.wlan/192.168.4.1
  11. 無線LAN機能のブロックを解除
    • sudo rfkill unblock wlan
  12. WiFi AP の設定
    • sudo vi /etc/hostapd/hostapd.conf で以下を設定
      • country_code=JP
      • interface=wlan1
      • ssid=(SSID)
      • hw_mode=g
      • channel=7
      • macaddr_acl=0
      • auth_algs=1
      • ignore_broadcast_ssid=0
      • wpa=2
      • wpa_passphrase=(パスワード)
      • wpa_key_mgmt=WPA-PSK
      • wpa_pairwise=TKIP
      • rsn_pairwise=CCMP
    • 再起動後、実際にスマホ等から wlan1 に設定したSSID/パスワードで接続し、インターネットにアクセスできることを確認。
  13. (オプション) 有線LANケーブルを外す。
    • この段階まで有線LANケーブルを接続していた場合は、ケーブルを外す。
    • eth0 が up した状態だと、このあとの設定がうまく動作しないケースがあった。
    • これ以降の操作はディスプレイ/キーボード/マウスを接続したラズパイ上から直接か、wlan0/wlan1 に設定した静的IPアドレスにSSH/VNCログインして操作すること。
  14. PC上のBurp で Invisible(透過型) Proxy を設定
    • 以下のように3つの Proxy Listener を設定する。
      1. tcp:8080 に bind した Proxy Listenr
        • "Bind to address" を "All interfaces" にして、スマホから http://(Burpを動かすPCのIPv4):8080 にアクセスできるようにする。これは Burp のルートCA証明書のダウンロードに使う。
      2. tcp:80 に bind したProxy Listener
        • "Bind to address" を "All interfaces" にする。
        • "Request handling" から "Support invisible proxying" にチェックを入れる。
      3. tcp:443 に bind したProxy Listener
        • "Bind to address" を "All interfaces" にする。
        • "Request handling" から "Support invisible proxying" にチェックを入れる。
        • "TLS Protocols" から "Use custom protocols" にチェックを入れ、TLSv1.3 のチェックを外す。
    • "Proxy" -> "Options" -> "TLS Pass Through" の "Automatically add entries on client TLS negotiation failure" にチェックを入れる。
    • "Project options" タブ → "HTTP/2" の "Enable HTTP/2" のチェックを外す。
    • PC側のファイアウォールで、tcp:80/443/8080 へのアクセス許可(inbound)を忘れずに。
  15. wlan1のWiFi APに接続したスマホから Burp のルートCA証明書をダウンロードしてインストールする。
    • http://(Burpを動かすPCのIPv4):8080 にスマホのブラウザでアクセスし、ページ右上の "CA Certificate" をDLする。
    • iOSの場合はプロファイルとしてイントールし、「設定」を開き「情報」→「証明書信頼設定」 → 「ルート証明書を全面的に信頼する」で「PortSwigger CA」をONにする。
  16. WiFi AP からの tcp:80/443 を Burp の Invisible(透過型) Proxy に転送する。
    • ラズパイから以下のコマンドを実行する。
      1. sudo iptables -t nat -A PREROUTING -i wlan1 -p tcp --dport 80 -j DNAT --to-destination (Burpを動かすPCのIPv4):80
      2. sudo iptables -t nat -A PREROUTING -i wlan1 -p tcp --dport 443 -j DNAT --to-destination (Burpを動かすPCのIPv4):443
    • iptablesの設定後、スマホ上のブラウザやアプリからアクセスしてみて、Burp Proxy でHTTP(S)通信をキャプチャできるか確認する。

1-3. トラブルシュート

正常に動作しない場合、まずはコーヒーや紅茶を淹れて一服し、お手洗いに行ったりSNSのタイムラインを眺めてしばし現実逃避をする。 その後、落ち着いて状況を切り分ける。

現状の把握:

  • 各種ログを調査し、エラーが出ていないか確認する。
    • $ dmesg
      • デバイスの検出状況、ドライバのロード状況
    • $ sudo journalctl -u dhcpcd
      • ネットワークインターフェイスのIP設定, WiFi 接続設定
    • $ sudo journalctl -u hostapd
      • WiFi AP 設定
    • $ sudo journalctl -u dnsmasq
      • WiFi AP 用DHCP設定
    • $ sudo less /var/log/messages
      • システム全体的なログメッセージ
  • ネットワークインターフェイスの状況を確認する。
    • $ ip a
      • ネットワークインターフェイス全体のアドレス設定状況
    • $ iw dev, $ iw phy, $ iwconfig
      • 無線LANインターフェイス系の情報取得
    • $ sudo ls -l /sys/class/net
      • ネットワークインターフェイスのデバイスツリー情報(sysfs)
    • $ sudo udevadm info /sys/class/net/(インターフェイス名)
      • udevが管理するデバイス情報の表示
    • $ sudo udevadm test /sys/class/net/(インターフェイス名)
      • udevルールのデバッグ実行

確認ポイント:

  1. ログや設定状況を精査し、設定ファイルのtypoやIPアドレスの打ち間違えなどが無いかチェックする。
    • 後述の「参考: 正常時のログ」と見比べてみる。
  2. wlan0 と wlan1 が入れ替わっていないか、ip a の結果や実際に無線LAN USB dongle を抜き差しして確認する。
  3. ログについてはエラーメッセージについても注意を払い、何か気になるエラーが出ていないか確認する。
    • dhcpcd や hostapd のログで、wlan1(無線LAN USB dongle) についてエラーが出ていた場合、デバイスドライバのインストールを忘れていないか, dmesg で何かエラーメッセージが出ていないか、想定と異なるドライバがロードされていないかあたりを確認する。

それでも動作しない場合、それぞれの機能を停止するなどして動作を切り分ける。

  1. WiFi AP 機能を停止してみる。
    • 下記の「参考: iptablesを戻してWiFi AP機能を停止」に従い、WiFi AP 機能を停止してみる。
    • → eth0 と wlan0 だけで正常に動作するか確認する。
    • → もしこれで正常に動作すれば、問題は iptables か WiFi AP 設定に絞り込めるので、それを確認する。
  2. 有線LANケーブルが接続されていれば、外してみる。
    • eth0/wlan0それぞれが自宅NWに接続していると、metricsの設定値などの影響でデフォルトゲートウェイに出ていくインターフェイスがeth0のままだったりする。
    • iptablesではwlan0にマスカレード設定するので、eht0の有線LANケーブルは外し、確実に wlan0 から自宅NWに出ていくようにしてみる。
  3. 無線LAN USB dongle が通常のWiFi接続で動作するか確認する。
    • 本記事では検証していないが、 WiFi AP機能を停止したうえで /etc/wpa_supplicant/wpa_supplicant-wlan1.conf を作成してWiFi接続を設定し、通常のWiFi接続に成功するか確認してみる。
      • /etc/dhcpcd.conf で wlan1 の静的IPアドレス指定をコメントアウトして動作確認する。
    • 正常に動作しないようであれば、dongleをWindows/Macマシンに挿してみて正常に動作するかチェックしてみる。

トラブルに遭遇した時は、上記を参考に対処してみていただきたい。

参考: iptablesを戻してWiFi AP機能を停止

iptables の転送ルール, マスカレード設定を削除:

# 1. 番号確認
$ sudo iptables -t nat -nvL --line-numbers

# 2. 透過型Proxyへの転送ルールが残っていれば、ルール削除
$ sudo iptables -t nat -D PREROUTING 1
$ sudo iptables -t nat -D PREROUTING 1

# 3. eth0 or wlan0 への転送ルールを削除
$ sudo iptables -t nat -D POSTROUTING (POSTROUTING のルール番号)

# 4. 次回起動時に向けた保存
$ sudo netfilter-persistent save

WiFi AP機能としての dnsmasq と hostapd を停止:

$ sudo systemctl stop dnsmasq
$ sudo systemctl disable dnsmasq

$ sudo systemctl stop hostapd
$ sudo systemctl disable hostapd

参考: その他 iptables コマンド紹介

iptables設定確認:

$ sudo iptables -nvL
$ sudo iptables -t nat -nvL
$ sudo cat /etc/iptables/rules.v4

BurpのIPが変わった場合などに、iptablesの設定を変更:

# 1. 番号確認
$ sudo iptables -t nat -nvL --line-numbers

# 2. 削除
$ sudo iptables -t nat -D PREROUTING 1
$ sudo iptables -t nat -D PREROUTING 1

※1番を2回削除してるのは、tcp:80 用と tcp:443 用の2つのフォワード設定をしてたので、1つ削除するともう片方が1番に繰り上がる。 → 1番を2回削除すれば tcp:80, tcp:443 の2つのフォワード設定を削除完了となる、ということ。 1つ削除するごとに sudo iptables -t nat -nvL --line-numbers すればその様子が分かる。

POSTROUTINGに設定したマスカレード設定等を削除:

$ sudo iptables -t nat -D POSTROUTING (POSTROUTING のルール番号)

参考: 正常時のログ

正常に設定できたときの各種のログを参考として以下に示す。 あくまでも筆者環境でのログとなるため、もしかしたら機材やソフトウェアのマイナーバージョンアップで若干変わる可能性もある。

kernel のデバッグメッセージ: $ dmesg | less

[    5.466443] 8188eu: loading out-of-tree module taints kernel.
[    5.489237] brcmfmac mmc1:0001:1: Direct firmware load for brcm/brcmfmac43455-sdio.raspberrypi,4-model-b.txt failed with error -2
[    5.742089] brcmfmac: brcmf_fw_alloc_request: using brcm/brcmfmac43455-sdio for chip BCM4345/6
[    5.752746] brcmfmac: brcmf_c_preinit_dcmds: Firmware: BCM4345/6 wl0: Jan  4 2021 19:56:29 version 7.45.229 (617f1f5 CY) FWID 01-2dbd9d2e
[    5.783810] bFWReady == _FALSE call reset 8051...
[    5.834699] usbcore: registered new interface driver 8188eu
[    7.757160] uart-pl011 fe201000.serial: no DMA platform data
[    7.874532] random: crng init done
[    7.874556] random: 7 urandom warning(s) missed due to ratelimiting
[    8.055414] 8021q: 802.1Q VLAN Support v1.8
[    8.393002] Adding 102396k swap on /var/swap.  Priority:-2 extents:1 across:102396k SSFS
[    8.504051] brcmfmac: brcmf_cfg80211_set_power_mgmt: power save enabled
[    9.033525] ==> rtl8188e_iol_efuse_patch
[    9.262043] IPv6: ADDRCONF(NETDEV_CHANGE): wlan1: link becomes ready
[   10.409294] bcmgenet fd580000.ethernet: configuring instance for external RGMII (RX delay)
[   10.409561] bcmgenet fd580000.ethernet eth0: Link is Down
[   10.705864] broken atomic modeset userspace detected, disabling atomic
[   13.413579] fuse: init (API version 7.32)
[   14.646509] IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready

8188eu ドライバのインストールメッセージや、 "wlan{0|1}: link becomes ready" メッセージがポイントになると思われる。

dhcpcd のログ : $ sudo journalctl -u dhcpcd

-- Logs begin at Fri 2021-08-13 01:17:26 JST, end at Fri 2021-08-13 10:56:58 JST. --
Aug 13 01:17:30 raspberrypi systemd[1]: Starting dhcpcd on all interfaces...
Aug 13 01:17:30 raspberrypi dhcpcd[376]: dev: loaded udev
Aug 13 01:17:30 raspberrypi dhcpcd[376]: forked to background, child pid 427
Aug 13 01:17:30 raspberrypi systemd[1]: Started dhcpcd on all interfaces.
Aug 13 01:17:31 raspberrypi dhcpcd-run-hooks[485]: wlan0: starting wpa_supplicant
Aug 13 01:17:31 raspberrypi dhcpcd[427]: wlan0: connected to Access Point `'
Aug 13 01:17:32 raspberrypi dhcpcd-run-hooks[590]: wlan1: /etc/wpa_supplicant.conf does not exist
Aug 13 01:17:32 raspberrypi dhcpcd-run-hooks[593]: wlan1: not interacting with wpa_supplicant(8)
Aug 13 01:17:32 raspberrypi dhcpcd[427]: wlan1: connected to Access Point `'
Aug 13 01:17:33 raspberrypi dhcpcd[427]: eth0: waiting for carrier
Aug 13 01:17:33 raspberrypi dhcpcd[427]: wlan0: waiting for carrier
Aug 13 01:17:33 raspberrypi dhcpcd[427]: wlan1: waiting for carrier
Aug 13 01:17:33 raspberrypi dhcpcd[427]: wlan1: carrier acquired
Aug 13 01:17:33 raspberrypi dhcpcd[427]: DUID 00:01:00:01:28:28:1e:01:e4:5f:01:31:e9:a9
Aug 13 01:17:33 raspberrypi dhcpcd[427]: wlan1: IAID 18:88:09:04
Aug 13 01:17:33 raspberrypi dhcpcd[427]: wlan1: adding address fe80::db23:4a6:96f2:d5a
Aug 13 01:17:33 raspberrypi dhcpcd[427]: wlan1: probing address 192.168.4.1/24
Aug 13 01:17:33 raspberrypi dhcpcd[427]: wlan1: soliciting an IPv6 router
Aug 13 01:17:37 raspberrypi dhcpcd[427]: wlan0: carrier acquired
Aug 13 01:17:37 raspberrypi dhcpcd[427]: wlan0: connected to Access Point `(SSID)'
Aug 13 01:17:37 raspberrypi dhcpcd[427]: wlan0: IAID 01:31:e9:a9
Aug 13 01:17:37 raspberrypi dhcpcd[427]: wlan0: adding address fe80::be11:3b48:9995:f63c
Aug 13 01:17:37 raspberrypi dhcpcd[427]: wlan0: probing address (自宅NW固定IP)/24
Aug 13 01:17:37 raspberrypi dhcpcd[427]: wlan0: soliciting an IPv6 router
Aug 13 01:17:38 raspberrypi dhcpcd[427]: wlan1: using static address 192.168.4.1/24
Aug 13 01:17:38 raspberrypi dhcpcd[427]: wlan1: adding route to 192.168.4.0/24
Aug 13 01:17:38 raspberrypi dhcpcd[427]: wlan0: Router Advertisement from fe80::a10:86ff:fe1a:c8af
Aug 13 01:17:38 raspberrypi dhcpcd[427]: wlan0: adding address 240f:109:c06e:1:70d3:aa8:6bc:302b/64
Aug 13 01:17:38 raspberrypi dhcpcd[427]: wlan0: adding route to 240f:109:c06e:1::/64
Aug 13 01:17:38 raspberrypi dhcpcd[427]: wlan0: requesting DHCPv6 information
Aug 13 01:17:38 raspberrypi dhcpcd[427]: wlan0: fe80::a10:86ff:fe1a:c8af is reachable again
Aug 13 01:17:38 raspberrypi dhcpcd[427]: wlan0: adding default route via fe80::a10:86ff:fe1a:c8af
Aug 13 01:17:42 raspberrypi dhcpcd[427]: wlan0: using static address (自宅NW固定IP)/24
Aug 13 01:17:42 raspberrypi dhcpcd[427]: wlan0: adding route to (自宅NWグループ)/24
Aug 13 01:17:42 raspberrypi dhcpcd[427]: wlan0: adding default route via (自宅NWルーターIP)
Aug 13 01:17:46 raspberrypi dhcpcd[427]: wlan1: no IPv6 Routers available
Aug 13 01:17:48 raspberrypi dhcpcd[427]: wlan0: fe80::a10:86ff:fe1a:c8af is reachable again
Aug 13 01:17:48 raspberrypi dhcpcd[427]: wlan0: fe80::a10:86ff:fe1a:c8af is reachable again
Aug 13 10:49:27 raspberrypi dhcpcd[427]: wlan0: Router Advertisement from fe80::a10:86ff:fe1a:c8af

→ wlan0 は自宅NWのSSIDに接続してIP情報を取得できている。 wlan1 については設定ファイルが無いため wpa_supplicant をスキップしていることが分かる。 WiFi AP用の静的IPをwlan1に設定しているログも確認できる。

dnsmasq のログ : $ sudo journalctl -u dnsmasq

-- Logs begin at Fri 2021-08-13 01:17:26 JST, end at Fri 2021-08-13 10:57:41 JST. --
Aug 13 01:17:31 raspberrypi systemd[1]: Starting dnsmasq - A lightweight DHCP and caching DNS server...
Aug 13 01:17:31 raspberrypi dnsmasq[517]: dnsmasq: syntax check OK.
Aug 13 01:17:31 raspberrypi dnsmasq[568]: started, version 2.80 cachesize 150
Aug 13 01:17:31 raspberrypi dnsmasq[568]: compile time options: IPv6 GNU-getopt DBus i18n IDN DHCP DHCPv6 no-Lua TFTP conntrack ipset auth DNSSEC loop-detect inotify dumpfile
Aug 13 01:17:31 raspberrypi dnsmasq[568]: warning: interface wlan1 does not currently exist
Aug 13 01:17:31 raspberrypi dnsmasq-dhcp[568]: DHCP, IP range 192.168.4.2 -- 192.168.4.20, lease time 1d
Aug 13 01:17:31 raspberrypi dnsmasq[568]: read /etc/hosts - 5 addresses
Aug 13 01:17:32 raspberrypi dnsmasq[568]: no servers found in /run/dnsmasq/resolv.conf, will retry
Aug 13 01:17:32 raspberrypi dnsmasq[572]: Too few arguments.
Aug 13 01:17:32 raspberrypi dnsmasq[572]: Too few arguments.
Aug 13 01:17:32 raspberrypi systemd[1]: Started dnsmasq - A lightweight DHCP and caching DNS server.
Aug 13 01:17:39 raspberrypi dnsmasq[568]: reading /run/dnsmasq/resolv.conf
Aug 13 01:17:39 raspberrypi dnsmasq[568]: using nameserver fe80::a10:86ff:fe1a:c8af%wlan0#53
Aug 13 01:17:42 raspberrypi dnsmasq[568]: reading /run/dnsmasq/resolv.conf
Aug 13 01:17:42 raspberrypi dnsmasq[568]: using nameserver (設定ファイルに設定したDNS1)#53
Aug 13 01:17:42 raspberrypi dnsmasq[568]: using nameserver (設定ファイルに設定したDNS2)#53
Aug 13 01:17:42 raspberrypi dnsmasq[568]: using nameserver fe80::a10:86ff:fe1a:c8af%wlan0#53

hostapd のログ : $ sudo journalctl -u hostapd

-- Logs begin at Fri 2021-08-13 01:17:26 JST, end at Fri 2021-08-13 10:57:10 JST. --
Aug 13 01:17:31 raspberrypi systemd[1]: Starting Advanced IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator...
Aug 13 01:17:31 raspberrypi hostapd[521]: Configuration file: /etc/hostapd/hostapd.conf
Aug 13 01:17:31 raspberrypi hostapd[521]: wlan1: interface state UNINITIALIZED->COUNTRY_UPDATE
Aug 13 01:17:31 raspberrypi hostapd[521]: Using interface wlan1 with hwaddr 04:ab:18:88:09:04 and ssid "(SSID)"
Aug 13 01:17:32 raspberrypi hostapd[521]: wlan1: interface state COUNTRY_UPDATE->ENABLED
Aug 13 01:17:32 raspberrypi hostapd[521]: wlan1: AP-ENABLED
Aug 13 01:17:32 raspberrypi systemd[1]: Started Advanced IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator.

udevadm info の結果参考:

pi@raspberrypi:~ $ sudo udevadm info /sys/class/net/wlan0
P: /devices/platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/net/wlan0
L: 0
E: DEVPATH=/devices/platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/net/wlan0
E: DEVTYPE=wlan
E: INTERFACE=wlan0
E: IFINDEX=3
E: SUBSYSTEM=net
E: USEC_INITIALIZED=5864827
E: ID_NET_NAMING_SCHEME=v240
E: ID_NET_NAME_MAC=wlxe45f0131e9a9
E: ID_PATH=platform-fe300000.mmcnr
E: ID_PATH_TAG=platform-fe300000_mmcnr
E: ID_NET_DRIVER=brcmfmac
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/wlan0
E: TAGS=:systemd:

pi@raspberrypi:~ $ sudo udevadm info /sys/class/net/wlan1
P: /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0/net/wlan1
L: 0
E: DEVPATH=/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0/net/wlan1
E: DEVTYPE=wlan
E: INTERFACE=wlan1
E: IFINDEX=4
E: SUBSYSTEM=net
E: USEC_INITIALIZED=5882156
E: ID_NET_NAMING_SCHEME=v240
E: ID_NET_NAME_MAC=wlx04ab18880904
E: ID_OUI_FROM_DATABASE=ELECOM CO.,LTD.
E: ID_NET_NAME_PATH=wlp1s0u1u4
E: ID_VENDOR=ELECOM
E: ID_VENDOR_ENC=ELECOM\x0d\x0d
E: ID_VENDOR_ID=056e
E: ID_MODEL=WDC-150SU2M
E: ID_MODEL_ENC=WDC-150SU2M
E: ID_MODEL_ID=4008
E: ID_REVISION=0000
E: ID_SERIAL=ELECOM_WDC-150SU2M
E: ID_TYPE=generic
E: ID_BUS=usb
E: ID_USB_INTERFACES=:ffffff:
E: ID_USB_INTERFACE_NUM=00
E: ID_USB_DRIVER=8188eu
E: ID_VENDOR_FROM_DATABASE=Elecom Co., Ltd
E: ID_PATH=platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.4:1.0
E: ID_PATH_TAG=platform-fd500000_pcie-pci-0000_01_00_0-usb-0_1_4_1_0
E: ID_NET_DRIVER=8188eu
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/wlan1
E: TAGS=:systemd:

2. 筆者環境での詳細な設定ログ

前述の通り、筆者自身は最初、ラズパイ内蔵無線LAN(wlan0)をWiFi APとして使う形で構築した。 本記事執筆にあたってはその状態から一度設定を巻き戻し、無線LAN USB dongle をwlan1として追加し、wlan1のWiFi AP構築を行っている。

Raspberry Pi 4 を使ったスマホ向け透過型Proxy環境の構築 - SSTエンジニアブログ に続く作業として、筆者環境で実際に設定したログを以下に整理する。

全体手順:

  1. WiFi APの停止
  2. wlan0 のインターフェイス名を固定
  3. wlan0 でSSIDに接続して固定IPを設定
  4. 無線LAN USB dongle の追加とインターフェイス名の固定
  5. WDC-150SU2MBK のドライバ, RTL8188EUS のインストール
  6. 無線LAN USB dongle のWiFi AP化
  7. tcp:80/443を Invisible(透過型) Proxy に転送

2-1. WiFi APの停止

まず前回セットアップしたWiFi AP機能を一時停止し、wlan0を通常の無線LAN子機としてSSIDに接続できるようにした。

iptables のルールを削除した:

# 1. 番号確認
$ sudo iptables -t nat -nvL --line-numbers

# 2. 透過型Proxyへの転送ルールが残っていれば、ルール削除
$ sudo iptables -t nat -D PREROUTING 1
$ sudo iptables -t nat -D PREROUTING 1

# 3. eth0 への転送ルールを削除
$ sudo iptables -t nat -D POSTROUTING (POSTROUTING のルール番号)

# 4. 次回起動時に向けた保存
$ sudo netfilter-persistent save

WiFi AP化するときに導入した dnsmasq と hostapd を停止した:

$ sudo systemctl stop dnsmasq
$ sudo systemctl disable dnsmasq

$ sudo systemctl stop hostapd
$ sudo systemctl disable hostapd

2-2. wlan0 のインターフェイス名を固定

そのままwlan0の作業を進めても良いが、その前にwlan0のインターフェイス名を固定する。

ラズパイでは kernel やドライバが検出した順序でネットワークインターフェイス名が決まる。 そのため無線LAN USB dongle を装着すると、再起動の前後で以下のようにインターフェイス名の順番がズレることがある。 このズレは筆者環境で実際に発生し、ズレに気づかない筆者は「あれ!?なんでこの設定で動かないの!?」と1-2時間ほどロスした。

  • 再起動前
    • wlan0 : 内蔵無線LAN
    • wlan1 : 無線LAN USB dongle
  • 再起動後
    • wlan0 : 無線LAN USB dongle
    • wlan1 : 内蔵無線LAN

トラブルを避けるため、今回は無線LAN子機のMACアドレスをキーとしてネットワークインターフェイス名を固定した。

ip a でインターフェイス名とMACアドレスの対応を確認できるが、練習として sysfs (/sys) の情報や udevadm(8) からのMACアドレス確認も試みる。 udevadm info コマンドを使うと、"wlan0", "wlan1" 等のネットワークインターフェイス名を元に、それがUSB接続か、内蔵なのかを判定することができる。

まず ip a で現在認識されている "wl" 始まりのインターフェイスを確認する。

$ ip a
(...)
3: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group default qlen 1000
    link/ether 04:ab:18:88:09:04 brd ff:ff:ff:ff:ff:ff
4: wlan1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether e4:5f:01:31:e9:a9 brd ff:ff:ff:ff:ff:ff
(...)

上記は無線LAN USB dongle を装着後にrebootし、実際にインターフェイス名がずれた状態での実行結果となる。

ネットワークインターフェイス名は kernel 内の情報として sysfs の /sys/class/net 以下にリンクとして参照することができる。

$ ls -l /sys/class/net
total 0
lrwxrwxrwx 1 root root 0 Aug 11 17:35 eth0 -> ../../devices/platform/scb/fd580000.ethernet/net/eth0
lrwxrwxrwx 1 root root 0 Aug 11 17:35 lo -> ../../devices/virtual/net/lo
lrwxrwxrwx 1 root root 0 Aug 11 17:35 wlan0 -> ../../devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0/net/wlan0
lrwxrwxrwx 1 root root 0 Aug 11 17:35 wlan1 -> ../../devices/platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/net/wlan1

→ wlan0 はUSB側のデバイスにリンクされており、wlan1 はそうでないことが分かる。 udevadm info コマンドを使うとデバイス管理を行っている udev (後述) 経由でインターフェイス名やデバイスツリー上の位置など詳細な情報を確認できる。

$ sudo udevadm info /sys/class/net/wlan0
P: /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0/net/wlan0
L: 0
E: DEVPATH=/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0/net/wlan0
E: DEVTYPE=wlan
E: INTERFACE=wlan0
E: IFINDEX=3
E: SUBSYSTEM=net
E: USEC_INITIALIZED=5887341
E: ID_NET_NAMING_SCHEME=v240
E: ID_NET_NAME_MAC=wlx04ab18880904
E: ID_OUI_FROM_DATABASE=ELECOM CO.,LTD.
E: ID_NET_NAME_PATH=wlp1s0u1u4
E: ID_VENDOR=ELECOM
E: ID_VENDOR_ENC=ELECOM\x0d\x0d
E: ID_VENDOR_ID=056e
E: ID_MODEL=WDC-150SU2M
E: ID_MODEL_ENC=WDC-150SU2M
E: ID_MODEL_ID=4008
E: ID_REVISION=0000
E: ID_SERIAL=ELECOM_WDC-150SU2M
E: ID_TYPE=generic
E: ID_BUS=usb
E: ID_USB_INTERFACES=:ffffff:
E: ID_USB_INTERFACE_NUM=00
E: ID_USB_DRIVER=r8188eu
E: ID_VENDOR_FROM_DATABASE=Elecom Co., Ltd
E: ID_PATH=platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.4:1.0
E: ID_PATH_TAG=platform-fd500000_pcie-pci-0000_01_00_0-usb-0_1_4_1_0
E: ID_NET_DRIVER=r8188eu
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/wlan0
E: TAGS=:systemd:

$ sudo udevadm info /sys/class/net/wlan1
P: /devices/platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/net/wlan1
L: 0
E: DEVPATH=/devices/platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/net/wlan1
E: DEVTYPE=wlan
E: INTERFACE=wlan1
E: IFINDEX=4
E: SUBSYSTEM=net
E: USEC_INITIALIZED=5882817
E: ID_NET_NAMING_SCHEME=v240
E: ID_NET_NAME_MAC=wlxe45f0131e9a9
E: ID_PATH=platform-fe300000.mmcnr
E: ID_PATH_TAG=platform-fe300000_mmcnr
E: ID_NET_DRIVER=brcmfmac
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/wlan1
E: TAGS=:systemd:

→ wlan0 がUSBに装着された無線LAN dongle であり、wlan1 の方がオンボードの内蔵無線LANであることを確認できた。

デバイス管理を行う udev では、ルールファイルを作成することでMACアドレスに対してインターフェイス名を固定することができる。 udevadm info -a [devpath] を使うことでルールファイルで参照するMACアドレスを確認できる。

$ sudo udevadm info -a /sys/class/net/wlan0
(...)
  looking at device '/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0/net/wlan0':
    KERNEL=="wlan0"
    SUBSYSTEM=="net"
(...)
    ATTR{address}=="04:ab:18:88:09:04"
(...)

$ sudo udevadm info -a /sys/class/net/wlan1
(...)
  looking at device '/devices/platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/net/wlan1':
    KERNEL=="wlan1"
    SUBSYSTEM=="net"
(...)
    ATTR{address}=="e4:5f:01:31:e9:a9"
(...)

ATTR{address} がMACアドレスであり、 ip a の結果とも一致する。 以下に ip a の結果を再掲する。

$ ip a
(...)
3: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group default qlen 1000
    link/ether 04:ab:18:88:09:04 brd ff:ff:ff:ff:ff:ff
4: wlan1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether e4:5f:01:31:e9:a9 brd ff:ff:ff:ff:ff:ff
(...)

以上でMACアドレスに対応する無線LANデバイスを特定できた。 続けて udev のルールファイルを作成し、内蔵無線LANを "wlan0", 無線LAN USB dongle を "wlan1" に固定する。

sudo vi /etc/udev/rules.d/72-static-name.rules を以下の内容で作成する。

ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="(内蔵無線LAN のMACアドレス)",       NAME="wlan0"
ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="(無線LAN USB dongleのMACアドレス)", NAME="wlan1"

sudo reboot で再起動後に ip a/sys/class/net 配下を確認する。

$ ip a
(...)
3: wlan1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group default qlen 1000
    link/ether 04:ab:18:88:09:04 brd ff:ff:ff:ff:ff:ff
(...)
4: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether e4:5f:01:31:e9:a9 brd ff:ff:ff:ff:ff:ff
(...)

$ ls -l /sys/class/net/
total 0
lrwxrwxrwx 1 root root 0 Aug 11 18:13 eth0 -> ../../devices/platform/scb/fd580000.ethernet/net/eth0
lrwxrwxrwx 1 root root 0 Aug 11 18:13 lo -> ../../devices/virtual/net/lo
lrwxrwxrwx 1 root root 0 Aug 11 18:13 wlan0 -> ../../devices/platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/net/wlan0
lrwxrwxrwx 1 root root 0 Aug 11 18:13 wlan1 -> ../../devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0/net/wlan1

MACアドレスおよび /sys/cass/net のリンク先から、内蔵無線LANが wlan0、無線LAN USB dongle が wlan1 で固定されたことを確認できた。

2-3. wlan0 でSSIDに接続して固定IPを設定

筆者環境での初期セットアップでは /etc/wpa_supplicant/wpa_supplicant.conf を編集し wlan0 で自宅のSSIDに接続していた。 WiFiAP化する時にSSID設定をコメントアウトしていたので、それを戻す。

pi@raspberrypi:~ $ sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
-> コメントアウトしていた network={...} を戻す。
------------------------------------------------------------------
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=JP

# wpa_passphrase <ssid> で生成された内容
network={
        ssid="(SSID)"
        psk=...
        # 筆者の自宅SSIDはステルス化しているため、以下を指定
        scan_ssid=1
}
------------------------------------------------------------------

このままだと wlan0, wlan1 の両方で上記SSIDが参照される。 今回は wlan0 のみ上記SSIDに接続したい。

もともと前回の WiFi AP 化では /etc/dhcpcd.confnohook wpa_supplicant を含めていた。 これは wlan0 をWiFi APにするため、デフォルトの wpa_supplicant によるSSID接続設定を無効化する意図と思われる。

dhcpcd が wpa_supplicant を呼び出すhook処理は /lib/dhcpcd/dhcpcd-hooks/10-wpa_supplicant にある。 このスクリプトを確認すると、 /etc/wpa_supplicant/wpa_supplicant-(インターフェイス名).conf という設定ファイル名を使うことでインターフェイス毎にSSID設定を分けられることが分かる。 また、デフォルトの /etc/wpa_supplicant/wpa_supplicant.conf が無ければ wpa_supplicant の設定も反映されない = WiFi接続がされないことも読み取れる。

そこで以下のようなアプローチにより wlan0 は wpa_supplicant で WiFi 接続しつつ、あとで追加する wlan1 についてはその対象外とすることを試みる。

  1. /etc/dhcpcd.conf から nohook wpa_supplicant をコメントアウトし、wpa_supplicant を動作させる。
  2. /etc/wpa_supplicant/wpa_supplicant.conf/etc/wpa_supplicant/wpa_supplicant-wlan0.conf にリネームし、wlan0 でのみ wpa_supplicant によるWiFi設定を可能とする。
    • /etc/wpa_supplicant/wpa_supplicant.conf は作らない。これにより、wlan1 で wpa_supplicant の hook が呼び出された時、対応する設定ファイルが見つからない状態となってWiFi接続はスキップされる。

上記により、まず /etc/dhcpcd.conf から nohook wpa_supplicant をコメントアウトして一旦 eth0 の静的IPアドレス設定のみに戻す。

$ sudo vi /etc/dhcpcd.conf

(...)

interface eth0
static ip_address=(設定済みの固定IPアドレス)/24
static routers=(自宅のインターネットルータ)
static domain_name_servers=(自宅のインターネットルータ) 8.8.8.8

# 以下をコメントアウト or 削除
#interface wlan0
#static ip_address=192.168.4.1/24
#nohook wpa_supplicant

さらに wpa_supplicant の設定ファイルを以下の通り wlan0 専用にする。

$ sudo mv /etc/wpa_supplicant/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant-wlan0.conf

sudo reboot で再起動して WiFi 接続に成功するか確認してみる:

[1. journalctl で dhcpcd のログを確認]
$ sudo journalctl -u dhcpcd | grep wlan0
Aug 11 18:17:25 raspberrypi dhcpcd-run-hooks[505]: wlan0: starting wpa_supplicant
Aug 11 18:17:25 raspberrypi dhcpcd[459]: wlan0: connected to Access Point `'
Aug 11 18:17:25 raspberrypi dhcpcd[459]: wlan0: waiting for carrier
Aug 11 18:17:31 raspberrypi dhcpcd[459]: wlan0: carrier acquired
Aug 11 18:17:31 raspberrypi dhcpcd[459]: wlan0: connected to Access Point `(SSID)'
(...)
Aug 11 18:17:36 raspberrypi dhcpcd[459]: wlan0: leased (...) for 3600 seconds
Aug 11 18:17:36 raspberrypi dhcpcd[459]: wlan0: adding route to (...)/24
Aug 11 18:17:36 raspberrypi dhcpcd[459]: wlan0: adding default route via (...)
(...)

[2. ipコマンドでIPアドレスの設定状況を確認]
$ ip a
(...)
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether e4:5f:01:31:e9:a9 brd ff:ff:ff:ff:ff:ff
    inet (...)/24 brd (...) scope global dynamic noprefixroute wlan0
       valid_lft 2377sec preferred_lft 1927sec
(...)

→ wlan0 で自宅のWiFi APに接続できた。

続いて wlan0 に固定IPアドレスを設定する。 既に eth0 に固定IPアドレスを設定している状態なので、以下のように wlan0 についての固定IPを追加する。

$ sudo vi /etc/dhcpcd.conf

(...)

interface eth0
static ip_address=(設定済みの固定IPアドレス)/24

interface wlan0
static ip_address=(新規追加の固定IPアドレス)/24

static routers=(自宅のインターネットルータ)
static domain_name_servers=(自宅のインターネットルータ) 8.8.8.8

もう一度 sudo reboot で再起動して、 ip コマンドで確認:

pi@raspberrypi:~ $ ip a
(...)
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether e4:5f:01:31:e9:a8 brd ff:ff:ff:ff:ff:ff
    inet (既に設定済みの固定IP)/24 brd (...) scope global noprefixroute eth0
(...)
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether e4:5f:01:31:e9:a9 brd ff:ff:ff:ff:ff:ff
    inet (新規に設定した固定IP)/24 brd (...) scope global noprefixroute wlan0
(...)

→ 無事 wlan0 に固定IPアドレスを設定できた。 実際に wlan0 側に設定したIPアドレスにPCからSSH接続し、ログインできることも確認した。

いよいよ無線LAN USB dongle を wlan1 として追加し、WiFi AP 化する。

2-4. 無線LAN USB dongle の追加とインターフェイス名の固定

ここまでの説明では、実は無線LAN USB dongle をUSBに挿入して認識済みの状態だった。

初めて挿入した時にMACアドレスとインターフェイス名を確認する流れは以下の通り:

[1. lsusb でUSBデバイスの認識状態を確認]
$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 056e:4008 Elecom Co., Ltd
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ←デバイスとして認識成功
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

[2. dmesg でドライバのロード状況を確認]
$ dmesg
...
[373098.909982] usb 1-1.4: new high-speed USB device number 3 using xhci_hcd
[373099.041384] usb 1-1.4: New USB device found, idVendor=056e, idProduct=4008, bcdDevice= 0.00
[373099.041406] usb 1-1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[373099.041425] usb 1-1.4: Product: WDC-150SU2M
[373099.041442] usb 1-1.4: Manufacturer: ELECOM
[373099.117076] lib80211: common routines for IEEE802.11 drivers
[373099.117093] lib80211_crypt: registered algorithm 'NULL'
[373099.133321] r8188eu: module is from the staging directory, the quality is unknown, you have been warned.
[373099.150423] Chip Version Info: CHIP_8188E_Normal_Chip_TSMC_D_CUT_1T1R_RomVer(0)
[373099.176348] usbcore: registered new interface driver r8188eu
[373099.810161] MAC Address = 04:ab:18:88:09:04
→ ラズパイのkernelに含まれているデフォルトのドライバがロードされ、MACアドレス取得成功

[3. ip コマンドからインターフェイス名を確認]
$ ip a
(...)
4: wlan1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group default qlen 1000
    link/ether 04:ab:18:88:09:04 brd ff:ff:ff:ff:ff:ff
→ wlan1 として認識された。

[4. sysfs からインターフェイス名で検索]
$ sudo find /sys | grep wlan1
(...)
/sys/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0/net/wlan1
(...)
/sys/class/net/wlan1

→ sysfs 上のパスを確認できた。

[4. udevadm info で udev のルールで参照できる属性一覧を取得]
$ sudo udevadm info -a /sys/class/net/wlan1
(...)

  looking at device '/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0/net/wlan1':
    KERNEL=="wlan1"
    SUBSYSTEM=="net"
(...)
    ATTR{address}=="04:ab:18:88:09:04"
(...)

→ udev用ルールファイルで指定するパラメータ(MACアドレス)を入手できた。

上記の通り、初めてUSBに挿入した時点では wlan1 として認識される。 再起動後はデバイスの検出順序の都合で wlan0 として認識されてしまい、内蔵無線LANが wlan1 にずれてしまった。

→ 既に説明の通り /etc/udev/rules.d/72-static-name.rules でMACアドレスに対してインターフェイス名を固定し、ずれることが無いようにした。

ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="(内蔵無線LAN のMACアドレス)",       NAME="wlan0"
ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="(USB 無線LAN dongleのMACアドレス)", NAME="wlan1"

2-5. WDC-150SU2MBK のドライバ, RTL8188EUS のインストール

そのままでもインターフェイス名までは認識できたが、後述のようにhostapd等でWiFi AP化を進めると以下のようなエラーが発生してしまった。

$ sudo journalctl -u hostapd
-- Logs begin at Wed 2021-08-11 21:31:38 JST, end at Wed 2021-08-11 21:33:15 JST. --
Aug 11 21:31:43 raspberrypi systemd[1]: Starting Advanced IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator...
Aug 11 21:31:43 raspberrypi hostapd[511]: Configuration file: /etc/hostapd/hostapd.conf
Aug 11 21:31:43 raspberrypi hostapd[511]: nl80211: Could not configure driver mode
Aug 11 21:31:43 raspberrypi hostapd[511]: nl80211: deinit ifname=wlan1 disabled_11b_rates=0
Aug 11 21:31:43 raspberrypi hostapd[511]: nl80211 driver initialization failed.
Aug 11 21:31:43 raspberrypi hostapd[511]: wlan1: interface state UNINITIALIZED->DISABLED
Aug 11 21:31:43 raspberrypi hostapd[511]: wlan1: AP-DISABLED
Aug 11 21:31:43 raspberrypi hostapd[511]: wlan1: CTRL-EVENT-TERMINATING
Aug 11 21:31:43 raspberrypi hostapd[511]: hostapd_free_hapd_data: Interface wlan1 wasn't started
Aug 11 21:31:43 raspberrypi systemd[1]: hostapd.service: Control process exited, code=exited, status=1/FAILURE
Aug 11 21:31:43 raspberrypi systemd[1]: hostapd.service: Failed with result 'exit-code'.
Aug 11 21:31:43 raspberrypi systemd[1]: Failed to start Advanced IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator.
Aug 11 21:31:45 raspberrypi systemd[1]: hostapd.service: Service RestartSec=2s expired, scheduling restart.
Aug 11 21:31:45 raspberrypi systemd[1]: hostapd.service: Scheduled restart job, restart counter is at 1.
Aug 11 21:31:45 raspberrypi systemd[1]: Stopped Advanced IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator.
Aug 11 21:31:45 raspberrypi systemd[1]: Starting Advanced IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator...

これを解決するには、以下の記事を参考に RTL8188EUS のドライバをインストールする。

まずラズパイで動いている linux kernel のバージョンを確認する。

$ uname -a
Linux raspberrypi 5.10.17-v7l+ #1421 SMP Thu May 27 14:00:13 BST 2021 armv7l GNU/Linux

続いて以下のサイトから対応する tar.gz ファイルを探す。

→ 今回は 8188eu-5.10.17-v7l-1421.tar.gz がちょうど uname -a の結果と一致するので、それをDLして展開し、 install.sh を実行する。

$ wget http://downloads.fars-robotics.net/wifi-drivers/8188eu-drivers/8188eu-5.10.17-v7l-1421.tar.gz

$ tar zxf 8188eu-5.10.17-v7l-1421.tar.gz

$ ./install.sh
sudo install -p -m 644 8188eu.ko /lib/modules/5.10.17-v7l+/kernel/drivers/net/wireless
sudo depmod 5.10.17-v7l+

Reboot to run the driver.

If you have already configured your wifi it should start up and connect to your
wireless network.

If you have not configured your wifi you will need to do that to enable the wifi.

sudo reboot で再起動し、 ip a コマンドでもう一度インターフェイス名とMACアドレスの対応を確認し、ズレが無いことを確認。

$ ip a
(...)
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether e4:5f:01:31:e9:a9 brd ff:ff:ff:ff:ff:ff
    → 内蔵無線LANのMACアドレスでOK.
(...)
4: wlan1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 04:ab:18:88:09:04 brd ff:ff:ff:ff:ff:ff
    → 無線LAN USB dongle のMACアドレスでOK.

正常に認識できると WDC-150SU2MBK のdongleのLEDが青色に点滅する。

2-6. 無線LAN USB dongle のWiFi AP化

これで準備が整ったので、WiFi AP化する。 (IPアドレスなどは適宜自分の好みに読み替えること。)

[1. dhcpcdで WiFi AP となる wlan1 に固定IPアドレスを設定]

$ sudo vi /etc/dhcpcd.conf
-----------------------------------------
(...)
interface eth0
static ip_address=(自宅NW向け有線LAN用固定IP)/24

interface wlan0
static ip_address=(自宅NW向け無線LAN用固定IP)/24

static routers=(自宅NWのルータIP)
static domain_name_servers=(自宅NWのルータIP) 8.8.8.8

# 以下を追記 : wlan1 に固定IPアドレスを設定
interface wlan1
static ip_address=192.168.4.1/24
-----------------------------------------

[2. iptables で wlan0 側にマスカレード設定]

$ sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE

$ sudo netfilter-persistent save

[3. dnsmasq で WiFi AP 側のIPアドレスレンジとDHCPを設定]

$ sudo vi /etc/dnsmasq.conf
-----------------------------------------
interface=wlan1 # Listening interface
dhcp-range=192.168.4.2,192.168.4.20,255.255.255.0,24h
                # Pool of IP addresses served via DHCP
domain=wlan     # Local wireless DNS domain
address=/gw.wlan/192.168.4.1
                # Alias for this router
-----------------------------------------

$ sudo systemctl enable dnsmasq

[4. hostapd で wlan1 のWiFi AP設定]

$ sudo vi /etc/hostapd/hostapd.conf
-----------------------------------------
country_code=JP
interface=wlan1
ssid=(SSID)
hw_mode=g
channel=7
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=(パスワード)
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
-----------------------------------------

$ sudo systemctl unmask hostapd
$ sudo systemctl enable hostapd

[5. 再起動]
$ sudo reboot

再起動後にスマホ等から wlan1 に設定したSSID/パスワードで接続し、ブラウザなどでインターネットに接続できることを確認する。

有線LANケーブルを接続した状態であれば、この段階でケーブルを外すことを推奨。

eth0とwlan0両方が自宅NWに接続した状態だと、iptablesの設定かmetricsの関係か不明だが、WiFiAPに接続したスマホから自宅NW内のIPに接続できない現象を確認した。 有線LANケーブルを外してwlan0一本だけにしたところ解消したので、この段階で有線LANケーブルを外すか、eth0をdownさせておいた方が良さそう。

2-7. tcp:80/443を Invisible(透過型) Proxy に転送

PC側で Burp を起動し、以下のように3つの Proxy Listener を設定する。

  1. tcp:8080 に bind した Proxy Listenr
    • "Bind to address" を "All interfaces" にして、スマホから http://(Burpを動かすPCのIPv4):8080 にアクセスできるようにする。これは Burp のルートCA証明書のダウンロードに使う。
  2. tcp:80 に bind したProxy Listener
    • "Bind to address" を "All interfaces" にする。
    • "Request handling" から "Support invisible proxying" にチェックを入れる。
  3. tcp:443 に bind したProxy Listener
    • "Bind to address" を "All interfaces" にする。
    • "Request handling" から "Support invisible proxying" にチェックを入れる。
    • "TLS Protocols" から "Use custom protocols" にチェックを入れ、TLSv1.3 のチェックを外す。

さらに以下も設定する。

  • "Proxy" -> "Options" -> "TLS Pass Through" の "Automatically add entries on client TLS negotiation failure" にチェックを入れる。
  • "Project options" タブ → "HTTP/2" の "Enable HTTP/2" のチェックを外す。

ここまで準備できたら、wlan1のWiFi APに接続したスマホから http://(Burpを動かすPCのIPv4):8080 にアクセスしてBurpのCA証明書がDLできるか確認する。 CA証明書をDLできたら、信頼できる証明書としてスマホにインストールしておく。

アクセスできない場合:

  • iptablesや各種サービスの設定を見直す。
  • 有線LANケーブルが接続された状態であれば、外す。
  • 再起動してみる。

この時点でスマホから WiFi AP 経由でBurpまで到達可能であることを確認できた。 最後に tcp:80/443 を Burp のInvisible(透過型) Proxyに転送する。

$ sudo iptables -t nat -A PREROUTING -i wlan1 -p tcp --dport 80  -j DNAT --to-destination (Burpを動かすPCのIPv4):80
$ sudo iptables -t nat -A PREROUTING -i wlan1 -p tcp --dport 443 -j DNAT --to-destination (Burpを動かすPCのIPv4):443

(設定を保存するなら
$ sudo netfilter-persistent save

iptablesの設定後、スマホ上のブラウザやアプリからアクセスしてみて、Burp Proxy でHTTP(S)通信をキャプチャできるか確認する。

3. まとめ

本記事では Raspberry Pi 4 のUSBポートに無線LAN USB dongle を挿入し、有線LANが無い状態でもWiFi AP として動作させることに挑戦し、成功した。

自宅NWの都合で有線LANを使えない場合でも、これにより脆弱性診断等でHTTP(S)通信を Burp の Invisible(透過型) Proxy でキャプチャすることが可能になる。

設定のポイント(再掲):

  1. wlan0, wlan1 を正しく認識させること。
    1. 動作実績のある無線LAN USB dongle を使う。
      • 今回は WDC-150SU2MBK を使用した。
    2. 無線LAN USB dongle にあわせたデバイスドライバをインストールする。
      • kernel組み込みのドライバでもインターフェイス名までは認識できるが、動作しないことがある。
    3. MACアドレスでインターフェイス名を固定する。
      • 再起動後にズレることがあるのでそれを防止する。
  2. wlan0 でのみ自宅NWにWiFi接続すること。
    • IPアドレスの設定は /etc/dhcpcd.conf, wlan0のWiFi 接続は /etc/wpa_supplicant/wpa_supplicant-wlan0.conf で行う。
  3. wlan1 を WiFi APとして動作させるのは、ほぼ公式ドキュメントの通り。
    • 公式ドキュメントの手順で "wlan0" となっている箇所を "wlan1" に入れ替えるだけでほぼOK。

4. 余談-1: ネットワークインターフェイス名について

本記事ではネットワークインターフェイス名がズレるのを防ぐため、MACアドレスとインターフェイス名を紐付けるようにしている。

これに関して、筆者は以下のような疑問を持った。

  • そもそも "eth0", "eth1", "wlan0", "wlan1", ... というインターフェイス名は誰が決定しているのか。
  • インターフェイス名はなぜズレるのか。
  • インターフェイス名を固定するには他にどのような方法があるのか。
    • 言い換えると、なぜ今回のケースでは /etc/udev/rules.d/72-static-name.rules に udev 用のルールを書く必要があり、それ以外の方法は使わなかったのか。

これに関して丸半日~一日ほど udev や systemd 関連のmanページをさまようこととなり、以下は筆者なりに噛み砕き、整理したものとなる。 筆者も Linux のブートプロセスやkernel、udev/systemd の仕組みに詳しいわけではないため、間違いが含まれている可能性もある。 それを公開するべきか悩むところもあったが、今の知識で精一杯信頼性高めるよう努めたつもりではあるので、誰かの役に立つことを祈って公開する。 (間違ってるところがあれば、TwitterのDM等でこっそり教えてください)

Linux のデバイス管理のイロハ

linuxではデバイスをメジャー番号とマイナー番号で分類し、 /dev/ 以下のデバイスノード (特殊なファイルとして見える) を通して読み書きする。 /dev/ 以下のデバイスノードには character device と block device がある。 character device は1文字ずつ読み書きするデバイスで、例として端末デバイス(tty)がある。 block device はブロック単位で読み書きするデバイスで、例としてストレージがある。

実際にラズパイに接続されている block device を lsblk コマンドで確認し、対応するデバイスノードを ls すると以下のようになる。

pi@raspberrypi:~ $ lsblk
NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
mmcblk0     179:0    0 59.5G  0 disk
├─mmcblk0p1 179:1    0  2.6G  0 part
├─mmcblk0p2 179:2    0    1K  0 part
├─mmcblk0p5 179:5    0   32M  0 part
├─mmcblk0p6 179:6    0  256M  0 part /boot
└─mmcblk0p7 179:7    0 56.6G  0 part /

pi@raspberrypi:~ $ ls -l /dev/mmcblk0*
brw-rw---- 1 root disk 179, 0 Jul 30 17:36 /dev/mmcblk0
brw-rw---- 1 root disk 179, 1 Jul 30 17:36 /dev/mmcblk0p1
brw-rw---- 1 root disk 179, 2 Jul 30 17:36 /dev/mmcblk0p2
brw-rw---- 1 root disk 179, 5 Jul 30 17:36 /dev/mmcblk0p5
brw-rw---- 1 root disk 179, 6 Jul 30 17:36 /dev/mmcblk0p6
brw-rw---- 1 root disk 179, 7 Jul 30 17:36 /dev/mmcblk0p7

端末デバイス(tty)のデバイスノード一覧を表示してみる:

pi@raspberrypi:~ $ ls -l /dev/tty*
crw-rw-rw- 1 root tty       5,  0 Jul 30 17:36 /dev/tty
crw--w---- 1 root tty       4,  0 Jul 30 17:36 /dev/tty0
crw------- 1 pi   tty       4,  1 Jul 30 17:36 /dev/tty1
(...)
crw--w---- 1 root tty       4,  2 Jul 30 17:36 /dev/tty2
(...)
crw--w---- 1 root tty       4,  3 Jul 30 17:36 /dev/tty3

ところが、ネットワーク設定で使う "eth0", "eth1", "wlan0", "wlan1" やそれに対応するようなデバイスノードは /dev/ 以下に存在しない。 ネットワークインターフェイスはどのように管理されているのか?

ネットワークインターフェイス用のデバイスの特殊性

ネットワークインターフェイス用のデバイスは、/dev/ 以下にある端末やブロックデバイスと以下の2点が大きく異なってくる。

  1. open(2) を使わず socket(2) を使って開く。その時も、インターフェイス名を直接指定することはない。
  2. ioctl(2) 等のシステムコールで制御する。

上記の特殊性と歴史的な事情により、ネットワークインターフェイス用のデバイスは /dev/ 以下には存在しない。 デバイス情報は kernel 内部に存在し、 ioctl(2) 等によりアクセスされてきたものと思われる。

kernel 2.6 から kernel 内の情報が sysfs (/sys) で提供されるようになった。 ネットワークインターフェイスに関する情報も /sys 以下を参照することで確認できる。

ラズパイの場合、ネットワークインターフェイス名は /sys/class/net 以下で確認できる。 実際は以下の通り /sys/devices/ への symlink となっていた。

pi@raspberrypi:~ $ ls -l /sys/class/net
total 0
lrwxrwxrwx 1 root root 0 Jul 30 17:36 eth0 -> ../../devices/platform/scb/fd580000.ethernet/net/eth0
lrwxrwxrwx 1 root root 0 Jul 30 17:36 lo -> ../../devices/virtual/net/lo
lrwxrwxrwx 1 root root 0 Jul 30 17:36 wlan0 -> ../../devices/platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/net/wlan0
lrwxrwxrwx 1 root root 0 Aug 10 17:57 wlan1 -> ../../devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0/net/wlan1

参考:

sysfs と udev

昔の Linux では /dev/ 以下のデバイスノードを mknod(1) コマンドで手動で作成していた。 さまざまなデバイスの登場と、Linux の発展に伴いデバイスノードを人間が管理するのは難しくなった。 そこで開発されたのが、kernel 内部の情報やイベントに応じて自動でデバイスノードを作成する仕組みである。 現在主流となっているのは udev という仕組みになる。

udev はユーザー空間で動くプロセスとして動作し、kernel 2.6.13 で導入された uevent からのイベントと sysfs からの情報を待つ。 情報が来たら、udev 独自のルールファイル (rules ファイル) に基づいてデバイスノードの作成等の処理を行う。

現在のudevは systemd(1) に含まれており、ラズパイでは systemd-udevd.service(8) として組み込まれている。 システムがデフォルトで使用するルールファイルは /lib/udev/rules.d/ 以下にある。

pi@raspberrypi:~ $ ls -l /lib/udev/rules.d/
total 608
-rw-r--r-- 1 root root    148 May 27 23:14 10-local-rpi.rules
-rw-r--r-- 1 root root    137 Nov 10  2017 15-i2c-modprobe.rules
(...)
-rw-r--r-- 1 root root   1518 Jul 29  2020 97-hid2hci.rules
-rw-r--r-- 1 root root   4363 Apr  1 21:57 99-systemd.rules

なおラズパイにおいては /usr/lib/udev/rules.d/ もあり、全く同じ内容が含まれているように見える。 これは /lib/udev/usr/lib/udev が hard link で同じi-nodeを指しているためであり、i-nodeを表示する ls -i オプションで確認することができる。

pi@raspberrypi:~ $ ls -lid /lib/udev /usr/lib/udev
3020544 drwxr-xr-x 4 root root 4096 May  7 23:52 /lib/udev
3020544 drwxr-xr-x 4 root root 4096 May  7 23:52 /usr/lib/udev

→ 同じ i-node であることが分かる。

パッケージとして管理しているのは /lib/udev の方らしく、ファイル名からそれを管理しているパッケージを逆引きする dpkg -S によると以下の通り /lib/udev はパッケージが表示されるものの、 /usr/lib/udev の方はパッケージが見つからない。

pi@raspberrypi:~ $ dpkg -S /lib/udev/
(...), udev, (...), systemd, (...), raspberrypi-sys-mods: /lib/udev

pi@raspberrypi:~ $ dpkg -S /usr/lib/udev/
dpkg-query: no path found matching pattern /usr/lib/udev/

パッケージ構成としては正式なディレクトリが /ilb/udev のようなので、以降はそちらで参照することとする。

参考:

udev におけるネットワークインターフェイス名の命名規則

今回のラズパイのOS, Raspbian (32bit) は Debian Buster (10.10) ベースであり、systemd に含まれる udev がデバイスを管理している。 udev とネットワークインターフェイス名の関係について調べると、以下の参考資料が見つかった。

上記参考資料の中では [1] の資料が歴史についてまとまっており、簡単にまとめてみる。

  1. 初期の簡単な命名規則(THE ORIGINAL SIMPLE SCHEME)
    • kernel やドライバにより検出された "eth0", "eth1", "wlan0", "wlan1", ... という名前が使われていた。
    • ブート時に検出順序が変わると名前がずれてしまうなどの問題があった。
    • ブート時の処理が複雑化し、またUSB LAN変換ケーブルやUSB WiFiドングルの登場等でホットプラグも広まった影響で、この問題がより顕在化した。
  2. 永続化した命名規則(THE "PERSISTENT NAMES" SCHEME)
    • udev のルールファイルを使って、MACアドレスに対して常に同じインターフェイス名を設定するようにした。
    • 例えば /etc/udev/rules.d/70-persistent-net.rules に対して、新しいネットワークインターフェイスを検出する都度、以下のようなルールを追加する。これにより、同じMACアドレスのデバイスに対して常に同じインターフェイス名を割り当てられる。
      • SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="01:23:45:67:89:ab", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"
    • ただしこれについても微妙な競合状態の問題があったり、 "/etc" が書き込み可能である必要がある。仮想化関連の問題もあったらしく、次の命名規則に移行することとなった。
  3. 予測可能な命名規則(THE "PREDICTABLE NAMES" SCHEME)
    • systemd v197 から導入されたもので、BIOSやファームウェア情報、ホットプラグで挿された物理的な位置を元に、予測可能なインターフェイス名を生成する。
    • eno1, wlp1s3 などこれまでの命名規則とはがらりと変わる。
    • 実際の命名規則については上記ドキュメントの以下のセクションを参照。
      1. [2] の "What precisely has changed in v197?" セクション
      2. [3] の資料全体

総合的には以下の資料が日本語でよく整理されている印象で、参考になった。

udev の rules ファイルでも、過去の設定の互換性を考慮して複数のルールでインターフェイス名を設定できるようになっているらしい。

資料[2] によると「予測可能な命名規則」(Predictable Names) を使いたくない場合は以下のいずれかをしておく。

  1. ln -s /dev/null /etc/systemd/network/99-default.link
  2. /etc/systemd/network/ 以下に systemd.link(5) に基づく .link ファイルを設定し、手動で命名規則を組み込む。
  3. kernel のコマンドラインパラメータに net.ifnames=0 を設定する。

Red Hat Enterprise Linux 7 (おそらく8系も) では、kernel のコマンドラインパラメータに biosdevname=0 も追加する。

ラズパイにおけるネットワークインターフェイス名の命名規則

ラズパイでネットワークインターフェイス名を確認すると "eth0", "wlan0" のように古い命名規則が使われていることが分かる。

pi@raspberrypi:~ $ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
(...)
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
(...)
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
(...)

pi@raspberrypi:~ $ ls -l /sys/class/net
total 0
lrwxrwxrwx 1 root root 0 Jul 30 17:36 eth0 -> ../../devices/platform/scb/fd580000.ethernet/net/eth0
lrwxrwxrwx 1 root root 0 Jul 30 17:36 lo -> ../../devices/virtual/net/lo
lrwxrwxrwx 1 root root 0 Jul 30 17:36 wlan0 -> ../../devices/platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/net/wlan0
lrwxrwxrwx 1 root root 0 Aug 10 17:57 wlan1 -> ../../devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0/net/wlan1

予測可能な命名規則 (Predictable Names) を無効化する設定のうち、どれが採用されているか確認する。

[1. /etc/systemd/network/99-default.link]
pi@raspberrypi:~ $ ls -l /etc/systemd/network/99-default.link
lrwxrwxrwx 1 root root 9 May  7 23:42 /etc/systemd/network/99-default.link -> /dev/null

[2. /etc/systemd/network/ 以下の .link ファイル]
pi@raspberrypi:~ $ ls -l /etc/systemd/network/
total 0
lrwxrwxrwx 1 root root 9 May  7 23:42 99-default.link -> /dev/null

[3. kernel のコマンドラインパラメータに net.ifnames=0 はあるか]
pi@raspberrypi:~ $ cat /proc/cmdline
coherent_pool=1M 8250.nr_uarts=0 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 \
video=HDMI-A-1:640x480M@60 smsc95xx.macaddr=E4:5F:01:31:E9:A8 vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000  \
console=ttyS0,115200 console=tty1 root=/dev/mmcblk0p7 rootfstype=ext4 elevator=deadline \
fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles

/etc/systemd/network/99-default.link/dev/null のシンボリックリンクとなっており、これにより予測可能な命名規則を使っていないことを確認できた。

udevadm(8) による udev ルール適用の確認

ラズパイのネットワークインターフェイスにおいて、実際にどのように udev ルールが適用されるか udevadm(8) の udevadm test [devpath] を使って確認してみる。

まず eth0 について確認してみる。 eth0 については本記事で設定した /etc/udev/rules.d/72-static-name.rules に含めておらず、デフォルトの udev ルールに従う。

pi@raspberrypi:~ $ sudo udevadm test /sys/class/net/eth0
This program is for debugging only, it does not run any program
specified by a RUN key. It may show incorrect results, because
some values may be different, or not available at a simulation run.

Load module index
Skipping empty file: /etc/systemd/network/99-default.link
Created link configuration context.
Reading rules file: /usr/lib/udev/rules.d/10-local-rpi.rules
(...)
Reading rules file: /usr/lib/udev/rules.d/97-hid2hci.rules
Reading rules file: /etc/udev/rules.d/99-com.rules
Reading rules file: /usr/lib/udev/rules.d/99-systemd.rules
Rules contain 393216 bytes tokens (32768 * 12 bytes), 29725 bytes strings
21032 strings (170910 bytes), 18283 de-duplicated (143935 bytes), 2750 trie nodes used
Using default interface naming scheme 'v240'.
DEVPATH=/devices/platform/scb/fd580000.ethernet/net/eth0
INTERFACE=eth0
IFINDEX=2
ACTION=add
SUBSYSTEM=net
ID_NET_NAMING_SCHEME=v240
ID_NET_NAME_MAC=enxe45f0131e9a8
ID_PATH=platform-fd580000.ethernet
ID_PATH_TAG=platform-fd580000_ethernet
ID_NET_DRIVER=bcmgenet
TAGS=:systemd:
SYSTEMD_ALIAS=/sys/subsystem/net/devices/eth0
USEC_INITIALIZED=4452402
run: 'ifupdown-hotplug'
run: '/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/eth0 --prefix=/net/ipv4/neigh/eth0 --prefix=/net/ipv6/conf/eth0 --prefix=/net/ipv6/neigh/eth0'
Unload module index
Unloaded link configuration context.

/usr/lib/udev/rules.d/ 以下のルールファイル、およびラズパイに既に含まれていた /etc/udev/rules.d/99-com.rules を見ていることが分かる。

続いて wlan0 について見てみる。 wlan0 は本記事で /etc/udev/rules.d/72-static-name.rules の設定により、MACアドレスとインターフェイス名を対応付けしている。

pi@raspberrypi:~ $ sudo udevadm test /sys/class/net/wlan0
This program is for debugging only, it does not run any program
specified by a RUN key. It may show incorrect results, because
some values may be different, or not available at a simulation run.

Load module index
Skipping empty file: /etc/systemd/network/99-default.link
Created link configuration context.
Reading rules file: /usr/lib/udev/rules.d/10-local-rpi.rules
(...)
Reading rules file: /etc/udev/rules.d/72-static-name.rules
(...)
Reading rules file: /usr/lib/udev/rules.d/97-hid2hci.rules
Reading rules file: /etc/udev/rules.d/99-com.rules
Reading rules file: /usr/lib/udev/rules.d/99-systemd.rules
Rules contain 393216 bytes tokens (32768 * 12 bytes), 29812 bytes strings
21044 strings (171056 bytes), 18290 de-duplicated (143999 bytes), 2755 trie nodes used
Using default interface naming scheme 'v240'.
DEVPATH=/devices/platform/soc/fe300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/net/wlan0
DEVTYPE=wlan
INTERFACE=wlan0
IFINDEX=4
ACTION=add
SUBSYSTEM=net
ID_NET_NAMING_SCHEME=v240
ID_NET_NAME_MAC=wlxe45f0131e9a9
ID_PATH=platform-fe300000.mmcnr
ID_PATH_TAG=platform-fe300000_mmcnr
ID_NET_DRIVER=brcmfmac
TAGS=:systemd:
SYSTEMD_ALIAS=/sys/subsystem/net/devices/wlan0
USEC_INITIALIZED=6226243
run: 'ifupdown-hotplug'
run: '/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/wlan0 --prefix=/net/ipv4/neigh/wlan0 --prefix=/net/ipv6/conf/wlan0 --prefix=/net/ipv6/neigh/wlan0'
Unload module index
Unloaded link configuration context.

→ 途中で /etc/udev/rules.d/72-static-name.rules を読んでいることが分かる。

起動時の kernel debug メッセージを dmesg コマンドで見てみると、以下のログが出力されていた。

[    5.869292] brcmfmac mmc1:0001:1 rename4: renamed from wlan1
[    5.936694] 8188eu 1-1.4:1.0 wlan1: renamed from wlan0
[    5.998098] brcmfmac mmc1:0001:1 wlan0: renamed from rename4

udev による名前の再設定で、当初 wlan1 だった内蔵無線LANが "rename4" を経由して wlan0 にリネームされ、無線LAN USB dongle の wlan0 が wlan1 にリネームされた様子を確認できる。

ネットワークインターフェイス名のまとめ

ネットワークインターフェイス名に関する当初の疑問について、整理してみた。

  • Q1. そもそも "eth0", "eth1", "wlan0", "wlan1", ... というインターフェイス名は誰が決定しているのか。
    • A1. 昔は検出した順序で kernel やドライバが決定していた。
    • 最近の Linux (少なくとも今回使用した Debian Buster (10.10) ベースの Raspbian) では、systemd と udev の連携により決定している。
      • 最近の systemd + udev では、BIOSやファームウェア情報などを参考にして、検出順序に依らず一貫して予測可能なインターフェイス名となるようになっている。(Predictable Names)
  • Q2. インターフェイス名はなぜズレるのか。
    • A2. USBなどホットプラグ方式で追加するデバイスだと、最初に追加する時はシステム起動後に認識する。再起動すると、追加したポートによっては起動中に認識され、それによりオンボードの他のデバイスとの順番が入れ替わることがある。
  • Q3. インターフェイス名を固定するには他にどのような方法があるのか。
    • A3. udev + systemd の Predictable Names であれば、物理ポートの位置でインターフェイス名が自動で固定される。
    • もし "eth0", "eth1", "wlan0", "wlan1", ... 等の昔のインターフェイス名で、独自のルールで固定したい場合は udev + systemd による Predictable Names を無効化した上で、udev のルールを独自に作成してインターフェイス名を設定する。
    • 今回使用したラズパイの場合、システムデフォルトで最初から Predictable Names が無効化されていた。そのため、MACアドレスに対してインターフェイス名を固定するルールを /etc/udev/rules.d/72-static-name.rules に設定することとなった。

本記事でも設定した /etc/udev/rules.d/72-static-name.rules については、いくつかの stackoverflow や stackexchange のスレッドで「このファイル名で設定するといいよ」と紹介されていたので、それを参考にした。 CentOS6の設定でも似たようなファイル名を触った記憶があり、Predictable Names 導入以前に、複数のLinux ディストリビューションでインターフェイス名のルールを設定していたファイルがこれだった名残り(歴史的経緯)かもしれない。推測となるが、udev のデフォルトのネットワークルールより先になる番号として 72 番あたりが使われているのではなかろうか。 70など、70番台がよく使われている印象がある。

5. 余談-2: 舞台裏メモ

主題とは関係ないため、本文中では書けなかった舞台裏について紹介する。

実際の調査ではすごい遠回りしていた。

インターフェイス名の調査でsystemdとudevに入門し、半日以上時間を持っていかれた。

"Predictable Names" に辿り着きようやく全貌が見えて納得できたものの、今度は eth0 と wlan0 のIPアドレスやWiFi設定についてあれこれ調べることとなり、dhcpcd.conf や wpa_supplicant 周りの設定例について延々とネット上の記事を検索して感触を掴むのに時間を費やしてしまった。

いよいよ設定するか・・・となったが、その時点で既にお試しで無線LAN USB dongle を挿入済みだった。 せっかくインターフェイス名の固定について調べたのに、それを設定するのをど忘れして、以下の2つの勘違いが重なってしまった。

  1. ドライバインストールせずに無線LAN USB dongleを挿入したら "wlan1" として認識されたため、「ドライバ要らないのかな?」と勘違いした。
  2. その後再起動したタイミングで wlan0/wlan1 の認識順序がズレたが、その後の試行錯誤で数時間経過するまでそれに気づかなかった。

2点目の影響で「そもそも内蔵無線LANのwlan0でWiFiが接続できない」(実際は、その時点でwlan0となっていたのは無線LAN USB dongleだった)となってしまい、状況整理にかなり時間を浪費してしまった。 2点目に気が付きインターフェイス名を正しく固定し、それにより wlan0 でのWiFi接続に成功したあとも 1点目になかなか気づけなかった。 「ドライバ周りで何かおかしい」と気づいたのが、sudo journalctl -u hostapd でログを確認した時に wlan1 が正常にUPしていなかったときで、そこからようやく「これはきちんとドライバをインストールする必要がありそう」となり、ドライバインストールからはさくさく進められた。

以上の通り、本文では最初からきれいに作業を進めたように書いているが、実際は行ったり来たりしたり、試行錯誤したり、勘違いに数時間気づかずに「なんでじゃ~~~??」と頭をかきむしったりしている。 それをそのまま書いても資料として役に立たないため、本文では最初からきれいに作業を進めた体にしている。

予測可能な命名規則(Predictable Names)を使うときのネットワークインターフェイス設定: systemd.link(5)

今回のラズパイでは /etc/systemd/network/99-default.link/dev/null にリンクされていたため、udev のルールによるカスタマイズは有効なものの、ネットワークインターフェイス名が古い規則で生成されたものを使うこととなった。 もしこの設定がされておらず、最新の予測可能な命名規則 (Predictable Names) を使っている場合に、それでもインターフェイス名や設定をカスタマイズをするにはどうするか?

systemd ではネットワーク設定を行う systemd.link(5) という仕組みが用意されており、これを使うことになる。 その時は以下のmanページ等を参考にする予定。

6. 参考資料

今回はネットワークインターフェイス名について調べたり、無線LAN USB dongle のドライバ周りで遠回りをするなど相当ネット検索のお世話になった。

直接参考となる資料は本文の方で紹介しているが、間接的にヒントをもらえたり、本文の主題にあまり重ならないため紹介を見送った参考資料を以下に紹介する。

なお Raspberry Pi のネットワーク設定周りについては /etc/network/interfaces を設定する記事も多数見つかった。 今回使用した Raspbian では以下のようになっており、直接触らないほうが良さそうに思えた。

# interfaces(5) file used by ifup(8) and ifdown(8)

# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'

# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d

このため、ネットで見つかった記事中で /etc/network/interfaces を設定する箇所については直接の参考とすることを避けている。

/etc/dhcpcd.conf で eth0 と wlan0 の両方に静的IPを設定する方法と、ルーティングに関するヒント

筆者が設定に試行錯誤していた時、eth0 で有線LANで自宅NWに静的IPアドレスを設定して、そこからSSHでログインしていた。 wlan0 も自宅NWにWiFi接続できる状態になった段階で、「両方に静的IPアドレスを設定するのはどうするのか?」や、「両方で同じNWに所属する静的IPを設定したら、デフォルトゲートウェイはどちらになるのか?」などが不明点として出てきた。

/etc/dhcpcd.conf で有線/無線それぞれに固定IPを設定する方法については、以下の記事を参考にした。

またデフォルトゲートウェイについてはmetricsが関連しているらしいページが見つかり、ざっくりと参考にした:

デフォルトゲートウェイの問題については最終的に「eth0のケーブルを外して運用する」という力技で解決したため、今回はそれ以上は踏み込まなかった。

ELECOM WDC-150SU2MBK をラズパイで使う記事

本文中ではすんなりとドライバをダウンロードしてインストールしたように書いてはいるが、実際のところ以下の2つの勘違いが重なってしまい、状況整理にかなり時間を浪費してしまった。

  1. kernel組み込みのドライバでインターフェイス名が認識されたため「正常に動いてる」と勘違いした。
  2. 途中まで wlan0/wlan1 の認識順序がずれていたことに気づかなかった。

2点目に気が付きインターフェイス名を正しく固定し、それにより wlan0 でのWiFi接続に成功したあとも 1点目になかなか気づけなかった。 「ドライバ周りで何かおかしい」と気づいたのが、sudo journalctl -u hostapd でログを確認した時に wlan1 が正常にUPしていなかったときで、そこからようやく「これはきちんとドライバをインストールする必要がありそう」となり、下記の記事を参考に作業を進めた。

ネットワークインターフェイス名の固定に関して

本文中で systemd, udev との関係から紹介を試みたが、それに辿り着く過程で以下の記事からもヒントをもらった。

systemd, udev の関連manページ

今回 systemd, udev について調べるにあたり、systemdの開発元である freedesktop.org のmanページと、今回使用したOSである Raspbian のベースとなった Debian Buster (10系) の man ページを参考にした。

複数の "wlanX" でそれぞれ wpa_supplicant で設定する方法

本記事では wlan0 のみ /etc/wpa_supplicant/wpa_supplicant-wlan0.conf としてWiFi接続を設定している。 これは結果としてそうなったのであり、試行錯誤の最中には「wlan1側も動作確認としてWiFi接続した方がいいのではないか?」や「そもそもWiFi接続を wlan0 だけに独立させるにはどうすれば?」など調べている。 その結果 /etc/wpa_supplicant/wpa_supplicant-(インターフェイス名).conf で分けられる記事を見つけ、さらにそこをヒントに dhcpcd 用の wpa_supplicant の hook スクリプトである /lib/dhcpcd/dhcpcd-hooks/10-wpa_supplicant を確認することで、本記事で紹介したアプローチに収まった。

以下はその過程で参考にした記事:

↑上記2つが /etc/wpa_supplicant/wpa_supplicant-(インターフェイス名).conf により分ける方法を紹介しており、直接参考にした。

以下は /etc/network/interfaces を使った記事だったため、直接の参考にはできなかったものの、ざっくりと雰囲気を掴むのに役立った。 全体的に wpa_supplicant や wpa_cli が dhcpcd から呼び出される仕組みを前提として書かれており、「dhcpcdから呼ばれる wpa_supplicant の hook の中身がポイントになりそう」という直感に導いてくれた。

無線LAN USB dongle と内蔵無線LANでAP化する方法

  • Pi 4 using WLAN dongle + built-in chip - Raspberry Pi Forums
  • hawksnowlog: RaspberryPi をモバイルルータの拡張アクセスポイントとして構築する
    • https://hawksnowlog.blogspot.com/2019/08/create-access-point-on-raspberrypi.html
    • こちらは Raspberry Pi Zero ということもあり、いくつかポイントとなる設定箇所で今回使用した機材と異なる流儀があった。
      • /etc/network/interfaces を使っているため、直接の参考とすることを避けた。
      • /etc/hostapd/hostapd.conf では driver=nl80211 行があった。今回の設定では hostapd のトラブルシュートで試行錯誤していた時に「この一行がもしかして必要?」と参考にしてみたが、最終的には不要だった。
      • DHCP が isc-dhcp-server を使っている。
    • ポイントとなる箇所が今回の機材と異なることもあり、直接の参考にはならなかったが、全体の流れとしてはヒントをもらえた。

hostapd がうまく動かなかった時は、以下の記事からもヒントをもらった。

これらの記事から、「driver行が怪しいかな?」となって dmesg や sudo journalctl -u hostapd のログ確認などをしたおかげで、最終的に driver 行不要でOKという確信につながった。 今回は driver行の設定は不要だったが、hostapd が無線LANデバイスを操作するときのインターフェイスタイプを指定するもの、というヒントはもらえたので、別の機会に役立つことを期待する。

*1:バージョンが古いのは、たまたま筆者の手元にあった個人用 iPad が古いせいで、特別な意図は無い。

*2: 今回最新版に更新した Raspbian (32bit) が Buster ベースとなったため、Busterのmanページを参照。