Documentation
¶
Overview ¶
Package resource は Resource Guard(設計文書 7a.5、7a.10 節)。wgft 自身が保持するフローの プロセス全体の予算と、その予算から導くルールごとの隔離とメモリのソフト上限を持つ。 エージェントの中継(relay)と vpsd のプロキシモードの中継(proxyrelay)が共有する。 プロセス全体の数とルールごとの数は、Pool が 1 つの排他の中で数える。
利用者が設定する通信方針(送信元ごとの同時フロー数の上限)は Admission Policy に属し、 internal/policy の AdmissionLimits が持つ(設計文書 7a.10 節の型の分割)。
Index ¶
- Constants
- type Limits
- type Listener
- type Pool
- func (p *Pool) InUse() int
- func (p *Pool) Listener(ruleID string) *Listener
- func (p *Pool) PendingListener(ruleID string) *Listener
- func (p *Pool) Refusals() map[string]map[Reason]uint64
- func (p *Pool) Reserve() int
- func (p *Pool) RuleCap() int
- func (p *Pool) RuleFlows(ruleID string) int
- func (p *Pool) Rules() int
- func (p *Pool) Total() int
- type Reason
- type Refusal
Constants ¶
const ( UDPTotal = 8192 TCPTotal = 2048 // プロセス全体の上限に設定できる範囲 TotalMin = 16 TotalMax = 65535 )
仕様 7 節の値。プロセス全体の上限(WGFT_MAX_UDP_FLOWS、WGFT_MAX_TCP_FLOWS)が設定項目で、 ここはその既定値。ルール 1 本の上限と隔離予約は設定項目ではなく、この上限から Pool が導く。
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Limits ¶
Limits はプロセス全体のフロー予算(設定値)。どの項目もゼロ値なら既定値を使う(WithDefaults)。 ゼロ値を「上限なし」にすると、設定層を通らずに組み立てた Limits で守りが黙って外れるためである。
func (Limits) MemoryLimit ¶
MemoryLimit は、この上限で動くプロセスに設定するメモリのソフト上限(バイト)。 係数はラボの実測からの定数で、ホストのメモリの量は見ない(仕様 7 節)。
type Listener ¶
type Listener struct {
// contains filtered or unexported fields
}
Listener は Pool に登録した待ち受け 1 つ。フローの枠はこの型を通して取る。 どの項目も Pool の排他が守るので、複数の goroutine から呼べる。
func (*Listener) Accept ¶
func (l *Listener) Accept()
Accept は StopAccepting でやめた受け付けを再開する(宣言に戻った UDP の待ち受け)。
func (*Listener) Acquire ¶
Acquire はフロー 1 つ分の枠を取る。取れたら真を返し、呼び出し側はフローの終わりに Release を 1 回呼ぶ。取れなければ理由を返し、何も数えない。拒否は理由ごとの数に 1 を足す。
判定は数の帳簿だけを見るので、受け付けていない handle(Retiring と、bind の済んでいない PendingListener)でも枠は取れる。取った枠はプロセス全体の数 u に入り、ルールごとの数 u_r には 入らず、そのルールを A にも入れない。中継は、待ち受けを Retiring にするときにソケットを閉じ、 bind が済んで Accept を呼んでから中継を始めるので、この状態で Acquire を呼ぶのは、閉じる直前に accept してしまったフローだけである。
func (*Listener) Close ¶
func (l *Listener) Close()
Close は待ち受けを Pool から外す。まだ返していないフローは、Release を呼ぶまでプロセス全体の数に 残る(閉じた待ち受けのフローは、中継が閉じ終えるまで資源を使っているため)。
func (*Listener) Release ¶
func (l *Listener) Release()
Release は Acquire で取った枠を返す。Acquire が真を返した回数より多く呼ばれたときは何もしない。 数を負にすると、以後の判定が予算を過大に空いていると見て守りが外れるためである。
func (*Listener) StopAccepting ¶
func (l *Listener) StopAccepting()
StopAccepting は、この待ち受けを新しいフローを受け付けない状態にする(設計文書 7a.3 節の Retiring)。残っているフローはプロセス全体の数 u に入り続けるが、ルールごとの数 u_r からは 外れ、そのルールは待ち受けが他に無ければ A から外れる。ルール単位の fail-closed はそのルールの 待ち受けをすべて Retiring にするので、そのルールは新しいフローを受け付けているルールではなくなる。
type Pool ¶
type Pool struct {
// contains filtered or unexported fields
}
Pool は 1 つのプロトコルのフロー予算(設計文書 7a.5、7a.10 節の Resource Guard)。プロセス全体の 予算 T を共有プールとし、受け付けているルールごとの隔離予約で 1 つのルールへのフラッドが他の ルールの新しいフローを止めることを防ぐ。判定は 1 つの排他の中で行い、ルールごと、理由ごとの 拒否の数を、この Pool を作ってからの累計として持つ。SQLite には保存しない。累計の起点は 呼び出し側の作り方で決まる。vpsd はプロセスの起動時に 1 つ作るのでプロセスが起動してからの 累計になり、エージェントはトンネルを立て直すたびに中継ごと作り直すので、そのたびに 0 に戻る (設計文書 10.2c 節の relay.refusals)。
フローは待ち受け(Listener)に付けて数え、待ち受けから所属ルールへの対応は Pool が持つ。 ルールのフロー数は、そのルールの受け付けている待ち受けのフロー数の合計である。分割と統合で 待ち受けの所属ルールが変わると、その待ち受けの既存のフローは移動先のルールで数える (仕様 7 節の規則のまま)。
記号は設計文書 7a.10 節に合わせる。T は予算、C はルールが 2 本以上あるときのルール 1 本の上限 (ceil(T/2))、A は受け付けているルールの集合、N は A の大きさ、q は N が 2 以上のときの floor((T - C) / (N - 1))、u はプロセス全体のフロー数、u_r はルール r のフロー数である。 ルール r の新しいフローは、u < T であり、N が 2 以上なら u_r < C であり、かつ u_r < q または T - u - Σ max(0, q - u_s) >= 1(和は A のうち r 以外)のときに通す。
判定を 1 回あたり一定の手間で済ませるため、u_r と Σ max(0, q - u_s) は足し引きで保つ。 q は N が変わったときだけ変わるので、待ち受けの登録、閉鎖、受け付けの開始と停止、所属ルールの 付け替えのときに作り直す(設計文書 7a.10 節が A と q を Commit で更新すると定めているとおり、 この 5 つの操作はどれも収束の Commit から呼ばれる)。
func (*Pool) Listener ¶
Listener は待ち受け 1 つ分の枠を Pool に登録する。呼び出し側は待ち受けを閉じるときに Close を呼ぶ。 この handle は登録した時点で新しいフローを受け付けている状態になり、そのルールは A に入る。 bind をまだ試みていない、または bind の成否に関わらず handle を保ちたい呼び出し側は、代わりに PendingListener を使う。
func (*Pool) PendingListener ¶
PendingListener は、まだ新しいフローを受け付けられない待ち受けの枠を登録する。そのルールは Accept を呼ぶまで A に入らない。A は開けた待ち受けを持つルールの集合なので(設計文書 7a.10 節)、bind の 最中の待ち受けと bind に失敗した待ち受けが、その間だけ N を増やして他のルールの予約を減らすことを 防ぐ。
2026-09-20 より前はこの handle をソケットの bind の前に作る設計だったが、今の本番の呼び出し側は どちらも bind を試みた後に handle を作る。relay.Manager.newListener は bind の成否に関わらず handle を作り(30 秒ごとの Retry が失敗した待ち受けを扱えるようにするため)、成功したときだけ Accept を呼ぶ。proxyrelay は逆に、bind に失敗したポートには handle を一切作らず(Prepare の時点で 開けたポートだけを Commit で登録し、Listener を直接使う)、Pending の状態を経ない。どちらの形でも bind が終わる前や失敗した待ち受けのルールを N に数えないことに変わりは無く、Pending の状態は この防止のために残す。
type Reason ¶
type Reason string
Reason は Pool が新しいフローを拒んだ理由(設計文書 7a.10 節の「拒否の報告」)。 Admission Policy の drop の種類とは別で、SQLite にも drop カウンタにも混ぜない。
type Refusal ¶
type Refusal struct {
Reason Reason
RuleID string
InUse int // 判定したときのプロセス全体のフロー数 u
Total int // プロセス全体の予算 T
RuleFlows int // 判定したときのそのルールのフロー数 u_r。予算で拒んだときは数えないので 0
RuleCap int // ルールが 2 本以上あるときのルール 1 本の上限 C
Reserve int // ルール 1 本あたりの隔離予約 q
OtherRules int // 予約を持つ他のルールの数(新しいフローを受け付けているルールのうち自分以外)
}
Refusal は拒んだ 1 回の判定の内訳。中継はこれをログの文言にする(設計文書 7a.10 節)。