【追記】
IX2015とtunnelbroker.net(he.net)の組み合わせにおけるIPv6トンネル接続の実践記録については、2010年2月2日の記事をご参照下さい。
【更に追記:2013-06-06】
本稿で扱うfeel6ですが、2013年6月5日付で、サービスの終了が発表されました。
http://start.feel6.jp/announce/#20130605終了日時は、2013年 7月31日 (水) 12時00分 で、利用者に対しては有料プランである Feel6@DTI への移行がアナウンスされています。
残念ながら、ここやここの情報を見る限り、DTCP での接続は難しい上、一般的なルーターを通しての利用には難があるようで、IX利用者として現実的な代替サービスではありません。
他にIX2015で無料で利用できる現実的な選択肢は、HE.net、SixXS.net の他、v6ip.tsukuba.wide.ad.jp(SoftEther) 辺り‥?
前者二つはISP的に利用できる環境が限られたり、海外をピンポンします。当方環境はICMPパケットが返せない為、どちらもNG。
某筑波サーバーは、別途ブリッジ用のVPNクライアントが必要です。feel6にぶら下げるクライアントは、DTCPの認証系さえ動けばCPUパワーは必要ありませんでしたが、今回は Raspberry Pi とかだと速度が出ません。
さて、どうしたもんでしょうか‥
DTCPとは,要は(こちら側から見て)ローカルの終端アドレスが変化するIP-over-IPなわけですね。
やりたいこと
この辺りを参考に、Linux機とIX2015を組み合わせてFeel6を終端させます。今回は認証系の操作だけをLinuxにお任せし,トンネリング関係は全てルーターに丸投げすることにします。
これらは,IX2015の下にUbuntuが,NAPTの状態でぶら下がっている環境を想定しています。IX2015とUbuntuでアドレスが異なる場合,Ubuntuがアクセスをかける時にfeel6側から見えるグローバルアドレスに対して,IPIPトンネルが張られます。どちらもがグローバルアドレスを持っているような環境の方は気をつけて下さい。
まずはスクリプトの入手。この辺りに転がってるスクリプトを拝借‥というかコピペ,「/usr/local/sbin/」あたりに「dtcpc」として配置して実行可能な状態にする。中に設定項目が幾つかあるので,環境に合わせて適宜書き換えること。
下記は私の場合の設定例です(ちょっとだけ値を書き換えてます)。
# cd /usr/local/sbin
# vi dtcpc
#!/usr/bin/
perl
use Net::
POP3;
use Digest::
MD5;
use Getopt::Std;
$ConfFile = "/etc/dtcpc/dtcpc.conf";
$AddrFile = "/var/run/dtcpc.addr";
$StopFile = "/tmp/dtcpc.stop";
$LogFile = "/var/log/dtcpc.log";
$RaConf = "/etc/
dtcp/radvd.conf";
$ENV{'PATH'} = "/sbin:/usr/sbin:/bin:/usr/bin";
$U_ID = 65534;
$G_ID = 65534;
$SIG{'TERM'} = $SIG{'INT'} = "sigexit";
our($local, $remote, $prefix, $nbits);
getopts('arxdn') || &help;
$Repeat = $opt_r;
$Debug = $opt_d;
if ($opt_x) {
open(OUT, ">$StopFile") || die;
chown $U_ID, $G_ID, $StopFile;
close(OUT);
exit 0;
}
if (-f $AddrFile) {
unlink $AddrFile;
}
open(CONF, $ConfFile) || die;
while (
) {
chop;
next if /^#/ || /^$/;
last;
}
die unless /^(\S+)\s+(\d+)\s+(\S+)\s+(\S+)$/;
$host = $1;
$port = $2;
$user = $3;
$pass = $4;
close(CONF);
open(LOG, ">>$LogFile") || open(LOG, ">&STDERR");
select(LOG);
$| = 1;
select(stdout);
print LOG &strtime(time), " host=$host port=$port user=$user\n";
if (!open(DTCP, "-|")) {
$| = 1;
($(,$)) = ($G_ID, $G_ID); # gid: nogroup
($<,$>) = ($U_ID, $U_ID); # uid: nobody
while (! &dtcp($host, $port, $user, $pass) && $Repeat) {
sleep 10;
print STDERR "reconnecting...\n" if $Debug;
}
exit 0;
}
$ret = 1;
while () {
s/[\r\n]*$//;
last unless /^\d+\.\d+\.\d+\.\d+/;
($local, $remote, $prefix, $nbits) = split;
# $remote = $_;
system "ip -6 route del default dev sit1" unless($opt_n);
system "ip -6 tunnel del sit1" unless($opt_n);
if (open(OUT, ">$AddrFile")) {
print OUT "$remote $prefix $nbits\n";
close(OUT);
}
print STDERR "execute: ifconfig sit0 up tunnel ::$remote\n" if $Debug;
system "ifconfig sit0 up tunnel ::$remote" unless($opt_n);
print STDERR "execute: ifconfig eth0 add ${prefix}1/64\n" if $Debug;
system "ifconfig eth0 add ${prefix}1/64" unless($opt_n);
print STDERR "execute: ip -6 route add default dev sit1\n" if $Debug;
system "ip -6 route add default dev sit1" unless($opt_n);
if($opt_a){
print STDERR "execute: start radvd.\n" if $Debug;
&radvd;
}
exit 0 unless $Repeat;
$ret = 0;
}
&clear_route;
if (-f $AddrFile) {
unlink $AddrFile;
}
exit $ret;
sub help {
print <
opt: -r ; reconnect
-x ; stop
-d ; debug
-a ; start radvd
-n ; dtcp only(not connect tunnel)
EOF
exit 1;
}
sub dtcp {
my ($host, $port, $user, $pass) = @_;
my $pop = Net::POP3->new($host, Port => $port, Debug => $Debug);
return undef unless $pop;
my $banner = ${*$pop}{'net_pop3_banner'};
if ($banner =~ /^(\S+)\s/) {
$banner = $1;
} else {
print LOG &strtime(time), " Unknown banner: $banner\n";
$pop->close();
return undef;
}
my $md = Digest::MD5->new();
$md->add($user, $banner, $pass);
my $cmd = $pop->command('tunnel', $user, $md->hexdigest, 'network');
my $response = $cmd->getline();
$pop->debug_print(0, $response) if $Debug;
if ($response =~ /^\+OK\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+\.\d+\.\d+\.\d+)\s+([\d\:a-fA-F]+)\/(\d+)/) {
($local, $remote, $prefix, $nbits) = ($1, $2, $3, $4);
} else {
$response =~ s/[\r\n]*$//;
print LOG &strtime(time), " Unknown response: $response\n";
$pop->close();
return undef;
}
print LOG &strtime(time), " local=$local remote=$remote network=$prefix/$nbits\n";
print "$local $remote $prefix $nbits\n";
while (1) {
sleep 10;
my $cmd = $pop->command('ping');
my $pong = $cmd->getline();
$pop->debug_print(0, $pong) if $Debug;
unless ($pong =~ /^\+OK pong/) {
$pong =~ s/[\r\n]*$//;
print LOG &strtime(time), " Unknown pong: $pong\n";
$pop->close();
return undef;
}
if (-f $StopFile) {
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($StopFile);
if ($uid == $U_ID) {
print LOG &strtime(time), " found $StopFile, terminating\n";
unlink $StopFile;
last;
}
}
}
$response = $pop->command('quit')->getline();
$pop->debug_print(0, $response) if $Debug;
$pop->close();
return 1;
}
sub strtime {
my ($time) = @_;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime($time);
sprintf("%4d-%02d-%02d %02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
}
sub radvd {
my($conf);
open(CONF,">$RaConf") || die "Can not write $RaConf\n";
$conf = <<"EOL";
interface eth0
{
AdvSendAdvert on;
prefix $prefix/64
{
AdvOnLink on;
AdvAutonomous on;
};
};
EOL
print CONF $conf;
close(CONF);
system "/etc/init.d/radvd start";
}
sub clear_route {
open(ADDR,$AddrFile) || return 1;
$_ = ;
my($remote, $prefix, $nbits) = split;
print STDERR "DEBUG: $remote, $prefix, $nbits\n" if $Debug;
if($opt_a){
print STDERR "execute: stop radvd.\n" if $Debug;
system "/etc/init.d/radvd stop" unless($opt_n && $opt_a);
}
print STDERR "execute: ifconfig eth0 del ${prefix}1/64\n" if $Debug;
system "ifconfig eth0 del ${prefix}1/64" unless($opt_n);
print STDERR "execute: ip -6 route del default dev sit1\n" if $Debug;
system "ip -6 route del default dev sit1" unless($opt_n);
system "ip -6 tunnel del sit1" unless($opt_n);
}
sub sigexit {
&clear_route;
exit;
}
# chmod +x dtcpc
※たぶんPerl用のPOPモジュールが必要になる。
次に認証設定。dtcpcの冒頭に合わせてファイルを配置すること。
# cd /etc/
# mkdir dtcp
# cd dtcp
# vi dtcpc.conf
# HOST Port ID Password
dtcp.feel6.jp 20200 hoge fuga
続けてサービスの設定。Linuxマシンが立ち上がった時点で勝手に起動してくれない事には使い勝手が悪すぎるので。
# cd /etc/init.d
# vi dtcpc-login
#!/bin/sh
# chkconfig: 2345 99 03
# description: DTCP Client Login Service
NAME="DTCP-Client Login"
DAEMON=/usr/local/sbin/dtcpc
LOCK=/var/opt/dtcp/dtcpc
STOP=/tmp/dtcpc.stop
START=""
#Functions
service_start()
{
if [ -e $LOCK ]
then
echo "Service $NAME has already started."
return 1
else
echo "Service $NAME starting..."
$DAEMON -n &
sleep 1
while [ -e $START ]
do
sleep 1
done
echo "done"
touch $LOCK
return 0
fi
}
service_stop()
{
if ! [ -e $LOCK ]
then
echo "Service $NAME is not started."
return 1
else
echo "Service $NAME stopping..."
$DAEMON -x &
sleep 1
while [ -e $STOP ]
do
sleep 1
done
rm $LOCK
echo "done"
return 0
fi
}
service_reconnect()
{
$DAEMON r &
echo The request was transmitted to $NAME!
}
# Service Management
test -x $DAEMON || exit 0
case "$1" in
start)
if service_start
then
exit 1
fi
;;
stop)
if service_stop
then
exit 1
fi
;;
restart)
service_stop
if service_start
then
exit
fi
;;
reconnect)
service_reconnect
;;
*)
echo "Usage: $0 {start|stop|restart|reconnect}"
exit 1
esac
exit 0
# chmod +x dtcpc-login
気が向いたら,dtcpc-loginからdtcpcの状態が見られるようにするつもり。
最後にサービスの登録。Ubuntuならsysv-rc-conf,CentOSならchkconfig --add dtcpc-loginとかで設定できるはず。
すべて設定できたら,service dtcpc-login startとかで起動させる。スクリプトの認証が成功すると,/var/run/dtcpc.addrのあたりに割り振られたIPv6アドレスとPrefixが書き込まれるので確認すること。
もしサービスを動かしてみて認証がうまくいかないようなら,先に「/usr/local/sbin/dtcpc -n」を実行し,様子を確認してみると良いかもしれない。
2. IX2015でトンネルを設定する
超適当。とりあえずリンクが張れるところまでは確認してるので,後はRAするなりpingするなり好きにして下さい。
ipv6 prefix [PrefixName] 2001:3e0:[YourAddress]::/48
ipv6 route Tunnel0.0
interface [LAN]
ipv6 address 2001:3e0:[YourAddress]::1/64
interface Tunnel0.0
tunnel mode 6-over-4
tunnel destination [DTCP-Server]
no ip address
ipv6 address autoconfig
no shutdown
ご指摘等は大歓迎です。