OCN IPv6ネットワークにマルチキャストされてる緊急地震速報を受信しちゃう

随分昔に、Weathernewsの緊急地震速報プロトコルを解析されたコードを公開されてる方を見つけて、それ改造してIRCに流すbot作ったのですが、プロトコルが変態すぎてなかなか安定しませんでした*1。今動くのかなこのコード*2

今はOCN IPv6を契約するとIPv6マルチキャストで契約してなくてもOCN緊急地震速報っぽいパケットがだだ漏れしてくる時代に。Twitter bot走らせてる人もいたり。

参考:

要するにIPv6マルチキャストを受信すればいいのね。というわけで、調べてみました。

例によって、書いてある内容は未保証です。

OCN IPv6でつなぐ

取り敢えずOCN IPv6繋がないと始まりません。このへん参照した気がする。

pppデバイスが出来ればMulticast受信はできるので、IPv6取ってきたりする設定まではしてません。いいのかそれで。

取り敢えず受信する

PerlだとIO::Socket::Multicast6使えば受信出来ます。こんな感じかしら。先述のURIにもありますが、30秒ごとにpingパケットが飛んでくるので、40秒でタイムアウトするようにしました。

#!/usr/bin/env perl

use strict;
use warnings;

use IO::Socket::Multicast6;
use IO::Select;

my $port = 8001;
my $group = 'FF3E::8000:1000';

my $eew_sock = IO::Socket::Multicast6->new( 
	Domain => AF_INET6 ,
	LocalPort => $port ,
	Proto => 'udp' ,
	LocalAddr => $group
);
$eew_sock->mcast_add($group);
my $s = IO::Select->new();
$s->add($eew_sock);

while (1) {
	my @socks = $s->can_read(40);
	if(scalar @socks == 0){
		print "recv timeout";
		exit;
	}
	foreach my $sock(@socks)
	{
		if($sock == $eew_sock){
			my $data;
			$sock->recv($data,512);
			process_packet($data);
		}
	}
}

sub process_packet
{
	my $data = shift;
	#受信しましたわ。
}

パケットを解析する

こうすると、「30秒ごとのpingパケット」「毎正時のテスト電文」「緊急地震速報電文」の三種類の電文が飛んできます。飛んでくるデータのhex dumpは文末にまとめました。三種類とか言いましたが、テスト電文も気象庁が送ってるので、実際は二種類ですね。しばらく上のコードを走らせる*3と色んなデータパケットがたまるので、それを眺めてどんな構造になっているのか猫よりバカな脳みそで考えてみたのが以下。

パケットの構造は先頭から「OCN緊急地震速報ヘッダ*4」「JMAソケットヘッダ+電文制御ヘッダ」「電文」の三種類になっています。

UDPなので、気象庁からNTTに飛んでくる電文のサイズが一定サイズを超えるとNTT側で複数のパケットに分割して送ってきます。この時、2つ目以降のパケットにはJMAソケットヘッダは付きません*5

OCN緊急地震速報ヘッダ

気象庁から電文を受信したNTTがつけるヘッダです。このヘッダには以下の情報が入ってるようです。

  • パケットのサイズ
  • パケットの種類
    • テストか、電文か
  • 何回目の送信か
    • 同じデータを三回送るよ、三回中何回目か、の二つ
  • 何分割していくつめのブロックか
  • 通し番号
    • 日付+通し番号でユニークですね。NTTが受信した際に振ってると思われ。

飛んでくるデータはUDPで、気象庁からNTTに飛んでくる電文のサイズが一定サイズを超えると複数のパケットに分割して送ってきますが、その情報もヘッダに入ってます。

先頭30バイトをunpackすると、こんなところですか。

# $length パケットのサイズ(short)
# $type パケットの種類(0x00 =>電文, 0xff => ping)
# $ptotal 何回同じパケット送るのか
# $pcount 何回目の送信か
# $dtotal 全部でいくつに分割されてるのか
# $dcount 幾つ目の分割パケットか
# $name 通し番号(16文字固定で、pingのときはnull)
my($length,$type,$ptotal,$pcount,$dtotal,$dcount,$name) = unpack('vx3CCCx2CxCxa16',$data);

サイズ実はshortやcharじゃなくてlongだったとかありそうだけど、そんなでかい電文来ないと信じて、食らったらその時考えることに。

で、このヘッダのサイズなんですが、基本的には0x30=48バイトです。が、分割された際の2つ目からは0x1e=30バイトに縮むようです。なんでやねん。ちなみに。30秒に一回のpingはこのヘッダだけ飛んできます。つまり48バイトのデータが来ます。

#実際にはprocess_packetを呼ぶ部分より前(whileの前あたりとか)に書かないとダメです
my $last_dcount = '';
my $last_name = '';
my @message = ();

sub process_packet
{
	my $data = shift;

	my($length,$type,$ptotal,$pcount,$dtotal,$dcount,$name) = unpack('vx3CCCx2CxCxa16',$data);

	#pingなので何もしない。
	return if $type == 255;

	#既に受信したことがあるパケットなら無視
	return if($last_dcount eq $dcount && $last_name eq $name);
	@message = () if $last_name ne $name;
	$last_dcount = $dcount; $last_name = $name;

	#OCNEEWヘッダを取る
	$message[$dcount-1] = substr($data,30);

	#全部受信しきったので繋いで処理する。
	if(count(@message) == $dtotal){
		#何故か最初のパケットだけ18バイトおまけがつく
		process_eew(substr(join('',@message),18),$name);
		@message = ();
	}
}

sub count
{
	my $c = 0;
	foreach my $i (@_){
		$c++ if defined $i;
	}
	$c;
}

sub process_eew
{
	my ($msg,$ocn_id) = @_;
	# $msgに電文、$ocn_idには電文のNTT的通し番号がはいる。
}
JMAソケットヘッダ+電文制御ヘッダ

気象業務支援センターが電文をNTT等へ送る際にJMAソケット付きTCP/IPなるものを使って送ってくるんですが、単にTCP/IPでヘッダを付けてデータを送るだけ*6

ぐぐると、仕様書めっけたので、これを参考によんでみる。

まず、JMAソケットヘッダ。10バイトで先頭8バイトがデータサイズが8桁の数字文字で入ってて、空きはゼロフィルされてます。のこり2バイトはデータの種類。

my($size,$type) = unpack('A8A2',$msg);
$size += 0;

こんな感じで。種類は「aN」で文字データだそうな。

次に、電文制御ヘッダ。あんまり意味は無さそう。ここに電文本体の文字コードが書いてあるんだけど、1バイト系という謎のcharsetとあとJISとシフトJISとバイナリという謎の種類しか定義されてない。

20バイトから最大60バイト変わり得るらしいけど、ヘッダ情報では20バイトだっていうのに実際には制御ヘッダ先頭から電文まで36バイトあるってどういうことよ。電文制御ヘッダ最初のバイトで上4bitが制御ヘッダバージョン、下4bitが制御ヘッダサイズの1/4が入ってます。

電文そのものは0x0aから始まるっぽいので、indexあたりで0x0aまで読み飛ばせばいいんじゃないかなーと。

sub split_header
{
	my $msg = shift;
	$msg = substr($msg,10);
	my $size = (unpack('C',$msg) & 0xf) *4;
	$msg = substr($msg,$size);
	my $stx = index($msg,"\x0a");
	return "" if($stx < 0);
	substr($msg,$stx);
}
sub process_eew
{
	my ($msg,$ocn_id) = @_;
	$msg = split_header($msg);

	#$msgには生の電文が入ってるはずだ。
}

あそぶ

あとはIRCに流すなりTwitterに流すなり。

電文の処理は Earthquake::EEW::Decoder使って解析できます。

本番の電文は1秒もあかずに随時新情報が降ってくるし、あんまりゆっくりしてると間に合いません。IRCに流すとかいいんじゃないでしょうかね。

ちなみに、ここで書かれているサンプルコードは複数のEEWが同時に飛んでくることを想定していないので、実際にはconcatするバッファをOCNのつけてくれるIDあたりで区分しないと正常にパケットを生成出来ないことがありますこれ、この前の大誤報の時に気付いたんですがまだサンプルコードでは修正できてません(22 Aug 2013追記)。

パケットの例

文字コードShift_JISで飛んできます(文中で解説)。

30秒ごとのping
        +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF
000000  30 00 00 00 00 FF 03 03-00 00 01 00 01 00 00 00  0............... 
000010  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................ 
000020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................ 
毎正時のテスト電文
        +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF
000000  E1 00 00 00 00 00 03 03-00 00 01 00 01 00 32 30  ..............20 
000010  31 33 30 31 30 36 30 30-30 30 30 30 32 33 00 00  13010600000023.. 
000020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................ 
000030  30 30 30 30 30 31 36 37-61 4E 35 04 14 71 00 20  00000167aN5..q.  
000040  00 00 00 00 AE 6A 08 00-00 00 00 00 00 00 00 00  ....ョj.......... 
000050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 0A C5  ...............ナ 
000060  B3 B7 D4 BD C4 C3 BD C4-39 31 20 B7 BC D6 B3 0A  ウキヤストテスト91 キシヨウ. 
000070  02 0A 33 38 20 30 33 20-32 30 20 31 33 30 31 30  ..38 03 20 13010 
000080  36 31 30 30 30 30 30 20-43 31 31 20 39 39 39 39  6100000 C11 9999 
000090  0A 0A 8B D9 8B 7D 92 6E-90 6B 91 AC 95 F1 82 CC  ..緊急地震速報の 
0000A0  83 65 83 58 83 67 93 64-95 B6 82 C5 82 B7 0A 82  テスト電文です.2
0000B0  51 82 4F 82 50 82 52 94-4E 82 4F 82 50 8C 8E 82   013年01月0
0000C0  4F 82 55 93 FA 82 50 82-4F 8E 9E 82 4F 82 4F 95   6日10時00分
0000D0  AA 82 4F 82 4F 95 62 0A-8B 43 8F DB 92 A1 3D 0A   00秒.気象庁=. 
0000E0  03                                               .                
緊急地震速報本文
        +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF
000000  FC 00 00 00 00 00 03 01-00 00 01 00 01 00 32 30  ..............20 
000010  31 33 30 31 31 31 30 30-30 30 30 30 30 39 00 00  13011100000009.. 
000020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................ 
000030  30 30 30 30 30 31 39 34-61 4E 35 04 16 66 00 10  00000194aN5..f.. 
000040  10 00 00 00 9C 85 08 00-00 00 00 00 00 00 00 00  ....怛.......... 
000050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 0A C5  ...............ナ 
000060  B3 B7 D4 BD C4 33 20 B7-BC D6 B3 0A 02 0A 33 37  ウキヤスト3 キシヨウ...37 
000070  20 30 33 20 30 30 20 31-33 30 31 31 31 30 36 32   03 00 130111062 
000080  32 32 36 20 43 31 31 0A-31 33 30 31 31 31 30 36  226 C11.13011106 
000090  32 32 30 38 0A 4E 44 32-30 31 33 30 31 31 31 30  2208.ND201301110 
0000A0  36 32 32 31 37 20 4E 43-4E 30 30 31 20 4A 44 2F  62217 NCN001 JD/ 
0000B0  2F 2F 2F 2F 2F 2F 2F 2F-2F 2F 2F 2F 2F 20 4A 4E  ///////////// JN 
0000C0  2F 2F 2F 0A 37 39 33 20-4E 32 38 35 20 45 31 32  ///.793 N285 E12 
0000D0  39 33 20 30 35 30 20 34-35 20 30 33 20 52 4B 34  93 050 45 03 RK4 
0000E0  34 35 31 34 20 52 54 31-30 2F 2F 2F 20 52 43 2F  4514 RT10/// RC/ 
0000F0  2F 2F 2F 2F 0A 39 39 39-39 3D 0A 03              ////.9999=..     
分割された電文の例

1/2

        +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF
000000  F4 01 00 00 00 00 03 01-00 00 02 00 01 00 32 30  ..............20 
000010  31 33 30 32 30 32 30 30-30 30 30 30 36 35 00 00  13020200000065.. 
000020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................ 
000030  30 30 30 30 30 36 39 38-61 4E 35 04 26 15 00 10  00000698aN5.&... 
000040  10 00 00 00 8C D6 08 00-00 00 00 00 00 00 00 00  ....誇.......... 
000050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 0A C5  ...............ナ 
000060  B3 B7 D4 BD C4 33 20 B7-BC D6 B3 0A 02 0A 33 37  ウキヤスト3 キシヨウ...37 
000070  20 30 33 20 30 30 20 31-33 30 32 30 32 32 33 31   03 00 130202231 
000080  38 32 30 20 43 31 31 0A-31 33 30 32 30 32 32 33  820 C11.13020223 
000090  31 37 33 34 0A 4E 44 32-30 31 33 30 32 30 32 32  1734.ND201302022 
0000A0  33 31 37 35 30 20 4E 43-4E 30 30 38 20 4A 44 2F  31750 NCN008 JD/ 
0000B0  2F 2F 2F 2F 2F 2F 2F 2F-2F 2F 2F 2F 2F 20 4A 4E  ///////////// JN 
0000C0  2F 2F 2F 0A 31 35 37 20-4E 34 32 36 20 45 31 34  ///.157 N426 E14 
0000D0  33 32 20 31 31 30 20 36-34 20 35 2D 20 52 4B 36  32 110 64 5- RK6 
0000E0  36 35 35 34 20 52 54 30-31 2F 2F 2F 20 52 43 30  6554 RT01/// RC0 
0000F0  2F 2F 2F 2F 0A 45 42 49-20 31 35 36 20 53 35 2D  ////.EBI 156 S5- 
000100  35 2D 20 2F 2F 2F 2F 2F-2F 20 31 31 20 31 35 37  5- ////// 11 157 
000110  20 53 30 34 30 34 20 2F-2F 2F 2F 2F 2F 20 31 31   S0404 ////// 11 
000120  20 31 35 32 20 53 30 34-30 34 20 2F 2F 2F 2F 2F   152 S0404 ///// 
000130  2F 20 31 31 0A 31 35 35-20 53 30 34 30 34 20 2F  / 11.155 S0404 / 
000140  2F 2F 2F 2F 2F 20 31 31-20 31 35 31 20 53 30 34  ///// 11 151 S04 
000150  30 34 20 2F 2F 2F 2F 2F-2F 20 31 31 20 31 35 30  04 ////// 11 150 
000160  20 53 30 34 30 34 20 2F-2F 2F 2F 2F 2F 20 31 31   S0404 ////// 11 
000170  0A 31 32 37 20 53 30 34-30 34 20 2F 2F 2F 2F 2F  .127 S0404 ///// 
000180  2F 20 31 31 20 31 34 36-20 53 30 34 30 34 20 2F  / 11 146 S0404 / 
000190  2F 2F 2F 2F 2F 20 31 31-20 31 36 31 20 53 30 34  ///// 11 161 S04 
0001A0  30 34 20 2F 2F 2F 2F 2F-2F 20 31 31 0A 31 34 31  04 ////// 11.141 
0001B0  20 53 30 34 30 34 20 2F-2F 2F 2F 2F 2F 20 30 31   S0404 ////// 01 
0001C0  20 31 32 32 20 53 30 34-30 34 20 2F 2F 2F 2F 2F   122 S0404 ///// 
0001D0  2F 20 31 31 20 31 32 31-20 53 30 34 30 34 20 2F  / 11 121 S0404 / 
0001E0  2F 2F 2F 2F 2F 20 30 31-0A 31 36 30 20 53 30 34  ///// 01.160 S04 
0001F0  30 34 20 2F                                      04 /             

2/2

        +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF
000000  1E 01 00 00 00 00 03 01-00 00 02 00 02 00 32 30  ..............20 
000010  31 33 30 32 30 32 30 30-30 30 30 30 36 35 2F 2F  13020200000065// 
000020  2F 2F 2F 20 30 31 20 31-30 31 20 53 30 34 30 34  /// 01 101 S0404 
000030  20 2F 2F 2F 2F 2F 2F 20-31 31 20 31 30 32 20 53   ////// 11 102 S 
000040  30 34 30 34 20 2F 2F 2F-2F 2F 2F 20 31 31 0A 31  0404 ////// 11.1 
000050  30 30 20 53 30 34 30 34-20 2F 2F 2F 2F 2F 2F 20  00 S0404 //////  
000060  31 31 20 31 36 36 20 53-30 34 30 34 20 2F 2F 2F  11 166 S0404 /// 
000070  2F 2F 2F 20 30 31 20 31-34 30 20 53 30 34 30 34  /// 01 140 S0404 
000080  20 32 33 31 38 32 31 20-30 30 0A 31 34 35 20 53   231821 00.145 S 
000090  30 34 30 34 20 32 33 31-38 32 32 20 31 30 20 31  0404 231822 10 1 
0000A0  36 37 20 53 30 34 30 34-20 32 33 31 38 32 34 20  67 S0404 231824  
0000B0  30 30 20 31 30 36 20 53-30 34 30 34 20 32 33 31  00 106 S0404 231 
0000C0  38 32 36 20 31 30 0A 31-36 35 20 53 30 34 30 34  826 10.165 S0404 
0000D0  20 32 33 31 38 32 37 20-30 30 20 32 30 33 20 53   231827 00 203 S 
0000E0  30 34 30 34 20 32 33 31-38 33 30 20 31 30 20 31  0404 231830 10 1 
0000F0  32 36 20 53 30 34 30 33-20 2F 2F 2F 2F 2F 2F 20  26 S0403 //////  
000100  30 31 0A 31 32 30 20 53-30 34 30 33 20 2F 2F 2F  01.120 S0403 /// 
000110  2F 2F 2F 20 30 31 0A 39-39 39 39 3D 0A 03        /// 01.9999=..   
(おまけ)WeathernewのEEW
        +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF
000000  01 0A 54 4B 43 43 20 32-36 33 34 36 20 20 41 44  ..TKCC 26346  AD 
000010  54 4B 20 31 32 32 31 35-39 31 34 0A 0A C5 B3 B7  TK 12215914..ナウキ 
000020  D4 BD C4 33 20 B7 BC D6-B3 0A 02 0A 02 0A 33 37  ヤスト3 キシヨウ.....37 
000030  20 30 33 20 30 30 20 31-31 30 33 31 33 30 36 35   03 00 110313065 
000040  39 31 34 20 43 31 31 0A-31 31 30 33 31 33 30 36  914 C11.11031306 
000050  35 38 31 35 0A 4E 44 32-30 31 31 30 33 31 33 30  5815.ND201103130 
000060  36 35 38 33 31 20 4E 43-4E 30 30 33 20 4A 44 2F  65831 NCN003 JD/ 
000070  2F 2F 2F 2F 2F 2F 2F 2F-2F 2F 2F 2F 2F 20 4A 4E  ///////////// JN 
000080  2F 2F 2F 0A 32 38 36 20-4E 33 39 30 20 45 31 34  ///.286 N390 E14 
000090  32 34 20 30 31 30 20 34-37 20 30 33 20 52 4B 36  24 010 47 03 RK6 
0000A0  36 32 30 34 20 52 54 31-30 2F 2F 2F 20 52 43 30  6204 RT10/// RC0 
0000B0  2F 2F 2F 2F 0A 39 39 39-39 3D 0A 03              ////.9999=..     

*1:東日本大震災の際にEEWが飛びまくった際に頑張ってデバッグして多少安定するようにはなったんですが、それでも何か変。

*2:と思って動かしてみたら動きはしたけど文字化けしたんでちょっと修正した。

*3:実際には受信したパケットをファイルへ保存するコードが端折られてますが。

*4:今勝手に名前つけた

*5:気象庁が送ってきたデータをNTTの都合で分けてるんだから当然ですね

*6:だけ、と断言するとちょっとアレかも。