YAMAHAのRTXからVALUE-DOMAINのDDNS更新をするLuaスクリプト

投稿者: | 2020年2月25日

目次

概要

YAMAHAルータはLuaを走らせることが出来る。
折角なのでDiCEでやってるDNSの更新をルータに任せようと思う。

前提とか

  • WANインターフェース(lan2)がグローバルIPを持つ
  • VALUE-DOMAINのDDNS
  • 20分に1回チェックする
  • 一度更新したら2時間は更新しない(DNSの反映に最大1時間強かかるため)
  • 文字コードはデフォルトのまま(SJIS)
  • 実行したら何らかのSYSLOGを残す
  • 特定のホスト名だけ更新したい

動き

  1. 20分に1回スケジュール実行
  2. 過去6回分(2時間分)のSYSLOGを検索し、"DDNS updated"が存在していたら終了
  3. nslookupでIPを取得
  4. グローバルIPを取得
  5. 取得した2つのIPを照合し、違っていたら更新する
  6. 複数のホストを更新する場合、1つ更新したら10秒待つ(一気にやるとちゃんと更新されなかったので)

注意点

LuaスクリプトはSJISで保存すること。
ターミナルをUTF-8に設定してようがLuaスクリプト内に日本語が含まれる場合はSJISでなければ無事にバグる。

Q. じゃあ英語/ASCIIに設定すればいいのでは?
A. 折角の日本製なんだから日本語使いたいじゃん?それはそれとしてSJISはクソ

参考にしたもの

全体的なイメージはこちらを参考に、

YAMAHA RTXシリーズでDDNSをLuaで更新通知するスクリプト - Github

split関数はこちらをお借りしました。

Lua言語の便利スニペットあれこれ - Qiita

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でログが溢れる。

コメントを残す

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