dump1090-fa

ADS-Bの受信用に購入したARM64のH5を搭載したシングルボードコンピュータのOrangePi Zero Plusは、DebianがベースのArmbianが動いている。昨年夏の時点ではADS-Bの受信にはdump1090-mutabilityを使用した。これはArmbianの標準リポジトリに入っているパッケージを単にインストールしたもの。簡単なので。
ただ、dump1090-mutabilityは昨年夏の時点で既にも既に開発者がもう開発を継続しないのでdump1090-faに移れと言っていたもの。流石に2020年に新しくインストールするならもうdump1090-mutabilityはナイ。
dump1090-faをググるとGithubではdump1090-faとしてhttps://github.com/adsbxchange/dump1090-fa がヒットするんだけど、こちらはArmbian上では全くビルドできなかった。
GitHubのdump1090-mutabilityのREADME.mdを見ると https://github.com/flightaware/dump1090 にリンクが張られていたので、dump1090-faはこちらが良いっぽい。

Orange Pi Zero Plus上でビルドする

$ sudo apt install git lighttpd debhelper librtlsdr-dev pkg-config dh-systemd libncurses5-dev libbladerf-dev libusb-1.0-0-dev

先ずはビルドに必要そうなパッケージをインストールする。Armbian新規インストール直後だともしかしたら他にも要るかも。後のビルド時のエラーを見て追加。

$ git clone https://github.com/flightaware/dump1090 dump1090-fa
$ cd dump1090-fa
$ dpkg-buildpackage -b --no-sign

gitでダウンロードしてからビルド。
今回は普通にMakeするのではなくDebian用パッケージとして作製する。ラクだし。
ビルドが終わったら1つ上の階層にパッケージが出来ている筈。

$ ls ../
dump1090_3.8.0_all.deb
dump1090-fa  (dir)
dump1090-fa_3.8.0_arm64.buildinfo
dump1090-fa_3.8.0_arm64.changes
dump1090-fa_3.8.0_arm64.deb    ←これ
dump1090-fa-dbgsym_3.8.0_arm64.deb
いくつかパッケージが出来ている筈だが、インストールするのは1つだけ。
$ sudo dpkg -i dump1090-fa_3.8.0_arm64.deb

パッケージをインストール。systemd向けのサービス起動用ファイルも入るのでラク。

/lib/systemd/system/dump1090-fa.service (特に触る必要無し)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# dump1090-fa service for systemd

[Unit]
Description=dump1090 ADS-B receiver (FlightAware customization)
Documentation=https://flightaware.com/adsb/piaware/
Wants=network.target
After=network.target

[Service]
User=dump1090
RuntimeDirectory=dump1090-fa
RuntimeDirectoryMode=0755
ExecStart=/usr/share/dump1090-fa/start-dump1090-fa --write-json %t/dump1090-fa --quiet
SyslogIdentifier=dump1090-fa
Type=simple
Restart=on-failure
RestartSec=30
RestartPreventExitStatus=64
Nice=-5

[Install]
WantedBy=default.target
パッケージだと自動で入るが、内容の紹介だけ。

dump1090-faの設定

パッケージで入る設定ファイルは/etc/defaultにあるdump1090-fa。
この設定の初期値はRECEIVER_OPTIONSが--device-index 0 --gain -10 --ppm 0になっている。例えば手動でゲインを変更するなら--gain の後の数値を変える。初期値はマイナス値の-10で、この-10がオートゲイン向けらしいので触らないのが無難?
--ppmは激安RTL-SDR登載クロックオシレータが怪しいやつで周波数がズレるのを修正する用かしら。
SoCがH5なOrange Pi Zero Plusでは--enable-agc指定でdump1090-faのCPU使用率が30%に届かない程度。--enable-agc且つ--dcfilterを指定すると35%程度になった。でも、DC Filterって意味あるのかしら?
基本的にはあまり初期値から変更する必要はないと思うけど、アンテナ設置場所の座標(--lat 緯度 --lon 経度)だけは書いたほうが良さげ。でないと、ウェブUIの地図の表示の初期値がヨーロッパになるので日本まで地図をずらすのが面倒。座標を指定すると、その座標を中心とした同心円も地図に書かれるし。その座標オプションははレシーバーオプションなのかデコーダオプションなのかよく判らないがとりあえずRECEIVER_OPTIONSの方に足して機能している。なお、ウェブUIを使わないなら座標指定は要らない。
ネットワーク設定は設定ファイルの初期値のままで普通に他のホストから参照できる設定になっているので特に触らなくて良い筈。
ヘルプは dump1090-fa --helpで表示される。

dump1090-faの起動

$ sudo systemctl enable dump1090-fa.service
$ sudo service dump1090-fa start

1行目はdump1090-faのサービス有効化。これを実行すると次にワザと無効化するまでシステムを再起動しても有効化はずっと保たれる。
2行目はサービスの開始。先に有効化しないと開始できない。

JSON出力

各種JSONは/var/run/dump1090-fa下に出力される。(/lib/systemd/system/dump1090-fa.serviceの起動オプションによる)
このJSONを他のアプリに渡して利用することも可能。

Web UI

今回はウェブ出力付きでビルドしているのでhttp://IPアドレス/dump1090-faをブラウザで開く。

dump1090-fa Web-UI
表示される。うん、動いてる。でも、特に何かできるわけではないので要らないね。
ちなみに、この画像はdump1090-faの設定で座標を指定していないので同心円は描かれていない。また、ウェブ上の設定で飛行機の軌跡も描かないものになっている。

関連記事:

Fail2BanでBAN発生時に警告灯を点灯させる

警告灯をブラウザで制御

Fail2Banは、サービスのログを監視して、ネットから攻撃された際にファイアウォールでそのIPを指定時間弾く(BANする)というようなことに使うツール。BANが発生したときには、それを何かに通知するだろうが、多くはメールで通知する程度?まぁ、メールで通知されて何か役に立つわけではないが、届いたメールの数で「昨日は午後多かったね」くらいは判る。通知先を監視システムにしていれば、それで統計にしたりリアルタイムで監視したりというのも。
で、「がとらぼ」では警告灯を作ったのでそれに通知させることにした。つまり、BANが発生したら警告灯が光るようにした。通知方法はシンプルにWebhookを使う(またかよ)。

Fail2Banの設定

以下、設定ファイルのPathはFreeBSDのpkg/portsでFail2Ban(security/py-fail2ban)をインストールした場合に倣ったもの。Linuxなどでは/usr/local/etc下ではないと思うので適当に読み替えて欲しい。

/usr/local/etc/fail2ban/action.d/webhook.conf (新規)
1
2
3
4
[Definition]
actionban = /usr/local/bin/curl -X POST -H "Content-Type: application/json" \
            -d '{"data":{"name":"Fail2Ban", "status":"warning"}}' \
            http://hoge.example.com/path/webhook.php

アクション用のファイルをaction.d下に作製する。
今回はBANが発生したときにWebhookするというものなのでactionbanを書いた。
curlでJSONをPOSTするだけ。今回は警告灯を光らせるだけが目的なのでJSONの内容はdataの中でnameとstatusを内容固定にした。2〜4行目は本来は1行で書くところを行末の \ で改行している。4行目はwebhookの通知先。

/usr/local/etc/fail2ban/jail.local (1行追加)
1
2
3
4
[DEFAULT]
action = pf[name=%(__name__)s, bantime="%(bantime)s", actiontype=<allports>]
         %(mta)s-whois[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s"]
         webhook[]
4行目が今回追加した行。
2〜4行目がアクションの指定。それぞれの行で、FreeBSDのパケットフィルタであるpfのテーブルに該当IPアドレスを追加、メールで通知、今回追加したアクションであるWebhookを指定している。上の例ではDEFAULTセクション(全てのフィルタのアクション)で指定しているが、サービス毎のセクションで指定するのももちろんあり。 今回は単に警告灯のランプを点灯させるだけの目的で、可変で何か情報を通知させたいわけではないのでwebhookの[ ]の中は何も無し。
# service fail2ban reload
OK

Fail2Banの設定をリロードさせる。OKが出れば設定に致命的なエラーもなく設定がリロードされた筈。
設定のリロードだけでなく、Fail2Banにサービスのログをリロードさせたい場合はFail2Banを再起動 (service fail2ban restart)。
/var/log/fail2ban.logを見てエラーが出ていないことを確認。
これで、Fail2Ban側が完了。

Webhookの受信側

上で設定したFail2Banのホストからネットワーク的に疎通できるウェブサーバにphpスクリプトを置く。上のFail2Banの設定では http://hoge.example.com/path/webhook.php になる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {

    //ここはfail2banのwebhookを単純に取得
    $body = file_get_contents('php://input');
    $json = json_decode($body, true);   // <- 2つめの引数にtrueを付けないと連想配列にならない
    //echo print_r($json);

    foreach($json as $ealert) {
        if ((array_key_exists('name', $ealert)) && ($ealert['name'] == 'Fail2Ban')) {
            light(2);       //黄色点灯
            usleep(500000); //0.5秒停止
            light(0);       //消灯
            exit;
        }
    }
}

function light($c) {
    switch ($c) {
        case 0:
            //警告灯 消灯
            $code = "0f";
            break;
        case 1:
            //警告灯 緑
            $code = "0b";
            break;
        case 2:
            //警告灯 黄
            $code = "0d";
            break;
        case 3:
            //警告灯 赤
            $code = "0e";
            break;
    }

    $cmd = 'echo "' .  $code . '" | xxd -r -p > /dev/cuaU0';
    exec($cmd , $error);
    //echo $error;
}
?>

このコードではdata内にnameが存在し、且つそのnameが「Fail2Ban」なWebhookを受けると0.5秒間だけ黄色点灯するというシンプルなもの。読みやすくするためFail2Ban側で送信したstatus内容の判断はこの例では入れていない。
ただし、これだと深夜0時からのFail2Banの再起動(日跨ぎ処理)の再BAN発生により暫く警告灯が激しくチカチカしまくることになる筈。点灯条件は必要に応じて増やしておいた方が良いと思う。

関連記事:

FreeBSDでUSBデバイスのデバイス名を固定する

FreeBSDでUSBデバイスを使う際、それが同じ用途のデバイスであるとUSBポートの差し替えとか認識される順序によっては/dev/デバイス名○の数字の部分が変わってしまう。ヘタにデバイス名をアテにして設定しているとデバイス名が変わることで困ってしまう。そこで特定のUSBデバイスを接続したらそれに応じたデバイス名になるよう固定する。

参考: https://habr.com/ru/post/200330/

まず、USBデバイスの情報を表示した。
# usbconfig dump_device_desc

中略

ugen1.2: <Linux 4.19.68-sunxi with musb-hdrc Gadget Serial v2.4> at usbus1, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=ON (2mA)

  bLength = 0x0012 
  bDescriptorType = 0x0001 
  bcdUSB = 0x0200 
  bDeviceClass = 0x0002  <Communication device>
  bDeviceSubClass = 0x0000 
  bDeviceProtocol = 0x0000 
  bMaxPacketSize0 = 0x0040 
  idVendor = 0x0525 
  idProduct = 0xa4a7 
  bcdDevice = 0x0419 
  iManufacturer = 0x0001  <Linux 4.19.68-sunxi with musb-hdrc>
  iProduct = 0x0002  <Gadget Serial v2.4>
  iSerialNumber = 0x0000  <no string>
  bNumConfigurations = 0x0001

中略

ugen2.3: <u-blox AG - www.u-blox.com u-blox 7 - GPS/GNSS Receiver> at usbus2, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (100mA)

  bLength = 0x0012 
  bDescriptorType = 0x0001 
  bcdUSB = 0x0110 
  bDeviceClass = 0x0002  <Communication device>
  bDeviceSubClass = 0x0000 
  bDeviceProtocol = 0x0000 
  bMaxPacketSize0 = 0x0040 
  idVendor = 0x1546 
  idProduct = 0x01a7 
  bcdDevice = 0x0100 
  iManufacturer = 0x0001  <u-blox AG - www.u-blox.com>
  iProduct = 0x0002  <u-blox 7 - GPS/GNSS Receiver>
  iSerialNumber = 0x0000  <no string>
  bNumConfigurations = 0x0001 

ugen1.2がUSBシリアル接続のデバイス(Linuxシングルボードコンピュータ)
ugen2.3がUSBシリアル接続のGPSモジュール

/etc/devd.confで使える変数 $device-nameは /dev/デバイス名 の「デバイス名」部分が入るのだろうと思っていたが、シリアルデバイスの一部(全部?)はデバイス名がttyU*ではなく、umodem*になるっぽい。/devにはそんなデバイス無いのにね。(「がとらぼ」で試した範囲では)
なので、umodemをttyUに置換して それを指定したデバイス名にシンボリックリンクする。

/usr/local/bin/ln_dev.sh (新規ファイル)
1
2
3
4
5
6
7
#!/bin/sh
# $1 置換前デバイス名 devd.confの$device-nameが入る前提
# $2 $device-nameのデバイス名で数字が付いていない部分下の例ではumodem
# $3 $device-nameのデバイス名の置換後
# $4 シンボリックリンク先 目的のデバイス名

/bin/ln -s $(echo /dev/$1 | /usr/bin/sed "s/$2/$3/g") $4
# chmod +x /usr/local/bin/ln_dev.sh
実行可能にしておく。
/etc/devd.conf (ファイル最後に追加)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//USBシリアル通信デバイス (シングルボードコンピュータ)
attach 100 {
    match "vendor" "0x0525";
    match "product" "0xa4a7";
    match "release" "0x0419";
    action "/usr/local/etc/ln_dev.sh $device-name umodem ttyU /dev/hoge0";
};

detach 100 {
    match "vendor" "0x0525";
    match "product" "0xa4a7";
    match "release" "0x0419";
    action "/bin/rm /dev/hoge0";
};


//USB GPS module
attach 100 {
    match "vendor" "0x1546";
    match "product" "0x01a7";
    match "release" "0x0100";
    action "/usr/local/etc/ln_dev.sh $device-name umodem ttyU /dev/gps0";
};

detach 100 {
    match "vendor" "0x1546";
    match "product" "0x01a7";
    match "release" "0x0100";
    action "/bin/rm /dev/gps0";
};

attachがデバイス接続時。detachがデバイスの接続が切れたときに使用される。
先にusbconfigで見た内容のidVendorがdev.confで指定するvendorに対応する。同様にidProductがproductに、bcdDeviceがreleaseに対応する。上の例では3つがマッチした場合にactionするようにしたが、3つでなければならないわけではない。マッチさせる条件が多い方がより確実だろうけど、シリアル番号のようなものがあるなら例えばベンダー名とシリアルの2つで確実にユニークなデバイスとして識別できるかもしれない。
ただ、今回の機器は2つともiSerialNumber: 0x0000 <no string>でデバイス識別には役立ちそうになかったのでmatch条件にはしていない。

上の例ではシングルボードコンピュータは/dev/hoge0、GPSモジュールは/dev/gps0となる。それぞれ最後の0は上のようにdevd.confで指定したから付くというだけであって、数字を付けなければならないというものではない。今後GPSモジュールを追加で接続するならgps1とかgps2とかにするという目論見での命名。

# service devd restart
devdを再起動。

これで、USBデバイスのケーブルを抜き差ししたりシステムを再起動したりで/dev/ttyU0だとか/etc/ttyU1だとかの数字が変わっても、その内容から識別してデバイス名のシンボリックリンクが作られるので安心。