Linux Multicast-Packets run dry after 4 minutes

Viewed 764

I try to process a multicast packet stream in Linux. For 266s - 278s (it is not always exactly the same time period) the receiving works fine, but after that, no packets are received anymore.

This is how I initialize the multicast:

int arg = 1;
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(fd == -1) {
    fprintf(stderr, "Error creating socket, %s\n", strerror(errno));
    return;
}

if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0) {
    fprintf(stderr, "Failed to set `SO_REUSEADDR`, %s\n", strerror(errno));
    return;
}

fcntl(fd, F_SETFL, O_NONBLOCK);

static struct ifreq intf;
strncpy(intf.ifr_name, cfg->ifname_buf, IF_NAMESIZE);

if(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (char*)&intf, sizeof(intf)) < 0) {
    fprintf(stderr, "Failed to set `SO_BINDTODEVICE`, %s\n", strerror(errno));
    return;
}


struct sockaddr_in sin;
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_port = htons(xxxxx);
sin.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.xxx");

if(bind(fd, (struct sockaddr*)&sin, sizeof(struct sockaddr)) < 0) {
    fprintf(stderr, "Error on binding socket, %s\n", strerror(errno));
    return;
}

ioctl(fd, SIOCGIFADDR, &intf);

struct ip_mreqn igmpv2_req;
memset(&igmpv2_req, 0, sizeof(struct ip_mreqn));

if(inet_pton(AF_INET, "xxx.xxx.xxx.xxx", &igmpv2_req.imr_multiaddr.s_addr)) {

    memcpy(&igmpv2_req.imr_address, &cfg->ifaddr.sin_addr, sizeof(struct in_addr));
    igmpv2_req.imr_ifindex = cfg->ifindex;

    printf("Multiaddr: %s\n", inet_ntoa(igmpv2_req.imr_multiaddr));
    printf("Interfaceaddr: %s\n", inet_ntoa(igmpv2_req.imr_address));
    printf("Ifindex: %d\n", igmpv2_req.imr_ifindex);

    if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &igmpv2_req, sizeof(struct ip_mreqn)) < 0) {
        fprintf(stderr, "Failed to set `IP_ADD_MEMBERSHIP`: %s\n", strerror(errno));
        return;
    } else {
        printf("Saved FD for igmp socket!\n");
        cfg->socket_fd_igmp = fd;
    }
} else {
    fprintf(stderr, "Failed `inet_pton` igmp-multiaddr, %s\n", strerror(errno));
    return;
}

As I said, it works fine for 266 - 280s. After that, no packets are received. I don't know if this is because of the way I add an interface to the multicast group or because of some Kernel queue being full (I receive up to 200k packets per second).

1 Answers

Multicast is not a trivial thing. From what you are describing, the following happens:

When you perform

setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &igmpv2_req, sizeof(struct ip_mreqn))

then the client sends out an igmp join message for this group. Your switch has igmp snooping enabled and now knows to forward the multicast packets for this group to the port of your host.

Now, after the time you observed (4-5 minutes, 260 seconds is indeed the default with many switches) this information in the switch times out, because the host does not send these messages regularly/unsolicited.

You need a multicast router on your network, that regularly queries the hosts, which respond with igmp reports for the multicast groups they are listening to, to keep the igmp snooping tables on the switching hardware up to date.

Alternatively, you can also try to disable igmp snooping on all devices between multicast sender and receiver, so that all multicast message are flooded to all ports (making them broadcast from a layer-2 point of view). This can put quite a high load on the layer-2 segment, so the first method is preferred.

An ugly workaround (that I have not tested yet) could be to drop and re-add membership with setsockopt() every 2-3 minutes. This should force an igmp packet and thus maintain multicast connectivity, but this is not how it is supposed to work.

Related