planner

package
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 26, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package planner turns a normalized rule set and admission policy inputs into a Plan: the backend-independent description of what wgft should forward (design.md 7a.2 節).

Planner "は、normalize したルール集合と AdmissionPolicy から Plan を組み立てる。OS、nftables、 gVisor の実装詳細を知らない。" This package holds no Backend, Runtime, or reconcile logic (those come later; design.md 7a.7 節 assigns them to internal/dataplane and internal/reconcile). It only computes what should exist, never applies anything.

This package is pure: it imports internal/model, internal/policy (OS-free types, admission limits included) and proto (the external contract), and nothing from dataplane/frontend/platform/vpsd/agent or OS-specific packages (design.md 7a.7 節).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Agent

type Agent struct {
	Name string
	Addr netip.Addr
}

Agent is a registered agent as the Planner sees it: its name and its address on wg0. This is the only agent-derived Planner input in Phase 1 (design.md 7a.8 節: "normalize したルール集合と AdmissionPolicy から Plan を組み立てる"). The full WireGuard peer configuration (public key, allowed IPs, keepalive) is assembled by the dataplane Backend from this address, not by Planner; see dataplane.WGConfig.Peers.

type Input

type Input struct {
	Generation uint64
	Rules      []model.Rule
	Limits     policy.AdmissionLimits
	Agents     []Agent
}

Input is everything Build needs to produce a Plan. Limits is the per-source concurrent flow cap setting (WGFT_MAX_*_FLOWS_PER_SOURCE; design.md 7a.5, 7a.10 節); Build passes it straight to policy.Build, so a zero-value AdmissionLimits{} means "use the default caps" (see PerSourceFlowCaps's doc comment in internal/policy), never "no cap". The process-wide flow budget belongs to Resource Guard (internal/resource) and is no Planner input.

type Plan

type Plan struct {
	Generation uint64
	Ports      []PortPlan
	Admission  policy.Policy
}

Plan is the desired generation and the ingress/admission plan and route per owned port (design.md 7a.2 節): backend-independent data a Backend (design.md 7a.7 節, Phase 2/3) reads to converge. internal/planner never calls into a Backend; the dependency is one-directional. The WireGuard peer set is not part of Plan: it is dataplane.WGConfig.Peers, built by the caller (internal/vpsd/apply.go's wgConfig) from the same registered-agent data Planner receives as Input.Agents, and reconcile.Runtime tracks its own changes through Desired.PeersChanged (see design.md 7a.3 節「ピアを変える操作」).

Admission is the AdmissionPolicy IR (internal/policy) for what this Plan forwards: its Rules hold only the rules that have an entry in Ports (disabled rules and rules of unregistered agents are left out), and PerSourceFlowCaps holds the global caps. It is not just the rule-level entries: a Backend given only a Plan must be able to compile admission policy end to end, including the per-source concurrent flow caps (design.md 7a.5 節), without reaching back into whatever built the Plan. This is also what the future nftables/Go-evaluator compilers (Phase 5, design.md 7a.8 節) will compile from. PortPlan.Policy is a per-port copy of the matching Admission.Rules entry, not a second, independently-computed value; see PortPlan.Policy's doc comment.

func Build

func Build(in Input) Plan

Build derives a Plan from normalized rules, admission policy settings, and agent addresses (design.md 7a.2 節). It is pure (no OS, nftables, or gVisor calls) and deterministic: for the same Input, Plan.Ports is always sorted by (Proto, ListenPort.Lo, RuleID), regardless of the input rule or agent order.

A rule is left out of Plan.Ports when it is disabled, or when its agent is not in Input.Agents. This matches what every dataplane already needs before it can forward anything: before this Plan existed, internal/vpsd/nft.emit skipped a rule when cfg.AgentAddr[r.Agent] was not found (and always skipped !r.Enabled), internal/vpsd/proxyrelay.FromRules did the same for the Relay declaration, and so did the userspace relay's rule-based listener set. Since Phase 2/3 (design.md 7a.8 節) all three read this Plan instead (the kernel nft package's emit(), the vpsd-side relayRules(), and the userspace Backend), so FromRules and the rules-based emit() no longer exist to duplicate the filter. A rule cannot be forwarded to an agent wgft does not know the address of.

func (Plan) Port added in v0.5.0

func (p Plan) Port(ruleID string) (PortPlan, bool)

Port returns the PortPlan of ruleID, if the Plan forwards it.

func (Plan) Relay

func (p Plan) Relay() []PortPlan

Relay returns the ports vpsd itself terminates and relays (Forwarding=Relay, today's vps_mode=proxy), regardless of DataplaneMode (design.md 6.2, 6.3 節: the relay is the same TCP-terminating code in both kernel and userspace dataplane modes).

func (Plan) Transparent

func (p Plan) Transparent() []PortPlan

Transparent returns the ports in kernel-DNAT/userspace-relay territory: Forwarding=Transparent. design.md 7a.2 節: "kernel backend では Transparent は nftables の DNAT で完結し...userspace backend では両者は同じ中継コードを使う." Which Backend a given Plan feeds decides the treatment; this helper just partitions Plan.Ports by Forwarding for callers (and tests) that need one kind at a time.

func (Plan) Without added in v0.5.0

func (p Plan) Without(ids map[string]bool) Plan

Without returns a copy of p that forwards none of the rules in ids: their ports and their Admission entries are left out, everything else (generation, per-source caps) is kept. The Runtime uses it to publish a fail-closed Plan, one where the rules whose Prepare failed have no dispatch at all (design.md 7a.3 節). p itself is not modified.

type PortPlan

type PortPlan struct {
	RuleID         string
	Agent          string
	Proto          proto.Proto
	ListenPort     proto.PortRange
	Forwarding     model.Forwarding
	SourceMetadata model.SourceMetadata
	// Target is the rule's declared LAN destination (host:port, unshifted). The agent, not the VPS
	// side, maps a range's individual ports onto it (design.md 5.3, 7 節 "実効宛先"); it is carried
	// through here only as the informational tail end of the route, for a future Backend/agent-wire
	// layer to consume (Phase 2+), not because the VPS side computes anything from it.
	Target string
	// AgentAddr is the agent's wg0 address wgft routes to. The VPS side never rewrites the port
	// (design.md 6.1 節: "DNAT では宛先アドレスだけを書き換え...ポートは書き換えない"; the relay in
	// 6.2/6.3 節 dials the agent at the same listen port too), so ListenPort doubles as the
	// agent-side port; there is no separate "route port" field.
	AgentAddr netip.Addr
	// Policy is this rule's AdmissionPolicy entry (source allow/deny and the three rate limits). It
	// is a copy of the matching entry in Plan.Admission.Rules (same RuleID), kept here too as a
	// convenience view for callers that already have a PortPlan and want its policy without
	// searching Plan.Admission.Rules; Plan.Admission is the single source of truth both are built
	// from (see Build). The zero value means the rule declared none of them, matching
	// policy.Build's convention.
	Policy policy.RulePolicy
}

PortPlan is the route, ingress admission plan, and forwarding kind for one owned port (one enabled rule whose agent is known; design.md 7a.2 節: Plan holds "送信元制限を含む ingress の 計画" and "宛先までの経路の集合" per owned port).

A port range (proto.PortRange with Lo != Hi) stays one entry here, at the same granularity a kernel-mode DNAT dispatch rule uses (design.md 6.1 節: one rule with a gte/lte port comparison, not one row per port). A Backend that needs one entry per individual port (design.md 6.3 節: the userspace relay opens one listener per port) expands the range itself; that expansion is a Backend concern, not something this backend-independent Plan does (design.md 7a.7 節).

Jump to

Keyboard shortcuts

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