Подтвердить что ты не робот

C - выберите интерфейс для UDP/многоадресного сокета

Я пытаюсь изменить пример многоадресного прослушивателя/отправителя, чтобы привязать UDP/многоадресный сокет к определенному интерфейсу и не использовать INADDR_ANY макрос.

У меня есть адрес IPv4 интерфейса. Я попробовал следующее, но сокет не получает никаких UDP (одноадресных, широковещательных, многоадресных) пакетов.

struct sockaddr_in addr;
int fd, nbytes;
socklen_t  addrlen;
struct ip_mreq mreq;

// my_ipv4Addr equals current IP as String, e.g. "89.89.89.89"

// create what looks like an ordinary UDP socket */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("socket");
    exit(1);
}

// set up addresses
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
// [-]    addr.sin_addr.s_addr = htonl(INADDR_ANY); 
addr.sin_addr.s_addr = inet_addr(my_ipv4Addr); 
addr.sin_port = htons(port);

// bind socket
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
    perror("bind");
    exit(1);
}

// use setsockopt() to request that the kernel join a multicast group
mreq.imr_multiaddr.s_addr = inet_addr(group);
// [-]    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
mreq.imr_interface.s_addr = inet_addr(my_ipv4Addr);
if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))< 0) {
    perror("setsockopt");
    exit(1);
}

Edit:

Позвольте мне объяснить цель моей программы. Я пишу небольшой инструмент, который проверяет, поддерживает ли сеть широковещательную/многоадресную рассылку. Поэтому я владею системой с двумя интерфейсами и передаю через интерфейс1 пакет многоадресной рассылки и пытаюсь получить его с помощью интерфейса2. Но: пакет должен проходить через сеть, а не устройство loopack.

Идея состоит в том, чтобы блокировать многоадресную петлю на thread1/interface1 с помощью:

u_char loop = 0;
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));

И прослушивать интерфейс thread2/interface 2 для интерфейса. Tcpdump показывает, что пакеты поступают, но сбрасываются с моей конфигурацией выше.

4b9b3361

Ответ 1

с
   addr.sin_addr.s_addr=inet_addr(my_ipv4Addr);
   bind(sockfd,(SA*)&addr,sizeof(addr));
вы можете отправлять пакеты только в группу многоадресной рассылки,
но вы не можете возвращать какие-либо пакеты, даже те, которые отправляются из `my_ipv4Addr '.

поэтому addr.sin_addr.s_addr должен быть htonl(INADDR_ANY).

с
   mreq.imr_interface.s_addr=inet_addr(my_ipv4Addr);
вы можете вернуть все пакеты из группы многоадресной рассылки,
но он отправляет пакеты с интерфейсом по умолчанию (возможно, eth0),
а не тот, который вы указали (например, eth1).
Так что это не влияет.

с
   setsockopt(sockfd,SOL_SOCKET,SO_BINDTODEVICE,ETH1,strlen(ETH1));
вы можете отправлять пакеты через интерфейс ETH1,
но вы можете отправлять только пакеты recv из ip, связанные с ETH1,
вы не можете возвращать какие-либо пакеты от других клиентов.

с
   mreq.imr_interface.s_addr=inet_addr(my_ipv4Addr);
   setsockopt(sockfd,IPPROTO_IP,IP_MULTICAST_IF,&mreq.imr_interface,sizeof(struct in_addr);
вы можете отправлять пакеты через интерфейс, связанный с my_ipv4addr,
также вы можете возвращать любые пакеты от любых клиентов в группе многоадресной рассылки.

Ответ 2

При привязке сокета для приема многоадресного трафика, если вы привязываетесь к локальному адресу, это предотвращает прием многоадресных пакетов в системах, отличных от Windows.

Я впервые обнаружил это, когда выпустил версию 3.6 UFTP с функцией привязки к определенному адресу. Windows отлично справляется с этим, но в системах Linux пакеты многоадресной передачи не были получены приложением. Мне пришлось удалить эту функцию в следующей версии.

Обратите внимание, что вам разрешено напрямую связываться с адресом многоадресной рассылки:

addr.sin_addr.s_addr = inet_addr(group);

В этом случае вы получите трафик только для этого многоадресного адреса в этом сокете. Тем не менее вам все равно необходимо присоединиться к группе многоадресной рассылки на определенном интерфейсе.

Если вы планируете получать данные из нескольких адресов многоадресной рассылки в одном и том же сокете, вам необходимо привязать к INADDR_ANY.

Ответ 3

bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY); 
//addr.sin_addr.s_addr = inet_addr(my_ipv4Addr); 
addr.sin_port = htons(port);

mreq.imr_multiaddr.s_addr = inet_addr(group);
// [-]    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
mreq.imr_interface.s_addr = inet_addr(my_ipv4Addr);

Вам просто нужно изменить свой код, как мой.

Ответ 4

Либо вы упростили свой код ради понимания, либо я что-то пропустил,

Это структура

struct ip_mreqn {
    struct in_addr imr_multiaddr; /* IP multicast group
                                     address */
    struct in_addr imr_address;   /* IP address of local
                                     interface */
    int            imr_ifindex;   /* interface index */
};

страница ip man - IP_ADD_MEMBERSHIP

Но вы имеете в виду

mreq.imr_interface.s_addr = inet_addr(my_ipv4Addr);

Что такое imr_interface? Скомпилирует ли он?

Если вы только что написали имя выше для лучшей читаемости, попробовали ли вы заполнить индекс интерфейса i.e. imr_ifindex к определенному интерфейсу, к которому вы хотите подключиться.

Я предполагаю, что если вы оставите imrr_address и назначьте только индекс интерфейса, он должен привязываться к этому интерфейсу и получать пакеты. Посмотрите, поможет ли это.