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: XLX834eth0192.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 ベースの Raspberry Pi Zero 2W(ホスト名 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 10001JSON インターフェース(XLX Core / ダッシュボード用)
UDP 10002XLX インターリンク(リフレクタ間の相互接続)

xlxd の README(Firewall settings)にも UDP 10002 (XLX interlink) と明記されている。netstat で 10001 だけを見ると xlxd が常時待ち受けているので「これがインターリンクだ」と早合点しやすいが、開けるべきは 10002 だ。ここを取り違えると、いくら設定してもインターリンクは張れない。

⚠️ 古い記事にある「10001=インターリンク」は誤り。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 非対応で、自局宛の応答だけが返ってこない。外部リフレクタに繋がるのは、あちらは素直に外へ出るだけだからだ。

⚠️ 同居機から自局の XLX に繋ぐときは WAN IP を指さない。NTT PR 系など多くの家庭用ルーターはヘアピン NAT 非対応で、自局宛の応答だけが返らない。参照先はローカル IP にする。
📝 余談: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 successfullyLinking 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。@rebootsleep 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 側の“ホットスポット前提”の作りに集約される。

  1. ファイアウォールpistar-firewall は XLX のインターリンク(10002)もリフレクタ受けの YSF(42000、任意送信元)も想定していない。スクリプトに ACCEPT を追記して恒久化する。OUTPUT が既定 DROP な点も忘れずに。
  2. 自局リンクはローカル IP で:同居機から自分の XLX に繋ぐときは WAN IP を指さない。ヘアピン NAT で死ぬ。XLXHosts.txt の該当行をローカル IP にし、/root/XLXHosts.txt オーバーライドで固定する。
  3. RO ファイルシステム & tmpfs:設定変更は remount,rw → 作業 → remount,ro/var/log は毎起動消えるので、必要なディレクトリは tmpfiles.d で作らせる。
  4. 恒久化=再起動で確認:ランタイムで直しただけでは Pi-Star の再生成で消える。スクリプト/設定ファイルに焼き込み、一度リブートして peer 自動復活・各ポート ACCEPT・ダッシュボード起動をまとめて検証して初めて完了。

インターリンク(10002)・YSF(42000)・DMRGateway リンク・Apache・DDNS まで押さえれば、Pi-Star 同居の XLX は安定して回せる。

(73)

📝 執筆:JI2TAB(尾張旭 DMR デジピーター 管理人)
🏛 JJ2YYK あいちデジタルコミュニケーションハムクラブ
JJ2YYK
  • JJ2YYK

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です