c - IPv6 组播接口(interface)选择

标签 c linux sockets ipv6 multicast

setsockopt 为 IPv4 传出流量选择接口(interface)的方法是 IP_MULTICAST_IF,它接受两个参数。来自 ip(4) 手册页:

Set the local device for a multicast socket. The argument for setsockopt(2) is an ip_mreqn or (since Linux 3.5) ip_mreq structure similar to IP_ADD_MEMBERSHIP, or an in_addr structure.

当尝试对 IPv6 流量执行类似操作时,选项会更改为接口(interface)索引。来自 ipv6(4) 手册页:

Set the device for outgoing multicast packets on the socket. This is allowed only for SOCK_DGRAM and SOCK_RAW socket. The argument is a pointer to an interface index (see netdevice(7)) in an integer.

如果网络接口(interface)(例如 eth0)被分配了多个地址,会发生什么情况?是不是 IPv6 套接字接口(interface)已经消除了单独使用每个地址的可能性?

最佳答案

如果您的接口(interface)只有一个链接本地地址 (fe80::/10) 和一个公共(public)可路由地址,则传出数据包的源地址取决于您的多播地址范围重新发送到。

IPv6 多播地址的格式为 ffxy::/16,其中 x 是标志字段,y 是范围。如果范围是 1(接口(interface)本地)或 2(链接本地),则源地址将是链接本地地址。如果范围是 3 或更高,源地址将是公共(public)可路由地址。

另一方面,如果您的接口(interface)有多个可公开路由的地址,您需要在发送数据报时使用sendmsg,以便您可以使用IPV6_PKTINFO 设置源地址> 控制标题。

下面是如何执行此操作的完整示例,假设您有 2001::1:2:32002::1:2:3 作为一个接口(interface)上的 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;
}

关于c - IPv6 组播接口(interface)选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49502427/

相关文章:

linux - 尽管与存档有直接链接,但对任何 libc 符号的 undefined reference

security - 套接字通信安全问题

c++ - toLocal8bit 通过 TCP 发送

c - 动态扩展数组C

c - epoll_wait 由于 EINTR 而失败,如何补救?

c - 管道只执行一次命令

c++ - 由于 sin6_port 值,sendto 在 Linux 中的 UDP 原始套接字 ipv6 上返回无效参数?

c - GET 请求总是返回一个横幅,就像 HEAD 请求一样

使用C更改文件中单词首字母的大小写

c - 如何在 Linux 中用 C 获取当前进程的处理器 ID?