Получение данных мультикастом
От: prrt  
Дата: 01.03.17 10:23
Оценка:
На один из интерфейсов сервера получаю multicast данные. Данные поступают корректно:

# tcpdump -i eth1 udp port 30001 -vv -X
показывает, что всё ок, данные есть

Но через boost asio ничего не приходит.

Получаю так:
boost::asio::ip::address listen_address = boost::asio::ip::address::from_string("192.168.2.2");
boost::asio::ip::address multicast_address = boost::asio::ip::address::from_string("239.255.0.1");
boost::asio::ip::udp::endpoint listen_endpoint(listen_address, 30001);
socket_.open(listen_endpoint.protocol(), error);
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket_.bind(listen_endpoint);
socket_.set_option(boost::asio::ip::multicast::join_group(multicast_address));
socket_.async_receive_from(
    boost::asio::buffer(&inputData[0], 10000000), sender_endpoint_,
        strand_.wrap(
            boost::bind(&ClientUdp::handle_receive_from, shared_from_this(),
              boost::asio::placeholders::error,
              boost::asio::placeholders::bytes_transferred)));

Здесь 192.168.2.2 — IP интерфейса eth1.
239.255.0.1 — IP группы
30001 — порт группы
boost::asio::ip::udp::socket socket_;
boost::asio::ip::udp::endpoint sender_endpoint_;
std::vector<uint8_t> inputData;

Подскажите, что может быть не так, как найти причину, почему через boost asio ничего не приходит?
Отредактировано 01.03.2017 10:43 prrt . Предыдущая версия .
Re: Получение данных мультикастом
От: Mr.Delphist  
Дата: 01.03.17 11:20
Оценка:
Здравствуйте, prrt, Вы писали:


P>Получаю так:

P>[ccode]
P>boost::asio::ip::address listen_address = boost::asio::ip::address::from_string("192.168.2.2");
P>...
P>boost::asio::ip::udp::endpoint listen_endpoint(listen_address, 30001);
P>...
P>socket_.bind(listen_endpoint);

Первое: почему свой локальный айпишник для бинда, ведь мультикаст адресуется не на "192.168.2.2", а на "239.255.0.1"? Попробуйте INADDR_ANY для 100% гарантии.

Второе: по дефолту мультикаст прилетает только на один интерфейс — и не факт что система выберет именно 192.168.2.2. Когда делаем join_group — нет ли там у asio перегрузки для двух аргументов: адрес группы и адрес своего интерфейса? Потому как у олдскульного API там два параметра:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms738695(v=vs.85).aspx

typedef struct ip_mreq {
  struct in_addr  imr_multiaddr;
  struct in_addr  imr_interface;
} IP_MREQ, *PIP_MREQ;

Members
imr_multiaddr
The address of the IPv4 multicast group.

imr_interface
The local IPv4 address of the interface or the interface index on which the multicast group should be joined or dropped. This value is in network byte order. If this member specifies an IPv4 address of 0.0.0.0, the default IPv4 multicast interface is used.

Re[2]: Получение данных мультикастом
От: prrt  
Дата: 01.03.17 11:55
Оценка:
Заменил listen_address на:
boost::asio::ip::address listen_address = boost::asio::ip::address_v4::any();


По поводу join_group — проверил, интерфейс там почему-то никак не задать: http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ip__multicast__join_group.html , хотя и написано: "Socket option to join a multicast group on a specified interface."
А как задать этот интерфейс, на котором ловим UDP пакеты? Делаю под Linux.

В итоге пока не работает...
Отредактировано 01.03.2017 12:10 prrt . Предыдущая версия .
Re[3]: Получение данных мультикастом
От: Mr.Delphist  
Дата: 01.03.17 13:13
Оценка:
Здравствуйте, prrt, Вы писали:

P>Заменил listen_address на:

P>
P>boost::asio::ip::address listen_address = boost::asio::ip::address_v4::any();
P>


P>По поводу join_group — проверил, интерфейс там почему-то никак не задать: http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ip__multicast__join_group.html , хотя и написано: "Socket option to join a multicast group on a specified interface."

P>А как задать этот интерфейс, на котором ловим UDP пакеты? Делаю под Linux.

P>В итоге пока не работает...


Вроде тут божатся, что можно задавать оба адреса:
http://stackoverflow.com/a/8684842/1964969
boost::asio::ip::udp::endpoint listen_endpoint(udp::v4(), multicast_port); 
...
socket_.set_option(multicast::join_group(
  address::from_string(multicast_address).to_v4(), 
  address::from_string(interface).to_v4()));
Re[4]: Получение данных мультикастом
От: prrt  
Дата: 01.03.17 13:39
Оценка:
Да, действительно...

Исправил:

boost::asio::ip::address loc_address = boost::asio::ip::address::from_string("192.168.2.2");
boost::asio::ip::address listen_address = boost::asio::ip::address_v4::any();
boost::asio::ip::address multicast_address = boost::asio::ip::address::from_string("239.255.0.1");
boost::asio::ip::udp::endpoint listen_endpoint(listen_address, 30001);
socket_.open(listen_endpoint.protocol(), error);
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket_.bind(listen_endpoint);
socket_.set_option(boost::asio::ip::multicast::join_group(multicast_address.to_v4(), loc_address.to_v4()));
socket_.async_receive_from(
    boost::asio::buffer(&inputData[0], 10000000), sender_endpoint_,
        strand_.wrap(
            boost::bind(&ClientUdp::handle_receive_from, shared_from_this(),
              boost::asio::placeholders::error,
              boost::asio::placeholders::bytes_transferred)));

И всё-равно ничего не получает. Прямо беда какая-то, хоть в исходниках tcpdump ковыряйся, чтобы оттуда нужный код взять, ведь он-то работает...
Re[5]: Получение данных мультикастом
От: Mr.Delphist  
Дата: 01.03.17 16:55
Оценка:
Здравствуйте, prrt, Вы писали:

P>Да, действительно...


P>Исправил:

P>И всё-равно ничего не получает. Прямо беда какая-то, хоть в исходниках tcpdump ковыряйся, чтобы оттуда нужный код взять, ведь он-то работает...

Я бы рекомендовал для начала нарисовать минимальный пример на обычных беркли-сокетах, безо всякого asio. Или всё же на asio, но запускать в случае если одна сетевуха в машине. Есть подозрение, что мультикаст асиошники сделали "шоб было", без учёта ряда факторов (типа мультихоминга, ipv6 и прочее).
Re[6]: Получение данных мультикастом
От: prrt  
Дата: 01.03.17 18:46
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>Я бы рекомендовал для начала нарисовать минимальный пример на обычных беркли-сокетах, безо всякого asio.


А Вы не посоветуете какой-нибудь подобный пример? Наверняка должны быть готовые решения. Я погуглил, но то, что нашел, тоже не заработало (вечное ожидание данных), например:

#define PORT  30001
#define BSIZE 256
 
int main()
{
    struct sockaddr_in  sin;
    char   *buffer[ BSIZE ];
    int    sock;
    int    bytes_read;
    socklen_t    sin_len;
    struct ip_mreq mreq;
 
    /* Setup address */
    memset( &sin, 0, sizeof( sin ) );
    sin.sin_family = AF_INET;
    sin.sin_port = htons( PORT );
    sin.sin_addr.s_addr = htonl(INADDR_ANY);
    sin_len = sizeof( sin );
 
    /* Create socket */
    sock = socket( AF_INET, SOCK_DGRAM, 0 );
    if ( sock > 0 )
    {
        if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
            perror("bind");
            exit(1);
        }
        string group = "239.255.0.1";
        string locAddr = "192.168.2.2";
        mreq.imr_multiaddr.s_addr = inet_addr(group.c_str());
        mreq.imr_interface.s_addr = inet_addr(locAddr.c_str());
        if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))< 0) {
            perror("setsockopt");
            exit(1);
        }        
        /* Display any data found */
        while( 1 )
        {
            bytes_read = recvfrom( sock, buffer, sizeof( buffer ), 0, ( struct sockaddr * ) &sin, &sin_len );
            if ( bytes_read > 0 )
            {
                printf( "%s\n", buffer );
            }
        }
    }
    
    return 0;
}


Проверять могу только на машине с несколькими интерфейсами, на которые поступают разные данные. При этом сам UDP поток изменять не могу, он поступает на один из этих интерфейсов.
Re[7]: Получение данных мультикастом
От: kov_serg Россия  
Дата: 01.03.17 19:50
Оценка:
Здравствуйте, prrt, Вы писали:

P>Здравствуйте, Mr.Delphist, Вы писали:


MD>>Я бы рекомендовал для начала нарисовать минимальный пример на обычных беркли-сокетах, безо всякого asio.


P>А Вы не посоветуете какой-нибудь подобный пример? Наверняка должны быть готовые решения. Я погуглил, но то, что нашел, тоже не заработало (вечное ожидание данных), например:


https://cloud.mail.ru/public/ADfL/6UnYWdXmh
Re[8]: Получение данных мультикастом
От: prrt  
Дата: 01.03.17 22:49
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>https://cloud.mail.ru/public/ADfL/6UnYWdXmh


Спасибо! Скомпилировал, запустил клиент. При запуске выдаёт:
setsockopt(IP_ADD_MEMBERSHIP): No such device

Почитал, такая ошибка означает, что либо ядро скомпилировано без поддержки мультикаста, либо не прописан роутинг для мультикаст трафика. Первое исключается, т.к. tcpdump работает. Для второго сделал:
# route add -net 224.0.0.0 netmask 240.0.0.0 eth1

Но всё равно не помогло, та же ошибка. Что же здесь еще может быть такое?
Re[9]: Получение данных мультикастом
От: prrt  
Дата: 01.03.17 22:55
Оценка:
P>setsockopt(IP_ADD_MEMBERSHIP): No such device

Хотя такая ошибка возникает только в случае, если указать интерфейс, т.е. запускать клиент с опцией --miface=eth1. Если эту опцию убрать, то запускается нормально, но данные уже не получает, опять вечный цикл ожидания...
Re[10]: Получение данных мультикастом
От: kov_serg Россия  
Дата: 02.03.17 10:58
Оценка:
Здравствуйте, prrt, Вы писали:


P>>setsockopt(IP_ADD_MEMBERSHIP): No such device


P>Хотя такая ошибка возникает только в случае, если указать интерфейс, т.е. запускать клиент с опцией --miface=eth1. Если эту опцию убрать, то запускается нормально, но данные уже не получает, опять вечный цикл ожидания...

надо не eth0 указывать,а его ipv4 адрес
например --miface=192.168.1.2

route у меня эти маршруты не прописывет

и по умолчанию достаточно запустить mcast-client и mcast-server без параметров что бы они увидели друг друга
Отредактировано 02.03.2017 11:03 kov_serg . Предыдущая версия .
Re[7]: Получение данных мультикастом
От: Mr.Delphist  
Дата: 02.03.17 11:28
Оценка:
Здравствуйте, prrt, Вы писали:

P>Здравствуйте, Mr.Delphist, Вы писали:


MD>>Я бы рекомендовал для начала нарисовать минимальный пример на обычных беркли-сокетах, безо всякого asio.


P>А Вы не посоветуете какой-нибудь подобный пример? Наверняка должны быть готовые решения. Я погуглил, но то, что нашел, тоже не заработало (вечное ожидание данных), например:


Можно для экспериментов брать порт 1900 — это протокол upnp, трафик по нему есть в любой сетке, в wireshark он показывается зелёненьким цветом
Re[11]: Получение данных мультикастом
От: prrt  
Дата: 02.03.17 11:39
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>надо не eth0 указывать,а его ipv4 адрес

_>например --miface=192.168.1.2

_>route у меня эти маршруты не прописывет


_>и по умолчанию достаточно запустить mcast-client и mcast-server без параметров что бы они увидели друг друга


Исправил, задал локальный IP интерфейса, в итоге тоже ничего опять не получает. С mcast-server оно наверняка заработает, т.к. у меня и с Asio всё работает, если пакеты отправлять тоже через Asio, и ловить им же. Не работает именно с этим хитрым трафиком, на этом интерфейсе.

Начал ковырять tcpdump. Похоже, он читает данные с интерфейса как с файла устройства. Меня этот вариант вполне бы устроил, парсинг raw данных можно сделать и на прикладном уровне. Вот только /dev/ethX отсутствуют, в итоге непонятно, как прочитать данные с интерфейса как файла устройства?
Re[8]: Получение данных мультикастом
От: prrt  
Дата: 02.03.17 11:43
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>Можно для экспериментов брать порт 1900 — это протокол upnp, трафик по нему есть в любой сетке, в wireshark он показывается зелёненьким цветом


В моём случае на этом порту данных нет. Т.к. это даже не сетка, а выделенный кабель на который поступают только те данные, которые мне и нужно ловить. tcpdump тоже показал, что на 1900 порту ничего нет.
Re[12]: Получение данных мультикастом
От: kov_serg Россия  
Дата: 02.03.17 14:34
Оценка:
Здравствуйте, prrt, Вы писали:

P>Здравствуйте, kov_serg, Вы писали:


_>>надо не eth0 указывать,а его ipv4 адрес

_>>например --miface=192.168.1.2

_>>route у меня эти маршруты не прописывет


_>>и по умолчанию достаточно запустить mcast-client и mcast-server без параметров что бы они увидели друг друга


P>Исправил, задал локальный IP интерфейса, в итоге тоже ничего опять не получает. С mcast-server оно наверняка заработает, т.к. у меня и с Asio всё работает, если пакеты отправлять тоже через Asio, и ловить им же. Не работает именно с этим хитрым трафиком, на этом интерфейсе.


P>Начал ковырять tcpdump. Похоже, он читает данные с интерфейса как с файла устройства. Меня этот вариант вполне бы устроил, парсинг raw данных можно сделать и на прикладном уровне. Вот только /dev/ethX отсутствуют, в итоге непонятно, как прочитать данные с интерфейса как файла устройства?


raw socket или libpcap

http://www.binarytides.com/raw-sockets-c-code-linux/
http://www.tenouk.com/Module43a.html

http://www.tcpdump.org/pcap.html
Отредактировано 02.03.2017 14:39 kov_serg . Предыдущая версия .
Re[9]: Получение данных мультикастом
От: andrey.desman  
Дата: 02.03.17 14:52
Оценка:
Здравствуйте, prrt, Вы писали:

P>Почитал, такая ошибка означает, что либо ядро скомпилировано без поддержки мультикаста, либо не прописан роутинг для мультикаст трафика. Первое исключается, т.к. tcpdump работает.


tcpdump снимает полный поток с карты, он не получает multicast, так что на него я бы не расчитывал.

P>Для второго сделал:

P># route add -net 224.0.0.0 netmask 240.0.0.0 eth1

Это исходящий маршрут по-умолчанию. Если ты указываешь интерфейс, то оно рояли не и грает. Впрочем, ты и не отправляешь ничего.
Re[9]: Получение данных мультикастом
От: andrey.desman  
Дата: 02.03.17 14:54
Оценка:
Здравствуйте, prrt, Вы писали:

P>В моём случае на этом порту данных нет. Т.к. это даже не сетка, а выделенный кабель на который поступают только те данные, которые мне и нужно ловить. tcpdump тоже показал, что на 1900 порту ничего нет.


Т.е. это прямое подключение?
Тогда это может быть и немультикаст вовсе. Приведи пример заголовка какого-нибудь пакета с этого интерфейса.
Некоторые бульбуляторы вообще могут некорректные заголовки отправлять.

Да и IP_ADD_MEMBERSHIP в этом случае сомнителен, т.к. вряд ли на устройстве есть что-то, что ответит на igmp report.
Отредактировано 02.03.2017 14:58 andrey.desman . Предыдущая версия .
Re[9]: Получение данных мультикастом
От: Mr.Delphist  
Дата: 02.03.17 15:32
Оценка:
Здравствуйте, prrt, Вы писали:

P>В моём случае на этом порту данных нет. Т.к. это даже не сетка, а выделенный кабель на который поступают только те данные, которые мне и нужно ловить. tcpdump тоже показал, что на 1900 порту ничего нет.


Вот я и предлагаю сначала потренироваться "на кошках" — хоть на своей девелоперской машине. Добиться устойчивого приёма мультикастов на примере upnp:
1) два инстанса приложения слушают один и тот же стрим
2) поднимаем VPN или второй сетевой интерфейс — проверяем, что работа с мультикастами продолжается (т.к. обычно vpn считается более приоритетным, то становится "дефолтным ухом" для приёма мультикастов).

А затем уже траблшутить свой "выделенный кабель".
Re[13]: Получение данных мультикастом
От: prrt  
Дата: 02.03.17 20:34
Оценка:
Здравствуйте, kov_serg, Вы писали:


_>raw socket или libpcap


_>http://www.binarytides.com/raw-sockets-c-code-linux/

_>http://www.tenouk.com/Module43a.html

_>http://www.tcpdump.org/pcap.html


Хорошие ссылки. Только по первым двум нигде не увидел, как задавать интерфейс, который будем слушать. Использование IP вместо интерфейса как-то не особо доверие вызывает...
А вот в pcap — там да, это есть. Похоже, всё через pcap и буду делать.
Re[10]: Получение данных мультикастом
От: prrt  
Дата: 02.03.17 20:39
Оценка:
Здравствуйте, andrey.desman, Вы писали:

AD>Т.е. это прямое подключение?

Да.
AD>Тогда это может быть и немультикаст вовсе. Приведи пример заголовка какого-нибудь пакета с этого интерфейса.
Похоже на то. Должна ли в мультикасте быть какая-то обратная связь? Должно что-то посылаться от меня к тому, кто вещает? Если да, то это точно не мультикаст. Т.к. в обратную сторону ничего не должно идти, это было четко сказано.
С пакетами как разберусь, пример приведу. Чтобы по шапке не получить, что какую-нибудь закрытую информацию оглашу


AD>Некоторые бульбуляторы вообще могут некорректные заголовки отправлять.


AD>Да и IP_ADD_MEMBERSHIP в этом случае сомнителен, т.к. вряд ли на устройстве есть что-то, что ответит на igmp report.
Re[10]: Получение данных мультикастом
От: prrt  
Дата: 02.03.17 21:56
Оценка:
Всё-таки, видимо, это не мультикаст. Поставил в tcpdump режим non-promiscuous, и он сразу перестал ловить трафик. Так что, наверное, здесь только с pcap надо делать.
Re[11]: Получение данных мультикастом
От: andrey.desman  
Дата: 02.03.17 22:24
Оценка:
Здравствуйте, prrt, Вы писали:

P>Похоже на то. Должна ли в мультикасте быть какая-то обратная связь? Должно что-то посылаться от меня к тому, кто вещает? Если да, то это точно не мультикаст. Т.к. в обратную сторону ничего не должно идти, это было четко сказано.


С одной сторны, тебе надо указать данные какой группы ты хочешь получать, иначе ядро будет игнорировать эти пакеты. С другой стороны, ядро пошлет igmp report, чтобы подписаться на группу. Насколько эта отправка критична для этого устройства х.з. Но на этом в принципе коммуникация в сторону устройства закончится.
Re[11]: Получение данных мультикастом
От: andrey.desman  
Дата: 02.03.17 22:26
Оценка:
Здравствуйте, prrt, Вы писали:

P>Всё-таки, видимо, это не мультикаст. Поставил в tcpdump режим non-promiscuous, и он сразу перестал ловить трафик. Так что, наверное, здесь только с pcap надо делать.


Возможно, устройство отправляет не на широковещательный MAC, а на какой-то хардкод. Сложно сказать, не видя заголовка пакета.
А ты пробовал сишные примеры запускать в промиск режиме?
Re[12]: Получение данных мультикастом
От: prrt  
Дата: 03.03.17 09:58
Оценка:
Здравствуйте, andrey.desman, Вы писали:

AD>А ты пробовал сишные примеры запускать в промиск режиме?


Хорошая идея. Попробовал. Использовал вот этот код — https://gist.github.com/austinmarton/2862515
Пакеты получает, всё ок. Но что странно — он их получает даже в случае, если задаю другой интерфейс. И наоборот — слушаю нужный интерфейс, а кроме нужных пакетов, получаю и пакеты со своим собственным IP в качестве Source IP, т.е. как я понимаю, это мой собственный ssh трафик (на сервере работаю удаленно, захожу через второй интерфейс).

Это наводит на подозрения, что таким образом я буду получать вообще весь трафик со всех интерфейсов...
Re[13]: Получение данных мультикастом
От: andrey.desman  
Дата: 03.03.17 10:18
Оценка:
Здравствуйте, prrt, Вы писали:

P>Хорошая идея. Попробовал. Использовал вот этот код — https://gist.github.com/austinmarton/2862515


Я имел в виду мультикастовые примеры в промиск режиме. Впрочем, может так и проще даже будет.

P>Пакеты получает, всё ок. Но что странно — он их получает даже в случае, если задаю другой интерфейс. И наоборот — слушаю нужный интерфейс, а кроме нужных пакетов, получаю и пакеты со своим собственным IP в качестве Source IP, т.е. как я понимаю, это мой собственный ssh трафик (на сервере работаю удаленно, захожу через второй интерфейс).

P>Это наводит на подозрения, что таким образом я буду получать вообще весь трафик со всех интерфейсов...

Так это...

https://linux.die.net/man/7/socket
SO_BINDTODEVICE
Note that this only works for some socket types, particularly AF_INET sockets. It is not supported for packet sockets (use normal bind(2) there).


Надо через bind() к интерфейсу привязываться.
Re[14]: Получение данных мультикастом
От: prrt  
Дата: 03.03.17 11:32
Оценка:
Здравствуйте, andrey.desman, Вы писали:

AD>Я имел в виду мультикастовые примеры в промиск режиме. Впрочем, может так и проще даже будет.


AD>Надо через bind() к интерфейсу привязываться.


Через bind() всё получилось, пошли пакеты на приём с нужного этого интерфейса. Ну и хоть без pcap заработало, не придется тянуть зависимость, да и так проще вышло.
Мульткастовый пример проверил в промиск режиме — не работает, не получает ничего.

Спасибо!
Отредактировано 03.03.2017 11:33 prrt . Предыдущая версия . Еще …
Отредактировано 03.03.2017 11:33 prrt . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.