aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/plugins/CMakeLists.txt1
-rw-r--r--src/plugins/openconfig/openconfig_local_routing.c730
-rw-r--r--src/plugins/openconfig/openconfig_local_routing.h47
3 files changed, 778 insertions, 0 deletions
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
index 7482ad7..2d1b13e 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -36,6 +36,7 @@ set(PLUGINS_SOURCES
sc_interface.c
sc_plugins.c
openconfig/openconfig_interfaces.c
+ openconfig/openconfig_local_routing.c
openconfig/sys_util.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 <assert.h>
+#include <string.h>
+#include <sysrepo/xpath.h>
+#include <sysrepo/values.h>
+
+#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);
+}
diff --git a/src/plugins/openconfig/openconfig_local_routing.h b/src/plugins/openconfig/openconfig_local_routing.h
new file mode 100644
index 0000000..4cde542
--- /dev/null
+++ b/src/plugins/openconfig/openconfig_local_routing.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#ifndef __OPENCONFIG_LOCAL_ROUTING_H__
+#define __OPENCONFIG_LOCAL_ROUTING_H__
+
+#include <sysrepo.h>
+
+int openconfig_local_routing_mod_cb(sr_session_ctx_t *session,
+ const char *module_name,
+ sr_notif_event_t event,
+ void *private_ctx);
+
+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,
+ void *private_ctx);
+
+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,
+ void *private_ctx);
+
+int openconfig_local_routing_local_routes_static_routes_static_state_cb(
+ const char *xpath, sr_val_t **values, size_t *values_cnt,
+ uint64_t request_id, void *private_ctx);
+
+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);
+
+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);
+
+#endif /* __OPENCONFIG_LOCAL_ROUTING_H__ */