send udp - ICMP error..
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.94] |
|
|
send udp - ICMP error..
|
Сообщ.
#1
,
|
|
|
|
Вопрос таков.
Есть сервер, с которого к клиенту валит трафик по UDP Банальным sendto ![]() ![]() res = ::sendto(socket, data, data_length, 0, reinterpret_cast<sockaddr*>(&dst), sizeof(dst)); идет передача, sendto возвращает количество байт, все шоколадно. Далее. Делаем клиенту kill -9 Смотрим на происходящее в канале сниффером и наблюдаем, что трафик продолжает идти.. Но при этом (внимание!!) С маршрутизатора, а говоря проще с моего мирового айпишника на мой локальный (192.168 ... ) валит ICMP трафик, Цитата Internet Control Message Protocol Type: 3 (Destination unreachable) Code: 3 (Port unreachable) при этом sendto отчитывается о том что у него все нормально и вся дата отправляется в полном объеме, errno тоже success. Как ловить событие отваливания клиента ? Да, все безобразие происходит на ubuntu server |
|
Сообщ.
#2
,
|
|
|
|
Цитата nemez @ Как ловить событие отваливания клиента ? Да, все безобразие происходит на ubuntu server Это же UDP, там нет соединения, подтверждения доставки, периодической проверки связи и т.д. Так что надо вручную реализовать подтверждение доставки, либо, если допустима потеря нескольких пакетов, сделать контрольные проверочные пакеты периодические, на которые от клиента должен будет прийти ответ. |
|
Сообщ.
#3
,
|
|
|
|
Да ладно нету контроля
вот дамп http://s019.radikal.ru/i618/1406/48/ef41ebe48822.jpg сверху UDP трафик, снизу - ICMP датаграмма, которая каждый из пакетов, отправляемых в никуда, дублирует вместе с заголовком. Система об этом знает и сокеты должны реагировать |
|
Сообщ.
#4
,
|
|
|
|
Цитата nemez @ сверху UDP трафик Идущий до маршрутизатора, который дальше его не переправляет, т.к. некуда, но никак не информирует отправляющую сторону, т.к. протокол не обязывает. Цитата nemez @ снизу - ICMP датаграмма Не имеющая к UDP протоколу никакого отношения ![]() Цитата nemez @ Система об этом знает и сокеты должны реагировать Система и сокеты не должны реагировать, т.к. UDP - протокол без подтверждения, контроля очерёдности и установления соединения. Добавлено Цитата Хрен @ Идущий до маршрутизатора, который дальше его не переправляет, т.к. некуда, но никак не информирует отправляющую сторону, т.к. протокол не обязывает. Тут ошибся немного. ICMP приходит отправляющей стороне как информирование о недоступности адресата, но ICMP не попадает на UDP'шный сокет, т.к. он не имеет к нему никакого отношения. Добавлено Если помнишь, в асько-клиентах (в частности в Миранде) был флажок "посылать контрольные пакеты серверу". Если его не установить, то клиент очень долго после фактического дисконнекта мог не знать о пропаже сервера и слать сообщения вникуда. Естественно, после этого они пропадали. Собственно, это из той же оперы - ICQ работает поверх UDP протокола. |
|
Сообщ.
#5
,
|
|
|
|
Цитата Не имеющая к UDP протоколу никакого отношения еще и как имеющая - по крайней мере заголовок исходного UDP пакета в ней присутствует! Цитата но ICMP не попадает на UDP'шный сокет а ему туда попадать не надо - по факту таких передач должен быть получен код возврата соответствующий и при помощи errno получить код ошибки, который скажет, что ошибка либо в отвале клиента, либо во фрагментации, либо закрытом порту. Если такой трафик пришел, то сокет должен эту тему отработать. По крайней мере для этого есть все предпосылки. Да и на худой конец можно подвязать libpcap или некую ерунду на raw sockets, и это событие перехватить. Но хочется все сделать через систему. Только не знаю как. Добавлено Цитата асько-клиентах (в частности в Миранде) ну это совсем не показатель - их писали кто попало левой ногой. Потому эта вся аська и накрылась медным тазом |
|
Сообщ.
#6
,
|
|
|
|
и вот что пишет великая камасутра
Цитата SO_BSDCOMPAT Enable BSD bug-to-bug compatibility. This is used by the UDP protocol module in Linux 2.0 and 2.2. If enabled ICMP errors received for a UDP socket will not be passed to the user program. In later kernel versions, support for this option has been phased out: Linux 2.4 silently ignores it, and Linux 2.6 generates a kernel warning (printk()) if a program uses this option. Linux 2.0 also enabled BSD bug-to-bug compatibility options (random header changing, skipping of the broadcast flag) for raw sockets with this option, but that was removed in Linux 2.2. |
|
Сообщ.
#7
,
|
|
|
|
Цитата nemez @ Если такой трафик пришел, то сокет должен эту тему отработать. По крайней мере для этого есть все предпосылки. Специально полистал первоначальный (1981 год) стандарт на эту тему. Именно первоначальный, т.к. сокеты были сделаны в 1983-м, и с того времени не менялись. Дык вот... Цитата If, according to the information in the gateway's routing tables, the network specified in the internet destination field of a datagram is unreachable, e.g., the distance to the network is infinity, the gateway may send a destination unreachable message to the internet source host of the datagram. In addition, in some networks, the gateway may be able to determine if the internet destination host is unreachable. Gateways in these networks may send destination unreachable messages to the source host when the destination host is unreachable. If, in the destination host, the IP module cannot deliver the datagram because the indicated protocol module or process port is not active, the destination host may send a destination unreachable message to the source host. Another case is when a datagram must be fragmented to be forwarded by a gateway yet the Don't Fragment flag is on. In this case the gateway must discard the datagram and may return a destination unreachable message. Вобщем, в этой версии маршрутизаторы не обязывают слать это подтверждение. Только уже позже, когда API сокетов было сформировано, "may" заменили на "should", а потом уже и на "must". Скорее всего, поэтому они и не умеют определять без ядерных костылей, о которых ты написал в последнем посте. Но я смотрю, поведение этого костыля меняется от версии к версии ядра, не понятно вообще, как оно ведёт себя в 3-й версии, и уж тем более это сделает твою прогу непереносимой на другие платформы. Так что я бы эту опцию не использовал, а добавил в протокол периодические проверочные посылки. Цитата nemez @ ну это совсем не показатель - их писали кто попало левой ногой. Потому эта вся аська и накрылась медным тазом Аська накрылась не поэтому |
|
Сообщ.
#8
,
|
|
|
|
Во первых по поводу kill-9
Смотрите здесь Не рекомендуют. Но это по большому счету к проблеме мало. Цитата Хрен @ ICMP приходит отправляющей стороне как информирование о недоступности адресата, но ICMP не попадает на UDP'шный сокет, т.к. он не имеет к нему никакого отношения. Совершенно справедливо. ICMP - к UDP-сокету не имеет никакого отношения. Если нет RAW-сокета, куда модуль ICMP может послать свой пакет, то его путь заканчивается в стеке на сетевом уровне. Цитата nemez @ Если такой трафик пришел, то сокет должен эту тему отработать. По крайней мере для этого есть все предпосылки. Ваш UDP сокет описан как SOCK_DGRAM... И никакого отношения к IPPROTO_ICMP Ну а по сути - Хрен совершенно прав - это UDP, и коль скоро вы убили клиента - серверу это по барабану. Пока он не закончит отсылку своего буфера - он будет слать свои пакеты "На деревню к дедущке" |
|
Сообщ.
#9
,
|
|
|
|
Цитата Ваш UDP сокет описан как SOCK_DGRAM... И никакого отношения к IPPROTO_ICMP имеет, при том самое непосредственное! RFC 792 говорит нам что: Type | Code | Checksum | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | unused | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Internet Header + 64 bits of Original Data Datagra что в свою очередь обозначает что на каждый такой пакет следует ICMP датаграмма с оригинальным заголовком. Получив такую датаграмму, система знает номера портов и говорит сокету о том что с передачей происходит фигня. Тот в свою очередь при send получает код ошибки и говорит юзеру что connection reset by peer. |
|
Сообщ.
#10
,
|
|
|
|
Цитата nemez @ Type | Code | Checksum | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | unused | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Internet Header + 64 bits of Original Data Datagra Это формат ICMP пакета. Причём тут UDP? И да, ICMP - это датаграммы. И да, на ICMP запрос приходит ответ с теми же данными, которые были отправлены. И да, с ICMP работает совсем другой тип сокета. |
|
Сообщ.
#11
,
|
|
|
|
Цитата nemez @ | Internet Header + 64 bits of Original Data Datagra что в свою очередь обозначает что на каждый такой пакет следует ICMP датаграмма с оригинальным заголовком. Получив такую датаграмму, система знает номера портов и говорит сокету о том что с передачей происходит фигня. Не совсем так. Сообщение ICMP получает сетевой уровень - т.е. модуль ICMP Анализируя 64 бита, которые несет в себе ICMP-пакет, система знает - какому приложению послать сответствующее оповещение. Приложению....а не сокету. Цитата Тот в свою очередь при send получает код ошибки и говорит юзеру что connection reset by peer. Нет в UDP соединения, а потому нет и "connection reset by peer" |
|
Сообщ.
#12
,
|
|
|
|
Цитата Это формат ICMP пакета. Причём тут UDP? Internet Header + 64 bits of Original Data Datagram - туда помещается UDP заголовок При этом имеем цепочку. Датаграмму ICMP - Заголовок IP - Заголовок UDP. Для каждого пакета, отправленного в никуда. Можно было бы говорить что UDP ни при чем, но его заголовок содержится в ICMP датаграмме. Это ЖЖЖЖЖЖ неспроста! Цитата Анализируя 64 бита, которые несет в себе ICMP-пакет, система знает - какому приложению послать сответствующее оповещение. Приложению....а не сокету. а как получить это оповещение? Добавлено Цитата Нет в UDP соединения, а потому нет и "connection reset by peer" зато есть "port unreachable" и кстати событие "connection reset by peer" в TCP отрабатывается именно таким способом - через ICMP |
|
Сообщ.
#13
,
|
|
|
|
Цитата nemez @ туда помещается UDP заголовок Он туда помещается не по стандарту, и помещает его туда роутер. Когда ICMP ответ приходит на отправляющую сторону, он не поступает на сокет, т.к. UDP и ICMP - это разные протоколы, которые инкапсулируются внутрь IP, они между собой никак не связаны на уровне сокетов. Цитата nemez @ Почему их не отлавливает система, хотя она это делать обязана судя по мануалам? С этого места поподробнее. Что за мануалы? Желаю лицезреть |
|
Сообщ.
#14
,
|
|
|
|
http://man7.org/linux/man-pages/man7/socket.7.html
читать там где про SO_BSDCOMPAT |
|
Сообщ.
#15
,
|
|
|
|
Цитата nemez @ читать там где про SO_BSDCOMPAT Повторюсь: Цитата Хрен @ поведение этого костыля меняется от версии к версии ядра, не понятно вообще, как оно ведёт себя в 3-й версии, и уж тем более это сделает твою прогу непереносимой на другие платформы. Т.е. в линуксе решили пойти против абстрагирования двух независимых протоколов и совместить их в этом плане на уровне сокетов. Но, как видно из цитаты Цитата In later kernel versions, support for this option has been phased out: Linux 2.4 silently ignores it, and Linux 2.6 generates a kernel warning (printk()) if a program uses this option. эта фича в том виде, который именно тебе нужен, продержалась совсем недолго. Позже её свели просто на уровень ворнингов. Поэтому да, ты можешь заюзать эту опцию, если согласен, что твой софт сможет быть использован только на линуксе с ядром версии 2.2. |
|
Сообщ.
#16
,
|
|
|
|
и да, вот что пишет мистер Стивенс
Цитата 5.3 Does doing a connect() call affect the receive behaviourof the socket? From Richard Stevens ( rstevens@noao.edu): Yes, in two ways. First, only datagrams from your "connected peer" are returned. All others arriving at your port are not delivered to you. But most importantly, a UDP socket must be connected to receive ICMP errors. Pp. 748-749 of "TCP/IP Illustrated, Volume 2" give all the gory details on why this is so. 5.4 How can I read ICMP errors from "connected" UDP sockets? If the target machine discards the message because there is no process reading on the requested port number, it sends an ICMP message to your machine which will cause the next system call on the socket to return ECONNREFUSED. Since delivery of ICMP messages is not guarenteed you may not recieve this notification on the first transaction. Remember that your socket must be "connected" in order to receive the ICMP errors. I've been told that Linux will return them on "unconnected" sockets, but I haven't verfied it. This may cause porting problems if your application isn't ready for it. там английским по белому написано что мои предположения правильные. Утром буду курить бамбук. |
|
Сообщ.
#17
,
|
|
|
|
Цитата nemez @ там английским по белому написано что мои предположения правильные. Написано, но не совсем там, где ты выделил. Я бы больше обратил внимание на это Цитата Remember that your socket must be "connected" in order to receive the ICMP errors. I've been told that Linux will return them on "unconnected" sockets, but I haven't verfied it. This may cause porting problems if your application isn't ready for it. Т.е. тебе нужно вызвать connect() для сокета, т.е. работать с ним как с TCP. Можешь попробовать так, возможно выйдет. Но я бы рекомендовал проверить на всех платформах, особенно в винде. Добавлено И да, там также написано: Цитата nemez @ Since delivery of ICMP messages is not guarenteed you may not recieve this notification on the first transaction. т.е. это тоже не 100% гарантия, что сообщение о дисконнекте к тебе дойдёт |
|
Сообщ.
#18
,
|
|
|
|
ну то что сообщения проходят - это факт.
А во всем остальном надо экспериментировать. Может кто по этим граблям уже походил? А насчет коннекта таки да, обязательно надо попробовать! |
|
Сообщ.
#19
,
|
|
|
|
Цитата nemez @ А насчет коннекта таки да, обязательно надо попробовать! после connect, кстати, нужно работать не через sendto и recvfrom, а через send и recv. Так, на всякий случай. |
|
Сообщ.
#20
,
|
|
|
|
Цитата Теперь посмотрим, что же произойдет, если в UDP-клиенте, например, вместо bind() записать connect()? Во-первых, ядро не будет инициировать "переговорный процесс", т.к. в UDP его нет. Во-вторых, ядро просто запишет в системную область памяти адресные данные удаленного хоста (IP-адрес и номер порта), на которые указывается в параметре вызова connect(), и на этом работа connect() завершится. Такой сокет называется "соединенным" (connected), в то время как по умолчанию для UDP создается "несоединенный" сокет "Соединенный" UDP-сокет налагает некоторые ограничения в его дальнейшем использовании. Вызов функции connect() в случае UDP фактически устанавливает фильтр для входящих дейтаграмм. Как видим, речь здесь идет о клиенте... А какой с него толк, если его убивают kill-9? |
|
Сообщ.
#21
,
|
|
|
|
Oleg2004
на самом деле на udp сокетах не особо есть какое-то понятие серверности и клиентности. Для того чтобы назначить src или dst порты, делаем bind() и connect() |
|
Сообщ.
#22
,
|
|
|
|
nemez
Как то протокольно - да. Но модель клиент-сервер - работает. Сервер всегда висит на recv(). На сервере - обязателен bind() |
|
Сообщ.
#23
,
|
|
|
|
Цитата Сервер всегда висит на recv(). как раз таки на recvfrom() - так мы можем различать клиентов. И в случае чего им ответить. а без sockaddr это нереально. Да и ответить можно с серверного сокета kb,j с sendto, либо через send с предварительным connect. В двух случаях нам нужно знать куда слать. А узнать это можно только через recvfrom. Вот клиенскому сокету как раз таки правильно всегда висеть на rcv - он будет принимать дату только от сервера. А если нам надо сделать сокет, для которого src и dst порты будут фиксированы - в этом случае нужно использовать связку bind() и connect(). И пофиг какой это сокет - клиенский или серверный. Эта тема применяется в ip телефонии очень активно Принимать дату он будет исключительно на порт для которого сделан bind() - если сокет рассматривается как серверный то bind делается на src порт а connect на dst. И все с точностью до наоборот для клиента. Скажем, нам необходимо передавать речевой трафик с порта 22375 на порт 10001. На сервере мы делаем bind на 10001 и connect на 22375. На клиенте мы биндимся на 22375 и коннектимся на 10001. И работаем на сокетах через send и recv - архитектурной разницы между клиентским и серверным сокетом нету кроме их зеркального отображения. |
|
Сообщ.
#24
,
|
|
|
|
Цитата nemez @ как раз таки на recvfrom() Ну это уже тонкости...recv() - здесь в смысле "работает на получение "Цитата nemez @ На сервере мы делаем bind на 10001 и connect на 22375. На клиенте мы биндимся на 22375 и коннектимся на 10001. И работаем на сокетах через send и recv - архитектурной разницы между клиентским и серверным сокетом нету кроме их зеркального отображения. Ну это уже несколько из другой оперы - и другого протокола пользовательского уровня. Ежику понятно, что одно и то же приложение может исполнять когда надо роль клиента, а надо - сервера...только все это надо закодить... |