Skip to content

Add CBOR Support to libyang (#2130)#2449

Open
MeherRushi wants to merge 7 commits intoCESNET:develfrom
MeherRushi:cbor_draft
Open

Add CBOR Support to libyang (#2130)#2449
MeherRushi wants to merge 7 commits intoCESNET:develfrom
MeherRushi:cbor_draft

Conversation

@MeherRushi
Copy link

@MeherRushi MeherRushi commented Nov 3, 2025

Summary

Adds CBOR (Concise Binary Object Representation) support to libyang, addressing #2130. This
enables parsing, validating, and printing instance data in CBOR format per RFC 9254, using [libcbor](https://github.com/PJK/libcbor) as the underlying library. CBOR support is optional — if libcbor is found at build time, support is compiled in automatically; otherwise it is silently skipped.

Changes

New files

  • CMakeModules/FindCBOR.cmake — CMake find module for libcbor dependency detection
  • src/lcbor.c / src/lcbor.h — libcbor abstraction layer (context management, type
    helpers)
  • src/parser_cbor.c — CBOR parser: constructs libyang data trees from CBOR input
  • src/printer_cbor.c — CBOR printer: serializes libyang data trees to CBOR output

Modified files

  • CMakeLists.txt — conditionally compile CBOR sources when libcbor is found
  • src/parser_internal.h — add lycbor_ctx to parser context union
  • src/tree_data.c — CBOR format handling in data tree operations
  • src/printer_cbor.c — migrated to current upstream API

New format type

  • LY_VALUE_CBOR added to the value format enum

Supported functionality

  • All YANG node types: leaf, leaf-list, list, container, anydata/anyxml, choice/case
  • Metadata parsing and printing
  • Opaque (unknown) nodes
  • RPC/action/notification/reply data trees
  • Named CBOR encoding (JSON-like keys)

PR review feedback addressed

  • Removed ENABLE_CBOR_SUPPORT option — libcbor is auto-detected via FindCBOR.cmake
    (no pkg-config dependency)
  • Removed file-level #ifdef guards — CMake handles conditional compilation
  • Removed LYD_ANYDATA_CBOR — uses LYD_ANYDATA_DATATREE as recommended
  • Removed all debug printf() statements and leftover artifacts
  • Reverted unintended parser_json.c changes — restored to upstream
  • Added lyd_parse_data_mem_len() high-level function for parsing CBOR from memory (required because CBOR binary data may contain null bytes, making strlen() unreliable)
  • Implemented consistent function structure mirroring JSON counterparts for parsing (name parsing, prefix handling, schema node lookup, metadata, opaque nodes, etc.)
  • Added proper error handling, null handling, array processing, and metadata support
  • Migrated parser_cbor.c to current upstream API:
    • lys_find_child_node instead of removed schema lookup functions
    • Updated LOGVAL calls with lnode parameter
    • Fixed LOG_LOCSET/LOG_LOCBACK to single-arg macros
    • Updated lyd_parser_create_term and lyd_parser_create_meta signatures
    • Updated lyd_create_any to new 7-arg signature
    • Removed lyplg_ext_insert (removed upstream)
    • Fixed lyd_node_any access (value.treechild)
    • LYD_PRINT_WITHSIBLINGSLYD_PRINT_SIBLINGS

Test plan

  • cmake configuration succeeds with libcbor installed
  • Full build completes without errors
  • Tested on Linux (build + runtime verification)

Future work

  • CBOR SID (Schema Item iDentifier) encoding support
  • Unit tests (to be added in follow-up)
  • Integration with yanglint CLI tool

Attempt to resolve #2130

Key Features

  • Optional Dependency: CBOR support is controlled via the compile-time flag ENABLE_CBOR_SUPPORT, allowing builds with or without CBOR capabilities
  • Full Parsing Support: Complete CBOR parser that constructs libyang data trees and links them with schema trees for validation
  • Full Printing Support: CBOR printer with parallel structure to printer_json.c, supporting all YANG node types (leaf, leaf-list, list, container, anydata, anyxml, RPC, action, notification)
  • Consistent API: Mirrors existing JSON functionality for ease of use and maintenance

TODO

  • CBOR SID support - Currently only named identifiers are supported; add SID (Schema Item iDentifier) support
  • Finalize input API - Determine optimal API design for CBOR input handling
  • Handle missing switch cases - Complete remaining switch statement cases - some warnings in cmake process
  • RPC/anydata edge cases - Address remaining edge cases for RPC and anydata handling

Dependencies

  • libcbor
  • same build processf

Testing :

Considered an example.json file :

{
  "ietf-interfaces:interfaces": {
    "interface": [
      {
        "name": "eth0",
        "description": "Loopback",
        "type": "iana-if-type:ethernetCsmacd",
        "enabled": true,
        "oper-status": "up",
        "statistics": {
          "discontinuity-time": "2024-01-01T00:00:00Z"
        }
      }
    ]
  }
}

Converted the same into cbor notation and output in example.cbor using standard cbor libraries in python (below code - json_to_cbor.py):

import json
import cbor2
import time

# Read the JSON file
with open("example.json", "r", encoding="utf-8") as f:
    json_data = json.load(f)

# Write CBOR-encoded output
with open("example.cbor", "wb") as f:
    cbor2.dump(json_data, f)

print("✅ example.cbor written from example.json")

time.sleep(5)

with open("example.cbor", "rb") as f:
    obj = cbor2.load(f)

print(json.dumps(obj, indent=2))

Now ran the following json_test.c on the json file with the necessary yang modules iana-if-type.yang and ietf-interfaces.yang in the directory to see functionality on the json data

#include <stdlib.h>
#include <libyang/libyang.h>

int main(void) {
    struct ly_ctx *ctx = NULL;
    struct lyd_node *data = NULL;

    // Context: search current dir only, no auto-loading
    if (ly_ctx_new(".", LY_CTX_NO_YANGLIBRARY, &ctx) != LY_SUCCESS) {
        fprintf(stderr, "Context creation failed.\n");
        return 1;
    }

    // Load only required official modules
    if (lys_parse_path(ctx, "iana-if-type.yang", LYS_IN_YANG, NULL) != LY_SUCCESS ||
        lys_parse_path(ctx, "ietf-interfaces.yang", LYS_IN_YANG, NULL) != LY_SUCCESS) {
        fprintf(stderr, "Module loading failed.\n");
        ly_ctx_destroy(ctx);
        return 1;
    }

    // Parse data
    if (lyd_parse_data_path(ctx, "example.json", LYD_JSON, LYD_PARSE_STRICT, 0, &data) != LY_SUCCESS) {
        fprintf(stderr, "JSON parsing failed.\n");
        ly_ctx_destroy(ctx);
        return 1;
    }

    // Print parsed data
    lyd_print_file(stdout, data, LYD_JSON, 0);

    // Cleanup
    lyd_free_all(data);
    ly_ctx_destroy(ctx);
    return 0;
}

Repeated the same with the cbor_test.c file (in the same directory) utilizing the cbor features introduced to test the newly built library

#include <stdlib.h>
#include <libyang/libyang.h>

int main(void) {
    struct ly_ctx *ctx = NULL;
    struct lyd_node *data = NULL;

    // Context: search current dir only, no auto-loading
    if (ly_ctx_new(".", LY_CTX_NO_YANGLIBRARY, &ctx) != LY_SUCCESS) {
        fprintf(stderr, "Context creation failed.\n");
        return 1;
    }

    // Load only required official modules
    if (lys_parse_path(ctx, "iana-if-type.yang", LYS_IN_YANG, NULL) != LY_SUCCESS ||
        lys_parse_path(ctx, "ietf-interfaces.yang", LYS_IN_YANG, NULL) != LY_SUCCESS) {
        fprintf(stderr, "Module loading failed.\n");
        ly_ctx_destroy(ctx);
        return 1;
    }

    // Parse data
    if (lyd_parse_data_path(ctx, "example.cbor", LYD_CBOR, LYD_PARSE_STRICT, 0, &data) != LY_SUCCESS) {
        fprintf(stderr, "CBOR parsing failed.\n");
        ly_ctx_destroy(ctx);
        return 1;
    }

    // Print parsed to file named "output.cbor"
    // output.cbor should be a file pointer opened in binary write mode
    FILE *output_file = fopen("output.cbor", "wb");
    if (!output_file) {
        fprintf(stderr, "Failed to open output.cbor for writing.\n");
        ly_ctx_destroy(ctx);
        return 1;
    }
    lyd_print_file(output_file, data, LYD_CBOR, 0);
    fclose(output_file);
    // lyd_print_file(stdout, data, LYD_JSON, 0);


    // Cleanup
    lyd_free_all(data);
    ly_ctx_destroy(ctx);
    return 0;
}

Following output:

$ gcc cbor_test.c -lyang -o cbor
$./cbor 
DEBUG: Parsed CBOR data:
{"ietf-interfaces:interfaces": {"interface": [{"name": "eth0", "description": "Loopback", "type": "iana-if-type:ethernetCsmacd", "enabled": true, "oper-status": "up", "statistics": {"discontinuity-time": "2024-01-01T00:00:00Z"}}]}}

And we can change the input file from example.cbor to output.cbor to see the parsed data being parsed properly

$ gcc cbor_test.c -lyang -o cbor
(base) meherrushi@MeherRushi:~/Meher/learning/c/cbor/test$ ./cbor 
DEBUG: Parsed CBOR data:
{"ietf-interfaces:interfaces": {"interface": [{"name": "eth0", "description": "Loopback", "type": "iana-if-type:ethernetCsmacd", "enabled": true, "oper-status": "up", "statistics": {"discontinuity-time": "2024-01-01T00:00:00+00:00"}}]}}

note: shall upload the test files on another repo

@michalvasko
Copy link
Member

Thanks a lot for this effort and I will try to provide some feedback today or tomorrow. But I can immediately tell you to change the libyang base branch to devel, PRs are never merged into master directly.

Copy link
Member

@michalvasko michalvasko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks fine but some improvements are possible and the coding style is somewhat different to ours.

@MeherRushi
Copy link
Author

@michalvasko Thank you for your reviews. Yup, we will correct as per the suggestions made and improve the coding style and make the PR to the devel branch. Sorry for the silly errors.

@MeherRushi MeherRushi changed the base branch from master to devel November 10, 2025 06:10
@irfanHaslanded
Copy link
Contributor

@MeherRushi I would like to know if this is still being worked on?

@MeherRushi
Copy link
Author

Yup, sorry for the delay. Will make the patches sometime soon

MeherRushi pushed a commit to MeherRushi/libyang that referenced this pull request Mar 14, 2026
…de, clean up ifdefs

  - Create CMakeModules/FindCBOR.cmake following FindXXHash pattern
  - Simplify CMakeLists.txt CBOR detection with find_package(CBOR)
  - Rename lcbor.c/lcbor.h to cbor.c/cbor.h
  - Remove file-level #ifdef ENABLE_CBOR_SUPPORT guards (handled by CMake)
  - Fix lycbor_ctx_free() to call cbor_decref() before free()
  - Remove #ifdef guards from parser_internal.h declarations
  - Remove all debug printf and print_json* functions
  - Remove LYD_ANYDATA_CBOR enum, use LYD_ANYDATA_STRING instead
@MeherRushi MeherRushi force-pushed the cbor_draft branch 4 times, most recently from 7c5feab to 1c78643 Compare March 14, 2026 07:37
- Add FindCBOR.cmake module for proper CBOR dependency detection
- Remove debug code and leftover artifacts
- Revert parser_json.c to upstream (undo unintended changes)
- Fix CBOR struct layout in parser_internal.h, tree_data.c, printer_cbor.c
- Migrate parser_cbor.c to current upstream API:
  - Rewrite lydcbor_get_snode to use lys_find_child_node
  - Add lnode parameter to all LOGVAL calls
  - Fix LOG_LOCSET/LOG_LOCBACK to single-arg macros
  - Fix lyd_parser_create_term signature (add parent, value_size_bits)
  - Fix lyd_parser_create_meta signature (add lnode, value_size_bits)
  - Fix lyd_create_any to new 7-arg signature
  - Remove lyplg_ext_insert (function removed upstream)
  - Fix lyd_node_any access (value.tree → child)
  - Add tree_schema_internal.h include
@MeherRushi MeherRushi marked this pull request as ready for review March 14, 2026 08:06
@MeherRushi MeherRushi changed the title Add CBOR Support to libyang (#2130) [Add CBOR Support to libyang ](feat: add CBOR data format support (RFC 9254)) (#2130) Mar 14, 2026
@MeherRushi MeherRushi changed the title [Add CBOR Support to libyang ](feat: add CBOR data format support (RFC 9254)) (#2130) Add CBOR Support to libyang (#2130) Mar 14, 2026
* @author MeherRushi <meherrrushi2@gmail.com>
* @brief CBOR data parser for libyang (abstraction over libcbor)
*
* Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just 2026 should work here and in the other new files.

Comment on lines +70 to +71
LY_ERR
lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cborctx_p);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functions declarations do not have a newline after the return value.

*
* @param[in] cborctx CBOR context to free.
*/
void lycbor_ctx_free(struct lycbor_ctx *cborctx)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functions definitions use a newline after the return value.

Comment on lines +49 to +53
/**
* @brief Free CBOR context.
*
* @param[in] cborctx CBOR context to free.
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-static functions have their doxygen comment exclusively with their declaration.

LY_ERR
lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cborctx_p)
{
/* TODO : Need to restructure error handling here */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix the TODO.

case LYD_LYB:
ret = lyb_print_data(out, root, options);
break;
#ifdef ENABLE_CBOR_SUPPORT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proper format-unsupported error would be better.

case LYD_LYB:
r = lyd_parse_lyb(ctx, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, &lydctx);
break;
#ifdef ENABLE_CBOR_SUPPORT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle the case, with error unsupported format if CBOR not supported.

case LYD_LYB:
rc = lyd_parse_lyb(ctx, parent, &first, in, parse_options, val_opts, int_opts, &parsed, &lydctx);
break;
#ifdef ENABLE_CBOR_SUPPORT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle the case, with error unsupported format if CBOR not supported.

LYD_ANYDATA_JSON, /**< Value is a string containing the data modeled by YANG and encoded as I-JSON. */
LYD_ANYDATA_LYB /**< Value is a memory chunk with the serialized data tree in LYB format. */
} LYD_ANYDATA_VALUETYPE;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not add this back, it is redundant in the current version.


find_package(CBOR)
if(CBOR_FOUND)
set(CBOR_SUPPORT ON)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like an unused variable.

@MeherRushi
Copy link
Author

@michalvasko Thanks for the review - will fix them soon

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CBOR support

3 participants