OpenWrt ルータを アクセスポイント 化(Dumb AP)すると、接続中の WiFi クライアントの ホスト名 が解決されず、 ? マークで 表示されない ことがよくあります。そこで、DHCPサーバであるルータ機がホスト名リストを生成し、アクセスポイントへ配布する仕組みを構築しました。
誰がWiFi接続中なのか分からない
通常、OpenWrt無線ルータでは、接続中のWiFiクライアントのホスト名やIPアドレスをLuCI上で確認することができます。
ところが、DHCPサーバを廃してアクセスポイント化した場合、自身でIPアドレス配っていないデバイスのことはわからないので、MACアドレスからホスト名を得られず ? マークになってしまいます(ちなみに一部のIPアドレスは、おそらく arp で得られたのでしょう)。
/etc/ethersに書けばよい、が
同じことを指摘するユーザは多く、さまざまな対処法が提示されていました。
要は /etc/ethers にMACアドレスとホスト名(もしくはIPアドレス)のペアを列記しておけば、一覧ページに反映されるとのこと。
デフォルトではコメント文だけのファイルに、ひとまず手打ちでMACアドレスとホスト名を追加します。
|
1 2 3 4 5 6 7 8 9 10 |
# # Lookup man 5 ethers for syntax documentation # # Examples : # 02:00:11:22:33:44 OpenWrt.lan # 02:00:11:22:33:44 192.168.1.1 34:CE:00:##:##:## chuangmi-plug 86:1E:0E:##:##:## iPhone13mini 7C:10:C9:##:##:## ZS672KS 78:11:DC:##:##:## MiCam360 |
ファイルを保存してしばらくすると、 ? マークだったホスト名が表示されました。
ちなみに、リースファイル /tmp/dhcp.leases に記述する対処法もありましたが、そもそもDumb APではDHCPサーバ機能を無効にしているので、このファイルは存在しないはず。
にも関わらず、例えばルータ機のリースファイルを安易にコピー配置してくると、LuCI上ではActive DHCP一覧に現れてしまい、これはこれで逆にトラブルのもとになりそう。
また、 arpwatch (OpenWrtでは addwatch )を利用して、この /etc/ethers を自動生成しようとする取り組みも見つけるも、生成されるのはMACアドレスとIPアドレスのペア。
リースファイルからethersのリストを更新
結局、全てを知っているルータ機が、DHCPリースファイルからMACアドレスとホスト名のペアを抜き出し、 /etc/ethers を更新して、ネットワーク上の他のOpenWrt機へ配布するような仕組みを考えてみます。
まずは、OpenWrtのDHCPサーバのリースファイル /tmp/dhcp.leases 。その中身はこのようになっています。
|
1 2 3 4 5 6 7 |
1708649222 7c:10:c9:##:##:## 192.168.51.182 ZS672KS * 1708653735 86:1e:0e:##:##:## 192.168.51.197 iPhone13mini * 1708653046 24:0a:c4:##:##:## 192.168.51.214 ESP32Cam4C 01:24:0a:c4:##:##:## 1708654033 60:01:94:##:##:## 192.168.51.212 ESP01EasyA2 * 1708653149 5c:cf:7f:##:##:## 192.168.51.216 ESPMatrix82 * 1708653557 34:ca:81:##:##:## 192.168.51.4 NX30ProA9 * 1708652924 34:ce:00:##:##:## 192.168.51.194 chuangmi-plug * |
このリースファイルから /etc/ethers の形のリストを得るのは次の要領で。
|
1 2 3 4 |
#!/bin/sh LEASE_FILE='/tmp/dhcp.leases' LEASE_RAWS=$(grep -i '[0-9A-F]\{2\}\(:[0-9A-F]\{2\}\)\{5\}' $LEASE_FILE | awk 'BEGIN {OFS="\t"} {print $2,$4}') echo "$LEASE_RAWS" |
|
1 2 3 4 5 6 7 |
7c:10:c9:##:##:## ZS672KS 86:1e:0e:##:##:## iPhone13mini 24:0a:c4:##:##:## ESP32Cam4C 60:01:94:##:##:## ESP01EasyA2 5c:cf:7f:##:##:## ESPMatrix82 34:ca:81:##:##:## NX30ProA9 34:ce:00:##:##:## chuangmi-plug |
後はこのリストをループで回して、 /etc/ethers とのクロスマッチングを行い、新しい情報であれば /etc/ethers に追記すればよし。
while文へパイプ渡しすると変数が外で正しく読めない
上述の、リストのような改行区切りの文字列を、 while ループで1行ずつ処理を進める次のようなスクリプトを組んでみたのですが、 while ループ内で更新された変数 $n の値が、その外では全く反映されていない事象に遭遇。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
n=0 echo "$LEASE_RAWS" | while read line while read line do . . n=1 . . done echo $n 0 |
調べてみるとそれは別プロセスになってしまうためらしく、パイプで渡してはいけないようです。以下の記事が勉強になりました。
BashならProcess Substitutionを使うところですが、OpenWrtのデフォルトはAshなので、互換性を鑑みてレガシーにヒアドキュメントを使うことにします。使い方はこんな感じで、 done の後方にくっつけます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
n=0 while read line do . . n=1 . . done << EOS $LEASE_RAWS EOS echo $n 1 |
whileループの回し方を会得したので、早速スクリプトを組んでみました。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#!/bin/sh LEASE_FILE='/tmp/dhcp.leases' ETHER_FILE='/etc/ethers' n=0 LEASE_RAWS=$(grep -i '[0-9A-F]\{2\}\(:[0-9A-F]\{2\}\)\{5\}' $LEASE_FILE | awk 'BEGIN {OFS="\t"} {print $2,$4}') while read line do mac=$(echo $line | cut -f1 -d' ' ) host=$(echo $line | cut -f2 -d' ' ) grep -qi $mac $ETHER_FILE if [ $? = 0 ]; then echo "$mac is known as $host" else n=`expr $n + 1` echo "$mac is new host, $host" echo $line >> $ETHER_FILE fi done << EOS $LEASE_RAWS EOS |
実行時の標準出力は以下の通りで、一行ずつ /etc/ethers へ追記されます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
root@vWrt:~# ./dhcplease2ethers.sh 7c:10:c9:##:##:## is new host, ZS672KS 7c:10:c9:##:##:## is known as ZS672KS.lan 86:1e:0e:##:##:## is known as iPhone13mini.lan 24:0a:c4:##:##:## is new host, ESP32Cam4C 60:01:94:##:##:## is new host, ESP01EasyA2 5c:cf:7f:##:##:## is new host, ESPMatrix82 34:ca:81:##:##:## is new host, NX30ProA9 34:ce:00:##:##:## is known as chuangmi-plug.lan 3c:71:bf:##:##:## is new host, ESPeasy06 78:11:dc:##:##:## is known as MiCam360.lan e4:95:6e:##:##:## is new host, GL-AR750SE4 60:be:b4:##:##:## is new host, esx10 c8:2b:96:##:##:## is new host, ESP32Cam60 |
排他的論理和(XOR)を利用した更新法
上述のように、新しいホスト情報を一つ一つファイルに追記するのは少し忍びないので、以前にサイトへのスパムバックリンクを否認する際に学んだ「集合」の考え方を応用して、 /etc/ethers ファイルへまとめて追記するような手法を探ります。
まず、DHCPリースファイル、 ethers ファイルそれぞれのMACアドレスリストを連結(一旦全て小文字に変換)、
|
1 2 3 4 |
LEASE_MACS=$(grep -i '[0-9A-F]\{2\}\(:[0-9A-F]\{2\}\)\{5\}' $LEASE_FILE | awk 'BEGIN {OFS="\t"} {print tolower($2)}') ETHER_MACS=$(grep -i '^[0-9A-F]\{2\}\(:[0-9A-F]\{2\}\)\{5\}' $ETHER_FILE | awk 'BEGIN {OFS="\t"} {print tolower($1)}') echo "$LEASE_MACS\n$ETHER_MACS" |
そのどちらかにしかない(重複していない)MACアドレスを抽出、
|
1 |
XOR_MACS=$( echo "$LEASE_MACS\n$ETHER_MACS" | sort | uniq -u ) |
そのMACアドレス群をDHCPリースファイルへまとめて grep 検索すれば、それが ethers ファイルへ追記すべきリストに。
|
1 2 |
NEW_RAWS=$(grep "$XOR_MACS" $LEASE_FILE | awk 'BEGIN {OFS="\t"} {print toupper($2),$4}') echo "$NEW_RAWS" >> $ETHER_FILE |
以上の流れでスクリプトを組んでみました。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#!/bin/sh LEASE_FILE='/tmp/dhcp.leases' ETHER_FILE='/etc/ethers' n=0 LEASE_MACS=$(grep -i '[0-9A-F]\{2\}\(:[0-9A-F]\{2\}\)\{5\}' $LEASE_FILE | awk 'BEGIN {OFS="\t"} {print tolower($2)}') ETHER_MACS=$(grep -i '^[0-9A-F]\{2\}\(:[0-9A-F]\{2\}\)\{5\}' $ETHER_FILE | awk 'BEGIN {OFS="\t"} {print tolower($1)}') XOR_MACS=$( echo "$LEASE_MACS\n$ETHER_MACS" | sort | uniq -u ) if [ -n "$XOR_MACS" ]; then NEW_RAWS=$(grep "$XOR_MACS" $LEASE_FILE | awk 'BEGIN {OFS="\t"} {print toupper($2),$4}') n=$(echo "$NEW_RAWS" | wc -l) if [ -n "$NEW_RAWS" ]; then echo "$NEW_RAWS" echo "Append $n New Host(s) to $ETHER_FILE" echo "$NEW_RAWS" >> $ETHER_FILE else echo "Nothing New, Batch End." fi else echo "Nothing New Irregularly..." fi |
実行結果は以下の通り、最後にまとめて追記するので生理的にも安心です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
root@vWrt:~# ./dhcplease2ethers.sh 80:56:F2:##:##:## BRAVIA800 54:C9:DF:##:##:## UBOX4W 84:14:4D:##:##:## VPCSA26GGu 14:BB:6E:##:##:## UA43J5500 7C:10:C9:##:##:## ZS672KS 24:0A:C4:##:##:## ESP32Cam4C 60:01:94:##:##:## ESP01EasyA2 5C:CF:7F:##:##:## ESPMatrix82 34:CA:81:##:##:## NX30ProA9 34:CE:00:##:##:## chuangmi-plug.lan 3C:71:BF:##:##:## ESPeasy06 E4:95:6E:##:##:## GL-AR750SE4 60:BE:B4:##:##:## esx10 Append 13 New Host(s) to /etc/ethers |
ルータからアクセスポイントへ転送
次にルータ機で生成した /etc/ethers ファイルをネットワーク上の他のOpenWrt機器へ配布する仕組みを構築します。
まず配布元のルータ機の公開鍵を、配布先のOpenWrt機器の authorized_keys へ登録して、シームレスなアクセスができるようにしておきます。
|
1 |
/etc/dropbear/authorized_keys |
計3機ある配布先の情報を配列に格納して、 scp によるファイル転送をループで回して、と思い付くも、配列が使えるのはBashだけ。
|
1 2 3 4 5 6 7 |
#!/bin/bash ETHER_FILE='/etc/ethers' DEVS=("root@192.168.51.2" "root@192.168.51.3" "root@192.168.51.4") for dev in "${DEVS[@]}" do scp -q $ETHER_FILE "$dev:/etc/ethers done |
仕方ないのでAshではまた、改行区切りのリストのような文字列で格納してループを回すことにしました。
|
1 2 3 4 5 6 7 8 9 10 11 |
#!/bin/sh ETHER_FILE='/etc/ethers' DEVS=" \ root@192.168.51.2 \ root@192.168.51.3 \ root@192.168.51.4" for dev in $DEVS do scp -q $ETHER_FILE "$dev:/etc/ethers done |
完成したスクリプトをスケジュールタスクに登録
ようやく完成したスクリプトの全体像は次の通りです。
|
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 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#!/bin/sh # UPDATE /ETC/ETHERS FILE FROM /TMP/DHCP_LEASES LEASE_FILE='/tmp/dhcp.leases' ETHER_FILE='/etc/ethers' DEVS=" \ root@192.168.51.2 \ root@192.168.51.3 \ root@192.168.51.4" n=0 # PUSH FILE VIA SCP push_file () { for dev in $DEVS do #PING CHECK devip=`echo $dev | cut -d"@" -f2` ping -c 1 $devip >> /dev/null if [ $? -eq 0 ]; then #SCP PUSH echo "$ETHER_FILE --> $dev:/etc/ethers" res=`scp -q $ETHER_FILE "$dev:/etc/ethers"` else echo "$devip Offline." fi done } # LEASE FILE EXISTS CHECK echo "[`date +"%Y/%m/%d(%a) %H:%M:%S"`]" if [ ! -e $LEASE_FILE ]; then echo "DHCP Lease File ($LEASE_FILE) Not Found, Aborting..." exit 0 fi # GEN MAC LISTS --> XOR --> NEW ROWS LEASE_MACS=$(grep -i '[0-9A-F]\{2\}\(:[0-9A-F]\{2\}\)\{5\}' $LEASE_FILE | awk 'BEGIN {OFS="\t"} {print tolower($2)}') ETHER_MACS=$(grep -i '^[0-9A-F]\{2\}\(:[0-9A-F]\{2\}\)\{5\}' $ETHER_FILE | awk 'BEGIN {OFS="\t"} {print tolower($1)}') XOR_MACS=$( echo "$LEASE_MACS\n$ETHER_MACS" | sort | uniq -u ) if [ -n "$XOR_MACS" ]; then NEW_RAWS=$(grep "$XOR_MACS" $LEASE_FILE | awk 'BEGIN {OFS="\t"} {print toupper($2),$4}') n=$(echo "$NEW_RAWS" | wc -l) if [ -n "$NEW_RAWS" ]; then echo "$NEW_RAWS" echo "Append $n New Host(s) to $ETHER_FILE" echo "$NEW_RAWS" >> $ETHER_FILE echo "--SCP_TRANSFER--" push_file else echo "Nothing New, Batch End." exit 0 fi else echo "Nothing New Irregularly..." fi |
これをルータ機のスケジュールタスク cron へ登録、取り敢えず1日1回の頻度で様子を見ます。
|
1 |
11 21 * * * /root/dhcplease2ethers.sh > /tmp/dhcplease2ethers.log 2>&1 |
直近の実行結果は /tmp へ出力されたテキストファイルで確認。公衆ネットワークではないので、新しいデバイス情報の追加はそうは発生しません。
|
1 2 3 |
root@vWrt:/# cat /tmp/dhcplease2ethers.log [2024/03/06(Wed) 21:11:01] Nothing New, Batch End. |







