Вероятно, что трудно найти другую, настолько же полезную и плохо документированную программу, как 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
.
![](https://habrastorage.org/files/0ca/abf/735/0caabf7359444c3d91cf459f09076f43.jpg)
В самом простом случае мы имеем архитектуру клиент — сервер, где в роли клиента может выступать 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 коллапсируют нарушителей очереди .