diff --git a/CMakeModules/FindNL.cmake b/CMakeModules/FindNL.cmake index 6131ffcb..2b1bdd39 100644 --- a/CMakeModules/FindNL.cmake +++ b/CMakeModules/FindNL.cmake @@ -5,12 +5,30 @@ find_path(NL_INCLUDE_DIRS netlink/netlink.h /usr/include/libnl3 /usr/local/include /usr/local/include/libnl3 + ${CMAKE_INCLUDE_PATH} + ${CMAKE_PREFIX_PATH}/include/libnl3 ) -find_library(NL_LIBRARY NAMES nl nl-3) -find_library(NL_ROUTE_LIBRARY NAMES nl-route nl-route-3) -find_library(NL_NETFILTER_LIBRARY NAMES nl-nf nl-nf-3) -find_library(NL_GENL_LIBRARY NAMES nl-genl nl-genl-3) +find_library( + NL_LIBRARY + NAMES nl nl-3 + PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib +) +find_library( + NL_ROUTE_LIBRARY + NAMES nl-route nl-route-3 + PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib +) +find_library( + NL_NETFILTER_LIBRARY + NAMES nl-nf nl-nf-3 + PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib +) +find_library( + NL_GENL_LIBRARY + NAMES nl-genl nl-genl-3 + PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib +) if (NL_INCLUDE_DIRS AND NL_LIBRARY) set(NL_FOUND TRUE) diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/change.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/change.c index 6d570337..9db2ad73 100644 --- a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/change.c +++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/change.c @@ -125,6 +125,22 @@ int interfaces_interface_ipv4_address_change_netmask(void* priv, sr_session_ctx_ error = -1; out: + if (request_addr) { + rtnl_addr_put(request_addr); + } + + if (delete_addr) { + rtnl_addr_put(delete_addr); + } + + if (old_local_addr) { + nl_addr_put(old_local_addr); + } + + if (local_addr) { + nl_addr_put(local_addr); + } + return error; } @@ -231,6 +247,22 @@ int interfaces_interface_ipv4_address_change_prefix_length(void* priv, sr_sessio error = -1; out: + if (request_addr) { + rtnl_addr_put(request_addr); + } + + if (delete_addr) { + rtnl_addr_put(delete_addr); + } + + if (old_local_addr) { + nl_addr_put(old_local_addr); + } + + if (local_addr) { + nl_addr_put(local_addr); + } + return error; } @@ -403,6 +435,10 @@ int interfaces_interface_ipv4_address_change_ip(void* priv, sr_session_ctx_t* se sr_session_stop(running_session); } + if (request_addr) { + rtnl_addr_put(request_addr); + } + // re-initialize mod_ctx data mod_ctx->mod_data.ipv4.address.prefix_length = 0; mod_ctx->mod_data.ipv4.address.prefix_set = false; diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/change.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/change.c index 067c5f98..b32722d3 100644 --- a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/change.c +++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/change.c @@ -4,13 +4,14 @@ #include "plugin/context.h" #include +#include #include #include #include #include -static int interfaces_interface_ipv4_address_get_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); +static int interfaces_interface_ipv4_neighbor_get_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); int interfaces_interface_ipv4_neighbor_change_link_layer_address_init(void* priv) { @@ -21,22 +22,83 @@ int interfaces_interface_ipv4_neighbor_change_link_layer_address_init(void* priv int interfaces_interface_ipv4_neighbor_change_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx) { int error = 0; + void* error_ptr = NULL; + + // strings and buffers const char* node_name = LYD_NAME(change_ctx->node); const char* node_value = lyd_get_value(change_ctx->node); + char path_buffer[PATH_MAX] = { 0 }; + char interface_name_buffer[100] = { 0 }; + char ip_buffer[100] = { 0 }; + + // app context + interfaces_ctx_t* ctx = priv; + + // mod changes context + interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx; + + // libnl + struct rtnl_neigh* request_neigh = NULL; + struct rtnl_link* current_link = NULL; + struct nl_addr* dst_addr = NULL; + struct nl_addr* ll_addr = NULL; SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation); + // get node path + SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out); + + // get interface name + SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out); + + // get IP + SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "neighbor", "ip", ip_buffer, sizeof(ip_buffer)), error_out); + + SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s; Neighbor IP: %s", path_buffer, interface_name_buffer, ip_buffer); + + // get link + SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out); + switch (change_ctx->operation) { case SR_OP_CREATED: + // not used - used only in IP change callback break; case SR_OP_MODIFIED: + // change lladdr + request_neigh = rtnl_neigh_alloc(); + + // set interface + rtnl_neigh_set_ifindex(request_neigh, rtnl_link_get_ifindex(current_link)); + + // parse destination and LL address + SRPC_SAFE_CALL_ERR(error, nl_addr_parse(ip_buffer, AF_INET, &dst_addr), error_out); + SRPC_SAFE_CALL_ERR(error, nl_addr_parse(node_value, AF_LLC, &ll_addr), error_out); + + // set destination and LL address + SRPC_SAFE_CALL_ERR(error, rtnl_neigh_set_dst(request_neigh, dst_addr), error_out); + rtnl_neigh_set_lladdr(request_neigh, ll_addr); + + // change neighbor + SRPC_SAFE_CALL_ERR(error, rtnl_neigh_add(mod_ctx->nl_ctx.socket, request_neigh, NLM_F_REPLACE), error_out); + break; case SR_OP_DELETED: + // not used - used only in IP change callback break; case SR_OP_MOVED: break; } + goto out; + +error_out: + error = -1; + +out: + if (request_neigh) { + rtnl_neigh_put(request_neigh); + } + return error; } @@ -55,17 +117,11 @@ int interfaces_interface_ipv4_neighbor_change_ip(void* priv, sr_session_ctx_t* s int error = 0; void* error_ptr = NULL; - // sysrepo - // sr_val_t* link_layer_address_val = NULL; - sr_conn_ctx_t* conn_ctx = NULL; - sr_session_ctx_t* running_session = NULL; - // strings and buffers const char* node_name = LYD_NAME(change_ctx->node); const char* node_value = lyd_get_value(change_ctx->node); char path_buffer[PATH_MAX] = { 0 }; char interface_name_buffer[100] = { 0 }; - // char address_buffer[100] = { 0 }; // app context interfaces_ctx_t* ctx = priv; @@ -92,12 +148,6 @@ int interfaces_interface_ipv4_neighbor_change_ip(void* priv, sr_session_ctx_t* s // get link SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out); - // get connection - SRPC_SAFE_CALL_PTR(conn_ctx, sr_session_get_connection(session), error_out); - - // start a running DS session - fetching data about prefix when deleting the address - SRPC_SAFE_CALL_ERR(error, sr_session_start(conn_ctx, SR_DS_RUNNING, &running_session), error_out); - switch (change_ctx->operation) { case SR_OP_CREATED: // new neighbor @@ -108,7 +158,7 @@ int interfaces_interface_ipv4_neighbor_change_ip(void* priv, sr_session_ctx_t* s // get prefix length by using prefix-length or netmask leafs SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(path_buffer, sizeof(path_buffer), INTERFACES_INTERFACES_INTERFACE_YANG_PATH "[name=\"%s\"]/ietf-ip:ipv4/neighbor[ip=\"%s\"]/link-layer-address", interface_name_buffer, node_value), error_out); - SRPC_SAFE_CALL_ERR(error, srpc_iterate_changes(ctx, session, path_buffer, interfaces_interface_ipv4_address_get_link_layer_address, NULL, NULL), error_out); + SRPC_SAFE_CALL_ERR(error, srpc_iterate_changes(ctx, session, path_buffer, interfaces_interface_ipv4_neighbor_get_link_layer_address, NULL, NULL), error_out); SRPLG_LOG_INF(PLUGIN_NAME, "Recieved link-layer-address %s for neighbor address %s", mod_ctx->mod_data.ipv4.neighbor.link_layer_address, node_value); @@ -160,9 +210,6 @@ int interfaces_interface_ipv4_neighbor_change_ip(void* priv, sr_session_ctx_t* s error = -1; out: - if (running_session) { - sr_session_stop(running_session); - } // re-initialize mod_ctx data if (mod_ctx->mod_data.ipv4.neighbor.link_layer_address) { @@ -171,6 +218,10 @@ int interfaces_interface_ipv4_neighbor_change_ip(void* priv, sr_session_ctx_t* s mod_ctx->mod_data.ipv4.neighbor.link_layer_address = NULL; mod_ctx->mod_data.ipv4.neighbor.link_layer_set = false; + if (request_neigh) { + rtnl_neigh_put(request_neigh); + } + return error; } @@ -178,7 +229,7 @@ void interfaces_interface_ipv4_neighbor_change_ip_free(void* priv) { } -static int interfaces_interface_ipv4_address_get_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx) +static int interfaces_interface_ipv4_neighbor_get_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx) { int error = 0; diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.c index c7ccab0d..25456806 100644 --- a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.c +++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.c @@ -1,8 +1,15 @@ #include "change.h" #include "plugin/common.h" +#include "plugin/context.h" +#include #include +#include +#include + +static int interfaces_interface_ipv6_address_get_prefix_length(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); + int interfaces_interface_ipv6_address_change_prefix_length_init(void* priv) { int error = 0; @@ -12,22 +19,112 @@ int interfaces_interface_ipv6_address_change_prefix_length_init(void* priv) int interfaces_interface_ipv6_address_change_prefix_length(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx) { int error = 0; + void* error_ptr = NULL; + + // strings and buffers const char* node_name = LYD_NAME(change_ctx->node); const char* node_value = lyd_get_value(change_ctx->node); + char path_buffer[PATH_MAX] = { 0 }; + char interface_name_buffer[100] = { 0 }; + char ip_buffer[100] = { 0 }; + char address_buffer[100] = { 0 }; + char old_address_buffer[100] = { 0 }; + + // app context + interfaces_ctx_t* ctx = priv; + + // mod changes context + interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx; + + // libnl + struct rtnl_addr* request_addr = NULL; + struct rtnl_addr* delete_addr = NULL; + struct rtnl_link* current_link = NULL; + struct nl_addr* local_addr = NULL; + struct nl_addr* old_local_addr = NULL; SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation); + // get node path + SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out); + + // get interface name + SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out); + + // get IP + SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "address", "ip", ip_buffer, sizeof(ip_buffer)), error_out); + + SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s; Address IP: %s", path_buffer, interface_name_buffer, ip_buffer); + + // get link + SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out); + switch (change_ctx->operation) { case SR_OP_CREATED: + // this change case should be handled only when creating the whole address in the IP callback + // no need to process created change break; case SR_OP_MODIFIED: + // change prefix length + request_addr = rtnl_addr_alloc(); + delete_addr = rtnl_addr_alloc(); + + // get full address + SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(address_buffer, sizeof(address_buffer), "%s/%s", ip_buffer, node_value), error_out); + SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(old_address_buffer, sizeof(old_address_buffer), "%s/%s", ip_buffer, change_ctx->previous_value), error_out); + + // parse local address + SRPC_SAFE_CALL_ERR(error, nl_addr_parse(address_buffer, AF_INET6, &local_addr), error_out); + SRPC_SAFE_CALL_ERR(error, nl_addr_parse(old_address_buffer, AF_INET6, &old_local_addr), error_out); + + // set to route address + SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(request_addr, local_addr), error_out); + SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(delete_addr, old_local_addr), error_out); + + // set interface + rtnl_addr_set_ifindex(request_addr, rtnl_link_get_ifindex(current_link)); + rtnl_addr_set_ifindex(delete_addr, rtnl_link_get_ifindex(current_link)); + + // delete old address + SRPC_SAFE_CALL_ERR(error, rtnl_addr_delete(mod_ctx->nl_ctx.socket, delete_addr, 0), error_out); + + // add new address + SRPC_SAFE_CALL_ERR(error, rtnl_addr_add(mod_ctx->nl_ctx.socket, request_addr, 0), error_out); + break; case SR_OP_DELETED: + // prefix is needed to find the appropriate address + // should be processed when IP deleted break; case SR_OP_MOVED: break; } + goto out; + +error_out: + if (error < 0) { + SRPLG_LOG_INF(PLUGIN_NAME, "nl_geterror(): %s", nl_geterror(error)); + } + error = -1; + +out: + if (request_addr) { + rtnl_addr_put(request_addr); + } + + if (delete_addr) { + rtnl_addr_put(delete_addr); + } + + if (old_local_addr) { + nl_addr_put(old_local_addr); + } + + if (local_addr) { + nl_addr_put(local_addr); + } + return error; } @@ -44,25 +141,171 @@ int interfaces_interface_ipv6_address_change_ip_init(void* priv) int interfaces_interface_ipv6_address_change_ip(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx) { int error = 0; + void* error_ptr = NULL; + + // sysrepo + sr_val_t* prefix_val = NULL; + sr_conn_ctx_t* conn_ctx = NULL; + sr_session_ctx_t* running_session = NULL; + + // strings and buffers const char* node_name = LYD_NAME(change_ctx->node); const char* node_value = lyd_get_value(change_ctx->node); + char path_buffer[PATH_MAX] = { 0 }; + char interface_name_buffer[100] = { 0 }; + char address_buffer[100] = { 0 }; + + // app context + interfaces_ctx_t* ctx = priv; + + // mod changes context + interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx; + + // libnl + struct rtnl_addr* request_addr = NULL; + struct rtnl_link* current_link = NULL; + struct nl_addr* local_addr = NULL; + + // data + uint8_t prefix_length = 0; SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation); + // get node path + SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out); + + // get interface name + SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out); + + SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s", path_buffer, interface_name_buffer); + + // get link + SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out); + + // get connection + SRPC_SAFE_CALL_PTR(conn_ctx, sr_session_get_connection(session), error_out); + + // start a running DS session - fetching data about prefix when deleting the address + SRPC_SAFE_CALL_ERR(error, sr_session_start(conn_ctx, SR_DS_RUNNING, &running_session), error_out); + switch (change_ctx->operation) { case SR_OP_CREATED: + // new address + request_addr = rtnl_addr_alloc(); + + // parse local address + SRPC_SAFE_CALL_ERR(error, nl_addr_parse(node_value, AF_INET6, &local_addr), error_out); + + // get prefix length by using prefix-length or netmask leafs + SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(path_buffer, sizeof(path_buffer), "%s[name=\"%s\"]/ietf-ip:ipv6/address[ip=\"%s\"]/prefix-length", INTERFACES_INTERFACES_LIST_YANG_PATH, interface_name_buffer, node_value), error_out); + SRPC_SAFE_CALL_ERR(error, srpc_iterate_changes(ctx, session, path_buffer, interfaces_interface_ipv6_address_get_prefix_length, NULL, NULL), error_out); + + SRPLG_LOG_INF(PLUGIN_NAME, "Recieved prefix-length of %d for address %s", mod_ctx->mod_data.ipv6.address.prefix_length, node_value); + + // prefix was set and found + + // set final prefix length + nl_addr_set_prefixlen(local_addr, mod_ctx->mod_data.ipv6.address.prefix_length); + + // set to route address + SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(request_addr, local_addr), error_out); + + // set interface + rtnl_addr_set_ifindex(request_addr, rtnl_link_get_ifindex(current_link)); + + // add address + SRPC_SAFE_CALL_ERR(error, rtnl_addr_add(mod_ctx->nl_ctx.socket, request_addr, 0), error_out); + break; case SR_OP_MODIFIED: + // should be impossible - address IP can only be created and deleted + SRPLG_LOG_ERR(PLUGIN_NAME, "Unsuported operation MODIFY for interface IPv6 address IP leaf"); + goto error_out; break; case SR_OP_DELETED: + // fetch info about prefix-length/netmask and use the pair address/prefix to delete address + // prefix is needed to find the appropriate address + request_addr = rtnl_addr_alloc(); + + // check for prefix-length + SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(path_buffer, sizeof(path_buffer), INTERFACES_INTERFACES_INTERFACE_YANG_PATH "[name=\"%s\"]/ietf-ip:ipv6/address[ip=\"%s\"]/prefix-length", interface_name_buffer, node_value), error_out); + SRPLG_LOG_INF(PLUGIN_NAME, "Searching running DS for %s", path_buffer); + SRPC_SAFE_CALL_ERR(error, sr_get_item(running_session, path_buffer, 0, &prefix_val), error_out); + + // set data + prefix_length = prefix_val->data.uint8_val; + + SRPLG_LOG_INF(PLUGIN_NAME, "Recieved prefix for address %s: %d", node_value, prefix_length); + + // after getting the prefix length - remove address + + // get full address + SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(address_buffer, sizeof(address_buffer), "%s/%d", node_value, prefix_length), error_out); + + // parse local address + SRPC_SAFE_CALL_ERR(error, nl_addr_parse(address_buffer, AF_INET6, &local_addr), error_out); + + // set to route address + SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(request_addr, local_addr), error_out); + + // set interface + rtnl_addr_set_ifindex(request_addr, rtnl_link_get_ifindex(current_link)); + + // remove wanted address + SRPC_SAFE_CALL_ERR(error, rtnl_addr_delete(mod_ctx->nl_ctx.socket, request_addr, 0), error_out); + break; case SR_OP_MOVED: break; } + goto out; + +error_out: + if (error < 0) { + SRPLG_LOG_INF(PLUGIN_NAME, "nl_geterror(): %s", nl_geterror(error)); + } + error = -1; + +out: + if (running_session) { + sr_session_stop(running_session); + } + + if (request_addr) { + rtnl_addr_put(request_addr); + } + + // re-initialize mod_ctx data + mod_ctx->mod_data.ipv6.address.prefix_length = 0; + mod_ctx->mod_data.ipv6.address.prefix_set = false; + return error; } void interfaces_interface_ipv6_address_change_ip_free(void* priv) { } + +static int interfaces_interface_ipv6_address_get_prefix_length(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx) +{ + int error = 0; + + // ctx + interfaces_ctx_t* ctx = priv; + interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx; + + const char* node_name = LYD_NAME(change_ctx->node); + const char* node_value = lyd_get_value(change_ctx->node); + + SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation); + + // this callback should only be called on CREATED operation + assert(change_ctx->operation == SR_OP_CREATED); + + // parse prefix length + mod_ctx->mod_data.ipv6.address.prefix_length = (uint8_t)atoi(node_value); + mod_ctx->mod_data.ipv6.address.prefix_set = true; + + return error; +} \ No newline at end of file diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.h index e90bac00..884a1701 100644 --- a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.h +++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.h @@ -7,6 +7,7 @@ int interfaces_interface_ipv6_address_change_prefix_length_init(void* priv); int interfaces_interface_ipv6_address_change_prefix_length(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); void interfaces_interface_ipv6_address_change_prefix_length_free(void* priv); + int interfaces_interface_ipv6_address_change_ip_init(void* priv); int interfaces_interface_ipv6_address_change_ip(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); void interfaces_interface_ipv6_address_change_ip_free(void* priv); diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/change.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/change.h index dc9d2584..a594cb14 100644 --- a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/change.h +++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/change.h @@ -7,18 +7,23 @@ int interfaces_interface_ipv6_change_dup_addr_detect_transmits_init(void* priv); int interfaces_interface_ipv6_change_dup_addr_detect_transmits(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); void interfaces_interface_ipv6_change_dup_addr_detect_transmits_free(void* priv); + int interfaces_interface_ipv6_change_neighbor_init(void* priv); int interfaces_interface_ipv6_change_neighbor(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); void interfaces_interface_ipv6_change_neighbor_free(void* priv); + int interfaces_interface_ipv6_change_address_init(void* priv); int interfaces_interface_ipv6_change_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); void interfaces_interface_ipv6_change_address_free(void* priv); + int interfaces_interface_ipv6_change_mtu_init(void* priv); int interfaces_interface_ipv6_change_mtu(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); void interfaces_interface_ipv6_change_mtu_free(void* priv); + int interfaces_interface_ipv6_change_forwarding_init(void* priv); int interfaces_interface_ipv6_change_forwarding(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); void interfaces_interface_ipv6_change_forwarding_free(void* priv); + int interfaces_interface_ipv6_change_enabled_init(void* priv); int interfaces_interface_ipv6_change_enabled(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); void interfaces_interface_ipv6_change_enabled_free(void* priv); diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.c index d43d4dec..9850f038 100644 --- a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.c +++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.c @@ -1,8 +1,16 @@ #include "change.h" #include "plugin/common.h" +#include "plugin/context.h" +#include #include +#include +#include +#include + +static int interfaces_interface_ipv6_neighbor_get_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); + int interfaces_interface_ipv6_neighbor_change_link_layer_address_init(void* priv) { int error = 0; @@ -12,22 +20,83 @@ int interfaces_interface_ipv6_neighbor_change_link_layer_address_init(void* priv int interfaces_interface_ipv6_neighbor_change_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx) { int error = 0; + void* error_ptr = NULL; + + // strings and buffers const char* node_name = LYD_NAME(change_ctx->node); const char* node_value = lyd_get_value(change_ctx->node); + char path_buffer[PATH_MAX] = { 0 }; + char interface_name_buffer[100] = { 0 }; + char ip_buffer[100] = { 0 }; + + // app context + interfaces_ctx_t* ctx = priv; + + // mod changes context + interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx; + + // libnl + struct rtnl_neigh* request_neigh = NULL; + struct rtnl_link* current_link = NULL; + struct nl_addr* dst_addr = NULL; + struct nl_addr* ll_addr = NULL; SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation); + // get node path + SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out); + + // get interface name + SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out); + + // get IP + SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "neighbor", "ip", ip_buffer, sizeof(ip_buffer)), error_out); + + SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s; Neighbor IP: %s", path_buffer, interface_name_buffer, ip_buffer); + + // get link + SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out); + switch (change_ctx->operation) { case SR_OP_CREATED: + // not used - used only in IP change callback break; case SR_OP_MODIFIED: + // change lladdr + request_neigh = rtnl_neigh_alloc(); + + // set interface + rtnl_neigh_set_ifindex(request_neigh, rtnl_link_get_ifindex(current_link)); + + // parse destination and LL address + SRPC_SAFE_CALL_ERR(error, nl_addr_parse(ip_buffer, AF_INET6, &dst_addr), error_out); + SRPC_SAFE_CALL_ERR(error, nl_addr_parse(node_value, AF_LLC, &ll_addr), error_out); + + // set destination and LL address + SRPC_SAFE_CALL_ERR(error, rtnl_neigh_set_dst(request_neigh, dst_addr), error_out); + rtnl_neigh_set_lladdr(request_neigh, ll_addr); + + // change neighbor + SRPC_SAFE_CALL_ERR(error, rtnl_neigh_add(mod_ctx->nl_ctx.socket, request_neigh, NLM_F_REPLACE), error_out); + break; case SR_OP_DELETED: + // not used - used only in IP change callback break; case SR_OP_MOVED: break; } + goto out; + +error_out: + error = -1; + +out: + if (request_neigh) { + rtnl_neigh_put(request_neigh); + } + return error; } @@ -44,25 +113,150 @@ int interfaces_interface_ipv6_neighbor_change_ip_init(void* priv) int interfaces_interface_ipv6_neighbor_change_ip(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx) { int error = 0; + void* error_ptr = NULL; + + // strings and buffers const char* node_name = LYD_NAME(change_ctx->node); const char* node_value = lyd_get_value(change_ctx->node); + char path_buffer[PATH_MAX] = { 0 }; + char interface_name_buffer[100] = { 0 }; + + // app context + interfaces_ctx_t* ctx = priv; + + // mod changes context + interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx; + + // libnl + struct rtnl_neigh* request_neigh = NULL; + struct rtnl_link* current_link = NULL; + struct nl_addr* dst_addr = NULL; + struct nl_addr* ll_addr = NULL; SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation); + // get node path + SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out); + + // get interface name + SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out); + + SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s", path_buffer, interface_name_buffer); + + // get link + SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out); + switch (change_ctx->operation) { case SR_OP_CREATED: + // new neighbor + request_neigh = rtnl_neigh_alloc(); + + // parse destination address + SRPC_SAFE_CALL_ERR(error, nl_addr_parse(node_value, AF_INET6, &dst_addr), error_out); + + // get prefix length by using prefix-length or netmask leafs + SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(path_buffer, sizeof(path_buffer), INTERFACES_INTERFACES_INTERFACE_YANG_PATH "[name=\"%s\"]/ietf-ip:ipv6/neighbor[ip=\"%s\"]/link-layer-address", interface_name_buffer, node_value), error_out); + SRPC_SAFE_CALL_ERR(error, srpc_iterate_changes(ctx, session, path_buffer, interfaces_interface_ipv6_neighbor_get_link_layer_address, NULL, NULL), error_out); + + SRPLG_LOG_INF(PLUGIN_NAME, "Recieved link-layer-address %s for neighbor address %s", mod_ctx->mod_data.ipv6.neighbor.link_layer_address, node_value); + + // parse link-layer address + SRPC_SAFE_CALL_ERR(error, nl_addr_parse(mod_ctx->mod_data.ipv6.neighbor.link_layer_address, AF_LLC, &ll_addr), error_out); + + // set addresses to the new neighbor + SRPC_SAFE_CALL_ERR(error, rtnl_neigh_set_dst(request_neigh, dst_addr), error_out); + rtnl_neigh_set_lladdr(request_neigh, ll_addr); + + // set interface + rtnl_neigh_set_ifindex(request_neigh, rtnl_link_get_ifindex(current_link)); + + // add neighbor + SRPC_SAFE_CALL_ERR(error, rtnl_neigh_add(mod_ctx->nl_ctx.socket, request_neigh, NLM_F_CREATE), error_out); + break; case SR_OP_MODIFIED: + // should be impossible - address IP can only be created and deleted + SRPLG_LOG_ERR(PLUGIN_NAME, "Unsuported operation MODIFY for interface IPv6 neighbor IP leaf"); + goto error_out; break; case SR_OP_DELETED: + request_neigh = rtnl_neigh_alloc(); + + // set interface + rtnl_neigh_set_ifindex(request_neigh, rtnl_link_get_ifindex(current_link)); + + // parse destination + SRPC_SAFE_CALL_ERR(error, nl_addr_parse(node_value, AF_INET6, &dst_addr), error_out); + + // set destination + SRPC_SAFE_CALL_ERR(error, rtnl_neigh_set_dst(request_neigh, dst_addr), error_out); + + // remove wanted neighbor + SRPC_SAFE_CALL_ERR(error, rtnl_neigh_delete(mod_ctx->nl_ctx.socket, request_neigh, 0), error_out); + break; case SR_OP_MOVED: break; } + goto out; + +error_out: + if (error < 0) { + SRPLG_LOG_INF(PLUGIN_NAME, "nl_geterror(): %s", nl_geterror(error)); + } + error = -1; + +out: + + // re-initialize mod_ctx data + if (mod_ctx->mod_data.ipv6.neighbor.link_layer_address) { + free(mod_ctx->mod_data.ipv6.neighbor.link_layer_address); + } + mod_ctx->mod_data.ipv6.neighbor.link_layer_address = NULL; + mod_ctx->mod_data.ipv6.neighbor.link_layer_set = false; + + if (request_neigh) { + rtnl_neigh_put(request_neigh); + } + return error; } void interfaces_interface_ipv6_neighbor_change_ip_free(void* priv) { } + +static int interfaces_interface_ipv6_neighbor_get_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx) +{ + int error = 0; + + // ctx + interfaces_ctx_t* ctx = priv; + interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx; + + const char* node_name = LYD_NAME(change_ctx->node); + const char* node_value = lyd_get_value(change_ctx->node); + + SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation); + + // this callback should only be called on CREATED operation + assert(change_ctx->operation == SR_OP_CREATED); + + // set mod changes prefix length + mod_ctx->mod_data.ipv6.neighbor.link_layer_address = strdup(node_value); + if (!mod_ctx->mod_data.ipv6.neighbor.link_layer_address) { + SRPLG_LOG_ERR(PLUGIN_NAME, "Unable to set link-layer-address value for module changes context"); + goto error_out; + } + mod_ctx->mod_data.ipv6.neighbor.link_layer_set = true; + + goto out; + +error_out: + error = -1; + mod_ctx->mod_data.ipv6.neighbor.link_layer_set = false; + +out: + return error; +} \ No newline at end of file diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.h index b3c6b129..ac15a9ab 100644 --- a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.h +++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.h @@ -7,6 +7,7 @@ int interfaces_interface_ipv6_neighbor_change_link_layer_address_init(void* priv); int interfaces_interface_ipv6_neighbor_change_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); void interfaces_interface_ipv6_neighbor_change_link_layer_address_free(void* priv); + int interfaces_interface_ipv6_neighbor_change_ip_init(void* priv); int interfaces_interface_ipv6_neighbor_change_ip(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx); void interfaces_interface_ipv6_neighbor_change_ip_free(void* priv); diff --git a/src/interfaces/src/plugin/context.h b/src/interfaces/src/plugin/context.h index e7b48b0f..f34160c0 100644 --- a/src/interfaces/src/plugin/context.h +++ b/src/interfaces/src/plugin/context.h @@ -48,6 +48,16 @@ struct interfaces_mod_changes_ctx_s { uint8_t link_layer_set; ///< link_layer_address has been set } neighbor; } ipv4; + struct { + struct { + uint8_t prefix_length; + uint8_t prefix_set; ///< prefix_length has been set + } address; + struct { + char* link_layer_address; + uint8_t link_layer_set; ///< link_layer_address has been set + } neighbor; + } ipv6; } mod_data; }; diff --git a/src/interfaces/src/plugin/subscription/change.c b/src/interfaces/src/plugin/subscription/change.c index 56674380..5643f899 100644 --- a/src/interfaces/src/plugin/subscription/change.c +++ b/src/interfaces/src/plugin/subscription/change.c @@ -1,5 +1,4 @@ #include "change.h" -#include "plugin/api/interfaces/interface/ipv4/neighbor/change.h" #include "plugin/common.h" #include "plugin/context.h" @@ -13,6 +12,9 @@ #include "plugin/api/interfaces/interface/change.h" #include "plugin/api/interfaces/interface/ipv4/address/change.h" #include "plugin/api/interfaces/interface/ipv4/change.h" +#include "plugin/api/interfaces/interface/ipv4/neighbor/change.h" +#include "plugin/api/interfaces/interface/ipv6/address/change.h" +#include "plugin/api/interfaces/interface/ipv6/neighbor/change.h" int interfaces_subscription_change_interfaces_interface(sr_session_ctx_t* session, uint32_t subscription_id, const char* module_name, const char* xpath, sr_event_t event, uint32_t request_id, void* private_data) { @@ -75,6 +77,22 @@ int interfaces_subscription_change_interfaces_interface(sr_session_ctx_t* sessio // ipv4/neighbor/link-layer-address SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv4/neighbor/link-layer-address", xpath), error_out); SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv4_neighbor_change_link_layer_address, interfaces_change_interface_init, interfaces_change_interface_free), error_out); + + // ipv6/address/ip + SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv6/address/ip", xpath), error_out); + SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv6_address_change_ip, interfaces_change_interface_init, interfaces_change_interface_free), error_out); + + // ipv6/address/prefix-length + SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv6/address/prefix-length", xpath), error_out); + SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv6_address_change_prefix_length, interfaces_change_interface_init, interfaces_change_interface_free), error_out); + + // ipv6/neighbor/ip + SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv6/neighbor/ip", xpath), error_out); + SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv6_neighbor_change_ip, interfaces_change_interface_init, interfaces_change_interface_free), error_out); + + // ipv6/neighbor/link-layer-address + SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv6/neighbor/link-layer-address", xpath), error_out); + SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv6_neighbor_change_link_layer_address, interfaces_change_interface_init, interfaces_change_interface_free), error_out); } goto out;