环回接口上的ipv6多播问题

时间:2019-05-03 16:43:50

标签: networking ipv6 multicast loopback

我们有一个模拟,其中I / O处理器使用IPv6多播消息将数据发送到子系统。但是,在某些情况下,我们希望子系统与I / O处理器在同一主机上建模。尝试执行此操作时,发送失败,并显示错误“网络不可达”。

我们正在运行SUSE Linux Enterprise Server 12 SP3,其内核为4.4.73-5-default。

如果我们使用IPv4多播,我们能够发送消息。我们必须为多播地址添加一条路由:

sudo ip route添加224.0.0.0/7 dev lo

我尝试为IPv6添加相应的路由:

sudo ip -6路由添加ff00 :: / 8 dev lo

但是它不起作用。当我尝试显示路由时,它还表示网络不可达(度量标准为-1)

ip -6路由显示表全部

在其输出中具有此

无法访问的默认开发者原型内核指标4294967295错误-101优选介质

为接口设置了多播标志:

ip -6链接显示dev lo

产生

1:lo:mtu 65536 qdisc无队列状态未知模式默认组默认qlen 1     链接/循环bass 00:00:00:00:00:00 00:00:00:00:00:00

这是一个我用来尝试发送数据的简单程序:


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <netdb.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int
main(void)
{
    int sock;
    int status;
    const char *dest_addr_str = "ff15::3";
    const int port_num = 50000;
    const char *ifname = "lo";
    unsigned int ifindex;
    const char *buffer = "Hello";
    size_t msg_len;
    ssize_t num_bytes;
    struct sockaddr_in6 dest_addr;

    sock = socket(AF_INET6, SOCK_DGRAM, 0);
    if (sock == -1)
    {
        printf("Error creating socket, errno = %d\n", errno);
        exit(EXIT_FAILURE);
    }

    /* Setup destinaton address */
    memset(&dest_addr, 0, sizeof(struct sockaddr_in6));
    dest_addr.sin6_family = AF_INET6;
    dest_addr.sin6_port = htons(port_num);
    if (inet_pton(AF_INET6, dest_addr_str, &dest_addr.sin6_addr) <= 0)
    {
        printf("inet_pton error\n, errno = %d", errno);
        exit(EXIT_FAILURE);
    }

    /* Set the outgoing interface */
    ifindex = if_nametoindex(ifname);
    printf("outgoing interface: %u\n", ifindex);
    status = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
                        &ifindex, sizeof(ifindex));

    if (status == -1)
    {
        printf("setting multicast if failed, errno = %d\n", errno);
        exit(EXIT_FAILURE);
    }

    /* Send message */
    msg_len = strlen(buffer);
    num_bytes = sendto(sock, buffer, msg_len, 0,
                       (struct sockaddr *) &dest_addr,
                       sizeof(struct sockaddr_in6));

    if (num_bytes == -1)
    {
        printf("sendto failed, errno = %d\n", errno);
    }
    else
    {
        printf("sent %d bytes\n", (int)num_bytes);
    }

    close(sock);

    return 0;
}

这是一个更简单的接收器,在我的测试中它什么也没收到:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUF_SIZE 1024

int
main(void)
{
    int status;
    int sock;
    char buf[BUF_SIZE];
    const char *grp_addr_str = "ff15::3";
    const char *local_addr_str = "::";
    const int port_num = 50000;
    const char *ifname = "lo";
    int flag = 1;
    struct sockaddr_in6 addr;
    struct sockaddr_in6 peer_addr;
    struct ipv6_mreq  mreq;
    ssize_t num_bytes;
    socklen_t len;
    const char *p_addr;
    char peer_addr_str[INET6_ADDRSTRLEN];

    sock = socket(AF_INET6, SOCK_DGRAM, 0);
    if (sock == -1)
    {
        printf("Error creating socket, errno = %d\n", errno);
        exit(EXIT_FAILURE);
    }

    status = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
    if (status == -1)
    {
        printf("Reuse ADDR failed, errno = %d\n", errno);
        exit(EXIT_FAILURE);
    }

    memset(&addr, 0, sizeof(struct sockaddr_in6));
    addr.sin6_family = AF_INET6;
    addr.sin6_port = htons(port_num);

    status = inet_pton(AF_INET6, local_addr_str, &addr.sin6_addr);
    if (status == 0)
    {
        printf("inet_pton failed for local addr, errno = %d\n", errno);
        exit(EXIT_FAILURE);
    }

    status = bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in6));
    if (status == -1)
    {
        printf ("Error binding socket, errno = %d\n", errno);
        exit(EXIT_FAILURE);
    }

    status = inet_pton(AF_INET6, grp_addr_str, &mreq.ipv6mr_multiaddr);
    if (status == 0)
    {
        printf("inet_pton failed for multicast addr, errno = %d\n", errno);
        exit(EXIT_FAILURE);
    }

    mreq.ipv6mr_interface = if_nametoindex(ifname);

    status = setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
                        (char *)&mreq, sizeof(mreq));

    if (status == -1)
    {
        printf("Join group failed, errno = %d\n", errno);
        exit(EXIT_FAILURE);
    }

    /* Receive message */
    len = sizeof(struct sockaddr_in6);
    num_bytes = recvfrom(sock, buf, BUF_SIZE, 0,
                         (struct sockaddr *) &peer_addr, &len);
    if (num_bytes == -1)
    {
        printf ("recvfrom failed, errno = %d\n", errno);
        exit(EXIT_FAILURE);
    }
    else
    {
        /* Display address of client that sent the message */
        p_addr = inet_ntop(AF_INET6, &peer_addr.sin6_addr, peer_addr_str,
                           INET6_ADDRSTRLEN);
        if (p_addr  == 0)
        {
            printf("Couldn't convert client address to string\n");
        }
        else
        {
            printf("Server received %d bytes from (%s, %u, %s)\n",
                   (int)num_bytes, peer_addr_str,
                   ntohs(peer_addr.sin6_port), buf);
        }
    }

    return 0;
}

如果我将发送方和接收方中的ifname更改为另一个接口,例如eth8,则发送方发送正常,接收方接收并打印消息。

但是,如果我将lo作为接口,则发件人会显示此错误:

传出接口:1 发送失败,errno = 101

表示“网络不可达”。

如果我在dest_addr_str中使用前缀ff11(本地接口)而不是ff15(本地站点),则能够将数据发送到其他接口(在lo之外)。那可能就是我们要做的。但是我们希望使用环回接口,我们不必更改多播地址。更改这些意味着我们必须维护两个消息数据库。

感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

您只是想测试?实际系统不会通过环回发送多播消息。

我建议您改用 dummy 界面:

# ip link add dummy0 type dummy
# ip link set dev dummy0 up
# ip address show dev dummy0
24: dummy0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether 86:0a:20:b1:b5:f1 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::840a:20ff:feb1:b5f1/64 scope link 
       valid_lft forever preferred_lft forever

虚拟接口模拟已建立但未连接到任何其他主机的以太网连接。它可以用于使用分配给它的任何IP地址与同一主机上的其他进程进行通信,并且它还应该接受多播流量。