1 /*
2  * Copyright (C) 2018  NetDEF, Inc.
3  *                     Renato Westphal
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; see the file COPYING; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include <zebra.h>
21 
22 #include "log.h"
23 #include "lib_errors.h"
24 #include "hash.h"
25 #include "yang.h"
26 #include "yang_translator.h"
27 #include "frrstr.h"
28 
29 DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR, "YANG Translator")
30 DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR_MODULE, "YANG Translator Module")
31 DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR_MAPPING, "YANG Translator Mapping")
32 
33 /* Generate the yang_translators tree. */
yang_translator_compare(const struct yang_translator * a,const struct yang_translator * b)34 static inline int yang_translator_compare(const struct yang_translator *a,
35 					  const struct yang_translator *b)
36 {
37 	return strcmp(a->family, b->family);
38 }
39 RB_GENERATE(yang_translators, yang_translator, entry, yang_translator_compare)
40 
41 struct yang_translators yang_translators = RB_INITIALIZER(&yang_translators);
42 
43 /* Separate libyang context for the translator module. */
44 static struct ly_ctx *ly_translator_ctx;
45 
46 static unsigned int
47 yang_translator_validate(struct yang_translator *translator);
48 static unsigned int yang_module_nodes_count(const struct lys_module *module);
49 
50 struct yang_mapping_node {
51 	char xpath_from_canonical[XPATH_MAXLEN];
52 	char xpath_from_fmt[XPATH_MAXLEN];
53 	char xpath_to_fmt[XPATH_MAXLEN];
54 };
55 
yang_mapping_hash_cmp(const void * value1,const void * value2)56 static bool yang_mapping_hash_cmp(const void *value1, const void *value2)
57 {
58 	const struct yang_mapping_node *c1 = value1;
59 	const struct yang_mapping_node *c2 = value2;
60 
61 	return strmatch(c1->xpath_from_canonical, c2->xpath_from_canonical);
62 }
63 
yang_mapping_hash_key(const void * value)64 static unsigned int yang_mapping_hash_key(const void *value)
65 {
66 	return string_hash_make(value);
67 }
68 
yang_mapping_hash_alloc(void * p)69 static void *yang_mapping_hash_alloc(void *p)
70 {
71 	struct yang_mapping_node *new, *key = p;
72 
73 	new = XCALLOC(MTYPE_YANG_TRANSLATOR_MAPPING, sizeof(*new));
74 	strlcpy(new->xpath_from_canonical, key->xpath_from_canonical,
75 		sizeof(new->xpath_from_canonical));
76 
77 	return new;
78 }
79 
yang_mapping_hash_free(void * arg)80 static void yang_mapping_hash_free(void *arg)
81 {
82 	XFREE(MTYPE_YANG_TRANSLATOR_MAPPING, arg);
83 }
84 
85 static struct yang_mapping_node *
yang_mapping_lookup(const struct yang_translator * translator,int dir,const char * xpath)86 yang_mapping_lookup(const struct yang_translator *translator, int dir,
87 		    const char *xpath)
88 {
89 	struct yang_mapping_node s;
90 
91 	strlcpy(s.xpath_from_canonical, xpath, sizeof(s.xpath_from_canonical));
92 	return hash_lookup(translator->mappings[dir], &s);
93 }
94 
yang_mapping_add(struct yang_translator * translator,int dir,const struct lys_node * snode,const char * xpath_from_fmt,const char * xpath_to_fmt)95 static void yang_mapping_add(struct yang_translator *translator, int dir,
96 			     const struct lys_node *snode,
97 			     const char *xpath_from_fmt,
98 			     const char *xpath_to_fmt)
99 {
100 	struct yang_mapping_node *mapping, s;
101 
102 	yang_snode_get_path(snode, YANG_PATH_DATA, s.xpath_from_canonical,
103 			    sizeof(s.xpath_from_canonical));
104 	mapping = hash_get(translator->mappings[dir], &s,
105 			   yang_mapping_hash_alloc);
106 	strlcpy(mapping->xpath_from_fmt, xpath_from_fmt,
107 		sizeof(mapping->xpath_from_fmt));
108 	strlcpy(mapping->xpath_to_fmt, xpath_to_fmt,
109 		sizeof(mapping->xpath_to_fmt));
110 
111 	const char *keys[] = {"KEY1", "KEY2", "KEY3", "KEY4"};
112 	char *xpfmt;
113 
114 	for (unsigned int i = 0; i < array_size(keys); i++) {
115 		xpfmt = frrstr_replace(mapping->xpath_from_fmt, keys[i],
116 				       "%[^']");
117 		strlcpy(mapping->xpath_from_fmt, xpfmt,
118 			sizeof(mapping->xpath_from_fmt));
119 		XFREE(MTYPE_TMP, xpfmt);
120 	}
121 
122 	for (unsigned int i = 0; i < array_size(keys); i++) {
123 		xpfmt = frrstr_replace(mapping->xpath_to_fmt, keys[i], "%s");
124 		strlcpy(mapping->xpath_to_fmt, xpfmt,
125 			sizeof(mapping->xpath_to_fmt));
126 		XFREE(MTYPE_TMP, xpfmt);
127 	}
128 }
129 
yang_translator_load(const char * path)130 struct yang_translator *yang_translator_load(const char *path)
131 {
132 	struct yang_translator *translator;
133 	struct yang_tmodule *tmodule;
134 	const char *family;
135 	struct lyd_node *dnode;
136 	struct ly_set *set;
137 	struct listnode *ln;
138 
139 	/* Load module translator (JSON file). */
140 	dnode = lyd_parse_path(ly_translator_ctx, path, LYD_JSON,
141 			       LYD_OPT_CONFIG);
142 	if (!dnode) {
143 		flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
144 			  "%s: lyd_parse_path() failed", __func__);
145 		return NULL;
146 	}
147 	dnode = yang_dnode_get(dnode,
148 			       "/frr-module-translator:frr-module-translator");
149 	/*
150 	 * libyang guarantees the "frr-module-translator" top-level container is
151 	 * always present since it contains mandatory child nodes.
152 	 */
153 	assert(dnode);
154 
155 	family = yang_dnode_get_string(dnode, "./family");
156 	translator = yang_translator_find(family);
157 	if (translator != NULL) {
158 		flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
159 			  "%s: module translator \"%s\" is loaded already",
160 			  __func__, family);
161 		return NULL;
162 	}
163 
164 	translator = XCALLOC(MTYPE_YANG_TRANSLATOR, sizeof(*translator));
165 	strlcpy(translator->family, family, sizeof(translator->family));
166 	translator->modules = list_new();
167 	for (size_t i = 0; i < YANG_TRANSLATE_MAX; i++)
168 		translator->mappings[i] = hash_create(yang_mapping_hash_key,
169 						      yang_mapping_hash_cmp,
170 						      "YANG translation table");
171 	RB_INSERT(yang_translators, &yang_translators, translator);
172 
173 	/* Initialize the translator libyang context. */
174 	translator->ly_ctx = yang_ctx_new_setup(false);
175 	if (!translator->ly_ctx) {
176 		flog_warn(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
177 		goto error;
178 	}
179 
180 	/* Load modules and deviations. */
181 	set = lyd_find_path(dnode, "./module");
182 	assert(set);
183 	for (size_t i = 0; i < set->number; i++) {
184 		const char *module_name;
185 
186 		tmodule =
187 			XCALLOC(MTYPE_YANG_TRANSLATOR_MODULE, sizeof(*tmodule));
188 
189 		module_name = yang_dnode_get_string(set->set.d[i], "./name");
190 		tmodule->module = ly_ctx_load_module(translator->ly_ctx,
191 						     module_name, NULL);
192 		if (!tmodule->module) {
193 			flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
194 				  "%s: failed to load module: %s", __func__,
195 				  module_name);
196 			ly_set_free(set);
197 			goto error;
198 		}
199 
200 		module_name =
201 			yang_dnode_get_string(set->set.d[i], "./deviations");
202 		tmodule->deviations = ly_ctx_load_module(translator->ly_ctx,
203 							 module_name, NULL);
204 		if (!tmodule->deviations) {
205 			flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
206 				  "%s: failed to load module: %s", __func__,
207 				  module_name);
208 			ly_set_free(set);
209 			goto error;
210 		}
211 		lys_set_disabled(tmodule->deviations);
212 
213 		listnode_add(translator->modules, tmodule);
214 	}
215 	ly_set_free(set);
216 
217 	/* Calculate the coverage. */
218 	for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) {
219 		tmodule->nodes_before_deviations =
220 			yang_module_nodes_count(tmodule->module);
221 
222 		lys_set_enabled(tmodule->deviations);
223 
224 		tmodule->nodes_after_deviations =
225 			yang_module_nodes_count(tmodule->module);
226 		tmodule->coverage = ((double)tmodule->nodes_after_deviations
227 				     / (double)tmodule->nodes_before_deviations)
228 				    * 100;
229 	}
230 
231 	/* Load mappings. */
232 	set = lyd_find_path(dnode, "./module/mappings");
233 	assert(set);
234 	for (size_t i = 0; i < set->number; i++) {
235 		const char *xpath_custom, *xpath_native;
236 		const struct lys_node *snode_custom, *snode_native;
237 
238 		xpath_custom = yang_dnode_get_string(set->set.d[i], "./custom");
239 		snode_custom = ly_ctx_get_node(translator->ly_ctx, NULL,
240 					       xpath_custom, 0);
241 		if (!snode_custom) {
242 			flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
243 				  "%s: unknown data path: %s", __func__,
244 				  xpath_custom);
245 			ly_set_free(set);
246 			goto error;
247 		}
248 
249 		xpath_native = yang_dnode_get_string(set->set.d[i], "./native");
250 		snode_native =
251 			ly_ctx_get_node(ly_native_ctx, NULL, xpath_native, 0);
252 		if (!snode_native) {
253 			flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
254 				  "%s: unknown data path: %s", __func__,
255 				  xpath_native);
256 			ly_set_free(set);
257 			goto error;
258 		}
259 
260 		yang_mapping_add(translator, YANG_TRANSLATE_TO_NATIVE,
261 				 snode_custom, xpath_custom, xpath_native);
262 		yang_mapping_add(translator, YANG_TRANSLATE_FROM_NATIVE,
263 				 snode_native, xpath_native, xpath_custom);
264 	}
265 	ly_set_free(set);
266 
267 	/* Validate mappings. */
268 	if (yang_translator_validate(translator) != 0)
269 		goto error;
270 
271 	yang_dnode_free(dnode);
272 
273 	return translator;
274 
275 error:
276 	yang_dnode_free(dnode);
277 	yang_translator_unload(translator);
278 
279 	return NULL;
280 }
281 
yang_tmodule_delete(struct yang_tmodule * tmodule)282 static void yang_tmodule_delete(struct yang_tmodule *tmodule)
283 {
284 	XFREE(MTYPE_YANG_TRANSLATOR_MODULE, tmodule);
285 }
286 
yang_translator_unload(struct yang_translator * translator)287 void yang_translator_unload(struct yang_translator *translator)
288 {
289 	for (size_t i = 0; i < YANG_TRANSLATE_MAX; i++)
290 		hash_clean(translator->mappings[i], yang_mapping_hash_free);
291 	translator->modules->del = (void (*)(void *))yang_tmodule_delete;
292 	list_delete(&translator->modules);
293 	ly_ctx_destroy(translator->ly_ctx, NULL);
294 	RB_REMOVE(yang_translators, &yang_translators, translator);
295 	XFREE(MTYPE_YANG_TRANSLATOR, translator);
296 }
297 
yang_translator_find(const char * family)298 struct yang_translator *yang_translator_find(const char *family)
299 {
300 	struct yang_translator s;
301 
302 	strlcpy(s.family, family, sizeof(s.family));
303 	return RB_FIND(yang_translators, &yang_translators, &s);
304 }
305 
306 enum yang_translate_result
yang_translate_xpath(const struct yang_translator * translator,int dir,char * xpath,size_t xpath_len)307 yang_translate_xpath(const struct yang_translator *translator, int dir,
308 		     char *xpath, size_t xpath_len)
309 {
310 	struct ly_ctx *ly_ctx;
311 	const struct lys_node *snode;
312 	struct yang_mapping_node *mapping;
313 	char xpath_canonical[XPATH_MAXLEN];
314 	char keys[4][LIST_MAXKEYLEN];
315 	int n;
316 
317 	if (dir == YANG_TRANSLATE_TO_NATIVE)
318 		ly_ctx = translator->ly_ctx;
319 	else
320 		ly_ctx = ly_native_ctx;
321 
322 	snode = ly_ctx_get_node(ly_ctx, NULL, xpath, 0);
323 	if (!snode) {
324 		flog_warn(EC_LIB_YANG_TRANSLATION_ERROR,
325 			  "%s: unknown data path: %s", __func__, xpath);
326 		return YANG_TRANSLATE_FAILURE;
327 	}
328 
329 	yang_snode_get_path(snode, YANG_PATH_DATA, xpath_canonical,
330 			    sizeof(xpath_canonical));
331 	mapping = yang_mapping_lookup(translator, dir, xpath_canonical);
332 	if (!mapping)
333 		return YANG_TRANSLATE_NOTFOUND;
334 
335 	n = sscanf(xpath, mapping->xpath_from_fmt, keys[0], keys[1], keys[2],
336 		   keys[3]);
337 	if (n < 0) {
338 		flog_warn(EC_LIB_YANG_TRANSLATION_ERROR,
339 			  "%s: sscanf() failed: %s", __func__,
340 			  safe_strerror(errno));
341 		return YANG_TRANSLATE_FAILURE;
342 	}
343 
344 	snprintf(xpath, xpath_len, mapping->xpath_to_fmt, keys[0], keys[1],
345 		 keys[2], keys[3]);
346 
347 	return YANG_TRANSLATE_SUCCESS;
348 }
349 
yang_translate_dnode(const struct yang_translator * translator,int dir,struct lyd_node ** dnode)350 int yang_translate_dnode(const struct yang_translator *translator, int dir,
351 			 struct lyd_node **dnode)
352 {
353 	struct ly_ctx *ly_ctx;
354 	struct lyd_node *new;
355 	struct lyd_node *root, *next, *dnode_iter;
356 
357 	/* Create new libyang data node to hold the translated data. */
358 	if (dir == YANG_TRANSLATE_TO_NATIVE)
359 		ly_ctx = ly_native_ctx;
360 	else
361 		ly_ctx = translator->ly_ctx;
362 	new = yang_dnode_new(ly_ctx, false);
363 
364 	/* Iterate over all nodes from the data tree. */
365 	LY_TREE_FOR (*dnode, root) {
366 		LY_TREE_DFS_BEGIN (root, next, dnode_iter) {
367 			char xpath[XPATH_MAXLEN];
368 			enum yang_translate_result ret;
369 
370 			yang_dnode_get_path(dnode_iter, xpath, sizeof(xpath));
371 			ret = yang_translate_xpath(translator, dir, xpath,
372 						   sizeof(xpath));
373 			switch (ret) {
374 			case YANG_TRANSLATE_SUCCESS:
375 				break;
376 			case YANG_TRANSLATE_NOTFOUND:
377 				goto next;
378 			case YANG_TRANSLATE_FAILURE:
379 				goto error;
380 			}
381 
382 			/* Create new node in the tree of translated data. */
383 			ly_errno = 0;
384 			if (!lyd_new_path(new, ly_ctx, xpath,
385 					  (void *)yang_dnode_get_string(
386 						  dnode_iter, NULL),
387 					  0, LYD_PATH_OPT_UPDATE)
388 			    && ly_errno) {
389 				flog_err(EC_LIB_LIBYANG,
390 					 "%s: lyd_new_path() failed", __func__);
391 				goto error;
392 			}
393 
394 		next:
395 			LY_TREE_DFS_END(root, next, dnode_iter);
396 		}
397 	}
398 
399 	/* Replace dnode by the new translated dnode. */
400 	yang_dnode_free(*dnode);
401 	*dnode = new;
402 
403 	return YANG_TRANSLATE_SUCCESS;
404 
405 error:
406 	yang_dnode_free(new);
407 
408 	return YANG_TRANSLATE_FAILURE;
409 }
410 
411 struct translator_validate_args {
412 	struct yang_translator *translator;
413 	unsigned int errors;
414 };
415 
yang_translator_validate_cb(const struct lys_node * snode_custom,void * arg)416 static int yang_translator_validate_cb(const struct lys_node *snode_custom,
417 				       void *arg)
418 {
419 	struct translator_validate_args *args = arg;
420 	struct yang_mapping_node *mapping;
421 	const struct lys_node *snode_native;
422 	const struct lys_type *stype_custom, *stype_native;
423 	char xpath[XPATH_MAXLEN];
424 
425 	yang_snode_get_path(snode_custom, YANG_PATH_DATA, xpath, sizeof(xpath));
426 	mapping = yang_mapping_lookup(args->translator,
427 				      YANG_TRANSLATE_TO_NATIVE, xpath);
428 	if (!mapping) {
429 		flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
430 			  "%s: missing mapping for \"%s\"", __func__, xpath);
431 		args->errors += 1;
432 		return YANG_ITER_CONTINUE;
433 	}
434 
435 	snode_native =
436 		ly_ctx_get_node(ly_native_ctx, NULL, mapping->xpath_to_fmt, 0);
437 	assert(snode_native);
438 
439 	/* Check if the YANG types are compatible. */
440 	stype_custom = yang_snode_get_type(snode_custom);
441 	stype_native = yang_snode_get_type(snode_native);
442 	if (stype_custom && stype_native) {
443 		if (stype_custom->base != stype_native->base) {
444 			flog_warn(
445 				EC_LIB_YANG_TRANSLATOR_LOAD,
446 				"%s: YANG types are incompatible (xpath: \"%s\")",
447 				__func__, xpath);
448 			args->errors += 1;
449 			return YANG_ITER_CONTINUE;
450 		}
451 
452 		/* TODO: check if the value spaces are identical. */
453 	}
454 
455 	return YANG_ITER_CONTINUE;
456 }
457 
458 /*
459  * Check if the modules from the translator have a mapping for all of their
460  * schema nodes (after loading the deviations).
461  */
yang_translator_validate(struct yang_translator * translator)462 static unsigned int yang_translator_validate(struct yang_translator *translator)
463 {
464 	struct yang_tmodule *tmodule;
465 	struct listnode *ln;
466 	struct translator_validate_args args;
467 
468 	args.translator = translator;
469 	args.errors = 0;
470 
471 	for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) {
472 		yang_snodes_iterate_module(
473 			tmodule->module, yang_translator_validate_cb,
474 			YANG_ITER_FILTER_NPCONTAINERS
475 				| YANG_ITER_FILTER_LIST_KEYS
476 				| YANG_ITER_FILTER_INPUT_OUTPUT,
477 			&args);
478 	}
479 
480 	if (args.errors)
481 		flog_warn(
482 			EC_LIB_YANG_TRANSLATOR_LOAD,
483 			"%s: failed to validate \"%s\" module translator: %u error(s)",
484 			__func__, translator->family, args.errors);
485 
486 	return args.errors;
487 }
488 
yang_module_nodes_count_cb(const struct lys_node * snode,void * arg)489 static int yang_module_nodes_count_cb(const struct lys_node *snode, void *arg)
490 {
491 	unsigned int *total = arg;
492 
493 	*total += 1;
494 
495 	return YANG_ITER_CONTINUE;
496 }
497 
498 /* Calculate the number of nodes for the given module. */
yang_module_nodes_count(const struct lys_module * module)499 static unsigned int yang_module_nodes_count(const struct lys_module *module)
500 {
501 	unsigned int total = 0;
502 
503 	yang_snodes_iterate_module(module, yang_module_nodes_count_cb,
504 				   YANG_ITER_FILTER_NPCONTAINERS
505 					   | YANG_ITER_FILTER_LIST_KEYS
506 					   | YANG_ITER_FILTER_INPUT_OUTPUT,
507 				   &total);
508 
509 	return total;
510 }
511 
yang_translator_init(void)512 void yang_translator_init(void)
513 {
514 	ly_translator_ctx = yang_ctx_new_setup(true);
515 	if (!ly_translator_ctx) {
516 		flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
517 		exit(1);
518 	}
519 
520 	if (!ly_ctx_load_module(ly_translator_ctx, "frr-module-translator",
521 				NULL)) {
522 		flog_err(
523 			EC_LIB_YANG_MODULE_LOAD,
524 			"%s: failed to load the \"frr-module-translator\" module",
525 			__func__);
526 		exit(1);
527 	}
528 }
529 
yang_translator_terminate(void)530 void yang_translator_terminate(void)
531 {
532 	while (!RB_EMPTY(yang_translators, &yang_translators)) {
533 		struct yang_translator *translator;
534 
535 		translator = RB_ROOT(yang_translators, &yang_translators);
536 		yang_translator_unload(translator);
537 	}
538 
539 	ly_ctx_destroy(ly_translator_ctx, NULL);
540 }
541