Вопрос или проблема
Я заметил, что ping
(iputils) увеличивает счетчик ICMP id
каждый раз, когда он выполняется. Я попытался взглянуть на исходный код ping и быстро его просмотрел, но не нашел счетчика. Насколько я знаю, ping не запускает никаких демонов или другого глобального состояния системы, так что сам ядро Linux ведет учет этого счетчика?
% ping -c 3 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) байт данных.
64 байта от 127.0.0.1: icmp_seq=1 ttl=64 время=0.039 мс
64 байта от 127.0.0.1: icmp_seq=2 ttl=64 время=0.039 мс
64 байта от 127.0.0.1: icmp_seq=3 ttl=64 время=0.033 мс
Tshark (id=0x0043):
14267 196372.989831489 127.0.0.1 → 127.0.0.1 ICMP 98 Запрос эха (ping) id=0x0043, seq=1/256, ttl=64
14268 196372.989843064 127.0.0.1 → 127.0.0.1 ICMP 98 Ответ эха (ping) id=0x0043, seq=1/256, ttl=64 (запрос в 14267)
14269 196374.001333266 127.0.0.1 → 127.0.0.1 ICMP 98 Запрос эха (ping) id=0x0043, seq=2/512, ttl=64
14270 196374.001345051 127.0.0.1 → 127.0.0.1 ICMP 98 Ответ эха (ping) id=0x0043, seq=2/512, ttl=64 (запрос в 14269)
14271 196375.014707417 127.0.0.1 → 127.0.0.1 ICMP 98 Запрос эха (ping) id=0x0043, seq=3/768, ttl=64
14272 196375.014717838 127.0.0.1 → 127.0.0.1 ICMP 98 Ответ эха (ping) id=0x0043, seq=3/768, ttl=64 (запрос в 14271)
Ping снова:
% ping -c 3 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) байт данных.
64 байта от 127.0.0.1: icmp_seq=1 ttl=64 время=0.047 мс
64 байта от 127.0.0.1: icmp_seq=2 ttl=64 время=0.062 мс
64 байта от 127.0.0.1: icmp_seq=3 ttl=64 время=0.037 мс
Tshark (id=0x0044):
14273 196381.959906106 127.0.0.1 → 127.0.0.1 ICMP 98 Запрос эха (ping) id=0x0044, seq=1/256, ttl=64
14274 196381.959921324 127.0.0.1 → 127.0.0.1 ICMP 98 Ответ эха (ping) id=0x0044, seq=1/256, ttl=64 (запрос в 14273)
14275 196382.961393309 127.0.0.1 → 127.0.0.1 ICMP 98 Запрос эха (ping) id=0x0044, seq=2/512, ttl=64
14276 196382.961404803 127.0.0.1 → 127.0.0.1 ICMP 98 Ответ эха (ping) id=0x0044, seq=2/512, ttl=64 (запрос в 14275)
14277 196383.974709944 127.0.0.1 → 127.0.0.1 ICMP 98 Запрос эха (ping) id=0x0044, seq=3/768, ttl=64
14278 196383.974722217 127.0.0.1 → 127.0.0.1 ICMP 98 Ответ эха (ping) id=0x0044, seq=3/768, ttl=64 (запрос в 14277)
Идентификатор в icmp
аналогичен исходному порту сеанса. Для сокета SOCK_DGRAM его идентификатор выбирается ядром (обычно это число с автоинкрементом), смотрите тут: https://github.com/torvalds/linux/blob/850925a8133c73c4a2453c360b2c3beb3bab67c9/net/ipv4/ping.c#L103C4-L103C37
Для SOCK_RAW (для ping -e) это младший байт идентификатора процесса, смотрите тут: https://github.com/iputils/iputils/blob/aeb124025c9d802ea2a00842dca4c7db66b780bb/ping/ping_common.c#L550
Ответ или решение
Откуда ping в Linux получает свой идентификатор?
Команда ping
в операционной системе Linux используется для отправки ICMP (Internet Control Message Protocol) Echo-запросов, что позволяет проверять связь с удалёнными узлами. Однако, не всегда очевидно, как формируется идентификатор (ID) ICMP-пакетов при выполнении команды ping
. В данном ответе мы подробно рассмотрим, как и откуда ping
получает этот идентификатор.
Анализ кода ping
Как было замечено, каждый раз при выполнении команды ping
, идентификатор пакета ICMP увеличивается. Это связано с тем, что Linux использует специфический механизм для назначения этого идентификатора. Код, отвечающий за присвоение идентификатора, можно найти в репозитории iputils
, который содержит исходники команды ping
.
-
Сокеты и идентификаторы
Команда
ping
может использовать разные типы сокетов, включаяSOCK_DGRAM
иSOCK_RAW
. ДляSOCK_DGRAM
сокетов идентификатор, как правило, автоматически инкрементируется и назначается ядром. Это значит, что в процессе выполнения команды, ядро отслеживает последний использованный идентификатор и выдает следующий доступный.В коде ядра Linux можно заметить, что идентификатор ICMP пакетов выбирается из одноразового увеличивающегося числа, которое также может быть использовано для всех запросов, отправленных из одного и того же процесса.
-
Идентификатор и PID
При использовании
SOCK_RAW
, который предоставляет больше контроля над содержимым пакетов, идентификатор формируется на основе PID (идентификатора процесса) программы. Конкретно, используется низкий байт PID процесса, который отправляет запросыping
. Это поведение можно увидеть в исходном кодеping
.
Важно знать
Каждый раз, когда вы запускаете команду ping
, создаётся новый процесс, что приводит к генерации нового PID. В результате идентификатор ICMP может варьироваться от одного запуска к другому, если процесс ping
используется с SOCK_RAW
. Однако, если вы используете SOCK_DGRAM
, идентификатор будет инкрементироваться от предыдущего значения.
Заключение
Таким образом, ping
в Linux увеличивает ICMP идентификатор, основываясь на механизме управления идентификаторами в ядре Linux, где для сокетов SOCK_DGRAM
идентификатор назначается автоматически и увеличивается, а для SOCK_RAW
используется PID процесса. Этот подход обеспечивает уникальность идентификаторов для различных сеансов связи и помогает различать разные запросы и ответы, что крайне важно для корректной работы сетевых утилит.
Если у вас остались вопросы или хотите узнать больше о функциональности команды ping
, не стесняйтесь обращаться!