Общие сетевые вопросы: адресация, маршрутизация, NAT, путь пакета
Кратко о теме
Заголовок раздела «Кратко о теме»Почти все вопросы этой подтемы разворачиваются из одной модели: стек протоколов и инкапсуляция. Данные приложения на прикладном уровне (HTTP, gRPC, DNS) отдаются транспорту (TCP/UDP), который добавляет порты и, в случае TCP, порядковые номера; транспортный сегмент упаковывается в IP-пакет с адресами отправителя и получателя; IP-пакет упаковывается в кадр канального уровня с MAC-адресами и уезжает в провод. На каждом хопе канальный заголовок отбрасывается и создаётся заново, а IP-заголовок едет от источника к получателю почти неизменным — меняются TTL и контрольная сумма. Отсюда сразу два вывода, на которых строится половина ответов: MAC-адрес живёт только внутри одного L2-сегмента (broadcast-домена) и не пересекает маршрутизатор, а IP-адрес глобально осмыслен и именно по нему принимаются решения о пересылке.
Вторая опорная идея — как маршрутизатор принимает решение. У него есть таблица маршрутов, и для адреса назначения он выбирает запись по правилу longest prefix match: из всех подходящих префиксов выигрывает самый длинный (самый конкретный), а 0.0.0.0/0 — маршрут последней надежды. Если один и тот же префикс пришёл из разных источников (статика, OSPF, BGP), сначала сравнивается административная дистанция протокола, потом уже метрика внутри протокола. Найденный маршрут даёт next-hop и выходной интерфейс; дальше нужен MAC этого next-hop — его даёт ARP (в IPv6 — NDP). Наполнять таблицу можно руками (статическая маршрутизация) или протоколами, которые обмениваются информацией о доступности сетей (динамическая): OSPF/IS-IS внутри автономной системы, BGP — между автономными системами.
Третья идея — NAT. Адресов IPv4 не хватает, поэтому внутренние сети живут на приватных диапазонах (10/8, 172.16/12, 192.168/16), а на границе стоит устройство, которое подменяет в заголовке адрес источника на свой публичный и запоминает соответствие в таблице трансляций. Поскольку одному публичному адресу соответствуют тысячи внутренних хостов, вместе с адресом подменяется и порт источника (PAT/NAPT, в Linux — MASQUERADE, состояние держит conntrack). Ключевое следствие: соединение может быть инициировано только изнутри наружу, а чтобы снаружи достучаться внутрь, нужен явный DNAT (проброс порта), реверс-прокси или обходной механизм вроде hole punching.
Четвёртая идея, важная для бэкендера, — что происходит на уровне приложения между «вызвал функцию» и «пришёл ответ». Синхронный сетевой вызов в Go выглядит как блокирующий, но под ним неблокирующий сокет и netpoller на базе epoll/kqueue: горутина паркуется, поток остаётся свободным. Это меняет цену «блокировки»: дорого не ожидание само по себе, а отсутствие таймаутов, отсутствие ограничения на число одновременных вызовов и незакрытые тела ответов, из-за которых не переиспользуются keep-alive-соединения. И отдельно — сеть ненадёжна: таймаут не означает, что запрос не выполнился, поэтому любые повторы требуют идемпотентности на стороне приёмника.
Вопросы и ответы
Заголовок раздела «Вопросы и ответы»А что такое NAT?
Заголовок раздела «А что такое NAT?»Коротко. NAT (Network Address Translation) — механизм подмены адресов в заголовках IP-пакетов на границе сетей. Чаще всего это подмена приватного адреса источника на публичный адрес шлюза, чтобы много хостов из внутренней сети ходили в интернет через один внешний IPv4-адрес. Устройство держит таблицу трансляций, чтобы ответный трафик вернуть тому, кто его инициировал.
Глубже. Различают SNAT (меняется адрес источника — трафик изнутри наружу), DNAT (меняется адрес назначения — проброс порта снаружи внутрь) и PAT/NAPT, он же «overload», где вместе с адресом подменяется и порт источника, что и позволяет мультиплексировать тысячи сессий на один IP. В Linux это реализовано в netfilter: цепочки POSTROUTING/PREROUTING таблицы nat, цели SNAT/MASQUERADE/DNAT, а состояние сессий держит подсистема conntrack (/proc/net/nf_conntrack, лимит net.netfilter.nf_conntrack_max — типичная точка отказа под нагрузкой). Классификация NAT из RFC 3489/5780 (full cone, restricted cone, port-restricted, symmetric) важна для P2P: через symmetric NAT hole punching обычно не работает, нужен TURN-релей. Побочные эффекты NAT: ломается сквозная адресуемость, протоколы, передающие адреса внутри полезной нагрузки (FTP active, SIP), требуют ALG; исходные IP клиентов теряются, поэтому нужны X-Forwarded-For или PROXY protocol; у операторов есть CGNAT (100.64.0.0/10), из-за которого один внешний IP делят сотни абонентов. IPv6 в NAT не нуждается — там хватает адресов, а безопасность обеспечивается фильтрацией, а не трансляцией.
Как на проекте защищали финансовую систему от дублирования платежей при повторной отправке запроса из-за сетевых сбоев или таймаутов?
Заголовок раздела «Как на проекте защищали финансовую систему от дублирования платежей при повторной отправке запроса из-за сетевых сбоев или таймаутов?»Коротко. Ключ идемпотентности: клиент генерирует Idempotency-Key на бизнес-операцию, сервер сохраняет его уникальным индексом в той же транзакции, что и сам платёж, и при повторе с тем же ключом не создаёт новый платёж, а возвращает сохранённый результат первой попытки. Плюс жёсткая машина состояний платежа и сверка с провайдером как последний рубеж.
Глубже. Это вопрос про опыт, поэтому отвечать надо каркасом «проблема → механизм → крайние случаи». Проблема: таймаут клиента не отличим от потери ответа, так что ретрай обязателен, а значит at-least-once доставка запроса — данность; exactly-once достигается только идемпотентностью приёмника. Механизм: таблица с ключом в качестве первичного, где хранится ещё и хеш тела запроса — если пришёл тот же ключ с другим телом, это ошибка 409, а не молчаливое возвращение чужого ответа. Вставка ключа и создание платежа — одна транзакция; конкурентный дубль ловится на unique_violation и переходит в ветку «вернуть существующий результат». Крайние случаи, которые стоит проговорить: параллельный ретрай, пришедший, пока первая попытка ещё в полёте (ответ 409 in progress либо ожидание на блокировке строки); идемпотентность на стороне внешнего провайдера (свой ключ в его API); дедупликация событий из брокера по event_id в inbox-таблице; невозможность отправить событие и записать в БД атомарно — отсюда transactional outbox; и сверка (reconciliation) по выпискам провайдера, которая ловит расхождения, пропущенные всеми предыдущими уровнями. Типичная ошибка кандидата — предлагать дедупликацию по «полям запроса за последние N минут»: это эвристика, которая ломает легитимные два одинаковых платежа подряд.
CREATE TABLE payment_requests ( idempotency_key text PRIMARY KEY, request_hash bytea NOT NULL, payment_id uuid NOT NULL, status text NOT NULL, response_body jsonb, created_at timestamptz NOT NULL DEFAULT now());
BEGIN;INSERT INTO payment_requests (idempotency_key, request_hash, payment_id, status)VALUES ('a1b2c3', digest('...', 'sha256'), gen_random_uuid(), 'processing');-- при конфликте по PK транзакция падает, обработчик читает сохранённый ответINSERT INTO payments (id, account_id, amount) VALUES (...);COMMIT;What is the typical speed of network communication?
Заголовок раздела «What is the typical speed of network communication?»Коротко. Скорость сети характеризуют двумя разными величинами: задержкой и пропускной способностью. Порядки задержек: внутри дата-центра RTT около 0,5 мс, между городами в пределах страны — единицы-десятки миллисекунд, межконтинентально — 100–250 мс. Пропускная способность внутри ДЦ — 10–100 Гбит/с на линк. Для сравнения: обращение к RAM ~100 нс, случайное чтение с NVMe SSD — десятки микросекунд, то есть сетевой вызов дороже локальной памяти примерно в тысячи-десятки тысяч раз.
Глубже. Полезно уметь оценивать снизу: свет в оптоволокне идёт со скоростью примерно 200 000 км/с (c, делённое на показатель преломления ~1,5), то есть около 5 мкс на километр в одну сторону, 10 мкс на километр RTT. Отсюда физический минимум Москва — Франкфурт (~1900 км по трассе) — порядка 20 мс RTT, а реальные 30–40 мс объясняются неоптимальной трассой волокна и задержками на оборудовании. Классический ориентир — «latency numbers every programmer should know»: L1-кеш ~1 нс, мьютекс ~20 нс, основная память ~100 нс, чтение 1 МБ из памяти — единицы микросекунд, случайное чтение SSD ~16 мкс, RTT внутри ДЦ ~500 мкс, seek у HDD ~10 мс, пакет из Калифорнии в Нидерланды и обратно ~150 мс. Практические следствия для проектирования: латентность между сервисами почти не уменьшается с ростом железа, поэтому борются с числом round trip’ов (батчинг, pipelining, HTTP/2-мультиплексирование, кеши), а не с шириной канала; TCP-хендшейк добавляет 1 RTT, TLS 1.3 — ещё 1 RTT (0 RTT при возобновлении), так что «холодный» HTTPS-запрос через океан стоит 300+ мс до первого байта; и хвост распределения (p99) обычно на порядок хуже медианы, поэтому средняя задержка — бесполезная метрика.
Что такое динамическая маршрутизация и какая еще маршрутизация бывает?
Заголовок раздела «Что такое динамическая маршрутизация и какая еще маршрутизация бывает?»Коротко. Динамическая маршрутизация — это когда таблица маршрутов наполняется автоматически, протоколами, которые обмениваются информацией о доступных сетях и пересчитывают пути при изменениях топологии. Кроме неё бывают: непосредственно подключённые сети (connected, появляются сами при поднятии интерфейса), статические маршруты, прописанные администратором, и частный случай статики — маршрут по умолчанию.
Глубже. Внутри динамической маршрутизации делят по алгоритму: distance-vector (RIP, EIGRP) — сосед сообщает «до сети X у меня метрика N», сам граф целиком никто не видит, отсюда проблемы счёта до бесконечности и костыли вроде split horizon и route poisoning; link-state (OSPF, IS-IS) — каждый маршрутизатор рассылает описание своих линков всем, собирает у себя идентичную базу LSDB и локально считает Дейкстру; path-vector (BGP) — как distance-vector, но передаётся весь список пройденных AS, что решает проблему петель и позволяет строить политики. Ещё встречается деление policy-based routing (решение принимается не только по адресу назначения, но и по источнику, порту, метке — в Linux это ip rule и несколько таблиц маршрутизации) и маршрутизация по меткам (MPLS), где forwarding идёт по метке, а не по IP-префиксу. Когда один префикс известен из нескольких источников, выбор идёт по административной дистанции: у Cisco connected 0, static 1, eBGP 20, OSPF 110, RIP 120, iBGP 200; в Linux аналогичную роль играет metric у маршрута и preference у правил.
Какие протоколы динамической маршрутизации вы знаете?
Заголовок раздела «Какие протоколы динамической маршрутизации вы знаете?»Коротко. Внутри автономной системы (IGP): RIP/RIPng — простой distance-vector с метрикой в хопах и лимитом 15; OSPF (v2 для IPv4, v3 для IPv6) и IS-IS — link-state; EIGRP — проприетарный Cisco advanced distance-vector. Между автономными системами (EGP): BGP-4, единственный используемый сегодня, path-vector поверх TCP/179.
Глубже. OSPF работает поверх IP как протокол 89, использует multicast 224.0.0.5/224.0.0.6, делит сеть на области (area) вокруг магистральной area 0 для ограничения размера LSDB, метрика — cost, обратно пропорциональный полосе. IS-IS живёт прямо в канальном уровне (CLNS), популярен у операторов из-за независимости от IP и лучшей масштабируемости. BGP держит TCP-сессии с соседями, различает eBGP (между разными AS) и iBGP (внутри AS, требует full mesh либо route reflector’ов), выбирает лучший путь длинным списком критериев: weight, local preference, локально сгенерированные, длина AS_PATH, origin, MED, eBGP над iBGP, IGP-метрика до next-hop и далее. В дата-центрах сейчас распространён BGP как единственный протокол по схеме Clos/leaf-spine (RFC 7938), в том числе BGP unnumbered поверх link-local IPv6. Смежно стоит упомянуть VRRP/CARP — это не маршрутизация, а резервирование шлюза, и BFD — быстрое (миллисекунды) обнаружение отказа линка, которым подпирают и OSPF, и BGP.
В чем плюсы динамической маршрутизации?
Заголовок раздела «В чем плюсы динамической маршрутизации?»Коротко. Она сама адаптируется: при отказе линка или узла путь пересчитывается без участия человека, при добавлении новой сети маршрут о ней распространяется автоматически. Это даёт отказоустойчивость, масштабируемость (не нужно вручную поддерживать N² статических записей) и возможность балансировать трафик по нескольким равнозначным путям (ECMP).
Глубже. Честный ответ включает и минусы, иначе выглядит как заучивание: динамическая маршрутизация тратит CPU и память (LSDB у OSPF, полная таблица интернета в BGP — это уже больше миллиона IPv4-префиксов), требует времени на сходимость (у OSPF секунды, у BGP при глобальных событиях — минуты), создаёт служебный трафик и расширяет поверхность атаки — отсюда обязательная аутентификация соседей и фильтрация анонсов. Поэтому в реальности схемы гибридные: статика на границах и в маленьких филиалах, динамика в ядре. Для маленькой сети из двух маршрутизаторов статический маршрут проще, предсказуемее и не ломается «сам по себе» — это тоже правильный ответ на «а когда динамика не нужна».
Чем отличается OSPF от BGP?
Заголовок раздела «Чем отличается OSPF от BGP?»Коротко. OSPF — IGP: работает внутри одной автономной системы, link-state, каждый узел знает полную топологию и считает кратчайший путь по Дейкстре, метрика — cost по пропускной способности, сходимость быстрая. BGP — EGP: работает между автономными системами, path-vector, знает не топологию, а списки AS на пути, выбирает маршрут по административным политикам, а не по «кратчайшести», и масштабируется до глобальной таблицы интернета.
Глубже. Технические различия: OSPF ходит поверх IP (protocol 89) по multicast, соседей находит сам через hello-пакеты, работает только внутри одного домена доверия; BGP использует обычную TCP-сессию на порт 179 с явно сконфигурированным соседом, что даёт надёжную доставку и позволяет строить сессии через несколько хопов. OSPF реагирует на изменение топологии почти мгновенно — любой LSA рассылается по всей области и все пересчитывают SPF; BGP намеренно демпфирует изменения (MRAI-таймеры, route flap damping), потому что глобальная сходимость важнее скорости. OSPF не умеет политик, кроме манипуляции cost и суммаризации на ABR; в BGP политики — суть протокола: local preference для выбора исходящего провайдера, AS-path prepend и MED для влияния на входящий трафик, communities для сигнализации. Практический вывод: OSPF отвечает на вопрос «как быстрее дойти», BGP — «через кого мне выгодно и разрешено идти». В современных ДЦ их иногда совмещают: IGP (OSPF/IS-IS) для достижимости loopback’ов и iBGP/eBGP поверх для распространения префиксов сервисов.
Как узнать MAC-адрес сервера, если он слушает на порту 8888?
Заголовок раздела «Как узнать MAC-адрес сервера, если он слушает на порту 8888?»Коротко. Номер порта к MAC-адресу отношения не имеет: MAC — это канальный уровень, и он виден только внутри того же L2-сегмента. Если вы в одной подсети с сервером — инициируйте к нему любой трафик (например, nc -z host 8888) и посмотрите ARP-кеш: ip neigh show 10.0.0.5 или arp -a. Если сервер за маршрутизатором, вы увидите MAC не сервера, а ближайшего шлюза, и узнать настоящий MAC можно только на самом сервере (ip link) или из управляющих систем (DHCP-лизы, таблица MAC на коммутаторе, IPMI/inventory).
Глубже. Полный локальный сценарий: сначала находим, на каком интерфейсе живёт слушатель — sudo ss -ltnp '( sport = :8888 )' покажет адрес прослушивания и процесс; если это 0.0.0.0:8888, конкретный интерфейс определяется маршрутом до клиента: ip route get 10.0.0.5 даст dev eth0, а ip link show dev eth0 — MAC. Со стороны клиента в том же сегменте работают arping -I eth0 10.0.0.5 (шлёт ARP-запрос напрямую, не требуя открытого порта), sudo nmap -sn 10.0.0.0/24 (в локальной сети nmap показывает MAC и вендора по OUI) и просто чтение ip neigh после установления TCP-соединения. Важная деталь для собеседования: ARP-кеш заполняется, только если ARP-запрос реально уходил, поэтому сначала трафик, потом чтение кеша; и записи в состоянии STALE/FAILED доверять нельзя. Отдельно стоит проговорить, что вопрос часто задают как ловушку — правильная реакция — назвать границу применимости MAC, а не просто перечислить команды.
Какую роль играет MAC-адрес?
Заголовок раздела «Какую роль играет MAC-адрес?»Коротко. MAC-адрес — идентификатор сетевого интерфейса на канальном уровне; по нему кадр адресуется внутри одного broadcast-домена. Коммутатор строит таблицу «MAC → порт» и пересылает кадр только в нужный порт, а не во все; сетевая карта принимает кадры со своим MAC, broadcast и подписанными multicast-адресами и отбрасывает остальные.
Глубже. MAC решает задачу «доставить кадр следующему устройству по проводу», тогда как IP решает «доставить пакет через сеть сетей». Связку между ними обеспечивает ARP: зная IP next-hop, хост спрашивает broadcast’ом «у кого 10.0.0.1» и получает MAC. На каждом маршрутизаторе L2-заголовок полностью переписывается — src MAC становится MAC выходного интерфейса роутера, dst MAC — MAC следующего устройства. Помимо адресации, MAC используется как ключ в куче механизмов: привязка DHCP-резерваций, 802.1X-аутентификация и port security, фильтрация в Wi-Fi, определение вендора по OUI при инвентаризации, хеширование потоков в LACP-агрегациях. Известные атаки: MAC-flooding (переполнение CAM-таблицы коммутатора, чтобы он начал вести себя как хаб) и ARP-spoofing, поэтому в серьёзных сетях включают DAI (dynamic ARP inspection) и DHCP snooping.
С чем хорошо работает В-tree, помимо диапазона значений и сетевых слоев?
Заголовок раздела «С чем хорошо работает В-tree, помимо диапазона значений и сетевых слоев?»Коротко. Вопрос, судя по формулировке, искажён при расшифровке (упоминание «сетевых слоев» в контексте B-tree смысла не имеет), поэтому отвечаю по существу того, что обычно спрашивают: помимо диапазонных запросов B-tree отлично закрывает точный поиск по равенству, сортировку (ORDER BY без отдельного шага сортировки), поиск минимума/максимума, префиксные условия по строкам (LIKE 'abc%'), проверку уникальности, левый префикс составного индекса и index-only scan.
Глубже. Все эти сценарии — следствия одного свойства: B-tree хранит ключи упорядоченно, а листья связаны в двусвязный список, поэтому любой запрос, сводимый к «найти позицию и пройти подряд», выполняется за O(log n) плюс линейный проход по нужному куску. Отсюда же вытекает, с чем B-tree работает плохо: условия вида LIKE '%abc' (нет левого префикса), поиск по неведущим колонкам составного индекса, низкоселективные предикаты (по колонке is_deleted планировщик предпочтёт seq scan), полнотекстовый поиск и поиск по массивам/JSONB — там нужны GIN, поиск по геометрии и «ближайшему соседу» — GiST/SP-GiST, огромные append-only таблицы с корреляцией по физическому порядку — BRIN, а чисто хеш-равенство иногда чуть быстрее закроет hash-индекс. В PostgreSQL стоит помнить про сортированные индексы с DESC/NULLS FIRST под конкретный ORDER BY, покрывающие индексы через INCLUDE, частичные индексы с WHERE и дедупликацию B-tree, появившуюся в 13-й версии.
Что такое протоколы сетевого взаимодействия?
Заголовок раздела «Что такое протоколы сетевого взаимодействия?»Коротко. Протокол — это согласованный набор правил обмена данными между узлами: формат сообщений (синтаксис), смысл полей и допустимые состояния (семантика), порядок и тайминги обмена. Протоколы организованы в стек по уровням, где каждый пользуется услугами нижележащего и добавляет свой заголовок к данным.
Глубже. Каноническая модель — семиуровневая OSI (физический, канальный, сетевой, транспортный, сеансовый, представления, прикладной), но реально реализован стек TCP/IP из четырёх уровней (link, internet, transport, application), а сеансовый и уровень представления в нём растворены в прикладном. Полезная классификация протоколов: по наличию состояния (stateful TCP против stateless UDP и, изначально, HTTP), по гарантиям (надёжная доставка с подтверждениями против best-effort), по формату (текстовые — HTTP/1.1, SMTP; бинарные — HTTP/2, gRPC, DNS), по модели взаимодействия (запрос-ответ, публикация-подписка, потоковая передача). Ключевой архитектурный принцип — инкапсуляция: каждый уровень видит полезную нагрузку верхнего уровня как непрозрачные байты, что и позволяет менять реализацию одного уровня, не трогая остальные (переход с IPv4 на IPv6 не требует переписывать HTTP). Стандартизуются протоколы в RFC (IETF), а описание «на словах» на собеседовании стоит подкреплять примером инкапсуляции: HTTP-запрос → TCP-сегмент → IP-пакет → Ethernet-кадр.
За что отвечает прикладной уровень?
Заголовок раздела «За что отвечает прикладной уровень?»Коротко. Прикладной уровень (L7 в OSI, application в TCP/IP) отвечает за протоколы, которыми напрямую пользуются приложения: формат запросов и ответов, семантику операций, идентификацию ресурсов, представление и кодирование данных. Примеры: HTTP, gRPC, DNS, SMTP, SSH, AMQP, Redis RESP, PostgreSQL wire protocol.
Глубже. В модели OSI выше транспорта лежат ещё сеансовый уровень (установление, поддержание и восстановление сессии) и уровень представления (сериализация, кодировки, сжатие, шифрование), но в реальном стеке TCP/IP эти функции реализуются внутри прикладных протоколов и библиотек: сессия — это cookie/JWT/HTTP/2-стрим, представление — JSON/Protobuf/gzip, шифрование — TLS, который формально сидит между транспортом и приложением. Практический смысл понятия «L7» для бэкендера — в различении L4- и L7-обработки: L4-балансировщик распределяет TCP-соединения и не смотрит внутрь, L7-балансировщик (nginx, Envoy) разбирает HTTP, умеет маршрутизировать по пути и заголовкам, ретраить отдельные запросы, мультиплексировать их в один upstream-коннект и терминировать TLS. Аналогично «ошибка L7» (HTTP 500) означает, что сеть отработала, а сломалась логика, тогда как «ошибка L4» (connection refused, timeout) — что до приложения не дошли.
Что такое 172.22.33.0/24 и как расшифровывается?
Заголовок раздела «Что такое 172.22.33.0/24 и как расшифровывается?»Коротко. Это запись подсети в нотации CIDR: 172.22.33.0 — адрес сети, /24 — длина префикса, то есть первые 24 бита адреса задают сеть, оставшиеся 8 — хост. Эквивалентная маска — 255.255.255.0. В этой подсети адреса от 172.22.33.0 до 172.22.33.255, из них 172.22.33.0 — адрес сети, 172.22.33.255 — широковещательный, доступных для хостов 254.
Глубже. Диапазон принадлежит приватному блоку 172.16.0.0/12 из RFC 1918 (то есть 172.16.0.0 — 172.31.255.255), поэтому маршрутизироваться в интернете он не будет и наружу ходит через NAT. Стоит уметь пересчитывать в уме: число адресов равно 2^(32 − длина префикса), пригодных хостов на два меньше (кроме /31, используемого для point-to-point линков по RFC 3021, и /32 — одиночного адреса, например loopback сервиса). Проверить принадлежность адреса подсети — побитовое И адреса и маски: 172.22.33.77 AND 255.255.255.0 = 172.22.33.0, значит адрес внутри. Полезные команды: ipcalc 172.22.33.0/24 покажет разбивку, ip route add 172.22.33.0/24 via 10.0.0.1 добавит маршрут, а в Go тот же разбор делает net/netip:
package main
import ( "fmt" "net/netip")
func main() { p := netip.MustParsePrefix("172.22.33.0/24") fmt.Println(p.Bits(), p.Masked().Addr(), p.Contains(netip.MustParseAddr("172.22.33.77"))) // 24 172.22.33.0 true}Что такое маска подсети?
Заголовок раздела «Что такое маска подсети?»Коротко. Маска подсети — 32-битное значение, в котором единицы отмечают биты, относящиеся к адресу сети, а нули — к адресу хоста. Хост применяет её побитовым И к своему адресу и к адресу назначения: если результаты совпали, получатель в той же подсети и до него можно достучаться напрямую через ARP, иначе пакет нужно отправить шлюзу по умолчанию.
Глубже. Исторически адреса делились на фиксированные классы A/B/C, что расходовало адресное пространство впустую; CIDR (RFC 4632) заменил классы произвольной длиной префикса и заодно позволил агрегировать маршруты. Маска всегда состоит из непрерывной последовательности единиц (255.255.254.0 корректна, 255.255.0.255 — нет); в CIDR пишут только длину. Отдельно существует wildcard-маска (инвертированная, в ACL Cisco) — не путать. Практические ошибки, связанные с масками: две машины с одинаковым IP-диапазоном, но разными масками, «видят» друг друга асимметрично; неправильная маска на сервере проявляется как «пинг до соседа идёт, а до шлюза нет»; при VLSM важно, чтобы подсети не перекрывались, иначе longest prefix match будет уводить трафик не туда. Также стоит помнить, что маска — понятие IPv4-ориентированное: в IPv6 говорят только о длине префикса, и стандартная подсеть там /64, потому что нижние 64 бита нужны SLAAC.
Как посмотреть, какие порты закрыты в Windows?
Заголовок раздела «Как посмотреть, какие порты закрыты в Windows?»Коротко. Строго говоря, «закрытых» портов 65 535 минус открытые, перечислить их нельзя — смотрят обратное: какие порты слушаются и что разрешает файрвол. Слушающие порты: netstat -ano или PowerShell Get-NetTCPConnection -State Listen. Правила брандмауэра: Get-NetFirewallRule | Where-Object Enabled -eq 'True' или netsh advfirewall firewall show rule name=all. Проверить конкретный порт снаружи/изнутри: Test-NetConnection -ComputerName host -Port 8888.
Глубже. Практическая последовательность при разборе «порт не отвечает»: сначала netstat -ano | findstr :8888 покажет, слушает ли кто-то и с каким PID (дальше tasklist /FI "PID eq 1234" или Get-Process -Id 1234); если слушает на 127.0.0.1, снаружи он недоступен независимо от файрвола. Затем проверяем брандмауэр — Get-NetFirewallProfile покажет, включён ли он для нужного профиля (Domain/Private/Public), Get-NetFirewallPortFilter в связке с Get-NetFirewallRule даст соответствие правил портам. Для проверки со стороны клиента Test-NetConnection вернёт TcpTestSucceeded : False и при закрытом порте, и при отфильтрованном — различить их можно по времени: RST приходит мгновенно (порт закрыт), таймаут означает DROP на файрволе. Для полноты картины: Get-NetUDPEndpoint для UDP, netsh interface ipv4 show excludedportrange protocol=tcp — зарезервированные системой диапазоны (частая причина «порт занят, но никто не слушает» после Hyper-V/WSL), а полноценное сканирование делают внешним nmap, потому что изнутри хоста нельзя увидеть фильтрацию на сетевом оборудовании.
Какой протокол передачи данных использовался между сервисами?
Заголовок раздела «Какой протокол передачи данных использовался между сервисами?»Коротко. Вопрос про опыт: интервьюер хочет услышать не название, а обоснование выбора. Хороший ответ — «синхронное взаимодействие на gRPC поверх HTTP/2 для внутренних вызовов, HTTP/1.1 + JSON для публичного API и внешних интеграций, асинхронное — событиями через Kafka», и дальше причины: контракт в .proto и кодогенерация, бинарная сериализация и мультиплексирование, стриминг, дедлайны из контекста.
Глубже. Каркас ответа: (1) какие взаимодействия были — синхронные запрос-ответ, длинные потоки, широковещательные события; (2) что выбрали и почему именно это, с признанием компромиссов: gRPC хуже отлаживается curl’ом и требует прокси или grpc-web для браузера, HTTP+JSON проще и универсальнее, но тяжелее и без строгого контракта; (3) как решались сквозные вопросы — таймауты и дедлайны, ретраи только для идемпотентных методов, circuit breaker, трассировка через propagation заголовков W3C traceparent, версионирование контрактов и обратная совместимость protobuf; (4) где потребовалась асинхронность и почему (развязка по доступности, сглаживание пиков, фан-аут). Плохой ответ — «использовали REST», без уточнения, что это HTTP/1.1 с JSON, и без единого слова о таймаутах и версионировании. Если проект был на очереди сообщений, стоит назвать конкретику: Kafka (лог с партициями, at-least-once, порядок в пределах ключа) против RabbitMQ/AMQP (маршрутизация, подтверждения, DLQ) — и почему выбрали то, а не другое.
Что происходит когда сетевой вызов завершен?
Заголовок раздела «Что происходит когда сетевой вызов завершен?»Коротко. На уровне приложения ответ прочитан, ресурсы должны быть освобождены: тело ответа дочитано и закрыто, буферы возвращены, горутина, ждавшая на сокете, разбужена и продолжила выполнение. На уровне транспорта соединение либо возвращается в пул keep-alive для переиспользования, либо закрывается четырёхсторонним рукопожатием FIN/ACK, после чего инициатор закрытия держит сокет в состоянии TIME_WAIT ~2×MSL (в Linux 60 секунд), чтобы отсеять запоздавшие сегменты.
Глубже. В Go самая частая ошибка на этом шаге — не дочитать тело ответа: http.Transport вернёт соединение в пул только если тело прочитано до EOF и закрыто, иначе соединение закрывается и следующий запрос платит за новый TCP+TLS-хендшейк, а при массовом повторении растёт число сокетов в TIME_WAIT. Правильный шаблон — defer resp.Body.Close() плюс дренаж остатка. Дополнительно на завершении вызова происходит: снятие таймеров дедлайна и отмена контекста (defer cancel(), иначе утечка таймера до истечения дедлайна), запись метрик длительности и кода ответа, завершение span’а трассировки, разблокировка слота в семафоре ограничения конкурентности. На стороне conntrack/NAT запись о сессии живёт ещё какое-то время после закрытия — это влияет на ёмкость шлюза при большом числе коротких соединений.
package client
import ( "io" "net/http")
func Fetch(c *http.Client, req *http.Request) ([]byte, error) { resp, err := c.Do(req) if err != nil { return nil, err } defer func() { // дочитываем остаток, чтобы соединение вернулось в keep-alive пул _, _ = io.Copy(io.Discard, resp.Body) _ = resp.Body.Close() }() return io.ReadAll(io.LimitReader(resp.Body, 1<<20))}Как происходит обработка синхронных сетевых вызовов?
Заголовок раздела «Как происходит обработка синхронных сетевых вызовов?»Коротко. Синхронный вызов выглядит как обычная блокирующая функция: горутина вызывает Read/Write на сокете и не идёт дальше, пока не получит данные. Под капотом сокет неблокирующий: если данных нет, рантайм Go регистрирует дескриптор в netpoller (epoll на Linux, kqueue на BSD, IOCP на Windows), паркует горутину и отдаёт поток ОС другой работе; при готовности дескриптора netpoller возвращает горутину в очередь планировщика.
Глубже. Именно поэтому в Go можно держать десятки тысяч одновременных «блокирующих» вызовов без пула потоков: блокируется горутина (несколько килобайт стека), а не поток ОС. Важные следствия для практики: (1) любой синхронный вызов обязан иметь дедлайн — context.WithTimeout плюс http.NewRequestWithContext, иначе зависший бэкенд накапливает горутины и приводит к исчерпанию памяти; (2) параллелизм нужно ограничивать явно (семафор через буферизованный канал или golang.org/x/sync/errgroup с SetLimit), иначе всплеск запросов обрушит downstream; (3) синхронная цепочка сервисов складывает задержки и умножает вероятности отказа, поэтому глубокие цепочки заменяют на события; (4) DNS-резолв и TLS-хендшейк — часть того же синхронного вызова, их тоже покрывает общий дедлайн, а увидеть разбивку можно через net/http/httptrace. Отдельно стоит упомянуть, что блокирующий системный вызов вне netpoller (например, обращение к файлу или cgo-вызов) действительно занимает поток ОС: планировщик через sysmon отцепит P от такого M и создаст новый поток.
package client
import ( "context" "net/http" "time")
func Call(ctx context.Context, c *http.Client, url string) (*http.Response, error) { ctx, cancel := context.WithTimeout(ctx, 2*time.Second) defer cancel() // без этого таймер живёт до дедлайна
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, err } return c.Do(req)}Какую функцию выполняет маршрутизатор?
Заголовок раздела «Какую функцию выполняет маршрутизатор?»Коротко. Маршрутизатор соединяет разные IP-сети и пересылает пакеты между ними, принимая решение по таблице маршрутов на основании адреса назначения. При этом он разделяет broadcast-домены, уменьшает TTL, пересобирает канальный заголовок под следующий сегмент и дополнительно обычно выполняет NAT, фильтрацию (ACL/firewall), QoS и раздачу DHCP.
Глубже. Внутри принято разделять control plane и data plane. Control plane строит таблицу маршрутов (RIB): статика, connected, анонсы OSPF/BGP; из неё компилируется FIB — структура, оптимизированная под быстрый поиск по префиксу (в софтовых роутерах это дерево LPC-trie, в железных — TCAM). Data plane для каждого пакета делает: проверку контрольной суммы заголовка, поиск в FIB по longest prefix match, декремент TTL (при нуле — отбросить и отправить ICMP Time Exceeded, на чём и работает traceroute), при необходимости фрагментацию или ICMP Fragmentation Needed для Path MTU Discovery, разрешение MAC next-hop через ARP-кеш и отправку кадра. Обычный Linux-хост становится маршрутизатором включением net.ipv4.ip_forward=1; в контейнерных средах роль маршрутизатора между namespace’ами играет тот же ядерный стек плюс iptables/nftables/eBPF. Не путать с коммутатором: тот работает на L2 по MAC внутри одного broadcast-домена и не трогает IP-заголовок (L3-коммутатор — гибрид, делающий маршрутизацию в железе).
По каким критериям роутер направляет пакет?
Заголовок раздела «По каким критериям роутер направляет пакет?»Коротко. Основной критерий — адрес назначения и правило longest prefix match: из всех маршрутов, покрывающих адрес, выбирается с самым длинным префиксом. Если одинаково длинных несколько, сравниваются административная дистанция (источник маршрута) и метрика внутри протокола; при полном равенстве трафик распределяется по нескольким путям через ECMP по хешу от 5-tuple.
Глубже. Пример: для 10.1.2.3 при наличии 10.0.0.0/8, 10.1.0.0/16 и 0.0.0.0/0 выиграет 10.1.0.0/16. Хеш ECMP считают по пятёрке (src IP, dst IP, protocol, src port, dst port), чтобы пакеты одного потока шли одним путём и не приходили вне порядка. За пределами базового forwarding’а бывает policy-based routing: в Linux сначала просматриваются правила ip rule (по src-адресу, метке fwmark, интерфейсу, UID), каждое из которых указывает на свою таблицу маршрутов, и только внутри таблицы работает LPM — это то, как реализуют «трафик из этой подсети через второго провайдера». Также на решение влияют: наличие маршрута с меньшим metric в Linux, состояние линка (маршруты через down-интерфейс не активны), проверка обратного пути rp_filter, а в MPLS-сетях forwarding вообще идёт по метке, минуя IP-lookup. Полезная команда для отладки — ip route get 10.1.2.3, которая показывает итоговое решение ядра со всеми правилами.
Чем IP-адрес отличается от MAC-адреса?
Заголовок раздела «Чем IP-адрес отличается от MAC-адреса?»Коротко. MAC — адрес канального уровня, 48 бит, зашит в интерфейс, уникален в пределах локального сегмента и не пересекает маршрутизатор: на каждом хопе L2-заголовок переписывается. IP — адрес сетевого уровня, 32 бита в IPv4, назначается конфигурацией или DHCP, иерархичен (сеть + хост), маршрутизируется глобально и остаётся неизменным на всём пути (кроме NAT).
Глубже. Разделение нужно, чтобы IP не зависел от технологии канала: тот же IP-пакет едет по Ethernet, Wi-Fi, PPP, VXLAN. Иерархия IP позволяет агрегировать маршруты — таблица интернета хранит префиксы, а не отдельные адреса, что физически невозможно было бы с плоским адресным пространством MAC. Связку обеспечивают ARP (IPv4) и NDP (IPv6, работает через ICMPv6 и multicast, без broadcast). Практические различия, которые любят уточнять: один интерфейс может иметь много IP-адресов и один MAC; MAC можно поменять программно (ip link set dev eth0 address ...), а в Wi-Fi современные ОС по умолчанию рандомизируют его ради приватности; IP меняется при переезде в другую сеть, MAC нет; в трассировке пакета src/dst IP одинаковы от начала до конца, а src/dst MAC различны на каждом сегменте.
Как внешний клиент обращается к сервису за NAT?
Заголовок раздела «Как внешний клиент обращается к сервису за NAT?»Коротко. Напрямую — никак: в таблице трансляций нет записи, и пакет извне будет отброшен. Нужен явный проброс: DNAT/port forwarding на шлюзе (iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to 10.0.0.5:8443), либо реверс-прокси/балансировщик с публичным адресом, либо соединение, инициированное изнутри (исходящий туннель, VPN, WebSocket), либо NAT traversal через STUN/TURN/ICE.
Глубже. Варианты по возрастанию сложности. Статический проброс подходит для одного-двух сервисов, но не масштабируется и опасен без фильтрации. Реверс-прокси (nginx, Envoy, Traefik; в Kubernetes — Service типа LoadBalancer или Ingress) — стандартное решение: снаружи один публичный адрес, внутри маршрутизация по имени хоста и пути, там же терминация TLS. Обратный туннель (SSH remote forwarding, VPN, wireguard, сервисы вроде ngrok/Cloudflare Tunnel) применяют, когда публичного адреса нет вовсе, — соединение всегда инициируется изнутри, поэтому NAT его пропускает. UPnP/NAT-PMP/PCP позволяют приложению попросить шлюз открыть порт автоматически, но в корпоративных сетях обычно выключены. Для P2P используется hole punching: обе стороны через STUN узнают свои внешние адрес:порт, обмениваются ими через сигнальный сервер и начинают слать пакеты навстречу, «пробивая» состояние в обоих NAT; при symmetric NAT (внешний порт меняется для каждого назначения) это не срабатывает и нужен ретранслятор TURN. Важное следствие проброса: приложение видит IP клиента только если прокси передаёт его в X-Forwarded-For/PROXY protocol, иначе все запросы придут с адреса прокси.
Разработать систему управления перевозками (ТМС), задачей которой является обеспечение доставки груза из точки А в точку В. Возможна доставка через промежуточные точки (хабы), при этом на каждом отрезке пути может быть назначен свой исполнитель (водитель/транспорт). Поездка по маршруту это грузоперевозка.
Заголовок раздела «Разработать систему управления перевозками (ТМС), задачей которой является обеспечение доставки груза из точки А в точку В. Возможна доставка через промежуточные точки (хабы), при этом на каждом отрезке пути может быть назначен свой исполнитель (водитель/транспорт). Поездка по маршруту это грузоперевозка.»Коротко. Это system design задача. Ядро модели — три уровня: Order (что и куда везём, требования клиента), Shipment/грузоперевозка (конкретная реализация заказа по маршруту), Leg (отрезок между двумя точками с собственным назначенным исполнителем и транспортом). Маршрут строится как путь в графе, где вершины — точки и хабы, рёбра — доступные плечи со стоимостью и временем; назначение исполнителей на плечи — отдельная задача распределения с ограничениями (грузоподъёмность, режим труда и отдыха, допуск к типу груза).
Глубже. Каркас ответа, который ждут на собеседовании.
- Уточняющие вопросы: объёмы (заказов в сутки, плеч на заказ), нужна ли реальная оптимизация маршрута или достаточно ручного планирования, есть ли перегрузка груза в хабе (значит, нужны события «выгрузили/погрузили» и учёт вместимости хаба), нужен ли трекинг в реальном времени, кто источник правды по статусу — водительское приложение или телематика.
- Домен и состояния. Order:
created → planned → in_transit → delivered | cancelled. Leg:planned → assigned → started → arrived → completed | failed. Переходы — явная машина состояний с проверкой допустимости; статус Order выводится из статусов плеч, а не хранится независимо (иначе рассинхрон). - Данные. Реляционная БД:
orders,shipments,legs (shipment_id, seq, from_point_id, to_point_id, carrier_id, vehicle_id, driver_id, planned_start, planned_end),points,hubs,vehicles,drivers,assignments,tracking_events. Уникальность(shipment_id, seq)гарантирует связность цепочки; события трекинга — отдельная таблица только на запись, партиционированная по времени, из неё же строится история. Пересечение назначений одного водителя ловится ограничением исключения (EXCLUDE USING gistпо временному диапазону) — это хороший ответ на «как не назначить водителя на два рейса одновременно». - Алгоритмы. Построение маршрута — Дейкстра/A* по графу плеч с весом «время + стоимость», при жёстких дедлайнах — с ограничениями по времени прибытия; распределение по исполнителям — жадная эвристика с последующей локальной оптимизацией, полноценный VRP решают отдельным солвером и офлайн.
- API и надёжность. Команды
POST /orders,POST /shipments/{id}/plan,POST /legs/{id}/assign,POST /legs/{id}/events— все с ключом идемпотентности, потому что водительское приложение работает в плохой сети и обязательно ретраит; события с мобильного приходят с задержкой и не по порядку, значит нужна дедупликация поevent_idи сортировка по времени устройства с проверкой на монотонность. - Масштабирование и интеграции: события в Kafka с ключом
shipment_id(сохраняет порядок в пределах перевозки), отдельные читатели для нотификаций, биллинга, аналитики; outbox для атомарной публикации; чтение статусов через денормализованную проекцию, чтобы не собирать её джойнами на каждый запрос клиента.
Опиши какой путь проходит информация от одного сервера к другому?
Заголовок раздела «Опиши какой путь проходит информация от одного сервера к другому?»Коротко. Приложение резолвит имя в IP через DNS, открывает сокет и устанавливает TCP-соединение (SYN, SYN-ACK, ACK), при HTTPS — ещё TLS-хендшейк. Данные разбиваются на сегменты, каждый упаковывается в IP-пакет, затем в Ethernet-кадр с MAC следующего устройства, найденным через ARP. Кадр идёт через коммутатор к маршрутизатору, тот по longest prefix match выбирает next-hop, уменьшает TTL и переписывает L2-заголовок; так пакет проходит цепочку маршрутизаторов и автономных систем, по пути обычно проходя NAT. На принимающем хосте происходит обратная декапсуляция, ядро по номеру порта находит сокет, приложение читает данные.
Глубже. Детали, которые отличают хороший ответ. DNS-резолв сам по себе сетевой вызов (обычно UDP/53 к резолверу, с кешированием на нескольких уровнях). Выбор исходного интерфейса и адреса определяется таблицей маршрутизации до старта соединения. Если получатель в той же подсети — ARP-запрос идёт прямо к нему; если нет — к шлюзу по умолчанию, и весь внешний трафик уходит с MAC шлюза. Размер сегмента ограничен MSS, который выводится из MTU канала (обычно 1500, минус 40 байт заголовков → MSS 1460); если по пути MTU меньше, а бит DF установлен, приходит ICMP Fragmentation Needed и работает Path MTU Discovery — заблокированный ICMP здесь превращается в классический «соединение устанавливается, но большие ответы виснут». TCP обеспечивает надёжность подтверждениями и ретрансмиссиями, а скорость отправки ограничена минимумом из окна получателя и окна перегрузки (CUBIC по умолчанию в Linux, BBR как альтернатива), поэтому на «длинных толстых» каналах пропускная способность упирается в BDP и размер буферов. Между провайдерами путь определяется анонсами BGP и может быть асимметричным — обратный трафик идёт другой дорогой. Наблюдать всё это удобно через traceroute/mtr (использует TTL и ICMP Time Exceeded), tcpdump, ss -i и curl -w с разбивкой по фазам.
Что такое MAC адрес?
Заголовок раздела «Что такое MAC адрес?»Коротко. MAC-адрес (Media Access Control) — 48-битный идентификатор сетевого интерфейса на канальном уровне, записывается как шесть байт в шестнадцатеричном виде, например 00:1a:2b:3c:4d:5e. Первые три байта — OUI, идентификатор производителя, выданный IEEE; остальные назначает производитель. Адрес ff:ff:ff:ff:ff:ff — широковещательный.
Глубже. В первом байте значимы два бита: младший (I/G) отличает individual от group (multicast — например, 01:00:5e:... для IPv4-multicast), следующий (U/L) отличает глобально уникальный адрес от локально администрируемого. Уникальность гарантируется только для глобальных адресов, и то в теории — MAC легко меняется программно (ip link set dev eth0 address 02:00:00:00:00:01), поэтому опираться на него как на средство аутентификации нельзя. Формально существуют EUI-48 (обычный MAC) и EUI-64; при автоконфигурации IPv6 по SLAAC из MAC исторически формировали интерфейсный идентификатор, но из-за трекинга это заменили на случайные адреса (RFC 8981). Современные ОС и телефоны рандомизируют MAC при сканировании Wi-Fi-сетей. Полезные места, где MAC встречается бэкендеру: ip link на хосте, docker network inspect и MAC у veth-интерфейсов контейнеров, поле в DHCP-лизах, идентификатор в UUID v1 (что и было причиной отказа от него в пользу v4/v7).
Что такое IP адрес?
Заголовок раздела «Что такое IP адрес?»Коротко. IP-адрес — логический адрес узла на сетевом уровне, по которому пакеты маршрутизируются в составной сети. В IPv4 это 32 бита, записываемые как четыре десятичных октета (192.168.1.10), в IPv6 — 128 бит в шестнадцатеричной записи. Адрес делится на сетевую и хостовую часть, границу задаёт префикс/маска.
Глубже. Классификация, которую стоит знать: публичные и приватные (RFC 1918: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), loopback 127.0.0.0/8 (в IPv6 ::1), link-local 169.254.0.0/16 и fe80::/10, CGNAT 100.64.0.0/10, multicast 224.0.0.0/4, «неопределённый» 0.0.0.0 (в контексте bind означает «все интерфейсы»). Адрес принадлежит интерфейсу, а не хосту: у машины их может быть несколько, плюс несколько адресов на одном интерфейсе (secondary), плюс адреса на loopback для anycast-сервисов. Назначается статически, через DHCP или, в IPv6, через SLAAC. Специальные адреса подсети — сетевой (все нули в хостовой части) и широковещательный (все единицы) — недоступны для хостов, из-за чего в /24 доступно 254 адреса. В IPv6 broadcast’а нет вовсе, его заменяет multicast, а NAT практически не используется. Для бэкендера практический смысл: 0.0.0.0:8080 слушает на всех интерфейсах и доступен снаружи, 127.0.0.1:8080 — только локально; в контейнере привязка к 127.0.0.1 означает недоступность из других контейнеров и с хоста.
Что такое nat и для чего он нужен?
Заголовок раздела «Что такое nat и для чего он нужен?»Коротко. См. выше первый вопрос про NAT — это тот же механизм трансляции адресов. Кратко о назначении: экономия дефицитных публичных IPv4-адресов (тысячи хостов за одним внешним адресом), сокрытие внутренней топологии и побочная фильтрация входящих соединений, а также возможность менять провайдера или внутреннюю адресацию, не трогая конфигурацию хостов.
Глубже. Отличие от предыдущего вопроса — акцент на «зачем», поэтому добавлю сценарии, где NAT применяют осознанно, а не вынужденно: DNAT как способ публикации сервисов (проброс портов, реализация Kubernetes Service через iptables/IPVS — это по сути DNAT на адрес пода), NAT для устранения конфликта одинаковых приватных подсетей при слиянии сетей двух компаний (двойной NAT 1:1), source NAT на выходном шлюзе, чтобы весь трафик к партнёру шёл с одного «белого» адреса, который партнёр вносит в whitelist. Ограничения тоже полезно назвать: состояние трансляций — узкое место и точка отказа (перезапуск шлюза рвёт все сессии), число одновременных сессий ограничено размером таблицы conntrack и диапазоном портов (~64k на пару адрес-адрес, отсюда SNAT port exhaustion в облаках), а сквозные протоколы и P2P требуют дополнительных механизмов.
Что выводит команда ip?
Заголовок раздела «Что выводит команда ip?»Коротко. ip — утилита из пакета iproute2, современная замена ifconfig/route/arp. Без аргументов она печатает справку по объектам и опциям. Реальный вывод зависит от объекта: ip addr — интерфейсы с их IP-адресами и состоянием, ip link — канальный уровень (MAC, MTU, флаги UP/DOWN), ip route — таблицу маршрутов, ip neigh — ARP/NDP-кеш, ip -s link — счётчики пакетов и ошибок.
Глубже. Часто используемые формы: ip -br -c addr — компактная таблица «интерфейс, состояние, адреса», удобна в скриптах; ip route get 8.8.8.8 — какое решение примет ядро для конкретного адреса, с указанием интерфейса и src-адреса; ip rule list — правила policy-based routing; ip netns list — сетевые namespace’ы (основа контейнерной изоляции); ip -6 addr — только IPv6; ip monitor — поток событий об изменениях в сетевом стеке. Стоит уметь читать вывод ip addr: state UP против state DOWN и NO-CARRIER, флаг LOWER_UP (линк физически поднят), mtu 1500, scope global против scope link и scope host, у адреса — dynamic (получен по DHCP) и время жизни. Ключевое отличие от ifconfig — ip показывает все адреса интерфейса (а не только первый) и работает с современными объектами ядра: namespace, VRF, туннели, XDP.
Какие бывают сетевые девайсы в Linux?
Заголовок раздела «Какие бывают сетевые девайсы в Linux?»Коротко. Физические (eth0, enp3s0, wlan0) и виртуальные: lo (loopback), bridge (программный коммутатор), veth (пара интерфейсов, соединяющая namespace’ы — основа Docker/Kubernetes), tun/tap (L3/L2-интерфейсы, через которые трафик уходит в пользовательское приложение, как в VPN), bond/team (агрегация линков), VLAN-интерфейсы 802.1Q (eth0.100), macvlan/ipvlan, туннели vxlan, gre, ipip, wireguard, а также dummy для тестов.
Глубже. Смотреть их — ip link show, создавать — ip link add ... type .... Что где применяется: veth плюс bridge — классическая сетевая схема Docker (docker0), где один конец пары внутри namespace контейнера, другой воткнут в мост; vxlan — оверлей для связи подов между узлами (Flannel, Calico в VXLAN-режиме), инкапсулирует Ethernet-кадр в UDP и потому уменьшает эффективный MTU (типично 1450); macvlan даёт контейнеру собственный MAC на физическом линке без моста, ipvlan — тот же родительский MAC с разделением по IP, что важно в облаках с port security. tun — то, что использует WireGuard в userspace-реализациях и OpenVPN; wireguard в современном ядре — отдельный тип устройства. bond объединяет линки в режимах active-backup, 802.3ad (LACP), balance-xor. Отдельно стоит знать ifb (перенаправление входящего трафика для шейпинга через tc) и то, что каждый namespace имеет свой набор устройств и свой lo — переключиться можно через ip netns exec <ns> ip addr.
Частые ошибки на собесе
Заголовок раздела «Частые ошибки на собесе»- Путать уровни: говорить, что «коммутатор смотрит IP», или пытаться узнать MAC удалённого сервера через интернет. MAC не выходит за пределы broadcast-домена.
- Считать, что маршрутизатор выбирает маршрут «по первому совпадению в таблице». Правило — longest prefix match, и только при равной длине сравниваются административная дистанция и метрика.
- Описывать NAT как средство безопасности. NAT скрывает адресацию, но фильтрацией занимается файрвол; NAT без правил не защищает от атак через установленные изнутри соединения.
- Обещать exactly-once доставку. В сети возможен только at-least-once плюс идемпотентность приёмника; «дедупликация по совпадению полей за 5 минут» — не идемпотентность.
- Забывать про таймауты и отмену контекста при описании синхронных вызовов, а в Go — не закрывать и не дочитывать
resp.Body, теряя keep-alive. - Смешивать OSPF и BGP: называть BGP «протоколом внутри дата-центра, который считает кратчайший путь». BGP выбирает по политикам, кратчайший путь считает IGP (хотя BGP в ДЦ действительно применяют — но как раз из-за политик и масштабирования, а не из-за метрик).
- Забывать про адрес сети и широковещательный адрес при подсчёте хостов в подсети (в /24 доступно 254, а не 256) и не помнить, что /31 и /32 — легитимные особые случаи.
Что почитать
Заголовок раздела «Что почитать»- RFC 1918 (приватные адреса), RFC 4632 (CIDR), RFC 2663 и RFC 3022 (терминология и механика NAT), RFC 7938 (BGP в дата-центрах) — https://www.rfc-editor.org/
- Kurose, Ross. «Computer Networking: A Top-Down Approach» — базовый учебник, лучший источник по стеку и маршрутизации.
- Документация iproute2 и man-страницы
ip(8),ip-route(8),ip-link(8),ss(8); для netfilter — https://www.netfilter.org/documentation/ - «Latency Numbers Every Programmer Should Know» (Jeff Dean / Peter Norvig) — ориентиры по задержкам: https://colin-scott.github.io/personal_website/research/interactive_latency.html
- Документация Go:
net/http(в частности семантикаTransportи переиспользования соединений) иnet/http/httptrace— https://pkg.go.dev/net/http