* Fixed: RFC 8040 yang-data extension allows non-key lists

* Added YANG_FLAG_NOKEY as exception to mandatory key lists
This commit is contained in:
Olof hagsand 2021-07-19 11:15:49 +02:00
parent 6bb6faadc9
commit e0ee365958
6 changed files with 45 additions and 24 deletions

View file

@ -42,6 +42,8 @@ Developers may need to change their code
### Corrected Bugs ### Corrected Bugs
* Fixed: RFC 8040 yang-data extension allows non-key lists
* Added YANG_FLAG_NOKEY as exception to mandatory key lists
* Fixed: mandatory leaf in a uses statement caused abort * Fixed: mandatory leaf in a uses statement caused abort
* Occurence was in ietf-yang-patch.yang * Occurence was in ietf-yang-patch.yang
* Native RESTCONF fixes for http/1 or http/2 only modes * Native RESTCONF fixes for http/1 or http/2 only modes

View file

@ -499,6 +499,14 @@ restconf_insert_attributes(cxobj *xdata,
* @param[in] ys Yang node of (unknown) statement belonging to extension * @param[in] ys Yang node of (unknown) statement belonging to extension
* @retval 0 OK, all callbacks executed OK * @retval 0 OK, all callbacks executed OK
* @retval -1 Error in one callback * @retval -1 Error in one callback
* @note This extension adds semantics to YANG according to RFC8040 as follows:
* - The list-stmt is not required to have a key-stmt defined.(NB!!)
* - The if-feature-stmt is ignored if present.
* - The config-stmt is ignored if present.
* - The available identity values for any 'identityref'
* leaf or leaf-list nodes are limited to the module
* containing this extension statement and the modules
* imported into that module.
*/ */
int int
restconf_main_extension_cb(clicon_handle h, restconf_main_extension_cb(clicon_handle h,
@ -522,6 +530,9 @@ restconf_main_extension_cb(clicon_handle h,
goto ok; goto ok;
if ((yn = ys_dup(yc)) == NULL) if ((yn = ys_dup(yc)) == NULL)
goto done; goto done;
/* yang-data extension: The list-stmt is not required to have a key-stmt defined.
*/
yang_flag_set(yn, YANG_FLAG_NOKEY);
if (yn_insert(yang_parent_get(ys), yn) < 0) if (yn_insert(yang_parent_get(ys), yn) < 0)
goto done; goto done;
ok: ok:

View file

@ -53,8 +53,12 @@
*/ */
#define YANG_FLAG_MARK 0x01 /* (Dynamic) marker for dynamic algorithms, eg expand and DAG */ #define YANG_FLAG_MARK 0x01 /* (Dynamic) marker for dynamic algorithms, eg expand and DAG */
#define YANG_FLAG_TMP 0x02 /* (Dynamic) marker for dynamic algorithms, eg DAG detection */ #define YANG_FLAG_TMP 0x02 /* (Dynamic) marker for dynamic algorithms, eg DAG detection */
#define YANG_FLAG_NOKEY 0x04 /* Key not mandatory in this list, see eg yang-data extension in
* RFC 8040 / ietf-restconf.yang
* see restconf_main_extension_cb
*/
#ifdef XML_EXPLICIT_INDEX #ifdef XML_EXPLICIT_INDEX
#define YANG_FLAG_INDEX 0x04 /* This yang node under list is (extra) index. --> you can access #define YANG_FLAG_INDEX 0x08 /* This yang node under list is (extra) index. --> you can access
* list elements using this index with binary search */ * list elements using this index with binary search */
#endif #endif

View file

@ -259,11 +259,15 @@ yang_cv_get(yang_stmt *ys)
/*! Set yang statement CLIgen variable /*! Set yang statement CLIgen variable
* @param[in] ys Yang statement node * @param[in] ys Yang statement node
* @param[in] cv cligen variable * @param[in] cv cligen variable
* @note: Frees on replace, not if cv is NULL. This is for some ys_cp/ys_dup cases, which means
* you need to free it explicitly to set it to NULL proper.
*/ */
int int
yang_cv_set(yang_stmt *ys, yang_cv_set(yang_stmt *ys,
cg_var *cv) cg_var *cv)
{ {
if (cv != NULL && ys->ys_cv != NULL)
cv_free(ys->ys_cv);
ys->ys_cv = cv; ys->ys_cv = cv;
return 0; return 0;
} }
@ -469,13 +473,14 @@ ys_free1(yang_stmt *ys,
int self) int self)
{ {
cg_var *cv; cg_var *cv;
if (ys->ys_argument){ if (ys->ys_argument){
free(ys->ys_argument); free(ys->ys_argument);
ys->ys_argument = NULL; ys->ys_argument = NULL;
} }
if ((cv = yang_cv_get(ys)) != NULL){ if ((cv = yang_cv_get(ys)) != NULL){
yang_cv_set(ys, NULL); /* only frees on replace */
cv_free(cv); cv_free(cv);
yang_cv_set(ys, NULL);
} }
if (ys->ys_cvec){ if (ys->ys_cvec){
cvec_free(ys->ys_cvec); cvec_free(ys->ys_cvec);
@ -629,6 +634,7 @@ ys_cp(yang_stmt *ynew,
clicon_err(OE_YANG, errno, "strdup"); clicon_err(OE_YANG, errno, "strdup");
goto done; goto done;
} }
yang_cv_set(ynew, NULL);
if ((cvo = yang_cv_get(yold)) != NULL){ if ((cvo = yang_cv_get(yold)) != NULL){
if ((cvn = cv_dup(cvo)) == NULL){ if ((cvn = cv_dup(cvo)) == NULL){
clicon_err(OE_YANG, errno, "cv_dup"); clicon_err(OE_YANG, errno, "cv_dup");
@ -1469,7 +1475,8 @@ ys_real_module(yang_stmt *ys,
* @param[in] ys Any yang statement in a yang tree * @param[in] ys Any yang statement in a yang tree
* @retval yspec The top yang specification * @retval yspec The top yang specification
* @see ys_module * @see ys_module
* @see yang_augment_node where shortcut is set * @see yang_augment_node where shortcut is set for augment
* @see yang_myroot for first node under (sub)module
*/ */
yang_stmt * yang_stmt *
ys_spec(yang_stmt *ys) ys_spec(yang_stmt *ys)

View file

@ -640,6 +640,9 @@ yang_expand_uses_node(yang_stmt *yn,
if (yang_when_nsc_set(yg, wnsc) < 0) if (yang_when_nsc_set(yg, wnsc) < 0)
goto done; goto done;
} }
/* This is for extensions that allow list keys to be optional, see restconf_main_extension_cb */
if (yang_flag_get(ys, YANG_FLAG_NOKEY))
yang_flag_set(yg, YANG_FLAG_NOKEY);
yn->ys_stmt[i+k] = yg; yn->ys_stmt[i+k] = yg;
yg->ys_parent = yn; yg->ys_parent = yn;
k++; k++;
@ -1196,7 +1199,7 @@ ys_schemanode_check(yang_stmt *ys,
* Verify the following rule: * Verify the following rule:
* RFC 7950 7.8.2: The "key" statement, which MUST be present if the list represents * RFC 7950 7.8.2: The "key" statement, which MUST be present if the list represents
* configuration and MAY be present otherwise * configuration and MAY be present otherwise
* Unless CLICON_YANG_LIST_CHECK is false * Unless CLICON_YANG_LIST_CHECK is false (obsolete)
* OR it is the "errors" rule of the ietf-restconf spec which seems to be a special case. * OR it is the "errors" rule of the ietf-restconf spec which seems to be a special case.
*/ */
static int static int
@ -1222,20 +1225,14 @@ ys_list_check(clicon_handle h,
if (keyw == Y_LIST && if (keyw == Y_LIST &&
yang_find(ys, Y_KEY, NULL) == 0){ yang_find(ys, Y_KEY, NULL) == 0){
ymod = ys_module(ys); ymod = ys_module(ys);
#if 1 /* Except nokey exceptions such as rrc 8040 yang-data */
/* Except restconf error extension from sanity check, dont know why it has no keys */ if (!yang_flag_get(yroot, YANG_FLAG_NOKEY)){
if (strcmp(yang_find_mynamespace(ys),"urn:ietf:params:xml:ns:yang:ietf-restconf")==0 && /* Note obsolete */
strcmp(yang_argument_get(ys),"error") == 0)
;
else
#endif
{
if (clicon_option_bool(h, "CLICON_YANG_LIST_CHECK")){ if (clicon_option_bool(h, "CLICON_YANG_LIST_CHECK")){
clicon_log(LOG_ERR, "Error: LIST \"%s\" in module \"%s\" lacks key statement which MUST be present (See RFC 7950 Sec 7.8.2)", clicon_log(LOG_ERR, "Error: LIST \"%s\" in module \"%s\" lacks key statement which MUST be present (See RFC 7950 Sec 7.8.2)",
yang_argument_get(ys), yang_argument_get(ys),
yang_argument_get(ymod) yang_argument_get(ymod)
); );
goto done; goto done;
} }
else else

View file

@ -40,7 +40,7 @@ function memonce(){
sudo chmod 660 $valgrindfile sudo chmod 660 $valgrindfile
sudo chown www-data $valgrindfile sudo chown www-data $valgrindfile
: ${DEMWAIT:=15} # valgrind backend needs some time to get up : ${DEMWAIT:=15} # valgrind backend needs some time to get up
clixon_restconf="/usr/bin/valgrind --leak-check=full --show-leak-kinds=all --suppressions=./valgrind-clixon.supp --track-fds=yes --trace-children=no --child-silent-after-fork=yes --log-file=$valgrindfile clixon_restconf" clixon_restconf="/usr/bin/valgrind --num-callers=50 --leak-check=full --show-leak-kinds=all --suppressions=./valgrind-clixon.supp --track-fds=yes --trace-children=no --child-silent-after-fork=yes --log-file=$valgrindfile clixon_restconf"
;; ;;
*) *)