1 /**
2  * @file yangdata.c
3  * @author Pavol Vican <vican.pavol@gmail.com>
4  * @brief libyang extension plugin - YANG DATA (RFC 8040)
5  *
6  * Copyright (c) 2018 CESNET, z.s.p.o.
7  *
8  * This source code is licensed under BSD 3-Clause License (the "License").
9  * You may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     https://opensource.org/licenses/BSD-3-Clause
13  */
14 
15 #ifdef __GNUC__
16 #  define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
17 #else
18 #  define UNUSED(x) UNUSED_ ## x
19 #endif
20 
21 #include <stdlib.h>
22 #include "../extensions.h"
23 
24 /**
25  * @brief Storage for ID used to check plugin API version compatibility.
26  */
27 LYEXT_VERSION_CHECK
28 
29 int check_node(struct lys_node *node);
30 
31 /**
32  * @brief Callback to check that the yang-data can be instantiated inside the provided node
33  *
34  * @param[in] parent The parent of the instantiated extension.
35  * @param[in] parent_type The type of the structure provided as \p parent.
36  * @param[in] substmt_type libyang does not store all the extension instances in the structures where they are
37  *                         instantiated in the module. In some cases (see #LYEXT_SUBSTMT) they are stored in parent
38  *                         structure and marked with flag to know in which substatement of the parent the extension
39  *                         was originally instantiated.
40  * @return 0 - ok
41  *         1 - error
42  */
yang_data_position(const void * UNUSED (parent),LYEXT_PAR parent_type,LYEXT_SUBSTMT UNUSED (substmt_type))43 int yang_data_position(const void * UNUSED(parent), LYEXT_PAR parent_type, LYEXT_SUBSTMT UNUSED(substmt_type))
44 {
45     /* yang-data can appear only at the top level of a YANG module or submodule */
46     if (parent_type == LYEXT_PAR_MODULE) {
47         return 0;
48     } else {
49         return 1;
50     }
51 }
52 
53 /* return values - 0 - OK
54  *                 1 - Something wrong
55  *                -1 - Absolute wrong
56  */
check_choice(struct lys_node * root)57 int check_choice(struct lys_node *root) {
58     struct lys_node *node, *next;
59     int result = 1, tmp_result;
60 
61     LY_TREE_FOR_SAFE(root->child, next, node) {
62         tmp_result = (node->nodetype == LYS_CASE) ? check_node(node->child) : check_node(node);
63         if (tmp_result == -1) {
64             return -1;
65         } else if (tmp_result == 0) {
66             result = 0;
67         }
68     }
69 
70     return result;
71 }
72 
73 /* return values - 0 - OK
74  *                 1 - Something wrong
75  *                -1 - Absolute wrong
76  */
check_node(struct lys_node * node)77 int check_node(struct lys_node *node) {
78 
79     int result = 0;
80 
81     if (node == NULL) {
82         return 1;
83     }
84 
85     /* check nodes and find only one container */
86     if (node->nodetype == LYS_CHOICE) {
87         result = check_choice(node);
88     } else if (node->nodetype == LYS_USES) {
89         result = check_node(((struct lys_node_uses*)node)->grp->child);
90     } else if (node->nodetype != LYS_CONTAINER || (node->next != NULL || node->prev != node)) {
91         result = -1;
92     }
93 
94     return result;
95 }
96 
97 
remove_iffeature(struct lys_iffeature ** iffeature,uint8_t * iffeature_size,struct ly_ctx * ctx)98 void remove_iffeature(struct lys_iffeature **iffeature, uint8_t *iffeature_size, struct ly_ctx *ctx) {
99 
100     lys_iffeature_free(ctx, *iffeature, *iffeature_size, 0, NULL);
101     *iffeature_size = 0;
102     *iffeature = NULL;
103 }
104 
remove_iffeature_type(struct lys_type * type,struct ly_ctx * ctx)105 void remove_iffeature_type(struct lys_type *type, struct ly_ctx *ctx) {
106     unsigned int i;
107 
108     if (type->base == LY_TYPE_ENUM) {
109         for (i = 0; i < type->info.enums.count; ++i) {
110             remove_iffeature(&type->info.enums.enm[i].iffeature, &type->info.enums.enm[i].iffeature_size, ctx);
111         }
112     } else if (type->base == LY_TYPE_BITS) {
113         for (i = 0; i < type->info.bits.count; ++i) {
114             remove_iffeature(&type->info.bits.bit[i].iffeature, &type->info.bits.bit[i].iffeature_size, ctx);
115         }
116     }
117 }
118 
119 /* fix schema - ignore config flag, iffeature */
fix_schema(struct lys_node * root,struct ly_ctx * ctx)120 void fix_schema(struct lys_node *root, struct ly_ctx *ctx) {
121     struct lys_node *node, *next;
122     struct lys_node_container *cont;
123     struct lys_node_rpc_action *action;
124     struct lys_node_grp *grp;
125     struct lys_node_uses *uses;
126     int i;
127 
128     LY_TREE_DFS_BEGIN(root, next, node) {
129         /* ignore config flag */
130         node->flags = node->flags & (~(LYS_CONFIG_MASK | LYS_CONFIG_SET));
131         remove_iffeature(&node->iffeature, &node->iffeature_size, ctx);
132         switch (node->nodetype) {
133             case LYS_CONTAINER:
134                 cont = (struct lys_node_container *)node;
135                 for (i = 0; i < cont->tpdf_size; ++i) {
136                     remove_iffeature_type(&cont->tpdf[i].type, ctx);
137                 }
138                 break;
139             case LYS_LEAF:
140                 remove_iffeature_type(&((struct lys_node_leaf *)node)->type, ctx);
141                 break;
142             case LYS_LEAFLIST:
143                 remove_iffeature_type(&((struct lys_node_leaflist *)node)->type, ctx);
144                 break;
145             case LYS_ACTION:
146             case LYS_NOTIF:
147                 action = (struct lys_node_rpc_action *)node;
148                 for (i = 0; i < action->tpdf_size; ++i) {
149                     remove_iffeature_type(&action->tpdf[i].type, ctx);
150                 }
151                 break;
152             case LYS_GROUPING:
153                 grp = (struct lys_node_grp *)node;
154                 for (i = 0; i < grp->tpdf_size; ++i) {
155                     remove_iffeature_type(&grp->tpdf[i].type, ctx);
156                 }
157                 break;
158             case LYS_USES:
159                 uses = (struct lys_node_uses *)node;
160                 for (i = 0; i < uses->augment_size; ++i) {
161                     remove_iffeature(&uses->augment[i].iffeature, &uses->augment[i].iffeature_size, ctx);
162                     fix_schema(uses->augment[i].child, ctx);
163                 }
164                 for (i = 0; i < uses->refine_size; ++i) {
165                     remove_iffeature(&uses->refine[i].iffeature, &uses->refine[i].iffeature_size, ctx);
166                 }
167                 break;
168             default:
169                 break;
170         }
171         LY_TREE_DFS_END(root, next, node)
172     }
173 }
174 
yang_data_result(struct lys_ext_instance * ext)175 int yang_data_result(struct lys_ext_instance *ext) {
176     struct lys_node **root;
177 
178     root = lys_ext_complex_get_substmt(LY_STMT_CONTAINER, (struct lys_ext_instance_complex *)ext, NULL);
179     if (!root || !(*root) || (*root)->next != NULL || check_node(*root)) {
180         return 1;
181     }
182 
183     fix_schema(*root, ext->def->module->ctx);
184     return 0;
185 }
186 
187 struct lyext_substmt yang_data_substmt[] = {
188     {LY_STMT_USES,        0, LY_STMT_CARD_OPT},
189     {LY_STMT_CONTAINER,   0, LY_STMT_CARD_OPT},
190     {LY_STMT_CHOICE,      0, LY_STMT_CARD_OPT},
191     {0, 0, 0} /* terminating item */
192 };
193 
194 /**
195  * @brief Plugin for the RFC 8040 restconf extension
196  */
197 struct lyext_plugin_complex yang_data = {
198     .type = LYEXT_COMPLEX,
199     .flags = 0,
200     .check_position = &yang_data_position,
201     .check_result = &yang_data_result,
202     .check_inherit = NULL,
203     .valid_data = NULL,
204     /* specification of allowed substatements of the extension instance */
205     .substmt = yang_data_substmt,
206 
207     /* final size of the extension instance structure with the space for storing the substatements */
208     .instance_size = (sizeof(struct lys_ext_instance_complex) - 1) + 2 * sizeof(void*)
209 };
210 
211 /**
212  * @brief list of all extension plugins implemented here
213  *
214  * MANDATORY object for all libyang extension plugins, the name must match the <name>.so
215  */
216 struct lyext_plugin_list yangdata[] = {
217     {"ietf-restconf", "2017-01-26", "yang-data", (struct lyext_plugin*)&yang_data},
218     {NULL, NULL, NULL, NULL} /* terminating item */
219 };
220