IPv6 Programing

課題1

tcpv6cはIPv4とIPv6の両方に対応しているにもかかわらず、tcpc(IPv4のみに対応)よりコードが短い。 この理由は何故か説明せよ。


tcp.cはgethostbyname()関数でサーバのIPアドレス、getservbyname()関数でポート番号を調べている。
   これに対し、tcpv6c.cではgetaddrinfo()関数で上記の2つの関数の役割を果たしているため、コードが短くなる。
getaddrinfo()関数は、4つの関数getipnodebyname()、getipnodebyaddr()、 getservbyname()、getservbyport()の機能をまとめて一つのインターフェースにしたものである。gethostbynemep()関数は、IPv4ネットワークファミリにしかアクセスできない
getipnodebyname()は複数のネットワークアドレスファミリーにアクセス可能。   

課題2

tcpv6sはAF_INET6でソケットをオープンしているにもかかわらず、IPv4でも通信できる。この理由は何故か説明せよ。


IPv6アドレス体系の中には、IPv4射影アドレスと呼ばれるIPv6アドレスが存在する。IPv4射影IPv6アドレスとは、IPv6アドレスの上位80bitに0、81〜96bit目に1を入れ、下位32bitにIPv4アドレスを埋め込んだアドレスである。AF_INET6ケットは、IPv6通信とIPv4通信の両方に使用できる。

IPv4射影アドレス

IPv4射影IPv6アドレスとは、 IPv6アドレスの上位80bitに0、81〜96bit目に1を入れ、下位32bitにIPv4アドレスを
埋め込んだアドレスである。IPv6をサポートしていないノードのアドレスをIPv6アドレスとして表現するために用
いられている。

課題3

サンプルプログラム(2)のクライアント(tcpc2.c)とサーバ(tcps2.c)をアドレスファミリ独立となるよう に書き換えよ。


myclient6.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 



int main __P((int, char **));

int
main(argc, argv)
int argc;
char **argv;
{
	struct addrinfo info, *res;
	ssize_t l;
	int s;
	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
	char buf[1024];
	char ip[256];
	
	/* 引数の数をチェック */
	if (argc != 3){
		fprintf(stderr, "usage: %s host (port)\n",argv[0]);
		exit(EXIT_FAILURE);
	}

	/*アドレスをバイナリに変更*/
	memset(&info, 0, sizeof(info));
	info.ai_flags    = AI_CANONNAME;
	info.ai_family   = AF_UNSPEC;
	info.ai_socktype = SOCK_STREAM;
	
	if (getaddrinfo(argv[1],argv[2], &info, &res) != 0) {
		perror("getaddrinfo");
		exit(EXIT_FAILURE);       
	}
	
	
	s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if (s < 0) {
		perror("socket");
		exit(EXIT_FAILURE);
	}
	
	getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), sbuf, 
								sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
	fprintf(stderr, "trying %s port %s\n", hbuf, argv[2]);

	if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
		perror("connect");
		exit(EXIT_FAILURE);
	}
	

	while ((l = read(s, buf, sizeof(buf))) > 0)
			write(STDOUT_FILENO, buf, l);
	close(s);
	return EXIT_SUCCESS;

}
myserver6.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


int main __P((int, char **));

int
		main(argc, argv)
int argc;
char **argv;
{
	struct servent *sp;
	unsigned long lport;
	u_int16_t port;
	char *ep;
	struct sockaddr_in6 serv;
	int servlen;
	struct sockaddr_in6 from;
	socklen_t fromlen;
	int s;
	int ls;
	char hbuf[INET6_ADDRSTRLEN];
	
	if (argc != 2) {
		fprintf(stderr, "usage: test (port)\n");
		exit(1);
		/*NOTREACHED*/
	}
	else{
		sp = getservbyname(argv[1], "tcp");
		if (sp)
				port = sp->s_port & 0xffff;
		else {
			ep =NULL;
			errno = 0;
			lport =strtoul(argv[1], &ep, 10);
			if (!*argv[1] || errno || !ep || *ep) {
				fprintf(stderr, "%s: no such service\n", argv[1]);
				exit(1);
				/*NOTREACHED*/
			}
			if (lport & ~0xffff) {
				fprintf(stderr, "%s: out of range\n", argv[1]);
				exit(1);
				/*NOTREACHED*/
			}
			
			port = htons(lport & 0xffff);
		}
	}
	endservent();
	
	memset(&serv, 0, sizeof(serv));
	serv.sin6_family = AF_INET6;
	/* linux/Solarisでは以下の行は不要 */
	serv.sin6_len = sizeof(struct sockaddr_in6);
	serv.sin6_port = port;
	servlen = sizeof(struct sockaddr_in6);
	
	s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
	if (s < 0) {
		perror("socket");
		exit(1);
		/*NOTREACHED*/
	}
	
	if (bind(s, (struct sockaddr *)&serv, servlen) < 0) {
		perror("bind");
		exit(1);
		/*NOTREACHED*/
	}
	if (listen(s, 5) < 0) {
		perror("listen");
		exit(1);
		/*NOTREACHED*/
	}
	
	while (1) {
		fromlen = sizeof(from);
		ls = accept(s, (struct sockaddr *)&from, &fromlen);
		if (ls < 0)
				continue;
		if (from.sin6_family != AF_INET6 || fromlen != sizeof(struct sockaddr_in6)) {
			exit(1);
			/*NOTREACHED*/
		}
		
		if (inet_ntop(AF_INET6, &from.sin6_addr, hbuf, sizeof(hbuf)) == NULL) {
			exit(1);
			/*NOTREACHED*/
		}
		write(ls, "Hello ",6);
		write(ls, hbuf, strlen(hbuf));
		write(ls, "\n", 1);
		close(ls);
	}
	/*NOTREACHED*/
}
まずサーバを起動する
[j05030@~]% ./myserver6 2000
次にIPv6でクライアントを起動する。
[j05030@~]% ./tcp6c 2001:02f8:001c:d048:0211:24ff:fe74:311a 2000
connected to '2001:2f8:1c:d048:211:24ff:fe74:311a' by IPv6
trying 2001:2f8:1c:d048:211:24ff:fe74:311a port 2000
Hello 2001:2f8:1c:d048:211:24ff:fe74:311a
次にIPv4でクライアントを起動する。
[j05030@~]%./tcp6c 133.13.59.30 2000
connected to '133.13.59.30' by IPv4
trying 133.13.59.30 port 2000
Hello ::ffff:133.13.59.30

参考文献