jlbhs.gif (6180 バイト) jlb.jp/hsinstall8/keepalive/

PerlでDDNS更新スクリプトを動かす。
( corega & asahi-net adsl 固定IP対応 )
 2003/8/02

 

 

<凡例>
赤字=入力する文字列
青字=表示される文字列(コンソール画面)
紫字=エディタ等で入力する文字列
緑字=特に強調するコメント等
  使わなくなった古い記述。

 


■約1年近くDDNSの更新にPerlスクリプトを使ってきましたが、2003年7月からASAHI−NETがADSLの固定IPを月額500円で提供するサービスを開始し、8月1日より申し込みました。

ADSLの固定IPは、例えばルータを再起動してもIPアドレスが変わらず(当然ですが)、すぐに接続状態になります。
しかし、回線が切断されたり、モデムの電源を落とした時には、モデムと回線のアイドル状態が終わるまで(5分程度)回線がダウンしてしまいます。
そのため、以前のスクリプトはDDNSの更新をメインにしていましたが、今回は、回線断等を検出して接続状態に復帰させるキープアライブ的な機能を盛り込んでみました。
 また、前バージョンは異常時の携帯メール等への発信に回数制限が無かったので、それも追加しています。

このままDDNSの更新スクリプトとして使うことも出来ると思います。

本当は、 BA8000 Pro (NTT-ME) 等で、キープアライブを設定するだけで使いたいのですが、フレッツ24Mを導入した後にでも考えるつもりです。

(注意)本スクリプト利用で、不具合が発生するかもしれません。参考にされるときは自己責任で実行してください。

 

1.仕様の定義

最初に定義を書きますが、本当は頭の中で考えていたことの中で、実現できた事を記述していますので本来の作り方では無いと思いますが、見るときにははっきりしていたほうが良いので、あえて記述します。

要求仕様
1.常駐動作をすること。

2.ログが残せること。

3.電源ONからの自動実行や、再実行が可能なこと。

4.異常時は、携帯電話にメール発信できること。

5.その他、気ままに機能追加ができること。

6.携帯メール等への発信回数が制限でき、その設定がプロセスを止めずに変更できること。(新)

 

2.DDNS更新のための Perl スクリプト


起動方法等の動作環境は後に記述しますが、簡潔な構成にするため、 Perl スクリプトの中だけで主な処理が完結するように、 DDNS 更新サイトの情報や、IPアドレスを取得するルーター固有の情報を、変数としてあらかじめ記述する方式を取りました。
そのため、DDNSサイトの違いやルーターの違いが発生したときは、そのたびに調査・実験が必要になります。 

Perl スクリプトは、 /usr/local/bin/corega/ に置くことにして、次のファイルを作成します。
[root@hs8 root] cd /usr/local/bin
[root@hs8 bin] mkdir corega
[root@hs8 corega] vi domupd.pl

#!/usr/bin/perl -w

#--------------------------------------------------------------------------------------
# my-domain でダイナミックDNSを利用する時の、DNS更新スクリプト
# ただし、このスクリプトは使用するBBルーターに依存する部分があります。
# また、常駐(デーモン)動作をさせるため、できるだけ終了しないように考慮しました。
# (C) je3jlb: 2002/08/27
#
# 2002/09/23 ログの時間表記を変更 yyyy/mm/dd HH:MM:SS
#
# 2003/08/01 asahi-net のADSL−固定IP(500円/月)を申し込んだ関係で、
#            回線の切断を想定した監視機能を加えた。(Ver 2.0)
#
# http://www13.cds.ne.jp/~ohsaru/perl/lwp.html  <--- 参照URL( TNX )
#
# 指定ルーター Corega BAR SW-4P 
#
# 
# 実行場所		: /usr/local/bin/corega/
# ルータの場所	: 192.168.1.1
#
#
# 起動は、
# [root@hs7 corega]# nohup perl domupd.pl &				バックグランドで実行。
#
# デーモンとして稼動しているかを確認するには、
# [root@hs7 corega]# ps -x|grep perl
#  2999 pts/0    SN     0:00 perl /usr/local/bin/corega/domupd.pl
#  3088 pts/0    S      0:00 grep perl
# [root@hs7 corega]#
# デーモンを停止するには、
# [root@hs7 corega]# kill プロセスID [ENT]  上記の例で言えば、 2999 が該当する。
#
#
# ブート後の自動実行設定は
# [root@hs7 root]# vi /etc/rc.local
# rc.local の最下行あたりに以下の記述を追加する。
# 		nohup は、ログアウトしてもプログラムを実行し続けるコマンド。
# 		& は、バックグランドで実行。
# -----------------------------------
# 		fi
# 		if [ -f /usr/local/bin/corega/domupd.pl ]; then
# 			nohup perl /usr/local/bin/corega/domupd.pl &
# 		fi
# fi      # <------- (最下行)
# -----------------------------------
# 実行時に nohup.out が / (最上位ディレクトリィー)にできる。
#    nohup.out には、 STDERR の内容が書き込まれる。( warn 関数)
#
#--------------------------------------------------------------------------------------

$RunDir			= "/usr/local/bin/corega/";		# 実行ディレクトリー(重要)

# IPアドレス取得に関する設定(IPアドレスをルーターから取得)
$RouterId		= "xxxxxx";
$RouterPasswd	= "xxxxxxx";
$CheckInterval	= 120;					# 秒単位で指定。(初期値は5分 = 300 秒とする)
$IpnowFile		= "ipnow.ddns";				# 取得したIPアドレス

# ダイナミックDNSの更新サイトに関する設定。
$DdnsId			= "REG-xxxxxxxxxxxxxxx";
$DdnsPasswd		= "xxxxxxx";
$DomainName		= "jlb.jp";

# 異常時の記録に関する設定。(不要時は先頭に # を付ける。)
$MailAdr		= "xxxxxx2\@xxxxxxxxx.jp";	# 緊急連絡のメールアドレス。2002/11:upd
$LogName		= "log_domupd.ddns";			# 検証用ログをファイルに出力する。(フルパスで指定)
$MailMaxFile	= "mailmax.ddns";			# メール発呼制限回数 保存ファイル (Ver 2.0)
$MaxMail		= 10;					# 最大メール発呼回数


# ブラウザ情報の設定
$AgentName = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)";
# Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)


# あらかじめ設定しておく変数
$ReadIP			= "0";
$GetIP			= "0";
$LinkStatus		= "1";	# 2003年8月1日 asahi-net の固定IP−ADSLに変えたため追加。 (Ver 2.0)
						#  0 = LinkDowen  1 = LinkActive
$MailCnt		= 0;		# 異常時のメール発呼回数を制限するためのカウンター


use LWP::UserAgent;
use LWP::Debug;

$ua = new LWP::UserAgent;
$ua->agent($AgentName);


errorlog("domupd プロセスをスタートしました。");

OUTERLOOP: while (1){
	$ReadIP = ReadIpFile();				# ファイルからIPアドレスを読み出す。
	$MailCnt = ReadMmFile();

	INTERLOOP: while (1){
		$GetIP = RouterGet();			# ルーターからグローバルIPを取得
		if ($GetIP eq "0.0.0.0"){		#  "0" なら読み込み失敗
			DummyReq();					# ダミーのリクエストをかける。
			sleep(60);					# 小休止
			$LinkStatus = "0";			# Lineが切れたことをセット (Ver.2) 
			next INTERLOOP;				# ループを最初から実行。
		}
		if ($LinkStatus eq "0"){		# Ver 2.0
				$LinkStatus = "1";		# フラグの復旧 Lineが接続状態になったことをセット (Ver 2.0)
				$MailCnt = ReadMmFile();		# 2003/08/11:記述位置を修正
			errorlog("==Linkの切断が発生しました。 (V2) == $MailCnt ");
				mail("==Linkの切断が発生しました。 (V2) == $MailCnt ");
				SaveMmFile();			# メール発呼回数の残数をセーブする。
		}
		if ($ReadIP ne $GetIP){			# ファイルからのIPと同じでないなら
			errorlog("==IPアドレスが変化しました。== $ReadIP --> $GetIP");
			$_ = RegistReq();			# DDNSへ登録
			if (m/200/g){
				errorlog("IPアドレスの変更に成功しました。 $GetIP");
					SaveIpFile();				# ルーターからGETしたIPをセーブする。
					$ReadIP = $GetIP;
				}
				elsif (m/300/g){
					errorlog("IPアドレスの変更はありませんでした。 $GetIP");
					SaveIpFile();				# ルーターからGETしたIPをセーブする。
					$ReadIP = $GetIP;
				}
				else{
					errorlog("IPアドレスの登録に失敗しました。 $GetIP");
			}
		}
		sleep($CheckInterval);			# 指定したインターバル時間(初期値=5分)
	}
	sleep(10);							# 少し休む。
warn;									# 警告を発する。
}
errorlog("OUTERLOOP を抜けて終了しました。");
mail("OUTERLOOP を抜けて終了しました。");
exit 0;



# ======================= Sub Routines ============================

# ルーターからIPアドレスを取り込む。=============
sub RouterGet{
	my $req = new HTTP::Request(GET => "http://${RouterId}:${RouterPasswd}\@192.168.1.1/_SystemInfo.htm");

	my $res = $ua->request($req);
	if ($res->is_success) {
		# Check IP Address	コレガのグローバルIPの位置
		$_ = $res->content;
		if (m/WAN/gc){		# WAN 状態 の後: S-JIS のため WAN だけ使用する。
			if (/(\d+)\.(\d+)\.(\d+)\.(\d+)/gc)	   # 最初に現れる 999.999.999.999 パターン
			{ $gip = $&;	# 次にサブネットマスク 255.255.255.255 が合致すればOKとする。
					if	(/(255)\.(255)\.(255)\.(255)/gc) {
						return $gip;			 # 正常終了は取得したIPアドレスを返す。
					}
			}
		}
	}
	return "0.0.0.0";				# 異常終了は 文字の "0.0.0.0" を返す。
}

# 再接続を促すために、WAN側の自サイトへリクエストをかける。=======
sub DummyReq {
	my $req2 = new HTTP::Request(GET => "http://www.yahoo.co.jp/");
	# ダミーのリクエストなので、レスポンスの受け取りだけ。
	my $res2 = $ua->request($req2);
}


# 新しいIPアドレスをDdnsのサイトに登録する。=======
sub RegistReq {
	my $req3 = new HTTP::Request(GET => "http://${DdnsId}:${DdnsPasswd}\@my-domain.jp/dyndns/?host=${DomainName}");
	# レスポンスを受け取り、正常時はその内容を返す。
	my $res3 = $ua->request($req3);
	if ($res3->is_success) {
		return $res3->content;
	}
	return "0";			# 異常終了は 文字の "0" を返す。
}


# IPアドレスをファイルに記録する。=============
sub SaveIpFile {
	if (! open(FOUT,"> $RunDir$IpnowFile")) {
		errorlog("Save Error> $IpnowFile");
		return;
	}
	print FOUT "$GetIP";
	close(FOUT);
}

# IPアドレスを記録したファイルから読み出し。=========
sub ReadIpFile {
	my($rip);
	if (! open(FIN,"< $RunDir$IpnowFile")) {
		MakeIpFile();
			if (! open(FIN,"< $RunDir$IpnowFile")) {
				errorlog("Read Error> $IpnowFile");
				return "0";
			}
	}
	$rip = <FIN>;
	chomp($rip);
	close(FIN);
	return $rip;			# IP Address
}

# IPアドレスを記録するファイルを作る。==========
sub MakeIpFile {
	if (! open(FOUT,"> $RunDir$IpnowFile")) {
		errorlog("Make Error> $IpnowFile");
		return;
	}
	print FOUT "0";		# 初期値は、 0 としておく。
	close(FOUT);
}



# メール発呼制限回数をファイルに記録する。=============
sub SaveMmFile {
	if (! open(FOUT,"> $RunDir$MailMaxFile")) {
		errorlog("Save Error>$MailMaxFile");
		return;
	}
	print FOUT "$MailCnt";
	close(FOUT);
}

# メール発呼制限回数を記録したファイルから読み出し。=========
sub ReadMmFile {
	my($rmc);
	if (! open(FIN,"< $RunDir$MailMaxFile")) {
		MakeMmFile();
			if (! open(FIN,"< $RunDir$MailMaxFile")) {
				errorlog("Read Error> $MailMaxFile");
				return 0;
			}
	}
	$rmc = <FIN>;
	chomp($rmc);
	close(FIN);
	return $rmc;			# MailCount
}

# メール発呼制限回数を記録するファイルを作る。==========
sub MakeMmFile {
	if (! open(FOUT,"> $RunDir$MailMaxFile")) {
		errorlog("Make Error> $MailMaxFile");
		return;
	}
	print FOUT $MaxMail;		# 
	close(FOUT);
}



# Mail を発信する。==================
sub mail {
	if ($MailAdr){				# Mail Address を定義している時だけ実行
		if ($MailCnt ge 1){
			my ($mess) = @_;
			if (! open(MAIL,"| /usr/sbin/sendmail -t")) {
				errorlog("Open Error> mail");
				warn;
				return;
			}
				print MAIL "To: $MailAdr\n";
				print MAIL "From: domupd\n";
				print MAIL "Subject: DDNS Info\n";

# Mail 着信の時間がずれるので、発信時間を加えるよう変更した。 2003/08/08

			@ret = localtime(time);
			$time_str = sprintf("%04d/%02d/%02d %02d:%02d:%02d ",
				 $ret[5]+1900, $ret[4]+1, $ret[3], $ret[2], $ret[1], $ret[0]);
			print MAIL $time_str, "\n", $mess, "\n";

				close(MAIL);
				$MailCnt--;		# 最大メール回数から1を減算
		}
	}
}

# エラーログを記録する。=================
sub errorlog {
  my($mess) = @_;
	if ($LogName){
		if (! open(FOUT,">> $RunDir$LogName")) {
			#print "Error> can't open log file\n";
			mail("Open Error> $IpnowFile");
		warn;
		return;
		}

			@ret = localtime(time);
			$time_str = sprintf("%04d/%02d/%02d %02d:%02d:%02d ",
				 $ret[5]+1900, $ret[4]+1, $ret[3], $ret[2], $ret[1], $ret[0]);
			print FOUT $time_str, " = ", $mess, "\n";
			close(FOUT);


#			$nowtime = localtime;
#			print FOUT $nowtime, " = ", $mess, "\n";
#			close(FOUT);
	}
}

# 主な変更部分は、ミドリ色の太字で表記しています。

スクリプトに、パスワード情報が含まれていますので、 root 以外から覗けないようにパーミッションを変更します。

[root@hs8 corega] chmod 700 domupd.pl

3.domupdの実行環境を設定する。


コマンドラインからの起動は、

 [root@hs8 corega]# nohup perl /usr/local/bin/corega/domupd.pl &
とします。

nohup は、ログアウトしてもプログラムを実行し続けるコマンド。
 & は、バックグランドで実行の意味。

 

電源ON等の起動時の指定は、/etc/rc.local ファイルの最後のあたりに、下記の行を追記します。

[root@hs8 root]# vi /etc/rc.local

-----------------------------------
    fi
        if [ -f /usr/local/bin/corega/domupd.pl ]; then
            nohup perl /usr/local/bin/corega/domupd.pl &
        fi

fi #
<------- (最下行)
-----------------------------------

電源ONまたは reboot からの実行時には nohup.out が / (最上位ディレクトリィー)にできます。
nohup.out には、 STDERR の内容が書き込まれます。( warn 関数のところも)

 

実行ディレクトリィーの内容(サンプル)は、以下のようなかんじに成ります。
ここの、 nohup.out は corega/ ディレクトリィーで実行したときにできたものです。 
また、通常でしたらIPアドレスが変化しても、nohup.out には何も出力されず size = ゼロ のままだと思います。

  [root@hs8 corega]# ll
合計 28
-rwx------    1 root     root         8845  8月  2 21:54 domupd.pl*
-rw-r--r--    1 root     root           15  8月  1 21:17 ipnow.ddns
-rw-r--r--    1 root     root         7096  8月  2 21:56 log_domupd.ddns
-rw-r--r--    1 root     root            2  8月  2 21:56 mailmax.ddns
-rw-------    1 root     root            0  8月  2 21:56 nohup.out
[root@hs8 corega]#  
メール発呼残回数が少なくなったら、 mailmax.ddns を直接 vi 等で開いて、数字を大きくしてください。
もし、あまりに大きな数字をセットすると、万が一異常事態が続いたとき、メールの発呼がこの回数まで止まらなくなります。(Linkが切れたり復旧したりを続けた場合)

vi mailmax.ddns

     
  

以下は、何回か実験を行っている時のログファイルの内容です。

2003/08/01 21:17:23 = ==IPアドレスが変化しました。== 211.13.138.13 --> 210.253.xxx.xxx
2003/08/01 21:17:23 = IPアドレスの変更に成功しました。 210.253.xxx.xxx 
<== 申し込み後、最初の更新が行われて、それ以後は、IPの変化はありません。
2003/08/01 22:47:21 = domupd プロセスをスタートしました。
2003/08/02 00:10:24 = domupd プロセスをスタートしました。 
2003/08/02 00:20:36 = ==Lineの切断が発生しました。 (V2) == 10 
<== 一番右の数字は、メール警告の
2003/08/02 06:15:24 = ==Lineの切断が発生しました。 (V2) == 9               
発呼残回数(テストで切断)
2003/08/02 07:35:34 = ==Lineの切断が発生しました。 (V2) == 8 
2003/08/02 07:50:56 = domupd プロセスをスタートしました。 
2003/08/02 08:03:38 = ==Linkの切断が発生しました。 (V2) == 0  
<== 発呼残回数がゼロになるとLOGには記録
2003/08/02 08:30:40 = ==Linkの切断が発生しました。 (V2) == 0              
されるが、メールは発呼しない。
2003/08/02 08:46:33 = domupd プロセスをスタートしました。

 

4.デーモンとして稼動しているかを確認する。


デーモン稼動の確認には、次の ps -x コマンドを使用します。

[root@hs8 corega]# ps -x|grep perl
2999 pts/0     SN   0:00 perl /usr/local/bin/corega/domupd.pl
3088 pts/0     S     0:00 grep perl

[root@hs8 corega]#


デーモンを停止するには、

[root@hs8 corega]# kill プロセスID[ENT]     <== プロセスID部は上記の例で言えば、 2999 が該当する。

kill コマンドを実行後は、下記のように ps -x コマンドの行だけが出力されます。
root@hs8 corega]# ps -x|grep perl
3088 pts/0     S     0:00 grep perl
[root@hs8 corega]#


11ヶ月ぶりに、メールの最大発呼回数の制限を記述しました。
(2003年8月2日から)