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 показывает, что пакеты поступают, но сбрасываются с моей конфигурацией выше.
Ответы
Ответ 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 и назначьте только индекс интерфейса, он должен привязываться к этому интерфейсу и получать пакеты. Посмотрите, поможет ли это.