From 18561adfde80d6665e24262d70d18f916e2662e5 Mon Sep 17 00:00:00 2001 From: YohanPipereau Date: Mon, 15 Jul 2019 15:37:46 +0200 Subject: vom: migration from scvpp to vom Change-Id: I79609f0bee9b8307da0d9bf704babe8ba06dba4d Signed-off-by: YohanPipereau Co-authored-by: Pavel Kotucek Co-authored-by: Andrej Kozemcak --- src/plugins/ietf/ietf_interface.c | 570 --------------------------------- src/plugins/ietf/ietf_interface.cpp | 613 ++++++++++++++++++++++++++++++++++++ src/plugins/ietf/ietf_nat.c | 525 ------------------------------ src/plugins/ietf/ietf_nat.cpp | 336 ++++++++++++++++++++ 4 files changed, 949 insertions(+), 1095 deletions(-) delete mode 100644 src/plugins/ietf/ietf_interface.c create mode 100644 src/plugins/ietf/ietf_interface.cpp delete mode 100644 src/plugins/ietf/ietf_nat.c create mode 100644 src/plugins/ietf/ietf_nat.cpp (limited to 'src/plugins/ietf') 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 -#include - -#include - -#include -#include - -#include - -/** - * @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 +#include +#include + +#include +#include +#include +#include + +#include + +#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 build() { + if (m_name.empty() || m_type.empty()) + return nullptr; + return make_shared(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 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; + shared_ptr 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(*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 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(); + 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::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 -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * @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 +#include +#include + +#include +#include + +#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 nat_pair_t; +typedef std::map 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 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(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 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); -- cgit 1.2.3-korg