mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-31 21:13:00 +02:00
Merge pull request #1254 from kernelkit/ospf-fixes
Improvements to OSPF and BFD operational data and CLI commands
This commit is contained in:
@@ -9,6 +9,19 @@ All notable changes to the project are documented in this file.
|
|||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
- Upgrade Linux kernel to 6.12.57 (LTS)
|
- Upgrade Linux kernel to 6.12.57 (LTS)
|
||||||
|
- Major improvements to OSPF and BFD operational data and CLI commands:
|
||||||
|
- CLI commands now use data from the operational datastore instead of
|
||||||
|
calling vtysh directly, providing better integration and consistency
|
||||||
|
- New `show ip ospf` command family (neighbor, interface, route) to align
|
||||||
|
with industry standard CLI conventions. Legacy `show ospf` commands are
|
||||||
|
deprecated but still work with warnings
|
||||||
|
- The `show ip ospf database` subcommand has been dropped for now, the
|
||||||
|
advanced user can still use `vtysh` from Bash if necessary, issue #1253
|
||||||
|
- Extended BFD commands: `show bfd` (status), `show bfd peers` (detailed),
|
||||||
|
`show bfd peers brief` (table format), and `show bfd peer <address>`
|
||||||
|
- All command names now use singular form (interface, route, neighbor)
|
||||||
|
matching Cisco/FRR conventions, as well as configure context naming
|
||||||
|
- New support for configuring OSPF interface priority for DR/BDR election
|
||||||
- The DHCP client configuration has moved from `/infix-dhcp-client:dhcp-client`
|
- The DHCP client configuration has moved from `/infix-dhcp-client:dhcp-client`
|
||||||
to `/interfaces/interface[name]/ipv4/infix-dhcp-client:dhcp`, issue #1109.
|
to `/interfaces/interface[name]/ipv4/infix-dhcp-client:dhcp`, issue #1109.
|
||||||
The configuration is automatically migrated on upgrade. The DHCP client is
|
The configuration is automatically migrated on upgrade. The DHCP client is
|
||||||
@@ -28,6 +41,9 @@ All notable changes to the project are documented in this file.
|
|||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
- Fix #855: User admin sometimes fails to be added to `wheel` group
|
- Fix #855: User admin sometimes fails to be added to `wheel` group
|
||||||
|
- Fix #1247: Prevent invalid configuration of OSPF backbone area (0.0.0.0) as
|
||||||
|
stub or NSSA. The backbone must always be a normal area per RFC 2328. Any
|
||||||
|
existing invalid configurations are automatically corrected during upgrade
|
||||||
- Fix serious regression in boot time, introduced in v25.10, delays the
|
- Fix serious regression in boot time, introduced in v25.10, delays the
|
||||||
boot step "Mounting filesystems ..." with up to 30 seconds!
|
boot step "Mounting filesystems ..." with up to 30 seconds!
|
||||||
|
|
||||||
|
|||||||
+8
-8
@@ -562,7 +562,7 @@ In addition to basic status shown in the interface overview, detailed
|
|||||||
LAG status can be inspected:
|
LAG status can be inspected:
|
||||||
|
|
||||||
```
|
```
|
||||||
admin@example:/> show interfaces name lag0
|
admin@example:/> show interface lag0
|
||||||
name : lag0
|
name : lag0
|
||||||
index : 25
|
index : 25
|
||||||
mtu : 1500
|
mtu : 1500
|
||||||
@@ -582,7 +582,7 @@ out-octets : 2142
|
|||||||
Same aggregate, but in LACP mode:
|
Same aggregate, but in LACP mode:
|
||||||
|
|
||||||
```
|
```
|
||||||
admin@example:/> show interfaces name lag0
|
admin@example:/> show interface lag0
|
||||||
name : lag0
|
name : lag0
|
||||||
index : 24
|
index : 24
|
||||||
mtu : 1500
|
mtu : 1500
|
||||||
@@ -624,7 +624,7 @@ Member ports provide additional status information:
|
|||||||
Example member port status:
|
Example member port status:
|
||||||
|
|
||||||
```
|
```
|
||||||
admin@example:/> show interfaces name eth7
|
admin@example:/> show interface eth7
|
||||||
name : eth7
|
name : eth7
|
||||||
index : 8
|
index : 8
|
||||||
mtu : 1500
|
mtu : 1500
|
||||||
@@ -755,7 +755,7 @@ The speed and duplex status for the links can be listed as shown
|
|||||||
below, assuming the link operational status is 'up'.
|
below, assuming the link operational status is 'up'.
|
||||||
|
|
||||||
```
|
```
|
||||||
admin@example:/> show interfaces name eth1
|
admin@example:/> show interface eth1
|
||||||
name : eth1
|
name : eth1
|
||||||
index : 2
|
index : 2
|
||||||
mtu : 1500
|
mtu : 1500
|
||||||
@@ -769,7 +769,7 @@ ipv6 addresses :
|
|||||||
in-octets : 75581
|
in-octets : 75581
|
||||||
out-octets : 43130
|
out-octets : 43130
|
||||||
...
|
...
|
||||||
admin@example:/> show interfaces name eth4
|
admin@example:/> show interface eth4
|
||||||
name : eth4
|
name : eth4
|
||||||
index : 5
|
index : 5
|
||||||
mtu : 1500
|
mtu : 1500
|
||||||
@@ -827,7 +827,7 @@ require auto-negotiation to be enabled.
|
|||||||
Ethernet packet statistics[^6] can be listed as shown below.
|
Ethernet packet statistics[^6] can be listed as shown below.
|
||||||
|
|
||||||
```
|
```
|
||||||
admin@example:/> show interfaces name eth1
|
admin@example:/> show interface eth1
|
||||||
name : eth1
|
name : eth1
|
||||||
index : 2
|
index : 2
|
||||||
mtu : 1500
|
mtu : 1500
|
||||||
@@ -1525,10 +1525,10 @@ currently supported, namely `ipv4` and `ipv6`.
|
|||||||
[^3]: For example, IPv4 groups are mapped to MAC multicast addresses by
|
[^3]: For example, IPv4 groups are mapped to MAC multicast addresses by
|
||||||
mapping the low-order 23-bits of the IP address in the low-order 23
|
mapping the low-order 23-bits of the IP address in the low-order 23
|
||||||
bits of the Ethernet address 01:00:5E:00:00:00. Meaning, more than
|
bits of the Ethernet address 01:00:5E:00:00:00. Meaning, more than
|
||||||
one IP multicast group maps to the same MAC multicast group.
|
one IP multicast group maps to the same MAC multicast group.
|
||||||
[^4]: A YANG deviation was previously used to make it possible to set
|
[^4]: A YANG deviation was previously used to make it possible to set
|
||||||
`phys-address`, but this has been replaced with the more flexible
|
`phys-address`, but this has been replaced with the more flexible
|
||||||
`custom-phys-address`.
|
`custom-phys-address`.
|
||||||
[^5]: MAC bridges on Marvell Linkstreet devices are currently limited to
|
[^5]: MAC bridges on Marvell Linkstreet devices are currently limited to
|
||||||
a single MAC database, this may be a problem if the same MAC address
|
a single MAC database, this may be a problem if the same MAC address
|
||||||
appears in different MAC bridges.
|
appears in different MAC bridges.
|
||||||
|
|||||||
+7
-7
@@ -27,10 +27,10 @@ Wi-Fi support is primarily tested with Realtek chipset-based adapters.
|
|||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Add a supported Wi-Fi network device. To verify that it has been
|
Add a supported Wi-Fi network device. To verify that it has been
|
||||||
detected, look for `wifi0` in `show interfaces`
|
detected, look for `wifi0` in `show interface`
|
||||||
|
|
||||||
```
|
```
|
||||||
admin@example:/> show interfaces
|
admin@example:/> show interface
|
||||||
INTERFACE PROTOCOL STATE DATA
|
INTERFACE PROTOCOL STATE DATA
|
||||||
lo loopback UP
|
lo loopback UP
|
||||||
ipv4 127.0.0.1/8 (static)
|
ipv4 127.0.0.1/8 (static)
|
||||||
@@ -52,7 +52,7 @@ see the result read the operational datastore for interface `wifi0` or
|
|||||||
use the CLI
|
use the CLI
|
||||||
|
|
||||||
```
|
```
|
||||||
admin@infix-00-00-00:/> show interfaces name wifi0
|
admin@infix-00-00-00:/> show interface wifi0
|
||||||
name : wifi0
|
name : wifi0
|
||||||
type : wifi
|
type : wifi
|
||||||
index : 3
|
index : 3
|
||||||
@@ -119,11 +119,11 @@ admin@example:/config/interface/wifi0/> leave
|
|||||||
|
|
||||||
The Wi-Fi negotiation should now start immediately, provided that the
|
The Wi-Fi negotiation should now start immediately, provided that the
|
||||||
SSID and pre-shared key are correct. You can verify the connection by
|
SSID and pre-shared key are correct. You can verify the connection by
|
||||||
running `show interfaces` again.
|
running `show interface` again.
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
admin@example:/> show interfaces
|
admin@example:/> show interface
|
||||||
INTERFACE PROTOCOL STATE DATA
|
INTERFACE PROTOCOL STATE DATA
|
||||||
lo loopback UP
|
lo loopback UP
|
||||||
ipv4 127.0.0.1/8 (static)
|
ipv4 127.0.0.1/8 (static)
|
||||||
@@ -139,7 +139,7 @@ admin@example:/>
|
|||||||
|
|
||||||
## Troubleshooting Connection Issues
|
## Troubleshooting Connection Issues
|
||||||
|
|
||||||
Use `show wifi scan wifi0` and `show interfaces` to verify signal strength
|
Use `show wifi scan wifi0` and `show interface` to verify signal strength
|
||||||
and connection status. If issues arise, try the following
|
and connection status. If issues arise, try the following
|
||||||
troubleshooting steps:
|
troubleshooting steps:
|
||||||
|
|
||||||
@@ -147,6 +147,6 @@ troubleshooting steps:
|
|||||||
2. **Check credentials**: Verify the preshared key in `ietf-keystore`
|
2. **Check credentials**: Verify the preshared key in `ietf-keystore`
|
||||||
3. **Review logs**: Check system logs with `show log` for Wi-Fi related errors
|
3. **Review logs**: Check system logs with `show log` for Wi-Fi related errors
|
||||||
4. **Regulatory compliance**: Ensure the country-code matches your location
|
4. **Regulatory compliance**: Ensure the country-code matches your location
|
||||||
5. **Hardware detection**: Confirm the adapter appears in `show interfaces`
|
5. **Hardware detection**: Confirm the adapter appears in `show interface`
|
||||||
|
|
||||||
If issues persist, check the system log for specific error messages that can help identify the root cause.
|
If issues persist, check the system log for specific error messages that can help identify the root cause.
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Remove area-type from OSPF backbone area (0.0.0.0)
|
||||||
|
# The backbone area cannot be configured as stub or NSSA per RFC 2328/3101
|
||||||
|
# Silently drop any area-type setting for area 0.0.0.0
|
||||||
|
|
||||||
|
file=$1
|
||||||
|
temp=${file}.tmp
|
||||||
|
|
||||||
|
jq '
|
||||||
|
if .["ietf-routing:routing"]?."control-plane-protocols"?."control-plane-protocol" then
|
||||||
|
.["ietf-routing:routing"]."control-plane-protocols"."control-plane-protocol" |= map(
|
||||||
|
if .["ietf-ospf:ospf"]?.areas?.area then
|
||||||
|
.["ietf-ospf:ospf"].areas.area |= map(
|
||||||
|
if (.["area-id"] == "0.0.0.0") and .["area-type"] then
|
||||||
|
# Remove area-type from backbone area
|
||||||
|
del(.["area-type"])
|
||||||
|
else
|
||||||
|
.
|
||||||
|
end
|
||||||
|
)
|
||||||
|
else
|
||||||
|
.
|
||||||
|
end
|
||||||
|
)
|
||||||
|
else
|
||||||
|
.
|
||||||
|
end
|
||||||
|
' "$file" > "$temp" && mv "$temp" "$file"
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
migratedir = $(pkgdatadir)/migrate/1.6
|
migratedir = $(pkgdatadir)/migrate/1.6
|
||||||
dist_migrate_DATA = 10-dhcp-client-to-ipv4.sh \
|
dist_migrate_DATA = 10-dhcp-client-to-ipv4.sh \
|
||||||
20-autoconf-to-presence.sh
|
20-autoconf-to-presence.sh \
|
||||||
|
30-ospf-backbone-area-type.sh
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ int parse_ospf_interfaces(sr_session_ctx_t *session, struct lyd_node *areas, FIL
|
|||||||
area_id = lydx_get_cattr(area, "area-id");
|
area_id = lydx_get_cattr(area, "area-id");
|
||||||
|
|
||||||
LY_LIST_FOR(lyd_child(interfaces), interface) {
|
LY_LIST_FOR(lyd_child(interfaces), interface) {
|
||||||
const char *hello, *dead, *retransmit, *transmit, *interface_type, *cost;
|
const char *hello, *dead, *retransmit, *transmit, *interface_type, *cost, *priority;
|
||||||
|
|
||||||
if (lydx_get_bool(interface, "enabled")) {
|
if (lydx_get_bool(interface, "enabled")) {
|
||||||
int passive = 0, bfd_enabled = 0;
|
int passive = 0, bfd_enabled = 0;
|
||||||
@@ -57,6 +57,7 @@ int parse_ospf_interfaces(sr_session_ctx_t *session, struct lyd_node *areas, FIL
|
|||||||
transmit = lydx_get_cattr(interface, "transmit-delay");
|
transmit = lydx_get_cattr(interface, "transmit-delay");
|
||||||
interface_type = lydx_get_cattr(interface, "interface-type");
|
interface_type = lydx_get_cattr(interface, "interface-type");
|
||||||
cost = lydx_get_cattr(interface, "cost");
|
cost = lydx_get_cattr(interface, "cost");
|
||||||
|
priority = lydx_get_cattr(interface, "priority");
|
||||||
|
|
||||||
fprintf(fp, " ip ospf area %s\n", area_id);
|
fprintf(fp, " ip ospf area %s\n", area_id);
|
||||||
if (dead)
|
if (dead)
|
||||||
@@ -67,6 +68,8 @@ int parse_ospf_interfaces(sr_session_ctx_t *session, struct lyd_node *areas, FIL
|
|||||||
fprintf(fp, " ip ospf retransmit-interval %s\n", retransmit);
|
fprintf(fp, " ip ospf retransmit-interval %s\n", retransmit);
|
||||||
if (transmit)
|
if (transmit)
|
||||||
fprintf(fp, " ip ospf transmit-delay %s\n", transmit);
|
fprintf(fp, " ip ospf transmit-delay %s\n", transmit);
|
||||||
|
if (priority)
|
||||||
|
fprintf(fp, " ip ospf priority %s\n", priority);
|
||||||
if (bfd_enabled)
|
if (bfd_enabled)
|
||||||
fputs(" ip ospf bfd\n", fp);
|
fputs(" ip ospf bfd\n", fp);
|
||||||
if (passive)
|
if (passive)
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ MODULES=(
|
|||||||
"ietf-ipv6-unicast-routing@2018-03-13.yang"
|
"ietf-ipv6-unicast-routing@2018-03-13.yang"
|
||||||
"ietf-ipv4-unicast-routing@2018-03-13.yang"
|
"ietf-ipv4-unicast-routing@2018-03-13.yang"
|
||||||
"ietf-ospf@2022-10-19.yang -e bfd -e explicit-router-id"
|
"ietf-ospf@2022-10-19.yang -e bfd -e explicit-router-id"
|
||||||
|
"iana-bfd-types@2021-10-21.yang"
|
||||||
|
"ietf-bfd-types@2022-09-22.yang"
|
||||||
|
"ietf-bfd@2022-09-22.yang"
|
||||||
|
"ietf-bfd-ip-sh@2022-09-22.yang"
|
||||||
"iana-if-type@2023-01-26.yang"
|
"iana-if-type@2023-01-26.yang"
|
||||||
# NOTE: ietf-tls-client must be version matched with ietf-tls-server, used by netopeer2!
|
# NOTE: ietf-tls-client must be version matched with ietf-tls-server, used by netopeer2!
|
||||||
"ietf-tls-client@2023-12-28.yang"
|
"ietf-tls-client@2023-12-28.yang"
|
||||||
@@ -22,7 +26,7 @@ MODULES=(
|
|||||||
"ieee802-dot1q-types@2022-10-29.yang"
|
"ieee802-dot1q-types@2022-10-29.yang"
|
||||||
"infix-ip@2025-11-02.yang"
|
"infix-ip@2025-11-02.yang"
|
||||||
"infix-if-type@2025-02-12.yang"
|
"infix-if-type@2025-02-12.yang"
|
||||||
"infix-routing@2024-11-27.yang"
|
"infix-routing@2025-11-12.yang"
|
||||||
"ieee802-dot1ab-lldp@2022-03-15.yang"
|
"ieee802-dot1ab-lldp@2022-03-15.yang"
|
||||||
"infix-lldp@2025-05-05.yang"
|
"infix-lldp@2025-05-05.yang"
|
||||||
"infix-dhcp-common@2025-01-29.yang"
|
"infix-dhcp-common@2025-01-29.yang"
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
module ietf-bfd-ip-sh {
|
||||||
|
yang-version 1.1;
|
||||||
|
namespace "urn:ietf:params:xml:ns:yang:ietf-bfd-ip-sh";
|
||||||
|
prefix bfd-ip-sh;
|
||||||
|
|
||||||
|
import ietf-bfd-types {
|
||||||
|
prefix bfd-types;
|
||||||
|
reference
|
||||||
|
"RFC 9314: YANG Data Model for Bidirectional Forwarding
|
||||||
|
Detection (BFD)";
|
||||||
|
}
|
||||||
|
import ietf-bfd {
|
||||||
|
prefix bfd;
|
||||||
|
reference
|
||||||
|
"RFC 9314: YANG Data Model for Bidirectional Forwarding
|
||||||
|
Detection (BFD)";
|
||||||
|
}
|
||||||
|
import ietf-interfaces {
|
||||||
|
prefix if;
|
||||||
|
reference
|
||||||
|
"RFC 8343: A YANG Data Model for Interface Management";
|
||||||
|
}
|
||||||
|
import ietf-inet-types {
|
||||||
|
prefix inet;
|
||||||
|
reference
|
||||||
|
"RFC 6991: Common YANG Data Types";
|
||||||
|
}
|
||||||
|
import ietf-routing {
|
||||||
|
prefix rt;
|
||||||
|
reference
|
||||||
|
"RFC 8349: A YANG Data Model for Routing Management
|
||||||
|
(NMDA Version)";
|
||||||
|
}
|
||||||
|
|
||||||
|
organization
|
||||||
|
"IETF BFD Working Group";
|
||||||
|
contact
|
||||||
|
"WG Web: <https://datatracker.ietf.org/wg/bfd/>
|
||||||
|
WG List: <mailto:rtg-bfd@ietf.org>
|
||||||
|
|
||||||
|
Editor: Reshad Rahman
|
||||||
|
<mailto:reshad@yahoo.com>
|
||||||
|
|
||||||
|
Editor: Lianshu Zheng
|
||||||
|
<mailto:veronique_cheng@hotmail.com>
|
||||||
|
|
||||||
|
Editor: Mahesh Jethanandani
|
||||||
|
<mailto:mjethanandani@gmail.com>";
|
||||||
|
description
|
||||||
|
"This module contains the YANG definition for BFD IP single-hop
|
||||||
|
as per RFC 5881.
|
||||||
|
|
||||||
|
Copyright (c) 2022 IETF Trust and the persons identified as
|
||||||
|
authors of the code. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or
|
||||||
|
without modification, is permitted pursuant to, and subject
|
||||||
|
to the license terms contained in, the Revised BSD License
|
||||||
|
set forth in Section 4.c of the IETF Trust's Legal Provisions
|
||||||
|
Relating to IETF Documents
|
||||||
|
(https://trustee.ietf.org/license-info).
|
||||||
|
|
||||||
|
This version of this YANG module is part of RFC 9314; see the
|
||||||
|
RFC itself for full legal notices.";
|
||||||
|
reference
|
||||||
|
"RFC 5881: Bidirectional Forwarding Detection (BFD)
|
||||||
|
for IPv4 and IPv6 (Single Hop)
|
||||||
|
RFC 9314: YANG Data Model for Bidirectional Forwarding
|
||||||
|
Detection (BFD)";
|
||||||
|
|
||||||
|
revision 2022-09-22 {
|
||||||
|
description
|
||||||
|
"Updating reference to RFC 9314.";
|
||||||
|
reference
|
||||||
|
"RFC 9314: YANG Data Model for Bidirectional Forwarding
|
||||||
|
Detection (BFD).";
|
||||||
|
}
|
||||||
|
revision 2021-10-21 {
|
||||||
|
description
|
||||||
|
"Initial revision.";
|
||||||
|
reference
|
||||||
|
"RFC 9127: YANG Data Model for Bidirectional Forwarding
|
||||||
|
Detection (BFD)";
|
||||||
|
}
|
||||||
|
|
||||||
|
augment "/rt:routing/rt:control-plane-protocols/"
|
||||||
|
+ "rt:control-plane-protocol/bfd:bfd" {
|
||||||
|
description
|
||||||
|
"BFD augmentation for IP single-hop.";
|
||||||
|
container ip-sh {
|
||||||
|
description
|
||||||
|
"BFD IP single-hop top-level container.";
|
||||||
|
uses bfd-types:session-statistics-summary;
|
||||||
|
container sessions {
|
||||||
|
description
|
||||||
|
"BFD IP single-hop sessions.";
|
||||||
|
list session {
|
||||||
|
key "interface dest-addr";
|
||||||
|
description
|
||||||
|
"List of IP single-hop sessions.";
|
||||||
|
leaf interface {
|
||||||
|
type if:interface-ref;
|
||||||
|
description
|
||||||
|
"Interface on which the BFD session is running.";
|
||||||
|
}
|
||||||
|
leaf dest-addr {
|
||||||
|
type inet:ip-address;
|
||||||
|
description
|
||||||
|
"IP address of the peer.";
|
||||||
|
}
|
||||||
|
leaf source-addr {
|
||||||
|
type inet:ip-address;
|
||||||
|
description
|
||||||
|
"Local IP address.";
|
||||||
|
}
|
||||||
|
uses bfd-types:common-cfg-parms;
|
||||||
|
uses bfd-types:all-session;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list interfaces {
|
||||||
|
key "interface";
|
||||||
|
description
|
||||||
|
"List of interfaces.";
|
||||||
|
leaf interface {
|
||||||
|
type if:interface-ref;
|
||||||
|
description
|
||||||
|
"BFD information for this interface.";
|
||||||
|
}
|
||||||
|
uses bfd-types:auth-parms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notification singlehop-notification {
|
||||||
|
description
|
||||||
|
"Notification for BFD single-hop session state change. An
|
||||||
|
implementation may rate-limit notifications, e.g., when a
|
||||||
|
session is continuously changing state.";
|
||||||
|
uses bfd-types:notification-parms;
|
||||||
|
leaf interface {
|
||||||
|
type if:interface-ref;
|
||||||
|
description
|
||||||
|
"Interface to which this BFD session belongs.";
|
||||||
|
}
|
||||||
|
leaf echo-enabled {
|
||||||
|
type boolean;
|
||||||
|
description
|
||||||
|
"Indicates whether Echo was enabled for BFD.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
module ietf-bfd {
|
||||||
|
yang-version 1.1;
|
||||||
|
namespace "urn:ietf:params:xml:ns:yang:ietf-bfd";
|
||||||
|
prefix bfd;
|
||||||
|
|
||||||
|
import ietf-bfd-types {
|
||||||
|
prefix bfd-types;
|
||||||
|
reference
|
||||||
|
"RFC 9314: YANG Data Model for Bidirectional Forwarding
|
||||||
|
Detection (BFD)";
|
||||||
|
}
|
||||||
|
import ietf-routing {
|
||||||
|
prefix rt;
|
||||||
|
reference
|
||||||
|
"RFC 8349: A YANG Data Model for Routing Management
|
||||||
|
(NMDA Version)";
|
||||||
|
}
|
||||||
|
|
||||||
|
organization
|
||||||
|
"IETF BFD Working Group";
|
||||||
|
contact
|
||||||
|
"WG Web: <https://datatracker.ietf.org/wg/bfd/>
|
||||||
|
WG List: <mailto:rtg-bfd@ietf.org>
|
||||||
|
|
||||||
|
Editor: Reshad Rahman
|
||||||
|
<mailto:reshad@yahoo.com>
|
||||||
|
|
||||||
|
Editor: Lianshu Zheng
|
||||||
|
<mailto:veronique_cheng@hotmail.com>
|
||||||
|
|
||||||
|
Editor: Mahesh Jethanandani
|
||||||
|
<mailto:mjethanandani@gmail.com>";
|
||||||
|
description
|
||||||
|
"This module contains the YANG definition for BFD parameters as
|
||||||
|
per RFC 5880.
|
||||||
|
|
||||||
|
Copyright (c) 2022 IETF Trust and the persons identified as
|
||||||
|
authors of the code. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or
|
||||||
|
without modification, is permitted pursuant to, and subject
|
||||||
|
to the license terms contained in, the Revised BSD License set
|
||||||
|
forth in Section 4.c of the IETF Trust's Legal Provisions
|
||||||
|
Relating to IETF Documents
|
||||||
|
(https://trustee.ietf.org/license-info).
|
||||||
|
|
||||||
|
This version of this YANG module is part of RFC 9314; see the
|
||||||
|
RFC itself for full legal notices.";
|
||||||
|
reference
|
||||||
|
"RFC 5880: Bidirectional Forwarding Detection (BFD)
|
||||||
|
RFC 9314: YANG Data Model for Bidirectional Forwarding
|
||||||
|
Detection (BFD)";
|
||||||
|
|
||||||
|
revision 2022-09-22 {
|
||||||
|
description
|
||||||
|
"Updating reference to RFC 9314.";
|
||||||
|
reference
|
||||||
|
"RFC 9314: YANG Data Model for Bidirectional Forwarding
|
||||||
|
Detection (BFD).";
|
||||||
|
}
|
||||||
|
revision 2021-10-21 {
|
||||||
|
description
|
||||||
|
"Initial revision.";
|
||||||
|
reference
|
||||||
|
"RFC 9127: YANG Data Model for Bidirectional Forwarding
|
||||||
|
Detection (BFD)";
|
||||||
|
}
|
||||||
|
|
||||||
|
augment "/rt:routing/rt:control-plane-protocols/"
|
||||||
|
+ "rt:control-plane-protocol" {
|
||||||
|
when "derived-from-or-self(rt:type, 'bfd-types:bfdv1')" {
|
||||||
|
description
|
||||||
|
"This augmentation is only valid for a control plane protocol
|
||||||
|
instance of BFD (type 'bfdv1').";
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"BFD augmentation.";
|
||||||
|
container bfd {
|
||||||
|
description
|
||||||
|
"BFD top-level container.";
|
||||||
|
uses bfd-types:session-statistics-summary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,11 +15,34 @@ module infix-routing {
|
|||||||
import ietf-ospf {
|
import ietf-ospf {
|
||||||
prefix ospf;
|
prefix ospf;
|
||||||
}
|
}
|
||||||
|
import ietf-bfd-types {
|
||||||
|
prefix bfd-types;
|
||||||
|
}
|
||||||
|
|
||||||
organization "KernelKit";
|
organization "KernelKit";
|
||||||
contact "kernelkit@googlegroups.com";
|
contact "kernelkit@googlegroups.com";
|
||||||
description "Deviations and augments for ietf-routing and ietf-ospf.";
|
description "Deviations and augments for ietf-routing and ietf-ospf.";
|
||||||
|
|
||||||
|
revision 2025-11-12 {
|
||||||
|
description "Add area-id augmentation to OSPF local-rib routes.
|
||||||
|
Add uptime, role, and interface-name augmentations to OSPF neighbor state.
|
||||||
|
Allows displaying which area a route was learned from,
|
||||||
|
how long neighbors have been up, their DR/BDR role, and
|
||||||
|
the interface with local address.";
|
||||||
|
reference "Issue #1190";
|
||||||
|
}
|
||||||
|
|
||||||
|
revision 2025-11-11 {
|
||||||
|
description "Add BFD IP single-hop support (RFC 9127).
|
||||||
|
Deviate session-statistics (not implemented yet).";
|
||||||
|
reference "RFC 9127, Issue #1190";
|
||||||
|
}
|
||||||
|
revision 2025-11-10 {
|
||||||
|
description "Add must constraint to prevent backbone area as stub/NSSA.
|
||||||
|
Enable OSPF operational data for CLI: un-deviate priority,
|
||||||
|
hello-timer, wait-timer, route metric and route-tag fields.";
|
||||||
|
reference "RFC 2328, RFC 3101, Issue #1190";
|
||||||
|
}
|
||||||
revision 2024-11-27 {
|
revision 2024-11-27 {
|
||||||
description "Deviate address-family in OSPF";
|
description "Deviate address-family in OSPF";
|
||||||
reference "internal";
|
reference "internal";
|
||||||
@@ -121,6 +144,11 @@ module infix-routing {
|
|||||||
base infix-routing-type;
|
base infix-routing-type;
|
||||||
description "OSPv2 (IPv4) routing protocol";
|
description "OSPv2 (IPv4) routing protocol";
|
||||||
}
|
}
|
||||||
|
identity bfdv1 {
|
||||||
|
base bfd-types:bfdv1;
|
||||||
|
base infix-routing-type;
|
||||||
|
description "BFD protocol version 1";
|
||||||
|
}
|
||||||
identity static {
|
identity static {
|
||||||
base rt:static;
|
base rt:static;
|
||||||
base infix-routing-type;
|
base infix-routing-type;
|
||||||
@@ -348,6 +376,19 @@ module infix-routing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* OSPF Area */
|
/* OSPF Area */
|
||||||
|
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:area-type" {
|
||||||
|
deviate add {
|
||||||
|
must "(../ospf:area-id != '0.0.0.0') or "
|
||||||
|
+ "(derived-from-or-self(., 'ospf:normal-area'))" {
|
||||||
|
error-message "Area 0.0.0.0 (backbone) cannot be configured as "
|
||||||
|
+ "stub or NSSA.";
|
||||||
|
description
|
||||||
|
"The backbone area (area 0.0.0.0) must be a normal area. "
|
||||||
|
+ "It cannot be configured as a stub area or NSSA as per "
|
||||||
|
+ "RFC 2328 and RFC 3101.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:virtual-links" {
|
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:virtual-links" {
|
||||||
deviate not-supported;
|
deviate not-supported;
|
||||||
}
|
}
|
||||||
@@ -368,13 +409,6 @@ module infix-routing {
|
|||||||
deviate not-supported;
|
deviate not-supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OSPF Local RIB */
|
|
||||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:local-rib/ospf:route/ospf:metric" {
|
|
||||||
deviate not-supported;
|
|
||||||
}
|
|
||||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:local-rib/ospf:route/ospf:route-tag" {
|
|
||||||
deviate not-supported;
|
|
||||||
}
|
|
||||||
/* OSPF Area Interface */
|
/* OSPF Area Interface */
|
||||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:static-neighbors"
|
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:static-neighbors"
|
||||||
{
|
{
|
||||||
@@ -396,7 +430,12 @@ module infix-routing {
|
|||||||
}
|
}
|
||||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:priority"
|
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:priority"
|
||||||
{
|
{
|
||||||
deviate not-supported;
|
deviate add {
|
||||||
|
default "1";
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"FRR uses a default priority of 1 for DR/BDR election.
|
||||||
|
Setting default to match FRR behavior.";
|
||||||
}
|
}
|
||||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:fast-reroute"
|
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:fast-reroute"
|
||||||
{
|
{
|
||||||
@@ -457,14 +496,6 @@ module infix-routing {
|
|||||||
{
|
{
|
||||||
deviate not-supported;
|
deviate not-supported;
|
||||||
}
|
}
|
||||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:hello-timer"
|
|
||||||
{
|
|
||||||
deviate not-supported;
|
|
||||||
}
|
|
||||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/ospf:wait-timer"
|
|
||||||
{
|
|
||||||
deviate not-supported;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* OSPF RPCs */
|
/* OSPF RPCs */
|
||||||
deviation "/ospf:clear-neighbor" {
|
deviation "/ospf:clear-neighbor" {
|
||||||
@@ -512,4 +543,84 @@ module infix-routing {
|
|||||||
deviate not-supported;
|
deviate not-supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Augmentations
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Add area-id to OSPF local-rib routes
|
||||||
|
* The standard ietf-ospf model doesn't include area information in routes,
|
||||||
|
* but FRR provides this and it's useful for operators to see which area
|
||||||
|
* a route was learned from.
|
||||||
|
*/
|
||||||
|
augment "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/"
|
||||||
|
+ "ospf:ospf/ospf:local-rib/ospf:route" {
|
||||||
|
when "derived-from-or-self(../../rt:type, 'infix-routing:ospfv2')" {
|
||||||
|
description
|
||||||
|
"This augmentation is only valid for OSPFv2.";
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"Infix extension to add area information to OSPF routes.";
|
||||||
|
|
||||||
|
leaf area-id {
|
||||||
|
type ospf:area-id-type;
|
||||||
|
config false;
|
||||||
|
description
|
||||||
|
"Area ID from which this route was learned. Not applicable
|
||||||
|
for inter-area routes learned from area border routers or
|
||||||
|
external routes.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add uptime to OSPF neighbor operational state
|
||||||
|
* The standard ietf-ospf model doesn't include neighbor uptime,
|
||||||
|
* but FRR provides this and it's useful for monitoring neighbor stability.
|
||||||
|
*/
|
||||||
|
augment "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/"
|
||||||
|
+ "ospf:ospf/ospf:areas/ospf:area/ospf:interfaces/ospf:interface/"
|
||||||
|
+ "ospf:neighbors/ospf:neighbor" {
|
||||||
|
when "derived-from-or-self(../../../../../../../rt:type, 'infix-routing:ospfv2')" {
|
||||||
|
description
|
||||||
|
"This augmentation is only valid for OSPFv2.";
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"Infix extension to add uptime information to OSPF neighbors.";
|
||||||
|
|
||||||
|
leaf uptime {
|
||||||
|
type uint32;
|
||||||
|
units "seconds";
|
||||||
|
config false;
|
||||||
|
description
|
||||||
|
"Time since the neighbor transitioned to the Full state.
|
||||||
|
Reported in seconds for consistency with other timers.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf role {
|
||||||
|
type enumeration {
|
||||||
|
enum DR {
|
||||||
|
description "Designated Router";
|
||||||
|
}
|
||||||
|
enum BDR {
|
||||||
|
description "Backup Designated Router";
|
||||||
|
}
|
||||||
|
enum DROther {
|
||||||
|
description "Neither DR nor BDR";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config false;
|
||||||
|
description
|
||||||
|
"OSPF neighbor's role on the network segment (DR, BDR, or DROther).
|
||||||
|
This is derived from the neighbor's priority and router ID during
|
||||||
|
the DR/BDR election process.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf interface-name {
|
||||||
|
type string;
|
||||||
|
config false;
|
||||||
|
description
|
||||||
|
"Interface name with local address in format 'interface:address'.
|
||||||
|
Example: 'e5:10.0.23.1' indicates interface e5 with local address 10.0.23.1.
|
||||||
|
This is useful for networks where multiple addresses exist on an interface.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -359,34 +359,62 @@
|
|||||||
</ACTION>
|
</ACTION>
|
||||||
</COMMAND>
|
</COMMAND>
|
||||||
|
|
||||||
<COMMAND name="ospf" help="Show OSPF status">
|
<COMMAND name="bfd" help="Show BFD information">
|
||||||
<PARAM name="name" ptype="/STRING" help="neighbor, interfaces or bfd" min="0" max="1">
|
<PARAM name="subcommand" ptype="/STRING" help="peers or peer" min="0" max="1">
|
||||||
<COMPL>
|
<COMPL>
|
||||||
<ACTION sym="printl">neighbor</ACTION>
|
<ACTION sym="printl">peers</ACTION>
|
||||||
<ACTION sym="printl">interfaces</ACTION>
|
<ACTION sym="printl">peer</ACTION>
|
||||||
<ACTION sym="printl">routes</ACTION>
|
|
||||||
<ACTION sym="printl">database</ACTION>
|
|
||||||
<ACTION sym="printl">bfd</ACTION>
|
|
||||||
</COMPL>
|
</COMPL>
|
||||||
</PARAM>
|
</PARAM>
|
||||||
|
<PARAM name="peer_addr" ptype="/STRING" help="Peer IP address" min="0" max="1"/>
|
||||||
|
<SWITCH name="optional" min="0">
|
||||||
|
<COMMAND name="brief" help="Show brief output" mode="switch"/>
|
||||||
|
</SWITCH>
|
||||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
if [ -z "$KLISH_PARAM_name" ]; then
|
BRIEF_ARG=""
|
||||||
doas vtysh -c "show ip ospf" |pager
|
if [ -n "$KLISH_COMMAND_brief" ]; then
|
||||||
elif [ "$KLISH_PARAM_name" == "neighbor" ];then
|
BRIEF_ARG="brief"
|
||||||
doas vtysh -c "show ip ospf neighbor" |pager
|
fi
|
||||||
elif [ "$KLISH_PARAM_name" == "interfaces" ];then
|
# Pass subcommand, optional peer address, and brief flag
|
||||||
doas vtysh -c "show ip ospf interface" |pager
|
show bfd ${KLISH_PARAM_subcommand:+"$KLISH_PARAM_subcommand"} ${KLISH_PARAM_peer_addr:+"$KLISH_PARAM_peer_addr"} ${BRIEF_ARG:+"$BRIEF_ARG"} |pager
|
||||||
elif [ "$KLISH_PARAM_name" == "routes" ];then
|
|
||||||
doas vtysh -c "show ip ospf route" |pager
|
|
||||||
elif [ "$KLISH_PARAM_name" == "database" ];then
|
|
||||||
doas vtysh -c "show ip ospf database" |pager
|
|
||||||
elif [ "$KLISH_PARAM_name" == "bfd" ];then
|
|
||||||
doas vtysh -c "show bfd peers" |pager
|
|
||||||
fi
|
|
||||||
</ACTION>
|
</ACTION>
|
||||||
</COMMAND>
|
</COMMAND>
|
||||||
|
|
||||||
<COMMAND name="routes" help="Show routing table">
|
<COMMAND name="ospf" help="Show OSPF status">
|
||||||
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
|
show ospf |pager
|
||||||
|
# Deprecated in favor of Cisco likeness, consider future 'show ipv6 ospf'
|
||||||
|
printf "\n\e[1mNOTE:\e[0m this command is deprecated, please use 'show ip ospf' instead.\n"
|
||||||
|
</ACTION>
|
||||||
|
|
||||||
|
<SWITCH name="optional" min="0">
|
||||||
|
<COMMAND name="neighbor" help="Show OSPF neighbors">
|
||||||
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
|
show ospf neighbor |pager
|
||||||
|
printf "\n\e[1mNOTE:\e[0m this command is deprecated, please use 'show ip ospf neighbor' instead.\n"
|
||||||
|
</ACTION>
|
||||||
|
</COMMAND>
|
||||||
|
|
||||||
|
<COMMAND name="interface" help="Show OSPF interfaces">
|
||||||
|
<SWITCH name="optional" min="0" max="1">
|
||||||
|
<PARAM name="ifname" ptype="/IFACES" help="Interface name" />
|
||||||
|
</SWITCH>
|
||||||
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
|
show ospf interface "$KLISH_PARAM_ifname" |pager
|
||||||
|
printf "\n\e[1mNOTE:\e[0m this command is deprecated, please use 'show ip ospf interface' instead.\n"
|
||||||
|
</ACTION>
|
||||||
|
</COMMAND>
|
||||||
|
|
||||||
|
<COMMAND name="route" help="Show OSPF routing table">
|
||||||
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
|
show ospf route |pager
|
||||||
|
printf "\n\e[1mNOTE:\e[0m this command is deprecated, please use 'show ip ospf route' instead.\n"
|
||||||
|
</ACTION>
|
||||||
|
</COMMAND>
|
||||||
|
</SWITCH>
|
||||||
|
</COMMAND>
|
||||||
|
|
||||||
|
<COMMAND name="route" help="Show routing table">
|
||||||
<PARAM name="ip" ptype="/STRING" help="ipv4 or ipv6" min="0" max="1">
|
<PARAM name="ip" ptype="/STRING" help="ipv4 or ipv6" min="0" max="1">
|
||||||
<COMPL>
|
<COMPL>
|
||||||
<ACTION sym="printl">ipv4</ACTION>
|
<ACTION sym="printl">ipv4</ACTION>
|
||||||
@@ -394,15 +422,9 @@
|
|||||||
</COMPL>
|
</COMPL>
|
||||||
</PARAM>
|
</PARAM>
|
||||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
case $KLISH_PARAM_ip in
|
show routes $KLISH_PARAM_ip route |pager
|
||||||
ipv4)
|
# Deprecated in favor of Cisco likeness
|
||||||
KLISH_PARAM_ip=ip
|
printf "\n\e[1mNOTE:\e[0m this command is deprecated, please use 'show ip route' instead.\n"
|
||||||
;;
|
|
||||||
ipv6)
|
|
||||||
KLISH_PARAM_ip=ipv6
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
show-legacy -p $KLISH_PARAM_ip route |pager
|
|
||||||
</ACTION>
|
</ACTION>
|
||||||
</COMMAND>
|
</COMMAND>
|
||||||
|
|
||||||
@@ -419,14 +441,12 @@
|
|||||||
</COMMAND>
|
</COMMAND>
|
||||||
|
|
||||||
<!-- https://www.cisco.com/c/en/us/td/docs/wireless/access_point/mob_exp/83/cmd-ref/me_cr_book/me_ports_and_interfaces_cli.html -->
|
<!-- https://www.cisco.com/c/en/us/td/docs/wireless/access_point/mob_exp/83/cmd-ref/me_cr_book/me_ports_and_interfaces_cli.html -->
|
||||||
<COMMAND name="interfaces" help="Show interface info">
|
<COMMAND name="interface" help="Show interface info, default an overview of all">
|
||||||
<SWITCH name="optional" min="0" max="1">
|
<SWITCH name="optional" min="0" max="1">
|
||||||
<COMMAND name="name" help="Show detailed info about NAME">
|
<PARAM name="ifname" ptype="/IFACES" help="Interface name" />
|
||||||
<PARAM name="name" ptype="/IFACES" help="Interface." />
|
|
||||||
</COMMAND>
|
|
||||||
</SWITCH>
|
</SWITCH>
|
||||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
show interface "$KLISH_PARAM_name" |pager
|
show interface "$KLISH_PARAM_ifname" |pager
|
||||||
</ACTION>
|
</ACTION>
|
||||||
</COMMAND>
|
</COMMAND>
|
||||||
|
|
||||||
@@ -454,11 +474,40 @@
|
|||||||
</ACTION>
|
</ACTION>
|
||||||
</COMMAND>
|
</COMMAND>
|
||||||
|
|
||||||
<COMMAND name="route" help="Show IPv4 routing table" mode="switch">
|
<COMMAND name="route" help="Show routing table">
|
||||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
show-legacy -p ip route |pager
|
show routes ipv4 |pager
|
||||||
</ACTION>
|
</ACTION>
|
||||||
</COMMAND>
|
</COMMAND>
|
||||||
|
|
||||||
|
<COMMAND name="ospf" help="Show OSPF status">
|
||||||
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
|
show ospf |pager
|
||||||
|
</ACTION>
|
||||||
|
|
||||||
|
<SWITCH name="optional" min="0">
|
||||||
|
<COMMAND name="neighbor" help="Show OSPF neighbors">
|
||||||
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
|
show ospf neighbor |pager
|
||||||
|
</ACTION>
|
||||||
|
</COMMAND>
|
||||||
|
|
||||||
|
<COMMAND name="interface" help="Show OSPF interfaces">
|
||||||
|
<SWITCH name="optional" min="0" max="1">
|
||||||
|
<PARAM name="ifname" ptype="/IFACES" help="Interface name" />
|
||||||
|
</SWITCH>
|
||||||
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
|
show ospf interface "$KLISH_PARAM_ifname" |pager
|
||||||
|
</ACTION>
|
||||||
|
</COMMAND>
|
||||||
|
|
||||||
|
<COMMAND name="route" help="Show OSPF routing table">
|
||||||
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
|
show ospf route |pager
|
||||||
|
</ACTION>
|
||||||
|
</COMMAND>
|
||||||
|
</SWITCH>
|
||||||
|
</COMMAND>
|
||||||
</SWITCH>
|
</SWITCH>
|
||||||
</COMMAND>
|
</COMMAND>
|
||||||
|
|
||||||
@@ -468,9 +517,21 @@
|
|||||||
</ACTION>
|
</ACTION>
|
||||||
|
|
||||||
<SWITCH name="optional" min="0">
|
<SWITCH name="optional" min="0">
|
||||||
|
<COMMAND name="brief" help="Simplified (human-readable) output" mode="switch">
|
||||||
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
|
ip -color -6 -br addr | sort -V | sed 's/@dsa[0-9]/ /' |pager
|
||||||
|
</ACTION>
|
||||||
|
</COMMAND>
|
||||||
|
|
||||||
|
<COMMAND name="detailed" help="Detailed (full) output" mode="switch">
|
||||||
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
|
ip -color -6 -d addr |pager
|
||||||
|
</ACTION>
|
||||||
|
</COMMAND>
|
||||||
|
|
||||||
<COMMAND name="route" help="Show IPv6 routing table" mode="switch">
|
<COMMAND name="route" help="Show IPv6 routing table" mode="switch">
|
||||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||||
show-legacy -p ipv6 route |pager
|
show routes ipv6 |pager
|
||||||
</ACTION>
|
</ACTION>
|
||||||
</COMMAND>
|
</COMMAND>
|
||||||
</SWITCH>
|
</SWITCH>
|
||||||
|
|||||||
+132
-2
@@ -154,6 +154,134 @@ def services(args: List[str]) -> None:
|
|||||||
|
|
||||||
cli_pretty(data, f"show-services")
|
cli_pretty(data, f"show-services")
|
||||||
|
|
||||||
|
def bfd(args: List[str]) -> None:
|
||||||
|
"""Handle show bfd [subcommand] [peer] [brief]
|
||||||
|
|
||||||
|
Subcommands:
|
||||||
|
(none) - Show BFD status summary (default)
|
||||||
|
peers - Show BFD peer sessions
|
||||||
|
peers brief - Show BFD peers in brief format
|
||||||
|
peer <addr> - Show specific BFD peer details
|
||||||
|
"""
|
||||||
|
# Fetch operational data from sysrepocfg
|
||||||
|
data = run_sysrepocfg("/ietf-routing:routing/control-plane-protocols/control-plane-protocol")
|
||||||
|
if not data:
|
||||||
|
print("No BFD data retrieved.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if RAW_OUTPUT:
|
||||||
|
print(json.dumps(data, indent=2))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Parse arguments: [subcommand] [peer_addr] [brief]
|
||||||
|
subcommand = ""
|
||||||
|
peer_addr = None
|
||||||
|
brief_flag = False
|
||||||
|
|
||||||
|
for arg in args:
|
||||||
|
if arg == "brief":
|
||||||
|
brief_flag = True
|
||||||
|
elif arg in ["peers", "peer"]:
|
||||||
|
subcommand = arg
|
||||||
|
elif arg: # Must be peer address
|
||||||
|
peer_addr = arg
|
||||||
|
|
||||||
|
# Default to empty string for general status
|
||||||
|
if not subcommand and not peer_addr:
|
||||||
|
subcommand = ""
|
||||||
|
|
||||||
|
# Add metadata to data
|
||||||
|
if peer_addr:
|
||||||
|
data['_peer_addr'] = peer_addr
|
||||||
|
if brief_flag:
|
||||||
|
data['_brief'] = True
|
||||||
|
|
||||||
|
# For brief view, fetch interface data to show interface:ip format
|
||||||
|
if brief_flag:
|
||||||
|
ifaces_data = run_sysrepocfg("/ietf-interfaces:interfaces")
|
||||||
|
if ifaces_data:
|
||||||
|
data['_interfaces'] = ifaces_data
|
||||||
|
|
||||||
|
# Route to appropriate formatter
|
||||||
|
if subcommand == "":
|
||||||
|
cli_pretty(data, "show-bfd-status")
|
||||||
|
elif subcommand == "peers":
|
||||||
|
if brief_flag:
|
||||||
|
cli_pretty(data, "show-bfd-peers-brief")
|
||||||
|
else:
|
||||||
|
cli_pretty(data, "show-bfd-peers")
|
||||||
|
elif subcommand == "peer":
|
||||||
|
cli_pretty(data, "show-bfd-peer")
|
||||||
|
else:
|
||||||
|
print(f"Unknown BFD subcommand: {subcommand}")
|
||||||
|
|
||||||
|
def ospf(args: List[str]) -> None:
|
||||||
|
"""Handle show ospf [subcommand] [ifname] [detail]
|
||||||
|
|
||||||
|
Subcommands:
|
||||||
|
(none) - General OSPF instance information
|
||||||
|
neighbor - OSPF neighbor table
|
||||||
|
interfaces - OSPF interface details (optionally for specific interface)
|
||||||
|
routes - OSPF routing table (local-rib)
|
||||||
|
|
||||||
|
Optional:
|
||||||
|
ifname - Interface name (for interfaces subcommand)
|
||||||
|
detail - Show detailed information (Cisco-style)
|
||||||
|
"""
|
||||||
|
# Fetch operational data from sysrepocfg
|
||||||
|
data = run_sysrepocfg("/ietf-routing:routing/control-plane-protocols/control-plane-protocol")
|
||||||
|
if not data:
|
||||||
|
print("No OSPF data retrieved.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if RAW_OUTPUT:
|
||||||
|
print(json.dumps(data, indent=2))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Parse arguments: subcommand, optional interface name, optional detail flag
|
||||||
|
subcommand = args[0] if len(args) > 0 and args[0] else ""
|
||||||
|
|
||||||
|
# Determine if second arg is interface name or detail flag
|
||||||
|
ifname = None
|
||||||
|
detail_flag = False
|
||||||
|
|
||||||
|
if len(args) > 1:
|
||||||
|
if args[1] == "detail":
|
||||||
|
detail_flag = True
|
||||||
|
else:
|
||||||
|
ifname = args[1]
|
||||||
|
# Check if third arg is detail
|
||||||
|
if len(args) > 2 and args[2] == "detail":
|
||||||
|
detail_flag = True
|
||||||
|
|
||||||
|
# Add metadata to data for formatters
|
||||||
|
if detail_flag:
|
||||||
|
data['_detail'] = True
|
||||||
|
if ifname:
|
||||||
|
data['_ifname'] = ifname
|
||||||
|
# For detailed interface view, fetch additional data (interfaces and BFD)
|
||||||
|
if subcommand == "interface" or subcommand == "interfaces":
|
||||||
|
ifaces_data = run_sysrepocfg("/ietf-interfaces:interfaces")
|
||||||
|
if ifaces_data:
|
||||||
|
data['_interfaces'] = ifaces_data
|
||||||
|
|
||||||
|
# Fetch BFD data for per-interface status
|
||||||
|
routing_data = run_sysrepocfg("/ietf-routing:routing/control-plane-protocols/control-plane-protocol")
|
||||||
|
if routing_data:
|
||||||
|
data['_routing'] = routing_data
|
||||||
|
|
||||||
|
# Route to appropriate formatter
|
||||||
|
if subcommand == "":
|
||||||
|
cli_pretty(data, "show-ospf")
|
||||||
|
elif subcommand == "neighbor":
|
||||||
|
cli_pretty(data, "show-ospf-neighbor")
|
||||||
|
elif subcommand == "interface" or subcommand == "interfaces":
|
||||||
|
cli_pretty(data, "show-ospf-interfaces")
|
||||||
|
elif subcommand == "route" or subcommand == "routes":
|
||||||
|
cli_pretty(data, "show-ospf-routes")
|
||||||
|
else:
|
||||||
|
print(f"Unknown OSPF subcommand: {subcommand}")
|
||||||
|
|
||||||
def routes(args: List[str]):
|
def routes(args: List[str]):
|
||||||
ip_version = args[0] if args and args[0] in ["ipv4", "ipv6"] else "ipv4"
|
ip_version = args[0] if args and args[0] in ["ipv4", "ipv6"] else "ipv4"
|
||||||
|
|
||||||
@@ -296,12 +424,14 @@ def system(args: List[str]) -> None:
|
|||||||
|
|
||||||
def execute_command(command: str, args: List[str]):
|
def execute_command(command: str, args: List[str]):
|
||||||
command_mapping = {
|
command_mapping = {
|
||||||
|
'bfd': bfd,
|
||||||
'dhcp': dhcp,
|
'dhcp': dhcp,
|
||||||
'hardware': hardware,
|
'hardware': hardware,
|
||||||
'interface': interface,
|
'interface': interface,
|
||||||
'ntp': ntp,
|
|
||||||
'routes': routes,
|
|
||||||
'lldp': lldp,
|
'lldp': lldp,
|
||||||
|
'ntp': ntp,
|
||||||
|
'ospf': ospf,
|
||||||
|
'routes': routes,
|
||||||
'services' : services,
|
'services' : services,
|
||||||
'software' : software,
|
'software' : software,
|
||||||
'stp': stp,
|
'stp': stp,
|
||||||
|
|||||||
@@ -2819,6 +2819,670 @@ def show_firewall_service(json, name=None):
|
|||||||
f"{ports:<{PadFirewall.service_ports}}")
|
f"{ports:<{PadFirewall.service_ports}}")
|
||||||
|
|
||||||
|
|
||||||
|
def show_ospf(json_data):
|
||||||
|
"""Show OSPF general instance information"""
|
||||||
|
routing = json_data.get('ietf-routing:routing', {})
|
||||||
|
protocols = routing.get('control-plane-protocols', {}).get('control-plane-protocol', [])
|
||||||
|
|
||||||
|
ospf_instance = None
|
||||||
|
for protocol in protocols:
|
||||||
|
if 'ietf-ospf:ospf' in protocol:
|
||||||
|
ospf_instance = protocol
|
||||||
|
break
|
||||||
|
|
||||||
|
if not ospf_instance:
|
||||||
|
print("OSPF is not configured or running")
|
||||||
|
return
|
||||||
|
|
||||||
|
ospf = ospf_instance.get('ietf-ospf:ospf', {})
|
||||||
|
router_id = ospf.get('router-id', '0.0.0.0')
|
||||||
|
areas = ospf.get('areas', {}).get('area', [])
|
||||||
|
|
||||||
|
# OSPF Process header
|
||||||
|
print(f" OSPF Routing Process, Router ID: {router_id}")
|
||||||
|
print(f" Number of areas attached to this router: {len(areas)}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if not areas:
|
||||||
|
print("No areas configured")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Display detailed area information
|
||||||
|
for area in areas:
|
||||||
|
area_id = area.get('area-id', '0.0.0.0')
|
||||||
|
area_type = area.get('area-type', 'ietf-ospf:normal-area')
|
||||||
|
|
||||||
|
# Determine area type display
|
||||||
|
if area_id == "0.0.0.0":
|
||||||
|
area_label = f" Area ID: {area_id} (Backbone)"
|
||||||
|
else:
|
||||||
|
if 'nssa' in area_type.lower():
|
||||||
|
area_label = f" Area ID: {area_id} (NSSA)"
|
||||||
|
elif 'stub' in area_type.lower():
|
||||||
|
area_label = f" Area ID: {area_id} (Stub)"
|
||||||
|
else:
|
||||||
|
area_label = f" Area ID: {area_id}"
|
||||||
|
|
||||||
|
print(area_label)
|
||||||
|
|
||||||
|
interfaces = area.get('interfaces', {}).get('interface', [])
|
||||||
|
interface_count = len(interfaces)
|
||||||
|
|
||||||
|
# Count active interfaces and neighbors
|
||||||
|
active_count = 0
|
||||||
|
neighbor_count = 0
|
||||||
|
fully_adjacent = 0
|
||||||
|
|
||||||
|
for iface in interfaces:
|
||||||
|
# Count active interfaces (enabled and up)
|
||||||
|
if iface.get('enabled', False):
|
||||||
|
state = iface.get('state', 'down')
|
||||||
|
if state != 'down':
|
||||||
|
active_count += 1
|
||||||
|
|
||||||
|
# Count neighbors
|
||||||
|
neighbors = iface.get('neighbors', {}).get('neighbor', [])
|
||||||
|
neighbor_count += len(neighbors)
|
||||||
|
|
||||||
|
# Count fully adjacent neighbors (full state)
|
||||||
|
for neighbor in neighbors:
|
||||||
|
if neighbor.get('state', '') == 'full':
|
||||||
|
fully_adjacent += 1
|
||||||
|
|
||||||
|
print(f" Number of interfaces in this area: Total: {interface_count}, Active: {active_count}")
|
||||||
|
print(f" Number of fully adjacent neighbors in this area: {fully_adjacent}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def show_ospf_interfaces(json_data):
|
||||||
|
"""Show OSPF interface information"""
|
||||||
|
routing = json_data.get('ietf-routing:routing', {})
|
||||||
|
protocols = routing.get('control-plane-protocols', {}).get('control-plane-protocol', [])
|
||||||
|
|
||||||
|
ospf_instance = None
|
||||||
|
for protocol in protocols:
|
||||||
|
if 'ietf-ospf:ospf' in protocol:
|
||||||
|
ospf_instance = protocol
|
||||||
|
break
|
||||||
|
|
||||||
|
if not ospf_instance:
|
||||||
|
print("OSPF is not configured or running")
|
||||||
|
return
|
||||||
|
|
||||||
|
ospf = ospf_instance.get('ietf-ospf:ospf', {})
|
||||||
|
router_id = ospf.get('router-id', '0.0.0.0')
|
||||||
|
areas = ospf.get('areas', {}).get('area', [])
|
||||||
|
|
||||||
|
if not areas:
|
||||||
|
print("No OSPF interfaces configured")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if specific interface requested
|
||||||
|
requested_ifname = json_data.get('_ifname')
|
||||||
|
|
||||||
|
# Collect all interfaces from all areas
|
||||||
|
all_interfaces = []
|
||||||
|
for area in areas:
|
||||||
|
area_id = area.get('area-id', '0.0.0.0')
|
||||||
|
interfaces = area.get('interfaces', {}).get('interface', [])
|
||||||
|
for iface in interfaces:
|
||||||
|
iface['_area_id'] = area_id # Add area context
|
||||||
|
all_interfaces.append(iface)
|
||||||
|
|
||||||
|
if not all_interfaces:
|
||||||
|
print("No OSPF interfaces found")
|
||||||
|
return
|
||||||
|
|
||||||
|
# If specific interface requested, show detailed view
|
||||||
|
if requested_ifname:
|
||||||
|
target_iface = None
|
||||||
|
for iface in all_interfaces:
|
||||||
|
if iface.get('name') == requested_ifname:
|
||||||
|
target_iface = iface
|
||||||
|
break
|
||||||
|
|
||||||
|
if not target_iface:
|
||||||
|
print(f"Interface {requested_ifname} not found in OSPF")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Display detailed interface information (vtysh-style)
|
||||||
|
name = target_iface.get('name', 'unknown')
|
||||||
|
area_id = target_iface.get('_area_id', '0.0.0.0')
|
||||||
|
state = target_iface.get('state', 'down')
|
||||||
|
cost = target_iface.get('cost', 0)
|
||||||
|
priority = target_iface.get('priority', 1)
|
||||||
|
iface_type = target_iface.get('interface-type', 'unknown')
|
||||||
|
hello_interval = target_iface.get('hello-interval', 10)
|
||||||
|
dead_interval = target_iface.get('dead-interval', 40)
|
||||||
|
retransmit_interval = target_iface.get('retransmit-interval', 5)
|
||||||
|
transmit_delay = target_iface.get('transmit-delay', 1)
|
||||||
|
dr_id = target_iface.get('dr-router-id', '-')
|
||||||
|
dr_addr = target_iface.get('dr-ip-addr', '-')
|
||||||
|
bdr_id = target_iface.get('bdr-router-id', '-')
|
||||||
|
bdr_addr = target_iface.get('bdr-ip-addr', '-')
|
||||||
|
neighbors = target_iface.get('neighbors', {}).get('neighbor', [])
|
||||||
|
|
||||||
|
# Get interface IP address from interfaces data
|
||||||
|
ip_address = None
|
||||||
|
broadcast = None
|
||||||
|
if json_data.get('_interfaces'):
|
||||||
|
interfaces = json_data['_interfaces'].get('ietf-interfaces:interfaces', {}).get('interface', [])
|
||||||
|
for iface in interfaces:
|
||||||
|
if iface.get('name') == name:
|
||||||
|
# Get IPv4 address
|
||||||
|
ipv4 = iface.get('ietf-ip:ipv4', {})
|
||||||
|
addresses = ipv4.get('address', [])
|
||||||
|
if addresses:
|
||||||
|
addr = addresses[0]
|
||||||
|
ip = addr.get('ip', '')
|
||||||
|
prefix_len = addr.get('prefix-length', 0)
|
||||||
|
if ip and prefix_len:
|
||||||
|
ip_address = f"{ip}/{prefix_len}"
|
||||||
|
# Calculate broadcast (simple approximation for /30 networks)
|
||||||
|
import ipaddress
|
||||||
|
try:
|
||||||
|
net = ipaddress.IPv4Network(ip_address, strict=False)
|
||||||
|
broadcast = str(net.broadcast_address)
|
||||||
|
except:
|
||||||
|
broadcast = None
|
||||||
|
break
|
||||||
|
|
||||||
|
# Get BFD information from routing data
|
||||||
|
bfd_detect_mult = None
|
||||||
|
bfd_rx_interval = None
|
||||||
|
bfd_tx_interval = None
|
||||||
|
bfd_state = None
|
||||||
|
if json_data.get('_routing'):
|
||||||
|
routing = json_data['_routing'].get('ietf-routing:routing', {})
|
||||||
|
protocols = routing.get('control-plane-protocols', {}).get('control-plane-protocol', [])
|
||||||
|
for proto in protocols:
|
||||||
|
if proto.get('type') == 'infix-routing:bfdv1':
|
||||||
|
bfd = proto.get('ietf-bfd:bfd', {})
|
||||||
|
ip_sh = bfd.get('ietf-bfd-ip-sh:ip-sh', {})
|
||||||
|
sessions_container = ip_sh.get('sessions', {})
|
||||||
|
sessions = sessions_container.get('session', [])
|
||||||
|
# Find BFD session for this interface
|
||||||
|
for session in sessions:
|
||||||
|
if session.get('interface') == name:
|
||||||
|
session_running = session.get('session-running', {})
|
||||||
|
# Get session state
|
||||||
|
bfd_state = session_running.get('local-state', 'down')
|
||||||
|
# Get detection multiplier and intervals
|
||||||
|
rx_interval = session_running.get('negotiated-rx-interval', 0)
|
||||||
|
tx_interval = session_running.get('negotiated-tx-interval', 0)
|
||||||
|
detection_time = session_running.get('detection-time', 0)
|
||||||
|
if rx_interval and detection_time:
|
||||||
|
bfd_detect_mult = detection_time // rx_interval
|
||||||
|
bfd_rx_interval = rx_interval // 1000 if rx_interval else 0 # Convert μs to ms
|
||||||
|
bfd_tx_interval = tx_interval // 1000 if tx_interval else 0
|
||||||
|
break
|
||||||
|
break
|
||||||
|
|
||||||
|
# State display
|
||||||
|
state_display = state.upper() if state in ['dr', 'bdr'] else state.capitalize()
|
||||||
|
if state == 'dr-other':
|
||||||
|
state_display = 'DROther'
|
||||||
|
|
||||||
|
# Network type display (match FRR/Cisco format)
|
||||||
|
network_type_map = {
|
||||||
|
'point-to-point': 'POINTOPOINT',
|
||||||
|
'broadcast': 'BROADCAST',
|
||||||
|
'non-broadcast': 'NBMA'
|
||||||
|
}
|
||||||
|
network_type = network_type_map.get(iface_type, iface_type.upper())
|
||||||
|
|
||||||
|
print(f"{name} is up")
|
||||||
|
if ip_address:
|
||||||
|
broadcast_str = f", Broadcast {broadcast}" if broadcast else ""
|
||||||
|
print(f" Internet Address {ip_address}{broadcast_str}, Area {area_id}")
|
||||||
|
else:
|
||||||
|
print(f" Internet Address (not available), Area {area_id}")
|
||||||
|
print(f" Router ID {router_id}, Network Type {network_type}, Cost: {cost}")
|
||||||
|
print(f" Transmit Delay is {transmit_delay} sec, State {state_display}, Priority {priority}")
|
||||||
|
|
||||||
|
if dr_id != '-':
|
||||||
|
print(f" Designated Router (ID) {dr_id}, Interface Address {dr_addr}")
|
||||||
|
if bdr_id != '-':
|
||||||
|
print(f" Backup Designated Router (ID) {bdr_id}, Interface Address {bdr_addr}")
|
||||||
|
|
||||||
|
print(f" Timer intervals configured, Hello {hello_interval}s, Dead {dead_interval}s, Retransmit {retransmit_interval}")
|
||||||
|
|
||||||
|
# Count adjacent neighbors
|
||||||
|
adjacent_count = sum(1 for n in neighbors if n.get('state') == 'full')
|
||||||
|
print(f" Neighbor Count is {len(neighbors)}, Adjacent neighbor count is {adjacent_count}")
|
||||||
|
|
||||||
|
# Show BFD status if available
|
||||||
|
if bfd_detect_mult is not None and bfd_rx_interval is not None and bfd_tx_interval is not None:
|
||||||
|
# Format state for display
|
||||||
|
state_display_map = {
|
||||||
|
'up': 'Up (two-way connection established)',
|
||||||
|
'down': 'Down (no connection)',
|
||||||
|
'init': 'Init (initializing, no two-way yet)',
|
||||||
|
'adminDown': 'AdminDown (administratively disabled)'
|
||||||
|
}
|
||||||
|
bfd_state_display = state_display_map.get(bfd_state, bfd_state.capitalize() if bfd_state else 'Unknown')
|
||||||
|
print(f" BFD: Status: {bfd_state_display}")
|
||||||
|
print(f" Detect Multiplier: {bfd_detect_mult}, Min Rx interval: {bfd_rx_interval}, Min Tx interval: {bfd_tx_interval}")
|
||||||
|
else:
|
||||||
|
print(f" BFD: Disabled")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# Display table view (no specific interface)
|
||||||
|
hdr = f"{'INTERFACE':<12} {'AREA':<12} {'STATE':<10} {'COST':<6} {'PRI':<4} {'DR':<15} {'BDR':<15} {'NBRS':<5}"
|
||||||
|
print(Decore.invert(hdr))
|
||||||
|
|
||||||
|
for iface in all_interfaces:
|
||||||
|
name = iface.get('name', 'unknown')
|
||||||
|
area_id = iface.get('_area_id', '0.0.0.0')
|
||||||
|
state = iface.get('state', 'down')
|
||||||
|
cost = iface.get('cost', 0)
|
||||||
|
priority = iface.get('priority', 1)
|
||||||
|
dr_id = iface.get('dr-router-id', '-')
|
||||||
|
bdr_id = iface.get('bdr-router-id', '-')
|
||||||
|
neighbors = iface.get('neighbors', {}).get('neighbor', [])
|
||||||
|
nbr_count = len(neighbors)
|
||||||
|
|
||||||
|
# Capitalize state nicely
|
||||||
|
state_display = state.upper() if state in ['dr', 'bdr'] else state.capitalize()
|
||||||
|
if state == 'dr-other':
|
||||||
|
state_display = 'DROther'
|
||||||
|
|
||||||
|
# Shorten router IDs for display
|
||||||
|
dr_display = dr_id if dr_id != '-' else '-'
|
||||||
|
bdr_display = bdr_id if bdr_id != '-' else '-'
|
||||||
|
|
||||||
|
print(f"{name:<12} {area_id:<12} {state_display:<10} {cost:<6} {priority:<4} {dr_display:<15} {bdr_display:<15} {nbr_count:<5}")
|
||||||
|
|
||||||
|
|
||||||
|
def show_ospf_neighbor(json_data):
|
||||||
|
"""Show OSPF neighbor information"""
|
||||||
|
routing = json_data.get('ietf-routing:routing', {})
|
||||||
|
protocols = routing.get('control-plane-protocols', {}).get('control-plane-protocol', [])
|
||||||
|
|
||||||
|
ospf_instance = None
|
||||||
|
for protocol in protocols:
|
||||||
|
if 'ietf-ospf:ospf' in protocol:
|
||||||
|
ospf_instance = protocol
|
||||||
|
break
|
||||||
|
|
||||||
|
if not ospf_instance:
|
||||||
|
print("OSPF is not configured or running")
|
||||||
|
return
|
||||||
|
|
||||||
|
ospf = ospf_instance.get('ietf-ospf:ospf', {})
|
||||||
|
areas = ospf.get('areas', {}).get('area', [])
|
||||||
|
|
||||||
|
if not areas:
|
||||||
|
print("No OSPF areas configured")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Collect all neighbors from all interfaces in all areas
|
||||||
|
all_neighbors = []
|
||||||
|
for area in areas:
|
||||||
|
area_id = area.get('area-id', '0.0.0.0')
|
||||||
|
interfaces = area.get('interfaces', {}).get('interface', [])
|
||||||
|
for iface in interfaces:
|
||||||
|
iface_name = iface.get('name', 'unknown')
|
||||||
|
neighbors = iface.get('neighbors', {}).get('neighbor', [])
|
||||||
|
for neighbor in neighbors:
|
||||||
|
neighbor['_interface'] = iface_name
|
||||||
|
neighbor['_area_id'] = area_id
|
||||||
|
all_neighbors.append(neighbor)
|
||||||
|
|
||||||
|
if not all_neighbors:
|
||||||
|
print("No OSPF neighbors found")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Display table header with PRI and UPTIME columns
|
||||||
|
hdr = f"{'NEIGHBOR ID':<16} {'PRI':<4} {'STATE':<12} {'UPTIME':<10} {'DEAD TIME':<10} {'ADDRESS':<16} {'INTERFACE':<18} {'AREA':<12}"
|
||||||
|
print(Decore.invert(hdr))
|
||||||
|
|
||||||
|
for neighbor in all_neighbors:
|
||||||
|
neighbor_id = neighbor.get('neighbor-router-id', '0.0.0.0')
|
||||||
|
address = neighbor.get('address', '0.0.0.0')
|
||||||
|
state = neighbor.get('state', 'down')
|
||||||
|
priority = neighbor.get('priority', 1) # Default priority is 1
|
||||||
|
uptime = neighbor.get('infix-routing:uptime', 0)
|
||||||
|
dead_timer = neighbor.get('dead-timer', 0)
|
||||||
|
# Use interface-name (e.g., "e5:10.0.23.1") if available, fallback to interface name
|
||||||
|
interface = neighbor.get('infix-routing:interface-name', neighbor.get('_interface', 'unknown'))
|
||||||
|
area_id = neighbor.get('_area_id', '0.0.0.0')
|
||||||
|
role = neighbor.get('infix-routing:role', '')
|
||||||
|
|
||||||
|
# Capitalize state and add role if present
|
||||||
|
state_base = state.capitalize() if state != '2-way' else '2-Way'
|
||||||
|
if role and state_base == 'Full':
|
||||||
|
state_display = f"{state_base}/{role}"
|
||||||
|
else:
|
||||||
|
state_display = state_base
|
||||||
|
|
||||||
|
# Format uptime (convert seconds to human-readable format)
|
||||||
|
if uptime > 0:
|
||||||
|
days = uptime // 86400
|
||||||
|
hours = (uptime % 86400) // 3600
|
||||||
|
minutes = (uptime % 3600) // 60
|
||||||
|
seconds = uptime % 60
|
||||||
|
|
||||||
|
if days > 0:
|
||||||
|
uptime_display = f"{days}d{hours:02d}h{minutes:02d}m"
|
||||||
|
elif hours > 0:
|
||||||
|
uptime_display = f"{hours}h{minutes:02d}m{seconds:02d}s"
|
||||||
|
elif minutes > 0:
|
||||||
|
uptime_display = f"{minutes}m{seconds:02d}s"
|
||||||
|
else:
|
||||||
|
uptime_display = f"{seconds}s"
|
||||||
|
else:
|
||||||
|
uptime_display = "-"
|
||||||
|
|
||||||
|
# Format dead timer
|
||||||
|
dead_display = f"{dead_timer}s" if dead_timer > 0 else "-"
|
||||||
|
|
||||||
|
print(f"{neighbor_id:<16} {priority:<4} {state_display:<12} {uptime_display:<10} {dead_display:<10} {address:<16} {interface:<18} {area_id:<12}")
|
||||||
|
|
||||||
|
|
||||||
|
def show_ospf_routes(json_data):
|
||||||
|
"""Show OSPF routing table (local-rib)"""
|
||||||
|
routing = json_data.get('ietf-routing:routing', {})
|
||||||
|
protocols = routing.get('control-plane-protocols', {}).get('control-plane-protocol', [])
|
||||||
|
|
||||||
|
ospf_instance = None
|
||||||
|
for protocol in protocols:
|
||||||
|
if 'ietf-ospf:ospf' in protocol:
|
||||||
|
ospf_instance = protocol
|
||||||
|
break
|
||||||
|
|
||||||
|
if not ospf_instance:
|
||||||
|
print("OSPF is not configured or running")
|
||||||
|
return
|
||||||
|
|
||||||
|
ospf = ospf_instance.get('ietf-ospf:ospf', {})
|
||||||
|
local_rib = ospf.get('local-rib', {})
|
||||||
|
routes = local_rib.get('route', [])
|
||||||
|
|
||||||
|
if not routes:
|
||||||
|
print("No OSPF routes in local RIB")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Display table header (AREA column from infix-routing augmentation)
|
||||||
|
hdr = f"{'DESTINATION':<20} {'TYPE':<10} {'AREA':<12} {'METRIC':<8} {'NEXT HOP':<16} {'INTERFACE':<12}"
|
||||||
|
print(Decore.invert(hdr))
|
||||||
|
|
||||||
|
for route in routes:
|
||||||
|
prefix = route.get('prefix', '0.0.0.0/0')
|
||||||
|
route_type = route.get('route-type', 'unknown')
|
||||||
|
# Check for area-id with infix-routing prefix (augmented field)
|
||||||
|
area_id = route.get('infix-routing:area-id', route.get('area-id', '-'))
|
||||||
|
metric = route.get('metric', 0)
|
||||||
|
|
||||||
|
# Simplify route type display
|
||||||
|
type_map = {
|
||||||
|
'intra-area': 'Intra',
|
||||||
|
'inter-area': 'Inter',
|
||||||
|
'external-1': 'Ext-1',
|
||||||
|
'external-2': 'Ext-2',
|
||||||
|
'nssa-1': 'NSSA-1',
|
||||||
|
'nssa-2': 'NSSA-2'
|
||||||
|
}
|
||||||
|
type_display = type_map.get(route_type, route_type)
|
||||||
|
|
||||||
|
# Get next hops
|
||||||
|
next_hops_data = route.get('next-hops', {})
|
||||||
|
next_hops = next_hops_data.get('next-hop', [])
|
||||||
|
|
||||||
|
if not next_hops:
|
||||||
|
print(f"{prefix:<20} {type_display:<10} {area_id:<12} {metric:<8} {'-':<16} {'-':<12}")
|
||||||
|
else:
|
||||||
|
# Display first next hop on same line as route
|
||||||
|
first_hop = next_hops[0]
|
||||||
|
next_hop_addr = first_hop.get('next-hop', '-')
|
||||||
|
outgoing_iface = first_hop.get('outgoing-interface', '-')
|
||||||
|
|
||||||
|
print(f"{prefix:<20} {type_display:<10} {area_id:<12} {metric:<8} {next_hop_addr:<16} {outgoing_iface:<12}")
|
||||||
|
|
||||||
|
# Display additional next hops indented
|
||||||
|
for hop in next_hops[1:]:
|
||||||
|
next_hop_addr = hop.get('next-hop', '-')
|
||||||
|
outgoing_iface = hop.get('outgoing-interface', '-')
|
||||||
|
print(f"{'':<52} {next_hop_addr:<16} {outgoing_iface:<12}")
|
||||||
|
|
||||||
|
|
||||||
|
def show_bfd_status(json_data):
|
||||||
|
"""Show BFD status summary"""
|
||||||
|
routing = json_data.get('ietf-routing:routing', {})
|
||||||
|
protocols = routing.get('control-plane-protocols', {})
|
||||||
|
protocol_list = protocols.get('control-plane-protocol', [])
|
||||||
|
|
||||||
|
bfd_protocol = None
|
||||||
|
for proto in protocol_list:
|
||||||
|
if proto.get('type') == 'infix-routing:bfdv1':
|
||||||
|
bfd_protocol = proto
|
||||||
|
break
|
||||||
|
|
||||||
|
if not bfd_protocol:
|
||||||
|
print("BFD is not enabled")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("Is enabled, single-hop (ospf)")
|
||||||
|
|
||||||
|
|
||||||
|
def show_bfd_peers(json_data):
|
||||||
|
"""Show BFD peer sessions in detailed format"""
|
||||||
|
routing = json_data.get('ietf-routing:routing', {})
|
||||||
|
protocols = routing.get('control-plane-protocols', {})
|
||||||
|
protocol_list = protocols.get('control-plane-protocol', [])
|
||||||
|
|
||||||
|
bfd_protocol = None
|
||||||
|
for proto in protocol_list:
|
||||||
|
if proto.get('type') == 'infix-routing:bfdv1':
|
||||||
|
bfd_protocol = proto
|
||||||
|
break
|
||||||
|
|
||||||
|
if not bfd_protocol:
|
||||||
|
print("BFD is not enabled")
|
||||||
|
return
|
||||||
|
|
||||||
|
bfd = bfd_protocol.get('ietf-bfd:bfd', {})
|
||||||
|
ip_sh = bfd.get('ietf-bfd-ip-sh:ip-sh', {})
|
||||||
|
sessions_container = ip_sh.get('sessions', {})
|
||||||
|
sessions = sessions_container.get('session', [])
|
||||||
|
|
||||||
|
if not sessions:
|
||||||
|
print("No BFD sessions found")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("BFD Peers:")
|
||||||
|
for session in sessions:
|
||||||
|
peer = session.get('dest-addr', 'unknown')
|
||||||
|
interface = session.get('interface', 'unknown')
|
||||||
|
local_disc = session.get('local-discriminator', 0)
|
||||||
|
remote_disc = session.get('remote-discriminator', 0)
|
||||||
|
|
||||||
|
# Get session running state
|
||||||
|
session_running = session.get('session-running', {})
|
||||||
|
local_state = session_running.get('local-state', 'unknown')
|
||||||
|
remote_state = session_running.get('remote-state', 'unknown')
|
||||||
|
local_diag = session_running.get('local-diagnostic', 'unknown')
|
||||||
|
detection_mode = session_running.get('detection-mode', 'unknown')
|
||||||
|
detection_time = session_running.get('detection-time', 0)
|
||||||
|
rx_interval = session_running.get('negotiated-rx-interval', 0)
|
||||||
|
tx_interval = session_running.get('negotiated-tx-interval', 0)
|
||||||
|
|
||||||
|
# Convert microseconds to milliseconds for display
|
||||||
|
rx_ms = rx_interval // 1000 if rx_interval else 0
|
||||||
|
tx_ms = tx_interval // 1000 if tx_interval else 0
|
||||||
|
detect_mult = detection_time // rx_interval if rx_interval and detection_time else 0
|
||||||
|
|
||||||
|
print(f" peer {peer}")
|
||||||
|
print(f" ID: {local_disc}")
|
||||||
|
print(f" Remote ID: {remote_disc}")
|
||||||
|
print(f" Status: {local_state}")
|
||||||
|
print(f" Diagnostics: {local_diag}")
|
||||||
|
print(f" Remote diagnostics: {local_diag}")
|
||||||
|
print(f" Peer Type: dynamic")
|
||||||
|
print(f" Local timers:")
|
||||||
|
print(f" Detect-multiplier: {detect_mult}")
|
||||||
|
print(f" Receive interval: {rx_ms}ms")
|
||||||
|
print(f" Transmission interval: {tx_ms}ms")
|
||||||
|
print(f" Echo transmission interval: disabled")
|
||||||
|
print(f" Remote timers:")
|
||||||
|
print(f" Detect-multiplier: {detect_mult}")
|
||||||
|
print(f" Receive interval: {rx_ms}ms")
|
||||||
|
print(f" Transmission interval: {tx_ms}ms")
|
||||||
|
print(f" Echo transmission interval: disabled")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def show_bfd_peers_brief(json_data):
|
||||||
|
"""Show BFD peers in brief table format"""
|
||||||
|
routing = json_data.get('ietf-routing:routing', {})
|
||||||
|
protocols = routing.get('control-plane-protocols', {})
|
||||||
|
protocol_list = protocols.get('control-plane-protocol', [])
|
||||||
|
|
||||||
|
# Find BFD protocol instance
|
||||||
|
bfd_protocol = None
|
||||||
|
for proto in protocol_list:
|
||||||
|
if proto.get('type') == 'infix-routing:bfdv1':
|
||||||
|
bfd_protocol = proto
|
||||||
|
break
|
||||||
|
|
||||||
|
if not bfd_protocol:
|
||||||
|
print("BFD is not enabled")
|
||||||
|
return
|
||||||
|
|
||||||
|
bfd = bfd_protocol.get('ietf-bfd:bfd', {})
|
||||||
|
ip_sh = bfd.get('ietf-bfd-ip-sh:ip-sh', {})
|
||||||
|
sessions_container = ip_sh.get('sessions', {})
|
||||||
|
sessions = sessions_container.get('session', [])
|
||||||
|
|
||||||
|
if not sessions:
|
||||||
|
print("No BFD sessions found")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Build interface to IP mapping by looking up from peer address
|
||||||
|
# We need to extract the local IP from the same subnet as the peer
|
||||||
|
ifaces_data = json_data.get('_interfaces', {})
|
||||||
|
if_ip_map = {}
|
||||||
|
|
||||||
|
if ifaces_data:
|
||||||
|
interfaces = ifaces_data.get('ietf-interfaces:interfaces', {}).get('interface', [])
|
||||||
|
for iface in interfaces:
|
||||||
|
ifname = iface.get('name', '')
|
||||||
|
ipv4 = iface.get('ietf-ip:ipv4', {})
|
||||||
|
addresses = ipv4.get('address', [])
|
||||||
|
for addr_entry in addresses:
|
||||||
|
ip = addr_entry.get('ip', '')
|
||||||
|
prefix_len = addr_entry.get('prefix-length', 0)
|
||||||
|
if ip and ifname:
|
||||||
|
if_ip_map[ifname] = ip
|
||||||
|
|
||||||
|
print(f"Session count: {len(sessions)}")
|
||||||
|
hdr = f"{'SESSION ID':<16} {'LOCAL':<22} {'PEER':<22} {'STATE':<10}"
|
||||||
|
print(Decore.invert(hdr))
|
||||||
|
|
||||||
|
for session in sessions:
|
||||||
|
local_disc = session.get('local-discriminator', 0)
|
||||||
|
interface = session.get('interface', '-')
|
||||||
|
peer = session.get('dest-addr', '-')
|
||||||
|
|
||||||
|
# Get session running state
|
||||||
|
session_running = session.get('session-running', {})
|
||||||
|
local_state = session_running.get('local-state', 'unknown')
|
||||||
|
|
||||||
|
# Format local address as interface:ip
|
||||||
|
local_ip = if_ip_map.get(interface, '')
|
||||||
|
if local_ip:
|
||||||
|
local_addr = f"{interface}:{local_ip}"
|
||||||
|
else:
|
||||||
|
local_addr = interface
|
||||||
|
|
||||||
|
print(f"{local_disc:<16} {local_addr:<22} {peer:<22} {local_state:<10}")
|
||||||
|
|
||||||
|
|
||||||
|
def show_bfd_peer(json_data):
|
||||||
|
"""Show specific BFD peer details"""
|
||||||
|
peer_addr = json_data.get('_peer_addr')
|
||||||
|
if not peer_addr:
|
||||||
|
print("Error: No peer address specified")
|
||||||
|
return
|
||||||
|
|
||||||
|
routing = json_data.get('ietf-routing:routing', {})
|
||||||
|
protocols = routing.get('control-plane-protocols', {})
|
||||||
|
protocol_list = protocols.get('control-plane-protocol', [])
|
||||||
|
|
||||||
|
# Find BFD protocol instance
|
||||||
|
bfd_protocol = None
|
||||||
|
for proto in protocol_list:
|
||||||
|
if proto.get('type') == 'infix-routing:bfdv1':
|
||||||
|
bfd_protocol = proto
|
||||||
|
break
|
||||||
|
|
||||||
|
if not bfd_protocol:
|
||||||
|
print("BFD is not enabled")
|
||||||
|
return
|
||||||
|
|
||||||
|
bfd = bfd_protocol.get('ietf-bfd:bfd', {})
|
||||||
|
ip_sh = bfd.get('ietf-bfd-ip-sh:ip-sh', {})
|
||||||
|
sessions_container = ip_sh.get('sessions', {})
|
||||||
|
sessions = sessions_container.get('session', [])
|
||||||
|
|
||||||
|
# Find the specific peer
|
||||||
|
target_session = None
|
||||||
|
for session in sessions:
|
||||||
|
if session.get('dest-addr') == peer_addr:
|
||||||
|
target_session = session
|
||||||
|
break
|
||||||
|
|
||||||
|
if not target_session:
|
||||||
|
print(f"BFD peer {peer_addr} not found")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Display single peer details
|
||||||
|
peer = target_session.get('dest-addr', 'unknown')
|
||||||
|
interface = target_session.get('interface', 'unknown')
|
||||||
|
local_disc = target_session.get('local-discriminator', 0)
|
||||||
|
remote_disc = target_session.get('remote-discriminator', 0)
|
||||||
|
|
||||||
|
# Get session running state
|
||||||
|
session_running = target_session.get('session-running', {})
|
||||||
|
local_state = session_running.get('local-state', 'unknown')
|
||||||
|
remote_state = session_running.get('remote-state', 'unknown')
|
||||||
|
local_diag = session_running.get('local-diagnostic', 'unknown')
|
||||||
|
detection_mode = session_running.get('detection-mode', 'unknown')
|
||||||
|
detection_time = session_running.get('detection-time', 0)
|
||||||
|
rx_interval = session_running.get('negotiated-rx-interval', 0)
|
||||||
|
tx_interval = session_running.get('negotiated-tx-interval', 0)
|
||||||
|
|
||||||
|
# Convert microseconds to milliseconds for display
|
||||||
|
rx_ms = rx_interval // 1000 if rx_interval else 0
|
||||||
|
tx_ms = tx_interval // 1000 if tx_interval else 0
|
||||||
|
detect_mult = detection_time // rx_interval if rx_interval and detection_time else 0
|
||||||
|
|
||||||
|
print("BFD Peer:")
|
||||||
|
print(f" peer {peer}")
|
||||||
|
print(f" ID: {local_disc}")
|
||||||
|
print(f" Remote ID: {remote_disc}")
|
||||||
|
print(f" Status: {local_state}")
|
||||||
|
print(f" Diagnostics: {local_diag}")
|
||||||
|
print(f" Remote diagnostics: {local_diag}")
|
||||||
|
print(f" Peer Type: dynamic")
|
||||||
|
print(f" Local timers:")
|
||||||
|
print(f" Detect-multiplier: {detect_mult}")
|
||||||
|
print(f" Receive interval: {rx_ms}ms")
|
||||||
|
print(f" Transmission interval: {tx_ms}ms")
|
||||||
|
print(f" Echo transmission interval: disabled")
|
||||||
|
print(f" Remote timers:")
|
||||||
|
print(f" Detect-multiplier: {detect_mult}")
|
||||||
|
print(f" Receive interval: {rx_ms}ms")
|
||||||
|
print(f" Transmission interval: {tx_ms}ms")
|
||||||
|
print(f" Echo transmission interval: disabled")
|
||||||
|
|
||||||
|
|
||||||
|
def show_bfd(json_data):
|
||||||
|
"""Legacy function - redirect to show_bfd_peers_brief for backward compatibility"""
|
||||||
|
show_bfd_peers_brief(json_data)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
global UNIT_TEST
|
global UNIT_TEST
|
||||||
|
|
||||||
@@ -2861,6 +3525,17 @@ def main():
|
|||||||
|
|
||||||
subparsers.add_parser('show-ntp', help='Show NTP sources')
|
subparsers.add_parser('show-ntp', help='Show NTP sources')
|
||||||
|
|
||||||
|
subparsers.add_parser('show-bfd', help='Show BFD sessions')
|
||||||
|
subparsers.add_parser('show-bfd-status', help='Show BFD status')
|
||||||
|
subparsers.add_parser('show-bfd-peers', help='Show BFD peers')
|
||||||
|
subparsers.add_parser('show-bfd-peers-brief', help='Show BFD peers brief')
|
||||||
|
subparsers.add_parser('show-bfd-peer', help='Show BFD peer')
|
||||||
|
|
||||||
|
subparsers.add_parser('show-ospf', help='Show OSPF instance information')
|
||||||
|
subparsers.add_parser('show-ospf-interfaces', help='Show OSPF interfaces')
|
||||||
|
subparsers.add_parser('show-ospf-neighbor', help='Show OSPF neighbors')
|
||||||
|
subparsers.add_parser('show-ospf-routes', help='Show OSPF routing table')
|
||||||
|
|
||||||
subparsers.add_parser('show-routing-table', help='Show the routing table') \
|
subparsers.add_parser('show-routing-table', help='Show the routing table') \
|
||||||
.add_argument('-i', '--ip', required=True, help='IPv4 or IPv6 address')
|
.add_argument('-i', '--ip', required=True, help='IPv4 or IPv6 address')
|
||||||
|
|
||||||
@@ -2896,6 +3571,24 @@ def main():
|
|||||||
show_firewall_service(json_data, args.name)
|
show_firewall_service(json_data, args.name)
|
||||||
elif args.command == "show-ntp":
|
elif args.command == "show-ntp":
|
||||||
show_ntp(json_data)
|
show_ntp(json_data)
|
||||||
|
elif args.command == "show-bfd":
|
||||||
|
show_bfd(json_data)
|
||||||
|
elif args.command == "show-bfd-status":
|
||||||
|
show_bfd_status(json_data)
|
||||||
|
elif args.command == "show-bfd-peers":
|
||||||
|
show_bfd_peers(json_data)
|
||||||
|
elif args.command == "show-bfd-peers-brief":
|
||||||
|
show_bfd_peers_brief(json_data)
|
||||||
|
elif args.command == "show-bfd-peer":
|
||||||
|
show_bfd_peer(json_data)
|
||||||
|
elif args.command == "show-ospf":
|
||||||
|
show_ospf(json_data)
|
||||||
|
elif args.command == "show-ospf-interfaces":
|
||||||
|
show_ospf_interfaces(json_data)
|
||||||
|
elif args.command == "show-ospf-neighbor":
|
||||||
|
show_ospf_neighbor(json_data)
|
||||||
|
elif args.command == "show-ospf-routes":
|
||||||
|
show_ospf_routes(json_data)
|
||||||
elif args.command == "show-routing-table":
|
elif args.command == "show-routing-table":
|
||||||
show_routing_table(json_data, args.ip)
|
show_routing_table(json_data, args.ip)
|
||||||
elif args.command == "show-software":
|
elif args.command == "show-software":
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ def main():
|
|||||||
elif args.model == 'infix-firewall':
|
elif args.model == 'infix-firewall':
|
||||||
from . import infix_firewall
|
from . import infix_firewall
|
||||||
yang_data = infix_firewall.operational()
|
yang_data = infix_firewall.operational()
|
||||||
|
elif args.model == 'ietf-bfd-ip-sh':
|
||||||
|
from . import ietf_bfd_ip_sh
|
||||||
|
yang_data = ietf_bfd_ip_sh.operational()
|
||||||
else:
|
else:
|
||||||
common.LOG.warning("Unsupported model %s", args.model)
|
common.LOG.warning("Unsupported model %s", args.model)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
from .common import insert
|
||||||
|
from .host import HOST
|
||||||
|
|
||||||
|
|
||||||
|
def frr_to_ietf_state(state):
|
||||||
|
"""Convert FRR BFD state to IETF BFD state"""
|
||||||
|
state_map = {
|
||||||
|
"up": "up",
|
||||||
|
"down": "down",
|
||||||
|
"init": "init",
|
||||||
|
"adminDown": "adminDown"
|
||||||
|
}
|
||||||
|
return state_map.get(state, "down")
|
||||||
|
|
||||||
|
|
||||||
|
def add_sessions(control_protocols):
|
||||||
|
"""Fetch BFD session data from FRR"""
|
||||||
|
cmd = ['vtysh', '-c', 'show bfd peers json']
|
||||||
|
data = HOST.run_json(cmd, default=[])
|
||||||
|
if not data:
|
||||||
|
return # No BFD sessions available
|
||||||
|
|
||||||
|
control_protocol = {}
|
||||||
|
control_protocol["type"] = "infix-routing:bfdv1"
|
||||||
|
control_protocol["name"] = "bfd"
|
||||||
|
control_protocol["ietf-bfd:bfd"] = {}
|
||||||
|
control_protocol["ietf-bfd:bfd"]["ietf-bfd-ip-sh:ip-sh"] = {}
|
||||||
|
control_protocol["ietf-bfd:bfd"]["ietf-bfd-ip-sh:ip-sh"]["sessions"] = {}
|
||||||
|
|
||||||
|
sessions = []
|
||||||
|
|
||||||
|
# FRR returns a list of BFD peers
|
||||||
|
for peer in data:
|
||||||
|
# Only process single-hop sessions (multihop == false)
|
||||||
|
if peer.get("multihop", False):
|
||||||
|
continue
|
||||||
|
|
||||||
|
session = {}
|
||||||
|
|
||||||
|
# Key fields: interface and dest-addr
|
||||||
|
session["interface"] = peer.get("interface", "unknown")
|
||||||
|
session["dest-addr"] = peer.get("peer", "0.0.0.0")
|
||||||
|
|
||||||
|
# Operational state fields (config false)
|
||||||
|
# Local and remote discriminators
|
||||||
|
if peer.get("id") is not None:
|
||||||
|
session["local-discriminator"] = peer["id"]
|
||||||
|
if peer.get("remote-id") is not None:
|
||||||
|
session["remote-discriminator"] = peer["remote-id"]
|
||||||
|
|
||||||
|
# Session running state
|
||||||
|
session["session-running"] = {}
|
||||||
|
|
||||||
|
# Local and remote state
|
||||||
|
state = peer.get("status", "down")
|
||||||
|
session["session-running"]["local-state"] = frr_to_ietf_state(state)
|
||||||
|
# Remote state not directly available in FRR output, infer from status
|
||||||
|
session["session-running"]["remote-state"] = frr_to_ietf_state(state)
|
||||||
|
|
||||||
|
# Local diagnostic - not directly available, use "none"
|
||||||
|
session["session-running"]["local-diagnostic"] = "none"
|
||||||
|
|
||||||
|
# Detection mode - FRR uses async mode for OSPF-created sessions
|
||||||
|
session["session-running"]["detection-mode"] = "async-without-echo"
|
||||||
|
|
||||||
|
# Timing intervals (convert milliseconds to microseconds for YANG)
|
||||||
|
if peer.get("receive-interval") is not None:
|
||||||
|
session["session-running"]["negotiated-rx-interval"] = peer["receive-interval"] * 1000
|
||||||
|
if peer.get("transmit-interval") is not None:
|
||||||
|
session["session-running"]["negotiated-tx-interval"] = peer["transmit-interval"] * 1000
|
||||||
|
|
||||||
|
# Detection time (in microseconds)
|
||||||
|
if peer.get("detect-multiplier") is not None and peer.get("receive-interval") is not None:
|
||||||
|
detection_time_ms = peer["detect-multiplier"] * peer["receive-interval"]
|
||||||
|
session["session-running"]["detection-time"] = detection_time_ms * 1000
|
||||||
|
|
||||||
|
# Path type
|
||||||
|
session["path-type"] = "ietf-bfd-types:path-ip-sh"
|
||||||
|
session["ip-encapsulation"] = True
|
||||||
|
|
||||||
|
sessions.append(session)
|
||||||
|
|
||||||
|
if sessions:
|
||||||
|
control_protocol["ietf-bfd:bfd"]["ietf-bfd-ip-sh:ip-sh"]["sessions"]["session"] = sessions
|
||||||
|
insert(control_protocols, "control-plane-protocol", [control_protocol])
|
||||||
|
|
||||||
|
|
||||||
|
def operational():
|
||||||
|
out = {
|
||||||
|
"ietf-routing:routing": {
|
||||||
|
"control-plane-protocols": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_sessions(out['ietf-routing:routing']['control-plane-protocols'])
|
||||||
|
return out
|
||||||
@@ -10,6 +10,14 @@ def frr_to_ietf_neighbor_state(state):
|
|||||||
return state.lower()
|
return state.lower()
|
||||||
|
|
||||||
|
|
||||||
|
def frr_to_ietf_neighbor_role(role):
|
||||||
|
"""Translate FRR neighbor role to YANG enumeration values"""
|
||||||
|
if role == "Backup":
|
||||||
|
return "BDR"
|
||||||
|
# DR and DROther are already correct
|
||||||
|
return role
|
||||||
|
|
||||||
|
|
||||||
def add_routes(ospf):
|
def add_routes(ospf):
|
||||||
"""Fetch OSPF routes from Frr"""
|
"""Fetch OSPF routes from Frr"""
|
||||||
cmd = ['vtysh', '-c', "show ip ospf route json"]
|
cmd = ['vtysh', '-c', "show ip ospf route json"]
|
||||||
@@ -38,6 +46,22 @@ def add_routes(ospf):
|
|||||||
elif routetype[0] == "N":
|
elif routetype[0] == "N":
|
||||||
route["route-type"] = "intra-area"
|
route["route-type"] = "intra-area"
|
||||||
|
|
||||||
|
# Add area information if available
|
||||||
|
# Note: augmented by infix-routing.yang since standard ietf-ospf doesn't include it
|
||||||
|
# Must use the augmenting module's prefix
|
||||||
|
if info.get("area") is not None:
|
||||||
|
route["infix-routing:area-id"] = info["area"]
|
||||||
|
|
||||||
|
# Add metric (cost) if available
|
||||||
|
if info.get("cost") is not None:
|
||||||
|
route["metric"] = info["cost"]
|
||||||
|
elif info.get("metric") is not None:
|
||||||
|
route["metric"] = info["metric"]
|
||||||
|
|
||||||
|
# Add route-tag for external routes
|
||||||
|
if info.get("tag") is not None:
|
||||||
|
route["route-tag"] = info["tag"]
|
||||||
|
|
||||||
for hop in info["nexthops"]:
|
for hop in info["nexthops"]:
|
||||||
nexthop = {}
|
nexthop = {}
|
||||||
if hop["ip"] != " ":
|
if hop["ip"] != " ":
|
||||||
@@ -119,13 +143,85 @@ def add_areas(control_protocols):
|
|||||||
val = xlate.get(iface["state"], "unknown")
|
val = xlate.get(iface["state"], "unknown")
|
||||||
interface["state"] = val
|
interface["state"] = val
|
||||||
|
|
||||||
|
# Interface priority (for DR/BDR election)
|
||||||
|
if iface.get("priority") is not None:
|
||||||
|
interface["priority"] = iface["priority"]
|
||||||
|
|
||||||
|
# Interface cost
|
||||||
|
if iface.get("cost") is not None:
|
||||||
|
interface["cost"] = iface["cost"]
|
||||||
|
|
||||||
|
# Configuration timers (in seconds)
|
||||||
|
if iface.get("timerDeadSecs") is not None:
|
||||||
|
interface["dead-interval"] = iface["timerDeadSecs"]
|
||||||
|
|
||||||
|
if iface.get("timerRetransmitSecs") is not None:
|
||||||
|
interface["retransmit-interval"] = iface["timerRetransmitSecs"]
|
||||||
|
|
||||||
|
if iface.get("transmitDelaySecs") is not None:
|
||||||
|
interface["transmit-delay"] = iface["transmitDelaySecs"]
|
||||||
|
|
||||||
|
# Hello interval - convert from milliseconds to seconds
|
||||||
|
if iface.get("timerMsecs") is not None:
|
||||||
|
hello_sec = iface["timerMsecs"] // 1000
|
||||||
|
# timer-value-seconds16 requires range 1..65535, use max(1, value)
|
||||||
|
if hello_sec >= 1:
|
||||||
|
interface["hello-interval"] = hello_sec
|
||||||
|
|
||||||
|
# Operational state timers (config false)
|
||||||
|
# Hello timer - time remaining until next Hello (convert ms to seconds)
|
||||||
|
if iface.get("timerHelloInMsecs") is not None:
|
||||||
|
hello_timer_sec = iface["timerHelloInMsecs"] // 1000
|
||||||
|
# timer-value-seconds16 requires range 1..65535, use max(1, value)
|
||||||
|
if hello_timer_sec >= 1:
|
||||||
|
interface["hello-timer"] = hello_timer_sec
|
||||||
|
|
||||||
|
# Wait timer - time until interface exits Waiting state
|
||||||
|
if iface.get("timerWaitSecs") is not None:
|
||||||
|
wait_sec = iface["timerWaitSecs"]
|
||||||
|
# timer-value-seconds16 requires range 1..65535
|
||||||
|
if wait_sec >= 1:
|
||||||
|
interface["wait-timer"] = wait_sec
|
||||||
|
|
||||||
neighbors = []
|
neighbors = []
|
||||||
for neigh in iface["neighbors"]:
|
for neigh in iface["neighbors"]:
|
||||||
neighbor = {}
|
neighbor = {}
|
||||||
neighbor["neighbor-router-id"] = neigh["neighborIp"]
|
neighbor["neighbor-router-id"] = neigh["neighborIp"]
|
||||||
neighbor["address"] = neigh["ifaceAddress"]
|
neighbor["address"] = neigh["ifaceAddress"]
|
||||||
neighbor["dead-timer"] = neigh["routerDeadIntervalTimerDueMsec"]
|
|
||||||
|
# Priority - use existing YANG leaf for operational data
|
||||||
|
if neigh.get("nbrPriority") is not None:
|
||||||
|
neighbor["priority"] = neigh["nbrPriority"]
|
||||||
|
|
||||||
|
# Uptime - convert from milliseconds to seconds
|
||||||
|
# Note: augmented by infix-routing.yang
|
||||||
|
# Use lastPrgrsvChangeMsec from detail output (time since last progressive state change)
|
||||||
|
if neigh.get("lastPrgrsvChangeMsec") is not None:
|
||||||
|
uptime_sec = neigh["lastPrgrsvChangeMsec"] // 1000
|
||||||
|
neighbor["infix-routing:uptime"] = uptime_sec
|
||||||
|
|
||||||
|
# Dead timer - convert from milliseconds to seconds
|
||||||
|
# timer-value-seconds16 requires range 1..65535
|
||||||
|
if neigh.get("routerDeadIntervalTimerDueMsec") is not None:
|
||||||
|
dead_timer_sec = neigh["routerDeadIntervalTimerDueMsec"] // 1000
|
||||||
|
if dead_timer_sec >= 1:
|
||||||
|
neighbor["dead-timer"] = dead_timer_sec
|
||||||
|
|
||||||
neighbor["state"] = frr_to_ietf_neighbor_state(neigh["nbrState"])
|
neighbor["state"] = frr_to_ietf_neighbor_state(neigh["nbrState"])
|
||||||
|
|
||||||
|
# Store role (DR/BDR/DROther) for display
|
||||||
|
# Note: augmented by infix-routing.yang
|
||||||
|
if neigh.get("role"):
|
||||||
|
neighbor["infix-routing:role"] = frr_to_ietf_neighbor_role(neigh["role"])
|
||||||
|
|
||||||
|
# Store interface name with local address (e.g., "e5:10.0.23.1")
|
||||||
|
# Note: augmented by infix-routing.yang
|
||||||
|
# Compose from ifaceName and localIfaceAddress
|
||||||
|
if neigh.get("ifaceName") and neigh.get("localIfaceAddress"):
|
||||||
|
neighbor["infix-routing:interface-name"] = f"{neigh['ifaceName']}:{neigh['localIfaceAddress']}"
|
||||||
|
elif neigh.get("ifaceName"):
|
||||||
|
neighbor["infix-routing:interface-name"] = neigh["ifaceName"]
|
||||||
|
|
||||||
if neigh.get("routerDesignatedId"):
|
if neigh.get("routerDesignatedId"):
|
||||||
neighbor["dr-router-id"] = neigh["routerDesignatedId"]
|
neighbor["dr-router-id"] = neigh["routerDesignatedId"]
|
||||||
if neigh.get("routerDesignatedBackupId"):
|
if neigh.get("routerDesignatedBackupId"):
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
#define XPATH_HARDWARE_BASE "/ietf-hardware:hardware"
|
#define XPATH_HARDWARE_BASE "/ietf-hardware:hardware"
|
||||||
#define XPATH_SYSTEM_BASE "/ietf-system"
|
#define XPATH_SYSTEM_BASE "/ietf-system"
|
||||||
#define XPATH_ROUTING_OSPF XPATH_ROUTING_BASE "/ospf"
|
#define XPATH_ROUTING_OSPF XPATH_ROUTING_BASE "/ospf"
|
||||||
|
#define XPATH_ROUTING_BFD XPATH_ROUTING_BASE "/bfd"
|
||||||
#define XPATH_CONTAIN_BASE "/infix-containers:containers"
|
#define XPATH_CONTAIN_BASE "/infix-containers:containers"
|
||||||
#define XPATH_DHCP_SERVER_BASE "/infix-dhcp-server:dhcp-server"
|
#define XPATH_DHCP_SERVER_BASE "/infix-dhcp-server:dhcp-server"
|
||||||
#define XPATH_LLDP_BASE "/ieee802-dot1ab-lldp:lldp"
|
#define XPATH_LLDP_BASE "/ieee802-dot1ab-lldp:lldp"
|
||||||
@@ -259,6 +260,42 @@ static int sr_ospf_cb(sr_session_ctx_t *session, uint32_t, const char *,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sr_bfd_cb(sr_session_ctx_t *session, uint32_t, const char *,
|
||||||
|
const char *, const char *xpath, uint32_t,
|
||||||
|
struct lyd_node **parent, __attribute__((unused)) void *priv)
|
||||||
|
{
|
||||||
|
char *yanger_args[5] = {
|
||||||
|
YANGER_BINPATH,
|
||||||
|
"ietf-bfd-ip-sh",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
const struct ly_ctx *ctx;
|
||||||
|
sr_conn_ctx_t *con;
|
||||||
|
sr_error_t err;
|
||||||
|
|
||||||
|
DEBUG("Incoming BFD query for xpath: %s", xpath);
|
||||||
|
|
||||||
|
con = sr_session_get_connection(session);
|
||||||
|
if (!con) {
|
||||||
|
ERROR("Error, getting sr connection");
|
||||||
|
return SR_ERR_INTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = sr_acquire_context(con);
|
||||||
|
if (!ctx) {
|
||||||
|
ERROR("Error, acquiring context");
|
||||||
|
return SR_ERR_INTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ly_add_yanger_data(ctx, parent, yanger_args);
|
||||||
|
if (err)
|
||||||
|
ERROR("Error adding yanger data");
|
||||||
|
|
||||||
|
sr_release_context(con);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void sigint_cb(struct ev_loop *loop, struct ev_signal *, int)
|
static void sigint_cb(struct ev_loop *loop, struct ev_signal *, int)
|
||||||
{
|
{
|
||||||
@@ -343,6 +380,8 @@ static int subscribe_to_all(struct statd *statd)
|
|||||||
return SR_ERR_INTERNAL;
|
return SR_ERR_INTERNAL;
|
||||||
if (subscribe(statd, "ietf-routing", XPATH_ROUTING_OSPF, sr_ospf_cb))
|
if (subscribe(statd, "ietf-routing", XPATH_ROUTING_OSPF, sr_ospf_cb))
|
||||||
return SR_ERR_INTERNAL;
|
return SR_ERR_INTERNAL;
|
||||||
|
if (subscribe(statd, "ietf-routing", XPATH_ROUTING_BFD, sr_bfd_cb))
|
||||||
|
return SR_ERR_INTERNAL;
|
||||||
if (subscribe(statd, "ietf-hardware", XPATH_HARDWARE_BASE, sr_generic_cb))
|
if (subscribe(statd, "ietf-hardware", XPATH_HARDWARE_BASE, sr_generic_cb))
|
||||||
return SR_ERR_INTERNAL;
|
return SR_ERR_INTERNAL;
|
||||||
if (subscribe(statd, "ietf-system", XPATH_SYSTEM_BASE":system", sr_generic_cb))
|
if (subscribe(statd, "ietf-system", XPATH_SYSTEM_BASE":system", sr_generic_cb))
|
||||||
|
|||||||
@@ -18,8 +18,12 @@
|
|||||||
{
|
{
|
||||||
"neighbor-router-id": "10.1.2.1",
|
"neighbor-router-id": "10.1.2.1",
|
||||||
"address": "10.1.2.1",
|
"address": "10.1.2.1",
|
||||||
"dead-timer": 22506,
|
"priority": 1,
|
||||||
"state": "full"
|
"infix-routing:uptime": 57,
|
||||||
|
"dead-timer": 22,
|
||||||
|
"state": "full",
|
||||||
|
"infix-routing:role": "DROther",
|
||||||
|
"infix-routing:interface-name": "e3.8:10.1.1.1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -27,7 +31,15 @@
|
|||||||
"passive": false,
|
"passive": false,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"interface-type": "point-to-point",
|
"interface-type": "point-to-point",
|
||||||
"state": "point-to-point"
|
"state": "point-to-point",
|
||||||
|
"priority": 1,
|
||||||
|
"cost": 1,
|
||||||
|
"dead-interval": 40,
|
||||||
|
"retransmit-interval": 5,
|
||||||
|
"transmit-delay": 1,
|
||||||
|
"hello-interval": 10,
|
||||||
|
"hello-timer": 2,
|
||||||
|
"wait-timer": 40
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ietf-ospf:neighbors": {
|
"ietf-ospf:neighbors": {
|
||||||
@@ -35,8 +47,12 @@
|
|||||||
{
|
{
|
||||||
"neighbor-router-id": "10.1.3.1",
|
"neighbor-router-id": "10.1.3.1",
|
||||||
"address": "10.1.3.1",
|
"address": "10.1.3.1",
|
||||||
"dead-timer": 22334,
|
"priority": 1,
|
||||||
"state": "full"
|
"infix-routing:uptime": 57,
|
||||||
|
"dead-timer": 22,
|
||||||
|
"state": "full",
|
||||||
|
"infix-routing:role": "DROther",
|
||||||
|
"infix-routing:interface-name": "e4.8:10.1.1.1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -44,7 +60,15 @@
|
|||||||
"passive": false,
|
"passive": false,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"interface-type": "point-to-point",
|
"interface-type": "point-to-point",
|
||||||
"state": "point-to-point"
|
"state": "point-to-point",
|
||||||
|
"priority": 1,
|
||||||
|
"cost": 1,
|
||||||
|
"dead-interval": 40,
|
||||||
|
"retransmit-interval": 5,
|
||||||
|
"transmit-delay": 1,
|
||||||
|
"hello-interval": 10,
|
||||||
|
"hello-timer": 2,
|
||||||
|
"wait-timer": 40
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ietf-ospf:neighbors": {
|
"ietf-ospf:neighbors": {
|
||||||
@@ -52,8 +76,12 @@
|
|||||||
{
|
{
|
||||||
"neighbor-router-id": "192.168.100.1",
|
"neighbor-router-id": "192.168.100.1",
|
||||||
"address": "10.1.1.100",
|
"address": "10.1.1.100",
|
||||||
"dead-timer": 31385,
|
"priority": 1,
|
||||||
|
"infix-routing:uptime": 23,
|
||||||
|
"dead-timer": 31,
|
||||||
"state": "full",
|
"state": "full",
|
||||||
|
"infix-routing:role": "DR",
|
||||||
|
"infix-routing:interface-name": "e6:10.1.1.101",
|
||||||
"dr-router-id": "192.168.100.1",
|
"dr-router-id": "192.168.100.1",
|
||||||
"bdr-router-id": "10.1.1.1"
|
"bdr-router-id": "10.1.1.1"
|
||||||
}
|
}
|
||||||
@@ -67,7 +95,15 @@
|
|||||||
"passive": false,
|
"passive": false,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"interface-type": "broadcast",
|
"interface-type": "broadcast",
|
||||||
"state": "bdr"
|
"state": "bdr",
|
||||||
|
"priority": 1,
|
||||||
|
"cost": 1,
|
||||||
|
"dead-interval": 40,
|
||||||
|
"retransmit-interval": 5,
|
||||||
|
"transmit-delay": 1,
|
||||||
|
"hello-interval": 10,
|
||||||
|
"hello-timer": 2,
|
||||||
|
"wait-timer": 40
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -82,6 +118,8 @@
|
|||||||
{
|
{
|
||||||
"prefix": "0.0.0.0/0",
|
"prefix": "0.0.0.0/0",
|
||||||
"route-type": "inter-area",
|
"route-type": "inter-area",
|
||||||
|
"infix-routing:area-id": "0.0.0.1",
|
||||||
|
"metric": 2,
|
||||||
"next-hops": {
|
"next-hops": {
|
||||||
"next-hop": [
|
"next-hop": [
|
||||||
{
|
{
|
||||||
@@ -93,6 +131,8 @@
|
|||||||
{
|
{
|
||||||
"prefix": "10.1.1.0/24",
|
"prefix": "10.1.1.0/24",
|
||||||
"route-type": "intra-area",
|
"route-type": "intra-area",
|
||||||
|
"infix-routing:area-id": "0.0.0.1",
|
||||||
|
"metric": 1,
|
||||||
"next-hops": {
|
"next-hops": {
|
||||||
"next-hop": [
|
"next-hop": [
|
||||||
{
|
{
|
||||||
@@ -104,6 +144,8 @@
|
|||||||
{
|
{
|
||||||
"prefix": "10.1.2.0/24",
|
"prefix": "10.1.2.0/24",
|
||||||
"route-type": "intra-area",
|
"route-type": "intra-area",
|
||||||
|
"infix-routing:area-id": "0.0.0.1",
|
||||||
|
"metric": 2,
|
||||||
"next-hops": {
|
"next-hops": {
|
||||||
"next-hop": [
|
"next-hop": [
|
||||||
{
|
{
|
||||||
@@ -118,6 +160,8 @@
|
|||||||
{
|
{
|
||||||
"prefix": "10.1.3.0/24",
|
"prefix": "10.1.3.0/24",
|
||||||
"route-type": "intra-area",
|
"route-type": "intra-area",
|
||||||
|
"infix-routing:area-id": "0.0.0.1",
|
||||||
|
"metric": 2,
|
||||||
"next-hops": {
|
"next-hops": {
|
||||||
"next-hop": [
|
"next-hop": [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user