nhrpd: add cisco-authentication password support

Taking over this development from https://github.com/FRRouting/frr/pull/14788

This commit addresses 4 issues found in the previous PR

1) FRR would accept messages from a spoke without authentication when FRR NHRP had auth configured.
2) The error indication was not being sent in network byte order
3) The debug print in nhrp_connection_authorized was not correctly printing the received password
4) The addresses portion of the mandatory part of the error indication was invalid on the wire (confirmed in wireshark)

Signed-off-by: Dave LeRoy <dleroy@labn.net>
Co-authored-by: Volodymyr Huti <volodymyr.huti@gmail.com>
This commit is contained in:
Dave LeRoy 2024-06-05 12:10:11 -07:00
parent 51f0700286
commit b5540d326b
7 changed files with 51 additions and 43 deletions

View file

@ -88,7 +88,7 @@ Configuring NHRP
Enables Cisco style authentication on NHRP packets. This embeds the Enables Cisco style authentication on NHRP packets. This embeds the
plaintext password to the outgoing NHRP packets. plaintext password to the outgoing NHRP packets.
Maximum length of the is 8 characters. Maximum length of the password 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

View file

@ -99,6 +99,8 @@ static int nhrp_if_delete_hook(struct interface *ifp)
free(nifp->ipsec_fallback_profile); free(nifp->ipsec_fallback_profile);
if (nifp->source) if (nifp->source)
free(nifp->source); free(nifp->source);
if (nifp->auth_token)
zbuf_free(nifp->auth_token);
XFREE(MTYPE_NHRP_IF, ifp->info); XFREE(MTYPE_NHRP_IF, ifp->info);
return 0; return 0;

View file

@ -730,7 +730,7 @@ static void nhrp_handle_registration_request(struct nhrp_packet_parser *p)
} }
} }
// auth ext was validated and copied from the request /* auth ext was validated and copied from the request */
nhrp_packet_complete_auth(zb, hdr, ifp, false); nhrp_packet_complete_auth(zb, hdr, ifp, false);
nhrp_peer_send(p->peer, zb); nhrp_peer_send(p->peer, zb);
err: err:
@ -1064,7 +1064,6 @@ static void nhrp_peer_forward(struct nhrp_peer *p,
nhrp_ext_complete(zb, dst); nhrp_ext_complete(zb, dst);
} }
// XXX: auth already handled ???
nhrp_packet_complete_auth(zb, hdr, pp->ifp, false); 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);
@ -1121,24 +1120,26 @@ static int nhrp_packet_send_error(struct nhrp_packet_parser *pp,
dst_proto = pp->src_proto; dst_proto = pp->src_proto;
} }
/* Create reply */ /* Create reply */
zb = zbuf_alloc(1500); // XXX: hardcode -> calculation routine zb = zbuf_alloc(1500);
hdr = nhrp_packet_push(zb, NHRP_PACKET_ERROR_INDICATION, &pp->src_nbma, hdr = nhrp_packet_push(zb, NHRP_PACKET_ERROR_INDICATION, &pp->src_nbma,
&src_proto, &dst_proto); &src_proto, &dst_proto);
hdr->u.error.code = indication_code; hdr->u.error.code = htons(indication_code);
hdr->u.error.offset = htons(offset); hdr->u.error.offset = htons(offset);
hdr->flags = pp->hdr->flags; hdr->flags = pp->hdr->flags;
hdr->hop_count = 0; // XXX: cisco returns 255 hdr->hop_count = 0; /* XXX: cisco returns 255 */
/* Payload is the packet causing error */ /* Payload is the packet causing error */
/* Don`t add extension according to RFC */ /* 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_put(zb, pp->hdr, sizeof(*pp->hdr));
zbuf_copy(zb, &pp->payload, zbuf_used(&pp->payload)); zbuf_put(zb, sockunion_get_addr(&pp->src_nbma),
hdr->src_nbma_address_len);
zbuf_put(zb, sockunion_get_addr(&pp->src_proto),
hdr->src_protocol_address_len);
zbuf_put(zb, sockunion_get_addr(&pp->dst_proto),
hdr->dst_protocol_address_len);
nhrp_packet_complete_auth(zb, hdr, pp->ifp, false); nhrp_packet_complete_auth(zb, hdr, pp->ifp, false);
/* nhrp_packet_debug(zb, "SEND_ERROR"); */
nhrp_peer_send(pp->peer, zb); nhrp_peer_send(pp->peer, zb);
zbuf_free(zb); zbuf_free(zb);
return 0; return 0;
@ -1151,8 +1152,7 @@ static bool nhrp_connection_authorized(struct nhrp_packet_parser *pp)
struct zbuf *auth = nifp->auth_token; struct zbuf *auth = nifp->auth_token;
struct nhrp_extension_header *ext; struct nhrp_extension_header *ext;
struct zbuf *extensions, pl; struct zbuf *extensions, pl;
int cmp = 0; int cmp = 1;
extensions = zbuf_alloc(zbuf_used(&pp->extensions)); extensions = zbuf_alloc(zbuf_used(&pp->extensions));
zbuf_copy_peek(extensions, &pp->extensions, zbuf_used(&pp->extensions)); zbuf_copy_peek(extensions, &pp->extensions, zbuf_used(&pp->extensions));
@ -1164,7 +1164,14 @@ static bool nhrp_connection_authorized(struct nhrp_packet_parser *pp)
auth->buf; auth->buf;
debugf(NHRP_DEBUG_COMMON, debugf(NHRP_DEBUG_COMMON,
"Processing Authentication Extension for (%s:%s|%d)", "Processing Authentication Extension for (%s:%s|%d)",
auth_ext->secret, (const char *)pl.buf, cmp); auth_ext->secret,
((struct nhrp_cisco_authentication_extension *)
pl.buf)
->secret,
cmp);
break;
default:
/* Ignoring all received extensions except Authentication*/
break; break;
} }
} }

View file

@ -467,7 +467,7 @@ DEFPY(if_nhrp_authentication, if_nhrp_authentication_cmd,
AFI_CMD "nhrp authentication PASSWORD$password", AFI_CMD "nhrp authentication PASSWORD$password",
AFI_STR AFI_STR
NHRP_STR NHRP_STR
"Specify plaint text password used for authenticantion\n" "Specify plain text password used for authenticantion\n"
"Password, plain text, limited to 8 characters\n") "Password, plain text, limited to 8 characters\n")
{ {
VTY_DECLVAR_CONTEXT(interface, ifp); VTY_DECLVAR_CONTEXT(interface, ifp);
@ -490,8 +490,6 @@ DEFPY(if_nhrp_authentication, if_nhrp_authentication_cmd,
auth->type = htonl(NHRP_AUTHENTICATION_PLAINTEXT); auth->type = htonl(NHRP_AUTHENTICATION_PLAINTEXT);
memcpy(auth->secret, password, pass_len); 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; return CMD_SUCCESS;
} }
@ -501,11 +499,12 @@ DEFPY(if_no_nhrp_authentication, if_no_nhrp_authentication_cmd,
NO_STR NO_STR
AFI_STR AFI_STR
NHRP_STR NHRP_STR
"Reset password used for authentication\n" "Specify plain text password used for authenticantion\n"
"Password, plain text\n") "Password, plain text, limited to 8 characters\n")
{ {
VTY_DECLVAR_CONTEXT(interface, ifp); VTY_DECLVAR_CONTEXT(interface, ifp);
struct nhrp_interface *nifp = ifp->info; struct nhrp_interface *nifp = ifp->info;
if (nifp->auth_token) if (nifp->auth_token)
zbuf_free(nifp->auth_token); zbuf_free(nifp->auth_token);
return CMD_SUCCESS; return CMD_SUCCESS;

View file

@ -2,7 +2,7 @@ log stdout debugging
! debug nhrp all ! debug nhrp all
interface r1-gre0 interface r1-gre0
ip nhrp authentication secret ip nhrp authentication secret
ip nhrp holdtime 500 ip nhrp holdtime 10
ip nhrp shortcut ip nhrp shortcut
ip nhrp network-id 42 ip nhrp network-id 42
ip nhrp nhs dynamic nbma 10.2.1.2 ip nhrp nhs dynamic nbma 10.2.1.2

View file

@ -3,7 +3,7 @@ log stdout debugging
nhrp nflog-group 1 nhrp nflog-group 1
interface r2-gre0 interface r2-gre0
ip nhrp authentication secret ip nhrp authentication secret
ip nhrp holdtime 500 ip nhrp holdtime 10
ip nhrp redirect ip nhrp redirect
ip nhrp network-id 42 ip nhrp network-id 42
ip nhrp registration no-unique ip nhrp registration no-unique

View file

@ -28,7 +28,7 @@ sys.path.append(os.path.join(CWD, "../"))
from lib import topotest from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger from lib.topolog import logger
from lib.common_config import required_linux_kernel_version from lib.common_config import required_linux_kernel_version, retry
# Required to instantiate the topology builder class. # Required to instantiate the topology builder class.
@ -231,14 +231,27 @@ def test_nhrp_connection():
tgen.net[r].cmd("ip l del {}-gre0".format(r)); tgen.net[r].cmd("ip l del {}-gre0".format(r));
_populate_iface(); _populate_iface();
@retry(retry_timeout=40, initial_wait=5)
def verify_same_password():
output = ping_helper()
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")
@retry(retry_timeout=40, initial_wait=5)
def verify_mismatched_password():
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 ### Passwords are the same
logger.info("Check Ping IPv4 from R1 to R2 = 10.255.255.2") logger.info("Check Ping IPv4 from R1 to R2 = 10.255.255.2")
output = ping_helper() verify_same_password()
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")
### Passwords are different ### Passwords are different
logger.info("Modify password and send ping again, should drop") logger.info("Modify password and send ping again, should drop")
@ -248,14 +261,8 @@ def test_nhrp_connection():
ip nhrp authentication secret12 ip nhrp authentication secret12
""") """)
relink_session() relink_session()
topotest.sleep(10, "Waiting for session to initialize") verify_mismatched_password()
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 ### Passwords are the same - again
logger.info("Recover password and verify conectivity is back") logger.info("Recover password and verify conectivity is back")
hubrouter.vtysh_cmd(""" hubrouter.vtysh_cmd("""
@ -264,14 +271,7 @@ def test_nhrp_connection():
ip nhrp authentication secret ip nhrp authentication secret
""") """)
relink_session() relink_session()
topotest.sleep(10, "Waiting for session to initialize") verify_same_password()
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)."