diff --git a/docs/openapi.yaml b/docs/openapi.yaml
index 4f19267..6689abb 100644
--- a/docs/openapi.yaml
+++ b/docs/openapi.yaml
@@ -700,7 +700,28 @@ components:
Settings:
type: object
- description: KV настройки tenant/глобальные лимиты и feature flags.
+ description: |
+ 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` в шаблоне).
+ properties:
+ bird_router_id:
+ type: string
+ description: BIRD `router id` (рекомендуется IPv4 dotted quad).
+ bird_local_ipv4:
+ type: string
+ bird_local_ipv6:
+ type: string
+ bird_local_asn:
+ type: integer
+ bird_bgp_source_ipv4:
+ type: string
+ description: Исходящий IPv4 для `source address` в блоках `protocol bgp … from bgp_template`.
+ bird_bgp_source_ipv6:
+ type: string
+ description: Исходящий IPv6 для `source address` у пиров IPv6.
additionalProperties: true
RevisionDiff:
diff --git a/internal/pipeline/refresh.go b/internal/pipeline/refresh.go
index 7c0eaac..5de1c06 100644
--- a/internal/pipeline/refresh.go
+++ b/internal/pipeline/refresh.go
@@ -333,6 +333,9 @@ 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 {
@@ -359,9 +362,29 @@ func birdLocalsFromStore(st store.Backend, tenantID string) birdLocals {
if n := uint32FromSettingsMap(settings, "bird_local_asn"); n != 0 {
loc.localASN = n
}
+ if s := stringFromSettingsMap(settings, "bird_bgp_source_ipv4"); s != "" {
+ loc.sourceV4 = 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 {
@@ -455,7 +478,7 @@ func renderPeersBirdFragment(st store.Backend, tenantID string, loc birdLocals)
TemplateName: birdfmt.BGPTemplateNameV4,
NeighborIP: addr.String(),
NeighborASN: ra,
- SourceAddress: lv4,
+ SourceAddress: bgpPeerSourceIPv4(loc, lv4),
}
if peerNeedsLocalOverride(loc, lv4, asn, true) {
opts.OverrideLocalIP = lv4
@@ -474,7 +497,7 @@ func renderPeersBirdFragment(st store.Backend, tenantID string, loc birdLocals)
TemplateName: birdfmt.BGPTemplateNameV6,
NeighborIP: addr.String(),
NeighborASN: ra,
- SourceAddress: lv6,
+ SourceAddress: bgpPeerSourceIPv6(loc, lv6),
}
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 0858b0f..b8bfdd8 100644
--- a/web/src/routes/settings/+page.svelte
+++ b/web/src/routes/settings/+page.svelte
@@ -95,8 +95,11 @@
Настройки системы (API)
- GET/PATCH /v1/settings — глобальные параметры control plane.
- Требуется роль operator.
+ GET/PATCH /v1/settings — глобальные параметры control plane (хранятся в БД).
+ Требуется роль operator. Для BIRD, например:
+ bird_local_ipv4,
+ bird_bgp_source_ipv4 (опционально — только для строки
+ source address у BGP-пиров).