diff --git a/.github/workflows/actions-compile.yml b/.github/workflows/actions-compile.yml index ef80dcb..985daa5 100644 --- a/.github/workflows/actions-compile.yml +++ b/.github/workflows/actions-compile.yml @@ -77,5 +77,7 @@ jobs: echo "Failed: did not exit with code 1." exit 1 fi + - name: run iproute2-sysrepo tests + run : chmod +x tests/run_tests.sh && sudo ./tests/run_tests.sh - name: yanglint run : make check diff --git a/Makefile b/Makefile index f9c5c9e..c5a59cd 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ EXEC = iproute2-sysrepo BIN = bin CC ?= gcc -LDFLAGS = -lsysrepo -lbpf -lelf -lmnl -lbsd -lcap -lselinux -lm -ldl -rdynamic -L/usr/local/lib +LDFLAGS = -lyang -lsysrepo -lbpf -lelf -lmnl -lbsd -lcap -lselinux -lm -ldl -rdynamic -L/usr/local/lib SUBDIRS = iproute2 IPR2_SR_LIB_SRC = $(wildcard src/lib/*.c) @@ -65,3 +65,4 @@ $(IPR2_SR_LIB_OBJ): $(IPR2_SR_LIB_SRC) $(IPR2_SR_OBJ): $(IPR2_SR_SRC) $(CC) -c $< -o $@ -Iiproute2/ip -Iiproute2/bridge -Iiproute2/tc -Iiproute2/include + diff --git a/src/iproute2_sysrepo.c b/src/iproute2_sysrepo.c index c4b104f..b0f07c0 100644 --- a/src/iproute2_sysrepo.c +++ b/src/iproute2_sysrepo.c @@ -353,12 +353,30 @@ int ip_sr_config_change_cb_apply(const struct lyd_node *change_dnode) } ipr2_cmds = lyd2cmd_argv(change_dnode); - + if (ipr2_cmds == NULL){ + fprintf(stderr, + "%s: failed to generate commands for the change \n", + __func__); + return EXIT_FAILURE; + } for (int i = 0; ipr2_cmds[i] != NULL; i++) { + fprintf(stdout,"%s: executing command: ",__func__ ); + for (int k = 0; k < ipr2_cmds[i]->argc; k++) { + printf("%s", ipr2_cmds[i]->argv[k]); + if (i < ipr2_cmds[i]->argc - 1) { + printf(" "); + } + } + fprintf(stdout,"\n"); + ret = do_cmd(ipr2_cmds[i]->argc, ipr2_cmds[i]->argv); - if (ret != EXIT_SUCCESS)// TODO: add rollback functionality. + if (ret != EXIT_SUCCESS) { + // TODO: add rollback functionality. + free(ipr2_cmds); return SR_ERR_INTERNAL; + } } + free(ipr2_cmds); return SR_ERR_OK; } @@ -369,24 +387,49 @@ int ip_sr_config_change_cb(sr_session_ctx_t *session, uint32_t sub_id, sr_event_t sr_ev, uint32_t request_id, void *private_data) { + sr_change_iter_t *it; int ret; + struct lyd_node *root_dnode; const struct lyd_node *dnode; + sr_change_oper_t sr_op; + + ret = sr_get_changes_iter(session, "//*", &it); + if (ret != SR_ERR_OK) { + fprintf(stderr, + "%s: sr_get_changes_iter() failed for \"%s\"", + __func__, module_name); + return ret; + } + + // we don't iterate through all changes, we just get the first changed node, then find it's root. + // this root will have all the changes. NOTE: this approach will not work if the yang has more than + // root containers. which is not the case in iproute2-yang modules. + ret = sr_get_change_tree_next(session, it, &sr_op, + &dnode, NULL, + NULL, NULL); + if (ret != SR_ERR_OK){ + fprintf(stderr, "%s: failed to get next change node: %s\n", + __func__ ,sr_strerror(ret)); + return SR_ERR_INTERNAL; + } + + root_dnode = (struct lyd_node *) dnode; + while (root_dnode->parent != NULL) + root_dnode = (struct lyd_node *) root_dnode->parent; + switch (sr_ev) { case SR_EV_ENABLED: case SR_EV_CHANGE: - dnode = sr_get_changes(session); - ret = ip_sr_config_change_cb_prepare(dnode); + ret = ip_sr_config_change_cb_apply(root_dnode); break; case SR_EV_DONE: - dnode = sr_get_change_diff(session); - ret = ip_sr_config_change_cb_apply(dnode); - break; + // TODO: add cleanup functionality. case SR_EV_ABORT: // TODO: add abort event functionality. case SR_EV_RPC: // TODO: rpc support for iproute2 commands. case SR_EV_UPDATE: - return 0; + return SR_ERR_OK; default: fprintf(stderr, "%s: unexpected sysrepo event: %u\n", __func__, sr_ev); diff --git a/src/lib/iproute2_cmdgen.c b/src/lib/iproute2_cmdgen.c index c6cd642..f3cac21 100644 --- a/src/lib/iproute2_cmdgen.c +++ b/src/lib/iproute2_cmdgen.c @@ -11,11 +11,336 @@ * Copyright (C) 2024 Okda Networks, */ +#include +#include + #include "iproute2_cmdgen.h" -// not implemented yet +#define CMD_LINE_SIZE 1024 + +typedef enum { + ADD_OPR, + DELETE_OPR, + UPDATE_OPR, + UNKNOWN_OPR, +} oper_t; + +typedef enum { + // list extensions + CMD_START_EXT, + CMD_ADD_EXT, + CMD_DELETE_EXT, + CMD_UPDATE_EXT, + + // leaf extensions + ARG_NAME_EXT, + FLAG_EXT, + VALUE_ONLY_EXT, + VALUE_ONLY_ON_UPDATE_EXT + + +} extension_t; + +char *yang_ext_map[] = { + [CMD_START_EXT] = "cmd-start", + [CMD_ADD_EXT] = "cmd-add", + [CMD_DELETE_EXT] = "cmd-delete", + [CMD_UPDATE_EXT] = "cmd-update", + + // leaf extensions + [ARG_NAME_EXT] = "arg-name", + [FLAG_EXT] = "flag", + [VALUE_ONLY_EXT] = "value-only", + [VALUE_ONLY_ON_UPDATE_EXT] = "value-only-on-update", +}; + +void dup_argv(char ***dest, char **src, int argc) +{ + *dest = (char **) malloc(argc * sizeof(char *)); + if (*dest == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(EXIT_FAILURE); + } + for (int i = 0; i < argc; i++) { + (*dest)[i] = strdup(src[i]); + if ((*dest)[i] == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(EXIT_FAILURE); + } + } +} + + +void free_argv(char **argv, int argc) +{ + // Free memory for each string + for (int i = 0; i < argc; i++) { + free(argv[i]); + argv[i] = NULL; + } + // Free memory for the array of pointers + free(argv); +} + +/* + * if input is "iproute2-ip-link:dummy" it return "dummy" + */ +char *strip_yang_iden_prefix(const char *input) +{ + const char *colon_pos = strchr(input, ':'); + if (colon_pos != NULL) { + // Calculate the length of the substring after the colon + size_t len = strlen(colon_pos + 1); + + // Allocate memory for output string + char *output = malloc(len + 1); + if (output == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(EXIT_FAILURE); + } + + // Copy the substring after the colon. + strlcpy(output, colon_pos + 1, len + 1); + + // Remove leading and trailing whitespace + char *end = output + len - 1; + while (end > output && isspace((unsigned char) *end)) { + *end-- = '\0'; + } + while (*output && isspace((unsigned char) *output)) { + ++output; + } + return output; + } else { + // No colon found, return the original string + return strdup(input); + } +} + + +oper_t get_operation(struct lyd_node *dnode) +{ + + struct lyd_meta *next; + const char *operation; + LY_LIST_FOR(dnode->meta, next) + { + if (!strcmp("operation", next->name)) { + operation = lyd_get_meta_value(next); + if (!strcmp("create", operation)) + return ADD_OPR; + if (!strcmp("delete", operation)) + return DELETE_OPR; + if (!strcmp("replace", operation) || !strcmp("none", operation))// for updated list the operation is none. + return UPDATE_OPR; + } + } + return UNKNOWN_OPR; +} + +/* + * return true if the node has ir2cgen:cmd-start extension. + */ +int is_startcmd_node(struct lyd_node *dnode) +{ + const struct lysc_node *schema = dnode->schema; + LY_ARRAY_COUNT_TYPE i; + LY_ARRAY_FOR(schema->exts, i) + { + if (schema->exts != NULL) { + if (!strcmp(schema->exts[i].def->name, "cmd-start")) + return 1; + } + } + return 0; +} + +/* + * this function search if the provided ex_t exist in the node, + * if not exist it return EXIT_FAILURE, if exist return EXIT_SUCCESS, + * if value is not NULL, the extension value will be dup to value. + */ +int get_extension(extension_t ex_t, const struct lyd_node *dnode, char **value) +{ + struct lysc_ext_instance *ys_extenstions = dnode->schema->exts; + if (ys_extenstions == NULL) + return EXIT_FAILURE; + + LY_ARRAY_COUNT_TYPE i; + LY_ARRAY_FOR(ys_extenstions, i) + { + if (!strcmp(ys_extenstions[i].def->name, yang_ext_map[ex_t])) { + if (value != NULL) + *value = strdup(ys_extenstions[i].argument); + return EXIT_SUCCESS; + } + } + return EXIT_FAILURE; +} + +/* + * this function will convert the command line string to argc, argv + */ +void parse_command(const char *command, int *argc, char ***argv) +{ + // Count the number of space-separated tokens to determine argc + *argc = 1; + for (const char *ptr = command; *ptr; ++ptr) { + if (*ptr == ' ') { + (*argc)++; + // Skip consecutive spaces + while (*(ptr + 1) == ' ') { + ptr++; + } + } + } + + // Allocate memory for argv + *argv = (char **) malloc((*argc) * sizeof(char *)); + if (*argv == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(EXIT_FAILURE); + } + + // Tokenize the command string and populate argv + char *token; + int i = 0; + char *cmd_copy = strdup(command);// We duplicate the command string as strtok modifies the original string + token = strtok(cmd_copy, " "); + while (token != NULL) { + (*argv)[i] = strdup(token); + token = strtok(NULL, " "); + i++; + } + free(cmd_copy); +} + +void add_command(char *cmd_line, struct cmd_args **cmds, int *cmd_idx, char **oper2cmd_prefix) +{ + int argc; + char **argv; + parse_command(cmd_line, &argc, &argv); + if (*cmd_idx >= CMDS_ARRAY_SIZE) { + fprintf(stderr, "process_command: cmds exceeded MAX allowed commands per transaction\n"); + free_argv(argv, argc); + return; + } + (cmds)[*cmd_idx] = malloc(sizeof(struct cmd_args)); + + if ((cmds)[*cmd_idx] == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(EXIT_FAILURE); + } + dup_argv(&((cmds)[*cmd_idx]->argv), argv, argc); + (cmds)[*cmd_idx]->argc = argc; + (*cmd_idx)++; + memset(cmd_line, 0, CMD_LINE_SIZE); + free_argv(argv, argc); +} + struct cmd_args **lyd2cmd_argv(const struct lyd_node *change_node) { - static struct cmd_args *cmds[CMDS_ARRAY_SIZE] = {NULL}; + char *result; + int cmd_idx = 0; + char cmd_line[CMD_LINE_SIZE] = {0}; + struct cmd_args **cmds = malloc(CMDS_ARRAY_SIZE * sizeof(struct cmd_args *)); + // Set all elements of the array to NULL + for (int i = 0; i < CMDS_ARRAY_SIZE; i++) { + cmds[i] = NULL; + } + + lyd_print_mem(&result, change_node, LYD_XML, 0); + printf("--%s", result); + + if (cmds == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + return NULL; + } + + // this will hold the add, update and delete cmd prefixes. + char *oper2cmd_prefix[3] = {NULL}; + + // first get the add, update, delete cmds prefixis from schema extensions + if (get_extension(CMD_ADD_EXT, change_node, &oper2cmd_prefix[ADD_OPR]) != EXIT_SUCCESS) { + fprintf(stderr, "%s: cmd-add extension is missing from root container " + "make sure root container has ir2cgen:cmd-add\n", + __func__); + return NULL; + } + if (get_extension(CMD_DELETE_EXT, change_node, &oper2cmd_prefix[DELETE_OPR]) != EXIT_SUCCESS) { + fprintf(stderr, "%s: cmd-delete extension is missing from root container " + "make sure root container has ir2cgen:cmd-delete\n", + __func__); + return NULL; + } + if (get_extension(CMD_UPDATE_EXT, change_node, &oper2cmd_prefix[UPDATE_OPR]) != EXIT_SUCCESS) { + fprintf(stderr, "%s: ir2cgen:cmd-update extension is missing from root container " + "make sure root container has ir2cgen:cmd-update\n", + __func__); + return NULL; + } + + + oper_t op_val; + struct lyd_node *next; + LYD_TREE_DFS_BEGIN(change_node, next) + { + // if this is startcmd node (schema has ir2cgen:cmd-start), then start building a new command + if (is_startcmd_node(next)) { + // check if the cmd is not empty (first cmd) + if (cmd_line[0] != 0) + add_command(cmd_line, cmds, &cmd_idx, oper2cmd_prefix); + + // prepare for new command + op_val = get_operation(next); + if (op_val == UNKNOWN_OPR) { + fprintf(stderr, "%s: unknown operation for startcmd node (%s) \n", + __func__, next->schema->name); + return NULL; + } + strlcpy(cmd_line, oper2cmd_prefix[op_val], sizeof(cmd_line)); + } else if (next->schema->nodetype != LYS_CONTAINER) { + // not a startcmd, get key-value pair and apped to cmd_line + + if (next->schema->nodetype == LYS_LEAF) { + char *key=NULL, *value = NULL; + // if list operation is delete, get the keys only + if (op_val == DELETE_OPR && !lysc_is_key(next->schema)) + goto next_iter; + // if FLAG extension, add schema name to the cmd and go to next iter. + if (get_extension(FLAG_EXT, next, NULL) == EXIT_SUCCESS) { + if (!strcmp("true", lyd_get_value(next))) + value = strdup(next->schema->name); + goto next_iter; + } + // if value only, don't include the schema->name (key) in the command. + if (get_extension(VALUE_ONLY_EXT, next, NULL) == EXIT_SUCCESS) + goto next_iter; + else + key = strdup(next->schema->name); + // set the value + // special case for identity leaf, where the module prefix might be added (e.g iproute-link:vti) + LY_DATA_TYPE type = ((struct lysc_node_leaf *) next->schema)->type->basetype; + if (type == LY_TYPE_IDENT) + value = strip_yang_iden_prefix(lyd_get_value(next)); + else + value = (char *) lyd_get_value(next); + next_iter: + if (key != NULL) { + strlcat(cmd_line, " ", sizeof(cmd_line)); + strlcat(cmd_line, key, sizeof(cmd_line)); + } + if (value != NULL){ + strlcat(cmd_line, " ", sizeof(cmd_line)); + strlcat(cmd_line, value, sizeof(cmd_line)); + } + + } + } + LYD_TREE_DFS_END(change_node, next); + } + if (cmd_line[0] != 0) + add_command(cmd_line, cmds, &cmd_idx, oper2cmd_prefix); + return cmds; -} \ No newline at end of file +} diff --git a/tests/cases/test_ip_link_data.xml b/tests/cases/test_ip_link_data.xml new file mode 100644 index 0000000..df71c41 --- /dev/null +++ b/tests/cases/test_ip_link_data.xml @@ -0,0 +1,10 @@ + + + testIf0 + dummy + + + testIf1 + dummy + + diff --git a/tests/cases/test_iplink.sh b/tests/cases/test_iplink.sh new file mode 100755 index 0000000..311cc7a --- /dev/null +++ b/tests/cases/test_iplink.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +##################################################################### +# Testbed Script for Testing iproute2-sysrepo "ip link" functionality +##################################################################### +# This script performs a series of tests on the iproute2-sysrepo +# functionality related to IP link manipulation. It verifies the +# creation, deletion, and updating of IP links by iproute2-sysrepo +# using sysrepocfg commands and checks if the operations are +# successful. +# +# Test Steps: +# 1. Test creating link +# 2. Test updating link +# 3. Test deleting link +##################################################################### + +ret=0 +#################################################################### +# Test: Create IP Links +#################################################################### +echo "--------------------" +echo "[1] Test Link CREATE" +echo "---------------------" + +# Step 1: Add IP links to RUNNING data store +sysrepocfg -d running --edit tests/cases/test_ip_link_data.xml || ret=$? +# Check if sysrepocfg command failed +if [ -n "$ret" ] && [ "$ret" -ne 0 ]; then + echo "TEST-ERROR: failed to create links in sysrepo datastore" + exit "$ret" +fi + +# Step 2: Check if IP testIf0 is created +if ip link show testIf0 >/dev/null 2>&1; then + echo "TEST-INFO: IP link testIf0 created successfully (OK)" +else + echo "TEST-ERROR: Failed to create IP link testIf0" + exit 1 +fi + +# Step 3: Check if IP testIf1 is created +if ip link show testIf1 >/dev/null 2>&1; then + echo "TEST-INFO: IP link testIf1 created successfully (OK)" +else + echo "TEST-ERROR: Failed to create IP link testIf1 (FAIL)" + exit 1 +fi +sleep 0.2 +#################################################################### +# Test: Update IP Links +#################################################################### +echo "--------------------" +echo "[2] Test Link UPDATE" +echo "---------------------" + +# Step 1: update MTU in sysrepo +sysrepocfg -S '/iproute2-ip-link:links/link[name="testIf0"]/mtu' --value 1400 + +# Step 2: Check if the MTU for IP testIf0 is updated by iproute2-sysrepo +current_mtu=$(ip link show dev testIf0 2>/dev/null | grep -oP '(?<=mtu )\d+' | head -n 1) + +if [ -z "$current_mtu" ]; then + echo "TEST-ERROR: Failed to retrieve MTU for IP link testIf0" + exit 1 +fi + +if [ "$current_mtu" -eq 1400 ]; then + echo "TEST-INFO: MTU for IP link testIf0 updated successfully (OK)" +else + echo "TEST-ERROR: Failed to update MTU for IP link testIf0 (FAIL)" + exit 1 +fi + +#################################################################### +# Test: Delete IP Links +#################################################################### +echo "--------------------" +echo "[3] Test Link DELETE" +echo "---------------------" + +# Step 1: delete data from sysrepo +sysrepocfg -C startup -d running -m iproute2-ip-link || ret=$? +# Check if sysrepocfg command failed +if [ -n "$ret" ] && [ "$ret" -ne 0 ]; then + echo "TEST-ERROR: failed to delete IP links from sysrepo" + exit "$ret" +fi + +# Step 2: check if interface deleted by iproute2-sysrepo +if ! ip link show testIf0 >/dev/null 2>&1 && ! ip link show testIf1 >/dev/null 2>&1; then + echo "TEST-INFO: IP links testIf0 and testIf1 are deleted successfully (OK)" +else + echo "TEST-ERROR: Failed to delete IP links testIf0 and testIf1 (FAIL)" + exit 1 +fi + + +# Exit with return value +exit $ret diff --git a/tests/run_tests.sh b/tests/run_tests.sh new file mode 100755 index 0000000..1303d3f --- /dev/null +++ b/tests/run_tests.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# Set default return value +ret=0 + +# Step 1: Install YANG Modules +for yang_file in yang/*.yang; do + echo "Installing $yang_file..." + # Install the YANG module + sysrepoctl -i "$yang_file" -s ./yang || ret=$? + if [ $ret -ne 0 ]; then + echo "Error: Failed to install $yang_file" + exit $ret + fi +done + + +# Step 2: Start iproute2-sysrepo +./bin/iproute2-sysrepo 2>&1 & +sysrepo_pid=$! +sleep 0.5 + +# Iterate over all .sh files in the current directory +for test_script in tests/cases/*.sh; do + # Check if the file is executable and has the .sh extension + if [ -x "$test_script" ] && [[ "$test_script" == *.sh ]]; then + echo "#############################################" + echo "##### Running case $test_script" + echo "#############################################" + ./"$test_script" + script_ret=$? # Capture the return value of the test script + if [ $script_ret -ne 0 ]; then + echo "Error: $test_script failed with exit code $script_ret" + ret=$script_ret # Update the overall return value if any test fails + fi + echo "Completed $test_script" + fi +done + +## cleanup +kill $sysrepo_pid +wait $sysrepo_pid + +# Check if the previous steps encountered any errors +if [ $ret -ne 0 ]; then + echo "Error: One or more test scripts failed." + exit $ret +fi + +# Exit with return value +exit $ret diff --git a/yang/iproute2-cmdgen-extensions.yang b/yang/iproute2-cmdgen-extensions.yang new file mode 100644 index 0000000..f82e320 --- /dev/null +++ b/yang/iproute2-cmdgen-extensions.yang @@ -0,0 +1,68 @@ +module iproute2-cmdgen-extensions { + yang-version 1.1; + namespace "urn:okda:iproute2:cmdgen:extensions"; + prefix "ir2cgen"; + + organization + "Okda networks"; + + contact + "Author: Ali Aqrabawi + "; + + description + "This module provide extensions to parse yang data to ip route cmd"; + + extension cmd-start { + description + "define at which node a new cmd start, typically used in the first list in the schema, example + ir2cmd:cmd-start"; + + } + + extension cmd-add { + description + "define the add cmd prefix , typically used in the first container, example + ir2cmd:cmd-add \"ip link add\" "; + argument "cmd"; + + } + extension cmd-delete { + description + "define the delete cmd prefix , typically used in the first container, example + ir2cmd:cmd-add \"ip link delete\" "; + argument "cmd"; + + } + extension cmd-update { + description + "define the update cmd prefix , typically used in the first container, example + ir2cmd:cmd-add \"ip link set\" "; + argument "cmd"; + + } + + extension arg-name { + description + "iproute2 cmd generator will use the leaf name as argname when generating the cmd, to override this use + arg-name extension"; + argument "arg-name"; + } + + extension flag { + description + "iproute2 cmd generator will use the leaf name only, ususally used for flags where the leaf type will be bool, + e.g \"ip nexthop add id 100 blackhole\" (blackhole is flag, and it's type is bool in yang)"; + } + + extension value-only{ + description + "instruct iproute2 to drop the leaf name (key) when generating the cmd, example \"ip link add dummy0 up\""; + } + + extension value-only-on-update{ + description + "instruct iproute2 to drop the leaf name (key) when generating the update cmd"; + } + +} \ No newline at end of file diff --git a/yang/iproute2-ip-link.yang b/yang/iproute2-ip-link.yang index c180908..2f4461d 100644 --- a/yang/iproute2-ip-link.yang +++ b/yang/iproute2-ip-link.yang @@ -3,6 +3,7 @@ module iproute2-ip-link { namespace "urn:okda:iproute2:ip:link"; prefix "iplink"; + import iproute2-cmdgen-extensions { prefix ir2cgen; } //import ietf-inet-types { prefix inet; } //import ietf-yang-types { prefix yang; } organization @@ -127,20 +128,18 @@ module iproute2-ip-link { "Virtual tunnel interface."; } - identity other { - base link-type; - description - "generic network interface (e.a eth0, lo1 .. etc)"; - } - - container links { + ir2cgen:cmd-add "ip link add"; + ir2cgen:cmd-update "ip link set"; + ir2cgen:cmd-delete "ip link delete"; description "ip-link - network device configuration"; list link { + ir2cgen:cmd-start; key "name"; description "ip-link - network device"; leaf name { + ir2cgen:value-only-on-update; type string; description "link name"; } @@ -152,15 +151,23 @@ module iproute2-ip-link { type identityref { base link-type; } - description "specifies the type of the new device"; + description "specifies the type of the device"; } + + leaf mtu { + type uint16{ + range "64..9000"; + } + description "specifies the mtu size for the device"; + } + leaf numtxqueues { type uint32; - description "specifies the number of transmit queues for new device."; + description "specifies the number of transmit queues for device."; } leaf numrxqueues { type uint32; - description "specifies the number of receive queues for new device."; + description "specifies the number of receive queues for device."; } leaf gso_max_size { type uint32; @@ -177,7 +184,7 @@ module iproute2-ip-link { leaf gso_max_segs { type uint32; description "specifies the recommended maximum number of a Generic Segment - Offload segments the new device should accept."; + Offload segments the device should accept."; } leaf gro_max_size { type uint32; @@ -191,12 +198,19 @@ module iproute2-ip-link { This is especially used for BIG TCP to allow the size of a merged IPv4 GSO packet on this device greater than 65536."; } + leaf admin-status{ + description "set admin status for the link"; + type enumeration { + enum "up"; + enum "down"; + } + } leaf index { type uint64; description "specifies the desired index of the new virtual device. The link creation fails, if the index is busy."; } - choice netns { + choice netns { description "create the device in the network namespace associated with process PID or the name NETNSNAME or the file NETNSFILE."; case process-id { diff --git a/yang/iproute2-ip-nexthop.yang b/yang/iproute2-ip-nexthop.yang index e11321a..7657d55 100644 --- a/yang/iproute2-ip-nexthop.yang +++ b/yang/iproute2-ip-nexthop.yang @@ -5,6 +5,7 @@ module iproute2-ip-nexthop { import ietf-inet-types { prefix inet; } import iproute2-ip-link { prefix iplink; } + import iproute2-cmdgen-extensions { prefix ir2cgen; } organization "Okda Networks"; @@ -26,13 +27,18 @@ module iproute2-ip-nexthop { } container nexthops{ + ir2cgen:cmd-add "ip nexthop add"; + ir2cgen:cmd-update "ip nexthop replace"; + ir2cgen:cmd-delete "ip nexthop del"; description "iproute2 nexthops configurations"; list nexthop{ + ir2cgen:cmd-start; key "id"; description "nexthop configuraton"; leaf id{ + ir2cgen:value-only-on-update; description "nexthop id"; type uint32; } @@ -62,14 +68,14 @@ module iproute2-ip-nexthop { } leaf blackhole { - default false; - description " specify if this is blackhole nexthop, which discards all traffic sent to it."; + ir2cgen:flag; + description "specify if this is blackhole nexthop, which discards all traffic sent to it."; type boolean; when "not(../dev) and not(../ipv6) and not(../ipv4)"; } - leaf online { + leaf onlink { + ir2cgen:flag; type boolean; - default false; description "Indicates that the nexthop is directly reachable on the link."; } choice encap { @@ -135,6 +141,7 @@ module iproute2-ip-nexthop { } } leaf fdb { + ir2cgen:flag; type boolean; description "nexthop and nexthop groups for use with layer-2 fdb entries. A fdb nexthop group can only have fdb nexthops. Example: Used to represent a vxlan remote