Skip to content

Commit

Permalink
nhrp: add cisco-authentication password support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Volodymyr Huti committed Nov 22, 2023
1 parent 04a587f commit 282a6e5
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 31 deletions.
6 changes: 6 additions & 0 deletions doc/user/nhrpd.rst
Expand Up @@ -84,6 +84,12 @@ Configuring NHRP
registration requests are sent. By default registrations are sent every one
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

Map an IP address of a station to the station's NBMA address.
Expand Down
4 changes: 2 additions & 2 deletions nhrpd/nhrp_nhs.c
Expand Up @@ -210,7 +210,7 @@ static void nhrp_reg_send_req(struct event *t)
cie->holding_time = htons(if_ad->holdtime);
cie->mtu = htons(if_ad->mtu);

nhrp_ext_request(zb, hdr, ifp);
nhrp_ext_request(zb, hdr);

/* Cisco NAT detection extension */
if (sockunion_family(&r->proto_addr) != AF_UNSPEC) {
Expand All @@ -234,7 +234,7 @@ static void nhrp_reg_send_req(struct event *t)
cie->mtu = htons(if_ad->mtu);
nhrp_ext_complete(zb, ext);

nhrp_packet_complete(zb, hdr);
nhrp_packet_complete(zb, hdr, ifp);
nhrp_peer_send(r->peer, zb);
zbuf_free(zb);
}
Expand Down
27 changes: 22 additions & 5 deletions nhrpd/nhrp_packet.c
Expand Up @@ -115,14 +115,32 @@ uint16_t nhrp_packet_calculate_checksum(const uint8_t *pdu, uint16_t len)
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;

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)
nhrp_ext_push(zb, hdr,
NHRP_EXTENSION_END
| NHRP_EXTENSION_FLAG_COMPULSORY);
NHRP_EXTENSION_END |
NHRP_EXTENSION_FLAG_COMPULSORY);

size = zb->tail - (uint8_t *)hdr;
hdr->packet_size = htons(size);
Expand Down Expand Up @@ -225,8 +243,7 @@ struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb,
return ext;
}

void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr,
struct interface *ifp)
void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr)
{
/* Place holders for standard extensions */
nhrp_ext_push(zb, hdr,
Expand Down
97 changes: 83 additions & 14 deletions nhrpd/nhrp_peer.c
Expand Up @@ -603,7 +603,7 @@ static void nhrp_handle_resolution_req(struct nhrp_packet_parser *pp)
break;
}
}
nhrp_packet_complete(zb, hdr);
nhrp_packet_complete(zb, hdr, ifp);
nhrp_peer_send(peer, zb);
err:
nhrp_peer_unref(peer);
Expand Down Expand Up @@ -730,7 +730,7 @@ static void nhrp_handle_registration_request(struct nhrp_packet_parser *p)
}
}

nhrp_packet_complete(zb, hdr);
nhrp_packet_complete(zb, hdr, ifp);
nhrp_peer_send(p->peer, zb);
err:
zbuf_free(zb);
Expand Down Expand Up @@ -812,7 +812,7 @@ void nhrp_peer_send_indication(struct interface *ifp, uint16_t protocol_type,

/* Payload is the packet causing indication */
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_unref(p);
zbuf_free(zb);
Expand Down Expand Up @@ -1063,7 +1063,8 @@ static void nhrp_peer_forward(struct nhrp_peer *p,
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);
zbuf_free(zb);
zbuf_free(zb_copy);
Expand All @@ -1089,8 +1090,7 @@ static void nhrp_packet_debug(struct zbuf *zb, const char *dir)

reply = packet_types[hdr->type].type == PACKET_REPLY;
debugf(NHRP_DEBUG_COMMON, "%s %s(%d) %pSU -> %pSU", dir,
(packet_types[hdr->type].name ? packet_types[hdr->type].name
: "Unknown"),
(packet_types[hdr->type].name ? : "Unknown"),
hdr->type, reply ? &dst_proto : &src_proto,
reply ? &src_proto : &dst_proto);
}
Expand All @@ -1106,11 +1106,70 @@ static int proto2afi(uint16_t proto)
return AF_UNSPEC;
}

struct nhrp_route_info {
int local;
struct interface *ifp;
struct nhrp_vc *vc;
};
static int nhrp_packet_send_error(struct nhrp_packet_parser *pp,
uint16_t indication_code, uint16_t offset)
{
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)
{
Expand Down Expand Up @@ -1191,10 +1250,20 @@ void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb)
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");

/* FIXME: Check authentication here. This extension needs to be
* pre-handled. */
if (nifp->auth_token &&
(hdr->type != NHRP_PACKET_ERROR_INDICATION ||
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 */
target_addr = (packet_types[hdr->type].type == PACKET_REPLY)
Expand Down
4 changes: 2 additions & 2 deletions nhrpd/nhrp_shortcut.c
Expand Up @@ -423,7 +423,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",
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 */
hdr->flags |= htons(NHRP_FLAG_RESOLUTION_NAT);
Expand All @@ -436,7 +436,7 @@ static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s)
nhrp_ext_complete(zb, ext);
}

nhrp_packet_complete(zb, hdr);
nhrp_packet_complete(zb, hdr, ifp);

nhrp_peer_send(peer, zb);
nhrp_peer_unref(peer);
Expand Down
58 changes: 58 additions & 0 deletions nhrpd/nhrp_vty.c
Expand Up @@ -12,6 +12,9 @@

#include "nhrpd.h"
#include "netlink.h"
#include "nhrp_protocol.h"

#include "nhrpd/nhrp_vty_clippy.c"

static int nhrp_config_write(struct vty *vty);
static struct cmd_node zebra_node = {
Expand Down Expand Up @@ -459,6 +462,51 @@ DEFUN(if_no_nhrp_holdtime, if_no_nhrp_holdtime_cmd,
return CMD_SUCCESS;
}

#define NHRP_CISCO_PASS_LEN 8
DEFPY(if_nhrp_authentication, if_nhrp_authentication_cmd,
"nhrp authentication PASSWORD$password",
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 nhrp authentication PASSWORD$password",
NO_STR NHRP_STR "Reset password used for authentication \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,
"ip nhrp mtu <(576-1500)|opennhrp>",
IP_STR
Expand Down Expand Up @@ -1053,6 +1101,7 @@ DEFUN(show_dmvpn, show_dmvpn_cmd,
static void clear_nhrp_cache(struct nhrp_cache *c, void *data)
{
struct info_ctx *ctx = data;

if (c->cur.type <= NHRP_CACHE_DYNAMIC) {
nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL,
NULL);
Expand Down Expand Up @@ -1129,6 +1178,7 @@ static void interface_config_write_nhrp_map(struct nhrp_cache_config *c,
static int interface_config_write(struct vty *vty)
{
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
struct nhrp_cisco_authentication_extension *auth;
struct write_map_ctx mapctx;
struct interface *ifp;
struct nhrp_interface *nifp;
Expand All @@ -1155,6 +1205,12 @@ static int interface_config_write(struct vty *vty)
if (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, " password %s\n", auth->secret);
}

for (afi = 0; afi < AFI_MAX; afi++) {
struct nhrp_afi_data *ad = &nifp->afi[afi];

Expand Down Expand Up @@ -1256,6 +1312,8 @@ void nhrp_config_init(void)
install_element(INTERFACE_NODE, &if_no_nhrp_network_id_cmd);
install_element(INTERFACE_NODE, &if_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_no_nhrp_mtu_cmd);
install_element(INTERFACE_NODE, &if_nhrp_flags_cmd);
Expand Down
10 changes: 7 additions & 3 deletions nhrpd/nhrpd.h
Expand Up @@ -312,6 +312,7 @@ DECLARE_DLIST(nhrp_reglist, struct nhrp_registration, reglist_entry);
struct nhrp_interface {
struct interface *ifp;

struct zbuf *auth_token;
unsigned enabled : 1;

char *ipsec_profile, *ipsec_fallback_profile, *source;
Expand Down Expand Up @@ -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_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);

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,
union sockunion *src_nbma,
union sockunion *src_proto,
Expand All @@ -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);
struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb,
struct zbuf *payload);
void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr,
struct interface *);
void nhrp_ext_request(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 zbuf *extpayload);
Expand Down
4 changes: 4 additions & 0 deletions nhrpd/subdir.am
Expand Up @@ -42,3 +42,7 @@ noinst_HEADERS += \
nhrpd/zbuf.h \
nhrpd/znl.h \
# end

clippy_scan += \
nhrpd/nhrp_vty.c \
# end
1 change: 1 addition & 0 deletions tests/topotests/nhrp_topo/r1/nhrpd.conf
@@ -1,6 +1,7 @@
log stdout debugging
! debug nhrp all
interface r1-gre0
nhrp authentication secret
ip nhrp holdtime 500
ip nhrp shortcut
ip nhrp network-id 42
Expand Down
1 change: 1 addition & 0 deletions tests/topotests/nhrp_topo/r2/nhrpd.conf
Expand Up @@ -2,6 +2,7 @@
log stdout debugging
nhrp nflog-group 1
interface r2-gre0
nhrp authentication secret
ip nhrp holdtime 500
ip nhrp redirect
ip nhrp network-id 42
Expand Down

0 comments on commit 282a6e5

Please sign in to comment.