Cisco IPv6環境でのDDNS定期実行 #NW編

# まえがき
Ciscoルータ C800Mを使用したIPv6環境で、DDNSを定期実行する必要に迫られて試行錯誤したときのメモ。
私はあまりNW系の仕事はしない上に滅多にCisco機器も触らないので、詳しい人からすれば恐らくもっと良い方法があるのではないかと思います。

# 環境
* 使用するルータは、Cisco C800M Version 15.5
* IPv4アドレスは一切保たず、全てIPv6アドレス
* 固定アドレスではなく、不定期にアドレスが変わってしまう環境にある
* そのため、変更後のIPv6アドレスをDDNSサーバに通知し、その他NW機器たちはそのDDNSサーバを参照することで、常に同一ホストに到達できるようにする。
* 各ルートもIPv6アドレスのものしか存在しない
* 特定のインターフェースが持っているIPv6アドレスをその向こう側に存在するDDNSサーバにHTTP GET通信にて通知する
* 具体的に本記事上では、GigabitEthernet0/5を想定し、DDNSサーバのIPv6アドレスは2001:1:1:1::3、このDDNSサーバに対してFE80から始まるリンクローカルアドレスを通知する

# 前提
標準で用意されているip ddns update xxxx というコマンドを使えばDDNSは使えるらしいのだけど、どうもこのコマンドではIPv6はサポートしてないらしい。
Cisco IOSはIPv6系のコマンドの場合、ipv6 xx xxっていうコマンド体系になるので、ipv6 ddns updateってのがあればよいのだけど、それもない模様。

```
ex)
IPv4の場合 ip route 10.0.0.0 255.0.0.0 192.168.0.1
IPv6の場合 ipv6 route 2001::/126 2000::2
```
というわけで、王道なやり方じゃ出来ないっぽいので、少しごにょごにょ頑張る。


# DDNSサーバについて
* フリーで良いサーバアプリケーションないかなぁと探したのだけど、良い感じのが見つからなかったので、DNSサーバ自体はBINDで作り、HTTPサーバとして待ち受ける部分はpythonで自作
※この辺はあとでDDNSサーバ編として別記事に書きます

* DDNSサーバへの通知は、/ddns?key=[任意の英数字文字列]&ipaddr=[自身のIPv6アドレス]といった形でGETを投げると更新処理がされる
* keyの英数字文字列は自分が誰なのかを表す一意の値であり、ある種の共通鍵のようなもの
※この値が知られると余裕でDNSポイゾニングされちゃうことになるので、絶対に知られないようにしないといけない
※だから本来はhttpsでやらないとね・・・

# 作業項目
大きくわけると、2つやることがある。
* IPv6アドレスを取得し、HTTP GET Requestを投げるtclスクリプトを作成
* 定期的にtclスクリプトを実行するようにkronで指定

# TCLって何？
Tool Command Language というCisco内部で動作させられる軽量スクリプト言語
私も詳しくないので詳細な説明はできないのだけど、今回初めて触った感想として言うと、本来NW機器といえば決められたコマンドを組合せてコンフィグを作っていくもので、メーカ側がそういったコマンドを用意していない限り、自由度なんてないに等しいようなものなんだけど、TCLを使えば細かい所まで手が届くようになり、飛躍的に自由度が上がる超絶武器、だと個人的には思っている。

※yamahaにおけるluaだと思えば良い、といってもNWエンジニアじゃないとピンとこないかもしれない・・・プログラマの方からすれば、え？あのヤマハがNW機器作ってるの？しかもlua動くの？って感じかも。

# kronって何？
UNIX/Linuxでいうcron、Windowsでいうタスクスケジューラだと思って下さい。

# 実作業

## tclスクリプトを作成
本来、Cisco IOS上では例えばviのようなエディタ機能は存在しないので、PCで予め作っておいたスクリプトをTFTPで持ってくるとかそういった面倒くさいことをやらなくちゃいけないのだけれど、tclsh内でputsとopenを組み合わせることでCisco IOSのみで内部に直接スクリプトを保存できる。
tclshというのは、調べてないけど多分tclsh(ell)の略なんだと思う。
特権EXECモードから**「tclsh」**を実行すると、プロンプトが切り替わり、様々なtclコマンドが使えるようになる。

というわけで、作成したいスクリプトはこちら。

```
regexp FE80.*$ [exec "sh ipv6 int brief gi0/5 | include FE80"] ipaddr
set url http://2001:1:1:1::3/ddns?key=abcde&ipaddr=[set ipaddr]
exec [more $url]
```

* regexpで文字列の中から正規表現で抽出したものを変数に代入する
* execはtclスクリプトの中でCisco IOSのコマンドを実行したいときに使うtclのコマンド
* setコマンドは変数の代入および表示


実際の操作としては以下のような感じ。
上記のtclスクリプトを内部フラッシュメモリに、ddns.tclというファイル名で保存しています。

```
Test_RT# tclsh
Test_RT(tcl)# puts [open sdflash:/ddns.tcl w+] {
+>regexp FE80.*$ [exec "sh ipv6 int brief gi0/5 | include FE80"] ipaddr
+>set url http://2001:1:1:1::3/ddns?key=abcde&ipaddr=[set ipaddr]
+>exec [more $url]
+>}
Test_RT(tcl)#
Test_RT(tcl)# dir
Directory of sdflash:/

    1  -rw-    59319960   Aug 1 2016 08:42:28 +00:00  c800m-universalk9-mz.SPA.155-3.M3.bin
    2  -rw-        3068  Nov 18 2015 07:35:48 +00:00  cpconfig-8xx.cfg
    3  -rw-    58948364   Jun 6 2016 02:29:42 +00:00  c800m-universalk9-mz.SPA.155-3.M2.bin
    4  drw-           0  Nov 18 2015 07:36:26 +00:00  ccpexp
  459  -rw-        2885  Nov 18 2015 07:40:04 +00:00  home.shtml
  460  -rw-    59504768  Jun 10 2016 00:16:16 +00:00  c800m-universalk9-mz.SPA.156-2.T.bin
  461  -rw-         720  Dec 15 2016 06:45:10 +00:00  vlan.dat
  462  -rw-         153  Dec 19 2016 06:57:40 +00:00  ddns.tcl
```



## 定期実行するようにkronで指定
前の工程で作成したtclスクリプトを1分間隔で定期実行する

```
kron occurrence ddns_schedule in 1 recurring
 policy-list ddns_policy

kron policy-list ddns_policy
 cli tclsh sdflash:ddns.tcl
```

# 参考
非常に参考にさせて頂いたWebサイト様たちです。本当に感謝申し上げます。

[Tclsh を使い、ルータ上でスクリプトを実行する](http://d.hatena.ne.jp/eco31/20091219/1261205259)
[kron - 定期実行タスク](http://changineer.info/network_ccie_v4/cisco_sysmanage/cisco_system_management_kron)
[Generate HTTP(S) requests from Tcl shell](http://blog.ipspace.net/2009/01/generate-https-requests-from-tcl-shell.html)
[Ciscoルータで tcl スクリプト監視の仕組み](http://oatrap485.blogspot.jp/2013/02/cisco-tcl.html)