C -IPv6多播接口选择



选择使用IPv4的传出流量的接口的setsockoptIP_MULTICAST_IF,它接受两个参数。从ip(4)手动页面:

设置多播套接字的本地设备。争论 setsockopt(2)ip_mreqn或(由于Linux 3.5(ip_mreq 结构类似于IP_ADD_MEMBERSHIPin_addr结构。

尝试使用IPv6流量进行类似操作时,该选项会更改为接口索引。从ipv6(4)手动页面:

设置用于插座上的多播数据包的设备。 仅适用于SOCK_DGRAMSOCK_RAW套接字。这 参数是指向接口索引的指针(请参阅netdevice(7(( 在整数中。

在网络接口(例如eth0(中有多个地址分配给它的情况下会发生什么?IPv6套接字接口已删除了分别使用每个地址的可能性吗?

如果您的界面只有一个链接 - 本地地址(fe80::/10(和一个公共路由地址,则外包数据包的源地址取决于您发送到的多播地址的范围。

ipv6多播地址具有 ffxy::/16的形式,其中 x 是标志字段, y 是范围。如果范围为1(本地接口(或2(链接本地(,则源地址将是链接 - 本地地址。如果范围为3或更高,则源地址将是公共路线地址。

另一方面,您的接口具有多个公共路由地址,则在发送数据报时需要使用sendmsg,以便您可以使用IPV6_PKTINFO控制标头设置源地址。

以下是您如何执行此操作的完整示例,假设您在一个接口上具有2001::1:2:32002::1:2:3为IPv6地址,而ff03::1:2:3是您发送的多播地址。

#define _GNU_SOURCE   // needed for some IPv6 datatypes to be visible
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
// multicast address to send to
const char *maddr = "ff03::1:2:3";
// uncomment the line for the source address you want to use
const char *srcaddr = "2001::1:2:3";
//const char *srcaddr = "2002::1:2:3";
int main()
{
    int sock;
    struct sockaddr_in6 dstaddr;
    struct iovec iovec[1];
    struct msghdr msg;
    struct cmsghdr* cmsg;
    char msg_control[1024];
    char udp_packet[] = "this is a test";
    int cmsg_space;
    struct in6_pktinfo *pktinfo;
    dstaddr.sin6_family = AF_INET6;
    inet_pton(AF_INET6, maddr, &dstaddr.sin6_addr);
    dstaddr.sin6_port = htons(5555);
    dstaddr.sin6_flowinfo = 0;
    dstaddr.sin6_scope_id = 0;
    if ((sock=socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
        perror("socket failed");
        exit(1);
    }
    // set up the msghdr structure with the destination address, 
    // buffer to send, and control info buffer
    iovec[0].iov_base = udp_packet;
    iovec[0].iov_len = strlen(udp_packet);
    msg.msg_name = &dstaddr;
    msg.msg_namelen = sizeof(dstaddr);
    msg.msg_iov = iovec;
    msg.msg_iovlen = sizeof(iovec) / sizeof(*iovec);
    msg.msg_control = msg_control;
    msg.msg_controllen = sizeof(msg_control);
    msg.msg_flags = 0;
    // add IPV6_PKTINFO control message to specify source address
    cmsg_space = 0;
    cmsg = CMSG_FIRSTHDR(&msg);
    cmsg->cmsg_level = IPPROTO_IPV6;
    cmsg->cmsg_type = IPV6_PKTINFO;
    cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
    pktinfo = (struct in6_pktinfo*) CMSG_DATA(cmsg);
    pktinfo->ipi6_ifindex = 0;
    inet_pton(AF_INET6, srcaddr, &pktinfo->ipi6_addr);
    cmsg_space += CMSG_SPACE(sizeof(*pktinfo));
    msg.msg_controllen = cmsg_space;
    // send packet
    if (sendmsg(sock, &msg, 0) == -1) {
        perror("send failed");
    }
    return 0;
}

最新更新