/** * @file yangdata.c * @author Pavol Vican * @brief libyang extension plugin - YANG DATA (RFC 8040) * * Copyright (c) 2018 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://opensource.org/licenses/BSD-3-Clause */ #ifdef __GNUC__ # define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) #else # define UNUSED(x) UNUSED_ ## x #endif #include #include "../extensions.h" int check_node(struct lys_node *node); /** * @brief Callback to check that the yang-data can be instantiated inside the provided node * * @param[in] parent The parent of the instantiated extension. * @param[in] parent_type The type of the structure provided as \p parent. * @param[in] substmt_type libyang does not store all the extension instances in the structures where they are * instantiated in the module. In some cases (see #LYEXT_SUBSTMT) they are stored in parent * structure and marked with flag to know in which substatement of the parent the extension * was originally instantiated. * @return 0 - ok * 1 - error */ int yang_data_position(const void * UNUSED(parent), LYEXT_PAR parent_type, LYEXT_SUBSTMT UNUSED(substmt_type)) { /* yang-data can appear only at the top level of a YANG module or submodule */ if (parent_type == LYEXT_PAR_MODULE) { return 0; } else { return 1; } } /* return values - 0 - OK * 1 - Something wrong * -1 - Absolute wrong */ int check_choice(struct lys_node *root) { struct lys_node *node, *next; int result = 1, tmp_result; LY_TREE_FOR_SAFE(root->child, next, node) { tmp_result = (node->nodetype == LYS_CASE) ? check_node(node->child) : check_node(node); if (tmp_result == -1) { return -1; } else if (tmp_result == 0) { result = 0; } } return result; } /* return values - 0 - OK * 1 - Something wrong * -1 - Absolute wrong */ int check_node(struct lys_node *node) { int result = 0; if (node == NULL) { return 1; } /* check nodes and find only one container */ if (node->nodetype == LYS_CHOICE) { result = check_choice(node); } else if (node->nodetype == LYS_USES) { result = check_node(((struct lys_node_uses*)node)->grp->child); } else if (node->nodetype != LYS_CONTAINER || (node->next != NULL || node->prev != node)) { result = -1; } return result; } void remove_iffeature(struct lys_iffeature **iffeature, uint8_t *iffeature_size, struct ly_ctx *ctx) { lys_iffeature_free(ctx, *iffeature, *iffeature_size, 0, NULL); *iffeature_size = 0; *iffeature = NULL; } void remove_iffeature_type(struct lys_type *type, struct ly_ctx *ctx) { unsigned int i; if (type->base == LY_TYPE_ENUM) { for (i = 0; i < type->info.enums.count; ++i) { remove_iffeature(&type->info.enums.enm[i].iffeature, &type->info.enums.enm[i].iffeature_size, ctx); } } else if (type->base == LY_TYPE_BITS) { for (i = 0; i < type->info.bits.count; ++i) { remove_iffeature(&type->info.bits.bit[i].iffeature, &type->info.bits.bit[i].iffeature_size, ctx); } } } /* fix schema - ignore config flag, iffeature */ void fix_schema(struct lys_node *root, struct ly_ctx *ctx) { struct lys_node *node, *next; struct lys_node_container *cont; struct lys_node_rpc_action *action; struct lys_node_grp *grp; struct lys_node_uses *uses; int i; LY_TREE_DFS_BEGIN(root, next, node) { /* ignore config flag */ node->flags = node->flags & (~(LYS_CONFIG_MASK | LYS_CONFIG_SET)); remove_iffeature(&node->iffeature, &node->iffeature_size, ctx); switch (node->nodetype) { case LYS_CONTAINER: cont = (struct lys_node_container *)node; for (i = 0; i < cont->tpdf_size; ++i) { remove_iffeature_type(&cont->tpdf[i].type, ctx); } break; case LYS_LEAF: remove_iffeature_type(&((struct lys_node_leaf *)node)->type, ctx); break; case LYS_LEAFLIST: remove_iffeature_type(&((struct lys_node_leaflist *)node)->type, ctx); break; case LYS_ACTION: case LYS_NOTIF: action = (struct lys_node_rpc_action *)node; for (i = 0; i < action->tpdf_size; ++i) { remove_iffeature_type(&action->tpdf[i].type, ctx); } break; case LYS_GROUPING: grp = (struct lys_node_grp *)node; for (i = 0; i < grp->tpdf_size; ++i) { remove_iffeature_type(&grp->tpdf[i].type, ctx); } break; case LYS_USES: uses = (struct lys_node_uses *)node; for (i = 0; i < uses->augment_size; ++i) { remove_iffeature(&uses->augment[i].iffeature, &uses->augment[i].iffeature_size, ctx); fix_schema(uses->augment[i].child, ctx); } for (i = 0; i < uses->refine_size; ++i) { remove_iffeature(&uses->refine[i].iffeature, &uses->refine[i].iffeature_size, ctx); } break; default: break; } LY_TREE_DFS_END(root, next, node) } } int yang_data_result(struct lys_ext_instance *ext) { struct lys_node **root; root = lys_ext_complex_get_substmt(LY_STMT_CONTAINER, (struct lys_ext_instance_complex *)ext, NULL); if (!root || !(*root) || (*root)->next != NULL || check_node(*root)) { return 1; } fix_schema(*root, ext->def->module->ctx); return 0; } struct lyext_substmt yang_data_substmt[] = { {LY_STMT_USES, 0, LY_STMT_CARD_OPT}, {LY_STMT_CONTAINER, 0, LY_STMT_CARD_OPT}, {LY_STMT_CHOICE, 0, LY_STMT_CARD_OPT}, {0, 0, 0} /* terminating item */ }; /** * @brief Plugin for the RFC 8040 restconf extension */ struct lyext_plugin_complex yang_data = { .type = LYEXT_COMPLEX, .flags = 0, .check_position = &yang_data_position, .check_result = &yang_data_result, .check_inherit = NULL, .valid_data = NULL, /* specification of allowed substatements of the extension instance */ .substmt = yang_data_substmt, /* final size of the extension instance structure with the space for storing the substatements */ .instance_size = (sizeof(struct lys_ext_instance_complex) - 1) + 2 * sizeof(void*) }; /** * @brief list of all extension plugins implemented here * * MANDATORY object for all libyang extension plugins, the name must match the .so */ struct lyext_plugin_list yangdata[] = { {"ietf-restconf", "2017-01-26", "yang-data", (struct lyext_plugin*)&yang_data}, {NULL, NULL, NULL, NULL} /* terminating item */ };