]> git.puffer.fish Git - matthieu/frr.git/commitdiff
nhrp: add `cisco-authentication` password support
authorVolodymyr Huti <v.huti@vyos.io>
Mon, 13 Nov 2023 20:47:31 +0000 (22:47 +0200)
committerDave LeRoy <dleroy@labn.net>
Mon, 10 Jun 2024 23:39:21 +0000 (16:39 -0700)
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>
doc/user/nhrpd.rst
nhrpd/nhrp_nhs.c
nhrpd/nhrp_packet.c
nhrpd/nhrp_peer.c
nhrpd/nhrp_shortcut.c
nhrpd/nhrp_vty.c
nhrpd/nhrpd.h
nhrpd/subdir.am
tests/topotests/nhrp_topo/r1/nhrpd.conf
tests/topotests/nhrp_topo/r2/nhrpd.conf
tests/topotests/nhrp_topo/test_nhrp_topo.py

index 54527a0c9a85203ddcf59210eca6a5eb6f3b392d..e0ba90fcc1395471d03c572f24470b869e0755d7 100644 (file)
@@ -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.
index f779f93486d821d6c7ad84249970306f3185d65c..b8958ba22595e07e081392cc95c7244271f36265 100644 (file)
@@ -216,7 +216,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) {
@@ -240,7 +240,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);
 }
index c6bd3bbbde0d2888bcc83611b4b2762f9fd0364d..71f5c2f007976941906abbc909871a337a76aad7 100644 (file)
@@ -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);
@@ -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,
index 6e7857c777a033e250a6ccaf0c587bbf9030fd0b..84fcdb06974470c4737f0742a2814f725e938389 100644 (file)
@@ -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);
@@ -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);
 err:
        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 */
        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);
@@ -1063,7 +1064,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);
@@ -1089,8 +1091,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);
 }
@@ -1106,11 +1107,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)
 {
@@ -1191,10 +1251,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)
index e83ce7f58f472527feecae9f9f4d18bf3c0e2ed6..9b47d974c3403b61c420b14b7a0c7bc6f509a8e2 100644 (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",
               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);
@@ -438,7 +438,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);
index 40d38c44d272d1a89a47b61974ce5e77d5c081f8..66659bdcdb920b869f7452943d1b92602c8c8ccc 100644 (file)
@@ -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 = {
@@ -459,6 +462,56 @@ 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,
+      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
@@ -1053,6 +1106,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);
@@ -1129,6 +1183,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;
@@ -1155,6 +1210,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, " ip nhrp authentication %s\n", auth->secret);
+               }
+
                for (afi = 0; afi < AFI_MAX; 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_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);
index e389b7489d3618a67d62dcee6e014037c88afa77..344e1d51780ffe20cdb7e7680df8218c8068c1f3 100644 (file)
@@ -311,6 +311,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;
@@ -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,
@@ -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);
index 227ff6c6787ddcb1e95833347b8fa739552a3e21..94fb3bb5bebe2442188f2aa6da8576d60191f08e 100644 (file)
@@ -42,3 +42,7 @@ noinst_HEADERS += \
        nhrpd/zbuf.h \
        nhrpd/znl.h \
        # end
+
+clippy_scan += \
+       nhrpd/nhrp_vty.c \
+       # end
index e5224e4aabe960780389ec5fad528256446f3b02..027312dcd53f9232127bc683b2854383284dc98d 100644 (file)
@@ -1,6 +1,7 @@
 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
index f9185f9a6398b4f16dfbbfca2d8497decfe7572d..621db6abc8382add985e4ddb7b6c1ed2d8fd0c5e 100644 (file)
@@ -2,6 +2,7 @@
 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
index 284c58a8e781c18b7efd98dbb76b145130495889..c57d28b709bc3f1e494914592cda253886ead02f 100644 (file)
@@ -214,19 +214,64 @@ def test_protocols_convergence():
 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)."