Могу ли я (IGMP) присоединиться к потоку на двух сетевых картах и ​​ответить на запросы (IGMP) на обеих сетевых картах в Linux? - PullRequest
3 голосов
/ 14 мая 2019

Я сделал приложение для Linux для получения многоадресного трафика.Это работает, когда я подключаюсь к одному интерфейсу.Когда я подключаюсь к потоку, в Wireshark я вижу соединение IGMP, и когда коммутатор отправляет запросы IGMP, Linux отвечает с отчетом IGMP для потока.

Однако мне нужна большая пропускная способность, чем мой единственный интерфейсможет обеспечить.Для большей пропускной способности у меня есть несколько интерфейсов в одной сети.Поэтому я продублировал свой код, чтобы два интерфейса подключались к потоку.В этом случае в Wireshark я вижу соединение IGMP на обоих интерфейсах, но когда коммутатор отправляет запросы IGMP, Linux отвечает только отчетом IGMP на одном интерфейсе.Таким образом происходит тайм-аут переключения, и я теряю поток на интерфейсе, который не сообщает.

Вот воспроизводимый пример.Он не получает никаких данных, но достаточно увидеть, что проблема возникает в Wireshark:

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
   // Create and fill internet socket address structure for both sockets.
   struct sockaddr_in internetSocketAdressStructure;
   bzero(&internetSocketAdressStructure, sizeof(internetSocketAdressStructure));
   internetSocketAdressStructure.sin_family = AF_INET;
   internetSocketAdressStructure.sin_addr.s_addr=htonl(INADDR_ANY);
   internetSocketAdressStructure.sin_port = htons(10000);

   // Create first socket.
   int firstSocketToUse;
   if ((firstSocketToUse = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
      perror("First socket failed");
      exit(1);
   }

   // Set first socket for address reuse.
   int reuseAddressForFirstSocket;
   reuseAddressForFirstSocket = 1;
   if (setsockopt(firstSocketToUse, SOL_SOCKET, SO_REUSEADDR, ( char* )&reuseAddressForFirstSocket, sizeof(reuseAddressForFirstSocket) ) == -1 ) {
      perror("Error setting first socket for address reuse");
      exit(1);
   }

   // Bind first socket.
   if (bind(firstSocketToUse, (struct sockaddr *)&internetSocketAdressStructure, sizeof(internetSocketAdressStructure))==-1) {
      perror("First bind failed");
      exit(1);
   }

   // Join stream on first socket.
   struct ip_mreq multicastRequestOnFirstInterface;
   multicastRequestOnFirstInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
   multicastRequestOnFirstInterface.imr_interface.s_addr = inet_addr("25.25.40.116");
   if (setsockopt(firstSocketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnFirstInterface, sizeof(multicastRequestOnFirstInterface)) == -1) {
      perror("Error joining multicast group on Interface 1");
      exit(1);
   }

   // Create second socket.
   int secondSocketToUse;
   if ((secondSocketToUse = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
      perror("Second socket failed");
      exit(1);
   }

   // Set second socket for address reuse.
   int reuseAddressForSecondSocket;
   reuseAddressForSecondSocket = 1;
   if (setsockopt(secondSocketToUse, SOL_SOCKET, SO_REUSEADDR, ( char* )&reuseAddressForSecondSocket, sizeof(reuseAddressForSecondSocket) ) == -1 ) {
      perror("Error setting second socket for address reuse");
      exit(1);
   }

   // Bind second socket.
   if (bind(secondSocketToUse, (struct sockaddr *)&internetSocketAdressStructure, sizeof(internetSocketAdressStructure))==-1) {
      perror("Second bind failed");
      exit(1);
   }

   // Join stream on second socket.
   struct ip_mreq multicastRequestOnSecondInterface;
   multicastRequestOnSecondInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
   multicastRequestOnSecondInterface.imr_interface.s_addr = inet_addr("25.25.40.134");
   if (setsockopt(secondSocketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnSecondInterface, sizeof(multicastRequestOnSecondInterface)) == -1) {
      perror("Error joining multicast group on Interface 2");
      exit(1);
   }

   // Wait forever.
   while(1) {}
}

Я видел сообщение в Stackoverflow, предлагающее сделать это только с одним сокетом,так что я попробовал, но возникает та же проблема.Вот код для этого:

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
   // Create and fill internet socket address structure for the socket.
   struct sockaddr_in internetSocketAdressStructure;
   bzero(&internetSocketAdressStructure, sizeof(internetSocketAdressStructure));
   internetSocketAdressStructure.sin_family = AF_INET;
   internetSocketAdressStructure.sin_addr.s_addr=htonl(INADDR_ANY);
   internetSocketAdressStructure.sin_port = htons(10000);

   // Create socket.
   int socketToUse;
   if ((socketToUse = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
      perror("socket failed");
      exit(1);
   }

   // Bind socket.
   if (bind(socketToUse, (struct sockaddr *)&internetSocketAdressStructure, sizeof(internetSocketAdressStructure))==-1) {
      perror("bind failed");
      exit(1);
   }

   // Set first socket for address reuse.
   int reuseAddress;
   reuseAddress = 1;
   if (setsockopt(socketToUse, SOL_SOCKET, SO_REUSEADDR, ( char* )&reuseAddress, sizeof(int) ) == -1 ) {
      perror("Error setting socket for address reuse");
      exit(1);
   }

   // Join stream on first interface.
   struct ip_mreq multicastRequestOnFirstInterface;
   multicastRequestOnFirstInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
   multicastRequestOnFirstInterface.imr_interface.s_addr = inet_addr("25.25.40.116");
   if (setsockopt(socketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnFirstInterface, sizeof(multicastRequestOnFirstInterface)) == -1)
   {
      perror("Error joining multicast group on first interface");
      exit(1);
   }

   // Join stream on second interface.
   struct ip_mreq multicastRequestOnSecondInterface;
   multicastRequestOnSecondInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
   multicastRequestOnSecondInterface.imr_interface.s_addr = inet_addr("25.25.40.134");
   if (setsockopt(socketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnSecondInterface, sizeof(multicastRequestOnSecondInterface)) == -1)
   {
      perror("Error joining multicast group on second interface");
      exit(1);
   }

   // Wait forever.
   while(1) {}
}

Я также пытался перевести интерфейсы в беспорядочный режим:

sudo ip link set interface1 promisc on
sudo ip link set interface2 promisc on

и добавить опцию noprefixroute для каждого интерфейса:

sudo ip addr change 25.25.40.134 dev interface1 noprefixroute
sudo ip addr change 25.25.40.116 dev interface2 noprefixroute

Обе эти проблемы не смогли решить мою проблему.

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

Мне удалось решить проблему, поместив оба порта на коммутаторе в разные vlans, хотя это неуклюжее / ужасное решение, которое можетнеприемлемо.

Возможно ли подключение к многоадресным потокам в одной сети через два интерфейса и настроить сеть для ответа на запросы IGMP на обоих интерфейсах?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...