1 /*
2 *
3 ***** BEGIN LICENSE BLOCK *****
4
5 Copyright (C) 2009-2019 Olof Hagsand
6 Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
7
8 This file is part of CLIXON.
9
10 Licensed under the Apache License, Version 2.0 (the "License");
11 you may not use this file except in compliance with the License.
12 You may obtain a copy of the License at
13
14 http://www.apache.org/licenses/LICENSE-2.0
15
16 Unless required by applicable law or agreed to in writing, software
17 distributed under the License is distributed on an "AS IS" BASIS,
18 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 See the License for the specific language governing permissions and
20 limitations under the License.
21
22 Alternatively, the contents of this file may be used under the terms of
23 the GNU General Public License Version 3 or later (the "GPL"),
24 in which case the provisions of the GPL are applicable instead
25 of those above. If you wish to allow use of your version of this file only
26 under the terms of the GPL, and not to allow others to
27 use your version of this file under the terms of Apache License version 2,
28 indicate your decision by deleting the provisions above and replace them with
29 the notice and other provisions required by the GPL. If you do not delete
30 the provisions above, a recipient may use your version of this file under
31 the terms of any one of the Apache License version 2 or the GPL.
32
33 ***** END LICENSE BLOCK *****
34
35 * Yang functions
36 * @see https://tools.ietf.org/html/rfc6020 YANG 1.0
37 * @see https://tools.ietf.org/html/rfc7950 YANG 1.1
38 *
39 * CALLING ORDER OF YANG PARSE FILES
40 * =================================
41 * yang_spec_parse_module
42 * | |
43 * v v v
44 * yang_spec_parse_file-> yang_parse_post->yang_parse_recurse->yang_parse_module
45 * \ / v
46 * yang_spec_load_dir ------------------------------------> yang_parse_filename
47 * v
48 * yang_parse_file
49 * v
50 * yang_parse_str
51 */
52
53 #ifdef HAVE_CONFIG_H
54 #include "clixon_config.h" /* generated by config & autoconf */
55 #endif
56
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <errno.h>
60 #include <limits.h>
61 #include <ctype.h>
62 #include <unistd.h>
63 #include <string.h>
64 #include <arpa/inet.h>
65 #include <regex.h>
66 #include <dirent.h>
67 #include <sys/types.h>
68 #include <fcntl.h>
69 #include <syslog.h>
70 #include <assert.h>
71 #include <libgen.h>
72 #include <sys/stat.h>
73 #include <sys/param.h>
74 #include <netinet/in.h>
75 #include <libgen.h>
76
77 /* cligen */
78 #include <cligen/cligen.h>
79
80 /* clicon */
81 #include "clixon_log.h"
82 #include "clixon_err.h"
83 #include "clixon_string.h"
84 #include "clixon_queue.h"
85 #include "clixon_hash.h"
86 #include "clixon_handle.h"
87 #include "clixon_file.h"
88 #include "clixon_yang.h"
89 #include "clixon_yang_internal.h"
90 #include "clixon_hash.h"
91 #include "clixon_xml.h"
92 #include "clixon_xml_nsctx.h"
93 #include "clixon_xpath_ctx.h"
94 #include "clixon_xpath.h"
95 #include "clixon_yang_module.h"
96 #include "clixon_plugin.h"
97 #include "clixon_data.h"
98 #include "clixon_options.h"
99 #include "clixon_yang_type.h"
100 #include "clixon_yang_parse.h"
101 #include "clixon_yang_cardinality.h"
102 #include "clixon_yang_parse_lib.h"
103
104 /* Size of json read buffer when reading from file*/
105 #define BUFLEN 1024
106
107 /*! Resolve a grouping name from a module, includes looking in submodules
108 */
109 static yang_stmt *
ys_grouping_module_resolve(yang_stmt * ymod,yang_stmt * yspec,char * name)110 ys_grouping_module_resolve(yang_stmt *ymod,
111 yang_stmt *yspec,
112 char *name)
113 {
114 yang_stmt *yinc;
115 yang_stmt *ysubm;
116 yang_stmt *yrealmod = NULL;
117 yang_stmt *ygrouping = NULL;
118 char *submname;
119
120 /* Find grouping from own sub/module */
121 if ((ygrouping = yang_find(ymod, Y_GROUPING, name)) != NULL)
122 goto done;
123 /* Find top-level module */
124 if (ys_real_module(ymod, &yrealmod) < 0)
125 goto done;
126 if (yrealmod == ymod) /* skip if module, continue if submodule */
127 goto done;
128 /* Find grouping from real module */
129 if ((ygrouping = yang_find(yrealmod, Y_GROUPING, name)) != NULL)
130 goto done;
131 /* Find grouping from sub-modules */
132 yinc = NULL;
133 while ((yinc = yn_each(yrealmod, yinc)) != NULL){
134 if (yang_keyword_get(yinc) != Y_INCLUDE)
135 continue;
136 submname = yang_argument_get(yinc);
137 if ((ysubm = yang_find_module_by_name(yspec, submname)) == NULL)
138 continue;
139 if (ysubm == ymod)
140 continue;
141 if ((ygrouping = yang_find(ysubm, Y_GROUPING, name)) != NULL)
142 break;
143 }
144 done:
145 return ygrouping;
146 }
147
148 /*! Resolve a grouping name from a point in the yang tree
149 * @param[in] ys Yang statement of "uses" statement doing the lookup
150 * @param[in] prefix Prefix of grouping to look for
151 * @param[in] name Name of grouping to look for
152 * @param[out] ygrouping0 A found grouping yang structure as result
153 * @retval 0 OK, ygrouping may be NULL
154 * @retval -1 Error, with clicon_err called
155 */
156 static int
ys_grouping_resolve(yang_stmt * yuses,char * prefix,char * name,yang_stmt ** ygrouping0)157 ys_grouping_resolve(yang_stmt *yuses,
158 char *prefix,
159 char *name,
160 yang_stmt **ygrouping0)
161 {
162 int retval = -1;
163 yang_stmt *ymod;
164 yang_stmt *ygrouping = NULL;
165 yang_stmt *yp;
166 yang_stmt *ys;
167 yang_stmt *yspec;
168 enum rfc_6020 keyw;
169
170 yspec = ys_spec(yuses);
171 /* find the grouping associated with argument and expand(?) */
172 if (prefix){ /* Go to top and find import that matches */
173 if ((ymod = yang_find_module_by_prefix(yuses, prefix)) != NULL)
174 ygrouping = ys_grouping_module_resolve(ymod, yspec, name);
175 }
176 else {
177 ys = yuses; /* Check upwards in hierarchy for matching groupings */
178 while (1){
179 if ((yp = yang_parent_get(ys)) == NULL)
180 break;
181 if ((keyw = yang_keyword_get(yp)) == Y_SPEC)
182 break;
183 else if (keyw == Y_MODULE || keyw == Y_SUBMODULE){ /* Try submodules */
184 ygrouping = ys_grouping_module_resolve(yp, yspec, name);
185 break;
186 }
187 else if ((ygrouping = yang_find(yp, Y_GROUPING, name)) != NULL) /* Here find grouping */
188 break;
189 ys = (yang_stmt*)yp; /* Proceed to next level */
190 }
191 }
192 *ygrouping0 = ygrouping;
193 retval = 0;
194 // done:
195 return retval;
196 }
197
198 /*! This is an augment node, augment the original datamodel.
199 * @param[in] ys The augment statement
200 * @param[in] yspec Yang specification
201 * @see RFC7950 Sec 7.17
202 * The target node MUST be either a container, list, choice, case, input,
203 * output, or notification node.
204 * If the "augment" statement is on the top level the absolute form MUST be
205 * used.
206 * All data nodes defined in the "augment" statement are defined as XML
207 * elements in the XML namespace of the module where the "augment" is
208 * specified.
209 *
210 * @note If the augment has a when statement, which is commonplace, the when statement is not copied as
211 * datanodes are, since it should not apply to the target node. Instead it is added as a special "when"
212 * struct to the yang statements being inserted.
213 */
214 static int
yang_augment_node(yang_stmt * ys)215 yang_augment_node(yang_stmt *ys)
216 {
217 int retval = -1;
218 char *schema_nodeid;
219 yang_stmt *ytarget = NULL;
220 yang_stmt *yc0;
221 yang_stmt *yc;
222 yang_stmt *ymod;
223 yang_stmt *ywhen;
224 char *wxpath = NULL; /* xpath of when statement */
225 cvec *wnsc = NULL; /* namespace context of when statement */
226 enum rfc_6020 targetkey;
227 enum rfc_6020 childkey;
228
229 if ((ymod = ys_module(ys)) == NULL){
230 clicon_err(OE_YANG, 0, "My yang module not found");
231 goto done;
232 }
233 /* */
234 schema_nodeid = yang_argument_get(ys);
235 clicon_debug(2, "%s %s", __FUNCTION__, schema_nodeid);
236 /* Find the target */
237 if (yang_abs_schema_nodeid(ys, schema_nodeid, &ytarget) < 0)
238 goto done;
239
240 if (ytarget == NULL){
241 #if 0 /* Lots of yang models fail here */
242 clicon_err(OE_YANG, 0, "Augment failed in module %s: target node %s not found",
243 yang_argument_get(ys_module(ys)),
244 schema_nodeid);
245 goto done;
246 #else
247 clicon_log(LOG_WARNING, "Warning: Augment failed in module %s: target node %s not found",
248 yang_argument_get(ys_module(ys)),
249 schema_nodeid);
250 goto ok;
251 #endif
252 }
253 /* The target node MUST be either a container, list, choice, case, input, output, or notification node.
254 * which means it is slightly different than a schema-nodeid ? */
255 targetkey = yang_keyword_get(ytarget);
256 if (targetkey != Y_CONTAINER && targetkey != Y_LIST && targetkey != Y_CHOICE &&
257 targetkey != Y_CASE && targetkey != Y_INPUT &&
258 targetkey != Y_OUTPUT && targetkey != Y_NOTIFICATION){
259 clicon_log(LOG_WARNING, "Warning: Augment failed in module %s: target node %s has wrong type %s",
260 yang_argument_get(ys_module(ys)),
261 schema_nodeid,
262 yang_key2str(targetkey));
263 goto ok;
264 }
265
266 /* Find when statement, if present */
267 if ((ywhen = yang_find(ys, Y_WHEN, NULL)) != NULL){
268 wxpath = yang_argument_get(ywhen);
269 if (xml_nsctx_yang(ywhen, &wnsc) < 0)
270 goto done;
271 }
272 /* Extend ytarget with ys' schemanode children */
273 yc0 = NULL;
274 while ((yc0 = yn_each(ys, yc0)) != NULL) {
275 if (!yang_schemanode(yc0))
276 continue;
277 childkey = yang_keyword_get(yc0);
278 switch (targetkey){
279 case Y_CONTAINER:
280 case Y_LIST:
281 /* If the target node is a container or list node, the "action" and
282 "notification" statements can be used within the "augment" statement.
283 */
284 if (childkey != Y_ACTION && childkey != Y_NOTIFICATION &&
285 childkey != Y_CONTAINER && childkey != Y_LEAF && childkey != Y_LIST &&
286 childkey != Y_LEAF_LIST && childkey != Y_USES && childkey != Y_CHOICE){
287 clicon_log(LOG_WARNING, "Warning: A Augment failed in module %s: node %s %d cannot be added to target node %s",
288 yang_argument_get(ys_module(ys)),
289 yang_key2str(childkey),
290 childkey,
291 schema_nodeid);
292 goto ok;
293 }
294 break;
295 case Y_CASE:
296 case Y_INPUT:
297 case Y_OUTPUT:
298 case Y_NOTIFICATION:
299 /* If the target node is a container, list, case, input, output, or
300 notification node, the "container", "leaf", "list", "leaf-list",
301 "uses", and "choice" statements can be used within the "augment"
302 statement. */
303 if (childkey != Y_CONTAINER && childkey != Y_LEAF && childkey != Y_LIST &&
304 childkey != Y_LEAF_LIST && childkey != Y_USES && childkey != Y_CHOICE){
305 clicon_log(LOG_WARNING, "Warning: B Augment failed in module %s: node %s %d cannot be added to target node %s",
306 yang_argument_get(ys_module(ys)),
307 yang_key2str(childkey),
308 childkey,
309 schema_nodeid);
310 goto ok;
311 }
312 break;
313 case Y_CHOICE:
314 /* If the target node is a choice node, the "case" statement or a
315 shorthand "case" statement (see Section 7.9.2) can be used within the
316 "augment" statement.
317 XXX could be more or less anything?
318 As a shorthand, the "case" statement can be omitted if the branch
319 contains a single "anydata", "anyxml", "choice", "container", "leaf",
320 "list", or "leaf-list" statement.
321 */
322 if (childkey != Y_CASE && childkey != Y_ANYDATA && childkey != Y_ANYXML &&
323 childkey != Y_CHOICE && childkey != Y_CONTAINER && childkey != Y_LEAF &&
324 childkey != Y_LIST && childkey != Y_LEAF_LIST){
325
326 clicon_log(LOG_WARNING, "Warning: C Augment failed in module %s: node %s %d cannot be added to target node %s",
327 yang_argument_get(ys_module(ys)),
328 yang_key2str(childkey),
329 childkey,
330 schema_nodeid);
331 goto ok;
332 }
333 break;
334 default:
335 break;
336 }
337
338 if ((yc = ys_dup(yc0)) == NULL)
339 goto done;
340 yc->ys_mymodule = ymod;
341
342 if (yn_insert(ytarget, yc) < 0)
343 goto done;
344 /* If there is an associated when statement, add a special when struct to the yang */
345 if (ywhen){
346 if (yang_when_xpath_set(yc, wxpath) < 0)
347 goto done;
348 if (yang_when_nsc_set(yc, wnsc) < 0)
349 goto done;
350 }
351 }
352 ok:
353 retval = 0;
354 done:
355 if (wnsc)
356 cvec_free(wnsc);
357 return retval;
358 }
359
360 /*! Find all top-level augments in a module and change original datamodels.
361 * @param[in] ymod Yang statement of type module/sub-module
362 * @retval 0 OK
363 * @retval -1 Error
364 * Note there is an ordering problem, where an augment in one module depends on an augment in
365 * another module not yet augmented.
366 */
367 static int
yang_augment_module(yang_stmt * ymod)368 yang_augment_module(yang_stmt *ymod)
369
370 {
371 int retval = -1;
372 yang_stmt *ys;
373
374 ys = NULL;
375 while ((ys = yn_each(ymod, ys)) != NULL){
376 switch (yang_keyword_get(ys)){
377 case Y_AUGMENT: /* top-level */
378 if (yang_augment_node(ys) < 0)
379 goto done;
380 break;
381 default:
382 break;
383 }
384 }
385 retval = 0;
386 done:
387 return retval;
388 }
389
390 /*! Given a refine node, perform the refinement action on the target refine node
391 * The RFC is somewhat complicate in the rules for refine.
392 * Most nodes will be replaced, but some are added
393 * @param[in] yr Refine node
394 * @param[in] yt Refine target node (will be modified)
395 * @see RFC7950 Sec 7.13.2
396 * There may be some missed cornercases
397 */
398 static int
ys_do_refine(yang_stmt * yr,yang_stmt * yt)399 ys_do_refine(yang_stmt *yr,
400 yang_stmt *yt)
401 {
402 int retval = -1;
403 yang_stmt *yrc; /* refine child */
404 yang_stmt *yrc1;
405 yang_stmt *ytc; /* target child */
406 enum rfc_6020 keyw;
407 int i;
408
409 /* Loop through refine node children. First if remove do that first
410 * In some cases remove a set of nodes.
411 */
412 yrc = NULL;
413 while ((yrc = yn_each(yr, yrc)) != NULL) {
414 keyw = yang_keyword_get(yrc);
415 switch (keyw){
416 case Y_DEFAULT: /* remove old, add new */
417 case Y_DESCRIPTION:
418 case Y_REFERENCE:
419 case Y_CONFIG:
420 case Y_MANDATORY:
421 case Y_PRESENCE:
422 case Y_MIN_ELEMENTS:
423 case Y_MAX_ELEMENTS:
424 case Y_EXTENSION:
425 /* Remove old matching, dont increment due to prune in loop */
426 for (i=0; i<yang_len_get(yt); ){
427 ytc = yt->ys_stmt[i];
428 if (keyw != yang_keyword_get(ytc)){
429 i++;
430 continue;
431 }
432 ys_prune(yt, i);
433 ys_free(ytc);
434 }
435 /* fall through and add if not found */
436 case Y_MUST: /* keep old, add new */
437 case Y_IF_FEATURE:
438 break;
439 default:
440 break;
441 }
442 }
443 /* Second, add the node(s) */
444 yrc = NULL;
445 while ((yrc = yn_each(yr, yrc)) != NULL) {
446 keyw = yang_keyword_get(yrc);
447 /* Make copy */
448 if ((yrc1 = ys_dup(yrc)) == NULL)
449 goto done;
450 if (yn_insert(yt, yrc1) < 0)
451 goto done;
452 }
453 retval = 0;
454 done:
455 return retval;
456 }
457
458 /*! Macro expansion of grouping/uses done in step 2 of yang parsing
459 * RFC7950:
460 * Identifiers appearing inside the grouping are resolved
461 * relative to the scope in which the grouping is defined, not where it is
462 * used. Prefix mappings, type names, grouping names, and extension usage are
463 * evaluated in the hierarchy where the "grouping" statement appears.
464 * The identifiers defined in the grouping are not bound to a namespace
465 * until the contents of the grouping are added to the schema tree via a
466 * "uses" statement that does not appear inside a "grouping" statement,
467 * at which point they are bound to the namespace of the current module.
468 */
469 static int
yang_expand_grouping(yang_stmt * yn)470 yang_expand_grouping(yang_stmt *yn)
471 {
472 int retval = -1;
473 yang_stmt *ys = NULL;
474 yang_stmt *ygrouping; /* grouping original */
475 yang_stmt *ygrouping2; /* grouping copy */
476 yang_stmt *yg; /* grouping child */
477 yang_stmt *yr; /* refinement */
478 int glen;
479 int i;
480 int j;
481 int k;
482 char *id = NULL;
483 char *prefix = NULL;
484 size_t size;
485 yang_stmt *yp;
486
487 /* Cannot use yang_apply here since child-list is modified (is destructive) */
488 i = 0;
489 while (i < yang_len_get(yn)){
490 ys = yn->ys_stmt[i];
491 switch (yang_keyword_get(ys)){
492 case Y_USES:
493 /* Split argument into prefix and name */
494 if (nodeid_split(yang_argument_get(ys), &prefix, &id) < 0)
495 goto done;
496 if (ys_grouping_resolve(ys, prefix, id, &ygrouping) < 0)
497 goto done;
498 if (prefix){
499 free(prefix);
500 prefix = NULL;
501 }
502 if (id){
503 free(id);
504 id = NULL;
505 }
506 if (ygrouping == NULL){
507 clicon_log(LOG_NOTICE, "%s: Yang error : grouping \"%s\" not found in module \"%s\"",
508 __FUNCTION__, yang_argument_get(ys), yang_argument_get(ys_module(ys)));
509 goto done;
510 break;
511 }
512 /* Check so that this uses statement is not a descendant of the grouping
513 */
514 yp = yn;
515 do {
516 if (yp == ygrouping){
517 clicon_err(OE_YANG, EFAULT, "Yang use of grouping %s in module %s is defined inside the grouping (recursion), see RFC 7950 Sec 7.12: A grouping MUST NOT reference itself",
518 yang_argument_get(ys),
519 yang_argument_get(ys_module(yn))
520 );
521 goto done;
522 }
523 } while((yp = yang_parent_get(yp)) != NULL);
524 if (yang_flag_get(ygrouping, YANG_FLAG_MARK) == 0){
525 /* Check mark flag to see if this grouping has been expanded before,
526 * here below in the traverse section
527 * A mark could be completely normal (several uses) or it could be a recursion.
528 */
529 yang_flag_set(ygrouping, YANG_FLAG_MARK); /* Mark as (being) expanded */
530 if (yang_expand_grouping(ygrouping) < 0)
531 goto done;
532 }
533 /* Make a copy of the grouping, then make refinements to this copy
534 * Note this ygrouping2 object does not gave a parent and does not work in many
535 * functions which assume a full hierarchy, use the original ygrouping in those cases.
536 */
537 if ((ygrouping2 = ys_dup(ygrouping)) == NULL)
538 goto done;
539
540 /* Only replace data/schemanodes:
541 * Compute the number of such nodes, and extend the child vector with that below
542 */
543 glen = 0;
544 yg = NULL;
545 while ((yg = yn_each(ygrouping2, yg)) != NULL) {
546 if (yang_schemanode(yg))
547 glen++;
548 }
549 /*
550 * yn is parent: the children of ygrouping replaces ys.
551 * Is there a case when glen == 0? YES AND THIS BREAKS
552 */
553 if (glen != 1){
554 size = (yang_len_get(yn) - i - 1)*sizeof(struct yang_stmt *);
555 yn->ys_len += glen - 1;
556 if (glen && (yn->ys_stmt = realloc(yn->ys_stmt, (yang_len_get(yn))*sizeof(yang_stmt *))) == 0){
557 clicon_err(OE_YANG, errno, "realloc");
558 goto done;
559 }
560 /* Then move all existing elements up from i+1 (not uses-stmt) */
561 if (size)
562 memmove(&yn->ys_stmt[i+glen],
563 &yn->ys_stmt[i+1],
564 size);
565 }
566 /* Iterate through refinements and modify grouping copy
567 * See RFC 7950 7.13.2 yrt is the refine target node
568 */
569 yr = NULL;
570 while ((yr = yn_each(ys, yr)) != NULL) {
571 yang_stmt *yrt; /* refine target node */
572 if (yang_keyword_get(yr) != Y_REFINE)
573 continue;
574 /* Find a node */
575 if (yang_desc_schema_nodeid(ygrouping, /* Cannot use ygrouping2 */
576 yang_argument_get(yr),
577 &yrt) < 0)
578 goto done;
579 /* Not found, try next */
580 if (yrt == NULL)
581 continue;
582 /* Do the actual refinement */
583 if (ys_do_refine(yr, yrt) < 0)
584 goto done;
585 /* RFC: The argument is a string that identifies a node in the
586 * grouping. I interpret that as only one node --> break */
587 break;
588 }
589 /* Then copy and insert each child element from ygrouping2 to yn */
590 k=0;
591 for (j=0; j<yang_len_get(ygrouping2); j++){
592 yg = ygrouping2->ys_stmt[j]; /* Child of refined copy */
593 /* Only replace data/schemanodes */
594 if (!yang_schemanode(yg)){
595 ys_free(yg);
596 continue;
597 }
598 yn->ys_stmt[i+k] = yg;
599 yg->ys_parent = yn;
600 k++;
601 }
602 /* Remove 'uses' node */
603 ys_free(ys);
604 /* Remove the grouping copy */
605 ygrouping2->ys_len = 0; /* Cant do with get access function */
606 ys_free(ygrouping2);
607 break; /* Note same child is re-iterated since it may be changed */
608 default:
609 i++;
610 break;
611 }
612 }
613 /* Second pass since length may have changed */
614 for (i=0; i<yang_len_get(yn); i++){
615 ys = yn->ys_stmt[i];
616 if (yang_keyword_get(ys) == Y_GROUPING){
617 /* Check mark flag to see if this grouping has been expanded before, here or in the
618 * 'uses' section
619 * A mark could be completely normal (several uses) or it could be a recursion.
620 */
621 if (yang_flag_get(ys, YANG_FLAG_MARK) == 0){
622 yang_flag_set(ys, YANG_FLAG_MARK); /* Mark as (being) expanded */
623 if (yang_expand_grouping(ys) < 0)
624 goto done;
625 }
626 }
627 else{
628 if (yang_expand_grouping(ys) < 0)
629 goto done;
630 }
631 }
632 retval = 0;
633 done:
634 if (prefix)
635 free(prefix);
636 if (id)
637 free(id);
638 return retval;
639 }
640
641 /*! Parse a string containing a YANG spec into a parse-tree
642 *
643 * Syntax parsing. A string is input and a YANG syntax-tree is returned (or error).
644 * As a side-effect, Yang modules present in the text will be inserted under the global Yang
645 * specification
646 * @param[in] str String of yang statements
647 * @param[in] name Log string, typically filename
648 * @param[in] yspec Yang specification.
649 * @retval ymod Top-level yang (sub)module
650 * @retval NULL Error encountered
651 * See top of file for diagram of calling order
652 */
653 static yang_stmt *
yang_parse_str(char * str,const char * name,yang_stmt * yspec)654 yang_parse_str(char *str,
655 const char *name, /* just for errs */
656 yang_stmt *yspec)
657 {
658 clixon_yang_yacc yy = {0,};
659 yang_stmt *ymod = NULL;
660
661 if (yspec == NULL){
662 clicon_err(OE_YANG, 0, "Yang parse need top level yang spec");
663 goto done;
664 }
665 yy.yy_name = (char*)name;
666 yy.yy_linenum = 1;
667 yy.yy_parse_string = str;
668 yy.yy_stack = NULL;
669 yy.yy_module = NULL; /* this is the return value - the module/sub-module */
670 if (ystack_push(&yy, yspec) == NULL)
671 goto done;
672 if (strlen(str)){ /* Not empty */
673 if (yang_scan_init(&yy) < 0)
674 goto done;
675 if (yang_parse_init(&yy) < 0)
676 goto done;
677 if (clixon_yang_parseparse(&yy) != 0) { /* yacc returns 1 on error */
678 clicon_log(LOG_NOTICE, "Yang error: %s on line %d", name, yy.yy_linenum);
679 if (clicon_errno == 0)
680 clicon_err(OE_YANG, 0, "yang parser error with no error code (should not happen)");
681 yang_parse_exit(&yy);
682 yang_scan_exit(&yy);
683 goto done;
684 }
685 if (yang_parse_exit(&yy) < 0)
686 goto done;
687 if (yang_scan_exit(&yy) < 0)
688 goto done;
689 }
690 ymod = yy.yy_module;
691 done:
692 ystack_pop(&yy);
693 if (yy.yy_stack)
694 free (yy.yy_stack);
695 return ymod; /* top-level (sub)module */
696 }
697
698 /*! Parse yang spec from an open file descriptor
699 * @param[in] fd File descriptor containing the YANG file as ASCII characters
700 * @param[in] name For debug, eg filename
701 * @param[in] yspec Yang specification. Should have been created by caller using yspec_new
702 * @retval ymod Top-level yang (sub)module
703 * @retval NULL Error
704 * @note this function simply parse a yang spec, no dependencies or checks
705 */
706 yang_stmt *
yang_parse_file(int fd,const char * name,yang_stmt * yspec)707 yang_parse_file(int fd,
708 const char *name,
709 yang_stmt *yspec)
710 {
711 char *buf = NULL;
712 int i;
713 char c;
714 int len;
715 yang_stmt *ymod = NULL;
716 int ret;
717
718 len = BUFLEN; /* any number is fine */
719 if ((buf = malloc(len)) == NULL){
720 clicon_err(OE_XML, errno, "malloc");
721 goto done;
722 }
723 memset(buf, 0, len);
724 i = 0; /* position in buf */
725 while (1){ /* read the whole file */
726 if ((ret = read(fd, &c, 1)) < 0){
727 clicon_err(OE_XML, errno, "read");
728 break;
729 }
730 if (ret == 0)
731 break; /* eof */
732 if (i == len-1){
733 if ((buf = realloc(buf, 2*len)) == NULL){
734 clicon_err(OE_XML, errno, "realloc");
735 goto done;
736 }
737 memset(buf+len, 0, len);
738 len *= 2;
739 }
740 buf[i++] = (char)(c&0xff);
741 } /* read a line */
742 if ((ymod = yang_parse_str(buf, name, yspec)) < 0)
743 goto done;
744 done:
745 if (buf != NULL)
746 free(buf);
747 return ymod; /* top-level (sub)module */
748 }
749
750 /*! Given a yang filename, extract the revision as an integer as YYYYMMDD
751 * @param[in] filename Filename on the form: name [+ @rev ] + .yang
752 * @param[out] basep "Base" filename, stripped: [+ @rev ] + .yang (malloced)
753 * @param[out] revp Revision as YYYYMMDD (0 if not found)
754 */
755 static int
filename2revision(const char * filename,char ** basep,uint32_t * revp)756 filename2revision(const char *filename,
757 char **basep,
758 uint32_t *revp)
759 {
760 int retval = -1;
761 char *base = NULL;
762 char *p;
763
764 /* base = module name [+ @rev ] + .yang */
765 if ((base = strdup(filename)) == NULL){
766 clicon_err(OE_UNIX, errno, "strdup");
767 goto done;
768 }
769 clicon_debug(2, "%s %s", __FUNCTION__, base);
770 if ((p = rindex(base, '.')) != NULL) /* strip postfix .yang */
771 *p = '\0';
772 if ((p = index(base, '@')) != NULL){ /* extract revision date */
773 *p++ = '\0';
774 if (revp && ys_parse_date_arg(p, revp) < 0)
775 goto done;
776 }
777 if (basep){
778 *basep = base;
779 base = NULL;
780 }
781 retval = 0;
782 done:
783 if (base)
784 free(base);
785 return retval;
786 }
787
788 /*! No specific revision give. Match a yang file given module
789 * @param[in] h CLICON handle
790 * @param[in] module Name of main YANG module.
791 * @param[in] revision Revision or NULL
792 * @param[out] revactual Actual revision (if retval=1)
793 * @param[out] fbuf Buffer containing filename (if retval=1)
794 * @retval 1 Match found, Most recent entry returned in fbuf and revactual
795 * @retval 0 No matching entry found
796 * @retval -1 Error
797 * @note for bootstrapping, dir may have to be set.
798 */
799 static int
yang_parse_find_match(clicon_handle h,const char * module,const char * revision,uint32_t * revactual,cbuf * fbuf)800 yang_parse_find_match(clicon_handle h,
801 const char *module,
802 const char *revision,
803 uint32_t *revactual,
804 cbuf *fbuf)
805 {
806 int retval = -1;
807 struct dirent *dp = NULL;
808 int ndp;
809 cbuf *regex = NULL;
810 cxobj *x;
811 cxobj *xc;
812 char *dir;
813
814 /* get clicon config file in xml form */
815 if ((x = clicon_conf_xml(h)) == NULL)
816 goto ok;
817 if ((regex = cbuf_new()) == NULL){
818 clicon_err(OE_YANG, errno, "cbuf_new");
819 goto done;
820 }
821 /* RFC 6020: The name of the file SHOULD be of the form:
822 * module-or-submodule-name ['@' revision-date] ( '.yang' / '.yin' )
823 * revision-date ::= 4DIGIT "-" 2DIGIT "-" 2DIGIT
824 */
825 if (revision)
826 cprintf(regex, "^%s@%s(.yang)$", module, revision);
827 else
828 cprintf(regex, "^%s(@[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])?(.yang)$",
829 module);
830 xc = NULL;
831 while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL) {
832 if (strcmp(xml_name(xc), "CLICON_YANG_DIR") != 0)
833 continue;
834 dir = xml_body(xc);
835 /* get all matching files in this directory */
836 if ((ndp = clicon_file_dirent(dir,
837 &dp,
838 cbuf_get(regex),
839 S_IFREG)) < 0)
840 goto done;
841 /* Entries are sorted, last entry should be most recent date
842 */
843 if (ndp != 0){
844 cprintf(fbuf, "%s/%s", dir, dp[ndp-1].d_name);
845 retval = 1;
846 goto done;
847 }
848 }
849 ok:
850 retval = 0;
851 done:
852 if (regex)
853 cbuf_free(regex);
854 if (dp)
855 free(dp);
856 return retval;
857 }
858
859 /*! Open a file, read into a string and invoke yang parsing
860 *
861 * Similar to clicon_yang_str(), just read a file first
862 * @param[in] filename Name of file
863 * @param[in] yspec Yang specification. Should have been created by caller using yspec_new
864 * @retval ymod Top-level yang (sub)module
865 * @retval NULL Error encountered
866
867 * The database symbols are inserted in alphabetical order.
868 * See top of file for diagram of calling order
869 */
870 yang_stmt *
yang_parse_filename(const char * filename,yang_stmt * yspec)871 yang_parse_filename(const char *filename,
872 yang_stmt *yspec)
873 {
874 yang_stmt *ymod = NULL;
875 int fd = -1;
876 struct stat st;
877
878 clicon_debug(1, "%s %s", __FUNCTION__, filename);
879 if (stat(filename, &st) < 0){
880 clicon_err(OE_YANG, errno, "%s not found", filename);
881 goto done;
882 }
883 if ((fd = open(filename, O_RDONLY)) < 0){
884 clicon_err(OE_YANG, errno, "open(%s)", filename);
885 goto done;
886 }
887 if ((ymod = yang_parse_file(fd, filename, yspec)) < 0)
888 goto done;
889 done:
890 if (fd != -1)
891 close(fd);
892 return ymod; /* top-level (sub)module */
893 }
894
895 /*! Given a (sub)module, parse all (sub)modules in turn recursively
896 *
897 * Find a yang module file, and then recursively parse all its imported modules.
898 * @param[in] h CLICON handle
899 * @param[in] module Module name
900 * @param[in] revision Revision (or NULL)
901 * @param[in] yspec Yang statement
902 * @retval 0 OK
903 * @retval -1 Error
904 *
905 * See top of file for diagram of calling order
906 */
907 static yang_stmt *
yang_parse_module(clicon_handle h,const char * module,const char * revision,yang_stmt * yspec)908 yang_parse_module(clicon_handle h,
909 const char *module,
910 const char *revision,
911 yang_stmt *yspec)
912 {
913 cbuf *fbuf = NULL;
914 char *filename;
915 int nr;
916 yang_stmt *ymod = NULL;
917 yang_stmt *yrev; /* yang revision */
918 uint32_t revf = 0; /* revision in filename */
919 uint32_t revm = 0; /* revision in parsed new module (should be same as revf) */
920
921 if ((fbuf = cbuf_new()) == NULL){
922 clicon_err(OE_YANG, errno, "cbuf_new");
923 goto done;
924 }
925 /* Match a yang file with or without revision in yang-dir list */
926 if ((nr = yang_parse_find_match(h, module, revision, &revf, fbuf)) < 0)
927 goto done;
928 if (nr == 0){
929 if (revision)
930 clicon_err(OE_YANG, errno, "No yang files found matching \"%s@%s\" in the list of CLICON_YANG_DIRs",
931 module, revision);
932 else
933 clicon_err(OE_YANG, errno, "No yang files found matching \"%s\" in the list of CLICON_YANG_DIRs", module);
934 goto done;
935 }
936 filename = cbuf_get(fbuf);
937 if ((ymod = yang_parse_filename(filename, yspec)) == NULL)
938 goto done;
939 /* Sanity check that requested module name matches loaded module
940 * If this does not match, the filename and containing module do not match
941 * RFC 7950 Sec 5.2
942 */
943 if (strcmp(yang_argument_get(ymod), module) != 0){
944 clicon_err(OE_YANG, EINVAL, "File %s contains yang module \"%s\" which does not match expected module %s",
945 filename,
946 yang_argument_get(ymod),
947 module);
948 ymod = NULL;
949 goto done;
950 }
951 if ((yrev = yang_find(ymod, Y_REVISION, NULL)) != NULL)
952 revm = cv_uint32_get(yang_cv_get(yrev));
953 if (filename2revision(filename, NULL, &revf) < 0)
954 goto done;
955 /* Sanity check that file revision does not match internal rev stmt */
956 if (revf && revm && revm != revf){
957 clicon_err(OE_YANG, EINVAL, "Yang module file revision and in yang does not match: %s vs %u", filename, revm);
958 ymod = NULL;
959 goto done;
960 }
961 done:
962 if (fbuf)
963 cbuf_free(fbuf);
964 return ymod; /* top-level (sub)module */
965 }
966
967 /*! Given a (sub)module, parse all (sub)modules in turn recursively
968 *
969 * Find a yang module file, and then recursively parse all its imported modules.
970 * @param[in] h CLICON handle
971 * @param[in] ymod Yang module.
972 * @param[in] yspec Yang specification.
973 * @retval 0 OK
974 * @retval -1 Error
975 *
976 * See top of file for diagram of calling order
977 */
978 static int
yang_parse_recurse(clicon_handle h,yang_stmt * ymod,yang_stmt * ysp)979 yang_parse_recurse(clicon_handle h,
980 yang_stmt *ymod,
981 yang_stmt *ysp)
982 {
983 int retval = -1;
984 yang_stmt *yi = NULL; /* import */
985 yang_stmt *yrev;
986 char *submodule;
987 char *subrevision;
988 yang_stmt *subymod;
989 enum rfc_6020 keyw;
990
991 /* go through all import (modules) and include(submodules) of ysp */
992 while ((yi = yn_each(ymod, yi)) != NULL){
993 keyw = yang_keyword_get(yi);
994 if (keyw != Y_IMPORT && keyw != Y_INCLUDE)
995 continue;
996 /* common part */
997 submodule = yang_argument_get(yi);
998 /* Is there a specific revision (or just latest)? */
999 if ((yrev = yang_find(yi, Y_REVISION_DATE, NULL)) != NULL)
1000 subrevision = yang_argument_get(yrev);
1001 else
1002 subrevision = NULL;
1003 /* if already loaded, ignore, else parse the file */
1004 if (yang_find(ysp,
1005 keyw==Y_IMPORT?Y_MODULE:Y_SUBMODULE,
1006 submodule) == NULL){
1007 /* recursive call */
1008 if ((subymod = yang_parse_module(h, submodule, subrevision, ysp)) == NULL)
1009 goto done;
1010 /* Go through its sub-modules recursively */
1011 if (yang_parse_recurse(h, subymod, ysp) < 0){
1012 ymod = NULL;
1013 goto done;
1014 }
1015 }
1016 }
1017 retval = 0;
1018 done:
1019 return retval; /* top-level (sub)module */
1020 }
1021
1022 /*!
1023 * @param[in] ys Yang statement
1024 * @param[in] dummy Necessary for called in yang_apply
1025 * @see yang_applyfn_t
1026 */
1027 static int
ys_schemanode_check(yang_stmt * ys,void * dummy)1028 ys_schemanode_check(yang_stmt *ys,
1029 void *dummy)
1030 {
1031 int retval = -1;
1032 yang_stmt *yres = NULL;
1033 yang_stmt *yp;
1034 char *arg;
1035 enum rfc_6020 keyword;
1036 char **vec = NULL;
1037 char *v;
1038 int nvec;
1039 int i;
1040
1041 yp = yang_parent_get(ys);
1042 arg = yang_argument_get(ys);
1043 keyword = yang_keyword_get(ys);
1044 switch (yang_keyword_get(ys)){
1045 case Y_AUGMENT:
1046 if (yang_keyword_get(yp) == Y_MODULE || /* Not top-level */
1047 yang_keyword_get(yp) == Y_SUBMODULE)
1048 break;
1049 /* fallthru */
1050 case Y_REFINE:
1051 if (yang_desc_schema_nodeid(yp, arg, &yres) < 0)
1052 goto done;
1053 if (yres == NULL){
1054 clicon_err(OE_YANG, 0, "schemanode sanity check of %s %s",
1055 yang_key2str(keyword), arg);
1056 goto done;
1057 }
1058 break;
1059 case Y_UNIQUE:{
1060 /* Unique: Sec 7.8.3 It takes as an argument a string that contains a space-
1061 separated list of schema node identifiers */
1062 if ((vec = clicon_strsep(arg, " \t\n", &nvec)) == NULL)
1063 goto done;
1064 for (i=0; i<nvec; i++){
1065 v = vec[i];
1066 if (yang_desc_schema_nodeid(yp, v, &yres) < 0)
1067 goto done;
1068 if (yres == NULL){
1069 clicon_err(OE_YANG, 0, "schemanode sanity check of %s %s",
1070 yang_key2str(yang_keyword_get(ys)), v);
1071 goto done;
1072 }
1073 }
1074 break;
1075 }
1076 case Y_DEVIATION:
1077 if (yang_abs_schema_nodeid(ys, arg, &yres) < 0)
1078 goto done;
1079 if (yres == NULL){
1080 clicon_err(OE_YANG, 0, "schemanode sanity check of %s", arg);
1081 goto done;
1082 }
1083 break;
1084 default:
1085 break;
1086 }
1087 retval = 0;
1088 done:
1089 if (vec)
1090 free(vec);
1091 return retval;
1092 }
1093
1094 /*! Check lists: non-config lists MUST have keys
1095 * @param[in] h Clicon handle
1096 * @param[in] ys Yang statement
1097 * Verify the following rule:
1098 * RFC 7950 7.8.2: The "key" statement, which MUST be present if the list represents
1099 * configuration and MAY be present otherwise
1100 * Unless CLICON_YANG_LIST_CHECK is false
1101 * OR it is the "errors" rule of the ietf-restconf spec which seems to be a special case.
1102 */
1103 static int
ys_list_check(clicon_handle h,yang_stmt * ys)1104 ys_list_check(clicon_handle h,
1105 yang_stmt *ys)
1106 {
1107 int retval = -1;
1108 yang_stmt *ymod;
1109 yang_stmt *yc = NULL;
1110 enum rfc_6020 keyw;
1111 yang_stmt *yroot;
1112
1113 /* This node is state, not config */
1114 if (yang_config_ancestor(ys) == 0)
1115 goto ok;
1116 /* Find root, examine if this node is part of a rpc declaration */
1117 if ((yroot = yang_myroot(ys)) != NULL &&
1118 yang_keyword_get(yroot) == Y_RPC)
1119 goto ok;
1120
1121 keyw = yang_keyword_get(ys);
1122 /* Check if list and if keys do not exist */
1123 if (keyw == Y_LIST &&
1124 yang_find(ys, Y_KEY, NULL) == 0){
1125 ymod = ys_module(ys);
1126 #if 1
1127 /* Except restconf error extension from sanity check, dont know why it has no keys */
1128 if (strcmp(yang_find_mynamespace(ys),"urn:ietf:params:xml:ns:yang:ietf-restconf")==0 &&
1129 strcmp(yang_argument_get(ys),"error") == 0)
1130 ;
1131 else
1132 #endif
1133 {
1134 if (clicon_option_bool(h, "CLICON_YANG_LIST_CHECK")){
1135 clicon_log(LOG_ERR, "Error: LIST \"%s\" in module \"%s\" lacks key statement which MUST be present (See RFC 7950 Sec 7.8.2)",
1136 yang_argument_get(ys),
1137 yang_argument_get(ymod)
1138 );
1139
1140 goto done;
1141 }
1142 else
1143 clicon_log(LOG_WARNING, "Warning: LIST \"%s\" in module \"%s\" lacks key statement which MUST be present (See RFC 7950 Sec 7.8.2)",
1144 yang_argument_get(ys),
1145 yang_argument_get(ymod)
1146 );
1147 }
1148 }
1149 /* Traverse subs */
1150 if (yang_schemanode(ys) || keyw == Y_MODULE || keyw == Y_SUBMODULE){
1151 yc = NULL;
1152 while ((yc = yn_each(ys, yc)) != NULL){
1153 if (ys_list_check(h, yc) < 0)
1154 goto done;
1155 }
1156 }
1157 ok:
1158 retval = 0;
1159 done:
1160 return retval;
1161 }
1162
1163 /*! Depth-first topological sort
1164 * Topological sort of a DAG
1165 * @param[in] yn Yang module node
1166 * @param[out] ylist Result list of sorted nodes with "least significant" first
1167 * @param[out] ylen Length of ylist
1168 * @retval 0 OK
1169 * @retval -1 Error. Eg circular
1170 * see eg https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
1171 */
1172 static int
ys_visit(struct yang_stmt * yn,struct yang_stmt *** ylist,int * ylen)1173 ys_visit(struct yang_stmt *yn,
1174 struct yang_stmt ***ylist,
1175 int *ylen)
1176 {
1177 int retval = -1;
1178 int i;
1179 struct yang_stmt *yi; /* import / include */
1180 struct yang_stmt *yspec;
1181 struct yang_stmt *ymod;
1182
1183 if (yn == NULL ||
1184 (yang_keyword_get(yn) != Y_MODULE && yang_keyword_get(yn) != Y_SUBMODULE)){
1185 clicon_err(OE_YANG, EINVAL, "Expected module or submodule");
1186 goto done;
1187 }
1188 yspec = ys_spec(yn);
1189 /* if n has a permanent mark then return */
1190 if (yang_flag_get(yn, YANG_FLAG_MARK))
1191 return 0;
1192 /* if n has a temporary mark then stop (not a DAG) */
1193 if (yang_flag_get(yn, YANG_FLAG_TMP)){
1194 clicon_err(OE_YANG, EFAULT, "Yang module %s import/include is circular", yang_argument_get(yn));
1195 goto done;
1196 }
1197 /* mark n with a temporary mark */
1198 yang_flag_set(yn, YANG_FLAG_TMP);
1199
1200 /* Loop through import and include statements and visit each */
1201 yi = NULL;
1202 for (i=0; i<yang_len_get(yn); i++){
1203 yi = yang_child_i(yn, i);
1204 if (yang_keyword_get(yi) != Y_IMPORT &&
1205 yang_keyword_get(yi) != Y_INCLUDE)
1206 continue;
1207 if ((ymod = yang_find(yspec, Y_MODULE, yang_argument_get(yi))) == NULL &&
1208 (ymod = yang_find(yspec, Y_SUBMODULE, yang_argument_get(yi))) == NULL){
1209 clicon_err(OE_YANG, EFAULT, "Yang module %s import/include not found",
1210 yang_argument_get(yi)); /* shouldnt happen */
1211 goto done;
1212 }
1213 if (ys_visit(ymod, ylist, ylen) < 0)
1214 goto done;
1215 }
1216 /* remove temporary mark from n */
1217 yang_flag_reset(yn, YANG_FLAG_TMP);
1218 /* mark n with a permanent mark */
1219 yang_flag_set(yn, YANG_FLAG_MARK);
1220 /* add n to head of L. NB reversed */
1221 (*ylen)++;
1222 if ((*ylist = realloc(*ylist, (*ylen)*sizeof(yang_stmt *))) == 0){
1223 clicon_err(OE_YANG, errno, "realloc");
1224 goto done;
1225 }
1226 (*ylist)[*ylen - 1] = yn;
1227 retval = 0;
1228 done:
1229 return retval;
1230 }
1231
1232 /*! Sort module/submodules according to import/include order and cycle detect
1233 * Topological sort of a DAG
1234 * @param[in] yspec Yang specification.
1235 * @param[in] modmin Start of interval of yspec:s module children
1236 * @param[in] modmax End of interval
1237 * @param[out] ylist Result list of sorted nodes with "least significant" first
1238 * @param[out] ylen Length of ylist
1239 * see eg https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
1240 */
1241 static int
yang_sort_modules(yang_stmt * yspec,int modmin,int modmax,struct yang_stmt *** ylist,int * ylen)1242 yang_sort_modules(yang_stmt *yspec,
1243 int modmin,
1244 int modmax,
1245 struct yang_stmt ***ylist,
1246 int *ylen)
1247 {
1248 int retval = -1;
1249 int i;
1250 struct yang_stmt *yn;
1251
1252
1253 for (i=modmin; i<modmax; i++){
1254 yn = yang_child_i(yspec, i);
1255 /* select an unmarked node n */
1256 if (yang_flag_get(yn, YANG_FLAG_MARK|YANG_FLAG_TMP) == 0){
1257 if (ys_visit(yn, ylist, ylen) < 0)
1258 goto done;
1259 }
1260 }
1261 if (*ylen != modmax-modmin){
1262 clicon_err(OE_YANG, EFAULT, "Internal error: mismatch sort vector lengths");
1263 }
1264 /* Unmark all nodes */
1265 for (i=modmin; i<modmax; i++){
1266 yn = yang_child_i(yspec, i);
1267 yang_flag_set(yn, YANG_FLAG_MARK|YANG_FLAG_TMP);
1268 }
1269 retval = 0;
1270 done:
1271 return retval;
1272 }
1273
1274 /*! Parse top yang module including all its sub-modules. Expand and populate yang tree
1275 *
1276 * Perform secondary actions after yang parsing. These actions cannot be made at
1277 * parse-time for various reasons:
1278 * - Detect imported yang specs that are not loaded and load and parse them too
1279 * - Check cardinality of yang (that nr of children match)
1280 * - Check features: remove disabled
1281 * - "Populate" yang, which means things like initiating caches, resolving references
1282 * - Resolve types
1283 * - Augments
1284 * - Defaults
1285 * There is some complexity in how modules are loaded vs how they need to be augmented
1286 * Therefore, after full loading, a topological sort is made to ensure the modules are
1287 * non-circular (a DAG) and that the rest of the operations are made in the topology order.
1288 * The loading order of the yang models (under yang spec) is kept.
1289 *
1290 * @param[in] h CLICON handle
1291 * @param[in] yspec Yang specification.
1292 * @param[in] modmin Perform checks after this number, prior are already complete
1293 * @retval 0 Everything OK
1294 * @retval -1 Error encountered
1295 */
1296 static int
yang_parse_post(clicon_handle h,yang_stmt * yspec,int modmin)1297 yang_parse_post(clicon_handle h,
1298 yang_stmt *yspec,
1299 int modmin)
1300 {
1301 int retval = -1;
1302 int i;
1303 int modmax;
1304 struct yang_stmt **ylist = NULL; /* Topology sorted modules */
1305 int ylen = 0; /* Length of ylist */
1306
1307 /* 1: Parse from text to yang parse-tree.
1308 * Iterate through modules and detect module/submodules to parse
1309 * NOTE: the list may grow on each iteration */
1310 for (i=modmin; i<yang_len_get(yspec); i++)
1311 if (yang_parse_recurse(h, yang_child_i(yspec, i), yspec) < 0)
1312 goto done;
1313
1314 modmax = yang_len_get(yspec);
1315 /* The set of modules [modmin..maxmax] is here complete wrt imports/includes and is a DAG
1316 * Example: A imports B, C and D, and C and D imports B
1317 * In some operations below (eg augment) need to be in topology order, eg B first.
1318 * Therefore the modules are sorted into a separate list that is used henceforth
1319 */
1320 if (yang_sort_modules(yspec, modmin, yang_len_get(yspec), &ylist, &ylen) < 0)
1321 goto done;
1322
1323 /* 2. Check cardinality a first time (done again last) */
1324 for (i=modmin; i<modmax; i++)
1325 if (yang_cardinality(h, yang_child_i(yspec, i), yang_argument_get(yspec->ys_stmt[i])) < 0)
1326 goto done;
1327
1328 /* 3: Check features/if-features: check if enabled and remove disabled features */
1329 for (i=modmin; i<modmax; i++)
1330 if (yang_features(h, yang_child_i(yspec, i)) < 0)
1331 goto done;
1332
1333 /* 4: Go through parse tree and populate it with cv types */
1334 for (i=modmin; i<modmax; i++){
1335 if (ys_populate(yang_child_i(yspec, i), h) < 0) /* Alt: make a yang_apply0 */
1336 goto done;
1337 if (yang_apply(yang_child_i(yspec, i), -1, ys_populate, (void*)h) < 0)
1338 goto done;
1339 }
1340
1341 /* 5: Resolve all types: populate type caches. Requires eg length/range cvecs
1342 * from ys_populate step.
1343 * Must be done using static binding.
1344 */
1345 for (i=modmin; i<modmax; i++)
1346 if (yang_apply(yang_child_i(yspec, i), Y_TYPE, ys_resolve_type, h) < 0)
1347 goto done;
1348
1349 /* Up to here resolving is made in the context they are defined, rather
1350 * than the context they are used (except for submodules being merged w
1351 * modules). Like static scoping.
1352 * After this we expand all grouping/uses and unfold all macros into a
1353 * single tree as they are used.
1354 */
1355
1356 /* 6: Macro expansion of all grouping/uses pairs. Expansion needs marking */
1357 for (i=0; i<ylen; i++){
1358 if (yang_expand_grouping(ylist[i]) < 0)
1359 goto done;
1360 yang_apply(ylist[i], -1, (yang_applyfn_t*)yang_flag_reset, (void*)YANG_FLAG_MARK);
1361 }
1362
1363 /* 7: Top-level augmentation of all modules.
1364 * Note: Clixon does not implement augment in USES
1365 * Note: There is an ordering problem, where an augment in one module depends on an augment in
1366 * another module not yet augmented.
1367 */
1368 for (i=0; i<ylen; i++)
1369 if (yang_augment_module(ylist[i]) < 0)
1370 goto done;
1371
1372 /* 4: Go through parse tree and do 2nd step populate (eg default) */
1373 for (i=0; i<ylen; i++)
1374 if (yang_apply(ylist[i], -1, ys_populate2, (void*)h) < 0)
1375 goto done;
1376
1377 /* 8: sanity checks of expanded yangs need more here */
1378 for (i=0; i<ylen; i++){
1379 /* Check schemanode references */
1380 if (yang_apply(ylist[i], -1, ys_schemanode_check, NULL) < 0)
1381 goto done;
1382 /* Check list key values */
1383 if (ys_list_check(h, ylist[i]) < 0)
1384 goto done;
1385 }
1386 /* 9. Check cardinality a second time after grouping/augment etc */
1387 for (i=0; i<ylen; i++)
1388 if (yang_cardinality(h, ylist[i], yang_argument_get(ylist[i])) < 0)
1389 goto done;
1390 retval = 0;
1391 done:
1392 if (ylist)
1393 free(ylist);
1394 return retval;
1395 }
1396
1397 /*! Parse yang specification and its dependencies recursively given module
1398 * @param[in] h clicon handle
1399 * @param[in] module Module name, or absolute filename (including dir)
1400 * @param[in] revision Revision, or NULL
1401 * @param[in] yspec Modules parse are added to this yangspec
1402 * @retval 0 OK
1403 * @retval -1 Error
1404 * @see yang_spec_parse_file
1405 */
1406 int
yang_spec_parse_module(clicon_handle h,const char * module,const char * revision,yang_stmt * yspec)1407 yang_spec_parse_module(clicon_handle h,
1408 const char *module,
1409 const char *revision,
1410 yang_stmt *yspec)
1411 {
1412 int retval = -1;
1413 int modmin; /* Existing number of modules */
1414 char *base = NULL;;
1415
1416 if (yspec == NULL){
1417 clicon_err(OE_YANG, EINVAL, "yang spec is NULL");
1418 goto done;
1419 }
1420 if (module == NULL){
1421 clicon_err(OE_YANG, EINVAL, "yang module not set");
1422 goto done;
1423 }
1424 /* Apply steps 2.. on new modules, ie ones after modmin. */
1425 modmin = yang_len_get(yspec);
1426 /* Do not load module if it already exists */
1427 if (yang_find(yspec, Y_MODULE, module) != NULL)
1428 goto ok;
1429 if (yang_parse_module(h, module, revision, yspec) == NULL)
1430 goto done;
1431 if (yang_parse_post(h, yspec, modmin) < 0)
1432 goto done;
1433 ok:
1434 retval = 0;
1435 done:
1436 if (base)
1437 free(base);
1438 return retval;
1439 }
1440
1441 /*! Parse yang specification and its dependencies recursively given filename
1442 * @param[in] h clicon handle
1443 * @param[in] filename Actual filename (including dir and revision)
1444 * @param[in] yspec Modules parse are added to this yangspec
1445 * @retval 0 OK
1446 * @retval -1 Error
1447 * @see yang_spec_parse_module for yang dir,module,revision instead of
1448 * actual filename
1449 * @see yang_spec_load_dir For loading all files in a directory
1450 */
1451 int
yang_spec_parse_file(clicon_handle h,char * filename,yang_stmt * yspec)1452 yang_spec_parse_file(clicon_handle h,
1453 char *filename,
1454 yang_stmt *yspec)
1455 {
1456 int retval = -1;
1457 int modmin; /* Existing number of modules */
1458 char *base = NULL;;
1459
1460 /* Apply steps 2.. on new modules, ie ones after modmin. */
1461 modmin = yang_len_get(yspec);
1462 /* Find module, and do not load file if module already exists */
1463 if (basename(filename) == NULL){
1464 clicon_err(OE_YANG, errno, "No basename");
1465 goto done;
1466 }
1467 if ((base = strdup(basename(filename))) == NULL){
1468 clicon_err(OE_YANG, errno, "strdup");
1469 goto done;
1470 }
1471 if (index(base, '@') != NULL)
1472 *index(base, '@') = '\0';
1473 if (yang_find(yspec, Y_MODULE, base) != NULL)
1474 goto ok;
1475 if (yang_parse_filename(filename, yspec) == NULL)
1476 goto done;
1477 if (yang_parse_post(h, yspec, modmin) < 0)
1478 goto done;
1479 ok:
1480 retval = 0;
1481 done:
1482 if (base)
1483 free(base);
1484 return retval;
1485 }
1486
1487 /*! Load all yang modules in directory
1488 * @param[in] h Clicon handle
1489 * @param[in] dir Load all yang modules in this directory
1490 * @param[in] yspec Modules parse are added to this yangspec
1491 * @retval 0 OK
1492 * @retval -1 Error
1493 * @see yang_spec_parse_file
1494 * Load all yang files in a directory as primary objects.
1495 * Some details if several same yang module x exists:
1496 * 1) If x is already loaded (eg via direct file loading) skip it
1497 * 2) Prefer x.yang over x@rev.yang (no revision)
1498 * 3) If only x@rev.yang's found, prefer newest (newest revision)
1499 * There is also an extra failsafe which may not be necessary, which removes
1500 * the oldest module if 1-3 for some reason fails.
1501 */
1502 int
yang_spec_load_dir(clicon_handle h,char * dir,yang_stmt * yspec)1503 yang_spec_load_dir(clicon_handle h,
1504 char *dir,
1505 yang_stmt *yspec)
1506 {
1507 int retval = -1;
1508 int ndp;
1509 struct dirent *dp = NULL;
1510 int i;
1511 int j;
1512 char filename[MAXPATHLEN];
1513 char *base = NULL; /* filename without dir */
1514 int modmin;
1515 yang_stmt *ym; /* yang module */
1516 yang_stmt *ym0; /* (existing) yang module */
1517 yang_stmt *yrev; /* yang revision */
1518 uint32_t revf = 0; /* revision in filename */
1519 uint32_t revm = 0; /* revision in parsed new module (should be same as revf) */
1520 uint32_t rev0; /* revision in existing module */
1521 char *oldbase = NULL;
1522 int taken = 0;
1523
1524 /* Get yang files names from yang module directory. Note that these
1525 * are sorted alphatetically:
1526 * a.yang,
1527 * a@2000-01-01.yang,
1528 * a@2111-11-11.yang
1529 */
1530 if((ndp = clicon_file_dirent(dir, &dp, "(.yang)$", S_IFREG)) < 0)
1531 goto done;
1532 if (ndp == 0)
1533 goto ok;
1534 /* Apply post steps on new modules, ie ones after modmin. */
1535 modmin = yang_len_get(yspec);
1536 /* Load all yang files in dir */
1537 for (i = 0; i < ndp; i++) {
1538 /* base = module name [+ @rev ] + .yang */
1539 if (oldbase)
1540 free(oldbase);
1541 oldbase = base;
1542 base = NULL;
1543 revf = 0;
1544 if (filename2revision(dp[i].d_name, &base, &revf) < 0)
1545 goto done;
1546 if (oldbase && strcmp(base, oldbase)) /* new yang file basename */
1547 taken = 0;
1548 if (revf == 0) /* No revision: a.yang - take that */
1549 taken = 1;
1550 else{ /* a@xxx.yang */
1551 if (taken)
1552 continue; /* skip if already taken */
1553 /* Look forward: is there anyone else later? (assume sorted revision dates) */
1554 if (i+1 < ndp){ /* not last in list */
1555 char *nextbase = NULL; /* XXX suboptimal algorithm, could combione old/next/base */
1556 if (filename2revision(dp[i+1].d_name, &nextbase, NULL) < 0)
1557 goto done;
1558 if (nextbase && strcmp(base, nextbase) == 0){
1559 free(nextbase);
1560 nextbase = NULL;
1561 continue; /* same base: skip; */
1562 }
1563 if (nextbase)
1564 free(nextbase);
1565 }
1566 taken = 1; /* last in line and not taken */
1567 }
1568 /* Here only a single file is reached(taken)
1569 * Check if module already exists -> ym0/rev0 */
1570 rev0 = 0;
1571 if ((ym0 = yang_find(yspec, Y_MODULE, base)) != NULL ||
1572 (ym0 = yang_find(yspec, Y_SUBMODULE, base)) != NULL){
1573 yrev = yang_find(ym0, Y_REVISION, NULL);
1574 rev0 = cv_uint32_get(yang_cv_get(yrev));
1575 continue; /* skip if already added by specific file or module */
1576 }
1577 /* Create full filename */
1578 snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
1579 if ((ym = yang_parse_filename(filename, yspec)) == NULL)
1580 goto done;
1581 revm = 0;
1582 if ((yrev = yang_find(ym, Y_REVISION, NULL)) != NULL)
1583 revm = cv_uint32_get(yang_cv_get(yrev));
1584 /* Sanity check that file revision does not match internal rev stmt */
1585 if (revf && revm && revm != revf){ /* XXX */
1586 clicon_err(OE_YANG, EINVAL, "Yang module file revision and in yang does not match: %s(%u) vs %u", filename, revf, revm);
1587 goto done;
1588 }
1589 /* If ym0 and ym exists, delete the yang with oldest revision
1590 * This is a failsafe in case anything else fails
1591 */
1592 if (revm && rev0){
1593 if (revm > rev0) /* Loaded module is older or eq -> remove ym */
1594 ym = ym0;
1595 for (j=0; j<yang_len_get(yspec); j++)
1596 if (yspec->ys_stmt[j] == ym)
1597 break;
1598 ys_prune(yspec, j);
1599 ys_free(ym);
1600 }
1601 }
1602 if (yang_parse_post(h, yspec, modmin) < 0)
1603 goto done;
1604 ok:
1605 retval = 0;
1606 done:
1607 if (dp)
1608 free(dp);
1609 if (base)
1610 free(base);
1611 if (oldbase)
1612 free(oldbase);
1613 return retval;
1614 }
1615
1616 /*! parse yang date-arg string and return a uint32 useful for arithmetics
1617 * @param[in] datearg yang revision string as "YYYY-MM-DD"
1618 * @param[out] dateint Integer version as YYYYMMDD
1619 * @retval 0 OK
1620 * @retval -1 Error, eg str is not on the format "YYYY-MM-DD"
1621 */
1622 int
ys_parse_date_arg(char * datearg,uint32_t * dateint)1623 ys_parse_date_arg(char *datearg,
1624 uint32_t *dateint)
1625 {
1626 int retval = -1;
1627 int i;
1628 uint32_t d = 0;
1629
1630 if (strlen(datearg) != 10 || datearg[4] != '-' || datearg[7] != '-'){
1631 clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", datearg);
1632 goto done;
1633 }
1634 if ((i = cligen_tonum(4, datearg)) < 0){
1635 clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", datearg);
1636 goto done;
1637 }
1638 d = i*10000; /* year */
1639 if ((i = cligen_tonum(2, &datearg[5])) < 0){
1640 clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", datearg);
1641 goto done;
1642 }
1643 d += i*100; /* month */
1644 if ((i = cligen_tonum(2, &datearg[8])) < 0){
1645 clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", datearg);
1646 goto done;
1647 }
1648 d += i; /* day */
1649 *dateint = d;
1650 retval = 0;
1651 done:
1652 return retval;
1653 }
1654
1655 /*! Parse argument as CV and save result in yang cv variable
1656 *
1657 * Note that some CV:s are parsed directly (eg fraction-digits) while others are parsed
1658 * in third pass (ys_populate). The reason being that all information is not
1659 * available in the first pass. Prefer to do stuff in ys_populate
1660 */
1661 cg_var *
ys_parse(yang_stmt * ys,enum cv_type cvtype)1662 ys_parse(yang_stmt *ys,
1663 enum cv_type cvtype)
1664 {
1665 int cvret;
1666 char *reason = NULL;
1667
1668 assert(yang_cv_get(ys) == NULL); /* Cv:s are parsed in different places, difficult to separate */
1669 if ((ys->ys_cv = cv_new(cvtype)) == NULL){
1670 clicon_err(OE_YANG, errno, "cv_new");
1671 goto done;
1672 }
1673 if ((cvret = cv_parse1(yang_argument_get(ys), ys->ys_cv, &reason)) < 0){ /* error */
1674 clicon_err(OE_YANG, errno, "parsing cv");
1675 ys->ys_cv = NULL;
1676 goto done;
1677 }
1678 if (cvret == 0){ /* parsing failed */
1679 clicon_err(OE_YANG, errno, "Parsing CV: %s", reason);
1680 ys->ys_cv = NULL;
1681 goto done;
1682 }
1683 /* cvret == 1 means parsing is OK */
1684 done:
1685 if (reason)
1686 free(reason);
1687 return ys->ys_cv;
1688 }
1689
1690 /*! First round yang syntactic statement specific checks. No context checks.
1691 *
1692 * Specific syntax checks and variable creation for stand-alone yang statements.
1693 * That is, siblings, etc, may not be there. Complete checks are made in
1694 * ys_populate instead.
1695 * @param[in] ys yang statement
1696 * @param[in] extra Yang extra for cornercases (unknown/extension). Is consumed
1697 *
1698 * The cv:s created in parse-tree as follows:
1699 * fraction-digits : Create cv as uint8, check limits [1:8] (must be made in 1st pass)
1700 * revision: cv as uint32 date: Integer version as YYYYMMDD
1701 * min-elements: cv as uint32
1702 * max-elements: cv as uint32, '0' means unbounded
1703 * @see ys_populate
1704 */
1705 int
ys_parse_sub(yang_stmt * ys,char * extra)1706 ys_parse_sub(yang_stmt *ys,
1707 char *extra)
1708 {
1709 int retval = -1;
1710 uint8_t fd;
1711 uint32_t date = 0;
1712 char *arg;
1713 enum rfc_6020 keyword;
1714 char *reason = NULL;
1715 int ret;
1716 uint32_t minmax;
1717
1718 arg = yang_argument_get(ys);
1719 keyword = yang_keyword_get(ys);
1720 switch (keyword){
1721 case Y_FRACTION_DIGITS:
1722 if (ys_parse(ys, CGV_UINT8) == NULL)
1723 goto done;
1724 fd = cv_uint8_get(ys->ys_cv);
1725 if (fd < 1 || fd > 18){
1726 clicon_err(OE_YANG, errno, "%u: Out of range, should be [1:18]", fd);
1727 goto done;
1728 }
1729 break;
1730 case Y_MUST:
1731 case Y_WHEN:
1732 if (xpath_parse(yang_argument_get(ys), NULL) < 0)
1733 goto done;
1734 break;
1735 case Y_REVISION:
1736 case Y_REVISION_DATE: /* YYYY-MM-DD encoded as uint32 YYYYMMDD */
1737 if (ys_parse_date_arg(arg, &date) < 0)
1738 goto done;
1739 if ((ys->ys_cv = cv_new(CGV_UINT32)) == NULL){
1740 clicon_err(OE_YANG, errno, "cv_new");
1741 goto done;
1742 }
1743 cv_uint32_set(ys->ys_cv, date);
1744 break;
1745 case Y_STATUS: /* RFC7950 7.21.2: "current", "deprecated", or "obsolete". */
1746 if (strcmp(arg, "current") &&
1747 strcmp(arg, "deprecated") &&
1748 strcmp(arg, "obsolete")){
1749 clicon_err(OE_YANG, errno, "Invalid status: \"%s\", expected current, deprecated, or obsolete", arg);
1750 goto done;
1751
1752 }
1753 break;
1754 case Y_MAX_ELEMENTS:
1755 case Y_MIN_ELEMENTS:
1756 if ((ys->ys_cv = cv_new(CGV_UINT32)) == NULL){
1757 clicon_err(OE_YANG, errno, "cv_new");
1758 goto done;
1759 }
1760 if (keyword == Y_MAX_ELEMENTS &&
1761 strcmp(arg, "unbounded") == 0)
1762 cv_uint32_set(ys->ys_cv, 0); /* 0 means unbounded for max */
1763 else{
1764 if ((ret = parse_uint32(arg, &minmax, &reason)) < 0){
1765 clicon_err(OE_YANG, errno, "parse_uint32");
1766 goto done;
1767 }
1768 if (ret == 0){
1769 clicon_err(OE_YANG, EINVAL, "element-min/max parse error: %s", reason);
1770 if (reason)
1771 free(reason);
1772 goto done;
1773 }
1774 cv_uint32_set(ys->ys_cv, minmax);
1775 }
1776 break;
1777 case Y_MODIFIER:
1778 if (strcmp(yang_argument_get(ys), "invert-match")){
1779 clicon_err(OE_YANG, EINVAL, "modifier %s, expected invert-match", yang_argument_get(ys));
1780 goto done;
1781 }
1782 break;
1783 case Y_UNKNOWN:{ /* save (optional) argument in ys_cv */
1784 if (extra == NULL)
1785 break;
1786 if ((ys->ys_cv = cv_new(CGV_STRING)) == NULL){
1787 clicon_err(OE_YANG, errno, "cv_new");
1788 goto done;
1789 }
1790 if ((ret = cv_parse1(extra, ys->ys_cv, &reason)) < 0){ /* error */
1791 clicon_err(OE_YANG, errno, "parsing cv");
1792 goto done;
1793 }
1794 if (ret == 0){ /* parsing failed */
1795 clicon_err(OE_YANG, errno, "Parsing CV: %s", reason);
1796 goto done;
1797 }
1798 break;
1799 }
1800 default:
1801 break;
1802 }
1803 retval = 0;
1804 done:
1805 if (extra)
1806 free(extra);
1807 return retval;
1808 }
1809