Нужен IPv6 многоадресный код C, который работает на iOS 9

Apple теперь требует, чтобы приложения для iOS 9 соответствовали требованиям IPv6. Мы в основном в порядке, за исключением небольшого кода, который отправляет широковещательную рассылку UDP - это теперь не работает в iOS 9.

Все, что я прочитал, говорит мне, что многоадресная рассылка UDP - это правильный способ сделать это в IPv6. Я нашел некоторый пример кода, но он не работает ни на какой версии iOS или Mac OS X, которую я пробовал.

Этот код вызывается из C/С++ lib внутри нашей программы - сложно сделать обратный вызов в Swift, Obj-C, Java и т.д. И этот код будет разделяться версиями Mac OS X и Android нашего приложение. Можно подумать, что можно сделать многоадресную IPv6 в C в любой среде POSIX!

В приведенном ниже примере выполнение выполняется до окончательного вызова sendto(), который фактически отправляет сообщение UDP. Ошибка sendto(), с ошибкой, установленной в EBROKENPIPE (22) после сбоя.

Мое лучшее предположение заключается в том, что мне не хватает какого-либо требуемого вызова setsockopt(), или я использую неправильный многоадресный адрес. Прямо сейчас, я в тупике.

Здесь вызов функции, которую я делаю (для многоадресной рассылки "Кто-нибудь есть?" на UDP-порту 4031):

char *msg = "Is anybody out there?";
err = multicast_udp_msg ( "FF01::1111", 4031, msg, strlen(msg) );

Здесь код, который вызывается:

// Multicasts a message on a specific UDP port.
// myhost - IPv6 address on which to multicast the message (i.e., ourself)
// port - UDP port on which to broadcast the mssage
// msg - message contents to broadcast
// msgsize - length of message in bytes
// Return value is zero if successful, or nonzero on error.

int multicast_udp_msg ( char *myhost, short port, char *msg, size_t msgsize )
{
    int        sockfd, n;
    char    service[16] = { 0 };
    int        err = 0;
    struct addrinfo hints = { 0 }, *res, *ressave;
    struct sockaddr_storage addr = { 0 };

    hints.ai_family = AF_INET6;
    hints.ai_socktype = SOCK_DGRAM;

    sprintf ( service, "%hd", port );
    n = getaddrinfo ( myhost, service, &hints, &res );
    if ( n < 0 )
    {
        fprintf(stderr, "getaddrinfo error:: [%s]\n", gai_strerror(n));
        return -1;
    }

    ressave = res;

    sockfd = socket ( res->ai_family, res->ai_socktype, res->ai_protocol );
    if ( sockfd >= 0 )
    {
        memcpy ( &addr, res->ai_addr, sizeof ( addr ) );
        if ( joinGroup ( sockfd, 0, 8, &addr ) == 0 )
            if ( bind ( sockfd, res->ai_addr, res->ai_addrlen ) == 0 )
                if ( sendto ( sockfd, msg, msgsize, 0, (struct sockaddr *) &addr, sizeof ( addr ) ) < 0 )
                    err = errno;

        close ( sockfd );

        res = res->ai_next;
    }

    freeaddrinfo ( ressave );
    return err;
}

int
joinGroup(int sockfd, int loopBack, int mcastTTL,
         struct sockaddr_storage *addr)
{
    int r1, r2, r3, retval;

    retval=-1;

    switch (addr->ss_family) {
        case AF_INET: {
            struct ip_mreq      mreq;

            mreq.imr_multiaddr.s_addr=
            ((struct sockaddr_in *)addr)->sin_addr.s_addr;
            mreq.imr_interface.s_addr= INADDR_ANY;

            r1= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP,
                           &loopBack, sizeof(loopBack));
            if (r1<0)
                perror("joinGroup:: IP_MULTICAST_LOOP:: ");

            r2= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
                           &mcastTTL, sizeof(mcastTTL));
            if (r2<0)
                perror("joinGroup:: IP_MULTICAST_TTL:: ");

            r3= setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                           (const void *)&mreq, sizeof(mreq));
            if (r3<0)
                perror("joinGroup:: IP_ADD_MEMBERSHIP:: ");

        } break;

        case AF_INET6: {
            struct ipv6_mreq    mreq6;

            memcpy(&mreq6.ipv6mr_multiaddr,
                   &(((struct sockaddr_in6 *)addr)->sin6_addr),
                   sizeof(struct in6_addr));

            mreq6.ipv6mr_interface= 0; // cualquier interfaz

            r1= setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
                           &loopBack, sizeof(loopBack));
            if (r1<0)
                perror("joinGroup:: IPV6_MULTICAST_LOOP:: ");

            r2= setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
                           &mcastTTL, sizeof(mcastTTL));
            if (r2<0)
                perror("joinGroup:: IPV6_MULTICAST_HOPS::  ");

            r3= setsockopt(sockfd, IPPROTO_IPV6,
                           IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6));
            if (r3<0)
                perror("joinGroup:: IPV6_ADD_MEMBERSHIP:: ");

        } break;

        default:
            r1=r2=r3=-1;
    }

    if ((r1>=0) && (r2>=0) && (r3>=0))
        retval=0;

    return retval;
}

Мысли приветствуются!

-Tim

Ответ 1

После некоторого времени назад и вперед с Apple, а также в дополнительном контексте, у нас есть ответ. Но это не ответ на мой оригинальный вопрос. Во-первых, здесь поток Apple для контекста:

https://forums.developer.apple.com/message/71107

Оказывается, что многоадресная передача IPv6 на самом деле не была тем, что нам нужно для решения реальной проблемы, а именно поиска устаревшего встроенного устройства в локальной сети Wi-Fi. Для этого нам действительно нужно было использовать широковещательную передачу IPv4 UDP. Наше встроенное устройство игнорирует многоадресные пакеты IPv6, такие как Земля, игнорирует нейтрино, пролетая через него.

Apple предоставила нам вызов setsockopt(), который разрешил широковещательную передачу IPv4 UDP для работы в iOS 9 в сети Wi-Fi инфраструктуры. Это предполагаемый прецедент для этой функции. И Apple также дала нам вероятную причину сбоя, когда эта трансляция не работала в сети Ad Hoc Wi-Fi (которая, похоже, известна как проблема с iOS 9).

Итак, хотя мой первоначальный вопрос здесь не ответил, основная проблема была решена.