From 482eeb122d5cfb4eb12204bbd801710545b75ba1 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Mon, 6 Apr 2026 14:03:25 +0700 Subject: [PATCH] refactor: remove strict bind configuration from BGP templates and update related logic. Eliminate 'strict bind on' from both IPv4 and IPv6 templates to simplify BGP peer rendering. Adjust OpenAPI documentation and internal handling of BGP source addresses to reflect these changes, enhancing clarity and compatibility. --- docs/openapi.yaml | 9 ++-- internal/birdfmt/bgp.go | 17 ++------ internal/birdfmt/bgp_test.go | 15 +++---- internal/birdfmt/standard_layout_test.go | 9 ++-- .../bird.d/evobgp_bgp_template.conf | 2 - .../standard_layout/bird.d/evobgp_peers.conf | 2 - internal/pipeline/refresh.go | 41 ++++--------------- web/src/routes/settings/+page.svelte | 6 +-- 8 files changed, 29 insertions(+), 72 deletions(-) diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 525f619..8767277 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -704,8 +704,8 @@ components: KV настройки tenant/глобальные лимиты и feature flags. Параметры BIRD (строки в `global_settings`, JSON-значения — обычно строка или число): `bird_router_id`, `bird_local_ipv4`, `bird_local_ipv6`, `bird_local_asn`; - `bird_bgp_source_ipv4` / `bird_bgp_source_ipv6` — для `source address` у BGP-пиров (если не заданы — эффективный `bird_local_ipv4` / политика пира). - Если задан `bird_bgp_source_ipv4`, он же подставляется как BIRD `router id` (перекрывает `bird_router_id`). + если задан `bird_bgp_source_ipv4`, он подставляется как BIRD `router id` (перекрывает `bird_router_id`). + Ключи `bird_bgp_source_ipv4` / `bird_bgp_source_ipv6` в сгенерированном BGP для пиров не используются (оставлены для совместимости API). Шаблон BGP в конфиге: `local as ;` без локального IP. properties: bird_router_id: @@ -720,11 +720,10 @@ components: bird_bgp_source_ipv4: type: string description: | - Исходящий IPv4 для `source address` у пиров; также задаёт `router id`, если указан. - На стороне MikroTik в BGP Connection поле **Remote address** должно совпадать с этим адресом (или с тем, с которого реально устанавливается TCP-сессия к 179/tcp), иначе сессия не поднимется. + Если задан — используется как BIRD `router id` (IPv4). В блоках `protocol bgp … from bgp_template` строка `source address` не генерируется. bird_bgp_source_ipv6: type: string - description: Исходящий IPv6 для `source address` у пиров IPv6. + description: Зарезервировано; в текущей генерации BGP не используется. additionalProperties: true RevisionDiff: diff --git a/internal/birdfmt/bgp.go b/internal/birdfmt/bgp.go index c9d83c2..5690a9c 100644 --- a/internal/birdfmt/bgp.go +++ b/internal/birdfmt/bgp.go @@ -12,7 +12,7 @@ const ( ) // BGPTemplatesOptions holds ASN and export filters for template bgp bgp_template (+ v6 mirror). -// Templates use "local as ;" only (no local IP); peers supply source address / optional local override. +// Templates use "local as ;" only (no local IP); peers add neighbor / multihop / passive and optional "local … as …" override. type BGPTemplatesOptions struct { LocalASN uint32 ExportFilterV4 string @@ -43,8 +43,6 @@ func RenderBGPTemplates(opts BGPTemplatesOptions) (string, error) { b.WriteString(" };\n") b.WriteString(" hold time 90;\n") b.WriteString(" keepalive time 30;\n") - // Жёсткая привязка к source address (см. пир): полезно в Docker / bridge, иначе listen может уйти на 0.0.0.0. - b.WriteString(" strict bind on;\n") b.WriteString("}\n\n") fmt.Fprintf(&b, "template bgp %s {\n", BGPTemplateNameV6) fmt.Fprintf(&b, " local as %d;\n", opts.LocalASN) @@ -56,7 +54,6 @@ func RenderBGPTemplates(opts BGPTemplatesOptions) (string, error) { b.WriteString(" };\n") b.WriteString(" hold time 90;\n") b.WriteString(" keepalive time 30;\n") - b.WriteString(" strict bind on;\n") b.WriteString("}\n") return b.String(), nil } @@ -67,14 +64,12 @@ type BGPPeerFromTemplateOptions struct { TemplateName string NeighborIP string NeighborASN uint32 - SourceAddress string // If set, emits "local … as …" before neighbor (overrides template local/ASN for this peer). OverrideLocalIP string OverrideLocalASN uint32 } -// RenderProtocolBGPFromTemplate renders protocol bgp … from TEMPLATE { neighbor; multihop; source address; strict bind; passive; }. -// strict bind дублируется и в шаблоне, и в каждом пире — явная фиксация на сессию. +// RenderProtocolBGPFromTemplate renders protocol bgp … from TEMPLATE { neighbor; multihop; passive; }. func RenderProtocolBGPFromTemplate(opts BGPPeerFromTemplateOptions) (string, error) { if strings.TrimSpace(opts.ProtocolName) == "" { return "", fmt.Errorf("birdfmt: protocol name is required") @@ -82,8 +77,8 @@ func RenderProtocolBGPFromTemplate(opts BGPPeerFromTemplateOptions) (string, err if strings.TrimSpace(opts.TemplateName) == "" { return "", fmt.Errorf("birdfmt: template name is required") } - if strings.TrimSpace(opts.NeighborIP) == "" || strings.TrimSpace(opts.SourceAddress) == "" { - return "", fmt.Errorf("birdfmt: neighbor and source address are required") + if strings.TrimSpace(opts.NeighborIP) == "" { + return "", fmt.Errorf("birdfmt: neighbor is required") } if opts.NeighborASN == 0 { return "", fmt.Errorf("birdfmt: neighbor ASN must be non-zero") @@ -107,10 +102,6 @@ func RenderProtocolBGPFromTemplate(opts BGPPeerFromTemplateOptions) (string, err b.WriteString(strings.TrimSpace(opts.NeighborIP)) fmt.Fprintf(&b, " as %d;\n", opts.NeighborASN) b.WriteString(" multihop;\n") - b.WriteString(" source address ") - b.WriteString(strings.TrimSpace(opts.SourceAddress)) - b.WriteString(";\n") - b.WriteString(" strict bind on;\n") b.WriteString(" passive;\n") b.WriteString("}\n") return b.String(), nil diff --git a/internal/birdfmt/bgp_test.go b/internal/birdfmt/bgp_test.go index 5bf76d0..22b955e 100644 --- a/internal/birdfmt/bgp_test.go +++ b/internal/birdfmt/bgp_test.go @@ -37,11 +37,10 @@ func TestRenderBGPTemplates_Validation(t *testing.T) { func TestRenderProtocolBGPFromTemplate_MultihopPassive(t *testing.T) { got, err := RenderProtocolBGPFromTemplate(BGPPeerFromTemplateOptions{ - ProtocolName: "evobgp_p_x", - TemplateName: BGPTemplateNameV4, - NeighborIP: "94.142.140.141", - NeighborASN: 65002, - SourceAddress: "77.232.38.173", + ProtocolName: "evobgp_p_x", + TemplateName: BGPTemplateNameV4, + NeighborIP: "94.142.140.141", + NeighborASN: 65002, }) if err != nil { t.Fatal(err) @@ -52,10 +51,7 @@ func TestRenderProtocolBGPFromTemplate_MultihopPassive(t *testing.T) { if !strings.Contains(got, "multihop;") || !strings.Contains(got, "passive;") { t.Fatal(got) } - if !strings.Contains(got, "source address 77.232.38.173;") { - t.Fatal(got) - } - if !strings.Contains(got, "strict bind on;") { + if strings.Contains(got, "source address") || strings.Contains(got, "strict bind") { t.Fatal(got) } } @@ -66,7 +62,6 @@ func TestRenderProtocolBGPFromTemplate_OverrideLocal(t *testing.T) { TemplateName: BGPTemplateNameV4, NeighborIP: "192.0.2.2", NeighborASN: 2, - SourceAddress: "10.0.0.1", OverrideLocalIP: "10.0.0.1", OverrideLocalASN: 65099, }) diff --git a/internal/birdfmt/standard_layout_test.go b/internal/birdfmt/standard_layout_test.go index 0c72685..2b8b3ba 100644 --- a/internal/birdfmt/standard_layout_test.go +++ b/internal/birdfmt/standard_layout_test.go @@ -40,11 +40,10 @@ func TestStandardLayout_GeneratorMatchesFixtures(t *testing.T) { assertFileEquals(t, "testdata/scenarios/standard_layout/bird.d/evobgp_bgp_template.conf", tpl) peer, err := RenderProtocolBGPFromTemplate(BGPPeerFromTemplateOptions{ - ProtocolName: "evobgp_peer_ci", - TemplateName: BGPTemplateNameV4, - NeighborIP: "192.0.2.2", - NeighborASN: 65002, - SourceAddress: "192.0.2.1", + ProtocolName: "evobgp_peer_ci", + TemplateName: BGPTemplateNameV4, + NeighborIP: "192.0.2.2", + NeighborASN: 65002, }) if err != nil { t.Fatal(err) diff --git a/internal/birdfmt/testdata/scenarios/standard_layout/bird.d/evobgp_bgp_template.conf b/internal/birdfmt/testdata/scenarios/standard_layout/bird.d/evobgp_bgp_template.conf index 1ecc5fa..72f51e9 100644 --- a/internal/birdfmt/testdata/scenarios/standard_layout/bird.d/evobgp_bgp_template.conf +++ b/internal/birdfmt/testdata/scenarios/standard_layout/bird.d/evobgp_bgp_template.conf @@ -6,7 +6,6 @@ template bgp bgp_template { }; hold time 90; keepalive time 30; - strict bind on; } template bgp bgp_template_v6 { @@ -17,5 +16,4 @@ template bgp bgp_template_v6 { }; hold time 90; keepalive time 30; - strict bind on; } diff --git a/internal/birdfmt/testdata/scenarios/standard_layout/bird.d/evobgp_peers.conf b/internal/birdfmt/testdata/scenarios/standard_layout/bird.d/evobgp_peers.conf index 8208944..dca1b1a 100644 --- a/internal/birdfmt/testdata/scenarios/standard_layout/bird.d/evobgp_peers.conf +++ b/internal/birdfmt/testdata/scenarios/standard_layout/bird.d/evobgp_peers.conf @@ -1,7 +1,5 @@ protocol bgp evobgp_peer_ci from bgp_template { neighbor 192.0.2.2 as 65002; multihop; - source address 192.0.2.1; - strict bind on; passive; } diff --git a/internal/pipeline/refresh.go b/internal/pipeline/refresh.go index c91f703..ce66a83 100644 --- a/internal/pipeline/refresh.go +++ b/internal/pipeline/refresh.go @@ -331,9 +331,6 @@ type birdLocals struct { localV4 string localV6 string localASN uint32 - // Optional BGP TCP source (BIRD "source address"); empty => use effective local per peer. - sourceV4 string - sourceV6 string } func birdLocalsFromStore(st store.Backend, tenantID string) birdLocals { @@ -361,30 +358,12 @@ func birdLocalsFromStore(st store.Backend, tenantID string) birdLocals { loc.localASN = n } if s := stringFromSettingsMap(settings, "bird_bgp_source_ipv4"); s != "" { - loc.sourceV4 = s - // BIRD router id must be an IPv4 address; align with BGP source when operator sets it. + // BIRD router id must be an IPv4 address; historically aligned with optional BGP source setting. loc.routerID = strings.TrimSpace(s) } - if s := stringFromSettingsMap(settings, "bird_bgp_source_ipv6"); s != "" { - loc.sourceV6 = s - } return loc } -func bgpPeerSourceIPv4(loc birdLocals, effectiveLocal string) string { - if s := strings.TrimSpace(loc.sourceV4); s != "" { - return s - } - return effectiveLocal -} - -func bgpPeerSourceIPv6(loc birdLocals, effectiveLocal string) string { - if s := strings.TrimSpace(loc.sourceV6); s != "" { - return s - } - return effectiveLocal -} - func stringFromSettingsMap(m map[string]any, key string) string { v, ok := m[key] if !ok || v == nil { @@ -474,11 +453,10 @@ func renderPeersBirdFragment(st store.Backend, tenantID string, loc birdLocals) ra := uint32(p.RemoteASN) if addr.Is4() { opts := birdfmt.BGPPeerFromTemplateOptions{ - ProtocolName: proto, - TemplateName: birdfmt.BGPTemplateNameV4, - NeighborIP: addr.String(), - NeighborASN: ra, - SourceAddress: bgpPeerSourceIPv4(loc, lv4), + ProtocolName: proto, + TemplateName: birdfmt.BGPTemplateNameV4, + NeighborIP: addr.String(), + NeighborASN: ra, } if peerNeedsLocalOverride(loc, lv4, asn, true) { opts.OverrideLocalIP = lv4 @@ -493,11 +471,10 @@ func renderPeersBirdFragment(st store.Backend, tenantID string, loc birdLocals) } if addr.Is6() { opts := birdfmt.BGPPeerFromTemplateOptions{ - ProtocolName: proto, - TemplateName: birdfmt.BGPTemplateNameV6, - NeighborIP: addr.String(), - NeighborASN: ra, - SourceAddress: bgpPeerSourceIPv6(loc, lv6), + ProtocolName: proto, + TemplateName: birdfmt.BGPTemplateNameV6, + NeighborIP: addr.String(), + NeighborASN: ra, } if peerNeedsLocalOverride(loc, lv6, asn, false) { opts.OverrideLocalIP = lv6 diff --git a/web/src/routes/settings/+page.svelte b/web/src/routes/settings/+page.svelte index d6977ac..1bf1678 100644 --- a/web/src/routes/settings/+page.svelte +++ b/web/src/routes/settings/+page.svelte @@ -98,8 +98,8 @@ GET/PATCH /v1/settings — глобальные параметры control plane (хранятся в БД). Требуется роль operator. Для BIRD, например: bird_local_ipv4, - bird_bgp_source_ipv4 (опционально — только для строки - source address у BGP-пиров). + bird_bgp_source_ipv4 (опционально — задаёт BIRD + router id). @@ -111,7 +111,7 @@

Должен быть строго валидный JSON: ключи и строки в двойных кавычках, без точки с запятой. Пример: {`{"bird_bgp_source_ipv4": "178.250.186.111"}`}{`{"bird_router_id": "203.0.113.1", "bird_local_asn": 65001}`}