sendmsg(),recvmsg()を使ったUDP送受信プログラム:IPv6
連載中(?)のsendmsg(),recvmsg()を使ったUDP送受信プログラムですが、いよいよIPv6です。IPv6ではIPV6_PKTINFOがどの処理系でも使えるので簡単、と思ったのですが、やってみると意外と苦労しました。。
まず、setsockopt()で指定するのは、IPV6_PKTINFOではなく、IPV6_RECVPKTINFOでないと駄目、というのが、SolarisとLinuxで、そもそもIPV6_RECVPKTINFOが無いのがBSD(MacOSX)というところで苦労しました。RECVPKTINFOと言っているのにsendmsg()にも効果があるのでなぜ?と思ったら、送信するときにはsetsockopt()しなくても、指定すれば有効になるみたいです。同じソケットで送信も受信もするならセットすることになるので、まあ、しておくことにしておけばいい感じです。
つづいて、struct msghdrのmsg_name,msg_namelenに指定する、あるいは取得するものが、struct sockaddr_storageだろうと思っていたら、struct sockaddr_in6みたいで、まあどちらでも同じように使えるのですが、取得したものをgetnameinfo()するときに、第二引数をsizeof(struct sockaddr_storage)にしてしまうと、Solarisだとエラーになってしまうと言うのも悩みました。取得したmsg_namelenの値にするか、sizeof(struct sockaddr_in6)を指定しないと駄目です。
ということで、いろいろ仕事の予定が入ってしまい、実行結果はあまり綺麗にまとまっていないので、ソースを紹介してしまいましょう。いちおうLinux、BSD(MacOSX)、Solarisで動作確認してあります。
受信プログラム
「
#ifdef __SunOS
#define _XPG4_2
#define __EXTENSIONS__
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <poll.h>
#include <net/if.h>
#define SOCK_NO 16
#define BUF_LEN 512
int recvFunc(int soc,int ifno)
{
char hbuf[NI_MAXHOST],sbuf[NI_MAXSERV];
unsigned char buf[BUF_LEN];
struct msghdr msg;
struct iovec iov[1];
struct cmsghdr *cmsg;
char cbuf[512],addr[256];
struct sockaddr_storage sin;
struct in6_pktinfo *pktinfo=NULL;
int rc;
int error;
iov[0].iov_base=buf;
iov[0].iov_len=BUF_LEN;
memset(&sin,0,sizeof(sin));
memset(&msg,0,sizeof(msg));
msg.msg_name=&sin;
msg.msg_namelen=sizeof(sin);
msg.msg_iov=iov;
msg.msg_iovlen=1;
msg.msg_control=cbuf;
msg.msg_controllen=512;
rc=recvmsg(soc,&msg,0);
if(rc<0){
perror("recvmsg");
return(-1);
}
for(cmsg=CMSG_FIRSTHDR(&msg);cmsg!=NULL;
cmsg=CMSG_NXTHDR(&msg,cmsg)){
if(cmsg->cmsg_level==IPPROTO_IPV6&& cmsg->cmsg_type==IPV6_PKTINFO){
pktinfo=(struct in6_pktinfo *)CMSG_DATA(cmsg);
}
}
printf("*************************\n");
printf("buf=[%s]\n",buf);
if((error=getnameinfo((struct sockaddr *)&sin,msg.msg_namelen,hbuf,sizeof(hbuf),sbuf,sizeof(sbuf),NI_NUMERICHOST|NI_NUMERICSERV))){
fprintf(stderr,"getnameinfo:%s\n",gai_strerror(error));
}
else{
fprintf(stderr,"from(%d):%s:%s\n",ifno,hbuf,sbuf);
}
if(pktinfo!=NULL){
inet_ntop(AF_INET6,&pktinfo->ipi6_addr,addr,sizeof(addr));
printf("pktinfo->ipi6_addr=%s\n",addr);
printf("pktinfo->ipi6_ifindex=%d\n",pktinfo->ipi6_ifindex);
}
else{
printf("no pktinfo\n");
}
printf("*************************\n");
return(0);
}
int main(int argc,char *argv[])
{
char *portnm;
int s[SOCK_NO];
struct addrinfo hints,*res,*res0;
int error;
int smax;
const int on=1;
char hbuf[NI_MAXHOST],sbuf[NI_MAXSERV];
struct pollfd targets[SOCK_NO];
int nready,i;
int end;
int rc;
struct if_nameindex *nindex,*iptr;
struct ipv6_mreq mreq;
struct in6_addr sin6_addr;
if(argc<2){
fprintf(stderr,"%s port\n",argv[0]);
return(1);
}
portnm=argv[1];
/* for debug */
nindex=if_nameindex();
for(iptr=nindex;iptr->if_index!=0;iptr++){
printf("%d:%s\n",iptr->if_index,iptr->if_name);
}
if_freenameindex(nindex);
/* for debug */
memset(&hints,0,sizeof(hints));
hints.ai_socktype=SOCK_DGRAM;
hints.ai_flags=AI_PASSIVE;
error=getaddrinfo(NULL,portnm,&hints,&res0);
if(error){
fprintf(stderr,"getaddrinfo():%s:%s\n",portnm,gai_strerror(error));
return(-1);
}
smax=0;
for(res=res0;res&&smax<SOCK_NO;res=res->ai_next){
if(res->ai_family!=AF_INET6){
s[smax]=-1;
continue;
}
s[smax]=socket(res->ai_family,res->ai_socktype,res->ai_protocol);
if(s[smax]<0){
continue;
}
#ifdef IPV6_V6ONLY
rc=setsockopt(s[smax],IPPROTO_IPV6,IPV6_V6ONLY,&on,sizeof(on));
if(rc<0){
perror("setsockopt(IPV6_V6ONLY)");
}
#endif
#ifdef IPV6_RECVPKTINFO
printf("use IPV6_RECVPKTINFO\n");
rc=setsockopt(s[smax],IPPROTO_IPV6,IPV6_RECVPKTINFO,&on,sizeof(on));
if(rc<0){
perror("setsockopt(IPV6_RECVPKTINFO)");
}
#else
printf("use IPV6_PKTINFO\n");
rc=setsockopt(s[smax],IPPROTO_IPV6,IPV6_PKTINFO,&on,sizeof(on));
if(rc<0){
perror("setsockopt(IPV6_PKTINFO)");
}
#endif
inet_pton(AF_INET6, "ff02::1", &sin6_addr);
mreq.ipv6mr_multiaddr = sin6_addr;
mreq.ipv6mr_interface = 0;
rc=setsockopt(s[smax], IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
if(rc<0){
perror("setsockopt(IPV6_JOIN_GROUP)");
}
if(bind(s[smax],res->ai_addr,res->ai_addrlen)<0){
close(s[smax]);
s[smax]=-1;
continue;
}
error=getnameinfo(res->ai_addr,res->ai_addrlen,hbuf,sizeof(hbuf),sbuf,sizeof(sbuf),NI_NUMERICHOST|NI_NUMERICSERV);
if(error){
fprintf(stderr,"getnameinfo():%s\n",gai_strerror(error));
freeaddrinfo(res0);
return(-1);
}
fprintf(stderr,"bind to %s %s\n",hbuf,sbuf);
smax++;
}
freeaddrinfo(res0);
if(smax==0){
fprintf(stderr,"no socket to bind to\n");
return(-1);
}
for(i=0;i<smax;i++){
if(s[i]==-1){
continue;
}
targets[i].fd=s[i];
targets[i].events=POLLIN|POLLERR;
}
end=0;
do{
switch((nready=poll(targets,smax,-1))){
case -1:
if(errno!=EINTR){
perror("poll");
end=1;
}
break;
case 0:
fprintf(stderr,"Timeout\n");
break;
default:
for(i=0;i<smax;i++){
if(targets[i].revents&(POLLIN|POLLERR)){
recvFunc(s[i],i);
}
}
break;
}
}while(end!=1);
for(i=0;i<smax;i++){
close(s[i]);
}
return(0);
}
」
OSによるのですが、リンクローカルマルチキャスト宛のパケットを、なにもしなくても受信できるものと、ちゃんと参加表明しないと受信できないものがありましたので、一応「ff02::1」に参加するコードも入れておきました。
送信プログラム
「
#ifdef __SunOS
#define _XPG4_2
#define __EXTENSIONS__
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#include <poll.h>
#define BUF_LEN 512
#define SOCK_NO 16
int main(int argc,char *argv[])
{
char *hostnm,*portnm;
struct addrinfo hints,*res,*res0;
int s[SOCK_NO];
int smax,i;
struct sockaddr_storage to[SOCK_NO];
int tolen[SOCK_NO];
char hbuf[NI_MAXHOST],sbuf[NI_MAXSERV];
int error;
unsigned char buf[BUF_LEN];
char buf2[BUF_LEN],*ptr;
struct in6_addr myaddr;
char *if_name;
const int on=1;
struct if_nameindex *nindex,*iptr;
int rc;
if(argc<=2){
fprintf(stderr,"%s if-name my-ipaddress\n",argv[0]);
return(-1);
}
if_name=argv[1];
if(!inet_pton(AF_INET6,argv[2],&myaddr)){
perror("inet_pton");
return(-1);
}
/* for debug */
nindex=if_nameindex();
for(iptr=nindex;iptr->if_index!=0;iptr++){
printf("%d:%s\n",iptr->if_index,iptr->if_name);
}
if_freenameindex(nindex);
/* for debug */
printf("input \"host port\"\n");
while(1){
if(fgets((char *)buf,sizeof(buf),stdin)==NULL){
perror("fgets");
break;
}
if((ptr=strchr((char *)buf,'\n'))!=NULL){
*ptr='\0';
}
strcpy(buf2,(char *)buf);
if((hostnm=strtok(buf2," "))==NULL){
fprintf(stderr,"Input-error\n");
fprintf(stderr,"host:port:nic\n");
continue;
}
if((portnm=strtok(NULL,"\r\n"))==NULL){
fprintf(stderr,"Input-error\n");
fprintf(stderr,"host:port:nic\n");
continue;
}
memset(&hints,0,sizeof(hints));
hints.ai_socktype=SOCK_DGRAM;
error=getaddrinfo(hostnm,portnm,&hints,&res0);
if(error){
fprintf(stderr,"getaddrinfo:%s %s:%s\n",hostnm,portnm,gai_strerror(error));
return(-1);
}
smax=0;
for(res=res0;res&&smax<SOCK_NO;res=res->ai_next){
error=getnameinfo(res->ai_addr,res->ai_addrlen,hbuf,sizeof(hbuf),sbuf,sizeof(sbuf),NI_NUMERICHOST|NI_NUMERICSERV);
if(error){
fprintf(stderr,"getnameinfo:%s %s:%s\n",hostnm,portnm,gai_strerror(error));
continue;
}
s[smax]=socket(res->ai_family,res->ai_socktype,res->ai_protocol);
if(s[smax]<0){
perror("socket");
continue;
}
#ifdef IPV6_RECVPKTINFO
printf("use IPV6_RECVPKTINFO\n");
rc=setsockopt(s[smax],IPPROTO_IPV6,IPV6_RECVPKTINFO,&on,sizeof(on));
if(rc<0){
perror("setsockopt(IPV6_RECVPKTINFO)");
}
#else
printf("use IPV6_PKTINFO\n");
rc=setsockopt(s[smax],IPPROTO_IPV6,IPV6_PKTINFO,&on,sizeof(on));
if(rc<0){
perror("setsockopt(IPV6_PKTINFO)");
}
#endif
memcpy(&to[smax],res->ai_addr,res->ai_addrlen);
tolen[smax]=res->ai_addrlen;
fprintf(stderr,"target %d : %s port %s\n",smax,hbuf,sbuf);
smax++;
}
freeaddrinfo(res0);
if(smax==0){
fprintf(stderr,"no destination to send\n");
continue;
}
for(i=0;i<smax;i++){
struct msghdr msg;
struct iovec iov[1];
struct cmsghdr *cmsg;
char cbuf[512];
struct in6_pktinfo pktinfo;
int rc;
iov[0].iov_base=buf;
iov[0].iov_len=BUF_LEN;
memset(&msg,0,sizeof(msg));
msg.msg_name=&to[i];
msg.msg_namelen=tolen[i];
msg.msg_iov=iov;
msg.msg_iovlen=1;
msg.msg_control=cbuf;
msg.msg_controllen=512;
memset(&pktinfo,0,sizeof(pktinfo));
pktinfo.ipi6_ifindex=if_nametoindex(if_name);
fprintf(stderr,"ifindex=%d\n",pktinfo.ipi6_ifindex);
memcpy(&pktinfo.ipi6_addr,&myaddr,sizeof(struct in6_addr));
cmsg=CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level=IPPROTO_IPV6;
cmsg->cmsg_type=IPV6_PKTINFO;
cmsg->cmsg_len=CMSG_LEN(sizeof(struct in6_pktinfo));
memcpy(CMSG_DATA(cmsg),&pktinfo,sizeof(pktinfo));
msg.msg_controllen=cmsg->cmsg_len;
rc=sendmsg(s[i],&msg,0);
if(rc<0){
perror("send");
}
}
for(i=0;i<smax;i++){
close(s[i]);
}
}
return(0);
}
」
前回までは、送信するデータを「IPアドレス:ポート番号」で指定していましたが、IPv6では":"をアドレス表記に使うので、「IPアドレス ポート番号」とスペース区切りで指定するようにしてありますので、実行して実験してみる人は気をつけてください。
ここまでくれば、実はIPv4、IPv6を一緒に扱うこともできますが、サンプルにしてはソースが長くなってしまうので、IPv6専用にしておきました。
===追記===
実行結果を載せておきます。
Linuxでの受信
「
eth0: fe80::221:5eff:fe46:3040/64
eth1: fe80::21b:21ff:fe35:61d0/64
1:lo
2:eth1
3:eth0
4:sit0
use IPV6_RECVPKTINFO
bind to :: 44444
*************************
buf=[fe80::221:5eff:fe46:3040 44444] <- eth0宛
from(0):fe80::21e:c2ff:feb8:9058%eth0:49382
pktinfo->ipi6_addr=fe80::221:5eff:fe46:3040
pktinfo->ipi6_ifindex=3
*************************
*************************
buf=[fe80::21b:21ff:fe35:61d0 44444] <- eth1宛
from(0):fe80::21e:c2ff:feb8:9058%eth1:55254
pktinfo->ipi6_addr=fe80::21b:21ff:fe35:61d0
pktinfo->ipi6_ifindex=2
*************************
*************************
buf=[ff02::1 44444] <- マルチキャスト宛
from(0):fe80::21e:c2ff:feb8:9058%eth0:55255
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=3
*************************
*************************
buf=[ff02::1 44444] <- マルチキャスト宛
from(0):fe80::21e:c2ff:feb8:9058%eth1:55255
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=2
*************************
」
Solarisでの受信
「
bge0: fe80::209:3dff:fe13:3e3b/10
bge1: fe80::209:3dff:fe13:3e3c/10
1:lo0
2:bge0
3:bge1
use IPV6_RECVPKTINFO
bind to :: 44444
*************************
buf=[fe80::209:3dff:fe13:3e3b 44444] <- bge0宛
from(0):fe80::21e:c2ff:feb8:9058%bge0:53065
pktinfo->ipi6_addr=fe80::209:3dff:fe13:3e3b
pktinfo->ipi6_ifindex=2
*************************
*************************
buf=[fe80::209:3dff:fe13:3e3c 44444] <- bge1宛
from(0):fe80::21e:c2ff:feb8:9058%bge1:53066
pktinfo->ipi6_addr=fe80::209:3dff:fe13:3e3c
pktinfo->ipi6_ifindex=3
*************************
*************************
buf=[ff02::1 44444] <- マルチキャスト宛
from(0):fe80::21e:c2ff:feb8:9058%bge0:53067
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=2
*************************
」
BSD(MacOSX)での受信
「
en0: fe80::21e:c2ff:feb8:9058
en5: fe80::21d:73ff:fe68:4f3e
1:lo0
2:gif0
3:stf0
4:en0
6:en3
7:en2
5:en5
use IPV6_PKTINFO
bind to :: 44444
*************************
buf=[fe80::21e:c2ff:feb8:9058 44444] <- en0宛
from(0):fe80::221:5eff:fe46:3040%en0:54100
pktinfo->ipi6_addr=fe80::21e:c2ff:feb8:9058
pktinfo->ipi6_ifindex=4
*************************
*************************
buf=[fe80::21d:73ff:fe68:4f3e 44444] <- en5宛
from(0):fe80::221:5eff:fe46:3040%en5:37547
pktinfo->ipi6_addr=fe80::21d:73ff:fe68:4f3e
pktinfo->ipi6_ifindex=5
*************************
*************************
buf=[ff02::1 44444] <- マルチキャスト宛
from(0):fe80::221:5eff:fe46:3040%en5:47019
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=5
*************************
*************************
buf=[ff02::1 44444] <- マルチキャスト宛
from(0):fe80::221:5eff:fe46:3040%en0:47019
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=4
*************************
」
送信の動き
「
en0: fe80::21e:c2ff:feb8:9058
en5: fe80::21d:73ff:fe68:4f3e
1:lo0
2:gif0
3:stf0
4:en0
6:en3
7:en2
5:en5
use IPV6_PKTINFO
bind to :: 44444
*************************
buf=[fe80::21e:c2ff:feb8:9058 44444] <- en0宛
from(0):fe80::221:5eff:fe46:3040%en0:54100
pktinfo->ipi6_addr=fe80::21e:c2ff:feb8:9058
pktinfo->ipi6_ifindex=4
*************************
*************************
buf=[fe80::21d:73ff:fe68:4f3e 44444] <- en5宛
from(0):fe80::221:5eff:fe46:3040%en5:37547
pktinfo->ipi6_addr=fe80::21d:73ff:fe68:4f3e
pktinfo->ipi6_ifindex=5
*************************
*************************
buf=[ff02::1 44444] <- マルチキャスト宛
from(0):fe80::221:5eff:fe46:3040%en5:47019
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=5
*************************
*************************
buf=[ff02::1 44444] <- マルチキャスト宛
from(0):fe80::221:5eff:fe46:3040%en0:47019
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=4
*************************
komata-MacBook-Air:SendRecvMsgTest komata$ send6.txt
-bash: send6.txt: command not found
komata-MacBook-Air:SendRecvMsgTest komata$ cat send6.txt
/// from linux ///
//// ./send6 eth0 fe80::221:5eff:fe46:3040
*************************
buf=[fe80::21e:c2ff:feb8:9058 44444]
from(0):fe80::221:5eff:fe46:3040%en0:52108 <- eth0からen0宛に届いている
pktinfo->ipi6_addr=fe80::21e:c2ff:feb8:9058
pktinfo->ipi6_ifindex=4
*************************
*************************
buf=[ff02::1 44444] <- マルチキャスト宛
from(0):fe80::221:5eff:fe46:3040%en5:35483 <- eth0からen5宛に届いている
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=5
*************************
*************************
buf=[ff02::1 44444]
from(0):fe80::221:5eff:fe46:3040%en0:35483 <- 同時にen0宛にも届いている
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=4
*************************
//// ./send6 eth1 fe80::21b:21ff:fe35:61d0
*************************
buf=[fe80::21e:c2ff:feb8:9058 44444]
from(0):fe80::21b:21ff:fe35:61d0%en0:56278 <- eth1からen0宛に届いている
pktinfo->ipi6_addr=fe80::21e:c2ff:feb8:9058
pktinfo->ipi6_ifindex=4
*************************
*************************
buf=[ff02::1 44444] <- マルチキャスト宛
from(0):fe80::21b:21ff:fe35:61d0%en5:51383 <- eth1からen5宛に届いている
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=5
*************************
*************************
buf=[ff02::1 44444]
from(0):fe80::21b:21ff:fe35:61d0%en0:51383 <- 同時にen0宛にも届いている
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=4
*************************
/// from solaris ///
//// ./send6 bge0 fe80::209:3dff:fe13:3e3b
*************************
buf=[fe80::21e:c2ff:feb8:9058 44444]
from(0):fe80::209:3dff:fe13:3e3b%en0:42806 <- bge0からen0宛に届いている
pktinfo->ipi6_addr=fe80::21e:c2ff:feb8:9058
pktinfo->ipi6_ifindex=4
*************************
*************************
buf=[ff02::1 44444] <- マルチキャスト宛
from(0):fe80::209:3dff:fe13:3e3b%en5:42841 <- bge0からen5宛に届いている
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=5
*************************
*************************
buf=[ff02::1 44444]
from(0):fe80::209:3dff:fe13:3e3b%en0:42841 <- 同時にen0宛にも届いている
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=4
*************************
//// ./send6 bge1 fe80::209:3dff:fe13:3e3c
*************************
buf=[fe80::21e:c2ff:feb8:9058 44444]
from(0):fe80::209:3dff:fe13:3e3c%en0:42809 <- bge1からen0宛に届いている
pktinfo->ipi6_addr=fe80::21e:c2ff:feb8:9058
pktinfo->ipi6_ifindex=4
*************************
*************************
buf=[ff02::1 44444] <- マルチキャスト宛
from(0):fe80::209:3dff:fe13:3e3c%en5:42842 <- bge1からen5宛に届いている
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=5
*************************
*************************
buf=[ff02::1 44444]
from(0):fe80::209:3dff:fe13:3e3c%en0:42842 <- 同時にen0宛にも届いている
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=4
*************************
/// BSD ///
//// ./send6 en0 fe80::21e:c2ff:feb8:9058
*************************
buf=[fe80::21e:c2ff:feb8:9058 44444]
from(0):fe80::21e:c2ff:feb8:9058%en0:51278 <- en0からen0宛に届いている
pktinfo->ipi6_addr=fe80::21e:c2ff:feb8:9058
pktinfo->ipi6_ifindex=4
*************************
bind to :: 44444
*************************
buf=[ff02::1 44444] <- マルチキャスト宛
from(0):fe80::21e:c2ff:feb8:9058%en0:62491 <- en0からen0宛に届いている
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=4
*************************
*************************
buf=[ff02::1 44444]
from(0):fe80::21e:c2ff:feb8:9058%en5:62491 <- 同時にen5宛にも届いている
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=5
*************************
//// ./send6 en5 fe80::21d:73ff:fe68:4f3e
*************************
buf=[fe80::21e:c2ff:feb8:9058 44444]
from(0):fe80::21d:73ff:fe68:4f3e%en0:51279 <- en5からen0宛に届いている
pktinfo->ipi6_addr=fe80::21e:c2ff:feb8:9058
pktinfo->ipi6_ifindex=4
*************************
*************************
buf=[ff02::1 44444] <- マルチキャスト宛
from(0):fe80::21d:73ff:fe68:4f3e%en5:62492 <- en5からen5宛に届いている
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=5
*************************
*************************
buf=[ff02::1 44444]
from(0):fe80::21d:73ff:fe68:4f3e%en0:62492 <- 同時にen0宛にも届いている
pktinfo->ipi6_addr=ff02::1
pktinfo->ipi6_ifindex=4
*************************
」
IPv4に比べるとOSによる違いは少ない感じです。Solarisでの受信でマルチキャスト宛が一つしか届かないのはIPMPにしているからかも知れません。