Quantcast
Channel: Bashタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 2892

UPnPで取得したIPアドレスでDDNS更新

$
0
0

DDNS(DynamicDNS)のIPアドレスを定期更新

DDNSサービスのIPアドレス情報を更新するスクリプト。
UPnPと組み合わせるパターンを見かけなかった(知らないだけ?)ので、メモとして残しておきます。

  • WAN側IPアドレスの取得はUPnP(トラフィックは無駄に増やさない)、デフォゲからの応答のみ選別。
  • IPアドレスに変更がない時はリクエストを送信しない
  • 24時間に1回はリクエストを送信(DDNS側の失効防止。No-IPは30日更新がなければ失効)
  • ログはsyslogに送信

DDNSサービス(No-IP)

無料のDDNSサービスはいくつかありますが、No-IPを対象に説明。
他のサービスでも似たような感じのはずなのでスクリプトは適宜修正のこと。

UPnPクライアント

ルータのWAN側IPアドレス取得のためにMiniUPnPを使用。
フレッツのGWはじめ家庭用のルータなら大抵UPnP使えるはず。

apt-getでインストール

Raspbian(Debian系)なら一発。

# apt-get install miniupnpc

自分でビルド

MiniUPnPからminiupnpcのソースを取得。
./configureで始めるタイプではないので少し癖があるものの、まだ読める範囲なので指定できる引数はMakefile参照。
下記は基本的な例。

$ tar xfz miniupnpc-2.1.20191224.tar.gz
$ cd niupnpc-2.1.20191224
$ make           # クロスコンパイル時はmake引数にCC=*****-gcc のようにコンパイラを指定$ sudo su
# make install   # インストール先を指定したい場合はDESTDIR, INSTALLPREFIXをmake引数に指定(指定なければ/usr/bin/upnpc)

デフォゲのアドレス取得

UPnPクライアントは初めにブロードキャストを投げてサーバーからの応答を待つ。
miniupnpcは、UPnPサーバーのdescription URLを指定しないと、初めに応答があったサーバーの情報しか表示しない。ルータと(ルータを転用した)無線LANのAPなど複数のUPnP対応機器がネットワーク内にあると期待した情報を取得できなくなるので、ターゲット(=デフォゲ)にするIPアドレスを事前に確認しておく必要あり。

# default GW IP addrdeclare-rDEFAULT_GW_ADDR=`ip route | grep default | awk'{ print $3 }'`

UPnPでWAN側IPアドレス取得

デフォゲから返ってきたdiscoveryに対する応答(description URL)のみ拾って、外部IPアドレスを取得。

# retrieving External IP address using miniupnpcdeclare-rIGD_URL=`upnpc -P | grep-P"^ desc: http://${DEFAULT_GW_ADDR}:.+$" | grep-o-P"http://.+$"`if![[${IGD_URL}=~ http:\/\/.+$ ]];then
    echo"No UPnP server found."exit 1
fi
declare-rEXTERNAL_IP_NEW=`upnpc -u${IGD_URL}-s | grep ExternalIPAddress | grep-o-P"\d+\.\d+\.\d+\.\d+"`if![[${EXTERNAL_IP_NEW}=~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]];then
    echo"No valid External IP address found."exit 1
fi

No-IPのIPアドレス更新

とりあえず把握しているレスポンス一覧
* good : 更新成功
* nochg : アドレス変更なし
* nohost : 該当ホスト名なし
* badauth : 認証エラー

declare-rNOIP_ACCOUNT='YOURACCOUNT'declare-rNOIP_PASSWD='YOURPASSWORD'declare-rNOIP_HOSTNAME='YOURFQDN'declare-rNOIP_URL="https://${NOIP_ACCOUNT}:${NOIP_PASSWD}@dynupdate.no-ip.com/nic/update?hostname=${NOIP_HOSTNAME}"declare-rNOIP_STATUS=`curl --silent${NOIP_URL}`if[[${NOIP_STATUS}=~ ^good.+ ]];then
    echo"Updated ${EXTERNAL_IP} to ${EXTERNAL_IP_NEW}"elif[[${NOIP_STATUS}=~ ^nochg.+ ]];then
    echo"Keep current IP addr ${EXTERNAL_IP_NEW}"else
    declare-rRESP=`echo${NOIP_STATUS} | sed-e's/[\r\n]*$//'`${LOG_WARN}"Unknown response (${RESP})"fi

スクリプト全体

ddns_noip.sh
ddns_noip.sh
#!/bin/bash -u# No-IP configurationdeclare-rNOIP_ACCOUNT='YOURACCOUNT'declare-rNOIP_PASSWD='YOURPASSWORD'declare-rNOIP_HOSTNAME='YOURFQDN'declare-rNOIP_URL="https://${NOIP_ACCOUNT}:${NOIP_PASSWD}@dynupdate.no-ip.com/nic/update?hostname=${NOIP_HOSTNAME}"############################################################################################## working dir(should be in tmpfs area)declare-rRUN_DIR='/run/ddns_noip'declare-rCURRENT_IP_FILE="${RUN_DIR}/current_ip"declare-rLAST_UPDATED_FILE="${RUN_DIR}/last_updated"# syslogdeclare-rLOG_TAG='DDNS_NOIP'# No space permitteddeclare-rLOG_WARN="logger -p warning -t ${LOG_TAG} "declare-rLOG_INFO="logger -p info    -t ${LOG_TAG} "############################################################################################## make working directorymkdir-p${RUN_DIR}#  Previous External IP addressif[-f${CURRENT_IP_FILE}];then
    declare-rEXTERNAL_IP=`cat${CURRENT_IP_FILE}`else
    declare-rEXTERNAL_IP='0.0.0.0'fi# is miniupnpc installed?
which upnpc  > /dev/null
if[$?-ne 0 ];then${LOG_WARN}"No miniupnpc found."exit 1
fi# default GW IP addrdeclare-rDEFAULT_GW_ADDR=`ip route | grep default | awk'{ print $3 }'`# retrieving External IP address using miniupnpcdeclare-rIGD_URL=`upnpc -P | grep-P"^ desc: http://${DEFAULT_GW_ADDR}:.+$" | grep-o-P"http://.+$"`if![[${IGD_URL}=~ http:\/\/.+$ ]];then${LOG_WARN}"No UPnP server found."exit 1
fi
declare-rEXTERNAL_IP_NEW=`upnpc -u${IGD_URL}-s | grep ExternalIPAddress | grep-o-P"\d+\.\d+\.\d+\.\d+"`if![[${EXTERNAL_IP_NEW}=~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]];then${LOG_WARN}"No valid External IP address found."exit 1
fi# get time of last updatedif[-f${LAST_UPDATED_FILE}];then
    declare-rLAST_EPOCH=`cat${LAST_UPDATED_FILE}`else
    declare-rLAST_EPOCH=0
fi# get current epoch timedeclare-rTIME=`date`declare-rTIME_EPOCH=`date-u +%s`declare-rTIME_DIFF=`expr${TIME_EPOCH} - ${LAST_EPOCH}`# update IP address to No-IP if#  * elapsed 1 day since last update OR#  * IP address is changedif[${TIME_DIFF}-gt 86400 ]||[${EXTERNAL_IP}!=${EXTERNAL_IP_NEW}];then
    declare-rNOIP_STATUS=`curl --silent${NOIP_URL}`if[[${NOIP_STATUS}=~ ^good.+ ]];then
        echo${EXTERNAL_IP_NEW}>${CURRENT_IP_FILE}echo${TIME_EPOCH}>${LAST_UPDATED_FILE}${LOG_INFO}"Updated ${EXTERNAL_IP} to ${EXTERNAL_IP_NEW}"elif[[${NOIP_STATUS}=~ ^nochg.+ ]];then
        echo${EXTERNAL_IP_NEW}>${CURRENT_IP_FILE}echo${TIME_EPOCH}>${LAST_UPDATED_FILE}${LOG_INFO}"Keep current IP ${EXTERNAL_IP_NEW}"else
        declare-rRESP=`echo${NOIP_STATUS} | sed-e's/[\r\n]*$//'`${LOG_WARN}"Unknown response (${RESP})"fi
else${LOG_INFO}"Nothing to do (IP:${EXTERNAL_IP_NEW}, TIMEDIFF:${TIME_DIFF})"fi

exit 0

おまけ。crontabに登録

/etc/cron.d/ddns_noip.sh (パーミッションは0755)に上記ファイルがあるとして、10分おきに実行するように設定。
変更時||1日1回 しか外部にアクセスしない、相手に迷惑をかけない優しいスクリプト。

/etc/crontab
*/10 *****   root    /etc/cron.d/ddns_noip.sh

Viewing all articles
Browse latest Browse all 2892

Trending Articles