Что обозначает вывод «ss -s»

На смену утилите netstat пришла утилита ss и очень часто вывод агрегированной (суммарной) информации «ss -s» (или «ss --summary») используется для нужд мониторинга. Однако, что же означает каждое из выводимых полей?

# 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.

Теперь еще раз то же самое, но в виде картинки (художник из меня так себе):

nhdoym9qq9nhakjcq0qs8pubpuu.png

Ничего странного не замечаете? 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» (для упрощения восприятия в картинках):

lly9r_hmfcfhkf1-tyu0opkbyg8.png

  • 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»:

oqi7jaqtagey4sy-kkgkejirudw.png

Тут вроде все просто — «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».

qo2xascklqre17kvzw9yxktyo-o.png

И последнее поле «closed» — есть сумма аллоцированных сокетов и сокетов в состоянии TIME_WAIT за вычетом суммы TCPv4 и TCPv6 сокетов в любом из незакрытых состояний:

1_spoafqh2_qydt6jxtrsszmf_s.png

P.S. И не забудьте подписать оси на графиках.

© Habrahabr.ru