Files
EvoBGP/internal/birdfmt/bgp.go
T
Denozordec 482eeb122d
CI / changes (push) Successful in 7s
CI / openapi (push) Successful in 23s
CI / go (push) Successful in 24s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Successful in 1m7s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m2s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 55s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 54s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 2m19s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 2m27s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m22s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m26s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m9s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m19s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m19s
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.
2026-04-06 14:03:25 +07:00

217 lines
7.0 KiB
Go

package birdfmt
import (
"fmt"
"strings"
)
// BGP template names referenced by generated peers (BIRD 2).
const (
BGPTemplateNameV4 = "bgp_template"
BGPTemplateNameV6 = "bgp_template_v6"
)
// BGPTemplatesOptions holds ASN and export filters for template bgp bgp_template (+ v6 mirror).
// Templates use "local as <asn>;" only (no local IP); peers add neighbor / multihop / passive and optional "local … as …" override.
type BGPTemplatesOptions struct {
LocalASN uint32
ExportFilterV4 string
ExportFilterV6 string
}
// RenderBGPTemplates renders two template bgp blocks (IPv4 and IPv6 AFI).
func RenderBGPTemplates(opts BGPTemplatesOptions) (string, error) {
if opts.LocalASN == 0 {
return "", fmt.Errorf("birdfmt: template local ASN must be non-zero")
}
exp4 := "all"
if strings.TrimSpace(opts.ExportFilterV4) != "" {
exp4 = "filter " + strings.TrimSpace(opts.ExportFilterV4)
}
exp6 := "all"
if strings.TrimSpace(opts.ExportFilterV6) != "" {
exp6 = "filter " + strings.TrimSpace(opts.ExportFilterV6)
}
var b strings.Builder
fmt.Fprintf(&b, "template bgp %s {\n", BGPTemplateNameV4)
fmt.Fprintf(&b, " local as %d;\n", opts.LocalASN)
b.WriteString(" ipv4 {\n")
b.WriteString(" import none;\n")
b.WriteString(" export ")
b.WriteString(exp4)
b.WriteString(";\n")
b.WriteString(" };\n")
b.WriteString(" hold time 90;\n")
b.WriteString(" keepalive time 30;\n")
b.WriteString("}\n\n")
fmt.Fprintf(&b, "template bgp %s {\n", BGPTemplateNameV6)
fmt.Fprintf(&b, " local as %d;\n", opts.LocalASN)
b.WriteString(" ipv6 {\n")
b.WriteString(" import none;\n")
b.WriteString(" export ")
b.WriteString(exp6)
b.WriteString(";\n")
b.WriteString(" };\n")
b.WriteString(" hold time 90;\n")
b.WriteString(" keepalive time 30;\n")
b.WriteString("}\n")
return b.String(), nil
}
// BGPPeerFromTemplateOptions describes protocol bgp NAME from TEMPLATE { … }.
type BGPPeerFromTemplateOptions struct {
ProtocolName string
TemplateName string
NeighborIP string
NeighborASN uint32
// 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; passive; }.
func RenderProtocolBGPFromTemplate(opts BGPPeerFromTemplateOptions) (string, error) {
if strings.TrimSpace(opts.ProtocolName) == "" {
return "", fmt.Errorf("birdfmt: protocol name is required")
}
if strings.TrimSpace(opts.TemplateName) == "" {
return "", fmt.Errorf("birdfmt: template name is 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")
}
if opts.OverrideLocalIP != "" && opts.OverrideLocalASN == 0 {
return "", fmt.Errorf("birdfmt: override local ASN is required when override local IP is set")
}
var b strings.Builder
b.WriteString("protocol bgp ")
b.WriteString(strings.TrimSpace(opts.ProtocolName))
b.WriteString(" from ")
b.WriteString(strings.TrimSpace(opts.TemplateName))
b.WriteString(" {\n")
if ip := strings.TrimSpace(opts.OverrideLocalIP); ip != "" {
b.WriteString(" local ")
b.WriteString(ip)
fmt.Fprintf(&b, " as %d;\n", opts.OverrideLocalASN)
}
b.WriteString(" neighbor ")
b.WriteString(strings.TrimSpace(opts.NeighborIP))
fmt.Fprintf(&b, " as %d;\n", opts.NeighborASN)
b.WriteString(" multihop;\n")
b.WriteString(" passive;\n")
b.WriteString("}\n")
return b.String(), nil
}
// BGPPeerIPv4Options describes a single BGP session (BIRD 2, IPv4 AF).
type BGPPeerIPv4Options struct {
ProtocolName string // e.g. evobgp_peer_uplink
LocalIP string // e.g. 192.0.2.1
LocalASN uint32
NeighborIP string
NeighborASN uint32
// ExportFilter is a filter name, or empty for "export all".
ExportFilter string
// ImportFilter is a filter name, or empty for "import all".
ImportFilter string
}
// RenderProtocolBGPIPv4 renders a protocol bgp { … ipv4 { … } } block.
func RenderProtocolBGPIPv4(opts BGPPeerIPv4Options) (string, error) {
if strings.TrimSpace(opts.ProtocolName) == "" {
return "", fmt.Errorf("birdfmt: protocol name is required")
}
if strings.TrimSpace(opts.LocalIP) == "" || strings.TrimSpace(opts.NeighborIP) == "" {
return "", fmt.Errorf("birdfmt: local and neighbor addresses are required")
}
if opts.LocalASN == 0 || opts.NeighborASN == 0 {
return "", fmt.Errorf("birdfmt: AS numbers must be non-zero")
}
imp := "all"
if strings.TrimSpace(opts.ImportFilter) != "" {
imp = "filter " + strings.TrimSpace(opts.ImportFilter)
}
exp := "all"
if strings.TrimSpace(opts.ExportFilter) != "" {
exp = "filter " + strings.TrimSpace(opts.ExportFilter)
}
var b strings.Builder
b.WriteString("protocol bgp ")
b.WriteString(strings.TrimSpace(opts.ProtocolName))
b.WriteString(" {\n")
b.WriteString(" local ")
b.WriteString(strings.TrimSpace(opts.LocalIP))
fmt.Fprintf(&b, " as %d;\n", opts.LocalASN)
b.WriteString(" neighbor ")
b.WriteString(strings.TrimSpace(opts.NeighborIP))
fmt.Fprintf(&b, " as %d;\n", opts.NeighborASN)
b.WriteString(" ipv4 {\n")
b.WriteString(" import ")
b.WriteString(imp)
b.WriteString(";\n")
b.WriteString(" export ")
b.WriteString(exp)
b.WriteString(";\n")
b.WriteString(" };\n")
b.WriteString("}\n")
return b.String(), nil
}
// BGPPeerIPv6Options describes a BGP session for the IPv6 AF.
type BGPPeerIPv6Options struct {
ProtocolName string
LocalIP string
LocalASN uint32
NeighborIP string
NeighborASN uint32
ExportFilter string
ImportFilter string
}
// RenderProtocolBGPIPv6 renders a protocol bgp block with ipv6 { import/export }.
func RenderProtocolBGPIPv6(opts BGPPeerIPv6Options) (string, error) {
if strings.TrimSpace(opts.ProtocolName) == "" {
return "", fmt.Errorf("birdfmt: protocol name is required")
}
if strings.TrimSpace(opts.LocalIP) == "" || strings.TrimSpace(opts.NeighborIP) == "" {
return "", fmt.Errorf("birdfmt: local and neighbor addresses are required")
}
if opts.LocalASN == 0 || opts.NeighborASN == 0 {
return "", fmt.Errorf("birdfmt: AS numbers must be non-zero")
}
imp := "all"
if strings.TrimSpace(opts.ImportFilter) != "" {
imp = "filter " + strings.TrimSpace(opts.ImportFilter)
}
exp := "all"
if strings.TrimSpace(opts.ExportFilter) != "" {
exp = "filter " + strings.TrimSpace(opts.ExportFilter)
}
var b strings.Builder
b.WriteString("protocol bgp ")
b.WriteString(strings.TrimSpace(opts.ProtocolName))
b.WriteString(" {\n")
b.WriteString(" local ")
b.WriteString(strings.TrimSpace(opts.LocalIP))
fmt.Fprintf(&b, " as %d;\n", opts.LocalASN)
b.WriteString(" neighbor ")
b.WriteString(strings.TrimSpace(opts.NeighborIP))
fmt.Fprintf(&b, " as %d;\n", opts.NeighborASN)
b.WriteString(" ipv6 {\n")
b.WriteString(" import ")
b.WriteString(imp)
b.WriteString(";\n")
b.WriteString(" export ")
b.WriteString(exp)
b.WriteString(";\n")
b.WriteString(" };\n")
b.WriteString("}\n")
return b.String(), nil
}