目次
概要
YAMAHAルータはLuaを走らせることが出来る。
折角なのでDiCEでやってるDNSの更新をルータに任せようと思う。
前提とか
- WANインターフェース(lan2)がグローバルIPを持つ
- VALUE-DOMAINのDDNS
- 20分に1回チェックする
- 一度更新したら2時間は更新しない(DNSの反映に最大1時間強かかるため)
- 文字コードはデフォルトのまま(SJIS)
- 実行したら何らかのSYSLOGを残す
- 特定のホスト名だけ更新したい
動き
- 20分に1回スケジュール実行
- 過去6回分(2時間分)のSYSLOGを検索し、"DDNS updated"が存在していたら終了
- nslookupでIPを取得
- グローバルIPを取得
- 取得した2つのIPを照合し、違っていたら更新する
- 複数のホストを更新する場合、1つ更新したら10秒待つ(一気にやるとちゃんと更新されなかったので)
注意点
LuaスクリプトはSJISで保存すること。
ターミナルをUTF-8に設定してようがLuaスクリプト内に日本語が含まれる場合はSJISでなければ無事にバグる。
Q. じゃあ英語/ASCIIに設定すればいいのでは?
A. 折角の日本製なんだから日本語使いたいじゃん?それはそれとしてSJISはクソ
参考にしたもの
全体的なイメージはこちらを参考に、
split関数はこちらをお借りしました。
ddns_update.lua
静的DNSレコードを登録しているために普通にドメイン聞きに行くとローカルIPが帰ってくるので、チェック用ホスト名を用意してそちらをnslookupする。
あとまあ細かい話はしないのでソース読んでください。
わりとゴリ押し感はある。きっとプロはもっと上手く書く。
以下の例だと domain.com , check.domain.com , host1.domain.com , host2.domain.com を更新する。
------------------------------------
-- 設定
------------------------------------
-- ドメイン
domain = "domain.com"
-- ホスト名
-- [1] : @
-- [2] : チェック用
-- [3] : これ以降任意の
hosts = {
"@",
"check",
"host1",
"host2"
}
-- DDNS更新パスワード
pwd = "password"
-- SYSLOG検索数
search_max = 6
----------------------------------------------------------
-- 文字列からIPv4アドレスを取得
----------------------------------------------------------
function get_ipv4_address(str)
local s1, s2, rtn, addr
s1, s2 = string.find(str, "[0-9.]+")
if s1 then
addr = string.sub(str, s1, s2)
end
return addr
end
----------------------------------------------------------
-- リクエストテーブル作成
----------------------------------------------------------
function create_reqest_table(domain, pwd, host, ipv4)
local url
url = "https://dyn.value-domain.com/cgi-bin/dyn.fcg?d="..domain.."&p="..pwd.."&h="..host.."&i="..ipv4
req_table = {
url = url,
method = "GET"
}
return req_table
end
----------------------------------------------------------
-- split
----------------------------------------------------------
function split(str, ts)
-- 引数がないときは空tableを返す
if ts == nil then return {} end
local t = {} ;
i = 1
for s in string.gmatch(str, "([^"..ts.."]+)") do
t[i] = s
i = i + 1
end
return t
end
----------------------------------------------------------
-- SYSLOGチェック
-- 20分に1回実行→過去6回(2時間)にDDNS updatedが無ければいい
----------------------------------------------------------
function update_check(max)
local rtn, str, key, s1, s2, log, i
rtn, str = rt.command("show log reverse | grep LUA_DDNS")
if rtn and str then
log = split(str, "\n")
if #log < 6 then
max = #log
end
for i = 1, max do
s1, s2 = string.find(log[i], "DDNS updated")
if s1 then
return false
end
end
end
return true
end
----------------------------------------------------------
-- メイン
----------------------------------------------------------
rtn = update_check(search_max)
-- true→実行 false→中止
if not rtn then
rt.syslog("info", "[LUA_DDNS] DDNS check aborted")
os.exit(0)
end
-- ipv4_addrとdns_addrを取得
rtn, get_str = rt.command("show status lan2 | grep IPアドレス")
if rtn and get_str then
ipv4_addr = get_ipv4_address(get_str)
if ipv4_addr then
rtn, get_str = rt.command("nslookup "..hosts[2].."."..domain)
if rtn and get_str then
dns_addr = get_ipv4_address(get_str)
if not dns_addr then
rt.syslog("info", "[LUA_DDNS] Failed to get forward lookup")
end
else
rt.syslog("info", "[LUA_DDNS] Failed to get forward lookup info")
end
else
rt.syslog("info", "[LUA_DDNS] Failed to get IPv4 address")
end
else
rt.syslog("info", "[LUA_DDNS] Failed to get IPv4 address info")
end
-- グローバルIPとDNS正引きIPが違っていたら更新
if ipv4_addr and dns_addr then
if (ipv4_addr == dns_addr) then
rt.syslog("info", "[LUA_DDNS] DDNS checked")
else
for key, host in ipairs(hosts) do
rt.httprequest(create_reqest_table(domain, pwd, host, ipv4_addr))
rt.sleep(10)
end
rt.syslog("info", "[LUA_DDNS] DDNS updated : "..ipv4_addr)
end
end
スケジュール登録
luaファイルをルータにコピーして毎時0分、20分、40分に動くようスケジュール登録
schedule at 1 */* *:00 * lua /lua/ddns_update.lua
schedule at 2 */* *:20 * lua /lua/ddns_update.lua
schedule at 3 */* *:40 * lua /lua/ddns_update.lua
動作確認
だいたいこいつらとDHCPDでログが溢れる。