aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/ietf
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/ietf')
-rw-r--r--src/plugins/ietf/ietf_interface.c570
-rw-r--r--src/plugins/ietf/ietf_interface.cpp613
-rw-r--r--src/plugins/ietf/ietf_nat.c525
-rw-r--r--src/plugins/ietf/ietf_nat.cpp336
4 files changed, 949 insertions, 1095 deletions
diff --git a/src/plugins/ietf/ietf_interface.c b/src/plugins/ietf/ietf_interface.c
deleted file mode 100644
index b4a2bc7..0000000
--- a/src/plugins/ietf/ietf_interface.c
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- * 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 <stdio.h>
-#include <sys/socket.h>
-
-#include <sc_plugins.h>
-
-#include <scvpp/interface.h>
-#include <scvpp/ip.h>
-
-#include <vpp-api/client/stat_client.h>
-
-/**
- * @brief Callback to be called by any config change of
- * "/ietf-interfaces:interfaces/interface/enabled" leaf.
- */
-static int
-ietf_interface_enable_disable_cb(sr_session_ctx_t *session, const char *xpath,
- sr_notif_event_t event, void *private_ctx)
-{
- UNUSED(private_ctx);
- char *if_name = NULL;
- sr_change_iter_t *iter = NULL;
- sr_change_oper_t op = SR_OP_CREATED;
- sr_val_t *old_val = NULL;
- sr_val_t *new_val = NULL;
- sr_xpath_ctx_t xpath_ctx = { 0, };
- int rc = SR_ERR_OK, op_rc = SR_ERR_OK;
-
- SRP_LOG_INF("In %s", __FUNCTION__);
-
- /* no-op for apply, we only care about SR_EV_ENABLED, SR_EV_VERIFY, SR_EV_ABORT */
- if (SR_EV_APPLY == event)
- return SR_ERR_OK;
-
- SRP_LOG_DBG("'%s' modified, event=%d", xpath, event);
-
- /* get changes iterator */
- rc = sr_get_changes_iter(session, xpath, &iter);
- if (SR_ERR_OK != rc) {
- sr_free_change_iter(iter);
- SRP_LOG_ERR("Unable to retrieve change iterator: %s", sr_strerror(rc));
- return rc;
- }
-
- foreach_change (session, iter, op, old_val, new_val) {
-
- SRP_LOG_DBG("A change detected in '%s', op=%d", new_val ? new_val->xpath : old_val->xpath, op);
- if_name = sr_xpath_key_value(new_val ? new_val->xpath : old_val->xpath, "interface", "name", &xpath_ctx);
- switch (op) {
- case SR_OP_CREATED:
- case SR_OP_MODIFIED:
- op_rc = interface_enable(if_name, new_val->data.bool_val);
- break;
- case SR_OP_DELETED:
- op_rc = interface_enable(if_name, false /* !enable */);
- break;
- default:
- break;
- }
- sr_xpath_recover(&xpath_ctx);
- if (SR_ERR_INVAL_ARG == op_rc) {
- sr_set_error(session, "Invalid interface name.", new_val ? new_val->xpath : old_val->xpath);
- }
- sr_free_val(old_val);
- sr_free_val(new_val);
- }
- sr_free_change_iter(iter);
-
- return op_rc;
-}
-
-/**
- * @brief Modify existing IPv4/IPv6 config on an interface.
- */
-static int
-interface_ipv46_config_modify(const char *if_name, sr_val_t *old_val,
- sr_val_t *new_val, bool is_ipv6)
-{
- sr_xpath_ctx_t xpath_ctx = { 0, };
- char *addr = NULL;
- uint8_t prefix = 0;
- int rc = SR_ERR_OK;
-
- SRP_LOG_DBG("Updating IP config on interface '%s'.", if_name);
-
- /* get old config to be deleted */
- if (SR_UINT8_T == old_val->type) {
- prefix = old_val->data.uint8_val;
- } else if (SR_STRING_T == old_val->type) {
- prefix = netmask_to_prefix(old_val->data.string_val);
- } else {
- return SR_ERR_INVAL_ARG;
- }
- addr = sr_xpath_key_value((char*)old_val->xpath, "address", "ip", &xpath_ctx);
- sr_xpath_recover(&xpath_ctx);
-
- /* delete old IP config */
- rc = ipv46_config_add_remove(if_name, addr, prefix, is_ipv6, false /* remove */);
- if (SR_ERR_OK != rc) {
- SRP_LOG_ERR("Unable to remove old IP address config, rc=%d", rc);
- return rc;
- }
-
- /* update the config with the new value */
- if (sr_xpath_node_name_eq(new_val->xpath, "prefix-length")) {
- prefix = new_val->data.uint8_val;
- } else if (sr_xpath_node_name_eq(new_val->xpath, "netmask")) {
- prefix = netmask_to_prefix(new_val->data.string_val);
- }
-
- /* set new IP config */
- rc = ipv46_config_add_remove(if_name, addr, prefix, is_ipv6, true /* add */);
- if (SR_ERR_OK != rc) {
- SRP_LOG_ERR("Unable to remove old IP address config, rc=%d", rc);
- return rc;
- }
-
- return rc;
-}
-
-/**
- * @brief Callback to be called by any config change in subtrees
- * "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/address"
- * or "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6/address".
- */
-static int
-ietf_interface_ipv46_address_change_cb(sr_session_ctx_t *session,
- const char *xpath,
- sr_notif_event_t event,
- void *private_ctx)
-{
- UNUSED(private_ctx);
- sr_change_iter_t *iter = NULL;
- sr_change_oper_t op = SR_OP_CREATED;
- sr_val_t *old_val = NULL;
- sr_val_t *new_val = NULL;
- sr_xpath_ctx_t xpath_ctx = { 0, };
- bool is_ipv6 = false, has_addr = false, has_prefix = false;
- char addr[VPP_IP6_ADDRESS_STRING_LEN] = {0};
- uint8_t prefix = 0;
- char *node_name = NULL, *if_name = NULL;
- int rc = SR_ERR_OK, op_rc = SR_ERR_OK;
-
- SRP_LOG_INF("In %s", __FUNCTION__);
-
- /* no-op for apply, we only care about SR_EV_ENABLED, SR_EV_VERIFY, SR_EV_ABORT */
- if (SR_EV_APPLY == event) {
- return SR_ERR_OK;
- }
- SRP_LOG_DBG("'%s' modified, event=%d", xpath, event);
-
- /* check whether we are handling ipv4 or ipv6 config */
- node_name = sr_xpath_node_idx((char*)xpath, 2, &xpath_ctx);
- if (NULL != node_name && 0 == strcmp(node_name, "ipv6")) {
- is_ipv6 = true;
- }
- sr_xpath_recover(&xpath_ctx);
-
- /* get changes iterator */
- rc = sr_get_changes_iter(session, xpath, &iter);
- if (SR_ERR_OK != rc) {
- sr_free_change_iter(iter);
- SRP_LOG_ERR("Unable to retrieve change iterator: %s", sr_strerror(rc));
- return rc;
- }
-
- foreach_change(session, iter, op, old_val, new_val) {
-
- SRP_LOG_DBG("A change detected in '%s', op=%d", new_val ? new_val->xpath : old_val->xpath, op);
- if_name = strdup(sr_xpath_key_value(new_val ? new_val->xpath : old_val->xpath, "interface", "name", &xpath_ctx));
- sr_xpath_recover(&xpath_ctx);
-
- switch (op) {
- case SR_OP_CREATED:
- if (SR_LIST_T == new_val->type) {
- /* create on list item - reset state vars */
- has_addr = has_prefix = false;
- } else {
- if (sr_xpath_node_name_eq(new_val->xpath, "ip")) {
- strncpy(addr, new_val->data.string_val, strlen(new_val->data.string_val));
- has_addr = true;
- } else if (sr_xpath_node_name_eq(new_val->xpath, "prefix-length")) {
- prefix = new_val->data.uint8_val;
- has_prefix = true;
- } else if (sr_xpath_node_name_eq(new_val->xpath, "netmask")) {
- prefix = netmask_to_prefix(new_val->data.string_val);
- has_prefix = true;
- }
- if (has_addr && has_prefix) {
- op_rc = ipv46_config_add_remove(if_name, addr, prefix, is_ipv6, true /* add */);
- }
- }
- break;
- case SR_OP_MODIFIED:
- //TODO Why is it using old_val and new_val?
- op_rc = interface_ipv46_config_modify(if_name, old_val, new_val, is_ipv6);
- break;
- case SR_OP_DELETED:
- if (SR_LIST_T == old_val->type) {
- /* delete on list item - reset state vars */
- has_addr = has_prefix = false;
- } else {
- if (sr_xpath_node_name_eq(old_val->xpath, "ip")) {
- strncpy(addr, old_val->data.string_val, strlen(old_val->data.string_val));
- has_addr = true;
- } else if (sr_xpath_node_name_eq(old_val->xpath, "prefix-length")) {
- prefix = old_val->data.uint8_val;
- has_prefix = true;
- } else if (sr_xpath_node_name_eq(old_val->xpath, "netmask")) {
- prefix = netmask_to_prefix(old_val->data.string_val);
- has_prefix = true;
- }
- if (has_addr && has_prefix) {
- op_rc = ipv46_config_add_remove(if_name, addr, prefix, is_ipv6, false /* !add */);
- }
- }
- break;
- default:
- break;
- }
- if (SR_ERR_INVAL_ARG == op_rc) {
- sr_set_error(session, "Invalid interface name.", new_val ? new_val->xpath : old_val->xpath);
- }
- free(if_name);
- sr_free_val(old_val);
- sr_free_val(new_val);
- }
- sr_free_change_iter(iter);
-
- return op_rc;
-}
-
-/**
- * @brief Callback to be called by any config change under "/ietf-interfaces:interfaces-state/interface" path.
- * Does not provide any functionality, needed just to cover not supported config leaves.
- */
-static int
-ietf_interface_change_cb(sr_session_ctx_t *session, const char *xpath,
- sr_notif_event_t event, void *private_ctx)
-{
- UNUSED(session); UNUSED(xpath); UNUSED(event); UNUSED(private_ctx);
-
- SRP_LOG_INF("In %s", __FUNCTION__);
-
- return SR_ERR_OK;
-}
-
-/**
- * @brief Callback to be called by any request for state data under "/ietf-interfaces:interfaces-state/interface" path.
- * Here we reply systematically with all interfaces, it the responsability of
- * sysrepo apply a filter not to answer undesired interfaces.
- */
-static int
-ietf_interface_state_cb(const char *xpath, sr_val_t **values,
- size_t *values_cnt, uint64_t request_id,
- const char *original_xpath, void *private_ctx)
-{
- UNUSED(request_id); UNUSED(original_xpath); UNUSED(private_ctx);
- struct elt* stack;
- sw_interface_dump_t *dump;
- sr_val_t *val = NULL;
- int vc = 5; //number of answer per interfaces
- int cnt = 0; //value counter
- int rc = SR_ERR_OK;
-
- SRP_LOG_INF("In %s", __FUNCTION__);
-
- if (!sr_xpath_node_name_eq(xpath, "interface"))
- goto nothing_todo; //no interface field specified
-
- /* dump interfaces */
- stack = interface_dump_all();
- if (!stack)
- goto nothing_todo; //no element returned
-
- /* allocate array of values to be returned */
- SRP_LOG_DBG("number of interfaces: %d", stack->id+1);
- rc = sr_new_values((stack->id + 1)* vc, &val);
- if (0 != rc)
- goto nothing_todo;
-
- foreach_stack_elt(stack) {
- dump = (sw_interface_dump_t *) data;
-
- SRP_LOG_DBG("State of interface %s, xpath %s", dump->interface_name, xpath);
- //TODO need support for type propvirtual
- sr_val_build_xpath(&val[cnt], "%s[name='%s']/type", xpath, dump->interface_name);
- sr_val_set_str_data(&val[cnt], SR_IDENTITYREF_T, "iana-if-type:ethernetCsmacd");
- cnt++;
-
- //Be careful, it needs if-mib feature to work !
- sr_val_build_xpath(&val[cnt], "%s[name='%s']/admin-status", xpath, dump->interface_name);
- sr_val_set_str_data(&val[cnt], SR_ENUM_T, dump->link_up_down ? "up" : "down");
- cnt++;
-
- sr_val_build_xpath(&val[cnt], "%s[name='%s']/oper-status", xpath, dump->interface_name);
- sr_val_set_str_data(&val[cnt], SR_ENUM_T, dump->link_up_down ? "up" : "down");
- cnt++;
-
- sr_val_build_xpath(&val[cnt], "%s[name='%s']/phys-address", xpath, dump->interface_name);
- if (dump->l2_address_length > 0) {
- sr_val_build_str_data(&val[cnt], SR_STRING_T,
- "%02x:%02x:%02x:%02x:%02x:%02x",
- dump->l2_address[0], dump->l2_address[1],
- dump->l2_address[2], dump->l2_address[3],
- dump->l2_address[4], dump->l2_address[5]);
- } else {
- sr_val_build_str_data(&val[cnt], SR_STRING_T, "%02x:%02x:%02x:%02x:%02x:%02x", 0,0,0,0,0,0);
- }
- cnt++;
-
- sr_val_build_xpath(&val[cnt], "%s[name='%s']/speed", xpath, dump->interface_name);
- val[cnt].type = SR_UINT64_T;
- val[cnt].data.uint64_val = dump->link_speed;
- cnt++;
-
- free(dump);
- }
-
- *values = val;
- *values_cnt = cnt;
-
- return SR_ERR_OK;
-
-nothing_todo:
- *values = NULL;
- *values_cnt = 0;
- return rc;
-}
-
-static inline stat_segment_data_t* fetch_stat_index(const char *path) {
- stat_segment_data_t *r;
- u32 *stats = NULL;
- u8 **patterns = NULL;
-
- patterns = stat_segment_string_vector(patterns, path);
- if (!patterns)
- return NULL;
-
- do {
- stats = stat_segment_ls(patterns);
- if (!stats)
- return NULL;
-
- r = stat_segment_dump(stats);
- } while (r == NULL); /* Memory layout has changed */
-
- return r;
-}
-
-static inline uint64_t get_counter(stat_segment_data_t *r, int itf_idx)
-{
- if (r == NULL) {
- SRP_LOG_ERR_MSG("stat segment can not be NULL");
- return 0;
- }
-
- if (r->type != STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE) {
- SRP_LOG_ERR_MSG("Only simple counter");
- return 0;
- }
-
- return r->simple_counter_vec[0][itf_idx];
-}
-
-static inline uint64_t get_bytes(stat_segment_data_t *r, int itf_idx)
-{
- if (r->type != STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED) {
- SRP_LOG_ERR_MSG("Only combined counter");
- return 0;
- }
-
- return r->combined_counter_vec[0][itf_idx].bytes;
-}
-
-static inline uint64_t get_packets(stat_segment_data_t *r, int itf_idx)
-{
- if (r->type != STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED) {
- SRP_LOG_ERR_MSG("Only combined counter");
- return 0;
- }
-
- return r->combined_counter_vec[0][itf_idx].packets;
-}
-
-/**
- * @brief Callback to be called by any request for state data under
- * "/ietf-interfaces:interfaces-state/interface/statistics" path.
- */
-static int
-interface_statistics_cb(const char *xpath, sr_val_t **values,
- size_t *values_cnt, uint64_t request_id,
- const char *original_xpath, void *private_ctx)
-{
- UNUSED(request_id); UNUSED(original_xpath); UNUSED(private_ctx);
- sr_val_t *val = NULL;
- int vc = 10;
- int cnt = 0; //value counter
- int rc = SR_ERR_OK;
- sr_xpath_ctx_t state = {0};
- char *tmp;
- char interface_name[VPP_INTFC_NAME_LEN] = {0};
- uint32_t itf_idx;
- stat_segment_data_t *r;
-
- SRP_LOG_INF("In %s", __FUNCTION__);
-
- /* Retrieve the interface asked */
- tmp = sr_xpath_key_value((char*) xpath, "interface", "name", &state);
- if (!tmp) {
- SRP_LOG_ERR_MSG("XPATH interface name not found");
- return SR_ERR_INVAL_ARG;
- }
- strncpy(interface_name, tmp, VPP_INTFC_NAME_LEN);
- sr_xpath_recover(&state);
-
- /* allocate array of values to be returned */
- rc = sr_new_values(vc, &val);
- if (0 != rc)
- goto nothing_todo;
-
- rc = get_interface_id(interface_name, &itf_idx);
- if (rc != 0)
- goto nothing_todo;
-
- SRP_LOG_DBG("name:%s index:%d", interface_name, itf_idx);
-
- r = fetch_stat_index("/if");
- if (!r)
- goto nothing_todo;
-
- for (int i = 0; i < stat_segment_vec_len(r); i++) {
- if (strcmp(r[i].name, "/if/rx") == 0) {
- sr_val_build_xpath(&val[cnt], "%s/in-octets", xpath, 5);
- val[cnt].type = SR_UINT64_T;
- val[cnt].data.uint64_val = get_bytes(&r[i], itf_idx);
- cnt++;
- } else if (strcmp(r[i].name, "/if/rx-unicast") == 0) {
- sr_val_build_xpath(&val[cnt], "%s/in-unicast-pkts", xpath, 5);
- val[cnt].type = SR_UINT64_T;
- val[cnt].data.uint64_val = get_packets(&r[i], itf_idx);
- cnt++;
- } else if (strcmp(r[i].name, "/if/rx-broadcast") == 0) {
- sr_val_build_xpath(&val[cnt], "%s/in-broadcast-pkts", xpath, 5);
- val[cnt].type = SR_UINT64_T;
- val[cnt].data.uint64_val = get_packets(&r[i], itf_idx);
- cnt++;
- } else if (strcmp(r[i].name, "/if/rx-multicast") == 0) {
- sr_val_build_xpath(&val[cnt], "%s/in-multicast-pkts", xpath, 5);
- val[cnt].type = SR_UINT64_T;
- val[cnt].data.uint64_val = get_packets(&r[i], itf_idx);
- cnt++;
- } else if (strcmp(r[i].name, "/if/rx-error") == 0) {
- sr_val_build_xpath(&val[cnt], "%s/in-errors", xpath, 5);
- val[cnt].type = SR_UINT32_T;
- //Be carefeul cast uint64 to uint32
- val[cnt].data.uint32_val = get_counter(&r[i], itf_idx);
- cnt++;
- } else if (strcmp(r[i].name, "/if/tx") == 0) {
- sr_val_build_xpath(&val[cnt], "%s/out-octets", xpath, 5);
- val[cnt].type = SR_UINT64_T;
- val[cnt].data.uint64_val = get_bytes(&r[i], itf_idx);
- cnt++;
- } else if (strcmp(r[i].name, "/if/tx-unicast") == 0) {
- sr_val_build_xpath(&val[cnt], "%s/out-unicast-pkts", xpath, 5);
- val[cnt].type = SR_UINT64_T;
- val[cnt].data.uint64_val = get_packets(&r[i], itf_idx);
- cnt++;
- } else if (strcmp(r[i].name, "/if/tx-broadcast") == 0) {
- sr_val_build_xpath(&val[cnt], "%s/out-broadcast-pkts", xpath, 5);
- val[cnt].type = SR_UINT64_T;
- val[cnt].data.uint64_val = get_packets(&r[i], itf_idx);
- cnt++;
- } else if (strcmp(r[i].name, "/if/tx-multicast") == 0) {
- sr_val_build_xpath(&val[cnt], "%s/out-multicast-pkts", xpath, 5);
- val[cnt].type = SR_UINT64_T;
- val[cnt].data.uint64_val = get_packets(&r[i], itf_idx);
- cnt++;
- } else if (strcmp(r[i].name, "/if/tx-error") == 0) {
- sr_val_build_xpath(&val[cnt], "%s/out-errors", xpath, 5);
- val[cnt].type = SR_UINT32_T;
- //Be carefeul cast uint64 to uint32
- val[cnt].data.uint32_val = get_counter(&r[i], itf_idx);
- cnt++;
- }
- }
-
- *values = val;
- *values_cnt = cnt;
-
- return SR_ERR_OK;
-
-nothing_todo:
- *values = NULL;
- *values_cnt = 0;
- return rc;
-}
-
-
-int
-ietf_interface_init(sc_plugin_main_t *pm)
-{
- int rc = SR_ERR_OK;
- SRP_LOG_DBG_MSG("Initializing ietf-interface plugin.");
-
- rc = sr_subtree_change_subscribe(pm->session, "/ietf-interfaces:interfaces/interface",
- ietf_interface_change_cb, NULL, 0, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_EV_ENABLED, &pm->subscription);
- if (SR_ERR_OK != rc) {
- goto error;
- }
-
- rc = sr_subtree_change_subscribe(pm->session, "/ietf-interfaces:interfaces/interface/enabled",
- ietf_interface_enable_disable_cb, NULL, 100, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_EV_ENABLED, &pm->subscription);
- if (SR_ERR_OK != rc) {
- goto error;
- }
-
- rc = sr_subtree_change_subscribe(pm->session, "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/address",
- ietf_interface_ipv46_address_change_cb, NULL, 99, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_EV_ENABLED, &pm->subscription);
- if (SR_ERR_OK != rc) {
- goto error;
- }
-
- rc = sr_subtree_change_subscribe(pm->session, "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6/address",
- ietf_interface_ipv46_address_change_cb, NULL, 98, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_EV_ENABLED, &pm->subscription);
- if (SR_ERR_OK != rc) {
- goto error;
- }
-
- rc = sr_dp_get_items_subscribe(pm->session, "/ietf-interfaces:interfaces-state",
- ietf_interface_state_cb, NULL, SR_SUBSCR_CTX_REUSE, &pm->subscription);
- if (SR_ERR_OK != rc) {
- goto error;
- }
-
- rc = sr_dp_get_items_subscribe(pm->session, "/ietf-interfaces:interfaces-state/interface/statistics",
- interface_statistics_cb, NULL, SR_SUBSCR_CTX_REUSE, &pm->subscription);
- if (SR_ERR_OK != rc) {
- goto error;
- }
-
- SRP_LOG_DBG_MSG("ietf-interface plugin initialized successfully.");
- return SR_ERR_OK;
-
-error:
- SRP_LOG_ERR("Error by initialization of ietf-interface plugin. Error : %d", rc);
- return rc;
-}
-
-void
-ietf_interface_exit(__attribute__((unused)) sc_plugin_main_t *pm)
-{
-}
-
-SC_INIT_FUNCTION(ietf_interface_init);
-SC_EXIT_FUNCTION(ietf_interface_exit);
diff --git a/src/plugins/ietf/ietf_interface.cpp b/src/plugins/ietf/ietf_interface.cpp
new file mode 100644
index 0000000..74fc594
--- /dev/null
+++ b/src/plugins/ietf/ietf_interface.cpp
@@ -0,0 +1,613 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * 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.
+ */
+
+/* This file implements:
+ * - creation, configuration, deletion of an interface.
+ * - adding, modifying, removing a prefix from an interface
+ * - streaming of an interface statistics
+ *
+ * Even physical interfaces must be created before applying configuration to
+ * them. Else, it results in undefined behavior. (cf RFC 8343)
+ */
+
+#include <string>
+#include <exception>
+#include <memory>
+
+#include <vom/interface.hpp>
+#include <vom/om.hpp>
+#include <vom/l3_binding.hpp>
+#include <vom/route.hpp>
+
+#include <vpp-oper/interface.hpp>
+
+#include "sc_plugins.h"
+#include "sys_util.h"
+
+using namespace std;
+
+using VOM::interface;
+using VOM::OM;
+using VOM::HW;
+using VOM::l3_binding;
+using VOM::rc_t;
+
+using type_t = VOM::interface::type_t;
+using admin_state_t = VOM::interface::admin_state_t;
+
+class interface_builder {
+ public:
+ interface_builder() {}
+
+ shared_ptr<VOM::interface> build() {
+ if (m_name.empty() || m_type.empty())
+ return nullptr;
+ return make_shared<interface>(m_name, type_t::from_string(m_type),
+ admin_state_t::from_int(m_state));
+ }
+
+ /* Getters */
+ string name() {
+ return m_name;
+ }
+
+ /* Setters */
+ interface_builder& set_name(string n) {
+ m_name = n;
+ return *this;
+ }
+
+ interface_builder& set_type(string t) {
+ if (t == "iana-if-type:ethernetCsmacd")
+ m_type = "ETHERNET";
+ return *this;
+ }
+
+ interface_builder& set_state(bool enable) {
+ m_state = enable;
+ return *this;
+ }
+
+ std::string to_string() {
+ std::ostringstream os;
+ os << m_name << "," << m_type << "," << m_state;
+ return os.str();
+ }
+
+ private:
+ string m_name;
+ string m_type;
+ bool m_state;
+};
+
+/* @brief creation of ethernet devices */
+static int
+ietf_interface_create_cb(sr_session_ctx_t *session, const char *xpath,
+ sr_notif_event_t event, void *private_ctx)
+{
+ UNUSED(private_ctx);
+ shared_ptr<VOM::interface> intf;
+ interface_builder builder;
+ string if_name;
+ sr_change_iter_t *iter = nullptr;
+ sr_xpath_ctx_t xpath_ctx;
+ sr_val_t *old_val = nullptr;
+ sr_val_t *new_val = nullptr;
+ sr_change_oper_t op;
+ bool create, remove, modify;
+ int rc;
+
+ SRP_LOG_INF("In %s", __FUNCTION__);
+
+ /* no-op for apply, we only care about SR_EV_{ENABLED,VERIFY,ABORT} */
+ if (SR_EV_VERIFY != event)
+ return SR_ERR_OK;
+
+ SRP_LOG_DBG("'%s' modified, event=%d", xpath, event);
+
+ /* get changes iterator */
+ rc = sr_get_changes_iter(session, xpath, &iter);
+ if (SR_ERR_OK != rc) {
+ sr_free_change_iter(iter);
+ SRP_LOG_ERR("Unable to retrieve change iterator: %s", sr_strerror(rc));
+ return SR_ERR_OPERATION_FAILED;
+ }
+
+ foreach_change (session, iter, op, old_val, new_val) {
+ SRP_LOG_INF("Change xpath: %s",
+ old_val ? old_val->xpath : new_val->xpath);
+ switch (op) {
+ case SR_OP_MODIFIED:
+ SRP_LOG_INF_MSG("Modified");
+ if (sr_xpath_node_name_eq(new_val->xpath, "enabled")) {
+ string n = sr_xpath_key_value(new_val->xpath, "interface",
+ "name", &xpath_ctx);
+ intf = interface::find(n);
+ if (!intf) {
+ rc = SR_ERR_OPERATION_FAILED;
+ goto nothing_todo;
+ }
+ intf->set(admin_state_t::from_int(new_val->data.bool_val));
+ modify = true;
+ }
+ break;
+ case SR_OP_CREATED:
+ if (sr_xpath_node_name_eq(new_val->xpath, "name")) {
+ builder.set_name(new_val->data.string_val);
+ create = true;
+ } else if (sr_xpath_node_name_eq(new_val->xpath, "type")) {
+ builder.set_type(new_val->data.string_val);
+ } else if (sr_xpath_node_name_eq(new_val->xpath, "enabled")) {
+ builder.set_state(new_val->data.bool_val);
+ }
+ break;
+ case SR_OP_DELETED:
+ if (sr_xpath_node_name_eq(old_val->xpath, "name")) {
+ builder.set_name(old_val->data.string_val);
+ remove = true;
+ }
+ break;
+ default:
+ rc = SR_ERR_UNSUPPORTED;
+ goto nothing_todo;
+ }
+
+ sr_free_val(old_val);
+ sr_free_val(new_val);
+ }
+
+ if (create) {
+ SRP_LOG_INF("creating interface '%s'", builder.name().c_str());
+ intf = builder.build();
+ if (nullptr == intf) {
+ SRP_LOG_ERR_MSG("Interface does not exist");
+ rc = SR_ERR_INVAL_ARG;
+ goto nothing_todo;
+ }
+ }
+
+ if (create || modify) {
+ /* Commit the changes to VOM DB and VPP with interface name as key.
+ * Work for modifications too, because OM::write() check for existing
+ * l3 bindings. */
+ if ( OM::write(intf->key(), *intf) != rc_t::OK ) {
+ SRP_LOG_ERR("Fail writing changes to VPP for: %s",
+ builder.to_string().c_str());
+ rc = SR_ERR_OPERATION_FAILED;
+ goto nothing_todo;
+ }
+
+ } else if (remove) {
+ SRP_LOG_INF("deleting interface '%s'", builder.name().c_str());
+ OM::remove(builder.name());
+ }
+
+ sr_free_change_iter(iter);
+
+ return SR_ERR_OK;
+
+nothing_todo:
+ sr_free_val(old_val);
+ sr_free_val(new_val);
+ sr_free_change_iter(iter);
+ return rc;
+}
+
+
+static int
+ipv46_config_add_remove(const string &if_name, const string &addr,
+ uint8_t prefix, bool add)
+{
+ shared_ptr<l3_binding> l3;
+ shared_ptr<interface> intf;
+ rc_t rc = rc_t::OK;
+
+ intf = interface::find(if_name);
+ if (nullptr == intf) {
+ SRP_LOG_ERR_MSG("Interface does not exist");
+ return SR_ERR_INVAL_ARG;
+ }
+
+ try {
+ VOM::route::prefix_t pfx(addr, prefix);
+ l3 = make_shared<l3_binding>(*intf, pfx);
+
+ #define KEY(l3) "l3_" + l3->itf().name() + "_" + l3->prefix().to_string()
+ if (add) {
+ /* Commit the changes to VOM DB and VPP */
+ if ( OM::write(KEY(l3), *l3) != rc_t::OK ) {
+ SRP_LOG_ERR_MSG("Fail writing changes to VPP");
+ return SR_ERR_OPERATION_FAILED;
+ }
+ } else {
+ /* Remove l3 thanks to its unique identifier */
+ OM::remove(KEY(l3));
+ }
+ #undef KEY
+
+ } catch (std::exception &exc) { //catch boost exception from prefix_t
+ SRP_LOG_ERR("Error: %s", exc.what());
+ return SR_ERR_OPERATION_FAILED;
+ }
+
+ return SR_ERR_OK;
+}
+
+static void
+parse_interface_ipv46_address(sr_val_t *val, string &addr,
+ uint8_t &prefix)
+{
+ if (val == nullptr)
+ throw std::runtime_error("Null pointer");
+
+ if (SR_LIST_T == val->type) {
+ /* create on list item - reset state vars */
+ addr.clear();
+ } else {
+ if (sr_xpath_node_name_eq(val->xpath, "ip")) {
+ addr = val->data.string_val;
+ } else if (sr_xpath_node_name_eq(val->xpath, "prefix-length")) {
+ prefix = val->data.uint8_val;
+ } else if (sr_xpath_node_name_eq(val->xpath, "netmask")) {
+ prefix = utils::netmask_to_plen(
+ boost::asio::ip::address::from_string(val->data.string_val));
+ }
+ }
+}
+
+/**
+ * @brief Callback to be called by any config change in subtrees
+ * "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/address"
+ * or "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6/address".
+ */
+static int
+ietf_interface_ipv46_address_change_cb(sr_session_ctx_t *session,
+ const char *xpath,
+ sr_notif_event_t event,
+ void *private_ctx)
+{
+ UNUSED(private_ctx);
+ sr_change_iter_t *iter = nullptr;
+ sr_change_oper_t op = SR_OP_CREATED;
+ sr_val_t *old_val = nullptr;
+ sr_val_t *new_val = nullptr;
+ sr_xpath_ctx_t xpath_ctx;
+ string new_addr, old_addr;
+ string if_name;
+ uint8_t new_prefix = 0;
+ uint8_t old_prefix = 0;
+ int rc = SR_ERR_OK, op_rc = SR_ERR_OK;
+ bool create = false;
+ bool del = false;
+
+ SRP_LOG_INF("In %s", __FUNCTION__);
+
+ /* no-op for apply, we only care about SR_EV_{ENABLED,VERIFY,ABORT} */
+ if (SR_EV_APPLY == event)
+ return SR_ERR_OK;
+
+ SRP_LOG_DBG("'%s' modified, event=%d", xpath, event);
+
+ /* get changes iterator */
+ rc = sr_get_changes_iter(session, xpath, &iter);
+ if (SR_ERR_OK != rc) {
+ sr_free_change_iter(iter);
+ SRP_LOG_ERR("Unable to retrieve change iterator: %s", sr_strerror(rc));
+ return rc;
+ }
+
+ foreach_change(session, iter, op, old_val, new_val) {
+
+ SRP_LOG_DBG("A change detected in '%s', op=%d",
+ new_val ? new_val->xpath : old_val->xpath, op);
+
+ if_name = sr_xpath_key_value(new_val ? new_val->xpath : old_val->xpath,
+ "interface", "name", &xpath_ctx);
+ if (if_name.empty()) {
+ rc = SR_ERR_OPERATION_FAILED;
+ goto nothing_todo;
+ }
+ sr_xpath_recover(&xpath_ctx);
+
+ try {
+ switch (op) {
+ case SR_OP_CREATED:
+ create = true;
+ parse_interface_ipv46_address(new_val, new_addr, new_prefix);
+ break;
+ case SR_OP_MODIFIED:
+ create = true;
+ del = true;
+ parse_interface_ipv46_address(old_val, old_addr, old_prefix);
+ parse_interface_ipv46_address(new_val, new_addr, new_prefix);
+ break;
+ case SR_OP_DELETED:
+ del = true;
+ parse_interface_ipv46_address(old_val, old_addr, old_prefix);
+ break;
+ default:
+ break;
+ }
+ } catch (std::exception &exc) {
+ SRP_LOG_ERR("Error: %s", exc.what());
+ }
+ sr_free_val(old_val);
+ sr_free_val(new_val);
+
+ if (del && !old_addr.empty()) {
+ op_rc = ipv46_config_add_remove(if_name, old_addr, old_prefix,
+ false /* del */);
+ }
+
+ if (create && !new_addr.empty()) {
+ op_rc = ipv46_config_add_remove(if_name, new_addr, new_prefix,
+ true /* add */);
+ }
+
+ }
+ sr_free_change_iter(iter);
+
+ return op_rc;
+
+nothing_todo:
+ sr_free_val(old_val);
+ sr_free_val(new_val);
+ sr_free_change_iter(iter);
+ return rc;
+}
+
+/**
+ * @brief Callback to be called by any request for state data under "/ietf-interfaces:interfaces-state/interface" path.
+ * Here we reply systematically with all interfaces, it the responsability of
+ * sysrepo apply a filter not to answer undesired interfaces.
+ */
+static int
+ietf_interface_state_cb(const char *xpath, sr_val_t **values,
+ size_t *values_cnt, uint64_t request_id,
+ const char *original_xpath, void *private_ctx)
+{
+ UNUSED(request_id); UNUSED(original_xpath); UNUSED(private_ctx);
+ vapi_payload_sw_interface_details interface;
+ shared_ptr<interface_dump> dump;
+ sr_val_t *val = nullptr;
+ int vc = 6; //number of answer per interfaces
+ int cnt = 0; //value counter
+ int interface_num = 0;
+ int rc = SR_ERR_OK;
+
+ SRP_LOG_INF("In %s", __FUNCTION__);
+
+ if (!sr_xpath_node_name_eq(xpath, "interface"))
+ goto nothing_todo; //no interface field specified
+
+ dump = make_shared<interface_dump>();
+ HW::enqueue(dump);
+ HW::write();
+
+ interface_num = std::distance((*dump).begin(), (*dump).end());
+
+ /* allocate array of values to be returned */
+ SRP_LOG_DBG("number of interfaces: %d", interface_num + 1);
+ rc = sr_new_values((interface_num + 1)* vc, &val);
+ if (0 != rc)
+ goto nothing_todo;
+
+ for (auto &it : *dump) {
+ interface = it.get_payload();
+
+ SRP_LOG_DBG("State of interface %s", interface.interface_name);
+
+ /* it needs if-mib YANG feature to work !
+ * admin-state: state as required by configuration */
+ sr_val_build_xpath(&val[cnt], "%s[name='%s']/admin-status", xpath,
+ interface.interface_name);
+ sr_val_set_str_data(&val[cnt], SR_ENUM_T,
+ interface.admin_up_down ? "up" : "down");
+ cnt++;
+
+ /* oper-state: effective state. can differ from admin-state */
+ sr_val_build_xpath(&val[cnt], "%s[name='%s']/oper-status", xpath,
+ interface.interface_name);
+ sr_val_set_str_data(&val[cnt], SR_ENUM_T,
+ interface.link_up_down ? "up" : "down");
+ cnt++;
+
+ sr_val_build_xpath(&val[cnt], "%s[name='%s']/phys-address", xpath,
+ interface.interface_name);
+ sr_val_build_str_data(&val[cnt], SR_STRING_T,
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ interface.l2_address[0], interface.l2_address[1],
+ interface.l2_address[2], interface.l2_address[3],
+ interface.l2_address[4], interface.l2_address[5]);
+ cnt++;
+
+ sr_val_build_xpath(&val[cnt], "%s[name='%s']/if-index", xpath,
+ interface.interface_name);
+ val[cnt].type = SR_INT32_T;
+ val[cnt].data.int32_val = interface.sw_if_index;
+ cnt++;
+
+ sr_val_build_xpath(&val[cnt], "%s[name='%s']/speed", xpath,
+ interface.interface_name);
+ val[cnt].type = SR_UINT64_T;
+ val[cnt].data.uint64_val = interface.link_speed;
+ cnt++;
+
+ }
+
+ *values = val;
+ *values_cnt = cnt;
+
+ return SR_ERR_OK;
+
+nothing_todo:
+ *values = nullptr;
+ *values_cnt = 0;
+ return rc;
+}
+
+/**
+ * @brief Callback to be called by any request for state data under
+ * "/ietf-interfaces:interfaces-state/interface/statistics" path.
+ */
+static int
+interface_statistics_cb(const char *xpath, sr_val_t **values,
+ size_t *values_cnt, uint64_t request_id,
+ const char *original_xpath, void *private_ctx)
+{
+ UNUSED(request_id); UNUSED(original_xpath); UNUSED(private_ctx);
+ shared_ptr<interface> interface;
+ interface::stats_t stats;
+ string intf_name;
+ sr_val_t *val = NULL;
+ int vc = 8;
+ int cnt = 0; //value counter
+ sr_xpath_ctx_t state;
+ int rc = SR_ERR_OK;
+
+ SRP_LOG_INF("In %s", __FUNCTION__);
+
+ /* Retrieve the interface asked */
+ intf_name = sr_xpath_key_value((char*) xpath, "interface", "name", &state);
+ if (intf_name.empty()) {
+ SRP_LOG_ERR_MSG("XPATH interface name not found");
+ return SR_ERR_INVAL_ARG;
+ }
+ sr_xpath_recover(&state);
+
+ interface = interface::find(intf_name);
+ if (interface == nullptr) {
+ SRP_LOG_WRN("interface %s not found in VOM", intf_name.c_str());
+ goto nothing_todo;
+ }
+
+ /* allocate array of values to be returned */
+ rc = sr_new_values(vc, &val);
+ if (0 != rc)
+ goto nothing_todo;
+
+ stats = interface->get_stats();
+
+ //if/rx
+ sr_val_build_xpath(&val[cnt], "%s/in-octets", xpath, 5);
+ val[cnt].type = SR_UINT64_T;
+ val[cnt].data.uint64_val = stats.m_rx.bytes;
+ cnt++;
+
+ //if/rx-unicast
+ sr_val_build_xpath(&val[cnt], "%s/in-unicast-pkts", xpath, 5);
+ val[cnt].type = SR_UINT64_T;
+ val[cnt].data.uint64_val = stats.m_rx_unicast.packets;
+ cnt++;
+
+ //if/rx-broadcast
+ sr_val_build_xpath(&val[cnt], "%s/in-broadcast-pkts", xpath, 5);
+ val[cnt].type = SR_UINT64_T;
+ val[cnt].data.uint64_val = stats.m_rx_broadcast.packets;
+ cnt++;
+
+ //if/rx-multicast
+ sr_val_build_xpath(&val[cnt], "%s/in-multicast-pkts", xpath, 5);
+ val[cnt].type = SR_UINT64_T;
+ val[cnt].data.uint64_val = stats.m_rx_multicast.packets;
+ cnt++;
+
+ //if/tx
+ sr_val_build_xpath(&val[cnt], "%s/out-octets", xpath, 5);
+ val[cnt].type = SR_UINT64_T;
+ val[cnt].data.uint64_val = stats.m_tx.bytes;
+ cnt++;
+
+ //if/tx-unicast
+ sr_val_build_xpath(&val[cnt], "%s/out-unicast-pkts", xpath, 5);
+ val[cnt].type = SR_UINT64_T;
+ val[cnt].data.uint64_val = stats.m_tx_unicast.packets;
+ cnt++;
+
+ //if/tx-broadcast
+ sr_val_build_xpath(&val[cnt], "%s/out-broadcast-pkts", xpath, 5);
+ val[cnt].type = SR_UINT64_T;
+ val[cnt].data.uint64_val = stats.m_tx_broadcast.packets;
+ cnt++;
+
+ //if/tx-multicast
+ sr_val_build_xpath(&val[cnt], "%s/out-multicast-pkts", xpath, 5);
+ val[cnt].type = SR_UINT64_T;
+ val[cnt].data.uint64_val = stats.m_tx_multicast.packets;
+ cnt++;
+
+ *values = val;
+ *values_cnt = cnt;
+
+ return SR_ERR_OK;
+
+nothing_todo:
+ *values = NULL;
+ *values_cnt = 0;
+ return rc;
+}
+
+
+int
+ietf_interface_init(sc_plugin_main_t *pm)
+{
+ int rc = SR_ERR_OK;
+ SRP_LOG_DBG_MSG("Initializing ietf-interface plugin.");
+
+ rc = sr_subtree_change_subscribe(pm->session, "/ietf-interfaces:interfaces/interface",
+ ietf_interface_create_cb, nullptr, 100, SR_SUBSCR_CTX_REUSE, &pm->subscription);
+ if (SR_ERR_OK != rc) {
+ goto error;
+ }
+
+ rc = sr_subtree_change_subscribe(pm->session, "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/address",
+ ietf_interface_ipv46_address_change_cb, nullptr, 99, SR_SUBSCR_CTX_REUSE, &pm->subscription);
+ if (SR_ERR_OK != rc) {
+ goto error;
+ }
+
+ rc = sr_subtree_change_subscribe(pm->session, "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6/address",
+ ietf_interface_ipv46_address_change_cb, nullptr, 98, SR_SUBSCR_CTX_REUSE, &pm->subscription);
+ if (SR_ERR_OK != rc) {
+ goto error;
+ }
+
+ rc = sr_dp_get_items_subscribe(pm->session, "/ietf-interfaces:interfaces-state",
+ ietf_interface_state_cb, nullptr, SR_SUBSCR_CTX_REUSE, &pm->subscription);
+ if (SR_ERR_OK != rc) {
+ goto error;
+ }
+
+ rc = sr_dp_get_items_subscribe(pm->session, "/ietf-interfaces:interfaces-state/interface/statistics",
+ interface_statistics_cb, NULL, SR_SUBSCR_CTX_REUSE, &pm->subscription);
+ if (SR_ERR_OK != rc) {
+ goto error;
+ }
+
+ SRP_LOG_DBG_MSG("ietf-interface plugin initialized successfully.");
+ return SR_ERR_OK;
+
+error:
+ SRP_LOG_ERR("Error by initialization of ietf-interface plugin. Error : %d", rc);
+ return rc;
+}
+
+void
+ietf_interface_exit(__attribute__((unused)) sc_plugin_main_t *pm)
+{
+}
+
+SC_INIT_FUNCTION(ietf_interface_init);
+SC_EXIT_FUNCTION(ietf_interface_exit);
diff --git a/src/plugins/ietf/ietf_nat.c b/src/plugins/ietf/ietf_nat.c
deleted file mode 100644
index 86b700f..0000000
--- a/src/plugins/ietf/ietf_nat.c
+++ /dev/null
@@ -1,525 +0,0 @@
-/*
- * Copyright (c) 2019 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 <scvpp/comm.h>
-#include <scvpp/interface.h>
-#include <scvpp/nat.h>
-
-#include <sc_plugins.h>
-
-#include <assert.h>
-#include <string.h>
-#include <sysrepo.h>
-#include <sysrepo/xpath.h>
-#include <sysrepo/values.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-/**
- * @brief Wrapper struct for VAPI address range payload.
- */
-struct address_range_t {
- nat44_add_del_address_range_t payload; //< VAPI payload for address range.
- bool last_ip_address_set; //< Variable last_ip_address in payload is set.
-};
-
-// Check input structure, set VAPI payload and call VAPI function to set
-// nat44 address range.
-static sr_error_t set_address_range(struct address_range_t *address_r)
-{
- sr_error_t rc = SR_ERR_OK;
-// char tmp_ip1[VPP_IP4_ADDRESS_STRING_LEN];
-// char tmp_ip2[VPP_IP4_ADDRESS_STRING_LEN];
-
- ARG_CHECK(SR_ERR_INVAL_ARG, address_r);
-
- //if end of IP range not provided, then range size = 1 with only first ip
- if (!address_r->last_ip_address_set) {
- memcpy(&address_r->payload.last_ip_address[0],
- &address_r->payload.first_ip_address[0],
- VPP_IP4_ADDRESS_LEN);
- }
-
- if (hardntohlu32(address_r->payload.last_ip_address) <
- hardntohlu32(address_r->payload.first_ip_address)) {
- SRP_LOG_ERR_MSG("End address less than start address");
- return SR_ERR_INVAL_ARG;
- }
-
-// strncpy(tmp_ip1, sc_ntoa(address_r->payload.first_ip_address),
-// VPP_IP4_ADDRESS_STRING_LEN);
-// strncpy(tmp_ip2, sc_ntoa(address_r->payload.last_ip_address),
-// VPP_IP4_ADDRESS_STRING_LEN);
-// SRP_LOG_DBG("Fist ip address: %s, last ip address: %s, twice_nat: %u,"
-// "is_add: %u", tmp_ip1, tmp_ip2, address_r->payload.twice_nat,
-// address_r->payload.is_add);
-
- int rv = nat44_add_del_addr_range(&address_r->payload);
- if (0 != rv) {
- SRP_LOG_ERR_MSG("Failed set address range.");
- rc = SR_ERR_OPERATION_FAILED;
- }
-
- return rc;
-}
-
-// parse leaf from this xpath:
-// /ietf-nat:nat/instances/instance[id='%s']/policy[id='%s']/external-ip-address-pool[pool-id='%s']/
-static int parse_instance_policy_external_ip_address_pool(
- const sr_val_t *val, struct address_range_t *address_range)
-{
- int rc;
- char tmp_str[VPP_IP4_PREFIX_STRING_LEN] = {0};
- uint8_t prefix = 0;
-
- ARG_CHECK2(SR_ERR_INVAL_ARG, val, address_range);
-
- if (sr_xpath_node_name_eq(val->xpath, "pool-id")) {
- SRP_LOG_WRN("%s not supported.", val->xpath);
- } else if(sr_xpath_node_name_eq(val->xpath, "external-ip-pool")) {
- rc = prefix2ip4(tmp_str, val->data.string_val, &prefix);
- if (0 != rc) {
- SRP_LOG_ERR_MSG("Error translate");
- return SR_ERR_INVAL_ARG;
- }
-
- rc = sc_aton(tmp_str, address_range->payload.first_ip_address,
- VPP_IP4_ADDRESS_LEN);
- if (0 != rc) {
- SRP_LOG_ERR_MSG("Failed convert string IP address to int.");
- return SR_ERR_INVAL_ARG;
- }
-
- if (prefix < VPP_IP4_HOST_PREFIX_LEN) {
- // External IP pool is represented as IPv4 prefix in YANG module.
- // VPP support range from first IPv4 address to last IPv4 address, not prefix.
- // In this concept the broadcast IPv4 address of this IPv4 prefix,
- // represent last IPv4 address for VPP
- get_last_ip_address(
- (sc_ipv4_addr*) &address_range->payload.last_ip_address[0],
- (sc_ipv4_addr*) &address_range->payload.first_ip_address[0],
- prefix);
- address_range->last_ip_address_set = true;
- }
- }
-
- return SR_ERR_OK;
-}
-
-// XPATH: /ietf-nat:nat/instances/instance[id='%s']/policy[id='%s']/external-ip-address-pool[pool-id='%s']/
-static int instances_instance_policy_external_ip_address_pool_cb(
- sr_session_ctx_t *ds, const char *xpath, sr_notif_event_t event,
- void *private_ctx)
-{
- UNUSED(private_ctx);
- sr_error_t rc = SR_ERR_OK;
- sr_change_iter_t *it = NULL;
- sr_change_oper_t oper;
- sr_val_t *old_val = NULL;
- sr_val_t *new_val = NULL;
- bool create = false;
- bool delete = false;
- struct address_range_t new_address_r = {0};
- struct address_range_t old_address_r = {0};
-
- ARG_CHECK2(SR_ERR_INVAL_ARG, ds, xpath);
-
- SRP_LOG_INF("In %s", __FUNCTION__);
-
- new_address_r.payload.vrf_id = ~0;
- old_address_r.payload.vrf_id = ~0;
-
- SRP_LOG_DBG("'%s' modified, event=%d", xpath, event);
-
- 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;
- }
-
- foreach_change (ds, it, oper, old_val, new_val) {
-
- SRP_LOG_DBG("A change detected in '%s', op=%d",
- new_val ? new_val->xpath : old_val->xpath, oper);
-
- switch (oper) {
- case SR_OP_CREATED:
- create = true;
- rc = parse_instance_policy_external_ip_address_pool(new_val,
- &new_address_r);
- break;
-
- case SR_OP_MODIFIED:
- delete = true;
- rc = parse_instance_policy_external_ip_address_pool(old_val,
- &old_address_r);
- if (SR_ERR_OK != rc) {
- break;
- }
-
- create = true;
- rc = parse_instance_policy_external_ip_address_pool(new_val,
- &new_address_r);
- break;
-
- case SR_OP_MOVED:
- break;
-
- case SR_OP_DELETED:
- delete = true;
- rc = parse_instance_policy_external_ip_address_pool(old_val,
- &old_address_r);
- break;
- }
-
- sr_free_val(old_val);
- sr_free_val(new_val);
-
- if (SR_ERR_OK != rc) {
- rc = SR_ERR_OPERATION_FAILED;
- goto error;
- }
- }
-
- if (delete) {
- old_address_r.payload.is_add = 0;
- rc = set_address_range(&old_address_r);
- if (SR_ERR_OK != rc) {
- goto error;
- }
- }
-
- if (create) {
- new_address_r.payload.is_add = 1;
- rc = set_address_range(&new_address_r);
- if (SR_ERR_OK != rc) {
- goto error;
- }
- }
-
-error:
- sr_free_change_iter(it);
- return rc;
-}
-
-enum mapping_type {
- STATIC,
- DYNAMIC_IMPLICIT,
- DYNAMIC_EXPLICIT,
- UNKNOWN,
-};
-
-/**
- * @brief Wrapper struct for VAPI static mapping payload.
- */
-struct static_mapping_t {
- enum mapping_type mtype; //< Mapping type
- bool local_ip_address_set; //< Variable are set in payload.
- bool external_ip_address_set; //< Variable are set in payload.
- bool protocol_set; //< Variable are set in payload.
- bool local_port_set; //< Variable are set in payload.
- bool external_port_set; //< Variable are set in payload.
- bool external_sw_if_index_set; //< Variable are set in payload.
- bool vrf_id_set; //< Variable are set in payload.
- bool twice_nat_set; //< Variable are set in payload.
- nat44_add_del_static_mapping_t payload; //< VAPI payload for static mapping
-};
-
-// Check input structure, set VAPI payload and call VAPI function to set
-// nat44 static mapping.
-static sr_error_t set_static_mapping(struct static_mapping_t *mapping)
-{
- int rc = 0;
-// char tmp_ip1[VPP_IP4_ADDRESS_STRING_LEN];
-// char tmp_ip2[VPP_IP4_ADDRESS_STRING_LEN];
-
- ARG_CHECK(SR_ERR_INVAL_ARG, mapping);
-
- if ((mapping->local_port_set != mapping->external_port_set) ||
- !mapping->local_ip_address_set || !mapping->external_ip_address_set) {
- SRP_LOG_ERR_MSG("NAT44 parameter missing.");
-
- return SR_ERR_VALIDATION_FAILED;
- }
-
- if (!mapping->local_port_set && !mapping->external_port_set) {
- //FIXME!!!!! Compile error
-// mapping->payload.flags = NAT_IS_ADDR_ONLY;
- } else {
- //FIXME!!!!! Compile error
-// mapping->payload.flags = NAT_IS_TWICE_NAT;
- if (!mapping->protocol_set) {
-
- SRP_LOG_ERR_MSG("NAT44 protocol missing.");
- return SR_ERR_VALIDATION_FAILED;
- }
- }
-
- mapping->payload.external_sw_if_index = ~0;
-
-// strncpy(tmp_ip1, sc_ntoa(mapping->payload.local_ip_address),
-// VPP_IP4_ADDRESS_STRING_LEN);
-// strncpy(tmp_ip2, sc_ntoa(mapping->payload.external_ip_address),
-// VPP_IP4_ADDRESS_STRING_LEN);
-// SRP_LOG_DBG("Local ip address: %s, external ip address: %s, addr_only: %u,"
-// " protocol: %u, local port: %u, external port: %u, twice_nat: %u,"
-// "is_add: %u", tmp_ip1, tmp_ip2, mapping->payload.addr_only,
-// mapping->payload.protocol, mapping->payload.local_port,
-// mapping->payload.external_port, mapping->payload.twice_nat,
-// mapping->payload.is_add);
-
- rc = nat44_add_del_static_mapping(&mapping->payload);
- if (0 != rc) {
- SRP_LOG_ERR_MSG("Failed set static mapping");
- return SR_ERR_OPERATION_FAILED;
- }
-
- return SR_ERR_OK;
-}
-
-// parse leaf from this xpath:
-// /ietf-nat:nat/instances/instance[id='%s']/mapping-table/mapping-entry[index='%s']/
-static int parse_instance_mapping_table_mapping_entry(
- const sr_val_t *val,
- struct static_mapping_t *mapping)
-{
- int rc;
- char tmp_str[VPP_IP4_PREFIX_STRING_LEN] = {0};
-
- ARG_CHECK2(SR_ERR_INVAL_ARG, val, mapping);
-
- sr_xpath_ctx_t state = {0};
-
- if(sr_xpath_node_name_eq(val->xpath, "type")) {
- if (!strncmp("static", val->data.string_val, strlen("static"))) {
- mapping->mtype = STATIC;
- } else if (!strncmp("dynamic-implicit", val->data.string_val,
- strlen("dynamic-implicit"))) {
- mapping->mtype = DYNAMIC_IMPLICIT;
- } else if (!strncmp("dynamic-explicit", val->data.string_val,
- strlen("dynamic-explicit"))) {
- mapping->mtype = DYNAMIC_EXPLICIT;
- }
- } else if(sr_xpath_node_name_eq(val->xpath, "transport-protocol")) {
- if (SR_UINT8_T != val->type) {
- SRP_LOG_ERR("Wrong transport-protocol, type, current type: %d.",
- val->type);
- return SR_ERR_INVAL_ARG;
- }
-
- mapping->payload.protocol = val->data.uint8_val;
- mapping->protocol_set = true;
- } else if(sr_xpath_node_name_eq(val->xpath, "internal-src-address")) {
- if (SR_STRING_T != val->type) {
- SRP_LOG_ERR("Wrong internal-src-address, type, current type: %d.",
- val->type);
- return SR_ERR_INVAL_ARG;
- }
-
- rc = prefix2ip4(tmp_str, val->data.string_val, NULL);
- if (0 != rc) {
- SRP_LOG_ERR_MSG("Error translate");
- return SR_ERR_INVAL_ARG;
- }
-
- rc = sc_aton(tmp_str, mapping->payload.local_ip_address,
- VPP_IP4_ADDRESS_LEN);
- if (0 != rc) {
- SRP_LOG_ERR_MSG("Failed convert string IP address to int.");
- return SR_ERR_INVAL_ARG;
- }
-
- mapping->local_ip_address_set = true;
- } else if(sr_xpath_node_name_eq(val->xpath, "external-src-address")) {
- if (SR_STRING_T != val->type) {
- SRP_LOG_ERR("Wrong external-src-address, type, current type: %d.",
- val->type);
- return SR_ERR_INVAL_ARG;
- }
-
- rc = prefix2ip4(tmp_str, val->data.string_val, NULL);
- if (0 != rc) {
- SRP_LOG_ERR_MSG("Error translate");
- return SR_ERR_INVAL_ARG;
- }
-
- rc = sc_aton(tmp_str, mapping->payload.external_ip_address,
- VPP_IP4_ADDRESS_LEN);
- if (0 != rc) {
- SRP_LOG_ERR_MSG("Failed convert string IP address to int.");
- return SR_ERR_INVAL_ARG;
- }
-
- mapping->external_ip_address_set = true;
- } else if (sr_xpath_node(val->xpath, "internal-src-port", &state)) {
- sr_xpath_recover(&state);
- if(sr_xpath_node_name_eq(val->xpath, "start-port-number")) {
- mapping->local_port_set = true;
- mapping->payload.local_port = val->data.uint16_val;
- }
- } else if (sr_xpath_node(val->xpath, "external-src-port", &state)) {
- sr_xpath_recover(&state);
- if(sr_xpath_node_name_eq(val->xpath, "start-port-number")) {
- mapping->external_port_set = true;
- mapping->payload.external_port = val->data.uint16_val;
- }
- }
-
- return SR_ERR_OK;
-}
-
-// XPATH: /ietf-nat:nat/instances/instance[id='%s']/mapping-table/mapping-entry[index='%s']/
-static int instances_instance_mapping_table_mapping_entry_cb(
- sr_session_ctx_t *ds, const char *xpath, sr_notif_event_t event,
- void *private_ctx)
-{
- UNUSED(private_ctx);
- sr_error_t rc = SR_ERR_OK;
- sr_change_iter_t *it = NULL;
- sr_change_oper_t oper;
- sr_val_t *old_val = NULL;
- sr_val_t *new_val = NULL;
- bool create = false;
- bool delete = false;
- struct static_mapping_t new_mapping = {0};
- struct static_mapping_t old_mapping = {0};
-
- ARG_CHECK2(SR_ERR_INVAL_ARG, ds, xpath);
-
- SRP_LOG_INF("In %s", __FUNCTION__);
-
- new_mapping.mtype = UNKNOWN;
- old_mapping.mtype = UNKNOWN;
-
- SRP_LOG_DBG("'%s' modified, event=%d", xpath, event);
-
- 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;
- }
-
- foreach_change (ds, it, oper, old_val, new_val) {
-
- SRP_LOG_DBG("A change detected in '%s', op=%d",
- new_val ? new_val->xpath : old_val->xpath, oper);
-
- switch (oper) {
- case SR_OP_CREATED:
- create = true;
- rc = parse_instance_mapping_table_mapping_entry(new_val,
- &new_mapping);
- break;
-
- case SR_OP_MODIFIED:
- delete = true;
- rc = parse_instance_mapping_table_mapping_entry(old_val,
- &old_mapping);
- if (SR_ERR_OK != rc) {
- break;
- }
-
- create = true;
- rc = parse_instance_mapping_table_mapping_entry(new_val,
- &new_mapping);
- break;
-
- case SR_OP_MOVED:
- break;
-
- case SR_OP_DELETED:
- delete = true;
- rc = parse_instance_mapping_table_mapping_entry(old_val,
- &old_mapping);
- break;
- }
-
- sr_free_val(old_val);
- sr_free_val(new_val);
-
- if (SR_ERR_OK != rc) {
- rc = SR_ERR_OPERATION_FAILED;
- goto error;
- }
- }
-
- if (delete) {
- old_mapping.payload.is_add = 0;
- if (STATIC == old_mapping.mtype) {
- rc = set_static_mapping(&old_mapping);
- if (SR_ERR_OK != rc) {
- goto error;
- }
- }
- }
-
- if (create) {
- new_mapping.payload.is_add = 1;
- if (STATIC == new_mapping.mtype) {
- rc = set_static_mapping(&new_mapping);
- if (SR_ERR_OK != rc) {
- goto error;
- }
- }
- }
-
-error:
- sr_free_change_iter(it);
- return rc;
-}
-
-int
-ietf_nat_init(sc_plugin_main_t *pm)
-{
- int rc = SR_ERR_OK;
- SRP_LOG_DBG_MSG("Initializing ietf-nat plugin.");
-
- rc = sr_subtree_change_subscribe(pm->session, "/ietf-nat:nat/instances/instance/policy/external-ip-address-pool",
- instances_instance_policy_external_ip_address_pool_cb, NULL, 0, SR_SUBSCR_CTX_REUSE, &pm->subscription);
- if (rc != SR_ERR_OK) {
- goto error;
- }
-
- rc = sr_subtree_change_subscribe(pm->session, "/ietf-nat:nat/instances/instance/mapping-table/mapping-entry",
- instances_instance_mapping_table_mapping_entry_cb, NULL, 0, SR_SUBSCR_CTX_REUSE, &pm->subscription);
- if (rc != SR_ERR_OK) {
- goto error;
- }
-
- SRP_LOG_DBG_MSG("ietf-nat plugin initialized successfully.");
- return SR_ERR_OK;
-
-error:
- SRP_LOG_ERR("Error by initialization of ietf-nat plugin. Error : %d", rc);
- return rc;
-}
-
-void
-ietf_nat_exit(__attribute__((unused)) sc_plugin_main_t *pm)
-{
-}
-
-SC_INIT_FUNCTION(ietf_nat_init);
-SC_EXIT_FUNCTION(ietf_nat_exit);
diff --git a/src/plugins/ietf/ietf_nat.cpp b/src/plugins/ietf/ietf_nat.cpp
new file mode 100644
index 0000000..4242df6
--- /dev/null
+++ b/src/plugins/ietf/ietf_nat.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ * Copyright (c) 2019 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+/*
+ * Instances:
+ * ==========
+ * IETF NAT defines NAT instances, it is like databases which carry NAT
+ * configurations.
+ * VPP offers a single NAT database, so users should use instance with id=0.
+ * Thus, enabling/disabling a NAT instance is not supported.
+ *
+ * NAT instance capabilities:
+ * ==========================
+ * We currently support:
+ * -"static-mapping"
+ *
+ * NAT instance policies:
+ * ======================
+ *
+ * VOM NAT
+ * ========
+ * -Support NAT44/NAT66 static mapping (IP by IP) without transport protocol (NAT_IS_ADDR_ONLY)
+ * -Support declaration of an interface as an INSIDE/OUTSIDE interface.
+ *
+ * TODO ideas of new features which can be supported
+ * -Support for internal/external port in VOM and sweetcomb
+ * -Support for dynamic NAT in VOM and sweetcomb
+ */
+
+#include <string>
+#include <exception>
+#include <memory>
+
+#include <vom/om.hpp>
+#include <vom/nat_static.hpp>
+
+#include "sc_plugins.h"
+#include "sys_util.h"
+
+using VOM::OM;
+using VOM::rc_t;
+
+/* Just to intercept request trying to create nat instances */
+static int
+nat_instance_config_cb(sr_session_ctx_t *ds, const char *xpath,
+ sr_notif_event_t event, void *private_ctx)
+{
+ UNUSED(ds); UNUSED(xpath); UNUSED(event); UNUSED(private_ctx);
+
+ SRP_LOG_INF("In %s", __FUNCTION__);
+
+ return SR_ERR_OK;
+}
+
+typedef std::pair<boost::asio::ip::address, boost::asio::ip::address> nat_pair_t;
+typedef std::map<uint32_t, nat_pair_t> nat_table_t;
+
+static nat_table_t static_mapping_table;
+
+/*
+ * Valid pairs for static NAT are:
+ * (internal_src address, external_src address)
+ * (inernal_dest address, external_dest address)
+ * Addresses must be of the same family i.e. no IP4 with IP6.
+ *
+ * Invalid pairs are:
+ * (internal_*, internal_*) , (external_*, external_*)
+ * (*_src, *_dest), (*_dest, *_src);
+ */
+class nat_static_builder {
+public:
+ /* Default constructor */
+ nat_static_builder() {}
+
+ shared_ptr<VOM::nat_static> build() {
+
+ if (m_type != "static") //only static is supported for now
+ return nullptr;
+
+ if (! m_internal_src.empty() && ! m_external_src.empty() ) {
+ m_inside = m_internal_src.address();
+ m_outside = m_external_src.address();
+ } else if (! m_internal_dest.empty() && ! m_external_dest.empty() ) {
+ m_inside = m_internal_dest.address();
+ m_outside = m_external_dest.address();
+ } else
+ return nullptr;
+
+ return make_shared<VOM::nat_static>(m_inside, m_outside);
+ }
+
+ /* Getters */
+ boost::asio::ip::address inside ()
+ { return m_inside; }
+ boost::asio::ip::address outside()
+ { return m_outside; }
+
+ /* Setters */
+ nat_static_builder& set_type(std::string t)
+ {
+ m_type = t;
+ return *this;
+ }
+ nat_static_builder& set_internal_src(std::string p)
+ {
+ m_internal_src = utils::prefix::make_prefix(p);
+ return *this;
+ }
+ nat_static_builder& set_external_src(std::string p)
+ {
+ m_external_src = utils::prefix::make_prefix(p);
+ return *this;
+ }
+ nat_static_builder& set_internal_dest(std::string p)
+ {
+ m_internal_dest = utils::prefix::make_prefix(p);
+ return *this;
+ }
+ nat_static_builder& set_external_dest(std::string p)
+ {
+ m_external_dest = utils::prefix::make_prefix(p);
+ return *this;
+ }
+
+private:
+ std::string m_type;
+ utils::prefix m_internal_src;
+ utils::prefix m_external_src;
+ utils::prefix m_internal_dest;
+ utils::prefix m_external_dest;
+ boost::asio::ip::address m_inside;
+ boost::asio::ip::address m_outside;
+};
+
+/*
+ * /ietf-nat:nat/instances/instance[id='%s']/mapping-table/mapping-entry[index='%s']/
+ */
+static int
+nat_mapping_table_config_cb(sr_session_ctx_t *ds, const char *xpath,
+ sr_notif_event_t event, void *private_ctx)
+{
+ UNUSED(private_ctx);
+ nat_static_builder builder;
+ std::shared_ptr<VOM::nat_static> ns = nullptr;
+ sr_val_t *ol = nullptr;
+ sr_val_t *ne = nullptr;
+ sr_change_iter_t *it;
+ sr_change_oper_t oper;
+ uint32_t xindex; // mapping entry index from xpath
+ bool create, remove;
+ int rc;
+
+ ARG_CHECK2(SR_ERR_INVAL_ARG, ds, xpath);
+
+ if (event != SR_EV_VERIFY)
+ return SR_ERR_OK;
+
+ SRP_LOG_INF("In %s", __FUNCTION__);
+
+ rc = sr_get_changes_iter(ds, (char *)xpath, &it);
+ if (rc != SR_ERR_OK)
+ goto error;
+
+ foreach_change(ds, it, oper, ol, ne) {
+
+ switch (oper) {
+ case SR_OP_CREATED:
+ if (sr_xpath_node_name_eq(ne->xpath, "index")) {
+ xindex = ne->data.uint32_val;
+ create = true;
+ } else if (sr_xpath_node_name_eq(ne->xpath, "type")) {
+ /* For configuration only "static" can be supported */
+ builder.set_type(string(ne->data.string_val));
+ } else if (sr_xpath_node_name_eq(ne->xpath, "internal-src-address")) {
+ /* source IP on NAT internal network src address */
+ builder.set_internal_src(string(ne->data.string_val));
+ } else if (sr_xpath_node_name_eq(ne->xpath, "external-src-address")) {
+ /* source IP on NAT external network src address */
+ builder.set_external_src(string(ne->data.string_val));
+ } else if (sr_xpath_node_name_eq(ne->xpath, "internal-dst-address")) {
+ /* destination IP on NAT internal network src address */
+ builder.set_internal_dest(string(ne->data.string_val));
+ } else if (sr_xpath_node_name_eq(ne->xpath, "external-dst-address")) {
+ /* destination IP on NAT internal network src address */
+ builder.set_external_dest(string(ne->data.string_val));
+ }
+ break;
+
+ case SR_OP_DELETED:
+ if (sr_xpath_node_name_eq(ol->xpath, "index")) {
+ xindex = ol->data.uint32_val;
+ remove = true;
+ }
+ break;
+
+ default:
+ SRP_LOG_WRN_MSG("Operation not supported");
+ rc = SR_ERR_UNSUPPORTED;
+ goto error;
+ }
+
+ sr_free_val(ne);
+ sr_free_val(ol);
+ }
+
+ sr_free_change_iter(it);
+
+ if (create) {
+ ns = builder.build();
+ if (ns == nullptr) {
+ SRP_LOG_ERR("Fail building nat %s %s",
+ builder.inside().to_string().c_str(),
+ builder.outside().to_string().c_str());
+ rc = SR_ERR_INVAL_ARG;
+ goto error;
+ }
+ }
+
+ if (create) {
+ #define KEY(xindex) to_string(xindex)
+ if (OM::write(KEY(xindex), *ns) != rc_t::OK) {
+ SRP_LOG_ERR("Fail writing changes for nat: %s",
+ ns->to_string().c_str());
+ rc = SR_ERR_OPERATION_FAILED;
+ goto error;
+ }
+ static_mapping_table[xindex] = nat_pair_t(builder.inside(),
+ builder.outside());
+ SRP_LOG_INF_MSG("Sucess creating nat static entry");
+ } else if (remove) {
+ OM::remove(KEY(xindex));
+ static_mapping_table.erase(xindex);
+ #undef KEY
+ }
+
+ return SR_ERR_OK;
+
+error:
+ sr_free_val(ol);
+ sr_free_val(ne);
+ sr_free_change_iter(it);
+ return rc;
+}
+
+/*
+ * /ietf-nat:nat/instances/instance/capabilities
+ */
+static int
+nat_cap_state_cb(const char *xpath, sr_val_t **values, size_t *values_cnt,
+ uint64_t request_id, const char *original_xpath,
+ void *private_ctx)
+{
+ UNUSED(request_id); UNUSED(original_xpath); UNUSED(private_ctx);
+ sr_val_t *val = nullptr;
+ int vc = 1; //expected number of answer
+ int cnt = 0; //value counter
+ int rc;
+
+ SRP_LOG_INF("In %s", __FUNCTION__);
+
+ rc = sr_new_values(vc, &val);
+ if (0 != rc) {
+ rc = SR_ERR_NOMEM;
+ goto nothing_todo;
+ }
+
+ sr_val_build_xpath(&val[cnt], "%s/static-mapping", xpath);
+ val[cnt].type = SR_BOOL_T;
+ val[cnt].data.bool_val = true;
+ cnt++;
+
+ *values = val;
+ *values_cnt = cnt;
+
+ return SR_ERR_OK;
+
+nothing_todo:
+ *values = NULL;
+ *values_cnt = 0;
+ return rc;
+}
+
+
+int
+ietf_nat_init(sc_plugin_main_t *pm)
+{
+ int rc = SR_ERR_OK;
+ SRP_LOG_DBG_MSG("Initializing ietf-nat plugin.");
+
+ rc = sr_subtree_change_subscribe(pm->session, "/ietf-nat:nat/instances/instance",
+ nat_instance_config_cb, NULL, 1, SR_SUBSCR_CTX_REUSE, &pm->subscription);
+ if (rc != SR_ERR_OK) {
+ goto error;
+ }
+
+ rc = sr_subtree_change_subscribe(pm->session, "/ietf-nat:nat/instances/instance/mapping-table/mapping-entry",
+ nat_mapping_table_config_cb, NULL, 10, SR_SUBSCR_CTX_REUSE, &pm->subscription);
+ if (rc != SR_ERR_OK) {
+ goto error;
+ }
+
+ rc = sr_dp_get_items_subscribe(pm->session, "/ietf-nat:nat/instances/instance/capabilities",
+ nat_cap_state_cb, NULL, SR_SUBSCR_CTX_REUSE, &pm->subscription);
+ if (SR_ERR_OK != rc) {
+ goto error;
+ }
+
+ SRP_LOG_DBG_MSG("ietf-nat plugin initialized successfully.");
+ return SR_ERR_OK;
+
+error:
+ SRP_LOG_ERR("Error by initialization of ietf-nat plugin. Error : %d", rc);
+ return rc;
+}
+
+void
+ietf_nat_exit(__attribute__((unused)) sc_plugin_main_t *pm)
+{
+}
+
+SC_INIT_FUNCTION(ietf_nat_init);
+SC_EXIT_FUNCTION(ietf_nat_exit);