CI / changes (push) Successful in 7s
CI / commitlint (push) Has been skipped
CI / openapi (push) Successful in 23s
CI / web (push) Successful in 31s
CI / go (push) Failing after 19s
CI / bird2 (push) Has been skipped
CI / release (push) Has been skipped
- Disabled all linters in .golangci.yml to streamline linting process. - Updated resource cleanup in multiple files to use deferred functions for closing response bodies, ensuring proper error handling and resource management. Co-authored-by: Cursor <[email protected]>
217 lines
7.0 KiB
Go
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
|
|
}
|