nhrp: add cisco-authentication password support

Implemented:
- handling 8 char long password, aka Cisco style.
- minimal error inidication routine
- test case, password change affects conection

Signed-off-by: Volodymyr Huti <v.huti@vyos.io>
This commit is contained in:
Volodymyr Huti 2023-11-13 22:47:31 +02:00 committed by Dave LeRoy
parent d5b0c76edd
commit 51f0700286
11 changed files with 242 additions and 31 deletions

View file

@ -84,6 +84,12 @@ Configuring NHRP
registration requests are sent. By default registrations are sent every one registration requests are sent. By default registrations are sent every one
third of the holdtime. third of the holdtime.
.. clicmd:: ip nhrp authentication PASSWORD
Enables Cisco style authentication on NHRP packets. This embeds the
plaintext password to the outgoing NHRP packets.
Maximum length of the is 8 characters.
.. clicmd:: ip nhrp map A.B.C.D|X:X::X:X A.B.C.D|local .. clicmd:: ip nhrp map A.B.C.D|X:X::X:X A.B.C.D|local
Map an IP address of a station to the station's NBMA address. Map an IP address of a station to the station's NBMA address.

View file

@ -216,7 +216,7 @@ static void nhrp_reg_send_req(struct event *t)
cie->holding_time = htons(if_ad->holdtime); cie->holding_time = htons(if_ad->holdtime);
cie->mtu = htons(if_ad->mtu); cie->mtu = htons(if_ad->mtu);
nhrp_ext_request(zb, hdr, ifp); nhrp_ext_request(zb, hdr);
/* Cisco NAT detection extension */ /* Cisco NAT detection extension */
if (sockunion_family(&r->proto_addr) != AF_UNSPEC) { if (sockunion_family(&r->proto_addr) != AF_UNSPEC) {
@ -240,7 +240,7 @@ static void nhrp_reg_send_req(struct event *t)
cie->mtu = htons(if_ad->mtu); cie->mtu = htons(if_ad->mtu);
nhrp_ext_complete(zb, ext); nhrp_ext_complete(zb, ext);
nhrp_packet_complete(zb, hdr); nhrp_packet_complete(zb, hdr, ifp);
nhrp_peer_send(r->peer, zb); nhrp_peer_send(r->peer, zb);
zbuf_free(zb); zbuf_free(zb);
} }

View file

@ -115,14 +115,32 @@ uint16_t nhrp_packet_calculate_checksum(const uint8_t *pdu, uint16_t len)
return (~csum) & 0xffff; return (~csum) & 0xffff;
} }
void nhrp_packet_complete(struct zbuf *zb, struct nhrp_packet_header *hdr) void nhrp_packet_complete(struct zbuf *zb, struct nhrp_packet_header *hdr,
struct interface *ifp)
{ {
nhrp_packet_complete_auth(zb, hdr, ifp, true);
}
void nhrp_packet_complete_auth(struct zbuf *zb, struct nhrp_packet_header *hdr,
struct interface *ifp, bool auth)
{
struct nhrp_interface *nifp = ifp->info;
struct zbuf *auth_token = nifp->auth_token;
struct nhrp_extension_header *dst;
unsigned short size; unsigned short size;
if (auth && auth_token) {
dst = nhrp_ext_push(zb, hdr,
NHRP_EXTENSION_AUTHENTICATION |
NHRP_EXTENSION_FLAG_COMPULSORY);
zbuf_copy_peek(zb, auth_token, zbuf_size(auth_token));
nhrp_ext_complete(zb, dst);
}
if (hdr->extension_offset) if (hdr->extension_offset)
nhrp_ext_push(zb, hdr, nhrp_ext_push(zb, hdr,
NHRP_EXTENSION_END NHRP_EXTENSION_END |
| NHRP_EXTENSION_FLAG_COMPULSORY); NHRP_EXTENSION_FLAG_COMPULSORY);
size = zb->tail - (uint8_t *)hdr; size = zb->tail - (uint8_t *)hdr;
hdr->packet_size = htons(size); hdr->packet_size = htons(size);
@ -225,8 +243,7 @@ struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb,
return ext; return ext;
} }
void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr, void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr)
struct interface *ifp)
{ {
/* Place holders for standard extensions */ /* Place holders for standard extensions */
nhrp_ext_push(zb, hdr, nhrp_ext_push(zb, hdr,

View file

@ -603,7 +603,7 @@ static void nhrp_handle_resolution_req(struct nhrp_packet_parser *pp)
break; break;
} }
} }
nhrp_packet_complete(zb, hdr); nhrp_packet_complete(zb, hdr, ifp);
nhrp_peer_send(peer, zb); nhrp_peer_send(peer, zb);
err: err:
nhrp_peer_unref(peer); nhrp_peer_unref(peer);
@ -730,7 +730,8 @@ static void nhrp_handle_registration_request(struct nhrp_packet_parser *p)
} }
} }
nhrp_packet_complete(zb, hdr); // auth ext was validated and copied from the request
nhrp_packet_complete_auth(zb, hdr, ifp, false);
nhrp_peer_send(p->peer, zb); nhrp_peer_send(p->peer, zb);
err: err:
zbuf_free(zb); zbuf_free(zb);
@ -812,7 +813,7 @@ void nhrp_peer_send_indication(struct interface *ifp, uint16_t protocol_type,
/* Payload is the packet causing indication */ /* Payload is the packet causing indication */
zbuf_copy(zb, pkt, zbuf_used(pkt)); zbuf_copy(zb, pkt, zbuf_used(pkt));
nhrp_packet_complete(zb, hdr); nhrp_packet_complete(zb, hdr, ifp);
nhrp_peer_send(p, zb); nhrp_peer_send(p, zb);
nhrp_peer_unref(p); nhrp_peer_unref(p);
zbuf_free(zb); zbuf_free(zb);
@ -1063,7 +1064,8 @@ static void nhrp_peer_forward(struct nhrp_peer *p,
nhrp_ext_complete(zb, dst); nhrp_ext_complete(zb, dst);
} }
nhrp_packet_complete(zb, hdr); // XXX: auth already handled ???
nhrp_packet_complete_auth(zb, hdr, pp->ifp, false);
nhrp_peer_send(p, zb); nhrp_peer_send(p, zb);
zbuf_free(zb); zbuf_free(zb);
zbuf_free(zb_copy); zbuf_free(zb_copy);
@ -1089,8 +1091,7 @@ static void nhrp_packet_debug(struct zbuf *zb, const char *dir)
reply = packet_types[hdr->type].type == PACKET_REPLY; reply = packet_types[hdr->type].type == PACKET_REPLY;
debugf(NHRP_DEBUG_COMMON, "%s %s(%d) %pSU -> %pSU", dir, debugf(NHRP_DEBUG_COMMON, "%s %s(%d) %pSU -> %pSU", dir,
(packet_types[hdr->type].name ? packet_types[hdr->type].name (packet_types[hdr->type].name ? : "Unknown"),
: "Unknown"),
hdr->type, reply ? &dst_proto : &src_proto, hdr->type, reply ? &dst_proto : &src_proto,
reply ? &src_proto : &dst_proto); reply ? &src_proto : &dst_proto);
} }
@ -1106,11 +1107,70 @@ static int proto2afi(uint16_t proto)
return AF_UNSPEC; return AF_UNSPEC;
} }
struct nhrp_route_info { static int nhrp_packet_send_error(struct nhrp_packet_parser *pp,
int local; uint16_t indication_code, uint16_t offset)
struct interface *ifp; {
struct nhrp_vc *vc; union sockunion src_proto, dst_proto;
}; struct nhrp_packet_header *hdr;
struct zbuf *zb;
src_proto = pp->src_proto;
dst_proto = pp->dst_proto;
if (packet_types[pp->hdr->type].type != PACKET_REPLY) {
src_proto = pp->dst_proto;
dst_proto = pp->src_proto;
}
/* Create reply */
zb = zbuf_alloc(1500); // XXX: hardcode -> calculation routine
hdr = nhrp_packet_push(zb, NHRP_PACKET_ERROR_INDICATION, &pp->src_nbma,
&src_proto, &dst_proto);
hdr->u.error.code = indication_code;
hdr->u.error.offset = htons(offset);
hdr->flags = pp->hdr->flags;
hdr->hop_count = 0; // XXX: cisco returns 255
/* Payload is the packet causing error */
/* Don`t add extension according to RFC */
/* wireshark gives bad checksum, without exts */
// pp->hdr->checksum = nhrp_packet_calculate_checksum(zbuf_used(&pp->payload))
zbuf_put(zb, pp->hdr, sizeof(*pp->hdr));
zbuf_copy(zb, &pp->payload, zbuf_used(&pp->payload));
nhrp_packet_complete_auth(zb, hdr, pp->ifp, false);
/* nhrp_packet_debug(zb, "SEND_ERROR"); */
nhrp_peer_send(pp->peer, zb);
zbuf_free(zb);
return 0;
}
static bool nhrp_connection_authorized(struct nhrp_packet_parser *pp)
{
struct nhrp_cisco_authentication_extension *auth_ext;
struct nhrp_interface *nifp = pp->ifp->info;
struct zbuf *auth = nifp->auth_token;
struct nhrp_extension_header *ext;
struct zbuf *extensions, pl;
int cmp = 0;
extensions = zbuf_alloc(zbuf_used(&pp->extensions));
zbuf_copy_peek(extensions, &pp->extensions, zbuf_used(&pp->extensions));
while ((ext = nhrp_ext_pull(extensions, &pl)) != NULL) {
switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
case NHRP_EXTENSION_AUTHENTICATION:
cmp = memcmp(auth->buf, pl.buf, zbuf_size(auth));
auth_ext = (struct nhrp_cisco_authentication_extension *)
auth->buf;
debugf(NHRP_DEBUG_COMMON,
"Processing Authentication Extension for (%s:%s|%d)",
auth_ext->secret, (const char *)pl.buf, cmp);
break;
}
}
zbuf_free(extensions);
return cmp == 0;
}
void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb) void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb)
{ {
@ -1191,10 +1251,20 @@ void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb)
goto drop; goto drop;
} }
/* RFC2332 5.3.4 - Authentication is always done pairwise on an NHRP
* hop-by-hop basis; i.e. regenerated at each hop. */
nhrp_packet_debug(zb, "Recv"); nhrp_packet_debug(zb, "Recv");
if (nifp->auth_token &&
/* FIXME: Check authentication here. This extension needs to be (hdr->type != NHRP_PACKET_ERROR_INDICATION ||
* pre-handled. */ hdr->u.error.code != NHRP_ERROR_AUTHENTICATION_FAILURE)) {
if (!nhrp_connection_authorized(&pp)) {
nhrp_packet_send_error(&pp,
NHRP_ERROR_AUTHENTICATION_FAILURE,
0);
info = "authentication failure";
goto drop;
}
}
/* Figure out if this is local */ /* Figure out if this is local */
target_addr = (packet_types[hdr->type].type == PACKET_REPLY) target_addr = (packet_types[hdr->type].type == PACKET_REPLY)

View file

@ -425,7 +425,7 @@ static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s)
"Shortcut res_req: set cie ht to %u and mtu to %u. shortcut ht is %u", "Shortcut res_req: set cie ht to %u and mtu to %u. shortcut ht is %u",
ntohs(cie->holding_time), ntohs(cie->mtu), s->holding_time); ntohs(cie->holding_time), ntohs(cie->mtu), s->holding_time);
nhrp_ext_request(zb, hdr, ifp); nhrp_ext_request(zb, hdr);
/* Cisco NAT detection extension */ /* Cisco NAT detection extension */
hdr->flags |= htons(NHRP_FLAG_RESOLUTION_NAT); hdr->flags |= htons(NHRP_FLAG_RESOLUTION_NAT);
@ -438,7 +438,7 @@ static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s)
nhrp_ext_complete(zb, ext); nhrp_ext_complete(zb, ext);
} }
nhrp_packet_complete(zb, hdr); nhrp_packet_complete(zb, hdr, ifp);
nhrp_peer_send(peer, zb); nhrp_peer_send(peer, zb);
nhrp_peer_unref(peer); nhrp_peer_unref(peer);

View file

@ -12,6 +12,9 @@
#include "nhrpd.h" #include "nhrpd.h"
#include "netlink.h" #include "netlink.h"
#include "nhrp_protocol.h"
#include "nhrpd/nhrp_vty_clippy.c"
static int nhrp_config_write(struct vty *vty); static int nhrp_config_write(struct vty *vty);
static struct cmd_node zebra_node = { static struct cmd_node zebra_node = {
@ -459,6 +462,56 @@ DEFUN(if_no_nhrp_holdtime, if_no_nhrp_holdtime_cmd,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
#define NHRP_CISCO_PASS_LEN 8
DEFPY(if_nhrp_authentication, if_nhrp_authentication_cmd,
AFI_CMD "nhrp authentication PASSWORD$password",
AFI_STR
NHRP_STR
"Specify plaint text password used for authenticantion\n"
"Password, plain text, limited to 8 characters\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
struct nhrp_cisco_authentication_extension *auth;
struct nhrp_interface *nifp = ifp->info;
int pass_len = strlen(password);
if (pass_len > NHRP_CISCO_PASS_LEN) {
vty_out(vty, "Password size limit exceeded (%d>%d)\n",
pass_len, NHRP_CISCO_PASS_LEN);
return CMD_WARNING_CONFIG_FAILED;
}
if (nifp->auth_token)
zbuf_free(nifp->auth_token);
nifp->auth_token = zbuf_alloc(pass_len + sizeof(uint32_t));
auth = (struct nhrp_cisco_authentication_extension *)
nifp->auth_token->buf;
auth->type = htonl(NHRP_AUTHENTICATION_PLAINTEXT);
memcpy(auth->secret, password, pass_len);
// XXX RFC: reset active (non-authorized) connections?
/* vty_out(vty, "NHRP passwd (%s:%s)", nifp->ifp->name, auth->secret); */
return CMD_SUCCESS;
}
DEFPY(if_no_nhrp_authentication, if_no_nhrp_authentication_cmd,
"no " AFI_CMD "nhrp authentication PASSWORD$password",
NO_STR
AFI_STR
NHRP_STR
"Reset password used for authentication\n"
"Password, plain text\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
struct nhrp_interface *nifp = ifp->info;
if (nifp->auth_token)
zbuf_free(nifp->auth_token);
return CMD_SUCCESS;
}
DEFUN(if_nhrp_mtu, if_nhrp_mtu_cmd, DEFUN(if_nhrp_mtu, if_nhrp_mtu_cmd,
"ip nhrp mtu <(576-1500)|opennhrp>", "ip nhrp mtu <(576-1500)|opennhrp>",
IP_STR IP_STR
@ -1053,6 +1106,7 @@ DEFUN(show_dmvpn, show_dmvpn_cmd,
static void clear_nhrp_cache(struct nhrp_cache *c, void *data) static void clear_nhrp_cache(struct nhrp_cache *c, void *data)
{ {
struct info_ctx *ctx = data; struct info_ctx *ctx = data;
if (c->cur.type <= NHRP_CACHE_DYNAMIC) { if (c->cur.type <= NHRP_CACHE_DYNAMIC) {
nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL, nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL,
NULL); NULL);
@ -1129,6 +1183,7 @@ static void interface_config_write_nhrp_map(struct nhrp_cache_config *c,
static int interface_config_write(struct vty *vty) static int interface_config_write(struct vty *vty)
{ {
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct nhrp_cisco_authentication_extension *auth;
struct write_map_ctx mapctx; struct write_map_ctx mapctx;
struct interface *ifp; struct interface *ifp;
struct nhrp_interface *nifp; struct nhrp_interface *nifp;
@ -1155,6 +1210,12 @@ static int interface_config_write(struct vty *vty)
if (nifp->source) if (nifp->source)
vty_out(vty, " tunnel source %s\n", nifp->source); vty_out(vty, " tunnel source %s\n", nifp->source);
if (nifp->auth_token) {
auth = (struct nhrp_cisco_authentication_extension *)
nifp->auth_token->buf;
vty_out(vty, " ip nhrp authentication %s\n", auth->secret);
}
for (afi = 0; afi < AFI_MAX; afi++) { for (afi = 0; afi < AFI_MAX; afi++) {
struct nhrp_afi_data *ad = &nifp->afi[afi]; struct nhrp_afi_data *ad = &nifp->afi[afi];
@ -1256,6 +1317,8 @@ void nhrp_config_init(void)
install_element(INTERFACE_NODE, &if_no_nhrp_network_id_cmd); install_element(INTERFACE_NODE, &if_no_nhrp_network_id_cmd);
install_element(INTERFACE_NODE, &if_nhrp_holdtime_cmd); install_element(INTERFACE_NODE, &if_nhrp_holdtime_cmd);
install_element(INTERFACE_NODE, &if_no_nhrp_holdtime_cmd); install_element(INTERFACE_NODE, &if_no_nhrp_holdtime_cmd);
install_element(INTERFACE_NODE, &if_nhrp_authentication_cmd);
install_element(INTERFACE_NODE, &if_no_nhrp_authentication_cmd);
install_element(INTERFACE_NODE, &if_nhrp_mtu_cmd); install_element(INTERFACE_NODE, &if_nhrp_mtu_cmd);
install_element(INTERFACE_NODE, &if_no_nhrp_mtu_cmd); install_element(INTERFACE_NODE, &if_no_nhrp_mtu_cmd);
install_element(INTERFACE_NODE, &if_nhrp_flags_cmd); install_element(INTERFACE_NODE, &if_nhrp_flags_cmd);

View file

@ -311,6 +311,7 @@ DECLARE_DLIST(nhrp_reglist, struct nhrp_registration, reglist_entry);
struct nhrp_interface { struct nhrp_interface {
struct interface *ifp; struct interface *ifp;
struct zbuf *auth_token;
unsigned enabled : 1; unsigned enabled : 1;
char *ipsec_profile, *ipsec_fallback_profile, *source; char *ipsec_profile, *ipsec_fallback_profile, *source;
@ -480,9 +481,13 @@ struct nhrp_packet_header *nhrp_packet_push(struct zbuf *zb, uint8_t type,
const union sockunion *src_nbma, const union sockunion *src_nbma,
const union sockunion *src_proto, const union sockunion *src_proto,
const union sockunion *dst_proto); const union sockunion *dst_proto);
void nhrp_packet_complete(struct zbuf *zb, struct nhrp_packet_header *hdr);
uint16_t nhrp_packet_calculate_checksum(const uint8_t *pdu, uint16_t len); uint16_t nhrp_packet_calculate_checksum(const uint8_t *pdu, uint16_t len);
void nhrp_packet_complete(struct zbuf *zb, struct nhrp_packet_header *hdr,
struct interface *ifp);
void nhrp_packet_complete_auth(struct zbuf *zb, struct nhrp_packet_header *hdr,
struct interface *ifp, bool auth);
struct nhrp_packet_header *nhrp_packet_pull(struct zbuf *zb, struct nhrp_packet_header *nhrp_packet_pull(struct zbuf *zb,
union sockunion *src_nbma, union sockunion *src_nbma,
union sockunion *src_proto, union sockunion *src_proto,
@ -501,8 +506,7 @@ nhrp_ext_push(struct zbuf *zb, struct nhrp_packet_header *hdr, uint16_t type);
void nhrp_ext_complete(struct zbuf *zb, struct nhrp_extension_header *ext); void nhrp_ext_complete(struct zbuf *zb, struct nhrp_extension_header *ext);
struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb, struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb,
struct zbuf *payload); struct zbuf *payload);
void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr, void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr);
struct interface *);
int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr, int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr,
struct interface *ifp, struct nhrp_extension_header *ext, struct interface *ifp, struct nhrp_extension_header *ext,
struct zbuf *extpayload); struct zbuf *extpayload);

View file

@ -42,3 +42,7 @@ noinst_HEADERS += \
nhrpd/zbuf.h \ nhrpd/zbuf.h \
nhrpd/znl.h \ nhrpd/znl.h \
# end # end
clippy_scan += \
nhrpd/nhrp_vty.c \
# end

View file

@ -1,6 +1,7 @@
log stdout debugging log stdout debugging
! debug nhrp all ! debug nhrp all
interface r1-gre0 interface r1-gre0
ip nhrp authentication secret
ip nhrp holdtime 500 ip nhrp holdtime 500
ip nhrp shortcut ip nhrp shortcut
ip nhrp network-id 42 ip nhrp network-id 42

View file

@ -2,6 +2,7 @@
log stdout debugging log stdout debugging
nhrp nflog-group 1 nhrp nflog-group 1
interface r2-gre0 interface r2-gre0
ip nhrp authentication secret
ip nhrp holdtime 500 ip nhrp holdtime 500
ip nhrp redirect ip nhrp redirect
ip nhrp network-id 42 ip nhrp network-id 42

View file

@ -214,19 +214,64 @@ def test_protocols_convergence():
def test_nhrp_connection(): def test_nhrp_connection():
"Assert that the NHRP peers can find themselves." "Assert that the NHRP peers can find themselves."
tgen = get_topogen() tgen = get_topogen()
pingrouter = tgen.gears["r1"]
hubrouter = tgen.gears["r2"]
if tgen.routers_have_failure(): if tgen.routers_have_failure():
pytest.skip(tgen.errors) pytest.skip(tgen.errors)
pingrouter = tgen.gears["r1"] def ping_helper():
logger.info("Check Ping IPv4 from R1 to R2 = 10.255.255.2)") output = pingrouter.run("ping 10.255.255.2 -f -c 100")
output = pingrouter.run("ping 10.255.255.2 -f -c 1000") logger.info(output)
logger.info(output) return output
if "1000 packets transmitted, 1000 received" not in output:
# force session to reinitialize
def relink_session():
for r in ["r1", "r2"]:
tgen.gears[r].vtysh_cmd("clear ip nhrp cache")
tgen.net[r].cmd("ip l del {}-gre0".format(r));
_populate_iface();
### Passwords are the same
logger.info("Check Ping IPv4 from R1 to R2 = 10.255.255.2")
output = ping_helper()
if "100 packets transmitted, 100 received" not in output:
assertmsg = "expected ping IPv4 from R1 to R2 should be ok" assertmsg = "expected ping IPv4 from R1 to R2 should be ok"
assert 0, assertmsg assert 0, assertmsg
else: else:
logger.info("Check Ping IPv4 from R1 to R2 OK") logger.info("Check Ping IPv4 from R1 to R2 OK")
### Passwords are different
logger.info("Modify password and send ping again, should drop")
hubrouter.vtysh_cmd("""
configure
interface r2-gre0
ip nhrp authentication secret12
""")
relink_session()
topotest.sleep(10, "Waiting for session to initialize")
output = ping_helper()
if "Network is unreachable" not in output:
assertmsg = "expected ping IPv4 from R1 to R2 - should be down"
assert 0, assertmsg
else:
logger.info("Check Ping IPv4 from R1 to R2 missing - OK")
### Passwords are the same - again
logger.info("Recover password and verify conectivity is back")
hubrouter.vtysh_cmd("""
configure
interface r2-gre0
ip nhrp authentication secret
""")
relink_session()
topotest.sleep(10, "Waiting for session to initialize")
output = pingrouter.run("ping 10.255.255.2 -f -c 100")
logger.info(output)
if "100 packets transmitted, 100 received" not in output:
assertmsg = "expected ping IPv4 from R1 to R2 should be ok"
assert 0, assertmsg
else:
logger.info("Check Ping IPv4 from R1 to R2 OK")
def test_route_install(): def test_route_install():
"Test use of NHRP routes by other protocols (sharpd here)." "Test use of NHRP routes by other protocols (sharpd here)."