Вероятно, что трудно найти другую, настолько же полезную и плохо документированную программу, как Netstat, имеется в виду, опция показа статистики сетевого потока данных. Когда мы проводим инспекцию состояния сети на отдельно взятом Linux узле, всегда можно быть уверенным, что это утилита имеется в наличии. И вот мы хотим понять — справляется ли сетевой стэк с нагрузкой, или проблема на верхних этажах OSI, собственно там, где сосредоточенно крутятся колесики бизнес-логики нашего приложения.

(5:562)$ netstat -s |wc -l 124
Ура, у нас куча полезной информации, сейчас мы быстренько сообразим, что к чему. Вот только бы понять, что же это за зверь такой timeout in transit, явно что-то нехорошее.
Icmp: 11475275 ICMP messages received 327527 input ICMP message failed. ICMP input histogram: detination unreachable: 2233840 timeout in transit: 5612259
Сейчас гляну в мануал.
# man netstat |grep timeout #
Пустое множество подсказывает, что пора узнавать у Яндекса, Гугла и даже у DuckDuckGo. Результат поиска был примерно такой-же, но только Гугл выдал 111 тыс. вариантов пустого множества, а Яндекс лаконично ограничился 326 вариантами.
Но мы ведь не напрасно выбрали открытое ПО, всегда можно свериться с первоисточником. В нашем случае исходный код находится в файле netstat.c пакета Net-tools.
Вот та часть функции int main, в которой определяется действие для показа статистики.
case '?': usage(E_OPTERR); case 'h': usage(E_USAGE); case 's': flag_sta++; ... if (flag_sta) { if (!afname[0]) safe_strncpy(afname, DFLT_AF, sizeof(afname)); if (!strcmp(afname, "inet")) { #if HAVE_AFINET parsesnmp(flag_raw, flag_tcp, flag_udp, flag_sctp);
Функция parsesnmp определена в файле statistics.c и парсит файлы:
/proc/net/netstat /proc/net/snmp /proc/net/sctp/snmp
Ясно понятно, значит можно просто заглянуть в /proc/net/snmp и увидеть, что стоит за timeout in transit.
# cat /proc/net/snmp |grep -iw icmp Icmp: InMsgs InErrors InCsumErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrors OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps Icmp: 127 2 0 90 0 0 0 0 25 0 8 0 4 0 1763 0 1730 0 0 0 0 0 25 0 8 0 0
Сравнивая эти значения с выводом команды netstat, можем заключить, что timeout in transit соответствует переменной InTimeExcds, а цепочка далее ведет к файлу /usr/include/linux/snmp.h, где определены все эти поля.
/* icmp mib definitions */ /* * RFC 1213: MIB-II ICMP Group * RFC 2011 (updates 1213): SNMPv2 MIB for IP: ICMP group */ enum { ICMP_MIB_NUM = 0, ICMP_MIB_INMSGS, /* InMsgs */ ICMP_MIB_INERRORS, /* InErrors */ ICMP_MIB_INDESTUNREACHS, /* InDestUnreachs */ ICMP_MIB_INTIMEEXCDS, /* InTimeExcds */ ICMP_MIB_INPARMPROBS, /* InParmProbs */ ICMP_MIB_INSRCQUENCHS, /* InSrcQuenchs */ ICMP_MIB_INREDIRECTS, /* InRedirects */ ICMP_MIB_INECHOS, /* InEchos */ ICMP_MIB_INECHOREPS, /* InEchoReps */ ICMP_MIB_INTIMESTAMPS, /* InTimestamps */ ICMP_MIB_INTIMESTAMPREPS, /* InTimestampReps */ ICMP_MIB_INADDRMASKS, /* InAddrMasks */ ICMP_MIB_INADDRMASKREPS, /* InAddrMaskReps */ ICMP_MIB_OUTMSGS, /* OutMsgs */ ICMP_MIB_OUTERRORS, /* OutErrors */ ICMP_MIB_OUTDESTUNREACHS, /* OutDestUnreachs */ ICMP_MIB_OUTTIMEEXCDS, /* OutTimeExcds */ ICMP_MIB_OUTPARMPROBS, /* OutParmProbs */ ICMP_MIB_OUTSRCQUENCHS, /* OutSrcQuenchs */ ICMP_MIB_OUTREDIRECTS, /* OutRedirects */ ICMP_MIB_OUTECHOS, /* OutEchos */ ICMP_MIB_OUTECHOREPS, /* OutEchoReps */ ICMP_MIB_OUTTIMESTAMPS, /* OutTimestamps */ ICMP_MIB_OUTTIMESTAMPREPS, /* OutTimestampReps */ ICMP_MIB_OUTADDRMASKS, /* OutAddrMasks */ ICMP_MIB_OUTADDRMASKREPS, /* OutAddrMaskReps */ ICMP_MIB_CSUMERRORS, /* InCsumErrors */ __ICMP_MIB_MAX };
Кому как, а для меня RFC 1213 как для химика таблица Менделеева — необходимо знать на зубок, так как, это один из краеугольных камней мониторинга сетевых устройств. Вот определение элемента из RFC 1213 пишу по памяти.
icmpInTimeExcds OBJECT-TYPE SYNTAX Counter ACCESS read-only STATUS mandatory DESCRIPTION "The number of ICMP Time Exceeded messages received." ::= { icmp 4 }
Пока что создается впечатление, что все эти определение как масло масляное, не раскрывают сути, но по счастью ICMP Time Exceeded — часть определения протокола ICMP.
Если обрабатывающий дейтаграмму шлюз видит, что поле TTL содержит нулевое значение, дейтаграмма должна быть отброшена. Шлюз может уведомить отправителя дейтаграммы с помощью сообщения time exceeded.
Итак, мы нашли то что искали. Вывод команды netstat, в котором было это загадочное поле, означает число отброшенных ICMP пакетов с нулевым TTL. IRL[1] это означает, что в сети есть кольцо, где пакеты профукали весь TTL[2] .
timeout in transit: 5612259
Linux MIB
В процессе этого погружения в дебри Linux ядра, я с удивлением обнаружил SNMP подсистему. Нет, не SNMP агента, упаси нас Alan Cox, а именно небольшой SNMP стэк. Об этом написано в комментария к файлу /usr/src/linux/include/net/snmp.h.
/* * SNMP MIB entries for the IP subsystem. * Alan Cox <gw4pts@gw4pts.ampr.org> * * We don't chose to implement SNMP in the kernel (this would * be silly as SNMP is a pain in the backside in places). We do * however need to collect the MIB statistics and export them * out of /proc (eventually) */
SNMP означает Simple Network Management Protocol, но как гласит бородатая шутка, никогда еще в аббревиатуре буква S так не лгала. Я планирую написать об этой мозгодробилке отдельно, так как именно на нем крутится весь софт мониторинга вычислительных сетей и сетевых узлов. Пока же — необходимый минимум, чтобы было понятно, откуда берутся переменные статистики netstat.

В самом простом случае мы имеем архитектуру клиент — сервер, где в роли клиента может выступать MRTG или Munin, а серверная часть — это SNMP агент на сетевом узле. Почти все современные сетевые устройства имеют на борту SNMP агента, даже домашние WiFi роутеры. Windows имеет свой SNMP Service, а Linux и открытые Unix системы используют Net-SNMP.
SNMP клиент и сервер обмениваются сообщениями: клиент посылает запрос, а сервер возвращает ответ. Обычно, этот обмен выгладит следующим образом.
К. — Чебурашка, приборы!
С. — Сорок
К. — Что сорок?
С. — А что приборы?
Клиент просит выдать значение некой переменной из Великого Словаря. Сервер смотрит в ВС, находит переменную, смотрит в свой реестр и возвращает значение. Великий Словарь — это MIB[3] Database из рисунка.
Вот как выглядит опрос системных переменных, uptime и других на man странице.
snmpwalk -Os -c public -v 1 zeus system sysDescr.0 = STRING: "SunOS zeus.net.cmu.edu 4.1.3_U1 1 sun4m" sysObjectID.0 = OID: enterprises.hp.nm.hpsystem.10.1.1 sysUpTime.0 = Timeticks: (155274552) 17 days, 23:19:05 sysContact.0 = STRING: "" sysName.0 = STRING: "zeus.net.cmu.edu" sysLocation.0 = STRING: "" sysServices.0 = INTEGER: 72
Надо понимать, что за эвфемизмами Великого Словаря, реестра и самого протокола SNMP скрываются десятки RFC и бездна их возможных реализаций. Возвращаясь к нашему файлу snmp.h, видна лишь надводная часть айсберга:
# grep RFC /usr/include/linux/snmp.h * RFC 1213: MIB-II * RFC 2011 (updates 1213): SNMPv2-MIB-IP * RFC 2863: Interfaces Group MIB * RFC 2465: IPv6 MIB: General Group * RFC 1213: MIB-II ICMP Group * RFC 2011 (updates 1213): SNMPv2 MIB for IP: ICMP group * RFC 2466: ICMPv6-MIB * RFC 1213: MIB-II TCP group * RFC 2012 (updates 1213): SNMPv2-MIB-TCP * RFC 1213: MIB-II UDP group * RFC 2013 (updates 1213): SNMPv2-MIB-UDP LINUX_MIB_TCPMINTTLDROP, /* RFC 5082 */
И если бы это было всё, расшифровка статистки netstat могла быть такой:
переменная из netstat --> соответствующая ей строка из /proc: * /proc/net/snmp * /proc/net/netstat * /proc/net/sctp/snmp --> соответствующее RFC --> определение.
Однако, даже такой многоступенчатой расшифровки недостаточно, так как значительную часть snmp.h занимает загадочный Linux MIB, переменные которого ни в каком RFC не определены, и в Великом Словаре — MIB Database, их тоже нет.
/* linux mib definitions */ enum { LINUX_MIB_NUM = 0, LINUX_MIB_SYNCOOKIESSENT, /* SyncookiesSent */ LINUX_MIB_SYNCOOKIESRECV, /* SyncookiesRecv */ LINUX_MIB_SYNCOOKIESFAILED, /* SyncookiesFailed */ LINUX_MIB_EMBRYONICRSTS, /* EmbryonicRsts */ LINUX_MIB_PRUNECALLED, /* PruneCalled */ LINUX_MIB_RCVPRUNED, /* RcvPruned */ LINUX_MIB_OFOPRUNED, /* OfoPruned */ ... LINUX_MIB_TCPMTUPSUCCESS, /* TCPMTUPSuccess */ __LINUX_MIB_MAX };
Мы готовы к следующему этапу погружения, в следующей статье попытаемся выяснить, что скрывается за ошибками памяти буфера, и как в tcp коллапсируют нарушителей очереди .