PingMap beta
8 мин. чтения

Проверка TCP соединения: curl, nc, nmap и фильтрация порта

Как проверить TCP соединение с внешнего узла: curl, nc, nmap, tcping, mtr и PowerShell. Что значит filtered, чем DROP отличается от REJECT.

Сайт не открывается. Или приложение перестало отвечать клиентам. Что делаем? Большинство по рефлексу стучит ping. Толку с этого, если честно, мало. Пинг работает по ICMP, а приложение слушает TCP на конкретном порту. Это разные вещи на разных уровнях, и одно про другое ничего не говорит. ICMP может проходить, пока сервис на 443-м лежит. И наоборот тоже бывает: пинг режется, а сервис живой.

Поэтому я давно не пингую первым делом. Я лезу в TCP — пытаюсь поднять реальное соединение на тот порт, который интересует. Прошло — значит SYN дошёл, сервер ответил SYN/ACK, я ответил ACK, рукопожатие случилось. Не прошло — а вот тут начинается разбор, кто конкретно мешает: то ли порт закрыт, то ли по дороге сидит фаервол.

Cheatsheet: проверка TCP-соединения — curl, nc, nmap, tcping, mtr, Test-NetConnection

Дальше — по инструментам. Локальные ss/netstat пропускаю, статья про взгляд снаружи.

Почему ICMP врёт чаще, чем кажется

Пинг живёт лет тридцать и сидит у всех в мышечной памяти. Но за эти тридцать лет в интернете много чего поменялось:

  • Корпоративные фаерволы давно режут ICMP пачками — он не нужен для работы, а флудить через него удобно.
  • Облачные Security Groups (AWS, GCP, Azure) работают на default-deny: пока ты явно не добавил правило для Echo Request — его не будет.
  • Host-фаерволы (iptables, nftables) тоже его часто прихлопывают по дефолту security policy.

Получается забавная картина: сервер живой, приложение работает, клиенты ходят — а ping тебе пишет 100% packet loss. И ты, увидев это, идёшь будить дежурного, хотя никакого инцидента нет.

Обратная история бывает не реже. ICMP проходит — потому что роутер у провайдера его пропускает — но на 5432 (Postgres) или 4222 (NATS) трафика нет. Пинг говорит «ок», а БД лежит.

Реальная проверка TCP-соединения — про L4, а не про L3. Пинг про L3, и он про другое.

curl с верботным флагом

HTTP/HTTPS я не пингую вообще. curl с -v рассказывает всё, что нужно:

curl -v https://example.com

Если порт нестандартный — двоеточие после хоста:

curl -v http://192.168.1.50:8080

Самое полезное тут не сама страница, а лог соединения. Курл проговаривает каждый шаг: куда полез, что ответили, где встал.

  • * Connecting to ... — висит — TCP вообще не поднялся, проблема в сети или в фаерволе по дороге.
  • * Connected to ... — но дальше тишина — порт открыт, но L7 молчит, ищи бэкенд.
  • HTTP/1.1 502 или 504 — сеть жива, лёг апстрим.
  • Сломался на TLS — порт открыт, проблема в сертификате или в протоколе (часто старый клиент пытается в TLS 1.0).

Из опыта этого хватает, чтобы понять, к кому идти — сетевикам, разработчикам или самому себе.

netcat: быстрая проверка хендшейка

Когда приложение по своему TCP-протоколу — БД, очередь, gRPC, что угодно — curl не годится. Беру nc:

nc -zv 8.8.8.8 53

Голый nc не подходит — он встанет и будет ждать. Поэтому два флага:

  • -z — только проверить хендшейк и сразу выйти,
  • -v — чтобы он голосом, а не молча.

На выходе либо Connection to 8.8.8.8 53 port [tcp/domain] succeeded!, либо refuse/таймаут.

Диапазон через дефис:

nc -zv 192.168.1.10 22-80

Удобно, когда забыл, на каком порту тестовая админка, и не хочется лезть в README.

tcping: для тех, кто хочет наблюдать

Иногда нужен не точечный ответ, а график во времени. Деплой только что прошёл — хочется убедиться, что доступность не плавает. Или клиент пишет «иногда не работает», и надо это поймать. Для таких случаев — tcping. По сути это пинг, только по TCP: раз в секунду поднимает короткое соединение и пишет, сколько занял хендшейк.

На Ubuntu ставится так:

sudo apt install tcptraceroute

Запуск:

tcping example.com 443

В выводе обычные строчки вида port 443 open. time=14.2 ms, бегущие сверху вниз. Когда среди них вылезает closed или таймаут — вот и пойман момент.

Сам по себе tcping отвечает только на одно: открыт ли порт прямо сейчас. Где именно проблема, он не покажет. Для этого следующий пункт.

mtr — когда коннект флапает и непонятно где

Если tcping показал, что соединение нестабильное, я переключаюсь на mtr. Это гибрид трассировки и пинга: он сыплет пробами на каждый хоп маршрута и считает потери. Сразу видно, на каком участке деградация.

По умолчанию mtr бьёт ICMP, но я почти всегда форсирую TCP-пробы — потому что ICMP, как мы уже выяснили, врёт:

sudo mtr --tcp -P 443 example.com

Пример того, что прилетает обратно:

HOST: laptop                   Loss%   Snt   Last  Avg
1.|-- 192.168.1.1               0.0%   100   0.4   0.5
2.|-- 10.0.0.1                  0.0%   100   1.2   1.5
3.|-- isp-edge.example.net      0.0%   100   3.1   3.4
4.|-- transit-1.upstream.net   38.0%   100   8.4  12.7
5.|-- transit-2.upstream.net    0.0%   100   9.1   9.3
6.|-- example.com               0.0%   100  10.2  10.4

Когда на каком-то промежуточном хопе вылезает 30–40% потерь, а дальше снова 0% — это обычно не реальный лосс, а rate-limit ICMP на роутере (даже когда мы льём TCP, ответы-то по ICMP идут). Внимание стоит обращать на потери, которые держатся до конца маршрута. Вот они уже реальные.

С таким выводом можно идти к сетевикам или к хостеру и предметно говорить, на каком участке деградация. Без mtr разговор обычно заканчивается фразой «у нас всё работает».

nmap, когда надо понять «почему»

nc отвечает: «доступен или нет». nmap отвечает: «если нет — то что именно происходит». Это уже разведка, не быстрая проверка.

nmap -p 443 8.8.8.8

Три возможных статуса:

  • open — порт слушает.
  • closed — хост ответил RST, но процесса нет.
  • filtered — самый интересный, разбираем ниже.

Что такое filtered: DROP против REJECT

filtered означает, что nmap отправил SYN и не получил внятного ответа. Не SYN/ACK, не RST, не ICMP-ошибки — ничего. Пакет ушёл в пустоту. Это работа фаервола, который сидит где-то по дороге. Может быть на хосте (iptables/nftables), может быть в облачной SG, может быть на корпоративном Cisco ASA, может на edge-роутере у провайдера.

Блокирует он двумя способами, и снаружи они выглядят по-разному:

  • DROP — пакет молча выкидывается, как будто его и не было. Никаких ответов. С твоей стороны коннект тупо висит до системного таймаута, а это 60–120 секунд в дефолте линукса. Nmap пишет filtered.
  • REJECT — фаервол отстреливается. Либо ICMP-пакетом «Destination Unreachable: Administratively Prohibited», либо TCP с флагом RST. С твоей стороны — мгновенный Connection refused. Nmap пишет либо closed (если прилетел RST), либо filtered с пометкой про ICMP-блокировку.

Мнемоника: висит — это DROP, refused — REJECT. По одному только этому симптому я в большинстве случаев уже знаю, кто режет: хост настроен на DROP, или edge с REJECT-политикой.

История с проды

Деплой недавно. Caddy в контейнере работает, локально с сервера curl -v localhost:443 отвечает корректно, всё ок. С моей машины из дома — мгновенный refused. Первая мысль очевидная: я что-то накосячил в nftables на сервере. Лезу проверять. Правила на месте, INPUT принимает 443.

Делаю nmap -p 443 server-ip с домашнего ноута. Статус — filtered. То есть не закрытый порт, и не моя локальная свистоперделка. Что-то по дороге.

Запускаю nmap с третьей машины — это уже коллега помог из другой подсетки и другого провайдера. Тоже filtered. Значит проблема не на моей стороне и не на моём провайдере. Скорее всего косяк у провайдера хостинга.

Залез в личный кабинет. У них на upstream-роутере default-DROP политика для всего, кроме whitelist. 80, 22, 8080 разрешены, 443 — нет. Запросил открытие, через минут двадцать поехало.

Если бы я не глянул nmap'ом с двух точек, я бы час сидел в логах Caddy и journalctl. А там пусто, потому что коннект до сервера вообще не доходил.

PowerShell: Test-NetConnection

В винде я обычно использую WSL, но возможно кто-то ещё пользуется PowerShell:

Test-NetConnection example.com -Port 443

С подробностями:

Test-NetConnection example.com -Port 443 -InformationLevel Detailed

В выводе TcpTestSucceeded: True/False плюс латентность. Аналог nc -zv без сторонних утилит.

Если терминала под рукой нет

Бывает, что проверить порт нужно с внешней точки, а не со своей машины. Например, у клиента вылезла проблема, и ты не уверен, не его ли это локальный фаервол. Или нужно показать заказчику, что снаружи всё открыто.

Для таких случаев у нас на PingMap есть инструмент проверки TCP-порта — он бьёт с наших узлов в разных регионах и показывает open / closed / filtered плюс время хендшейка. Один URL, одна кнопка.

Рядом по теме: проверка ICMP и trace, HTTP-статус-чек, распределённая HTTP-проверка.

Итог

Если запомнить только три вещи — пусть это будут эти:

  1. Пинг — не про TCP. ICMP может проходить, пока сервис лежит, и наоборот. Не используй его как первый сигнал.
  2. Симптом «висит» vs «refused» уже половина диагностики. Висит — DROP, refused — REJECT. Это сразу подсказывает, где смотреть: на хосте или на роутере по дороге.
  3. Если nmap пишет filtered — проверь с другой точки. Чужой провайдер, мобильный интернет, чужая сетка — нужно подтвердить, что блокирует не «по дороге к тебе», а реально перед сервером.

Шпаргалку выше сохрани в закладки — пригодится в следующий пожар.

FAQ

Пинг же показывает, что сервер живой — зачем ещё что-то?

Пинг — это ICMP, он про L3. Приложение слушает TCP на конкретном порту, это L4. Пинг может проходить, пока сервис лежит, и наоборот. Реальная проверка — это попытка поднять TCP-соединение на нужный порт.

А что значит filtered у nmap?

Это когда nmap отправил SYN и ничего не получил в ответ — ни SYN/ACK, ни RST, ни ICMP-ошибки. Фаервол по дороге дропает пакет молча.

DROP и REJECT — в чём разница?

DROP пакет молча выкидывает, и ты ждёшь системного таймаута. REJECT активно отказывает — TCP RST или ICMP «Destination Unreachable», и ты сразу видишь refused.

UDP можно теми же тулами проверять?

Частично. nc -zuv — попытка, но UDP без подтверждения, поэтому «успех» чаще означает «пакет ушёл и никто не возразил». nmap -sU точнее, но в разы медленнее TCP-сканирования.

Telnet говорит refused, но сервер же работает?

Скорее всего, на сервере действительно нет процесса, слушающего этот порт. Хост получил SYN и ответил RST — это и есть refused. Поведение по умолчанию для пустого порта.

Настройте мониторинг за 2 минуты

Добавьте URL, выберите канал уведомлений — и получайте сигнал о реальных проблемах.