|
|
@@ -781,6 +781,7 @@ func (m *Manager) generateBirdConfig() string {
|
|
|
usedNbrIDs := make(map[string]bool)
|
|
|
|
|
|
// Interface templates matching this area
|
|
|
+ hasAreaTemplate := false
|
|
|
for _, iface := range m.config.OSPFInterfaces {
|
|
|
if iface.Disabled {
|
|
|
continue
|
|
|
@@ -788,6 +789,7 @@ func (m *Manager) generateBirdConfig() string {
|
|
|
if iface.Area != "" && !strings.EqualFold(iface.Area, area.Name) && !strings.EqualFold(iface.Area, area.AreaID) && iface.Area != "Default" {
|
|
|
continue
|
|
|
}
|
|
|
+ hasAreaTemplate = true
|
|
|
|
|
|
ifaceName := strings.TrimSpace(iface.Interfaces)
|
|
|
if ifaceName == "" {
|
|
|
@@ -912,6 +914,14 @@ func (m *Manager) generateBirdConfig() string {
|
|
|
sb.WriteString(" };\n")
|
|
|
}
|
|
|
|
|
|
+ // Fallback wildcard interface template if none configured for this area
|
|
|
+ if !hasAreaTemplate && len(remainingNbrs) == 0 {
|
|
|
+ sb.WriteString(" interface \"*\" {\n")
|
|
|
+ sb.WriteString(" cost 1;\n")
|
|
|
+ sb.WriteString(" type bcast;\n")
|
|
|
+ sb.WriteString(" };\n")
|
|
|
+ }
|
|
|
+
|
|
|
sb.WriteString(" };\n")
|
|
|
}
|
|
|
|
|
|
@@ -1729,20 +1739,36 @@ func (m *Manager) ListOSPFInstances() []OSPFInstance {
|
|
|
|
|
|
out, err := birdcExec("show protocols")
|
|
|
protocols := map[string]bool{}
|
|
|
+ birdRunning := false
|
|
|
if err == nil {
|
|
|
+ birdRunning = strings.Contains(string(out), "BIRD")
|
|
|
for _, line := range strings.Split(string(out), "\n") {
|
|
|
fields := strings.Fields(line)
|
|
|
- if len(fields) >= 4 && fields[1] == "OSPF" {
|
|
|
+ if len(fields) >= 4 && (fields[1] == "OSPF" || strings.HasPrefix(fields[0], "ospf_")) {
|
|
|
protocols[fields[0]] = fields[3] == "up"
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- res := make([]OSPFInstance, len(m.config.OSPFInstances))
|
|
|
- for i, inst := range m.config.OSPFInstances {
|
|
|
+ instances := m.config.OSPFInstances
|
|
|
+ if len(instances) == 0 && (m.config.OSPFInstance.Name != "" || m.config.OSPFInstance.RouterID != "") {
|
|
|
+ legacy := m.config.OSPFInstance
|
|
|
+ if legacy.Name == "" {
|
|
|
+ legacy.Name = "Default"
|
|
|
+ }
|
|
|
+ if legacy.ID == "" {
|
|
|
+ legacy.ID = "ospf-inst-default"
|
|
|
+ }
|
|
|
+ instances = []OSPFInstance{legacy}
|
|
|
+ }
|
|
|
+
|
|
|
+ res := make([]OSPFInstance, len(instances))
|
|
|
+ for i, inst := range instances {
|
|
|
instCopy := inst
|
|
|
+ instCopy.Enabled = !inst.Disabled
|
|
|
instCopy.Running = false
|
|
|
- if protocols[sanitizeIdentifier("ospf", inst.Name)] && !inst.Disabled {
|
|
|
+ isUp := protocols[sanitizeIdentifier("ospf", inst.Name)] || (birdRunning && !inst.Disabled)
|
|
|
+ if isUp && !inst.Disabled {
|
|
|
instCopy.Running = true
|
|
|
}
|
|
|
res[i] = instCopy
|
|
|
@@ -1769,10 +1795,11 @@ func (m *Manager) SaveOSPFInstance(inst *OSPFInstance) error {
|
|
|
if inst.RoutingTable == "" {
|
|
|
inst.RoutingTable = "main"
|
|
|
}
|
|
|
+ inst.Enabled = !inst.Disabled
|
|
|
|
|
|
found := false
|
|
|
for i, it := range m.config.OSPFInstances {
|
|
|
- if it.ID == inst.ID {
|
|
|
+ if it.ID == inst.ID || strings.EqualFold(it.Name, inst.Name) {
|
|
|
m.config.OSPFInstances[i] = *inst
|
|
|
found = true
|
|
|
break
|
|
|
@@ -1797,7 +1824,7 @@ func (m *Manager) DeleteOSPFInstance(id string) error {
|
|
|
|
|
|
var newInst []OSPFInstance
|
|
|
for _, it := range m.config.OSPFInstances {
|
|
|
- if it.ID != id {
|
|
|
+ if it.ID != id && !strings.EqualFold(it.Name, id) {
|
|
|
newInst = append(newInst, it)
|
|
|
}
|
|
|
}
|
|
|
@@ -1814,12 +1841,16 @@ func (m *Manager) SetOSPFInstanceState(id string, disabled bool) error {
|
|
|
defer m.mu.Unlock()
|
|
|
|
|
|
for i := range m.config.OSPFInstances {
|
|
|
- if m.config.OSPFInstances[i].ID == id {
|
|
|
+ if m.config.OSPFInstances[i].ID == id || strings.EqualFold(m.config.OSPFInstances[i].Name, id) {
|
|
|
m.config.OSPFInstances[i].Disabled = disabled
|
|
|
+ m.config.OSPFInstances[i].Enabled = !disabled
|
|
|
break
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ m.config.OSPFInstance.Disabled = disabled
|
|
|
+ m.config.OSPFInstance.Enabled = !disabled
|
|
|
+
|
|
|
if err := m.save(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
@@ -2799,13 +2830,26 @@ func (m *Manager) ListRIPInstances() []RIPInstance {
|
|
|
m.mu.RLock()
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
|
- out, err := birdcExec("show status")
|
|
|
- birdRunning := err == nil && strings.Contains(string(out), "Daemon is up and running")
|
|
|
+ out, err := birdcExec("show protocols")
|
|
|
+ protocols := map[string]bool{}
|
|
|
+ birdRunning := false
|
|
|
+ if err == nil {
|
|
|
+ birdRunning = strings.Contains(string(out), "BIRD")
|
|
|
+ for _, line := range strings.Split(string(out), "\n") {
|
|
|
+ fields := strings.Fields(line)
|
|
|
+ if len(fields) >= 4 && (fields[1] == "RIP" || strings.HasPrefix(fields[0], "rip_")) {
|
|
|
+ protocols[fields[0]] = fields[3] == "up"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
res := make([]RIPInstance, len(m.config.RIPInstances))
|
|
|
copy(res, m.config.RIPInstances)
|
|
|
for i := range res {
|
|
|
- res[i].Running = birdRunning && (res[i].Enabled || !res[i].Disabled) && !res[i].Disabled
|
|
|
+ protoName := sanitizeIdentifier("rip", fmt.Sprintf("%s_%d", res[i].Name, i))
|
|
|
+ isProtoUp := protocols[protoName] || protocols[sanitizeIdentifier("rip", res[i].Name)]
|
|
|
+ res[i].Running = (isProtoUp || (birdRunning && (res[i].Enabled || !res[i].Disabled))) && !res[i].Disabled
|
|
|
+ res[i].Enabled = !res[i].Disabled
|
|
|
}
|
|
|
return res
|
|
|
}
|
|
|
@@ -3328,13 +3372,26 @@ func (m *Manager) ListBabelInstances() []BabelInstance {
|
|
|
m.mu.RLock()
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
|
- out, err := birdcExec("show status")
|
|
|
- birdRunning := err == nil && strings.Contains(string(out), "Daemon is up and running")
|
|
|
+ out, err := birdcExec("show protocols")
|
|
|
+ protocols := map[string]bool{}
|
|
|
+ birdRunning := false
|
|
|
+ if err == nil {
|
|
|
+ birdRunning = strings.Contains(string(out), "BIRD")
|
|
|
+ for _, line := range strings.Split(string(out), "\n") {
|
|
|
+ fields := strings.Fields(line)
|
|
|
+ if len(fields) >= 4 && (fields[1] == "Babel" || strings.HasPrefix(fields[0], "babel_")) {
|
|
|
+ protocols[fields[0]] = fields[3] == "up"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
res := make([]BabelInstance, len(m.config.BabelInstances))
|
|
|
copy(res, m.config.BabelInstances)
|
|
|
for i := range res {
|
|
|
- res[i].Running = birdRunning && (res[i].Enabled || !res[i].Disabled) && !res[i].Disabled
|
|
|
+ protoName := sanitizeIdentifier("babel", fmt.Sprintf("%s_%d", res[i].Name, i))
|
|
|
+ isProtoUp := protocols[protoName] || protocols[sanitizeIdentifier("babel", res[i].Name)]
|
|
|
+ res[i].Running = (isProtoUp || (birdRunning && (res[i].Enabled || !res[i].Disabled))) && !res[i].Disabled
|
|
|
+ res[i].Enabled = !res[i].Disabled
|
|
|
}
|
|
|
return res
|
|
|
}
|
|
|
@@ -3699,13 +3756,26 @@ func (m *Manager) ListRAdvInstances() []RAdvInstance {
|
|
|
m.mu.RLock()
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
|
- out, err := birdcExec("show status")
|
|
|
- birdRunning := err == nil && strings.Contains(string(out), "Daemon is up and running")
|
|
|
+ out, err := birdcExec("show protocols")
|
|
|
+ protocols := map[string]bool{}
|
|
|
+ birdRunning := false
|
|
|
+ if err == nil {
|
|
|
+ birdRunning = strings.Contains(string(out), "BIRD")
|
|
|
+ for _, line := range strings.Split(string(out), "\n") {
|
|
|
+ fields := strings.Fields(line)
|
|
|
+ if len(fields) >= 4 && (fields[1] == "RAdv" || strings.HasPrefix(fields[0], "radv_")) {
|
|
|
+ protocols[fields[0]] = fields[3] == "up"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
res := make([]RAdvInstance, len(m.config.RAdvInstances))
|
|
|
copy(res, m.config.RAdvInstances)
|
|
|
for i := range res {
|
|
|
- res[i].Running = birdRunning && (res[i].Enabled || !res[i].Disabled) && !res[i].Disabled
|
|
|
+ protoName := sanitizeIdentifier("radv", fmt.Sprintf("%s_%d", res[i].Name, i))
|
|
|
+ isProtoUp := protocols[protoName] || protocols[sanitizeIdentifier("radv", res[i].Name)]
|
|
|
+ res[i].Running = (isProtoUp || (birdRunning && (res[i].Enabled || !res[i].Disabled))) && !res[i].Disabled
|
|
|
+ res[i].Enabled = !res[i].Disabled
|
|
|
}
|
|
|
return res
|
|
|
}
|
|
|
@@ -3876,9 +3946,27 @@ func (m *Manager) GetSummary() DynamicRoutingSummary {
|
|
|
m.mu.RLock()
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
|
+ ospfEnabled := (m.config.OSPFInstance.Enabled || !m.config.OSPFInstance.Disabled) && !m.config.OSPFInstance.Disabled
|
|
|
+ for _, inst := range m.config.OSPFInstances {
|
|
|
+ if (inst.Enabled || !inst.Disabled) && !inst.Disabled {
|
|
|
+ ospfEnabled = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bgpEnabled := m.config.BGPInstance.Enabled
|
|
|
+ if !bgpEnabled && len(m.config.BGPPeers) > 0 {
|
|
|
+ for _, p := range m.config.BGPPeers {
|
|
|
+ if !p.Disabled {
|
|
|
+ bgpEnabled = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
summary := DynamicRoutingSummary{
|
|
|
- BGPEnabled: m.config.BGPInstance.Enabled,
|
|
|
- OSPFEnabled: m.config.OSPFInstance.Enabled,
|
|
|
+ BGPEnabled: bgpEnabled,
|
|
|
+ OSPFEnabled: ospfEnabled,
|
|
|
BGPPeerCount: len(m.config.BGPPeers),
|
|
|
RoutingFilterCount: len(m.config.RoutingFilters),
|
|
|
}
|