Pi-Star に XLX リフレクタ(XLX834)を同居させてみた ― ハマりどころと対処メモ
Pi-Star が動いている Raspberry Pi Zero 2W に、XLX リフレクタ(xlxd)を同居させてみた。単体で XLX を立てるのとは勝手が違い、Pi-Star 標準のファイアウォールやファイルシステム保護(読み取り専用ルート)が、XLX の通信を静かに邪魔するのが最大の落とし穴だった。動くまでに踏んだ地雷と、その対処を残しておく。
同じ構成(Pi-Star + xlxd 同居)を試す人の時間節約になれば幸いだ。
構成
- ホスト: Raspberry Pi Zero 2W(Pi-Star ベース、ホスト名
pi-star-93) - xlxd: XLX834、
eth0の192.168.4.254で待ち受け - インターリンク相手: XLX168 / XLX833(いずれもモジュール Z)
- WAN 側: MyDNS の DDNS 運用、ルーターは NTT PR 系
前提として、Pi-Star のルートは通常 読み取り専用(RO)マウントで、設定変更のたびに sudo mount -o remount,rw / で書き込み可にし、終わったら sudo mount -o remount,ro / で戻す。以下の作業でも頻出する。
pi-star-93)+ xlxd(XLX834)。以下の手順はこの構成で動作を確認済み。ハマりどころ 1:インターリンクが張れない(UDP 10002)
同居させた直後、XLX834 から XLX168 / XLX833 へのインターリンクが確立しなかった。ログには次が延々と流れ続ける。
xlxd[...]: Sending connect packet to XLX peer XLX168 @ ... for modules Z xlxd[...]: Sending connect packet to XLX peer XLX833 @ ... for modules Z
XLX のインターリンクは、確立すると connect の再送が止まって keepalive に移行する。再送が無限に続く=ハンドシェイクが片道も通っていないサインだ。
原因
Pi-Star の標準 iptables はホットスポット用に作られていて、XLX リフレクタのインターリンク用ポートを通す ACCEPT が無い。該当しないパケットは最後の LOGNDROP で落とされる。しかも Pi-Star の OUTPUT は既定ポリシーが DROP なので、受信も送信も両方向で塞がれる。
注意:インターリンクは 10001 ではなく 10002
ここで一番の注意点。ネット上の古い記事には「10001=インターリンク」と書いてあるものがあるが、実機の挙動はこうだ。
| ポート | 用途 |
|---|---|
| UDP 10001 | JSON インターフェース(XLX Core / ダッシュボード用) |
| UDP 10002 | XLX インターリンク(リフレクタ間の相互接続) |
xlxd の README(Firewall settings)にも UDP 10002 (XLX interlink) と明記されている。netstat で 10001 だけを見ると xlxd が常時待ち受けているので「これがインターリンクだ」と早合点しやすいが、開けるべきは 10002 だ。ここを取り違えると、いくら設定してもインターリンクは張れない。
10001 は JSON インターフェース用で、インターリンクで開けるのは UDP 10002。ここを取り違えるとリンクは張れない。対処
Pi-Star のファイアウォール生成スクリプト /usr/local/sbin/pistar-firewall に、10002 の ACCEPT を恒久追加する。既存の 30001:30007(DExtra)の行の直後に、書式を合わせて差し込むのが手堅い。
sudo mount -o remount,rw / sudo cp /usr/local/sbin/pistar-firewall /usr/local/sbin/pistar-firewall.bak sudo sed -i '/^iptables -A OUTPUT -p udp --dport 30001:30007 -j ACCEPT/a iptables -A OUTPUT -p udp --dport 10002 -j ACCEPT # XLX Interlink Outbound' /usr/local/sbin/pistar-firewall sudo sed -i '/^iptables -A INPUT -p udp --dport 30001:30007 -j ACCEPT/a iptables -A INPUT -p udp --dport 10002 -j ACCEPT # XLX Interlink' /usr/local/sbin/pistar-firewall sudo /usr/local/sbin/pistar-firewall # ルール再生成(=即反映) sudo iptables -L INPUT -n | grep 10002 sudo iptables -L OUTPUT -n | grep 10002 sudo mount -o remount,ro /
Pi-Star は起動時にこのスクリプトを実行してルールを組み直すので、スクリプトに書いておけば再起動後も自動で開く。念のため保存版も更新しておく(リダイレクトが root で走るよう sh -c でくくるのがコツ)。
sudo mount -o remount,rw / sudo sh -c 'iptables-save > /etc/iptables.rules ; ip6tables-save > /etc/ip6tables.rules' sudo mount -o remount,ro /
10002 を開けた瞬間、ログが変わった。
xlxd[...]: XLX ack packet for modules Z from XLX168 at ... xlxd[...]: New peer XLX168 added xlxd[...]: New peer XLX833 added
なお、ルーター側の 10002 ポートフォワードは元々正しく入っていた。塞いでいたのは Pi-Star のローカル iptables だけだった。
ハマりどころ 2:DMRGateway から“自局の” XLX834Z に繋がらない(ヘアピン NAT)
インターリンクは通ったのに、同居している Pi-Star 自身の DMRGateway から XLX834 のモジュール Z にリンクできない。一方、外部の XLX833Z には繋がる。ログは接続を投げるところで止まっていた。
XLX, Connecting to XLX834 (この後の "Logged into the master" / "Linking to reflector XLX834 Z" が出ない)
原因
DMRGateway が参照する /usr/local/etc/XLXHosts.txt の 834 の行が、自局の WAN IP(グローバル)を指していた。
834;<YOUR_WAN_IP>;4004 ← 自分の WAN IP
自機が自分の WAN IP 宛に UDP を投げると、パケットが一度ルーターへ出て自分へ戻る「ヘアピン NAT」が必要になる。ところが NTT PR 系をはじめ多くの家庭用ルーターはヘアピン NAT 非対応で、自局宛の応答だけが返ってこない。外部リフレクタに繋がるのは、あちらは素直に外へ出るだけだからだ。
ping で自分の WAN IP に返ってくることがあるが、それは ICMP の話。UDP のポートフォワード戻りは別挙動なので、切り分けを誤りやすい。対処
同居なのだから、WAN IP ではなくローカル IP を指せばよい。xlxd は 192.168.4.254 で待っている。
sudo mount -o remount,rw / sudo sed -i 's|^834;<YOUR_WAN_IP>;4004|834;192.168.4.254;4004|' /usr/local/etc/XLXHosts.txt sudo systemctl restart dmrgateway sudo mount -o remount,ro /
これで Logged into the master successfully → Linking to reflector XLX834 Z まで到達し、無線機から通れるようになった。
恒久化:/root/XLXHosts.txt オーバーライド
XLXHosts.txt は Pi-Star が定期的(既定 60 分)に公式リストで上書き再生成する。手で直した 834 行はそのままだと次の更新で WAN IP に戻ってしまう。
Pi-Star にはこれ用の正式な仕組みがある。HostFilesUpdate.sh は /root/XLXHosts.txt があれば、その内容で XLXHosts.txt の該当 ID 行を毎回書き戻す(P25/NXDN の 〜Local.txt に相当する XLX 版)。ここに 1 行置けば恒久化できる。
sudo mount -o remount,rw / echo "834;192.168.4.254;4004" | sudo tee /root/XLXHosts.txt sudo mount -o remount,ro /
これで、公式リストの再取得が走っても 834 は常にローカル IP に固定される。
ハマりどころ 3:YSF リフレクタが外から見えない(UDP 42000)
YSF(ポート 42000)でリフレクタとして公開しているのに、登録検証サービスが 408 timeout / reflector did not respond を返す。実クライアントの一部も繋がらない。tcpdump -i any udp port 42000 -nn で覗くと、応答が返る相手と、着信はあるのに一切応答しない相手にきれいに分かれていた。
分かれ目は相手の送信元ポートだった。
- 送信元ポートが 42000〜43000 の相手 → 応答が返る
- それ以外の高位ポートから来る相手(=ふつうの YSF クライアントや検証サーバー)→ 応答ゼロ
原因
Pi-Star の YSF 着信ルールがこれ。
iptables -A INPUT -p udp --sport 42000:43000 --dport 1024:65535 -j ACCEPT
送信元ポートが 42000〜43000 のものしか通さない。これは YSFGateway 同士の通信向けで、任意ポートから 42000 に来るリフレクタ受けを想定していない。tcpdump は netfilter の手前で見えるので着信は映るが、その後 INPUT で落ちている。
対処
42000 宛の着信を送信元ポートによらず通す。応答は conntrack の ESTABLISHED で自動的に戻るので INPUT 側だけでよい。YSF の INPUT ルール直後に追記し、恒久化する。
sudo mount -o remount,rw / sudo sed -i '/^iptables -A INPUT -p udp --sport 42000:43000 --dport 1024:65535 -j ACCEPT/a iptables -A INPUT -p udp --dport 42000 -j ACCEPT # YSF Reflector inbound' /usr/local/sbin/pistar-firewall sudo /usr/local/sbin/pistar-firewall sudo iptables -L INPUT -n | grep "dpt:42000" sudo sh -c 'iptables-save > /etc/iptables.rules ; ip6tables-save > /etc/ip6tables.rules' sudo mount -o remount,ro /
これで任意の相手から YSF 着信を受けられるようになり、登録検証も通った。リフレクタとして外部公開する場合の正しい対処だ。
ハマりどころ 4:再起動すると Apache が起動しない
再起動後、ダッシュボード(Apache)が上がらないことがあった。原因は単純で、Pi-Star は SD 保護のため /var/log を tmpfs(RAM ディスク)に載せている。標準イメージに無い /var/log/apache2 は再起動のたびに消え、Apache がログを開けずに落ちる。
その場しのぎは作り直すだけ。
sudo mkdir -p /var/log/apache2 sudo chown www-data:www-data /var/log/apache2 sudo systemctl start apache2
恒久化は systemd-tmpfiles に「起動時に作れ」と登録しておく。これなら Apache 起動前に自動でディレクトリが用意される。
sudo mount -o remount,rw / echo 'd /var/log/apache2 0755 www-data www-data -' | sudo tee /etc/tmpfiles.d/apache2.conf sudo systemd-tmpfiles --create /etc/tmpfiles.d/apache2.conf sudo mount -o remount,ro /
DDNS(MyDNS)の維持
WAN IP 追従に MyDNS を使っているので、定期通知を cron に載せる。MyDNS は一定期間アクセスが無いと登録が抹消されるため、日次の保険と、再起動時の即時更新の二段構えにした。
0 4 * * * curl -4 -s -u <MASTERID>:<PASSWORD> https://ipv4.mydns.jp/login.html > /dev/null 2>&1 @reboot sleep 30 && curl -4 -s -u <MASTERID>:<PASSWORD> https://ipv4.mydns.jp/login.html > /dev/null 2>&1
crontab -e で登録。手動実行して Login and IP address notify OK / login_status = 1 が返れば OK。@reboot に sleep 30 を入れているのは、起動直後でネットワークが上がりきる前に空振りしないため(不安定なら 60 秒に伸ばす)。
他にやっていたこと:DMR↔D-Star トランスコード(今回は見送り)
ついでに XLX のトランスコード(Vocoder)で 同一モジュール DMR↔D-Star を通せないか検討したが、今回は見送った。理由を残しておく(同じ勘違いをしないように)。
- ネットワーク上の AMBE 機で動いていたのは
ambedではなくAMBEserverだった。両者は別物で、AMBEserver は AMBE ドングルをネット越しに共有する DVSwitch 用の仕組み。xlxd の内蔵トランスコーダ(ambed)は AMBEserver には繋がらない(プロトコルが違う)。 ambed(xlxd 付属)は libftd2xx で FTDI ドングルを直接叩く設計で、ftdi_sio(VCP)と排他。ARM 版 libftd2xx のビルドがハードル。- そして本質的な壁として、
ambedの対応表いわく DMR↔D-Star の双方向変換には AMBE3000 を「ペア(2本)」か AMBE3003(1枚)が必要。手持ちの AMBE3000 は 1 本のみで、単体では成立しない。 - ソフト変換の 380EMU も、エミュレートするのは AMBE+2(DMR/YSF 系)で、D-Star の旧 AMBE codec は対象外。DMR↔D-Star には本物の AMBE ハードが要る。
結論として、DMR↔D-Star をやるなら AMBE3000 をもう 1 本足すか AMBE3003 へ置換が前提、という宿題になった。既存の AMBEserver / ドングル / 運用には一切手を付けず撤収した。
まとめ:Pi-Star 同居で効いた勘所
同居で詰まるポイントは、ほぼ Pi-Star 側の“ホットスポット前提”の作りに集約される。
- ファイアウォール:
pistar-firewallは XLX のインターリンク(10002)もリフレクタ受けの YSF(42000、任意送信元)も想定していない。スクリプトに ACCEPT を追記して恒久化する。OUTPUT が既定 DROP な点も忘れずに。 - 自局リンクはローカル IP で:同居機から自分の XLX に繋ぐときは WAN IP を指さない。ヘアピン NAT で死ぬ。
XLXHosts.txtの該当行をローカル IP にし、/root/XLXHosts.txtオーバーライドで固定する。 - RO ファイルシステム & tmpfs:設定変更は
remount,rw→ 作業 →remount,ro。/var/logは毎起動消えるので、必要なディレクトリはtmpfiles.dで作らせる。 - 恒久化=再起動で確認:ランタイムで直しただけでは Pi-Star の再生成で消える。スクリプト/設定ファイルに焼き込み、一度リブートして peer 自動復活・各ポート ACCEPT・ダッシュボード起動をまとめて検証して初めて完了。
インターリンク(10002)・YSF(42000)・DMRGateway リンク・Apache・DDNS まで押さえれば、Pi-Star 同居の XLX は安定して回せる。
(73)
🏛 JJ2YYK あいちデジタルコミュニケーションハムクラブ