From 53b1826e015b153b1ebc0953f852105ab5f4a402 Mon Sep 17 00:00:00 2001 From: Andrej Kozemcak Date: Tue, 4 Dec 2018 14:12:40 +0100 Subject: Openconfig local route xpath. Change-Id: Id180b4a54ab6a67f25077120fead854da79ec2b6 Signed-off-by: Andrej Kozemcak --- src/plugins/openconfig/openconfig_local_routing.c | 730 ++++++++++++++++++++++ 1 file changed, 730 insertions(+) create mode 100644 src/plugins/openconfig/openconfig_local_routing.c (limited to 'src/plugins/openconfig/openconfig_local_routing.c') diff --git a/src/plugins/openconfig/openconfig_local_routing.c b/src/plugins/openconfig/openconfig_local_routing.c new file mode 100644 index 0000000..37d986f --- /dev/null +++ b/src/plugins/openconfig/openconfig_local_routing.c @@ -0,0 +1,730 @@ +/* + * Copyright (c) 2018 PANTHEON.tech. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "openconfig_local_routing.h" +#include "sys_util.h" +#include "sc_vpp_operation.h" + +#include "../bapi/bapi.h" +#include "../bapi/bapi_interface.h" +#include "../bapi/bapi_ip.h" + +#include +#include +#include +#include + +#define XPATH_SIZE 2000 + +int openconfig_local_routing_mod_cb( + __attribute__((unused)) sr_session_ctx_t* session, + __attribute__((unused)) const char* module_name, + __attribute__((unused)) sr_notif_event_t event, + __attribute__((unused)) void* private_ctx) +{ + SRP_LOG_DBG("Local routing module subscribe: %s", module_name); + + return SR_ERR_OK; +} + +static int set_route(sr_session_ctx_t *sess, const char *index, + const char *n_interface /*NULLABLE*/, + const char *n_next_hop /*NULLABLE*/, + const char *prefix, bool is_add) +{ + int rc = SR_ERR_OK; + sr_val_t *value = NULL; + char xpath[XPATH_SIZE] = {0}; + const char *interface = NULL; + const char *next_hop = NULL; + + ARG_CHECK3(SR_ERR_INVAL_ARG, sess, index, prefix); + + if (NULL == n_interface) { + snprintf(xpath, XPATH_SIZE, + "/openconfig-local-routing:local-routes/static-routes/static[prefix='%s']/next-hops/next-hop[index='%s']/interface-ref/config/interface", prefix, index); + + rc = sr_get_item(sess, xpath, &value); + if (SR_ERR_OK != rc) { + SRP_LOG_DBG_MSG("Interface not set"); + return 0; + } + + interface = value->data.string_val; + } else { + interface = n_interface; + } + + if (NULL == n_next_hop) { + snprintf(xpath, XPATH_SIZE, + "/openconfig-local-routing:local-routes/static-routes/static[prefix='%s']/next-hops/next-hop[index='%s']/config/next-hop", prefix, index); + + rc = sr_get_item(sess, xpath, &value); + if (SR_ERR_OK != rc) { + SRP_LOG_DBG_MSG("Next-hop not set"); + return 0; + } + + next_hop = value->data.string_val; + } else { + next_hop = n_next_hop; + } + + int mask = ip_prefix_split(prefix); + if (mask < 1) { + return SR_ERR_INVAL_ARG; + } + + vapi_payload_ip_add_del_route_reply reply = {0}; + + vapi_error_e rv = bin_api_ip_add_del_route(&reply, prefix, mask, + next_hop, is_add, 0, interface); + if (VAPI_OK != rv || reply.retval > 0) { + return SR_ERR_INVAL_ARG; + } + + return VAPI_OK; +} + +// XPATH: /openconfig-local-routing:local-routes/static-routes/static[prefix='%s']/next-hops/next-hop[index='%s']/config/ +int openconfig_local_routing_local_routes_static_routes_static_next_hops_next_hop_config_cb( + sr_session_ctx_t *ds, const char *xpath, sr_notif_event_t event, + __attribute__((unused)) void *private_ctx) +{ + sr_change_iter_t *it = NULL; + sr_change_oper_t oper; + sr_val_t *old_val = NULL; + sr_val_t *new_val = NULL; + int rc = SR_ERR_OK; + char *tmp = NULL; + char static_prefix[XPATH_SIZE] = {0}; + char next_hop_index[XPATH_SIZE] = {0}; + char next_hop[XPATH_SIZE] = {0}; + char old_next_hop_index[XPATH_SIZE] = {0}; + char old_next_hop[XPATH_SIZE] = {0}; + bool index_set = false, next_hop_set = false; + bool old_index_set = false, old_next_hop_set = false; + + ARG_CHECK2(SR_ERR_INVAL_ARG, ds, xpath); + + // if we receive event SR_EV_APPLY - config has changed + + log_recv_event(event, "subtree_change_cb received"); + SRP_LOG_DBG("[SRP_LOG_DBG] xpath: %s", xpath); + + if (event != SR_EV_APPLY) { + return SR_ERR_OK; + } + + if (sr_get_changes_iter(ds, (char *)xpath, &it) != SR_ERR_OK) { + // in example he calls even on fale + sr_free_change_iter(it); + return SR_ERR_OK; + } + + while (sr_get_change_next(ds, it, &oper, + &old_val, &new_val) == SR_ERR_OK) { + + log_recv_oper(oper, "subtree_change_cb received"); + + SRP_LOG_DBG("xpath: %s", new_val->xpath); + + sr_xpath_ctx_t state = {0}; + + tmp = xpath_find_first_key(new_val->xpath, "prefix", &state); + if (NULL == tmp) { + SRP_LOG_DBG_MSG("static_prefix NOT found."); + continue; + } + + strncpy(static_prefix, tmp, XPATH_SIZE); + sr_xpath_recover(&state); + + switch (oper) { + case SR_OP_CREATED: + if (sr_xpath_node_name_eq(new_val->xpath, "index")) { + strncpy(next_hop_index, new_val->data.string_val, + XPATH_SIZE); + index_set = true; + } else if(sr_xpath_node_name_eq(new_val->xpath, "next-hop")) { + next_hop_set = true; + strncpy(next_hop, new_val->data.string_val, XPATH_SIZE); + } else if(sr_xpath_node_name_eq(new_val->xpath, "metric")) { + //TODO: LEAF: metric, type: uint32 + } else if(sr_xpath_node_name_eq(new_val->xpath, "recurse")) { + //TODO: LEAF: recurse, type: boolean + } + + if (index_set && next_hop_set) { + rc = set_route(ds, next_hop_index, NULL, next_hop, + static_prefix, true); + } + break; + + case SR_OP_MODIFIED: + if (sr_xpath_node_name_eq(old_val->xpath, "index")) { + strncpy(old_next_hop_index, old_val->data.string_val, + XPATH_SIZE); + old_index_set = true; + } else if(sr_xpath_node_name_eq(old_val->xpath, "next-hop")) { + old_next_hop_set = true; + strncpy(old_next_hop, old_val->data.string_val, XPATH_SIZE); + } else if(sr_xpath_node_name_eq(old_val->xpath, "metric")) { + //TODO: LEAF: metric, type: uint32 + } else if(sr_xpath_node_name_eq(old_val->xpath, "recurse")) { + //TODO: LEAF: recurse, type: boolean + } + + if (sr_xpath_node_name_eq(new_val->xpath, "index")) { + strncpy(next_hop_index, new_val->data.string_val, + XPATH_SIZE); + index_set = true; + } else if(sr_xpath_node_name_eq(new_val->xpath, "next-hop")) { + next_hop_set = true; + strncpy(next_hop, new_val->data.string_val, XPATH_SIZE); + } else if(sr_xpath_node_name_eq(new_val->xpath, "metric")) { + //TODO: LEAF: metric, type: uint32 + } else if(sr_xpath_node_name_eq(new_val->xpath, "recurse")) { + //TODO: LEAF: recurse, type: boolean + } + + if (old_index_set && old_next_hop_set) { + rc = set_route(ds, old_next_hop_index, NULL, old_next_hop, + static_prefix, false); + } + + if (index_set && next_hop_set) { + rc = set_route(ds, next_hop_index, NULL, next_hop, + static_prefix, true); + } + break; + + case SR_OP_MOVED: + break; + + case SR_OP_DELETED: + if (sr_xpath_node_name_eq(old_val->xpath, "index")) { + strncpy(old_next_hop_index, old_val->data.string_val, + XPATH_SIZE); + old_index_set = true; + } else if(sr_xpath_node_name_eq(old_val->xpath, "next-hop")) { + old_next_hop_set = true; + strncpy(old_next_hop, old_val->data.string_val, XPATH_SIZE); + } else if(sr_xpath_node_name_eq(old_val->xpath, "metric")) { + //TODO: LEAF: metric, type: uint32 + } else if(sr_xpath_node_name_eq(old_val->xpath, "recurse")) { + //TODO: LEAF: recurse, type: boolean + } + + if (old_index_set && old_next_hop_set) { + rc = set_route(ds, old_next_hop_index, NULL, old_next_hop, + static_prefix, false); + } + break; + } + + if (SR_ERR_OK != rc) { + sr_xpath_recover(&state); + + sr_free_val(old_val); + sr_free_val(new_val); + + sr_free_change_iter(it); + return SR_ERR_OPERATION_FAILED; + } + + sr_xpath_recover(&state); + + sr_free_val(old_val); + sr_free_val(new_val); + } + + sr_free_change_iter(it); + return SR_ERR_OK; +} + +// XPATH: /openconfig-local-routing:local-routes/static-routes/static[prefix='%s']/next-hops/next-hop[index='%s']/interface-ref/config/ +int openconfig_local_routing_local_routes_static_routes_static_next_hops_next_hop_interface_ref_config_cb( + sr_session_ctx_t *ds, const char *xpath, sr_notif_event_t event, + __attribute__((unused)) void *private_ctx) +{ + sr_change_iter_t *it = NULL; + sr_change_oper_t oper; + sr_val_t *old_val = NULL; + sr_val_t *new_val = NULL; + char *tmp = NULL; + char static_prefix[XPATH_SIZE] = {0}; + char next_hop_index[XPATH_SIZE] = {0}; + char interface[XPATH_SIZE] = {0}; + char old_interface[XPATH_SIZE] = {0}; + int rc = SR_ERR_OK; + + // if we receive event SR_EV_APPLY - config has changed + + log_recv_event(event, "subtree_change_cb received"); + SRP_LOG_DBG("[SRP_LOG_DBG] xpath: %s", xpath); + + if (event != SR_EV_APPLY) { + return SR_ERR_OK; + } + + if (sr_get_changes_iter(ds, (char *)xpath, &it) != SR_ERR_OK) { + // in example he calls even on fale + sr_free_change_iter(it); + return SR_ERR_OK; + } + + while (sr_get_change_next(ds, it, &oper, + &old_val, &new_val) == SR_ERR_OK) { + + log_recv_oper(oper, "subtree_change_cb received"); + + SRP_LOG_DBG("xpath: %s", new_val->xpath); + + sr_xpath_ctx_t state = {0}; + + tmp = xpath_find_first_key(new_val->xpath, "prefix", &state); + if (NULL == tmp) { + SRP_LOG_DBG_MSG("static_prefix NOT found."); + continue; + } + + strncpy(static_prefix, tmp, XPATH_SIZE); + sr_xpath_recover(&state); + + tmp = xpath_find_first_key(new_val->xpath, "index", &state); + if (NULL == tmp) { + SRP_LOG_DBG_MSG("next-hop_index NOT found."); + continue; + } + + strncpy(next_hop_index, tmp, XPATH_SIZE); + sr_xpath_recover(&state); + + switch (oper) { + case SR_OP_CREATED: + if (sr_xpath_node_name_eq(new_val->xpath, "interface")) { + strncpy(interface, new_val->data.string_val, XPATH_SIZE); + rc = set_route(ds, next_hop_index, interface, NULL, + static_prefix, true); + } else if (sr_xpath_node_name_eq(new_val->xpath, + "subinterface")) { + } + break; + + case SR_OP_MODIFIED: + if (sr_xpath_node_name_eq(old_val->xpath, "interface")) { + strncpy(old_interface, old_val->data.string_val, + XPATH_SIZE); + rc = set_route(ds, next_hop_index, old_interface, NULL, + static_prefix, false); + } else if (sr_xpath_node_name_eq(old_val->xpath, + "subinterface")) { + } + + if (sr_xpath_node_name_eq(new_val->xpath, "interface")) { + strncpy(interface, new_val->data.string_val, XPATH_SIZE); + rc = set_route(ds, next_hop_index, interface, NULL, + static_prefix, true); + } else if (sr_xpath_node_name_eq(new_val->xpath, + "subinterface")) { + } + break; + + case SR_OP_MOVED: + break; + + case SR_OP_DELETED: + if (sr_xpath_node_name_eq(old_val->xpath, "interface")) { + strncpy(old_interface, old_val->data.string_val, + XPATH_SIZE); + rc = set_route(ds, next_hop_index, old_interface, NULL, + static_prefix, false); + } else if (sr_xpath_node_name_eq(old_val->xpath, + "subinterface")) { + } + break; + } + + if (SR_ERR_OK != rc) { + sr_xpath_recover(&state); + + sr_free_val(old_val); + sr_free_val(new_val); + + sr_free_change_iter(it); + return SR_ERR_OPERATION_FAILED; + } + + sr_xpath_recover(&state); + + sr_free_val(old_val); + sr_free_val(new_val); + } + + sr_free_change_iter(it); + return SR_ERR_OK; +} + +typedef struct +{ + u8 length; + u8 address[4]; +} address_prefix_t; + +typedef struct { + char *next_hop_index; + address_prefix_t address_prefix; + const bool is_interface_ref; + sw_interface_details_query_t sw_interface_details_query; + sysr_values_ctx_t sysr_values_ctx; +} sysr_ip_fib_details_ctx_t; + +static +bool address_prefix_cmp(address_prefix_t* address_prefix, + vapi_payload_ip_fib_details* reply) +{ + ARG_CHECK2(false, address_prefix, reply); + + if (address_prefix->length == reply->address_length) + { + if (0 == memcmp(address_prefix->address, reply->address, sizeof(reply->address))) + return true; + } + + return false; +} + +static +bool address_prefix_init(address_prefix_t* address_prefix, char* str_prefix) +{ + ARG_CHECK2(false, address_prefix, str_prefix); + + address_prefix->length = ip_prefix_split(str_prefix); + if(address_prefix->length < 1) + { + SRP_LOG_ERR_MSG("not a valid prefix"); + return false; + } + + return bapi_aton(str_prefix, address_prefix->address); +} + +// XPATH: /openconfig-local-routing:local-routes/static-routes/static/state +int openconfig_local_routing_local_routes_static_routes_static_state_vapi_cb( + vapi_payload_ip_fib_details *reply, + sysr_ip_fib_details_ctx_t *sysr_ip_fib_details_ctx) +{ + sr_val_t *vals = NULL; + int rc = 0; + int vc = 0; + char address_prefix[INET_ADDRSTRLEN + 3] = {0}; + vc = 1; + + ARG_CHECK2(SR_ERR_INVAL_ARG, reply, sysr_ip_fib_details_ctx); + + rc = sr_new_values(vc, &vals); + if (SR_ERR_OK != rc) { + return rc; + } + + //Filling the structure + snprintf(address_prefix, sizeof(address_prefix), "%s/%u", + bapi_ntoa(reply->address), reply->address_length); + + sr_val_build_xpath(&vals[0], "%s/prefix", + sysr_ip_fib_details_ctx->sysr_values_ctx.xpath_root); + sr_val_set_str_data(&vals[0], SR_STRING_T, address_prefix); + + sysr_ip_fib_details_ctx->sysr_values_ctx.values = vals; + sysr_ip_fib_details_ctx->sysr_values_ctx.values_cnt = vc; + + return SR_ERR_OK; +} + +vapi_error_e +ip_routing_dump_cb (struct vapi_ctx_s *ctx, void *callback_ctx, + vapi_error_e rv, bool is_last, + vapi_payload_ip_fib_details * reply) +{ + if (is_last) + { + assert (NULL == reply); + } + else + { + sysr_ip_fib_details_ctx_t *dctx = callback_ctx; + assert(dctx && reply); + + if (address_prefix_cmp(&dctx->address_prefix, reply)) + { + openconfig_local_routing_local_routes_static_routes_static_state_vapi_cb(reply, + dctx); + } + } + + return VAPI_OK; +} + +int openconfig_local_routing_local_routes_static_routes_static_state_cb( + const char *xpath, sr_val_t **values, size_t *values_cnt, + __attribute__((unused)) uint64_t request_id, + __attribute__((unused)) void *private_ctx) +{ + sr_xpath_ctx_t state = {0}; + vapi_error_e rv = 0; + char *tmp = NULL; + char static_prefix[XPATH_SIZE] = {0}; + sysr_ip_fib_details_ctx_t dctx = {0}; + + ARG_CHECK3(SR_ERR_INVAL_ARG, xpath, values, values_cnt); + + tmp = xpath_find_first_key(xpath, "prefix", &state); + if (NULL == tmp) { + SRP_LOG_ERR_MSG("static_prefix not found."); + return SR_ERR_INVAL_ARG; + } + + strncpy(static_prefix, tmp, XPATH_SIZE); + sr_xpath_recover(&state); + + //VPP callback + snprintf(dctx.sysr_values_ctx.xpath_root, XPATH_SIZE, "/openconfig-local-routing:local-routes/static-routes/static[prefix='%s']/state", static_prefix); + + if (!address_prefix_init(&dctx.address_prefix, static_prefix)) + { + return SR_ERR_INVAL_ARG; + } + + + vapi_msg_ip_fib_dump *mp = vapi_alloc_ip_fib_dump (g_vapi_ctx); + + VAPI_CALL(vapi_ip_fib_dump(g_vapi_ctx, mp, ip_routing_dump_cb, &dctx)); + if(VAPI_OK != rv) + { + SRP_LOG_ERR_MSG("VAPI call failed"); + return SR_ERR_INVAL_ARG; + } + + sr_xpath_recover(&state); + *values = dctx.sysr_values_ctx.values; + *values_cnt = dctx.sysr_values_ctx.values_cnt; + + return SR_ERR_OK; +} + +// // XPATH: /openconfig-local-routing:local-routes/static-routes/l/next-hops/next-hop/state +int openconfig_local_routing_local_routes_static_routes_static_next_hops_next_hop_state_vapi_cb( + vapi_type_fib_path *reply, + sysr_ip_fib_details_ctx_t *sysr_ip_fib_details_ctx) +{ + sr_val_t *vals = NULL; + int rc = 0; + int vc = 0; + char next_hop[INET_ADDRSTRLEN] = {0}; + + ARG_CHECK2(SR_ERR_INVAL_ARG, reply, sysr_ip_fib_details_ctx); + + vc = 3; + /* convenient functions such as this can be found in sysrepo/values.h */ + rc = sr_new_values(vc, &vals); + if (SR_ERR_OK != rc) { + return rc; + } + + sr_val_build_xpath(&vals[0], "%s/index", + sysr_ip_fib_details_ctx->sysr_values_ctx.xpath_root); + sr_val_set_str_data(&vals[0], SR_STRING_T, + sysr_ip_fib_details_ctx->next_hop_index); + + strncpy(next_hop, bapi_ntoa(reply->next_hop), sizeof(next_hop)); + sr_val_build_xpath(&vals[1], "%s/next-hop", sysr_ip_fib_details_ctx->sysr_values_ctx.xpath_root); + sr_val_set_str_data(&vals[1], SR_STRING_T, next_hop); + + sr_val_build_xpath(&vals[2], "%s/metric", sysr_ip_fib_details_ctx->sysr_values_ctx.xpath_root); + vals[2].type = SR_UINT32_T; + vals[2].data.uint32_val = reply->weight; + + // sr_val_build_xpath(&vals[3], "%s/recurse", sysr_ip_fib_details_ctx->sysr_values_ctx.xpath_root); + // vals[3].type = SR_BOOL_T; + // vals[3].data.bool_val = YANG INPUT TYPE: boolean; + + sysr_ip_fib_details_ctx->sysr_values_ctx.values = vals; + sysr_ip_fib_details_ctx->sysr_values_ctx.values_cnt = vc; + + return SR_ERR_OK; +} + +// // XPATH: /openconfig-local-routing:local-routes/static-routes/l/next-hops/next-hop/interface-ref/state +int openconfig_local_routing_local_routes_static_routes_static_next_hops_next_hop_interface_ref_state_vapi_cb( + sysr_ip_fib_details_ctx_t * dctx) +{ + sr_val_t *vals = NULL; + int rc = 0; + int vc = 0; + + ARG_CHECK(SR_ERR_INVAL_ARG, dctx); + + vc = 2; + /* convenient functions such as this can be found in sysrepo/values.h */ + rc = sr_new_values(vc, &vals); + if (SR_ERR_OK != rc) { + return rc; + } + + sr_val_build_xpath(&vals[0], "%s/interface", + dctx->sysr_values_ctx.xpath_root); + sr_val_set_str_data(&vals[0], SR_STRING_T, + (const char*)dctx->sw_interface_details_query.sw_interface_details.interface_name); + + sr_val_build_xpath(&vals[1], "%s/subinterface", + dctx->sysr_values_ctx.xpath_root); + vals[1].type = SR_UINT32_T; + vals[1].data.uint32_val = 0; + + dctx->sysr_values_ctx.values = vals; + dctx->sysr_values_ctx.values_cnt = vc; + + return SR_ERR_OK; +} + +vapi_error_e +ip_routing_next_hop_dump_cb (struct vapi_ctx_s *ctx, void *callback_ctx, + vapi_error_e rv, bool is_last, + vapi_payload_ip_fib_details * reply) +{ + ARG_CHECK(VAPI_EINVAL, callback_ctx); + + sysr_ip_fib_details_ctx_t *dctx = callback_ctx; + + if (is_last) + { + assert (NULL == reply); + } + else + { + assert (NULL != reply); + + if (reply->count > 0 && address_prefix_cmp(&dctx->address_prefix, reply)) + { + if (dctx->is_interface_ref) + { + dctx->sw_interface_details_query.interface_found = true; + dctx->sw_interface_details_query.sw_interface_details.sw_if_index = reply->path[0].sw_if_index; + //sw_interface_dump will have to be called outside this VAPI + } + else + { + openconfig_local_routing_local_routes_static_routes_static_next_hops_next_hop_state_vapi_cb(reply->path, dctx); + } + } + } + + return VAPI_OK; +} + +int next_hop_inner( + bool is_interface_ref, const char *xpath, sr_val_t **values, + size_t *values_cnt, __attribute__((unused)) uint64_t request_id, + __attribute__((unused)) void *private_ctx) +{ + sr_xpath_ctx_t state = {0}; + vapi_error_e rv; + char *tmp = NULL; + char static_prefix[XPATH_SIZE] = {0}; + char next_hop_index[XPATH_SIZE] = {0}; + sysr_ip_fib_details_ctx_t dctx = {.is_interface_ref = is_interface_ref}; + + ARG_CHECK3(SR_ERR_INVAL_ARG, xpath, values, values_cnt); + + tmp = xpath_find_first_key(xpath, "prefix", &state); + if (NULL == tmp) { + SRP_LOG_ERR_MSG("static_prefix not found."); + return SR_ERR_INVAL_ARG; + } + strncpy(static_prefix, tmp, XPATH_SIZE); + sr_xpath_recover(&state); + + tmp = xpath_find_first_key(xpath, "index", &state); + if (NULL == tmp) { + SRP_LOG_ERR_MSG("index not found."); + return SR_ERR_INVAL_ARG; + } + strncpy(next_hop_index, tmp, XPATH_SIZE); + sr_xpath_recover(&state); + + //VPP callback + dctx.next_hop_index = next_hop_index; + if (is_interface_ref) { + snprintf(dctx.sysr_values_ctx.xpath_root, XPATH_SIZE, + "/openconfig-local-routing:local-routes/static-routes/static[prefix='%s']/next-hops/next-hop[index='%s']/interface-ref/state", + static_prefix, next_hop_index); + } else { + snprintf(dctx.sysr_values_ctx.xpath_root, XPATH_SIZE, + "/openconfig-local-routing:local-routes/static-routes/static[prefix='%s']/next-hops/next-hop[index='%s']/state", + static_prefix, next_hop_index); + } + + if (!address_prefix_init(&dctx.address_prefix, static_prefix)) + { + return SR_ERR_INVAL_ARG; + } + + vapi_msg_ip_fib_dump *mp = vapi_alloc_ip_fib_dump (g_vapi_ctx); + VAPI_CALL(vapi_ip_fib_dump(g_vapi_ctx, mp, ip_routing_next_hop_dump_cb, &dctx)); + if (VAPI_OK != rv) + { + SRP_LOG_ERR_MSG("VAPI call failed"); + return SR_ERR_INVAL_ARG; + } + + if (is_interface_ref) + { + if (dctx.sw_interface_details_query.interface_found) + { + if (!get_interface_name(&dctx.sw_interface_details_query)) + { + return SR_ERR_INVAL_ARG; + } + if (strlen((const char*) + dctx.sw_interface_details_query.sw_interface_details.interface_name)) { + openconfig_local_routing_local_routes_static_routes_static_next_hops_next_hop_interface_ref_state_vapi_cb(&dctx); + } + } + } + + sr_xpath_recover(&state); + *values = dctx.sysr_values_ctx.values; + *values_cnt = dctx.sysr_values_ctx.values_cnt; + + return SR_ERR_OK; +} + +int openconfig_local_routing_local_routes_static_routes_static_next_hops_next_hop_state_cb( + const char *xpath, sr_val_t **values, size_t *values_cnt, + uint64_t request_id, void *private_ctx) +{ + return next_hop_inner(false, xpath, values, values_cnt, request_id, + private_ctx); +} + +int openconfig_local_routing_local_routes_static_routes_static_next_hops_next_hop_interface_ref_state_cb( + const char *xpath, sr_val_t **values, size_t *values_cnt, + uint64_t request_id, void *private_ctx) +{ + return next_hop_inner(true, xpath, values, values_cnt, request_id, + private_ctx); +} -- cgit 1.2.3-korg