サーバーの緊急脱出口 — OS のネットワークスタックとは独立した、常時動作する非常口。
English version → README-EN.md
あなたにも経験があるはず。
[自宅の PC] ──── インターネット ──✗── [遠くにあるサーバー]
iptables -F を実行して SSH のポートを開け直すのを忘れた。
ネットワークインターフェースを誤って設定してしまった。
テスト中に NetworkManager を終了してしまった。
ルーティングテーブルのエントリを誤って消した。
マシンは生きている。ディスクも正常。でもアクセスできない。
物理的にアクセスするか、誰かに頼むしかない。
6ドルの Raspberry Pi Pico W をサーバーの空き USB ポートに挿すだけ。
[自宅 / スマホ]
│
▼ WiFi (192.168.4.0/24)
┌────────────┐
│ Pico W │ ← 4cm × 2cm、USB 給電、常時動作
│ Rescue NIC│
└────────────┘
│ USB CDC-NCM (10.0.0.0/24)
▼
[ロックアウトされたサーバー]
- SSH が通る
- curl が通る
- ping が通る
- iptables/ルーティング変更も効く
Pico W はサーバーに 標準 USB イーサネットアダプター (CDC-NCM) として認識されます。
同時に WiFi アクセスポイント を動作させます。
スマホやラップトップから WiFi 経由で接続し、ブリッジ越しに SSH して問題を修正できます。
ドライバ不要。クラウド不要。ベンダーロックインなし。オフラインでも動作。
Warning
注意! 使用は自己責任です!
これを使用してサーバーを復活させられる保証はありません。
正しく動かないことがあるかもしれません。
逆にこれの使用がトドメを刺す可能性もあります。
何が起きても、私は責任を取れません。
事前に実験機などでテストしておくことをお勧めします。
| 機能 | 詳細 |
|---|---|
| 🔌 USB イーサネット (CDC-NCM) | Linux・macOS・Windows 10+ でプラグアンドプレイ |
| 📡 WiFi アクセスポイント | WPA2-AES、SSID・パスワード設定可能 |
| 🔄 双方向 IP ルーティング | フル NAT/NAPT — SSH・curl・ping・SCP |
| 🏷️ 自動ルート配布 | DHCP オプション 121 で WiFi ルートを USB ホストに自動設定 |
| 🖥️ デバッグコンソール | CDC-ACM シリアル — 同じ USB ケーブルでファームウェアログ確認 |
| ⚡ 依存ゼロ | OS 不要・RTOS 不要・カーネルモジュール不要 |
| 🔋 USB バス給電 | 約 100mA — サーバー再起動後も USB 経由で自動復帰 |
iptables/nftablesの設定ミスでサーバーにログインできなくなった- ネットワーク設定変更のテスト中のフォールバックとして常設
- WiFi カードのないマシンへのリモートアクセス
- 管理ネットワークがダウンしたときの緊急アクセス
- IPMI/iDRAC と並ぶ副系 OOB(アウトオブバンド)パス
- "Break glass(非常時用)" オプションとして事前インストール
- ネットワーク非依存の組み込みターゲットアクセス
- 本番ネットワークを触らない独立したテストネットワーク
- ライブシステムでのネットワークスタック変更テスト
- ラップトップへの USB テザリング WiFi ブリッジ
- デモ用の軽量ネットワークブリッジ
┌─────────────────────────────┐
│ Raspberry Pi Pico W │
│ │
[WiFi クライアント] │ 192.168.4.1 ←→ 10.0.0.1 │ [USB サーバー]
192.168.4.10 ───────│ WiFi AP CDC-NCM │──── 10.0.0.2
│ ↕ NAPT/ルーティング ↕ │
└─────────────────────────────┘
WiFi サブネット : 192.168.4.0/24 (Pico = 192.168.4.1)
USB サブネット : 10.0.0.0/24 (Pico = 10.0.0.1)
WiFi クライアント: 192.168.4.10 (DHCP 固定)
USB サーバー : 10.0.0.2 (DHCP 固定)
スマホ (192.168.4.10)
│ SSH TCP SYN → 10.0.0.2:22
▼
Pico W WiFi (192.168.4.1) — パケット受信
│ NAT: src 192.168.4.10 → 10.0.0.1
▼
Pico W USB (10.0.0.1) — CDC-NCM 経由で転送
│
▼
サーバー (10.0.0.2) — 10.0.0.1 からの SSH として受信
│ SSH SYN-ACK → 10.0.0.1
▼
Pico W USB — 受信、NAT テーブル検索
│ dst 10.0.0.1 → 192.168.4.10 に復元
▼
スマホ — SSH セッション確立 ✓
- Raspberry Pi Pico W (RP2040 + CYW43439)
約 1,000 円 — これだけ - USB-A to Micro-USB ケーブル(データ通信対応のもの)
- ホスト: 空き USB ポートのある Linux / macOS / Windows マシン
Pico 2 W は? Pico 2 W (RP2350) は SDK を少し変更すれば動作するはずですが未テストです。PR 歓迎。
ビルド環境を用意しなくても、GitHub Releases から最新の .uf2 ファイルを直接ダウンロードできます。
1. Releases ページを開く
2. 最新リリースの「Assets」から picow_nic_vX.X.uf2 をダウンロード
3. Pico W を BOOTSEL モードにして(ボタンを押しながら USB 接続)
4. 現れた RPI-RP2 ドライブにファイルをコピー → 完了
ソースコードからビルドする場合は以下を参照してください。
# ARM ツールチェーン インストール (Ubuntu/Debian)
sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential
# Pico SDK のクローン
git clone https://github.com/raspberrypi/pico-sdk --recurse-submodules
export PICO_SDK_PATH=/path/to/pico-sdkgit clone https://github.com/mar1mo-41414/PicoW-Rescue-NIC
cd PicoW-Rescue-NIC
mkdir build && cd build
cmake .. -DPICO_BOARD=pico_w -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)出力: build/picow_nic.uf2
ビルド前に CMakeLists.txt を編集:
target_compile_definitions(picow_nic PRIVATE
WIFI_SSID="YourRescueNet"
WIFI_PASSWORD="YourSecret"
WIFI_CHANNEL=6
)# BOOTSEL ボタンを押しながら USB に接続
# "RPI-RP2" ドライブが現れたらコピー
cp build/picow_nic.uf2 /media/$USER/RPI-RP2/
# またはヘルパースクリプトを使用
./scripts/flash.shopenocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg \
-c "program build/picow_nic.elf verify reset exit"USB に挿すと、サーバーは DHCP 経由で CDC-NCM インターフェースを自動設定します。
# サーバーで確認
ip addr show # → 10.0.0.2/24 が picow/usb0 などに割り当て済み
ip route # → 192.168.4.0/24 via 10.0.0.1 が自動設定済み(DHCP opt 121)手動で ip route add する必要はありません — DHCP が自動で WiFi サブネットのルートを配布します。
スマホやラップトップから PicoBridge(パスワード: picobridge123)に接続。
あなたのデバイスに割り当てられる IP: 192.168.4.10
# スマホ/ラップトップ (PicoBridge WiFi) から
ssh user@10.0.0.2 # ロックアウトされたサーバーに SSH
curl http://10.0.0.2 # HTTP
ping 10.0.0.2 # Ping
scp file user@10.0.0.2:~ # ファイルコピーWiFi クライアントから 10.0.0.x に到達するにはルートが必要です:
# macOS
sudo route add -net 10.0.0.0/24 192.168.4.1
# Linux
sudo ip route add 10.0.0.0/24 via 192.168.4.1
# Windows
route add 10.0.0.0 mask 255.255.255.0 192.168.4.1Pico は同じ USB ケーブルで CDC-ACM シリアルポートも提供します。
# Linux
screen /dev/ttyACM0 115200
# macOS
screen /dev/cu.usbmodem* 115200
# Windows
# デバイスマネージャーで COM ポートを確認 → PuTTY================================================
PicoW-NIC USB-WiFi Bridge
USB subnet : 10.0.0.0/24
WiFi subnet: 192.168.4.0/24 SSID=PicoBridge
================================================
WiFi AP: SSID=PicoBridge Pico=192.168.4.1 Client=192.168.4.10
Network: IP forwarding enabled (IP_FORWARD=1)
Network: NAPT active
Network: WiFi TX checksum wrapper installed
Ready — connect PC to USB and/or WiFi AP "PicoBridge"
--- Status ---
USB (us1): IP=10.0.0.1 link=UP
WiFi (w10): IP=192.168.4.1 link=UP RSSI=-42 dBm
パケット単位のトレース(NAT/ルーティングのデバッグに使用):
// lwipopts.h — 1 に設定してリビルド
#define VERBOSE_LOG 1パケット単位の出力例:
USB RX: 98 bytes IPv4 proto=1 dport=12345
NAT ICMP: inp=w1 src=192.168.4.10 dst=10.0.0.2 type=8
USB TX: 98 bytes ready=1 can_xmit=1
┌─────────────────────────────────────────────────────┐
│ アプリケーション │
│ main.c — 協調ポーリングループ │
├──────────────────┬──────────────────────────────────┤
│ TinyUSB │ lwIP (NO_SYS=1) │
│ CDC-NCM │ IP_FORWARD + LWIP_HOOK_IP4_INPUT │
│ CDC-ACM │ NAPT (nat.c) │
├──────────────────┼──────────────────────────────────┤
│ USB ペリフェラル │ CYW43439 WiFi ドライバ │
│ (RP2040 USB) │ (cyw43_arch_lwip_poll) │
└──────────────────┴──────────────────────────────────┘
RP2040 @ 125 MHz — 264 KB SRAM
なぜカスタム NAPT?
Pico SDK 同梱の lwIP スナップショットには ip4_napt.c(上流 2.1.x 以降に追加)が含まれていません。
LWIP_HOOK_IP4_INPUT を使って src/nat.c に最小限の NAT テーブルを実装しています。
なぜソフトウェアチェックサム?
ip4_forward() はチェックサムオフロードを前提に IP/TCP/UDP/ICMP チェックサムをゼロ化します。
TinyUSB の CDC-NCM も CYW43 ドライバもオフロードを実装していないため、
両インターフェースの linkoutput パスでソフトウェア再計算しています。
なぜ RTOS なし?
TinyUSB と lwIP はどちらも協調ポーリングモードを持ち、クリーンに組み合わせられます。
RTOS を追加しても、このユースケースではメリットより複雑さが増すだけです。
詳細: docs/architecture.md / docs/investigation.md
PicoW-Rescue-NIC/
├── README.md このファイル(日本語)
├── README-EN.md English version
├── LICENSE
├── CMakeLists.txt ビルド設定
├── lwipopts.h lwIP 設定(VERBOSE_LOG、チェックサム、NAT フック)
├── tusb_config.h TinyUSB 設定(CDC-NCM + CDC-ACM 複合デバイス)
├── pico_sdk_import.cmake
│
├── src/
│ ├── main.c エントリーポイント、協調ポーリングループ
│ ├── usb_net.c/h CDC-NCM ↔ lwIP ブリッジ + ソフトウェアチェックサムエンジン
│ ├── wifi_ap.c/h CYW43 AP モード + DHCP サーバー初期化
│ ├── network.c/h WiFi TX チェックサムラッパー、network_init()
│ ├── nat.c/h 双方向 NAPT(TCP/UDP/ICMP 対応)
│ ├── dhcpserver.c/h 固定 IP DHCP サーバー(DHCP オプション 121 対応)
│ ├── usb_descriptors.c/h 複合 USB 記述子(IAD)
│ └── stdio_cdc.c printf → CDC-ACM シリアル
│
├── docs/
│ ├── architecture.md 設計判断、データフロー図(日本語)
│ ├── architecture-EN.md Architecture (English)
│ ├── investigation.md バグ解析:チェックサム、NAT、lwIP 内部(日本語)
│ ├── investigation-EN.md Investigation log (English)
│ ├── troubleshooting.md よくある問題と解決策(日本語)
│ ├── troubleshooting-EN.md Troubleshooting (English)
│ ├── spec.md 完全仕様書(日本語)
│ ├── spec-EN.md Specification (English)
│ └── release-notes-v1.0.md リリースノート(日本語)
│
├── images/ 写真、図(予定)
└── scripts/
└── flash.sh ワンコマンドフラッシュスクリプト
| 制限 | 備考 |
|---|---|
| WiFi クライアント 1 台のみ | DHCP 固定 IP のため — 複数対応は dhcpserver 拡張が必要 |
| USB Full Speed | 物理 12 Mbps 制限、NAT スループット 2–4 Mbps 程度 |
| IPv6 非対応 | LWIP_IPV6=0 — IPv4 のみ |
| WiFi クライアントの手動ルート | WiFi→USB 方向は手動 ip route add が必要(USB→WiFi は DHCP opt 121 で自動) |
| 追加認証なし | WiFi は WPA2 だがブリッジ自体には追加の認証層なし |
| Pico W のみ | RP2040 + CYW43439 でのみテスト済み |
- 複数 WiFi クライアント — DHCP リーステーブル実装
- ステーションモード — 上流 WiFi に接続して USB にブリッジ(逆方向)
- Web ステータスページ — lwIP httpd でリンク状態・NAT テーブル・RSSI 表示
- DNS プロキシ — USB 側からの DNS クエリを WiFi 経由で転送
- WireGuard — WiFi リンク上の暗号化トンネル
- RNDIS サポート — 古い Windows バージョン向け
- Pico 2 W (RP2350) — 移植・テスト
- USB High Speed — 外部 PHY または RP2350 で 12 Mbps 超
Issue や PR は歓迎です。提出前に:
docs/architecture.mdで設計の背景を確認docs/investigation.mdで既知の NAT/チェックサムの注意点を確認- 提出前に両方向で ping + SSH のベーシックテストを実行
MIT — LICENSE を参照
- Raspberry Pi Foundation — Pico W ハードウェア + SDK
- TinyUSB — USB デバイススタック
- lwIP — TCP/IP スタック
- Pico SDK — ボードサポート + CYW43 ドライバ
📷 写真・デモ(クリックして展開)
写真: サーバーの USB ポートに挿さった Pico W、スマホから WiFi 接続中
GIF: Pico W ブリッジ越しに確立した SSH セッション