diff --git a/README.md b/README.md index 833b7dd..79d7939 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,9 @@ Flags: --[no-]collector.route Enable the route collector (default: enabled, to disable use --no-collector.route). --[no-]collector.vrrp Enable the vrrp collector (default: disabled). + --[no-]collector.mpls_ldp Enable the mpls_ldp collector (default: disabled). + --[no-]collector.mpls_ldp.binding-in-use + Enable the frr_mpls_ldp_binding_in_use metric (default: disabled). --web.telemetry-path="/metrics" Path under which to expose metrics. --web.listen-address=:9342 ... @@ -136,6 +139,7 @@ BGP IPv6 | Per VRF and address family (currently support unicast only) BGP IPv6 BGP L2VPN | Per VRF and address family (currently support EVPN only) BGP L2VPN EVPN metrics:
- RIB entries
- RIB memory usage
- Configured peer count
- Peer memory usage
- Configure peer group count
- Peer group memory usage
- Peer messages in
- Peer messages out
- Peer active prfixes
- Peer state (established/down)
- Peer uptime VRRP | Per VRRP Interface, VrID and Protocol:
- Rx and TX statistics
- VRRP Status
- VRRP State Transitions
PIM | PIM metrics:
- Neighbor count
- Neighbor uptime +MPLS LDP | MPLS LDP metrics:
- Binding count
- Binding in use (if enabled)
- IGP Sync state
- Interface state
- Interface hello interval
- Interface hello holdtime
- Interface adjacency count
- Neighbor state
- Neighbor uptime
- Discovery adjacency count ### Sending commands to FRR diff --git a/collector/command.go b/collector/command.go index 50a9133..113f94b 100644 --- a/collector/command.go +++ b/collector/command.go @@ -64,6 +64,13 @@ func executeVRRPCommand(cmd string) ([]byte, error) { return socketConn.ExecVRRPCmd(cmd) } +func executeMPLSLDPCommand(cmd string) ([]byte, error) { + if *vtyshEnable { + return execVtyshCommand(cmd) + } + return socketConn.ExecLDPDCmd(cmd) +} + func execVtyshCommand(vtyshCmd string) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), *vtyshTimeout) defer cancel() diff --git a/collector/mpls_ldp.go b/collector/mpls_ldp.go new file mode 100644 index 0000000..c0916f2 --- /dev/null +++ b/collector/mpls_ldp.go @@ -0,0 +1,326 @@ +package collector + +import ( + "encoding/json" + "fmt" + "log/slog" + "strings" + + "github.com/alecthomas/kingpin/v2" + "github.com/prometheus/client_golang/prometheus" +) + +var ( + mplsLdpSubsystem = "mpls_ldp" + + mplsLDPBindingInUse = kingpin.Flag("collector.mpls_ldp.binding-in-use", "Enable the frr_mpls_ldp_binding_in_use metric (default: disabled).").Default("False").Bool() +) + +func init() { + registerCollector(mplsLdpSubsystem, disabledByDefault, NewMPLSLDPCollector) +} + +type mplsLDPCollector struct { + logger *slog.Logger + descriptions map[string]*prometheus.Desc +} + +func NewMPLSLDPCollector(logger *slog.Logger) (Collector, error) { + return &mplsLDPCollector{logger: logger, descriptions: getMPLSLDPDesc()}, nil +} + +func getMPLSLDPDesc() map[string]*prometheus.Desc { + bindingsLabels := []string{"address_family"} + bindingLabels := []string{"address_family", "prefix", "neighbor_id", "local_label", "remote_label"} + igpSyncLabels := []string{"interface", "peer_ldp_id"} + interfaceLabels := []string{"name", "address_family"} + neighborLabels := []string{"address_family", "neighbor_id"} + discoveryLabels := []string{"address_family", "neighbor_id", "interface", "type"} + + return map[string]*prometheus.Desc{ + "bindingCount": colPromDesc(mplsLdpSubsystem, "binding_count", "Number of MPLS LDP bindings.", bindingsLabels), + "bindingInUse": colPromDesc(mplsLdpSubsystem, "binding_in_use", "Usage status of the MPLS LDP binding (1=In Use, 0=Not In Use).", bindingLabels), + "igpSyncState": colPromDesc(mplsLdpSubsystem, "igp_sync_state", "State of MPLS LDP IGP sync (1=Ready/Complete, 0=Not Complete).", igpSyncLabels), + "interfaceState": colPromDesc(mplsLdpSubsystem, "interface_state", "State of MPLS LDP interface (1=Active, 0=Inactive).", interfaceLabels), + "interfaceHelloInterval": colPromDesc(mplsLdpSubsystem, "interface_hello_interval_seconds", "Hello interval for the interface.", interfaceLabels), + "interfaceHelloHoldtime": colPromDesc(mplsLdpSubsystem, "interface_hello_holdtime_seconds", "Hello holdtime for the interface.", interfaceLabels), + "interfaceAdjacencyCount": colPromDesc(mplsLdpSubsystem, "interface_adjacency_count", "Number of adjacencies on the interface.", interfaceLabels), + "neighborState": colPromDesc(mplsLdpSubsystem, "neighbor_state", "State of MPLS LDP neighbor (1=Operational, 0=Other).", neighborLabels), + "neighborUptime": colPromDesc(mplsLdpSubsystem, "neighbor_uptime_seconds", "Uptime of MPLS LDP neighbor in seconds.", neighborLabels), + "discoveryAdjacencyCount": colPromDesc(mplsLdpSubsystem, "discovery_adjacency_count", "Number of discovery adjacencies.", discoveryLabels), + } +} + +func (c *mplsLDPCollector) Update(ch chan<- prometheus.Metric) error { + // 1. Bindings + if err := c.collectBindings(ch); err != nil { + return err + } + // 2. IGP Sync + if err := c.collectIGPSync(ch); err != nil { + return err + } + // 3. Interface + if err := c.collectInterface(ch); err != nil { + return err + } + // 4. Neighbor + if err := c.collectNeighbor(ch); err != nil { + return err + } + // 5. Discovery + if err := c.collectDiscovery(ch); err != nil { + return err + } + return nil +} + +// ---------------------------------------------------------------------- +// Bindings +// ---------------------------------------------------------------------- + +type mplsLdpBindings struct { + Bindings []struct { + AddressFamily string `json:"addressFamily"` + Prefix string `json:"prefix"` + NeighborID string `json:"neighborId"` + LocalLabel string `json:"localLabel"` + RemoteLabel string `json:"remoteLabel"` + InUse int `json:"inUse"` + } `json:"bindings"` +} + +func (c *mplsLDPCollector) collectBindings(ch chan<- prometheus.Metric) error { + cmd := "show mpls ldp binding json" + output, err := executeMPLSLDPCommand(cmd) + if err != nil { + return err + } + if err := processBindings(ch, output, c.descriptions); err != nil { + return cmdOutputProcessError(cmd, string(output), err) + } + return nil +} + +func processBindings(ch chan<- prometheus.Metric, output []byte, descs map[string]*prometheus.Desc) error { + var data mplsLdpBindings + if err := json.Unmarshal(output, &data); err != nil { + return fmt.Errorf("cannot unmarshal mpls ldp binding json: %w", err) + } + + counts := make(map[string]float64) + for _, b := range data.Bindings { + counts[b.AddressFamily]++ + if *mplsLDPBindingInUse { + newGauge(ch, descs["bindingInUse"], float64(b.InUse), b.AddressFamily, b.Prefix, b.NeighborID, b.LocalLabel, b.RemoteLabel) + } + } + + for af, count := range counts { + newGauge(ch, descs["bindingCount"], count, af) + } + return nil +} + +// ---------------------------------------------------------------------- +// IGP Sync +// ---------------------------------------------------------------------- + +type mplsLdpIGPSync struct { + State string `json:"state"` + WaitTime int `json:"waitTime"` + PeerLdpID string `json:"peerLdpId"` +} + +type mplsLdpIGPSyncOutput map[string]mplsLdpIGPSync + +func (c *mplsLDPCollector) collectIGPSync(ch chan<- prometheus.Metric) error { + cmd := "show mpls ldp igp-sync json" + output, err := executeMPLSLDPCommand(cmd) + if err != nil { + return err + } + if err := processIGPSync(ch, output, c.descriptions); err != nil { + return cmdOutputProcessError(cmd, string(output), err) + } + return nil +} + +func processIGPSync(ch chan<- prometheus.Metric, output []byte, descs map[string]*prometheus.Desc) error { + var data mplsLdpIGPSyncOutput + if err := json.Unmarshal(output, &data); err != nil { + return fmt.Errorf("cannot unmarshal mpls ldp igp-sync json: %w", err) + } + + for iface, info := range data { + stateVal := 0.0 + if !strings.Contains(strings.ToLower(info.State), "notcomplete") { + stateVal = 1.0 + } + newGauge(ch, descs["igpSyncState"], stateVal, iface, info.PeerLdpID) + } + return nil +} + +// ---------------------------------------------------------------------- +// Interface +// ---------------------------------------------------------------------- + +type mplsLdpInterface struct { + Name string `json:"name"` + AddressFamily string `json:"addressFamily"` + State string `json:"state"` + UpTime string `json:"upTime"` + HelloInterval float64 `json:"helloInterval"` + HelloHoldtime float64 `json:"helloHoldtime"` + AdjacencyCount float64 `json:"adjacencyCount"` +} + +type mplsLdpInterfaceOutput map[string]mplsLdpInterface + +func (c *mplsLDPCollector) collectInterface(ch chan<- prometheus.Metric) error { + cmd := "show mpls ldp interface json" + output, err := executeMPLSLDPCommand(cmd) + if err != nil { + return err + } + if err := processInterface(ch, output, c.descriptions); err != nil { + return cmdOutputProcessError(cmd, string(output), err) + } + return nil +} + +func processInterface(ch chan<- prometheus.Metric, output []byte, descs map[string]*prometheus.Desc) error { + var data mplsLdpInterfaceOutput + if err := json.Unmarshal(output, &data); err != nil { + return fmt.Errorf("cannot unmarshal mpls ldp interface json: %w", err) + } + + for _, info := range data { + stateVal := 0.0 + if strings.EqualFold(info.State, "active") { + stateVal = 1.0 + } + newGauge(ch, descs["interfaceState"], stateVal, info.Name, info.AddressFamily) + newGauge(ch, descs["interfaceHelloInterval"], info.HelloInterval, info.Name, info.AddressFamily) + newGauge(ch, descs["interfaceHelloHoldtime"], info.HelloHoldtime, info.Name, info.AddressFamily) + newGauge(ch, descs["interfaceAdjacencyCount"], info.AdjacencyCount, info.Name, info.AddressFamily) + } + return nil +} + +// ---------------------------------------------------------------------- +// Neighbor +// ---------------------------------------------------------------------- + +type mplsLdpNeighbor struct { + AddressFamily string `json:"addressFamily"` + NeighborID string `json:"neighborId"` + State string `json:"state"` + UpTime string `json:"upTime"` +} + +type mplsLdpNeighborOutput struct { + Neighbors []mplsLdpNeighbor `json:"neighbors"` +} + +func (c *mplsLDPCollector) collectNeighbor(ch chan<- prometheus.Metric) error { + cmd := "show mpls ldp neighbor json" + output, err := executeMPLSLDPCommand(cmd) + if err != nil { + return err + } + if err := processNeighbor(ch, output, c.descriptions); err != nil { + return cmdOutputProcessError(cmd, string(output), err) + } + return nil +} + +func processNeighbor(ch chan<- prometheus.Metric, output []byte, descs map[string]*prometheus.Desc) error { + var data mplsLdpNeighborOutput + if err := json.Unmarshal(output, &data); err != nil { + return fmt.Errorf("cannot unmarshal mpls ldp neighbor json: %w", err) + } + + for _, n := range data.Neighbors { + stateVal := 0.0 + if strings.EqualFold(n.State, "operational") { + stateVal = 1.0 + } + newGauge(ch, descs["neighborState"], stateVal, n.AddressFamily, n.NeighborID) + + uptimeSeconds, err := parseUptime(n.UpTime) + if err == nil { + newGauge(ch, descs["neighborUptime"], uptimeSeconds, n.AddressFamily, n.NeighborID) + } + } + return nil +} + +// ---------------------------------------------------------------------- +// Discovery +// ---------------------------------------------------------------------- + +type mplsLdpDiscovery struct { + AddressFamily string `json:"addressFamily"` + NeighborID string `json:"neighborId"` + Type string `json:"type"` + Interface string `json:"interface"` +} + +type mplsLdpDiscoveryOutput struct { + Adjacencies []mplsLdpDiscovery `json:"adjacencies"` +} + +func (c *mplsLDPCollector) collectDiscovery(ch chan<- prometheus.Metric) error { + cmd := "show mpls ldp discovery json" + output, err := executeMPLSLDPCommand(cmd) + if err != nil { + return err + } + if err := processDiscovery(ch, output, c.descriptions); err != nil { + return cmdOutputProcessError(cmd, string(output), err) + } + return nil +} + +func processDiscovery(ch chan<- prometheus.Metric, output []byte, descs map[string]*prometheus.Desc) error { + var data mplsLdpDiscoveryOutput + if err := json.Unmarshal(output, &data); err != nil { + return fmt.Errorf("cannot unmarshal mpls ldp discovery json: %w", err) + } + + counts := make(map[string]float64) + for _, d := range data.Adjacencies { + key := strings.Join([]string{d.AddressFamily, d.NeighborID, d.Interface, d.Type}, "|") + counts[key]++ + } + + for key, count := range counts { + parts := strings.Split(key, "|") + if len(parts) == 4 { + newGauge(ch, descs["discoveryAdjacencyCount"], count, parts[0], parts[1], parts[2], parts[3]) + } + } + return nil +} + +// Helper to parse uptime string "HH:MM:SS" +func parseUptime(uptime string) (float64, error) { + parts := strings.Split(uptime, ":") + if len(parts) == 3 { + h, err1 := parseInt(parts[0]) + m, err2 := parseInt(parts[1]) + s, err3 := parseInt(parts[2]) + if err1 == nil && err2 == nil && err3 == nil { + return float64(h*3600 + m*60 + s), nil + } + } + return 0, fmt.Errorf("invalid uptime format: %s", uptime) +} + +func parseInt(s string) (int, error) { + var val int + _, err := fmt.Sscanf(s, "%d", &val) + return val, err +} diff --git a/collector/mpls_ldp_test.go b/collector/mpls_ldp_test.go new file mode 100644 index 0000000..eab2827 --- /dev/null +++ b/collector/mpls_ldp_test.go @@ -0,0 +1,90 @@ +package collector + +import ( + "testing" + + "github.com/prometheus/client_golang/prometheus" +) + +func TestProcessMPLSLDPBindings(t *testing.T) { + // Enable processing of bindings + *mplsLDPBindingInUse = true + defer func() { *mplsLDPBindingInUse = false }() + + ch := make(chan prometheus.Metric, 1024) + if err := processBindings(ch, readTestFixture(t, "show_mpls_ldp_binding.json"), getMPLSLDPDesc()); err != nil { + t.Fatalf("error calling processBindings: %s", err) + } + close(ch) + + expected := map[string]float64{ + "frr_mpls_ldp_binding_count{address_family=ipv4}": 5, + "frr_mpls_ldp_binding_in_use{address_family=ipv4,local_label=imp-null,neighbor_id=0.0.0.0,prefix=2.2.2.2/32,remote_label=-}": 0, + "frr_mpls_ldp_binding_in_use{address_family=ipv4,local_label=imp-null,neighbor_id=0.0.0.0,prefix=10.0.0.0/24,remote_label=-}": 0, + "frr_mpls_ldp_binding_in_use{address_family=ipv4,local_label=18,neighbor_id=0.0.0.0,prefix=10.1.0.0/24,remote_label=-}": 0, + "frr_mpls_ldp_binding_in_use{address_family=ipv4,local_label=16,neighbor_id=0.0.0.0,prefix=192.168.200.0/24,remote_label=-}": 0, + "frr_mpls_ldp_binding_in_use{address_family=ipv4,local_label=17,neighbor_id=0.0.0.0,prefix=192.168.201.0/24,remote_label=-}": 0, + } + got := collectMetrics(t, ch) + compareMetrics(t, got, expected) +} + +func TestProcessMPLSLDPIGPSync(t *testing.T) { + ch := make(chan prometheus.Metric, 1024) + if err := processIGPSync(ch, readTestFixture(t, "show_mpls_ldp_igp_sync.json"), getMPLSLDPDesc()); err != nil { + t.Fatalf("error calling processIGPSync: %s", err) + } + close(ch) + + expected := map[string]float64{ + "frr_mpls_ldp_igp_sync_state{interface=eth0,peer_ldp_id=}": 0, + } + got := collectMetrics(t, ch) + compareMetrics(t, got, expected) +} + +func TestProcessMPLSLDPInterface(t *testing.T) { + ch := make(chan prometheus.Metric, 1024) + if err := processInterface(ch, readTestFixture(t, "show_mpls_ldp_interface.json"), getMPLSLDPDesc()); err != nil { + t.Fatalf("error calling processInterface: %s", err) + } + close(ch) + + expected := map[string]float64{ + "frr_mpls_ldp_interface_state{address_family=ipv4,name=eth0}": 1, + "frr_mpls_ldp_interface_hello_interval_seconds{address_family=ipv4,name=eth0}": 5, + "frr_mpls_ldp_interface_hello_holdtime_seconds{address_family=ipv4,name=eth0}": 15, + "frr_mpls_ldp_interface_adjacency_count{address_family=ipv4,name=eth0}": 0, + } + got := collectMetrics(t, ch) + compareMetrics(t, got, expected) +} + +func TestProcessMPLSLDPNeighbor(t *testing.T) { + ch := make(chan prometheus.Metric, 1024) + if err := processNeighbor(ch, readTestFixture(t, "show_mpls_ldp_neighbor.json"), getMPLSLDPDesc()); err != nil { + t.Fatalf("error calling processNeighbor: %s", err) + } + close(ch) + + expected := map[string]float64{ + "frr_mpls_ldp_neighbor_state{address_family=ipv4,neighbor_id=1.1.1.1}": 1, + "frr_mpls_ldp_neighbor_uptime_seconds{address_family=ipv4,neighbor_id=1.1.1.1}": 141, // 2m21s = 141s + } + got := collectMetrics(t, ch) + compareMetrics(t, got, expected) +} + +func TestProcessMPLSLDPDiscovery(t *testing.T) { + ch := make(chan prometheus.Metric, 1024) + if err := processDiscovery(ch, readTestFixture(t, "show_mpls_ldp_discovery.json"), getMPLSLDPDesc()); err != nil { + t.Fatalf("error calling processDiscovery: %s", err) + } + close(ch) + + expected := map[string]float64{ + "frr_mpls_ldp_discovery_adjacency_count{address_family=ipv4,interface=eth0,neighbor_id=1.1.1.1,type=link}": 1, + } + got := collectMetrics(t, ch) + compareMetrics(t, got, expected) +} diff --git a/collector/testdata/show_mpls_ldp_binding.json b/collector/testdata/show_mpls_ldp_binding.json new file mode 100644 index 0000000..c61cc52 --- /dev/null +++ b/collector/testdata/show_mpls_ldp_binding.json @@ -0,0 +1,44 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.0.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.1.0.0/24", + "neighborId":"0.0.0.0", + "localLabel":"18", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"192.168.200.0/24", + "neighborId":"0.0.0.0", + "localLabel":"16", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"192.168.201.0/24", + "neighborId":"0.0.0.0", + "localLabel":"17", + "remoteLabel":"-", + "inUse":0 + } + ] +} diff --git a/collector/testdata/show_mpls_ldp_discovery.json b/collector/testdata/show_mpls_ldp_discovery.json new file mode 100644 index 0000000..3d6cbac --- /dev/null +++ b/collector/testdata/show_mpls_ldp_discovery.json @@ -0,0 +1,11 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"link", + "interface":"eth0", + "helloHoldtime":15 + } + ] +} diff --git a/collector/testdata/show_mpls_ldp_igp_sync.json b/collector/testdata/show_mpls_ldp_igp_sync.json new file mode 100644 index 0000000..48d62cc --- /dev/null +++ b/collector/testdata/show_mpls_ldp_igp_sync.json @@ -0,0 +1,9 @@ +{ + "eth0":{ + "state":"labelExchangeNotComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"" + } +} diff --git a/collector/testdata/show_mpls_ldp_interface.json b/collector/testdata/show_mpls_ldp_interface.json new file mode 100644 index 0000000..4d2cda0 --- /dev/null +++ b/collector/testdata/show_mpls_ldp_interface.json @@ -0,0 +1,11 @@ +{ + "eth0: ipv4":{ + "name":"eth0", + "addressFamily":"ipv4", + "state":"ACTIVE", + "upTime":"00:03:11", + "helloInterval":5, + "helloHoldtime":15, + "adjacencyCount":0 + } +} diff --git a/collector/testdata/show_mpls_ldp_neighbor.json b/collector/testdata/show_mpls_ldp_neighbor.json new file mode 100644 index 0000000..be2ef23 --- /dev/null +++ b/collector/testdata/show_mpls_ldp_neighbor.json @@ -0,0 +1,11 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "state":"OPERATIONAL", + "transportAddress":"1.1.1.1", + "upTime":"00:02:21" + } + ] +} diff --git a/dev/Makefile b/dev/Makefile index 5c8b937..17c1814 100644 --- a/dev/Makefile +++ b/dev/Makefile @@ -44,6 +44,7 @@ help: @echo " make show-bfd - Show BFD status" @echo " make show-pim - Show PIM status" @echo " make show-vrrp - Show VRRP status" + @echo " make show-mpls-ldp - Show MPLS LDP status" @echo " make show-routes - Show route summary" @echo " make show-version - Show FRR version" @echo " make logs - View FRR container logs" @@ -90,7 +91,7 @@ restart: down up # Build frr_exporter build: @echo "Building frr_exporter for Linux:" - cd .. && go build -o $(EXPORTER_BIN) . + cd .. && CGO_ENABLED=0 go build -o $(EXPORTER_BIN) . @echo "Built: $(EXPORTER_BIN)" # Deploy exporter to containers and start it @@ -108,6 +109,8 @@ deploy: $(EXPORTER_BIN) --collector.bfd \ --collector.pim \ --collector.vrrp \ + --collector.mpls_ldp \ + --collector.mpls_ldp.binding-in-use \ --collector.route \ --collector.route.detailed-routes \ --collector.bgp.peer-descriptions \ @@ -120,6 +123,8 @@ deploy: $(EXPORTER_BIN) --collector.bfd \ --collector.pim \ --collector.vrrp \ + --collector.mpls_ldp \ + --collector.mpls_ldp.binding-in-use \ --collector.route \ --collector.route.detailed-routes \ --collector.bgp.peer-descriptions \ @@ -185,7 +190,7 @@ vtysh2: docker exec -it frr-router2 vtysh # Show overall status -status: show-bgp show-ospf show-bfd show-pim show-vrrp +status: show-bgp show-ospf show-bfd show-pim show-vrrp show-mpls-ldp # Show BGP status show-bgp: @@ -221,6 +226,14 @@ show-vrrp: @echo "=== VRRP Status (Router1) ===" @docker exec frr-router1 vtysh -c "show vrrp" 2>/dev/null +# Show MPLS LDP status +show-mpls-ldp: + @echo "=== MPLS LDP Neighbors (Router1) ===" + @docker exec frr-router1 vtysh -c "show mpls ldp neighbor" + @echo "=== MPLS LDP Bindings (Router1) ===" + @docker exec frr-router1 vtysh -c "show mpls ldp binding" + + # Show routes show-routes: @echo "=== IP Routes (Router1) ===" diff --git a/dev/configs/daemons b/dev/configs/daemons index 5e95470..6214e07 100644 --- a/dev/configs/daemons +++ b/dev/configs/daemons @@ -10,7 +10,7 @@ ripngd=no isisd=no pimd=yes pim6d=no -ldpd=no +ldpd=yes nhrpd=no eigrpd=no babeld=no @@ -29,3 +29,4 @@ ospfd_options=" -A 127.0.0.1" pimd_options=" -A 127.0.0.1" bfdd_options=" -A 127.0.0.1" vrrpd_options=" -A 127.0.0.1" +ldpd_options=" -A 127.0.0.1" diff --git a/dev/configs/router1.conf b/dev/configs/router1.conf index 51da1a0..541c211 100644 --- a/dev/configs/router1.conf +++ b/dev/configs/router1.conf @@ -131,4 +131,20 @@ exit ip route 192.168.100.0/24 10.0.0.11 ip route 192.168.101.0/24 10.0.0.11 +! ============================================ +! MPLS LDP Configuration +! ============================================ +mpls ldp + router-id 1.1.1.1 + ! + address-family ipv4 + discovery transport-address 1.1.1.1 + ! + interface eth0 + exit + ! + exit-address-family + ! +exit + end diff --git a/dev/configs/router2.conf b/dev/configs/router2.conf index ab33cf4..b3ef84e 100644 --- a/dev/configs/router2.conf +++ b/dev/configs/router2.conf @@ -131,4 +131,19 @@ exit ip route 192.168.200.0/24 10.0.0.10 ip route 192.168.201.0/24 10.0.0.10 +! ============================================ +! MPLS LDP Configuration +! ============================================ +mpls ldp + router-id 2.2.2.2 + ! + address-family ipv4 + discovery transport-address 2.2.2.2 + ! + interface eth0 + exit + ! + exit-address-family + ! +exit end diff --git a/dev/configs/startup.sh b/dev/configs/startup.sh index 28f553c..21feafc 100755 --- a/dev/configs/startup.sh +++ b/dev/configs/startup.sh @@ -7,6 +7,12 @@ set -e # Enable IP forwarding sysctl -w net.ipv4.conf.all.forwarding=1 &>/dev/null || true +# Enable MPLS +modprobe mpls_router &>/dev/null || true +modprobe mpls_iptunnel &>/dev/null || true +sysctl -w net.mpls.platform_labels=1000 &>/dev/null || true +sysctl -w net.mpls.conf.eth0.input=1 &>/dev/null || true + # Create VRF if kernel supports it if ip link add vrf-red type vrf table 10 2>/dev/null; then echo "Created VRF vrf-red" diff --git a/internal/frrsockets/frrsockets.go b/internal/frrsockets/frrsockets.go index 3c4e619..0528d46 100644 --- a/internal/frrsockets/frrsockets.go +++ b/internal/frrsockets/frrsockets.go @@ -45,6 +45,10 @@ func (c Connection) ExecZebraCmd(cmd string) ([]byte, error) { return executeCmd(filepath.Join(c.dirPath, "zebra.vty"), cmd, c.timeout) } +func (c Connection) ExecLDPDCmd(cmd string) ([]byte, error) { + return executeCmd(filepath.Join(c.dirPath, "ldpd.vty"), cmd, c.timeout) +} + func executeCmd(socketPath, cmd string, timeout time.Duration) ([]byte, error) { var response bytes.Buffer