linuxsys

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 17, 2026 License: GPL-3.0 Imports: 25 Imported by: 0

Documentation

Overview

Package linuxsys — системная часть Linux для Э4: выбор физического интерфейса и подписка на смену сети (plan-e4.md, блок N), дальше — адаптер, маршруты и DNS. Аналог winsys.

Код пакета живёт в файлах *_linux.go, и только они импортируют netlink из github.com/vishvananda/netlink. Под другие системы пакет пустой.

Index

Constants

View Source
const (
	RPFilterOff    = 0
	RPFilterStrict = 1
	RPFilterLoose  = 2
)

RPFilter* — значения rp_filter IPv4.

Variables

This section is empty.

Functions

func ClearTunnelRoutes

func ClearTunnelRoutes(index int, prefixes []netip.Prefix) error

ClearTunnelRoutes снимает маршруты prefixes с интерфейса index; маршрута уже нет — не ошибка. Снимает все, что может, и возвращает первую ошибку.

func ConfigureAdapter

func ConfigureAdapter(index int, addrs []netip.Addr, mtu int, logf func(string, ...any)) error

ConfigureAdapter задаёт своему адаптеру MTU и адреса и поднимает его. Аналог winsys.ConfigureAdapter: каждый адрес — префиксом во всю длину (/32, /128), маршруты ставит вызывающий. Повторный вызов — перенастройка: адреса глобальной области, которых нет в addrs, снимаются (на своём адаптере все они наши), автоматический fe80:: не трогается.

MTU меньше 1280 IPv6 на интерфейсе не оставляет: адреса IPv6 тогда не ставятся, о чём пишется в logf, — IPv6 через туннель всё равно не работает, а подъём из-за него не падает (как на Windows).

func FirewalldStrictIPv6

func FirewalldStrictIPv6() (bool, error)

FirewalldStrictIPv6 — работает ли firewalld со строгим обратным фильтром IPv6 (IPv6_rpfilter в firewalld.conf: yes — умолчание, или strict). Его правило (fib saddr . mark . iif oif missing drop) отбрасывает ответы IPv6 сокетам ядра так же, как строгий rp_filter — IPv4 (проверено опытом в T3). Режимы loose и *-forward входящие ответы не трогают.

firewalld узнаётся по процессу (comm «firewalld»): правила у него в nftables, но служба может быть установлена и не запущена.

func LinkIndex

func LinkIndex(name string) (int, error)

LinkIndex — индекс интерфейса name.

func LinkKind

func LinkKind(name string) (kind string, ok bool, err error)

LinkKind — тип интерфейса с именем name, как его называет ip -d link («tuntap», «wireguard», «dummy»); ok = false — интерфейса нет.

func ReversePathFilter

func ReversePathFilter(name string) (int, error)

ReversePathFilter — действующий rp_filter IPv4 интерфейса name: ядро берёт большее из net.ipv4.conf.all и значения самого интерфейса.

func SetTunnelRoutes

func SetTunnelRoutes(index int, prefixes []netip.Prefix) error

SetTunnelRoutes ставит маршруты prefixes через интерфейс index таблицы main: без шлюза, область link, как wg-quick. Повторный вызов безвреден (RouteReplace). Маршруты висят на интерфейсе и уходят вместе с ним. Аналог winsys.SetTunnelRoutes.

func WatchNetwork

func WatchNetwork(notify func(index uint64)) (stop func(), err error)

WatchNetwork подписывается через netlink на смену маршрутов, интерфейсов и адресов обоих семейств и зовёт notify с индексом интерфейса, которого коснулось изменение; 0 — интерфейс неизвестен (маршрут с несколькими шлюзами, обрыв подписки). Аналог winsys.WatchNetwork.

Сами события ничего не решают: сворачивает серию и решает, сменилась ли сеть, вызывающий (netguard.Guard.Watch).

notify зовётся из нескольких горутин, возможно одновременно. stop снимает подписку и ждёт уже начатых вызовов notify; после stop новых нет.

Types

type DNSGuard

type DNSGuard struct {
	// contains filtered or unexported fields
}

DNSGuard — защита от утечки DNS на Linux (plan-e4.md, D1), аналог winsys.DNSGuard: правила nftables запрещают обращения на порт 53, UDP и TCP, ко всем адресам, кроме резолверов туннеля, loopback и сокетов qwdtt-cli.

Таблица inet qwdtt-cli создаётся с флагом owner: ядро держит её, пока открыт netlink-сокет, который её создал, и удаляет вместе с ним — в том числе когда qwdtt-cli падает. Уборка при запуске поэтому не нужна. Менять таблицу с owner может только этот сокет, так что всё идёт через одно долгоживущее соединение (nftables.AsLasting).

Сокеты ядра qwdtt-cli опознаются по метке SO_MARK (netguard.SocketMark): фильтр по uid пропустил бы под sudo все процессы root. Метка только опознаёт — маршрутизации по ней нет.

Цепочка forward — ради контейнеров и ВМ: их запросы к резолверу в локальной сети (маршрут конкретнее /1) иначе ушли бы мимо туннеля.

Таблица и цепочки создаются один раз (OpenDNSGuard), а правила ставятся и снимаются по ходу (Enable, Lockdown, Disable): отказ так узнаётся до подключения, а не посреди него. В тех же цепочках живёт и kill-switch (plan-e3.md, K2) — набор «закрыто», см. Lockdown.

func OpenDNSGuard

func OpenDNSGuard(mark uint32) (*DNSGuard, error)

OpenDNSGuard создаёт таблицу inet qwdtt-cli с пустыми цепочками; правил ещё нет. mark — метка сокетов qwdtt-cli, им DNS разрешён к любым адресам. Нужны CAP_NET_ADMIN и ядро 5.12 или новее (флаг owner).

func (*DNSGuard) BlockIPv6

func (g *DNSGuard) BlockIPv6(block bool)

BlockIPv6 задаёт, закрывать ли при следующем Enable весь IPv6 системы, кроме loopback и сокетов qwdtt-cli: туннель его не везёт (MTU < 1280, ядро снимает IPv6 с адаптера), и без запрета он шёл бы мимо туннеля.

func (*DNSGuard) Close

func (g *DNSGuard) Close() error

Close закрывает соединение, и ядро удаляет таблицу вместе с правилами.

func (*DNSGuard) Disable

func (g *DNSGuard) Disable() error

Disable снимает правила; таблица и цепочки остаются пустыми и ничего не запрещают — ни DNS, ни (после Lockdown) прочий трафик. Повторный вызов безвреден.

func (*DNSGuard) Enable

func (g *DNSGuard) Enable(dns []netip.Addr) error

Enable ставит правила: DNS — только к резолверам dns. Прежние правила заменяются одним пакетом nftables, так что повторный вызов безвреден, а на отказе ядро не применяет ничего. Тогда правила снимаются, чтобы не осталось и старых, — кроме набора «закрыто»: он строже защиты DNS, и открывать систему из-за отказа незачем.

func (*DNSGuard) Lockdown

func (g *DNSGuard) Lockdown() error

Lockdown ставит набор «закрыто» — kill-switch (plan-e3.md, K2), пока туннель не поднят: системе разрешены только сокеты qwdtt-cli, loopback, служебные протоколы (DHCP, ICMPv6 ND и RA) и локальная сеть, а DNS — никуда, кроме сокетов qwdtt-cli и loopback, даже к локальной сети: резолвер роутера отдал бы запросы провайдеру. Прочее отбрасывается; отправка UDP тогда кончается EPERM, TCP — таймаутом.

Правила заменяются одним пакетом, как в Enable: перехода через пустую таблицу нет. На отказе ядро не применяет ничего, и остаётся прежний набор.

type DNSMethod

type DNSMethod int

DNSMethod — способ, которым qwdtt-cli задаёт DNS системы.

const (
	DNSResolved DNSMethod = iota + 1
	DNSResolvconfDebian
	DNSOpenresolv
	DNSFile
)

func (DNSMethod) String

func (m DNSMethod) String() string

type Interface

type Interface struct {
	Index uint32 // индекс для netguard (SO_BINDTOIFINDEX)
	Name  string // имя, как в ip link: «eth0», «wlan0»
}

Interface — выбранный физический интерфейс.

func DefaultInterface

func DefaultInterface(ipv6 bool, exclude uint64) (iface Interface, ok bool, err error)

DefaultInterface выбирает интерфейс маршрута по умолчанию (0.0.0.0/0 или ::/0) таблицы main для семейства: с наименьшей метрикой маршрута, только интерфейсы в состоянии UP, без интерфейса exclude (индекс своего адаптера; 0 — исключать нечего). Маршруты туннеля — две половины /1, в выбор они не попадают, а свой адаптер исключается ещё и явно, поэтому выбор работает и при поднятом туннеле. Аналог winsys.DefaultInterface.

ok = false — маршрута по умолчанию для семейства нет (например, сеть без IPv6). Это не ошибка.

type SystemDNS

type SystemDNS struct {
	// contains filtered or unexported fields
}

SystemDNS — DNS туннеля в системе для адаптера с именем name. Не потокобезопасен: зовёт его одна горутина адаптера.

func DetectSystemDNS

func DetectSystemDNS(name string) (*SystemDNS, error)

DetectSystemDNS узнаёт, кто ведёт /etc/resolv.conf, и способ, которым qwdtt-cli задаст DNS туннеля адаптеру name. Ничего не меняет. Ссылка на чужой генератор — ошибка: перезаписать файл значило бы сломать его хозяину resolv.conf до выхода qwdtt-cli.

func (*SystemDNS) Applied

func (s *SystemDNS) Applied() bool

Applied — поставил ли Set DNS, который ещё не снят.

func (*SystemDNS) Method

func (s *SystemDNS) Method() DNSMethod

Method — выбранный способ.

func (*SystemDNS) Recover

func (s *SystemDNS) Recover() (string, error)

Recover убирает то, что оставил упавший прошлый запуск: запись resolvconf и перезаписанный resolv.conf. Звать до Set и только когда второго qwdtt-cli на машине быть не может (после блокировки адаптера) — иначе снялся бы DNS работающего. Возвращает, что убрано; "" — ничего.

func (*SystemDNS) Revert

func (s *SystemDNS) Revert(index int) error

Revert снимает то, что поставил Set; без Set ничего не делает. Повторный вызов безвреден.

func (*SystemDNS) Set

func (s *SystemDNS) Set(index int, servers []netip.Addr) error

Set делает servers DNS системы; повторный вызов заменяет прежние. index — индекс интерфейса адаптера (для systemd-resolved). Пустой servers снимает поставленное: сервер DNS не выдал, остаётся DNS системы.

func (*SystemDNS) String

func (s *SystemDNS) String() string

String — способ для строки вывода.

func (*SystemDNS) Warning

func (s *SystemDNS) Warning() string

Warning — чем выбранный способ ненадёжен; "" — ничем.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL