Проблема с многоадресной передачей ipv6 через петлевой интерфейс - PullRequest
0 голосов
/ 03 мая 2019

У нас есть симуляция, в которой процессор ввода-вывода отправляет данные подсистемам, используя многоадресные сообщения IPv6. Однако у нас есть сценарии, в которых мы хотим, чтобы подсистемы были смоделированы на том же хосте, что и процессор ввода-вывода. Когда мы пытаемся это сделать, отправка завершается с ошибкой «Сеть недоступна».

Мы используем SUSE Linux Enterprise Server 12 SP3 с ядром 4.4.73-5 по умолчанию.

Мы можем отправлять сообщения, если используем многоадресную рассылку IPv4. Нам пришлось добавить маршрут для адресов многоадресной рассылки:

sudo ip route add 224.0.0.0/7 dev lo

Я попытался добавить соответствующий маршрут для IPv6:

sudo ip -6 route add ff00 :: / 8 dev lo

но это не работает. Когда я пытаюсь показать маршрут, он также говорит, что сеть недоступна (и имеет -1 для метрики)

ip -6 route show table all

имеет это среди своих выходных

недостижимое значение по умолчанию dev lo Proto метрика ядра 4294967295 ошибка -101 pref medium

Для интерфейса установлен флаг многоадресной рассылки:

ip -6 link show dev lo

производит

1: lo: mtu 65536 Состояние qdisc noqueue Режим НЕИЗВЕСТНЫЙ Группа ПО УМОЛЧАНИЮ по умолчанию qlen 1 link / loopbask 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;
}

Если я изменю имя if в отправителе и получателе на другой интерфейс, такой как eth8, отправитель отправляет штраф, а получатель получает и печатает сообщение.

Однако, если в качестве интерфейса используется lo, отправитель печатает эту ошибку:

исходящий интерфейс: 1 отправить не удалось, errno = 101

, что переводится как «Сеть недоступна».

Я могу отправлять данные на другие интерфейсы (кроме lo), если я использую префикс ff11 (интерфейс локальный) вместо ff15 (сайт локальный) в dest_addr_str. Это может быть то, что мы должны сделать. Но мы надеялись, что при использовании интерфейса обратной связи нам не придется менять адреса многоадресной рассылки. Изменение этих средств означает, что мы должны поддерживать две базы данных сообщений.

Спасибо за любую помощь.

1 Ответ

0 голосов
/ 03 мая 2019

Вы просто пытаетесь проверить? Реальная система не будет отправлять многоадресные сообщения через обратную связь.

Я бы посоветовал вам вместо этого установить dummy interface:

# 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

Пустой интерфейс имитирует соединение Ethernet, которое установлено, но не подключено ни к каким другим хостам. Его можно использовать для связи с другими процессами на том же хосте, используя любые назначенные ему IP-адреса, и он также должен принимать многоадресный трафик.

...