Documentation
¶
Overview ¶
Package netguard уводит собственные сокеты ядра мимо туннеля (architecture.md §05, дорожка 2).
Когда datapath забирает весь трафик системы (tun, external), сокеты ядра по общей таблице маршрутов попали бы в тот же туннель, и туннель зависел бы от самого себя. Guard.Control ставится в поле Control у net.Dialer и привязывает сокет к физическому интерфейсу: на Windows — опциями IP_UNICAST_IF / IPV6_UNICAST_IF, на Linux — SO_BINDTOIFINDEX. На других системах Control пока ничего не делает (macOS — этап Э4).
watch.go — слежение за сменой сети (задача S2 Э2, architecture.md §07).
Физический интерфейс выбирается при запуске (SelectInterfaces), но сеть меняется на ходу: Wi-Fi → Ethernet, отключение кабеля, сон и пробуждение. Watch ловит изменения маршрутов и интерфейсов, сворачивает их серию в одно решение, ставит новые индексы и говорит вызывающему, что сеть сменилась, — перезапуск туннеля его дело.
Index ¶
- Constants
- type Choice
- type Guard
- func (g *Guard) Choice() Choice
- func (g *Guard) Control(network, address string, c syscall.RawConn) error
- func (g *Guard) Interfaces() (index4, index6 uint32)
- func (g *Guard) SelectInterfaces(exclude uint64) (string, error)
- func (g *Guard) SetInterfaces(index4, index6 uint32)
- func (g *Guard) Watch(opts WatchOptions) (stop func(), err error)
- type WatchOptions
Constants ¶
const DefaultMaxWait = 10 * time.Second
DefaultMaxWait — потолок ожидания тишины: сеть, которая шумит без конца, всё равно получает решение.
const DefaultSettle = 2 * time.Second
DefaultSettle — сколько тишины после последнего события ждать, прежде чем решать. Переключение Wi-Fi → Ethernet даёт серию событий маршрутов и интерфейсов за секунду-другую; решение по первому из них дало бы несколько перезапусков подряд (§07).
const SocketMark uint32 = 0x1074
SocketMark — значение Mark для сокетов tt. Свободно от известных пользователей меток: wg-quick (0xca6c), kube-proxy (0x4000, 0x8000), Tailscale (маска 0xff0000), Calico (маска 0xffff0000), Cilium (маска 0xf00). Маршрутизации по метке tt не заводит — только опознание.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Choice ¶
type Choice struct {
Index4, Index6 uint32
// Alias4, Alias6 — имена интерфейсов для вывода («Ethernet 3», «Wi-Fi»,
// «enp7s0»).
Alias4, Alias6 string
}
Choice — выбор физического интерфейса для обоих семейств. Индекс 0 — маршрута по умолчанию для семейства нет.
type Guard ¶
type Guard struct {
// Logf, если задан, получает строку на каждый привязанный сокет:
// «[NETGUARD] tcp4 203.0.113.80:443 → интерфейс 3». Главный инструмент
// отладки петли; CLI ставит его при -v. Задаётся до первого сокета.
// Имени хоста в строке нет: в Control адрес приходит уже разрешённым.
Logf func(format string, args ...any)
// Mark, если не 0, ставится сокетам опцией SO_MARK (только Linux): по
// ней защита DNS узнаёт сокеты tt и пропускает их запросы к DNS для
// доменов VK (plan-e4.md, D1). Метке нужен CAP_NET_ADMIN, без него
// сокет не создаётся (EPERM), поэтому CLI ставит её, только когда
// защита DNS включена. Задаётся до первого сокета. На Windows не
// используется: там защита DNS пропускает весь процесс tt.
Mark uint32
// NoIPv6 — сокетам ядра IPv6 не давать: выбор интерфейса IPv6
// отбрасывается, и сокет IPv6 не создаётся, а Go уходит на IPv4. Нужно,
// когда ответы IPv6 на физический интерфейс отбросит обратный фильтр
// (firewalld, plan-e4.md, T3). Задаётся до SelectInterfaces и Watch.
NoIPv6 bool
// contains filtered or unexported fields
}
Guard хранит индексы физического интерфейса для IPv4 и IPv6. Нулевое значение готово к работе, но сокеты не пропускает: ни один интерфейс ещё не выбран. Индексы меняются на ходу (смена сети): новые сокеты берут новый индекс, уже открытые доживают со старым.
func (*Guard) Interfaces ¶
Interfaces возвращает текущие индексы для IPv4 и IPv6.
func (*Guard) SelectInterfaces ¶
SelectInterfaces выбирает физический интерфейс маршрута по умолчанию для IPv4 и IPv6 и ставит их индексы. exclude — свой адаптер (AdapterID datapath: на Windows — LUID, на Linux — индекс), 0 — исключать нечего. Возвращает выбор словами для вывода.
Сети нет вовсе — не ошибка: индексы обнуляются, и сокеты ядра откажут с понятным текстом, а не уйдут в туннель. Ошибка — только отказ системы отдать таблицу маршрутов.
func (*Guard) SetInterfaces ¶
SetInterfaces задаёт индексы физического интерфейса. 0 — для этого семейства интерфейса нет, и сокеты этого семейства не создаются.
func (*Guard) Watch ¶
func (g *Guard) Watch(opts WatchOptions) (stop func(), err error)
Watch следит за сменой сети, пока не вызвана stop. Начинает с выбора, который поставил SelectInterfaces. stop ждёт конца уже идущего Changed.
type WatchOptions ¶
type WatchOptions struct {
// Exclude — свой адаптер на момент события (AdapterID datapath: на
// Windows — LUID, на Linux — индекс интерфейса); 0 или nil —
// исключать нечего. События этого интерфейса пропускаются, и в выбор он
// не попадает: иначе туннель, ставя свои маршруты, перезапускал бы
// себя сам.
Exclude func() uint64
// Changed получает новый и прежний выбор — один раз на свёрнутую серию,
// когда новые индексы уже стоят. Выборы могут совпасть: интерфейс
// пропадал и вернулся (сон, переподключение Wi-Fi), и открытые через
// него сокеты уже мертвы. Вызовы не перекрываются. Обязателен.
Changed func(now, before Choice)
// Settle и MaxWait — окно сворачивания; 0 — DefaultSettle
// и DefaultMaxWait.
Settle, MaxWait time.Duration
}
WatchOptions — настройки Watch.