Coverage Report

Created: 2025-08-03 06:36

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