aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/ietf/ietf_nat.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/ietf/ietf_nat.cpp')
-rw-r--r--src/plugins/ietf/ietf_nat.cpp336
1 files changed, 336 insertions, 0 deletions
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);