aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHongjun Ni <hongjun.ni@intel.com>2018-11-20 03:35:38 +0800
committerHongjun Ni <hongjun.ni@intel.com>2018-11-20 03:41:08 +0800
commitfd6c6f7c42d148a980030c7535846a4643d8cc9b (patch)
tree42df5fa7198777b4f3f711c0142f6cb0a7e7fea5
parenta45498c3a487d9fb561c24b2f3df7910fa54c0f8 (diff)
Initial code commit for Sweetcomb project
The initial code is contruted by Cisco and HuachenTel. It supports three key features: 1). Keeps compatible with IETF Yang Model and Honeycomb Yang Model. 2). Supports mulitiple clients to configure VPP simultaneously. 3). Automatically connect to VPP. Change-Id: I3dda4fddb14062e5f113d505c6a7ee64d60c44c0 Signed-off-by: Rastislav Szabo <raszabo@cisco.com> Signed-off-by: Chuanguo Wang <wangchuanguo@huachentel.com> Signed-off-by: Hongjun Ni <hongjun.ni@intel.com>
-rw-r--r--Makefile112
-rwxr-xr-xbuild-root/scripts/checkstyle.sh150
-rw-r--r--src/CMakeLists.txt35
-rw-r--r--src/README.md9
-rw-r--r--src/plugins/CMakeLists.txt39
-rw-r--r--src/plugins/vpp-interfaces.c593
-rw-r--r--src/plugins/yang/ietf-interfaces.data.xml21
-rw-r--r--src/plugins/yang/ietf-interfaces@2014-05-08.yang707
-rw-r--r--src/plugins/yang/ietf-ip@2014-06-16.yang742
-rw-r--r--src/plugins/yang/ietf-yang-types@2013-07-15.yang474
-rw-r--r--src/srvpp/CMakeLists.txt40
-rw-r--r--src/srvpp/libsrvpp.pc.in10
-rw-r--r--src/srvpp/src/CMakeLists.txt44
-rw-r--r--src/srvpp/src/srvpp.c823
-rw-r--r--src/srvpp/src/srvpp.h185
-rw-r--r--src/srvpp/src/srvpp_logger.c83
-rw-r--r--src/srvpp/src/srvpp_logger.h89
-rw-r--r--src/srvpp/tests/CMakeLists.txt40
-rw-r--r--src/srvpp/tests/srvpp_test.c160
19 files changed, 4356 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..4383135
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,112 @@
+# Copyright (c) 2018 Intel 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.
+
+export WS_ROOT=$(CURDIR)
+export BR=$(WS_ROOT)/build-root
+CCACHE_DIR?=$(BR)/.ccache
+GDB?=gdb
+PLATFORM?=sweetcomb
+MACHINE=$(shell uname -m)
+SUDO?=sudo
+
+#
+# OS Detection
+#
+ifneq ($(shell uname),Darwin)
+OS_ID = $(shell grep '^ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g')
+OS_VERSION_ID= $(shell grep '^VERSION_ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g')
+endif
+
+ifeq ($(filter ubuntu debian,$(OS_ID)),$(OS_ID))
+PKG=deb
+else ifeq ($(filter rhel centos fedora opensuse opensuse-leap opensuse-tumbleweed,$(OS_ID)),$(OS_ID))
+PKG=rpm
+endif
+
+# +libganglia1-dev if building the gmond plugin
+
+DEB_DEPENDS = curl build-essential autoconf automake ccache
+DEB_DEPENDS += bison flex libpcre3-dev libev-dev libavl-dev libprotobuf-c-dev protobuf-c-compiler
+DEB_DEPENDS += cmake ninja-build
+ifeq ($(OS_VERSION_ID),14.04)
+ DEB_DEPENDS += libssl-dev
+else
+ DEB_DEPENDS += libssl-dev
+endif
+
+ifeq ($(findstring y,$(UNATTENDED)),y)
+CONFIRM=-y
+FORCE=--force-yes
+endif
+
+TARGETS = sweetcomb
+
+.PHONY: help wipe wipe-release build build-release rebuild rebuild-release
+
+define banner
+ @echo "========================================================================"
+ @echo " $(1)"
+ @echo "========================================================================"
+ @echo " "
+endef
+
+help:
+ @echo "Make Targets:"
+ @echo " install-dep - install software dependencies"
+ @echo " checkstyle - check coding style"
+ @echo " fixstyle - fix coding style"
+
+$(BR)/.deps.ok:
+ifeq ($(findstring y,$(UNATTENDED)),y)
+ make install-dep
+endif
+ifeq ($(filter ubuntu debian,$(OS_ID)),$(OS_ID))
+ @MISSING=$$(apt-get install -y -qq -s $(DEB_DEPENDS) | grep "^Inst ") ; \
+ if [ -n "$$MISSING" ] ; then \
+ echo "\nPlease install missing packages: \n$$MISSING\n" ; \
+ echo "by executing \"make install-dep\"\n" ; \
+ exit 1 ; \
+ fi ; \
+ exit 0
+endif
+ @touch $@
+
+install-dep:
+ifeq ($(filter ubuntu debian,$(OS_ID)),$(OS_ID))
+ifeq ($(OS_VERSION_ID),14.04)
+ @sudo -E apt-get $(CONFIRM) $(FORCE) install software-properties-common
+ @sudo -E add-apt-repository ppa:openjdk-r/ppa $(CONFIRM)
+endif
+ @sudo -E apt-get update
+ @sudo -E apt-get $(APT_ARGS) $(CONFIRM) $(FORCE) install $(DEB_DEPENDS)
+else
+ $(error "This option currently works only on Ubuntu, Debian, Centos or openSUSE systems")
+endif
+
+define make
+ @make -C $(BR) PLATFORM=$(PLATFORM) TAG=$(1) $(2)
+endef
+
+$(BR)/scripts/.version:
+ifneq ("$(wildcard /etc/redhat-release)","")
+ $(shell $(BR)/scripts/version rpm-string > $(BR)/scripts/.version)
+else
+ $(shell $(BR)/scripts/version > $(BR)/scripts/.version)
+endif
+
+checkstyle:
+ @build-root/scripts/checkstyle.sh
+
+fixstyle:
+ @build-root/scripts/checkstyle.sh --fix
+
diff --git a/build-root/scripts/checkstyle.sh b/build-root/scripts/checkstyle.sh
new file mode 100755
index 0000000..3a898cb
--- /dev/null
+++ b/build-root/scripts/checkstyle.sh
@@ -0,0 +1,150 @@
+#!/bin/bash
+
+# Copyright (c) 2015 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.
+
+SWEETCOMB_DIR=`dirname $0`/../../
+EXIT_CODE=0
+FIX="0"
+FULL="0"
+CHECKSTYLED_FILES=""
+UNCHECKSTYLED_FILES=""
+
+# If the user provides --fix, then actually fix things
+# Note: this is meant for use outside of the CI Jobs, by users cleaning things up
+
+while true; do
+ case ${1} in
+ --fix)
+ FIX="1"
+ ;;
+ --full)
+ FULL="1"
+ ;;
+ esac
+ shift || break
+done
+
+if [ "${FULL}" == "1" ]; then
+ FILELIST=$(git ls-tree -r HEAD --name-only)
+else
+ FILELIST=$((git diff HEAD~1.. --name-only; git ls-files -m ) | sort -u)
+fi
+
+# Check to make sure we have indent. Exit if we don't with an error message, but
+# don't *fail*.
+command -v indent > /dev/null
+if [ $? != 0 ]; then
+ echo "Cound not find required command \"indent\". Checkstyle aborted"
+ exit ${EXIT_CODE}
+fi
+indent --version
+
+# Check to make sure we have clang-format. Exit if we don't with an error message, but
+# don't *fail*.
+HAVE_CLANG_FORMAT=0
+command -v clang-format > /dev/null
+if [ $? != 0 ]; then
+ echo "Could not find command \"clang-format\". Checking C++ files will cause abort"
+else
+ clang-format --version
+ x=$(echo "" | clang-format 2>&1)
+ if [[ "$x" == "" ]]; then
+ HAVE_CLANG_FORMAT=1
+ else
+ echo "Output produced while formatting empty file (expected empty string):"
+ echo "$x"
+ echo "Could not find working \"clang-format\". Checking C++ files will cause abort"
+ fi
+fi
+
+cd ${SWEETCOMB_DIR}
+git status
+for i in ${FILELIST}; do
+ if [ -f ${i} ] && [ ${i} != "build-root/scripts/checkstyle.sh" ] && [ ${i} != "extras/emacs/fix-coding-style.el" ]; then
+ grep -q "fd.io coding-style-patch-verification: ON" ${i}
+ if [ $? == 0 ]; then
+ EXTENSION=`basename ${i} | sed 's/^\w\+.//'`
+ case ${EXTENSION} in
+ hpp|cpp|cc|hh)
+ CMD="clang-format"
+ if [ ${HAVE_CLANG_FORMAT} == 0 ]; then
+ echo "C++ file detected. Abort. (missing clang-format)"
+ exit ${EXIT_CODE}
+ fi
+ ;;
+ *)
+ CMD="indent"
+ ;;
+ esac
+ CHECKSTYLED_FILES="${CHECKSTYLED_FILES} ${i}"
+ if [ ${FIX} == 0 ]; then
+ if [ "${CMD}" == "clang-format" ]
+ then
+ clang-format ${i} > ${i}.out2
+ else
+ indent ${i} -o ${i}.out1 > /dev/null 2>&1
+ indent ${i}.out1 -o ${i}.out2 > /dev/null 2>&1
+ fi
+ # Remove trailing whitespace
+ sed -i -e 's/[[:space:]]*$//' ${i}.out2
+ diff -q ${i} ${i}.out2
+ else
+ if [ "${CMD}" == "clang-format" ]; then
+ clang-format -i ${i} > /dev/null 2>&1
+ else
+ indent ${i}
+ indent ${i}
+ fi
+ # Remove trailing whitespace
+ sed -i -e 's/[[:space:]]*$//' ${i}
+ fi
+ if [ $? != 0 ]; then
+ EXIT_CODE=1
+ echo
+ echo "Checkstyle failed for ${i}."
+ if [ "${CMD}" == "clang-format" ]; then
+ echo "Run clang-format as shown to fix the problem:"
+ echo "clang-format -i ${SWEETCOMB_DIR}${i}"
+ else
+ echo "Run indent (twice!) as shown to fix the problem:"
+ echo "indent ${SWEETCOMB_DIR}${i}"
+ echo "indent ${SWEETCOMB_DIR}${i}"
+ fi
+ fi
+ if [ -f ${i}.out1 ]; then
+ rm ${i}.out1
+ fi
+ if [ -f ${i}.out2 ]; then
+ rm ${i}.out2
+ fi
+ else
+ UNCHECKSTYLED_FILES="${UNCHECKSTYLED_FILES} ${i}"
+ fi
+ else
+ UNCHECKSTYLED_FILES="${UNCHECKSTYLED_FILES} ${i}"
+ fi
+done
+
+if [ ${EXIT_CODE} == 0 ]; then
+ echo "*******************************************************************"
+ echo "* SWEETCOMB CHECKSTYLE SUCCESSFULLY COMPLETED"
+ echo "*******************************************************************"
+else
+ echo "*******************************************************************"
+ echo "* VPP CHECKSTYLE FAILED"
+ echo "* CONSULT FAILURE LOG ABOVE"
+ echo "* NOTE: Running 'build-root/scripts/checkstyle.sh --fix' *MAY* fix the issue"
+ echo "*******************************************************************"
+fi
+exit ${EXIT_CODE}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..8d31550
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,35 @@
+#
+# 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.
+#
+
+cmake_minimum_required(VERSION 2.8)
+project(sysrepo-vpp)
+
+include(GNUInstallDirs)
+
+# set default build type if not specified by user
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE debug)
+endif()
+string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
+
+# set compiler options
+set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -std=gnu99")
+set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG -O2")
+set(CMAKE_C_FLAGS_DEBUG "-g -O0")
+
+# add subdirectories
+add_subdirectory(srvpp)
+add_subdirectory(plugins)
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..3375d25
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,9 @@
+# Sysrepo - VPP Integration
+
+This repository contains a work-in-progress code of the integration of [Sysrepo datastore](https://github.com/sysrepo/sysrepo/) with [VPP](https://fd.io/).
+
+It consists of two components:
+- [srvpp](srvpp/) library that provides convenient API for managing VPP from Sysrepo plugins,
+- [plugins](plugins/) for Sysrepo that provide the VPP management functionality.
+
+As of now, basic VPP interface management (interface enable/disble, IPv4/IPv6 address config) has been implemented in [vpp-interfaces plugin](plugins/vpp-interfaces.c).
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
new file mode 100644
index 0000000..f21d06f
--- /dev/null
+++ b/src/plugins/CMakeLists.txt
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+cmake_minimum_required(VERSION 2.8)
+project(sysrepo-vpp-plugins)
+
+find_package(PkgConfig)
+pkg_check_modules(SYSREPO REQUIRED libsysrepo)
+pkg_check_modules(SRVPP REQUIRED libsrvpp)
+
+# get sysrepo plugins directory from pkgconfig
+if (NOT SR_PLUGINS_DIR)
+ if (PKG_CONFIG_FOUND)
+ execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} "--variable=SR_PLUGINS_DIR" "libsysrepo" OUTPUT_VARIABLE SR_PLUGINS_DIR)
+ string(STRIP ${SR_PLUGINS_DIR} SR_PLUGINS_DIR)
+ endif()
+endif()
+if (NOT SR_PLUGINS_DIR)
+ message(FATAL_ERROR "Cannot get sysrepo plugins directory due to missing pkg-config, set SR_PLUGINS_DIR manually.")
+endif()
+
+# build the source code into shared library
+add_library(vpp-interfaces SHARED vpp-interfaces.c)
+target_link_libraries(vpp-interfaces ${SYSREPO_LIBRARIES} ${SRVPP_LIBRARIES})
+
+# install the plugin into plugins dir
+install(TARGETS vpp-interfaces DESTINATION ${SR_PLUGINS_DIR})
diff --git a/src/plugins/vpp-interfaces.c b/src/plugins/vpp-interfaces.c
new file mode 100644
index 0000000..9525c18
--- /dev/null
+++ b/src/plugins/vpp-interfaces.c
@@ -0,0 +1,593 @@
+/*
+ * 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 <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sysrepo.h>
+#include <sysrepo/plugins.h>
+#include <sysrepo/values.h>
+#include <sysrepo/xpath.h>
+#include <srvpp.h>
+
+/** forward declaration */
+void sr_plugin_cleanup_cb(sr_session_ctx_t *session, void *private_ctx);
+
+/**
+ * @brief Plugin context structure.
+ */
+typedef struct plugin_ctx_s {
+ srvpp_ctx_t *srvpp_ctx; /**< srvpp context. */
+ sr_subscription_ctx_t *sr_subscription; /**< Sysrepo subscription context. */
+} plugin_ctx_t;
+
+/**
+ * @brief Helper function for converting netmask into prefix length.
+ */
+static uint8_t
+netmask_to_prefix(const char *netmask)
+{
+ in_addr_t n = 0;
+ uint8_t i = 0;
+
+ inet_pton(AF_INET, netmask, &n);
+
+ while (n > 0) {
+ n = n >> 1;
+ i++;
+ }
+
+ return i;
+}
+
+/**
+ * @brief Helper function for converting IPv4/IPv6 address string into binary representation.
+ */
+static void
+ip_addr_str_to_binary(const char *ip_address_str, uint8_t *ip_address_bin, bool is_ipv6)
+{
+ struct in6_addr addr6 = { 0, };
+ struct in_addr addr4 = { 0, };
+
+ if (is_ipv6) {
+ inet_pton(AF_INET6, ip_address_str, &(addr6));
+ memcpy(ip_address_bin, &addr6, sizeof(addr6));
+ } else {
+ inet_pton(AF_INET, ip_address_str, &(addr4));
+ memcpy(ip_address_bin, &addr4, sizeof(addr4));
+ }
+}
+
+/**
+ * @brief Helper function for converting VPP interface speed information into actual number in bits per second.
+ */
+static uint64_t
+get_if_link_speed(uint8_t speed)
+{
+#define ONE_MEGABIT (uint64_t)1000000
+ switch (speed) {
+ case 1:
+ /* 10M */
+ return 10 * ONE_MEGABIT;
+ break;
+ case 2:
+ /* 100M */
+ return 100 * ONE_MEGABIT;
+ break;
+ case 4:
+ /* 1G */
+ return 1000 * ONE_MEGABIT;
+ break;
+ case 8:
+ /* 10G */
+ return 10000 * ONE_MEGABIT;
+ break;
+ case 16:
+ /* 40 G */
+ return 40000 * ONE_MEGABIT;
+ break;
+ case 32:
+ /* 100G */
+ return 100000 * ONE_MEGABIT;
+ break;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * @brief Enable or disable given interface.
+ */
+static int
+interface_enable_disable(plugin_ctx_t *plugin_ctx, const char *if_name, bool enable)
+{
+ vl_api_sw_interface_set_flags_t *if_set_flags_req = NULL;
+ uint32_t if_index = ~0;
+ int rc = 0;
+
+ SRP_LOG_DBG("%s interface '%s'", enable ? "Enabling" : "Disabling", if_name);
+
+ /* get interface index */
+ rc = srvpp_get_if_index(plugin_ctx->srvpp_ctx, if_name, &if_index);
+ if (0 != rc) {
+ SRP_LOG_ERR("Invalid interface name: %s", if_name);
+ return SR_ERR_INVAL_ARG;
+ }
+
+ /* process VPP API request */
+ if_set_flags_req = srvpp_alloc_msg(VL_API_SW_INTERFACE_SET_FLAGS, sizeof(*if_set_flags_req));
+
+ if_set_flags_req->sw_if_index = ntohl(if_index);
+ if_set_flags_req->admin_up_down = (uint8_t)enable;
+
+ rc = srvpp_send_request(plugin_ctx->srvpp_ctx, if_set_flags_req, NULL);
+
+ if (0 != rc) {
+ SRP_LOG_ERR("Error by processing of the sw_interface_set_flags request, rc=%d", rc);
+ return SR_ERR_OPERATION_FAILED;
+ } else {
+ return SR_ERR_OK;
+ }
+}
+
+/**
+ * @brief Callback to be called by any config change of "/ietf-interfaces:interfaces/interface/enabled" leaf.
+ */
+static int
+interface_enable_disable_cb(sr_session_ctx_t *session, const char *xpath, sr_notif_event_t event, void *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;
+
+ /* 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) {
+ SRP_LOG_ERR("Unable to retrieve change iterator: %s", sr_strerror(rc));
+ return rc;
+ }
+
+ /* iterate over all changes */
+ while ((SR_ERR_OK == op_rc || event == SR_EV_ABORT) &&
+ (SR_ERR_OK == (rc = sr_get_change_next(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_disable(private_ctx, if_name, new_val->data.bool_val);
+ break;
+ case SR_OP_DELETED:
+ op_rc = interface_enable_disable(private_ctx, 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 Add or remove IPv4/IPv6 address to/from an interface.
+ */
+static int
+interface_ipv46_config_add_remove(plugin_ctx_t *plugin_ctx, const char *if_name, uint8_t *addr, uint8_t prefix,
+ bool is_ipv6, bool add)
+{
+ vl_api_sw_interface_add_del_address_t *add_del_req = NULL;
+ uint32_t if_index = ~0;
+ int rc = 0;
+
+ SRP_LOG_DBG("%s IP config on interface '%s'.", add ? "Adding" : "Removing", if_name);
+
+ /* get interface index */
+ rc = srvpp_get_if_index(plugin_ctx->srvpp_ctx, if_name, &if_index);
+ if (0 != rc) {
+ SRP_LOG_ERR("Invalid interface name: %s", if_name);
+ return SR_ERR_INVAL_ARG;
+ }
+
+ /* process VPP API request */
+ add_del_req = srvpp_alloc_msg(VL_API_SW_INTERFACE_ADD_DEL_ADDRESS, sizeof(*add_del_req));
+
+ memcpy(&add_del_req->address, addr, is_ipv6 ? 16 : 4);
+ add_del_req->is_ipv6 = (uint32_t)is_ipv6;
+ add_del_req->address_length = prefix;
+
+ add_del_req->sw_if_index = ntohl(if_index);
+ add_del_req->is_add = (uint32_t)add;
+
+ rc = srvpp_send_request(plugin_ctx->srvpp_ctx, add_del_req, NULL);
+
+ if (0 != rc) {
+ SRP_LOG_ERR("Error by processing of the sw_interface_set_flags request, rc=%d", rc);
+ return SR_ERR_OPERATION_FAILED;
+ } else {
+ return SR_ERR_OK;
+ }
+}
+
+/**
+ * @brief Modify existing IPv4/IPv6 config on an interface.
+ */
+static int
+interface_ipv46_config_modify(plugin_ctx_t *plugin_ctx, sr_session_ctx_t *session, 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_str = NULL;
+ uint8_t addr[16] = { 0, };
+ 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_str = sr_xpath_key_value((char*)old_val->xpath, "address", "ip", &xpath_ctx);
+ ip_addr_str_to_binary(addr_str, addr, is_ipv6);
+ sr_xpath_recover(&xpath_ctx);
+
+ /* delete old IP config */
+ rc = interface_ipv46_config_add_remove(plugin_ctx, 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 = interface_ipv46_config_add_remove(plugin_ctx, 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
+interface_ipv46_address_change_cb(sr_session_ctx_t *session, const char *xpath, sr_notif_event_t event, void *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;
+ uint8_t addr[16] = { 0, };
+ uint8_t prefix = 0;
+ char *node_name = NULL, *if_name = NULL;
+ int rc = SR_ERR_OK, op_rc = SR_ERR_OK;
+
+ /* 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) {
+ SRP_LOG_ERR("Unable to retrieve change iterator: %s", sr_strerror(rc));
+ return rc;
+ }
+
+ /* iterate over all changes */
+ while ((SR_ERR_OK == op_rc || event == SR_EV_ABORT) &&
+ (SR_ERR_OK == (rc = sr_get_change_next(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")) {
+ ip_addr_str_to_binary(new_val->data.string_val, addr, is_ipv6);
+ 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 = interface_ipv46_config_add_remove(private_ctx, if_name, addr, prefix, is_ipv6, true /* add */);
+ }
+ }
+ break;
+ case SR_OP_MODIFIED:
+ op_rc = interface_ipv46_config_modify(private_ctx, session, 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")) {
+ ip_addr_str_to_binary(old_val->data.string_val, addr, is_ipv6);
+ 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 = interface_ipv46_config_add_remove(private_ctx, 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
+interface_change_cb(sr_session_ctx_t *session, const char *xpath, sr_notif_event_t event, void *private_ctx)
+{
+ SRP_LOG_DBG("'%s' modified, event=%d", xpath, event);
+
+ return SR_ERR_OK;
+}
+
+/**
+ * @brief Callback to be called by any request for state data under "/ietf-interfaces:interfaces-state/interface" path.
+ */
+static int
+interface_state_cb(const char *xpath, sr_val_t **values, size_t *values_cnt, void *private_ctx)
+{
+ plugin_ctx_t *plugin_ctx = private_ctx;
+ vl_api_sw_interface_dump_t *if_dump_req = NULL;
+ vl_api_sw_interface_details_t *if_details = NULL;
+ void **details = NULL;
+ size_t details_cnt = 0;
+ sr_val_t *values_arr = NULL;
+ size_t values_arr_size = 0, values_arr_cnt = 0;
+ int rc = 0;
+
+ SRP_LOG_DBG("Requesting state data for '%s'", xpath);
+
+ if (! sr_xpath_node_name_eq(xpath, "interface")) {
+ /* statistics, ipv4 and ipv6 state data not supported */
+ *values_cnt = 0;
+ return SR_ERR_OK;
+ }
+
+ /* dump interfaces */
+ if_dump_req = srvpp_alloc_msg(VL_API_SW_INTERFACE_DUMP, sizeof(*if_dump_req));
+ rc = srvpp_send_dumprequest(plugin_ctx->srvpp_ctx, if_dump_req, &details, &details_cnt);
+ if (0 != rc) {
+ SRP_LOG_ERR_MSG("Error by processing of a interface dump request.");
+ return SR_ERR_INTERNAL;
+ }
+
+ /* allocate array of values to be returned */
+ values_arr_size = details_cnt * 5;
+ rc = sr_new_values(values_arr_size, &values_arr);
+ if (0 != rc) {
+ return rc;
+ }
+
+ for (size_t i = 0; i < details_cnt; i++) {
+ if_details = (vl_api_sw_interface_details_t *) details[i];
+
+ /* currently the only supported interface types are propVirtual / ethernetCsmacd */
+ sr_val_build_xpath(&values_arr[values_arr_cnt], "%s[name='%s']/type", xpath, if_details->interface_name);
+ sr_val_set_str_data(&values_arr[values_arr_cnt], SR_IDENTITYREF_T,
+ strstr((char*)if_details->interface_name, "local") ? "iana-if-type:propVirtual" : "iana-if-type:ethernetCsmacd");
+ values_arr_cnt++;
+
+ sr_val_build_xpath(&values_arr[values_arr_cnt], "%s[name='%s']/admin-status", xpath, if_details->interface_name);
+ sr_val_set_str_data(&values_arr[values_arr_cnt], SR_ENUM_T, if_details->admin_up_down ? "up" : "down");
+ values_arr_cnt++;
+
+ sr_val_build_xpath(&values_arr[values_arr_cnt], "%s[name='%s']/oper-status", xpath, if_details->interface_name);
+ sr_val_set_str_data(&values_arr[values_arr_cnt], SR_ENUM_T, if_details->link_up_down ? "up" : "down");
+ values_arr_cnt++;
+
+ if (if_details->l2_address_length > 0) {
+ sr_val_build_xpath(&values_arr[values_arr_cnt], "%s[name='%s']/phys-address", xpath, if_details->interface_name);
+ sr_val_build_str_data(&values_arr[values_arr_cnt], SR_STRING_T, "%02x:%02x:%02x:%02x:%02x:%02x",
+ if_details->l2_address[0], if_details->l2_address[1], if_details->l2_address[2],
+ if_details->l2_address[3], if_details->l2_address[4], if_details->l2_address[5]);
+ values_arr_cnt++;
+ }
+
+ sr_val_build_xpath(&values_arr[values_arr_cnt], "%s[name='%s']/speed", xpath, if_details->interface_name);
+ values_arr[values_arr_cnt].type = SR_UINT64_T;
+ values_arr[values_arr_cnt].data.uint64_val = get_if_link_speed(if_details->link_speed);
+ values_arr_cnt++;
+ }
+
+ SRP_LOG_DBG("Returning %zu state data elements for '%s'", values_arr, xpath);
+
+ *values = values_arr;
+ *values_cnt = values_arr_cnt;
+ return SR_ERR_OK;
+}
+
+/**
+ * @brief Callback to be called by plugin daemon upon plugin load.
+ */
+int
+sr_plugin_init_cb(sr_session_ctx_t *session, void **private_ctx)
+{
+ plugin_ctx_t *ctx = NULL;
+ int rc = SR_ERR_OK;
+
+ SRP_LOG_DBG_MSG("Initializing vpp-interfaces plugin.");
+
+ /* allocate the plugin context */
+ ctx = calloc(1, sizeof(*ctx));
+ if (NULL == ctx) {
+ return SR_ERR_NOMEM;
+ }
+
+ /* get srvpp context */
+ ctx->srvpp_ctx = srvpp_get_ctx();
+ if (NULL == ctx->srvpp_ctx) {
+ return SR_ERR_INIT_FAILED;
+ }
+
+ /* setup handlers for required VPP API messages */
+ srvpp_setup_handler(SW_INTERFACE_SET_FLAGS_REPLY, sw_interface_set_flags_reply);
+ srvpp_setup_handler(SW_INTERFACE_ADD_DEL_ADDRESS_REPLY, sw_interface_add_del_address_reply);
+
+ rc = sr_subtree_change_subscribe(session, "/ietf-interfaces:interfaces/interface",
+ interface_change_cb, ctx, 0, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_EV_ENABLED, &ctx->sr_subscription);
+ if (SR_ERR_OK != rc) {
+ goto error;
+ }
+
+ rc = sr_subtree_change_subscribe(session, "/ietf-interfaces:interfaces/interface/enabled",
+ interface_enable_disable_cb, ctx, 100, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_EV_ENABLED, &ctx->sr_subscription);
+ if (SR_ERR_OK != rc) {
+ goto error;
+ }
+
+ rc = sr_subtree_change_subscribe(session, "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/address",
+ interface_ipv46_address_change_cb, ctx, 99, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_EV_ENABLED, &ctx->sr_subscription);
+ if (SR_ERR_OK != rc) {
+ goto error;
+ }
+
+ rc = sr_subtree_change_subscribe(session, "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6/address",
+ interface_ipv46_address_change_cb, ctx, 98, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_EV_ENABLED, &ctx->sr_subscription);
+ if (SR_ERR_OK != rc) {
+ goto error;
+ }
+
+ rc = sr_dp_get_items_subscribe(session, "/ietf-interfaces:interfaces-state",
+ interface_state_cb, ctx, SR_SUBSCR_CTX_REUSE, &ctx->sr_subscription);
+ if (SR_ERR_OK != rc) {
+ goto error;
+ }
+
+ *private_ctx = ctx;
+
+ SRP_LOG_INF_MSG("vpp-interfaces plugin initialized successfully.");
+
+ return SR_ERR_OK;
+
+error:
+ SRP_LOG_ERR_MSG("Error by initialization of the vpp-interfaces plugin.");
+ sr_plugin_cleanup_cb(session, ctx);
+ return rc;
+}
+
+/**
+ * @brief Callback to be called by plugin daemon upon plugin unload.
+ */
+void
+sr_plugin_cleanup_cb(sr_session_ctx_t *session, void *private_ctx)
+{
+ plugin_ctx_t *ctx = (plugin_ctx_t *) private_ctx;
+
+ SRP_LOG_DBG_MSG("Cleanup of vpp-interfaces plugin requested.");
+
+ sr_unsubscribe(session, ctx->sr_subscription);
+
+ srvpp_release_ctx(ctx->srvpp_ctx);
+ free(ctx);
+}
+
+/**
+ * @brief Callback to be called by plugin daemon periodically, to check whether the plugin and managed app is healthy.
+ */
+/*
+int
+sr_plugin_health_check_cb(sr_session_ctx_t *session, void *private_ctx)
+{
+ plugin_ctx_t *ctx = (plugin_ctx_t *) private_ctx;
+ vl_api_control_ping_t *ping = NULL;
+ int rc = 0;
+
+ ping = srvpp_alloc_msg(VL_API_CONTROL_PING, sizeof(*ping));
+
+ rc = srvpp_send_request(ctx->srvpp_ctx, ping, NULL);
+
+ if (0 != rc) {
+ return SR_ERR_DISCONNECT;
+ } else {
+ return SR_ERR_OK;
+ }
+}
+*/
diff --git a/src/plugins/yang/ietf-interfaces.data.xml b/src/plugins/yang/ietf-interfaces.data.xml
new file mode 100644
index 0000000..3aae96e
--- /dev/null
+++ b/src/plugins/yang/ietf-interfaces.data.xml
@@ -0,0 +1,21 @@
+<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
+ <interface>
+ <name>GigabitEthernet0/8/0</name>
+ <description>Ethernet 8</description>
+ <type>ethernetCsmacd</type>
+ <enabled>true</enabled>
+ <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
+ <address>
+ <ip>192.168.2.100</ip>
+ <prefix-length>24</prefix-length>
+ </address>
+ </ipv4>
+ <ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
+ <address>
+ <ip>2001:db8::10</ip>
+ <prefix-length>32</prefix-length>
+ </address>
+ </ipv6>
+ </interface>
+</interfaces>
+
diff --git a/src/plugins/yang/ietf-interfaces@2014-05-08.yang b/src/plugins/yang/ietf-interfaces@2014-05-08.yang
new file mode 100644
index 0000000..a0eb30e
--- /dev/null
+++ b/src/plugins/yang/ietf-interfaces@2014-05-08.yang
@@ -0,0 +1,707 @@
+module ietf-interfaces {
+
+ yang-version 1;
+
+ namespace
+ "urn:ietf:params:xml:ns:yang:ietf-interfaces";
+
+ prefix if;
+
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: Thomas Nadeau
+ <mailto:tnadeau@lucidvision.com>
+
+ WG Chair: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+
+ description
+ "This module contains a collection of YANG definitions for
+ managing network interfaces.
+
+ Copyright (c) 2014 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 7223; see
+ the RFC itself for full legal notices.";
+
+ revision "2014-05-08" {
+ description "Initial revision.";
+ reference
+ "RFC 7223: A YANG Data Model for Interface Management";
+
+ }
+
+
+ typedef interface-ref {
+ type leafref {
+ path "/if:interfaces/if:interface/if:name";
+ }
+ description
+ "This type is used by data models that need to reference
+ configured interfaces.";
+ }
+
+ typedef interface-state-ref {
+ type leafref {
+ path "/if:interfaces-state/if:interface/if:name";
+ }
+ description
+ "This type is used by data models that need to reference
+ the operationally present interfaces.";
+ }
+
+ identity interface-type {
+ description
+ "Base identity from which specific interface types are
+ derived.";
+ }
+
+ feature arbitrary-names {
+ description
+ "This feature indicates that the device allows user-controlled
+ interfaces to be named arbitrarily.";
+ }
+
+ feature pre-provisioning {
+ description
+ "This feature indicates that the device supports
+ pre-provisioning of interface configuration, i.e., it is
+ possible to configure an interface whose physical interface
+ hardware is not present on the device.";
+ }
+
+ feature if-mib {
+ description
+ "This feature indicates that the device implements
+ the IF-MIB.";
+ reference
+ "RFC 2863: The Interfaces Group MIB";
+
+ }
+
+ container interfaces {
+ description
+ "Interface configuration parameters.";
+ list interface {
+ key "name";
+ description
+ "The list of configured interfaces on the device.
+
+ The operational state of an interface is available in the
+ /interfaces-state/interface list. If the configuration of a
+ system-controlled interface cannot be used by the system
+ (e.g., the interface hardware present does not match the
+ interface type), then the configuration is not applied to
+ the system-controlled interface shown in the
+ /interfaces-state/interface list. If the configuration
+ of a user-controlled interface cannot be used by the system,
+ the configured interface is not instantiated in the
+ /interfaces-state/interface list.";
+ leaf name {
+ type string;
+ description
+ "The name of the interface.
+
+ A device MAY restrict the allowed values for this leaf,
+ possibly depending on the type of the interface.
+ For system-controlled interfaces, this leaf is the
+ device-specific name of the interface. The 'config false'
+ list /interfaces-state/interface contains the currently
+ existing interfaces on the device.
+
+ If a client tries to create configuration for a
+ system-controlled interface that is not present in the
+ /interfaces-state/interface list, the server MAY reject
+ the request if the implementation does not support
+ pre-provisioning of interfaces or if the name refers to
+ an interface that can never exist in the system. A
+ NETCONF server MUST reply with an rpc-error with the
+ error-tag 'invalid-value' in this case.
+
+ If the device supports pre-provisioning of interface
+ configuration, the 'pre-provisioning' feature is
+ advertised.
+
+ If the device allows arbitrarily named user-controlled
+ interfaces, the 'arbitrary-names' feature is advertised.
+
+ When a configured user-controlled interface is created by
+ the system, it is instantiated with the same name in the
+ /interface-state/interface list.";
+ }
+
+ leaf description {
+ type string;
+ description
+ "A textual description of the interface.
+
+ A server implementation MAY map this leaf to the ifAlias
+ MIB object. Such an implementation needs to use some
+ mechanism to handle the differences in size and characters
+ allowed between this leaf and ifAlias. The definition of
+ such a mechanism is outside the scope of this document.
+
+ Since ifAlias is defined to be stored in non-volatile
+ storage, the MIB implementation MUST map ifAlias to the
+ value of 'description' in the persistently stored
+ datastore.
+
+ Specifically, if the device supports ':startup', when
+ ifAlias is read the device MUST return the value of
+ 'description' in the 'startup' datastore, and when it is
+ written, it MUST be written to the 'running' and 'startup'
+ datastores. Note that it is up to the implementation to
+
+ decide whether to modify this single leaf in 'startup' or
+ perform an implicit copy-config from 'running' to
+ 'startup'.
+
+ If the device does not support ':startup', ifAlias MUST
+ be mapped to the 'description' leaf in the 'running'
+ datastore.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifAlias";
+
+ }
+
+ leaf type {
+ type identityref {
+ base interface-type;
+ }
+ mandatory true;
+ description
+ "The type of the interface.
+
+ When an interface entry is created, a server MAY
+ initialize the type leaf with a valid value, e.g., if it
+ is possible to derive the type from the name of the
+ interface.
+
+ If a client tries to set the type of an interface to a
+ value that can never be used by the system, e.g., if the
+ type is not supported or if the type does not match the
+ name of the interface, the server MUST reject the request.
+ A NETCONF server MUST reply with an rpc-error with the
+ error-tag 'invalid-value' in this case.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifType";
+
+ }
+
+ leaf enabled {
+ type boolean;
+ default "true";
+ description
+ "This leaf contains the configured, desired state of the
+ interface.
+
+ Systems that implement the IF-MIB use the value of this
+ leaf in the 'running' datastore to set
+ IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry
+ has been initialized, as described in RFC 2863.
+
+
+
+ Changes in this leaf in the 'running' datastore are
+ reflected in ifAdminStatus, but if ifAdminStatus is
+ changed over SNMP, this leaf is not affected.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+
+ }
+
+ leaf link-up-down-trap-enable {
+ if-feature if-mib;
+ type enumeration {
+ enum "enabled" {
+ value 1;
+ }
+ enum "disabled" {
+ value 2;
+ }
+ }
+ description
+ "Controls whether linkUp/linkDown SNMP notifications
+ should be generated for this interface.
+
+ If this node is not configured, the value 'enabled' is
+ operationally used by the server for interfaces that do
+ not operate on top of any other interface (i.e., there are
+ no 'lower-layer-if' entries), and 'disabled' otherwise.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifLinkUpDownTrapEnable";
+
+ }
+ } // list interface
+ } // container interfaces
+
+ container interfaces-state {
+ config false;
+ description
+ "Data nodes for the operational state of interfaces.";
+ list interface {
+ key "name";
+ description
+ "The list of interfaces on the device.
+
+ System-controlled interfaces created by the system are
+ always present in this list, whether they are configured or
+ not.";
+ leaf name {
+ type string;
+ description
+ "The name of the interface.
+
+ A server implementation MAY map this leaf to the ifName
+ MIB object. Such an implementation needs to use some
+ mechanism to handle the differences in size and characters
+ allowed between this leaf and ifName. The definition of
+ such a mechanism is outside the scope of this document.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifName";
+
+ }
+
+ leaf type {
+ type identityref {
+ base interface-type;
+ }
+ mandatory true;
+ description
+ "The type of the interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifType";
+
+ }
+
+ leaf admin-status {
+ if-feature if-mib;
+ type enumeration {
+ enum "up" {
+ value 1;
+ description
+ "Ready to pass packets.";
+ }
+ enum "down" {
+ value 2;
+ description
+ "Not ready to pass packets and not in some test mode.";
+ }
+ enum "testing" {
+ value 3;
+ description
+ "In some test mode.";
+ }
+ }
+ mandatory true;
+ description
+ "The desired state of the interface.
+
+ This leaf has the same read semantics as ifAdminStatus.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+
+ }
+
+ leaf oper-status {
+ type enumeration {
+ enum "up" {
+ value 1;
+ description
+ "Ready to pass packets.";
+ }
+ enum "down" {
+ value 2;
+ description
+ "The interface does not pass any packets.";
+ }
+ enum "testing" {
+ value 3;
+ description
+ "In some test mode. No operational packets can
+ be passed.";
+ }
+ enum "unknown" {
+ value 4;
+ description
+ "Status cannot be determined for some reason.";
+ }
+ enum "dormant" {
+ value 5;
+ description
+ "Waiting for some external event.";
+ }
+ enum "not-present" {
+ value 6;
+ description
+ "Some component (typically hardware) is missing.";
+ }
+ enum "lower-layer-down" {
+ value 7;
+ description
+ "Down due to state of lower-layer interface(s).";
+ }
+ }
+ mandatory true;
+ description
+ "The current operational state of the interface.
+
+ This leaf has the same semantics as ifOperStatus.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifOperStatus";
+
+ }
+
+ leaf last-change {
+ type yang:date-and-time;
+ description
+ "The time the interface entered its current operational
+ state. If the current state was entered prior to the
+ last re-initialization of the local network management
+ subsystem, then this node is not present.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifLastChange";
+
+ }
+
+ leaf if-index {
+ if-feature if-mib;
+ type int32 {
+ range "1..2147483647";
+ }
+ mandatory true;
+ description
+ "The ifIndex value for the ifEntry represented by this
+ interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifIndex";
+
+ }
+
+ leaf phys-address {
+ type yang:phys-address;
+ description
+ "The interface's address at its protocol sub-layer. For
+ example, for an 802.x interface, this object normally
+ contains a Media Access Control (MAC) address. The
+ interface's media-specific modules must define the bit
+
+
+ and byte ordering and the format of the value of this
+ object. For interfaces that do not have such an address
+ (e.g., a serial line), this node is not present.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifPhysAddress";
+
+ }
+
+ leaf-list higher-layer-if {
+ type interface-state-ref;
+ description
+ "A list of references to interfaces layered on top of this
+ interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifStackTable";
+
+ }
+
+ leaf-list lower-layer-if {
+ type interface-state-ref;
+ description
+ "A list of references to interfaces layered underneath this
+ interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifStackTable";
+
+ }
+
+ leaf speed {
+ type yang:gauge64;
+ units "bits/second";
+ description
+ "An estimate of the interface's current bandwidth in bits
+ per second. For interfaces that do not vary in
+ bandwidth or for those where no accurate estimation can
+ be made, this node should contain the nominal bandwidth.
+ For interfaces that have no concept of bandwidth, this
+ node is not present.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifSpeed, ifHighSpeed";
+
+ }
+
+ container statistics {
+ description
+ "A collection of interface-related statistics objects.";
+ leaf discontinuity-time {
+ type yang:date-and-time;
+ mandatory true;
+ description
+ "The time on the most recent occasion at which any one or
+ more of this interface's counters suffered a
+ discontinuity. If no such discontinuities have occurred
+ since the last re-initialization of the local management
+ subsystem, then this node contains the time the local
+ management subsystem re-initialized itself.";
+ }
+
+ leaf in-octets {
+ type yang:counter64;
+ description
+ "The total number of octets received on the interface,
+ including framing characters.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCInOctets";
+
+ }
+
+ leaf in-unicast-pkts {
+ type yang:counter64;
+ description
+ "The number of packets, delivered by this sub-layer to a
+ higher (sub-)layer, that were not addressed to a
+ multicast or broadcast address at this sub-layer.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts";
+
+ }
+
+ leaf in-broadcast-pkts {
+ type yang:counter64;
+ description
+ "The number of packets, delivered by this sub-layer to a
+ higher (sub-)layer, that were addressed to a broadcast
+ address at this sub-layer.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCInBroadcastPkts";
+
+ }
+
+ leaf in-multicast-pkts {
+ type yang:counter64;
+ description
+ "The number of packets, delivered by this sub-layer to a
+ higher (sub-)layer, that were addressed to a multicast
+ address at this sub-layer. For a MAC-layer protocol,
+ this includes both Group and Functional addresses.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCInMulticastPkts";
+
+ }
+
+ leaf in-discards {
+ type yang:counter32;
+ description
+ "The number of inbound packets that were chosen to be
+ discarded even though no errors had been detected to
+ prevent their being deliverable to a higher-layer
+ protocol. One possible reason for discarding such a
+ packet could be to free up buffer space.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifInDiscards";
+
+ }
+
+ leaf in-errors {
+ type yang:counter32;
+ description
+ "For packet-oriented interfaces, the number of inbound
+ packets that contained errors preventing them from being
+ deliverable to a higher-layer protocol. For character-
+ oriented or fixed-length interfaces, the number of
+ inbound transmission units that contained errors
+ preventing them from being deliverable to a higher-layer
+ protocol.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifInErrors";
+
+ }
+
+ leaf in-unknown-protos {
+ type yang:counter32;
+ description
+ "For packet-oriented interfaces, the number of packets
+ received via the interface that were discarded because
+ of an unknown or unsupported protocol. For
+ character-oriented or fixed-length interfaces that
+ support protocol multiplexing, the number of
+ transmission units received via the interface that were
+ discarded because of an unknown or unsupported protocol.
+ For any interface that does not support protocol
+ multiplexing, this counter is not present.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos";
+
+ }
+
+ leaf out-octets {
+ type yang:counter64;
+ description
+ "The total number of octets transmitted out of the
+ interface, including framing characters.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCOutOctets";
+
+ }
+
+ leaf out-unicast-pkts {
+ type yang:counter64;
+ description
+ "The total number of packets that higher-level protocols
+ requested be transmitted, and that were not addressed
+ to a multicast or broadcast address at this sub-layer,
+ including those that were discarded or not sent.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts";
+
+ }
+
+ leaf out-broadcast-pkts {
+ type yang:counter64;
+ description
+ "The total number of packets that higher-level protocols
+ requested be transmitted, and that were addressed to a
+ broadcast address at this sub-layer, including those
+ that were discarded or not sent.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCOutBroadcastPkts";
+
+ }
+
+ leaf out-multicast-pkts {
+ type yang:counter64;
+ description
+ "The total number of packets that higher-level protocols
+ requested be transmitted, and that were addressed to a
+ multicast address at this sub-layer, including those
+ that were discarded or not sent. For a MAC-layer
+ protocol, this includes both Group and Functional
+ addresses.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCOutMulticastPkts";
+
+ }
+
+ leaf out-discards {
+ type yang:counter32;
+ description
+ "The number of outbound packets that were chosen to be
+ discarded even though no errors had been detected to
+ prevent their being transmitted. One possible reason
+ for discarding such a packet could be to free up buffer
+ space.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifOutDiscards";
+
+ }
+
+ leaf out-errors {
+ type yang:counter32;
+ description
+ "For packet-oriented interfaces, the number of outbound
+ packets that could not be transmitted because of errors.
+ For character-oriented or fixed-length interfaces, the
+ number of outbound transmission units that could not be
+ transmitted because of errors.
+
+
+
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifOutErrors";
+
+ }
+ } // container statistics
+ } // list interface
+ } // container interfaces-state
+ } // module ietf-interfaces
diff --git a/src/plugins/yang/ietf-ip@2014-06-16.yang b/src/plugins/yang/ietf-ip@2014-06-16.yang
new file mode 100644
index 0000000..79606cc
--- /dev/null
+++ b/src/plugins/yang/ietf-ip@2014-06-16.yang
@@ -0,0 +1,742 @@
+module ietf-ip {
+
+ yang-version 1;
+
+ namespace
+ "urn:ietf:params:xml:ns:yang:ietf-ip";
+
+ prefix ip;
+
+ import ietf-interfaces {
+ prefix if;
+ }
+ import ietf-inet-types {
+ prefix inet;
+ }
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: Thomas Nadeau
+ <mailto:tnadeau@lucidvision.com>
+
+ WG Chair: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+
+ description
+ "This module contains a collection of YANG definitions for
+ configuring IP implementations.
+
+ Copyright (c) 2014 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 7277; see
+ the RFC itself for full legal notices.";
+
+ revision "2014-06-16" {
+ description "Initial revision.";
+ reference
+ "RFC 7277: A YANG Data Model for IP Management";
+
+ }
+
+
+ feature ipv4-non-contiguous-netmasks {
+ description
+ "Indicates support for configuring non-contiguous
+ subnet masks.";
+ }
+
+ feature ipv6-privacy-autoconf {
+ description
+ "Indicates support for Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6";
+
+ }
+
+ typedef ip-address-origin {
+ type enumeration {
+ enum "other" {
+ value 0;
+ description
+ "None of the following.";
+ }
+ enum "static" {
+ value 1;
+ description
+ "Indicates that the address has been statically
+ configured - for example, using NETCONF or a Command Line
+ Interface.";
+ }
+ enum "dhcp" {
+ value 2;
+ description
+ "Indicates an address that has been assigned to this
+ system by a DHCP server.";
+ }
+ enum "link-layer" {
+ value 3;
+ description
+ "Indicates an address created by IPv6 stateless
+ autoconfiguration that embeds a link-layer address in its
+ interface identifier.";
+ }
+ enum "random" {
+ value 4;
+ description
+ "Indicates an address chosen by the system at
+
+ random, e.g., an IPv4 address within 169.254/16, an
+ RFC 4941 temporary address, or an RFC 7217 semantically
+ opaque address.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6
+ RFC 7217: A Method for Generating Semantically Opaque
+ Interface Identifiers with IPv6 Stateless
+ Address Autoconfiguration (SLAAC)";
+
+ }
+ }
+ description
+ "The origin of an address.";
+ }
+
+ typedef neighbor-origin {
+ type enumeration {
+ enum "other" {
+ value 0;
+ description
+ "None of the following.";
+ }
+ enum "static" {
+ value 1;
+ description
+ "Indicates that the mapping has been statically
+ configured - for example, using NETCONF or a Command Line
+ Interface.";
+ }
+ enum "dynamic" {
+ value 2;
+ description
+ "Indicates that the mapping has been dynamically resolved
+ using, e.g., IPv4 ARP or the IPv6 Neighbor Discovery
+ protocol.";
+ }
+ }
+ description
+ "The origin of a neighbor entry.";
+ }
+
+ augment /if:interfaces/if:interface {
+ description
+ "Parameters for configuring IP on interfaces.
+
+ If an interface is not capable of running IP, the server
+ must not allow the client to configure these parameters.";
+ container ipv4 {
+ presence
+ "Enables IPv4 unless the 'enabled' leaf
+ (which defaults to 'true') is set to 'false'";
+ description
+ "Parameters for the IPv4 address family.";
+ leaf enabled {
+ type boolean;
+ default 'true';
+ description
+ "Controls whether IPv4 is enabled or disabled on this
+ interface. When IPv4 is enabled, this interface is
+ connected to an IPv4 stack, and the interface can send
+ and receive IPv4 packets.";
+ }
+
+ leaf forwarding {
+ type boolean;
+ default 'false';
+ description
+ "Controls IPv4 packet forwarding of datagrams received by,
+ but not addressed to, this interface. IPv4 routers
+ forward datagrams. IPv4 hosts do not (except those
+ source-routed via the host).";
+ }
+
+ leaf mtu {
+ type uint16 {
+ range "68..max";
+ }
+ units "octets";
+ description
+ "The size, in octets, of the largest IPv4 packet that the
+ interface will send and receive.
+
+ The server may restrict the allowed values for this leaf,
+ depending on the interface's type.
+
+ If this leaf is not configured, the operationally used MTU
+ depends on the interface's type.";
+ reference
+ "RFC 791: Internet Protocol";
+
+ }
+
+ list address {
+ key "ip";
+ description
+ "The list of configured IPv4 addresses on the interface.";
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address on the interface.";
+ }
+
+ choice subnet {
+ mandatory true;
+ description
+ "The subnet can be specified as a prefix-length, or,
+ if the server supports non-contiguous netmasks, as
+ a netmask.";
+ leaf prefix-length {
+ type uint8 {
+ range "0..32";
+ }
+ description
+ "The length of the subnet prefix.";
+ }
+ leaf netmask {
+ if-feature ipv4-non-contiguous-netmasks;
+ type yang:dotted-quad;
+ description
+ "The subnet specified as a netmask.";
+ }
+ } // choice subnet
+ } // list address
+
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv4 addresses to
+ link-layer addresses.
+
+ Entries in this list are used as static entries in the
+ ARP Cache.";
+ reference
+ "RFC 826: An Ethernet Address Resolution Protocol";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address of the neighbor node.";
+ }
+
+ leaf link-layer-address {
+ type yang:phys-address;
+ mandatory true;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ } // list neighbor
+ } // container ipv4
+
+ container ipv6 {
+ presence
+ "Enables IPv6 unless the 'enabled' leaf
+ (which defaults to 'true') is set to 'false'";
+ description
+ "Parameters for the IPv6 address family.";
+ leaf enabled {
+ type boolean;
+ default 'true';
+ description
+ "Controls whether IPv6 is enabled or disabled on this
+ interface. When IPv6 is enabled, this interface is
+ connected to an IPv6 stack, and the interface can send
+ and receive IPv6 packets.";
+ }
+
+ leaf forwarding {
+ type boolean;
+ default 'false';
+ description
+ "Controls IPv6 packet forwarding of datagrams received by,
+ but not addressed to, this interface. IPv6 routers
+ forward datagrams. IPv6 hosts do not (except those
+ source-routed via the host).";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+ Section 6.2.1, IsRouter";
+
+ }
+
+ leaf mtu {
+ type uint32 {
+ range "1280..max";
+ }
+ units "octets";
+ description
+ "The size, in octets, of the largest IPv6 packet that the
+ interface will send and receive.
+
+ The server may restrict the allowed values for this leaf,
+ depending on the interface's type.
+
+ If this leaf is not configured, the operationally used MTU
+ depends on the interface's type.";
+ reference
+ "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+ Section 5";
+
+ }
+
+ list address {
+ key "ip";
+ description
+ "The list of configured IPv6 addresses on the interface.";
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address on the interface.";
+ }
+
+ leaf prefix-length {
+ type uint8 {
+ range "0..128";
+ }
+ mandatory true;
+ description
+ "The length of the subnet prefix.";
+ }
+ } // list address
+
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv6 addresses to
+ link-layer addresses.
+
+ Entries in this list are used as static entries in the
+ Neighbor Cache.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address of the neighbor node.";
+ }
+
+ leaf link-layer-address {
+ type yang:phys-address;
+ mandatory true;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ } // list neighbor
+
+ leaf dup-addr-detect-transmits {
+ type uint32;
+ default '1';
+ description
+ "The number of consecutive Neighbor Solicitation messages
+ sent while performing Duplicate Address Detection on a
+ tentative address. A value of zero indicates that
+ Duplicate Address Detection is not performed on
+ tentative addresses. A value of one indicates a single
+ transmission with no follow-up retransmissions.";
+ reference
+ "RFC 4862: IPv6 Stateless Address Autoconfiguration";
+
+ }
+
+ container autoconf {
+ description
+ "Parameters to control the autoconfiguration of IPv6
+ addresses, as described in RFC 4862.";
+ reference
+ "RFC 4862: IPv6 Stateless Address Autoconfiguration";
+
+ leaf create-global-addresses {
+ type boolean;
+ default 'true';
+ description
+ "If enabled, the host creates global addresses as
+ described in RFC 4862.";
+ reference
+ "RFC 4862: IPv6 Stateless Address Autoconfiguration
+ Section 5.5";
+
+ }
+
+ leaf create-temporary-addresses {
+ if-feature ipv6-privacy-autoconf;
+ type boolean;
+ default 'false';
+ description
+ "If enabled, the host creates temporary addresses as
+ described in RFC 4941.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6";
+
+ }
+
+ leaf temporary-valid-lifetime {
+ if-feature ipv6-privacy-autoconf;
+ type uint32;
+ units "seconds";
+ default '604800';
+ description
+ "The time period during which the temporary address
+ is valid.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6
+ - TEMP_VALID_LIFETIME";
+
+ }
+
+ leaf temporary-preferred-lifetime {
+ if-feature ipv6-privacy-autoconf;
+ type uint32;
+ units "seconds";
+ default '86400';
+ description
+ "The time period during which the temporary address is
+ preferred.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6
+ - TEMP_PREFERRED_LIFETIME";
+
+ }
+ } // container autoconf
+ } // container ipv6
+ }
+
+ augment /if:interfaces-state/if:interface {
+ description
+ "Data nodes for the operational state of IP on interfaces.";
+ container ipv4 {
+ presence
+ "Present if IPv4 is enabled on this interface";
+ config false;
+ description
+ "Interface-specific parameters for the IPv4 address family.";
+ leaf forwarding {
+ type boolean;
+ description
+ "Indicates whether IPv4 packet forwarding is enabled or
+ disabled on this interface.";
+ }
+
+ leaf mtu {
+ type uint16 {
+ range "68..max";
+ }
+ units "octets";
+ description
+ "The size, in octets, of the largest IPv4 packet that the
+ interface will send and receive.";
+ reference
+ "RFC 791: Internet Protocol";
+
+ }
+
+ list address {
+ key "ip";
+ description
+ "The list of IPv4 addresses on the interface.";
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address on the interface.";
+ }
+
+ choice subnet {
+ description
+ "The subnet can be specified as a prefix-length, or,
+ if the server supports non-contiguous netmasks, as
+ a netmask.";
+ leaf prefix-length {
+ type uint8 {
+ range "0..32";
+ }
+ description
+ "The length of the subnet prefix.";
+ }
+ leaf netmask {
+ if-feature ipv4-non-contiguous-netmasks;
+ type yang:dotted-quad;
+ description
+ "The subnet specified as a netmask.";
+ }
+ } // choice subnet
+
+ leaf origin {
+ type ip-address-origin;
+ description
+ "The origin of this address.";
+ }
+ } // list address
+
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv4 addresses to
+ link-layer addresses.
+
+ This list represents the ARP Cache.";
+ reference
+ "RFC 826: An Ethernet Address Resolution Protocol";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address of the neighbor node.";
+ }
+
+ leaf link-layer-address {
+ type yang:phys-address;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+
+ leaf origin {
+ type neighbor-origin;
+ description
+ "The origin of this neighbor entry.";
+ }
+ } // list neighbor
+ } // container ipv4
+
+ container ipv6 {
+ presence
+ "Present if IPv6 is enabled on this interface";
+ config false;
+ description
+ "Parameters for the IPv6 address family.";
+ leaf forwarding {
+ type boolean;
+ default 'false';
+ description
+ "Indicates whether IPv6 packet forwarding is enabled or
+ disabled on this interface.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+ Section 6.2.1, IsRouter";
+
+ }
+
+ leaf mtu {
+ type uint32 {
+ range "1280..max";
+ }
+ units "octets";
+ description
+ "The size, in octets, of the largest IPv6 packet that the
+ interface will send and receive.";
+ reference
+ "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+ Section 5";
+
+ }
+
+ list address {
+ key "ip";
+ description
+ "The list of IPv6 addresses on the interface.";
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address on the interface.";
+ }
+
+ leaf prefix-length {
+ type uint8 {
+ range "0..128";
+ }
+ mandatory true;
+ description
+ "The length of the subnet prefix.";
+ }
+
+ leaf origin {
+ type ip-address-origin;
+ description
+ "The origin of this address.";
+ }
+
+ leaf status {
+ type enumeration {
+ enum "preferred" {
+ value 0;
+ description
+ "This is a valid address that can appear as the
+ destination or source address of a packet.";
+ }
+ enum "deprecated" {
+ value 1;
+ description
+ "This is a valid but deprecated address that should
+ no longer be used as a source address in new
+ communications, but packets addressed to such an
+ address are processed as expected.";
+ }
+ enum "invalid" {
+ value 2;
+ description
+ "This isn't a valid address, and it shouldn't appear
+ as the destination or source address of a packet.";
+ }
+ enum "inaccessible" {
+ value 3;
+ description
+ "The address is not accessible because the interface
+ to which this address is assigned is not
+ operational.";
+ }
+ enum "unknown" {
+ value 4;
+ description
+ "The status cannot be determined for some reason.";
+ }
+ enum "tentative" {
+ value 5;
+ description
+ "The uniqueness of the address on the link is being
+ verified. Addresses in this state should not be
+ used for general communication and should only be
+ used to determine the uniqueness of the address.";
+ }
+ enum "duplicate" {
+ value 6;
+ description
+ "The address has been determined to be non-unique on
+ the link and so must not be used.";
+ }
+ enum "optimistic" {
+ value 7;
+ description
+ "The address is available for use, subject to
+ restrictions, while its uniqueness on a link is
+ being verified.";
+ }
+ }
+ description
+ "The status of an address. Most of the states correspond
+ to states from the IPv6 Stateless Address
+ Autoconfiguration protocol.";
+ reference
+ "RFC 4293: Management Information Base for the
+ Internet Protocol (IP)
+ - IpAddressStatusTC
+ RFC 4862: IPv6 Stateless Address Autoconfiguration";
+
+ }
+ } // list address
+
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv6 addresses to
+ link-layer addresses.
+
+ This list represents the Neighbor Cache.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address of the neighbor node.";
+ }
+
+ leaf link-layer-address {
+ type yang:phys-address;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+
+ leaf origin {
+ type neighbor-origin;
+ description
+ "The origin of this neighbor entry.";
+ }
+
+ leaf is-router {
+ type empty;
+ description
+ "Indicates that the neighbor node acts as a router.";
+ }
+
+ leaf state {
+ type enumeration {
+ enum "incomplete" {
+ value 0;
+ description
+ "Address resolution is in progress, and the link-layer
+ address of the neighbor has not yet been
+ determined.";
+ }
+ enum "reachable" {
+ value 1;
+ description
+ "Roughly speaking, the neighbor is known to have been
+ reachable recently (within tens of seconds ago).";
+ }
+ enum "stale" {
+ value 2;
+ description
+ "The neighbor is no longer known to be reachable, but
+ until traffic is sent to the neighbor no attempt
+ should be made to verify its reachability.";
+ }
+ enum "delay" {
+ value 3;
+ description
+ "The neighbor is no longer known to be reachable, and
+ traffic has recently been sent to the neighbor.
+ Rather than probe the neighbor immediately, however,
+ delay sending probes for a short while in order to
+ give upper-layer protocols a chance to provide
+ reachability confirmation.";
+ }
+ enum "probe" {
+ value 4;
+ description
+ "The neighbor is no longer known to be reachable, and
+ unicast Neighbor Solicitation probes are being sent
+ to verify reachability.";
+ }
+ }
+ description
+ "The Neighbor Unreachability Detection state of this
+ entry.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+ Section 7.3.2";
+
+ }
+ } // list neighbor
+ } // container ipv6
+ }
+ } // module ietf-ip
diff --git a/src/plugins/yang/ietf-yang-types@2013-07-15.yang b/src/plugins/yang/ietf-yang-types@2013-07-15.yang
new file mode 100644
index 0000000..ee58fa3
--- /dev/null
+++ b/src/plugins/yang/ietf-yang-types@2013-07-15.yang
@@ -0,0 +1,474 @@
+module ietf-yang-types {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+ prefix "yang";
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: David Kessens
+ <mailto:david.kessens@nsn.com>
+
+ WG Chair: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Editor: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>";
+
+ description
+ "This module contains a collection of generally useful derived
+ YANG data types.
+
+ Copyright (c) 2013 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6991; see
+ the RFC itself for full legal notices.";
+
+ revision 2013-07-15 {
+ description
+ "This revision adds the following new data types:
+ - yang-identifier
+ - hex-string
+ - uuid
+ - dotted-quad";
+ reference
+ "RFC 6991: Common YANG Data Types";
+ }
+
+ revision 2010-09-24 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 6021: Common YANG Data Types";
+ }
+
+ /*** collection of counter and gauge types ***/
+
+ typedef counter32 {
+ type uint32;
+ description
+ "The counter32 type represents a non-negative integer
+ that monotonically increases until it reaches a
+ maximum value of 2^32-1 (4294967295 decimal), when it
+ wraps around and starts increasing again from zero.
+
+ Counters have no defined 'initial' value, and thus, a
+ single value of a counter has (in general) no information
+ content. Discontinuities in the monotonically increasing
+ value normally occur at re-initialization of the
+ management system, and at other times as specified in the
+ description of a schema node using this type. If such
+ other times can occur, for example, the creation of
+ a schema node of type counter32 at times other than
+ re-initialization, then a corresponding schema node
+ should be defined, with an appropriate type, to indicate
+ the last discontinuity.
+
+ The counter32 type should not be used for configuration
+ schema nodes. A default statement SHOULD NOT be used in
+ combination with the type counter32.
+
+ In the value set and its semantics, this type is equivalent
+ to the Counter32 type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef zero-based-counter32 {
+ type yang:counter32;
+ default "0";
+ description
+ "The zero-based-counter32 type represents a counter32
+ that has the defined 'initial' value zero.
+
+ A schema node of this type will be set to zero (0) on creation
+ and will thereafter increase monotonically until it reaches
+ a maximum value of 2^32-1 (4294967295 decimal), when it
+ wraps around and starts increasing again from zero.
+
+ Provided that an application discovers a new schema node
+ of this type within the minimum time to wrap, it can use the
+ 'initial' value as a delta. It is important for a management
+ station to be aware of this minimum time and the actual time
+ between polls, and to discard data if the actual time is too
+ long or there is no defined minimum time.
+
+ In the value set and its semantics, this type is equivalent
+ to the ZeroBasedCounter32 textual convention of the SMIv2.";
+ reference
+ "RFC 4502: Remote Network Monitoring Management Information
+ Base Version 2";
+ }
+
+ typedef counter64 {
+ type uint64;
+ description
+ "The counter64 type represents a non-negative integer
+ that monotonically increases until it reaches a
+ maximum value of 2^64-1 (18446744073709551615 decimal),
+ when it wraps around and starts increasing again from zero.
+
+ Counters have no defined 'initial' value, and thus, a
+ single value of a counter has (in general) no information
+ content. Discontinuities in the monotonically increasing
+ value normally occur at re-initialization of the
+ management system, and at other times as specified in the
+ description of a schema node using this type. If such
+ other times can occur, for example, the creation of
+ a schema node of type counter64 at times other than
+ re-initialization, then a corresponding schema node
+ should be defined, with an appropriate type, to indicate
+ the last discontinuity.
+
+ The counter64 type should not be used for configuration
+ schema nodes. A default statement SHOULD NOT be used in
+ combination with the type counter64.
+
+ In the value set and its semantics, this type is equivalent
+ to the Counter64 type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef zero-based-counter64 {
+ type yang:counter64;
+ default "0";
+ description
+ "The zero-based-counter64 type represents a counter64 that
+ has the defined 'initial' value zero.
+
+ A schema node of this type will be set to zero (0) on creation
+ and will thereafter increase monotonically until it reaches
+ a maximum value of 2^64-1 (18446744073709551615 decimal),
+ when it wraps around and starts increasing again from zero.
+
+ Provided that an application discovers a new schema node
+ of this type within the minimum time to wrap, it can use the
+ 'initial' value as a delta. It is important for a management
+ station to be aware of this minimum time and the actual time
+ between polls, and to discard data if the actual time is too
+ long or there is no defined minimum time.
+
+ In the value set and its semantics, this type is equivalent
+ to the ZeroBasedCounter64 textual convention of the SMIv2.";
+ reference
+ "RFC 2856: Textual Conventions for Additional High Capacity
+ Data Types";
+ }
+
+ typedef gauge32 {
+ type uint32;
+ description
+ "The gauge32 type represents a non-negative integer, which
+ may increase or decrease, but shall never exceed a maximum
+ value, nor fall below a minimum value. The maximum value
+ cannot be greater than 2^32-1 (4294967295 decimal), and
+ the minimum value cannot be smaller than 0. The value of
+ a gauge32 has its maximum value whenever the information
+ being modeled is greater than or equal to its maximum
+ value, and has its minimum value whenever the information
+ being modeled is smaller than or equal to its minimum value.
+ If the information being modeled subsequently decreases
+ below (increases above) the maximum (minimum) value, the
+ gauge32 also decreases (increases).
+
+ In the value set and its semantics, this type is equivalent
+ to the Gauge32 type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef gauge64 {
+ type uint64;
+ description
+ "The gauge64 type represents a non-negative integer, which
+ may increase or decrease, but shall never exceed a maximum
+ value, nor fall below a minimum value. The maximum value
+ cannot be greater than 2^64-1 (18446744073709551615), and
+ the minimum value cannot be smaller than 0. The value of
+ a gauge64 has its maximum value whenever the information
+ being modeled is greater than or equal to its maximum
+ value, and has its minimum value whenever the information
+ being modeled is smaller than or equal to its minimum value.
+ If the information being modeled subsequently decreases
+ below (increases above) the maximum (minimum) value, the
+ gauge64 also decreases (increases).
+
+ In the value set and its semantics, this type is equivalent
+ to the CounterBasedGauge64 SMIv2 textual convention defined
+ in RFC 2856";
+ reference
+ "RFC 2856: Textual Conventions for Additional High Capacity
+ Data Types";
+ }
+
+ /*** collection of identifier-related types ***/
+
+ typedef object-identifier {
+ type string {
+ pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))'
+ + '(\.(0|([1-9]\d*)))*';
+ }
+ description
+ "The object-identifier type represents administratively
+ assigned names in a registration-hierarchical-name tree.
+
+ Values of this type are denoted as a sequence of numerical
+ non-negative sub-identifier values. Each sub-identifier
+ value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers
+ are separated by single dots and without any intermediate
+ whitespace.
+
+ The ASN.1 standard restricts the value space of the first
+ sub-identifier to 0, 1, or 2. Furthermore, the value space
+ of the second sub-identifier is restricted to the range
+ 0 to 39 if the first sub-identifier is 0 or 1. Finally,
+ the ASN.1 standard requires that an object identifier
+ has always at least two sub-identifiers. The pattern
+ captures these restrictions.
+
+ Although the number of sub-identifiers is not limited,
+ module designers should realize that there may be
+ implementations that stick with the SMIv2 limit of 128
+ sub-identifiers.
+
+ This type is a superset of the SMIv2 OBJECT IDENTIFIER type
+ since it is not restricted to 128 sub-identifiers. Hence,
+ this type SHOULD NOT be used to represent the SMIv2 OBJECT
+ IDENTIFIER type; the object-identifier-128 type SHOULD be
+ used instead.";
+ reference
+ "ISO9834-1: Information technology -- Open Systems
+ Interconnection -- Procedures for the operation of OSI
+ Registration Authorities: General procedures and top
+ arcs of the ASN.1 Object Identifier tree";
+ }
+
+ typedef object-identifier-128 {
+ type object-identifier {
+ pattern '\d*(\.\d*){1,127}';
+ }
+ description
+ "This type represents object-identifiers restricted to 128
+ sub-identifiers.
+
+ In the value set and its semantics, this type is equivalent
+ to the OBJECT IDENTIFIER type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef yang-identifier {
+ type string {
+ length "1..max";
+ pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
+ pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*';
+ }
+ description
+ "A YANG identifier string as defined by the 'identifier'
+ rule in Section 12 of RFC 6020. An identifier must
+ start with an alphabetic character or an underscore
+ followed by an arbitrary sequence of alphabetic or
+ numeric characters, underscores, hyphens, or dots.
+
+ A YANG identifier MUST NOT start with any possible
+ combination of the lowercase or uppercase character
+ sequence 'xml'.";
+ reference
+ "RFC 6020: YANG - A Data Modeling Language for the Network
+ Configuration Protocol (NETCONF)";
+ }
+
+ /*** collection of types related to date and time***/
+
+ typedef date-and-time {
+ type string {
+ pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+ + '(Z|[\+\-]\d{2}:\d{2})';
+ }
+ description
+ "The date-and-time type is a profile of the ISO 8601
+ standard for representation of dates and times using the
+ Gregorian calendar. The profile is defined by the
+ date-time production in Section 5.6 of RFC 3339.
+
+ The date-and-time type is compatible with the dateTime XML
+ schema type with the following notable exceptions:
+
+ (a) The date-and-time type does not allow negative years.
+
+ (b) The date-and-time time-offset -00:00 indicates an unknown
+ time zone (see RFC 3339) while -00:00 and +00:00 and Z
+ all represent the same time zone in dateTime.
+
+ (c) The canonical format (see below) of data-and-time values
+ differs from the canonical format used by the dateTime XML
+ schema type, which requires all times to be in UTC using
+ the time-offset 'Z'.
+
+ This type is not equivalent to the DateAndTime textual
+ convention of the SMIv2 since RFC 3339 uses a different
+ separator between full-date and full-time and provides
+ higher resolution of time-secfrac.
+
+ The canonical format for date-and-time values with a known time
+ zone uses a numeric time zone offset that is calculated using
+ the device's configured known offset to UTC time. A change of
+ the device's offset to UTC time will cause date-and-time values
+ to change accordingly. Such changes might happen periodically
+ in case a server follows automatically daylight saving time
+ (DST) time zone offset changes. The canonical format for
+ date-and-time values with an unknown time zone (usually
+ referring to the notion of local time) uses the time-offset
+ -00:00.";
+ reference
+ "RFC 3339: Date and Time on the Internet: Timestamps
+ RFC 2579: Textual Conventions for SMIv2
+ XSD-TYPES: XML Schema Part 2: Datatypes Second Edition";
+ }
+
+ typedef timeticks {
+ type uint32;
+ description
+ "The timeticks type represents a non-negative integer that
+ represents the time, modulo 2^32 (4294967296 decimal), in
+ hundredths of a second between two epochs. When a schema
+ node is defined that uses this type, the description of
+ the schema node identifies both of the reference epochs.
+
+ In the value set and its semantics, this type is equivalent
+ to the TimeTicks type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef timestamp {
+ type yang:timeticks;
+ description
+ "The timestamp type represents the value of an associated
+ timeticks schema node at which a specific occurrence
+ happened. The specific occurrence must be defined in the
+ description of any schema node defined using this type. When
+ the specific occurrence occurred prior to the last time the
+ associated timeticks attribute was zero, then the timestamp
+ value is zero. Note that this requires all timestamp values
+ to be reset to zero when the value of the associated timeticks
+ attribute reaches 497+ days and wraps around to zero.
+
+ The associated timeticks schema node must be specified
+ in the description of any schema node using this type.
+
+ In the value set and its semantics, this type is equivalent
+ to the TimeStamp textual convention of the SMIv2.";
+ reference
+ "RFC 2579: Textual Conventions for SMIv2";
+ }
+
+ /*** collection of generic address types ***/
+
+ typedef phys-address {
+ type string {
+ pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+ }
+
+ description
+ "Represents media- or physical-level addresses represented
+ as a sequence octets, each octet represented by two hexadecimal
+ numbers. Octets are separated by colons. The canonical
+ representation uses lowercase characters.
+
+ In the value set and its semantics, this type is equivalent
+ to the PhysAddress textual convention of the SMIv2.";
+ reference
+ "RFC 2579: Textual Conventions for SMIv2";
+ }
+
+ typedef mac-address {
+ type string {
+ pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
+ }
+ description
+ "The mac-address type represents an IEEE 802 MAC address.
+ The canonical representation uses lowercase characters.
+
+ In the value set and its semantics, this type is equivalent
+ to the MacAddress textual convention of the SMIv2.";
+ reference
+ "IEEE 802: IEEE Standard for Local and Metropolitan Area
+ Networks: Overview and Architecture
+ RFC 2579: Textual Conventions for SMIv2";
+ }
+
+ /*** collection of XML-specific types ***/
+
+ typedef xpath1.0 {
+ type string;
+ description
+ "This type represents an XPATH 1.0 expression.
+
+ When a schema node is defined that uses this type, the
+ description of the schema node MUST specify the XPath
+ context in which the XPath expression is evaluated.";
+ reference
+ "XPATH: XML Path Language (XPath) Version 1.0";
+ }
+
+ /*** collection of string types ***/
+
+ typedef hex-string {
+ type string {
+ pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+ }
+ description
+ "A hexadecimal string with octets represented as hex digits
+ separated by colons. The canonical representation uses
+ lowercase characters.";
+ }
+
+ typedef uuid {
+ type string {
+ pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-'
+ + '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
+ }
+ description
+ "A Universally Unique IDentifier in the string representation
+ defined in RFC 4122. The canonical representation uses
+ lowercase characters.
+
+ The following is an example of a UUID in string representation:
+ f81d4fae-7dec-11d0-a765-00a0c91e6bf6
+ ";
+ reference
+ "RFC 4122: A Universally Unique IDentifier (UUID) URN
+ Namespace";
+ }
+
+ typedef dotted-quad {
+ type string {
+ pattern
+ '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
+ }
+ description
+ "An unsigned 32-bit number expressed in the dotted-quad
+ notation, i.e., four octets written as decimal numbers
+ and separated with the '.' (full stop) character.";
+ }
+}
diff --git a/src/srvpp/CMakeLists.txt b/src/srvpp/CMakeLists.txt
new file mode 100644
index 0000000..20fe6f0
--- /dev/null
+++ b/src/srvpp/CMakeLists.txt
@@ -0,0 +1,40 @@
+#
+# 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.
+#
+
+cmake_minimum_required(VERSION 2.8)
+project(srvpp)
+
+# add subdirectories
+add_subdirectory(src)
+
+# enable testing if requested and possible
+SET(ENABLE_TESTS 1 CACHE BOOL "Enable unit tests.")
+if(ENABLE_TESTS)
+ find_package(CMOCKA)
+ if(CMOCKA_FOUND)
+ MESSAGE(STATUS "CMocka found, tests are enabled.")
+ enable_testing()
+ add_subdirectory(tests)
+ else(CMOCKA_FOUND)
+ MESSAGE(WARNING "CMocka not found, tests are disabled.")
+ endif(CMOCKA_FOUND)
+endif(ENABLE_TESTS)
+
+find_package(PkgConfig QUIET)
+if(PKG_CONFIG_FOUND)
+ # generate and install pkg-config file
+ configure_file("libsrvpp.pc.in" "libsrvpp.pc" @ONLY)
+ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libsrvpp.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+endif()
diff --git a/src/srvpp/libsrvpp.pc.in b/src/srvpp/libsrvpp.pc.in
new file mode 100644
index 0000000..c57c267
--- /dev/null
+++ b/src/srvpp/libsrvpp.pc.in
@@ -0,0 +1,10 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
+
+Name: srvpp
+Description: VPP-Sysrepo integration library
+Version: 0.0.1
+Libs: -L${libdir} -lsrvpp
+Cflags: -I${includedir}
+
diff --git a/src/srvpp/src/CMakeLists.txt b/src/srvpp/src/CMakeLists.txt
new file mode 100644
index 0000000..7fec80d
--- /dev/null
+++ b/src/srvpp/src/CMakeLists.txt
@@ -0,0 +1,44 @@
+#
+# 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(GNUInstallDirs)
+
+# srvpp sources
+set(SRVPP_SOURCES
+ srvpp_logger.c
+ srvpp.c
+)
+
+# srvpp public headers
+set(SRVPP_HEADERS
+ srvpp.h
+)
+
+set(CMAKE_C_FLAGS " -g -O0 -fpic -fPIC -std=gnu99 -Wl,-rpath-link=/usr/lib")
+
+# libraries to link with
+set(LINK_LIBRARIES sysrepo vlibmemoryclient vapiclient vppapiclient svm vppinfra pthread rt dl)
+
+# build instructions
+add_library(srvpp SHARED ${SRVPP_SOURCES})
+add_library(srvpp_a ${SRVPP_SOURCES})
+
+# linker instructions
+target_link_libraries(srvpp ${LINK_LIBRARIES})
+target_link_libraries(srvpp_a ${LINK_LIBRARIES})
+
+# install rules
+install(TARGETS srvpp DESTINATION ${CMAKE_INSTALL_LIBDIR})
+install(FILES ${SRVPP_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
diff --git a/src/srvpp/src/srvpp.c b/src/srvpp/src/srvpp.c
new file mode 100644
index 0000000..9b3467b
--- /dev/null
+++ b/src/srvpp/src/srvpp.c
@@ -0,0 +1,823 @@
+/*
+ * 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 <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+#include "srvpp.h"
+#include "srvpp_logger.h"
+
+#undef vl_api_version
+#define vl_api_version(n,v) static u32 vpe_api_version = v;
+#include <vpp/api/vpe.api.h>
+#undef vl_api_version
+
+
+#define SRVPP_RESPONSE_TIMEOUT 2 /**< Maximum time (in seconds) that a client waits for the response(s) to a request or dumprequest. */
+
+#define CHECK_NULL(ARG) \
+ if (NULL == ARG) { \
+ SRVPP_LOG_ERR("NULL value detected for %s argument of %s", #ARG, __func__); \
+ return -1; \
+ } \
+
+#define CHECK_NULL_RET(ARG, RET) \
+ if (NULL == ARG) { \
+ SRVPP_LOG_ERR("NULL value detected for %s argument of %s", #ARG, __func__); \
+ return RET; \
+ } \
+
+/**
+ * @brief Type of the response expected from VPP.
+ */
+typedef enum srvpp_response_type_e {
+ SRVPP_REPLY, /**< A reply message (single message). */
+ SRVPP_DETAILS, /**< Multiple details messages. */
+} srvpp_response_type_t;
+
+/**
+ * @brief srvpp request context structure.
+ */
+typedef struct srvpp_request_ctx_s {
+ struct srvpp_request_ctx_s *_next; /**< Pointer to the next request context in the linked-list. */
+ u32 ctx_id; /**< Context ID used to map responses with requests. */
+ srvpp_response_type_t resp_type; /**< Type of the response expected from VPP. */
+
+ i32 resp_retval; /**< Return value of the last response (0 = success). */
+ bool resp_data_copy; /**< Controls whether data of the responses shall be copied (returned) or ignored. */
+ void **resp_msg_arr; /**< Array of the pointers to response messages. */
+ size_t *resp_msg_sizes; /**< Array of sizes of messages in the ::resp_msg_arr. */
+ size_t resp_msg_arr_size; /**< Size of the ::resp_msg_arr array. */
+ size_t resp_msg_cnt; /**< Count of the messages currently stored in ::resp_msg_arr array. */
+
+ bool resp_ready; /**< Signals that the expected response has arrived and is ready to be returned. */
+ pthread_cond_t resp_cv; /**< Condition variable for ::resp_ready. */
+ pthread_mutex_t lock; /**< Mutex to protect shared access to the context. */
+} srvpp_request_ctx_t;
+
+/**
+ * @brief srvpp interface info context structure.
+ */
+typedef struct srvpp_if_info_s {
+ struct srvpp_if_info_s *_next;
+ const char *if_name;
+ u32 if_index;
+} srvpp_if_info_t;
+
+/**
+ * @brief srvpp context structure.
+ */
+typedef struct srvpp_ctx_s {
+ size_t ref_cnt; /**< Context reference counter. */
+ unix_shared_memory_queue_t *vlib_input_queue; /**< VPP Library input queue. */
+ u32 vlib_client_index; /**< VPP Library client index. */
+ srvpp_request_ctx_t *request_ctx_list; /**< Linked-list of request contexts. */
+ srvpp_if_info_t *if_info_list; /**< Linked-list of VPP interfaces information. */
+ pthread_key_t request_key; /**< Key to the thread-specific request context. */
+} srvpp_ctx_t;
+
+/**
+ * @brief Generic VPP request structure.
+ */
+typedef struct __attribute__ ((packed)) vl_generic_request_s {
+ unsigned short _vl_msg_id;
+ unsigned int client_index;
+ unsigned int context;
+} vl_generic_request_t;
+
+/**
+ * @brief Generic VPP response structure.
+ */
+typedef struct __attribute__ ((packed)) vl_generic_response_s {
+ u16 _vl_msg_id;
+ u32 context;
+} vl_generic_response_t;
+
+/**
+ * @brief Generic VPP reply structure (response with a single message).
+ */
+typedef struct __attribute__ ((packed)) vl_generic_reply_s {
+ u16 _vl_msg_id;
+ u32 context;
+ i32 retval;
+} vl_generic_reply_t;
+
+/**
+ * @brief Global srvpp context.
+ */
+static srvpp_ctx_t *srvpp_ctx = NULL;
+
+/**
+ * @brief Mutex for the global context.
+ */
+static pthread_mutex_t srvpp_ctx_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * @brief Not used, just to satisfy external references when -lvlib is not available.
+ */
+void
+vlib_cli_output(struct vlib_main_t *vm, char *fmt, ...)
+{
+ SRVPP_LOG_WRN_MSG("vlib_cli_output callled!");
+}
+
+/**
+ * @brief Sets correct VPP API version.
+ */
+void
+vl_client_add_api_signatures(vl_api_memclnt_create_t *mp)
+{
+ /*
+ * Send the main API signature in slot 0. This bit of code must
+ * match the checks in ../vpe/api/api.c: vl_msg_api_version_check().
+ */
+ mp->api_versions[0] = clib_host_to_net_u32(vpe_api_version);
+}
+
+/**
+ * @brief Returns the request context assigned to the active thread.
+ * If no request context exists for the thread, it will be automatically created.
+ */
+static srvpp_request_ctx_t *
+srvpp_get_thread_request_ctx(srvpp_ctx_t *ctx)
+{
+ srvpp_request_ctx_t *req_ctx = NULL;
+
+ CHECK_NULL_RET(ctx, NULL);
+
+ if (NULL == (req_ctx = pthread_getspecific(ctx->request_key))) {
+ /* allocate a new request context */
+ req_ctx = calloc(1, sizeof(*req_ctx));
+ if (NULL != req_ctx) {
+ /* initialize the new context */
+ req_ctx->ctx_id = (u32)(((uintptr_t)req_ctx) & 0xFFFFFFFF) << 16;
+ SRVPP_LOG_DBG("Creating new request ctx with id=%u", req_ctx->ctx_id);
+
+ pthread_mutex_init(&req_ctx->lock, NULL);
+ pthread_cond_init (&req_ctx->resp_cv, NULL);
+
+ /* save the request ctx in the srvpp context */
+ pthread_mutex_lock(&srvpp_ctx_lock);
+ req_ctx->_next = ctx->request_ctx_list;
+ ctx->request_ctx_list = req_ctx;
+ pthread_mutex_unlock(&srvpp_ctx_lock);
+
+ /* save the request ctx in the thread-local memory */
+ pthread_setspecific(ctx->request_key, req_ctx);
+ } else {
+ SRVPP_LOG_ERR_MSG("Unable to allocate new request context.");
+ }
+ }
+
+ return req_ctx;
+}
+
+/**
+ * @brief Returns the request context matching with the provided context id.
+ */
+static srvpp_request_ctx_t *
+srvpp_get_request_ctx(srvpp_ctx_t *ctx, u32 req_ctx_id)
+{
+ srvpp_request_ctx_t *req_ctx = NULL, *match = NULL;
+
+ CHECK_NULL_RET(ctx, NULL);
+
+ pthread_mutex_lock(&srvpp_ctx_lock);
+
+ req_ctx = ctx->request_ctx_list;
+
+ while (NULL != req_ctx) {
+ if (req_ctx->ctx_id == req_ctx_id) {
+ match = req_ctx;
+ break;
+ }
+ req_ctx = req_ctx->_next;
+ }
+
+ pthread_mutex_unlock(&srvpp_ctx_lock);
+
+ return match;
+}
+
+/**
+ * @brief Copy data of the message into the provided request context.
+ */
+static int
+srvpp_msg_data_copy(srvpp_request_ctx_t *req_ctx, void *msg, size_t msg_size)
+{
+ void **msg_arr = NULL;
+ size_t *sizes_arr = NULL;
+ void *msg_space = NULL;
+
+ CHECK_NULL(req_ctx);
+
+ if (req_ctx->resp_msg_arr_size < (req_ctx->resp_msg_cnt + 1)) {
+ /* reallocate arrays to fit one new message */
+ msg_arr = realloc(req_ctx->resp_msg_arr, (req_ctx->resp_msg_cnt + 1) * sizeof(*req_ctx->resp_msg_arr));
+ if (NULL == msg_arr) {
+ SRVPP_LOG_ERR_MSG("Unable to reallocate message array.");
+ return -1;
+ }
+ sizes_arr = realloc(req_ctx->resp_msg_sizes, (req_ctx->resp_msg_cnt + 1) * sizeof(*req_ctx->resp_msg_sizes));
+ if (NULL == sizes_arr) {
+ SRVPP_LOG_ERR_MSG("Unable to reallocate message sizes array.");
+ return -1;
+ }
+ req_ctx->resp_msg_arr = msg_arr;
+ req_ctx->resp_msg_sizes = sizes_arr;
+ req_ctx->resp_msg_arr_size = req_ctx->resp_msg_cnt + 1;
+
+ req_ctx->resp_msg_arr[req_ctx->resp_msg_cnt] = NULL;
+ req_ctx->resp_msg_sizes[req_ctx->resp_msg_cnt] = 0;
+ }
+
+ if (req_ctx->resp_msg_sizes[req_ctx->resp_msg_cnt] < msg_size) {
+ /* reallocate space for the message */
+ msg_space = realloc(req_ctx->resp_msg_arr[req_ctx->resp_msg_cnt], msg_size);
+ if (NULL == msg_space) {
+ SRVPP_LOG_ERR_MSG("Unable to reallocate message space.");
+ return -1;
+ }
+ req_ctx->resp_msg_arr[req_ctx->resp_msg_cnt] = msg_space;
+ req_ctx->resp_msg_sizes[req_ctx->resp_msg_cnt] = msg_size;
+ }
+
+ /* copy the message content */
+ memcpy(req_ctx->resp_msg_arr[req_ctx->resp_msg_cnt], msg, msg_size);
+
+ req_ctx->resp_msg_cnt++;
+
+ return 0;
+}
+
+/**
+ * @brief Processes a reply to a single VPP request.
+ */
+static int
+srvpp_process_reply_msg(srvpp_request_ctx_t *req_ctx, u16 msg_id, void *msg, size_t msg_size)
+{
+ vl_generic_reply_t *reply = NULL;
+ int rc = 0;
+
+ CHECK_NULL(req_ctx);
+
+ reply = (vl_generic_reply_t *) msg;
+
+ if (req_ctx->ctx_id != reply->context) {
+ SRVPP_LOG_ERR_MSG("Invalid request context for provided message, ignoring the message.");
+ return -1;
+ }
+
+ if (req_ctx->resp_data_copy) {
+ /* copy msg data into req_ctx */
+ rc = srvpp_msg_data_copy(req_ctx, msg, msg_size);
+ }
+
+ if (0 == rc) {
+ req_ctx->resp_retval = reply->retval;
+ } else {
+ req_ctx->resp_retval = rc;
+ }
+
+ /* signal the requesting thread */
+ req_ctx->resp_ready = true;
+ pthread_cond_signal(&req_ctx->resp_cv);
+
+ return rc;
+}
+
+/**
+ * @brief Processes a reply to a dump request to VPP (response consisting of multiple messages).
+ */
+static int
+srvpp_process_details_msg(srvpp_request_ctx_t *req_ctx, u16 msg_id, void *msg, size_t msg_size)
+{
+ vl_generic_response_t *response = NULL;
+ int rc = 0;
+
+ CHECK_NULL(req_ctx);
+
+ response = (vl_generic_response_t *) msg;
+
+ if (req_ctx->ctx_id != response->context) {
+ SRVPP_LOG_ERR_MSG("Invalid request context for provided message, ignoring the message.");
+ return -1;
+ }
+
+ if (VL_API_CONTROL_PING_REPLY != msg_id) {
+ /* details message - copy message data into req contex*/
+ rc = srvpp_msg_data_copy(req_ctx, msg, msg_size);
+ if (0 != rc && 0 == req_ctx->resp_retval) {
+ /* in case of error, propagate it to the req context */
+ req_ctx->resp_retval = rc;
+ }
+ } else {
+ /* control ping reply, signal the requesting thread */
+ req_ctx->resp_ready = true;
+ pthread_cond_signal(&req_ctx->resp_cv);
+ }
+
+ return req_ctx->resp_retval;
+}
+
+/**
+ * @brief Internal callback automatically called by VPP library when a message
+ * from VPP is received.
+ */
+void
+_srvpp_receive_msg_handler(void *msg)
+{
+ srvpp_request_ctx_t *req_ctx = NULL;
+ vl_generic_response_t *response = NULL;
+ msgbuf_t *msg_header = NULL;
+ size_t msg_size = 0;
+ u16 msg_id = 0;
+
+ if (NULL == msg) {
+ SRVPP_LOG_WRN_MSG("NULL message received, ignoring.");
+ return;
+ }
+
+ response = (vl_generic_response_t *) msg;
+
+ /* get message details */
+ msg_header = (msgbuf_t *) (((u8 *) msg) - offsetof(msgbuf_t, data));
+ msg_size = ntohl(msg_header->data_len);
+ msg_id = ntohs(*((u16 *)msg));
+
+ SRVPP_LOG_DBG("New message received from VPP (id=%d, size=%zu).", msg_id, msg_size);
+
+ /* get request context matching with context id */
+ req_ctx = srvpp_get_request_ctx(srvpp_ctx, response->context);
+ if (NULL == req_ctx) {
+ SRVPP_LOG_WRN("Unexpected context id=%d within the received message, ignoring.", response->context);
+ return;
+ }
+
+ pthread_mutex_lock(&req_ctx->lock);
+
+ if (SRVPP_REPLY == req_ctx->resp_type) {
+ srvpp_process_reply_msg(req_ctx, msg_id, msg, msg_size);
+ } else {
+ srvpp_process_details_msg(req_ctx, msg_id, msg, msg_size);
+ }
+
+ pthread_mutex_unlock(&req_ctx->lock);
+}
+
+/**
+ * @brief Blocks the thread until a response from VPP comes or until a timeout expires.
+ */
+static int
+srvpp_wait_response(srvpp_request_ctx_t *req_ctx)
+{
+ struct timespec ts = { 0, };
+ int retval = 0, rc = 0;
+
+ CHECK_NULL(req_ctx);
+
+ pthread_mutex_lock(&req_ctx->lock);
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += SRVPP_RESPONSE_TIMEOUT;
+
+ while (!req_ctx->resp_ready && (0 == rc)) {
+ rc = pthread_cond_timedwait(&req_ctx->resp_cv, &req_ctx->lock, &ts);
+ }
+ if (0 == rc) {
+ SRVPP_LOG_DBG("Received the response from VPP, retval=%d", req_ctx->resp_retval);
+ retval = req_ctx->resp_retval;
+ } else {
+ SRVPP_LOG_ERR("Response not received from VPP within the timeout period (%d sec).", SRVPP_RESPONSE_TIMEOUT);
+ retval = -1;
+ }
+
+ /* invalidate previous context id */
+ ++req_ctx->ctx_id;
+
+ pthread_mutex_unlock(&req_ctx->lock);
+
+ return retval;
+}
+
+/**
+ * @brief Connects to VPP.
+ */
+static int
+srvpp_vlib_connect(srvpp_ctx_t *ctx)
+{
+ api_main_t *am = &api_main;
+ int rc = 0;
+
+ CHECK_NULL(ctx);
+
+ SRVPP_LOG_DBG_MSG("Connecting to VPP...");
+
+ rc = vl_client_connect_to_vlib("/vpe-api", "srvpp", 32);
+
+ if (rc < 0) {
+ SRVPP_LOG_ERR("Unable to connect to VPP, rc=%d.", rc);
+ } else {
+ SRVPP_LOG_DBG("Connection to VPP established, client index=%d.", am->my_client_index);
+ ctx->vlib_client_index = am->my_client_index;
+ ctx->vlib_input_queue = am->shmem_hdr->vl_input_queue;
+ }
+
+ return rc;
+}
+
+/**
+ * @brief Disconnects from VPP.
+ */
+static void
+srvpp_vlib_disconnect()
+{
+ vl_client_disconnect_from_vlib();
+}
+
+/**
+ * @brief Adds a new interface into interfaces list.
+ */
+static int
+srvpp_if_info_add(srvpp_ctx_t *ctx, const char *if_name, u32 if_index)
+{
+ srvpp_if_info_t *tmp = NULL, *if_info = NULL;
+
+ SRVPP_LOG_DBG("Adding interface '%s', id=%d", if_name, if_index);
+
+ if_info = calloc(1, sizeof(*if_info));
+ if (NULL == if_info) {
+ return 1;
+ }
+
+ if_info->if_name = strdup(if_name);
+ if (NULL == if_info->if_name) {
+ return 1;
+ }
+
+ if_info->if_index = if_index;
+
+ if (NULL == ctx->if_info_list) {
+ ctx->if_info_list = if_info;
+ } else {
+ tmp = ctx->if_info_list;
+ while (NULL != tmp->_next) {
+ tmp = tmp->_next;
+ }
+ tmp->_next = if_info;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Loads VPP interfaces information.
+ */
+static int
+srvpp_if_info_load(srvpp_ctx_t *ctx)
+{
+ vl_api_sw_interface_dump_t *if_dump_req = NULL;
+ vl_api_sw_interface_details_t *if_details = NULL;
+ void **details = NULL;
+ size_t details_cnt = 0;
+ int ret = 0;
+
+ SRVPP_LOG_DBG_MSG("Loading VPP interfaces information");
+
+ /* dump interfaces */
+ if_dump_req = srvpp_alloc_msg(VL_API_SW_INTERFACE_DUMP, sizeof(*if_dump_req));
+
+ ret = srvpp_send_dumprequest(ctx, if_dump_req, &details, &details_cnt);
+ if (0 != ret) {
+ return ret;
+ }
+
+ pthread_mutex_lock(&srvpp_ctx_lock);
+
+ for (size_t i = 0; i < details_cnt; i++) {
+ if_details = (vl_api_sw_interface_details_t *) details[i];
+ ret = srvpp_if_info_add(ctx, (char*)if_details->interface_name, ntohl(if_details->sw_if_index));
+ }
+
+ pthread_mutex_unlock(&srvpp_ctx_lock);
+
+ return 0;
+}
+
+/**
+ * @brief Cleans up VPP interfaces information.
+ */
+static void
+srvpp_if_info_cleanup(srvpp_ctx_t *ctx)
+{
+ srvpp_if_info_t *tmp = NULL, *if_info = NULL;
+
+ if (NULL != ctx) {
+ if_info = ctx->if_info_list;
+
+ while (NULL != if_info) {
+ tmp = if_info;
+ if_info = if_info->_next;
+ free((void*)tmp->if_name);
+ free(tmp);
+ }
+ }
+}
+
+/**
+ * @brief Initializes the srvpp context.
+ */
+srvpp_ctx_t *
+srvpp_ctx_init()
+{
+ srvpp_ctx_t *ctx = NULL;
+ int rc = 0;
+
+ srvpp_logger_init();
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (NULL == ctx) {
+ SRVPP_LOG_ERR_MSG("Unable to allocate srvpp context.");
+ return NULL;
+ }
+
+ rc = srvpp_vlib_connect(ctx);
+ if (0 != rc) {
+ SRVPP_LOG_ERR_MSG("Unable to initialize srvpp context.");
+ free(ctx);
+ return NULL;
+ }
+
+ while (pthread_key_create(&ctx->request_key, NULL) == EAGAIN);
+ pthread_setspecific(ctx->request_key, NULL);
+
+ ctx->ref_cnt = 1;
+
+ SRVPP_LOG_INF_MSG("srvpp context initialized successfully.");
+
+ return ctx;
+}
+
+/**
+ * @brief Cleans up the srvpp context.
+ */
+static void
+srvpp_ctx_cleanup(srvpp_ctx_t *ctx)
+{
+ srvpp_request_ctx_t *tmp = NULL, *req_ctx = NULL;
+
+ if (NULL != ctx) {
+ srvpp_vlib_disconnect();
+
+ srvpp_if_info_cleanup(ctx);
+
+ req_ctx = ctx->request_ctx_list;
+ while (NULL != req_ctx) {
+ tmp = req_ctx;
+ req_ctx = req_ctx->_next;
+
+ pthread_mutex_destroy(&tmp->lock);
+ pthread_cond_destroy(&tmp->resp_cv);
+
+ for (size_t i = 0; i < tmp->resp_msg_arr_size; i++) {
+ free(tmp->resp_msg_arr[i]);
+ }
+ free(tmp->resp_msg_arr);
+ free(tmp->resp_msg_sizes);
+
+ free(tmp);
+ }
+ free(ctx);
+
+ srvpp_logger_cleanup();
+
+ SRVPP_LOG_INF_MSG("srvpp context cleaned up successfully.");
+ }
+}
+
+srvpp_ctx_t *
+srvpp_get_ctx()
+{
+ bool setup_handlers = false;
+
+ pthread_mutex_lock(&srvpp_ctx_lock);
+
+ if (NULL == srvpp_ctx) {
+ /* initialize a new context */
+ SRVPP_LOG_DBG_MSG("Initializing a new srvpp context.");
+ srvpp_ctx = srvpp_ctx_init();
+ setup_handlers = true;
+ } else {
+ /* increment ref. count */
+ srvpp_ctx->ref_cnt++;
+ SRVPP_LOG_DBG("Reusing existing srvpp context, new refcount=%zu.", srvpp_ctx->ref_cnt);
+ }
+
+ pthread_mutex_unlock(&srvpp_ctx_lock);
+
+ if (setup_handlers) {
+ /* setup required handlers */
+ srvpp_setup_handler(CONTROL_PING_REPLY, control_ping_reply);
+ srvpp_setup_handler(SW_INTERFACE_DETAILS, sw_interface_details);
+
+ /* load VPP interfaces information */
+ srvpp_if_info_load(srvpp_ctx);
+ }
+
+ return srvpp_ctx;
+}
+
+void
+srvpp_release_ctx(srvpp_ctx_t *ctx)
+{
+ pthread_mutex_lock(&srvpp_ctx_lock);
+
+ if (ctx != srvpp_ctx) {
+ SRVPP_LOG_ERR_MSG("Invalid srvpp context passed in, unable to release.");
+ pthread_mutex_unlock(&srvpp_ctx_lock);
+ return;
+ }
+
+ if (srvpp_ctx->ref_cnt > 1) {
+ /* there are still some other references */
+ srvpp_ctx->ref_cnt--;
+ SRVPP_LOG_DBG("Releasing a reference to srvpp context, new refcount=%zu.", srvpp_ctx->ref_cnt);
+ } else {
+ /* last reference - cleanup */
+ SRVPP_LOG_DBG_MSG("Releasing srvpp context (last ctx reference).");
+ srvpp_ctx_cleanup(srvpp_ctx);
+ srvpp_ctx = NULL;
+ }
+
+ pthread_mutex_unlock(&srvpp_ctx_lock);
+}
+
+void *
+srvpp_alloc_msg(uint16_t msg_id, size_t msg_size)
+{
+ vl_generic_request_t *req = NULL;
+
+ req = vl_msg_api_alloc(msg_size);
+ if (NULL != req) {
+ memset(req, 0, msg_size);
+ req->_vl_msg_id = ntohs(msg_id);
+ }
+
+ return req;
+}
+
+int
+srvpp_send_request(srvpp_ctx_t *ctx, void *request, void **response)
+{
+ vl_generic_request_t *req = NULL;
+ srvpp_request_ctx_t *req_ctx = NULL;
+ int retval = 0;
+
+ CHECK_NULL(ctx);
+ CHECK_NULL(request);
+
+ req_ctx = srvpp_get_thread_request_ctx(ctx);
+ if (NULL == req_ctx) {
+ SRVPP_LOG_ERR_MSG("Unable to obtain a request context.");
+ return -1;
+ }
+
+ req = (vl_generic_request_t *) request;
+ req->client_index = ctx->vlib_client_index;
+
+ pthread_mutex_lock(&req_ctx->lock);
+
+ if (NULL != response) {
+ /* response data is requested */
+ req_ctx->resp_data_copy = true;
+ req_ctx->resp_msg_cnt = 0;
+ } else {
+ /* not interested in response data */
+ req_ctx->resp_data_copy = false;
+ }
+
+ req_ctx->resp_type = SRVPP_REPLY;
+ req_ctx->resp_ready = false;
+ req_ctx->resp_retval = 0;
+ req->context = ++req_ctx->ctx_id;
+
+ pthread_mutex_unlock(&req_ctx->lock);
+
+ SRVPP_LOG_DBG_MSG("Sending a request to VPP.");
+
+ vl_msg_api_send_shmem(ctx->vlib_input_queue, (u8*)&req);
+
+ /* wait for expected response */
+ retval = srvpp_wait_response(req_ctx);
+
+ if (0 == retval) {
+ if (NULL != response && req_ctx->resp_msg_cnt > 0) {
+ *response = req_ctx->resp_msg_arr[0];
+ }
+ SRVPP_LOG_DBG_MSG("VPP request successfully processed.");
+ } else {
+ SRVPP_LOG_ERR_MSG("Error by handling of a VPP request.");
+ }
+
+ return retval;
+}
+
+int
+srvpp_send_dumprequest(srvpp_ctx_t *ctx, void *request, void ***response_arr, size_t *response_cnt)
+{
+ vl_generic_request_t *req = NULL;
+ srvpp_request_ctx_t *req_ctx = NULL;
+ vl_api_control_ping_t *ping = NULL;
+ int retval = 0;
+
+ CHECK_NULL(ctx);
+ CHECK_NULL(request);
+ CHECK_NULL(response_arr);
+ CHECK_NULL(response_cnt);
+
+ req_ctx = srvpp_get_thread_request_ctx(ctx);
+ if (NULL == req_ctx) {
+ SRVPP_LOG_ERR_MSG("Unable to obtain a request context.");
+ return -1;
+ }
+
+ req = (vl_generic_request_t *) request;
+ req->client_index = ctx->vlib_client_index;
+
+ /* allocate control ping request */
+ ping = srvpp_alloc_msg(VL_API_CONTROL_PING, sizeof(*ping));
+ if (NULL == ping) {
+ SRVPP_LOG_ERR_MSG("Unable to allocate control ping message.");
+ return -1;
+ }
+ ping->client_index = ctx->vlib_client_index;
+
+ pthread_mutex_lock(&req_ctx->lock);
+
+ req_ctx->resp_data_copy = true;
+ req_ctx->resp_msg_cnt = 0;
+
+ req_ctx->resp_type = SRVPP_DETAILS;
+ req_ctx->resp_ready = false;
+ req_ctx->resp_retval = 0;
+
+ req_ctx->ctx_id++;
+ req->context = req_ctx->ctx_id;
+ ping->context = req_ctx->ctx_id;
+
+ pthread_mutex_unlock(&req_ctx->lock);
+
+ SRVPP_LOG_DBG_MSG("Sending a dumprequest to VPP.");
+
+ vl_msg_api_send_shmem(ctx->vlib_input_queue, (u8*)&req);
+
+ vl_msg_api_send_shmem(ctx->vlib_input_queue, (u8*)&ping);
+
+ /* wait for expected response */
+ retval = srvpp_wait_response(req_ctx);
+
+ if (0 == retval) {
+ *response_arr = req_ctx->resp_msg_arr;
+ *response_cnt = req_ctx->resp_msg_cnt;
+ SRVPP_LOG_DBG_MSG("VPP dumprequest successfully processed.");
+ } else {
+ SRVPP_LOG_ERR_MSG("Error by handling of a VPP dumprequest.");
+ }
+
+ return retval;
+}
+
+int
+srvpp_get_if_index(srvpp_ctx_t *ctx, const char *if_name, uint32_t *if_index)
+{
+ CHECK_NULL(if_name);
+ CHECK_NULL(if_index);
+
+ srvpp_if_info_t *if_info = NULL;
+
+ if_info = ctx->if_info_list;
+
+ while ((NULL != if_info) && (NULL != if_info->if_name)) {
+ if (0 == strcmp(if_info->if_name, if_name)) {
+ *if_index = if_info->if_index;
+ return 0;
+ }
+ if_info = if_info->_next;
+ }
+
+ return 1;
+}
diff --git a/src/srvpp/src/srvpp.h b/src/srvpp/src/srvpp.h
new file mode 100644
index 0000000..4830d7b
--- /dev/null
+++ b/src/srvpp/src/srvpp.h
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#ifndef INC_SRVPP_H_
+#define INC_SRVPP_H_
+
+/**
+ * @defgroup srvpp Sysrepo-VPP Integration Library
+ * @{
+ *
+ * @brief Provides synchronous interface to VPP binary API aimed primarily for
+ * the integration of VPP with Sysrepo datastore.
+ *
+ * The library is thread-safe - can be used to communicate with VPP from multiple
+ * threads simultaneously.
+ */
+
+#include <stddef.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vapi/vpe.api.vapi.h>
+
+#include <vpp/api/vpe_msg_enum.h>
+
+#define vl_typedefs /* define message structures */
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...)
+#define vl_printfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_printfun
+
+/**
+ * @brief Sysrepo - VPP interface context.
+ */
+typedef struct srvpp_ctx_s srvpp_ctx_t;
+
+/**
+ * @brief srvpp logger severity levels.
+ */
+typedef enum srvpp_log_level_e {
+ SRVPP_NONE, /**< Do not print any messages. */
+ SRVPP_ERR, /**< Print only error messages. */
+ SRVPP_WRN, /**< Print error and warning messages. */
+ SRVPP_INF, /**< Besides errors and warnings, print some other informational messages. */
+ SRVPP_DBG, /**< Print all messages including some development debug messages. */
+} srvpp_log_level_t;
+
+/**
+ * @brief Sets callback that will be called when a log entry would be populated.
+ *
+ * @param[in] level Severity level of the log entry.
+ * @param[in] message Message of the log entry.
+ */
+typedef void (*srvpp_log_cb)(srvpp_log_level_t level, const char *message);
+
+/**
+ * @brief Returns a reference to the global srvpp context. If the global context
+ * does not exists yet, it will be automatically created (a connection to VPP
+ * will be established).
+ *
+ * @note The caller is supposed to call ::srvpp_release_ctx after it finishes
+ * the work with VPP.
+ *
+ * @return srvpp context to be used for subsequent API calls.
+ */
+srvpp_ctx_t* srvpp_get_ctx();
+
+/**
+ * @brief Releases a reference to the global srvpp context. If this is the last
+ * reference, the context will be released (the connection to the VPP will be closed).
+ *
+ * @param[in] ctx srvpp context acquired using ::srvpp_get_ctx call.
+ */
+void srvpp_release_ctx(srvpp_ctx_t *ctx);
+
+/**
+ * @brief Allocates a VPP API message of specified type and size.
+ *
+ * @param[in] msg_id Message ID.
+ * @param[in] msg_size Size of the message.
+ *
+ * @return Space allocated for the message.
+ */
+void* srvpp_alloc_msg(uint16_t msg_id, size_t msg_size);
+
+/**
+ * @brief Sends a simple request to VPP and receive the response.
+ *
+ * @note Subsequent ::srvpp_send_request or ::srvpp_send_dumprequest calls from the same
+ * thread may overwrite the content of ::response, therefore it is not safe to call
+ * them until the response is fully consumed / processed by the caller.
+ *
+ * @param[in] ctx srvpp context acquired using ::srvpp_get_ctx call.
+ * @param[in] request Message with the request to be sent to VPP.
+ * @param[out] response (optional) Response to the request received from VPP.
+ * Caller must not free it - to save memory allocations, response is always
+ * placed into the same thread-local buffer owned by the library.
+ *
+ * @return 0 on success, non-zero in case of error.
+ */
+int srvpp_send_request(srvpp_ctx_t *ctx, void *request, void **response);
+
+/**
+ * @brief Sends a dump request to VPP and receives all responses with details.
+ *
+ * @note Subsequent ::srvpp_send_request or ::srvpp_send_dumprequest calls from the same
+ * thread may overwrite the content of ::response_arr, therefore it is not safe to call
+ * them until the response array is fully consumed / processed by the caller.
+ *
+ * @param[in] ctx srvpp context acquired using ::srvpp_get_ctx call.
+ * @param[in] request Message with the request to be sent to VPP.
+ * @param[out] response_arr Array of responses to the request received from VPP.
+ * Caller must not free it - to save memory allocations, response_arr is always
+ * placed into the same thread-local buffer owned by the library.
+ * @param[out] response_cnt Count of the response messages in the response_arr array.
+ *
+ * @return 0 on success, non-zero in case of error.
+ */
+int srvpp_send_dumprequest(srvpp_ctx_t *ctx, void *request, void ***response_arr, size_t *response_cnt);
+
+/**
+ * @brief Get interface index for provided interface name.
+ *
+ * @param[in] ctx srvpp context acquired using ::srvpp_get_ctx call.
+ * @param[in] if_name Name of an existing VPP interface.
+ * @param[out] if_index Index of the interface.
+ *
+ * @return 0 on success, non-zero in case of error.
+ */
+int srvpp_get_if_index(srvpp_ctx_t *ctx, const char *if_name, uint32_t *if_index);
+
+/**
+ * @brief Sets callback that will be called when a log entry would be populated.
+ * Callback will be called for each message with any log level.
+ *
+ * @param[in] log_callback Callback to be called when a log entry would populated.
+ */
+void srvpp_set_log_cb(srvpp_log_cb log_callback);
+
+/**
+ * @brief Setups message handler to provided VPP API call. Needs to be called
+ * for each VPP API function that can arrive as response from VPP.
+ *
+ * @param[in] ID VPP API function ID.
+ * @param[in] NAME VPP API function name.
+ */
+#define srvpp_setup_handler(ID, NAME) \
+ do { \
+ vl_msg_api_set_handlers(VL_API_##ID, #NAME, \
+ (void*)_srvpp_receive_msg_handler, \
+ (void*)vl_noop_handler, \
+ (void*)vl_api_##NAME##_t_endian, \
+ (void*)vl_api_##NAME##_t_print, \
+ sizeof(vl_api_##NAME##_t), 1); \
+ } while(0)
+
+/**
+ * @brief Internal callback automatically called by VPP library when a message
+ * from VPP is received.
+ */
+void _srvpp_receive_msg_handler(void *message);
+
+/**@} srvpp */
+
+#endif /* INC_SRVPP_H_ */
diff --git a/src/srvpp/src/srvpp_logger.c b/src/srvpp/src/srvpp_logger.c
new file mode 100644
index 0000000..ee03a4b
--- /dev/null
+++ b/src/srvpp/src/srvpp_logger.c
@@ -0,0 +1,83 @@
+/*
+ * 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 <stdlib.h>
+#include <pthread.h>
+
+#include "srvpp_logger.h"
+
+#define MAX_LOG_MSG_SIZE 2048 /**< Maximum size of one log entry. */
+
+volatile srvpp_log_cb srvpp_log_callback = NULL; /**< Global variable used to store logging callback, if set. */
+
+static pthread_once_t srvpp_log_buff_create_key_once = PTHREAD_ONCE_INIT; /** Used to control that ::srvpp_log_buff_create_key is called only once per thread. */
+static pthread_key_t srvpp_log_buff_key; /**< Key for thread-specific buffer data. */
+
+/**
+ * @brief Create key for thread-specific buffer data. Should be called only once per thread.
+ */
+static void
+srvpp_log_buff_create_key(void)
+{
+ while (pthread_key_create(&srvpp_log_buff_key, free) == EAGAIN);
+ pthread_setspecific(srvpp_log_buff_key, NULL);
+}
+
+void
+srvpp_logger_init()
+{
+ /* no init needed */
+}
+
+void
+srvpp_logger_cleanup()
+{
+ /* since the thread-specific data for the main thread seems to be not auto-freed,
+ * (at least on Linux), we explicitly release it here for the thread from which
+ * sr_logger_cleanup has been called (which should be always the main thread). */
+ pthread_once(&srvpp_log_buff_create_key_once, srvpp_log_buff_create_key);
+ char *msg_buff = pthread_getspecific(srvpp_log_buff_key);
+ if (NULL != msg_buff) {
+ free(msg_buff);
+ pthread_setspecific(srvpp_log_buff_key, NULL);
+ }
+}
+
+void
+srvpp_log_to_cb(srvpp_log_level_t level, const char *format, ...)
+{
+ char *msg_buff = NULL;
+ va_list arg_list;
+
+ if (NULL != srvpp_log_callback) {
+ /* get thread-local message buffer */
+ pthread_once(&srvpp_log_buff_create_key_once, srvpp_log_buff_create_key);
+ msg_buff = pthread_getspecific(srvpp_log_buff_key);
+ if (NULL == msg_buff) {
+ msg_buff = calloc(MAX_LOG_MSG_SIZE, sizeof(*msg_buff));
+ pthread_setspecific(srvpp_log_buff_key, msg_buff);
+ }
+ /* print the message into buffer and call callback */
+ if (NULL != msg_buff) {
+ va_start(arg_list, format);
+ vsnprintf(msg_buff, MAX_LOG_MSG_SIZE - 1, format, arg_list);
+ va_end(arg_list);
+ msg_buff[MAX_LOG_MSG_SIZE - 1] = '\0';
+ /* call the callback */
+ srvpp_log_callback(level, msg_buff);
+ }
+ }
+}
+
diff --git a/src/srvpp/src/srvpp_logger.h b/src/srvpp/src/srvpp_logger.h
new file mode 100644
index 0000000..3a1c947
--- /dev/null
+++ b/src/srvpp/src/srvpp_logger.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#ifndef SRC_SRVPP_LOGGER_H_
+#define SRC_SRVPP_LOGGER_H_
+
+#include "srvpp.h"
+
+/**
+ * @brief Pointer to logging callback, if set.
+ */
+extern volatile srvpp_log_cb srvpp_log_callback;
+
+/**
+ * @brief Returns the string representing the specified log level.
+ */
+#define _SRVPP_LL_STR(LL) \
+ ((SRVPP_ERR == LL) ? "ERR" : \
+ (SRVPP_WRN == LL) ? "WRN" : \
+ (SRVPP_INF == LL) ? "INF" : \
+ (SRVPP_DBG == LL) ? "DBG" : \
+ "")
+
+/**
+ * @brief Internal logging macro for formatted messages.
+ */
+#ifdef NDEBUG
+#define _SRVPP_LOG_FMT(LL, MSG, ...) \
+ do { \
+ if (NULL != srvpp_log_callback) { \
+ srvpp_log_to_cb(LL, MSG, __VA_ARGS__); \
+ } else { \
+ fprintf(stderr, "[%s] " MSG "\n", _SRVPP_LL_STR(LL), __VA_ARGS__); \
+ } \
+} while(0)
+#else
+#define _SRVPP_LOG_FMT(LL, MSG, ...) \
+ do { \
+ if (NULL != srvpp_log_callback) { \
+ srvpp_log_to_cb(LL, "(%s:%d) " MSG, __func__, __LINE__, __VA_ARGS__); \
+ } else { \
+ fprintf(stderr, "[%s] (%s:%d) " MSG "\n", _SRVPP_LL_STR(LL), __func__, __LINE__, __VA_ARGS__); \
+ } \
+} while(0)
+#endif
+
+/* Logging macros for formatted messages. */
+#define SRVPP_LOG_ERR(MSG, ...) _SRVPP_LOG_FMT(SRVPP_ERR, MSG, __VA_ARGS__);
+#define SRVPP_LOG_WRN(MSG, ...) _SRVPP_LOG_FMT(SRVPP_WRN, MSG, __VA_ARGS__);
+#define SRVPP_LOG_INF(MSG, ...) _SRVPP_LOG_FMT(SRVPP_INF, MSG, __VA_ARGS__);
+#define SRVPP_LOG_DBG(MSG, ...) _SRVPP_LOG_FMT(SRVPP_DBG, MSG, __VA_ARGS__);
+
+/* Logging macros for unformatted messages. */
+#define SRVPP_LOG_ERR_MSG(MSG) _SRVPP_LOG_FMT(SRVPP_ERR, MSG "%s", "");
+#define SRVPP_LOG_WRN_MSG(MSG) _SRVPP_LOG_FMT(SRVPP_WRN, MSG "%s", "");
+#define SRVPP_LOG_INF_MSG(MSG) _SRVPP_LOG_FMT(SRVPP_INF, MSG "%s", "");
+#define SRVPP_LOG_DBG_MSG(MSG) _SRVPP_LOG_FMT(SRVPP_DBG, MSG "%s", "");
+
+/**
+ * @brief Initializes logger.
+ */
+void srvpp_logger_init();
+
+/**
+ * @brief Cleans up resources used by he logger.
+ */
+void srvpp_logger_cleanup();
+
+/**
+ * @brief Logs into the callback pre-specified by ::srvpp_set_log_cb.
+ *
+ * @param[in] level Log level.
+ * @param[in] format Format message.
+ */
+void srvpp_log_to_cb(srvpp_log_level_t level, const char *format, ...);
+
+#endif /* SRC_SRVPP_LOGGER_H_ */
diff --git a/src/srvpp/tests/CMakeLists.txt b/src/srvpp/tests/CMakeLists.txt
new file mode 100644
index 0000000..37b8c32
--- /dev/null
+++ b/src/srvpp/tests/CMakeLists.txt
@@ -0,0 +1,40 @@
+#
+# 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_directories ("${PROJECT_SOURCE_DIR}/src")
+
+# check whether valgrind is installed
+find_program(valgrind_FOUND valgrind)
+
+# macro for adding of an unit test
+macro(ADD_UNIT_TEST TEST_NAME)
+ set(TEST_SRC
+ ${TEST_NAME}.c
+ )
+ add_executable(${TEST_NAME} ${TEST_SRC})
+ target_link_libraries(${TEST_NAME} ${CMOCKA_LIBRARIES} srvpp_a)
+ add_test(${TEST_NAME} ${TEST_NAME})
+
+ if(valgrind_FOUND)
+ add_test(${TEST_NAME}_valgrind valgrind
+ --error-exitcode=1 --read-var-info=yes
+ --leak-check=full --show-leak-kinds=all
+ ./${TEST_NAME}
+ )
+ endif(valgrind_FOUND)
+endmacro(ADD_UNIT_TEST)
+
+# add individual unit-tests
+ADD_UNIT_TEST(srvpp_test) \ No newline at end of file
diff --git a/src/srvpp/tests/srvpp_test.c b/src/srvpp/tests/srvpp_test.c
new file mode 100644
index 0000000..89e56db
--- /dev/null
+++ b/src/srvpp/tests/srvpp_test.c
@@ -0,0 +1,160 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "srvpp.h"
+
+#include <vpp/api/vpe_msg_enum.h>
+
+#define vl_typedefs /* define message structures */
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...)
+#define vl_printfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_printfun
+
+static int
+srvpp_test_setup(void **state)
+{
+ srvpp_ctx_t *ctx = NULL;
+
+ ctx = srvpp_get_ctx();
+ assert_non_null(ctx);
+
+ srvpp_setup_handler(CONTROL_PING_REPLY, control_ping_reply);
+ srvpp_setup_handler(CREATE_LOOPBACK_REPLY, create_loopback_reply);
+ srvpp_setup_handler(DELETE_LOOPBACK_REPLY, delete_loopback_reply);
+ srvpp_setup_handler(SW_INTERFACE_DETAILS, sw_interface_details);
+
+ *state = ctx;
+ return 0;
+}
+
+static int
+srvpp_test_teardown(void **state)
+{
+ srvpp_ctx_t *ctx = *state;
+
+ srvpp_release_ctx(ctx);
+
+ return 0;
+}
+
+static void
+srvpp_ctx_test(void **state)
+{
+ srvpp_ctx_t *ctx = *state;
+ assert_non_null(ctx);
+
+ srvpp_ctx_t *ctx1 = NULL, *ctx2 = NULL;
+ vl_api_control_ping_t *msg = NULL;
+ int ret = 0;
+
+ /* try to get additional contexts */
+ ctx1 = srvpp_get_ctx();
+ assert_non_null(ctx1);
+
+ ctx2 = srvpp_get_ctx();
+ assert_non_null(ctx2);
+
+ assert_true(ctx == ctx1);
+ assert_true(ctx1 == ctx2);
+
+ /* send a control ping */
+ msg = srvpp_alloc_msg(VL_API_CONTROL_PING, sizeof(*msg));
+ assert_non_null(msg);
+
+ ret = srvpp_send_request(ctx1, msg, NULL);
+ assert_int_equal(ret, 0);
+
+ /* release not needed contexts */
+ srvpp_release_ctx(ctx1);
+ srvpp_release_ctx(ctx2);
+}
+
+static void
+srvpp_msg_test(void **state)
+{
+ srvpp_ctx_t *ctx = *state;
+ assert_non_null(ctx);
+
+ vl_api_create_loopback_t *create_loop_req = NULL;
+ vl_api_create_loopback_reply_t *create_loop_reply = NULL;
+ vl_api_delete_loopback_t *del_loop_req = NULL;
+ vl_api_sw_interface_dump_t *if_dump_req = NULL;
+ vl_api_sw_interface_details_t *if_details = NULL;
+ void **details = NULL;
+ size_t details_cnt = 0;
+ u8 mac[6] = { 0, 10, 11, 12, 13, 14};
+ int if_index = -1;
+ int ret = 0;
+
+ /* create a loopback interface */
+ create_loop_req = srvpp_alloc_msg(VL_API_CREATE_LOOPBACK, sizeof(*create_loop_req));
+ assert_non_null(create_loop_req);
+
+ memcpy(create_loop_req->mac_address, mac, sizeof(mac));
+
+ ret = srvpp_send_request(ctx, create_loop_req, (void**)&create_loop_reply);
+ assert_int_equal(ret, 0);
+ assert_non_null(create_loop_reply);
+ if_index = ntohl(create_loop_reply->sw_if_index);
+
+ printf("connected loopback interface, ifindex=%d\n", if_index);
+
+ /* dump interfaces */
+ if_dump_req = srvpp_alloc_msg(VL_API_SW_INTERFACE_DUMP, sizeof(*if_dump_req));
+ assert_non_null(if_dump_req);
+
+ ret = srvpp_send_dumprequest(ctx, if_dump_req, &details, &details_cnt);
+ assert_int_equal(ret, 0);
+
+ for (size_t i = 0; i < details_cnt; i++) {
+ if_details = (vl_api_sw_interface_details_t *) details[i];
+ printf("interface %s id=%d %s\n", if_details->interface_name, ntohl(if_details->sw_if_index), if_details->admin_up_down ? "up" : "down");
+ }
+
+ /* delete a loopback interface */
+ del_loop_req = srvpp_alloc_msg(VL_API_DELETE_LOOPBACK, sizeof(*del_loop_req));
+ assert_non_null(del_loop_req);
+
+ del_loop_req->sw_if_index = htonl(if_index);
+
+ ret = srvpp_send_request(ctx, del_loop_req, NULL);
+ assert_int_equal(ret, 0);
+}
+
+int
+main()
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(srvpp_ctx_test, srvpp_test_setup, srvpp_test_teardown),
+ cmocka_unit_test_setup_teardown(srvpp_msg_test, srvpp_test_setup, srvpp_test_teardown),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}