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.
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) {
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);
}
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);
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,
break;
}
}
- nhrp_packet_complete(zb, hdr);
+ nhrp_packet_complete(zb, hdr, ifp);
nhrp_peer_send(peer, zb);
err:
nhrp_peer_unref(peer);
}
}
- 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);
err:
zbuf_free(zb);
/* 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);
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);
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);
}
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)
{
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)
"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);
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);
#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 = {
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,
"ip nhrp mtu <(576-1500)|opennhrp>",
IP_STR
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);
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;
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, " ip nhrp authentication %s\n", auth->secret);
+ }
+
for (afi = 0; afi < AFI_MAX; afi++) {
struct nhrp_afi_data *ad = &nifp->afi[afi];
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);
struct nhrp_interface {
struct interface *ifp;
+ struct zbuf *auth_token;
unsigned enabled : 1;
char *ipsec_profile, *ipsec_fallback_profile, *source;
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,
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);
nhrpd/zbuf.h \
nhrpd/znl.h \
# end
+
+clippy_scan += \
+ nhrpd/nhrp_vty.c \
+ # end
log stdout debugging
! debug nhrp all
interface r1-gre0
+ ip nhrp authentication secret
ip nhrp holdtime 500
ip nhrp shortcut
ip nhrp network-id 42
log stdout debugging
nhrp nflog-group 1
interface r2-gre0
+ ip nhrp authentication secret
ip nhrp holdtime 500
ip nhrp redirect
ip nhrp network-id 42
def test_nhrp_connection():
"Assert that the NHRP peers can find themselves."
tgen = get_topogen()
+ pingrouter = tgen.gears["r1"]
+ hubrouter = tgen.gears["r2"]
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
- pingrouter = tgen.gears["r1"]
- logger.info("Check Ping IPv4 from R1 to R2 = 10.255.255.2)")
- output = pingrouter.run("ping 10.255.255.2 -f -c 1000")
- logger.info(output)
- if "1000 packets transmitted, 1000 received" not in output:
+ def ping_helper():
+ output = pingrouter.run("ping 10.255.255.2 -f -c 100")
+ logger.info(output)
+ return 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"
assert 0, assertmsg
else:
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():
"Test use of NHRP routes by other protocols (sharpd here)."