На смену утилите netstat пришла утилита ss и очень часто вывод агрегированной (суммарной) информации «ss -s» (или «ss --summary») используется для нужд мониторинга. Однако, что же означает каждое из выводимых полей?
Как выяснилось, тут есть тонкости.
Источником истины является исходный код. Исходный код утилиты «ss» можно найти в iproute2/misc/ss.c. Вывод «ss -s» производится в функции print_summary.
До версии 4.17.0 вывод «ss -s» отображен выше. После коммита 90ee99d он стал выглядеть следующим образом:
Как можно заметить, из строки «Total» удалено поле «kernel». В строке «TCP» удалено поле «synrecv», осталось одно (первое) число «timewait», удалено поле «ports». В таблице «Transport» удалена строка «*». Общий смысл коммита можно перевести как: "Мы удалили из вывода то, что давно было сломано".
В print_summary версии 4.16.0 используется структура slabstat, заполняемая вызовом get_slabstat. Если попробовать перевести вызов «get_slabstat» в команды shell, получим следующее:
Где ключи из «/proc/slabinfo» соответствуют полям структуры «slabstat»:
Другими словами, из всех значений получаемых из «/proc/slabinfo» на сегодняшний день можно получить только значение «tcp_bind_bucket», которое отображается в поле «ports». Приведу его описание из книги «TCP/IP Architecture, Design, and Implementation in Linux. By S. Seth and M. A. Venkatesulu 2008 the IEEE Computer Society»:
Ничего странного не замечаете? Slab «sock» (удаленный в 2.6.12) имеет ненулевое значение. Объяснение очень простое — из за особенности сравнения строк в значение поля попадет любое число у которого ключ начинается на «sock». В моем случае это «sock_inode_cache» из «net/socket.c:init_inodecache» относящийся к «sockfs» — список «inode», содержащих «socket struct» (возможно это не совсем то, что задумал автор утилиты):
Ну а «tcp_bind_bucket» отсутствует как раз из за опций сборки ядра (и, соответственно, поле «ports» всегда имеет значение «0»).
Перед погружением в хитросплетения вычисления полей имеет смысл освежить в памяти состояния сокетов (ESTABLISHED, CLOSE-WAIT, TIME-WAIT и т.д.). Для тех кто подзабыл, вики в помощь: RU, EN.
Вывод утилиты строится на базе значений, получаемых из файлов:
Значения, рассчитываемые по содержимому «net/sockstat» (для упрощения восприятия в картинках):
Значения, рассчитываемые по содержимому «net/sockstat6»:
Тут вроде все просто — «Transport / Total» есть поколоночная сумма значений «IP» и «IPv6».
Значения, рассчитываемые по содержимому «net/snmp». Согласно RFC-4022, «tcpCurrEstab» есть «The number of TCP connections for which the current state is either ESTABLISHED or CLOSE-WAIT».
И последнее поле «closed» — есть сумма аллоцированных сокетов и сокетов в состоянии TIME_WAIT за вычетом суммы TCPv4 и TCPv6 сокетов в любом из незакрытых состояний:
P.S. И не забудьте подписать оси на графиках.
# ss -s
Total: 15046 (kernel 16739)
TCP: 39306 (estab 11458, closed 25092, orphaned 110, synrecv 0, timewait 24929/0), ports 0
Transport Total IP IPv6
* 16739 - -
RAW 0 0 0
UDP 15 5 10
TCP 14214 1214 13000
INET 14229 1219 13010
FRAG 0 0 0
Как выяснилось, тут есть тонкости.
Источником истины является исходный код. Исходный код утилиты «ss» можно найти в iproute2/misc/ss.c. Вывод «ss -s» производится в функции print_summary.
До версии 4.17.0 вывод «ss -s» отображен выше. После коммита 90ee99d он стал выглядеть следующим образом:
# ss -s
Total: 15046
TCP: 39306 (estab 11458, closed 25092, orphaned 110, timewait 24929)
Transport Total IP IPv6
RAW 0 0 0
UDP 15 5 10
TCP 14214 1214 13000
INET 14229 1219 13010
FRAG 0 0 0
Как можно заметить, из строки «Total» удалено поле «kernel». В строке «TCP» удалено поле «synrecv», осталось одно (первое) число «timewait», удалено поле «ports». В таблице «Transport» удалена строка «*». Общий смысл коммита можно перевести как: "Мы удалили из вывода то, что давно было сломано".
Что же было сломано?
В print_summary версии 4.16.0 используется структура slabstat, заполняемая вызовом get_slabstat. Если попробовать перевести вызов «get_slabstat» в команды shell, получим следующее:
# egrep '^(sock|tcp_bind_bucket|tcp_tw_bucket|tcp_open_request|skbuff_head_cache)' /proc/slabinfo | awk '{ print $1, $2; }'
Где ключи из «/proc/slabinfo» соответствуют полям структуры «slabstat»:
struct slabstat {
int socks; // sock, net/core/sock.c:sk_init, удалено в 2.6.12
int tcp_ports; // tcp_bind_bucket, net/ipv4/tcp.c:tcp_init
int tcp_tws; // tcp_tw_bucket, net/ipv4/tcp.c:tcp_init, удалено в 2.6.14
int tcp_syns; // tcp_open_request, net/ipv4/tcp.c:tcp_init, удалено в 2.6.13
int skbs; // skbuff_head_cache, net/core/skbuff:skb_init, не используется
};
Другими словами, из всех значений получаемых из «/proc/slabinfo» на сегодняшний день можно получить только значение «tcp_bind_bucket», которое отображается в поле «ports». Приведу его описание из книги «TCP/IP Architecture, Design, and Implementation in Linux. By S. Seth and M. A. Venkatesulu 2008 the IEEE Computer Society»:
This structure keeps information about the port number usage by sockets and the way the port number is being used. The information is useful enough to tell the new binding socket whether it can bind itself to a particular port number that is already in use. The data structure also keeps track of all the socket’s that are associated with this port number.Теперь еще раз то же самое, но в виде картинки (художник из меня так себе):
Ничего странного не замечаете? Slab «sock» (удаленный в 2.6.12) имеет ненулевое значение. Объяснение очень простое — из за особенности сравнения строк в значение поля попадет любое число у которого ключ начинается на «sock». В моем случае это «sock_inode_cache» из «net/socket.c:init_inodecache» относящийся к «sockfs» — список «inode», содержащих «socket struct» (возможно это не совсем то, что задумал автор утилиты):
# egrep '^sock' /proc/slabinfo | awk '{ print $1, $2; }'
sock_inode_cache 16739
Ну а «tcp_bind_bucket» отсутствует как раз из за опций сборки ядра (и, соответственно, поле «ports» всегда имеет значение «0»).
Описание вывода «ss -s»
Перед погружением в хитросплетения вычисления полей имеет смысл освежить в памяти состояния сокетов (ESTABLISHED, CLOSE-WAIT, TIME-WAIT и т.д.). Для тех кто подзабыл, вики в помощь: RU, EN.
Вывод утилиты строится на базе значений, получаемых из файлов:
# cat /proc/net/sockstat
sockets: used 15046
TCP: inuse 1205 orphan 111 tw 24952 alloc 14368 mem 5890
UDP: inuse 5 mem 86
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0
# cat /proc/net/sockstat6
TCP6: inuse 13000
UDP6: inuse 10
UDPLITE6: inuse 0
RAW6: inuse 0
FRAG6: inuse 0 memory 0
# egrep '^Tcp:' /proc/net/snmp
Tcp: RtoAlgorithm RtoMin RtoMax ... AttemptFails EstabResets CurrEstab ...
Tcp: 1 200 120000 ... 1348218 4095008 11458 ...
Значения, рассчитываемые по содержимому «net/sockstat» (для упрощения восприятия в картинках):
- Total — общее число сокетов в системе (включая unix-сокеты) в любом состоянии кроме TIME_WAIT;
- TCP: — общее число TCP сокетов (включая IPv6) в любом состоянии, tw сокеты отделены от alloc сокетов из за т.н. «death row» — их нельзя использовать до перевода в состояние CLOSED.
- orphaned — общее число «осиротевших» TCP сокетов (сокетов, не связанных с дескрипторами в пользовательских процессах);
- timewait — общее число TCP сокетов в состоянии TIME_WAIT;
- inuse TCP, UDP, RAW — общее число v4 сокетов соответствующего типа в любом состоянии за исключением CLOSED и TIME_WAIT;
- inuse FRAG (bool) — флаг фрагментации памяти, memory — объем памяти для дефрагментации в байтах (в отличии от mem, которая указываются в страницах).
Значения, рассчитываемые по содержимому «net/sockstat6»:
Тут вроде все просто — «Transport / Total» есть поколоночная сумма значений «IP» и «IPv6».
Значения, рассчитываемые по содержимому «net/snmp». Согласно RFC-4022, «tcpCurrEstab» есть «The number of TCP connections for which the current state is either ESTABLISHED or CLOSE-WAIT».
И последнее поле «closed» — есть сумма аллоцированных сокетов и сокетов в состоянии TIME_WAIT за вычетом суммы TCPv4 и TCPv6 сокетов в любом из незакрытых состояний:
P.S. И не забудьте подписать оси на графиках.