diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 6689abb..3f3ec89 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -582,8 +582,8 @@ components: description: > JSON-объект (строка). Поля `local_ipv4`, `local_ipv6`, `local_asn` переопределяют глобальные `bird_local_ipv4` / `bird_local_ipv6` / `bird_local_asn` тенанта. - Если эффективный локальный адрес или ASN пира отличается от значений в шаблоне BIRD, - в сгенерированный блок `protocol bgp … from bgp_template` добавляется строка `local … as …`. + Если эффективный локальный адрес или ASN пира отличается от дефолтов тенанта (`bird_local_*`), + в блок `protocol bgp … from bgp_template` добавляется строка `local … as …`. additionalProperties: true BgpSpeaker: @@ -704,12 +704,13 @@ 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-пиров (если не заданы, используется эффективный локальный адрес, как для `local` в шаблоне). + `bird_bgp_source_ipv4` / `bird_bgp_source_ipv6` — для `source address` у BGP-пиров (если не заданы — эффективный `bird_local_ipv4` / политика пира). + Если задан `bird_bgp_source_ipv4`, он же подставляется как BIRD `router id` (перекрывает `bird_router_id`). + Шаблон BGP в конфиге: `local as ;` без локального IP. properties: bird_router_id: type: string - description: BIRD `router id` (рекомендуется IPv4 dotted quad). + description: BIRD `router id` (IPv4 dotted quad), если не задан `bird_bgp_source_ipv4`. bird_local_ipv4: type: string bird_local_ipv6: @@ -718,7 +719,7 @@ components: type: integer bird_bgp_source_ipv4: type: string - description: Исходящий IPv4 для `source address` в блоках `protocol bgp … from bgp_template`. + description: Исходящий IPv4 для `source address` у пиров; также задаёт `router id`, если указан. bird_bgp_source_ipv6: type: string description: Исходящий IPv6 для `source address` у пиров IPv6. diff --git a/internal/birdfmt/bgp.go b/internal/birdfmt/bgp.go index df0ac55..a634a4d 100644 --- a/internal/birdfmt/bgp.go +++ b/internal/birdfmt/bgp.go @@ -11,10 +11,9 @@ const ( BGPTemplateNameV6 = "bgp_template_v6" ) -// BGPTemplatesOptions holds tenant defaults for template bgp bgp_template (+ v6 mirror). +// 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. type BGPTemplatesOptions struct { - LocalIPv4 string - LocalIPv6 string LocalASN uint32 ExportFilterV4 string ExportFilterV6 string @@ -22,9 +21,6 @@ type BGPTemplatesOptions struct { // RenderBGPTemplates renders two template bgp blocks (IPv4 and IPv6 AFI). func RenderBGPTemplates(opts BGPTemplatesOptions) (string, error) { - if strings.TrimSpace(opts.LocalIPv4) == "" || strings.TrimSpace(opts.LocalIPv6) == "" { - return "", fmt.Errorf("birdfmt: template local IPv4 and IPv6 are required") - } if opts.LocalASN == 0 { return "", fmt.Errorf("birdfmt: template local ASN must be non-zero") } @@ -38,9 +34,7 @@ func RenderBGPTemplates(opts BGPTemplatesOptions) (string, error) { } var b strings.Builder fmt.Fprintf(&b, "template bgp %s {\n", BGPTemplateNameV4) - b.WriteString(" local ") - b.WriteString(strings.TrimSpace(opts.LocalIPv4)) - fmt.Fprintf(&b, " as %d;\n", opts.LocalASN) + fmt.Fprintf(&b, " local as %d;\n", opts.LocalASN) b.WriteString(" ipv4 {\n") b.WriteString(" import all;\n") b.WriteString(" export ") @@ -49,9 +43,7 @@ func RenderBGPTemplates(opts BGPTemplatesOptions) (string, error) { b.WriteString(" };\n") b.WriteString("}\n\n") fmt.Fprintf(&b, "template bgp %s {\n", BGPTemplateNameV6) - b.WriteString(" local ") - b.WriteString(strings.TrimSpace(opts.LocalIPv6)) - fmt.Fprintf(&b, " as %d;\n", opts.LocalASN) + fmt.Fprintf(&b, " local as %d;\n", opts.LocalASN) b.WriteString(" ipv6 {\n") b.WriteString(" import all;\n") b.WriteString(" export ") diff --git a/internal/birdfmt/standard_layout_test.go b/internal/birdfmt/standard_layout_test.go index 715c313..0c72685 100644 --- a/internal/birdfmt/standard_layout_test.go +++ b/internal/birdfmt/standard_layout_test.go @@ -30,8 +30,6 @@ func TestStandardLayout_GeneratorMatchesFixtures(t *testing.T) { assertFileEquals(t, "testdata/scenarios/standard_layout/bird.d/evobgp_prefixes_v6.conf", staticV6) tpl, err := RenderBGPTemplates(BGPTemplatesOptions{ - LocalIPv4: "192.0.2.1", - LocalIPv6: "2001:db8::1", LocalASN: 65001, ExportFilterV4: "evobgp_export_v4", ExportFilterV6: "evobgp_export_v6", 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 43eda1c..5930b6e 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 @@ -1,5 +1,5 @@ template bgp bgp_template { - local 192.0.2.1 as 65001; + local as 65001; ipv4 { import all; export filter evobgp_export_v4; @@ -7,7 +7,7 @@ template bgp bgp_template { } template bgp bgp_template_v6 { - local 2001:db8::1 as 65001; + local as 65001; ipv6 { import all; export filter evobgp_export_v6; diff --git a/internal/pipeline/refresh.go b/internal/pipeline/refresh.go index 5de1c06..c91f703 100644 --- a/internal/pipeline/refresh.go +++ b/internal/pipeline/refresh.go @@ -285,8 +285,6 @@ func buildPreviewFragments(st store.Backend, tenantID, moduleID, revisionID stri locals := birdLocalsFromStore(st, tenantID) tplBody, err := birdfmt.RenderBGPTemplates(birdfmt.BGPTemplatesOptions{ - LocalIPv4: locals.localV4, - LocalIPv6: locals.localV6, LocalASN: locals.localASN, ExportFilterV4: birdFilterNameV4, ExportFilterV6: birdFilterNameV6, @@ -364,6 +362,8 @@ func birdLocalsFromStore(st store.Backend, tenantID string) birdLocals { } 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. + loc.routerID = strings.TrimSpace(s) } if s := stringFromSettingsMap(settings, "bird_bgp_source_ipv6"); s != "" { loc.sourceV6 = s @@ -445,7 +445,7 @@ func effectivePeerLocals(loc birdLocals, pol peerPolicyJSON) (v4, v6 string, asn return v4, v6, asn } -// peerNeedsLocalOverride is true when the peer's effective local IP or ASN differs from tenant defaults in the BGP template. +// peerNeedsLocalOverride is true when the peer's effective local IP or ASN should override template "local as …" (add explicit "local as …" on the peer). func peerNeedsLocalOverride(loc birdLocals, effLocal string, effASN uint32, ipv4 bool) bool { if ipv4 { return strings.TrimSpace(effLocal) != strings.TrimSpace(loc.localV4) || effASN != loc.localASN diff --git a/web/src/routes/settings/+page.svelte b/web/src/routes/settings/+page.svelte index b8bfdd8..d6977ac 100644 --- a/web/src/routes/settings/+page.svelte +++ b/web/src/routes/settings/+page.svelte @@ -108,6 +108,12 @@ {:else if apiSettings !== null}
+

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