package birdfmt import ( "net/netip" "sort" "strings" ) // StaticRoute is one static route line in BIRD (optionally with a per-route body). type StaticRoute struct { Prefix netip.Prefix RouteBody string // optional lines inside `route P unreachable { ... }` } // RenderStaticIPv4Routes renders a BIRD 2 protocol static block for IPv4. func RenderStaticIPv4Routes(protocolName string, routes []StaticRoute) string { if protocolName == "" { protocolName = "evobgp_prefixes_v4" } type key struct { p string b string } uniq := make(map[key]StaticRoute) for _, r := range routes { if !r.Prefix.Addr().Is4() { continue } p := r.Prefix.Masked() body := strings.TrimSpace(r.RouteBody) uniq[key{p.String(), body}] = StaticRoute{Prefix: p, RouteBody: body} } keys := make([]key, 0, len(uniq)) for k := range uniq { keys = append(keys, k) } sort.Slice(keys, func(i, j int) bool { if keys[i].p != keys[j].p { return keys[i].p < keys[j].p } return keys[i].b < keys[j].b }) var b strings.Builder b.WriteString("protocol static ") b.WriteString(protocolName) b.WriteString(" {\n ipv4;\n") for _, k := range keys { r := uniq[k] p := r.Prefix.String() if r.RouteBody == "" { b.WriteString(" route ") b.WriteString(p) b.WriteString(" unreachable;\n") continue } b.WriteString(" route ") b.WriteString(p) b.WriteString(" unreachable {\n") for _, line := range strings.Split(r.RouteBody, "\n") { line = strings.TrimRight(line, "\r") if strings.TrimSpace(line) == "" { continue } if !strings.HasPrefix(line, " ") && !strings.HasPrefix(line, "\t") { b.WriteString(" ") } b.WriteString(line) b.WriteByte('\n') } b.WriteString(" };\n") } b.WriteString("}\n") return b.String() } // RenderStaticIPv6Routes renders a BIRD 2 protocol static block for IPv6. func RenderStaticIPv6Routes(protocolName string, routes []StaticRoute) string { if protocolName == "" { protocolName = "evobgp_prefixes_v6" } type key struct { p string b string } uniq := make(map[key]StaticRoute) for _, r := range routes { if !r.Prefix.Addr().Is6() { continue } p := r.Prefix.Masked() body := strings.TrimSpace(r.RouteBody) uniq[key{p.String(), body}] = StaticRoute{Prefix: p, RouteBody: body} } keys := make([]key, 0, len(uniq)) for k := range uniq { keys = append(keys, k) } sort.Slice(keys, func(i, j int) bool { if keys[i].p != keys[j].p { return keys[i].p < keys[j].p } return keys[i].b < keys[j].b }) var b strings.Builder b.WriteString("protocol static ") b.WriteString(protocolName) b.WriteString(" {\n ipv6;\n") for _, k := range keys { r := uniq[k] p := r.Prefix.String() if r.RouteBody == "" { b.WriteString(" route ") b.WriteString(p) b.WriteString(" unreachable;\n") continue } b.WriteString(" route ") b.WriteString(p) b.WriteString(" unreachable {\n") for _, line := range strings.Split(r.RouteBody, "\n") { line = strings.TrimRight(line, "\r") if strings.TrimSpace(line) == "" { continue } if !strings.HasPrefix(line, " ") && !strings.HasPrefix(line, "\t") { b.WriteString(" ") } b.WriteString(line) b.WriteByte('\n') } b.WriteString(" };\n") } b.WriteString("}\n") return b.String() }