mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-23 09:23:02 +02:00
Merge pull request #1306 from kernelkit/rip
This commit is contained in:
@@ -8,10 +8,17 @@ All notable changes to the project are documented in this file.
|
||||
|
||||
### Changes
|
||||
|
||||
- Add RIPv2 routing support, issue #582
|
||||
- Add support for configurable OSPF debug logging, issue #1281. Debug options
|
||||
can now be enabled per category (bfd, packet, ism, nsm, default-information,
|
||||
nssa). All debug options are disabled by default to prevent log flooding in
|
||||
production environments. See the documentation for usage examples
|
||||
- Add support for "routing interfaces", issue #647. Lists interfaces with IP
|
||||
forwarding. Inspect from CLI using `show interface`, look for `⇅` flag
|
||||
|
||||
### Fixes
|
||||
|
||||
N/A
|
||||
|
||||
[v25.11.0][] - 2025-12-02
|
||||
-------------------------
|
||||
|
||||
@@ -1339,6 +1339,7 @@ Currently supported YANG models:
|
||||
| ietf-ipv4-unicast-routing | Static IPv4 unicast routing |
|
||||
| ietf-ipv6-unicast-routing | Static IPv6 unicast routing |
|
||||
| ietf-ospf | OSPF routing |
|
||||
| ietf-rip | RIP routing |
|
||||
| infix-routing | Infix deviations and extensions |
|
||||
|
||||
The base model, ietf-routing, is where all the other models hook in. It
|
||||
@@ -1545,6 +1546,121 @@ all options back to `false`:
|
||||
admin@example:/>
|
||||
|
||||
|
||||
### RIP Routing
|
||||
|
||||
The system supports RIP dynamic routing for IPv4, i.e., RIPv2. To enable
|
||||
RIP and set active interfaces:
|
||||
|
||||
admin@example:/config/> edit routing control-plane-protocol ripv2 name default rip
|
||||
admin@example:/config/routing/…/rip/> set interfaces interface e0
|
||||
admin@example:/config/routing/…/rip/> set interfaces interface e1
|
||||
admin@example:/config/routing/…/rip/> leave
|
||||
admin@example:/>
|
||||
|
||||
> [!TIP]
|
||||
> Remember to enable [IPv4 forwarding](#ipv4-forwarding) for all the
|
||||
> interfaces you want to route between.
|
||||
|
||||
|
||||
#### RIP interface settings
|
||||
|
||||
By default, interfaces send and receive RIPv2 packets. To control the
|
||||
RIP version per interface:
|
||||
|
||||
admin@example:/config/routing/…/rip/> edit interfaces interface e0
|
||||
admin@example:/config/routing/…/rip/interfaces/interface/e0/> set send-version 1
|
||||
admin@example:/config/routing/…/rip/interfaces/interface/e0/> set receive-version 1-2
|
||||
admin@example:/config/routing/…/rip/interfaces/interface/e0/> leave
|
||||
admin@example:/>
|
||||
|
||||
Valid version values are `1`, `2`, or `1-2` (both versions).
|
||||
|
||||
To configure a passive interface (advertise network but don't send/receive
|
||||
RIP updates):
|
||||
|
||||
admin@example:/config/routing/…/rip/> edit interfaces interface e0
|
||||
admin@example:/config/routing/…/rip/interfaces/interface/e0/> set passive
|
||||
admin@example:/config/routing/…/rip/interfaces/interface/e0/> leave
|
||||
admin@example:/>
|
||||
|
||||
|
||||
#### RIP global settings
|
||||
|
||||
RIP supports redistribution of connected and static routes:
|
||||
|
||||
admin@example:/config/routing/…/rip/> set redistribute connected
|
||||
admin@example:/config/routing/…/rip/> set redistribute static
|
||||
admin@example:/config/routing/…/rip/> leave
|
||||
admin@example:/>
|
||||
|
||||
|
||||
#### Debug RIPv2
|
||||
|
||||
The CLI provides various RIP status commands:
|
||||
|
||||
admin@example:/> show ip rip
|
||||
|
||||
Default version control: send version 2, receive version 2
|
||||
Interface Send Recv Key-chain
|
||||
e0 2 2
|
||||
e1 2 2
|
||||
|
||||
Routing for Networks:
|
||||
e0
|
||||
e1
|
||||
|
||||
Routing Information Sources:
|
||||
Gateway BadPackets BadRoutes Distance Last Update
|
||||
10.0.1.2 0 0 120 00:00:16
|
||||
Distance: (default is 120)
|
||||
|
||||
admin@example:/> show ip rip neighbor
|
||||
|
||||
ADDRESS BAD-PACKETS BAD-ROUTES
|
||||
10.0.1.2 0 0
|
||||
|
||||
admin@example:/>
|
||||
|
||||
For more detailed troubleshooting, RIP debug logging can be enabled to
|
||||
capture specific protocol events. Debug messages are written to the
|
||||
routing log file (`/var/log/routing`).
|
||||
|
||||
> [!CAUTION]
|
||||
> Debug logging significantly increases log output and may impact
|
||||
> performance. Only enable debug categories needed for troubleshooting,
|
||||
> and disable them when done.
|
||||
|
||||
To enable specific RIP debug categories:
|
||||
|
||||
admin@example:/> configure
|
||||
admin@example:/config/> edit routing control-plane-protocol ripv2 name default rip debug
|
||||
admin@example:/config/routing/…/rip/debug/> set events true
|
||||
admin@example:/config/routing/…/rip/debug/> set packet true
|
||||
admin@example:/config/routing/…/rip/debug/> leave
|
||||
admin@example:/>
|
||||
|
||||
Available debug categories include:
|
||||
|
||||
- `events`: RIP events (sending/receiving packets, timers, interface changes)
|
||||
- `packet`: Detailed packet debugging (packet dumps with origin and port)
|
||||
- `kernel`: Kernel routing table updates (route add/delete, interface updates)
|
||||
|
||||
All debug options are disabled by default. Refer to the `infix-routing`
|
||||
YANG model for the complete list of available debug options.
|
||||
|
||||
To view current debug settings:
|
||||
|
||||
admin@example:/> show running-config routing control-plane-protocol
|
||||
|
||||
To disable all debug logging, simply delete the debug settings or set
|
||||
all options back to `false`:
|
||||
|
||||
admin@example:/> configure
|
||||
admin@example:/config/> delete routing control-plane-protocol ripv2 name default rip debug
|
||||
admin@example:/config/> leave
|
||||
admin@example:/>
|
||||
|
||||
|
||||
### View routing table
|
||||
|
||||
The routing table can be inspected from the operational datastore, XPath
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# --log-level debug
|
||||
RIPD_ARGS="-A 127.0.0.1 -u frr -g frr -f /etc/frr/ripd.conf --log syslog"
|
||||
@@ -1 +1,2 @@
|
||||
service [2345] log:null <pid/zebra> ripd -A 127.0.0.1 -u frr -g frr -- RIP daemon
|
||||
service <pid/zebra> env:-/etc/default/ripd \
|
||||
[2345] ripd $RIPD_ARGS -- RIP daemon
|
||||
|
||||
+247
-2
@@ -14,6 +14,9 @@
|
||||
#define OSPFD_CONF "/etc/frr/ospfd.conf"
|
||||
#define OSPFD_CONF_NEXT OSPFD_CONF "+"
|
||||
#define OSPFD_CONF_PREV OSPFD_CONF "-"
|
||||
#define RIPD_CONF "/etc/frr/ripd.conf"
|
||||
#define RIPD_CONF_NEXT RIPD_CONF "+"
|
||||
#define RIPD_CONF_PREV RIPD_CONF "-"
|
||||
#define BFDD_CONF "/etc/frr/bfd_enabled" /* Just signal that bfd should be enabled*/
|
||||
#define BFDD_CONF_NEXT BFDD_CONF "+"
|
||||
|
||||
@@ -26,6 +29,219 @@ no log unique-id\n\
|
||||
log syslog informational\n\
|
||||
log facility local2\n"
|
||||
|
||||
int parse_rip_redistribute(sr_session_ctx_t *session, struct lyd_node *redistributes, FILE *fp)
|
||||
{
|
||||
struct lyd_node *tmp;
|
||||
|
||||
LY_LIST_FOR(lyd_child(redistributes), tmp) {
|
||||
const char *protocol = lydx_get_cattr(tmp, "protocol");
|
||||
|
||||
fprintf(fp, " redistribute %s\n", protocol);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_rip_interfaces(sr_session_ctx_t *session, struct lyd_node *interfaces, FILE *fp)
|
||||
{
|
||||
struct lyd_node *interface, *neighbors_node, *neighbor;
|
||||
int num_interfaces = 0;
|
||||
|
||||
/* First pass: network and passive-interface statements (inside router rip block) */
|
||||
LY_LIST_FOR(lyd_child(interfaces), interface) {
|
||||
const char *name;
|
||||
int passive;
|
||||
|
||||
name = lydx_get_cattr(interface, "interface");
|
||||
if (!name)
|
||||
continue;
|
||||
|
||||
passive = lydx_get_bool(interface, "passive");
|
||||
|
||||
/* Enable RIP on the interface by adding it to the network statement */
|
||||
fprintf(fp, " network %s\n", name);
|
||||
|
||||
if (passive)
|
||||
fprintf(fp, " passive-interface %s\n", name);
|
||||
|
||||
num_interfaces++;
|
||||
}
|
||||
|
||||
/* Handle explicit neighbors (inside router rip block) */
|
||||
LY_LIST_FOR(lyd_child(interfaces), interface) {
|
||||
neighbors_node = lydx_get_child(interface, "neighbors");
|
||||
if (neighbors_node) {
|
||||
LY_LIST_FOR(lyd_child(neighbors_node), neighbor) {
|
||||
const char *address = lydx_get_cattr(neighbor, "address");
|
||||
if (address)
|
||||
fprintf(fp, " neighbor %s\n", address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Second pass: interface-specific settings (outside router rip block) */
|
||||
LY_LIST_FOR(lyd_child(interfaces), interface) {
|
||||
const char *name, *split_horizon, *cost, *send_version, *recv_version;
|
||||
|
||||
name = lydx_get_cattr(interface, "interface");
|
||||
if (!name)
|
||||
continue;
|
||||
|
||||
split_horizon = lydx_get_cattr(interface, "split-horizon");
|
||||
cost = lydx_get_cattr(interface, "cost");
|
||||
send_version = lydx_get_cattr(interface, "send-version");
|
||||
recv_version = lydx_get_cattr(interface, "receive-version");
|
||||
|
||||
/* Only create interface block if there are per-interface settings */
|
||||
if (split_horizon || cost || send_version || recv_version) {
|
||||
fprintf(fp, "interface %s\n", name);
|
||||
|
||||
if (split_horizon) {
|
||||
if (!strcmp(split_horizon, "poison-reverse"))
|
||||
fputs(" ip rip split-horizon poisoned-reverse\n", fp);
|
||||
else if (!strcmp(split_horizon, "disabled"))
|
||||
fputs(" no ip rip split-horizon\n", fp);
|
||||
/* "simple" is default, no need to configure */
|
||||
}
|
||||
|
||||
/* Configure send version */
|
||||
if (send_version) {
|
||||
if (!strcmp(send_version, "1"))
|
||||
fputs(" ip rip send version 1\n", fp);
|
||||
else if (!strcmp(send_version, "1-2"))
|
||||
fputs(" ip rip send version 1 2\n", fp);
|
||||
/* "2" is default in augmentation, explicit config if needed */
|
||||
else if (!strcmp(send_version, "2"))
|
||||
fputs(" ip rip send version 2\n", fp);
|
||||
}
|
||||
|
||||
/* Configure receive version */
|
||||
if (recv_version) {
|
||||
if (!strcmp(recv_version, "1"))
|
||||
fputs(" ip rip receive version 1\n", fp);
|
||||
else if (!strcmp(recv_version, "1-2"))
|
||||
fputs(" ip rip receive version 1 2\n", fp);
|
||||
/* "2" is default in augmentation, explicit config if needed */
|
||||
else if (!strcmp(recv_version, "2"))
|
||||
fputs(" ip rip receive version 2\n", fp);
|
||||
}
|
||||
|
||||
if (cost) {
|
||||
/* FRR uses offset-list for per-interface cost adjustment */
|
||||
/* Note: offset-list is configured globally, not per-interface */
|
||||
/* This is just a placeholder - actual implementation would need
|
||||
access lists and global offset-list configuration */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return num_interfaces;
|
||||
}
|
||||
|
||||
int parse_rip_timers(sr_session_ctx_t *session, struct lyd_node *timers, FILE *fp)
|
||||
{
|
||||
const char *update, *invalid, *holddown, *flush;
|
||||
|
||||
if (!timers)
|
||||
return 0;
|
||||
|
||||
update = lydx_get_cattr(timers, "update-interval");
|
||||
invalid = lydx_get_cattr(timers, "invalid-interval");
|
||||
holddown = lydx_get_cattr(timers, "holddown-interval");
|
||||
flush = lydx_get_cattr(timers, "flush-interval");
|
||||
|
||||
/* FRR timers basic: UPDATE TIMEOUT GARBAGE
|
||||
* TIMEOUT = invalid-interval (when route becomes invalid)
|
||||
* GARBAGE = flush-interval (when route is flushed)
|
||||
* Note: holddown-interval is used between invalid and flush
|
||||
*/
|
||||
DEBUG("Ignoring 'holddown interval %s' for now", holddown);
|
||||
if (update || invalid || flush) {
|
||||
fprintf(fp, " timers basic %s %s %s\n",
|
||||
update ? update : "30",
|
||||
invalid ? invalid : "180",
|
||||
flush ? flush : "240");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_rip(sr_session_ctx_t *session, struct lyd_node *rip)
|
||||
{
|
||||
struct lyd_node *interfaces, *timers, *default_route, *debug;
|
||||
const char *default_metric, *distance;
|
||||
int num_interfaces = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(RIPD_CONF_NEXT, "w");
|
||||
if (!fp) {
|
||||
ERROR("Failed to open %s", RIPD_CONF_NEXT);
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
fputs(FRR_STATIC_CONFIG, fp);
|
||||
|
||||
/* Handle RIP debug configuration */
|
||||
debug = lydx_get_child(rip, "debug");
|
||||
if (debug) {
|
||||
int any_debug = 0;
|
||||
|
||||
if (lydx_get_bool(debug, "events")) {
|
||||
fputs("debug rip events\n", fp);
|
||||
any_debug = 1;
|
||||
}
|
||||
if (lydx_get_bool(debug, "packet")) {
|
||||
fputs("debug rip packet\n", fp);
|
||||
any_debug = 1;
|
||||
}
|
||||
if (lydx_get_bool(debug, "kernel")) {
|
||||
fputs("debug rip zebra\n", fp);
|
||||
any_debug = 1;
|
||||
}
|
||||
|
||||
if (any_debug)
|
||||
fputs("!\n", fp);
|
||||
}
|
||||
|
||||
fputs("router rip\n", fp);
|
||||
fputs(" version 2\n", fp);
|
||||
|
||||
/* Global RIP parameters */
|
||||
default_metric = lydx_get_cattr(rip, "default-metric");
|
||||
if (default_metric)
|
||||
fprintf(fp, " default-metric %s\n", default_metric);
|
||||
|
||||
distance = lydx_get_cattr(rip, "distance");
|
||||
if (distance)
|
||||
fprintf(fp, " distance %s\n", distance);
|
||||
|
||||
/* Timers */
|
||||
timers = lydx_get_child(rip, "timers");
|
||||
parse_rip_timers(session, timers, fp);
|
||||
|
||||
/* Default route origination */
|
||||
default_route = lydx_get_child(rip, "originate-default-route");
|
||||
if (default_route && lydx_get_bool(default_route, "enabled"))
|
||||
fputs(" default-information originate\n", fp);
|
||||
|
||||
/* Redistribution */
|
||||
parse_rip_redistribute(session, lydx_get_child(rip, "redistribute"), fp);
|
||||
|
||||
/* Interfaces - must be done after router rip block and before interface blocks */
|
||||
interfaces = lydx_get_child(rip, "interfaces");
|
||||
if (interfaces)
|
||||
num_interfaces = parse_rip_interfaces(session, interfaces, fp);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (!num_interfaces) {
|
||||
(void)remove(RIPD_CONF_NEXT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_ospf_interfaces(sr_session_ctx_t *session, struct lyd_node *areas, FILE *fp)
|
||||
{
|
||||
struct lyd_node *interface, *interfaces, *area;
|
||||
@@ -274,9 +490,9 @@ static int parse_static_routes(sr_session_ctx_t *session, struct lyd_node *paren
|
||||
|
||||
int routing_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd)
|
||||
{
|
||||
int staticd_enabled = 0, ospfd_enabled = 0, bfdd_enabled = 0;
|
||||
int staticd_enabled = 0, ospfd_enabled = 0, ripd_enabled = 0, bfdd_enabled = 0;
|
||||
struct lyd_node *cplane, *cplanes;
|
||||
bool ospfd_running, bfdd_running;
|
||||
bool ospfd_running, ripd_running, bfdd_running;
|
||||
bool restart_zebra = false;
|
||||
int rc = SR_ERR_OK;
|
||||
FILE *fp;
|
||||
@@ -303,8 +519,10 @@ int routing_change(sr_session_ctx_t *session, struct lyd_node *config, struct ly
|
||||
/* Check if passed validation in previous event */
|
||||
staticd_enabled = fexist(STATICD_CONF_NEXT);
|
||||
ospfd_enabled = fexist(OSPFD_CONF_NEXT);
|
||||
ripd_enabled = fexist(RIPD_CONF_NEXT);
|
||||
bfdd_enabled = fexist(BFDD_CONF_NEXT);
|
||||
ospfd_running = !systemf("initctl -bfq status ospfd");
|
||||
ripd_running = !systemf("initctl -bfq status ripd");
|
||||
bfdd_running = !systemf("initctl -bfq status bfdd");
|
||||
|
||||
if (bfdd_running && !bfdd_enabled) {
|
||||
@@ -327,6 +545,16 @@ int routing_change(sr_session_ctx_t *session, struct lyd_node *config, struct ly
|
||||
(void)remove(OSPFD_CONF);
|
||||
}
|
||||
|
||||
if (ripd_running && !ripd_enabled) {
|
||||
if (systemf("initctl -bfq disable ripd")) {
|
||||
ERROR("Failed to disable RIP routing daemon");
|
||||
rc = SR_ERR_INTERNAL;
|
||||
goto err_abandon;
|
||||
}
|
||||
/* Remove all generated files */
|
||||
(void)remove(RIPD_CONF);
|
||||
}
|
||||
|
||||
if (bfdd_enabled) {
|
||||
(void)rename(BFDD_CONF_NEXT, BFDD_CONF);
|
||||
if (!bfdd_running) {
|
||||
@@ -353,6 +581,21 @@ int routing_change(sr_session_ctx_t *session, struct lyd_node *config, struct ly
|
||||
}
|
||||
}
|
||||
|
||||
if (ripd_enabled) {
|
||||
(void)remove(RIPD_CONF_PREV);
|
||||
(void)rename(RIPD_CONF, RIPD_CONF_PREV);
|
||||
(void)rename(RIPD_CONF_NEXT, RIPD_CONF);
|
||||
if (!ripd_running) {
|
||||
if (systemf("initctl -bnq enable ripd")) {
|
||||
ERROR("Failed to enable RIP routing daemon");
|
||||
rc = SR_ERR_INTERNAL;
|
||||
goto err_abandon;
|
||||
}
|
||||
} else {
|
||||
restart_zebra = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (staticd_enabled) {
|
||||
(void)remove(STATICD_CONF_PREV);
|
||||
(void)rename(STATICD_CONF, STATICD_CONF_PREV);
|
||||
@@ -389,6 +632,8 @@ int routing_change(sr_session_ctx_t *session, struct lyd_node *config, struct ly
|
||||
staticd_enabled = parse_static_routes(session, lydx_get_child(cplane, "static-routes"), fp);
|
||||
} else if (!strcmp(type, "infix-routing:ospfv2")) {
|
||||
parse_ospf(session, lydx_get_child(cplane, "ospf"));
|
||||
} else if (!strcmp(type, "infix-routing:ripv2")) {
|
||||
parse_rip(session, lydx_get_child(cplane, "rip"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ MODULES=(
|
||||
"ietf-ipv6-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-rip@2020-02-20.yang"
|
||||
"iana-bfd-types@2021-10-21.yang"
|
||||
"ietf-bfd-types@2022-09-22.yang"
|
||||
"ietf-bfd@2022-09-22.yang"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,17 +18,18 @@ module infix-routing {
|
||||
import ietf-bfd-types {
|
||||
prefix bfd-types;
|
||||
}
|
||||
import ietf-rip {
|
||||
prefix rip;
|
||||
}
|
||||
|
||||
organization "KernelKit";
|
||||
contact "kernelkit@googlegroups.com";
|
||||
description "Deviations and augments for ietf-routing and ietf-ospf.";
|
||||
description "Deviations and augments for ietf-routing, ietf-ospf, and ietf-rip.";
|
||||
|
||||
revision 2025-12-02 {
|
||||
description "Add configurable OSPF debug logging container.
|
||||
Allows users to enable specific OSPF debug categories
|
||||
(bfd, packet, ism, nsm, default-information, nssa) for troubleshooting.
|
||||
All debug options default to disabled to prevent log flooding.";
|
||||
reference "Issue #1281";
|
||||
Add RIP (Routing Information Protocol) support.";
|
||||
reference "RFC 8695";
|
||||
}
|
||||
|
||||
revision 2025-11-12 {
|
||||
@@ -110,10 +111,6 @@ module infix-routing {
|
||||
}
|
||||
|
||||
/* General routing */
|
||||
deviation "/rt:routing/rt:interfaces" {
|
||||
description "Initial limitation";
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:router-id" {
|
||||
description "Set in OSPF";
|
||||
@@ -133,7 +130,10 @@ module infix-routing {
|
||||
typedef infix-distribute-protocol {
|
||||
type enumeration {
|
||||
enum ospf {
|
||||
description "Redistribute Ospf";
|
||||
description "Redistribute OSPF routes";
|
||||
}
|
||||
enum rip {
|
||||
description "Redistribute RIP routes";
|
||||
}
|
||||
enum static {
|
||||
description "Redistribute Static routes";
|
||||
@@ -150,7 +150,12 @@ module infix-routing {
|
||||
identity ospfv2 {
|
||||
base ospf:ospfv2;
|
||||
base infix-routing-type;
|
||||
description "OSPv2 (IPv4) routing protocol";
|
||||
description "OSPFv2 (IPv4) routing protocol";
|
||||
}
|
||||
identity ripv2 {
|
||||
base rip:ripv2;
|
||||
base infix-routing-type;
|
||||
description "RIPv2 (IPv4) routing protocol";
|
||||
}
|
||||
identity bfdv1 {
|
||||
base bfd-types:bfdv1;
|
||||
@@ -590,6 +595,190 @@ module infix-routing {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
/* RIP */
|
||||
/* RIP features not supported in Infix/FRR */
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:originate-default-route/rip:route-policy" {
|
||||
deviate not-supported;
|
||||
description "Route policies are not supported.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:triggered-update-threshold" {
|
||||
deviate not-supported;
|
||||
description "Triggered update threshold is not supported in FRR.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:maximum-paths" {
|
||||
deviate not-supported;
|
||||
description "Maximum paths configuration is not supported in FRR.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:output-delay" {
|
||||
deviate not-supported;
|
||||
description "Output delay configuration is not supported in FRR.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:distribute-list" {
|
||||
deviate not-supported;
|
||||
description "Distribute lists are not supported.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:redistribute" {
|
||||
deviate not-supported;
|
||||
description "Use Infix-specific redistribute container instead.";
|
||||
}
|
||||
|
||||
/* RIP Interface features not supported */
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:interfaces/rip:interface/rip:authentication" {
|
||||
deviate not-supported;
|
||||
description "RIP authentication is not supported.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:interfaces/rip:interface/rip:bfd" {
|
||||
deviate not-supported;
|
||||
description "BFD support for RIP is not available in FRR.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:interfaces/rip:interface/rip:no-listen" {
|
||||
deviate not-supported;
|
||||
description "No-listen mode is not supported.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:interfaces/rip:interface/rip:originate-default-route" {
|
||||
deviate not-supported;
|
||||
description "Per-interface default route origination is not supported.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:interfaces/rip:interface/rip:summary-address" {
|
||||
deviate not-supported;
|
||||
description "Summary addresses are not supported.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:interfaces/rip:interface/rip:timers" {
|
||||
deviate not-supported;
|
||||
description "Per-interface timers are not supported in FRR.";
|
||||
}
|
||||
|
||||
/* RIP Operational state not supported */
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:interfaces/rip:interface/rip:statistics" {
|
||||
deviate not-supported;
|
||||
description "Per-interface statistics are not collected.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:next-triggered-update" {
|
||||
deviate not-supported;
|
||||
description "Next triggered update information is not available.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:num-of-routes" {
|
||||
deviate not-supported;
|
||||
description "Number of routes is not tracked separately.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:ipv6" {
|
||||
deviate not-supported;
|
||||
description "RIPng (IPv6) is not currently supported in Infix.";
|
||||
}
|
||||
|
||||
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip/rip:statistics" {
|
||||
deviate not-supported;
|
||||
description "Global statistics are not collected.";
|
||||
}
|
||||
|
||||
/* RIP RPC */
|
||||
deviation "/rip:clear-rip-route" {
|
||||
deviate not-supported;
|
||||
description "Clear RIP route RPC is not supported.";
|
||||
}
|
||||
|
||||
/* RIP Augmentations */
|
||||
augment "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip" {
|
||||
description "Infix-specific redistribute container for RIP.
|
||||
The standard ietf-rip redistribute container has complex protocol-specific
|
||||
lists (bgp, isis, ospfv2, etc.), but Infix uses a simpler model similar to OSPF.";
|
||||
container redistribute {
|
||||
list redistribute {
|
||||
key "protocol";
|
||||
description "Redistribute protocols into RIP";
|
||||
leaf protocol {
|
||||
type infix-distribute-protocol;
|
||||
description "Set protocol to redistribute";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
augment "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/rip:rip" {
|
||||
description "Add support for configurable RIP debug logging.
|
||||
Allows users to enable specific RIP debug categories for troubleshooting.
|
||||
All debug options default to disabled to prevent log flooding in
|
||||
production environments.";
|
||||
container debug {
|
||||
description "RIP debug logging configuration";
|
||||
leaf events {
|
||||
type boolean;
|
||||
default false;
|
||||
description "Enable RIP events debug messages (sending/receiving packets, timers, interface changes)";
|
||||
}
|
||||
leaf packet {
|
||||
type boolean;
|
||||
default false;
|
||||
description "Enable detailed RIP packet debug messages (packet dumps with origin and port)";
|
||||
}
|
||||
leaf kernel {
|
||||
type boolean;
|
||||
default false;
|
||||
description "Enable kernel routing table debug messages (route add/delete, interface updates)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add send/receive version control per RIP interface
|
||||
* The standard ietf-rip model doesn't include version control,
|
||||
* but FRR supports this and it's useful for controlling RIP protocol versions
|
||||
* on a per-interface basis (e.g., for NBMA links or mixed networks).
|
||||
*/
|
||||
augment "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/"
|
||||
+ "rip:rip/rip:interfaces/rip:interface" {
|
||||
when "derived-from-or-self(../../../rt:type, 'infix-routing:ripv2')" {
|
||||
description
|
||||
"This augmentation is only valid for RIPv2.";
|
||||
}
|
||||
description
|
||||
"Infix extension to add RIP version control per interface.";
|
||||
|
||||
leaf send-version {
|
||||
type enumeration {
|
||||
enum "1" {
|
||||
description "Send RIPv1 packets";
|
||||
}
|
||||
enum "2" {
|
||||
description "Send RIPv2 packets";
|
||||
}
|
||||
enum "1-2" {
|
||||
description "Send both RIPv1 and RIPv2 packets";
|
||||
}
|
||||
}
|
||||
description
|
||||
"RIP version to send on this interface. If not configured, FRR defaults to version 2.";
|
||||
}
|
||||
|
||||
leaf receive-version {
|
||||
type enumeration {
|
||||
enum "1" {
|
||||
description "Receive RIPv1 packets";
|
||||
}
|
||||
enum "2" {
|
||||
description "Receive RIPv2 packets";
|
||||
}
|
||||
enum "1-2" {
|
||||
description "Receive both RIPv1 and RIPv2 packets";
|
||||
}
|
||||
}
|
||||
description
|
||||
"RIP version to receive on this interface. If not configured, FRR defaults to version 2.";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Augmentations
|
||||
*/
|
||||
|
||||
@@ -528,6 +528,35 @@
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="rip" help="Show RIP status">
|
||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||
show rip |pager
|
||||
</ACTION>
|
||||
|
||||
<SWITCH name="optional" min="0">
|
||||
<COMMAND name="route" help="Show RIP routing table">
|
||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||
show rip route |pager
|
||||
</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="interface" help="Show RIP 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 rip interface "$KLISH_PARAM_ifname" |pager
|
||||
</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="neighbor" help="Show RIP neighbors">
|
||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||
show rip neighbor |pager
|
||||
</ACTION>
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
</COMMAND>
|
||||
|
||||
|
||||
+50
-2
@@ -95,6 +95,12 @@ def interface(args: List[str]) -> None:
|
||||
print("No interface data retrieved.")
|
||||
return
|
||||
|
||||
# Also fetch routing interface list for forwarding indication
|
||||
routing_data = run_sysrepocfg("/ietf-routing:routing")
|
||||
if routing_data:
|
||||
# Merge routing data into the main data structure
|
||||
data.update(routing_data)
|
||||
|
||||
if RAW_OUTPUT:
|
||||
print(json.dumps(data, indent=2))
|
||||
return
|
||||
@@ -295,6 +301,47 @@ def ospf(args: List[str]) -> None:
|
||||
else:
|
||||
print(f"Unknown OSPF subcommand: {subcommand}")
|
||||
|
||||
def rip(args: List[str]) -> None:
|
||||
"""Handle show rip [subcommand] [ifname]
|
||||
|
||||
Subcommands:
|
||||
(none) - General RIP instance information
|
||||
route - RIP routing table
|
||||
interface - RIP interface details (optionally for specific interface)
|
||||
neighbor - RIP neighbor information
|
||||
|
||||
Optional:
|
||||
ifname - Interface name (for interface subcommand)
|
||||
"""
|
||||
# Fetch operational data from sysrepocfg
|
||||
data = run_sysrepocfg("/ietf-routing:routing/control-plane-protocols/control-plane-protocol")
|
||||
if not data:
|
||||
data = {}
|
||||
|
||||
if RAW_OUTPUT:
|
||||
print(json.dumps(data, indent=2))
|
||||
return
|
||||
|
||||
# Parse arguments: subcommand, optional interface name
|
||||
subcommand = args[0] if len(args) > 0 and args[0] else ""
|
||||
ifname = args[1] if len(args) > 1 else None
|
||||
|
||||
# Add metadata to data for formatters
|
||||
if ifname:
|
||||
data['_ifname'] = ifname
|
||||
|
||||
# Route to appropriate formatter
|
||||
if subcommand == "":
|
||||
cli_pretty(data, "show-rip")
|
||||
elif subcommand == "route" or subcommand == "routes":
|
||||
cli_pretty(data, "show-rip-routes")
|
||||
elif subcommand == "interface" or subcommand == "interfaces":
|
||||
cli_pretty(data, "show-rip-interfaces")
|
||||
elif subcommand == "neighbor" or subcommand == "neighbors":
|
||||
cli_pretty(data, "show-rip-neighbors")
|
||||
else:
|
||||
print(f"Unknown RIP subcommand: {subcommand}")
|
||||
|
||||
def routes(args: List[str]):
|
||||
ip_version = args[0] if args and args[0] in ["ipv4", "ipv6"] else "ipv4"
|
||||
|
||||
@@ -445,9 +492,10 @@ def execute_command(command: str, args: List[str]):
|
||||
'lldp': lldp,
|
||||
'ntp': ntp,
|
||||
'ospf': ospf,
|
||||
'rip': rip,
|
||||
'routes': routes,
|
||||
'services' : services,
|
||||
'software' : software,
|
||||
'services': services,
|
||||
'software': software,
|
||||
'stp': stp,
|
||||
'system': system,
|
||||
'wifi': wifi
|
||||
|
||||
@@ -98,6 +98,7 @@ def compress_interface_list(interfaces):
|
||||
|
||||
|
||||
class Pad:
|
||||
flags = 2
|
||||
iface = 16
|
||||
proto = 11
|
||||
state = 12
|
||||
@@ -803,6 +804,9 @@ class DhcpServer:
|
||||
|
||||
|
||||
class Iface:
|
||||
# Class variable to hold routing-enabled interfaces for the current display session
|
||||
_routing_ifaces = set()
|
||||
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.name = data.get('name', '')
|
||||
@@ -915,13 +919,16 @@ class Iface:
|
||||
return self.oper_status
|
||||
|
||||
def pr_name(self, pipe=""):
|
||||
flag = "⇅" if self.name in Iface._routing_ifaces else " "
|
||||
print(f"{flag:<{Pad.flags}}", end="")
|
||||
print(f"{pipe}{self.name:<{Pad.iface - len(pipe)}}", end="")
|
||||
|
||||
def pr_proto_ipv4(self, pipe=''):
|
||||
for addr in self.ipv4_addr:
|
||||
origin = f"({addr['origin']})" if addr.get('origin') else ""
|
||||
|
||||
row = f"{pipe:<{Pad.iface}}"
|
||||
row = f"{'':<{Pad.flags}}"
|
||||
row += f"{pipe:<{Pad.iface}}"
|
||||
row += f"{'ipv4':<{Pad.proto}}"
|
||||
row += f"{'':<{Pad.state}}{addr['ip']}/{addr['prefix-length']} {origin}"
|
||||
print(row)
|
||||
@@ -930,7 +937,8 @@ class Iface:
|
||||
for addr in self.ipv6_addr:
|
||||
origin = f"({addr['origin']})" if addr.get('origin') else ""
|
||||
|
||||
row = f"{pipe:<{Pad.iface}}"
|
||||
row = f"{'':<{Pad.flags}}"
|
||||
row += f"{pipe:<{Pad.iface}}"
|
||||
row += f"{'ipv6':<{Pad.proto}}"
|
||||
row += f"{'':<{Pad.state}}{addr['ip']}/{addr['prefix-length']} {origin}"
|
||||
print(row)
|
||||
@@ -938,7 +946,8 @@ class Iface:
|
||||
def _pr_proto_common(self, name, phys_address, pipe=''):
|
||||
row = ""
|
||||
if len(pipe) > 0:
|
||||
row = f"{pipe:<{Pad.iface}}"
|
||||
row = f"{'':<{Pad.flags}}"
|
||||
row += f"{pipe:<{Pad.iface}}"
|
||||
|
||||
row += f"{name:<{Pad.proto}}"
|
||||
dec = Decore.green if self.oper() == "up" else Decore.red
|
||||
@@ -1184,6 +1193,11 @@ class Iface:
|
||||
parent.pr_proto_eth()
|
||||
|
||||
def pr_container(self):
|
||||
# Add ⇅ flag for interfaces with IP forwarding enabled
|
||||
flag = "⇅" if self.name in Iface._routing_ifaces else " "
|
||||
# Flag column is outside the gray background
|
||||
print(f"{flag:<{Pad.flags}}", end="")
|
||||
|
||||
row = f"{self.name:<{Pad.iface}}"
|
||||
row += f"{'container':<{Pad.proto}}"
|
||||
row += f"{'':<{Pad.state}}"
|
||||
@@ -1204,6 +1218,9 @@ class Iface:
|
||||
if self.oper():
|
||||
print(f"{'operational status':<{20}}: {self.oper(detail=True)}")
|
||||
|
||||
forwarding = "enabled" if self.name in Iface._routing_ifaces else "disabled"
|
||||
print(f"{'ip forwarding':<{20}}: {forwarding}")
|
||||
|
||||
if self.lower_if:
|
||||
print(f"{'lower-layer-if':<{20}}: {self.lower_if}")
|
||||
|
||||
@@ -1407,13 +1424,21 @@ def print_interface(iface):
|
||||
|
||||
|
||||
def pr_interface_list(json):
|
||||
hdr = (f"{'INTERFACE':<{Pad.iface}}"
|
||||
hdr = (f"{'⚑':<{Pad.flags}}"
|
||||
f"{'INTERFACE':<{Pad.iface}}"
|
||||
f"{'PROTOCOL':<{Pad.proto}}"
|
||||
f"{'STATE':<{Pad.state}}"
|
||||
f"{'DATA':<{Pad.data}}")
|
||||
|
||||
print(Decore.invert(hdr))
|
||||
|
||||
# Set class variable with routing-enabled interfaces (with IP forwarding)
|
||||
if "ietf-routing:routing" in json:
|
||||
routing_data = json["ietf-routing:routing"].get("interfaces", {})
|
||||
Iface._routing_ifaces = set(routing_data.get("interface", []))
|
||||
else:
|
||||
Iface._routing_ifaces = set()
|
||||
|
||||
ifaces = sorted(json["ietf-interfaces:interfaces"]["interface"],
|
||||
key=ifname_sort)
|
||||
iface = find_iface(ifaces, "lo")
|
||||
@@ -1472,6 +1497,13 @@ def pr_interface_list(json):
|
||||
|
||||
|
||||
def show_interfaces(json, name):
|
||||
# Set class variable with routing-enabled interfaces (with IP forwarding)
|
||||
if "ietf-routing:routing" in json:
|
||||
routing_data = json["ietf-routing:routing"].get("interfaces", {})
|
||||
Iface._routing_ifaces = set(routing_data.get("interface", []))
|
||||
else:
|
||||
Iface._routing_ifaces = set()
|
||||
|
||||
if name:
|
||||
if not json.get("ietf-interfaces:interfaces"):
|
||||
print(f"No interface data found for \"{name}\"")
|
||||
@@ -3260,6 +3292,165 @@ def show_ospf_routes(json_data):
|
||||
print(f"{'':<52} {next_hop_addr:<16} {outgoing_iface:<12}")
|
||||
|
||||
|
||||
def show_rip(json_data):
|
||||
"""Show RIP general instance information"""
|
||||
routing = json_data.get('ietf-routing:routing', {})
|
||||
protocols = routing.get('control-plane-protocols', {}).get('control-plane-protocol', [])
|
||||
|
||||
rip_instance = None
|
||||
for protocol in protocols:
|
||||
if 'ietf-rip:rip' in protocol:
|
||||
rip_instance = protocol
|
||||
break
|
||||
|
||||
if not rip_instance:
|
||||
print("RIP is not configured or running")
|
||||
return
|
||||
|
||||
rip = rip_instance.get('ietf-rip:rip', {})
|
||||
|
||||
# Display RIP configuration
|
||||
print(" RIP Routing Process")
|
||||
print()
|
||||
|
||||
distance = rip.get('distance', 120)
|
||||
default_metric = rip.get('default-metric', 1)
|
||||
num_routes = rip.get('num-of-routes', 0)
|
||||
|
||||
print(f" Administrative distance: {distance}")
|
||||
print(f" Default metric: {default_metric}")
|
||||
print(f" Number of RIP routes: {num_routes}")
|
||||
print()
|
||||
|
||||
# Display timers if available
|
||||
timers = rip.get('timers', {})
|
||||
if timers:
|
||||
update = timers.get('update-interval', 30)
|
||||
invalid = timers.get('invalid-interval', 180)
|
||||
flush = timers.get('flush-interval', 240)
|
||||
print(" Timers:")
|
||||
print(f" Update interval: {update} seconds")
|
||||
print(f" Invalid interval: {invalid} seconds")
|
||||
print(f" Flush interval: {flush} seconds")
|
||||
print()
|
||||
|
||||
# Display interfaces
|
||||
interfaces = rip.get('interfaces', {}).get('interface', [])
|
||||
if interfaces:
|
||||
print(f" Number of interfaces: {len(interfaces)}")
|
||||
for iface in interfaces:
|
||||
iface_name = iface.get('interface', 'unknown')
|
||||
print(f" {iface_name}")
|
||||
print()
|
||||
|
||||
|
||||
def show_rip_routes(json_data):
|
||||
"""Show RIP routing table"""
|
||||
routing = json_data.get('ietf-routing:routing', {})
|
||||
protocols = routing.get('control-plane-protocols', {}).get('control-plane-protocol', [])
|
||||
|
||||
rip_instance = None
|
||||
for protocol in protocols:
|
||||
if 'ietf-rip:rip' in protocol:
|
||||
rip_instance = protocol
|
||||
break
|
||||
|
||||
if not rip_instance:
|
||||
print("RIP is not configured or running")
|
||||
return
|
||||
|
||||
rip = rip_instance.get('ietf-rip:rip', {})
|
||||
routes = rip.get('ipv4', {}).get('routes', {}).get('route', [])
|
||||
|
||||
if not routes:
|
||||
print("No RIP routes")
|
||||
return
|
||||
|
||||
# Header
|
||||
hdr = f"{'PREFIX':<20} {'METRIC':<8} {'NEXT-HOP':<16} {'INTERFACE':<12}"
|
||||
print(Decore.invert(hdr))
|
||||
|
||||
for route in routes:
|
||||
prefix = route.get('ipv4-prefix', 'unknown')
|
||||
metric = route.get('metric', 0)
|
||||
next_hop = route.get('next-hop', '-')
|
||||
interface = route.get('interface', '-')
|
||||
|
||||
print(f"{prefix:<20} {metric:<8} {next_hop:<16} {interface:<12}")
|
||||
|
||||
|
||||
def show_rip_interfaces(json_data):
|
||||
"""Show RIP interface information"""
|
||||
routing = json_data.get('ietf-routing:routing', {})
|
||||
protocols = routing.get('control-plane-protocols', {}).get('control-plane-protocol', [])
|
||||
|
||||
rip_instance = None
|
||||
for protocol in protocols:
|
||||
if 'ietf-rip:rip' in protocol:
|
||||
rip_instance = protocol
|
||||
break
|
||||
|
||||
if not rip_instance:
|
||||
print("RIP is not configured or running")
|
||||
return
|
||||
|
||||
rip = rip_instance.get('ietf-rip:rip', {})
|
||||
interfaces = rip.get('interfaces', {}).get('interface', [])
|
||||
|
||||
if not interfaces:
|
||||
print("No RIP interfaces")
|
||||
return
|
||||
|
||||
# Header
|
||||
hdr = f"{'INTERFACE':<12} {'STATUS':<10} {'SEND':<6} {'RECV':<6} {'SPLIT-HORIZON':<20} {'PASSIVE':<10}"
|
||||
print(Decore.invert(hdr))
|
||||
|
||||
for iface in interfaces:
|
||||
iface_name = iface.get('interface', 'unknown')
|
||||
oper_status = iface.get('oper-status', 'down')
|
||||
send_ver = iface.get('send-version', '-')
|
||||
recv_ver = iface.get('receive-version', '-')
|
||||
split_horizon = iface.get('split-horizon', 'simple')
|
||||
passive = 'Yes' if iface.get('passive', False) else 'No'
|
||||
|
||||
print(f"{iface_name:<12} {oper_status:<10} {send_ver:<6} {recv_ver:<6} {split_horizon:<20} {passive:<10}")
|
||||
|
||||
|
||||
def show_rip_neighbors(json_data):
|
||||
"""Show RIP neighbor information"""
|
||||
routing = json_data.get('ietf-routing:routing', {})
|
||||
protocols = routing.get('control-plane-protocols', {}).get('control-plane-protocol', [])
|
||||
|
||||
rip_instance = None
|
||||
for protocol in protocols:
|
||||
if 'ietf-rip:rip' in protocol:
|
||||
rip_instance = protocol
|
||||
break
|
||||
|
||||
if not rip_instance:
|
||||
print("RIP is not configured or running")
|
||||
return
|
||||
|
||||
rip = rip_instance.get('ietf-rip:rip', {})
|
||||
ipv4 = rip.get('ipv4', {})
|
||||
neighbors = ipv4.get('neighbors', {}).get('neighbor', [])
|
||||
|
||||
if not neighbors:
|
||||
print("No RIP neighbors")
|
||||
return
|
||||
|
||||
# Header
|
||||
hdr = f"{'ADDRESS':<16} {'BAD-PACKETS':<14} {'BAD-ROUTES':<12}"
|
||||
print(Decore.invert(hdr))
|
||||
|
||||
for neighbor in neighbors:
|
||||
address = neighbor.get('ipv4-address', 'unknown')
|
||||
bad_packets = neighbor.get('bad-packets-rcvd', 0)
|
||||
bad_routes = neighbor.get('bad-routes-rcvd', 0)
|
||||
|
||||
print(f"{address:<16} {bad_packets:<14} {bad_routes:<12}")
|
||||
|
||||
|
||||
def show_bfd_status(json_data):
|
||||
"""Show BFD status summary"""
|
||||
routing = json_data.get('ietf-routing:routing', {})
|
||||
@@ -3549,6 +3740,11 @@ def main():
|
||||
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-rip', help='Show RIP instance information')
|
||||
subparsers.add_parser('show-rip-routes', help='Show RIP routing table')
|
||||
subparsers.add_parser('show-rip-interfaces', help='Show RIP interfaces')
|
||||
subparsers.add_parser('show-rip-neighbors', help='Show RIP neighbors')
|
||||
|
||||
subparsers.add_parser('show-routing-table', help='Show the routing table') \
|
||||
.add_argument('-i', '--ip', required=True, help='IPv4 or IPv6 address')
|
||||
|
||||
@@ -3602,6 +3798,14 @@ def main():
|
||||
show_ospf_neighbor(json_data)
|
||||
elif args.command == "show-ospf-routes":
|
||||
show_ospf_routes(json_data)
|
||||
elif args.command == "show-rip":
|
||||
show_rip(json_data)
|
||||
elif args.command == "show-rip-routes":
|
||||
show_rip_routes(json_data)
|
||||
elif args.command == "show-rip-interfaces":
|
||||
show_rip_interfaces(json_data)
|
||||
elif args.command == "show-rip-neighbors":
|
||||
show_rip_neighbors(json_data)
|
||||
elif args.command == "show-routing-table":
|
||||
show_routing_table(json_data, args.ip)
|
||||
elif args.command == "show-software":
|
||||
|
||||
@@ -6,7 +6,8 @@ license = "MIT"
|
||||
packages = [
|
||||
{ include = "yanger" },
|
||||
{ include = "cli_pretty" },
|
||||
{ include = "ospf_status" }
|
||||
{ include = "ospf_status" },
|
||||
{ include = "rip_status" }
|
||||
]
|
||||
authors = [
|
||||
"KernelKit developers"
|
||||
@@ -21,3 +22,4 @@ build-backend = "poetry.core.masonry.api"
|
||||
yanger = "yanger.__main__:main"
|
||||
cli-pretty = "cli_pretty:main"
|
||||
ospf-status = "ospf_status:main"
|
||||
rip-status = "rip_status.rip_status:main"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/python3
|
||||
# This script is used to transform the output from the show ip rip commands
|
||||
# to match the ietf-rip YANG model structure.
|
||||
#
|
||||
# This makes the parsing for the operational parts of YANG model more easy
|
||||
#
|
||||
|
||||
import sys
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
|
||||
def run_json_cmd(cmd, default=None, check=True):
|
||||
"""Run a command (array of args) with JSON output and return the JSON"""
|
||||
try:
|
||||
result = subprocess.run(cmd, check=check, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, text=True)
|
||||
output = result.stdout
|
||||
data = json.loads(output)
|
||||
except (subprocess.CalledProcessError, json.JSONDecodeError):
|
||||
if default is not None:
|
||||
return default
|
||||
raise
|
||||
return data
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Collects RIP operational data from FRR vtysh and transforms it
|
||||
into a structure that matches the ietf-rip YANG model.
|
||||
"""
|
||||
rip_routes_cmd = ['sudo', 'vtysh', '-c', "show ip rip json"]
|
||||
rip_status_cmd = ['sudo', 'vtysh', '-c', "show ip rip status json"]
|
||||
|
||||
try:
|
||||
routes = run_json_cmd(rip_routes_cmd, default={})
|
||||
status = run_json_cmd(rip_status_cmd, default={})
|
||||
except (subprocess.CalledProcessError, json.JSONDecodeError):
|
||||
return {}
|
||||
|
||||
# Build the structure matching ietf-rip YANG model
|
||||
result = {
|
||||
"routes": routes.get("routes", {}),
|
||||
"status": status
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
data = main()
|
||||
print(json.dumps(data, indent=2))
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
@@ -63,6 +63,9 @@ def main():
|
||||
elif args.model == 'ietf-ospf':
|
||||
from . import ietf_ospf
|
||||
yang_data = ietf_ospf.operational()
|
||||
elif args.model == 'ietf-rip':
|
||||
from . import ietf_rip
|
||||
yang_data = ietf_rip.operational()
|
||||
elif args.model == 'ietf-hardware':
|
||||
from . import ietf_hardware
|
||||
yang_data = ietf_hardware.operational()
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
import re
|
||||
from .host import HOST
|
||||
|
||||
|
||||
def parse_rip_status():
|
||||
"""Parse 'show ip rip status' text output to extract operational state
|
||||
|
||||
Returns dict with keys: update-interval, invalid-interval, flush-interval,
|
||||
default-metric, distance, interfaces (list), neighbors (list)
|
||||
"""
|
||||
try:
|
||||
# HOST.run expects tuple, returns text string directly
|
||||
text = HOST.run(tuple(['vtysh', '-c', 'show ip rip status']), default="")
|
||||
if not text:
|
||||
return {}
|
||||
except Exception as e:
|
||||
return {}
|
||||
|
||||
status = {}
|
||||
|
||||
# Parse: "Sending updates every 30 seconds"
|
||||
match = re.search(r'Sending updates every (\d+) seconds', text)
|
||||
if match:
|
||||
status['update-interval'] = int(match.group(1))
|
||||
|
||||
# Parse: "Timeout after 180 seconds"
|
||||
match = re.search(r'Timeout after (\d+) seconds', text)
|
||||
if match:
|
||||
status['invalid-interval'] = int(match.group(1))
|
||||
|
||||
# Parse: "garbage collect after 240 seconds"
|
||||
match = re.search(r'garbage collect after (\d+) seconds', text)
|
||||
if match:
|
||||
status['flush-interval'] = int(match.group(1))
|
||||
|
||||
# Parse: "Default redistribution metric is 1"
|
||||
match = re.search(r'Default redistribution metric is (\d+)', text)
|
||||
if match:
|
||||
status['default-metric'] = int(match.group(1))
|
||||
|
||||
# Parse: "Distance: (default is 120)"
|
||||
match = re.search(r'Distance: \(default is (\d+)\)', text)
|
||||
if match:
|
||||
status['distance'] = int(match.group(1))
|
||||
|
||||
# Parse interface table:
|
||||
# Interface Send Recv Key-chain
|
||||
# e5 2 2
|
||||
interfaces = []
|
||||
in_interface_section = False
|
||||
for line in text.split('\n'):
|
||||
line = line.strip()
|
||||
|
||||
# Detect start of interface section
|
||||
if 'Interface' in line and 'Send' in line and 'Recv' in line:
|
||||
in_interface_section = True
|
||||
continue
|
||||
|
||||
# Stop at next section
|
||||
if in_interface_section and (line.startswith('Routing for Networks:') or
|
||||
line.startswith('Routing Information Sources:')):
|
||||
break
|
||||
|
||||
# Parse interface lines
|
||||
if in_interface_section and line:
|
||||
# Format: "e5 2 2 "
|
||||
parts = line.split()
|
||||
if len(parts) >= 3 and not line.startswith('Interface'):
|
||||
iface_name = parts[0]
|
||||
send_version = parts[1]
|
||||
recv_version = parts[2]
|
||||
|
||||
interfaces.append({
|
||||
'name': iface_name,
|
||||
'send-version': int(send_version),
|
||||
'recv-version': int(recv_version)
|
||||
})
|
||||
|
||||
if interfaces:
|
||||
status['interfaces'] = interfaces
|
||||
|
||||
# Parse Routing Information Sources table:
|
||||
# Gateway BadPackets BadRoutes Distance Last Update
|
||||
# 192.168.50.2 0 0 120 00:00:09
|
||||
neighbors = []
|
||||
in_neighbor_section = False
|
||||
for line in text.split('\n'):
|
||||
line = line.strip()
|
||||
|
||||
# Detect start of Routing Information Sources section
|
||||
if line.startswith('Routing Information Sources:'):
|
||||
in_neighbor_section = True
|
||||
continue
|
||||
|
||||
# Skip the header line
|
||||
if in_neighbor_section and 'Gateway' in line and 'BadPackets' in line:
|
||||
continue
|
||||
|
||||
# Stop at next section (Distance line or empty lines after table)
|
||||
if in_neighbor_section and (line.startswith('Distance:') or
|
||||
(not line and neighbors)):
|
||||
break
|
||||
|
||||
# Parse neighbor lines
|
||||
if in_neighbor_section and line:
|
||||
# Format: "192.168.50.2 0 0 120 00:00:09"
|
||||
parts = line.split()
|
||||
if len(parts) >= 5:
|
||||
try:
|
||||
gateway = parts[0]
|
||||
bad_packets = int(parts[1])
|
||||
bad_routes = int(parts[2])
|
||||
distance = int(parts[3])
|
||||
last_update = parts[4] # Format: HH:MM:SS
|
||||
|
||||
neighbors.append({
|
||||
'address': gateway,
|
||||
'bad-packets': bad_packets,
|
||||
'bad-routes': bad_routes,
|
||||
'distance': distance,
|
||||
'last-update': last_update
|
||||
})
|
||||
except (ValueError, IndexError):
|
||||
# Skip lines that don't parse correctly
|
||||
continue
|
||||
|
||||
if neighbors:
|
||||
status['neighbors'] = neighbors
|
||||
|
||||
return status
|
||||
|
||||
|
||||
def add_rip(control_protocols):
|
||||
"""Populate RIP operational data
|
||||
|
||||
Note: FRR's RIP implementation provides JSON for routing table but not for
|
||||
status commands, so we combine JSON route data with text parsing for status.
|
||||
"""
|
||||
# Get operational status from text parsing
|
||||
status = parse_rip_status()
|
||||
|
||||
# Check if RIP is running - if we can't get status, it's probably not running
|
||||
if not status:
|
||||
return
|
||||
|
||||
control_protocol = {}
|
||||
control_protocol["type"] = "infix-routing:ripv2"
|
||||
control_protocol["name"] = "default"
|
||||
control_protocol["ietf-rip:rip"] = {}
|
||||
|
||||
rip = control_protocol["ietf-rip:rip"]
|
||||
|
||||
# Add global operational state
|
||||
if status.get('distance'):
|
||||
rip['distance'] = status['distance']
|
||||
if status.get('default-metric'):
|
||||
rip['default-metric'] = status['default-metric']
|
||||
|
||||
# Add timers if available
|
||||
if any(k in status for k in ['update-interval', 'invalid-interval', 'flush-interval']):
|
||||
rip['timers'] = {}
|
||||
if status.get('update-interval'):
|
||||
rip['timers']['update-interval'] = status['update-interval']
|
||||
if status.get('invalid-interval'):
|
||||
rip['timers']['invalid-interval'] = status['invalid-interval']
|
||||
if status.get('flush-interval'):
|
||||
rip['timers']['flush-interval'] = status['flush-interval']
|
||||
|
||||
# Add interfaces if available
|
||||
if status.get('interfaces'):
|
||||
rip['interfaces'] = {'interface': []}
|
||||
for iface in status['interfaces']:
|
||||
iface_data = {
|
||||
'interface': iface['name'],
|
||||
'oper-status': 'up' # If it's in the list, it's operational
|
||||
}
|
||||
|
||||
# Map FRR version numbers to YANG enum values
|
||||
# FRR shows: 1=v1, 2=v2, and we assume if both are enabled it would show differently
|
||||
send_ver = iface.get('send-version')
|
||||
if send_ver == 1:
|
||||
iface_data['send-version'] = '1'
|
||||
elif send_ver == 2:
|
||||
iface_data['send-version'] = '2'
|
||||
# Note: FRR might show this differently for mixed mode
|
||||
|
||||
recv_ver = iface.get('recv-version')
|
||||
if recv_ver == 1:
|
||||
iface_data['receive-version'] = '1'
|
||||
elif recv_ver == 2:
|
||||
iface_data['receive-version'] = '2'
|
||||
|
||||
rip['interfaces']['interface'].append(iface_data)
|
||||
|
||||
# Get RIP-learned routes from routing table (JSON)
|
||||
# This shows routes learned via RIP (R(n) entries), not redistributed routes
|
||||
route_data = HOST.run_json(['vtysh', '-c', 'show ip route rip json'], default={})
|
||||
|
||||
routes = []
|
||||
for prefix, entries in route_data.items():
|
||||
if not entries or '/' not in prefix:
|
||||
continue
|
||||
|
||||
# Use first entry (RIP doesn't typically have ECMP)
|
||||
entry = entries[0] if isinstance(entries, list) else entries
|
||||
|
||||
route = {
|
||||
"ipv4-prefix": prefix,
|
||||
"metric": entry.get("metric", 0),
|
||||
"route-type": "rip"
|
||||
}
|
||||
|
||||
# Get next hop information
|
||||
nexthops = entry.get("nexthops", [])
|
||||
if nexthops:
|
||||
first_hop = nexthops[0]
|
||||
if first_hop.get("ip"):
|
||||
route["next-hop"] = first_hop["ip"]
|
||||
if first_hop.get("interfaceName"):
|
||||
route["interface"] = first_hop["interfaceName"]
|
||||
|
||||
routes.append(route)
|
||||
|
||||
# Add neighbors to operational data
|
||||
neighbors_list = []
|
||||
if status.get('neighbors'):
|
||||
for neighbor in status['neighbors']:
|
||||
neighbor_data = {
|
||||
'ipv4-address': neighbor['address'],
|
||||
'bad-packets-rcvd': neighbor['bad-packets'],
|
||||
'bad-routes-rcvd': neighbor['bad-routes']
|
||||
}
|
||||
# Note: IETF YANG expects last-update as yang:date-and-time
|
||||
# but FRR gives us a relative time like "00:00:09"
|
||||
# We'll skip last-update for now or convert it if needed
|
||||
neighbors_list.append(neighbor_data)
|
||||
|
||||
# Add routes and neighbors to operational data
|
||||
if routes or neighbors_list:
|
||||
if "ipv4" not in rip:
|
||||
rip["ipv4"] = {}
|
||||
|
||||
if routes:
|
||||
rip["ipv4"]["routes"] = {
|
||||
"route": routes
|
||||
}
|
||||
# Add route count
|
||||
rip["num-of-routes"] = len(routes)
|
||||
|
||||
if neighbors_list:
|
||||
rip["ipv4"]["neighbors"] = {
|
||||
"neighbor": neighbors_list
|
||||
}
|
||||
|
||||
# Add the control-protocol
|
||||
if "ietf-routing:control-plane-protocol" not in control_protocols:
|
||||
control_protocols["ietf-routing:control-plane-protocol"] = []
|
||||
control_protocols["ietf-routing:control-plane-protocol"].append(control_protocol)
|
||||
|
||||
|
||||
def operational():
|
||||
"""Return RIP operational data in YANG format"""
|
||||
out = {
|
||||
"ietf-routing:routing": {
|
||||
"control-plane-protocols": {}
|
||||
}
|
||||
}
|
||||
|
||||
add_rip(out['ietf-routing:routing']['control-plane-protocols'])
|
||||
return out
|
||||
@@ -51,6 +51,7 @@ def add_protocol(routes, proto):
|
||||
'static': 'static',
|
||||
'ospf': 'ietf-ospf:ospfv2',
|
||||
'ospf6': 'ietf-ospf:ospfv3',
|
||||
'rip': 'ietf-rip:rip',
|
||||
}
|
||||
|
||||
out = {}
|
||||
@@ -75,9 +76,11 @@ def add_protocol(routes, proto):
|
||||
new['source-protocol'] = pmap.get(frr, 'infix-routing:kernel')
|
||||
new['route-preference'] = route.get('distance', 0)
|
||||
|
||||
# Metric only available in the model for OSPF routes
|
||||
# Metric only available in the model for OSPF and RIP routes
|
||||
if 'ospf' in frr:
|
||||
new['ietf-ospf:metric'] = route.get('metric', 0)
|
||||
elif 'rip' in frr:
|
||||
new['ietf-rip:metric'] = route.get('metric', 0)
|
||||
|
||||
# See https://datatracker.ietf.org/doc/html/rfc7951#section-6.9
|
||||
# for details on how presence leaves are encoded in JSON: [null]
|
||||
@@ -121,9 +124,37 @@ def add_protocol(routes, proto):
|
||||
insert(routes, 'routes', out)
|
||||
|
||||
|
||||
def get_routing_interfaces():
|
||||
"""Get list of interfaces with IPv4 forwarding enabled"""
|
||||
import json
|
||||
|
||||
# Get all interfaces
|
||||
links_json = HOST.run(tuple(['ip', '-j', 'link', 'show']), default="[]")
|
||||
links = json.loads(links_json)
|
||||
|
||||
routing_ifaces = []
|
||||
for link in links:
|
||||
ifname = link.get('ifname')
|
||||
if not ifname:
|
||||
continue
|
||||
|
||||
# Check if IPv4 forwarding is enabled
|
||||
# Note: We only check IPv4 forwarding. IPv6 forwarding behaves differently
|
||||
# and will be handled separately when Linux 6.17+ force_forwarding is available.
|
||||
ipv4_fwd = HOST.run(tuple(['sysctl', '-n', f'net.ipv4.conf.{ifname}.forwarding']), default="0").strip()
|
||||
|
||||
if ipv4_fwd == "1":
|
||||
routing_ifaces.append(ifname)
|
||||
|
||||
return routing_ifaces
|
||||
|
||||
|
||||
def operational():
|
||||
out = {
|
||||
"ietf-routing:routing": {
|
||||
"interfaces": {
|
||||
"interface": get_routing_interfaces()
|
||||
},
|
||||
"ribs": {
|
||||
"rib": [{
|
||||
"name": "ipv4",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#define XPATH_HARDWARE_BASE "/ietf-hardware:hardware"
|
||||
#define XPATH_SYSTEM_BASE "/ietf-system"
|
||||
#define XPATH_ROUTING_OSPF XPATH_ROUTING_BASE "/ospf"
|
||||
#define XPATH_ROUTING_RIP XPATH_ROUTING_BASE "/rip"
|
||||
#define XPATH_ROUTING_BFD XPATH_ROUTING_BASE "/bfd"
|
||||
#define XPATH_CONTAIN_BASE "/infix-containers:containers"
|
||||
#define XPATH_DHCP_SERVER_BASE "/infix-dhcp-server:dhcp-server"
|
||||
@@ -260,6 +261,42 @@ static int sr_ospf_cb(sr_session_ctx_t *session, uint32_t, const char *,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sr_rip_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-rip",
|
||||
NULL
|
||||
};
|
||||
const struct ly_ctx *ctx;
|
||||
sr_conn_ctx_t *con;
|
||||
sr_error_t err;
|
||||
|
||||
DEBUG("Incoming rip 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 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)
|
||||
@@ -380,6 +417,8 @@ static int subscribe_to_all(struct statd *statd)
|
||||
return SR_ERR_INTERNAL;
|
||||
if (subscribe(statd, "ietf-routing", XPATH_ROUTING_OSPF, sr_ospf_cb))
|
||||
return SR_ERR_INTERNAL;
|
||||
if (subscribe(statd, "ietf-routing", XPATH_ROUTING_RIP, sr_rip_cb))
|
||||
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))
|
||||
|
||||
@@ -7,9 +7,13 @@ Tests verifying standard routing protocols and configuration:
|
||||
- OSPF basic neighbor discovery and LSA exchange
|
||||
- OSPF unnumbered interface configuration
|
||||
- OSPF multi-area configuration and inter-area routing
|
||||
- OSPF with BFD (Bidirectional Forwarding Detection)
|
||||
- OSPF with BFD (Bidirectional Forwarding Detection)
|
||||
- OSPF default route advertisement and propagation
|
||||
- OSPF debug logging configuration and verification
|
||||
- RIP basic neighbor discovery and route exchange
|
||||
- RIP passive interface configuration
|
||||
- RIP route redistribution (connected, static, and OSPF)
|
||||
- RIP multi-hop routing with multiple neighbors
|
||||
- Route preference handling for OSPF routes
|
||||
- Route preference handling for DHCP-learned routes
|
||||
- Administrative distance configuration (route preference 255)
|
||||
@@ -42,6 +46,22 @@ include::ospf_debug/Readme.adoc[]
|
||||
|
||||
<<<
|
||||
|
||||
include::rip_basic/Readme.adoc[]
|
||||
|
||||
<<<
|
||||
|
||||
include::rip_passive_interface/Readme.adoc[]
|
||||
|
||||
<<<
|
||||
|
||||
include::rip_redistribute/Readme.adoc[]
|
||||
|
||||
<<<
|
||||
|
||||
include::rip_multihop/Readme.adoc[]
|
||||
|
||||
<<<
|
||||
|
||||
include::route_pref_ospf/Readme.adoc[]
|
||||
|
||||
<<<
|
||||
|
||||
@@ -28,3 +28,15 @@
|
||||
|
||||
- name: OSPF Debug Logging
|
||||
case: ospf_debug/test.py
|
||||
|
||||
- name: RIP Basic
|
||||
case: rip_basic/test.py
|
||||
|
||||
- name: RIP Passive Interface
|
||||
case: rip_passive_interface/test.py
|
||||
|
||||
- name: RIP Redistribution
|
||||
case: rip_redistribute/test.py
|
||||
|
||||
- name: RIP Multi-hop
|
||||
case: rip_multihop/test.py
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
test.adoc
|
||||
@@ -0,0 +1,28 @@
|
||||
=== RIP Basic
|
||||
|
||||
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/routing/rip_basic]
|
||||
|
||||
==== Description
|
||||
|
||||
Verifies basic RIP functionality by configuring two routers (R1 and R2)
|
||||
with RIP on their interconnecting link. The test ensures RIP routes are
|
||||
exchanged between the routers and end-to-end connectivity is achieved.
|
||||
|
||||
The test PC uses data1 interface to connect to R1's data port, and data2
|
||||
interface to connect to R2's data port (which does not have RIP enabled).
|
||||
This verifies that RIP status information remains accessible when a router
|
||||
has non-RIP interfaces.
|
||||
|
||||
==== Topology
|
||||
|
||||
image::topology.svg[RIP Basic topology, align=center, scaledwidth=75%]
|
||||
|
||||
==== Sequence
|
||||
|
||||
. Set up topology and attach to target DUTs
|
||||
. Configure targets
|
||||
. Wait for RIP routes to be exchanged
|
||||
. Test connectivity from PC:data1 to R2 loopback via RIP
|
||||
. Test connectivity from PC:data2 to R1 loopback via RIP
|
||||
|
||||
|
||||
Executable
+215
@@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env python3
|
||||
"""RIP Basic
|
||||
|
||||
Verifies basic RIP functionality by configuring two routers (R1 and R2)
|
||||
with RIP on their interconnecting link. The test ensures RIP routes are
|
||||
exchanged between the routers and end-to-end connectivity is achieved.
|
||||
|
||||
The test PC uses data1 interface to connect to R1's data port, and data2
|
||||
interface to connect to R2's data port (which does not have RIP enabled).
|
||||
This verifies that RIP status information remains accessible when a router
|
||||
has non-RIP interfaces.
|
||||
|
||||
"""
|
||||
|
||||
import infamy
|
||||
import infamy.route as route
|
||||
from infamy.util import until, parallel
|
||||
|
||||
|
||||
def config_target1(target, data, link):
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": data,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.10.1",
|
||||
"prefix-length": 24
|
||||
}]}
|
||||
}, {
|
||||
"name": link,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.50.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": "lo",
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "192.168.100.1",
|
||||
"prefix-length": 32
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"ietf-system": {
|
||||
"system": {
|
||||
"hostname": "R1"
|
||||
}
|
||||
},
|
||||
"ietf-routing": {
|
||||
"routing": {
|
||||
"control-plane-protocols": {
|
||||
"control-plane-protocol": [{
|
||||
"type": "infix-routing:static",
|
||||
"name": "default",
|
||||
"static-routes": {
|
||||
"ipv4": {
|
||||
"route": [{
|
||||
"destination-prefix": "192.168.33.1/32",
|
||||
"next-hop": {
|
||||
"special-next-hop": "blackhole"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"type": "infix-routing:ripv2",
|
||||
"name": "default",
|
||||
"rip": {
|
||||
"timers": {
|
||||
"update-interval": 5,
|
||||
"invalid-interval": 15,
|
||||
"flush-interval": 20
|
||||
},
|
||||
"redistribute": {
|
||||
"redistribute": [{
|
||||
"protocol": "static"
|
||||
}, {
|
||||
"protocol": "connected"
|
||||
}]
|
||||
},
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"interface": link
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def config_target2(target, link, data):
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": link,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.50.2",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": data,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.60.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": "lo",
|
||||
"enabled": True,
|
||||
"forwarding": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "192.168.200.1",
|
||||
"prefix-length": 32
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"ietf-system": {
|
||||
"system": {
|
||||
"hostname": "R2"
|
||||
}
|
||||
},
|
||||
"ietf-routing": {
|
||||
"routing": {
|
||||
"control-plane-protocols": {
|
||||
"control-plane-protocol": [{
|
||||
"type": "infix-routing:ripv2",
|
||||
"name": "default",
|
||||
"rip": {
|
||||
"timers": {
|
||||
"update-interval": 5,
|
||||
"invalid-interval": 15,
|
||||
"flush-interval": 20
|
||||
},
|
||||
"redistribute": {
|
||||
"redistribute": [{
|
||||
"protocol": "connected"
|
||||
}]
|
||||
},
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"interface": link
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Set up topology and attach to target DUTs"):
|
||||
env = infamy.Env()
|
||||
R1 = env.attach("R1", "mgmt")
|
||||
R2 = env.attach("R2", "mgmt")
|
||||
|
||||
with test.step("Configure targets"):
|
||||
_, R1data = env.ltop.xlate("R1", "data")
|
||||
_, R2link = env.ltop.xlate("R2", "link")
|
||||
_, R1link = env.ltop.xlate("R1", "link")
|
||||
_, R2data = env.ltop.xlate("R2", "data")
|
||||
|
||||
parallel(config_target1(R1, R1data, R1link),
|
||||
config_target2(R2, R2link, R2data))
|
||||
|
||||
with test.step("Wait for RIP routes to be exchanged"):
|
||||
print("Waiting for RIP routes to propagate...")
|
||||
# R1 should learn R2's loopback
|
||||
until(lambda: route.ipv4_route_exist(R1, "192.168.200.1/32", proto="ietf-rip:rip"), attempts=40)
|
||||
# R2 should learn R1's loopback
|
||||
until(lambda: route.ipv4_route_exist(R2, "192.168.100.1/32", proto="ietf-rip:rip"), attempts=40)
|
||||
# R2 should learn R1's static route (redistributed)
|
||||
until(lambda: route.ipv4_route_exist(R2, "192.168.33.1/32", proto="ietf-rip:rip"), attempts=40)
|
||||
|
||||
with test.step("Test connectivity from PC:data1 to R2 loopback via RIP"):
|
||||
_, hport0 = env.ltop.xlate("PC", "data1")
|
||||
with infamy.IsolatedMacVlan(hport0) as ns0:
|
||||
ns0.addip("192.168.10.2")
|
||||
ns0.addroute("192.168.200.1/32", "192.168.10.1")
|
||||
ns0.must_reach("192.168.200.1")
|
||||
|
||||
with test.step("Test connectivity from PC:data2 to R1 loopback via RIP"):
|
||||
_, hport1 = env.ltop.xlate("PC", "data2")
|
||||
with infamy.IsolatedMacVlan(hport1) as ns1:
|
||||
ns1.addip("192.168.60.2")
|
||||
ns1.addroute("192.168.100.1/32", "192.168.60.1")
|
||||
ns1.must_reach("192.168.100.1")
|
||||
|
||||
test.succeed()
|
||||
@@ -0,0 +1,34 @@
|
||||
graph "2x2" {
|
||||
layout="neato";
|
||||
overlap="false";
|
||||
esep="+40";
|
||||
size=10
|
||||
|
||||
node [shape=record, fontname="DejaVu Sans Mono, Book"];
|
||||
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
|
||||
|
||||
PC [
|
||||
label="PC | { <mgmt1> mgmt1 | <data1> data1 | \n\n\n\n\n\n\n\n | <mgmt2> mgmt2 | <data2> data2 }",
|
||||
pos="20,50!",
|
||||
requires="controller",
|
||||
];
|
||||
|
||||
R1 [
|
||||
label="{ <mgmt> mgmt | <data> data | <link> link} | R1 \n 192.168.100.1/32 \n(lo)",
|
||||
pos="190,85!",
|
||||
|
||||
requires="infix",
|
||||
];
|
||||
R2 [
|
||||
label="{ <link> link | <mgmt> mgmt | <data> data } | R2 \n 192.168.200.1/32 \n(lo)",
|
||||
pos="190,15!",
|
||||
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
PC:mgmt1 -- R1:mgmt [requires="mgmt", color="lightgray"]
|
||||
PC:mgmt2 -- R2:mgmt [requires="mgmt", color="lightgray"]
|
||||
PC:data1 -- R1:data [color="black", label="192.168.10.0/24", headlabel=".1", taillabel=".2", labeldistance=2, fontcolor="black"]
|
||||
R1:link -- R2:link [label="192.168.50.0/24", headlabel=".2", taillabel=".1", labeldistance=2, fontcolor="black", color="black"]
|
||||
PC:data2 -- R2:data [label="192.168.60.0/24", headlabel=".1", taillabel=".2", labeldistance=2, fontcolor="black", color="black"]
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Title: 2x2 Pages: 1 -->
|
||||
<svg width="574pt" height="249pt"
|
||||
viewBox="0.00 0.00 573.90 248.51" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 244.51)">
|
||||
<title>2x2</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-244.51 569.9,-244.51 569.9,4 -4,4"/>
|
||||
<!-- PC -->
|
||||
<g id="node1" class="node">
|
||||
<title>PC</title>
|
||||
<polygon fill="none" stroke="black" points="0,-6.5 0,-234.5 91,-234.5 91,-6.5 0,-6.5"/>
|
||||
<text text-anchor="middle" x="16.5" y="-116.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">PC</text>
|
||||
<polyline fill="none" stroke="black" points="33,-6.5 33,-234.5 "/>
|
||||
<text text-anchor="middle" x="62" y="-219.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt1</text>
|
||||
<polyline fill="none" stroke="black" points="33,-211.5 91,-211.5 "/>
|
||||
<text text-anchor="middle" x="62" y="-196.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
|
||||
<polyline fill="none" stroke="black" points="33,-188.5 91,-188.5 "/>
|
||||
<polyline fill="none" stroke="black" points="33,-52.5 91,-52.5 "/>
|
||||
<text text-anchor="middle" x="62" y="-37.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt2</text>
|
||||
<polyline fill="none" stroke="black" points="33,-29.5 91,-29.5 "/>
|
||||
<text text-anchor="middle" x="62" y="-14.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
|
||||
</g>
|
||||
<!-- R1 -->
|
||||
<g id="node2" class="node">
|
||||
<title>R1</title>
|
||||
<polygon fill="none" stroke="black" points="350.9,-171.01 350.9,-240.01 565.9,-240.01 565.9,-171.01 350.9,-171.01"/>
|
||||
<text text-anchor="middle" x="375.9" y="-224.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="350.9,-217.01 400.9,-217.01 "/>
|
||||
<text text-anchor="middle" x="375.9" y="-201.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
|
||||
<polyline fill="none" stroke="black" points="350.9,-194.01 400.9,-194.01 "/>
|
||||
<text text-anchor="middle" x="375.9" y="-178.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
|
||||
<polyline fill="none" stroke="black" points="400.9,-171.01 400.9,-240.01 "/>
|
||||
<text text-anchor="middle" x="483.4" y="-216.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">R1 </text>
|
||||
<text text-anchor="middle" x="483.4" y="-201.81" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.100.1/32 </text>
|
||||
<text text-anchor="middle" x="483.4" y="-186.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
|
||||
</g>
|
||||
<!-- PC--R1 -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>PC:mgmt1--R1:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-223.5C91.5,-223.5 350.4,-228.51 350.4,-228.51"/>
|
||||
</g>
|
||||
<!-- PC--R1 -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>PC:data1--R1:data</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M91.5,-200.5C91.5,-200.5 350.4,-205.51 350.4,-205.51"/>
|
||||
<text text-anchor="middle" x="161.95" y="-206.81" font-family="DejaVu Serif, Book" font-size="14.00">192.168.10.0/24</text>
|
||||
<text text-anchor="middle" x="332.11" y="-209.91" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
|
||||
<text text-anchor="middle" x="109.79" y="-188.7" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
|
||||
</g>
|
||||
<!-- R2 -->
|
||||
<g id="node3" class="node">
|
||||
<title>R2</title>
|
||||
<polygon fill="none" stroke="black" points="350.9,-1 350.9,-70 565.9,-70 565.9,-1 350.9,-1"/>
|
||||
<text text-anchor="middle" x="375.9" y="-54.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
|
||||
<polyline fill="none" stroke="black" points="350.9,-47 400.9,-47 "/>
|
||||
<text text-anchor="middle" x="375.9" y="-31.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="350.9,-24 400.9,-24 "/>
|
||||
<text text-anchor="middle" x="375.9" y="-8.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
|
||||
<polyline fill="none" stroke="black" points="400.9,-1 400.9,-70 "/>
|
||||
<text text-anchor="middle" x="483.4" y="-46.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">R2 </text>
|
||||
<text text-anchor="middle" x="483.4" y="-31.8" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.200.1/32 </text>
|
||||
<text text-anchor="middle" x="483.4" y="-16.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
|
||||
</g>
|
||||
<!-- PC--R2 -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>PC:mgmt2--R2:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-40.5C91.5,-40.5 350.4,-35.5 350.4,-35.5"/>
|
||||
</g>
|
||||
<!-- PC--R2 -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>PC:data2--R2:data</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M91.5,-17.5C91.5,-17.5 350.4,-12.5 350.4,-12.5"/>
|
||||
<text text-anchor="middle" x="250.45" y="-3.8" font-family="DejaVu Serif, Book" font-size="14.00">192.168.60.0/24</text>
|
||||
<text text-anchor="middle" x="332.44" y="-17.6" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
|
||||
<text text-anchor="middle" x="109.46" y="-5" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
|
||||
</g>
|
||||
<!-- R1--R2 -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>R1:link--R2:link</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M375.4,-170.51C375.4,-170.51 375.4,-70.5 375.4,-70.5"/>
|
||||
<text text-anchor="middle" x="316.4" y="-124.3" font-family="DejaVu Serif, Book" font-size="14.00">192.168.50.0/24</text>
|
||||
<text text-anchor="middle" x="383.85" y="-84.92" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
|
||||
<text text-anchor="middle" x="366.95" y="-148.69" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1 @@
|
||||
test.adoc
|
||||
Executable
+314
@@ -0,0 +1,314 @@
|
||||
#!/usr/bin/env python3
|
||||
"""RIP Multi-hop
|
||||
|
||||
Verifies RIP functionality across multiple hops with three routers in a line
|
||||
topology (R1 -- R2 -- R3). This test ensures:
|
||||
- RIP routes propagate through multiple hops
|
||||
- R2 (middle router) has two RIP neighbors
|
||||
- End-to-end connectivity works across the RIP network
|
||||
|
||||
Topology:
|
||||
PC:data1 -- R1 -- R2 -- R3 -- PC:data2
|
||||
"""
|
||||
|
||||
import infamy
|
||||
import infamy.route as route
|
||||
from infamy.util import until, parallel
|
||||
|
||||
|
||||
def config_r1(target, data, link):
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": data,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.10.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": link,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.50.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": "lo",
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "192.168.11.1",
|
||||
"prefix-length": 32
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"ietf-system": {
|
||||
"system": {
|
||||
"hostname": "R1"
|
||||
}
|
||||
},
|
||||
"ietf-routing": {
|
||||
"routing": {
|
||||
"control-plane-protocols": {
|
||||
"control-plane-protocol": [{
|
||||
"type": "infix-routing:ripv2",
|
||||
"name": "default",
|
||||
"rip": {
|
||||
"timers": {
|
||||
"update-interval": 5,
|
||||
"invalid-interval": 15,
|
||||
"flush-interval": 20
|
||||
},
|
||||
"redistribute": {
|
||||
"redistribute": [{
|
||||
"protocol": "connected"
|
||||
}]
|
||||
},
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"interface": link
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def config_r2(target, west, east):
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": west,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.50.2",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": east,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.60.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": "lo",
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "192.168.22.1",
|
||||
"prefix-length": 32
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"ietf-system": {
|
||||
"system": {
|
||||
"hostname": "R2"
|
||||
}
|
||||
},
|
||||
"ietf-routing": {
|
||||
"routing": {
|
||||
"control-plane-protocols": {
|
||||
"control-plane-protocol": [{
|
||||
"type": "infix-routing:ripv2",
|
||||
"name": "default",
|
||||
"rip": {
|
||||
"timers": {
|
||||
"update-interval": 5,
|
||||
"invalid-interval": 15,
|
||||
"flush-interval": 20
|
||||
},
|
||||
"redistribute": {
|
||||
"redistribute": [{
|
||||
"protocol": "connected"
|
||||
}]
|
||||
},
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"interface": west
|
||||
}, {
|
||||
"interface": east
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def config_r3(target, link, data):
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": link,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.60.2",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": data,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.70.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": "lo",
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "192.168.33.1",
|
||||
"prefix-length": 32
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"ietf-system": {
|
||||
"system": {
|
||||
"hostname": "R3"
|
||||
}
|
||||
},
|
||||
"ietf-routing": {
|
||||
"routing": {
|
||||
"control-plane-protocols": {
|
||||
"control-plane-protocol": [{
|
||||
"type": "infix-routing:ripv2",
|
||||
"name": "default",
|
||||
"rip": {
|
||||
"timers": {
|
||||
"update-interval": 5,
|
||||
"invalid-interval": 15,
|
||||
"flush-interval": 20
|
||||
},
|
||||
"redistribute": {
|
||||
"redistribute": [{
|
||||
"protocol": "connected"
|
||||
}]
|
||||
},
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"interface": link
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Set up topology and attach to target DUTs"):
|
||||
env = infamy.Env()
|
||||
R1 = env.attach("R1", "mgmt")
|
||||
R2 = env.attach("R2", "mgmt")
|
||||
R3 = env.attach("R3", "mgmt")
|
||||
|
||||
with test.step("Configure routers"):
|
||||
_, R1data = env.ltop.xlate("R1", "data")
|
||||
_, R1link = env.ltop.xlate("R1", "link")
|
||||
_, R2west = env.ltop.xlate("R2", "west")
|
||||
_, R2east = env.ltop.xlate("R2", "east")
|
||||
_, R3link = env.ltop.xlate("R3", "link")
|
||||
_, R3data = env.ltop.xlate("R3", "data")
|
||||
|
||||
parallel(config_r1(R1, R1data, R1link),
|
||||
config_r2(R2, R2west, R2east),
|
||||
config_r3(R3, R3link, R3data))
|
||||
|
||||
with test.step("Wait for RIP routes to be exchanged"):
|
||||
print("Waiting for RIP routes to propagate...")
|
||||
# R1 should learn R2's loopback
|
||||
until(lambda: route.ipv4_route_exist(R1, "192.168.22.1/32", proto="ietf-rip:rip"), attempts=40)
|
||||
# R1 should learn R3's loopback (via R2)
|
||||
until(lambda: route.ipv4_route_exist(R1, "192.168.33.1/32", proto="ietf-rip:rip"), attempts=40)
|
||||
# R2 should learn R1's loopback
|
||||
until(lambda: route.ipv4_route_exist(R2, "192.168.11.1/32", proto="ietf-rip:rip"), attempts=40)
|
||||
# R2 should learn R3's loopback
|
||||
until(lambda: route.ipv4_route_exist(R2, "192.168.33.1/32", proto="ietf-rip:rip"), attempts=40)
|
||||
# R3 should learn R2's loopback
|
||||
until(lambda: route.ipv4_route_exist(R3, "192.168.22.1/32", proto="ietf-rip:rip"), attempts=40)
|
||||
# R3 should learn R1's loopback (via R2)
|
||||
until(lambda: route.ipv4_route_exist(R3, "192.168.11.1/32", proto="ietf-rip:rip"), attempts=40)
|
||||
|
||||
with test.step("Verify R2 has two RIP neighbors"):
|
||||
print("Checking R2 has two RIP neighbors...")
|
||||
# R2 should have neighbors: 192.168.50.1 (R1) and 192.168.60.2 (R3)
|
||||
# Query without predicates to avoid RESTCONF encoding issues
|
||||
routing_data = R2.get_data("/ietf-routing:routing/control-plane-protocols")
|
||||
|
||||
# Navigate to RIP protocol
|
||||
protocols = routing_data.get("routing", {}).get("control-plane-protocols", {}).get("control-plane-protocol", [])
|
||||
if not protocols:
|
||||
raise Exception("No protocols found")
|
||||
|
||||
# Find RIP protocol
|
||||
rip = None
|
||||
for protocol in protocols:
|
||||
if protocol.get("type") == "infix-routing:ripv2" and protocol.get("name") == "default":
|
||||
rip = protocol.get("rip", {})
|
||||
break
|
||||
|
||||
if not rip:
|
||||
raise Exception("RIP protocol not found in control-plane-protocols")
|
||||
|
||||
ipv4_data = rip.get("ipv4", {})
|
||||
neighbors_data = ipv4_data.get("neighbors", {})
|
||||
neighbor_list = neighbors_data.get("neighbor", [])
|
||||
|
||||
assert len(neighbor_list) == 2, f"Expected 2 neighbors, found {len(neighbor_list)}"
|
||||
|
||||
neighbor_ips = [n.get("ipv4-address") for n in neighbor_list]
|
||||
assert "192.168.50.1" in neighbor_ips, "R1 not in neighbor list"
|
||||
assert "192.168.60.2" in neighbor_ips, "R3 not in neighbor list"
|
||||
print(f"R2 has correct neighbors: {neighbor_ips}")
|
||||
|
||||
with test.step("Test end-to-end connectivity PC:data1 to R3 loopback"):
|
||||
_, hport1 = env.ltop.xlate("PC", "data1")
|
||||
with infamy.IsolatedMacVlan(hport1) as ns1:
|
||||
ns1.addip("192.168.10.2")
|
||||
ns1.addroute("192.168.33.1/32", "192.168.10.1")
|
||||
ns1.must_reach("192.168.33.1")
|
||||
|
||||
with test.step("Test end-to-end connectivity PC:data2 to R1 loopback"):
|
||||
_, hport2 = env.ltop.xlate("PC", "data2")
|
||||
with infamy.IsolatedMacVlan(hport2) as ns2:
|
||||
ns2.addip("192.168.70.2")
|
||||
ns2.addroute("192.168.11.1/32", "192.168.70.1")
|
||||
ns2.must_reach("192.168.11.1")
|
||||
|
||||
test.succeed()
|
||||
@@ -0,0 +1,41 @@
|
||||
graph "rip-multihop" {
|
||||
layout="neato";
|
||||
overlap="false";
|
||||
esep="+40";
|
||||
size=10
|
||||
|
||||
node [shape=record, fontname="DejaVu Sans Mono, Book"];
|
||||
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
|
||||
|
||||
PC [
|
||||
label="PC | { <mgmt1> mgmt1 | <data1> data1 | \n\n\n\n | <mgmt2> mgmt2 | <mgmt3> mgmt3 | <data2> data2 }",
|
||||
pos="20,54!",
|
||||
requires="controller",
|
||||
];
|
||||
|
||||
R1 [
|
||||
label="{ <mgmt> mgmt | <data> data | <link> link} | R1 \n 192.168.11.1/32 \n(lo)",
|
||||
pos="100,70!",
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
R2 [
|
||||
label="{ <west> west | <mgmt> mgmt | <east> east } | R2 \n 192.168.22.1/32 \n(lo)",
|
||||
pos="100,50!",
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
R3 [
|
||||
label="{ <link> link | <mgmt> mgmt | <data> data } | R3 \n 192.168.33.1/32 \n(lo)",
|
||||
pos="100,30!",
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
PC:mgmt1 -- R1:mgmt [requires="mgmt", color="lightgray"]
|
||||
PC:mgmt2 -- R2:mgmt [requires="mgmt", color="lightgray"]
|
||||
PC:mgmt3 -- R3:mgmt [requires="mgmt", color="lightgray"]
|
||||
PC:data1 -- R1:data [label="192.168.10.0/24", headlabel=".1", taillabel=".2", labeldistance=2, fontcolor="black", color="black"]
|
||||
R1:link -- R2:west [label="192.168.50.0/24", headlabel=".2", taillabel=".1", labeldistance=2, fontcolor="black", color="black"]
|
||||
R2:east -- R3:link [label="192.168.60.0/24", headlabel=".2", taillabel=".1", labeldistance=2, fontcolor="black", color="black"]
|
||||
R3:data -- PC:data2 [label="192.168.70.0/24", headlabel=".2", taillabel=".1", labeldistance=2, fontcolor="black", color="black"]
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Title: rip-multihop Pages: 1 -->
|
||||
<svg width="720pt" height="360pt"
|
||||
viewBox="0.00 0.00 720.00 359.57" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(0.86 0.86) rotate(0) translate(4 414.03)">
|
||||
<title>rip-multihop</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-414.03 833.07,-414.03 833.07,4 -4,4"/>
|
||||
<!-- PC -->
|
||||
<g id="node1" class="node">
|
||||
<title>PC</title>
|
||||
<polygon fill="none" stroke="black" points="0,-145.52 0,-332.52 91,-332.52 91,-145.52 0,-145.52"/>
|
||||
<text text-anchor="middle" x="16.5" y="-235.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">PC</text>
|
||||
<polyline fill="none" stroke="black" points="33,-145.52 33,-332.52 "/>
|
||||
<text text-anchor="middle" x="62" y="-317.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt1</text>
|
||||
<polyline fill="none" stroke="black" points="33,-309.52 91,-309.52 "/>
|
||||
<text text-anchor="middle" x="62" y="-294.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
|
||||
<polyline fill="none" stroke="black" points="33,-286.52 91,-286.52 "/>
|
||||
<polyline fill="none" stroke="black" points="33,-214.52 91,-214.52 "/>
|
||||
<text text-anchor="middle" x="62" y="-199.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt2</text>
|
||||
<polyline fill="none" stroke="black" points="33,-191.52 91,-191.52 "/>
|
||||
<text text-anchor="middle" x="62" y="-176.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt3</text>
|
||||
<polyline fill="none" stroke="black" points="33,-168.52 91,-168.52 "/>
|
||||
<text text-anchor="middle" x="62" y="-153.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
|
||||
</g>
|
||||
<!-- R1 -->
|
||||
<g id="node2" class="node">
|
||||
<title>R1</title>
|
||||
<polygon fill="none" stroke="black" points="622.07,-340.53 622.07,-409.53 829.07,-409.53 829.07,-340.53 622.07,-340.53"/>
|
||||
<text text-anchor="middle" x="647.07" y="-394.33" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="622.07,-386.53 672.07,-386.53 "/>
|
||||
<text text-anchor="middle" x="647.07" y="-371.33" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
|
||||
<polyline fill="none" stroke="black" points="622.07,-363.53 672.07,-363.53 "/>
|
||||
<text text-anchor="middle" x="647.07" y="-348.33" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
|
||||
<polyline fill="none" stroke="black" points="672.07,-340.53 672.07,-409.53 "/>
|
||||
<text text-anchor="middle" x="750.57" y="-386.33" font-family="DejaVu Sans Mono, Book" font-size="14.00">R1 </text>
|
||||
<text text-anchor="middle" x="750.57" y="-371.33" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.11.1/32 </text>
|
||||
<text text-anchor="middle" x="750.57" y="-356.33" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
|
||||
</g>
|
||||
<!-- PC--R1 -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>PC:mgmt1--R1:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-321.02C91.5,-321.02 621.57,-398.03 621.57,-398.03"/>
|
||||
</g>
|
||||
<!-- PC--R1 -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>PC:data1--R1:data</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M91.5,-298.02C91.5,-298.02 621.57,-375.03 621.57,-375.03"/>
|
||||
<text text-anchor="middle" x="297.53" y="-340.33" font-family="DejaVu Serif, Book" font-size="14.00">192.168.10.0/24</text>
|
||||
<text text-anchor="middle" x="602.41" y="-377.09" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
|
||||
<text text-anchor="middle" x="110.65" y="-288.56" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
|
||||
</g>
|
||||
<!-- R2 -->
|
||||
<g id="node3" class="node">
|
||||
<title>R2</title>
|
||||
<polygon fill="none" stroke="black" points="622.07,-170.52 622.07,-239.52 829.07,-239.52 829.07,-170.52 622.07,-170.52"/>
|
||||
<text text-anchor="middle" x="647.07" y="-224.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">west</text>
|
||||
<polyline fill="none" stroke="black" points="622.07,-216.52 672.07,-216.52 "/>
|
||||
<text text-anchor="middle" x="647.07" y="-201.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="622.07,-193.52 672.07,-193.52 "/>
|
||||
<text text-anchor="middle" x="647.07" y="-178.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">east</text>
|
||||
<polyline fill="none" stroke="black" points="672.07,-170.52 672.07,-239.52 "/>
|
||||
<text text-anchor="middle" x="750.57" y="-216.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">R2 </text>
|
||||
<text text-anchor="middle" x="750.57" y="-201.32" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.22.1/32 </text>
|
||||
<text text-anchor="middle" x="750.57" y="-186.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
|
||||
</g>
|
||||
<!-- PC--R2 -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>PC:mgmt2--R2:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-203.02C91.5,-203.02 621.57,-205.02 621.57,-205.02"/>
|
||||
</g>
|
||||
<!-- R3 -->
|
||||
<g id="node4" class="node">
|
||||
<title>R3</title>
|
||||
<polygon fill="none" stroke="black" points="622.07,-0.5 622.07,-69.5 829.07,-69.5 829.07,-0.5 622.07,-0.5"/>
|
||||
<text text-anchor="middle" x="647.07" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
|
||||
<polyline fill="none" stroke="black" points="622.07,-46.5 672.07,-46.5 "/>
|
||||
<text text-anchor="middle" x="647.07" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="622.07,-23.5 672.07,-23.5 "/>
|
||||
<text text-anchor="middle" x="647.07" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
|
||||
<polyline fill="none" stroke="black" points="672.07,-0.5 672.07,-69.5 "/>
|
||||
<text text-anchor="middle" x="750.57" y="-46.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">R3 </text>
|
||||
<text text-anchor="middle" x="750.57" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.33.1/32 </text>
|
||||
<text text-anchor="middle" x="750.57" y="-16.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
|
||||
</g>
|
||||
<!-- PC--R3 -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>PC:mgmt3--R3:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-180.02C91.5,-180.02 621.57,-35 621.57,-35"/>
|
||||
</g>
|
||||
<!-- R1--R2 -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>R1:link--R2:west</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M646.57,-340.03C646.57,-340.03 646.57,-240.02 646.57,-240.02"/>
|
||||
<text text-anchor="middle" x="587.57" y="-293.83" font-family="DejaVu Serif, Book" font-size="14.00">192.168.50.0/24</text>
|
||||
<text text-anchor="middle" x="655.02" y="-254.44" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
|
||||
<text text-anchor="middle" x="638.12" y="-318.21" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
|
||||
</g>
|
||||
<!-- R2--R3 -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>R2:east--R3:link</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M646.57,-170.02C646.57,-170.02 646.57,-70 646.57,-70"/>
|
||||
<text text-anchor="middle" x="587.57" y="-123.81" font-family="DejaVu Serif, Book" font-size="14.00">192.168.60.0/24</text>
|
||||
<text text-anchor="middle" x="655.02" y="-84.43" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
|
||||
<text text-anchor="middle" x="638.12" y="-148.19" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
|
||||
</g>
|
||||
<!-- R3--PC -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>R3:data--PC:data2</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M621.57,-12C621.57,-12 91.5,-157.02 91.5,-157.02"/>
|
||||
<text text-anchor="middle" x="297.53" y="-88.31" font-family="DejaVu Serif, Book" font-size="14.00">192.168.70.0/24</text>
|
||||
<text text-anchor="middle" x="106.75" y="-140.38" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
|
||||
<text text-anchor="middle" x="606.31" y="-21.24" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.9 KiB |
@@ -0,0 +1 @@
|
||||
test.adoc
|
||||
@@ -0,0 +1,29 @@
|
||||
=== RIP Passive Interface
|
||||
|
||||
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/routing/rip_passive_interface]
|
||||
|
||||
==== Description
|
||||
|
||||
Verifies RIP passive interface functionality. A passive interface means that
|
||||
RIP will include the interface's network in routing updates but will not send
|
||||
or receive RIP updates on that interface.
|
||||
|
||||
R1 has two RIP-enabled interfaces:
|
||||
- data: Passive interface (192.168.10.0/24 advertised but no updates sent/received)
|
||||
- link: Active interface (RIP updates exchanged with R2)
|
||||
|
||||
R2 should learn about 192.168.10.0/24 from R1 via the link interface, even
|
||||
though the data interface is passive.
|
||||
|
||||
==== Topology
|
||||
|
||||
image::topology.svg[RIP Passive Interface topology, align=center, scaledwidth=75%]
|
||||
|
||||
==== Sequence
|
||||
|
||||
. Set up topology and attach to target DUTs
|
||||
. Configure targets
|
||||
. Wait for RIP to exchange routes
|
||||
. Verify connectivity to passive interface network
|
||||
|
||||
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
"""RIP Passive Interface
|
||||
|
||||
Verifies RIP passive interface functionality. A passive interface means that
|
||||
RIP will include the interface's network in routing updates but will not send
|
||||
or receive RIP updates on that interface.
|
||||
|
||||
R1 has two RIP-enabled interfaces:
|
||||
- data: Passive interface (192.168.10.0/24 advertised but no updates sent/received)
|
||||
- link: Active interface (RIP updates exchanged with R2)
|
||||
|
||||
R2 should learn about 192.168.10.0/24 from R1 via the link interface, even
|
||||
though the data interface is passive.
|
||||
|
||||
"""
|
||||
|
||||
import infamy
|
||||
import infamy.route as route
|
||||
from infamy.util import until, parallel
|
||||
|
||||
|
||||
def config_target1(target, data, link):
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": data,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.10.1",
|
||||
"prefix-length": 24
|
||||
}]}
|
||||
}, {
|
||||
"name": link,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.50.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"ietf-system": {
|
||||
"system": {
|
||||
"hostname": "R1"
|
||||
}
|
||||
},
|
||||
"ietf-routing": {
|
||||
"routing": {
|
||||
"control-plane-protocols": {
|
||||
"control-plane-protocol": [{
|
||||
"type": "infix-routing:ripv2",
|
||||
"name": "default",
|
||||
"rip": {
|
||||
"timers": {
|
||||
"update-interval": 5,
|
||||
"invalid-interval": 15,
|
||||
"flush-interval": 20
|
||||
},
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"interface": data,
|
||||
"passive": None
|
||||
}, {
|
||||
"interface": link
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def config_target2(target, link):
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": link,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.50.2",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"ietf-system": {
|
||||
"system": {
|
||||
"hostname": "R2"
|
||||
}
|
||||
},
|
||||
"ietf-routing": {
|
||||
"routing": {
|
||||
"control-plane-protocols": {
|
||||
"control-plane-protocol": [{
|
||||
"type": "infix-routing:ripv2",
|
||||
"name": "default",
|
||||
"rip": {
|
||||
"timers": {
|
||||
"update-interval": 5,
|
||||
"invalid-interval": 15,
|
||||
"flush-interval": 20
|
||||
},
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"interface": link
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Set up topology and attach to target DUTs"):
|
||||
env = infamy.Env()
|
||||
R1 = env.attach("R1", "mgmt")
|
||||
R2 = env.attach("R2", "mgmt")
|
||||
|
||||
with test.step("Configure targets"):
|
||||
_, R1data = env.ltop.xlate("R1", "data")
|
||||
_, R2link = env.ltop.xlate("R2", "link")
|
||||
_, R1link = env.ltop.xlate("R1", "link")
|
||||
|
||||
parallel(config_target1(R1, R1data, R1link),
|
||||
config_target2(R2, R2link))
|
||||
|
||||
with test.step("Wait for RIP to exchange routes"):
|
||||
print("Waiting for RIP routes to propagate...")
|
||||
# R2 should learn about R1's passive interface network (192.168.10.0/24)
|
||||
# even though it's passive, R1 should still advertise it
|
||||
until(lambda: route.ipv4_route_exist(R2, "192.168.10.0/24", proto="ietf-rip:rip"), attempts=40)
|
||||
|
||||
with test.step("Verify connectivity to passive interface network"):
|
||||
# Test that we can reach the passive interface from PC
|
||||
_, hport0 = env.ltop.xlate("PC", "data")
|
||||
with infamy.IsolatedMacVlan(hport0) as ns0:
|
||||
ns0.addip("192.168.10.2")
|
||||
# No need for route since we're on the same network
|
||||
ns0.must_reach("192.168.10.1")
|
||||
|
||||
test.succeed()
|
||||
@@ -0,0 +1,33 @@
|
||||
graph "rip-passive" {
|
||||
layout="neato";
|
||||
overlap="false";
|
||||
esep="+30";
|
||||
size=10
|
||||
|
||||
node [shape=record, fontname="DejaVu Sans Mono, Book"];
|
||||
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
|
||||
|
||||
PC [
|
||||
label="PC | { <mgmt1> mgmt1 | <data> data | <mgmt2> mgmt2 }",
|
||||
pos="20,50!",
|
||||
requires="controller",
|
||||
];
|
||||
|
||||
R1 [
|
||||
label="{ <mgmt> mgmt | <data> data | <link> link} | R1",
|
||||
pos="160,60!",
|
||||
|
||||
requires="infix",
|
||||
];
|
||||
R2 [
|
||||
label="{ <link> link | <mgmt> mgmt } | R2",
|
||||
pos="160,30!",
|
||||
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
PC:mgmt1 -- R1:mgmt [requires="mgmt", color="lightgray"]
|
||||
PC:mgmt2 -- R2:mgmt [requires="mgmt", color="lightgray"]
|
||||
PC:data -- R1:data [color="black", label="192.168.10.0/24", headlabel=".1", taillabel=".2", labeldistance=2, fontcolor="black"]
|
||||
R1:link -- R2:link [taillabel="192.168.50.0/24", headlabel=".2", taillabel=".1", labeldistance=2, fontcolor="black", color="black"]
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Title: rip-passive Pages: 1 -->
|
||||
<svg width="718pt" height="200pt"
|
||||
viewBox="0.00 0.00 718.06 200.01" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 196.01)">
|
||||
<title>rip-passive</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-196.01 714.06,-196.01 714.06,4 -4,4"/>
|
||||
<!-- PC -->
|
||||
<g id="node1" class="node">
|
||||
<title>PC</title>
|
||||
<polygon fill="none" stroke="black" points="0,-78.01 0,-147.01 91,-147.01 91,-78.01 0,-78.01"/>
|
||||
<text text-anchor="middle" x="16.5" y="-108.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">PC</text>
|
||||
<polyline fill="none" stroke="black" points="33,-78.01 33,-147.01 "/>
|
||||
<text text-anchor="middle" x="62" y="-131.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt1</text>
|
||||
<polyline fill="none" stroke="black" points="33,-124.01 91,-124.01 "/>
|
||||
<text text-anchor="middle" x="62" y="-108.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
|
||||
<polyline fill="none" stroke="black" points="33,-101.01 91,-101.01 "/>
|
||||
<text text-anchor="middle" x="62" y="-85.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt2</text>
|
||||
</g>
|
||||
<!-- R1 -->
|
||||
<g id="node2" class="node">
|
||||
<title>R1</title>
|
||||
<polygon fill="none" stroke="black" points="627.06,-122.51 627.06,-191.51 710.06,-191.51 710.06,-122.51 627.06,-122.51"/>
|
||||
<text text-anchor="middle" x="652.06" y="-176.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="627.06,-168.51 677.06,-168.51 "/>
|
||||
<text text-anchor="middle" x="652.06" y="-153.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
|
||||
<polyline fill="none" stroke="black" points="627.06,-145.51 677.06,-145.51 "/>
|
||||
<text text-anchor="middle" x="652.06" y="-130.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
|
||||
<polyline fill="none" stroke="black" points="677.06,-122.51 677.06,-191.51 "/>
|
||||
<text text-anchor="middle" x="693.56" y="-153.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">R1</text>
|
||||
</g>
|
||||
<!-- PC--R1 -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>PC:mgmt1--R1:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-135.51C91.5,-135.51 626.56,-180.01 626.56,-180.01"/>
|
||||
</g>
|
||||
<!-- PC--R1 -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>PC:data--R1:data</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M91.5,-112.51C91.5,-112.51 626.56,-157.01 626.56,-157.01"/>
|
||||
<text text-anchor="middle" x="300.03" y="-138.56" font-family="DejaVu Serif, Book" font-size="14.00">192.168.10.0/24</text>
|
||||
<text text-anchor="middle" x="607.8" y="-160.23" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
|
||||
<text text-anchor="middle" x="110.26" y="-101.89" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
|
||||
</g>
|
||||
<!-- R2 -->
|
||||
<g id="node3" class="node">
|
||||
<title>R2</title>
|
||||
<polygon fill="none" stroke="black" points="627.06,-0.5 627.06,-46.5 710.06,-46.5 710.06,-0.5 627.06,-0.5"/>
|
||||
<text text-anchor="middle" x="652.06" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
|
||||
<polyline fill="none" stroke="black" points="627.06,-23.5 677.06,-23.5 "/>
|
||||
<text text-anchor="middle" x="652.06" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="677.06,-0.5 677.06,-46.5 "/>
|
||||
<text text-anchor="middle" x="693.56" y="-19.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">R2</text>
|
||||
</g>
|
||||
<!-- PC--R2 -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>PC:mgmt2--R2:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-89.51C91.5,-89.51 626.56,-11.5 626.56,-11.5"/>
|
||||
</g>
|
||||
<!-- R1--R2 -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>R1:link--R2:link</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M651.56,-122.01C651.56,-122.01 651.56,-46.5 651.56,-46.5"/>
|
||||
<text text-anchor="middle" x="660.01" y="-60.93" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
|
||||
<text text-anchor="middle" x="643.11" y="-100.19" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1 @@
|
||||
test.adoc
|
||||
@@ -0,0 +1,37 @@
|
||||
=== RIP Redistribution
|
||||
|
||||
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/routing/rip_redistribute]
|
||||
|
||||
==== Description
|
||||
|
||||
Verifies that RIP can redistribute routes from other protocols.
|
||||
|
||||
Topology:
|
||||
- R1: Gateway router running both RIP and OSPF
|
||||
- RIP interface to R2
|
||||
- OSPF interface to R3
|
||||
- Redistributes OSPF routes into RIP
|
||||
- Redistributes RIP routes into OSPF
|
||||
|
||||
- R2: RIP-only router with loopback 192.168.200.1/32
|
||||
|
||||
- R3: OSPF-only router with loopback 192.168.100.1/32
|
||||
|
||||
Expected behavior:
|
||||
- R2 (RIP) should learn R3's OSPF loopback (192.168.100.1/32) via RIP redistribution
|
||||
- R3 (OSPF) should learn R2's RIP loopback (192.168.200.1/32) via OSPF redistribution
|
||||
|
||||
==== Topology
|
||||
|
||||
image::topology.svg[RIP Redistribution topology, align=center, scaledwidth=75%]
|
||||
|
||||
==== Sequence
|
||||
|
||||
. Set up topology and attach to target DUTs
|
||||
. Configure routers
|
||||
. Wait for OSPF to converge on R1-R3 link
|
||||
. Wait for RIP to converge on R1-R2 link
|
||||
. Verify R2 (RIP) learns R3's OSPF routes via redistribution
|
||||
. Verify R3 (OSPF) learns R2's RIP routes via redistribution
|
||||
|
||||
|
||||
Executable
+286
@@ -0,0 +1,286 @@
|
||||
#!/usr/bin/env python3
|
||||
"""RIP Redistribution
|
||||
|
||||
Verifies that RIP can redistribute routes from other protocols.
|
||||
|
||||
Topology:
|
||||
- R1: Gateway router running both RIP and OSPF
|
||||
- RIP interface to R2
|
||||
- OSPF interface to R3
|
||||
- Redistributes OSPF routes into RIP
|
||||
- Redistributes RIP routes into OSPF
|
||||
|
||||
- R2: RIP-only router with loopback 192.168.200.1/32
|
||||
|
||||
- R3: OSPF-only router with loopback 192.168.100.1/32
|
||||
|
||||
Expected behavior:
|
||||
- R2 (RIP) should learn R3's OSPF loopback (192.168.100.1/32) via RIP redistribution
|
||||
- R3 (OSPF) should learn R2's RIP loopback (192.168.200.1/32) via OSPF redistribution
|
||||
|
||||
"""
|
||||
|
||||
import infamy
|
||||
import infamy.route as route
|
||||
from infamy.util import until, parallel
|
||||
|
||||
|
||||
def config_r1_gateway(target, rip_link, ospf_link):
|
||||
"""Configure R1 as gateway running both RIP and OSPF"""
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": rip_link,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.50.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": ospf_link,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.60.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"ietf-system": {
|
||||
"system": {
|
||||
"hostname": "R1"
|
||||
}
|
||||
},
|
||||
"ietf-routing": {
|
||||
"routing": {
|
||||
"control-plane-protocols": {
|
||||
"control-plane-protocol": [{
|
||||
"type": "infix-routing:ripv2",
|
||||
"name": "default",
|
||||
"rip": {
|
||||
"timers": {
|
||||
"update-interval": 5,
|
||||
"invalid-interval": 15,
|
||||
"flush-interval": 20
|
||||
},
|
||||
"redistribute": {
|
||||
"redistribute": [{
|
||||
"protocol": "ospf"
|
||||
}, {
|
||||
"protocol": "connected"
|
||||
}]
|
||||
},
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"interface": rip_link
|
||||
}]
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"type": "infix-routing:ospfv2",
|
||||
"name": "default",
|
||||
"ospf": {
|
||||
"redistribute": {
|
||||
"redistribute": [{
|
||||
"protocol": "rip"
|
||||
}, {
|
||||
"protocol": "connected"
|
||||
}]
|
||||
},
|
||||
"areas": {
|
||||
"area": [{
|
||||
"area-id": "0.0.0.0",
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"enabled": True,
|
||||
"name": ospf_link,
|
||||
"hello-interval": 1,
|
||||
"dead-interval": 3
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def config_r2_rip(target, link):
|
||||
"""Configure R2 with RIP only"""
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": link,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.50.2",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": "lo",
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "192.168.200.1",
|
||||
"prefix-length": 32
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"ietf-system": {
|
||||
"system": {
|
||||
"hostname": "R2"
|
||||
}
|
||||
},
|
||||
"ietf-routing": {
|
||||
"routing": {
|
||||
"control-plane-protocols": {
|
||||
"control-plane-protocol": [{
|
||||
"type": "infix-routing:ripv2",
|
||||
"name": "default",
|
||||
"rip": {
|
||||
"timers": {
|
||||
"update-interval": 5,
|
||||
"invalid-interval": 15,
|
||||
"flush-interval": 20
|
||||
},
|
||||
"redistribute": {
|
||||
"redistribute": [{
|
||||
"protocol": "connected"
|
||||
}]
|
||||
},
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"interface": link
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def config_r3_ospf(target, link):
|
||||
"""Configure R3 with OSPF only"""
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": link,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": "192.168.60.2",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": "lo",
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "192.168.100.1",
|
||||
"prefix-length": 32
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"ietf-system": {
|
||||
"system": {
|
||||
"hostname": "R3"
|
||||
}
|
||||
},
|
||||
"ietf-routing": {
|
||||
"routing": {
|
||||
"control-plane-protocols": {
|
||||
"control-plane-protocol": [{
|
||||
"type": "infix-routing:ospfv2",
|
||||
"name": "default",
|
||||
"ospf": {
|
||||
"redistribute": {
|
||||
"redistribute": [{
|
||||
"protocol": "connected"
|
||||
}]
|
||||
},
|
||||
"areas": {
|
||||
"area": [{
|
||||
"area-id": "0.0.0.0",
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"enabled": True,
|
||||
"name": link,
|
||||
"hello-interval": 1,
|
||||
"dead-interval": 3
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Set up topology and attach to target DUTs"):
|
||||
env = infamy.Env()
|
||||
R1 = env.attach("R1", "mgmt")
|
||||
R2 = env.attach("R2", "mgmt")
|
||||
R3 = env.attach("R3", "mgmt")
|
||||
|
||||
with test.step("Configure routers"):
|
||||
_, R1rip = env.ltop.xlate("R1", "rip")
|
||||
_, R1ospf = env.ltop.xlate("R1", "ospf")
|
||||
_, R2link = env.ltop.xlate("R2", "link")
|
||||
_, R3link = env.ltop.xlate("R3", "link")
|
||||
|
||||
parallel(config_r1_gateway(R1, R1rip, R1ospf),
|
||||
config_r2_rip(R2, R2link),
|
||||
config_r3_ospf(R3, R3link))
|
||||
|
||||
with test.step("Wait for OSPF to converge on R1-R3 link"):
|
||||
print("Waiting for OSPF convergence...")
|
||||
# R1 should learn R3's loopback via OSPF
|
||||
until(lambda: route.ipv4_route_exist(R1, "192.168.100.1/32", proto="ietf-ospf:ospfv2"), attempts=40)
|
||||
# R3 should learn R1's OSPF link via OSPF
|
||||
until(lambda: route.ipv4_route_exist(R3, "192.168.60.0/24", proto="ietf-ospf:ospfv2"), attempts=40)
|
||||
|
||||
with test.step("Wait for RIP to converge on R1-R2 link"):
|
||||
print("Waiting for RIP convergence...")
|
||||
# R1 should learn R2's loopback via RIP
|
||||
until(lambda: route.ipv4_route_exist(R1, "192.168.200.1/32", proto="ietf-rip:rip"), attempts=40)
|
||||
# R2 should learn R1's OSPF link (192.168.60.0/24) via RIP (redistributed from connected)
|
||||
until(lambda: route.ipv4_route_exist(R2, "192.168.60.0/24", proto="ietf-rip:rip"), attempts=40)
|
||||
|
||||
with test.step("Verify R2 (RIP) learns R3's OSPF routes via redistribution"):
|
||||
print("Checking OSPF->RIP redistribution...")
|
||||
# R2 should learn R3's loopback (OSPF route) via RIP redistribution on R1
|
||||
until(lambda: route.ipv4_route_exist(R2, "192.168.100.1/32", proto="ietf-rip:rip"), attempts=40)
|
||||
|
||||
with test.step("Verify R3 (OSPF) learns R2's RIP routes via redistribution"):
|
||||
print("Checking RIP->OSPF redistribution...")
|
||||
# R3 should learn R2's loopback (RIP route) via OSPF redistribution on R1
|
||||
until(lambda: route.ipv4_route_exist(R3, "192.168.200.1/32", proto="ietf-ospf:ospfv2"), attempts=40)
|
||||
|
||||
test.succeed()
|
||||
@@ -0,0 +1,39 @@
|
||||
graph "rip-redistribute" {
|
||||
layout="neato";
|
||||
overlap="false";
|
||||
esep="+40";
|
||||
size=10
|
||||
|
||||
node [shape=record, fontname="DejaVu Sans Mono, Book"];
|
||||
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
|
||||
|
||||
PC [
|
||||
label="PC | { <mgmt3> mgmt3 | \n\n | <mgmt1> mgmt1 | \n\n | <mgmt2> mgmt2 }",
|
||||
pos="20,50!",
|
||||
requires="controller",
|
||||
];
|
||||
|
||||
R3 [
|
||||
label="{ <mgmt> mgmt | <link> link } | R3 (OSPF) \n 192.168.100.1/32 \n(lo)",
|
||||
pos="100,100!",
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
R1 [
|
||||
label="{ <ospf> ospf | <mgmt> mgmt | <rip> rip } | R1 (ASBR)",
|
||||
pos="91,50!",
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
R2 [
|
||||
label="{ <link> link | <mgmt> mgmt } | R2 (RIP) \n 192.168.200.1/32 \n(lo)",
|
||||
pos="100,10!",
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
PC:mgmt3 -- R3:mgmt [requires="mgmt", color="lightgray"]
|
||||
PC:mgmt1 -- R1:mgmt [requires="mgmt", color="lightgray"]
|
||||
PC:mgmt2 -- R2:mgmt [requires="mgmt", color="lightgray"]
|
||||
R3:link -- R1:ospf [label="192.168.60.0/24", headlabel=".1", taillabel=".2", labeldistance=2, fontcolor="black", color="blue"]
|
||||
R1:rip -- R2:link [label="192.168.50.0/24", headlabel=".2", taillabel=".1", labeldistance=2, fontcolor="black", color="black"]
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Title: rip-redistribute Pages: 1 -->
|
||||
<svg width="485pt" height="427pt"
|
||||
viewBox="0.00 0.00 485.03 426.54" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 422.54)">
|
||||
<title>rip-redistribute</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-422.54 481.03,-422.54 481.03,4 -4,4"/>
|
||||
<!-- PC -->
|
||||
<g id="node1" class="node">
|
||||
<title>PC</title>
|
||||
<polygon fill="none" stroke="black" points="0,-114.52 0,-263.52 91,-263.52 91,-114.52 0,-114.52"/>
|
||||
<text text-anchor="middle" x="16.5" y="-185.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">PC</text>
|
||||
<polyline fill="none" stroke="black" points="33,-114.52 33,-263.52 "/>
|
||||
<text text-anchor="middle" x="62" y="-248.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt3</text>
|
||||
<polyline fill="none" stroke="black" points="33,-240.52 91,-240.52 "/>
|
||||
<polyline fill="none" stroke="black" points="33,-200.52 91,-200.52 "/>
|
||||
<text text-anchor="middle" x="62" y="-185.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt1</text>
|
||||
<polyline fill="none" stroke="black" points="33,-177.52 91,-177.52 "/>
|
||||
<polyline fill="none" stroke="black" points="33,-137.52 91,-137.52 "/>
|
||||
<text text-anchor="middle" x="62" y="-122.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt2</text>
|
||||
</g>
|
||||
<!-- R3 -->
|
||||
<g id="node2" class="node">
|
||||
<title>R3</title>
|
||||
<polygon fill="none" stroke="black" points="262.03,-365.04 262.03,-418.04 477.03,-418.04 477.03,-365.04 262.03,-365.04"/>
|
||||
<text text-anchor="middle" x="287.03" y="-401.34" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="262.03,-392.04 312.03,-392.04 "/>
|
||||
<text text-anchor="middle" x="287.03" y="-374.84" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
|
||||
<polyline fill="none" stroke="black" points="312.03,-365.04 312.03,-418.04 "/>
|
||||
<text text-anchor="middle" x="394.53" y="-402.84" font-family="DejaVu Sans Mono, Book" font-size="14.00">R3 (OSPF) </text>
|
||||
<text text-anchor="middle" x="394.53" y="-387.84" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.100.1/32 </text>
|
||||
<text text-anchor="middle" x="394.53" y="-372.84" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
|
||||
</g>
|
||||
<!-- PC--R3 -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>PC:mgmt3--R3:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-252.02C91.5,-252.02 261.53,-405.54 261.53,-405.54"/>
|
||||
</g>
|
||||
<!-- R1 -->
|
||||
<g id="node3" class="node">
|
||||
<title>R1</title>
|
||||
<polygon fill="none" stroke="black" points="262.58,-154.52 262.58,-223.52 403.58,-223.52 403.58,-154.52 262.58,-154.52"/>
|
||||
<text text-anchor="middle" x="287.58" y="-208.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">ospf</text>
|
||||
<polyline fill="none" stroke="black" points="262.58,-200.52 312.58,-200.52 "/>
|
||||
<text text-anchor="middle" x="287.58" y="-185.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="262.58,-177.52 312.58,-177.52 "/>
|
||||
<text text-anchor="middle" x="287.58" y="-162.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">rip</text>
|
||||
<polyline fill="none" stroke="black" points="312.58,-154.52 312.58,-223.52 "/>
|
||||
<text text-anchor="middle" x="358.08" y="-185.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">R1 (ASBR)</text>
|
||||
</g>
|
||||
<!-- PC--R1 -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>PC:mgmt1--R1:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-189.02C91.5,-189.02 262.08,-189.02 262.08,-189.02"/>
|
||||
</g>
|
||||
<!-- R2 -->
|
||||
<g id="node4" class="node">
|
||||
<title>R2</title>
|
||||
<polygon fill="none" stroke="black" points="262.03,-0.5 262.03,-53.5 477.03,-53.5 477.03,-0.5 262.03,-0.5"/>
|
||||
<text text-anchor="middle" x="287.03" y="-36.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
|
||||
<polyline fill="none" stroke="black" points="262.03,-27.5 312.03,-27.5 "/>
|
||||
<text text-anchor="middle" x="287.03" y="-10.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="312.03,-0.5 312.03,-53.5 "/>
|
||||
<text text-anchor="middle" x="394.53" y="-38.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">R2 (RIP) </text>
|
||||
<text text-anchor="middle" x="394.53" y="-23.3" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.200.1/32 </text>
|
||||
<text text-anchor="middle" x="394.53" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
|
||||
</g>
|
||||
<!-- PC--R2 -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>PC:mgmt2--R2:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-126.02C91.5,-126.02 261.53,-14 261.53,-14"/>
|
||||
</g>
|
||||
<!-- R3--R1 -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>R3:link--R1:ospf</title>
|
||||
<path fill="none" stroke="blue" stroke-width="2" d="M286.53,-364.54C286.53,-364.54 287.08,-224.02 287.08,-224.02"/>
|
||||
<text text-anchor="middle" x="227.81" y="-298.08" font-family="DejaVu Serif, Book" font-size="14.00">192.168.60.0/24</text>
|
||||
<text text-anchor="middle" x="295.46" y="-238.48" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
|
||||
<text text-anchor="middle" x="278.15" y="-342.68" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
|
||||
</g>
|
||||
<!-- R1--R2 -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>R1:rip--R2:link</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M287.08,-154.02C287.08,-154.02 286.53,-54 286.53,-54"/>
|
||||
<text text-anchor="middle" x="227.81" y="-107.81" font-family="DejaVu Serif, Book" font-size="14.00">192.168.50.0/24</text>
|
||||
<text text-anchor="middle" x="295.08" y="-68.38" font-family="DejaVu Serif, Book" font-size="14.00">.2</text>
|
||||
<text text-anchor="middle" x="278.53" y="-132.24" font-family="DejaVu Serif, Book" font-size="14.00">.1</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
@@ -1,41 +1,41 @@
|
||||
[7mINTERFACE PROTOCOL STATE DATA [0m
|
||||
lo loopback [32mUP [39m
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
br0 bridge [33m [39mvlan:1u pvid:1
|
||||
│ ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
│ ipv4 169.254.1.1/16 (random)
|
||||
│ ipv6 fe80::2a0:85ff:fe00:300/64 (link-layer)
|
||||
├ e3 bridge [32mFORWARDING [39mvlan:1u pvid:1
|
||||
├ e4 bridge [32mFORWARDING [39mvlan:1u pvid:1
|
||||
├ veth0b bridge [32mFORWARDING [39mvlan:1u pvid:1
|
||||
└ veth2b bridge [32mFORWARDING [39mvlan:1u pvid:1
|
||||
br1 bridge [33m [39m
|
||||
│ ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
├ veth1b bridge [32mFORWARDING [39mvlan:6u pvid:6
|
||||
└ veth3b bridge [32mFORWARDING [39m
|
||||
e1 ethernet [32mUP [39m00:a0:85:00:03:01
|
||||
ipv6 fe80::2a0:85ff:fe00:301/64 (link-layer)
|
||||
e2 ethernet [32mUP [39m00:a0:85:00:03:02
|
||||
ipv6 fe80::2a0:85ff:fe00:302/64 (link-layer)
|
||||
e3.8 ethernet [32mUP [39m00:a0:85:00:03:03
|
||||
│ ipv4 10.1.1.1/32 (static)
|
||||
└ e3 ethernet [32mUP [39m00:a0:85:00:03:03
|
||||
e4.8 ethernet [32mUP [39m00:a0:85:00:03:04
|
||||
│ ipv4 10.1.1.1/32 (static)
|
||||
└ e4 ethernet [32mUP [39m00:a0:85:00:03:04
|
||||
e5 ethernet [32mUP [39m00:a0:85:00:03:05
|
||||
ipv6 fe80::2a0:85ff:fe00:305/64 (link-layer)
|
||||
e6 ethernet [32mUP [39m00:a0:85:00:03:06
|
||||
ipv4 10.1.1.101/24 (static)
|
||||
ipv6 fe80::2a0:85ff:fe00:306/64 (link-layer)
|
||||
e7 ethernet [32mUP [39m00:a0:85:00:03:07
|
||||
ipv6 fe80::2a0:85ff:fe00:307/64 (link-layer)
|
||||
[100mveth0a container container-A [0m
|
||||
veth0b veth [32mUP [39me6:19:75:86:80:dd
|
||||
[100mveth1a container container-A [0m
|
||||
veth1b veth [32mUP [39m3e:67:0f:15:24:14
|
||||
[100mveth2a container container-B [0m
|
||||
veth2b veth [32mUP [39mb2:fa:30:63:98:f0
|
||||
[100mveth3a container container-B [0m
|
||||
veth3b veth [32mUP [39mb2:1c:fa:a7:45:eb
|
||||
[7m⚑ INTERFACE PROTOCOL STATE DATA [0m
|
||||
lo loopback [32mUP [39m
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
br0 bridge [33m [39mvlan:1u pvid:1
|
||||
│ ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
│ ipv4 169.254.1.1/16 (random)
|
||||
│ ipv6 fe80::2a0:85ff:fe00:300/64 (link-layer)
|
||||
├ e3 bridge [32mFORWARDING [39mvlan:1u pvid:1
|
||||
├ e4 bridge [32mFORWARDING [39mvlan:1u pvid:1
|
||||
├ veth0b bridge [32mFORWARDING [39mvlan:1u pvid:1
|
||||
└ veth2b bridge [32mFORWARDING [39mvlan:1u pvid:1
|
||||
br1 bridge [33m [39m
|
||||
│ ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
├ veth1b bridge [32mFORWARDING [39mvlan:6u pvid:6
|
||||
└ veth3b bridge [32mFORWARDING [39m
|
||||
e1 ethernet [32mUP [39m00:a0:85:00:03:01
|
||||
ipv6 fe80::2a0:85ff:fe00:301/64 (link-layer)
|
||||
e2 ethernet [32mUP [39m00:a0:85:00:03:02
|
||||
ipv6 fe80::2a0:85ff:fe00:302/64 (link-layer)
|
||||
e3.8 ethernet [32mUP [39m00:a0:85:00:03:03
|
||||
│ ipv4 10.1.1.1/32 (static)
|
||||
└ e3 ethernet [32mUP [39m00:a0:85:00:03:03
|
||||
e4.8 ethernet [32mUP [39m00:a0:85:00:03:04
|
||||
│ ipv4 10.1.1.1/32 (static)
|
||||
└ e4 ethernet [32mUP [39m00:a0:85:00:03:04
|
||||
e5 ethernet [32mUP [39m00:a0:85:00:03:05
|
||||
ipv6 fe80::2a0:85ff:fe00:305/64 (link-layer)
|
||||
e6 ethernet [32mUP [39m00:a0:85:00:03:06
|
||||
ipv4 10.1.1.101/24 (static)
|
||||
ipv6 fe80::2a0:85ff:fe00:306/64 (link-layer)
|
||||
e7 ethernet [32mUP [39m00:a0:85:00:03:07
|
||||
ipv6 fe80::2a0:85ff:fe00:307/64 (link-layer)
|
||||
[100mveth0a container container-A [0m
|
||||
veth0b veth [32mUP [39me6:19:75:86:80:dd
|
||||
[100mveth1a container container-A [0m
|
||||
veth1b veth [32mUP [39m3e:67:0f:15:24:14
|
||||
[100mveth2a container container-B [0m
|
||||
veth2b veth [32mUP [39mb2:fa:30:63:98:f0
|
||||
[100mveth3a container container-B [0m
|
||||
veth3b veth [32mUP [39mb2:1c:fa:a7:45:eb
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"ietf-routing:routing": {
|
||||
"interfaces": {
|
||||
"interface": []
|
||||
},
|
||||
"ribs": {
|
||||
"rib": [
|
||||
{
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
[7mINTERFACE PROTOCOL STATE DATA [0m
|
||||
lo loopback [32mUP [39m
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
br-0 bridge [31mDOWN [39m
|
||||
ethernet [31mDOWN [39m00:a0:85:00:03:00
|
||||
br-D bridge [33m [39m
|
||||
│ ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
│ ipv4 10.0.0.1/8 (static)
|
||||
│ ipv4 192.168.20.1/24 (static)
|
||||
│ ipv6 2001:db8::1/64 (static)
|
||||
│ ipv6 fe80::2a0:85ff:fe00:300/64 (link-layer)
|
||||
└ veth0a.20 bridge [32mFORWARDING [39m
|
||||
br-Q bridge [33m [39mvlan:20u,30u,40t
|
||||
│ ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
├ e3 bridge [32mFORWARDING [39mvlan:20t,30t,40t
|
||||
└ veth0b bridge [32mFORWARDING [39mvlan:20t,30t,40t
|
||||
br-Q.40 ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
└ br-Q ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
br-X bridge [33m [39m
|
||||
│ ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
└ e2.30 bridge [32mFORWARDING [39m
|
||||
e1 ethernet [32mUP [39m00:a0:85:00:03:01
|
||||
ipv6 fe80::2a0:85ff:fe00:301/64 (link-layer)
|
||||
e2 ethernet [32mUP [39m00:a0:85:00:03:02
|
||||
ipv6 fe80::2a0:85ff:fe00:302/64 (link-layer)
|
||||
e2.30 ethernet [32mUP [39m00:a0:85:00:03:02
|
||||
└ e2 ethernet [32mUP [39m00:a0:85:00:03:02
|
||||
e3.10 ethernet [32mUP [39m00:a0:85:00:03:03
|
||||
└ e3 ethernet [32mUP [39m00:a0:85:00:03:03
|
||||
e4 ethernet [32mUP [39m00:a0:85:00:03:04
|
||||
ipv6 fe80::2a0:85ff:fe00:304/64 (link-layer)
|
||||
e5 ethernet [32mUP [39m00:a0:85:00:03:05
|
||||
ipv6 fe80::2a0:85ff:fe00:305/64 (link-layer)
|
||||
e6 ethernet [32mUP [39m00:a0:85:00:03:06
|
||||
ipv6 fe80::2a0:85ff:fe00:306/64 (link-layer)
|
||||
e7 ethernet [32mUP [39m00:a0:85:00:03:07
|
||||
ipv6 fe80::2a0:85ff:fe00:307/64 (link-layer)
|
||||
gre-v4 gre [32mUP [39m
|
||||
gre-v6 gre [32mUP [39m
|
||||
ipv4 192.168.50.2/16 (static)
|
||||
gretap-v4 gretap [32mUP [39mf6:d4:96:d8:9f:96
|
||||
gretap-v6 gretap [32mUP [39m7a:46:13:bd:fa:e6
|
||||
veth0a veth [32mUP [39m6e:d0:98:c4:e7:ef
|
||||
veth0a.20 ethernet [32mUP [39m6e:d0:98:c4:e7:ef
|
||||
└ veth0a ethernet [32mUP [39m6e:d0:98:c4:e7:ef
|
||||
veth0b veth [32mUP [39m36:da:80:06:7f:99
|
||||
vxlan-v4 vxlan [32mUP [39m8a:ea:59:32:df:70
|
||||
ipv4 192.168.30.2/24 (static)
|
||||
vxlan-v6 vxlan [32mUP [39m3e:30:c6:a1:71:64
|
||||
ipv4 192.168.40.2/24 (static)
|
||||
[7m⚑ INTERFACE PROTOCOL STATE DATA [0m
|
||||
lo loopback [32mUP [39m
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
br-0 bridge [31mDOWN [39m
|
||||
ethernet [31mDOWN [39m00:a0:85:00:03:00
|
||||
br-D bridge [33m [39m
|
||||
│ ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
│ ipv4 10.0.0.1/8 (static)
|
||||
│ ipv4 192.168.20.1/24 (static)
|
||||
│ ipv6 2001:db8::1/64 (static)
|
||||
│ ipv6 fe80::2a0:85ff:fe00:300/64 (link-layer)
|
||||
└ veth0a.20 bridge [32mFORWARDING [39m
|
||||
br-Q bridge [33m [39mvlan:20u,30u,40t
|
||||
│ ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
├ e3 bridge [32mFORWARDING [39mvlan:20t,30t,40t
|
||||
└ veth0b bridge [32mFORWARDING [39mvlan:20t,30t,40t
|
||||
br-Q.40 ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
└ br-Q ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
br-X bridge [33m [39m
|
||||
│ ethernet [32mUP [39m00:a0:85:00:03:00
|
||||
└ e2.30 bridge [32mFORWARDING [39m
|
||||
e1 ethernet [32mUP [39m00:a0:85:00:03:01
|
||||
ipv6 fe80::2a0:85ff:fe00:301/64 (link-layer)
|
||||
e2 ethernet [32mUP [39m00:a0:85:00:03:02
|
||||
ipv6 fe80::2a0:85ff:fe00:302/64 (link-layer)
|
||||
e2.30 ethernet [32mUP [39m00:a0:85:00:03:02
|
||||
└ e2 ethernet [32mUP [39m00:a0:85:00:03:02
|
||||
e3.10 ethernet [32mUP [39m00:a0:85:00:03:03
|
||||
└ e3 ethernet [32mUP [39m00:a0:85:00:03:03
|
||||
e4 ethernet [32mUP [39m00:a0:85:00:03:04
|
||||
ipv6 fe80::2a0:85ff:fe00:304/64 (link-layer)
|
||||
e5 ethernet [32mUP [39m00:a0:85:00:03:05
|
||||
ipv6 fe80::2a0:85ff:fe00:305/64 (link-layer)
|
||||
e6 ethernet [32mUP [39m00:a0:85:00:03:06
|
||||
ipv6 fe80::2a0:85ff:fe00:306/64 (link-layer)
|
||||
e7 ethernet [32mUP [39m00:a0:85:00:03:07
|
||||
ipv6 fe80::2a0:85ff:fe00:307/64 (link-layer)
|
||||
gre-v4 gre [32mUP [39m
|
||||
gre-v6 gre [32mUP [39m
|
||||
ipv4 192.168.50.2/16 (static)
|
||||
gretap-v4 gretap [32mUP [39mf6:d4:96:d8:9f:96
|
||||
gretap-v6 gretap [32mUP [39m7a:46:13:bd:fa:e6
|
||||
veth0a veth [32mUP [39m6e:d0:98:c4:e7:ef
|
||||
veth0a.20 ethernet [32mUP [39m6e:d0:98:c4:e7:ef
|
||||
└ veth0a ethernet [32mUP [39m6e:d0:98:c4:e7:ef
|
||||
veth0b veth [32mUP [39m36:da:80:06:7f:99
|
||||
vxlan-v4 vxlan [32mUP [39m8a:ea:59:32:df:70
|
||||
ipv4 192.168.30.2/24 (static)
|
||||
vxlan-v6 vxlan [32mUP [39m3e:30:c6:a1:71:64
|
||||
ipv4 192.168.40.2/24 (static)
|
||||
|
||||
@@ -3,6 +3,7 @@ type : bridge
|
||||
index : 9
|
||||
mtu : 1500
|
||||
operational status : down
|
||||
ip forwarding : disabled
|
||||
physical address : 00:a0:85:00:03:00
|
||||
ipv4 addresses :
|
||||
ipv6 addresses :
|
||||
|
||||
@@ -3,6 +3,7 @@ type : bridge
|
||||
index : 15
|
||||
mtu : 1500
|
||||
operational status : up
|
||||
ip forwarding : disabled
|
||||
physical address : 00:a0:85:00:03:00
|
||||
ipv4 addresses : 10.0.0.1/8 (static)
|
||||
192.168.20.1/24 (static)
|
||||
|
||||
@@ -3,6 +3,7 @@ type : bridge
|
||||
index : 16
|
||||
mtu : 1500
|
||||
operational status : up
|
||||
ip forwarding : disabled
|
||||
physical address : 00:a0:85:00:03:00
|
||||
ipv4 addresses :
|
||||
ipv6 addresses :
|
||||
|
||||
@@ -3,6 +3,7 @@ type : vlan
|
||||
index : 17
|
||||
mtu : 1500
|
||||
operational status : up
|
||||
ip forwarding : disabled
|
||||
lower-layer-if : br-Q
|
||||
physical address : 00:a0:85:00:03:00
|
||||
ipv4 addresses :
|
||||
|
||||
@@ -3,6 +3,7 @@ type : bridge
|
||||
index : 11
|
||||
mtu : 1500
|
||||
operational status : up
|
||||
ip forwarding : disabled
|
||||
physical address : 00:a0:85:00:03:00
|
||||
ipv4 addresses :
|
||||
ipv6 addresses :
|
||||
|
||||
@@ -3,6 +3,7 @@ type : gre
|
||||
index : 19
|
||||
mtu : 1476
|
||||
operational status : up
|
||||
ip forwarding : disabled
|
||||
ipv4 addresses :
|
||||
ipv6 addresses :
|
||||
local address : 192.168.20.1
|
||||
|
||||
@@ -3,6 +3,7 @@ type : gre
|
||||
index : 20
|
||||
mtu : 1448
|
||||
operational status : up
|
||||
ip forwarding : disabled
|
||||
ipv4 addresses : 192.168.50.2/16 (static)
|
||||
ipv6 addresses :
|
||||
local address : 2001:db8::1
|
||||
|
||||
@@ -3,6 +3,7 @@ type : gretap
|
||||
index : 21
|
||||
mtu : 1462
|
||||
operational status : up
|
||||
ip forwarding : disabled
|
||||
physical address : f6:d4:96:d8:9f:96
|
||||
ipv4 addresses :
|
||||
ipv6 addresses :
|
||||
|
||||
@@ -3,6 +3,7 @@ type : gretap
|
||||
index : 22
|
||||
mtu : 1434
|
||||
operational status : up
|
||||
ip forwarding : disabled
|
||||
physical address : 7a:46:13:bd:fa:e6
|
||||
ipv4 addresses :
|
||||
ipv6 addresses :
|
||||
|
||||
@@ -3,6 +3,7 @@ type : veth
|
||||
index : 13
|
||||
mtu : 1500
|
||||
operational status : up
|
||||
ip forwarding : disabled
|
||||
physical address : 6e:d0:98:c4:e7:ef
|
||||
ipv4 addresses :
|
||||
ipv6 addresses :
|
||||
|
||||
@@ -3,6 +3,7 @@ type : vlan
|
||||
index : 14
|
||||
mtu : 1500
|
||||
operational status : up
|
||||
ip forwarding : disabled
|
||||
lower-layer-if : veth0a
|
||||
physical address : 6e:d0:98:c4:e7:ef
|
||||
ipv4 addresses :
|
||||
|
||||
@@ -3,6 +3,7 @@ type : veth
|
||||
index : 12
|
||||
mtu : 1500
|
||||
operational status : up
|
||||
ip forwarding : disabled
|
||||
physical address : 36:da:80:06:7f:99
|
||||
ipv4 addresses :
|
||||
ipv6 addresses :
|
||||
|
||||
@@ -3,6 +3,7 @@ type : vxlan
|
||||
index : 23
|
||||
mtu : 1500
|
||||
operational status : up
|
||||
ip forwarding : disabled
|
||||
physical address : 8a:ea:59:32:df:70
|
||||
ipv4 addresses : 192.168.30.2/24 (static)
|
||||
ipv6 addresses :
|
||||
|
||||
@@ -3,6 +3,7 @@ type : vxlan
|
||||
index : 24
|
||||
mtu : 1500
|
||||
operational status : up
|
||||
ip forwarding : disabled
|
||||
physical address : 3e:30:c6:a1:71:64
|
||||
ipv4 addresses : 192.168.40.2/24 (static)
|
||||
ipv6 addresses :
|
||||
|
||||
Reference in New Issue
Block a user