]> git.puffer.fish Git - matthieu/frr.git/commitdiff
lib: mgmtd: add xpath arg to notification message
authorChristian Hopps <chopps@gmail.com>
Wed, 14 Feb 2024 15:04:11 +0000 (10:04 -0500)
committerMergify <37929162+mergify[bot]@users.noreply.github.com>
Tue, 20 Feb 2024 17:14:55 +0000 (17:14 +0000)
Signed-off-by: Christian Hopps <chopps@labn.net>
(cherry picked from commit 4a93d171c2e3ec1ff6c4fc553d6acf42e035e0d4)

lib/mgmt_be_client.c
lib/mgmt_fe_client.c
lib/mgmt_msg_native.h
lib/yang.c
lib/yang.h
mgmtd/mgmt_be_adapter.c
tests/topotests/lib/fe_client.py

index 589bf82c95c6193ef05a8c97ce69071bf7c392d1..5896db1e58592f06131532e145cee6c4c3fc5ffa 100644 (file)
@@ -333,6 +333,8 @@ static int mgmt_be_send_notification(void *__be_client, const char *xpath,
        msg->code = MGMT_MSG_CODE_NOTIFY;
        msg->result_type = format;
 
+       mgmt_msg_native_xpath_encode(msg, xpath);
+
        darrp = mgmt_msg_native_get_darrp(msg);
        err = yang_print_tree_append(darrp, tree, format,
                                     (LYD_PRINT_SHRINK | LYD_PRINT_WD_EXPLICIT |
@@ -921,27 +923,40 @@ static void be_client_handle_notify(struct mgmt_be_client *client, void *msgbuf,
 {
        struct mgmt_msg_notify_data *notif_msg = msgbuf;
        struct nb_node *nb_node;
-       char notif[XPATH_MAXLEN];
        struct lyd_node *dnode;
+       const char *data;
+       const char *notif;
        LY_ERR err;
 
        debug_be_client("Received notification for client %s", client->name);
 
-       err = yang_parse_notification(notif_msg->result_type,
-                                     (char *)notif_msg->result, &dnode);
-       if (err)
+       notif = mgmt_msg_native_xpath_data_decode(notif_msg, msg_len, data);
+       if (!notif || !data) {
+               log_err_be_client("Corrupt notify msg");
                return;
-
-       lysc_path(dnode->schema, LYSC_PATH_DATA, notif, sizeof(notif));
+       }
 
        nb_node = nb_node_find(notif);
-       if (!nb_node || !nb_node->cbs.notify) {
-               debug_be_client("No notification callback for %s", notif);
-               goto cleanup;
+       if (!nb_node) {
+               log_err_be_client("No schema found for notification: %s", notif);
+               return;
+       }
+
+       if (!nb_node->cbs.notify) {
+               debug_be_client("No notification callback for: %s", notif);
+               return;
+       }
+
+       err = yang_parse_notification(notif, notif_msg->result_type, data,
+                                     &dnode);
+       if (err) {
+               log_err_be_client("Can't parse notification data for: %s",
+                                 notif);
+               return;
        }
 
        nb_callback_notify(nb_node, notif, dnode);
-cleanup:
+
        lyd_free_all(dnode);
 }
 
index 9f98a241fe462a219b65d252b2e7e698dc6649dc..bfdecedc4e1e7509aadf5083fa1c1e8cd6dab7f0 100644 (file)
@@ -504,7 +504,8 @@ static void fe_client_handle_native_msg(struct mgmt_fe_client *client,
        struct mgmt_msg_notify_data *notify_msg;
        struct mgmt_msg_tree_data *tree_msg;
        struct mgmt_msg_error *err_msg;
-       char *notify_data = NULL;
+       const char *data = NULL;
+       size_t dlen;
 
        debug_fe_client("Got native message for session-id %" PRIu64,
                        msg->refer_id);
@@ -563,20 +564,17 @@ static void fe_client_handle_native_msg(struct mgmt_fe_client *client,
                        return;
                }
 
-               if (notify_msg->result_type != LYD_LYB &&
-                   !MGMT_MSG_VALIDATE_NUL_TERM(notify_msg, msg_len)) {
+               data = mgmt_msg_native_data_decode(notify_msg, msg_len);
+               if (!data) {
                        log_err_fe_client("Corrupt error msg recv");
                        return;
                }
-               if (notify_msg->result_type == LYD_JSON)
-                       notify_data = (char *)notify_msg->result;
-               else
-                       notify_data =
-                               yang_convert_lyd_format(notify_msg->result,
-                                                       msg_len,
-                                                       notify_msg->result_type,
-                                                       LYD_JSON, true);
-               if (!notify_data) {
+               dlen = mgmt_msg_native_data_len_decode(notify_msg, msg_len);
+               if (notify_msg->result_type != LYD_JSON)
+                       data = yang_convert_lyd_format(data, dlen,
+                                                      notify_msg->result_type,
+                                                      LYD_JSON, true);
+               if (!data) {
                        log_err_fe_client("Can't convert format %d to JSON",
                                          notify_msg->result_type);
                        return;
@@ -588,11 +586,10 @@ static void fe_client_handle_native_msg(struct mgmt_fe_client *client,
                        session->client->cbs
                                .async_notification(client, client->user_data,
                                                    session->client_id,
-                                                   session->user_ctx,
-                                                   notify_data);
+                                                   session->user_ctx, data);
                }
                if (notify_msg->result_type != LYD_JSON)
-                       darr_free(notify_data);
+                       darr_free(data);
                break;
        default:
                log_err_fe_client("unknown native message session-id %" PRIu64
index 7273170a132cafb161ab2bff68d8e9a3460d72ca..53bb81be283250d8111fd2652584e6d83c0e732d 100644 (file)
@@ -77,6 +77,11 @@ extern "C" {
  * mgmt_msg_native_get_msg_len() - Get the total length of the msg.
  * mgmt_msg_native_send_msg() - Send the message.
  *
+ * mgmt_msg_native_xpath_encode() - Encode xpath in xpath, data format message.
+ * mgmt_msg_native_xpath_data_decode() - Decode xpath, data format message.
+ * mgmt_msg_native_xpath_decode() - Get the xpath, from xpath, data format message.
+ * mgmt_msg_native_data_decode() - Get the secondary data from xpath, data message.
+ * mgmt_msg_native_data_len_decode() - Get length of secondary data.
  *
  * -------------------------------------
  * [Advanced Use] Dynamic Array Messages
@@ -299,18 +304,18 @@ _Static_assert(sizeof(struct mgmt_msg_get_data) ==
  * struct mgmt_msg_notify_data - Message carrying notification data.
  *
  * @result_type: ``LYD_FORMAT`` for format of the @result value.
- * @result: The tree data in @result_type format.
- *
+ * @data: The xpath string of the notification followed by the tree data in
+ *        @result_type format.
  */
 struct mgmt_msg_notify_data {
        struct mgmt_msg_header;
        uint8_t result_type;
        uint8_t resv2[7];
 
-       alignas(8) uint8_t result[];
+       alignas(8) char data[];
 };
 _Static_assert(sizeof(struct mgmt_msg_notify_data) ==
-                      offsetof(struct mgmt_msg_notify_data, result),
+                      offsetof(struct mgmt_msg_notify_data, data),
               "Size mismatch");
 
 /*
@@ -404,7 +409,12 @@ extern int vmgmt_msg_native_send_error(struct msg_conn *conn,
  * Return: a pointer to the newly appended data.
  */
 #define mgmt_msg_native_append(msg, data, len)                                 \
-       memcpy(darr_append(*mgmt_msg_native_get_darrp(msg), len), data, len)
+       ({                                                                     \
+               uint8_t **darrp = mgmt_msg_native_get_darrp(msg);              \
+               uint8_t *p = darr_append_n(*darrp, len);                       \
+               memcpy(p, data, len);                                          \
+               p;                                                             \
+       })
 
 /**
  * mgmt_msg_native_send_msg(msg, short_circuit_ok) - Send a native msg.
@@ -458,6 +468,116 @@ extern int vmgmt_msg_native_send_error(struct msg_conn *conn,
  */
 #define mgmt_msg_native_get_darrp(msg) ((uint8_t **)&(msg))
 
+/* ------------------------- */
+/* Encode and Decode Helpers */
+/* ------------------------- */
+
+/**
+ * mgmt_msg_native_xpath_encode() - encode an xpath in a xpath, data message.
+ * @msg: Pointer to the native message.
+ * @xpath: The xpath string to encode.
+ *
+ * This function starts the encoding of a message that can be decoded with
+ * `mgmt_msg_native_xpath_data_decode()`. The variable length data is comprised
+ * of a NUL terminated string followed by some data of any format. This starts
+ * the first half of the encoding, after which one can simply append the
+ * secondary data to the message.
+ */
+#define mgmt_msg_native_xpath_encode(msg, xpath)                               \
+       do {                                                                   \
+               size_t __slen = strlen(xpath) + 1;                             \
+               mgmt_msg_native_append(msg, xpath, __slen);                    \
+               (msg)->vsplit = __slen;                                        \
+       } while (0)
+
+/**
+ * mgmt_msg_native_xpath_data_decode() - decode an xpath, data format message.
+ * @msg: Pointer to the native message.
+ * @msglen: Length of the message.
+ * @data: [OUT] Pointer to the data section of the variable data
+ *
+ * This function decodes a message that was encoded with
+ * `mgmt_msg_native_xpath_encode()`. The variable length data is comprised of a
+ * NUL terminated string followed by some data of any format.
+ *
+ * Return:
+ *     The xpath string or NULL if there was an error decoding (i.e., the
+ *     message is corrupt).
+ */
+#define mgmt_msg_native_xpath_data_decode(msg, msglen, data)                   \
+       ({                                                                     \
+               size_t __len = (msglen) - sizeof(*msg);                        \
+               const char *__s = NULL;                                        \
+               if (msg->vsplit && msg->vsplit <= __len &&                     \
+                   msg->data[msg->vsplit - 1] == 0) {                         \
+                       (data) = msg->data + msg->vsplit;                      \
+                       __s = msg->data;                                       \
+               }                                                              \
+               __s;                                                           \
+       })
+
+/**
+ * mgmt_msg_native_xpath_decode() - return the xpath from xpath, data message.
+ * @msg: Pointer to the native message.
+ * @msglen: Length of the message.
+ *
+ * This function decodes the xpath from a message that was encoded with
+ * `mgmt_msg_native_xpath_encode()`. The variable length data is comprised of a
+ * NUL terminated string followed by some data of any format.
+ *
+ * Return:
+ *     The xpath string or NULL if there was an error decoding (i.e., the
+ *     message is corrupt).
+ */
+#define mgmt_msg_native_xpath_decode(msg, msglen)                              \
+       ({                                                                     \
+               size_t __len = (msglen) - sizeof(*msg);                        \
+               const char *__s = msg->data;                                   \
+               if (!msg->vsplit || msg->vsplit > __len ||                     \
+                   __s[msg->vsplit - 1] != 0)                                 \
+                       __s = NULL;                                            \
+               __s;                                                           \
+       })
+
+/**
+ * mgmt_msg_native_data_decode() - return the data from xpath, data message.
+ * @msg: Pointer to the native message.
+ * @msglen: Length of the message.
+ *
+ * This function decodes the secondary data from a message that was encoded with
+ * `mgmt_msg_native_xpath_encode()`. The variable length data is comprised of a
+ * NUL terminated string followed by some data of any format.
+ *
+ * Return:
+ *     The secondary data or NULL if there was an error decoding (i.e., the
+ *     message is corrupt).
+ */
+#define mgmt_msg_native_data_decode(msg, msglen)                               \
+       ({                                                                     \
+               size_t __len = (msglen) - sizeof(*msg);                        \
+               const char *__data = msg->data + msg->vsplit;                  \
+               if (!msg->vsplit || msg->vsplit > __len || __data[-1] != 0)    \
+                       __data = NULL;                                         \
+               __data;                                                        \
+       })
+
+/**
+ * mgmt_msg_native_data_len_decode() - len of data in xpath, data format message.
+ * @msg: Pointer to the native message.
+ * @msglen: Length of the message.
+ *
+ * This function returns the length of the secondary variable data from a
+ * message that was encoded with `mgmt_msg_native_xpath_encode()`. The variable
+ * length data is comprised of a NUL terminated string followed by some data of
+ * any format.
+ *
+ * Return:
+ *     The length of the secondary variable data. The message is assumed to be
+ *     validated as not corrupt already.
+ */
+#define mgmt_msg_native_data_len_decode(msg, msglen)                           \
+       ((msglen) - sizeof(*msg) - msg->vsplit)
+
 #ifdef __cplusplus
 }
 #endif
index 3ce24a318d43c78b172e345042626f0f65308ffe..03044fc29e9b4771c928002b2db6105a75daef30 100644 (file)
@@ -714,12 +714,12 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path)
                zlog(priority, "libyang: %s", msg);
 }
 
-LY_ERR yang_parse_notification(LYD_FORMAT format, const char *data,
-                              struct lyd_node **notif)
+LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format,
+                              const char *data, struct lyd_node **notif)
 {
-       struct lyd_node *tree, *dnode;
+       struct lyd_node *tree;
+       struct ly_set *set = NULL;
        struct ly_in *in = NULL;
-       bool found = false;
        LY_ERR err;
 
        err = ly_in_new_memory(data, &in);
@@ -736,26 +736,20 @@ LY_ERR yang_parse_notification(LYD_FORMAT format, const char *data,
                return err;
        }
 
-       /*
-        * Notification can be a child of some data node, so traverse the tree
-        * until we find the notification.
-        */
-       LYD_TREE_DFS_BEGIN (tree, dnode) {
-               if (dnode->schema->nodetype == LYS_NOTIF) {
-                       found = true;
-                       break;
-               }
-               LYD_TREE_DFS_END(tree, dnode);
+       err = lyd_find_xpath3(NULL, tree, xpath, NULL, &set);
+       if (err) {
+               zlog_err("Failed to parse notification: %s", ly_last_errmsg());
+               lyd_free_all(tree);
+               return err;
        }
-
-       if (!found) {
-               zlog_err("Notification not found in the parsed tree");
+       if (set->count == 0) {
+               zlog_err("Notification not found in the parsed tree: %s", xpath);
+               ly_set_free(set, NULL);
                lyd_free_all(tree);
                return LY_ENOTFOUND;
        }
-
-       *notif = dnode;
-
+       *notif = set->dnodes[0];
+       ly_set_free(set, NULL);
        return LY_SUCCESS;
 }
 
@@ -790,9 +784,9 @@ uint8_t *yang_print_tree(const struct lyd_node *root, LYD_FORMAT format,
        return darr;
 }
 
-char *yang_convert_lyd_format(const uint8_t *data, size_t data_len,
-                                    LYD_FORMAT in_format,
-                                    LYD_FORMAT out_format, bool shrink)
+char *yang_convert_lyd_format(const char *data, size_t data_len,
+                             LYD_FORMAT in_format, LYD_FORMAT out_format,
+                             bool shrink)
 {
        struct lyd_node *tree = NULL;
        uint32_t options = LYD_PRINT_WD_EXPLICIT | LYD_PRINT_WITHSIBLINGS;
index 9c221445cd2ec893f11b73765a8a4a223ef06acd..65f6a73e0b503f87c4e767690521943ccca4dc39 100644 (file)
@@ -611,6 +611,7 @@ extern void yang_debugging_set(bool enable);
  * Parse a YANG notification.
  *
  * Args:
+ *     xpath: xpath of notification.
  *     format: LYD_FORMAT of input data.
  *     data: input data.
  *     notif: pointer to the libyang data tree to store the parsed notification.
@@ -618,8 +619,8 @@ extern void yang_debugging_set(bool enable);
  *            the pointer to the notification node is still returned, but it's
  *            part of the full data tree with all its parents.
  */
-extern LY_ERR yang_parse_notification(LYD_FORMAT format, const char *data,
-                                     struct lyd_node **notif);
+extern LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format,
+                                     const char *data, struct lyd_node **notif);
 
 /*
  * "Print" the yang tree in `root` into dynamic sized array.
@@ -647,7 +648,7 @@ extern uint8_t *yang_print_tree(const struct lyd_node *root, LYD_FORMAT format,
  * Return:
  *     A darr based string or NULL for error.
  */
-extern char *yang_convert_lyd_format(const uint8_t *data, size_t msg_len,
+extern char *yang_convert_lyd_format(const char *data, size_t msg_len,
                                     LYD_FORMAT in_format,
                                     LYD_FORMAT out_format, bool shrink);
 
index d85d87b4b68954b4437d0b910fa20cea731d01fd..b311bf4698781f47c272a2f018e8d981f680651c 100644 (file)
@@ -592,25 +592,29 @@ static void mgmt_be_adapter_send_notify(struct mgmt_msg_notify_data *msg,
 {
        struct mgmt_be_client_adapter *adapter;
        struct mgmt_be_xpath_map *map;
-       char notif[XPATH_MAXLEN];
-       struct lyd_node *dnode;
-       LY_ERR err;
-       uint id;
+       struct nb_node *nb_node;
+       const char *notif;
+       uint id, len;
 
        if (!darr_len(be_notif_xpath_map))
                return;
 
-       err = yang_parse_notification(msg->result_type, (char *)msg->result,
-                                     &dnode);
-       if (err)
+       notif = mgmt_msg_native_xpath_decode(msg, msglen);
+       if (!notif) {
+               __log_err("Corrupt notify msg");
                return;
+       }
 
-       lysc_path(dnode->schema, LYSC_PATH_DATA, notif, sizeof(notif));
-
-       lyd_free_all(dnode);
+       nb_node = nb_node_find(notif);
+       if (!nb_node) {
+               __log_err("No schema found for notification: %s", notif);
+               return;
+       }
 
        darr_foreach_p (be_notif_xpath_map, map) {
-               if (strncmp(map->xpath_prefix, notif, strlen(map->xpath_prefix)))
+               len = strlen(map->xpath_prefix);
+               if (strncmp(map->xpath_prefix, nb_node->xpath, len) &&
+                   strncmp(map->xpath_prefix, notif, len))
                        continue;
 
                FOREACH_BE_CLIENT_BITS (id, map->clients) {
index ec643bb0bf60f4c9db280285e090469a7146299b..07059ccf3a864bb3f7c8f8be1fdc39854b0a35a3 100755 (executable)
@@ -321,12 +321,18 @@ class Session:
         while True:
             logging.debug("Waiting for Notify Message")
             mhdr, mfixed, mdata = self.recv_native_msg()
-            assert mdata[-1] == 0
-            result = mdata[:-1].decode("utf-8")
             if mhdr[HDR_FIELD_CODE] == MSG_CODE_NOTIFY:
                 logging.debug("Received Notify Message: %s: %s", mfixed, mdata)
             else:
                 raise Exception(f"Received NON-NOTIFY Message: {mfixed}: {mdata}")
+
+            vsplit = mhdr[HDR_FIELD_VSPLIT]
+            assert mdata[vsplit - 1] == 0
+            xpath = mdata[: vsplit - 1].decode("utf-8")
+
+            assert mdata[-1] == 0
+            result = mdata[vsplit:-1].decode("utf-8")
+
             if not xpaths:
                 return result
             js = json.loads(result)