iPhoneがiOS9からIPv6(Internet Protocol version 6)が必須となるらしいです。これまでIPv4アドレスの枯渇から何度も、今年はIPv6元年だ、なんて言われつづけてきましたが、なかなかメリットが見えないことから、ブームごとに学習はするものの、使うことはあまりありませんでした。(機器は対応済なのに) インフラ開発の現場でもNATがあるからそれほど不便でもないし、特に要望もなければ、使う機会もありませんでした。iPhoneの影響力がIPv6移行をさらに進めることで、使う機会が増えるかも(?)、ということで、IPv6に慣れておくためにも、Ubuntu 14.04 とRaspberry PiとのIPv6通信をテストしてみました。

ラズパイは、/etc/modules に ipv6 を追加すると、IPv6対応になります。
また、サーバとしてApache2は走らせておきます。(apt-get install apache2でインストール。IPv6対応済)
Ubuntu側は、以下のプログラムを使います。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68  | 
						#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #define BUFFERSIZE 1024 int main(int argc, char **argv) { 	struct addrinfo hints, *res, *ai0; 	ssize_t l; 	int s; 	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 	char buf[1024]; 	int error; 	if (argc != 3) { 		fprintf(stderr, "usage: testip host port\n"); 		exit(1); 	} 	memset(&hints, 0, sizeof(hints)); 	hints.ai_family= AF_UNSPEC; 	hints.ai_socktype = SOCK_STREAM; 	hints.ai_flags = AI_NUMERICSERV; 	error = getaddrinfo(argv[1], argv[2], &hints, &ai0); 	if(error){ 		fprintf(stderr, "%s %s: %s\n", argv[1], argv[2], gai_strerror(error)); 		exit(1); 	} 	for (res = ai0; res; res = res->ai_next) { 		error = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),NI_NUMERICHOST | NI_NUMERICSERV); 		if(res->ai_family == AF_INET6){ 			printf("[IPv6] "); 		} 		else if(res->ai_family == AF_INET){ 			printf("[IPv4] "); 		} 		else{ 			printf("[Other] "); 		} 		printf("Host : %s  Port : %s\n", hbuf, sbuf); 		if(error){ 			fprintf(stderr, "%s %s: %s\n", argv[1], argv[2], gai_strerror(error)); 			continue; 		} 		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 		if (s < 0) continue; 		if (connect(s, res->ai_addr, res->ai_addrlen)< 0) { 			close(s); 			fprintf(stderr, "connect error!\n"); 			continue; 		} 		printf("-------\n"); 		write(s, "GET / \n\n", 8); 		while ((l = read(s, buf, sizeof(buf))) > 0){ 			write(STDOUT_FILENO, buf, l); 		} 		close(s); 	} }  | 
					
ホスト名からIPv4, IPv6のアドレスを取得して、それぞれのアドレスでサーバにアクセスします。/etc/hostsは下のようになっています。

localhostのテストのため、Ubuntu側もApache2を立ち上げておきます。
index.htmlの内容は、”Index Page”です。
ホスト名localhost、rpiで接続してみます。

rpiの場合は、アドレスは引けるのですが、IPv6の接続がうまくいきません。これは、リンクローカルアドレスになっているためと思われます。そこで、IPv6アドレスにネットワークインターフェイス名を追加して接続しました。netcatでも同様の結果になりました。
下は、プログラム実行したときのパケットキャプチャです。

次に、参考までにping6、curlによる接続です。

アドレスの記述方法に、随分トライ&エラーをしました。%25は、%をエスケープしたものです。eth0でなく2なのは、よくわかりませんでしたが、インターフェイスの順番指定なのでしようか。
環境が、グローバルユニキャストアドレスならば、もっと簡単にいきそうです。
一番興味があるのは、マルチキャストなのですが、環境がそろったらトライしてみたいです。
参考 : http://www.v6pc.jp/jp/upload/pdf/socket-sample-20121203.pdf
