Назад | Перейти на главную страницу

Linux: отслеживание источника netstat -s «неудачных попыток подключения»

У меня есть несколько серверов, на которых метрика неудачных попыток подключения, возвращаемая netstat -s (from / proc / net / snmp), растет примерно на один в секунду, и я хотел бы диагностировать их источник.

Используя это правило ipTables (на другом сервере):

-A ВЫХОД -p tcp --dport 23 -j ОТКАЗАТЬ

Я блокирую исходящий телнет, поэтому могу запустить этот цикл:

пока правда; делать
telnet www.google.co.uk
netstat -s | grep "не удалось установить соединение"
сделано

Пробуем 209.85.203.94 ...
telnet: невозможно подключиться к удаленному хосту: соединение отклонено
52 неудачных попытки подключения
Попытка 209.85.203.94 ... telnet: невозможно подключиться к удаленному узлу: соединение отклонено
53 неудачных попытки подключения
Попытка 209.85.203.94 ... telnet: невозможно подключиться к удаленному узлу: соединение отклонено
54 неудачных попытки подключения

Таким образом доказывается, что счетчик увеличивается из-за неудачных попыток подключения к удаленным сокетам. (Хотя это, конечно, не доказывает, что это единственная причина инкрементов).

Вопрос в том, как мне найти конкретную комбинацию удаленного адреса и порта (или их множественное число), которая дает сбой, чтобы я мог перейти к следующему шагу; проблемы с маршрутизацией / брандмауэром?
В стороне, если я запустил это:

часы -n1 'ss | grep "\ <23 \>" '

Я надеялся увидеть сокеты в состоянии SYN-SENT, но этого не произошло. Это потому, что я использовал REJECT, а не DROP? Спасибо

Попробуем ответить на вопрос по-другому (сложно). Прочтите исходный код ядра, чтобы увидеть, что есть только одно место, где эта метрика увеличивается - tcp_done функция. Как видно из кода, приращение происходит только для соединений в состояниях SYN_SEND или SYN_RECV. Затем мы проверяем, откуда можно вызвать tcp_done. И мы можем найти несколько мест:

  1. tcp_reset - вызывается при разрыве соединения (получен ответный пакет с первым флагом). Да, это может произойти в состояниях SYN_SENT и SYN_RECV (и теоретически в других состояниях).
  2. tcp_rcv_state_process - вызывается в состояниях TCP_FIN_WAIT1 и TCP_LAST_ACK, поэтому метрика не увеличивается - это не наш случай.
  3. tcp_v4_error - вызывается в случае SYN_SENT или SYN_RECV. Функция tcp_v4_error, вызываемая обработчиком ICMP.
  4. tcp_time_wait - вызывается при переводе сокета в состояние time-wait или fin-wait-2 - тоже не наш случай.
  5. tcp_write_error - звонили из нескольких мест по таймаутам и превышено количество повторных передач. Это тоже может быть наш подозреваемый.

Теперь откройте любую диаграмму TCP FSM, чтобы проверить, в каких случаях наше соединение может быть в SYN_SENT или SYN_RECV.

В случае клиента это может быть только состояние SYN_SENT, в котором синхронные пакеты передаются, а соединение прервано из-за получения отказа (ошибка tcp-rst или icmp) или ответ не получен.

В случае с сервером это может быть только состояние SYN_RECV (syn уже получено и syn + ack уже отправлено), и соединение прервано из-за получения отклонения (syn + ack где-то отклонено) или превышен таймаут ожидания ответа (подтверждение не получили).

Теперь вы знаете причины обновления этой метрики и можете проверить ее возможные источники в вашей системе. В современном ядре есть мощные инструменты для устранения неполадок на уровне ядра. Начать с это краткое руководство от Брендана Грегга.

Когда-то значительным источником сброшенных соединений становятся попытки подключиться к неотвечающим серверам. Помните, мы считаем, что «неудачные попытки подключения» относятся к исходящий соединения.

Бег

сс | awk '$ 1 ~ / SYN-SENT / {print $ NF}'

10.160.32.211:8312
10.160.33.61:8312
10.160.32.146:8312
10.160.33.216:8312
10.160.34.186:8312
10.160.35.18:8312
10.160.32.157:8312
10.160.33.159:8312
10.160.34.246:8312

показывает много соединений в этом состоянии. Интересно, что это указывает на то, что все они пытаются подключиться к одному порту. Если я попробую случайные IP-адреса из этого списка и попытаюсь подключиться к порту 8312 с помощью telnet, например:

$ telnet 10.160.34.246 8312
telnet: подключиться к адресу 10.160.32.48: время ожидания истекло

Отправка SYN-пакета - это первый шаг в установлении соединения. Другая сторона должна ответить пакетом SYN-ACK - в этом случае мы отвечаем ACK, и соединение устанавливается. Однако, если между двумя серверами установлен брандмауэр, блокирующий соединение, то SYN-ACK не поступит, поэтому сокет остается в состоянии SYN_SENT до истечения времени ожидания.
Вот диаграмма, украденная с lwn.net:

Этот тайм-аут невелик (я пытаюсь выяснить, как долго, и обновлюсь соответствующим образом) - насколько я могу судить до сих пор, это порядка пары секунд (я бы подумал, что 2x MSL, где MSL это максимальное время жизни сегмента - но это предположение).

Теперь нам нужно различать попытки подключения, при которых отправляется SYN и ничего не возвращается, и попытки подключения, при которых возвращается RST. Брандмауэр на пути обычно довольно груб; он молча отбрасывает исходный пакет SYN - он не отправляет RST, что является нормальным способом сообщить клиенту, что здесь ничего нет.

Вы можете увидеть аналогичное поведение, пытаясь подключиться к www.google.co.uk через порт, который, как вы подозреваете, они не будут прослушивать, например:

$ telnet www.google.co.uk 32654
Попытка 74.125.203.94 ... telnet: подключение к адресу 74.125.203.94: время ожидания подключения истекло

При этом одновременно выполняется что-то вроде этого:

пока правда; делать сс | awk '/ SYN-SENT / && $ NF! ~ /^10./'; сон 2; сделано
СИНХРОНИЗАЦИЯ 0 1 10.137.6.62:46088 74.125.203.94:32654
СИНХРОНИЗАЦИЯ 0 1 10.137.6.62:46088 74.125.203.94:32654
СИНХРОНИЗАЦИЯ 0 1 10.137.6.62:46088 74.125.203.94:32654

Теперь я нахожусь в корпоративной сети, и почти наверняка доступ к Google через обычный порт 80/443 проксируется, а любые другие порты защищены брандмауэром, поэтому мы не ожидаем увидеть RST-пакеты. Вот почему в вопросе я спрашиваю о разнице в моих правилах IPTables между REJECT и DROP. DROP просто отбрасывает пакет в IPTables, тогда как REJECT отправляет RST, я полагаю.

Что я сделаю дальше, так это tcpdump для подключения к порту, который не прослушивает, и обновлю его соответствующим образом.

$ tcpdump -nn -t -i eth0 dst 8.8.8.8
tcpdump: ВНИМАНИЕ: eth0: IPv4-адрес не назначен
tcpdump: подробный вывод подавлен, используйте -v или -vv для полного декодирования протокола
прослушивание eth0, линк-тип EN10MB (Ethernet),
размер захвата 65535 байт
IP 10.137.6.62.40822> 8.8.8.8.12345: флаги [S], seq 505811469, win 14600, параметры [mss 1460, sackOK, TS val 1513647100 ecr 0, nop, wscale 9], длина 0
IP 10.137.6.62.40822 8.8.8.8.12345: флаги [S], seq 505811469, win 14600, параметры [mss 1460, sackOK, TS val 1513648100 ecr 0, nop, wscale 9], длина 0
IP 10.137.6.62.40822> 8.8.8.8.12345: флаги [S], seq 505811469, win 14600, параметры [mss 1460, sackOK, TS val 1513650100 ecr 0, nop, wscale 9], длина 0
IP 10.137.6.62.40822> 8.8.8.8.12345: флаги [S], seq 505811469, win 14600, параметры [mss 1460, sackOK, TS val 1513654100 ecr 0, nop, wscale 9], длина 0
IP 10.137.6.62.40822> 8.8.8.8.12345: флаги [S], seq 505811469, win 14600, параметры [mss 1460, sackOK, TS val 1513662100 ecr 0, nop, wscale 9], длина 0
IP 10.137.6.62.40822> 8.8.8.8.12345: флаги [S], seq 505811469, win 14600, параметры [mss 1460, sackOK, TS val 1513678100 ecr 0, nop, wscale 9], длина 0

TODO: Добавьте tcpdump случая, когда нет брандмауэра, чтобы мы видели RST-пакеты.

Предостережение Существует множество полезных источников информации об отладке TCP-соединения Linux. Red Hat - один из таких источников. На одной из своих страниц они предлагают использовать инструмент dropwatch, чтобы установить, где в сетевом стеке ядра отбрасываются пакеты. На этой странице не говорится, что «отбрасывание» пакетов из программного стека является нормальным явлением - после обработки пакета он отбрасывается. Инструмент dropwatch не делает различий между пакетом, который был отброшен, потому что он завершен, и пакетом, который был отброшен из-за переполнения буфера, тайм-аута бюджета прерывания или ...

Пусть покупатель будет бдителен.