diff --git a/.clang-format b/.clang-format
index e364fce6..ae441e8c 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,17 +1,166 @@
-Language : Cpp
-BasedOnStyle: LLVM
-BreakBeforeBraces : Linux
-IndentWidth: 4
-TabWidth : 4
-UseTab : Always
-ColumnLimit : 0
+---
+Language: Cpp
+# BasedOnStyle: WebKit
+AccessModifierOffset: -4
+AlignAfterOpenBracket: DontAlign
+AlignConsecutiveMacros: None
+AlignConsecutiveAssignments: None
+AlignConsecutiveBitFields: None
+AlignConsecutiveDeclarations: None
+AlignEscapedNewlines: Right
+AlignOperands: DontAlign
+AlignTrailingComments: false
+AllowAllArgumentsOnNextLine: true
+AllowAllConstructorInitializersOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortEnumsOnASingleLine: true
+AllowShortBlocksOnASingleLine: Empty
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: All
+AllowShortLambdasOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: MultiLine
+AttributeMacros:
+ - __capability
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: false
+ AfterControlStatement: Never
+ AfterEnum: false
+ AfterFunction: true
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: false
+ BeforeCatch: false
+ BeforeElse: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakBeforeBinaryOperators: All
+BreakBeforeConceptDeclarations: true
+BreakBeforeBraces: WebKit
+BreakBeforeInheritanceComma: false
+BreakInheritanceList: BeforeColon
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeComma
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit: 0
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: false
+DeriveLineEnding: true
+DerivePointerAlignment: false
+DisableFormat: false
+EmptyLineBeforeAccessModifier: LogicalBlock
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: false
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+StatementAttributeLikeMacros:
+ - Q_EMIT
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
+ Priority: 2
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '^(<|"(gtest|gmock|isl|json)/)'
+ Priority: 3
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '.*'
+ Priority: 1
+ SortPriority: 0
+ CaseSensitive: false
+IncludeIsMainRegex: '(Test)?$'
+IncludeIsMainSourceRegex: ''
+IndentCaseLabels: false
+IndentCaseBlocks: false
+IndentGotoLabels: true
+IndentPPDirectives: None
+IndentExternBlock: AfterExternBlock
+IndentRequires: false
+IndentWidth: 4
+IndentWrappedFunctionNames: false
+InsertTrailingCommas: None
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: Inner
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 4
+ObjCBreakBeforeNestedBlockParam: true
+ObjCSpaceAfterProperty: true
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PenaltyIndentedWhitespace: 0
+PointerAlignment: Left
+ReflowComments: true
+SortIncludes: true
+SortJavaStaticImport: Before
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
+SpaceBeforeCpp11BracedList: true
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceAroundPointerQualifiers: Default
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyBlock: true
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInConditionalStatement: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+SpaceBeforeSquareBrackets: false
+BitFieldColonSpacing: Both
+Standard: Latest
+StatementMacros:
+ - Q_UNUSED
+ - QT_REQUIRE_VERSION
+TabWidth: 8
+UseCRLF: false
+UseTab: Never
+WhitespaceSensitiveMacros:
+ - STRINGIZE
+ - PP_STRINGIZE
+ - BOOST_PP_STRINGIZE
+ - NS_SWIFT_NAME
+ - CF_SWIFT_NAME
+...
-SortIncludes: false
-AllowShortIfStatementsOnASingleLine : false
-AllowShortFunctionsOnASingleLine : Empty
-AlwaysBreakAfterReturnType : None
-AlignAfterOpenBracket : Align
-AlignOperands : true
-AlignTrailingComments : true
-IndentCaseLabels : true
-SpaceAfterCStyleCast : true
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 64992f08..22819ac6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@ build/
.vscode/
.clangd/
.test/
+.cache/
compile_commands.json
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..5bc4f061
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "deps/uthash"]
+ path = deps/uthash
+ url = https://github.com/troydhanson/uthash
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0b51c960..5d3180a5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 3.0)
project(sysrepo-interfaces-plugins C)
include(CompileOptions.cmake)
@@ -8,42 +8,35 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
include_directories(
${CMAKE_SOURCE_DIR}/src/
+ ${CMAKE_SOURCE_DIR}/deps/uthash/include/
)
-# get sysrepo version
-find_package(PkgConfig)
-if (PKG_CONFIG_FOUND)
- execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} "--modversion" "sysrepo" OUTPUT_VARIABLE SYSREPO_VERSION)
- if(SYSREPO_VERSION)
- # strip new line from string
- string(STRIP ${SYSREPO_VERSION} SYSREPO_VERSION)
- if(${SYSREPO_VERSION} VERSION_LESS "1.0.0")
- message(FATAL_ERROR "${PROJECT_NAME} requires at least libsysrepo verision 1.0.0")
- endif()
- endif()
-endif()
-
-set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules")
find_package(SYSREPO REQUIRED)
find_package(LIBYANG REQUIRED)
+find_package(SRPC REQUIRED)
+find_package(NL REQUIRED)
include_directories(
${SYSREPO_INCLUDE_DIRS}
${LIBYANG_INCLUDE_DIRS}
+ ${SRPC_INCLUDE_DIRS}
+ ${NL_INCLUDE_DIRS}
)
-option(INTERFACES_PLUGIN "Enable interfaces plugin" ON)
-option(ROUTING_PLUGIN "Enable interfaces plugin" ON)
+option(INTERFACES_PLUGIN "Enable interfaces plugin" OFF)
+option(ROUTING_PLUGIN "Enable interfaces plugin" OFF)
if(INTERFACES_PLUGIN)
- add_subdirectory(src/interfaces)
+ add_subdirectory(src/interfaces)
endif()
if(ROUTING_PLUGIN)
- add_subdirectory(src/routing)
+ add_subdirectory(src/routing)
endif()
if(ENABLE_BUILD_TESTS)
- find_package(CMOCKA REQUIRED)
- include (CTest)
+ find_package(CMOCKA REQUIRED)
+ include(CTest)
+ include(tests/Tests.cmake)
endif()
diff --git a/CMakeModules/FindAUGYANG.cmake b/CMakeModules/FindAUGYANG.cmake
new file mode 100644
index 00000000..d8bf3530
--- /dev/null
+++ b/CMakeModules/FindAUGYANG.cmake
@@ -0,0 +1,32 @@
+if (AUGYANG_LIBRARIES)
+ # in cache already
+ set(AUGYANG_FOUND TRUE)
+else(AUGYANG_LIBRARIES)
+ find_library(
+ AUGYANG_LIBRARY
+ NAMES
+ srds_augeas.so
+ PATHS
+ /usr/lib
+ /usr/lib64
+ /usr/local/lib
+ /usr/local/lib64
+ /opt/local/lib
+ /sw/lib
+ ${CMAKE_LIBRARY_PATH}
+ ${CMAKE_INSTALL_PREFIX}/lib
+
+ # srds_augeas.so
+ ${CMAKE_INSTALL_PREFIX}/lib/sysrepo/plugins
+ )
+
+ if (AUGYANG_LIBRARY)
+ set(AUGYANG_FOUND TRUE)
+ else (AUGYANG_LIBRARY)
+ set(AUGYANG_FOUND FALSE)
+ endif (AUGYANG_LIBRARY)
+
+ set(AUGYANG_LIBRARIES ${AUGYANG_LIBRARY})
+
+ mark_as_advanced(AUGYANG_LIBRARIES)
+endif(AUGYANG_LIBRARIES)
\ No newline at end of file
diff --git a/CMakeModules/FindLIBSYSTEMD.cmake b/CMakeModules/FindLIBSYSTEMD.cmake
new file mode 100644
index 00000000..31224416
--- /dev/null
+++ b/CMakeModules/FindLIBSYSTEMD.cmake
@@ -0,0 +1,24 @@
+# - Try to find SYSTEMD
+# Once done, this will define
+#
+# SYSTEMD_FOUND - system has SYSTEMD
+# SYSTEMD_INCLUDE_DIRS - the SYSTEMD include directories
+# SYSTEMD_LIBRARIES - the SYSTEMD library
+find_package(PkgConfig)
+
+pkg_check_modules(SYSTEMD_PKGCONF libsystemd)
+
+find_path(SYSTEMD_INCLUDE_DIRS
+ NAMES systemd/sd-bus.h
+ PATHS ${SYSTEMD_PKGCONF_INCLUDE_DIRS}
+)
+
+find_library(SYSTEMD_LIBRARIES
+ NAMES systemd
+ PATHS ${SYSTEMD_PKGCONF_LIBRARY_DIRS}
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Systemd DEFAULT_MSG SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES)
+
+mark_as_advanced(SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES)
\ No newline at end of file
diff --git a/cmake/Modules/FindLIBYANG.cmake b/CMakeModules/FindLIBYANG.cmake
similarity index 100%
rename from cmake/Modules/FindLIBYANG.cmake
rename to CMakeModules/FindLIBYANG.cmake
diff --git a/CMakeModules/FindNL.cmake b/CMakeModules/FindNL.cmake
new file mode 100644
index 00000000..2b1bdd39
--- /dev/null
+++ b/CMakeModules/FindNL.cmake
@@ -0,0 +1,47 @@
+# modified version of https://github.com/nasa/channel-emulator/blob/master/cmake/Modules/FindLibNL.cmake
+
+find_path(NL_INCLUDE_DIRS netlink/netlink.h
+ /usr/include
+ /usr/include/libnl3
+ /usr/local/include
+ /usr/local/include/libnl3
+ ${CMAKE_INCLUDE_PATH}
+ ${CMAKE_PREFIX_PATH}/include/libnl3
+)
+
+find_library(
+ NL_LIBRARY
+ NAMES nl nl-3
+ PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib
+)
+find_library(
+ NL_ROUTE_LIBRARY
+ NAMES nl-route nl-route-3
+ PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib
+)
+find_library(
+ NL_NETFILTER_LIBRARY
+ NAMES nl-nf nl-nf-3
+ PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib
+)
+find_library(
+ NL_GENL_LIBRARY
+ NAMES nl-genl nl-genl-3
+ PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib
+)
+
+if (NL_INCLUDE_DIRS AND NL_LIBRARY)
+ set(NL_FOUND TRUE)
+endif (NL_INCLUDE_DIRS AND NL_LIBRARY)
+
+if (NL_FOUND)
+ if (NOT NL_FIND_QUIETLY)
+ set(NL_LIBRARIES ${NL_LIBRARY} ${NL_ROUTE_LIBRARY} ${NL_NETFILTER_LIBRARY} ${NL_GENL_LIBRARY})
+ message("Found netlink libraries: ${NL_LIBRARIES}")
+ message("Found netlink includes: ${NL_INCLUDE_DIRS}")
+ endif (NOT NL_FIND_QUIETLY)
+ELSE (NL_FOUND)
+ if (NL_FIND_REQUIRED)
+ message(FATAL_ERROR "Could not find netlink library.")
+ endif (NL_FIND_REQUIRED)
+endif (NL_FOUND)
diff --git a/CMakeModules/FindPTHREAD.cmake b/CMakeModules/FindPTHREAD.cmake
new file mode 100644
index 00000000..edf8ab1d
--- /dev/null
+++ b/CMakeModules/FindPTHREAD.cmake
@@ -0,0 +1,24 @@
+if(PTHREAD_LIBRARIES AND PTHREAD_INCLUDE_DIRS)
+ set(PTHREAD_FOUND TRUE)
+else()
+ find_path(
+ PTHREAD_INCLUDE_DIR
+ NAMES pthread.h
+ PATHS /usr/include /usr/local/include /opt/local/include /sw/include ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include
+ )
+
+ find_library(
+ PTHREAD_LIBRARY
+ NAMES pthread
+ PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib
+ )
+
+ if(PTHREAD_INCLUDE_DIR AND PTHREAD_LIBRARY)
+ set(PTHREAD_FOUND TRUE)
+ else(PTHREAD_INCLUDE_DIR AND PTHREAD_LIBRARY)
+ set(PTHREAD_FOUND FALSE)
+ endif(PTHREAD_INCLUDE_DIR AND PTHREAD_LIBRARY)
+
+ set(PTHREAD_INCLUDE_DIRS ${PTHREAD_INCLUDE_DIR})
+ set(PTHREAD_LIBRARIES ${PTHREAD_LIBRARY})
+endif()
\ No newline at end of file
diff --git a/CMakeModules/FindSRPC.cmake b/CMakeModules/FindSRPC.cmake
new file mode 100644
index 00000000..128189a0
--- /dev/null
+++ b/CMakeModules/FindSRPC.cmake
@@ -0,0 +1,25 @@
+if (SRPC_LIBRARIES AND SRPC_INCLUDE_DIRS)
+ set(SRPC_FOUND TRUE)
+else ()
+
+ find_path(
+ SRPC_INCLUDE_DIR
+ NAMES srpc.h
+ PATHS /usr/include /usr/local/include /opt/local/include /sw/include ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include
+ )
+
+ find_library(
+ SRPC_LIBRARY
+ NAMES srpc
+ PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib
+ )
+
+ if (SRPC_INCLUDE_DIR AND SRPC_LIBRARY)
+ set(SRPC_FOUND TRUE)
+ else (SRPC_INCLUDE_DIR AND SRPC_LIBRARY)
+ set(SRPC_FOUND FALSE)
+ endif (SRPC_INCLUDE_DIR AND SRPC_LIBRARY)
+
+ set(SRPC_INCLUDE_DIRS ${SRPC_INCLUDE_DIR})
+ set(SRPC_LIBRARIES ${SRPC_LIBRARY})
+endif ()
\ No newline at end of file
diff --git a/CMakeModules/FindSYSREPO.cmake b/CMakeModules/FindSYSREPO.cmake
new file mode 100644
index 00000000..532bf9d5
--- /dev/null
+++ b/CMakeModules/FindSYSREPO.cmake
@@ -0,0 +1,24 @@
+if(SYSREPO_LIBRARIES AND SYSREPO_INCLUDE_DIRS)
+ set(SYSREPO_FOUND TRUE)
+else()
+ find_path(
+ SYSREPO_INCLUDE_DIR
+ NAMES sysrepo.h
+ PATHS /usr/include /usr/local/include /opt/local/include /sw/include ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include
+ )
+
+ find_library(
+ SYSREPO_LIBRARY
+ NAMES sysrepo
+ PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib
+ )
+
+ if(SYSREPO_INCLUDE_DIR AND SYSREPO_LIBRARY)
+ set(SYSREPO_FOUND TRUE)
+ else(SYSREPO_INCLUDE_DIR AND SYSREPO_LIBRARY)
+ set(SYSREPO_FOUND FALSE)
+ endif(SYSREPO_INCLUDE_DIR AND SYSREPO_LIBRARY)
+
+ set(SYSREPO_INCLUDE_DIRS ${SYSREPO_INCLUDE_DIR})
+ set(SYSREPO_LIBRARIES ${SYSREPO_LIBRARY})
+endif()
\ No newline at end of file
diff --git a/CompileOptions.cmake b/CompileOptions.cmake
index 00bf1a93..fbe23896 100644
--- a/CompileOptions.cmake
+++ b/CompileOptions.cmake
@@ -1,20 +1,24 @@
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic")
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra")
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion")
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-align")
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes")
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wuninitialized")
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow")
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat=2")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-align")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wuninitialized")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat=2")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE")
+
if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5)
- set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=incompatible-pointer-types")
-endif ()
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-parameter")
-if (CMAKE_C_COMPILER_ID MATCHES "Clang")
- set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-newline-eof")
- set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-gnu-zero-variadic-macro-arguments")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=incompatible-pointer-types")
+endif()
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-parameter")
+
+if(CMAKE_C_COMPILER_ID MATCHES "Clang")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-newline-eof")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-gnu-zero-variadic-macro-arguments")
endif()
diff --git a/README.md b/README.md
index d9e2a5f6..fa2c0675 100644
--- a/README.md
+++ b/README.md
@@ -46,8 +46,9 @@ Besides the usual C development environment, the following additional dependenci
First clone the repository:
-```
-$ git clone https://github.com/telekom/sysrepo-plugin-interfaces
+```sh
+# clone recursively to also clone uthash library
+$ git clone https://github.com/telekom/sysrepo-plugin-interfaces --recursive
```
Next, create a build directory and generate the build recipes using CMake:
@@ -77,7 +78,7 @@ Lastly, invoke the build and install using `make`:
$ make -j$(nproc) install
```
-The plugins require several YANG modules to be loaded into the Sysrepo datastore.
+The plugins require several YANG modules to be loaded into the Sysrepo datastore and several features need to be enabled.
For the interfaces plugin this can be achieved by invoking the following commands:
```
@@ -87,6 +88,8 @@ $ sysrepoctl -i ./yang/ietf-ip@2018-02-22.yang
$ sysrepoctl -i ./yang/ietf-if-extensions@2020-07-29.yang
$ sysrepoctl -i ./yang/ieee802-dot1q-types.yang
$ sysrepoctl -i ./yang/ietf-if-vlan-encapsulation@2020-07-13.yang
+$ sysrepoctl --change ietf-interfaces --enable-feature if-mib
+$ sysrepoctl --change ietf-if-extensions --enable-feature sub-interfaces
```
For the routing plugin, the following models have to be installed:
@@ -117,10 +120,10 @@ The full documentation for the Sysrepo interfaces and routing plugins can be fou
The following channels are available for discussions, feedback, and support requests:
-| Type | Channel |
-| ------------------------ | ------------------------------------------------------ |
-| **Issues** | |
-| **Other Requests** | |
+| Type | Channel |
+| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Issues** | |
+| **Other Requests** | |
## How to Contribute
diff --git a/cmake/Modules/FindNL.cmake b/cmake/Modules/FindNL.cmake
deleted file mode 100644
index 6131ffcb..00000000
--- a/cmake/Modules/FindNL.cmake
+++ /dev/null
@@ -1,29 +0,0 @@
-# modified version of https://github.com/nasa/channel-emulator/blob/master/cmake/Modules/FindLibNL.cmake
-
-find_path(NL_INCLUDE_DIRS netlink/netlink.h
- /usr/include
- /usr/include/libnl3
- /usr/local/include
- /usr/local/include/libnl3
-)
-
-find_library(NL_LIBRARY NAMES nl nl-3)
-find_library(NL_ROUTE_LIBRARY NAMES nl-route nl-route-3)
-find_library(NL_NETFILTER_LIBRARY NAMES nl-nf nl-nf-3)
-find_library(NL_GENL_LIBRARY NAMES nl-genl nl-genl-3)
-
-if (NL_INCLUDE_DIRS AND NL_LIBRARY)
- set(NL_FOUND TRUE)
-endif (NL_INCLUDE_DIRS AND NL_LIBRARY)
-
-if (NL_FOUND)
- if (NOT NL_FIND_QUIETLY)
- set(NL_LIBRARIES ${NL_LIBRARY} ${NL_ROUTE_LIBRARY} ${NL_NETFILTER_LIBRARY} ${NL_GENL_LIBRARY})
- message("Found netlink libraries: ${NL_LIBRARIES}")
- message("Found netlink includes: ${NL_INCLUDE_DIRS}")
- endif (NOT NL_FIND_QUIETLY)
-ELSE (NL_FOUND)
- if (NL_FIND_REQUIRED)
- message(FATAL_ERROR "Could not find netlink library.")
- endif (NL_FIND_REQUIRED)
-endif (NL_FOUND)
diff --git a/cmake/Modules/FindSYSREPO.cmake b/cmake/Modules/FindSYSREPO.cmake
deleted file mode 100644
index 3d6ece20..00000000
--- a/cmake/Modules/FindSYSREPO.cmake
+++ /dev/null
@@ -1,26 +0,0 @@
-# SYSREPO_FOUND - System has SYSREPO
-# SYSREPO_INCLUDE_DIRS - The SYSREPO include directories
-# SYSREPO_LIBRARIES - The libraries needed to use SYSREPO
-# SYSREPO_DEFINITIONS - Compiler switches required for using SYSREPO
-
-find_package(PkgConfig)
-pkg_check_modules(PC_SYSREPO QUIET sysrepo)
-set(SYSREPO_DEFINITIONS ${PC_SYSREPO_CFLAGS_OTHER})
-
-find_path(SYSREPO_INCLUDE_DIR sysrepo.h
- HINTS ${PC_SYSREPO_INCLUDEDIR} ${PC_SYSREPO_INCLUDE_DIRS}
- PATH_SUFFIXES sysrepo )
-
-find_library(SYSREPO_LIBRARY NAMES sysrepo
- HINTS ${PC_SYSREPO_LIBDIR} ${PC_SYSREPO_LIBRARY_DIRS} )
-
-set(SYSREPO_LIBRARIES ${SYSREPO_LIBRARY} )
-set(SYSREPO_INCLUDE_DIRS ${SYSREPO_INCLUDE_DIR} )
-
-include(FindPackageHandleStandardArgs)
-# handle the QUIETLY and REQUIRED arguments and set SYSREPO_FOUND to TRUE
-# if all listed variables are TRUE
-find_package_handle_standard_args(sysrepo DEFAULT_MSG
- SYSREPO_LIBRARY SYSREPO_INCLUDE_DIR)
-
-mark_as_advanced(SYSREPO_INCLUDE_DIR SYSREPO_LIBRARY )
diff --git a/deps/uthash b/deps/uthash
new file mode 160000
index 00000000..44a66fe8
--- /dev/null
+++ b/deps/uthash
@@ -0,0 +1 @@
+Subproject commit 44a66fe8e008c0a7f1c5af96f4a4cdf304b8bc81
diff --git a/examples/test_config.xml b/examples/test_config.xml
new file mode 100644
index 00000000..74a734be
--- /dev/null
+++ b/examples/test_config.xml
@@ -0,0 +1,20 @@
+
+
+
+ test_if
+ Test Interface
+ ianaift:other
+ false
+
+
+
+ if0
+ Test Interface
+ ianaift:other
+ false
+
+
\ No newline at end of file
diff --git a/src/interfaces/CMakeLists.txt b/src/interfaces/CMakeLists.txt
index daed2259..8ac11d4c 100644
--- a/src/interfaces/CMakeLists.txt
+++ b/src/interfaces/CMakeLists.txt
@@ -1,53 +1,152 @@
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 3.0)
project(sysrepo-plugin-interfaces C)
-set(SOURCES
- interfaces.c
- if_state.c
- link_data.c
- ip_data.c
- ipv6_data.c
- if_nic_stats.c
- ${CMAKE_SOURCE_DIR}/src/utils/memory.c
+set(PLUGIN_LIBRARY_NAME srplg-interfaces)
+
+include_directories(
+ ${CMAKE_SOURCE_DIR}/src/interfaces/src
+ ${CMAKE_SOURCE_DIR}/deps/uthash
)
-# get sysrepo version
-find_package(PkgConfig)
-if (PKG_CONFIG_FOUND)
- execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} "--modversion" "sysrepo" OUTPUT_VARIABLE SYSREPO_VERSION)
- if(SYSREPO_VERSION)
- # strip new line from string
- string(STRIP ${SYSREPO_VERSION} SYSREPO_VERSION)
- if(${SYSREPO_VERSION} VERSION_LESS "1.0.0")
- message(FATAL_ERROR "${PROJECT_NAME} requires at least libsysrepo verision 1.0.0")
- endif()
- endif()
-endif()
+set(PLUGIN 0 CACHE BOOL "Build a plugin")
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules")
-if(PLUGIN)
- add_library(${PROJECT_NAME} MODULE ${SOURCES})
- install(TARGETS ${PROJECT_NAME} DESTINATION lib)
-else()
- add_executable(${PROJECT_NAME} ${SOURCES})
- install(TARGETS ${PROJECT_NAME} DESTINATION bin)
-endif()
+set(
+ SOURCES
-set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${PROJECT_NAME} PREFIX "")
+ # startup
+ src/plugin/startup/load.c
+ src/plugin/startup/store.c
+ # running
+ src/plugin/running/load.c
+ src/plugin/running/store.c
+
+ # subscription
+ src/plugin/subscription/change.c
+ src/plugin/subscription/operational.c
+ src/plugin/subscription/rpc.c
+
+ # ly_tree
+ src/plugin/ly_tree.c
+
+ # API
+ src/plugin/api/interfaces/check.c
+ src/plugin/api/interfaces/load.c
+ src/plugin/api/interfaces/read.c
+ src/plugin/api/interfaces/store.c
+ src/plugin/api/interfaces/change.c
+ src/plugin/api/interfaces/interface/load.c
+ src/plugin/api/interfaces/interface/change.c
+ src/plugin/api/interfaces/interface/ipv6/load.c
+ src/plugin/api/interfaces/interface/ipv6/change.c
+ src/plugin/api/interfaces/interface/ipv6/autoconf/change.c
+ src/plugin/api/interfaces/interface/ipv6/neighbor/change.c
+ src/plugin/api/interfaces/interface/ipv6/neighbor/load.c
+ src/plugin/api/interfaces/interface/ipv6/address/change.c
+ src/plugin/api/interfaces/interface/ipv6/address/load.c
+ src/plugin/api/interfaces/interface/ipv4/change.c
+ src/plugin/api/interfaces/interface/ipv4/load.c
+ src/plugin/api/interfaces/interface/ipv4/neighbor/change.c
+ src/plugin/api/interfaces/interface/ipv4/neighbor/load.c
+ src/plugin/api/interfaces/interface/ipv4/address/change.c
+ src/plugin/api/interfaces/interface/ipv4/address/load.c
+ src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/second-tag/change.c
+ src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/outer-tag/change.c
+ src/plugin/api/interfaces/interface/dampening/change.c
+ src/plugin/api/interfaces/interface/carrier-delay/change.c
+
+ # data
+ src/plugin/data/interfaces/interface.c
+ src/plugin/data/interfaces/interface_state.c
+ src/plugin/data/interfaces/interface/ipv4.c
+ src/plugin/data/interfaces/interface/ipv6.c
+ src/plugin/data/interfaces/interface/ipv4/address.c
+ src/plugin/data/interfaces/interface/ipv4/neighbor.c
+ src/plugin/data/interfaces/interface/ipv6/address.c
+ src/plugin/data/interfaces/interface/ipv6/neighbor.c
+
+ # main files
+ src/plugin.c
+ ${CMAKE_SOURCE_DIR}/src/utils/memory.c
+)
+
+# packages
+find_package(AUGYANG)
+find_package(PTHREAD REQUIRED)
+find_package(LIBYANG REQUIRED)
+find_package(SYSREPO REQUIRED)
+find_package(SRPC REQUIRED)
find_package(NL REQUIRED)
-# pthread api
-set(THREADS_PREFER_PTHREAD_FLAG ON)
-find_package(Threads REQUIRED)
+# plugin library
+add_library(
+ ${PLUGIN_LIBRARY_NAME}
+ STATIC
+ ${SOURCES}
+)
+# link plugin library
target_link_libraries(
- ${PROJECT_NAME}
+ ${PLUGIN_LIBRARY_NAME}
+
${SYSREPO_LIBRARIES}
${LIBYANG_LIBRARIES}
+ ${SRPC_LIBRARIES}
${NL_LIBRARIES}
- Threads::Threads
+ ${PTHREAD_LIBRARIES}
)
+if(PLUGIN)
+ # ignore plugin library and compile PROJECT_NAME as a module
+ add_library(
+ ${PROJECT_NAME}
+ MODULE ${SOURCES}
+ )
+ target_link_libraries(
+ ${PROJECT_NAME}
+ ${SYSREPO_LIBRARIES}
+ ${LIBYANG_LIBRARIES}
+ ${SRPC_LIBRARIES}
+ ${NL_LIBRARIES}
+ ${PTHREAD_LIBRARIES}
+ )
+else()
+ add_executable(
+ ${PROJECT_NAME}
+ src/main.c
+ )
+ target_link_libraries(
+ ${PROJECT_NAME}
+
+ # link plugin library with executable
+ ${PLUGIN_LIBRARY_NAME}
+
+ ${SYSREPO_LIBRARIES}
+ ${LIBYANG_LIBRARIES}
+ ${SRPC_LIBRARIES}
+ ${NL_LIBRARIES}
+ ${PTHREAD_LIBRARIES}
+ )
+endif()
+
include_directories(
+ ${SYSREPO_INCLUDE_DIRS}
+ ${LIBYANG_INCLUDE_DIRS}
+ ${SRPC_INCLUDE_DIRS}
${NL_INCLUDE_DIRS}
+ ${PTHREAD_INCLUDE_DIRS}
)
+
+# augyang support
+if(AUGYANG_FOUND)
+ add_compile_definitions(AUGYANG)
+else(AUGYANG_FOUND)
+ message(WARNING "AUGYANG not found - augeas support will be disabled")
+endif()
+
+if(ENABLE_BUILD_TESTS)
+ find_package(CMOCKA REQUIRED)
+ include(CTest)
+ include(tests/CMakeLists.txt)
+endif()
diff --git a/src/interfaces/if_nic_stats.c b/src/interfaces/if_nic_stats.c
deleted file mode 100644
index cf4f65ea..00000000
--- a/src/interfaces/if_nic_stats.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * telekom / sysrepo-plugin-interfaces
- *
- * This program is made available under the terms of the
- * BSD 3-Clause license which is available at
- * https://opensource.org/licenses/BSD-3-Clause
- *
- * SPDX-FileCopyrightText: 2021 Deutsche Telekom AG
- * SPDX-FileContributor: Sartura Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "if_nic_stats.h"
-#include "utils/memory.h"
-
-int get_nic_stats(char *if_name, nic_stats_t *nic_stats)
-{
- int skfd = -1;
- struct ethtool_drvinfo drvinfo = {0};
- struct ethtool_gstrings *gstrings = NULL;
- struct ethtool_stats *stats = NULL;
- struct ifreq ifr = {0};
- unsigned int n_stats = 0;
- unsigned int sz_str = 0;
- unsigned int sz_stats = 0;
-
- skfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (skfd < 0) {
- goto error_out;
- }
-
- // set interface name
- strncpy(&ifr.ifr_name[0], if_name, IFNAMSIZ);
- ifr.ifr_name[IFNAMSIZ - 1] = 0;
-
- // how many stats are available
- drvinfo.cmd = ETHTOOL_GDRVINFO;
- ifr.ifr_data = (caddr_t) &drvinfo;
-
- if (ioctl(skfd, SIOCETHTOOL, &ifr) != 0) {
- goto error_out;
- }
-
- n_stats = drvinfo.n_stats;
- if (n_stats < 1) {
- goto error_out;
- }
-
- // allocate memory for stat names and values
- sz_str = n_stats * ETH_GSTRING_LEN;
- sz_stats = n_stats * sizeof(uint64_t);
-
- gstrings = xcalloc(1, sz_str + sizeof(struct ethtool_gstrings));
-
- stats = xcalloc(1, sz_stats + sizeof(struct ethtool_stats));
-
- // get stat names
- gstrings->cmd = ETHTOOL_GSTRINGS;
- gstrings->string_set = ETH_SS_STATS;
- gstrings->len = n_stats;
- ifr.ifr_data = (caddr_t) gstrings;
-
- if (ioctl(skfd, SIOCETHTOOL, &ifr) != 0) {
- goto error_out;
- }
-
- // get stat values
- stats->cmd = ETHTOOL_GSTATS;
- stats->n_stats = n_stats;
- ifr.ifr_data = (caddr_t) stats;
-
- if (ioctl(skfd, SIOCETHTOOL, &ifr) != 0) {
- goto error_out;
- }
-
- for (unsigned int i = 0; i < n_stats; i++) {
- char *stat_name = (char *)&gstrings->data[i * ETH_GSTRING_LEN];
- uint64_t stat_val = stats->data[i];
-
- if (strncmp(stat_name, "rx_packets", ETH_GSTRING_LEN) == 0) {
- nic_stats->rx_packets = stat_val;
- } else if (strncmp(stat_name, "rx_broadcast", ETH_GSTRING_LEN) == 0) {
- nic_stats->rx_broadcast = stat_val;
- } else if (strncmp(stat_name, "tx_packets", ETH_GSTRING_LEN) == 0) {
- nic_stats->tx_packets = stat_val;
- } else if (strncmp(stat_name, "tx_broadcast", ETH_GSTRING_LEN) == 0) {
- nic_stats->tx_broadcast = stat_val;
- } else if (strncmp(stat_name, "tx_multicast", ETH_GSTRING_LEN) == 0) {
- nic_stats->tx_multicast = stat_val;
- }
- }
-
- FREE_SAFE(gstrings);
- FREE_SAFE(stats);
- close(skfd);
-
- return 0;
-
-error_out:
- if (gstrings != NULL) {
- FREE_SAFE(gstrings);
- }
-
- if (stats != NULL) {
- FREE_SAFE(stats);
- }
-
- if (skfd >= 0) {
- close(skfd);
- }
-
- return -1;
-}
diff --git a/src/interfaces/if_nic_stats.h b/src/interfaces/if_nic_stats.h
deleted file mode 100644
index 36f54a07..00000000
--- a/src/interfaces/if_nic_stats.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * telekom / sysrepo-plugin-interfaces
- *
- * This program is made available under the terms of the
- * BSD 3-Clause license which is available at
- * https://opensource.org/licenses/BSD-3-Clause
- *
- * SPDX-FileCopyrightText: 2021 Deutsche Telekom AG
- * SPDX-FileContributor: Sartura Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef IF_NIC_STATS_H_ONCE
-#define IF_NIC_STATS_H_ONCE
-
-#include
-
-typedef struct nic_stats_s nic_stats_t;
-
-struct nic_stats_s {
- uint64_t rx_packets;
- uint64_t rx_broadcast;
- uint64_t tx_packets;
- uint64_t tx_broadcast;
- uint64_t tx_multicast;
-};
-
-int get_nic_stats(char *if_name, nic_stats_t *nic_stats);
-
-#endif /* IF_NIC_STATS_H_ONCE */
diff --git a/src/interfaces/if_state.c b/src/interfaces/if_state.c
deleted file mode 100644
index dd2b6f11..00000000
--- a/src/interfaces/if_state.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * telekom / sysrepo-plugin-interfaces
- *
- * This program is made available under the terms of the
- * BSD 3-Clause license which is available at
- * https://opensource.org/licenses/BSD-3-Clause
- *
- * SPDX-FileCopyrightText: 2021 Deutsche Telekom AG
- * SPDX-FileContributor: Sartura Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "if_state.h"
-#include "utils/memory.h"
-#include
-#include
-
-void if_state_init(if_state_t *st)
-{
- st->name = NULL;
- st->last_change = 0;
- st->state = 0;
-}
-
-void if_state_free(if_state_t *st)
-{
- if (st->name != NULL) {
- FREE_SAFE(st->name);
- }
-}
-
-void if_state_list_init(if_state_list_t *ls)
-{
- ls->data = NULL;
- ls->count = 0;
-}
-
-if_state_t *if_state_list_get(if_state_list_t *ls, uint idx)
-{
- if (idx < ls->count) {
- return &ls->data[idx];
- }
- return NULL;
-}
-
-if_state_t *if_state_list_get_by_if_name(if_state_list_t *ls, char *name)
-{
- for (uint i = 0; i < ls->count; i++) {
- if (strcmp(ls->data[i].name, name) == 0) {
- return &ls->data[i];
- }
- }
- return NULL;
-}
-
-void if_state_list_alloc(if_state_list_t *ls, uint count)
-{
- ls->count = count;
- ls->data = (if_state_t *) malloc(sizeof(if_state_t) * count);
- for (uint i = 0; i < count; i++) {
- if_state_init(ls->data + i);
- }
-}
-
-void if_state_list_add(if_state_list_t *ls, uint8_t state, char *name)
-{
- uint count = ++ls->count;
- ls->data = (if_state_t *) realloc(ls->data, sizeof(if_state_t) * count);
-
- ls->data[count-1].last_change = 0;
-
- size_t len = strlen(name);
- ls->data[count-1].name = xcalloc(len + 1, sizeof(char));
- strncpy(ls->data[count-1].name, name, len);
- ls->data[count-1].name[len] = '\0';
-
- ls->data[count-1].state = state;
-}
-
-void if_state_list_free(if_state_list_t *ls)
-{
- if (ls->count) {
- for (uint i = 0; i < ls->count; i++) {
- if_state_free(ls->data + i);
- }
- FREE_SAFE(ls->data);
- }
-}
diff --git a/src/interfaces/if_state.h b/src/interfaces/if_state.h
deleted file mode 100644
index 850cd134..00000000
--- a/src/interfaces/if_state.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * telekom / sysrepo-plugin-interfaces
- *
- * This program is made available under the terms of the
- * BSD 3-Clause license which is available at
- * https://opensource.org/licenses/BSD-3-Clause
- *
- * SPDX-FileCopyrightText: 2021 Deutsche Telekom AG
- * SPDX-FileContributor: Sartura Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef IF_STATE_H_ONCE
-#define IF_STATE_H_ONCE
-
-#include
-#include
-
-typedef struct if_state_s if_state_t;
-typedef struct if_state_list_s if_state_list_t;
-typedef unsigned int uint;
-
-struct if_state_s {
- char *name;
- uint8_t state;
- time_t last_change;
-};
-
-void if_state_init(if_state_t *st);
-void if_state_free(if_state_t *st);
-
-struct if_state_list_s {
- if_state_t *data;
- uint count;
-};
-
-void if_state_list_init(if_state_list_t *ls);
-if_state_t *if_state_list_get(if_state_list_t *ls, uint idx);
-if_state_t *if_state_list_get_by_if_name(if_state_list_t *ls, char *name);
-void if_state_list_alloc(if_state_list_t *ls, uint count);
-void if_state_list_add(if_state_list_t *ls, uint8_t state, char *name);
-void if_state_list_free(if_state_list_t *ls);
-
-#endif /* IF_STATE_H_ONCE */
diff --git a/src/interfaces/interfaces.c b/src/interfaces/interfaces.c
deleted file mode 100644
index cef11e1b..00000000
--- a/src/interfaces/interfaces.c
+++ /dev/null
@@ -1,3536 +0,0 @@
-/*
- * telekom / sysrepo-plugin-interfaces
- *
- * This program is made available under the terms of the
- * BSD 3-Clause license which is available at
- * https://opensource.org/licenses/BSD-3-Clause
- *
- * SPDX-FileCopyrightText: 2021 Deutsche Telekom AG
- * SPDX-FileContributor: Sartura Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-
-#include "if_nic_stats.h"
-#include "if_state.h"
-#include "ip_data.h"
-#include "link_data.h"
-#include "utils/memory.h"
-
-#define BASE_YANG_MODEL "ietf-interfaces"
-#define BASE_IP_YANG_MODEL "ietf-ip"
-
-// config data
-#define INTERFACES_YANG_MODEL "/" BASE_YANG_MODEL ":interfaces"
-#define INTERFACE_LIST_YANG_PATH INTERFACES_YANG_MODEL "/interface"
-
-// other #defines
-#define MAC_ADDR_MAX_LENGTH 18
-#define MAX_DESCR_LEN 100
-#define DATETIME_BUF_SIZE 30
-#define CLASS_NET_LINE_LEN 1024
-#define ADDR_STR_BUF_SIZE 45 // max ip string length (15 for ipv4 and 45 for ipv6)
-#define MAX_IF_NAME_LEN IFNAMSIZ // 16 bytes
-#define CMD_LEN 1024
-
-// callbacks
-static int interfaces_module_change_cb(sr_session_ctx_t *session, uint32_t subscription_id, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data);
-static int interfaces_state_data_cb(sr_session_ctx_t *session, uint32_t subscription_id, const char *module_name, const char *path, const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data);
-
-// helper functions
-static bool system_running_datastore_is_empty_check(sr_session_ctx_t *session);
-static int load_data(sr_session_ctx_t *session, link_data_list_t *ld);
-static int load_startup(sr_session_ctx_t *session, link_data_list_t *ld);
-static bool check_system_interface(const char *interface_name, bool *system_interface);
-int set_config_value(const char *xpath, const char *value);
-int add_interface_ipv4(link_data_t *ld, struct rtnl_link *old, struct rtnl_link *req, int if_idx);
-static int remove_ipv4_address(ip_address_list_t *addr_list, struct nl_sock *socket, struct rtnl_link *old);
-int add_interface_ipv6(link_data_t *ld, struct rtnl_link *old, struct rtnl_link *req, int if_idx);
-static int remove_ipv6_address(ip_address_list_t *addr_list, struct nl_sock *socket, struct rtnl_link *old);
-static int remove_neighbors(ip_neighbor_list_t *nbor_list, struct nl_sock *socket, int addr_ver, int if_index);
-int write_to_proc_file(const char *dir_path, char *interface, const char *fn, int val);
-static int read_from_proc_file(const char *dir_path, char *interface, const char *fn, int *val);
-static int read_from_sys_file(const char *dir_path, char *interface, int *val);
-int delete_config_value(const char *xpath, const char *value);
-int update_link_info(link_data_list_t *ld, sr_change_oper_t operation);
-static char *convert_ianaiftype(char *iana_if_type);
-int add_existing_links(sr_session_ctx_t *session, link_data_list_t *ld);
-static int get_interface_description(sr_session_ctx_t *session, char *name, char **description);
-static int create_vlan_qinq(char *name, char *parent_interface, uint16_t outer_vlan_id, uint16_t second_vlan_id);
-static int get_system_boot_time(char boot_datetime[]);
-
-// function to start all threads for each interface
-static int init_state_changes(void);
-
-// callback function for a thread to track state changes on a specific interface (ifindex passed using void* data param)
-static void *manager_thread_cb(void *data);
-static void cache_change_cb(struct nl_cache *cache, struct nl_object *obj, int val, void *arg);
-
-// static list of interface states for tracking state changes using threads
-static if_state_list_t if_state_changes;
-
-// global list of link_data structs
-static link_data_list_t link_data_list = {0};
-
-// link manager used for cacheing links info constantly
-static struct nl_cache_mngr *link_manager = NULL;
-static struct nl_cache *link_cache = NULL;
-
-volatile int exit_application = 0;
-
-int sr_plugin_init_cb(sr_session_ctx_t *session, void **private_data)
-{
- int error = 0;
- sr_conn_ctx_t *connection = NULL;
- sr_session_ctx_t *startup_session = NULL;
- sr_subscription_ctx_t *subscription = NULL;
- char *desc_file_path = NULL;
-
- *private_data = NULL;
-
- error = link_data_list_init(&link_data_list);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_init error");
- goto out;
- }
-
- error = add_existing_links(session, &link_data_list);
- if (error != 0) {
- SRP_LOG_ERR("add_existing_links error");
- goto out;
- }
-
- SRP_LOG_INF("start session to startup datastore");
-
- if_state_list_init(&if_state_changes);
-
- error = init_state_changes();
- if (error != 0) {
- SRP_LOG_ERR("Error occurred while initializing threads to track interface changes... exiting");
- goto out;
- }
-
- connection = sr_session_get_connection(session);
- error = sr_session_start(connection, SR_DS_STARTUP, &startup_session);
- if (error) {
- SRP_LOG_ERR("sr_session_start error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- *private_data = startup_session;
-
- if (system_running_datastore_is_empty_check(session) == true) {
- SRP_LOG_INF("running DS is empty, loading data");
-
- error = load_data(session, &link_data_list);
- if (error) {
- SRP_LOG_ERR("load_data error");
- goto error_out;
- }
-
- error = sr_copy_config(startup_session, BASE_YANG_MODEL, SR_DS_RUNNING, 0);
- if (error) {
- SRP_LOG_ERR("sr_copy_config error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
- }
-
- // load data from startup datastore to internal list
- error = load_startup(startup_session, &link_data_list);
- if (error != 0) {
- SRP_LOG_ERR("load_startup error");
- goto error_out;
- }
-
- // apply what is present in the startup datastore
- error = update_link_info(&link_data_list, SR_OP_CREATED);
- if (error != 0) {
- SRP_LOG_ERR("update_link_info error");
- goto error_out;
- }
-
- SRP_LOG_INF("subscribing to module change");
-
- // sub to any module change - for now
- error = sr_module_change_subscribe(session, BASE_YANG_MODEL, "/" BASE_YANG_MODEL ":*//.", interfaces_module_change_cb, *private_data, 0, SR_SUBSCR_DEFAULT, &subscription);
- if (error) {
- SRP_LOG_ERR("sr_module_change_subscribe error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- error = sr_oper_get_items_subscribe(session, BASE_YANG_MODEL, INTERFACES_YANG_MODEL "/*", interfaces_state_data_cb, NULL, SR_SUBSCR_CTX_REUSE, &subscription);
- if (error) {
- SRP_LOG_ERR("sr_oper_get_items_subscribe error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- SRP_LOG_INF("plugin init done");
-
- FREE_SAFE(desc_file_path);
-
- goto out;
-
-error_out:
- if (subscription != NULL) {
- sr_unsubscribe(subscription);
- }
-
- if (desc_file_path != NULL) {
- FREE_SAFE(desc_file_path);
- }
-out:
- return error ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK;
-}
-
-static bool system_running_datastore_is_empty_check(sr_session_ctx_t *session)
-{
- int error = SR_ERR_OK;
- bool is_empty = true;
- sr_val_t *values = NULL;
- size_t value_cnt = 0;
-
- error = sr_get_items(session, INTERFACE_LIST_YANG_PATH, 0, SR_OPER_DEFAULT, &values, &value_cnt);
- if (error) {
- SRP_LOG_ERR("sr_get_items error (%d): %s", error, sr_strerror(error));
- goto out;
- }
-
- // check if interface list is empty
- if (value_cnt > 0) {
- sr_free_values(values, value_cnt);
- is_empty = false;
- }
-out:
- return is_empty;
-}
-
-static int load_data(sr_session_ctx_t *session, link_data_list_t *ld)
-{
- int error = 0;
- char interface_path_buffer[PATH_MAX] = {0};
- char xpath_buffer[PATH_MAX] = {0};
- char tmp_buffer[PATH_MAX] = {0};
-
- for (int i = 0; i < ld->count; i++) {
- char *name = ld->links[i].name;
- char *type = ld->links[i].type;
- char *description = ld->links[i].description;
- char *enabled = ld->links[i].enabled;
- char *parent_interface = ld->links[i].extensions.parent_interface;
-
- snprintf(interface_path_buffer, sizeof(interface_path_buffer) / sizeof(char), "%s[name=\"%s\"]", INTERFACE_LIST_YANG_PATH, name);
-
- if (strcmp(type, "lo") == 0) {
- type = "iana-if-type:softwareLoopback";
- } else if (strcmp(type, "eth") == 0) {
- type = "iana-if-type:ethernetCsmacd";
- } else if (strcmp(type, "vlan") == 0) {
- type = "iana-if-type:l2vlan";
- } else if (strcmp(type, "dummy") == 0) {
- type = "iana-if-type:other"; // since dummy is not a real type
- } else {
- SRP_LOG_INF("load_data unsupported interface type %s", type);
- continue;
- }
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/name", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- error = sr_set_item_str(session, xpath_buffer, name, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/description", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- error = sr_set_item_str(session, xpath_buffer, description, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/type", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- error = sr_set_item_str(session, xpath_buffer, type, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/enabled", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- error = sr_set_item_str(session, xpath_buffer, enabled, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- if (parent_interface != 0) {
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-if-extensions:parent-interface", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- error = sr_set_item_str(session, xpath_buffer, parent_interface, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
- }
-
- // handle vlan interfaces
-
-
- // ietf-ip
- // TODO: refactor this!
- // list of ipv4 addresses
- if (strcmp(ld->links[i].name, name ) == 0) {
- // enabled
- // TODO
-
- // forwarding
- uint8_t ipv4_forwarding = ld->links[i].ipv4.forwarding;
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv4/forwarding", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("xpath_buffer: %s = %s", xpath_buffer, ipv4_forwarding == 0 ? "false" : "true");
-
- error = sr_set_item_str(session, xpath_buffer, ipv4_forwarding == 0 ? "false" : "true", NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- uint32_t ipv4_addr_count = ld->links[i].ipv4.addr_list.count;
-
- for (uint32_t j = 0; j < ipv4_addr_count; j++) {
- if (ld->links[i].ipv4.addr_list.addr[j].ip != NULL) { // in case we deleted an ip address it will be NULL
- char *ip_addr = ld->links[i].ipv4.addr_list.addr[j].ip;
-
- int ipv4_mtu = ld->links[i].ipv4.mtu;
- if (ipv4_mtu > 0) {
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv4/mtu", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", ipv4_mtu);
-
- SRP_LOG_DBG("xpath_buffer: %s = %s", xpath_buffer, tmp_buffer);
-
- error = sr_set_item_str(session, xpath_buffer, tmp_buffer, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
- }
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv4/address[ip='%s']/ip", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
-
- // ip
- SRP_LOG_DBG("xpath_buffer: %s = %s", xpath_buffer, ld->links[i].ipv4.addr_list.addr[j].ip);
- error = sr_set_item_str(session, xpath_buffer, ld->links[i].ipv4.addr_list.addr[j].ip, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- // subnet
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", ld->links[i].ipv4.addr_list.addr[j].subnet);
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv4/address[ip='%s']/prefix-length", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("xpath_buffer: %s = %s", xpath_buffer, tmp_buffer);
-
- error = sr_set_item_str(session, xpath_buffer, tmp_buffer, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
- }
- }
-
- // list of ipv4 neighbors
- uint32_t ipv4_neigh_count = ld->links[i].ipv4.nbor_list.count;
- for (uint32_t j = 0; j < ipv4_neigh_count; j++) {
- char *ip_addr = ld->links[i].ipv4.nbor_list.nbor[j].ip;
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv4/neighbor[ip='%s']/ip", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
-
- // ip
- SRP_LOG_DBG("xpath_buffer: %s = %s", xpath_buffer, ld->links[i].ipv4.nbor_list.nbor[j].ip);
- error = sr_set_item_str(session, xpath_buffer, ld->links[i].ipv4.nbor_list.nbor[j].ip, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- // link-layer-address
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv4/neighbor[ip='%s']/link-layer-address", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("xpath_buffer: %s = %s", xpath_buffer, ld->links[i].ipv4.nbor_list.nbor[j].phys_addr);
-
- error = sr_set_item_str(session, xpath_buffer, ld->links[i].ipv4.nbor_list.nbor[j].phys_addr, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
- }
-
- // list of ipv6 addresses
- // enabled
- uint8_t ipv6_enabled = ld->links[i].ipv6.ip_data.enabled;
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/enabled", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("xpath_buffer: %s = %s", xpath_buffer, ipv6_enabled == 0 ? "false" : "true");
-
- error = sr_set_item_str(session, xpath_buffer, ipv6_enabled == 0 ? "false" : "true", NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- // forwarding
- uint8_t ipv6_forwarding = ld->links[i].ipv6.ip_data.forwarding;
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/forwarding", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("xpath_buffer: %s = %s", xpath_buffer, ipv6_forwarding == 0 ? "false" : "true");
-
- error = sr_set_item_str(session, xpath_buffer, ipv6_forwarding == 0 ? "false" : "true", NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- uint32_t ipv6_addr_count = ld->links[i].ipv6.ip_data.addr_list.count;
-
- for (uint32_t j = 0; j < ipv6_addr_count; j++) {
- if (ld->links[i].ipv6.ip_data.addr_list.addr[j].ip != NULL) { // in case we deleted an ip address it will be NULL
- char *ip_addr = ld->links[i].ipv6.ip_data.addr_list.addr[j].ip;
- int ipv6_mtu = ld->links[i].ipv6.ip_data.mtu;
-
- // mtu
- if (ipv6_mtu > 0 && ip_addr != NULL) {
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/mtu", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", ipv6_mtu);
-
- SRP_LOG_DBG("xpath_buffer: %s = %s", xpath_buffer, tmp_buffer);
-
- error = sr_set_item_str(session, xpath_buffer, tmp_buffer, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
- }
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/address[ip='%s']/ip", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
-
- // ip
- SRP_LOG_DBG("xpath_buffer: %s = %s", xpath_buffer, ld->links[i].ipv6.ip_data.addr_list.addr[j].ip);
-
- error = sr_set_item_str(session, xpath_buffer, ld->links[i].ipv6.ip_data.addr_list.addr[j].ip, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- // subnet
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", ld->links[i].ipv6.ip_data.addr_list.addr[j].subnet);
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/address[ip='%s']/prefix-length", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("xpath_buffer: %s = %s", xpath_buffer, tmp_buffer);
-
- error = sr_set_item_str(session, xpath_buffer, tmp_buffer, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
- }
- }
-
- // list of ipv6 neighbors
- uint32_t ipv6_neigh_count = ld->links[i].ipv6.ip_data.nbor_list.count;
- for (uint32_t j = 0; j < ipv6_neigh_count; j++) {
- char *ip_addr = ld->links[i].ipv6.ip_data.nbor_list.nbor[j].ip;
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/neighbor[ip='%s']/ip", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
-
- // ip
- SRP_LOG_DBG("xpath_buffer: %s = %s", xpath_buffer, ld->links[i].ipv6.ip_data.nbor_list.nbor[j].ip);
- error = sr_set_item_str(session, xpath_buffer, ld->links[i].ipv6.ip_data.nbor_list.nbor[j].ip, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- // link-layer-address
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/neighbor[ip='%s']/link-layer-address", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("xpath_buffer: %s = %s", xpath_buffer, ld->links[i].ipv6.ip_data.nbor_list.nbor[j].phys_addr);
-
- error = sr_set_item_str(session, xpath_buffer, ld->links[i].ipv6.ip_data.nbor_list.nbor[j].phys_addr, NULL, SR_EDIT_DEFAULT);
- if (error) {
- SRP_LOG_ERR("sr_set_item_str error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
- }
- }
- }
-
- error = sr_apply_changes(session, 0);
- if (error) {
- SRP_LOG_ERR("sr_apply_changes error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- return 0;
-error_out:
- return -1;
-}
-
-static int load_startup(sr_session_ctx_t *session, link_data_list_t *ld)
-{
- int error = 0;
- sr_val_t *vals = NULL;
- char *val = NULL;
- char val_buf[10] = {0};
- char *xpath = NULL;
- size_t i, val_count = 0;
-
- error = sr_get_items(session, "/ietf-interfaces:interfaces//.", 0, 0, &vals, &val_count);
- if (error != SR_ERR_OK) {
- SRP_LOG_ERR("sr_get_items error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- for (i = 0; i < val_count; ++i) {
- switch (vals[i].type) {
- case SR_STRING_T:
- case SR_IDENTITYREF_T:
- val = xstrdup(vals[i].data.string_val);
- break;
- case SR_BOOL_T:
- val = xstrdup(vals[i].data.bool_val ? "true" : "false");
- break;
- case SR_UINT8_T:
- case SR_UINT16_T:
- case SR_UINT32_T:
- error = snprintf(val_buf, sizeof(val_buf), "%d", vals[i].data.uint16_val);
- if (error < 0) {
- SRP_LOG_ERR("snprintf error");
- goto error_out;
- }
-
- val = xstrdup(val_buf);
- break;
- default:
- continue;
- }
-
- xpath = xstrdup(vals[i].xpath);
-
- error = set_config_value(xpath, val);
- if (error != 0) {
- SRP_LOG_ERR("set_config_value error (%d)", error);
- goto error_out;
- }
-
- FREE_SAFE(xpath);
- FREE_SAFE(val);
- }
-
- return 0;
-
-error_out:
- if (xpath != NULL) {
- FREE_SAFE(xpath);
- }
- if (val != NULL) {
- FREE_SAFE(val);
- }
- return -1;
-}
-
-void sr_plugin_cleanup_cb(sr_session_ctx_t *session, void *private_data)
-{
- sr_session_ctx_t *startup_session = (sr_session_ctx_t *) private_data;
-
- // copy the running datastore to startup one, in case we reboot
- sr_copy_config(startup_session, BASE_YANG_MODEL, SR_DS_RUNNING, 0);
-
- exit_application = 1;
-
- if (startup_session) {
- sr_session_stop(startup_session);
- }
-
- link_data_list_free(&link_data_list);
- if_state_list_free(&if_state_changes);
- nl_cache_mngr_free(link_manager);
-
- SRP_LOG_INF("plugin cleanup finished");
-}
-
-static int interfaces_module_change_cb(sr_session_ctx_t *session, uint32_t subscription_id, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data)
-{
- int error = 0;
- sr_change_iter_t *system_change_iter = NULL;
- sr_change_oper_t operation = SR_OP_CREATED;
- const struct lyd_node *node = NULL;
- const char *prev_value = NULL;
- const char *prev_list = NULL;
- int prev_default = false;
- char *node_xpath = NULL;
- char *node_value = NULL;
-
- SRP_LOG_INF("module_name: %s, xpath: %s, event: %d, request_id: %u", module_name, xpath, event, request_id);
-
- if (event == SR_EV_ABORT) {
- SRP_LOG_ERR("aborting changes for: %s", xpath);
- error = -1;
- goto error_out;
- }
-
- if (event == SR_EV_DONE) {
- error = sr_copy_config(session, BASE_YANG_MODEL, SR_DS_RUNNING, 0);
- if (error) {
- SRP_LOG_ERR("sr_copy_config error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
- }
-
- if (event == SR_EV_CHANGE) {
- error = sr_get_changes_iter(session, xpath, &system_change_iter);
- if (error) {
- SRP_LOG_ERR("sr_get_changes_iter error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
- while (sr_get_change_tree_next(session, system_change_iter, &operation, &node, &prev_value, &prev_list, &prev_default) == SR_ERR_OK) {
- node_xpath = lyd_path(node, LYD_PATH_STD, NULL, 0);
-
- if (node->schema->nodetype == LYS_LEAF || node->schema->nodetype == LYS_LEAFLIST) {
- node_value = xstrdup(lyd_get_value(node));
- }
-
- SRP_LOG_DBG("node_xpath: %s; prev_val: %s; node_val: %s; operation: %d", node_xpath, prev_value, node_value, operation);
-
- if (node->schema->nodetype == LYS_LEAF || node->schema->nodetype == LYS_LEAFLIST) {
- if (operation == SR_OP_CREATED || operation == SR_OP_MODIFIED) {
- error = set_config_value(node_xpath, node_value);
- if (error) {
- SRP_LOG_ERR("set_config_value error (%d)", error);
- goto error_out;
- }
- } else if (operation == SR_OP_DELETED) {
- // check if this is a system interface (e.g.: lo, wlan0, enp0s31f6 etc.)
- bool system_interface = false;
- error = check_system_interface(node_value, &system_interface);
- if (error) {
- SRP_LOG_ERR("check_system_interface error");
- goto error_out;
- }
-
- // check if system interface but also
- // check if parent-interface node (virtual interfaces can have a system interface as parent)
- if (system_interface && !(strstr(node_xpath, "/ietf-if-extensions:parent-interface") != NULL)) {
- SRP_LOG_ERR("Can't delete a system interface");
- FREE_SAFE(node_xpath);
- sr_free_change_iter(system_change_iter);
- return SR_ERR_INVAL_ARG;
- }
-
- error = delete_config_value(node_xpath, node_value);
- if (error) {
- SRP_LOG_ERR("delete_config_value error (%d)", error);
- goto error_out;
- }
- }
- }
- FREE_SAFE(node_xpath);
- FREE_SAFE(node_value);
- }
-
- error = update_link_info(&link_data_list, operation);
- if (error) {
- error = SR_ERR_CALLBACK_FAILED;
- SRP_LOG_ERR("update_link_info error");
- goto error_out;
- }
- }
- goto out;
-
-error_out:
- // nothing for now
-out:
- if (node_xpath != NULL) {
- FREE_SAFE(node_xpath);
- }
- if (node_value != NULL) {
- FREE_SAFE(node_value);
- }
- if (system_change_iter != NULL) {
- sr_free_change_iter(system_change_iter);
- }
-
- return error ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK;
-}
-
-int set_config_value(const char *xpath, const char *value)
-{
- int error = SR_ERR_OK;
- char *interface_node = NULL;
- char *interface_node_name = NULL;
- char *address_node_ip = NULL;
- char *neighbor_node_ip = NULL;
- sr_xpath_ctx_t state = {0};
- char *xpath_cpy = strdup(xpath);
-
- interface_node = sr_xpath_node_name((char *) xpath);
- if (interface_node == NULL) {
- SRP_LOG_ERR("sr_xpath_node_name error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
-
- interface_node_name = sr_xpath_key_value((char *) xpath, "interface", "name", &state);
-
- if (interface_node_name == NULL) {
- SRP_LOG_ERR("sr_xpath_key_value error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
-
- error = link_data_list_add(&link_data_list, interface_node_name);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_add error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
-
- if (strcmp(interface_node, "description") == 0) {
- // change desc
- error = link_data_list_set_description(&link_data_list, interface_node_name, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_description error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strcmp(interface_node, "type") == 0) {
- // change type
-
- // convert the iana-if-type to a "real" interface type which libnl understands
- char *interface_type = convert_ianaiftype((char *) value);
- if (interface_type == NULL) {
- SRP_LOG_ERR("convert_ianaiftype error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
-
- error = link_data_list_set_type(&link_data_list, interface_node_name, interface_type);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_type error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strcmp(interface_node, "enabled") == 0 && strstr(xpath_cpy, "ietf-ip:") == 0) {
- // change enabled
- error = link_data_list_set_enabled(&link_data_list, interface_node_name, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_enabled error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- }
- else if (strstr(xpath_cpy, "ietf-ip:ipv4") != 0) {
- SRP_LOG_DBG("ietf-ip:ipv4 change: '%s' on interface '%s'", interface_node, interface_node_name);
- memset(&state, 0, sizeof(sr_xpath_ctx_t));
- // check if an ip address has been added
- address_node_ip = sr_xpath_key_value((char *) xpath_cpy, "address", "ip", &state);
-
- // check if a neighbor has been added
- neighbor_node_ip = sr_xpath_key_value((char *) xpath_cpy, "neighbor", "ip", &state);
-
- if (address_node_ip != NULL) {
- // address has been added -> check for interface node (last node of the path) -> (prefix-length || netmask)
- if (strcmp(interface_node, "prefix-length") == 0) {
- error = link_data_list_add_ipv4_address(&link_data_list, interface_node_name, address_node_ip, (char *) value, ip_subnet_type_prefix_length);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_add_ipv4_address error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strcmp(interface_node, "netmask") == 0) {
- error = link_data_list_add_ipv4_address(&link_data_list, interface_node_name, address_node_ip, (char *) value, ip_subnet_type_netmask);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_add_ipv4_address error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- }
- } else if (neighbor_node_ip != NULL) {
- if (strcmp(interface_node, "link-layer-address") == 0) {
- error = link_data_list_add_ipv4_neighbor(&link_data_list, interface_node_name, neighbor_node_ip, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_add_ipv4_neighbor error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- }
- } else if (strcmp(interface_node, "enabled") == 0) {
- error = link_data_list_set_ipv4_enabled(&link_data_list, interface_node_name, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_ipv4_enabled error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strcmp(interface_node, "forwarding") == 0) {
- error = link_data_list_set_ipv4_forwarding(&link_data_list, interface_node_name, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_ipv4_forwarding error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strcmp(interface_node, "mtu") == 0) {
- error = link_data_list_set_ipv4_mtu(&link_data_list, interface_node_name, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_ipv4_mtu error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- }
- } else if (strstr(xpath_cpy, "ietf-ip:ipv6") != 0) {
- SRP_LOG_DBG("ietf-ip:ipv6 change: '%s' on interface '%s'", interface_node, interface_node_name);
- // check if an ip address has been added
- address_node_ip = sr_xpath_key_value((char *) xpath_cpy, "address", "ip", &state);
- // check if a neighbor has been added
- neighbor_node_ip = sr_xpath_key_value((char *) xpath_cpy, "neighbor", "ip", &state);
-
- if (address_node_ip != NULL) {
- // address has been added -> check for interface node (last node of the path) -> (prefix-length || netmask)
- if (strcmp(interface_node, "prefix-length") == 0) {
- error = link_data_list_add_ipv6_address(&link_data_list, interface_node_name, address_node_ip, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_add_ipv4_address error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strcmp(interface_node, "netmask") == 0) {
- error = link_data_list_add_ipv6_address(&link_data_list, interface_node_name, address_node_ip, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_add_ipv4_address error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- }
- } else if (neighbor_node_ip != NULL) {
- if (strcmp(interface_node, "link-layer-address") == 0) {
- error = link_data_list_add_ipv6_neighbor(&link_data_list, interface_node_name, neighbor_node_ip, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_add_ipv4_neighbor error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- }
- } else if (strcmp(interface_node, "enabled") == 0) {
- error = link_data_list_set_ipv6_enabled(&link_data_list, interface_node_name, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_ipv4_enabled error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strcmp(interface_node, "forwarding") == 0) {
- error = link_data_list_set_ipv6_forwarding(&link_data_list, interface_node_name, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_ipv4_forwarding error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strcmp(interface_node, "mtu") == 0) {
- error = link_data_list_set_ipv6_mtu(&link_data_list, interface_node_name, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_ipv4_mtu error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- }
- } else if (strcmp(interface_node, "ietf-if-extensions:parent-interface") == 0) {
- // change parent-interface
- error = link_data_list_set_parent(&link_data_list, interface_node_name, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_parent error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strstr(xpath_cpy, "ietf-if-extensions:encapsulation/ietf-if-vlan-encapsulation:dot1q-vlan/outer-tag/tag-type") != 0) {
- error = link_data_list_set_outer_tag_type(&link_data_list, interface_node_name, (char *)value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_parent error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strstr(xpath_cpy, "ietf-if-extensions:encapsulation/ietf-if-vlan-encapsulation:dot1q-vlan/outer-tag/vlan-id") != 0) {
- error = link_data_list_set_outer_vlan_id(&link_data_list, interface_node_name, (uint16_t) atoi(value));
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_parent error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strstr(xpath_cpy, "ietf-if-extensions:encapsulation/ietf-if-vlan-encapsulation:dot1q-vlan/second-tag/tag-type") != 0) {
- error = link_data_list_set_second_tag_type(&link_data_list, interface_node_name, (char *)value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_parent error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strstr(xpath_cpy, "ietf-if-extensions:encapsulation/ietf-if-vlan-encapsulation:dot1q-vlan/second-tag/vlan-id") != 0) {
- error = link_data_list_set_second_vlan_id(&link_data_list, interface_node_name, (uint16_t) atoi(value));
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_parent error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- }
-
- goto out;
-
-out:
- FREE_SAFE(xpath_cpy);
- if (xpath_cpy != NULL) {
- FREE_SAFE(xpath_cpy);
- }
-
- return error ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK;
-}
-
-// TODO: move this function to a helper functions file in utils
-static char *convert_ianaiftype(char *iana_if_type)
-{
- char *if_type = NULL;
-
- if (strstr(iana_if_type, "softwareLoopback") != NULL) {
- if_type = "lo";
- } else if (strstr(iana_if_type, "ethernetCsmacd") != NULL) {
- if_type = "eth";
- } else if (strstr(iana_if_type, "l2vlan") != NULL) {
- if_type = "vlan";
- } else if (strstr(iana_if_type, "other") != NULL) {
- if_type = "dummy";
- }
- // TODO: add support for more interface types
-
- return if_type;
-}
-
-int delete_config_value(const char *xpath, const char *value)
-{
- int error = SR_ERR_OK;
- char *interface_node = NULL;
- char *interface_node_name = NULL;
- sr_xpath_ctx_t state = {0};
- char *xpath_cpy = strdup(xpath);
-
- interface_node = sr_xpath_node_name((char *) xpath);
- if (interface_node == NULL) {
- SRP_LOG_ERR("sr_xpath_node_name error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
-
- interface_node_name = sr_xpath_key_value((char *) xpath, "interface", "name", &state);
- if (interface_node_name == NULL) {
- SRP_LOG_ERR("sr_xpath_key_value error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
-
- if (strcmp(interface_node, "name") == 0) {
- // mark for deletion
- error = link_data_list_set_delete(&link_data_list, interface_node_name, true);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_delete error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strcmp(interface_node, "description") == 0) {
- // set description to empty string
- error = link_data_list_set_description(&link_data_list, interface_node_name, "");
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_description error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strcmp(interface_node, "enabled") == 0) {
- // set enabled to false
- error = link_data_list_set_enabled(&link_data_list, interface_node_name, "");
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_enabled error");
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- } else if (strstr(xpath_cpy, "ietf-ip:ipv4") != 0) {
- memset(&state, 0, sizeof(sr_xpath_ctx_t));
-
- // check if an ip address needs to be removed
- char *address_node_ip = sr_xpath_key_value((char *) xpath_cpy, "address", "ip", &state);
-
- // check if a neighbor needs to be removed
- char *neighbor_node_ip = sr_xpath_key_value((char *) xpath_cpy, "neighbor", "ip", &state);
-
- if (address_node_ip != NULL) {
- if (strcmp(interface_node, "ip") == 0) {
- error = link_data_list_set_delete_ipv4_address(&link_data_list, interface_node_name, address_node_ip);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_delete_ipv4_address error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- }
- } else if (neighbor_node_ip != NULL) {
- if (strcmp(interface_node, "ip") == 0) {
- error = link_data_list_set_delete_ipv4_neighbor(&link_data_list, interface_node_name, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_delete_ipv4_neighbor error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- }
- }
- } else if (strstr(xpath_cpy, "ietf-ip:ipv6") != 0) {
- memset(&state, 0, sizeof(sr_xpath_ctx_t));
-
- // check if an ip address needs to be removed
- char *address_node_ip = sr_xpath_key_value((char *) xpath_cpy, "address", "ip", &state);
-
- // check if a neighbor needs to be removed
- char *neighbor_node_ip = sr_xpath_key_value((char *) xpath_cpy, "neighbor", "ip", &state);
-
- if (address_node_ip != NULL) {
- if (strcmp(interface_node, "ip") == 0) {
- error = link_data_list_set_delete_ipv6_address(&link_data_list, interface_node_name, address_node_ip);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_delete_ipv6_address error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- }
- } else if (neighbor_node_ip != NULL) {
- if (strcmp(interface_node, "ip") == 0) {
- error = link_data_list_set_delete_ipv6_neighbor(&link_data_list, interface_node_name, (char *) value);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_delete_ipv6_neighbor error (%d) : %s", error, strerror(error));
- error = SR_ERR_CALLBACK_FAILED;
- goto out;
- }
- }
- }
- }
-
-out:
- FREE_SAFE(xpath_cpy);
- return error ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK;
-}
-
-int update_link_info(link_data_list_t *ld, sr_change_oper_t operation)
-{
- struct nl_sock *socket = NULL;
- struct nl_cache *cache = NULL;
- struct rtnl_link *old = NULL;
- struct rtnl_link *old_vlan_qinq = NULL;
- struct rtnl_link *request = NULL;
-
- int error = SR_ERR_OK;
-
- socket = nl_socket_alloc();
- if (socket == NULL) {
- SRP_LOG_ERR("nl_socket_alloc error: invalid socket");
- goto out;
- }
-
- if ((error = nl_connect(socket, NETLINK_ROUTE)) != 0) {
- SRP_LOG_ERR("nl_connect error (%d): %s", error, nl_geterror(error));
- goto out;
- }
-
- error = rtnl_link_alloc_cache(socket, AF_UNSPEC, &cache);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_link_alloc_cache error (%d): %s", error, nl_geterror(error));
- goto out;
- }
-
- for (int i = 0; i < ld->count; i++) {
- char *name = ld->links[i].name;
- char *type = ld->links[i].type;
- char *enabled = ld->links[i].enabled;
- char *parent_interface = ld->links[i].extensions.parent_interface;
- uint16_t outer_vlan_id = ld->links[i].extensions.encapsulation.dot1q_vlan.outer_vlan_id;
- uint16_t second_vlan_id = ld->links[i].extensions.encapsulation.dot1q_vlan.second_vlan_id;
- char second_vlan_name[MAX_IF_NAME_LEN] = {0};
- bool delete = ld->links[i].delete;
-
- if (name == NULL ){//|| type == 0) {
- continue;
- }
-
- // handle vlan QinQ interfaces
- if (type != NULL && strcmp(type, "vlan") == 0 && second_vlan_id != 0) {
- error = snprintf(second_vlan_name, sizeof(second_vlan_name), "%s.%d", name, second_vlan_id);
- if (error < 0) {
- SRP_LOG_ERR("snprintf error");
- goto out;
- }
- }
-
- old = rtnl_link_get_by_name(cache, name);
- // check for second vlan (QinQ) as well
- old_vlan_qinq = rtnl_link_get_by_name(cache, second_vlan_name);
- request = rtnl_link_alloc();
-
- // delete link (interface) if marked for deletion
- if (delete) {
- // delete the link
- error = rtnl_link_delete(socket, old);
- if (error < 0) {
- SRP_LOG_ERR("rtnl_link_delete error (%d): %s", error, nl_geterror(error));
- goto out;
- }
-
- // free the link from link_data_list
- link_data_free(&ld->links[i]);
- // set delete to false
- ld->links[i].delete = false;
-
- // cleanup
- if (old != NULL) {
- rtnl_link_put(old);
- }
-
- if (old_vlan_qinq != NULL) {
- rtnl_link_put(old_vlan_qinq);
- }
-
- if (request != NULL) {
- rtnl_link_put(request);
- }
-
- // and continue
- continue;
- }
-
- // check if any ipv4 addresses need to be removed
- uint32_t ipv4_addr_count = ld->links[i].ipv4.addr_list.count;
-
- if (ipv4_addr_count > 0) {
- error = remove_ipv4_address(&ld->links[i].ipv4.addr_list, socket, old);
- if (error != 0) {
- SRP_LOG_ERR("remove_ipv4_address error (%d): %s", error, nl_geterror(error));
- goto out;
- }
- }
-
- // check if any ipv6 addresses need to be removed
- uint32_t ipv6_addr_count = ld->links[i].ipv6.ip_data.addr_list.count;
-
- if (ipv6_addr_count > 0) {
- error = remove_ipv6_address(&ld->links[i].ipv6.ip_data.addr_list, socket, old);
- if (error != 0) {
- SRP_LOG_ERR("remove_ipv6_address error (%d): %s", error, nl_geterror(error));
- goto out;
- }
- }
-
- if (old != NULL) {
- int index = rtnl_link_get_ifindex(old);
-
- // check if any ipv4 neighbors need to be removed
- uint32_t ipv4_neigh_count = ld->links[i].ipv4.nbor_list.count;
-
- if (ipv4_neigh_count > 0) {
- error = remove_neighbors(&ld->links[i].ipv4.nbor_list, socket, AF_INET, index);
- if (error != 0) {
- SRP_LOG_ERR("remove_neighbors error");
- goto out;
- }
- }
-
- // check if any ipv6 neighbors need to be removed
- uint32_t ipv6_neigh_count = ld->links[i].ipv6.ip_data.nbor_list.count;
-
- if (ipv6_neigh_count > 0) {
- error = remove_neighbors(&ld->links[i].ipv6.ip_data.nbor_list, socket, AF_INET6, index);
- if (error != 0) {
- SRP_LOG_ERR("remove_neighbors error");
- goto out;
- }
- }
- }
-
- // type
- if (type != NULL) {
- // handle vlan interfaces
- if (strcmp(type, "vlan") == 0) {
- // if second vlan id is present treat it as QinQ vlan
- if (second_vlan_id != 0) {
- // update the last-change state list
- uint8_t state = rtnl_link_get_operstate(request);
-
- if_state_list_add(&if_state_changes, state, second_vlan_name);
- if_state_list_add(&if_state_changes, state, name);
-
- // then create the interface with new parameters
- create_vlan_qinq(name, parent_interface, outer_vlan_id, second_vlan_id);
-
- } else if (second_vlan_id == 0) {
- // normal vlan interface
- error = rtnl_link_set_type(request, type);
- if (error < 0) {
- SRP_LOG_ERR("rtnl_link_set_type error (%d): %s", error, nl_geterror(error));
- goto out;
- }
- // if only the outer vlan is present, treat is an normal vlan
- int master_index = rtnl_link_name2i(cache, parent_interface);
- rtnl_link_set_link(request, master_index);
-
- error = rtnl_link_vlan_set_id(request, outer_vlan_id);
- }
- } else {
- error = rtnl_link_set_type(request, type);
- if (error < 0) {
- SRP_LOG_ERR("rtnl_link_set_type error (%d): %s", error, nl_geterror(error));
- goto out;
- }
- }
- }
-
- // enabled
- if (enabled != NULL) {
- if (strcmp(enabled, "true") == 0) {
- // set the interface to UP
- rtnl_link_set_flags(request, (unsigned int) rtnl_link_str2flags("up"));
- rtnl_link_set_operstate(request, IF_OPER_UP);
- } else {
- // set the interface to DOWN
- rtnl_link_unset_flags(request, (unsigned int) rtnl_link_str2flags("up"));
- rtnl_link_set_operstate(request, IF_OPER_DOWN);
- }
- }
-
- if (old != NULL) {
- // add ipv4/ipv6 options
- error = add_interface_ipv4(&ld->links[i], old, request, rtnl_link_get_ifindex(old));
- if (error != 0) {
- SRP_LOG_ERR("add_interface_ipv4 error");
- goto out;
- }
-
- error = add_interface_ipv6(&ld->links[i], old, request, rtnl_link_get_ifindex(old));
- if (error != 0) {
- SRP_LOG_ERR("add_interface_ipv6 error");
- goto out;
- }
-
- // the interface with name already exists, change it
- error = rtnl_link_change(socket, old, request, 0);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_link_change error (%d): %s", error, nl_geterror(error));
- goto out;
- }
- } else if (operation != SR_OP_DELETED) {
- // the interface doesn't exist
-
- // check if the interface is a system interface
- // non-virtual interfaces can't be created
- bool system_interface = false;
- error = check_system_interface(name, &system_interface);
- if (error) {
- SRP_LOG_ERR("check_system_interface error");
- error = -1;
- goto out;
- }
-
- if (system_interface || strcmp(type, "eth") == 0 || strcmp(type, "lo") == 0) {
- SRP_LOG_ERR("Can't create non-virtual interface %s of type: %s", name, type);
- error = -1;
- goto out;
- }
-
- // don't create if it's a QinQ vlan interface since it's already been created
- if (second_vlan_id == 0) {
- // update the last-change state list
- uint8_t state = rtnl_link_get_operstate(request);
-
- if_state_list_add(&if_state_changes, state, name);
-
- // set the new name
- rtnl_link_set_name(request, name);
-
- // add the interface
- // note: if type is not set, you can't add the new link
- error = rtnl_link_add(socket, request, NLM_F_CREATE);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_link_add error (%d): %s", error, nl_geterror(error));
- goto out;
- }
- }
-
- // in order to add ipv4/ipv6 options we first have to create the interface
- // and then get its interface index, because we need it inside add_interface_ipv4/ipv6
- // and don't want to set it manually...
- nl_cache_free(cache);
- error = rtnl_link_alloc_cache(socket, AF_UNSPEC, &cache);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_link_alloc_cache error (%d): %s", error, nl_geterror(error));
- goto out;
- }
-
- old = rtnl_link_get_by_name(cache, name);
- old_vlan_qinq = rtnl_link_get_by_name(cache, second_vlan_name);
-
- if (old != NULL) {
- error = add_interface_ipv4(&ld->links[i], old, request, rtnl_link_get_ifindex(old));
- if (error != 0) {
- SRP_LOG_ERR("add_interface_ipv4 error");
- goto out;
- }
-
- error = add_interface_ipv6(&ld->links[i], old, request, rtnl_link_get_ifindex(old));
- if (error != 0) {
- SRP_LOG_ERR("add_interface_ipv6 error");
- goto out;
- }
-
- error = rtnl_link_change(socket, old, request, 0);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_link_change error (%d): %s", error, nl_geterror(error));
- goto out;
- }
- }
-
- if (old_vlan_qinq != NULL) {
- error = add_interface_ipv4(&ld->links[i], old_vlan_qinq, request, rtnl_link_get_ifindex(old_vlan_qinq));
- if (error != 0) {
- SRP_LOG_ERR("add_interface_ipv4 error");
- goto out;
- }
-
- error = add_interface_ipv6(&ld->links[i], old_vlan_qinq, request, rtnl_link_get_ifindex(old_vlan_qinq));
- if (error != 0) {
- SRP_LOG_ERR("add_interface_ipv6 error");
- goto out;
- }
-
- error = rtnl_link_change(socket, old_vlan_qinq, request, 0);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_link_change error (%d): %s", error, nl_geterror(error));
- goto out;
- }
- }
- }
- rtnl_link_put(old);
- rtnl_link_put(old_vlan_qinq);
- rtnl_link_put(request);
- }
-
-out:
- nl_socket_free(socket);
- nl_cache_free(cache);
-
- return error;
-}
-
-static int remove_ipv4_address(ip_address_list_t *addr_list, struct nl_sock *socket, struct rtnl_link *old)
-{
- int error = 0;
- uint32_t addr_count = addr_list->count;
-
- // iterate through list of IPv4 addresses and check delete flag
- for (uint32_t j = 0; j < addr_count; j++) {
-
- if (addr_list->addr[j].delete == true) {
- struct rtnl_addr *addr = rtnl_addr_alloc();
- struct nl_addr *local_addr = nl_addr_alloc(32);
-
- error = nl_addr_parse(addr_list->addr[j].ip, AF_INET, &local_addr);
- if (error != 0) {
- SRP_LOG_ERR("nl_addr_parse error (%d): %s", error, nl_geterror(error));
- rtnl_addr_put(addr);
- nl_addr_put(local_addr);
- return -1;
- }
- int32_t if_index = rtnl_link_get_ifindex(old);
-
- nl_addr_set_prefixlen(local_addr, addr_list->addr[j].subnet);
-
- rtnl_addr_set_ifindex(addr, if_index);
-
- rtnl_addr_set_local(addr, local_addr);
-
- error = rtnl_addr_delete(socket, addr, 0);
- if (error < 0) {
- SRP_LOG_ERR("rtnl_addr_delete error (%d): %s", error, nl_geterror(error));
- rtnl_addr_put(addr);
- nl_addr_put(local_addr);
- return -1;
- }
-
- rtnl_addr_put(addr);
- nl_addr_put(local_addr);
-
- // remove this IP address from list
- ip_address_free(&addr_list->addr[j]);
- }
- }
-
- return 0;
-}
-
-int add_interface_ipv4(link_data_t *ld, struct rtnl_link *old, struct rtnl_link *req, int if_idx)
-{
- int error = 0;
- const char *ipv4_base = "/proc/sys/net/ipv4/conf";
- char *if_name = ld->name;
- ipv4_data_t *ipv4 = &ld->ipv4;
- ip_address_list_t *addr_ls = &ipv4->addr_list;
- ip_neighbor_list_t *neigh_ls = &ipv4->nbor_list;
- struct nl_sock *socket = NULL;
- struct nl_addr *local_addr = NULL;
- struct rtnl_addr *r_addr = NULL;
- struct rtnl_neigh *neigh = NULL;
- struct nl_addr *ll_addr = NULL;
- struct nl_cache *cache = NULL;
-
- // add ipv4 options from given link data to the req link object
- // also set forwarding options to the given files for a particular link
- // enabled
- // TODO: fix this
- // note: commented out because there is no disable_ipv4 file on arch (need to find workaround)
- /*
- error = write_to_proc_file(ipv4_base, if_name, "disable_ipv4", ipv4->enabled == 0);
- if (error != 0) {
- goto out;
- }*/
-
- // forwarding
- error = write_to_proc_file(ipv4_base, if_name, "forwarding", ipv4->forwarding);
- if (error != 0) {
- goto out;
- }
-
- // set mtu
- if (ipv4->mtu != 0) {
- rtnl_link_set_mtu(req, ipv4->mtu);
- }
-
- // address list
- socket = nl_socket_alloc();
- if (socket == NULL) {
- SRP_LOG_ERR("nl_socket_alloc error: invalid socket");
- goto out;
- }
-
- if ((error = nl_connect(socket, NETLINK_ROUTE)) != 0) {
- SRP_LOG_ERR("nl_connect error (%d): %s", error, nl_geterror(error));
- goto out;
- }
-
- for (uint i = 0; i < addr_ls->count; i++) {
- if (addr_ls->addr[i].ip == NULL) { // ip was deleted
- continue;
- }
- r_addr = rtnl_addr_alloc();
- local_addr = nl_addr_alloc(32);
- error = nl_addr_parse(addr_ls->addr[i].ip, AF_INET, &local_addr);
- if (error != 0) {
- rtnl_addr_put(r_addr);
- nl_addr_put(local_addr);
- SRP_LOG_ERR("nl_addr_parse error (%d): %s", error, nl_geterror(error));
- goto out;
- }
- nl_addr_set_prefixlen(local_addr, addr_ls->addr[i].subnet);
-
- // configure rtln_addr for a link
- rtnl_addr_set_ifindex(r_addr, if_idx);
- rtnl_addr_set_local(r_addr, local_addr);
-
- // send message
- rtnl_addr_add(socket, r_addr, 0);
-
- // Free the memory
- nl_addr_put(local_addr);
- rtnl_addr_put(r_addr);
- }
-
- for (uint i = 0; i < neigh_ls->count; i++) {
- if (neigh_ls->nbor[i].ip == NULL) { // neighbor was deleted
- continue;
- }
- neigh = rtnl_neigh_alloc();
- local_addr = nl_addr_alloc(32);
- ll_addr = nl_addr_alloc(32);
-
- error = nl_addr_parse(neigh_ls->nbor[i].ip, AF_INET, &local_addr);
- if (error != 0) {
- nl_addr_put(ll_addr);
- nl_addr_put(local_addr);
- rtnl_neigh_put(neigh);
- SRP_LOG_ERR("nl_addr_parse error (%d): %s", error, nl_geterror(error));
- goto out;
- }
-
- error = nl_addr_parse(neigh_ls->nbor[i].phys_addr, AF_LLC, &ll_addr);
- if (error != 0) {
- nl_addr_put(ll_addr);
- nl_addr_put(local_addr);
- rtnl_neigh_put(neigh);
- SRP_LOG_ERR("nl_addr_parse error (%d): %s", error, nl_geterror(error));
- goto out;
- }
-
- rtnl_neigh_set_ifindex(neigh, if_idx);
-
- // set dst and ll addr
- rtnl_neigh_set_lladdr(neigh, ll_addr);
- rtnl_neigh_set_dst(neigh, local_addr);
-
- error = rtnl_link_alloc_cache(socket, AF_UNSPEC, &cache);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_link_alloc_cache error (%d): %s", error, nl_geterror(error));
- nl_addr_put(ll_addr);
- nl_addr_put(local_addr);
- rtnl_neigh_put(neigh);
- goto out;
- }
-
- struct rtnl_neigh *tmp_neigh = rtnl_neigh_get(cache, if_idx, local_addr);
- int neigh_oper = NLM_F_CREATE;
-
- if (tmp_neigh != NULL) {
- // if the neighbor already exists, replace it
- // otherwise create it (NLM_F_CREATE)
- neigh_oper = NLM_F_REPLACE;
- }
-
- error = rtnl_neigh_add(socket, neigh, neigh_oper);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_neigh_add error (%d): %s", error, nl_geterror(error));
- nl_addr_put(ll_addr);
- nl_addr_put(local_addr);
- rtnl_neigh_put(neigh);
- goto out;
- }
-
- nl_addr_put(ll_addr);
- nl_addr_put(local_addr);
- rtnl_neigh_put(neigh);
- }
-
-out:
- nl_socket_free(socket);
- return error;
-}
-
-static int remove_ipv6_address(ip_address_list_t *addr_list, struct nl_sock *socket, struct rtnl_link *old)
-{
- int error = 0;
- uint32_t addr_count = addr_list->count;
-
- // iterate through list of IPv6 addresses and check delete flag
- if (addr_count > 0) {
- for (uint32_t j = 0; j < addr_count; j++) {
-
- if (addr_list->addr[j].delete == true) {
- struct rtnl_addr *addr = rtnl_addr_alloc();
- struct nl_addr *local_addr = nl_addr_alloc(32);
-
- error = nl_addr_parse(addr_list->addr[j].ip, AF_INET6, &local_addr);
- if (error != 0) {
- SRP_LOG_ERR("nl_addr_parse error (%d): %s", error, nl_geterror(error));
- rtnl_addr_put(addr);
- nl_addr_put(local_addr);
- return -1;
- }
- int32_t if_index = rtnl_link_get_ifindex(old);
-
- nl_addr_set_prefixlen(local_addr, addr_list->addr[j].subnet);
-
- rtnl_addr_set_ifindex(addr, if_index);
-
- rtnl_addr_set_local(addr, local_addr);
-
- error = rtnl_addr_delete(socket, addr, 0);
- if (error < 0) {
- SRP_LOG_ERR("rtnl_addr_delete error (%d): %s", error, nl_geterror(error));
- rtnl_addr_put(addr);
- nl_addr_put(local_addr);
- return -1;
- }
- rtnl_addr_put(addr);
- nl_addr_put(local_addr);
-
- // remove this IP address from list
- ip_address_free(&addr_list->addr[j]);
- }
- }
- }
-
- return 0;
-}
-
-int add_interface_ipv6(link_data_t *ld, struct rtnl_link *old, struct rtnl_link *req, int if_idx)
-{
- int error = 0;
- const char *ipv6_base = "/proc/sys/net/ipv6/conf";
- char *if_name = ld->name;
- ipv6_data_t *ipv6 = &ld->ipv6;
- ip_address_list_t *addr_ls = &ipv6->ip_data.addr_list;
- ip_neighbor_list_t *neigh_ls = &ipv6->ip_data.nbor_list;
- struct nl_sock *socket = NULL;
- struct nl_addr *local_addr = NULL;
- struct rtnl_addr *r_addr = NULL;
- struct rtnl_neigh *neigh = NULL;
- struct nl_addr *ll_addr = NULL;
- struct nl_cache *cache = NULL;
-
- // enabled
- error = write_to_proc_file(ipv6_base, if_name, "disable_ipv6", ipv6->ip_data.enabled == 0);
- if (error != 0) {
- goto out;
- }
-
- // forwarding
- error = write_to_proc_file(ipv6_base, if_name, "forwarding", ipv6->ip_data.forwarding);
- if (error != 0) {
- goto out;
- }
-
- // set mtu
- if (ipv6->ip_data.mtu != 0) {
- error = write_to_proc_file(ipv6_base, if_name, "mtu", ipv6->ip_data.mtu);
- if (error != 0) {
- goto out;
- }
- }
-
- // address list
- socket = nl_socket_alloc();
- if (socket == NULL) {
- SRP_LOG_ERR("nl_socket_alloc error: invalid socket");
- goto out;
- }
-
- if ((error = nl_connect(socket, NETLINK_ROUTE)) != 0) {
- SRP_LOG_ERR("nl_connect error (%d): %s", error, nl_geterror(error));
- goto out;
- }
-
- for (uint i = 0; i < addr_ls->count; i++) {
- if (addr_ls->addr[i].ip == NULL) { // ip was deleted
- continue;
- }
-
- r_addr = rtnl_addr_alloc();
- local_addr = nl_addr_alloc(32);
-
- error = nl_addr_parse(addr_ls->addr[i].ip, AF_INET6, &local_addr);
- if (error != 0) {
- rtnl_addr_put(r_addr);
- nl_addr_put(local_addr);
- goto out;
- }
- nl_addr_set_prefixlen(local_addr, addr_ls->addr[i].subnet);
-
- // configure rtln_addr for a link
- rtnl_addr_set_ifindex(r_addr, if_idx);
- rtnl_addr_set_local(r_addr, local_addr);
-
- if (ipv6->autoconf.temp_valid_lifetime != 0) {
- rtnl_addr_set_valid_lifetime(r_addr, ipv6->autoconf.temp_valid_lifetime);
- }
-
- if (ipv6->autoconf.temp_preffered_lifetime != 0) {
- rtnl_addr_set_preferred_lifetime(r_addr, ipv6->autoconf.temp_preffered_lifetime);
- }
-
- // send message
- rtnl_addr_add(socket, r_addr, 0);
-
- // Free the memory
- nl_addr_put(local_addr);
- rtnl_addr_put(r_addr);
- }
-
- for (uint i = 0; i < neigh_ls->count; i++) {
- if (neigh_ls->nbor[i].ip == NULL) { // neighbor was deleted
- continue;
- }
- neigh = rtnl_neigh_alloc();
- local_addr = nl_addr_alloc(32);
- ll_addr = nl_addr_alloc(32);
-
- error = nl_addr_parse(neigh_ls->nbor[i].ip, AF_INET6, &local_addr);
- if (error != 0) {
- nl_addr_put(ll_addr);
- nl_addr_put(local_addr);
- rtnl_neigh_put(neigh);
- goto out;
- }
-
- error = nl_addr_parse(neigh_ls->nbor[i].phys_addr, AF_LLC, &ll_addr);
- if (error != 0) {
- nl_addr_put(ll_addr);
- nl_addr_put(local_addr);
- rtnl_neigh_put(neigh);
- goto out;
- }
-
- rtnl_neigh_set_ifindex(neigh, if_idx);
-
- // set dst and ll addr
- rtnl_neigh_set_lladdr(neigh, ll_addr);
- rtnl_neigh_set_dst(neigh, local_addr);
-
- error = rtnl_link_alloc_cache(socket, AF_UNSPEC, &cache);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_link_alloc_cache error (%d): %s", error, nl_geterror(error));
- nl_addr_put(ll_addr);
- nl_addr_put(local_addr);
- rtnl_neigh_put(neigh);
- goto out;
- }
-
- struct rtnl_neigh *tmp_neigh = rtnl_neigh_get(cache, if_idx, local_addr);
- int neigh_oper = NLM_F_CREATE;
-
- if (tmp_neigh != NULL) {
- // if the neighbor already exists, replace it
- // otherwise create it (NLM_F_CREATE)
- neigh_oper = NLM_F_REPLACE;
- }
-
- error = rtnl_neigh_add(socket, neigh, neigh_oper);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_neigh_add error (%d): %s", error, nl_geterror(error));
- nl_addr_put(ll_addr);
- nl_addr_put(local_addr);
- rtnl_neigh_put(neigh);
- goto out;
- }
-
- nl_addr_put(ll_addr);
- nl_addr_put(local_addr);
- rtnl_neigh_put(neigh);
- }
-out:
- nl_socket_free(socket);
- return error;
-}
-
-static int remove_neighbors(ip_neighbor_list_t *nbor_list, struct nl_sock *socket, int addr_ver, int if_index)
-{
- int error = 0;
- // Iterate through list of neighbors and check delete flag
- for (uint32_t i = 0; i < nbor_list->count; i++) {
-
- if (nbor_list->nbor[i].delete == true) {
- // Allocate an empty neighbour object to be filled out with the attributes
- // matching the neighbour to be deleted. Alternatively a fully equipped
- // neighbour object out of a cache can be used instead.
- struct nl_addr *dst_addr = nl_addr_alloc(32);
- struct rtnl_neigh *neigh = rtnl_neigh_alloc();
-
- error = nl_addr_parse(nbor_list->nbor[i].ip, addr_ver, &dst_addr);
- if (error != 0) {
- SRP_LOG_ERR("nl_addr_parse error (%d): %s", error, nl_geterror(error));
- nl_addr_put(dst_addr);
- rtnl_neigh_put(neigh);
- return -1;
- }
- // Neighbours are uniquely identified by their interface index and
- // destination address, you may fill out other attributes but they
- // will have no influence.
- rtnl_neigh_set_ifindex(neigh, if_index);
- rtnl_neigh_set_dst(neigh, dst_addr);
-
- // Build the netlink message and send it to the kernel, the operation will
- // block until the operation has been completed. Alternatively the required
- // netlink message can be built using rtnl_neigh_build_delete_request()
- // to be sent out using nl_send_auto_complete().
- error = rtnl_neigh_delete(socket, neigh, NLM_F_ACK);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_neigh_delete error (%d): %s", error, nl_geterror(error));
- nl_addr_put(dst_addr);
- rtnl_neigh_put(neigh);
- return -1;
- }
-
- // Free the memory
- nl_addr_put(dst_addr);
- rtnl_neigh_put(neigh);
-
- // Remove neighbor from list
- ip_neighbor_free(&nbor_list->nbor[i]);
- }
- }
-
- return 0;
-}
-
-static int create_vlan_qinq(char *name, char *parent_interface, uint16_t outer_vlan_id, uint16_t second_vlan_id)
-{
- int error = 0;
- char cmd[CMD_LEN] = {0};
-
- // e.g.: # ip link add link eth0 name eth0.10 type vlan id 10 protocol 802.1ad
- error = snprintf(cmd, sizeof(cmd), "ip link add link %s name %s type vlan id %d protocol 802.1ad", parent_interface, name, outer_vlan_id);
- if (error < 0) {
- SRP_LOG_ERR("snprintf error");
- return -1;
- }
-
- error = system(cmd);
-
- // e.g.: # ip link add link eth0.10 name eth0.10.20 type vlan id 20
- error = snprintf(cmd, sizeof(cmd), "ip link add link %s name %s.%d type vlan id %d", name, name, second_vlan_id, second_vlan_id);
- if (error < 0) {
- SRP_LOG_ERR("snprintf error");
- return -1;
- }
-
- error = system(cmd);
-
- return 0;
-}
-
-static bool check_system_interface(const char *interface_name, bool *system_interface)
-{
- int error = 0;
- char *all_devices_cmd = "ls /sys/class/net";
- char *check_system_devices_cmd = "ls -l /sys/class/net";
- char line[CLASS_NET_LINE_LEN] = {0};
- int all_cnt = 0;
- int sys_cnt = 0;
- char *all_interfaces[LD_MAX_LINKS] = {0};
- char *system_interfaces[LD_MAX_LINKS] = {0};
- FILE *system_interface_check = NULL;
-
- system_interface_check = popen(all_devices_cmd, "r");
- if (system_interface_check == NULL) {
- SRP_LOG_WRN("could not execute %s", all_devices_cmd);
- *system_interface = false;
- error = -1;
- goto out;
- }
-
- // get all interfaces from /sys/class/net
- while (fgets(line, sizeof(line), system_interface_check) != NULL) {
- // remove newline char from line
- line[strlen(line) - 1] = '\0';
- all_interfaces[all_cnt] = strndup(line, strlen(line) + 1);
- all_cnt++;
- }
-
- pclose(system_interface_check);
- // reset everything to reuse it
- system_interface_check = NULL;
- memset(line, 0, CLASS_NET_LINE_LEN);
-
- system_interface_check = popen(check_system_devices_cmd, "r");
- if (system_interface_check == NULL) {
- SRP_LOG_WRN("could not execute %s", check_system_devices_cmd);
- *system_interface = false;
- error = -1;
- goto out;
- }
-
- // check if an interface is virtual or system
- while (fgets(line, sizeof(line), system_interface_check) != NULL) {
- // loopback device is virtual but handle it as a physical device here
- // because libnl won't let us delete it
- if (strstr(line, "/lo") != NULL ||
- (strstr(line, "/virtual/") == NULL &&
- strncmp(line, "total", strlen("total") != 0))) {
- // this is a system interface
-
- // add it to system_interfaces
- for (int i = 0; i < LD_MAX_LINKS; i++) {
- if (all_interfaces[i] != 0) {
- if (strstr(line, all_interfaces[i]) != NULL) {
- system_interfaces[sys_cnt] = strndup(all_interfaces[i], strlen(all_interfaces[i]) + 1);
- sys_cnt++;
- break;
- }
- }
- }
- }
- memset(line, 0, CLASS_NET_LINE_LEN);
- }
-
- for (int i = 0; i < LD_MAX_LINKS; i++) {
- if (system_interfaces[i] != 0) {
- if (strcmp(interface_name, system_interfaces[i]) == 0) {
- *system_interface = true;
- break;
- }
- }
- }
-
- // cleanup
- for (int i = 0; i < LD_MAX_LINKS; i++) {
- if (system_interfaces[i] != 0) {
- FREE_SAFE(system_interfaces[i]);
- }
-
- if (all_interfaces[i] != 0) {
- FREE_SAFE(all_interfaces[i]);
- }
- }
-
-out:
- if (system_interface_check) {
- pclose(system_interface_check);
- }
-
- return error;
-}
-
-int write_to_proc_file(const char *dir_path, char *interface, const char *fn, int val)
-{
- int error = 0;
- char tmp_buffer[PATH_MAX];
- FILE *fptr = NULL;
-
- error = snprintf(tmp_buffer, sizeof(tmp_buffer), "%s/%s/%s", dir_path, interface, fn);
- if (error < 0) {
- // snprintf error
- SRP_LOG_ERR("snprintf failed");
- goto out;
- }
-
- // snprintf returns return the number of bytes that are written
- // reset error to 0
- error = 0;
-
- fptr = fopen((const char *) tmp_buffer, "w");
-
- if (fptr != NULL) {
- fprintf(fptr, "%d", val);
- fclose(fptr);
- } else {
- SRP_LOG_ERR("failed to open %s: %s", tmp_buffer, strerror(errno));
- error = -1;
- goto out;
- }
-
-out:
- return error;
-}
-
-
-static int read_from_proc_file(const char *dir_path, char *interface, const char *fn, int *val)
-{
- int error = 0;
- char tmp_buffer[PATH_MAX];
- FILE *fptr = NULL;
- char tmp_val[2] = {0};
-
- error = snprintf(tmp_buffer, sizeof(tmp_buffer), "%s/%s/%s", dir_path, interface, fn);
- if (error < 0) {
- // snprintf error
- SRP_LOG_ERR("snprintf failed");
- goto out;
- }
-
- // snprintf returns return the number of bytes that are written
- // reset error to 0
- error = 0;
-
- fptr = fopen((const char *) tmp_buffer, "r");
-
- if (fptr != NULL) {
- fgets(tmp_val, sizeof(tmp_val), fptr);
-
- *val = atoi(tmp_val);
-
- fclose(fptr);
- } else {
- SRP_LOG_ERR("failed to open %s: %s", tmp_buffer, strerror(errno));
- error = -1;
- goto out;
- }
-
-out:
- return error;
-}
-
-static int read_from_sys_file(const char *dir_path, char *interface, int *val)
-{
- int error = 0;
- char tmp_buffer[PATH_MAX];
- FILE *fptr = NULL;
- char tmp_val[4] = {0};
-
- error = snprintf(tmp_buffer, sizeof(tmp_buffer), "%s/%s/type", dir_path, interface);
- if (error < 0) {
- // snprintf error
- SRP_LOG_ERR("snprintf failed");
- goto out;
- }
-
- // snprintf returns return the number of bytes that are written
- // reset error to 0
- error = 0;
-
- fptr = fopen((const char *) tmp_buffer, "r");
-
- if (fptr != NULL) {
- fgets(tmp_val, sizeof(tmp_val), fptr);
-
- *val = atoi(tmp_val);
-
- fclose(fptr);
- } else {
- SRP_LOG_ERR("failed to open %s: %s", tmp_buffer, strerror(errno));
- error = -1;
- goto out;
- }
-
-out:
- return error;
-}
-
-int add_existing_links(sr_session_ctx_t *session, link_data_list_t *ld)
-{
- int error = 0;
- struct nl_sock *socket = NULL;
- struct nl_cache *cache = NULL;
- struct rtnl_link *link = NULL;
- struct nl_cache *addr_cache = NULL;
- struct nl_cache *neigh_cache = NULL;
- struct rtnl_addr *addr = {0};
- char *name = NULL;
- char *description = NULL;
- char *type = NULL;
- char *enabled = NULL;
- char *parent_interface = NULL;
- uint16_t vlan_id = 0;
- unsigned int mtu = 0;
- char tmp_buffer[10] = {0};
- char parent_buffer[MAX_IF_NAME_LEN] = {0};
- int addr_family = 0;
- char addr_str[ADDR_STR_BUF_SIZE];
- char dst_addr_str[ADDR_STR_BUF_SIZE];
- char ll_addr_str[ADDR_STR_BUF_SIZE];
-
- socket = nl_socket_alloc();
- if (socket == NULL) {
- SRP_LOG_ERR("nl_socket_alloc error: invalid socket");
- goto error_out;
- }
-
- error = nl_connect(socket, NETLINK_ROUTE);
- if (error != 0) {
- SRP_LOG_ERR("nl_connect error (%d): %s", error, nl_geterror(error));
- goto error_out;
- }
-
- error = rtnl_link_alloc_cache(socket, AF_UNSPEC, &cache);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_link_alloc_cache error (%d): %s", error, nl_geterror(error));
- goto error_out;
- }
-
- link = (struct rtnl_link *) nl_cache_get_first(cache);
-
- while (link != NULL) {
- name = rtnl_link_get_name(link);
- if (name == NULL) {
- SRP_LOG_ERR("rtnl_link_get_name error");
- goto error_out;
- }
-
- error = get_interface_description(session, name, &description);
- if (error != 0) {
- SRP_LOG_ERR("get_interface_description error");
- // don't return in case of error
- // some interfaces may not have a description already set (wlan0, etc.)
- }
-
- type = rtnl_link_get_type(link);
- if (type == NULL) {
- /* rtnl_link_get_type() will return NULL for interfaces that were not
- * set with rtnl_link_set_type()
- *
- * get the type from: /sys/class/net//type
- */
- const char *path_to_sys = "/sys/class/net/";
- int type_id = 0;
-
- error = read_from_sys_file(path_to_sys, name, &type_id);
- if (error != 0) {
- SRP_LOG_ERR("read_from_sys_file error");
- goto error_out;
- }
-
- // values taken from: if_arp.h
- if (type_id == 1) {
- // eth interface
- type = "eth";
- } else if (type_id == 772) {
- // loopback interface
- type = "lo";
- }
- }
-
- // enabled
- uint8_t tmp_enabled = rtnl_link_get_operstate(link);
- // lo interface has state unknown, treat it as enabled
- // otherwise it will be set to down, and dns resolution won't work
- if (IF_OPER_UP == tmp_enabled || IF_OPER_UNKNOWN == tmp_enabled) {
- enabled = "true";
- } else if (IF_OPER_DOWN == tmp_enabled ) {
- enabled = "false";
- }
-
- // mtu
- mtu = rtnl_link_get_mtu(link);
-
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", mtu);
-
- // vlan
- if (rtnl_link_is_vlan(link)) {
- // parent interface
- int parent_index = rtnl_link_get_link(link);
- parent_interface = rtnl_link_i2name(cache, parent_index, parent_buffer, MAX_IF_NAME_LEN);
-
- // outer vlan id
- vlan_id = (uint16_t)rtnl_link_vlan_get_id(link);
- if (vlan_id <= 0) {
- SRP_LOG_ERR("couldn't get vlan ID");
- goto error_out;
- }
-
- // check if vlan_id in name, if it is this is the QinQ interface, skip it
- char *first = NULL;
- char *second = NULL;
-
- first = strchr(name, '.');
- second = strchr(first+1, '.');
-
- if (second != 0) {
- link = (struct rtnl_link *) nl_cache_get_next((struct nl_object *) link);
- continue;
- }
- }
-
- error = link_data_list_add(ld, name);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_add error");
- goto error_out;
- }
-
- if (description != NULL) {
- error = link_data_list_set_description(ld, name, description);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_description error");
- goto error_out;
- }
- }
-
- if (type != NULL) {
- error = link_data_list_set_type(ld, name, type);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_type error");
- goto error_out;
- }
- }
-
- error = link_data_list_set_enabled(ld, name, enabled);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_enabled error");
- goto error_out;
- }
-
- if (parent_interface != 0) {
- error = link_data_list_set_parent(ld, name, parent_interface);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_parent error");
- goto error_out;
- }
- }
-
- if (vlan_id != 0) {
- error = link_data_list_set_outer_vlan_id(ld, name, vlan_id);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_outer_vlan_id error");
- goto error_out;
- }
- }
-
- int if_index = rtnl_link_get_ifindex(link);
-
- // neighbors
- error = rtnl_neigh_alloc_cache(socket, &neigh_cache);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_neigh_alloc_cache error (%d): %s", error, nl_geterror(error));
- goto error_out;
- }
-
- int neigh_count = nl_cache_nitems(neigh_cache);
-
- struct nl_object *nl_neigh_object;
- nl_neigh_object = nl_cache_get_first(neigh_cache);
-
- for (int i=0; i < neigh_count; i++) {
- struct nl_addr *nl_dst_addr = rtnl_neigh_get_dst((struct rtnl_neigh *) nl_neigh_object);
-
- char *dst_addr = nl_addr2str(nl_dst_addr, dst_addr_str, sizeof(dst_addr_str));
- if (dst_addr == NULL) {
- SRP_LOG_ERR("nl_addr2str error");
- goto error_out;
- }
-
- struct rtnl_neigh *neigh = rtnl_neigh_get(neigh_cache, if_index, nl_dst_addr);
-
- if (neigh != NULL) {
- // get neigh state
- int neigh_state = rtnl_neigh_get_state(neigh);
-
- // skip neighs with no arp state
- if (NUD_NOARP == neigh_state) {
- nl_neigh_object = nl_cache_get_next(nl_neigh_object);
- continue;
- }
-
- int cur_neigh_index = rtnl_neigh_get_ifindex(neigh);
-
- if (if_index != cur_neigh_index) {
- nl_neigh_object = nl_cache_get_next(nl_neigh_object);
- continue;
- }
-
- struct nl_addr *ll_addr = rtnl_neigh_get_lladdr(neigh);
-
- char *ll_addr_s = nl_addr2str(ll_addr, ll_addr_str, sizeof(ll_addr_str));
- if (NULL == ll_addr_s) {
- SRP_LOG_ERR("nl_addr2str error");
- goto error_out;
- }
-
- // check if ipv4 or ipv6
- addr_family = rtnl_neigh_get_family(neigh);
-
- if (addr_family == AF_INET) {
- error = link_data_list_add_ipv4_neighbor(&link_data_list, name, dst_addr, ll_addr_s);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_add_ipv4_neighbor error (%d) : %s", error, strerror(error));
- goto error_out;
- }
- } else if (addr_family == AF_INET6) {
- error = link_data_list_add_ipv6_neighbor(&link_data_list, name, dst_addr, ll_addr_s);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_add_ipv6_neighbor error (%d) : %s", error, strerror(error));
- goto error_out;
- }
- }
- rtnl_neigh_put(neigh);
- }
- nl_neigh_object = nl_cache_get_next(nl_neigh_object);
- }
-
- nl_cache_free(neigh_cache);
- neigh_cache = NULL;
-
- error = rtnl_addr_alloc_cache(socket, &addr_cache);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_addr_alloc_cache error (%d): %s", error, nl_geterror(error));
- goto error_out;
- }
-
- // get ipv4 and ipv6 addresses
- int addr_count = nl_cache_nitems(addr_cache);
-
- struct nl_object *nl_object;
- nl_object = nl_cache_get_first(addr_cache);
-
- addr = (struct rtnl_addr *) nl_object;
-
- for (int i=0; i < addr_count; i++) {
- struct nl_addr *nl_addr_local = rtnl_addr_get_local(addr);
- if (nl_addr_local == NULL) {
- SRP_LOG_ERR("rtnl_addr_get_local error");
- goto error_out;
- }
-
- int cur_if_index = rtnl_addr_get_ifindex(addr);
-
- if (if_index != cur_if_index) {
- nl_object = nl_cache_get_next(nl_object);
- addr = (struct rtnl_addr *) nl_object;
- continue;
- }
-
- const char*addr_s = nl_addr2str(nl_addr_local, addr_str, sizeof(addr_str));
- if (NULL == addr_s) {
- SRP_LOG_ERR("nl_addr2str error");
- goto error_out;
- }
-
- char *str = xstrdup(addr_s);
-
- // get address
- char *token = strtok(str, "/");
- if (token == NULL) {
- SRP_LOG_ERR("couldn't parse ip address");
-
- FREE_SAFE(str);
- goto error_out;
- }
-
- char *address = xstrdup(token);
-
- // get subnet
- token = strtok(NULL, "/");
- if (token == NULL) {
- // the address exists
- // skip it
- // we didn't add this address
- // e.g.: ::1
- FREE_SAFE(str);
- FREE_SAFE(address);
- continue;
- }
-
- char *subnet = xstrdup(token);
-
- // check if ipv4 or ipv6
- addr_family = rtnl_addr_get_family(addr);
-
- if (addr_family == AF_INET) {
- // ipv4
- error = link_data_list_add_ipv4_address(&link_data_list, name, address, subnet, ip_subnet_type_prefix_length);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_add_ipv4_address error (%d) : %s", error, strerror(error));
-
- FREE_SAFE(str);
- FREE_SAFE(address);
- FREE_SAFE(subnet);
- goto error_out;
- }
-
- if (mtu > 0) {
- error = link_data_list_set_ipv4_mtu(&link_data_list, name, tmp_buffer);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_ipv4_mtu error (%d) : %s", error, strerror(error));
-
- FREE_SAFE(str);
- FREE_SAFE(address);
- FREE_SAFE(subnet);
- goto error_out;
- }
- }
-
- // enabled
- const char *ipv4_base = "/proc/sys/net/ipv4/conf";
- // TODO: figure out how to enable/disable ipv4
- // since disable_ipv4 doesn't exist in /proc/sys/net/ipv6/conf/interface_name
-
- // forwarding
- int ipv4_forwarding = 0;
-
- error = read_from_proc_file(ipv4_base, name, "forwarding", &ipv4_forwarding);
- if (error != 0) {
- FREE_SAFE(str);
- FREE_SAFE(address);
- FREE_SAFE(subnet);
- goto error_out;
- }
-
- error = link_data_list_set_ipv4_forwarding(&link_data_list, name, ipv4_forwarding == 0 ? "false" : "true");
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_ipv4_forwarding error (%d) : %s", error, strerror(error));
-
- FREE_SAFE(str);
- FREE_SAFE(address);
- FREE_SAFE(subnet);
- goto error_out;
- }
-
- } else if (addr_family == AF_INET6) {
- // ipv6
- error = link_data_list_add_ipv6_address(&link_data_list, name, address, subnet);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_add_ipv6_address error (%d) : %s", error, strerror(error));
-
- FREE_SAFE(str);
- FREE_SAFE(address);
- FREE_SAFE(subnet);
- goto error_out;
- }
-
- if (mtu > 0) {
- error = link_data_list_set_ipv6_mtu(&link_data_list, name, tmp_buffer);
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_ipv6_mtu error (%d) : %s", error, strerror(error));
-
- FREE_SAFE(str);
- FREE_SAFE(address);
- FREE_SAFE(subnet);
- goto error_out;
- }
- }
-
- // enabled
- const char *ipv6_base = "/proc/sys/net/ipv6/conf";
-
- int ipv6_enabled = 0;
-
- error = read_from_proc_file(ipv6_base, name, "disable_ipv6", &ipv6_enabled);
- if (error != 0) {
- FREE_SAFE(str);
- FREE_SAFE(address);
- FREE_SAFE(subnet);
- goto error_out;
- }
- // since we check the value of 'disable_ipv6' file, the ipv6_enabled should be reversed
- error = link_data_list_set_ipv6_enabled(&link_data_list, name, ipv6_enabled == 0 ? "true" : "false");
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_ipv6_enabled error (%d) : %s", error, strerror(error));
-
- FREE_SAFE(str);
- FREE_SAFE(address);
- FREE_SAFE(subnet);
- goto error_out;
- }
-
- // forwarding
- int ipv6_forwarding = 0;
-
- error = read_from_proc_file(ipv6_base, name, "forwarding", &ipv6_forwarding);
- if (error != 0) {
- FREE_SAFE(str);
- FREE_SAFE(address);
- FREE_SAFE(subnet);
- goto error_out;
- }
-
- error = link_data_list_set_ipv6_forwarding(&link_data_list, name, ipv6_forwarding == 0 ? "false" : "true");
- if (error != 0) {
- SRP_LOG_ERR("link_data_list_set_ipv6_forwarding error (%d) : %s", error, strerror(error));
-
- FREE_SAFE(str);
- FREE_SAFE(address);
- FREE_SAFE(subnet);
- goto error_out;
- }
- }
-
- nl_object = nl_cache_get_next(nl_object);
- addr = (struct rtnl_addr *) nl_object;
-
- FREE_SAFE(str);
- FREE_SAFE(address);
- FREE_SAFE(subnet);
- }
- nl_cache_free(addr_cache);
- addr_cache = NULL;
-
- link = (struct rtnl_link *) nl_cache_get_next((struct nl_object *) link);
-
- if (description != NULL) { // it was allocated in get_interface_description
- FREE_SAFE(description);
- }
- }
-
- rtnl_link_put(link);
-
- nl_socket_free(socket);
- nl_cache_free(cache);
-
- return 0;
-
-error_out:
- if (socket != NULL) {
- nl_socket_free(socket);
- }
-
- if (link != NULL) {
- rtnl_link_put(link);
- }
-
- nl_cache_free(cache);
-
- if (addr_cache != NULL) {
- nl_cache_free(addr_cache);
- }
-
- if (description != NULL) {
- FREE_SAFE(description);
- }
-
- return -1;
-}
-
-static int get_interface_description(sr_session_ctx_t *session, char *name, char **description)
-{
- int error = SR_ERR_OK;
- char path_buffer[PATH_MAX] = {0};
- sr_val_t *val = {0};
-
- // conjure description path for this interface
- // /ietf-interfaces:interfaces/interface[name='test_interface']/description
- error = snprintf(path_buffer, sizeof(path_buffer) / sizeof(char), "%s[name=\"%s\"]/description", INTERFACE_LIST_YANG_PATH, name);
- if (error < 0) {
- SRP_LOG_ERR("snprintf error");
- goto error_out;
- }
-
- // get the interface description value
- error = sr_get_item(session, path_buffer, 0, &val);
- if (error != SR_ERR_OK) {
- SRP_LOG_ERR("sr_get_item error (%d): %s", error, sr_strerror(error));
- goto error_out;
- }
-
- if (strlen(val->data.string_val) > 0) {
- *description = val->data.string_val;
- }
-
- return 0;
-
-error_out:
- return -1;
-}
-
-static int interfaces_state_data_cb(sr_session_ctx_t *session, uint32_t subscription_id, const char *module_name, const char *path, const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data)
-{
- int error = SR_ERR_OK;
- const struct ly_ctx *ly_ctx = NULL;
- struct nl_sock *socket = NULL;
- struct nl_cache *cache = NULL;
- struct rtnl_link *link = NULL;
- struct nl_addr *addr = NULL;
- struct rtnl_tc *tc = NULL;
- struct rtnl_qdisc *qdisc = NULL;
-
- int32_t tmp_if_index = 0;
- uint64_t tmp_len = 0;
- struct rtnl_link *tmp_link = NULL;
-
- char tmp_buffer[PATH_MAX] = {0};
- char xpath_buffer[PATH_MAX] = {0};
- char interface_path_buffer[PATH_MAX] = {0};
-
- if_state_t *tmp_ifs = NULL;
-
- unsigned int mtu = 0;
-
- struct {
- char *name;
- char *description;
- char *type;
- char *enabled;
- char *link_up_down_trap_enable;
- char *admin_status;
- const char *oper_status;
- struct tm *last_change;
- int32_t if_index;
- char *phys_address;
- struct {
- char *masters[LD_MAX_LINKS];
- uint32_t count;
- } higher_layer_if;
- uint64_t speed;
- struct {
- char *discontinuity_time;
- uint64_t in_octets;
- uint64_t in_unicast_pkts;
- uint64_t in_broadcast_pkts;
- uint64_t in_multicast_pkts;
- uint32_t in_discards;
- uint32_t in_errors;
- uint32_t in_unknown_protos;
- uint64_t out_octets;
- uint64_t out_unicast_pkts;
- uint64_t out_broadcast_pkts;
- uint64_t out_multicast_pkts;
- uint32_t out_discards;
- uint32_t out_errors;
- } statistics;
- } interface_data = {0};
-
- typedef struct {
- char *slave_name;
- char *master_names[LD_MAX_LINKS];
- uint32_t count;
- } master_t;
-
- typedef struct {
- master_t masters[LD_MAX_LINKS];
- uint32_t count;
- } master_list_t;
-
- master_list_t master_list = {0};
-
- typedef struct {
- char *master_name;
- char *slave_names[LD_MAX_LINKS];
- uint32_t count;
- } slave_t;
-
- typedef struct {
- slave_t slaves[LD_MAX_LINKS];
- uint32_t count;
- } slave_list_t;
-
- slave_list_t slave_list = {0};
-
- const char *OPER_STRING_MAP[] = {
- [IF_OPER_UNKNOWN] = "unknown",
- [IF_OPER_NOTPRESENT] = "not-present",
- [IF_OPER_DOWN] = "down",
- [IF_OPER_LOWERLAYERDOWN] = "lower-layer-down",
- [IF_OPER_TESTING] = "testing",
- [IF_OPER_DORMANT] = "dormant",
- [IF_OPER_UP] = "up",
- };
-
- if (*parent == NULL) {
- ly_ctx = sr_get_context(sr_session_get_connection(session));
- if (ly_ctx == NULL) {
- error = SR_ERR_CALLBACK_FAILED;
- goto error_out;
- }
- lyd_new_path(*parent, ly_ctx, request_xpath, NULL, 0, NULL);
- }
-
- socket = nl_socket_alloc();
- if (socket == NULL) {
- SRP_LOG_ERR("nl_socket_alloc error: invalid socket");
- goto error_out;
- }
-
- if ((error = nl_connect(socket, NETLINK_ROUTE)) != 0) {
- SRP_LOG_ERR("nl_connect error (%d): %s", error, nl_geterror(error));
- goto error_out;
- }
-
- error = rtnl_link_alloc_cache(socket, AF_UNSPEC, &cache);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_link_alloc_cache error (%d): %s", error, nl_geterror(error));
- goto error_out;
- }
-
- // collect all master interfaces
- link = (struct rtnl_link *) nl_cache_get_first(cache);
-
- while (link != NULL) {
- char *slave_name = rtnl_link_get_name(link);
-
- // higher-layer-if
- tmp_if_index = rtnl_link_get_master(link);
- while (tmp_if_index) {
- tmp_link = rtnl_link_get(cache, tmp_if_index);
-
- char *master_name = rtnl_link_get_name(tmp_link);
-
- tmp_len = strlen(master_name);
-
- interface_data.higher_layer_if.masters[interface_data.higher_layer_if.count] = xstrndup(master_name, tmp_len);
-
- interface_data.higher_layer_if.count++;
-
- tmp_if_index = rtnl_link_get_master(tmp_link);
- }
-
- if (interface_data.higher_layer_if.count > 0) {
- for (uint64_t i = 0; i < interface_data.higher_layer_if.count; i++) {
- char *master_name = interface_data.higher_layer_if.masters[i];
-
- tmp_len = strlen(slave_name);
- master_list.masters[master_list.count].slave_name = xstrndup(slave_name, tmp_len);
-
- tmp_len = strlen(master_name);
- master_list.masters[master_list.count].master_names[i] = xstrndup(master_name, tmp_len);
- }
-
- master_list.masters[master_list.count].count = interface_data.higher_layer_if.count;
- master_list.count++;
- }
-
- // continue to next link node
- link = (struct rtnl_link *) nl_cache_get_next((struct nl_object *) link);
- }
-
- // collect all slave interfaces
- link = (struct rtnl_link *) nl_cache_get_first(cache);
-
- while (link != NULL) {
- // lower-layer-if
- char *if_name = rtnl_link_get_name(link);
-
- bool break_out = false;
- for (uint64_t i = 0; i < master_list.count; i++) {
- for (uint64_t j = 0; j < master_list.masters[i].count; j++) {
- if (strcmp(master_list.masters[i].slave_name, master_list.masters[i].master_names[j]) == 0) {
- continue;
- }
-
- if (strcmp(master_list.masters[i].master_names[j], if_name) == 0) {
- SRP_LOG_DBG("Slave of interface %s: %s", if_name, master_list.masters[i].slave_name);
-
- tmp_len = strlen(if_name);
- slave_list.slaves[slave_list.count].master_name = xstrndup(if_name, tmp_len);
-
- tmp_len = strlen(master_list.masters[i].slave_name);
- slave_list.slaves[slave_list.count].slave_names[i] = xstrndup(master_list.masters[i].slave_name, tmp_len);
-
- slave_list.slaves[slave_list.count].count++;
-
- break_out = true;
- break;
- }
- }
- if (break_out) {
- slave_list.count++;
- break;
- }
- }
- // continue to next link node
- link = (struct rtnl_link *) nl_cache_get_next((struct nl_object *) link);
- }
-
- link = (struct rtnl_link *) nl_cache_get_first(cache);
- qdisc = rtnl_qdisc_alloc();
-
- while (link != NULL) {
- // get tc and set the link
- tc = TC_CAST(qdisc);
- rtnl_tc_set_link(tc, link);
-
- interface_data.name = rtnl_link_get_name(link);
-
- link_data_t *l = data_list_get_by_name(&link_data_list, interface_data.name);
- interface_data.description = l->description;
-
- interface_data.type = rtnl_link_get_type(link);
- interface_data.enabled = rtnl_link_get_operstate(link) == IF_OPER_UP ? "enabled" : "disabled";
- // interface_data.link_up_down_trap_enable = ?
- // interface_data.admin_status = ?
- interface_data.oper_status = OPER_STRING_MAP[rtnl_link_get_operstate(link)];
- interface_data.if_index = rtnl_link_get_ifindex(link);
-
- // last-change field
- tmp_ifs = if_state_list_get_by_if_name(&if_state_changes, interface_data.name);
- interface_data.last_change = (tmp_ifs->last_change != 0) ? localtime(&tmp_ifs->last_change) : NULL;
-
- // get_system_boot_time will change the struct tm which is held in interface_data.last_change if it's not NULL
- char system_time[DATETIME_BUF_SIZE] = {0};
- if (interface_data.last_change != NULL) {
- // convert it to human readable format here
- strftime(system_time, sizeof system_time, "%FT%TZ", interface_data.last_change);
- }
-
- // mac address
- addr = rtnl_link_get_addr(link);
- interface_data.phys_address = xmalloc(sizeof(char) * (MAC_ADDR_MAX_LENGTH + 1));
- nl_addr2str(addr, interface_data.phys_address, MAC_ADDR_MAX_LENGTH);
- interface_data.phys_address[MAC_ADDR_MAX_LENGTH] = 0;
-
- interface_data.speed = rtnl_tc_get_stat(tc, RTNL_TC_RATE_BPS);
-
- // stats:
- char system_boot_time[DATETIME_BUF_SIZE] = {0};
- error = get_system_boot_time(system_boot_time);
- if (error != 0) {
- SRP_LOG_ERR("get_system_boot_time error: %s", strerror(errno));
- goto error_out;
- }
- interface_data.statistics.discontinuity_time = system_boot_time;
-
- // gather interface statistics that are not accessable via netlink
- nic_stats_t nic_stats = {0};
- error = get_nic_stats(interface_data.name, &nic_stats);
- if (error != 0) {
- SRP_LOG_ERR("get_nic_stats error: %s", strerror(errno));
- }
-
- // Rx
- interface_data.statistics.in_octets = rtnl_link_get_stat(link, RTNL_LINK_RX_BYTES);
- interface_data.statistics.in_broadcast_pkts = nic_stats.rx_broadcast;
- interface_data.statistics.in_multicast_pkts = rtnl_link_get_stat(link, RTNL_LINK_MULTICAST);
- interface_data.statistics.in_unicast_pkts = nic_stats.rx_packets - nic_stats.rx_broadcast - interface_data.statistics.in_multicast_pkts;
-
- interface_data.statistics.in_discards = (uint32_t) rtnl_link_get_stat(link, RTNL_LINK_RX_DROPPED);
- interface_data.statistics.in_errors = (uint32_t) rtnl_link_get_stat(link, RTNL_LINK_RX_ERRORS);
- interface_data.statistics.in_unknown_protos = (uint32_t) rtnl_link_get_stat(link, RTNL_LINK_IP6_INUNKNOWNPROTOS);
-
- // Tx
- interface_data.statistics.out_octets = rtnl_link_get_stat(link, RTNL_LINK_TX_BYTES);
- interface_data.statistics.out_broadcast_pkts = nic_stats.tx_broadcast;
- interface_data.statistics.out_multicast_pkts = nic_stats.tx_multicast;
- interface_data.statistics.out_unicast_pkts = nic_stats.tx_packets - nic_stats.tx_broadcast - nic_stats.tx_multicast;
-
- interface_data.statistics.out_discards = (uint32_t) rtnl_link_get_stat(link, RTNL_LINK_TX_DROPPED);
- interface_data.statistics.out_errors = (uint32_t) rtnl_link_get_stat(link, RTNL_LINK_TX_ERRORS);
-
- snprintf(interface_path_buffer, sizeof(interface_path_buffer) / sizeof(char), "%s[name=\"%s\"]", INTERFACE_LIST_YANG_PATH, rtnl_link_get_name(link));
-
- // name
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/name", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- SRP_LOG_DBG("%s = %s", xpath_buffer, interface_data.name);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, interface_data.name, LYD_ANYDATA_STRING, 0);
-
- // description
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/description", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- SRP_LOG_DBG("%s = %s", xpath_buffer, interface_data.description);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, interface_data.description, LYD_ANYDATA_STRING, 0);
-
- // type
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/type", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- SRP_LOG_DBG("%s = %s", xpath_buffer, interface_data.type);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, interface_data.type, LYD_ANYDATA_STRING, 0);
-
- // oper-status
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/oper-status", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- SRP_LOG_DBG("%s = %s", xpath_buffer, interface_data.oper_status);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, (char *) interface_data.oper_status, LYD_ANYDATA_STRING, 0);
-
- // last-change -> only if changed at one point
- if (interface_data.last_change != NULL) {
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/last-change", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- SRP_LOG_DBG("%s = %s", xpath_buffer, interface_data.type);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, system_time, LYD_ANYDATA_STRING, 0);
- } else {
- // default value of last-change should be system boot time
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/last-change", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- SRP_LOG_DBG("%s = %s", xpath_buffer, interface_data.type);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, system_boot_time, LYD_ANYDATA_STRING, 0);
- }
-
- // if-index
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/if-index", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- SRP_LOG_DBG("%s = %d", xpath_buffer, interface_data.if_index);
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", interface_data.if_index);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // phys-address
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/phys-address", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- SRP_LOG_DBG("%s = %s", xpath_buffer, interface_data.phys_address);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, interface_data.phys_address, LYD_ANYDATA_STRING, 0);
-
- // speed
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/speed", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- SRP_LOG_DBG("%s = %s", xpath_buffer, interface_data.speed);
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%lu", interface_data.speed);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // higher-layer-if
- for (uint64_t i = 0; i < master_list.count; i++) {
- if (strcmp(interface_data.name, master_list.masters[i].slave_name) == 0) {
- for (uint64_t j = 0; j < master_list.masters[i].count; j++) {
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/higher-layer-if", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("%s += %s", xpath_buffer, master_list.masters[i].master_names[j]);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, master_list.masters[i].master_names[j], LYD_ANYDATA_STRING, 0);
-
- FREE_SAFE(interface_data.higher_layer_if.masters[i]);
- }
- }
- }
-
- // lower-layer-if
- for (uint64_t i = 0; i < slave_list.count; i++) {
- if (strcmp(interface_data.name, slave_list.slaves[i].master_name) == 0) {
- for (uint64_t j = 0; j < slave_list.slaves[i].count; j++) {
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/lower-layer-if", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("%s += %s", xpath_buffer, slave_list.slaves[i].slave_names[j]);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, slave_list.slaves[i].slave_names[j], LYD_ANYDATA_STRING, 0);
- }
- }
- }
-
- // ietf-ip
- // mtu
- mtu = rtnl_link_get_mtu(link);
-
- // list of ipv4 addresses
- for (uint32_t i = 0; i < link_data_list.count; i++) {
- if (link_data_list.links[i].name != NULL) { // in case we deleted a link it will be NULL
- if (strcmp(link_data_list.links[i].name, interface_data.name ) == 0) {
-
- // enabled
- // TODO
-
- // forwarding
- uint8_t ipv4_forwarding = link_data_list.links[i].ipv4.forwarding;
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv4/forwarding", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("%s = %d", xpath_buffer, ipv4_forwarding);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, ipv4_forwarding == 0 ? "false" : "true", LYD_ANYDATA_STRING, 0);
-
- uint32_t ipv4_addr_count = link_data_list.links[i].ipv4.addr_list.count;
-
- for (uint32_t j = 0; j < ipv4_addr_count; j++) {
- if (link_data_list.links[i].ipv4.addr_list.addr[j].ip != NULL) { // in case we deleted an ip address it will be NULL
- char *ip_addr = link_data_list.links[i].ipv4.addr_list.addr[j].ip;
-
- if (mtu > 0) {
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv4/mtu", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", mtu);
- SRP_LOG_DBG("%s = %s", xpath_buffer, tmp_buffer);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
- }
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv4/address[ip='%s']/ip", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
- // ip
- SRP_LOG_DBG("%s = %s", xpath_buffer, link_data_list.links[i].ipv4.addr_list.addr[j].ip);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, link_data_list.links[i].ipv4.addr_list.addr[j].ip, LYD_ANYDATA_STRING, 0);
-
- // subnet
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", link_data_list.links[i].ipv4.addr_list.addr[j].subnet);
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv4/address[ip='%s']/prefix-length", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("%s = %s", xpath_buffer, tmp_buffer);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
- }
- }
-
- // neighbors
- uint32_t ipv4_neigh_count = link_data_list.links[i].ipv4.nbor_list.count;
-
- for (uint32_t j = 0; j < ipv4_neigh_count; j++) {
- if (link_data_list.links[i].ipv4.nbor_list.nbor[j].ip != NULL) { // in case we deleted an ip address it will be NULL
- char *ip_addr = link_data_list.links[i].ipv4.nbor_list.nbor[j].ip;
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv4/neighbor[ip='%s']/ip", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
- // ip
- SRP_LOG_DBG("%s = %s", xpath_buffer, link_data_list.links[i].ipv4.nbor_list.nbor[j].ip);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, link_data_list.links[i].ipv4.nbor_list.nbor[j].ip, LYD_ANYDATA_STRING, 0);
-
- // link-layer-address
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv4/neighbor[ip='%s']/link-layer-address", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("%s = %s", xpath_buffer, link_data_list.links[i].ipv4.nbor_list.nbor[j].phys_addr);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, link_data_list.links[i].ipv4.nbor_list.nbor[j].phys_addr, LYD_ANYDATA_STRING, 0);
- }
- }
- }
- }
- }
-
- // list of ipv6 addresses
- for (uint32_t i = 0; i < link_data_list.count; i++) {
- if (link_data_list.links[i].name != NULL) { // in case we deleted a link it will be NULL
- if (strcmp(link_data_list.links[i].name, interface_data.name ) == 0) {
-
- // enabled
- uint8_t ipv6_enabled = link_data_list.links[i].ipv6.ip_data.enabled;
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/enabled", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("%s = %d", xpath_buffer, ipv6_enabled);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, ipv6_enabled == 0 ? "false" : "true", LYD_ANYDATA_STRING, 0);
-
- // forwarding
- uint8_t ipv6_forwarding = link_data_list.links[i].ipv6.ip_data.forwarding;
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/forwarding", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("%s = %d", xpath_buffer, ipv6_forwarding);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, ipv6_forwarding == 0 ? "false" : "true", LYD_ANYDATA_STRING, 0);
-
- uint32_t ipv6_addr_count = link_data_list.links[i].ipv6.ip_data.addr_list.count;
-
- for (uint32_t j = 0; j < ipv6_addr_count; j++) {
- if (link_data_list.links[i].ipv6.ip_data.addr_list.addr[j].ip != NULL) { // in case we deleted an ip address it will be NULL
- char *ip_addr = link_data_list.links[i].ipv6.ip_data.addr_list.addr[j].ip;
-
- // mtu
- if (mtu > 0 && ip_addr != NULL) {
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/mtu", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", mtu);
- SRP_LOG_DBG("%s = %s", xpath_buffer, tmp_buffer);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
- }
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/address[ip='%s']/ip", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
- // ip
- SRP_LOG_DBG("%s = %s", xpath_buffer, link_data_list.links[i].ipv6.ip_data.addr_list.addr[j].ip);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, link_data_list.links[i].ipv6.ip_data.addr_list.addr[j].ip, LYD_ANYDATA_STRING, 0);
-
- // subnet
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", link_data_list.links[i].ipv6.ip_data.addr_list.addr[j].subnet);
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/address[ip='%s']/prefix-length", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("%s = %s", xpath_buffer, tmp_buffer);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
- }
- }
-
- // neighbors
- uint32_t ipv6_neigh_count = link_data_list.links[i].ipv6.ip_data.nbor_list.count;
-
- for (uint32_t j = 0; j < ipv6_neigh_count; j++) {
- if (link_data_list.links[i].ipv6.ip_data.nbor_list.nbor[j].ip != NULL) { // in case we deleted an ip address it will be NULL
- char *ip_addr = link_data_list.links[i].ipv6.ip_data.nbor_list.nbor[j].ip;
-
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/neighbor[ip='%s']/ip", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
- // ip
- SRP_LOG_DBG("%s = %s", xpath_buffer, link_data_list.links[i].ipv6.ip_data.nbor_list.nbor[j].ip);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, link_data_list.links[i].ipv6.ip_data.nbor_list.nbor[j].ip, LYD_ANYDATA_STRING, 0);
-
- // link-layer-address
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/ietf-ip:ipv6/neighbor[ip='%s']/link-layer-address", interface_path_buffer, ip_addr);
- if (error < 0) {
- goto error_out;
- }
-
- SRP_LOG_DBG("%s = %s", xpath_buffer, link_data_list.links[i].ipv6.ip_data.nbor_list.nbor[j].phys_addr);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, link_data_list.links[i].ipv6.ip_data.nbor_list.nbor[j].phys_addr, LYD_ANYDATA_STRING, 0);
- }
- }
- }
- }
- }
-
- // stats:
- // discontinuity-time
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/discontinuity-time", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- SRP_LOG_DBG("%s = %s", xpath_buffer, interface_data.statistics.discontinuity_time);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, interface_data.statistics.discontinuity_time, LYD_ANYDATA_STRING, 0);
-
- // in-octets
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/in-octets", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%lu", interface_data.statistics.in_octets);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // in-unicast-pkts
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/in-unicast-pkts", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%lu", interface_data.statistics.in_unicast_pkts);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // in-broadcast-pkts
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/in-broadcast-pkts", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%lu", interface_data.statistics.in_broadcast_pkts);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // in-multicast-pkts
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/in-multicast-pkts", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%lu", interface_data.statistics.in_multicast_pkts);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // in-discards
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/in-discards", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", interface_data.statistics.in_discards);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // in-errors
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/in-errors", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", interface_data.statistics.in_errors);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // in-unknown-protos
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/in-unknown-protos", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", interface_data.statistics.in_unknown_protos);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // out-octets
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/out-octets", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%lu", interface_data.statistics.out_octets);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // out-unicast-pkts
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/out-unicast-pkts", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%lu", interface_data.statistics.out_unicast_pkts);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // out-broadcast-pkts
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/out-broadcast-pkts", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%lu", interface_data.statistics.out_broadcast_pkts);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // out-multicast-pkts
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/out-multicast-pkts", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%lu", interface_data.statistics.out_multicast_pkts);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // out-discards
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/out-discards", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", interface_data.statistics.out_discards);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // out-errors
- error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/statistics/out-errors", interface_path_buffer);
- if (error < 0) {
- goto error_out;
- }
- snprintf(tmp_buffer, sizeof(tmp_buffer), "%u", interface_data.statistics.out_errors);
- lyd_new_path(*parent, ly_ctx, xpath_buffer, tmp_buffer, LYD_ANYDATA_STRING, 0);
-
- // free all allocated data
- FREE_SAFE(interface_data.phys_address);
-
- // continue to next link node
- link = (struct rtnl_link *) nl_cache_get_next((struct nl_object *) link);
- }
-
- rtnl_qdisc_put(qdisc);
- nl_cache_free(cache);
-
- error = SR_ERR_OK; // set error to OK, since it will be modified by snprintf
-
- goto out;
-
-error_out:
- error = SR_ERR_CALLBACK_FAILED;
-
-out:
- for (uint64_t i = 0; i < master_list.count; i++) {
- for (uint64_t j = 0; j < master_list.masters[i].count; j++) {
- FREE_SAFE(master_list.masters[i].master_names[j]);
- }
- FREE_SAFE(master_list.masters[i].slave_name);
- }
-
- for (uint64_t i = 0; i < slave_list.count; i++) {
- for (uint64_t j = 0; j < slave_list.slaves[i].count; j++) {
- FREE_SAFE(slave_list.slaves[i].slave_names[j]);
- }
- FREE_SAFE(slave_list.slaves[i].master_name);
- }
-
- nl_socket_free(socket);
- return error ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK;
-}
-
-static int get_system_boot_time(char boot_datetime[])
-{
- time_t now = 0;
- struct tm *ts = {0};
- struct sysinfo s_info = {0};
- time_t uptime_seconds = 0;
-
- now = time(NULL);
-
- ts = localtime(&now);
- if (ts == NULL)
- return -1;
-
- if (sysinfo(&s_info) != 0)
- return -1;
-
- uptime_seconds = s_info.uptime;
-
- time_t diff = now - uptime_seconds;
-
- ts = localtime(&diff);
- if (ts == NULL)
- return -1;
-
- /* must satisfy constraint (type yang:date-and-time):
- "\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-]\d{2}:\d{2})"
- TODO: Add support for:
- - 2021-02-09T06:02:39.234+01:00
- - 2021-02-09T06:02:39.234Z
- - 2021-02-09T06:02:39+11:11
- */
-
- strftime(boot_datetime, DATETIME_BUF_SIZE, "%FT%TZ", ts);
-
- return 0;
-}
-
-static int init_state_changes(void)
-{
- int error = 0;
- struct nl_sock *socket = NULL;
- struct nl_cache *cache = NULL;
- struct rtnl_link *link = NULL;
- if_state_t *tmp_st = NULL;
- pthread_attr_t attr;
-
- uint if_cnt = 0;
-
- struct {
- pthread_t *data;
- uint count;
- } thread_ls;
-
- pthread_t manager_thread;
-
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, 1);
-
- socket = nl_socket_alloc();
- if (socket == NULL) {
- SRP_LOG_ERR("nl_socket_alloc error: invalid socket");
- return -1;
- }
-
- if ((error = nl_connect(socket, NETLINK_ROUTE)) != 0) {
- SRP_LOG_ERR("nl_connect error (%d): %s", error, nl_geterror(error));
- goto error_out;
- }
-
- error = rtnl_link_alloc_cache(socket, AF_UNSPEC, &cache);
- if (error != 0) {
- SRP_LOG_ERR("rtnl_link_alloc_cache error (%d): %s", error, nl_geterror(error));
- goto error_out;
- }
-
- link = (struct rtnl_link *) nl_cache_get_first(cache);
-
- while (link != NULL) {
- ++if_cnt;
-
- link = (struct rtnl_link *) nl_cache_get_next((struct nl_object *) link);
- }
-
- // allocate a list to contain if_cnt number of interface states
- if_state_list_alloc(&if_state_changes, if_cnt);
-
- thread_ls.data = (pthread_t *) malloc(sizeof(pthread_t) * if_cnt);
- thread_ls.count = if_cnt;
-
- link = (struct rtnl_link *) nl_cache_get_first(cache);
- if_cnt = 0;
-
- while (link != NULL) {
- tmp_st = if_state_list_get(&if_state_changes, if_cnt);
- if (tmp_st) {
- tmp_st->state = rtnl_link_get_operstate(link);
-
- char *tmp_name = NULL;
- tmp_name = rtnl_link_get_name(link);
-
- size_t len = strlen(tmp_name);
- tmp_st->name = xcalloc(len + 1, sizeof(char));
- strncpy(tmp_st->name, tmp_name, len);
- tmp_st->name[len] = '\0';
- }
-
- ++if_cnt;
- link = (struct rtnl_link *) nl_cache_get_next((struct nl_object *) link);
- }
-
- error = nl_cache_mngr_alloc(NULL, NETLINK_ROUTE, 0, &link_manager);
- if (error != 0) {
- SRP_LOG_ERR("nl_cache_mngr_alloc failed (%d): %s", error, nl_geterror(error));
- goto error_out;
- }
-
- error = nl_cache_mngr_add(link_manager, "route/link", cache_change_cb, NULL, &link_cache);
- if (error != 0) {
- SRP_LOG_ERR("nl_cache_mngr_add failed (%d): %s", error, nl_geterror(error));
- goto error_out;
- }
-
- pthread_create(&manager_thread, NULL, manager_thread_cb, 0);
-
- pthread_detach(manager_thread);
-
-error_out:
-
- // clear libnl data
- nl_cache_free(cache);
- nl_socket_free(socket);
-
- // free tmp struct
- if (thread_ls.count) {
- free(thread_ls.data);
- }
- return error;
-}
-
-static void cache_change_cb(struct nl_cache *cache, struct nl_object *obj, int val, void *arg)
-{
- struct rtnl_link *link = NULL;
- char *name = NULL;
- if_state_t *tmp_st = NULL;
- uint8_t tmp_state = 0;
-
- SRP_LOG_DBG("entered cb function for a link manager");
-
- link = (struct rtnl_link *) nl_cache_get_first(cache);
-
- while (link != NULL) {
- name = rtnl_link_get_name(link);
- tmp_st = if_state_list_get_by_if_name(&if_state_changes, name);
- tmp_state = rtnl_link_get_operstate(link);
-
- if (tmp_state != tmp_st->state) {
- SRP_LOG_DBG("Interface %s changed operstate from %d to %d", name, tmp_st->state, tmp_state);
- tmp_st->state = tmp_state;
- tmp_st->last_change = time(NULL);
- }
- link = (struct rtnl_link *) nl_cache_get_next((struct nl_object *) link);
- }
-}
-
-static void *manager_thread_cb(void *data)
-{
- do {
- nl_cache_mngr_data_ready(link_manager);
- sleep(1);
- } while (exit_application == 0);
-
- return NULL;
-}
-
-#ifndef PLUGIN
-#include
-#include
-
-static void sigint_handler(__attribute__((unused)) int signum);
-
-int main(void)
-{
- int error = SR_ERR_OK;
- sr_conn_ctx_t *connection = NULL;
- sr_session_ctx_t *session = NULL;
- void *private_data = NULL;
-
- sr_log_stderr(SR_LL_DBG);
-
- error = sr_connect(SR_CONN_DEFAULT, &connection);
- if (error) {
- SRP_LOG_ERR("sr_connect error (%d): %s", error, sr_strerror(error));
- goto out;
- }
-
- error = sr_session_start(connection, SR_DS_RUNNING, &session);
- if (error) {
- SRP_LOG_ERR("sr_session_start error (%d): %s", error, sr_strerror(error));
- goto out;
- }
-
- error = sr_plugin_init_cb(session, &private_data);
- if (error) {
- SRP_LOG_ERR("sr_plugin_init_cb error");
- goto out;
- }
-
- signal(SIGINT, sigint_handler);
- signal(SIGPIPE, SIG_IGN);
- while (!exit_application) {
- sleep(1);
- }
-
-out:
- sr_plugin_cleanup_cb(session, private_data);
- sr_disconnect(connection);
-
- pthread_exit(0);
-
- return error ? -1 : 0;
-}
-
-static void sigint_handler(__attribute__((unused)) int signum)
-{
- SRP_LOG_INF("Sigint called, exiting...");
- exit_application = 1;
-}
-
-#endif
diff --git a/src/interfaces/ip_data.c b/src/interfaces/ip_data.c
deleted file mode 100644
index 303e315d..00000000
--- a/src/interfaces/ip_data.c
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * telekom / sysrepo-plugin-interfaces
- *
- * This program is made available under the terms of the
- * BSD 3-Clause license which is available at
- * https://opensource.org/licenses/BSD-3-Clause
- *
- * SPDX-FileCopyrightText: 2021 Deutsche Telekom AG
- * SPDX-FileContributor: Sartura Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "ip_data.h"
-#include "utils/memory.h"
-#include
-#include
-#include
-
-void ip_data_init(ip_data_t *ip)
-{
- ip->enabled = 0;
- ip->forwarding = 0;
- ip->mtu = 0;
- ip_address_list_init(&ip->addr_list);
- ip_neighbor_list_init(&ip->nbor_list);
-}
-
-void ip_data_set_enabled(ip_data_t *ip, char *enabled)
-{
- ip->enabled = (strcmp(enabled, "true") == 0) ? 1 : 0;
-}
-
-void ip_data_set_forwarding(ip_data_t *ip, char *forwarding)
-{
- ip->forwarding = (strcmp(forwarding, "true") == 0) ? 1 : 0;
-}
-
-void ip_data_set_mtu(ip_data_t *ip, char *mtu)
-{
- ip->mtu = (uint16_t) atoi(mtu);
-}
-
-void ip_data_add_address(ip_data_t *ip, char *addr, char *subnet, ip_subnet_type_t st)
-{
- ip_address_list_add(&ip->addr_list, addr, subnet, st);
-}
-
-void ip_data_add_neighbor(ip_data_t *ip, char *addr, char *phys_addr)
-{
- ip_neighbor_list_add(&ip->nbor_list, addr, phys_addr);
-}
-
-void ip_data_free(ip_data_t *ip)
-{
- ip_address_list_free(&ip->addr_list);
- ip_neighbor_list_free(&ip->nbor_list);
- ip_data_init(ip);
-}
-
-void ip_address_init(ip_address_t *addr)
-{
- addr->ip = NULL;
- addr->subnet_type = ip_subnet_type_unknown;
- addr->delete = false;
-}
-
-void ip_address_set_ip(ip_address_t *addr, char *ip)
-{
- addr->ip = xstrdup(ip);
-}
-
-void ip_address_set_delete(ip_address_list_t *addr_ls, char *ip)
-{
- if (addr_ls->count > 0) {
- for (uint32_t i = 0; i < addr_ls->count; i++) {
- if (addr_ls->addr[i].ip != NULL) {
- if (strcmp(addr_ls->addr[i].ip, ip) == 0) {
- addr_ls->addr[i].delete = true;
- }
- }
- }
- }
-}
-
-/*
- * Function: netmask_to_prefix_len
- * --------------------------------
- * calculates the prefix length (32 or 128 bit Hamming weight) of a network mask
- *
- * nm: network mask
- *
- * returns:
- * prefix length
- */
-uint8_t netmask_to_prefix_len(char *nm)
-{
- int ret;
- struct sockaddr_in sa;
- struct sockaddr_in6 sa6;
-
- // IPv6 if a ':' is found
- if (strchr(nm, ':')) {
- ret = inet_pton(AF_INET6, nm, &(sa6.sin6_addr));
- // invalid network address mask
- if (!ret) {
- // TODO: error handling on refactor
- }
-
- // s6_addr is a uint8_t array of length 16, all the byte popcounts need to be summarized
- // avoid branching, use popcountll's 64 bits minimum
- uint64_t *s6_addr64 = (uint64_t *) sa6.sin6_addr.s6_addr;
-
- return __builtin_popcountll(s6_addr64[0]) + __builtin_popcountll(s6_addr64[1]);
-
- }
-
- // IPv4 otherwise
- ret = inet_pton(AF_INET, nm, &(sa.sin_addr));
- // invalid network address mask
- if (!ret) {
- // TODO: error handling on refactor
- }
-
- return __builtin_popcountl(sa.sin_addr.s_addr);
-}
-
-void ip_address_set_subnet(ip_address_t *addr, char *subnet, ip_subnet_type_t st)
-{
- addr->subnet_type = st;
- if (st == ip_subnet_type_netmask) {
- addr->subnet = netmask_to_prefix_len(subnet);
- } else {
- addr->subnet = (uint8_t) atoi(subnet);
- }
-}
-
-void ip_address_free(ip_address_t *addr)
-{
- if (addr->ip != NULL) {
- FREE_SAFE(addr->ip);
- }
- ip_address_init(addr);
-}
-
-void ip_address_list_init(ip_address_list_t *addr_ls)
-{
- addr_ls->addr = NULL;
- addr_ls->count = 0;
-}
-
-void ip_address_list_add(ip_address_list_t *addr_ls, char *ip, char *subnet, ip_subnet_type_t st)
-{
- ip_address_t *addr = NULL;
-
- if (addr_ls->count > 0) {
- for (uint32_t i = 0; i < addr_ls->count; i++) {
- // if the address is already in the list, don't add it
- if (strcmp(addr_ls->addr[i].ip, ip) == 0) {
- return;
- }
- // in case an address was deleted, we can reuse that portion of memory
- // find it and set the new address at that location
- if (addr_ls->addr[i].ip == NULL) {
- addr = &addr_ls->addr[i];
- break;
- }
- }
- }
-
- // in case the list doesn't contain empty space (none of the addresses were deleted)
- if (addr == NULL) {
- ++addr_ls->count;
- addr_ls->addr = (ip_address_t *) xrealloc(addr_ls->addr, sizeof(ip_address_t) * addr_ls->count);
- addr = &addr_ls->addr[addr_ls->count - 1];
- }
-
- ip_address_init(addr);
- ip_address_set_ip(addr, ip);
- ip_address_set_subnet(addr, subnet, st);
-}
-
-void ip_address_list_free(ip_address_list_t *addr_ls)
-{
- if (addr_ls->count > 0) {
- for (uint32_t i = 0; i < addr_ls->count; i++) {
- ip_address_free(&addr_ls->addr[i]);
- }
- FREE_SAFE(addr_ls->addr);
- }
- ip_address_list_init(addr_ls);
-}
-
-void ip_neighbor_init(ip_neighbor_t *n)
-{
- n->ip = 0;
- n->phys_addr = 0;
- n->delete = false;
-}
-
-void ip_neighbor_set_ip(ip_neighbor_t *n, char *ip)
-{
- n->ip = xstrdup(ip);
-}
-
-void ip_neighbor_set_delete(ip_neighbor_list_t *nbor_ls, char *ip)
-{
- if (nbor_ls->count > 0) {
- for (uint32_t i = 0; i < nbor_ls->count; i++) {
- if (nbor_ls->nbor[i].ip != NULL) {
- if (strcmp(nbor_ls->nbor[i].ip, ip) == 0) {
- nbor_ls->nbor[i].delete = true;
- }
- }
- }
- }
-}
-
-void ip_neighbor_set_phys_addr(ip_neighbor_t *n, char *phys_addr)
-{
- n->phys_addr = xstrdup(phys_addr);
-}
-
-void ip_neighbor_free(ip_neighbor_t *n)
-{
- if (n->ip != NULL) {
- FREE_SAFE(n->ip);
- }
- if (n->phys_addr != NULL) {
- FREE_SAFE(n->phys_addr);
- }
- ip_neighbor_init(n);
-}
-
-void ip_neighbor_list_init(ip_neighbor_list_t *nbor_ls)
-{
- nbor_ls->count = 0;
- nbor_ls->nbor = NULL;
-}
-
-void ip_neighbor_list_add(ip_neighbor_list_t *nbor_ls, char *ip, char *phys_addr)
-{
-
- ip_neighbor_t *n = NULL;
-
- if (nbor_ls->count > 0) {
- for (uint32_t i = 0; i < nbor_ls->count; i++) {
- // in case a neighbor was deleted, we can reuse that portion of memory
- // find it and set the new neighbor at that location
- if (nbor_ls->nbor[i].ip == NULL) {
- n = &nbor_ls->nbor[i];
- break;
- }
- }
- }
-
- // in case the list doesn't contain empty space (none of the neighbors were deleted)
- if (n == NULL) {
- ++nbor_ls->count;
- nbor_ls->nbor = (ip_neighbor_t *) xrealloc(nbor_ls->nbor, sizeof(ip_neighbor_t) * nbor_ls->count);
- n = &nbor_ls->nbor[nbor_ls->count - 1];
- }
-
- ip_neighbor_init(n);
- ip_neighbor_set_ip(n, ip);
-
- // don't set if phys_addr is "none"
- if (strcmp(phys_addr, "none") != 0) {
- ip_neighbor_set_phys_addr(n, phys_addr);
- }
-}
-
-void ip_neighbor_list_free(ip_neighbor_list_t *nbor_ls)
-{
- if (nbor_ls->count > 0) {
- for (uint32_t i = 0; i < nbor_ls->count; i++) {
- ip_neighbor_free(&nbor_ls->nbor[i]);
- }
- FREE_SAFE(nbor_ls->nbor);
- }
- ip_neighbor_list_init(nbor_ls);
-}
diff --git a/src/interfaces/ip_data.h b/src/interfaces/ip_data.h
deleted file mode 100644
index 4d61b21b..00000000
--- a/src/interfaces/ip_data.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * telekom / sysrepo-plugin-interfaces
- *
- * This program is made available under the terms of the
- * BSD 3-Clause license which is available at
- * https://opensource.org/licenses/BSD-3-Clause
- *
- * SPDX-FileCopyrightText: 2021 Deutsche Telekom AG
- * SPDX-FileContributor: Sartura Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef IP_DATA_H_ONCE
-#define IP_DATA_H_ONCE
-
-#include
-#include
-
-enum ip_subnet_type_e {
- ip_subnet_type_unknown = 0,
- ip_subnet_type_prefix_length,
- ip_subnet_type_netmask,
-};
-
-typedef enum ip_subnet_type_e ip_subnet_type_t;
-typedef struct ip_address_s ip_address_t;
-typedef struct ip_address_list_s ip_address_list_t;
-typedef struct ip_neighbor_s ip_neighbor_t;
-typedef struct ip_neighbor_list_s ip_neighbor_list_t;
-typedef struct ip_data_s ip_data_t;
-
-struct ip_address_s {
- char *ip;
- uint8_t subnet;
- ip_subnet_type_t subnet_type;
- bool delete;
-};
-
-struct ip_address_list_s {
- ip_address_t *addr;
- uint32_t count;
-};
-
-struct ip_neighbor_s {
- char *ip;
- char *phys_addr;
- bool delete;
-};
-
-struct ip_neighbor_list_s {
- ip_neighbor_t *nbor;
- uint32_t count;
-};
-
-struct ip_data_s {
- uint8_t enabled;
- uint8_t forwarding;
- uint16_t mtu;
- ip_address_list_t addr_list;
- ip_neighbor_list_t nbor_list;
-};
-
-void ip_data_init(ip_data_t *ip);
-void ip_data_set_enabled(ip_data_t *ip, char *enabled);
-void ip_data_set_forwarding(ip_data_t *ip, char *forwarding);
-void ip_data_set_mtu(ip_data_t *ip, char *mtu);
-void ip_data_add_address(ip_data_t *ip, char *addr, char *subnet, ip_subnet_type_t st);
-void ip_data_add_neighbor(ip_data_t *ip, char *addr, char *phys_addr);
-void ip_data_free(ip_data_t *ip);
-
-void ip_address_init(ip_address_t *addr);
-void ip_address_set_ip(ip_address_t *addr, char *ip);
-void ip_address_set_delete(ip_address_list_t *addr_ls, char *ip);
-void ip_address_set_subnet(ip_address_t *addr, char *subnet, ip_subnet_type_t st);
-void ip_address_free(ip_address_t *addr);
-
-void ip_address_list_init(ip_address_list_t *addr_ls);
-void ip_address_list_add(ip_address_list_t *addr_ls, char *ip, char *subnet, ip_subnet_type_t st);
-void ip_address_list_free(ip_address_list_t *addr_ls);
-
-void ip_neighbor_init(ip_neighbor_t *n);
-void ip_neighbor_set_ip(ip_neighbor_t *n, char *ip);
-void ip_neighbor_set_delete(ip_neighbor_list_t *nbor_ls, char *ip);
-void ip_neighbor_set_phys_addr(ip_neighbor_t *n, char *phys_addr);
-void ip_neighbor_free(ip_neighbor_t *n);
-
-void ip_neighbor_list_init(ip_neighbor_list_t *nbor_ls);
-void ip_neighbor_list_add(ip_neighbor_list_t *nbor_ls, char *ip, char *phys_addr);
-void ip_neighbor_list_free(ip_neighbor_list_t *nbor_ls);
-
-#endif // IP_DATA_H_ONCE
diff --git a/src/interfaces/ipv6_data.c b/src/interfaces/ipv6_data.c
deleted file mode 100644
index ec8368af..00000000
--- a/src/interfaces/ipv6_data.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * telekom / sysrepo-plugin-interfaces
- *
- * This program is made available under the terms of the
- * BSD 3-Clause license which is available at
- * https://opensource.org/licenses/BSD-3-Clause
- *
- * SPDX-FileCopyrightText: 2021 Deutsche Telekom AG
- * SPDX-FileContributor: Sartura Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include "ipv6_data.h"
-#include
-#include
-
-void ipv6_data_init(ipv6_data_t *ipv6)
-{
- ipv6_autoconf_init(&ipv6->autoconf);
- ip_data_init(&ipv6->ip_data);
-}
-
-void ipv6_data_set_cga(ipv6_data_t *ipv6, char *cga)
-{
- ipv6->autoconf.create_global_addr = (strcmp(cga, "true") == 0) ? 1 : 0;
-}
-
-void ipv6_data_set_cta(ipv6_data_t *ipv6, char *cta)
-{
- ipv6->autoconf.create_temp_addr = (strcmp(cta, "true") == 0) ? 1 : 0;
-}
-
-void ipv6_data_set_tvl(ipv6_data_t *ipv6, char *tvl)
-{
- ipv6->autoconf.temp_valid_lifetime = (uint32_t) atoi(tvl);
-}
-
-void ipv6_data_set_tpl(ipv6_data_t *ipv6, char *tpl)
-{
- ipv6->autoconf.temp_preffered_lifetime = (uint32_t) atoi(tpl);
-}
-
-void ipv6_data_free(ipv6_data_t *ipv6)
-{
- ipv6_autoconf_init(&ipv6->autoconf);
- ip_data_free(&ipv6->ip_data);
-}
-
-void ipv6_autoconf_init(ipv6_autoconf_t *a)
-{
- a->create_global_addr = 0;
- a->create_temp_addr = 0;
- a->temp_preffered_lifetime = 0;
- a->temp_valid_lifetime = 0;
-}
diff --git a/src/interfaces/ipv6_data.h b/src/interfaces/ipv6_data.h
deleted file mode 100644
index 9d10390a..00000000
--- a/src/interfaces/ipv6_data.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * telekom / sysrepo-plugin-interfaces
- *
- * This program is made available under the terms of the
- * BSD 3-Clause license which is available at
- * https://opensource.org/licenses/BSD-3-Clause
- *
- * SPDX-FileCopyrightText: 2021 Deutsche Telekom AG
- * SPDX-FileContributor: Sartura Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef IPv6_DATA_H_ONCE
-#define IPv6_DATA_H_ONCE
-
-#include "ip_data.h"
-
-typedef struct ipv6_data_s ipv6_data_t;
-typedef struct ipv6_autoconf_s ipv6_autoconf_t;
-
-struct ipv6_autoconf_s {
- uint8_t create_global_addr;
- uint8_t create_temp_addr;
- uint32_t temp_valid_lifetime;
- uint32_t temp_preffered_lifetime;
-};
-
-struct ipv6_data_s {
- ip_data_t ip_data;
- ipv6_autoconf_t autoconf;
-};
-
-void ipv6_data_init(ipv6_data_t *ipv6);
-void ipv6_data_set_cga(ipv6_data_t *ipv6, char *cga);
-void ipv6_data_set_cta(ipv6_data_t *ipv6, char *cta);
-void ipv6_data_set_tvl(ipv6_data_t *ipv6, char *tvl);
-void ipv6_data_set_tpl(ipv6_data_t *ipv6, char *tpl);
-void ipv6_data_free(ipv6_data_t *ipv6);
-
-void ipv6_autoconf_init(ipv6_autoconf_t *a);
-
-#endif // IPv4_DATA_H_ONCE
diff --git a/src/interfaces/link_data.c b/src/interfaces/link_data.c
deleted file mode 100644
index dba566bb..00000000
--- a/src/interfaces/link_data.c
+++ /dev/null
@@ -1,638 +0,0 @@
-/*
- * telekom / sysrepo-plugin-interfaces
- *
- * This program is made available under the terms of the
- * BSD 3-Clause license which is available at
- * https://opensource.org/licenses/BSD-3-Clause
- *
- * SPDX-FileCopyrightText: 2021 Deutsche Telekom AG
- * SPDX-FileContributor: Sartura Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include
-#include "ip_data.h"
-#include "ipv4_data.h"
-#include "ipv6_data.h"
-#include "link_data.h"
-#include "utils/memory.h"
-#include
-#include
-
-void link_data_init(link_data_t *l)
-{
- l->name = NULL;
- l->description = NULL;
- l->type = NULL;
- l->enabled = NULL;
- l->delete = false;
- ip_data_init(&l->ipv4);
- ipv6_data_init(&l->ipv6);
- l->extensions.parent_interface = NULL;
- l->extensions.encapsulation.dot1q_vlan.outer_tag_type = NULL;
- l->extensions.encapsulation.dot1q_vlan.outer_vlan_id = 0;
- l->extensions.encapsulation.dot1q_vlan.second_tag_type = NULL;
- l->extensions.encapsulation.dot1q_vlan.second_vlan_id = 0;
-}
-
-int link_data_list_init(link_data_list_t *ld)
-{
- for (int i = 0; i < LD_MAX_LINKS; i++) {
- link_data_init(&ld->links[i]);
- }
- ld->count = 0;
-
- return 0;
-}
-
-void link_data_set_name(link_data_t *l, char *name)
-{
- l->name = xstrdup(name);
-}
-
-int link_data_list_add(link_data_list_t *ld, char *name)
-{
- bool name_found = false;
-
- if (ld->count >= LD_MAX_LINKS) {
- return EINVAL;
- }
-
- for (int i = 0; i < ld->count; i++) {
- if (ld->links[i].name != NULL) { // in case we deleted a link it will be NULL
- if (strcmp(ld->links[i].name, name) == 0) {
- name_found = true;
- break;
- }
- }
- }
-
- if (!name_found) {
- // set the new link to the first free one in the list
- // the one with name == 0
- int pos = ld->count;
- for (int i = 0; i < ld->count; i++) {
- if (ld->links[i].name == NULL) {
- pos = i;
- break;
- }
- }
- link_data_set_name(&ld->links[pos], name);
- if (pos == ld->count) {
- ++ld->count;
- }
- }
-
- return 0;
-}
-
-int link_data_list_set_ipv4_forwarding(link_data_list_t *ld, char *name, char *forwarding)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_data_set_forwarding(&l->ipv4, forwarding);
- } else {
- error = EINVAL;
- }
-
- return error;
-}
-
-int link_data_list_set_ipv4_enabled(link_data_list_t *ld, char *name, char *enabled)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_data_set_enabled(&l->ipv4, enabled);
- } else {
- error = EINVAL;
- }
-
- return error;
-}
-
-int link_data_list_set_ipv4_mtu(link_data_list_t *ld, char *name, char *mtu)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_data_set_mtu(&l->ipv4, mtu);
- } else {
- error = EINVAL;
- }
-
- return error;
-}
-
-int link_data_list_add_ipv4_address(link_data_list_t *ld, char *name, char *ip, char *subnet, ip_subnet_type_t st)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_data_add_address(&l->ipv4, ip, subnet, st);
- } else {
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_delete_ipv4_address(link_data_list_t *ld, char *name, char *ip)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_address_set_delete(&l->ipv4.addr_list, ip);
- } else {
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_add_ipv4_neighbor(link_data_list_t *ld, char *name, char *ip, char *phys_addr)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_data_add_neighbor(&l->ipv4, ip, phys_addr);
- } else {
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_delete_ipv4_neighbor(link_data_list_t *ld, char *name, char *ip)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_neighbor_set_delete(&l->ipv4.nbor_list, ip);
- } else {
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_ipv6_forwarding(link_data_list_t *ld, char *name, char *forwarding)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_data_set_forwarding(&l->ipv6.ip_data, forwarding);
- } else {
- error = EINVAL;
- }
-
- return error;
-}
-
-int link_data_list_set_ipv6_enabled(link_data_list_t *ld, char *name, char *enabled)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_data_set_enabled(&l->ipv6.ip_data, enabled);
- } else {
- error = EINVAL;
- }
-
- return error;
-}
-
-int link_data_list_set_ipv6_mtu(link_data_list_t *ld, char *name, char *mtu)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_data_set_mtu(&l->ipv6.ip_data, mtu);
- } else {
- error = EINVAL;
- }
-
- return error;
-}
-
-int link_data_list_add_ipv6_address(link_data_list_t *ld, char *name, char *ip, char *subnet)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_data_add_address(&l->ipv6.ip_data, ip, subnet, ip_subnet_type_prefix_length);
- } else {
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_delete_ipv6_address(link_data_list_t *ld, char *name, char *ip)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_address_set_delete(&l->ipv6.ip_data.addr_list, ip);
- } else {
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_add_ipv6_neighbor(link_data_list_t *ld, char *name, char *ip, char *phys_addr)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_data_add_neighbor(&l->ipv6.ip_data, ip, phys_addr);
- } else {
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_delete_ipv6_neighbor(link_data_list_t *ld, char *name, char *ip)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ip_neighbor_set_delete(&l->ipv6.ip_data.nbor_list, ip);
- } else {
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_ipv6_cga(link_data_list_t *ld, char *name, char *cga)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ipv6_data_set_cga(&l->ipv6, cga);
- } else {
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_ipv6_cta(link_data_list_t *ld, char *name, char *cta)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ipv6_data_set_cta(&l->ipv6, cta);
- } else {
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_ipv6_tvl(link_data_list_t *ld, char *name, char *tvl)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ipv6_data_set_tvl(&l->ipv6, tvl);
- } else {
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_ipv6_tpl(link_data_list_t *ld, char *name, char *tpl)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- ipv6_data_set_tpl(&l->ipv6, tpl);
- } else {
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_description(link_data_list_t *ld, char *name, char *description)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- if (l->description) {
- FREE_SAFE(l->description);
- }
- l->description = xstrdup(description);
- if (l->description == NULL) {
- error = EINVAL;
- }
- } else {
- error = EINVAL;
- }
-
- return error;
-}
-
-int link_data_list_set_type(link_data_list_t *ld, char *name, char *type)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- // if previously set -> free allocated mem
- if (l->type) {
- FREE_SAFE(l->type);
- }
- l->type = xstrdup(type);
- if (l->type == NULL) {
- error = EINVAL;
- }
- } else {
- error = EINVAL;
- }
-
- return error;
-}
-
-int link_data_list_set_enabled(link_data_list_t *ld, char *name, char *enabled)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- if (l->enabled) {
- FREE_SAFE(l->enabled);
- }
- l->enabled = xstrdup(enabled);
- if (l->enabled == NULL) {
- error = EINVAL;
- }
- } else {
- error = EINVAL;
- }
-
- return error;
-}
-
-int link_data_list_set_delete(link_data_list_t *ld, char *name, bool delete)
-{
- int error = 0;
- link_data_t *l = NULL;
-
- l = data_list_get_by_name(ld, name);
-
- if (l != NULL) {
- l->delete = delete;
- } else {
- error = EINVAL;
- }
-
- return error;
-}
-
-link_data_t *data_list_get_by_name(link_data_list_t *ld, char *name)
-{
- link_data_t *l = NULL;
- for (int i = 0; i < ld->count; i++) {
- if (ld->links[i].name != NULL) {
- if (strcmp(ld->links[i].name, name) == 0) {
- l = &ld->links[i];
- break;
- }
- }
- }
- return l;
-}
-
-// TODO: update
-int link_data_list_set_parent(link_data_list_t *ld, char *name, char *parent)
-{
- int error = 0;
- int name_found = 0;
-
- for (int i = 0; i < ld->count; i++) {
- if (ld->links[i].name != NULL) {
- if (strcmp(ld->links[i].name, name) == 0) {
- name_found = 1;
-
- if (ld->links[i].extensions.parent_interface != NULL) {
- FREE_SAFE(ld->links[i].extensions.parent_interface);
- }
-
- unsigned long tmp_len = 0;
- tmp_len = strlen(parent);
- ld->links[i].extensions.parent_interface = xmalloc(sizeof(char) * (tmp_len + 1));
- memcpy(ld->links[i].extensions.parent_interface, parent, tmp_len);
- ld->links[i].extensions.parent_interface[tmp_len] = 0;
-
- break;
- }
- }
- }
- if (!name_found) {
- // error
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_outer_vlan_id(link_data_list_t *ld, char *name, uint16_t outer_vlan_id)
-{
- int error = 0;
- int name_found = 0;
-
- for (int i = 0; i < ld->count; i++) {
- if (ld->links[i].name != NULL) {
- if (strcmp(ld->links[i].name, name) == 0) {
- name_found = 1;
- ld->links[i].extensions.encapsulation.dot1q_vlan.outer_vlan_id = outer_vlan_id;
- break;
- }
- }
- }
- if (!name_found) {
- // error
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_outer_tag_type(link_data_list_t *ld, char *name, char *outer_tag_type)
-{
- int error = 0;
- int name_found = 0;
-
- for (int i = 0; i < ld->count; i++) {
- if (ld->links[i].name != NULL) {
- if (strcmp(ld->links[i].name, name) == 0) {
- name_found = 1;
-
- if (ld->links[i].extensions.encapsulation.dot1q_vlan.outer_tag_type != NULL) {
- FREE_SAFE(ld->links[i].extensions.encapsulation.dot1q_vlan.outer_tag_type);
- }
-
- unsigned long tmp_len = 0;
- tmp_len = strlen(outer_tag_type);
- ld->links[i].extensions.encapsulation.dot1q_vlan.outer_tag_type = xmalloc(sizeof(char) * (tmp_len + 1));
- memcpy(ld->links[i].extensions.encapsulation.dot1q_vlan.outer_tag_type, outer_tag_type, tmp_len);
- ld->links[i].extensions.encapsulation.dot1q_vlan.outer_tag_type[tmp_len] = 0;
-
- break;
- }
- }
- }
- if (!name_found) {
- // error
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_second_vlan_id(link_data_list_t *ld, char *name, uint16_t second_vlan_id)
-{
- int error = 0;
- int name_found = 0;
-
- for (int i = 0; i < ld->count; i++) {
- if (ld->links[i].name != NULL) {
- if (strcmp(ld->links[i].name, name) == 0) {
- name_found = 1;
- ld->links[i].extensions.encapsulation.dot1q_vlan.second_vlan_id = second_vlan_id;
- break;
- }
- }
- }
- if (!name_found) {
- // error
- error = EINVAL;
- }
- return error;
-}
-
-int link_data_list_set_second_tag_type(link_data_list_t *ld, char *name, char *second_tag_type)
-{
- int error = 0;
- int name_found = 0;
-
- for (int i = 0; i < ld->count; i++) {
- if (ld->links[i].name != NULL) {
- if (strcmp(ld->links[i].name, name) == 0) {
- name_found = 1;
-
- if (ld->links[i].extensions.encapsulation.dot1q_vlan.second_tag_type != NULL) {
- FREE_SAFE(ld->links[i].extensions.encapsulation.dot1q_vlan.second_tag_type);
- }
-
- unsigned long tmp_len = 0;
- tmp_len = strlen(second_tag_type);
- ld->links[i].extensions.encapsulation.dot1q_vlan.second_tag_type = xmalloc(sizeof(char) * (tmp_len + 1));
- memcpy(ld->links[i].extensions.encapsulation.dot1q_vlan.second_tag_type, second_tag_type, tmp_len);
- ld->links[i].extensions.encapsulation.dot1q_vlan.second_tag_type[tmp_len] = 0;
-
- break;
- }
- }
- }
- if (!name_found) {
- // error
- error = EINVAL;
- }
- return error;
-}
-
-void link_data_free(link_data_t *l)
-{
- if (l->name) {
- FREE_SAFE(l->name);
- }
-
- if (l->description) {
- FREE_SAFE(l->description);
- }
-
- if (l->type) {
- FREE_SAFE(l->type);
- }
-
- if (l->enabled) {
- FREE_SAFE(l->enabled);
- }
-
- ip_data_free(&l->ipv4);
- ipv6_data_free(&l->ipv6);
-
- if (l->extensions.parent_interface) {
- FREE_SAFE(l->extensions.parent_interface);
- }
-
- if (l->extensions.encapsulation.dot1q_vlan.outer_tag_type) {
- FREE_SAFE(l->extensions.encapsulation.dot1q_vlan.outer_tag_type);
- }
-
- if (l->extensions.encapsulation.dot1q_vlan.second_tag_type) {
- FREE_SAFE(l->extensions.encapsulation.dot1q_vlan.second_tag_type);
- }
-}
-
-void link_data_list_free(link_data_list_t *ld)
-{
- for (int i = 0; i < ld->count; i++) {
- link_data_free(&ld->links[i]);
- }
-}
diff --git a/src/interfaces/link_data.h b/src/interfaces/link_data.h
deleted file mode 100644
index 1b97c4b2..00000000
--- a/src/interfaces/link_data.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * telekom / sysrepo-plugin-interfaces
- *
- * This program is made available under the terms of the
- * BSD 3-Clause license which is available at
- * https://opensource.org/licenses/BSD-3-Clause
- *
- * SPDX-FileCopyrightText: 2021 Deutsche Telekom AG
- * SPDX-FileContributor: Sartura Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef LINK_DATA_H_ONCE
-#define LINK_DATA_H_ONCE
-
-#include
-#include
-#include "ip_data.h"
-#include "ipv4_data.h"
-#include "ipv6_data.h"
-
-#define LD_MAX_LINKS 100 // TODO: check this
-
-typedef struct link_data_s link_data_t;
-typedef struct link_data_list_s link_data_list_t;
-
-struct link_data_s {
- char *name;
- char *description;
- char *type;
- char *enabled;
- bool delete;
- ipv4_data_t ipv4;
- ipv6_data_t ipv6;
- struct {
- struct {
- struct {
- char *outer_tag_type;
- uint16_t outer_vlan_id;
- char *second_tag_type;
- uint16_t second_vlan_id;
- } dot1q_vlan;
- // TODO: add more interface types that can have a configurable L2 encapsulation
- } encapsulation;
-
- char *parent_interface;
- // TOOD: cover more yang nodes from extensions
- } extensions;
-};
-
-struct link_data_list_s {
- link_data_t links[LD_MAX_LINKS];
- uint8_t count;
-};
-
-// link_data struct functions
-void link_data_init(link_data_t *l);
-void link_data_set_name(link_data_t *l, char *name);
-void link_data_free(link_data_t *l);
-
-// link_data_list functions - init
-int link_data_list_init(link_data_list_t *ld);
-int link_data_list_add(link_data_list_t *ld, char *name);
-
-// basic options
-link_data_t *data_list_get_by_name(link_data_list_t *ld, char *name);
-int link_data_list_set_description(link_data_list_t *ld, char *name, char *description);
-int link_data_list_set_type(link_data_list_t *ld, char *name, char *type);
-int link_data_list_set_enabled(link_data_list_t *ld, char *name, char *enabled);
-int link_data_list_set_delete(link_data_list_t *ld, char *name, bool delete);
-
-// ipv4 options
-int link_data_list_set_ipv4_forwarding(link_data_list_t *ld, char *name, char *forwarding);
-int link_data_list_set_ipv4_enabled(link_data_list_t *ld, char *name, char *enabled);
-int link_data_list_set_ipv4_mtu(link_data_list_t *ld, char *name, char *mtu);
-int link_data_list_add_ipv4_address(link_data_list_t *ld, char *name, char *ip, char *subnet, ip_subnet_type_t st);
-int link_data_list_set_delete_ipv4_address(link_data_list_t *ld, char *name, char *ip);
-int link_data_list_add_ipv4_neighbor(link_data_list_t *ld, char *name, char *ip, char *phys_addr);
-int link_data_list_set_delete_ipv4_neighbor(link_data_list_t *ld, char *name, char *ip);
-
-// ipv6 options
-int link_data_list_set_ipv6_forwarding(link_data_list_t *ld, char *name, char *forwarding);
-int link_data_list_set_ipv6_enabled(link_data_list_t *ld, char *name, char *enabled);
-int link_data_list_set_ipv6_mtu(link_data_list_t *ld, char *name, char *mtu);
-int link_data_list_add_ipv6_address(link_data_list_t *ld, char *name, char *ip, char *subnet);
-int link_data_list_set_delete_ipv6_address(link_data_list_t *ld, char *name, char *ip);
-int link_data_list_add_ipv6_neighbor(link_data_list_t *ld, char *name, char *ip, char *phys_addr);
-int link_data_list_set_delete_ipv6_neighbor(link_data_list_t *ld, char *name, char *ip);
-int link_data_list_set_ipv6_cga(link_data_list_t *ld, char *name, char *cga);
-int link_data_list_set_ipv6_cta(link_data_list_t *ld, char *name, char *cta);
-int link_data_list_set_ipv6_tvl(link_data_list_t *ld, char *name, char *tvl);
-int link_data_list_set_ipv6_tpl(link_data_list_t *ld, char *name, char *tpl);
-
-// if-extensions options
-int link_data_list_set_parent(link_data_list_t *ld, char *name, char *parent);
-
-// vlan-encapsulation options
-int link_data_list_set_outer_tag_type(link_data_list_t *ld, char *name, char *outer_tag_type);
-int link_data_list_set_outer_vlan_id(link_data_list_t *ld, char *name, uint16_t outer_vlan_id);
-int link_data_list_set_second_tag_type(link_data_list_t *ld, char *name, char *second_tag_type);
-int link_data_list_set_second_vlan_id(link_data_list_t *ld, char *name, uint16_t second_vlan_id);
-
-void link_data_list_free(link_data_list_t *ld);
-
-#endif /* IF_STATE_H_ONCE */
diff --git a/src/interfaces/src/main.c b/src/interfaces/src/main.c
new file mode 100644
index 00000000..00236085
--- /dev/null
+++ b/src/interfaces/src/main.c
@@ -0,0 +1,49 @@
+#include
+#include
+#include
+
+#include "plugin.h"
+#include "plugin/common.h"
+
+#include
+
+volatile int exit_application = 0;
+
+static void sigint_handler(__attribute__((unused)) int signum);
+
+int main(void)
+{
+ int error = SR_ERR_OK;
+ sr_conn_ctx_t* connection = NULL;
+ sr_session_ctx_t* session = NULL;
+ void* private_data = NULL;
+
+ sr_log_stderr(SR_LL_INF);
+
+ /* connect to sysrepo */
+ SRPC_SAFE_CALL_ERR(error, sr_connect(SR_CONN_DEFAULT, &connection), out);
+ SRPC_SAFE_CALL_ERR(error, sr_session_start(connection, SR_DS_RUNNING, &session), out);
+
+ /* init plugin */
+ SRPC_SAFE_CALL_ERR(error, sr_plugin_init_cb(session, &private_data), out);
+
+ /* loop until ctrl-c is pressed / SIGINT is received */
+ signal(SIGINT, sigint_handler);
+ signal(SIGPIPE, SIG_IGN);
+ while (!exit_application) {
+ sleep(1);
+ }
+
+out:
+ /* cleanup plugin */
+ sr_plugin_cleanup_cb(session, private_data);
+ sr_disconnect(connection);
+
+ return error ? -1 : 0;
+}
+
+static void sigint_handler(__attribute__((unused)) int signum)
+{
+ SRPLG_LOG_INF(PLUGIN_NAME, "Sigint called, exiting...");
+ exit_application = 1;
+}
\ No newline at end of file
diff --git a/src/interfaces/src/plugin.c b/src/interfaces/src/plugin.c
new file mode 100644
index 00000000..dc621716
--- /dev/null
+++ b/src/interfaces/src/plugin.c
@@ -0,0 +1,533 @@
+#include "plugin.h"
+#include "netlink/cache.h"
+#include "netlink/route/link.h"
+#include "netlink/socket.h"
+#include "plugin/common.h"
+#include "plugin/context.h"
+#include "plugin/data/interfaces/interface_state.h"
+
+// startup DS
+#include "plugin/startup/load.h"
+#include "plugin/startup/store.h"
+
+// running DS
+#include "plugin/running/load.h"
+#include "plugin/running/store.h"
+
+// subscription
+#include "plugin/subscription/change.h"
+#include "plugin/subscription/operational.h"
+#include "plugin/subscription/rpc.h"
+#include "srpc/common.h"
+#include "srpc/feature_status.h"
+#include "srpc/types.h"
+
+#include
+#include
+#include
+#include
+
+static int interfaces_init_state_changes_tracking(interfaces_state_changes_ctx_t* ctx);
+static void interfaces_link_cache_change_cb(struct nl_cache* cache, struct nl_object* obj, int val, void* arg);
+static void* interfaces_link_manager_thread_cb(void* data);
+
+int sr_plugin_init_cb(sr_session_ctx_t* running_session, void** private_data)
+{
+ int error = 0;
+
+ bool empty_startup = false;
+
+ // sysrepo
+ sr_session_ctx_t* startup_session = NULL;
+ sr_conn_ctx_t* connection = NULL;
+ sr_subscription_ctx_t* subscription = NULL;
+
+ // plugin
+ interfaces_ctx_t* ctx = NULL;
+
+ // init context
+ ctx = malloc(sizeof(*ctx));
+ *ctx = (interfaces_ctx_t) { 0 };
+
+ *private_data = ctx;
+
+ // load enabled features from modules in sysrepo
+ SRPC_SAFE_CALL_ERR(error, srpc_feature_status_hash_load(&ctx->features.ietf_interfaces_features, running_session, "ietf-interfaces"), error_out);
+ SRPC_SAFE_CALL_ERR(error, srpc_feature_status_hash_load(&ctx->features.ietf_if_extensions_features, running_session, "ietf-if-extensions"), error_out);
+ SRPC_SAFE_CALL_ERR(error, srpc_feature_status_hash_load(&ctx->features.ietf_ip_features, running_session, "ietf-ip"), error_out);
+
+ // module changes
+ srpc_module_change_t module_changes[] = {
+ {
+ INTERFACES_INTERFACES_INTERFACE_YANG_PATH,
+ interfaces_subscription_change_interfaces_interface,
+ },
+ };
+
+ // operational getters
+ srpc_operational_t oper[] = {
+ {
+ // depends on if-mib feature
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_ADMIN_STATUS_YANG_PATH,
+ srpc_feature_status_hash_check(ctx->features.ietf_interfaces_features, "if-mib") ? interfaces_subscription_operational_interfaces_interface_admin_status : NULL,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_OPER_STATUS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_oper_status,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_LAST_CHANGE_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_last_change,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ // depends on if-mib feature
+ INTERFACES_INTERFACES_INTERFACE_IF_INDEX_YANG_PATH,
+ srpc_feature_status_hash_check(ctx->features.ietf_interfaces_features, "if-mib") ? interfaces_subscription_operational_interfaces_interface_if_index : NULL,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_PHYS_ADDRESS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_phys_address,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_HIGHER_LAYER_IF_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_higher_layer_if,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_LOWER_LAYER_IF_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_lower_layer_if,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_SPEED_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_speed,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_DISCONTINUITY_TIME_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_discontinuity_time,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_OCTETS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_in_octets,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_UNICAST_PKTS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_in_unicast_pkts,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_BROADCAST_PKTS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_in_broadcast_pkts,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_MULTICAST_PKTS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_in_multicast_pkts,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_DISCARDS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_in_discards,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_ERRORS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_in_errors,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_UNKNOWN_PROTOS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_in_unknown_protos,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_OUT_OCTETS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_out_octets,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_OUT_UNICAST_PKTS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_out_unicast_pkts,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_OUT_BROADCAST_PKTS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_out_broadcast_pkts,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_OUT_MULTICAST_PKTS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_out_multicast_pkts,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_OUT_DISCARDS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_out_discards,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_OUT_ERRORS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_out_errors,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_DISCARD_UNKNOWN_ENCAPS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_statistics_in_discard_unknown_encaps,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ // depends on carrier-delay feature
+ INTERFACES_INTERFACES_INTERFACE_CARRIER_DELAY_CARRIER_TRANSITIONS_YANG_PATH,
+ srpc_feature_status_hash_check(ctx->features.ietf_if_extensions_features, "carrier-delay") ? interfaces_subscription_operational_interfaces_interface_carrier_delay_carrier_transitions : NULL,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ // depends on carrier-delay feature
+ INTERFACES_INTERFACES_INTERFACE_CARRIER_DELAY_TIMER_RUNNING_YANG_PATH,
+ srpc_feature_status_hash_check(ctx->features.ietf_if_extensions_features, "carrier-delay") ? interfaces_subscription_operational_interfaces_interface_carrier_delay_timer_running : NULL,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ // depends on dampening feature
+ INTERFACES_INTERFACES_INTERFACE_DAMPENING_PENALTY_YANG_PATH,
+ srpc_feature_status_hash_check(ctx->features.ietf_if_extensions_features, "dampening") ? interfaces_subscription_operational_interfaces_interface_dampening_penalty : NULL,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ // depends on dampening feature
+ INTERFACES_INTERFACES_INTERFACE_DAMPENING_SUPPRESSED_YANG_PATH,
+ srpc_feature_status_hash_check(ctx->features.ietf_if_extensions_features, "dampening") ? interfaces_subscription_operational_interfaces_interface_dampening_suppressed : NULL,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ // depends on dampening feature
+ INTERFACES_INTERFACES_INTERFACE_DAMPENING_TIME_REMAINING_YANG_PATH,
+ srpc_feature_status_hash_check(ctx->features.ietf_if_extensions_features, "dampening") ? interfaces_subscription_operational_interfaces_interface_dampening_time_remaining : NULL,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_FORWARDING_MODE_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_forwarding_mode,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_IPV4_ADDRESS_ORIGIN_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_ipv4_address_origin,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_IPV4_ADDRESS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_ipv4_address,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_IPV4_NEIGHBOR_ORIGIN_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_ipv4_neighbor_origin,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_IPV4_NEIGHBOR_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_ipv4_neighbor,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_IPV6_ADDRESS_ORIGIN_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_ipv6_address_origin,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_IPV6_ADDRESS_STATUS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_ipv6_address_status,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_IPV6_ADDRESS_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_ipv6_address,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_ORIGIN_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_ipv6_neighbor_origin,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_IS_ROUTER_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_ipv6_neighbor_is_router,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_STATE_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_ipv6_neighbor_state,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface_ipv6_neighbor,
+ },
+ {
+ IETF_INTERFACES_YANG_MODULE,
+ INTERFACES_INTERFACES_INTERFACE_YANG_PATH,
+ interfaces_subscription_operational_interfaces_interface,
+ },
+ };
+
+ // get connection
+ SRPC_SAFE_CALL_PTR(connection, sr_session_get_connection(running_session), error_out);
+
+ // start a session
+ SRPC_SAFE_CALL_ERR(error, sr_session_start(connection, SR_DS_STARTUP, &startup_session), error_out);
+
+ ctx->startup_ctx.startup_session = startup_session;
+
+ SRPC_SAFE_CALL_ERR(error, srpc_check_empty_datastore(running_session, INTERFACES_INTERFACES_INTERFACE_YANG_PATH, &empty_startup), error_out);
+
+ if (empty_startup) {
+ SRPLG_LOG_INF(PLUGIN_NAME, "Running datastore is empty");
+ SRPLG_LOG_INF(PLUGIN_NAME, "Loading initial system data");
+
+ // load initial running DS data on the system
+ SRPC_SAFE_CALL_ERR(error, interfaces_running_load(ctx, running_session), error_out);
+ } else {
+ // make sure the data from startup DS is stored in the interfaces
+ SRPLG_LOG_INF(PLUGIN_NAME, "Running datastore contains data");
+ SRPLG_LOG_INF(PLUGIN_NAME, "Checking running datastore against system data");
+
+ // check and store running DS data on the system
+ // SRPC_SAFE_CALL_ERR(error, interfaces_running_store(ctx, running_session), error_out);
+ }
+
+ // subscribe every module change
+ for (size_t i = 0; i < ARRAY_SIZE(module_changes); i++) {
+ const srpc_module_change_t* change = &module_changes[i];
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Subscribing module change callback %s", change->path);
+
+ // in case of work on a specific callback set it to NULL
+ if (change->cb) {
+ error = sr_module_change_subscribe(running_session, IETF_INTERFACES_YANG_MODULE, change->path, change->cb, *private_data, 0, SR_SUBSCR_DEFAULT, &subscription);
+ if (error) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_module_change_subscribe() error for \"%s\" (%d): %s", change->path, error, sr_strerror(error));
+ goto error_out;
+ }
+ }
+ }
+
+ // subscribe every operational getter
+ for (size_t i = 0; i < ARRAY_SIZE(oper); i++) {
+ const srpc_operational_t* op = &oper[i];
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Subscribing operational callback %s:%s", op->module, op->path);
+
+ // in case of work on a specific callback set it to NULL
+ if (op->cb) {
+ error = sr_oper_get_subscribe(running_session, op->module, op->path, op->cb, *private_data, SR_SUBSCR_DEFAULT, &subscription);
+ if (error) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_oper_get_subscribe() error for \"%s\" (%d): %s", op->path, error, sr_strerror(error));
+ goto error_out;
+ }
+ }
+ }
+
+ // tracking oper-status changes for interfaces
+ SRPC_SAFE_CALL_ERR(error, interfaces_init_state_changes_tracking(&ctx->oper_ctx.state_changes_ctx), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Error occured while initializing the plugin (%d)", error);
+
+out:
+
+ return error ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK;
+}
+
+void sr_plugin_cleanup_cb(sr_session_ctx_t* running_session, void* private_data)
+{
+ interfaces_ctx_t* ctx = (interfaces_ctx_t*)private_data;
+
+ if (ctx->oper_ctx.nl_ctx.link_cache) {
+ nl_cache_put(ctx->oper_ctx.nl_ctx.link_cache);
+ }
+
+ if (ctx->oper_ctx.nl_ctx.socket) {
+ nl_socket_free(ctx->oper_ctx.nl_ctx.socket);
+ }
+
+ pthread_mutex_lock(&ctx->oper_ctx.state_changes_ctx.state_hash_mutex);
+
+ if (ctx->oper_ctx.state_changes_ctx.nl_ctx.socket) {
+ nl_socket_free(ctx->oper_ctx.state_changes_ctx.nl_ctx.socket);
+ }
+
+ if (ctx->oper_ctx.state_changes_ctx.nl_ctx.link_cache_manager) {
+ nl_cache_mngr_free(ctx->oper_ctx.state_changes_ctx.nl_ctx.link_cache_manager);
+ }
+
+ if (ctx->oper_ctx.state_changes_ctx.state_hash) {
+ interfaces_interface_state_hash_free(&ctx->oper_ctx.state_changes_ctx.state_hash);
+ }
+
+ pthread_mutex_unlock(&ctx->oper_ctx.state_changes_ctx.state_hash_mutex);
+
+ // free feature status hashes
+ srpc_feature_status_hash_free(&ctx->features.ietf_interfaces_features);
+ srpc_feature_status_hash_free(&ctx->features.ietf_if_extensions_features);
+ srpc_feature_status_hash_free(&ctx->features.ietf_ip_features);
+
+ free(ctx);
+}
+
+static int interfaces_init_state_changes_tracking(interfaces_state_changes_ctx_t* ctx)
+{
+ int error = 0;
+ struct rtnl_link* link = NULL;
+ pthread_attr_t attr;
+ interfaces_interface_state_hash_element_t* new_element = NULL;
+
+ // init hash
+ ctx->state_hash = interfaces_interface_state_hash_new();
+
+ // init libnl data
+ SRPC_SAFE_CALL_PTR(ctx->nl_ctx.socket, nl_socket_alloc(), error_out);
+
+ // connect and get all links
+ SRPC_SAFE_CALL_ERR(error, nl_connect(ctx->nl_ctx.socket, NETLINK_ROUTE), error_out);
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_alloc_cache(ctx->nl_ctx.socket, AF_UNSPEC, &ctx->nl_ctx.link_cache), error_out);
+
+ // init hash mutex
+ SRPC_SAFE_CALL_ERR(error, pthread_mutex_init(&ctx->state_hash_mutex, NULL), error_out);
+
+ link = (struct rtnl_link*)nl_cache_get_first(ctx->nl_ctx.link_cache);
+
+ while (link != NULL) {
+ // create hash entries
+ const uint8_t oper_state = rtnl_link_get_operstate(link);
+ const time_t current_time = time(NULL);
+ const char* link_name = rtnl_link_get_name(link);
+
+ // create new state element
+ SRPC_SAFE_CALL_PTR(new_element, interfaces_interface_state_hash_element_new(), error_out);
+
+ // set element data
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_state_hash_element_set_name(&new_element, link_name), error_out);
+ interfaces_interface_state_hash_element_set_state(&new_element, oper_state);
+ interfaces_interface_state_hash_element_set_last_change(&new_element, current_time);
+
+ // add entry to the hash table
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_state_hash_add(&ctx->state_hash, new_element), error_out);
+
+ link = (struct rtnl_link*)nl_cache_get_next((struct nl_object*)link);
+ }
+
+ // setup cache manager
+ SRPC_SAFE_CALL_ERR(error, nl_cache_mngr_alloc(NULL, NETLINK_ROUTE, 0, &ctx->nl_ctx.link_cache_manager), error_out);
+ SRPC_SAFE_CALL_ERR(error, nl_cache_mngr_add(ctx->nl_ctx.link_cache_manager, "route/link", interfaces_link_cache_change_cb, ctx, &ctx->nl_ctx.link_cache), error_out);
+
+ // setup detatched thread for sending change signals to the cache manager
+ SRPC_SAFE_CALL_ERR(error, pthread_attr_init(&attr), error_out);
+ SRPC_SAFE_CALL_ERR(error, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED), error_out);
+ SRPC_SAFE_CALL_ERR(error, pthread_create(&ctx->manager_thread, &attr, interfaces_link_manager_thread_cb, ctx), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
+
+static void interfaces_link_cache_change_cb(struct nl_cache* cache, struct nl_object* obj, int val, void* arg)
+{
+ interfaces_state_changes_ctx_t* ctx = arg;
+ char time_buffer[100] = { 0 };
+ struct tm* last_change = NULL;
+ time_t current = 0;
+ interfaces_interface_state_hash_element_t* new_element = NULL;
+ int rc = 0;
+
+ // block further access using mutex
+
+ pthread_mutex_lock(&ctx->state_hash_mutex);
+
+ struct rtnl_link* link = NULL;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Entered callback function for handling link cache changes");
+
+ link = (struct rtnl_link*)nl_cache_get_first(cache);
+
+ while (link != NULL) {
+ const char* link_name = rtnl_link_get_name(link);
+ interfaces_interface_state_hash_element_t* state_element = interfaces_interface_state_hash_get(ctx->state_hash, link_name);
+ const uint8_t oper_state = rtnl_link_get_operstate(link);
+
+ if (state_element) {
+ SRPLG_LOG_INF(PLUGIN_NAME, "State for interface %s already exists - updating last change", link_name);
+ if (oper_state != state_element->state.state) {
+ current = time(NULL);
+ last_change = localtime(¤t);
+ strftime(time_buffer, sizeof(time_buffer), "%FT%TZ", last_change);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Interface %s changed oper-state from %d to %d at %s", link_name, state_element->state.state, oper_state, time_buffer);
+
+ // update state info
+ interfaces_interface_state_hash_element_set_state(&state_element, oper_state);
+ interfaces_interface_state_hash_element_set_last_change(&state_element, time(NULL));
+ }
+ } else {
+ SRPLG_LOG_INF(PLUGIN_NAME, "State for interface %s doesn\'t exist - creating a new entry in the state data hash table", link_name);
+ // new link has been added - add new data to the hash
+ current = time(NULL);
+ last_change = localtime(¤t);
+
+ // create new state element
+ new_element = interfaces_interface_state_hash_element_new();
+ if (!new_element) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Unable to create new hash element for interface %s", link_name);
+ } else {
+ rc = interfaces_interface_state_hash_element_set_name(&new_element, link_name);
+ if (rc) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Error setting interface name for a newly created hash element for interface %s", link_name);
+ } else {
+ interfaces_interface_state_hash_element_set_state(&new_element, oper_state);
+ interfaces_interface_state_hash_element_set_last_change(&new_element, current);
+ rc = interfaces_interface_state_hash_add(&ctx->state_hash, new_element);
+ if (rc) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Unable to add new interface %s to the interface hash");
+ } else {
+ strftime(time_buffer, sizeof(time_buffer), "%FT%TZ", last_change);
+ SRPLG_LOG_INF(PLUGIN_NAME, "Interface %s added to the state data hash: state = %d, time = %s", link_name, oper_state, time_buffer);
+ }
+ }
+ }
+ }
+
+ link = (struct rtnl_link*)nl_cache_get_next((struct nl_object*)link);
+ }
+
+ // unlock further access to the state hash
+ pthread_mutex_unlock(&ctx->state_hash_mutex);
+}
+
+static void* interfaces_link_manager_thread_cb(void* data)
+{
+ interfaces_state_changes_ctx_t* ctx = data;
+
+ do {
+ nl_cache_mngr_data_ready(ctx->nl_ctx.link_cache_manager);
+ sleep(1);
+ } while (1);
+
+ return NULL;
+}
\ No newline at end of file
diff --git a/src/interfaces/src/plugin.h b/src/interfaces/src/plugin.h
new file mode 100644
index 00000000..72b7bc91
--- /dev/null
+++ b/src/interfaces/src/plugin.h
@@ -0,0 +1,9 @@
+#ifndef INTERFACES_PLUGIN_H
+#define INTERFACES_PLUGIN_H
+
+#include
+
+int sr_plugin_init_cb(sr_session_ctx_t* session, void** private_data);
+void sr_plugin_cleanup_cb(sr_session_ctx_t* session, void* private_data);
+
+#endif // INTERFACES_PLUGIN_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/change.c b/src/interfaces/src/plugin/api/interfaces/change.c
new file mode 100644
index 00000000..fc8fa98b
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/change.c
@@ -0,0 +1,109 @@
+#include "change.h"
+#include "netlink/route/addr.h"
+#include "plugin/common.h"
+#include "plugin/context.h"
+
+#include "interface/change.h"
+
+#include
+#include
+#include
+
+int interfaces_change_interface_init(void* priv)
+{
+ int error = 0;
+ interfaces_ctx_t* ctx = (interfaces_ctx_t*)priv;
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Initializing context data for interface changes");
+
+ // allocate socket
+ SRPC_SAFE_CALL_PTR(mod_ctx->nl_ctx.socket, nl_socket_alloc(), error_out);
+
+ // connect
+ SRPC_SAFE_CALL_ERR(error, nl_connect(mod_ctx->nl_ctx.socket, NETLINK_ROUTE), error_out);
+
+ // allocate link cache
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_alloc_cache(mod_ctx->nl_ctx.socket, AF_UNSPEC, &mod_ctx->nl_ctx.link_cache), error_out);
+
+ // allocate address cache
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_alloc_cache(mod_ctx->nl_ctx.socket, &mod_ctx->nl_ctx.addr_cache), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
+
+int interfaces_change_interface(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ int rc = 0;
+
+ interfaces_ctx_t* ctx = (interfaces_ctx_t*)priv;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+ char change_xpath_buffer[PATH_MAX] = { 0 };
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ // get node xpath
+ lyd_path(change_ctx->node, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer));
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s; Value: %s; Operation: %d, Node XPath: %s", node_name, change_ctx->previous_value, node_value, change_ctx->operation, xpath_buffer);
+
+ // name
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/name", xpath_buffer), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_change_name, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // description
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/description", xpath_buffer), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_change_description, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // type
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/type", xpath_buffer), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_change_type, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // enabled
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/enabled", xpath_buffer), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_change_enabled, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // link-up-down-trap-enable
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/link-up-down-trap-enable", xpath_buffer), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_change_link_up_down_trap_enable, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ return error;
+}
+
+void interfaces_change_interface_free(void* priv)
+{
+ interfaces_ctx_t* ctx = (interfaces_ctx_t*)priv;
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Freeing context data for interface changes");
+
+ if (mod_ctx->nl_ctx.link_cache) {
+ nl_cache_put(mod_ctx->nl_ctx.link_cache);
+ }
+
+ if (mod_ctx->nl_ctx.addr_cache) {
+ nl_cache_put(mod_ctx->nl_ctx.addr_cache);
+ }
+
+ if (mod_ctx->nl_ctx.socket) {
+ nl_socket_free(mod_ctx->nl_ctx.socket);
+ }
+
+ // set to NULL
+ ctx->mod_ctx = (interfaces_mod_changes_ctx_t) { 0 };
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/change.h b/src/interfaces/src/plugin/api/interfaces/change.h
new file mode 100644
index 00000000..c8e4247e
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/change.h
@@ -0,0 +1,11 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_CHANGE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_CHANGE_H
+
+#include
+#include
+
+int interfaces_change_interface_init(void* priv);
+int interfaces_change_interface(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_change_interface_free(void* priv);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/check.c b/src/interfaces/src/plugin/api/interfaces/check.c
new file mode 100644
index 00000000..2f9c62c6
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/check.c
@@ -0,0 +1,38 @@
+#include "check.h"
+#include "plugin/api/interfaces/load.h"
+#include "plugin/common.h"
+#include "src/uthash.h"
+#include "srpc/types.h"
+#include "sysrepo.h"
+
+#include
+
+srpc_check_status_t interfaces_check_interface(interfaces_ctx_t* ctx, const interfaces_interface_hash_element_t* if_hash)
+{
+ srpc_check_status_t status = srpc_check_status_none;
+ int error = 0;
+
+ // system data
+ interfaces_interface_hash_element_t* system_if_hash = NULL;
+ const interfaces_interface_hash_element_t *if_iter = NULL, *if_tmp = NULL;
+
+ // load system interfaces
+ // SRPC_SAFE_CALL_ERR(error, interfaces_load_interface(ctx, &system_if_hash), error_out);
+
+ HASH_ITER(hh, if_hash, if_iter, if_tmp)
+ {
+ }
+
+ status = srpc_check_status_non_existant;
+
+ goto out;
+
+error_out:
+ status = srpc_check_status_error;
+
+out:
+ // free system interface data
+ interfaces_interface_hash_free(&system_if_hash);
+
+ return status;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/check.h b/src/interfaces/src/plugin/api/interfaces/check.h
new file mode 100644
index 00000000..4d8fc646
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/check.h
@@ -0,0 +1,11 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_CHECK_H
+#define INTERFACES_PLUGIN_API_INTERFACES_CHECK_H
+
+#include "plugin/context.h"
+#include
+
+#include
+
+srpc_check_status_t interfaces_check_interface(interfaces_ctx_t* ctx, const interfaces_interface_hash_element_t* if_hash);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_CHECK_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/carrier-delay/change.c b/src/interfaces/src/plugin/api/interfaces/interface/carrier-delay/change.c
new file mode 100644
index 00000000..240ffa3c
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/carrier-delay/change.c
@@ -0,0 +1,48 @@
+#include "change.h"
+#include "plugin/common.h"
+
+#include
+
+int interfaces_interface_carrier_delay_change_up(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+int interfaces_interface_carrier_delay_change_down(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/carrier-delay/change.h b/src/interfaces/src/plugin/api/interfaces/interface/carrier-delay/change.h
new file mode 100644
index 00000000..94faedb6
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/carrier-delay/change.h
@@ -0,0 +1,10 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_CARRIER_DELAY_CHANGE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_CARRIER_DELAY_CHANGE_H
+
+#include
+#include
+
+int interfaces_interface_carrier_delay_change_up(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+int interfaces_interface_carrier_delay_change_down(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_CARRIER_DELAY_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/change.c b/src/interfaces/src/plugin/api/interfaces/interface/change.c
new file mode 100644
index 00000000..c35ffa1f
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/change.c
@@ -0,0 +1,384 @@
+#include "change.h"
+#include "netlink/errno.h"
+#include "netlink/route/link.h"
+#include "plugin/common.h"
+#include "plugin/context.h"
+#include "sysrepo/xpath.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+static int interfacecs_interface_extract_name(sr_session_ctx_t* session, const struct lyd_node* node, char* name_buffer, size_t buffer_size);
+
+int interfaces_interface_change_parent_interface(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s; Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+int interfaces_interface_change_max_frame_size(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s; Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+int interfaces_interface_change_loopback(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s; Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+int interfaces_interface_change_link_up_down_trap_enable(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s; Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+int interfaces_interface_change_enabled(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+
+ char interface_name_buffer[256] = { 0 };
+
+ interfaces_ctx_t* ctx = (interfaces_ctx_t*)priv;
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+ struct rtnl_link* current_link = NULL;
+ struct rtnl_link* request_link = NULL;
+
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s; Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // get interface name
+ SRPC_SAFE_CALL_ERR(error, interfacecs_interface_extract_name(session, change_ctx->node, interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ case SR_OP_MODIFIED:
+ // get link by name
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out);
+
+ // create request link
+ SRPC_SAFE_CALL_PTR(request_link, rtnl_link_alloc(), error_out);
+
+ // set name
+ rtnl_link_set_name(request_link, interface_name_buffer);
+ rtnl_link_set_type(request_link, rtnl_link_get_type(current_link));
+
+ // set operstate
+ rtnl_link_set_flags(request_link, (strcmp(node_value, "true") == 0) ? (unsigned int)rtnl_link_str2flags("up") : (unsigned int)rtnl_link_str2flags("down"));
+ rtnl_link_unset_flags(request_link, (strcmp(node_value, "true") == 0) ? (unsigned int)rtnl_link_str2flags("down") : (unsigned int)rtnl_link_str2flags("up"));
+ rtnl_link_set_operstate(request_link, (strcmp(node_value, "true") == 0) ? IF_OPER_UP : IF_OPER_DOWN);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Current link status: %d", rtnl_link_get_operstate(current_link));
+ SRPLG_LOG_INF(PLUGIN_NAME, "Changed link status: %d", rtnl_link_get_operstate(request_link));
+
+ // apply changes
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_change(mod_ctx->nl_ctx.socket, current_link, request_link, NLM_F_REPLACE), error_out);
+ break;
+ case SR_OP_DELETED:
+ // get link by name
+ // goto out if this function fails - if the link has completely been deleted, no need to delete enabled leaf
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), out);
+
+ // create request link
+ SRPC_SAFE_CALL_PTR(request_link, rtnl_link_alloc(), error_out);
+
+ // set name
+ rtnl_link_set_name(request_link, interface_name_buffer);
+ rtnl_link_set_type(request_link, rtnl_link_get_type(current_link));
+
+ // treat as set to up - default value
+ rtnl_link_set_flags(request_link, (unsigned int)rtnl_link_str2flags("up"));
+ rtnl_link_set_operstate(request_link, IF_OPER_UP);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Current link status: %d", rtnl_link_get_operstate(current_link));
+ SRPLG_LOG_INF(PLUGIN_NAME, "Changed link status: %d", rtnl_link_get_operstate(request_link));
+
+ // apply changes
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_change(mod_ctx->nl_ctx.socket, current_link, request_link, NLM_F_REPLACE), error_out);
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ goto out;
+
+error_out:
+ if (error < 0) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "nl_geterror(): %s", nl_geterror(error));
+ }
+ error = -1;
+
+out:
+ if (request_link) {
+ rtnl_link_put(request_link);
+ }
+
+ return error;
+}
+
+int interfaces_interface_change_type(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+
+ char interface_name_buffer[256] = { 0 };
+
+ interfaces_ctx_t* ctx = (interfaces_ctx_t*)priv;
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+ struct rtnl_link* current_link = NULL;
+ struct rtnl_link* request_link = NULL;
+ bool type_set = false;
+
+ struct {
+ const char* yang_type;
+ const char* type;
+ } type_pairs[] = {
+ {
+ "iana-if-type:ethernetCsmacd",
+ "veth",
+ },
+ {
+ "iana-if-type:softwareLoopback",
+ "lo",
+ },
+ {
+ "iana-if-type:l2vlan",
+ "vlan",
+ },
+ {
+ "iana-if-type:other",
+ "dummy",
+ },
+ };
+
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s; Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ case SR_OP_MODIFIED:
+ // get interface name
+ SRPC_SAFE_CALL_ERR(error, interfacecs_interface_extract_name(session, change_ctx->node, interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ // get link by name
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out);
+
+ // create request link
+ SRPC_SAFE_CALL_PTR(request_link, rtnl_link_alloc(), error_out);
+
+ // set name
+ rtnl_link_set_name(request_link, interface_name_buffer);
+
+ // set type
+ for (size_t i = 0; i < ARRAY_SIZE(type_pairs); i++) {
+ if (!strcmp(node_value, type_pairs[i].yang_type)) {
+ rtnl_link_set_type(request_link, type_pairs[i].type);
+ type_set = true;
+ break;
+ }
+ }
+
+ if (!type_set) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Unsupported interface type %s", node_value);
+ goto error_out;
+ }
+
+ // apply changes
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_change(mod_ctx->nl_ctx.socket, current_link, request_link, 0), error_out);
+ break;
+ case SR_OP_DELETED:
+ // unsupported - type is necessarry
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ return error;
+}
+
+int interfaces_interface_change_description(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s; Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+int interfaces_interface_change_name(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+
+ interfaces_ctx_t* ctx = (interfaces_ctx_t*)priv;
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+ struct rtnl_link* new_link = NULL;
+ struct rtnl_link* old_link = NULL;
+
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s; Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ old_link = rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, node_value);
+
+ // add a new link if such a link doesn't exist
+ if (!old_link) {
+ // create a new link
+ SRPC_SAFE_CALL_PTR(new_link, rtnl_link_alloc(), error_out);
+
+ // setup link and add it to the system
+ rtnl_link_set_name(new_link, node_value);
+
+ // set temp as initial type
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_set_type(new_link, "dummy"), error_out);
+
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_add(mod_ctx->nl_ctx.socket, new_link, NLM_F_CREATE), error_out);
+ }
+ break;
+ case SR_OP_MODIFIED:
+ // name cannot be modified - only deleted and created again
+ goto error_out;
+ case SR_OP_DELETED:
+ // get link and delete it
+ SRPC_SAFE_CALL_PTR(old_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, node_value), error_out);
+
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_delete(mod_ctx->nl_ctx.socket, old_link), error_out);
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ goto out;
+
+error_out:
+ if (error < 0) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "nl_geterror(): %d = %s", error, nl_geterror(error));
+ }
+ error = -1;
+
+out:
+ return error;
+}
+
+static int interfacecs_interface_extract_name(sr_session_ctx_t* session, const struct lyd_node* node, char* name_buffer, size_t buffer_size)
+{
+ int error = 0;
+ int rc = 0;
+ void* error_ptr = NULL;
+
+ const char* name = NULL;
+
+ sr_xpath_ctx_t xpath_ctx = { 0 };
+ char path_buffer[PATH_MAX] = { 0 };
+
+ // get node full path
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out);
+
+ // extract key
+ SRPC_SAFE_CALL_PTR(name, sr_xpath_key_value(path_buffer, "interface", "name", &xpath_ctx), error_out);
+
+ // store to buffer
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(name_buffer, buffer_size, "%s", name), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ return error;
+}
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/change.h b/src/interfaces/src/plugin/api/interfaces/interface/change.h
new file mode 100644
index 00000000..531a0a4c
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/change.h
@@ -0,0 +1,16 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_CHANGE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_CHANGE_H
+
+#include
+#include
+
+int interfaces_interface_change_parent_interface(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+int interfaces_interface_change_max_frame_size(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+int interfaces_interface_change_loopback(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+int interfaces_interface_change_link_up_down_trap_enable(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+int interfaces_interface_change_enabled(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+int interfaces_interface_change_type(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+int interfaces_interface_change_description(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+int interfaces_interface_change_name(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/dampening/change.c b/src/interfaces/src/plugin/api/interfaces/interface/dampening/change.c
new file mode 100644
index 00000000..0ef5e8d6
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/dampening/change.c
@@ -0,0 +1,92 @@
+#include "change.h"
+#include "plugin/common.h"
+
+#include
+
+int interfaces_interface_dampening_change_max_suppress_time(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+int interfaces_interface_dampening_change_suppress(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+int interfaces_interface_dampening_change_reuse(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+int interfaces_interface_dampening_change_half_life(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/dampening/change.h b/src/interfaces/src/plugin/api/interfaces/interface/dampening/change.h
new file mode 100644
index 00000000..22e7a76b
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/dampening/change.h
@@ -0,0 +1,12 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_DAMPENING_CHANGE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_DAMPENING_CHANGE_H
+
+#include
+#include
+
+int interfaces_interface_dampening_change_max_suppress_time(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+int interfaces_interface_dampening_change_suppress(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+int interfaces_interface_dampening_change_reuse(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+int interfaces_interface_dampening_change_half_life(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_DAMPENING_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/outer-tag/change.c b/src/interfaces/src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/outer-tag/change.c
new file mode 100644
index 00000000..bc8aecf0
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/outer-tag/change.c
@@ -0,0 +1,48 @@
+#include "change.h"
+#include "plugin/common.h"
+
+#include
+
+int interfaces_interface_encapsulation_dot1q_vlan_outer_tag_change_vlan_id(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+int interfaces_interface_encapsulation_dot1q_vlan_outer_tag_change_tag_type(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/outer-tag/change.h b/src/interfaces/src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/outer-tag/change.h
new file mode 100644
index 00000000..7966fa9f
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/outer-tag/change.h
@@ -0,0 +1,10 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_OUTER_TAG_CHANGE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_OUTER_TAG_CHANGE_H
+
+#include
+#include
+
+int interfaces_interface_encapsulation_dot1q_vlan_outer_tag_change_vlan_id(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+int interfaces_interface_encapsulation_dot1q_vlan_outer_tag_change_tag_type(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_OUTER_TAG_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/second-tag/change.c b/src/interfaces/src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/second-tag/change.c
new file mode 100644
index 00000000..5562ac57
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/second-tag/change.c
@@ -0,0 +1,48 @@
+#include "change.h"
+#include "plugin/common.h"
+
+#include
+
+int interfaces_interface_encapsulation_dot1q_vlan_second_tag_change_vlan_id(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+int interfaces_interface_encapsulation_dot1q_vlan_second_tag_change_tag_type(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/second-tag/change.h b/src/interfaces/src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/second-tag/change.h
new file mode 100644
index 00000000..57be5706
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/encapsulation/dot1q-vlan/second-tag/change.h
@@ -0,0 +1,10 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_SECOND_TAG_CHANGE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_SECOND_TAG_CHANGE_H
+
+#include
+#include
+
+int interfaces_interface_encapsulation_dot1q_vlan_second_tag_change_vlan_id(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+int interfaces_interface_encapsulation_dot1q_vlan_second_tag_change_tag_type(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_SECOND_TAG_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/change.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/change.c
new file mode 100644
index 00000000..9db2ad73
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/change.c
@@ -0,0 +1,509 @@
+#include "change.h"
+#include "plugin/common.h"
+#include "plugin/context.h"
+#include "plugin/data/interfaces/interface/ipv4/address.h"
+#include "srpc/common.h"
+#include "srpc/types.h"
+#include "sysrepo_types.h"
+
+#include
+
+#include
+
+#include
+#include
+#include
+
+static int interfaces_interface_ipv4_address_get_prefix_length(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+static int interfaces_interface_ipv4_address_get_netmask(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+
+int interfaces_interface_ipv4_address_change_netmask_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv4_address_change_netmask(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+
+ // strings and buffers
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+ char path_buffer[PATH_MAX] = { 0 };
+ char interface_name_buffer[100] = { 0 };
+ char ip_buffer[100] = { 0 };
+ char address_buffer[100] = { 0 };
+ char old_address_buffer[100] = { 0 };
+
+ // app context
+ interfaces_ctx_t* ctx = priv;
+
+ // mod changes context
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ // libnl
+ struct rtnl_addr* request_addr = NULL;
+ struct rtnl_addr* delete_addr = NULL;
+ struct rtnl_link* current_link = NULL;
+ struct nl_addr* local_addr = NULL;
+ struct nl_addr* old_local_addr = NULL;
+
+ // data
+ uint8_t prefix_length = 0;
+ uint8_t old_prefix_length = 0;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // get node path
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out);
+
+ // get interface name
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ // get IP
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "address", "ip", ip_buffer, sizeof(ip_buffer)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s; Address IP: %s", path_buffer, interface_name_buffer, ip_buffer);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ // this change case should be handled only when creating the whole address in the IP callback
+ // no need to process created change
+ break;
+ case SR_OP_MODIFIED:
+ // change netmask/prefix length
+ request_addr = rtnl_addr_alloc();
+ delete_addr = rtnl_addr_alloc();
+
+ // convert netmask to prefix length
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_address_netmask2prefix(node_value, &prefix_length), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_address_netmask2prefix(change_ctx->previous_value, &old_prefix_length), error_out);
+
+ // get full address
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(address_buffer, sizeof(address_buffer), "%s/%d", ip_buffer, prefix_length), error_out);
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(old_address_buffer, sizeof(old_address_buffer), "%s/%d", ip_buffer, old_prefix_length), error_out);
+
+ // parse local address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(address_buffer, AF_INET, &local_addr), error_out);
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(old_address_buffer, AF_INET, &old_local_addr), error_out);
+
+ // set to route address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(request_addr, local_addr), error_out);
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(delete_addr, old_local_addr), error_out);
+
+ // set interface
+ rtnl_addr_set_ifindex(request_addr, rtnl_link_get_ifindex(current_link));
+ rtnl_addr_set_ifindex(delete_addr, rtnl_link_get_ifindex(current_link));
+
+ // delete old address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_delete(mod_ctx->nl_ctx.socket, delete_addr, 0), error_out);
+
+ // add new address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_add(mod_ctx->nl_ctx.socket, request_addr, 0), error_out);
+
+ break;
+ case SR_OP_DELETED:
+ // netmask is needed to find the appropriate address
+ // should be processed when IP deleted
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ goto out;
+
+error_out:
+ if (error < 0) {
+ // libnl error
+ SRPLG_LOG_INF(PLUGIN_NAME, "nl_geterror(): %s", nl_geterror(error));
+ }
+ error = -1;
+
+out:
+ if (request_addr) {
+ rtnl_addr_put(request_addr);
+ }
+
+ if (delete_addr) {
+ rtnl_addr_put(delete_addr);
+ }
+
+ if (old_local_addr) {
+ nl_addr_put(old_local_addr);
+ }
+
+ if (local_addr) {
+ nl_addr_put(local_addr);
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv4_address_change_netmask_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv4_address_change_prefix_length_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv4_address_change_prefix_length(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+
+ // strings and buffers
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+ char path_buffer[PATH_MAX] = { 0 };
+ char interface_name_buffer[100] = { 0 };
+ char ip_buffer[100] = { 0 };
+ char address_buffer[100] = { 0 };
+ char old_address_buffer[100] = { 0 };
+
+ // app context
+ interfaces_ctx_t* ctx = priv;
+
+ // mod changes context
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ // libnl
+ struct rtnl_addr* request_addr = NULL;
+ struct rtnl_addr* delete_addr = NULL;
+ struct rtnl_link* current_link = NULL;
+ struct nl_addr* local_addr = NULL;
+ struct nl_addr* old_local_addr = NULL;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // get node path
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out);
+
+ // get interface name
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ // get IP
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "address", "ip", ip_buffer, sizeof(ip_buffer)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s; Address IP: %s", path_buffer, interface_name_buffer, ip_buffer);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ // this change case should be handled only when creating the whole address in the IP callback
+ // no need to process created change
+ break;
+ case SR_OP_MODIFIED:
+ // change prefix length
+ request_addr = rtnl_addr_alloc();
+ delete_addr = rtnl_addr_alloc();
+
+ // get full address
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(address_buffer, sizeof(address_buffer), "%s/%s", ip_buffer, node_value), error_out);
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(old_address_buffer, sizeof(old_address_buffer), "%s/%s", ip_buffer, change_ctx->previous_value), error_out);
+
+ // parse local address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(address_buffer, AF_INET, &local_addr), error_out);
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(old_address_buffer, AF_INET, &old_local_addr), error_out);
+
+ // set to route address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(request_addr, local_addr), error_out);
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(delete_addr, old_local_addr), error_out);
+
+ // set interface
+ rtnl_addr_set_ifindex(request_addr, rtnl_link_get_ifindex(current_link));
+ rtnl_addr_set_ifindex(delete_addr, rtnl_link_get_ifindex(current_link));
+
+ // delete old address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_delete(mod_ctx->nl_ctx.socket, delete_addr, 0), error_out);
+
+ // add new address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_add(mod_ctx->nl_ctx.socket, request_addr, 0), error_out);
+
+ break;
+ case SR_OP_DELETED:
+ // prefix is needed to find the appropriate address
+ // should be processed when IP deleted
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ goto out;
+
+error_out:
+ if (error < 0) {
+ SRPLG_LOG_INF(PLUGIN_NAME, "nl_geterror(): %s", nl_geterror(error));
+ }
+ error = -1;
+
+out:
+ if (request_addr) {
+ rtnl_addr_put(request_addr);
+ }
+
+ if (delete_addr) {
+ rtnl_addr_put(delete_addr);
+ }
+
+ if (old_local_addr) {
+ nl_addr_put(old_local_addr);
+ }
+
+ if (local_addr) {
+ nl_addr_put(local_addr);
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv4_address_change_prefix_length_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv4_address_change_ip_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv4_address_change_ip(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+
+ // sysrepo
+ sr_val_t* prefix_val = NULL;
+ sr_val_t* netmask_val = NULL;
+ sr_conn_ctx_t* conn_ctx = NULL;
+ sr_session_ctx_t* running_session = NULL;
+
+ // strings and buffers
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+ char path_buffer[PATH_MAX] = { 0 };
+ char interface_name_buffer[100] = { 0 };
+ char address_buffer[100] = { 0 };
+
+ // app context
+ interfaces_ctx_t* ctx = priv;
+
+ // mod changes context
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ // libnl
+ struct rtnl_addr* request_addr = NULL;
+ struct rtnl_link* current_link = NULL;
+ struct nl_addr* local_addr = NULL;
+
+ // data
+ uint8_t prefix_length = 0;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // get node path
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out);
+
+ // get interface name
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s", path_buffer, interface_name_buffer);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out);
+
+ // get connection
+ SRPC_SAFE_CALL_PTR(conn_ctx, sr_session_get_connection(session), error_out);
+
+ // start a running DS session - fetching data about prefix when deleting the address
+ SRPC_SAFE_CALL_ERR(error, sr_session_start(conn_ctx, SR_DS_RUNNING, &running_session), error_out);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ // new address
+ request_addr = rtnl_addr_alloc();
+
+ // parse local address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(node_value, AF_INET, &local_addr), error_out);
+
+ // get prefix length by using prefix-length or netmask leafs
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(path_buffer, sizeof(path_buffer), "%s[name=\"%s\"]/ietf-ip:ipv4/address[ip=\"%s\"]/prefix-length", INTERFACES_INTERFACES_LIST_YANG_PATH, interface_name_buffer, node_value), error_out);
+ SRPC_SAFE_CALL_ERR(error, srpc_iterate_changes(ctx, session, path_buffer, interfaces_interface_ipv4_address_get_prefix_length, NULL, NULL), error_out);
+
+ if (!mod_ctx->mod_data.ipv4.address.prefix_set) {
+ // prefix not found - check for netmask
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(path_buffer, sizeof(path_buffer), "%s[name=\"%s\"]/ietf-ip:ipv4/address[ip=\"%s\"]/netmask", INTERFACES_INTERFACES_LIST_YANG_PATH, interface_name_buffer, node_value), error_out);
+ SRPC_SAFE_CALL_ERR(error, srpc_iterate_changes(ctx, session, path_buffer, interfaces_interface_ipv4_address_get_netmask, NULL, NULL), error_out);
+
+ if (!mod_ctx->mod_data.ipv4.address.prefix_set) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Unable to get prefix-length/netmask for address %s... Discarding changes", node_value);
+ goto error_out;
+ }
+ }
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Recieved prefix-length of %d for address %s", mod_ctx->mod_data.ipv4.address.prefix_length, node_value);
+
+ // prefix was set and found
+
+ // set final prefix length
+ nl_addr_set_prefixlen(local_addr, mod_ctx->mod_data.ipv4.address.prefix_length);
+
+ // set to route address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(request_addr, local_addr), error_out);
+
+ // set interface
+ rtnl_addr_set_ifindex(request_addr, rtnl_link_get_ifindex(current_link));
+
+ // add address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_add(mod_ctx->nl_ctx.socket, request_addr, 0), error_out);
+
+ break;
+ case SR_OP_MODIFIED:
+ // should be impossible - address IP can only be created and deleted
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Unsuported operation MODIFY for interface IPv4 address IP leaf");
+ goto error_out;
+ break;
+ case SR_OP_DELETED:
+ // fetch info about prefix-length/netmask and use the pair address/prefix to delete address
+ // prefix is needed to find the appropriate address
+ request_addr = rtnl_addr_alloc();
+
+ // check for prefix-length
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(path_buffer, sizeof(path_buffer), INTERFACES_INTERFACES_INTERFACE_YANG_PATH "[name=\"%s\"]/ietf-ip:ipv4/address[ip=\"%s\"]/prefix-length", interface_name_buffer, node_value), error_out);
+ SRPLG_LOG_INF(PLUGIN_NAME, "Searching running DS for %s", path_buffer);
+
+ error = sr_get_item(running_session, path_buffer, 0, &prefix_val);
+ if (error == SR_ERR_OK) {
+ // parse prefix-length
+ prefix_length = prefix_val->data.uint8_val;
+ } else if (error == SR_ERR_NOT_FOUND) {
+ // fetch netmask
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(path_buffer, sizeof(path_buffer), "%s[name=\"%s\"]/ietf-ip:ipv4/address[ip=\"%s\"]/netmask", INTERFACES_INTERFACES_LIST_YANG_PATH, interface_name_buffer, node_value), error_out);
+ SRPC_SAFE_CALL_ERR(error, sr_get_item(running_session, path_buffer, 0, &netmask_val), error_out);
+
+ // convert netmask to prefix
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_address_netmask2prefix(netmask_val->data.string_val, &prefix_length), error_out);
+ } else {
+ // other error - treat as invalid
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Error retrieving prefix-length value for address %s", node_value);
+ goto error_out;
+ }
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Recieved prefix for address %s: %d", node_value, prefix_length);
+
+ // after getting the prefix length - remove address
+
+ // get full address
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(address_buffer, sizeof(address_buffer), "%s/%d", node_value, prefix_length), error_out);
+
+ // parse local address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(address_buffer, AF_INET, &local_addr), error_out);
+
+ // set to route address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(request_addr, local_addr), error_out);
+
+ // set interface
+ rtnl_addr_set_ifindex(request_addr, rtnl_link_get_ifindex(current_link));
+
+ // remove wanted address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_delete(mod_ctx->nl_ctx.socket, request_addr, 0), error_out);
+
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ goto out;
+
+error_out:
+ if (error < 0) {
+ SRPLG_LOG_INF(PLUGIN_NAME, "nl_geterror(): %s", nl_geterror(error));
+ }
+ error = -1;
+
+out:
+ if (running_session) {
+ sr_session_stop(running_session);
+ }
+
+ if (request_addr) {
+ rtnl_addr_put(request_addr);
+ }
+
+ // re-initialize mod_ctx data
+ mod_ctx->mod_data.ipv4.address.prefix_length = 0;
+ mod_ctx->mod_data.ipv4.address.prefix_set = false;
+
+ return error;
+}
+
+void interfaces_interface_ipv4_address_change_ip_free(void* priv)
+{
+}
+
+static int interfaces_interface_ipv4_address_get_prefix_length(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+
+ // ctx
+ interfaces_ctx_t* ctx = priv;
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // this callback should only be called on CREATED operation
+ assert(change_ctx->operation == SR_OP_CREATED);
+
+ // parse prefix length
+ mod_ctx->mod_data.ipv4.address.prefix_length = (uint8_t)atoi(node_value);
+ mod_ctx->mod_data.ipv4.address.prefix_set = true;
+
+ return error;
+}
+
+static int interfaces_interface_ipv4_address_get_netmask(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+
+ // ctx
+ interfaces_ctx_t* ctx = priv;
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ uint8_t prefix_length = 0;
+
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // this callback should only be called on CREATED operation
+ assert(change_ctx->operation == SR_OP_CREATED);
+
+ // parse netmask into prefix length
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_address_netmask2prefix(node_value, &prefix_length), error_out);
+
+ // set mod changes prefix length
+ mod_ctx->mod_data.ipv4.address.prefix_length = prefix_length;
+ mod_ctx->mod_data.ipv4.address.prefix_set = true;
+
+ goto out;
+
+error_out:
+ error = -1;
+ mod_ctx->mod_data.ipv4.address.prefix_set = false;
+
+out:
+ return error;
+}
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/change.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/change.h
new file mode 100644
index 00000000..a4c807af
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/change.h
@@ -0,0 +1,19 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_ADDRESS_CHANGE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_ADDRESS_CHANGE_H
+
+#include
+#include
+
+int interfaces_interface_ipv4_address_change_netmask_init(void* priv);
+int interfaces_interface_ipv4_address_change_netmask(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv4_address_change_netmask_free(void* priv);
+
+int interfaces_interface_ipv4_address_change_prefix_length_init(void* priv);
+int interfaces_interface_ipv4_address_change_prefix_length(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv4_address_change_prefix_length_free(void* priv);
+
+int interfaces_interface_ipv4_address_change_ip_init(void* priv);
+int interfaces_interface_ipv4_address_change_ip(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv4_address_change_ip_free(void* priv);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_ADDRESS_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/load.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/load.c
new file mode 100644
index 00000000..95985878
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/load.c
@@ -0,0 +1,52 @@
+#include "load.h"
+#include "netlink/addr.h"
+#include "netlink/route/addr.h"
+#include "plugin/data/interfaces/interface/ipv4/address.h"
+
+int interfaces_interface_ipv4_address_load_ip(interfaces_ctx_t* ctx, interfaces_interface_ipv4_address_element_t** element, struct rtnl_addr* addr)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+ char ip_buffer[100] = { 0 };
+
+ // convert to nl_addr
+ struct nl_addr* local = rtnl_addr_get_local(addr);
+
+ // parse to string
+ SRPC_SAFE_CALL_PTR(error_ptr, nl_addr2str(local, ip_buffer, sizeof(ip_buffer)), error_out);
+
+ char* prefix = strchr(ip_buffer, '/');
+ if (prefix) {
+ *prefix = 0;
+ }
+
+ // set IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_address_element_set_ip(element, ip_buffer), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
+
+int interfaces_interface_ipv4_address_load_prefix_length(interfaces_ctx_t* ctx, interfaces_interface_ipv4_address_element_t** element, struct rtnl_addr* addr)
+{
+ int error = 0;
+
+ const uint8_t prefix = (uint8_t)rtnl_addr_get_prefixlen(addr);
+
+ // set prefix length
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_address_element_set_prefix_length(element, prefix), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ return error;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/load.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/load.h
new file mode 100644
index 00000000..84288cea
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/address/load.h
@@ -0,0 +1,11 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_ADDRESS_LOAD_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_ADDRESS_LOAD_H
+
+#include "netlink/route/addr.h"
+#include "plugin/context.h"
+#include "plugin/types.h"
+
+int interfaces_interface_ipv4_address_load_ip(interfaces_ctx_t* ctx, interfaces_interface_ipv4_address_element_t** element, struct rtnl_addr* addr);
+int interfaces_interface_ipv4_address_load_prefix_length(interfaces_ctx_t* ctx, interfaces_interface_ipv4_address_element_t** element, struct rtnl_addr* addr);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_ADDRESS_LOAD_H
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/change.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/change.c
new file mode 100644
index 00000000..5fda0cfe
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/change.c
@@ -0,0 +1,273 @@
+#include "change.h"
+#include "libyang/tree_data.h"
+#include "netlink/cache.h"
+#include "netlink/route/link.h"
+#include "plugin/common.h"
+#include "plugin/context.h"
+#include "sysrepo_types.h"
+
+#include
+#include
+#include
+#include
+
+int interfaces_interface_ipv4_change_neighbor_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv4_change_neighbor(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv4_change_neighbor_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv4_change_address_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv4_change_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv4_change_address_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv4_change_mtu_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv4_change_mtu(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+ char path_buffer[PATH_MAX] = { 0 };
+ char interface_name_buffer[100] = { 0 };
+
+ // app context
+ interfaces_ctx_t* ctx = priv;
+
+ // mod changes context
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ // libnl
+ struct rtnl_link* current_link = NULL;
+ struct rtnl_link* request_link = NULL;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // get node path
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out);
+
+ // get interface name
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s", path_buffer, interface_name_buffer);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out);
+
+ // create request
+ request_link = rtnl_link_alloc();
+ rtnl_link_set_name(request_link, rtnl_link_get_name(current_link));
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_set_type(request_link, rtnl_link_get_type(current_link)), error_out);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ case SR_OP_MODIFIED: {
+ // convert to int
+ uint16_t mtu = (uint16_t)atoi(node_value);
+
+ // set data
+ rtnl_link_set_mtu(request_link, mtu);
+ break;
+ }
+ case SR_OP_DELETED:
+ // set default MTU
+ rtnl_link_set_mtu(request_link, 1500);
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ // apply changes
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_change(mod_ctx->nl_ctx.socket, current_link, request_link, 0), error_out);
+
+ goto out;
+
+error_out:
+ if (error < 0) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "nl_geterror(): %s", nl_geterror(error));
+ }
+ error = -1;
+
+out:
+ return error;
+}
+
+void interfaces_interface_ipv4_change_mtu_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv4_change_forwarding_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv4_change_forwarding(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ // int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return -1;
+}
+
+void interfaces_interface_ipv4_change_forwarding_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv4_change_enabled_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv4_change_enabled(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+ interfaces_ctx_t* ctx = priv;
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ // sysrepo
+ sr_conn_ctx_t* conn = NULL;
+ sr_session_ctx_t* running_session = NULL;
+
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ char path_buffer[PATH_MAX] = { 0 };
+ char interface_name_buffer[100] = { 0 };
+
+ struct rtnl_link* current_link = NULL;
+ struct rtnl_addr* addr_iter = NULL;
+
+ // IPv4 can be disabled by deleting all IPv4 addresses associated with the interface
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // get node path
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out);
+
+ // get interface name
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s", path_buffer, interface_name_buffer);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out);
+
+ // get address iterator
+ SRPC_SAFE_CALL_PTR(addr_iter, (struct rtnl_addr*)nl_cache_get_first(mod_ctx->nl_ctx.addr_cache), error_out);
+
+ // get connection
+ SRPC_SAFE_CALL_PTR(conn, sr_session_get_connection(session), error_out);
+
+ // start running session
+ SRPC_SAFE_CALL_ERR(error, sr_session_start(conn, SR_DS_RUNNING, &running_session), error_out);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ case SR_OP_MODIFIED:
+ // if IPv4 disabled - delete all v4 addresses on the interface
+ if (!strcmp(node_value, "false")) {
+ // configure path buffer
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(path_buffer, sizeof(path_buffer), "%s[name=\"%s\"]/ietf-ip:ipv4/address", INTERFACES_INTERFACES_LIST_YANG_PATH, interface_name_buffer), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Deleting every IPv4 address for interface %s: path = %s", interface_name_buffer, path_buffer);
+ }
+ break;
+ case SR_OP_DELETED:
+ // default value = true, don't do anything
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ if (running_session) {
+ sr_session_stop(running_session);
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv4_change_enabled_free(void* priv)
+{
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/change.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/change.h
new file mode 100644
index 00000000..016e4c0f
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/change.h
@@ -0,0 +1,27 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_CHANGE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_CHANGE_H
+
+#include
+#include
+
+int interfaces_interface_ipv4_change_neighbor_init(void* priv);
+int interfaces_interface_ipv4_change_neighbor(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv4_change_neighbor_free(void* priv);
+
+int interfaces_interface_ipv4_change_address_init(void* priv);
+int interfaces_interface_ipv4_change_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv4_change_address_free(void* priv);
+
+int interfaces_interface_ipv4_change_mtu_init(void* priv);
+int interfaces_interface_ipv4_change_mtu(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv4_change_mtu_free(void* priv);
+
+int interfaces_interface_ipv4_change_forwarding_init(void* priv);
+int interfaces_interface_ipv4_change_forwarding(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv4_change_forwarding_free(void* priv);
+
+int interfaces_interface_ipv4_change_enabled_init(void* priv);
+int interfaces_interface_ipv4_change_enabled(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv4_change_enabled_free(void* priv);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/load.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/load.c
new file mode 100644
index 00000000..dbecfd2c
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/load.c
@@ -0,0 +1,167 @@
+#include "load.h"
+#include "netlink/route/addr.h"
+#include "netlink/route/link.h"
+#include "netlink/route/neighbour.h"
+#include "plugin/api/interfaces/interface/ipv4/address/load.h"
+#include "plugin/api/interfaces/interface/ipv4/neighbor/load.h"
+#include "plugin/common.h"
+#include "plugin/context.h"
+#include "plugin/data/interfaces/interface.h"
+#include "plugin/data/interfaces/interface/ipv4.h"
+#include "plugin/data/interfaces/interface/ipv4/address.h"
+#include "plugin/types.h"
+#include "sysrepo.h"
+
+int interfaces_interface_ipv4_load_enabled(interfaces_ctx_t* ctx, interfaces_interface_ipv4_t* ipv4, struct rtnl_link* link)
+{
+ int error = 0;
+
+ // enabled by default
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_element_ipv4_set_enabled(ipv4, 1), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
+
+int interfaces_interface_ipv4_load_forwarding(interfaces_ctx_t* ctx, interfaces_interface_ipv4_t* ipv4, struct rtnl_link* link)
+{
+ int error = 0;
+
+ // TODO: implement
+
+ return error;
+}
+
+int interfaces_interface_ipv4_load_mtu(interfaces_ctx_t* ctx, interfaces_interface_ipv4_t* ipv4, struct rtnl_link* link)
+{
+ int error = 0;
+
+ const unsigned int mtu = rtnl_link_get_mtu(link);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_element_ipv4_set_mtu(ipv4, (uint16_t)mtu), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
+
+int interfaces_interface_ipv4_load_address(interfaces_ctx_t* ctx, interfaces_interface_ipv4_t* ipv4, struct rtnl_link* link)
+{
+ int error = 0;
+ interfaces_nl_ctx_t* nl_ctx = &ctx->startup_ctx.nl_ctx;
+ struct rtnl_addr* addr_iter = NULL;
+
+ // created element
+ interfaces_interface_ipv4_address_element_t* new_element = NULL;
+ uint8_t element_added = 0;
+
+ // allocate address list
+ ipv4->address = interfaces_interface_ipv4_address_new();
+
+ // allocate cache
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_alloc_cache(nl_ctx->socket, &nl_ctx->addr_cache), error_out);
+
+ // get link iterator
+ SRPC_SAFE_CALL_PTR(addr_iter, (struct rtnl_addr*)nl_cache_get_first(nl_ctx->addr_cache), error_out);
+
+ // iterate links
+ while (addr_iter) {
+ // get all addresses from the link and extract info
+ if (rtnl_addr_get_ifindex(addr_iter) == rtnl_link_get_ifindex(link) && rtnl_addr_get_family(addr_iter) == AF_INET) {
+ // create new element
+ new_element = interfaces_interface_ipv4_address_element_new();
+ element_added = 0;
+
+ // load IP and prefix
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_address_load_ip(ctx, &new_element, addr_iter), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_address_load_prefix_length(ctx, &new_element, addr_iter), error_out);
+
+ // add element to the list
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_address_add_element(&ipv4->address, new_element), error_out);
+ element_added = 1;
+ }
+
+ // iterate
+ addr_iter = (struct rtnl_addr*)nl_cache_get_next((struct nl_object*)addr_iter);
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+ // if interrupted see if any memory is left over from the allocated element - free the element
+ if (!element_added) {
+ interfaces_interface_ipv4_address_element_free(&new_element);
+ }
+
+out:
+
+ return error;
+}
+
+int interfaces_interface_ipv4_load_neighbor(interfaces_ctx_t* ctx, interfaces_interface_ipv4_t* ipv4, struct rtnl_link* link)
+{
+ int error = 0;
+ interfaces_nl_ctx_t* nl_ctx = &ctx->startup_ctx.nl_ctx;
+ struct rtnl_neigh* neigh_iter = NULL;
+
+ // created element
+ interfaces_interface_ipv4_neighbor_element_t* new_element = NULL;
+ uint8_t element_added = 0;
+
+ // allocate address list
+ ipv4->neighbor = interfaces_interface_ipv4_neighbor_new();
+
+ // allocate cache
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_alloc_cache(nl_ctx->socket, &nl_ctx->neigh_cache), error_out);
+
+ // get link iterator
+ SRPC_SAFE_CALL_PTR(neigh_iter, (struct rtnl_neigh*)nl_cache_get_first(nl_ctx->neigh_cache), error_out);
+
+ // iterate links
+ while (neigh_iter) {
+ // get all neighbors from the link and extract info
+ if (rtnl_neigh_get_ifindex(neigh_iter) == rtnl_link_get_ifindex(link) && rtnl_neigh_get_family(neigh_iter) == AF_INET) {
+ // create new element
+ new_element = interfaces_interface_ipv4_neighbor_element_new();
+ element_added = 0;
+
+ // load IP and prefix
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_neighbor_load_ip(ctx, &new_element, neigh_iter), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_neighbor_load_link_layer_address(ctx, &new_element, neigh_iter), error_out);
+
+ // add element to the list
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_neighbor_add_element(&ipv4->neighbor, new_element), error_out);
+ element_added = 1;
+ }
+
+ // iterate
+ neigh_iter = (struct rtnl_neigh*)nl_cache_get_next((struct nl_object*)neigh_iter);
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+ // if interrupted see if any memory is left over from the allocated element - free the element
+ if (!element_added) {
+ interfaces_interface_ipv4_neighbor_element_free(&new_element);
+ }
+
+out:
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/load.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/load.h
new file mode 100644
index 00000000..d8bbd4b2
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/load.h
@@ -0,0 +1,15 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_LOAD_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_LOAD_H
+
+#include "plugin/context.h"
+#include "plugin/data/interfaces/interface.h"
+#include "plugin/data/interfaces/interface/linked_list.h"
+#include "plugin/types.h"
+
+int interfaces_interface_ipv4_load_enabled(interfaces_ctx_t* ctx, interfaces_interface_ipv4_t* ipv4, struct rtnl_link* link);
+int interfaces_interface_ipv4_load_forwarding(interfaces_ctx_t* ctx, interfaces_interface_ipv4_t* ipv4, struct rtnl_link* link);
+int interfaces_interface_ipv4_load_mtu(interfaces_ctx_t* ctx, interfaces_interface_ipv4_t* ipv4, struct rtnl_link* link);
+int interfaces_interface_ipv4_load_address(interfaces_ctx_t* ctx, interfaces_interface_ipv4_t* ipv4, struct rtnl_link* link);
+int interfaces_interface_ipv4_load_neighbor(interfaces_ctx_t* ctx, interfaces_interface_ipv4_t* ipv4, struct rtnl_link* link);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_LOAD_H
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/change.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/change.c
new file mode 100644
index 00000000..b32722d3
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/change.c
@@ -0,0 +1,264 @@
+#include "change.h"
+#include "netlink/addr.h"
+#include "plugin/common.h"
+#include "plugin/context.h"
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+static int interfaces_interface_ipv4_neighbor_get_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+
+int interfaces_interface_ipv4_neighbor_change_link_layer_address_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv4_neighbor_change_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+
+ // strings and buffers
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+ char path_buffer[PATH_MAX] = { 0 };
+ char interface_name_buffer[100] = { 0 };
+ char ip_buffer[100] = { 0 };
+
+ // app context
+ interfaces_ctx_t* ctx = priv;
+
+ // mod changes context
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ // libnl
+ struct rtnl_neigh* request_neigh = NULL;
+ struct rtnl_link* current_link = NULL;
+ struct nl_addr* dst_addr = NULL;
+ struct nl_addr* ll_addr = NULL;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // get node path
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out);
+
+ // get interface name
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ // get IP
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "neighbor", "ip", ip_buffer, sizeof(ip_buffer)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s; Neighbor IP: %s", path_buffer, interface_name_buffer, ip_buffer);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ // not used - used only in IP change callback
+ break;
+ case SR_OP_MODIFIED:
+ // change lladdr
+ request_neigh = rtnl_neigh_alloc();
+
+ // set interface
+ rtnl_neigh_set_ifindex(request_neigh, rtnl_link_get_ifindex(current_link));
+
+ // parse destination and LL address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(ip_buffer, AF_INET, &dst_addr), error_out);
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(node_value, AF_LLC, &ll_addr), error_out);
+
+ // set destination and LL address
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_set_dst(request_neigh, dst_addr), error_out);
+ rtnl_neigh_set_lladdr(request_neigh, ll_addr);
+
+ // change neighbor
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_add(mod_ctx->nl_ctx.socket, request_neigh, NLM_F_REPLACE), error_out);
+
+ break;
+ case SR_OP_DELETED:
+ // not used - used only in IP change callback
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ if (request_neigh) {
+ rtnl_neigh_put(request_neigh);
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv4_neighbor_change_link_layer_address_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv4_neighbor_change_ip_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv4_neighbor_change_ip(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+
+ // strings and buffers
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+ char path_buffer[PATH_MAX] = { 0 };
+ char interface_name_buffer[100] = { 0 };
+
+ // app context
+ interfaces_ctx_t* ctx = priv;
+
+ // mod changes context
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ // libnl
+ struct rtnl_neigh* request_neigh = NULL;
+ struct rtnl_link* current_link = NULL;
+ struct nl_addr* dst_addr = NULL;
+ struct nl_addr* ll_addr = NULL;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // get node path
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out);
+
+ // get interface name
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s", path_buffer, interface_name_buffer);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ // new neighbor
+ request_neigh = rtnl_neigh_alloc();
+
+ // parse destination address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(node_value, AF_INET, &dst_addr), error_out);
+
+ // get prefix length by using prefix-length or netmask leafs
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(path_buffer, sizeof(path_buffer), INTERFACES_INTERFACES_INTERFACE_YANG_PATH "[name=\"%s\"]/ietf-ip:ipv4/neighbor[ip=\"%s\"]/link-layer-address", interface_name_buffer, node_value), error_out);
+ SRPC_SAFE_CALL_ERR(error, srpc_iterate_changes(ctx, session, path_buffer, interfaces_interface_ipv4_neighbor_get_link_layer_address, NULL, NULL), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Recieved link-layer-address %s for neighbor address %s", mod_ctx->mod_data.ipv4.neighbor.link_layer_address, node_value);
+
+ // parse link-layer address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(mod_ctx->mod_data.ipv4.neighbor.link_layer_address, AF_LLC, &ll_addr), error_out);
+
+ // set addresses to the new neighbor
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_set_dst(request_neigh, dst_addr), error_out);
+ rtnl_neigh_set_lladdr(request_neigh, ll_addr);
+
+ // set interface
+ rtnl_neigh_set_ifindex(request_neigh, rtnl_link_get_ifindex(current_link));
+
+ // add neighbor
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_add(mod_ctx->nl_ctx.socket, request_neigh, NLM_F_CREATE), error_out);
+
+ break;
+ case SR_OP_MODIFIED:
+ // should be impossible - address IP can only be created and deleted
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Unsuported operation MODIFY for interface IPv4 neighbor IP leaf");
+ goto error_out;
+ break;
+ case SR_OP_DELETED:
+ request_neigh = rtnl_neigh_alloc();
+
+ // set interface
+ rtnl_neigh_set_ifindex(request_neigh, rtnl_link_get_ifindex(current_link));
+
+ // parse destination
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(node_value, AF_INET, &dst_addr), error_out);
+
+ // set destination
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_set_dst(request_neigh, dst_addr), error_out);
+
+ // remove wanted neighbor
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_delete(mod_ctx->nl_ctx.socket, request_neigh, 0), error_out);
+
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ goto out;
+
+error_out:
+ if (error < 0) {
+ SRPLG_LOG_INF(PLUGIN_NAME, "nl_geterror(): %s", nl_geterror(error));
+ }
+ error = -1;
+
+out:
+
+ // re-initialize mod_ctx data
+ if (mod_ctx->mod_data.ipv4.neighbor.link_layer_address) {
+ free(mod_ctx->mod_data.ipv4.neighbor.link_layer_address);
+ }
+ mod_ctx->mod_data.ipv4.neighbor.link_layer_address = NULL;
+ mod_ctx->mod_data.ipv4.neighbor.link_layer_set = false;
+
+ if (request_neigh) {
+ rtnl_neigh_put(request_neigh);
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv4_neighbor_change_ip_free(void* priv)
+{
+}
+
+static int interfaces_interface_ipv4_neighbor_get_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+
+ // ctx
+ interfaces_ctx_t* ctx = priv;
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // this callback should only be called on CREATED operation
+ assert(change_ctx->operation == SR_OP_CREATED);
+
+ // set mod changes prefix length
+ mod_ctx->mod_data.ipv4.neighbor.link_layer_address = strdup(node_value);
+ if (!mod_ctx->mod_data.ipv4.neighbor.link_layer_address) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Unable to set link-layer-address value for module changes context");
+ goto error_out;
+ }
+ mod_ctx->mod_data.ipv4.neighbor.link_layer_set = true;
+
+ goto out;
+
+error_out:
+ error = -1;
+ mod_ctx->mod_data.ipv4.neighbor.link_layer_set = false;
+
+out:
+ return error;
+}
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/change.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/change.h
new file mode 100644
index 00000000..96fc4108
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/change.h
@@ -0,0 +1,15 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_NEIGHBOR_CHANGE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_NEIGHBOR_CHANGE_H
+
+#include
+#include
+
+int interfaces_interface_ipv4_neighbor_change_link_layer_address_init(void* priv);
+int interfaces_interface_ipv4_neighbor_change_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv4_neighbor_change_link_layer_address_free(void* priv);
+
+int interfaces_interface_ipv4_neighbor_change_ip_init(void* priv);
+int interfaces_interface_ipv4_neighbor_change_ip(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv4_neighbor_change_ip_free(void* priv);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_NEIGHBOR_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/load.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/load.c
new file mode 100644
index 00000000..63540792
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/load.c
@@ -0,0 +1,58 @@
+#include "load.h"
+#include "netlink/route/neighbour.h"
+#include "plugin/data/interfaces/interface/ipv4/neighbor.h"
+
+int interfaces_interface_ipv4_neighbor_load_ip(interfaces_ctx_t* ctx, interfaces_interface_ipv4_neighbor_element_t** element, struct rtnl_neigh* neigh)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+ char ip_buffer[100] = { 0 };
+
+ // convert to nl_addr
+ struct nl_addr* dst = rtnl_neigh_get_dst(neigh);
+
+ // parse to string
+ SRPC_SAFE_CALL_PTR(error_ptr, nl_addr2str(dst, ip_buffer, sizeof(ip_buffer)), error_out);
+
+ char* prefix = strchr(ip_buffer, '/');
+ if (prefix) {
+ *prefix = 0;
+ }
+
+ // set IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_neighbor_element_set_ip(element, ip_buffer), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
+
+int interfaces_interface_ipv4_neighbor_load_link_layer_address(interfaces_ctx_t* ctx, interfaces_interface_ipv4_neighbor_element_t** element, struct rtnl_neigh* neigh)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+ char lladdr_buffer[100] = { 0 };
+
+ // convert to nl_addr
+ struct nl_addr* dst = rtnl_neigh_get_lladdr(neigh);
+
+ // parse to string
+ SRPC_SAFE_CALL_PTR(error_ptr, nl_addr2str(dst, lladdr_buffer, sizeof(lladdr_buffer)), error_out);
+
+ // set lladdr
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_neighbor_element_set_link_layer_address(element, lladdr_buffer), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/load.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/load.h
new file mode 100644
index 00000000..74dc942e
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv4/neighbor/load.h
@@ -0,0 +1,11 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_NEIGHBOR_LOAD_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_NEIGHBOR_LOAD_H
+
+#include "netlink/route/neighbour.h"
+#include "plugin/context.h"
+#include "plugin/types.h"
+
+int interfaces_interface_ipv4_neighbor_load_ip(interfaces_ctx_t* ctx, interfaces_interface_ipv4_neighbor_element_t** element, struct rtnl_neigh* neigh);
+int interfaces_interface_ipv4_neighbor_load_link_layer_address(interfaces_ctx_t* ctx, interfaces_interface_ipv4_neighbor_element_t** element, struct rtnl_neigh* neigh);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV4_NEIGHBOR_LOAD_H
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.c
new file mode 100644
index 00000000..25456806
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.c
@@ -0,0 +1,311 @@
+#include "change.h"
+#include "plugin/common.h"
+#include "plugin/context.h"
+
+#include
+#include
+
+#include
+#include
+
+static int interfaces_interface_ipv6_address_get_prefix_length(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+
+int interfaces_interface_ipv6_address_change_prefix_length_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_address_change_prefix_length(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+
+ // strings and buffers
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+ char path_buffer[PATH_MAX] = { 0 };
+ char interface_name_buffer[100] = { 0 };
+ char ip_buffer[100] = { 0 };
+ char address_buffer[100] = { 0 };
+ char old_address_buffer[100] = { 0 };
+
+ // app context
+ interfaces_ctx_t* ctx = priv;
+
+ // mod changes context
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ // libnl
+ struct rtnl_addr* request_addr = NULL;
+ struct rtnl_addr* delete_addr = NULL;
+ struct rtnl_link* current_link = NULL;
+ struct nl_addr* local_addr = NULL;
+ struct nl_addr* old_local_addr = NULL;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // get node path
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out);
+
+ // get interface name
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ // get IP
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "address", "ip", ip_buffer, sizeof(ip_buffer)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s; Address IP: %s", path_buffer, interface_name_buffer, ip_buffer);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ // this change case should be handled only when creating the whole address in the IP callback
+ // no need to process created change
+ break;
+ case SR_OP_MODIFIED:
+ // change prefix length
+ request_addr = rtnl_addr_alloc();
+ delete_addr = rtnl_addr_alloc();
+
+ // get full address
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(address_buffer, sizeof(address_buffer), "%s/%s", ip_buffer, node_value), error_out);
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(old_address_buffer, sizeof(old_address_buffer), "%s/%s", ip_buffer, change_ctx->previous_value), error_out);
+
+ // parse local address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(address_buffer, AF_INET6, &local_addr), error_out);
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(old_address_buffer, AF_INET6, &old_local_addr), error_out);
+
+ // set to route address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(request_addr, local_addr), error_out);
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(delete_addr, old_local_addr), error_out);
+
+ // set interface
+ rtnl_addr_set_ifindex(request_addr, rtnl_link_get_ifindex(current_link));
+ rtnl_addr_set_ifindex(delete_addr, rtnl_link_get_ifindex(current_link));
+
+ // delete old address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_delete(mod_ctx->nl_ctx.socket, delete_addr, 0), error_out);
+
+ // add new address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_add(mod_ctx->nl_ctx.socket, request_addr, 0), error_out);
+
+ break;
+ case SR_OP_DELETED:
+ // prefix is needed to find the appropriate address
+ // should be processed when IP deleted
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ goto out;
+
+error_out:
+ if (error < 0) {
+ SRPLG_LOG_INF(PLUGIN_NAME, "nl_geterror(): %s", nl_geterror(error));
+ }
+ error = -1;
+
+out:
+ if (request_addr) {
+ rtnl_addr_put(request_addr);
+ }
+
+ if (delete_addr) {
+ rtnl_addr_put(delete_addr);
+ }
+
+ if (old_local_addr) {
+ nl_addr_put(old_local_addr);
+ }
+
+ if (local_addr) {
+ nl_addr_put(local_addr);
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv6_address_change_prefix_length_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv6_address_change_ip_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_address_change_ip(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+
+ // sysrepo
+ sr_val_t* prefix_val = NULL;
+ sr_conn_ctx_t* conn_ctx = NULL;
+ sr_session_ctx_t* running_session = NULL;
+
+ // strings and buffers
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+ char path_buffer[PATH_MAX] = { 0 };
+ char interface_name_buffer[100] = { 0 };
+ char address_buffer[100] = { 0 };
+
+ // app context
+ interfaces_ctx_t* ctx = priv;
+
+ // mod changes context
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ // libnl
+ struct rtnl_addr* request_addr = NULL;
+ struct rtnl_link* current_link = NULL;
+ struct nl_addr* local_addr = NULL;
+
+ // data
+ uint8_t prefix_length = 0;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // get node path
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out);
+
+ // get interface name
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s", path_buffer, interface_name_buffer);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out);
+
+ // get connection
+ SRPC_SAFE_CALL_PTR(conn_ctx, sr_session_get_connection(session), error_out);
+
+ // start a running DS session - fetching data about prefix when deleting the address
+ SRPC_SAFE_CALL_ERR(error, sr_session_start(conn_ctx, SR_DS_RUNNING, &running_session), error_out);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ // new address
+ request_addr = rtnl_addr_alloc();
+
+ // parse local address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(node_value, AF_INET6, &local_addr), error_out);
+
+ // get prefix length by using prefix-length or netmask leafs
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(path_buffer, sizeof(path_buffer), "%s[name=\"%s\"]/ietf-ip:ipv6/address[ip=\"%s\"]/prefix-length", INTERFACES_INTERFACES_LIST_YANG_PATH, interface_name_buffer, node_value), error_out);
+ SRPC_SAFE_CALL_ERR(error, srpc_iterate_changes(ctx, session, path_buffer, interfaces_interface_ipv6_address_get_prefix_length, NULL, NULL), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Recieved prefix-length of %d for address %s", mod_ctx->mod_data.ipv6.address.prefix_length, node_value);
+
+ // prefix was set and found
+
+ // set final prefix length
+ nl_addr_set_prefixlen(local_addr, mod_ctx->mod_data.ipv6.address.prefix_length);
+
+ // set to route address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(request_addr, local_addr), error_out);
+
+ // set interface
+ rtnl_addr_set_ifindex(request_addr, rtnl_link_get_ifindex(current_link));
+
+ // add address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_add(mod_ctx->nl_ctx.socket, request_addr, 0), error_out);
+
+ break;
+ case SR_OP_MODIFIED:
+ // should be impossible - address IP can only be created and deleted
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Unsuported operation MODIFY for interface IPv6 address IP leaf");
+ goto error_out;
+ break;
+ case SR_OP_DELETED:
+ // fetch info about prefix-length/netmask and use the pair address/prefix to delete address
+ // prefix is needed to find the appropriate address
+ request_addr = rtnl_addr_alloc();
+
+ // check for prefix-length
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(path_buffer, sizeof(path_buffer), INTERFACES_INTERFACES_INTERFACE_YANG_PATH "[name=\"%s\"]/ietf-ip:ipv6/address[ip=\"%s\"]/prefix-length", interface_name_buffer, node_value), error_out);
+ SRPLG_LOG_INF(PLUGIN_NAME, "Searching running DS for %s", path_buffer);
+ SRPC_SAFE_CALL_ERR(error, sr_get_item(running_session, path_buffer, 0, &prefix_val), error_out);
+
+ // set data
+ prefix_length = prefix_val->data.uint8_val;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Recieved prefix for address %s: %d", node_value, prefix_length);
+
+ // after getting the prefix length - remove address
+
+ // get full address
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(address_buffer, sizeof(address_buffer), "%s/%d", node_value, prefix_length), error_out);
+
+ // parse local address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(address_buffer, AF_INET6, &local_addr), error_out);
+
+ // set to route address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(request_addr, local_addr), error_out);
+
+ // set interface
+ rtnl_addr_set_ifindex(request_addr, rtnl_link_get_ifindex(current_link));
+
+ // remove wanted address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_delete(mod_ctx->nl_ctx.socket, request_addr, 0), error_out);
+
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ goto out;
+
+error_out:
+ if (error < 0) {
+ SRPLG_LOG_INF(PLUGIN_NAME, "nl_geterror(): %s", nl_geterror(error));
+ }
+ error = -1;
+
+out:
+ if (running_session) {
+ sr_session_stop(running_session);
+ }
+
+ if (request_addr) {
+ rtnl_addr_put(request_addr);
+ }
+
+ // re-initialize mod_ctx data
+ mod_ctx->mod_data.ipv6.address.prefix_length = 0;
+ mod_ctx->mod_data.ipv6.address.prefix_set = false;
+
+ return error;
+}
+
+void interfaces_interface_ipv6_address_change_ip_free(void* priv)
+{
+}
+
+static int interfaces_interface_ipv6_address_get_prefix_length(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+
+ // ctx
+ interfaces_ctx_t* ctx = priv;
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // this callback should only be called on CREATED operation
+ assert(change_ctx->operation == SR_OP_CREATED);
+
+ // parse prefix length
+ mod_ctx->mod_data.ipv6.address.prefix_length = (uint8_t)atoi(node_value);
+ mod_ctx->mod_data.ipv6.address.prefix_set = true;
+
+ return error;
+}
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.h
new file mode 100644
index 00000000..884a1701
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/change.h
@@ -0,0 +1,15 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_ADDRESS_CHANGE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_ADDRESS_CHANGE_H
+
+#include
+#include
+
+int interfaces_interface_ipv6_address_change_prefix_length_init(void* priv);
+int interfaces_interface_ipv6_address_change_prefix_length(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_address_change_prefix_length_free(void* priv);
+
+int interfaces_interface_ipv6_address_change_ip_init(void* priv);
+int interfaces_interface_ipv6_address_change_ip(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_address_change_ip_free(void* priv);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_ADDRESS_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/load.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/load.c
new file mode 100644
index 00000000..1dda97ff
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/load.c
@@ -0,0 +1,52 @@
+#include "load.h"
+#include "netlink/addr.h"
+#include "netlink/route/addr.h"
+#include "plugin/data/interfaces/interface/ipv6/address.h"
+
+int interfaces_interface_ipv6_address_load_ip(interfaces_ctx_t* ctx, interfaces_interface_ipv6_address_element_t** element, struct rtnl_addr* addr)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+ char ip_buffer[100] = { 0 };
+
+ // convert to nl_addr
+ struct nl_addr* local = rtnl_addr_get_local(addr);
+
+ // parse to string
+ SRPC_SAFE_CALL_PTR(error_ptr, nl_addr2str(local, ip_buffer, sizeof(ip_buffer)), error_out);
+
+ char* prefix = strchr(ip_buffer, '/');
+ if (prefix) {
+ *prefix = 0;
+ }
+
+ // set IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_address_element_set_ip(element, ip_buffer), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
+
+int interfaces_interface_ipv6_address_load_prefix_length(interfaces_ctx_t* ctx, interfaces_interface_ipv6_address_element_t** element, struct rtnl_addr* addr)
+{
+ int error = 0;
+
+ const uint8_t prefix = (uint8_t)rtnl_addr_get_prefixlen(addr);
+
+ // set prefix length
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_address_element_set_prefix_length(element, prefix), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ return error;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/load.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/load.h
new file mode 100644
index 00000000..140245fe
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/address/load.h
@@ -0,0 +1,11 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_ADDRESS_LOAD_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_ADDRESS_LOAD_H
+
+#include "netlink/route/addr.h"
+#include "plugin/context.h"
+#include "plugin/types.h"
+
+int interfaces_interface_ipv6_address_load_ip(interfaces_ctx_t* ctx, interfaces_interface_ipv6_address_element_t** element, struct rtnl_addr* addr);
+int interfaces_interface_ipv6_address_load_prefix_length(interfaces_ctx_t* ctx, interfaces_interface_ipv6_address_element_t** element, struct rtnl_addr* addr);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_ADDRESS_LOAD_H
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/autoconf/change.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/autoconf/change.c
new file mode 100644
index 00000000..3414625b
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/autoconf/change.c
@@ -0,0 +1,132 @@
+#include "change.h"
+#include "plugin/common.h"
+
+#include
+
+int interfaces_interface_ipv6_autoconf_change_temporary_preferred_lifetime_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_autoconf_change_temporary_preferred_lifetime(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv6_autoconf_change_temporary_preferred_lifetime_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv6_autoconf_change_temporary_valid_lifetime_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_autoconf_change_temporary_valid_lifetime(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv6_autoconf_change_temporary_valid_lifetime_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv6_autoconf_change_create_temporary_addresses_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_autoconf_change_create_temporary_addresses(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv6_autoconf_change_create_temporary_addresses_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv6_autoconf_change_create_global_addresses_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_autoconf_change_create_global_addresses(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv6_autoconf_change_create_global_addresses_free(void* priv)
+{
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/autoconf/change.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/autoconf/change.h
new file mode 100644
index 00000000..080ad2ba
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/autoconf/change.h
@@ -0,0 +1,20 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_AUTOCONF_CHANGE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_AUTOCONF_CHANGE_H
+
+#include
+#include
+
+int interfaces_interface_ipv6_autoconf_change_temporary_preferred_lifetime_init(void* priv);
+int interfaces_interface_ipv6_autoconf_change_temporary_preferred_lifetime(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_autoconf_change_temporary_preferred_lifetime_free(void* priv);
+int interfaces_interface_ipv6_autoconf_change_temporary_valid_lifetime_init(void* priv);
+int interfaces_interface_ipv6_autoconf_change_temporary_valid_lifetime(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_autoconf_change_temporary_valid_lifetime_free(void* priv);
+int interfaces_interface_ipv6_autoconf_change_create_temporary_addresses_init(void* priv);
+int interfaces_interface_ipv6_autoconf_change_create_temporary_addresses(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_autoconf_change_create_temporary_addresses_free(void* priv);
+int interfaces_interface_ipv6_autoconf_change_create_global_addresses_init(void* priv);
+int interfaces_interface_ipv6_autoconf_change_create_global_addresses(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_autoconf_change_create_global_addresses_free(void* priv);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_AUTOCONF_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/change.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/change.c
new file mode 100644
index 00000000..9fd99278
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/change.c
@@ -0,0 +1,196 @@
+#include "change.h"
+#include "plugin/common.h"
+
+#include
+
+int interfaces_interface_ipv6_change_dup_addr_detect_transmits_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_change_dup_addr_detect_transmits(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv6_change_dup_addr_detect_transmits_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv6_change_neighbor_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_change_neighbor(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv6_change_neighbor_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv6_change_address_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_change_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv6_change_address_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv6_change_mtu_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_change_mtu(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv6_change_mtu_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv6_change_forwarding_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_change_forwarding(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv6_change_forwarding_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv6_change_enabled_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_change_enabled(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ break;
+ case SR_OP_MODIFIED:
+ break;
+ case SR_OP_DELETED:
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv6_change_enabled_free(void* priv)
+{
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/change.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/change.h
new file mode 100644
index 00000000..a594cb14
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/change.h
@@ -0,0 +1,31 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_CHANGE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_CHANGE_H
+
+#include
+#include
+
+int interfaces_interface_ipv6_change_dup_addr_detect_transmits_init(void* priv);
+int interfaces_interface_ipv6_change_dup_addr_detect_transmits(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_change_dup_addr_detect_transmits_free(void* priv);
+
+int interfaces_interface_ipv6_change_neighbor_init(void* priv);
+int interfaces_interface_ipv6_change_neighbor(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_change_neighbor_free(void* priv);
+
+int interfaces_interface_ipv6_change_address_init(void* priv);
+int interfaces_interface_ipv6_change_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_change_address_free(void* priv);
+
+int interfaces_interface_ipv6_change_mtu_init(void* priv);
+int interfaces_interface_ipv6_change_mtu(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_change_mtu_free(void* priv);
+
+int interfaces_interface_ipv6_change_forwarding_init(void* priv);
+int interfaces_interface_ipv6_change_forwarding(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_change_forwarding_free(void* priv);
+
+int interfaces_interface_ipv6_change_enabled_init(void* priv);
+int interfaces_interface_ipv6_change_enabled(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_change_enabled_free(void* priv);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/load.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/load.c
new file mode 100644
index 00000000..a204546e
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/load.c
@@ -0,0 +1,160 @@
+#include "load.h"
+#include "netlink/route/link.h"
+#include "plugin/api/interfaces/interface/ipv6/address/load.h"
+#include "plugin/api/interfaces/interface/ipv6/neighbor/load.h"
+#include "plugin/data/interfaces/interface.h"
+#include "plugin/data/interfaces/interface/ipv6.h"
+
+int interfaces_interface_ipv6_load_enabled(interfaces_ctx_t* ctx, interfaces_interface_ipv6_t* ipv6, struct rtnl_link* link)
+{
+ int error = 0;
+
+ // enabled by default
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_element_ipv6_set_enabled(ipv6, 1), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
+
+int interfaces_interface_ipv6_load_forwarding(interfaces_ctx_t* ctx, interfaces_interface_ipv6_t* ipv6, struct rtnl_link* link)
+{
+ int error = 0;
+
+ // TODO: implement
+
+ return error;
+}
+
+int interfaces_interface_ipv6_load_mtu(interfaces_ctx_t* ctx, interfaces_interface_ipv6_t* ipv6, struct rtnl_link* link)
+{
+ int error = 0;
+
+ const unsigned int mtu = rtnl_link_get_mtu(link);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_element_ipv6_set_mtu(ipv6, (uint16_t)mtu), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
+
+int interfaces_interface_ipv6_load_address(interfaces_ctx_t* ctx, interfaces_interface_ipv6_t* ipv6, struct rtnl_link* link)
+{
+ int error = 0;
+ interfaces_nl_ctx_t* nl_ctx = &ctx->startup_ctx.nl_ctx;
+ struct rtnl_addr* addr_iter = NULL;
+
+ // created element
+ interfaces_interface_ipv6_address_element_t* new_element = NULL;
+ uint8_t element_added = 0;
+
+ // allocate address list
+ ipv6->address = interfaces_interface_ipv6_address_new();
+
+ // allocate cache
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_alloc_cache(nl_ctx->socket, &nl_ctx->addr_cache), error_out);
+
+ // get link iterator
+ SRPC_SAFE_CALL_PTR(addr_iter, (struct rtnl_addr*)nl_cache_get_first(nl_ctx->addr_cache), error_out);
+
+ // iterate links
+ while (addr_iter) {
+ // get all addresses from the link and extract info
+ if (rtnl_addr_get_ifindex(addr_iter) == rtnl_link_get_ifindex(link) && rtnl_addr_get_family(addr_iter) == AF_INET6) {
+ // create new element
+ new_element = interfaces_interface_ipv6_address_element_new();
+ element_added = 0;
+
+ // load IP and prefix
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_address_load_ip(ctx, &new_element, addr_iter), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_address_load_prefix_length(ctx, &new_element, addr_iter), error_out);
+
+ // add element to the list
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_address_add_element(&ipv6->address, new_element), error_out);
+ element_added = 1;
+ }
+
+ // iterate
+ addr_iter = (struct rtnl_addr*)nl_cache_get_next((struct nl_object*)addr_iter);
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+ // if interrupted see if any memory is left over from the allocated element - free the element
+ if (!element_added) {
+ interfaces_interface_ipv6_address_element_free(&new_element);
+ }
+
+out:
+
+ return error;
+}
+
+int interfaces_interface_ipv6_load_neighbor(interfaces_ctx_t* ctx, interfaces_interface_ipv6_t* ipv6, struct rtnl_link* link)
+{
+ int error = 0;
+ interfaces_nl_ctx_t* nl_ctx = &ctx->startup_ctx.nl_ctx;
+ struct rtnl_neigh* neigh_iter = NULL;
+
+ // created element
+ interfaces_interface_ipv6_neighbor_element_t* new_element = NULL;
+ uint8_t element_added = 0;
+
+ // allocate address list
+ ipv6->neighbor = interfaces_interface_ipv6_neighbor_new();
+
+ // allocate cache
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_alloc_cache(nl_ctx->socket, &nl_ctx->neigh_cache), error_out);
+
+ // get link iterator
+ SRPC_SAFE_CALL_PTR(neigh_iter, (struct rtnl_neigh*)nl_cache_get_first(nl_ctx->neigh_cache), error_out);
+
+ // iterate links
+ while (neigh_iter) {
+ // get all neighbors from the link and extract info
+ if (rtnl_neigh_get_ifindex(neigh_iter) == rtnl_link_get_ifindex(link) && rtnl_neigh_get_family(neigh_iter) == AF_INET6) {
+ // create new element
+ new_element = interfaces_interface_ipv6_neighbor_element_new();
+ element_added = 0;
+
+ // load IP and prefix
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_neighbor_load_ip(ctx, &new_element, neigh_iter), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_neighbor_load_link_layer_address(ctx, &new_element, neigh_iter), error_out);
+
+ // add element to the list
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_neighbor_add_element(&ipv6->neighbor, new_element), error_out);
+ element_added = 1;
+ }
+
+ // iterate
+ neigh_iter = (struct rtnl_neigh*)nl_cache_get_next((struct nl_object*)neigh_iter);
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+ // if interrupted see if any memory is left over from the allocated element - free the element
+ if (!element_added) {
+ interfaces_interface_ipv6_neighbor_element_free(&new_element);
+ }
+
+out:
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/load.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/load.h
new file mode 100644
index 00000000..b92e7343
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/load.h
@@ -0,0 +1,15 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_LOAD_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_LOAD_H
+
+#include "plugin/context.h"
+#include "plugin/data/interfaces/interface.h"
+#include "plugin/data/interfaces/interface/linked_list.h"
+#include "plugin/types.h"
+
+int interfaces_interface_ipv6_load_enabled(interfaces_ctx_t* ctx, interfaces_interface_ipv6_t* ipv6, struct rtnl_link* link);
+int interfaces_interface_ipv6_load_forwarding(interfaces_ctx_t* ctx, interfaces_interface_ipv6_t* ipv6, struct rtnl_link* link);
+int interfaces_interface_ipv6_load_mtu(interfaces_ctx_t* ctx, interfaces_interface_ipv6_t* ipv6, struct rtnl_link* link);
+int interfaces_interface_ipv6_load_address(interfaces_ctx_t* ctx, interfaces_interface_ipv6_t* ipv6, struct rtnl_link* link);
+int interfaces_interface_ipv6_load_neighbor(interfaces_ctx_t* ctx, interfaces_interface_ipv6_t* ipv6, struct rtnl_link* link);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_LOAD_H
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.c
new file mode 100644
index 00000000..9850f038
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.c
@@ -0,0 +1,262 @@
+#include "change.h"
+#include "plugin/common.h"
+#include "plugin/context.h"
+
+#include
+#include
+
+#include
+#include
+#include
+
+static int interfaces_interface_ipv6_neighbor_get_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+
+int interfaces_interface_ipv6_neighbor_change_link_layer_address_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_neighbor_change_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+
+ // strings and buffers
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+ char path_buffer[PATH_MAX] = { 0 };
+ char interface_name_buffer[100] = { 0 };
+ char ip_buffer[100] = { 0 };
+
+ // app context
+ interfaces_ctx_t* ctx = priv;
+
+ // mod changes context
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ // libnl
+ struct rtnl_neigh* request_neigh = NULL;
+ struct rtnl_link* current_link = NULL;
+ struct nl_addr* dst_addr = NULL;
+ struct nl_addr* ll_addr = NULL;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // get node path
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out);
+
+ // get interface name
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ // get IP
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "neighbor", "ip", ip_buffer, sizeof(ip_buffer)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s; Neighbor IP: %s", path_buffer, interface_name_buffer, ip_buffer);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ // not used - used only in IP change callback
+ break;
+ case SR_OP_MODIFIED:
+ // change lladdr
+ request_neigh = rtnl_neigh_alloc();
+
+ // set interface
+ rtnl_neigh_set_ifindex(request_neigh, rtnl_link_get_ifindex(current_link));
+
+ // parse destination and LL address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(ip_buffer, AF_INET6, &dst_addr), error_out);
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(node_value, AF_LLC, &ll_addr), error_out);
+
+ // set destination and LL address
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_set_dst(request_neigh, dst_addr), error_out);
+ rtnl_neigh_set_lladdr(request_neigh, ll_addr);
+
+ // change neighbor
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_add(mod_ctx->nl_ctx.socket, request_neigh, NLM_F_REPLACE), error_out);
+
+ break;
+ case SR_OP_DELETED:
+ // not used - used only in IP change callback
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ if (request_neigh) {
+ rtnl_neigh_put(request_neigh);
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv6_neighbor_change_link_layer_address_free(void* priv)
+{
+}
+
+int interfaces_interface_ipv6_neighbor_change_ip_init(void* priv)
+{
+ int error = 0;
+ return error;
+}
+
+int interfaces_interface_ipv6_neighbor_change_ip(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+
+ // strings and buffers
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+ char path_buffer[PATH_MAX] = { 0 };
+ char interface_name_buffer[100] = { 0 };
+
+ // app context
+ interfaces_ctx_t* ctx = priv;
+
+ // mod changes context
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ // libnl
+ struct rtnl_neigh* request_neigh = NULL;
+ struct rtnl_link* current_link = NULL;
+ struct nl_addr* dst_addr = NULL;
+ struct nl_addr* ll_addr = NULL;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // get node path
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(change_ctx->node, LYD_PATH_STD, path_buffer, sizeof(path_buffer)), error_out);
+
+ // get interface name
+ SRPC_SAFE_CALL_ERR(error, srpc_extract_xpath_key_value(path_buffer, "interface", "name", interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Path: %s; Interface Name: %s", path_buffer, interface_name_buffer);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(current_link, rtnl_link_get_by_name(mod_ctx->nl_ctx.link_cache, interface_name_buffer), error_out);
+
+ switch (change_ctx->operation) {
+ case SR_OP_CREATED:
+ // new neighbor
+ request_neigh = rtnl_neigh_alloc();
+
+ // parse destination address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(node_value, AF_INET6, &dst_addr), error_out);
+
+ // get prefix length by using prefix-length or netmask leafs
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(path_buffer, sizeof(path_buffer), INTERFACES_INTERFACES_INTERFACE_YANG_PATH "[name=\"%s\"]/ietf-ip:ipv6/neighbor[ip=\"%s\"]/link-layer-address", interface_name_buffer, node_value), error_out);
+ SRPC_SAFE_CALL_ERR(error, srpc_iterate_changes(ctx, session, path_buffer, interfaces_interface_ipv6_neighbor_get_link_layer_address, NULL, NULL), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Recieved link-layer-address %s for neighbor address %s", mod_ctx->mod_data.ipv6.neighbor.link_layer_address, node_value);
+
+ // parse link-layer address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(mod_ctx->mod_data.ipv6.neighbor.link_layer_address, AF_LLC, &ll_addr), error_out);
+
+ // set addresses to the new neighbor
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_set_dst(request_neigh, dst_addr), error_out);
+ rtnl_neigh_set_lladdr(request_neigh, ll_addr);
+
+ // set interface
+ rtnl_neigh_set_ifindex(request_neigh, rtnl_link_get_ifindex(current_link));
+
+ // add neighbor
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_add(mod_ctx->nl_ctx.socket, request_neigh, NLM_F_CREATE), error_out);
+
+ break;
+ case SR_OP_MODIFIED:
+ // should be impossible - address IP can only be created and deleted
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Unsuported operation MODIFY for interface IPv6 neighbor IP leaf");
+ goto error_out;
+ break;
+ case SR_OP_DELETED:
+ request_neigh = rtnl_neigh_alloc();
+
+ // set interface
+ rtnl_neigh_set_ifindex(request_neigh, rtnl_link_get_ifindex(current_link));
+
+ // parse destination
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(node_value, AF_INET6, &dst_addr), error_out);
+
+ // set destination
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_set_dst(request_neigh, dst_addr), error_out);
+
+ // remove wanted neighbor
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_delete(mod_ctx->nl_ctx.socket, request_neigh, 0), error_out);
+
+ break;
+ case SR_OP_MOVED:
+ break;
+ }
+
+ goto out;
+
+error_out:
+ if (error < 0) {
+ SRPLG_LOG_INF(PLUGIN_NAME, "nl_geterror(): %s", nl_geterror(error));
+ }
+ error = -1;
+
+out:
+
+ // re-initialize mod_ctx data
+ if (mod_ctx->mod_data.ipv6.neighbor.link_layer_address) {
+ free(mod_ctx->mod_data.ipv6.neighbor.link_layer_address);
+ }
+ mod_ctx->mod_data.ipv6.neighbor.link_layer_address = NULL;
+ mod_ctx->mod_data.ipv6.neighbor.link_layer_set = false;
+
+ if (request_neigh) {
+ rtnl_neigh_put(request_neigh);
+ }
+
+ return error;
+}
+
+void interfaces_interface_ipv6_neighbor_change_ip_free(void* priv)
+{
+}
+
+static int interfaces_interface_ipv6_neighbor_get_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx)
+{
+ int error = 0;
+
+ // ctx
+ interfaces_ctx_t* ctx = priv;
+ interfaces_mod_changes_ctx_t* mod_ctx = &ctx->mod_ctx;
+
+ const char* node_name = LYD_NAME(change_ctx->node);
+ const char* node_value = lyd_get_value(change_ctx->node);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "Node Name: %s; Previous Value: %s, Value: %s; Operation: %d", node_name, change_ctx->previous_value, node_value, change_ctx->operation);
+
+ // this callback should only be called on CREATED operation
+ assert(change_ctx->operation == SR_OP_CREATED);
+
+ // set mod changes prefix length
+ mod_ctx->mod_data.ipv6.neighbor.link_layer_address = strdup(node_value);
+ if (!mod_ctx->mod_data.ipv6.neighbor.link_layer_address) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Unable to set link-layer-address value for module changes context");
+ goto error_out;
+ }
+ mod_ctx->mod_data.ipv6.neighbor.link_layer_set = true;
+
+ goto out;
+
+error_out:
+ error = -1;
+ mod_ctx->mod_data.ipv6.neighbor.link_layer_set = false;
+
+out:
+ return error;
+}
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.h
new file mode 100644
index 00000000..ac15a9ab
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/change.h
@@ -0,0 +1,15 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_NEIGHBOR_CHANGE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_NEIGHBOR_CHANGE_H
+
+#include
+#include
+
+int interfaces_interface_ipv6_neighbor_change_link_layer_address_init(void* priv);
+int interfaces_interface_ipv6_neighbor_change_link_layer_address(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_neighbor_change_link_layer_address_free(void* priv);
+
+int interfaces_interface_ipv6_neighbor_change_ip_init(void* priv);
+int interfaces_interface_ipv6_neighbor_change_ip(void* priv, sr_session_ctx_t* session, const srpc_change_ctx_t* change_ctx);
+void interfaces_interface_ipv6_neighbor_change_ip_free(void* priv);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_NEIGHBOR_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/load.c b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/load.c
new file mode 100644
index 00000000..1301f6f8
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/load.c
@@ -0,0 +1,58 @@
+#include "load.h"
+#include "netlink/route/neighbour.h"
+#include "plugin/data/interfaces/interface/ipv6/neighbor.h"
+
+int interfaces_interface_ipv6_neighbor_load_ip(interfaces_ctx_t* ctx, interfaces_interface_ipv6_neighbor_element_t** element, struct rtnl_neigh* neigh)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+ char ip_buffer[100] = { 0 };
+
+ // convert to nl_addr
+ struct nl_addr* dst = rtnl_neigh_get_dst(neigh);
+
+ // parse to string
+ SRPC_SAFE_CALL_PTR(error_ptr, nl_addr2str(dst, ip_buffer, sizeof(ip_buffer)), error_out);
+
+ char* prefix = strchr(ip_buffer, '/');
+ if (prefix) {
+ *prefix = 0;
+ }
+
+ // set IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_neighbor_element_set_ip(element, ip_buffer), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
+
+int interfaces_interface_ipv6_neighbor_load_link_layer_address(interfaces_ctx_t* ctx, interfaces_interface_ipv6_neighbor_element_t** element, struct rtnl_neigh* neigh)
+{
+ int error = 0;
+ void* error_ptr = NULL;
+ char lladdr_buffer[100] = { 0 };
+
+ // convert to nl_addr
+ struct nl_addr* dst = rtnl_neigh_get_lladdr(neigh);
+
+ // parse to string
+ SRPC_SAFE_CALL_PTR(error_ptr, nl_addr2str(dst, lladdr_buffer, sizeof(lladdr_buffer)), error_out);
+
+ // set lladdr
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_neighbor_element_set_link_layer_address(element, lladdr_buffer), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/load.h b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/load.h
new file mode 100644
index 00000000..eb3aaa6b
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/ipv6/neighbor/load.h
@@ -0,0 +1,11 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_NEIGHBOR_LOAD_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_NEIGHBOR_LOAD_H
+
+#include "netlink/route/neighbour.h"
+#include "plugin/context.h"
+#include "plugin/types.h"
+
+int interfaces_interface_ipv6_neighbor_load_ip(interfaces_ctx_t* ctx, interfaces_interface_ipv6_neighbor_element_t** element, struct rtnl_neigh* neigh);
+int interfaces_interface_ipv6_neighbor_load_link_layer_address(interfaces_ctx_t* ctx, interfaces_interface_ipv6_neighbor_element_t** element, struct rtnl_neigh* neigh);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_IPV6_NEIGHBOR_LOAD_H
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/load.c b/src/interfaces/src/plugin/api/interfaces/interface/load.c
new file mode 100644
index 00000000..d45c38c7
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/load.c
@@ -0,0 +1,68 @@
+#include "load.h"
+#include "plugin/common.h"
+#include "plugin/data/interfaces/interface.h"
+#include "sysrepo.h"
+
+#include
+#include
+
+int interfaces_interface_load_name(interfaces_ctx_t* ctx, interfaces_interface_hash_element_t** element, struct rtnl_link* link)
+{
+ int error = 0;
+
+ const char* nl_if_name = NULL;
+
+ nl_if_name = rtnl_link_get_name(link);
+
+ // set element values
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_element_set_name(element, nl_if_name), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ return error;
+}
+
+int interfaces_interface_load_type(interfaces_ctx_t* ctx, interfaces_interface_hash_element_t** element, struct rtnl_link* link)
+{
+ int error = 0;
+
+ const char* nl_if_type = NULL;
+ const char* ly_if_type = NULL;
+
+ // interface type - nl version -> convert to libyang version
+ nl_if_type = rtnl_link_get_type(link);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_type_nl2ly(nl_if_type, &ly_if_type), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_element_set_type(element, ly_if_type), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
+
+int interfaces_interface_load_enabled(interfaces_ctx_t* ctx, interfaces_interface_hash_element_t** element, struct rtnl_link* link)
+{
+ int error = 0;
+
+ uint8_t nl_enabled = (rtnl_link_get_operstate(link) == IF_OPER_UP || rtnl_link_get_operstate(link) == IF_OPER_UNKNOWN);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_element_set_enabled(element, nl_enabled), error_out);
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/interface/load.h b/src/interfaces/src/plugin/api/interfaces/interface/load.h
new file mode 100644
index 00000000..0920c2af
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/interface/load.h
@@ -0,0 +1,13 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_LOAD_H
+#define INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_LOAD_H
+
+#include "plugin/context.h"
+#include "plugin/types.h"
+
+#include
+
+int interfaces_interface_load_name(interfaces_ctx_t* ctx, interfaces_interface_hash_element_t** element, struct rtnl_link* link);
+int interfaces_interface_load_type(interfaces_ctx_t* ctx, interfaces_interface_hash_element_t** element, struct rtnl_link* link);
+int interfaces_interface_load_enabled(interfaces_ctx_t* ctx, interfaces_interface_hash_element_t** element, struct rtnl_link* link);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_INTERFACE_LOAD_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/load.c b/src/interfaces/src/plugin/api/interfaces/load.c
new file mode 100644
index 00000000..2eb825de
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/load.c
@@ -0,0 +1,122 @@
+#include "load.h"
+#include "interface/ipv4/load.h"
+#include "interface/ipv6/load.h"
+#include "plugin/common.h"
+#include "plugin/context.h"
+#include "plugin/data/interfaces/interface.h"
+#include "plugin/types.h"
+#include "read.h"
+#include "utils/memory.h"
+#include "utlist.h"
+
+// load APIs
+#include "interface/load.h"
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+int interfaces_load_interface(interfaces_ctx_t* ctx, interfaces_interface_hash_element_t** if_hash)
+{
+ int error = 0;
+
+ // ctx
+ interfaces_startup_ctx_t* startup_ctx = &ctx->startup_ctx;
+ interfaces_nl_ctx_t* nl_ctx = &startup_ctx->nl_ctx;
+
+ // temp data
+ interfaces_interface_hash_element_t* new_element = NULL;
+ uint8_t element_added = 0;
+
+ struct rtnl_link* link_iter = NULL;
+
+ // init hash
+ *if_hash = interfaces_interface_hash_new();
+
+ // socket + cache
+ SRPC_SAFE_CALL_PTR(nl_ctx->socket, nl_socket_alloc(), error_out);
+ SRPC_SAFE_CALL_ERR(error, nl_connect(nl_ctx->socket, NETLINK_ROUTE), error_out);
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_alloc_cache(nl_ctx->socket, AF_UNSPEC, &nl_ctx->link_cache), error_out);
+
+ // get link iterator
+ SRPC_SAFE_CALL_PTR(link_iter, (struct rtnl_link*)nl_cache_get_first(nl_ctx->link_cache), error_out);
+
+ // iterate links
+ while (link_iter) {
+ // allocate new element
+ SRPC_SAFE_CALL_PTR(new_element, interfaces_interface_hash_element_new(), error_out);
+ element_added = 0;
+
+ // load interface data
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_load_name(ctx, &new_element, link_iter), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_load_type(ctx, &new_element, link_iter), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_load_enabled(ctx, &new_element, link_iter), error_out);
+
+ // load interface IPv4 data
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_load_enabled(ctx, &new_element->interface.ipv4, link_iter), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_load_forwarding(ctx, &new_element->interface.ipv4, link_iter), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_load_mtu(ctx, &new_element->interface.ipv4, link_iter), error_out);
+
+ // load interface IPv6 data
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_load_enabled(ctx, &new_element->interface.ipv6, link_iter), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_load_forwarding(ctx, &new_element->interface.ipv6, link_iter), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_load_mtu(ctx, &new_element->interface.ipv6, link_iter), error_out);
+
+ // load IPv4 address and neighbor lists
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_load_address(ctx, &new_element->interface.ipv4, link_iter), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_load_neighbor(ctx, &new_element->interface.ipv4, link_iter), error_out);
+
+ // load IPv6 address and neighbor lists
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_load_address(ctx, &new_element->interface.ipv6, link_iter), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_load_neighbor(ctx, &new_element->interface.ipv6, link_iter), error_out);
+
+ // add element to the hash
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_add_element(if_hash, new_element), error_out);
+ element_added = 1;
+
+ // iterate
+ link_iter = (struct rtnl_link*)nl_cache_get_next((struct nl_object*)link_iter);
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+ if (!element_added) {
+ interfaces_interface_hash_element_free(&new_element);
+ }
+
+out:
+ // dealloc nl_ctx data
+
+ if (nl_ctx->socket != NULL) {
+ nl_socket_free(nl_ctx->socket);
+ }
+
+ if (nl_ctx->link_cache != NULL) {
+ nl_cache_free(nl_ctx->link_cache);
+ }
+
+ // address and neighbor caches should be freed by their functions (_load_address and _load_neighbor)
+
+ return error;
+}
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/api/interfaces/load.h b/src/interfaces/src/plugin/api/interfaces/load.h
new file mode 100644
index 00000000..b3103459
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/load.h
@@ -0,0 +1,10 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_LOAD_H
+#define INTERFACES_PLUGIN_API_INTERFACES_LOAD_H
+
+#include "plugin/common.h"
+#include "plugin/context.h"
+#include "plugin/data/interfaces/interface.h"
+
+int interfaces_load_interface(interfaces_ctx_t* ctx, interfaces_interface_hash_element_t** if_hash);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_LOAD_H
diff --git a/src/interfaces/src/plugin/api/interfaces/read.c b/src/interfaces/src/plugin/api/interfaces/read.c
new file mode 100644
index 00000000..91af20de
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/read.c
@@ -0,0 +1,75 @@
+#include "plugin/common.h"
+#include "srpc/common.h"
+
+#include
+
+int read_from_proc_file(const char *dir_path, char *interface, const char *fn, int *val)
+{
+ int error = 0;
+ char tmp_buffer[PATH_MAX];
+ FILE *fptr = NULL;
+ char tmp_val[2] = {0};
+
+ error = snprintf(tmp_buffer, sizeof(tmp_buffer), "%s/%s/%s", dir_path, interface, fn);
+ if (error < 0) {
+ // snprintf error
+ SRPLG_LOG_ERR(PLUGIN_NAME, "snprintf failed");
+ goto out;
+ }
+
+ // snprintf returns return the number of bytes that are written
+ // reset error to 0
+ error = 0;
+
+ fptr = fopen((const char *) tmp_buffer, "r");
+
+ if (fptr != NULL) {
+ fgets(tmp_val, sizeof(tmp_val), fptr);
+
+ *val = atoi(tmp_val);
+
+ fclose(fptr);
+ } else {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "failed to open %s: %s", tmp_buffer, strerror(errno));
+ error = -1;
+ goto out;
+ }
+
+out:
+ return error;
+}
+
+int read_from_sys_file(const char* dir_path, char* interface, int* val)
+{
+ int error = 0;
+ char tmp_buffer[PATH_MAX];
+ FILE* fptr = NULL;
+ char tmp_val[4] = { 0 };
+
+ error = snprintf(tmp_buffer, sizeof(tmp_buffer), "%s/%s/type", dir_path, interface);
+ if (error < 0) {
+ // snprintf error
+ SRPLG_LOG_ERR(PLUGIN_NAME, "%s: snprintf failed", __func__);
+ goto out;
+ }
+
+ /* snprintf returns return the number of bytes that are written - reset error to 0 */
+ error = 0;
+
+ fptr = fopen((const char*)tmp_buffer, "r");
+
+ if (fptr != NULL) {
+ fgets(tmp_val, sizeof(tmp_val), fptr);
+
+ *val = atoi(tmp_val);
+
+ fclose(fptr);
+ } else {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "%s: failed to open %s: %s", __func__, tmp_buffer, strerror(errno));
+ error = -1;
+ goto out;
+ }
+
+out:
+ return error;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/read.h b/src/interfaces/src/plugin/api/interfaces/read.h
new file mode 100644
index 00000000..5ea196aa
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/read.h
@@ -0,0 +1,8 @@
+#ifndef READ_H
+#define READ_H
+
+int read_from_proc_file(const char *dir_path, char *interface, const char *fn, int *val);
+
+int read_from_sys_file(const char* dir_path, char* interface, int* val);
+
+#endif /* READ_H */
diff --git a/src/interfaces/src/plugin/api/interfaces/store.c b/src/interfaces/src/plugin/api/interfaces/store.c
new file mode 100644
index 00000000..2bfceba1
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/store.c
@@ -0,0 +1,171 @@
+#include "store.h"
+#include "netlink/addr.h"
+#include "netlink/cache.h"
+#include "netlink/object.h"
+#include "netlink/route/addr.h"
+#include "netlink/socket.h"
+#include "plugin/data/interfaces/interface/ipv4/address.h"
+#include "src/utlist.h"
+#include "srpc/common.h"
+#include "utlist.h"
+#include "uthash.h"
+#include "plugin/types.h"
+#include
+#include "plugin/common.h"
+#include "plugin/context.h"
+#include
+#include "sysrepo/xpath.h"
+#include "netlink/errno.h"
+#include "netlink/route/link.h"
+#include "/usr/include/linux/if.h"
+
+int interfaces_store_interface(interfaces_ctx_t* ctx, const interfaces_interface_hash_element_t* if_hash)
+{
+ int error = 0;
+ uint8_t if_status, prefix_length;
+ uint32_t mtu;
+ char *if_type = NULL;
+ char *if_type_libnl = NULL;
+ const interfaces_interface_hash_element_t *i = NULL, *tmp = NULL;
+ interfaces_interface_ipv4_address_element_t *ipv4_iter = NULL;
+ interfaces_interface_ipv6_address_element_t *ipv6_iter = NULL;
+ struct nl_sock *sk = NULL;
+ struct nl_addr* local_addr = NULL;
+ struct nl_cache* link_cache = NULL;
+ struct rtnl_link* new_link = NULL;
+ struct rtnl_addr *link_ipv4_addr = NULL;
+ struct rtnl_addr *link_ipv6_addr = NULL;
+
+ // setup nl socket
+ SRPC_SAFE_CALL_PTR(sk, nl_socket_alloc(), error_out);
+
+ // connect
+ SRPC_SAFE_CALL_ERR(error, nl_connect(sk, NETLINK_ROUTE), error_out);
+
+ // allocate link cache
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache), error_out);
+
+ HASH_ITER (hh, if_hash, i, tmp) {
+ if_type = i->interface.type;
+ if_status = i->interface.enabled;
+ mtu = i->interface.ipv4.mtu < i->interface.ipv6.mtu ? i->interface.ipv4.mtu : i->interface.ipv6.mtu;
+
+ // create a new link
+ SRPC_SAFE_CALL_PTR(new_link, rtnl_link_alloc(), error_out);
+
+ //determine link type
+ if (strcmp(if_type, "iana-if-type:ethernetCsmacd") == 0) {
+ if_type_libnl = "veth";
+ // !! ADDS 2 INTERFACES (one without ip addresses)
+ } else if (strcmp(if_type, "iana-if-type:softwareLoopback") == 0) {
+ if_type_libnl = "vcan";
+ //!! vcan INSTEAD OF lo
+ } else if (strcmp(if_type, "iana-if-type:l2vlan") == 0) {
+ if_type_libnl = "vlan";
+ //!! doesn't work
+ } else if (strcmp(if_type, "iana-if-type:other") == 0) {
+ if_type_libnl = "dummy";
+ }
+
+ // set link type
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_set_type(new_link, if_type_libnl), error_out);
+
+ // setup link name
+ rtnl_link_set_name(new_link, i->interface.name);
+
+ // initialize desired link status
+ if (if_status) {
+ rtnl_link_set_flags(new_link, IFF_UP);
+ } else {
+ rtnl_link_unset_flags(new_link, IFF_UP);
+ }
+ rtnl_link_set_operstate(new_link, if_status ? IF_OPER_UP : IF_OPER_DOWN);
+
+ // set MTU
+ if (mtu) {
+ rtnl_link_set_mtu(new_link, mtu);
+ }
+
+ // add link
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_add(sk, new_link, NLM_F_CREATE), error_out);
+
+ // update link cache
+ SRPC_SAFE_CALL_ERR(error, nl_cache_refill(sk, link_cache), error_out);
+
+ // get updated new_link
+ SRPC_SAFE_CALL_PTR(new_link, rtnl_link_get_by_name(link_cache, i->interface.name), error_out);
+
+ LL_FOREACH (i->interface.ipv4.address, ipv4_iter) {
+ link_ipv4_addr = rtnl_addr_alloc();
+
+ //parse local ipv4 address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(ipv4_iter->address.ip, AF_INET, &local_addr), error_out);
+
+ // get ipv4 prefix length by using prefix-length or netmask
+ switch (ipv4_iter->address.subnet_type) {
+ case interfaces_interface_ipv4_address_subnet_none:
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Unable to get prefix-length/netmask for address %s", ipv4_iter->address.ip);
+ break;
+ case interfaces_interface_ipv4_address_subnet_prefix_length:
+ prefix_length = ipv4_iter->address.subnet.prefix_length;
+ break;
+ case interfaces_interface_ipv4_address_subnet_netmask:
+ interfaces_interface_ipv4_address_netmask2prefix(ipv4_iter->address.subnet.netmask, &prefix_length);
+ break;
+ default:
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Unable to get prefix-length/netmask for address %s", ipv4_iter->address.ip);
+ }
+
+ // set ipv4 prefix length
+ nl_addr_set_prefixlen(local_addr, prefix_length);
+
+ //set to route address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(link_ipv4_addr, local_addr), error_out);
+
+ // set interface
+ rtnl_addr_set_ifindex(link_ipv4_addr, rtnl_link_get_ifindex(new_link));
+
+ // add ipv4 address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_add(sk, link_ipv4_addr, 0), error_out);
+
+ nl_addr_put(local_addr);
+ rtnl_addr_put(link_ipv4_addr);
+ }
+
+
+ LL_FOREACH (i->interface.ipv6.address, ipv6_iter) {
+ link_ipv6_addr = rtnl_addr_alloc();
+
+ // parse local ipv6 address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(ipv6_iter->address.ip, AF_INET6, &local_addr), error_out);
+
+ // set ipv6 prefix length
+ nl_addr_set_prefixlen(local_addr, ipv6_iter->address.prefix_length);
+
+ //set to route address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_set_local(link_ipv6_addr, local_addr), error_out);
+
+ // set interface
+ rtnl_addr_set_ifindex(link_ipv6_addr, rtnl_link_get_ifindex(new_link));
+
+ //add ipv6 address
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_add(sk, link_ipv6_addr, 0), error_out);
+
+ nl_addr_put(local_addr);
+ rtnl_addr_put(link_ipv6_addr);
+ }
+
+ }
+
+ goto out;
+
+error_out:
+ if (error < 0) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "nl_geterror(): %d = %s", error, nl_geterror(error));
+ }
+ error = -1;
+
+out:
+ nl_socket_free(sk);
+ return error;
+}
diff --git a/src/interfaces/src/plugin/api/interfaces/store.h b/src/interfaces/src/plugin/api/interfaces/store.h
new file mode 100644
index 00000000..ced145b6
--- /dev/null
+++ b/src/interfaces/src/plugin/api/interfaces/store.h
@@ -0,0 +1,9 @@
+#ifndef INTERFACES_PLUGIN_API_INTERFACES_STORE_H
+#define INTERFACES_PLUGIN_API_INTERFACES_STORE_H
+
+#include "plugin/context.h"
+#include
+
+int interfaces_store_interface(interfaces_ctx_t* ctx, const interfaces_interface_hash_element_t* if_hash);
+
+#endif // INTERFACES_PLUGIN_API_INTERFACES_STORE_H
diff --git a/src/interfaces/src/plugin/common.h b/src/interfaces/src/plugin/common.h
new file mode 100644
index 00000000..cf58aa40
--- /dev/null
+++ b/src/interfaces/src/plugin/common.h
@@ -0,0 +1,161 @@
+#ifndef INTERFACES_PLUGIN_COMMON_H
+#define INTERFACES_PLUGIN_COMMON_H
+
+#define PLUGIN_NAME "ietf-interfaces-plugin"
+
+#define IETF_INTERFACES_YANG_MODULE "ietf-interfaces"
+#define IETF_IP_YANG_MODULE "ietf-ip"
+#define IETF_IF_EXTENSIONS_YANG_MODULE "ietf-if-extensions"
+
+#define INTERFACES_INTERFACES_CONTAINER_YANG_PATH "/" IETF_INTERFACES_YANG_MODULE ":interfaces"
+#define INTERFACES_INTERFACES_LIST_YANG_PATH INTERFACES_INTERFACES_CONTAINER_YANG_PATH "/interface"
+
+#define INTERFACES_INTERFACES_STATE_INTERFACE_NAME_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH "/name"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_TYPE_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH "/type"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_ADMIN_STATUS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH "/admin-status"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_OPER_STATUS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH "/oper-status"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_LAST_CHANGE_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH "/last-change"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IF_INDEX_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH "/if-index"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_PHYS_ADDRESS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH "/phys-address"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_HIGHER_LAYER_IF_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH "/higher-layer-if"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_LOWER_LAYER_IF_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH "/lower-layer-if"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_SPEED_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH "/speed"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_DISCONTINUITY_TIME_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/discontinuity-time"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_IN_OCTETS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/in-octets"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_IN_UNICAST_PKTS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/in-unicast-pkts"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_IN_BROADCAST_PKTS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/in-broadcast-pkts"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_IN_MULTICAST_PKTS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/in-multicast-pkts"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_IN_DISCARDS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/in-discards"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_IN_ERRORS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/in-errors"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_IN_UNKNOWN_PROTOS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/in-unknown-protos"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_OUT_OCTETS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/out-octets"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_OUT_UNICAST_PKTS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/out-unicast-pkts"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_OUT_BROADCAST_PKTS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/out-broadcast-pkts"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_OUT_MULTICAST_PKTS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/out-multicast-pkts"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_OUT_DISCARDS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/out-discards"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_OUT_ERRORS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH "/out-errors"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_STATISTICS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH "/statistics"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_FORWARDING_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_YANG_PATH "/forwarding"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_MTU_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_YANG_PATH "/mtu"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_ADDRESS_IP_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_ADDRESS_YANG_PATH "/ip"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_ADDRESS_PREFIX_LENGTH_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_ADDRESS_YANG_PATH "/prefix-length"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_ADDRESS_NETMASK_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_ADDRESS_YANG_PATH "/netmask"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_ADDRESS_ORIGIN_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_ADDRESS_YANG_PATH "/origin"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_ADDRESS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_YANG_PATH "/address"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_NEIGHBOR_IP_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_NEIGHBOR_YANG_PATH "/ip"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_NEIGHBOR_LINK_LAYER_ADDRESS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_NEIGHBOR_YANG_PATH "/link-layer-address"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_NEIGHBOR_ORIGIN_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_NEIGHBOR_YANG_PATH "/origin"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_NEIGHBOR_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_YANG_PATH "/neighbor"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV4_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH "/ipv4"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_FORWARDING_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_YANG_PATH "/forwarding"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_MTU_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_YANG_PATH "/mtu"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_ADDRESS_IP_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_ADDRESS_YANG_PATH "/ip"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_ADDRESS_PREFIX_LENGTH_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_ADDRESS_YANG_PATH "/prefix-length"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_ADDRESS_ORIGIN_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_ADDRESS_YANG_PATH "/origin"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_ADDRESS_STATUS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_ADDRESS_YANG_PATH "/status"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_ADDRESS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_YANG_PATH "/address"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_NEIGHBOR_IP_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_NEIGHBOR_YANG_PATH "/ip"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_NEIGHBOR_LINK_LAYER_ADDRESS_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_NEIGHBOR_YANG_PATH "/link-layer-address"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_NEIGHBOR_ORIGIN_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_NEIGHBOR_YANG_PATH "/origin"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_NEIGHBOR_IS_ROUTER_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_NEIGHBOR_YANG_PATH "/is-router"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_NEIGHBOR_STATE_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_NEIGHBOR_YANG_PATH "/state"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_NEIGHBOR_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_YANG_PATH "/neighbor"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_IPV6_YANG_PATH INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH "/ipv6"
+#define INTERFACES_INTERFACES_STATE_INTERFACE_YANG_PATH INTERFACES_INTERFACES_STATE_YANG_PATH "/interface"
+#define INTERFACES_INTERFACES_STATE_YANG_PATH "/ietf-interfaces:interfaces-state"
+#define INTERFACES_INTERFACES_INTERFACE_NAME_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/name"
+#define INTERFACES_INTERFACES_INTERFACE_DESCRIPTION_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/description"
+#define INTERFACES_INTERFACES_INTERFACE_TYPE_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/type"
+#define INTERFACES_INTERFACES_INTERFACE_ENABLED_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/enabled"
+#define INTERFACES_INTERFACES_INTERFACE_LINK_UP_DOWN_TRAP_ENABLE_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/link-up-down-trap-enable"
+#define INTERFACES_INTERFACES_INTERFACE_ADMIN_STATUS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/admin-status"
+#define INTERFACES_INTERFACES_INTERFACE_OPER_STATUS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/oper-status"
+#define INTERFACES_INTERFACES_INTERFACE_LAST_CHANGE_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/last-change"
+#define INTERFACES_INTERFACES_INTERFACE_IF_INDEX_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/if-index"
+#define INTERFACES_INTERFACES_INTERFACE_PHYS_ADDRESS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/phys-address"
+#define INTERFACES_INTERFACES_INTERFACE_HIGHER_LAYER_IF_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/higher-layer-if"
+#define INTERFACES_INTERFACES_INTERFACE_LOWER_LAYER_IF_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/lower-layer-if"
+#define INTERFACES_INTERFACES_INTERFACE_SPEED_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/speed"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_DISCONTINUITY_TIME_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/discontinuity-time"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_OCTETS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/in-octets"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_UNICAST_PKTS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/in-unicast-pkts"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_BROADCAST_PKTS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/in-broadcast-pkts"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_MULTICAST_PKTS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/in-multicast-pkts"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_DISCARDS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/in-discards"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_ERRORS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/in-errors"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_UNKNOWN_PROTOS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/in-unknown-protos"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_OUT_OCTETS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/out-octets"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_OUT_UNICAST_PKTS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/out-unicast-pkts"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_OUT_BROADCAST_PKTS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/out-broadcast-pkts"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_OUT_MULTICAST_PKTS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/out-multicast-pkts"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_OUT_DISCARDS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/out-discards"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_OUT_ERRORS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/out-errors"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_IN_DISCARD_UNKNOWN_ENCAPS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH "/in-discard-unknown-encaps"
+#define INTERFACES_INTERFACES_INTERFACE_STATISTICS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/statistics"
+#define INTERFACES_INTERFACES_INTERFACE_CARRIER_DELAY_DOWN_YANG_PATH INTERFACES_INTERFACES_INTERFACE_CARRIER_DELAY_YANG_PATH "/down"
+#define INTERFACES_INTERFACES_INTERFACE_CARRIER_DELAY_UP_YANG_PATH INTERFACES_INTERFACES_INTERFACE_CARRIER_DELAY_YANG_PATH "/up"
+#define INTERFACES_INTERFACES_INTERFACE_CARRIER_DELAY_CARRIER_TRANSITIONS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_CARRIER_DELAY_YANG_PATH "/carrier-transitions"
+#define INTERFACES_INTERFACES_INTERFACE_CARRIER_DELAY_TIMER_RUNNING_YANG_PATH INTERFACES_INTERFACES_INTERFACE_CARRIER_DELAY_YANG_PATH "/timer-running"
+#define INTERFACES_INTERFACES_INTERFACE_CARRIER_DELAY_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/carrier-delay"
+#define INTERFACES_INTERFACES_INTERFACE_DAMPENING_HALF_LIFE_YANG_PATH INTERFACES_INTERFACES_INTERFACE_DAMPENING_YANG_PATH "/half-life"
+#define INTERFACES_INTERFACES_INTERFACE_DAMPENING_REUSE_YANG_PATH INTERFACES_INTERFACES_INTERFACE_DAMPENING_YANG_PATH "/reuse"
+#define INTERFACES_INTERFACES_INTERFACE_DAMPENING_SUPPRESS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_DAMPENING_YANG_PATH "/suppress"
+#define INTERFACES_INTERFACES_INTERFACE_DAMPENING_MAX_SUPPRESS_TIME_YANG_PATH INTERFACES_INTERFACES_INTERFACE_DAMPENING_YANG_PATH "/max-suppress-time"
+#define INTERFACES_INTERFACES_INTERFACE_DAMPENING_PENALTY_YANG_PATH INTERFACES_INTERFACES_INTERFACE_DAMPENING_YANG_PATH "/penalty"
+#define INTERFACES_INTERFACES_INTERFACE_DAMPENING_SUPPRESSED_YANG_PATH INTERFACES_INTERFACES_INTERFACE_DAMPENING_YANG_PATH "/suppressed"
+#define INTERFACES_INTERFACES_INTERFACE_DAMPENING_TIME_REMAINING_YANG_PATH INTERFACES_INTERFACES_INTERFACE_DAMPENING_YANG_PATH "/time-remaining"
+#define INTERFACES_INTERFACES_INTERFACE_DAMPENING_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/dampening"
+#define INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_OUTER_TAG_TAG_TYPE_YANG_PATH INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_OUTER_TAG_YANG_PATH "/tag-type"
+#define INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_OUTER_TAG_VLAN_ID_YANG_PATH INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_OUTER_TAG_YANG_PATH "/vlan-id"
+#define INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_OUTER_TAG_YANG_PATH INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_YANG_PATH "/outer-tag"
+#define INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_SECOND_TAG_TAG_TYPE_YANG_PATH INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_SECOND_TAG_YANG_PATH "/tag-type"
+#define INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_SECOND_TAG_VLAN_ID_YANG_PATH INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_SECOND_TAG_YANG_PATH "/vlan-id"
+#define INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_SECOND_TAG_YANG_PATH INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_YANG_PATH "/second-tag"
+#define INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_DOT1Q_VLAN_YANG_PATH INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_YANG_PATH "/dot1q-vlan"
+#define INTERFACES_INTERFACES_INTERFACE_ENCAPSULATION_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/encapsulation"
+#define INTERFACES_INTERFACES_INTERFACE_LOOPBACK_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/loopback"
+#define INTERFACES_INTERFACES_INTERFACE_MAX_FRAME_SIZE_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/max-frame-size"
+#define INTERFACES_INTERFACES_INTERFACE_FORWARDING_MODE_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/forwarding-mode"
+#define INTERFACES_INTERFACES_INTERFACE_PARENT_INTERFACE_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/parent-interface"
+#define INTERFACES_INTERFACES_INTERFACE_IPV4_ENABLED_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV4_YANG_PATH "/enabled"
+#define INTERFACES_INTERFACES_INTERFACE_IPV4_FORWARDING_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV4_YANG_PATH "/forwarding"
+#define INTERFACES_INTERFACES_INTERFACE_IPV4_MTU_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV4_YANG_PATH "/mtu"
+#define INTERFACES_INTERFACES_INTERFACE_IPV4_ADDRESS_IP_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV4_ADDRESS_YANG_PATH "/ip"
+#define INTERFACES_INTERFACES_INTERFACE_IPV4_ADDRESS_PREFIX_LENGTH_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV4_ADDRESS_YANG_PATH "/prefix-length"
+#define INTERFACES_INTERFACES_INTERFACE_IPV4_ADDRESS_NETMASK_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV4_ADDRESS_YANG_PATH "/netmask"
+#define INTERFACES_INTERFACES_INTERFACE_IPV4_ADDRESS_ORIGIN_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV4_ADDRESS_YANG_PATH "/origin"
+#define INTERFACES_INTERFACES_INTERFACE_IPV4_ADDRESS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV4_YANG_PATH "/address"
+#define INTERFACES_INTERFACES_INTERFACE_IPV4_NEIGHBOR_IP_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV4_NEIGHBOR_YANG_PATH "/ip"
+#define INTERFACES_INTERFACES_INTERFACE_IPV4_NEIGHBOR_LINK_LAYER_ADDRESS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV4_NEIGHBOR_YANG_PATH "/link-layer-address"
+#define INTERFACES_INTERFACES_INTERFACE_IPV4_NEIGHBOR_ORIGIN_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV4_NEIGHBOR_YANG_PATH "/origin"
+#define INTERFACES_INTERFACES_INTERFACE_IPV4_NEIGHBOR_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV4_YANG_PATH "/neighbor"
+#define INTERFACES_INTERFACES_INTERFACE_IPV4_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/ietf-ip:ipv4"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_ENABLED_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_YANG_PATH "/enabled"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_FORWARDING_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_YANG_PATH "/forwarding"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_MTU_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_YANG_PATH "/mtu"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_ADDRESS_IP_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_ADDRESS_YANG_PATH "/ip"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_ADDRESS_PREFIX_LENGTH_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_ADDRESS_YANG_PATH "/prefix-length"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_ADDRESS_ORIGIN_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_ADDRESS_YANG_PATH "/origin"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_ADDRESS_STATUS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_ADDRESS_YANG_PATH "/status"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_ADDRESS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_YANG_PATH "/address"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_IP_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_YANG_PATH "/ip"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_LINK_LAYER_ADDRESS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_YANG_PATH "/link-layer-address"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_ORIGIN_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_YANG_PATH "/origin"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_IS_ROUTER_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_YANG_PATH "/is-router"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_STATE_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_YANG_PATH "/state"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_NEIGHBOR_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_YANG_PATH "/neighbor"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_DUP_ADDR_DETECT_TRANSMITS_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_YANG_PATH "/dup-addr-detect-transmits"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_AUTOCONF_CREATE_GLOBAL_ADDRESSES_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_AUTOCONF_YANG_PATH "/create-global-addresses"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_AUTOCONF_CREATE_TEMPORARY_ADDRESSES_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_AUTOCONF_YANG_PATH "/create-temporary-addresses"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_AUTOCONF_TEMPORARY_VALID_LIFETIME_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_AUTOCONF_YANG_PATH "/temporary-valid-lifetime"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_AUTOCONF_TEMPORARY_PREFERRED_LIFETIME_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_AUTOCONF_YANG_PATH "/temporary-preferred-lifetime"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_AUTOCONF_YANG_PATH INTERFACES_INTERFACES_INTERFACE_IPV6_YANG_PATH "/autoconf"
+#define INTERFACES_INTERFACES_INTERFACE_IPV6_YANG_PATH INTERFACES_INTERFACES_INTERFACE_YANG_PATH "/ipv6"
+#define INTERFACES_INTERFACES_INTERFACE_YANG_PATH INTERFACES_INTERFACES_YANG_PATH "/interface"
+#define INTERFACES_INTERFACES_YANG_PATH "/ietf-interfaces:interfaces"
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
+
+#define ADDR_STR_BUF_SIZE 45 // max ip string length (15 for ipv4 and 45 for ipv6)
+
+#endif // INTERFACES_PLUGIN_COMMON_H
diff --git a/src/interfaces/src/plugin/context.h b/src/interfaces/src/plugin/context.h
new file mode 100644
index 00000000..f9fe3e41
--- /dev/null
+++ b/src/interfaces/src/plugin/context.h
@@ -0,0 +1,109 @@
+#ifndef INTERFACES_PLUGIN_CONTEXT_H
+#define INTERFACES_PLUGIN_CONTEXT_H
+
+#include "netlink/cache.h"
+#include "plugin/types.h"
+#include
+#include
+
+#include
+
+#include
+
+// typedefs
+typedef struct interfaces_nl_ctx_s interfaces_nl_ctx_t;
+typedef struct interfaces_ctx_s interfaces_ctx_t;
+typedef struct interfaces_state_changes_ctx_s interfaces_state_changes_ctx_t;
+typedef struct interfaces_mod_changes_ctx_s interfaces_mod_changes_ctx_t;
+typedef struct interfaces_oper_ctx_s interfaces_oper_ctx_t;
+typedef struct interfaces_startup_ctx_s interfaces_startup_ctx_t;
+typedef struct interfaces_features_ctx_s interfaces_features_ctx_t;
+
+struct interfaces_features_ctx_s {
+ srpc_feature_status_hash_t* ietf_interfaces_features;
+ srpc_feature_status_hash_t* ietf_if_extensions_features;
+ srpc_feature_status_hash_t* ietf_ip_features;
+};
+
+struct interfaces_nl_ctx_s {
+ struct nl_sock* socket;
+ struct nl_cache* link_cache;
+ struct nl_cache* addr_cache;
+ struct nl_cache* neigh_cache;
+ struct nl_cache_mngr* link_cache_manager;
+};
+
+struct interfaces_mod_changes_ctx_s {
+ // libnl links data
+ interfaces_nl_ctx_t nl_ctx;
+
+ // temporary module changing data
+ struct {
+ struct {
+ struct {
+ uint8_t prefix_length;
+ uint8_t prefix_set; ///< prefix_length has been set
+ } address;
+ struct {
+ char* link_layer_address;
+ uint8_t link_layer_set; ///< link_layer_address has been set
+ } neighbor;
+ } ipv4;
+ struct {
+ struct {
+ uint8_t prefix_length;
+ uint8_t prefix_set; ///< prefix_length has been set
+ } address;
+ struct {
+ char* link_layer_address;
+ uint8_t link_layer_set; ///< link_layer_address has been set
+ } neighbor;
+ } ipv6;
+ } mod_data;
+};
+
+struct interfaces_state_changes_ctx_s {
+ // libnl data
+ interfaces_nl_ctx_t nl_ctx;
+
+ // cache manager refresh thread
+ pthread_t manager_thread;
+
+ // main hash DS for storing state info
+ interfaces_interface_state_hash_element_t* state_hash;
+
+ // mutex for accessing state hash data
+ pthread_mutex_t state_hash_mutex;
+};
+
+struct interfaces_oper_ctx_s {
+ // operational libnl context - refill cache of links
+ interfaces_nl_ctx_t nl_ctx;
+
+ // state changes monitoring
+ interfaces_state_changes_ctx_t state_changes_ctx;
+};
+
+struct interfaces_startup_ctx_s {
+ // startup DS
+ sr_session_ctx_t* startup_session;
+
+ // libnl context
+ interfaces_nl_ctx_t nl_ctx;
+};
+
+struct interfaces_ctx_s {
+ // startup data
+ interfaces_startup_ctx_t startup_ctx;
+
+ // module changes data
+ interfaces_mod_changes_ctx_t mod_ctx;
+
+ // operational data
+ interfaces_oper_ctx_t oper_ctx;
+
+ // features enabled on plugin load
+ interfaces_features_ctx_t features;
+};
+
+#endif // INTERFACES_PLUGIN_CONTEXT_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/data/interfaces/interface.c b/src/interfaces/src/plugin/data/interfaces/interface.c
new file mode 100644
index 00000000..efb46763
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface.c
@@ -0,0 +1,656 @@
+#include "interface.h"
+#include "interface/ipv4.h"
+#include "libyang/log.h"
+#include "libyang/tree_data.h"
+#include "plugin/common.h"
+#include "plugin/ly_tree.h"
+#include "plugin/types.h"
+#include "srpc/ly_tree.h"
+#include "sysrepo.h"
+#include "uthash.h"
+#include "utils/memory.h"
+#include "utlist.h"
+
+// other data API
+#include "interface/ipv4/address.h"
+#include "interface/ipv4/neighbor.h"
+#include "interface/ipv6/address.h"
+#include "interface/ipv6/neighbor.h"
+#include "interface/linked_list.h"
+
+#include
+#include
+#include
+#include
+
+/*
+ Libyang conversion functions.
+*/
+
+interfaces_interface_hash_element_t* interfaces_interface_hash_new(void)
+{
+ return NULL;
+}
+
+void interfaces_interface_hash_element_free(interfaces_interface_hash_element_t** el)
+{
+ if (*el) {
+ // name
+ if ((*el)->interface.name) {
+ free((*el)->interface.name);
+ }
+
+ // description
+ if ((*el)->interface.description) {
+ free((*el)->interface.description);
+ }
+
+ // type
+ if ((*el)->interface.type) {
+ free((*el)->interface.type);
+ }
+
+ // loopback
+ if ((*el)->interface.loopback) {
+ free((*el)->interface.loopback);
+ }
+
+ // parent-interface
+ if ((*el)->interface.parent_interface) {
+ free((*el)->interface.parent_interface);
+ }
+
+ // lists
+
+ if ((*el)->interface.ipv4.address) {
+ INTERFACES_INTERFACE_LIST_FREE((*el)->interface.ipv4.address);
+ }
+
+ if ((*el)->interface.ipv6.address) {
+ INTERFACES_INTERFACE_LIST_FREE((*el)->interface.ipv6.address);
+ }
+
+ if ((*el)->interface.ipv4.address) {
+ INTERFACES_INTERFACE_LIST_FREE((*el)->interface.ipv4.neighbor);
+ }
+
+ if ((*el)->interface.ipv6.address) {
+ INTERFACES_INTERFACE_LIST_FREE((*el)->interface.ipv6.neighbor);
+ }
+
+ // element data
+ free(*el);
+ *el = NULL;
+ }
+}
+
+void interfaces_interface_hash_print_debug(const interfaces_interface_hash_element_t* if_hash)
+{
+ const interfaces_interface_hash_element_t *iter = NULL, *tmp = NULL;
+ interfaces_interface_ipv4_address_element_t* v4addr_iter = NULL;
+ interfaces_interface_ipv6_address_element_t* v6addr_iter = NULL;
+ interfaces_interface_ipv4_neighbor_element_t* v4neigh_iter = NULL;
+ interfaces_interface_ipv6_neighbor_element_t* v6neigh_iter = NULL;
+
+ HASH_ITER(hh, if_hash, iter, tmp)
+ {
+ SRPLG_LOG_INF(PLUGIN_NAME, "Interface %s:", iter->interface.name);
+ SRPLG_LOG_INF(PLUGIN_NAME, "\t Name = %s", iter->interface.name);
+ SRPLG_LOG_INF(PLUGIN_NAME, "\t Type = %s", iter->interface.type);
+ SRPLG_LOG_INF(PLUGIN_NAME, "\t Enabled = %d", iter->interface.enabled);
+ SRPLG_LOG_INF(PLUGIN_NAME, "\t IPv4:");
+ SRPLG_LOG_INF(PLUGIN_NAME, "\t IPv4:Enabled = %d", iter->interface.ipv4.enabled);
+ SRPLG_LOG_INF(PLUGIN_NAME, "\t IPv4:Forwarding = %d", iter->interface.ipv4.forwarding);
+ SRPLG_LOG_INF(PLUGIN_NAME, "\t IPv4:MTU = %hu", iter->interface.ipv4.mtu);
+
+ LL_FOREACH(iter->interface.ipv4.address, v4addr_iter)
+ {
+ SRPLG_LOG_INF(PLUGIN_NAME, "\t IPv4:Address = %s/%d", v4addr_iter->address.ip, v4addr_iter->address.subnet.prefix_length);
+ }
+
+ LL_FOREACH(iter->interface.ipv4.neighbor, v4neigh_iter)
+ {
+ SRPLG_LOG_INF(PLUGIN_NAME, "\t IPv4:Neighbor = %s : %s", v4neigh_iter->neighbor.ip, v4neigh_iter->neighbor.link_layer_address);
+ }
+
+ LL_FOREACH(iter->interface.ipv6.address, v6addr_iter)
+ {
+ SRPLG_LOG_INF(PLUGIN_NAME, "\t IPv6:Address = %s/%d", v6addr_iter->address.ip, v6addr_iter->address.prefix_length);
+ }
+
+ LL_FOREACH(iter->interface.ipv6.neighbor, v6neigh_iter)
+ {
+ SRPLG_LOG_INF(PLUGIN_NAME, "\t IPv6:Neighbor = %s : %s", v6neigh_iter->neighbor.ip, v6neigh_iter->neighbor.link_layer_address);
+ }
+ }
+}
+
+int interfaces_interface_hash_from_ly(interfaces_interface_hash_element_t** if_hash, const struct lyd_node* interface_list_node)
+{
+ int error = 0;
+
+ // make sure the hash is empty at the start
+ assert(*if_hash == NULL);
+
+ // libyang
+ struct lyd_node* if_iter = (struct lyd_node*)interface_list_node;
+ struct lyd_node *if_name_node = NULL, *if_type_node = NULL, *if_enabled_node = NULL;
+ struct lyd_node *ipv4_container_node = NULL, *ipv6_container_node = NULL;
+ struct lyd_node *ipv4_enabled_node = NULL, *ipv4_forwarding_node = NULL, *ipv4_mtu_node = NULL, *ipv4_address_node = NULL;
+ struct lyd_node *ipv6_enabled_node = NULL, *ipv6_forwarding_node = NULL, *ipv6_mtu_node = NULL, *ipv6_address_node = NULL;
+ struct lyd_node *ipv4_ip_node = NULL, *ipv4_prefix_node = NULL, *ipv4_netmask_node = NULL;
+ struct lyd_node *ipv6_ip_node = NULL, *ipv6_prefix_node = NULL;
+
+ // internal DS
+ interfaces_interface_hash_element_t* new_element = NULL;
+ interfaces_interface_ipv4_address_element_t* new_v4_element = NULL;
+ interfaces_interface_ipv6_address_element_t* new_v6_element = NULL;
+
+ while (if_iter) {
+ // create new element
+ new_element = interfaces_interface_hash_element_new();
+
+ // get existing nodes
+ SRPC_SAFE_CALL_PTR(if_name_node, srpc_ly_tree_get_child_leaf(if_iter, "name"), error_out);
+ SRPC_SAFE_CALL_PTR(if_type_node, srpc_ly_tree_get_child_leaf(if_iter, "type"), error_out);
+ if_enabled_node = srpc_ly_tree_get_child_leaf(if_iter, "enabled");
+ ipv4_container_node = srpc_ly_tree_get_child_container(if_iter, "ipv4");
+ ipv6_container_node = srpc_ly_tree_get_child_container(if_iter, "ipv6");
+
+ // ipv4
+ if (ipv4_container_node) {
+ ipv4_enabled_node = srpc_ly_tree_get_child_leaf(ipv4_container_node, "enabled");
+ ipv4_forwarding_node = srpc_ly_tree_get_child_leaf(ipv4_container_node, "forwarding");
+ ipv4_mtu_node = srpc_ly_tree_get_child_leaf(ipv4_container_node, "mtu");
+ ipv4_address_node = srpc_ly_tree_get_child_list(ipv4_container_node, "address");
+ }
+
+ // ipv6
+ if (ipv6_container_node) {
+ ipv6_enabled_node = srpc_ly_tree_get_child_leaf(ipv6_container_node, "enabled");
+ ipv6_forwarding_node = srpc_ly_tree_get_child_leaf(ipv6_container_node, "forwarding");
+ ipv6_mtu_node = srpc_ly_tree_get_child_leaf(ipv6_container_node, "mtu");
+ ipv6_address_node = srpc_ly_tree_get_child_list(ipv6_container_node, "address");
+ }
+
+ // extract and set data
+ if (if_name_node) {
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_element_set_name(&new_element, lyd_get_value(if_name_node)), error_out);
+ }
+
+ if (if_type_node) {
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_element_set_type(&new_element, lyd_get_value(if_type_node)), error_out);
+ }
+
+ if (if_enabled_node) {
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_element_set_enabled(&new_element, strcmp(lyd_get_value(if_enabled_node), "true") == 0 ? 1 : 0), error_out);
+ }
+
+ if (ipv4_enabled_node) {
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_element_ipv4_set_enabled(&new_element->interface.ipv4, strcmp(lyd_get_value(ipv4_enabled_node), "true") == 0 ? 1 : 0), error_out);
+ }
+
+ if (ipv4_forwarding_node) {
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_element_ipv4_set_forwarding(&new_element->interface.ipv4, strcmp(lyd_get_value(ipv4_forwarding_node), "true") == 0 ? 1 : 0), error_out);
+ }
+
+ if (ipv4_mtu_node) {
+ const char* mtu_str = lyd_get_value(ipv4_mtu_node);
+ uint16_t mtu = (uint16_t)atoi(mtu_str);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_element_ipv4_set_mtu(&new_element->interface.ipv4, mtu), error_out);
+ }
+
+ // init every list
+
+ // ipv4
+ INTERFACES_INTERFACE_LIST_NEW(new_element->interface.ipv4.address);
+ INTERFACES_INTERFACE_LIST_NEW(new_element->interface.ipv4.neighbor);
+
+ // ipv6
+ INTERFACES_INTERFACE_LIST_NEW(new_element->interface.ipv6.address);
+ INTERFACES_INTERFACE_LIST_NEW(new_element->interface.ipv6.neighbor);
+
+ // IPv4 address list
+ while (ipv4_address_node) {
+ // add new ipv4 address element
+ new_v4_element = interfaces_interface_ipv4_address_element_new();
+
+ // fetch address info nodes
+ SRPC_SAFE_CALL_PTR(ipv4_ip_node, srpc_ly_tree_get_child_leaf(ipv4_address_node, "ip"), error_out);
+ ipv4_prefix_node = srpc_ly_tree_get_child_leaf(ipv4_address_node, "prefix-length");
+ ipv4_netmask_node = srpc_ly_tree_get_child_leaf(ipv4_address_node, "netmask");
+
+ // set IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_address_element_set_ip(&new_v4_element, lyd_get_value(ipv4_ip_node)), error_out);
+
+ // set subnet data
+ if (ipv4_prefix_node) {
+ const char* prefix_length_str = NULL;
+
+ SRPC_SAFE_CALL_PTR(prefix_length_str, lyd_get_value(ipv4_prefix_node), error_out);
+ const uint8_t prefix_length = (uint8_t)atoi(prefix_length_str);
+
+ // set prefix length
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_address_element_set_prefix_length(&new_v4_element, prefix_length), error_out);
+ } else if (ipv4_netmask_node) {
+ const char* netmask_str = NULL;
+
+ SRPC_SAFE_CALL_PTR(netmask_str, lyd_get_value(ipv4_netmask_node), error_out);
+
+ // set netmask
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv4_address_element_set_netmask(&new_v4_element, netmask_str), error_out);
+ } else {
+ // should be impossible due to libyang's mandatory statement... but throw error
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Unable to determine subnet of the following address: %s", lyd_get_value(ipv4_ip_node));
+ goto error_out;
+ }
+
+ // data set correctly - add address to the list
+ INTERFACES_INTERFACE_LIST_ADD_ELEMENT(new_element->interface.ipv4.address, new_v4_element);
+
+ // null new element - free()
+ new_v4_element = NULL;
+
+ // iterate
+ ipv4_address_node = srpc_ly_tree_get_list_next(ipv4_address_node);
+ }
+
+ // IPv6 address list
+ while (ipv6_address_node) {
+ // add new ipv4 address element
+ new_v6_element = interfaces_interface_ipv6_address_element_new();
+
+ // fetch address info nodes
+ SRPC_SAFE_CALL_PTR(ipv6_ip_node, srpc_ly_tree_get_child_leaf(ipv6_address_node, "ip"), error_out);
+ SRPC_SAFE_CALL_PTR(ipv6_prefix_node, srpc_ly_tree_get_child_leaf(ipv6_address_node, "prefix-length"), error_out);
+
+ // set IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_address_element_set_ip(&new_v6_element, lyd_get_value(ipv6_ip_node)), error_out);
+
+ // set prefix-length
+ const char* prefix_length_str = NULL;
+
+ SRPC_SAFE_CALL_PTR(prefix_length_str, lyd_get_value(ipv6_prefix_node), error_out);
+
+ const uint8_t prefix_length = (uint8_t)atoi(prefix_length_str);
+
+ // set prefix length
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_ipv6_address_element_set_prefix_length(&new_v6_element, prefix_length), error_out);
+
+ // data set correctly - add address to the list
+ INTERFACES_INTERFACE_LIST_ADD_ELEMENT(new_element->interface.ipv6.address, new_v6_element);
+
+ // null new element - free()
+ new_v6_element = NULL;
+
+ // iterate
+ ipv6_address_node = srpc_ly_tree_get_list_next(ipv6_address_node);
+ }
+
+ // add element to the hash
+ interfaces_interface_hash_add_element(if_hash, new_element);
+
+ // set to NULL - free()
+ new_element = NULL;
+
+ // iterate next
+ if_iter = srpc_ly_tree_get_list_next(if_iter);
+ }
+
+ goto out;
+error_out:
+ error = -1;
+
+out:
+ if (new_element) {
+ interfaces_interface_hash_element_free(&new_element);
+ }
+
+ if (new_v4_element) {
+ interfaces_interface_ipv4_address_element_free(&new_v4_element);
+ }
+
+ if (new_v6_element) {
+ interfaces_interface_ipv6_address_element_free(&new_v6_element);
+ }
+
+ return error;
+}
+
+int interfaces_interface_hash_to_ly(const struct ly_ctx* ly_ctx, interfaces_interface_hash_element_t* if_hash, struct lyd_node** interfaces_container_node)
+{
+ int error = 0;
+
+ // interface iterator
+ interfaces_interface_hash_element_t *if_iter = NULL, *tmp = NULL;
+
+ // address iterators
+ interfaces_interface_ipv4_address_element_t* v4_addr_iter = NULL;
+ interfaces_interface_ipv6_address_element_t* v6_addr_iter = NULL;
+
+ // neighbor iterators
+ interfaces_interface_ipv4_neighbor_element_t* v4_neigh_iter = NULL;
+ interfaces_interface_ipv6_neighbor_element_t* v6_neigh_iter = NULL;
+
+ // libyang data
+ struct lyd_node* interface_list_node = NULL;
+ struct lyd_node *ipv4_container_node = NULL, *ipv6_container_node = NULL;
+ struct lyd_node *ipv4_address_node = NULL, *ipv6_address_node = NULL;
+ struct lyd_node *ipv4_neighbor_node = NULL, *ipv6_neighbor_node = NULL;
+
+ // buffers
+ char mtu_buffer[100] = { 0 };
+ char prefix_length_buffer[100] = { 0 };
+
+ HASH_ITER(hh, if_hash, if_iter, tmp)
+ {
+ // alloc new interface list node and fill that node with info
+ // name added via list creation
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface(ly_ctx, *interfaces_container_node, &interface_list_node, if_iter->interface.name), error_out);
+
+ // enabled
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_enabled(ly_ctx, interface_list_node, if_iter->interface.enabled ? "true" : "false"), error_out);
+
+ // type
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_type(ly_ctx, interface_list_node, if_iter->interface.type), error_out);
+
+ // IPv4
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4(ly_ctx, interface_list_node, &ipv4_container_node), error_out);
+
+ // IPv4 properties
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_enabled(ly_ctx, ipv4_container_node, if_iter->interface.ipv4.enabled ? "true" : "false"), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_forwarding(ly_ctx, ipv4_container_node, if_iter->interface.ipv4.forwarding ? "true" : "false"), error_out);
+
+ // write IPv4 MTU to the buffer
+ if (if_iter->interface.ipv4.mtu != 0) {
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(mtu_buffer, sizeof(mtu_buffer), "%u", if_iter->interface.ipv4.mtu), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_mtu(ly_ctx, ipv4_container_node, mtu_buffer), error_out);
+ }
+
+ // IPv4 address list
+ LL_FOREACH(if_iter->interface.ipv4.address, v4_addr_iter)
+ {
+ // create list element
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_address(ly_ctx, ipv4_container_node, &ipv4_address_node, v4_addr_iter->address.ip), error_out);
+
+ // add properties to the list element
+ switch (v4_addr_iter->address.subnet_type) {
+ case interfaces_interface_ipv4_address_subnet_none:
+ break;
+ case interfaces_interface_ipv4_address_subnet_prefix_length:
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(prefix_length_buffer, sizeof(prefix_length_buffer), "%d", v4_addr_iter->address.subnet.prefix_length), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_address_prefix_length(ly_ctx, ipv4_address_node, prefix_length_buffer), error_out);
+ break;
+ case interfaces_interface_ipv4_address_subnet_netmask:
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_address_netmask(ly_ctx, ipv4_address_node, v4_addr_iter->address.subnet.netmask), error_out);
+ break;
+ }
+ }
+
+ // IPv4 neighbor list
+ LL_FOREACH(if_iter->interface.ipv4.neighbor, v4_neigh_iter)
+ {
+ // create list element
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_neighbor(ly_ctx, ipv4_container_node, &ipv4_neighbor_node, v4_neigh_iter->neighbor.ip), error_out);
+
+ // add properties to the list element
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_neighbor_link_layer_address(ly_ctx, ipv4_neighbor_node, v4_neigh_iter->neighbor.link_layer_address), error_out);
+ }
+
+ // IPv6
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6(ly_ctx, interface_list_node, &ipv6_container_node), error_out);
+
+ // IPv6 properties
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_enabled(ly_ctx, ipv6_container_node, if_iter->interface.ipv6.enabled ? "true" : "false"), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_forwarding(ly_ctx, ipv6_container_node, if_iter->interface.ipv6.forwarding ? "true" : "false"), error_out);
+
+ // write IPv6 MTU to the buffer
+ if (if_iter->interface.ipv6.mtu != 0) {
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(mtu_buffer, sizeof(mtu_buffer), "%u", if_iter->interface.ipv6.mtu), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_mtu(ly_ctx, ipv6_container_node, mtu_buffer), error_out);
+ }
+
+ // IPv6 address list
+ LL_FOREACH(if_iter->interface.ipv6.address, v6_addr_iter)
+ {
+ // create list element
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_address(ly_ctx, ipv6_container_node, &ipv6_address_node, v6_addr_iter->address.ip), error_out);
+
+ // element properties
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(prefix_length_buffer, sizeof(prefix_length_buffer), "%d", v6_addr_iter->address.prefix_length), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_address_prefix_length(ly_ctx, ipv6_address_node, prefix_length_buffer), error_out);
+ }
+
+ // IPv4 neighbor list
+ LL_FOREACH(if_iter->interface.ipv6.neighbor, v6_neigh_iter)
+ {
+ // create list element
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor(ly_ctx, ipv6_container_node, &ipv6_neighbor_node, v6_neigh_iter->neighbor.ip), error_out);
+
+ // add properties to the list element
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_link_layer_address(ly_ctx, ipv6_neighbor_node, v6_neigh_iter->neighbor.link_layer_address), error_out);
+ }
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ return error;
+}
+
+int interfaces_interface_hash_add_element(interfaces_interface_hash_element_t** hash, interfaces_interface_hash_element_t* new_element)
+{
+ interfaces_interface_hash_element_t* found_element = NULL;
+
+ HASH_FIND_STR(*hash, new_element->interface.name, found_element);
+
+ // element already exists
+ if (found_element != NULL) {
+ return -1;
+ }
+
+ // element not found - add new element to the hash
+ HASH_ADD_KEYPTR(hh, *hash, new_element->interface.name, strlen(new_element->interface.name), new_element);
+
+ return 0;
+}
+
+interfaces_interface_hash_element_t* interfaces_interface_hash_get_element(interfaces_interface_hash_element_t** hash, const char* name)
+{
+ interfaces_interface_hash_element_t* found_element = NULL;
+
+ HASH_FIND_STR(*hash, name, found_element);
+
+ return found_element;
+}
+
+void interfaces_interface_hash_free(interfaces_interface_hash_element_t** hash)
+{
+ interfaces_interface_hash_element_t *tmp = NULL, *element = NULL;
+
+ HASH_ITER(hh, *hash, element, tmp)
+ {
+ HASH_DEL(*hash, element);
+ interfaces_interface_hash_element_free(&element);
+ }
+
+ *hash = NULL;
+}
+
+interfaces_interface_hash_element_t* interfaces_interface_hash_element_new(void)
+{
+ interfaces_interface_hash_element_t* new_element = NULL;
+
+ new_element = xmalloc(sizeof(interfaces_interface_hash_element_t));
+ if (!new_element)
+ return NULL;
+
+ // NULL all fields
+ new_element->interface = (interfaces_interface_t) { 0 };
+
+ return new_element;
+}
+
+int interfaces_interface_hash_element_set_name(interfaces_interface_hash_element_t** el, const char* name)
+{
+ if ((*el)->interface.name) {
+ FREE_SAFE((*el)->interface.name);
+ }
+
+ if (name) {
+ (*el)->interface.name = xstrdup(name);
+ return (*el)->interface.name == NULL;
+ }
+
+ return 0;
+}
+
+int interfaces_interface_hash_element_set_description(interfaces_interface_hash_element_t** el, const char* description)
+{
+ if ((*el)->interface.description) {
+ FREE_SAFE((*el)->interface.description);
+ }
+
+ if (description) {
+ (*el)->interface.description = xstrdup(description);
+ return (*el)->interface.description == NULL;
+ }
+
+ return 0;
+}
+
+int interfaces_interface_hash_element_set_type(interfaces_interface_hash_element_t** el, const char* type)
+{
+ if ((*el)->interface.type) {
+ FREE_SAFE((*el)->interface.type);
+ }
+
+ if (type) {
+ (*el)->interface.type = xstrdup(type);
+ return (*el)->interface.type == NULL;
+ }
+
+ return 0;
+}
+
+int interfaces_interface_hash_element_set_enabled(interfaces_interface_hash_element_t** el, uint8_t enabled)
+{
+ (*el)->interface.enabled = enabled;
+
+ return 0;
+}
+
+int interfaces_interface_hash_element_set_link_up_down_trap_enable(interfaces_interface_hash_element_t** el, interfaces_interface_link_up_down_trap_enable_t link_up_down_trap_enable)
+{
+ (*el)->interface.link_up_down_trap_enable = link_up_down_trap_enable;
+ return 0;
+}
+
+int interfaces_interface_hash_element_set_carrier_delay(interfaces_interface_hash_element_t** el, interfaces_interface_carrier_delay_t carrier_delay)
+{
+ (*el)->interface.carrier_delay = carrier_delay;
+ return 0;
+}
+
+int interfaces_interface_hash_element_set_dampening(interfaces_interface_hash_element_t** el, interfaces_interface_dampening_t dampening)
+{
+ (*el)->interface.dampening = dampening;
+ return 0;
+}
+
+int interfaces_interface_hash_element_set_encapsulation(interfaces_interface_hash_element_t** el, interfaces_interface_encapsulation_t encapsulation)
+{
+ (*el)->interface.encapsulation = encapsulation;
+ (*el)->interface.encapsulation.dot1q_vlan.outer_tag.tag_type = xstrdup(encapsulation.dot1q_vlan.outer_tag.tag_type);
+ (*el)->interface.encapsulation.dot1q_vlan.second_tag.tag_type = xstrdup(encapsulation.dot1q_vlan.second_tag.tag_type);
+
+ return 0;
+}
+
+int interfaces_interface_hash_element_set_loopback(interfaces_interface_hash_element_t** el, const char* loopback)
+{
+ if ((*el)->interface.loopback) {
+ FREE_SAFE((*el)->interface.loopback);
+ }
+
+ if (loopback) {
+ (*el)->interface.loopback = xstrdup(loopback);
+ return (*el)->interface.loopback == NULL;
+ }
+
+ return 0;
+}
+
+int interfaces_interface_hash_element_set_max_frame_size(interfaces_interface_hash_element_t** el, uint32_t max_frame_size)
+{
+ (*el)->interface.max_frame_size = max_frame_size;
+ return 0;
+}
+
+int interfaces_interface_hash_element_set_parent_interface(interfaces_interface_hash_element_t** el, const char* parent_interface)
+{
+ if ((*el)->interface.parent_interface) {
+ FREE_SAFE((*el)->interface.parent_interface);
+ }
+
+ if (parent_interface) {
+ (*el)->interface.parent_interface = xstrdup(parent_interface);
+ return (*el)->interface.parent_interface == NULL;
+ }
+
+ return 0;
+}
+
+int interfaces_interface_type_nl2ly(const char* nl_type, const char** ly_type)
+{
+ int error = 0;
+
+ if (nl_type == NULL) {
+ // fix for now - investigate more - lo interface has type == NULL
+ *ly_type = "iana-if-type:other";
+ return 0;
+ }
+
+ if (strcmp(nl_type, "veth") == 0) {
+ *ly_type = "iana-if-type:ethernetCsmacd";
+ } else if (strcmp(nl_type, "vcan") == 0) {
+ *ly_type = "iana-if-type:softwareLoopback";
+ } else if (strcmp(nl_type, "vlan") == 0) {
+ *ly_type = "iana-if-type:l2vlan";
+ } else if (strcmp(nl_type, "dummy") == 0) {
+ *ly_type = "iana-if-type:other";
+ } else if (strcmp(nl_type, "bridge") == 0) {
+ *ly_type = "iana-if-type:bridge";
+ } else {
+ error = -2;
+ }
+
+ return error;
+}
+
+int interfaces_interface_type_ly2nl(const char* ly_type, const char** nl_type)
+{
+ int error = 0;
+
+ if (ly_type == NULL) {
+ return -1;
+ }
+
+ if (strcmp(ly_type, "iana-if-type:ethernetCsmacd") == 0) {
+ *nl_type = "veth";
+ } else if (strcmp(ly_type, "iana-if-type:softwareLoopback") == 0) {
+ *nl_type = "vcan";
+ } else if (strcmp(ly_type, "iana-if-type:l2vlan") == 0) {
+ *nl_type = "vlan";
+ } else if (strcmp(ly_type, "iana-if-type:other") == 0) {
+ *nl_type = "dummy";
+ } else {
+ error = -2;
+ }
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/data/interfaces/interface.h b/src/interfaces/src/plugin/data/interfaces/interface.h
new file mode 100644
index 00000000..eda09843
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface.h
@@ -0,0 +1,44 @@
+#ifndef INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_H
+#define INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_H
+
+#include "plugin/types.h"
+#include
+
+/*
+ Hash table operations
+*/
+
+interfaces_interface_hash_element_t* interfaces_interface_hash_new(void);
+void interfaces_interface_hash_print_debug(const interfaces_interface_hash_element_t* if_hash);
+int interfaces_interface_hash_from_ly(interfaces_interface_hash_element_t** if_hash, const struct lyd_node* interface_list_node);
+int interfaces_interface_hash_to_ly(const struct ly_ctx* ly_ctx, interfaces_interface_hash_element_t* if_hash, struct lyd_node** interfaces_container_node);
+int interfaces_interface_hash_add_element(interfaces_interface_hash_element_t** hash, interfaces_interface_hash_element_t* new_element);
+interfaces_interface_hash_element_t* interfaces_interface_hash_get_element(interfaces_interface_hash_element_t** hash, const char* name);
+void interfaces_interface_hash_free(interfaces_interface_hash_element_t** hash);
+
+/*
+ Element operations
+*/
+
+interfaces_interface_hash_element_t* interfaces_interface_hash_element_new(void);
+void interfaces_interface_hash_element_free(interfaces_interface_hash_element_t** el);
+int interfaces_interface_hash_element_set_name(interfaces_interface_hash_element_t** el, const char* name);
+int interfaces_interface_hash_element_set_description(interfaces_interface_hash_element_t** el, const char* description);
+int interfaces_interface_hash_element_set_type(interfaces_interface_hash_element_t** el, const char* type);
+int interfaces_interface_hash_element_set_enabled(interfaces_interface_hash_element_t** el, uint8_t enabled);
+int interfaces_interface_hash_element_set_link_up_down_trap_enable(interfaces_interface_hash_element_t** el, interfaces_interface_link_up_down_trap_enable_t link_up_down_trap_enable);
+int interfaces_interface_hash_element_set_carrier_delay(interfaces_interface_hash_element_t** el, interfaces_interface_carrier_delay_t carrier_delay);
+int interfaces_interface_hash_element_set_dampening(interfaces_interface_hash_element_t** el, interfaces_interface_dampening_t dampening);
+int interfaces_interface_hash_element_set_encapsulation(interfaces_interface_hash_element_t** el, interfaces_interface_encapsulation_t encapsulation);
+int interfaces_interface_hash_element_set_loopback(interfaces_interface_hash_element_t** el, const char* loopback);
+int interfaces_interface_hash_element_set_max_frame_size(interfaces_interface_hash_element_t** el, uint32_t max_frame_size);
+int interfaces_interface_hash_element_set_parent_interface(interfaces_interface_hash_element_t** el, const char* parent_interface);
+
+/*
+ Helper functionality.
+*/
+
+int interfaces_interface_type_nl2ly(const char* nl_type, const char** ly_type);
+int interfaces_interface_type_ly2nl(const char* ly_type, const char** nl_type);
+
+#endif // INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_H
diff --git a/src/interfaces/src/plugin/data/interfaces/interface/ipv4.c b/src/interfaces/src/plugin/data/interfaces/interface/ipv4.c
new file mode 100644
index 00000000..cbb7e4dd
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface/ipv4.c
@@ -0,0 +1,22 @@
+#include "ipv4.h"
+
+int interfaces_interface_hash_element_ipv4_set_enabled(interfaces_interface_ipv4_t* ipv4, uint8_t enabled)
+{
+ ipv4->enabled = enabled;
+
+ return 0;
+}
+
+int interfaces_interface_hash_element_ipv4_set_forwarding(interfaces_interface_ipv4_t* ipv4, uint8_t forwarding)
+{
+ ipv4->forwarding = forwarding;
+
+ return 0;
+}
+
+int interfaces_interface_hash_element_ipv4_set_mtu(interfaces_interface_ipv4_t* ipv4, uint16_t mtu)
+{
+ ipv4->mtu = mtu;
+
+ return 0;
+}
diff --git a/src/interfaces/src/plugin/data/interfaces/interface/ipv4.h b/src/interfaces/src/plugin/data/interfaces/interface/ipv4.h
new file mode 100644
index 00000000..61fb3f4a
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface/ipv4.h
@@ -0,0 +1,10 @@
+#ifndef INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV4_H
+#define INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV4_H
+
+#include "plugin/types.h"
+
+int interfaces_interface_hash_element_ipv4_set_enabled(interfaces_interface_ipv4_t* ipv4, uint8_t enabled);
+int interfaces_interface_hash_element_ipv4_set_forwarding(interfaces_interface_ipv4_t* ipv4, uint8_t forwarding);
+int interfaces_interface_hash_element_ipv4_set_mtu(interfaces_interface_ipv4_t* ipv4, uint16_t mtu);
+
+#endif // INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV4_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/data/interfaces/interface/ipv4/address.c b/src/interfaces/src/plugin/data/interfaces/interface/ipv4/address.c
new file mode 100644
index 00000000..ca810c2f
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface/ipv4/address.c
@@ -0,0 +1,143 @@
+#include "address.h"
+#include "plugin/types.h"
+#include "src/utlist.h"
+#include "srpc/common.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+interfaces_interface_ipv4_address_element_t* interfaces_interface_ipv4_address_new(void)
+{
+ return NULL;
+}
+
+int interfaces_interface_ipv4_address_add_element(interfaces_interface_ipv4_address_element_t** address, interfaces_interface_ipv4_address_element_t* new_element)
+{
+ LL_PREPEND(*address, new_element);
+ return 0;
+}
+
+void interfaces_interface_ipv4_address_free(interfaces_interface_ipv4_address_element_t** address)
+{
+ interfaces_interface_ipv4_address_element_t *iter = NULL, *tmp = NULL;
+
+ LL_FOREACH_SAFE(*address, iter, tmp)
+ {
+ // remove from list
+ LL_DELETE(*address, iter);
+
+ // free element data
+ interfaces_interface_ipv4_address_element_free(&iter);
+ }
+}
+
+interfaces_interface_ipv4_address_element_t* interfaces_interface_ipv4_address_element_new(void)
+{
+ interfaces_interface_ipv4_address_element_t* new_element = NULL;
+
+ new_element = malloc(sizeof(interfaces_interface_ipv4_address_element_t));
+ new_element->address = (interfaces_interface_ipv4_address_t) { 0 };
+
+ return new_element;
+}
+
+void interfaces_interface_ipv4_address_element_free(interfaces_interface_ipv4_address_element_t** el)
+{
+ if (*el) {
+ // IP
+ if ((*el)->address.ip) {
+ free((*el)->address.ip);
+ }
+
+ // netmask if one exists
+ if ((*el)->address.subnet_type != interfaces_interface_ipv4_address_subnet_none) {
+ if ((*el)->address.subnet_type == interfaces_interface_ipv4_address_subnet_netmask && (*el)->address.subnet.netmask != NULL) {
+ free((*el)->address.subnet.netmask);
+ }
+ }
+
+ // address data
+ free(*el);
+ *el = NULL;
+ }
+}
+
+int interfaces_interface_ipv4_address_element_set_ip(interfaces_interface_ipv4_address_element_t** el, const char* ip)
+{
+ if ((*el)->address.ip) {
+ free((*el)->address.ip);
+ }
+
+ if (ip) {
+ (*el)->address.ip = strdup(ip);
+ return (*el)->address.ip == NULL;
+ }
+
+ return 0;
+}
+
+int interfaces_interface_ipv4_address_element_set_prefix_length(interfaces_interface_ipv4_address_element_t** el, uint8_t prefix_length)
+{
+ (*el)->address.subnet.prefix_length = prefix_length;
+ (*el)->address.subnet_type = interfaces_interface_ipv4_address_subnet_prefix_length;
+
+ return 0;
+}
+
+int interfaces_interface_ipv4_address_element_set_netmask(interfaces_interface_ipv4_address_element_t** el, const char* netmask)
+{
+ if ((*el)->address.subnet.netmask) {
+ free((*el)->address.subnet.netmask);
+ (*el)->address.subnet_type = interfaces_interface_ipv4_address_subnet_none;
+ }
+
+ if (netmask) {
+ (*el)->address.subnet.netmask = strdup(netmask);
+
+ // check for correctly set netmask
+ if ((*el)->address.subnet.netmask != NULL) {
+ (*el)->address.subnet_type = interfaces_interface_ipv4_address_subnet_netmask;
+ return 0;
+ } else {
+ (*el)->address.subnet_type = interfaces_interface_ipv4_address_subnet_none;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int interfaces_interface_ipv4_address_netmask2prefix(const char* netmask, uint8_t* prefix_length)
+{
+ int error = 0;
+ struct in_addr addr = { 0 };
+ uint8_t prefix = 0;
+
+ // convert to bits (uint32_t -> addr.s_addr)
+ SRPC_SAFE_CALL_ERR_COND(error, error != 1, inet_pton(AF_INET, netmask, &addr), error_out);
+
+ // count address bits
+ while (addr.s_addr) {
+ if (addr.s_addr & 0x1) {
+ ++prefix;
+ }
+
+ addr.s_addr >>= 1;
+ }
+
+ // set the provided value
+ *prefix_length = prefix;
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ return error;
+}
diff --git a/src/interfaces/src/plugin/data/interfaces/interface/ipv4/address.h b/src/interfaces/src/plugin/data/interfaces/interface/ipv4/address.h
new file mode 100644
index 00000000..057cd8a0
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface/ipv4/address.h
@@ -0,0 +1,33 @@
+#ifndef INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV4_ADDRESS_H
+#define INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV4_ADDRESS_H
+
+#include "plugin/types.h"
+
+#include
+#include
+
+/*
+ Linked list operations.
+*/
+
+interfaces_interface_ipv4_address_element_t* interfaces_interface_ipv4_address_new(void);
+int interfaces_interface_ipv4_address_add_element(interfaces_interface_ipv4_address_element_t** address, interfaces_interface_ipv4_address_element_t* new_element);
+void interfaces_interface_ipv4_address_free(interfaces_interface_ipv4_address_element_t** address);
+
+/*
+ Element operations.
+*/
+
+interfaces_interface_ipv4_address_element_t* interfaces_interface_ipv4_address_element_new(void);
+void interfaces_interface_ipv4_address_element_free(interfaces_interface_ipv4_address_element_t** el);
+int interfaces_interface_ipv4_address_element_set_ip(interfaces_interface_ipv4_address_element_t** el, const char* ip);
+int interfaces_interface_ipv4_address_element_set_prefix_length(interfaces_interface_ipv4_address_element_t** el, uint8_t prefix_length);
+int interfaces_interface_ipv4_address_element_set_netmask(interfaces_interface_ipv4_address_element_t** el, const char* netmask);
+
+/*
+ Helper functions
+*/
+
+int interfaces_interface_ipv4_address_netmask2prefix(const char* netmask, uint8_t* prefix_length);
+
+#endif // INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV4_ADDRESS_H
diff --git a/src/interfaces/src/plugin/data/interfaces/interface/ipv4/neighbor.c b/src/interfaces/src/plugin/data/interfaces/interface/ipv4/neighbor.c
new file mode 100644
index 00000000..0d251b9c
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface/ipv4/neighbor.c
@@ -0,0 +1,101 @@
+#include "neighbor.h"
+#include "src/utlist.h"
+
+interfaces_interface_ipv4_neighbor_element_t* interfaces_interface_ipv4_neighbor_new(void)
+{
+ return NULL;
+}
+
+int interfaces_interface_ipv4_neighbor_add_element(interfaces_interface_ipv4_neighbor_element_t** address, interfaces_interface_ipv4_neighbor_element_t* new_element)
+{
+ LL_PREPEND(*address, new_element);
+ return 0;
+}
+
+void interfaces_interface_ipv4_neighbor_free(interfaces_interface_ipv4_neighbor_element_t** address)
+{
+ interfaces_interface_ipv4_neighbor_element_t *iter = NULL, *tmp = NULL;
+
+ LL_FOREACH_SAFE(*address, iter, tmp)
+ {
+ // remove from list
+ LL_DELETE(*address, iter);
+
+ // free element data
+ interfaces_interface_ipv4_neighbor_element_free(&iter);
+ }
+}
+
+interfaces_interface_ipv4_neighbor_element_t* interfaces_interface_ipv4_neighbor_element_new(void)
+{
+ interfaces_interface_ipv4_neighbor_element_t* new_element = NULL;
+
+ new_element = malloc(sizeof(interfaces_interface_ipv4_neighbor_element_t));
+ new_element->neighbor = (interfaces_interface_ipv4_neighbor_t) { 0 };
+
+ return new_element;
+}
+
+void interfaces_interface_ipv4_neighbor_element_free(interfaces_interface_ipv4_neighbor_element_t** el)
+{
+ if (*el) {
+ if ((*el)->neighbor.ip) {
+ free((*el)->neighbor.ip);
+ }
+
+ if ((*el)->neighbor.link_layer_address) {
+ free((*el)->neighbor.link_layer_address);
+ }
+
+ // address data
+ free(*el);
+ *el = NULL;
+ }
+}
+
+/* set (deepcopy) an IPv4 neighbor list */
+int interfaces_interface_ipv4_neighbor_element_set(interfaces_interface_ipv4_neighbor_element_t** src,
+ interfaces_interface_ipv4_neighbor_element_t **dst)
+{
+ interfaces_interface_ipv4_neighbor_element_t *src_iter = NULL;
+ interfaces_interface_ipv4_neighbor_element_t *new_elem = NULL;
+
+ LL_FOREACH(*src, src_iter) {
+ new_elem = interfaces_interface_ipv4_neighbor_element_new();
+ interfaces_interface_ipv4_neighbor_element_set_ip(&new_elem, src_iter->neighbor.ip);
+ interfaces_interface_ipv4_neighbor_element_set_link_layer_address(&new_elem,
+ src_iter->neighbor.link_layer_address);
+
+ interfaces_interface_ipv4_neighbor_add_element(dst, new_elem);
+ }
+
+ return 0;
+}
+
+int interfaces_interface_ipv4_neighbor_element_set_ip(interfaces_interface_ipv4_neighbor_element_t** el, const char* ip)
+{
+ if ((*el)->neighbor.ip) {
+ free((*el)->neighbor.ip);
+ }
+
+ if (ip) {
+ (*el)->neighbor.ip = strdup(ip);
+ return (*el)->neighbor.ip == NULL;
+ }
+
+ return 0;
+}
+
+int interfaces_interface_ipv4_neighbor_element_set_link_layer_address(interfaces_interface_ipv4_neighbor_element_t** el, const char* link_layer_address)
+{
+ if ((*el)->neighbor.link_layer_address) {
+ free((*el)->neighbor.link_layer_address);
+ }
+
+ if (link_layer_address) {
+ (*el)->neighbor.link_layer_address = strdup(link_layer_address);
+ return (*el)->neighbor.link_layer_address == NULL;
+ }
+
+ return 0;
+}
diff --git a/src/interfaces/src/plugin/data/interfaces/interface/ipv4/neighbor.h b/src/interfaces/src/plugin/data/interfaces/interface/ipv4/neighbor.h
new file mode 100644
index 00000000..1616958f
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface/ipv4/neighbor.h
@@ -0,0 +1,27 @@
+#ifndef INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV4_NEIGHBOR_H
+#define INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV4_NEIGHBOR_H
+
+#include "plugin/types.h"
+
+#include
+#include
+
+/*
+ Linked list operations.
+*/
+
+interfaces_interface_ipv4_neighbor_element_t* interfaces_interface_ipv4_neighbor_new(void);
+int interfaces_interface_ipv4_neighbor_add_element(interfaces_interface_ipv4_neighbor_element_t** address, interfaces_interface_ipv4_neighbor_element_t* new_element);
+void interfaces_interface_ipv4_neighbor_free(interfaces_interface_ipv4_neighbor_element_t** address);
+
+/*
+ Element operations.
+*/
+
+interfaces_interface_ipv4_neighbor_element_t* interfaces_interface_ipv4_neighbor_element_new(void);
+void interfaces_interface_ipv4_neighbor_element_free(interfaces_interface_ipv4_neighbor_element_t** el);
+int interfaces_interface_ipv4_neighbor_element_set_ip(interfaces_interface_ipv4_neighbor_element_t** el, const char* ip);
+int interfaces_interface_ipv4_neighbor_element_set_link_layer_address(interfaces_interface_ipv4_neighbor_element_t** el, const char* link_layer_address);
+int interfaces_interface_ipv4_neighbor_element_set(interfaces_interface_ipv4_neighbor_element_t** src, interfaces_interface_ipv4_neighbor_element_t **dst);
+
+#endif // INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV4_NEIGHBOR_H
diff --git a/src/interfaces/src/plugin/data/interfaces/interface/ipv6.c b/src/interfaces/src/plugin/data/interfaces/interface/ipv6.c
new file mode 100644
index 00000000..36dbbbe1
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface/ipv6.c
@@ -0,0 +1,22 @@
+#include "ipv6.h"
+
+int interfaces_interface_hash_element_ipv6_set_enabled(interfaces_interface_ipv6_t* ipv6, uint8_t enabled)
+{
+ ipv6->enabled = enabled;
+
+ return 0;
+}
+
+int interfaces_interface_hash_element_ipv6_set_forwarding(interfaces_interface_ipv6_t* ipv6, uint8_t forwarding)
+{
+ ipv6->forwarding = forwarding;
+
+ return 0;
+}
+
+int interfaces_interface_hash_element_ipv6_set_mtu(interfaces_interface_ipv6_t* ipv6, uint16_t mtu)
+{
+ ipv6->mtu = mtu;
+
+ return 0;
+}
diff --git a/src/interfaces/src/plugin/data/interfaces/interface/ipv6.h b/src/interfaces/src/plugin/data/interfaces/interface/ipv6.h
new file mode 100644
index 00000000..302671af
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface/ipv6.h
@@ -0,0 +1,10 @@
+#ifndef INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV6_H
+#define INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV6_H
+
+#include "plugin/types.h"
+
+int interfaces_interface_hash_element_ipv6_set_enabled(interfaces_interface_ipv6_t* ipv6, uint8_t enabled);
+int interfaces_interface_hash_element_ipv6_set_forwarding(interfaces_interface_ipv6_t* ipv6, uint8_t forwarding);
+int interfaces_interface_hash_element_ipv6_set_mtu(interfaces_interface_ipv6_t* ipv6, uint16_t mtu);
+
+#endif // INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV6_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/data/interfaces/interface/ipv6/address.c b/src/interfaces/src/plugin/data/interfaces/interface/ipv6/address.c
new file mode 100644
index 00000000..d7dc4a97
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface/ipv6/address.c
@@ -0,0 +1,94 @@
+#include "address.h"
+#include "plugin/types.h"
+#include "src/utlist.h"
+#include
+
+interfaces_interface_ipv6_address_element_t* interfaces_interface_ipv6_address_new(void)
+{
+ return NULL;
+}
+
+int interfaces_interface_ipv6_address_add_element(interfaces_interface_ipv6_address_element_t** address, interfaces_interface_ipv6_address_element_t* new_element)
+{
+ LL_PREPEND(*address, new_element);
+ return 0;
+}
+
+void interfaces_interface_ipv6_address_free(interfaces_interface_ipv6_address_element_t** address)
+{
+ interfaces_interface_ipv6_address_element_t *iter = NULL, *tmp = NULL;
+
+ LL_FOREACH_SAFE(*address, iter, tmp)
+ {
+ // remove from list
+ LL_DELETE(*address, iter);
+
+ // free element data
+ interfaces_interface_ipv6_address_element_free(&iter);
+ }
+}
+
+interfaces_interface_ipv6_address_element_t* interfaces_interface_ipv6_address_element_new(void)
+{
+ interfaces_interface_ipv6_address_element_t* new_element = NULL;
+
+ new_element = malloc(sizeof(interfaces_interface_ipv6_address_element_t));
+ new_element->address = (interfaces_interface_ipv6_address_t) { 0 };
+
+ return new_element;
+}
+
+int interfaces_interface_ipv6_address_element_set(interfaces_interface_ipv6_address_element_t** src,
+ interfaces_interface_ipv6_address_element_t **dst)
+{
+ interfaces_interface_ipv6_address_element_t *src_iter = NULL;
+ interfaces_interface_ipv6_address_element_t *new_elem = NULL;
+
+ LL_FOREACH(*src, src_iter) {
+ new_elem = interfaces_interface_ipv6_address_element_new();
+ interfaces_interface_ipv6_address_element_set_ip(&new_elem, src_iter->address.ip);
+ interfaces_interface_ipv6_address_element_set_prefix_length(&new_elem,
+ src_iter->address.prefix_length);
+
+ interfaces_interface_ipv6_address_add_element(dst, new_elem);
+ }
+
+ return 0;
+
+}
+
+
+void interfaces_interface_ipv6_address_element_free(interfaces_interface_ipv6_address_element_t** el)
+{
+ if (*el) {
+ // IP
+ if ((*el)->address.ip) {
+ free((*el)->address.ip);
+ }
+
+ // address data
+ free(*el);
+ *el = NULL;
+ }
+}
+
+int interfaces_interface_ipv6_address_element_set_ip(interfaces_interface_ipv6_address_element_t** el, const char* ip)
+{
+ if ((*el)->address.ip) {
+ free((*el)->address.ip);
+ }
+
+ if (ip) {
+ (*el)->address.ip = strdup(ip);
+ return (*el)->address.ip == NULL;
+ }
+
+ return 0;
+}
+
+int interfaces_interface_ipv6_address_element_set_prefix_length(interfaces_interface_ipv6_address_element_t** el, uint8_t prefix_length)
+{
+ (*el)->address.prefix_length = prefix_length;
+
+ return 0;
+}
diff --git a/src/interfaces/src/plugin/data/interfaces/interface/ipv6/address.h b/src/interfaces/src/plugin/data/interfaces/interface/ipv6/address.h
new file mode 100644
index 00000000..13761fb0
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface/ipv6/address.h
@@ -0,0 +1,27 @@
+#ifndef INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV6_ADDRESS_H
+#define INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV6_ADDRESS_H
+
+#include "plugin/types.h"
+
+#include
+#include
+
+/*
+ Linked list operations.
+*/
+
+interfaces_interface_ipv6_address_element_t* interfaces_interface_ipv6_address_new(void);
+int interfaces_interface_ipv6_address_add_element(interfaces_interface_ipv6_address_element_t** address, interfaces_interface_ipv6_address_element_t* new_element);
+void interfaces_interface_ipv6_address_free(interfaces_interface_ipv6_address_element_t** address);
+
+/*
+ Element operations.
+*/
+
+interfaces_interface_ipv6_address_element_t* interfaces_interface_ipv6_address_element_new(void);
+void interfaces_interface_ipv6_address_element_free(interfaces_interface_ipv6_address_element_t** el);
+int interfaces_interface_ipv6_address_element_set_ip(interfaces_interface_ipv6_address_element_t** el, const char* ip);
+int interfaces_interface_ipv6_address_element_set_prefix_length(interfaces_interface_ipv6_address_element_t** el, uint8_t prefix_length);
+int interfaces_interface_ipv6_address_element_set(interfaces_interface_ipv6_address_element_t** src, interfaces_interface_ipv6_address_element_t **dst);
+
+#endif // INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV6_ADDRESS_H
diff --git a/src/interfaces/src/plugin/data/interfaces/interface/ipv6/neighbor.c b/src/interfaces/src/plugin/data/interfaces/interface/ipv6/neighbor.c
new file mode 100644
index 00000000..a702abe4
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface/ipv6/neighbor.c
@@ -0,0 +1,101 @@
+#include "neighbor.h"
+#include "src/utlist.h"
+
+interfaces_interface_ipv6_neighbor_element_t* interfaces_interface_ipv6_neighbor_new(void)
+{
+ return NULL;
+}
+
+int interfaces_interface_ipv6_neighbor_add_element(interfaces_interface_ipv6_neighbor_element_t** address, interfaces_interface_ipv6_neighbor_element_t* new_element)
+{
+ LL_PREPEND(*address, new_element);
+ return 0;
+}
+
+void interfaces_interface_ipv6_neighbor_free(interfaces_interface_ipv6_neighbor_element_t** address)
+{
+ interfaces_interface_ipv6_neighbor_element_t *iter = NULL, *tmp = NULL;
+
+ LL_FOREACH_SAFE(*address, iter, tmp)
+ {
+ // remove from list
+ LL_DELETE(*address, iter);
+
+ // free element data
+ interfaces_interface_ipv6_neighbor_element_free(&iter);
+ }
+}
+
+interfaces_interface_ipv6_neighbor_element_t* interfaces_interface_ipv6_neighbor_element_new(void)
+{
+ interfaces_interface_ipv6_neighbor_element_t* new_element = NULL;
+
+ new_element = malloc(sizeof(interfaces_interface_ipv6_neighbor_element_t));
+ new_element->neighbor = (interfaces_interface_ipv6_neighbor_t) { 0 };
+
+ return new_element;
+}
+
+void interfaces_interface_ipv6_neighbor_element_free(interfaces_interface_ipv6_neighbor_element_t** el)
+{
+ if (*el) {
+ if ((*el)->neighbor.ip) {
+ free((*el)->neighbor.ip);
+ }
+
+ if ((*el)->neighbor.link_layer_address) {
+ free((*el)->neighbor.link_layer_address);
+ }
+
+ // address data
+ free(*el);
+ *el = NULL;
+ }
+}
+
+int interfaces_interface_ipv6_neighbor_element_set_ip(interfaces_interface_ipv6_neighbor_element_t** el, const char* ip)
+{
+ if ((*el)->neighbor.ip) {
+ free((*el)->neighbor.ip);
+ }
+
+ if (ip) {
+ (*el)->neighbor.ip = strdup(ip);
+ return (*el)->neighbor.ip == NULL;
+ }
+
+ return 0;
+}
+
+int interfaces_interface_ipv6_neighbor_element_set_link_layer_address(interfaces_interface_ipv6_neighbor_element_t** el, const char* link_layer_address)
+{
+ if ((*el)->neighbor.link_layer_address) {
+ free((*el)->neighbor.link_layer_address);
+ }
+
+ if (link_layer_address) {
+ (*el)->neighbor.link_layer_address = strdup(link_layer_address);
+ return (*el)->neighbor.link_layer_address == NULL;
+ }
+
+ return 0;
+}
+
+/* set (deepcopy) an ipv6 neighbor list */
+int interfaces_interface_ipv6_neighbor_element_set(interfaces_interface_ipv6_neighbor_element_t** src,
+ interfaces_interface_ipv6_neighbor_element_t **dst)
+{
+ interfaces_interface_ipv6_neighbor_element_t *src_iter = NULL;
+ interfaces_interface_ipv6_neighbor_element_t *new_elem = NULL;
+
+ LL_FOREACH(*src, src_iter) {
+ new_elem = interfaces_interface_ipv6_neighbor_element_new();
+ interfaces_interface_ipv6_neighbor_element_set_ip(&new_elem, src_iter->neighbor.ip);
+ interfaces_interface_ipv6_neighbor_element_set_link_layer_address(&new_elem,
+ src_iter->neighbor.link_layer_address);
+
+ interfaces_interface_ipv6_neighbor_add_element(dst, new_elem);
+ }
+
+ return 0;
+}
diff --git a/src/interfaces/src/plugin/data/interfaces/interface/ipv6/neighbor.h b/src/interfaces/src/plugin/data/interfaces/interface/ipv6/neighbor.h
new file mode 100644
index 00000000..467e8012
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface/ipv6/neighbor.h
@@ -0,0 +1,27 @@
+#ifndef INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV6_NEIGHBOR_H
+#define INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV6_NEIGHBOR_H
+
+#include "plugin/types.h"
+
+#include
+#include
+
+/*
+ Linked list operations.
+*/
+
+interfaces_interface_ipv6_neighbor_element_t* interfaces_interface_ipv6_neighbor_new(void);
+int interfaces_interface_ipv6_neighbor_add_element(interfaces_interface_ipv6_neighbor_element_t** address, interfaces_interface_ipv6_neighbor_element_t* new_element);
+void interfaces_interface_ipv6_neighbor_free(interfaces_interface_ipv6_neighbor_element_t** address);
+
+/*
+ Element operations.
+*/
+
+interfaces_interface_ipv6_neighbor_element_t* interfaces_interface_ipv6_neighbor_element_new(void);
+void interfaces_interface_ipv6_neighbor_element_free(interfaces_interface_ipv6_neighbor_element_t** el);
+int interfaces_interface_ipv6_neighbor_element_set_ip(interfaces_interface_ipv6_neighbor_element_t** el, const char* ip);
+int interfaces_interface_ipv6_neighbor_element_set_link_layer_address(interfaces_interface_ipv6_neighbor_element_t** el, const char* link_layer_address);
+int interfaces_interface_ipv6_neighbor_element_set(interfaces_interface_ipv6_neighbor_element_t** src, interfaces_interface_ipv6_neighbor_element_t **dst);
+
+#endif // INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_IPV6_NEIGHBOR_H
diff --git a/src/interfaces/src/plugin/data/interfaces/interface/linked_list.h b/src/interfaces/src/plugin/data/interfaces/interface/linked_list.h
new file mode 100644
index 00000000..853345f0
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface/linked_list.h
@@ -0,0 +1,57 @@
+#ifndef INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_LIST_H
+#define INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_LIST_H
+
+#include "ipv4/address.h"
+#include "ipv4/neighbor.h"
+#include "ipv6/address.h"
+#include "ipv6/neighbor.h"
+#include "plugin/types.h"
+
+#include
+#include
+
+/*
+ Linked list operations
+*/
+
+#define INTERFACES_INTERFACE_LIST_NEW(ll_ptr) \
+ do { \
+ (ll_ptr) = NULL; \
+ } while (0)
+
+#define INTERFACES_INTERFACE_LIST_FREE(ll_ptr) \
+ _Generic((ll_ptr), \
+ interfaces_interface_ipv4_address_element_t * \
+ : interfaces_interface_ipv4_address_free, \
+ interfaces_interface_ipv4_neighbor_element_t \
+ * \
+ : interfaces_interface_ipv4_neighbor_free, \
+ interfaces_interface_ipv6_address_element_t \
+ * \
+ : interfaces_interface_ipv6_address_free, \
+ interfaces_interface_ipv6_neighbor_element_t \
+ * \
+ : interfaces_interface_ipv6_neighbor_free)(&ll_ptr)
+
+/* prepend since ordering doesn't matter - O(1) */
+#define INTERFACES_INTERFACE_LIST_ADD_ELEMENT(ll_ptr, new_element_ptr) \
+ do { \
+ LL_PREPEND(ll_ptr, new_element_ptr); \
+ } while (0)
+
+#define INTERFACES_INTERFACE_LIST_GET_ELEMENT_STRING(ll_ptr, element_ptr, member, value) \
+ do { \
+ LL_FOREACH(ll_ptr, element_ptr) \
+ { \
+ if (strcmp(element_ptr->member, value) == 0) { \
+ break; \
+ } \
+ } \
+ } while (0)
+
+#define INTERFACES_INTERFACE_LIST_GET_ELEMENT_SCALAR(ll_ptr, element_ptr, member, value) \
+ do { \
+ LL_SEARCH_SCALAR(ll_ptr, element_ptr, member, value); \
+ } while (0)
+
+#endif // INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_LIST_H
diff --git a/src/interfaces/src/plugin/data/interfaces/interface_state.c b/src/interfaces/src/plugin/data/interfaces/interface_state.c
new file mode 100644
index 00000000..426dff94
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface_state.c
@@ -0,0 +1,95 @@
+#include "interface_state.h"
+#include "src/uthash.h"
+#include
+
+interfaces_interface_state_hash_element_t* interfaces_interface_state_hash_new(void)
+{
+ return NULL;
+}
+
+int interfaces_interface_state_hash_add(interfaces_interface_state_hash_element_t** state_hash, interfaces_interface_state_hash_element_t* new_element)
+{
+ interfaces_interface_state_hash_element_t* found_element = NULL;
+
+ HASH_FIND_STR(*state_hash, new_element->state.name, found_element);
+
+ // element already exists
+ if (found_element != NULL) {
+ return -1;
+ }
+
+ // element not found - add new element to the hash
+ HASH_ADD_KEYPTR(hh, *state_hash, new_element->state.name, strlen(new_element->state.name), new_element);
+
+ return 0;
+}
+
+interfaces_interface_state_hash_element_t* interfaces_interface_state_hash_get(const interfaces_interface_state_hash_element_t* state_hash, const char* name)
+{
+ interfaces_interface_state_hash_element_t* state = NULL;
+
+ HASH_FIND_STR(state_hash, name, state);
+
+ return state;
+}
+
+void interfaces_interface_state_hash_free(interfaces_interface_state_hash_element_t** state_hash)
+{
+ interfaces_interface_state_hash_element_t *current = NULL, *tmp = NULL;
+
+ HASH_ITER(hh, *state_hash, current, tmp)
+ {
+ HASH_DEL(*state_hash, current);
+
+ interfaces_interface_state_hash_element_free(¤t);
+ }
+}
+
+interfaces_interface_state_hash_element_t* interfaces_interface_state_hash_element_new(void)
+{
+ interfaces_interface_state_hash_element_t* new_element = NULL;
+
+ new_element = malloc(sizeof(interfaces_interface_state_hash_element_t));
+ if (!new_element) {
+ return NULL;
+ }
+
+ new_element->state = (interfaces_interface_state_t) { 0 };
+
+ return new_element;
+}
+
+int interfaces_interface_state_hash_element_set_name(interfaces_interface_state_hash_element_t** el, const char* name)
+{
+ if ((*el)->state.name) {
+ free((*el)->state.name);
+ (*el)->state.name = NULL;
+ }
+
+ if (name) {
+ (*el)->state.name = strdup(name);
+ return (*el)->state.name == NULL;
+ }
+
+ return 0;
+}
+
+void interfaces_interface_state_hash_element_set_state(interfaces_interface_state_hash_element_t** el, const uint8_t state)
+{
+ (*el)->state.state = state;
+}
+
+void interfaces_interface_state_hash_element_set_last_change(interfaces_interface_state_hash_element_t** el, const time_t last_change)
+{
+ (*el)->state.last_change = last_change;
+}
+
+void interfaces_interface_state_hash_element_free(interfaces_interface_state_hash_element_t** el)
+{
+ if (*el) {
+ free((*el)->state.name);
+
+ free(*el);
+ *el = NULL;
+ }
+}
diff --git a/src/interfaces/src/plugin/data/interfaces/interface_state.h b/src/interfaces/src/plugin/data/interfaces/interface_state.h
new file mode 100644
index 00000000..8ee785e7
--- /dev/null
+++ b/src/interfaces/src/plugin/data/interfaces/interface_state.h
@@ -0,0 +1,25 @@
+#ifndef INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_STATE_H
+#define INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_STATE_H
+
+#include "plugin/types.h"
+
+/*
+ Hash table operations
+*/
+
+interfaces_interface_state_hash_element_t* interfaces_interface_state_hash_new(void);
+int interfaces_interface_state_hash_add(interfaces_interface_state_hash_element_t** state_hash, interfaces_interface_state_hash_element_t* new_element);
+interfaces_interface_state_hash_element_t* interfaces_interface_state_hash_get(const interfaces_interface_state_hash_element_t* state_hash, const char* name);
+void interfaces_interface_state_hash_free(interfaces_interface_state_hash_element_t** state_hash);
+
+/*
+ Element operations
+*/
+
+interfaces_interface_state_hash_element_t* interfaces_interface_state_hash_element_new(void);
+int interfaces_interface_state_hash_element_set_name(interfaces_interface_state_hash_element_t** el, const char* name);
+void interfaces_interface_state_hash_element_set_state(interfaces_interface_state_hash_element_t** el, const uint8_t state);
+void interfaces_interface_state_hash_element_set_last_change(interfaces_interface_state_hash_element_t** el, const time_t last_change);
+void interfaces_interface_state_hash_element_free(interfaces_interface_state_hash_element_t** el);
+
+#endif // INTERFACES_PLUGIN_DATA_INTERFACES_INTERFACE_STATE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/ly_tree.c b/src/interfaces/src/plugin/ly_tree.c
new file mode 100644
index 00000000..dd02ec48
--- /dev/null
+++ b/src/interfaces/src/plugin/ly_tree.c
@@ -0,0 +1,459 @@
+#include "ly_tree.h"
+#include "common.h"
+
+#include
+
+int interfaces_ly_tree_create_interfaces(const struct ly_ctx* ly_ctx, struct lyd_node** interfaces_node)
+{
+ return srpc_ly_tree_create_container(ly_ctx, NULL, interfaces_node, "/ietf-interfaces:interfaces");
+}
+
+int interfaces_ly_tree_create_interfaces_interface(const struct ly_ctx* ly_ctx, struct lyd_node* interfaces_node, struct lyd_node** interface_node, const char* name)
+{
+ // TODO: fix this for multiple keys with SRPC library
+ return srpc_ly_tree_create_list(ly_ctx, interfaces_node, interface_node, "interface", "name", name);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, struct lyd_node** ipv6_node)
+{
+ return srpc_ly_tree_create_container(ly_ctx, interface_node, ipv6_node, "ietf-ip:ipv6");
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_autoconf(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, struct lyd_node** autoconf_node)
+{
+ return srpc_ly_tree_create_container(ly_ctx, ipv6_node, autoconf_node, "autoconf");
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_autoconf_temporary_preferred_lifetime(const struct ly_ctx* ly_ctx, struct lyd_node* autoconf_node, const char* temporary_preferred_lifetime)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, autoconf_node, NULL, "temporary-preferred-lifetime", temporary_preferred_lifetime);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_autoconf_temporary_valid_lifetime(const struct ly_ctx* ly_ctx, struct lyd_node* autoconf_node, const char* temporary_valid_lifetime)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, autoconf_node, NULL, "temporary-valid-lifetime", temporary_valid_lifetime);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_autoconf_create_temporary_addresses(const struct ly_ctx* ly_ctx, struct lyd_node* autoconf_node, const char* create_temporary_addresses)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, autoconf_node, NULL, "create-temporary-addresses", create_temporary_addresses);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_autoconf_create_global_addresses(const struct ly_ctx* ly_ctx, struct lyd_node* autoconf_node, const char* create_global_addresses)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, autoconf_node, NULL, "create-global-addresses", create_global_addresses);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_dup_addr_detect_transmits(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, const char* dup_addr_detect_transmits)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, ipv6_node, NULL, "dup-addr-detect-transmits", dup_addr_detect_transmits);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, struct lyd_node** neighbor_node, const char* ip)
+{
+ // TODO: fix this for multiple keys with SRPC library
+ return srpc_ly_tree_create_list(ly_ctx, ipv6_node, neighbor_node, "neighbor", "ip", ip);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_state(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* state)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, neighbor_node, NULL, "state", state);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_is_router(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* is_router)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, neighbor_node, NULL, "is-router", is_router);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_origin(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* origin)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, neighbor_node, NULL, "origin", origin);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_link_layer_address(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* link_layer_address)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, neighbor_node, NULL, "link-layer-address", link_layer_address);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_ip(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* ip)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, neighbor_node, NULL, "ip", ip);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_address(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, struct lyd_node** address_node, const char* ip)
+{
+ // TODO: fix this for multiple keys with SRPC library
+ return srpc_ly_tree_create_list(ly_ctx, ipv6_node, address_node, "address", "ip", ip);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_address_status(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* status)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, address_node, NULL, "status", status);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_address_origin(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* origin)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, address_node, NULL, "origin", origin);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_address_prefix_length(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* prefix_length)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, address_node, NULL, "prefix-length", prefix_length);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_address_ip(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* ip)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, address_node, NULL, "ip", ip);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_mtu(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, const char* mtu)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, ipv6_node, NULL, "mtu", mtu);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_forwarding(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, const char* forwarding)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, ipv6_node, NULL, "forwarding", forwarding);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv6_enabled(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, const char* enabled)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, ipv6_node, NULL, "enabled", enabled);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv4(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, struct lyd_node** ipv4_node)
+{
+ return srpc_ly_tree_create_container(ly_ctx, interface_node, ipv4_node, "ietf-ip:ipv4");
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv4_neighbor(const struct ly_ctx* ly_ctx, struct lyd_node* ipv4_node, struct lyd_node** neighbor_node, const char* ip)
+{
+ // TODO: fix this for multiple keys with SRPC library
+ return srpc_ly_tree_create_list(ly_ctx, ipv4_node, neighbor_node, "neighbor", "ip", ip);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv4_neighbor_origin(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* origin)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, neighbor_node, NULL, "origin", origin);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv4_neighbor_link_layer_address(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* link_layer_address)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, neighbor_node, NULL, "link-layer-address", link_layer_address);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv4_neighbor_ip(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* ip)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, neighbor_node, NULL, "ip", ip);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv4_address(const struct ly_ctx* ly_ctx, struct lyd_node* ipv4_node, struct lyd_node** address_node, const char* ip)
+{
+ // TODO: fix this for multiple keys with SRPC library
+ return srpc_ly_tree_create_list(ly_ctx, ipv4_node, address_node, "address", "ip", ip);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv4_address_origin(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* origin)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, address_node, NULL, "origin", origin);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv4_address_netmask(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* netmask)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, address_node, NULL, "netmask", netmask);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv4_address_prefix_length(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* prefix_length)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, address_node, NULL, "prefix-length", prefix_length);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv4_address_ip(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* ip)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, address_node, NULL, "ip", ip);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv4_mtu(const struct ly_ctx* ly_ctx, struct lyd_node* ipv4_node, const char* mtu)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, ipv4_node, NULL, "mtu", mtu);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv4_forwarding(const struct ly_ctx* ly_ctx, struct lyd_node* ipv4_node, const char* forwarding)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, ipv4_node, NULL, "forwarding", forwarding);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_ipv4_enabled(const struct ly_ctx* ly_ctx, struct lyd_node* ipv4_node, const char* enabled)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, ipv4_node, NULL, "enabled", enabled);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_parent_interface(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* parent_interface)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "parent-interface", parent_interface);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_forwarding_mode(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* forwarding_mode)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "forwarding-mode", forwarding_mode);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_max_frame_size(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* max_frame_size)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "max-frame-size", max_frame_size);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_loopback(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* loopback)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "loopback", loopback);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_encapsulation(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, struct lyd_node** encapsulation_node)
+{
+ return srpc_ly_tree_create_container(ly_ctx, interface_node, encapsulation_node, "encapsulation");
+}
+
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan(const struct ly_ctx* ly_ctx, struct lyd_node* encapsulation_node, struct lyd_node** dot1q_vlan_node)
+{
+ return srpc_ly_tree_create_container(ly_ctx, encapsulation_node, dot1q_vlan_node, "dot1q-vlan");
+}
+
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan_second_tag(const struct ly_ctx* ly_ctx, struct lyd_node* dot1q_vlan_node, struct lyd_node** second_tag_node)
+{
+ return srpc_ly_tree_create_container(ly_ctx, dot1q_vlan_node, second_tag_node, "second-tag");
+}
+
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan_second_tag_vlan_id(const struct ly_ctx* ly_ctx, struct lyd_node* second_tag_node, const char* vlan_id)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, second_tag_node, NULL, "vlan-id", vlan_id);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan_second_tag_tag_type(const struct ly_ctx* ly_ctx, struct lyd_node* second_tag_node, const char* tag_type)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, second_tag_node, NULL, "tag-type", tag_type);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan_outer_tag(const struct ly_ctx* ly_ctx, struct lyd_node* dot1q_vlan_node, struct lyd_node** outer_tag_node)
+{
+ return srpc_ly_tree_create_container(ly_ctx, dot1q_vlan_node, outer_tag_node, "outer-tag");
+}
+
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan_outer_tag_vlan_id(const struct ly_ctx* ly_ctx, struct lyd_node* outer_tag_node, const char* vlan_id)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, outer_tag_node, NULL, "vlan-id", vlan_id);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan_outer_tag_tag_type(const struct ly_ctx* ly_ctx, struct lyd_node* outer_tag_node, const char* tag_type)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, outer_tag_node, NULL, "tag-type", tag_type);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_dampening(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, struct lyd_node** dampening_node)
+{
+ return srpc_ly_tree_create_container(ly_ctx, interface_node, dampening_node, "dampening");
+}
+
+int interfaces_ly_tree_create_interfaces_interface_dampening_time_remaining(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* time_remaining)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, dampening_node, NULL, "time-remaining", time_remaining);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_dampening_suppressed(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* suppressed)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, dampening_node, NULL, "suppressed", suppressed);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_dampening_penalty(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* penalty)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, dampening_node, NULL, "penalty", penalty);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_dampening_max_suppress_time(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* max_suppress_time)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, dampening_node, NULL, "max-suppress-time", max_suppress_time);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_dampening_suppress(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* suppress)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, dampening_node, NULL, "suppress", suppress);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_dampening_reuse(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* reuse)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, dampening_node, NULL, "reuse", reuse);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_dampening_half_life(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* half_life)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, dampening_node, NULL, "half-life", half_life);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_carrier_delay(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, struct lyd_node** carrier_delay_node)
+{
+ return srpc_ly_tree_create_container(ly_ctx, interface_node, carrier_delay_node, "carrier-delay");
+}
+
+int interfaces_ly_tree_create_interfaces_interface_carrier_delay_timer_running(const struct ly_ctx* ly_ctx, struct lyd_node* carrier_delay_node, const char* timer_running)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, carrier_delay_node, NULL, "timer-running", timer_running);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_carrier_delay_carrier_transitions(const struct ly_ctx* ly_ctx, struct lyd_node* carrier_delay_node, const char* carrier_transitions)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, carrier_delay_node, NULL, "carrier-transitions", carrier_transitions);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_carrier_delay_up(const struct ly_ctx* ly_ctx, struct lyd_node* carrier_delay_node, const char* up)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, carrier_delay_node, NULL, "up", up);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_carrier_delay_down(const struct ly_ctx* ly_ctx, struct lyd_node* carrier_delay_node, const char* down)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, carrier_delay_node, NULL, "down", down);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, struct lyd_node** statistics_node)
+{
+ return srpc_ly_tree_create_container(ly_ctx, interface_node, statistics_node, "statistics");
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_discard_unknown_encaps(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_discard_unknown_encaps)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "in-discard-unknown-encaps", in_discard_unknown_encaps);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_out_errors(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* out_errors)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "out-errors", out_errors);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_out_discards(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* out_discards)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "out-discards", out_discards);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_out_multicast_pkts(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* out_multicast_pkts)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "out-multicast-pkts", out_multicast_pkts);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_out_broadcast_pkts(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* out_broadcast_pkts)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "out-broadcast-pkts", out_broadcast_pkts);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_out_unicast_pkts(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* out_unicast_pkts)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "out-unicast-pkts", out_unicast_pkts);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_out_octets(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* out_octets)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "out-octets", out_octets);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_unknown_protos(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_unknown_protos)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "in-unknown-protos", in_unknown_protos);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_errors(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_errors)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "in-errors", in_errors);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_discards(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_discards)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "in-discards", in_discards);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_multicast_pkts(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_multicast_pkts)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "in-multicast-pkts", in_multicast_pkts);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_broadcast_pkts(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_broadcast_pkts)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "in-broadcast-pkts", in_broadcast_pkts);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_unicast_pkts(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_unicast_pkts)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "in-unicast-pkts", in_unicast_pkts);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_octets(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_octets)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "in-octets", in_octets);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_statistics_discontinuity_time(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* discontinuity_time)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, statistics_node, NULL, "discontinuity-time", discontinuity_time);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_speed(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* speed)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "speed", speed);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_lower_layer_if(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* lower_layer_if)
+{
+ return srpc_ly_tree_append_leaf_list(ly_ctx, interface_node, NULL, "lower-layer-if", lower_layer_if);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_higher_layer_if(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* higher_layer_if)
+{
+ return srpc_ly_tree_append_leaf_list(ly_ctx, interface_node, NULL, "higher-layer-if", higher_layer_if);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_phys_address(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* phys_address)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "phys-address", phys_address);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_if_index(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* if_index)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "if-index", if_index);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_last_change(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* last_change)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "last-change", last_change);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_oper_status(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* oper_status)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "oper-status", oper_status);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_admin_status(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* admin_status)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "admin-status", admin_status);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_link_up_down_trap_enable(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* link_up_down_trap_enable)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "link-up-down-trap-enable", link_up_down_trap_enable);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_enabled(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* enabled)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "enabled", enabled);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_type(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* type)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "type", type);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_description(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* description)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "description", description);
+}
+
+int interfaces_ly_tree_create_interfaces_interface_name(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* name)
+{
+ return srpc_ly_tree_create_leaf(ly_ctx, interface_node, NULL, "name", name);
+}
diff --git a/src/interfaces/src/plugin/ly_tree.h b/src/interfaces/src/plugin/ly_tree.h
new file mode 100644
index 00000000..8e47b513
--- /dev/null
+++ b/src/interfaces/src/plugin/ly_tree.h
@@ -0,0 +1,97 @@
+#ifndef INTERFACES_PLUGIN_LY_TREE_H
+#define INTERFACES_PLUGIN_LY_TREE_H
+
+#include
+
+int interfaces_ly_tree_create_interfaces(const struct ly_ctx* ly_ctx, struct lyd_node** interfaces_node);
+int interfaces_ly_tree_create_interfaces_interface(const struct ly_ctx* ly_ctx, struct lyd_node* interfaces_node, struct lyd_node** interface_node, const char* name);
+int interfaces_ly_tree_create_interfaces_interface_ipv6(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, struct lyd_node** ipv6_node);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_autoconf(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, struct lyd_node** autoconf_node);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_autoconf_temporary_preferred_lifetime(const struct ly_ctx* ly_ctx, struct lyd_node* autoconf_node, const char* temporary_preferred_lifetime);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_autoconf_temporary_valid_lifetime(const struct ly_ctx* ly_ctx, struct lyd_node* autoconf_node, const char* temporary_valid_lifetime);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_autoconf_create_temporary_addresses(const struct ly_ctx* ly_ctx, struct lyd_node* autoconf_node, const char* create_temporary_addresses);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_autoconf_create_global_addresses(const struct ly_ctx* ly_ctx, struct lyd_node* autoconf_node, const char* create_global_addresses);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_dup_addr_detect_transmits(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, const char* dup_addr_detect_transmits);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, struct lyd_node** neighbor_node, const char* ip);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_state(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* state);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_is_router(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* is_router);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_origin(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* origin);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_link_layer_address(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* link_layer_address);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_ip(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* ip);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_address(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, struct lyd_node** address_node, const char* ip);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_address_status(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* status);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_address_origin(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* origin);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_address_prefix_length(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* prefix_length);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_address_ip(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* ip);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_mtu(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, const char* mtu);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_forwarding(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, const char* forwarding);
+int interfaces_ly_tree_create_interfaces_interface_ipv6_enabled(const struct ly_ctx* ly_ctx, struct lyd_node* ipv6_node, const char* enabled);
+int interfaces_ly_tree_create_interfaces_interface_ipv4(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, struct lyd_node** ipv4_node);
+int interfaces_ly_tree_create_interfaces_interface_ipv4_neighbor(const struct ly_ctx* ly_ctx, struct lyd_node* ipv4_node, struct lyd_node** neighbor_node, const char* ip);
+int interfaces_ly_tree_create_interfaces_interface_ipv4_neighbor_origin(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* origin);
+int interfaces_ly_tree_create_interfaces_interface_ipv4_neighbor_link_layer_address(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* link_layer_address);
+int interfaces_ly_tree_create_interfaces_interface_ipv4_neighbor_ip(const struct ly_ctx* ly_ctx, struct lyd_node* neighbor_node, const char* ip);
+int interfaces_ly_tree_create_interfaces_interface_ipv4_address(const struct ly_ctx* ly_ctx, struct lyd_node* ipv4_node, struct lyd_node** address_node, const char* ip);
+int interfaces_ly_tree_create_interfaces_interface_ipv4_address_origin(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* origin);
+int interfaces_ly_tree_create_interfaces_interface_ipv4_address_netmask(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* netmask);
+int interfaces_ly_tree_create_interfaces_interface_ipv4_address_prefix_length(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* prefix_length);
+int interfaces_ly_tree_create_interfaces_interface_ipv4_address_ip(const struct ly_ctx* ly_ctx, struct lyd_node* address_node, const char* ip);
+int interfaces_ly_tree_create_interfaces_interface_ipv4_mtu(const struct ly_ctx* ly_ctx, struct lyd_node* ipv4_node, const char* mtu);
+int interfaces_ly_tree_create_interfaces_interface_ipv4_forwarding(const struct ly_ctx* ly_ctx, struct lyd_node* ipv4_node, const char* forwarding);
+int interfaces_ly_tree_create_interfaces_interface_ipv4_enabled(const struct ly_ctx* ly_ctx, struct lyd_node* ipv4_node, const char* enabled);
+int interfaces_ly_tree_create_interfaces_interface_parent_interface(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* parent_interface);
+int interfaces_ly_tree_create_interfaces_interface_forwarding_mode(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* forwarding_mode);
+int interfaces_ly_tree_create_interfaces_interface_max_frame_size(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* max_frame_size);
+int interfaces_ly_tree_create_interfaces_interface_loopback(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* loopback);
+int interfaces_ly_tree_create_interfaces_interface_encapsulation(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, struct lyd_node** encapsulation_node);
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan(const struct ly_ctx* ly_ctx, struct lyd_node* encapsulation_node, struct lyd_node** dot1q_vlan_node);
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan_second_tag(const struct ly_ctx* ly_ctx, struct lyd_node* dot1q_vlan_node, struct lyd_node** second_tag_node);
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan_second_tag_vlan_id(const struct ly_ctx* ly_ctx, struct lyd_node* second_tag_node, const char* vlan_id);
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan_second_tag_tag_type(const struct ly_ctx* ly_ctx, struct lyd_node* second_tag_node, const char* tag_type);
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan_outer_tag(const struct ly_ctx* ly_ctx, struct lyd_node* dot1q_vlan_node, struct lyd_node** outer_tag_node);
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan_outer_tag_vlan_id(const struct ly_ctx* ly_ctx, struct lyd_node* outer_tag_node, const char* vlan_id);
+int interfaces_ly_tree_create_interfaces_interface_encapsulation_dot1q_vlan_outer_tag_tag_type(const struct ly_ctx* ly_ctx, struct lyd_node* outer_tag_node, const char* tag_type);
+int interfaces_ly_tree_create_interfaces_interface_dampening(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, struct lyd_node** dampening_node);
+int interfaces_ly_tree_create_interfaces_interface_dampening_time_remaining(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* time_remaining);
+int interfaces_ly_tree_create_interfaces_interface_dampening_suppressed(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* suppressed);
+int interfaces_ly_tree_create_interfaces_interface_dampening_penalty(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* penalty);
+int interfaces_ly_tree_create_interfaces_interface_dampening_max_suppress_time(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* max_suppress_time);
+int interfaces_ly_tree_create_interfaces_interface_dampening_suppress(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* suppress);
+int interfaces_ly_tree_create_interfaces_interface_dampening_reuse(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* reuse);
+int interfaces_ly_tree_create_interfaces_interface_dampening_half_life(const struct ly_ctx* ly_ctx, struct lyd_node* dampening_node, const char* half_life);
+int interfaces_ly_tree_create_interfaces_interface_carrier_delay(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, struct lyd_node** carrier_delay_node);
+int interfaces_ly_tree_create_interfaces_interface_carrier_delay_timer_running(const struct ly_ctx* ly_ctx, struct lyd_node* carrier_delay_node, const char* timer_running);
+int interfaces_ly_tree_create_interfaces_interface_carrier_delay_carrier_transitions(const struct ly_ctx* ly_ctx, struct lyd_node* carrier_delay_node, const char* carrier_transitions);
+int interfaces_ly_tree_create_interfaces_interface_carrier_delay_up(const struct ly_ctx* ly_ctx, struct lyd_node* carrier_delay_node, const char* up);
+int interfaces_ly_tree_create_interfaces_interface_carrier_delay_down(const struct ly_ctx* ly_ctx, struct lyd_node* carrier_delay_node, const char* down);
+int interfaces_ly_tree_create_interfaces_interface_statistics(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, struct lyd_node** statistics_node);
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_discard_unknown_encaps(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_discard_unknown_encaps);
+int interfaces_ly_tree_create_interfaces_interface_statistics_out_errors(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* out_errors);
+int interfaces_ly_tree_create_interfaces_interface_statistics_out_discards(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* out_discards);
+int interfaces_ly_tree_create_interfaces_interface_statistics_out_multicast_pkts(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* out_multicast_pkts);
+int interfaces_ly_tree_create_interfaces_interface_statistics_out_broadcast_pkts(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* out_broadcast_pkts);
+int interfaces_ly_tree_create_interfaces_interface_statistics_out_unicast_pkts(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* out_unicast_pkts);
+int interfaces_ly_tree_create_interfaces_interface_statistics_out_octets(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* out_octets);
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_unknown_protos(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_unknown_protos);
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_errors(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_errors);
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_discards(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_discards);
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_multicast_pkts(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_multicast_pkts);
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_broadcast_pkts(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_broadcast_pkts);
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_unicast_pkts(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_unicast_pkts);
+int interfaces_ly_tree_create_interfaces_interface_statistics_in_octets(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* in_octets);
+int interfaces_ly_tree_create_interfaces_interface_statistics_discontinuity_time(const struct ly_ctx* ly_ctx, struct lyd_node* statistics_node, const char* discontinuity_time);
+int interfaces_ly_tree_create_interfaces_interface_speed(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* speed);
+int interfaces_ly_tree_create_interfaces_interface_lower_layer_if(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* lower_layer_if);
+int interfaces_ly_tree_create_interfaces_interface_higher_layer_if(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* higher_layer_if);
+int interfaces_ly_tree_create_interfaces_interface_phys_address(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* phys_address);
+int interfaces_ly_tree_create_interfaces_interface_if_index(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* if_index);
+int interfaces_ly_tree_create_interfaces_interface_last_change(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* last_change);
+int interfaces_ly_tree_create_interfaces_interface_oper_status(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* oper_status);
+int interfaces_ly_tree_create_interfaces_interface_admin_status(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* admin_status);
+int interfaces_ly_tree_create_interfaces_interface_link_up_down_trap_enable(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* link_up_down_trap_enable);
+int interfaces_ly_tree_create_interfaces_interface_enabled(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* enabled);
+int interfaces_ly_tree_create_interfaces_interface_type(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* type);
+int interfaces_ly_tree_create_interfaces_interface_description(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* description);
+int interfaces_ly_tree_create_interfaces_interface_name(const struct ly_ctx* ly_ctx, struct lyd_node* interface_node, const char* name);
+
+#endif // INTERFACES_PLUGIN_LY_TREE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/running/load.c b/src/interfaces/src/plugin/running/load.c
new file mode 100644
index 00000000..986b1966
--- /dev/null
+++ b/src/interfaces/src/plugin/running/load.c
@@ -0,0 +1,104 @@
+#include "load.h"
+#include "libyang/printer_data.h"
+#include "plugin/common.h"
+#include "plugin/data/interfaces/interface.h"
+#include "plugin/ly_tree.h"
+
+#include "plugin/api/interfaces/load.h"
+#include "plugin/types.h"
+#include "src/uthash.h"
+#include "src/utlist.h"
+#include "srpc/common.h"
+
+#include
+#include
+#include
+
+static int interfaces_running_load_interface(void* priv, sr_session_ctx_t* session, const struct ly_ctx* ly_ctx, struct lyd_node* parent_node);
+
+int interfaces_running_load(interfaces_ctx_t* ctx, sr_session_ctx_t* session)
+{
+ int error = 0;
+
+ const struct ly_ctx* ly_ctx = NULL;
+ struct lyd_node* root_node = NULL;
+ sr_conn_ctx_t* conn_ctx = NULL;
+
+ srpc_startup_load_t load_values[] = {
+ {
+ "/ietf-interfaces:interfaces/interface",
+ interfaces_running_load_interface,
+ },
+ };
+
+ SRPC_SAFE_CALL_PTR(conn_ctx, sr_session_get_connection(session), error_out);
+ SRPC_SAFE_CALL_PTR(ly_ctx, sr_acquire_context(conn_ctx), error_out);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces(ly_ctx, &root_node), error_out);
+
+ for (size_t i = 0; i < ARRAY_SIZE(load_values); i++) {
+ const srpc_startup_load_t* load = &load_values[i];
+
+ error = load->cb((void*)ctx, session, ly_ctx, root_node);
+ if (error) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Node creation callback failed for value %s", load->name);
+ goto error_out;
+ }
+ }
+
+/* enable or disable storing into startup, use for testing */
+#define INTERFACES_PLUGIN_LOAD_STARTUP
+/* disable for now */
+// #undef INTERFACES_PLUGIN_LOAD_STARTUP
+#ifdef INTERFACES_PLUGIN_LOAD_STARTUP
+ error = sr_edit_batch(session, root_node, "merge");
+ if (error != SR_ERR_OK) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_edit_batch() error (%d): %s", error, sr_strerror(error));
+ goto error_out;
+ }
+
+ error = sr_apply_changes(session, 0);
+ if (error != 0) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_apply_changes() error (%d): %s", error, sr_strerror(error));
+ goto error_out;
+ }
+#endif
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ if (root_node) {
+ lyd_free_tree(root_node);
+ }
+ sr_release_context(conn_ctx);
+
+ return error;
+}
+
+static int interfaces_running_load_interface(void* priv, sr_session_ctx_t* session, const struct ly_ctx* ly_ctx, struct lyd_node* parent_node)
+{
+ int error = 0;
+ interfaces_ctx_t* ctx = (interfaces_ctx_t*)priv;
+ interfaces_interface_hash_element_t* interface_hash = NULL;
+
+ // load interfaces data
+ SRPC_SAFE_CALL_ERR(error, interfaces_load_interface(ctx, &interface_hash), error_out);
+
+ // convert to libyang
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_to_ly(ly_ctx, interface_hash, &parent_node), error_out);
+
+ // print created tree
+ // lyd_print_file(stdout, parent_node, LYD_XML, 0);
+
+ goto out;
+
+error_out:
+ error = -1;
+out:
+ interfaces_interface_hash_free(&interface_hash);
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/running/load.h b/src/interfaces/src/plugin/running/load.h
new file mode 100644
index 00000000..b17d10aa
--- /dev/null
+++ b/src/interfaces/src/plugin/running/load.h
@@ -0,0 +1,8 @@
+#ifndef INTERFACES_PLUGIN_RUNNING_LOAD_H
+#define INTERFACES_PLUGIN_RUNNING_LOAD_H
+
+#include "plugin/context.h"
+
+int interfaces_running_load(interfaces_ctx_t* ctx, sr_session_ctx_t* session);
+
+#endif // INTERFACES_PLUGIN_RUNNING_LOAD_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/running/store.c b/src/interfaces/src/plugin/running/store.c
new file mode 100644
index 00000000..45e69cd0
--- /dev/null
+++ b/src/interfaces/src/plugin/running/store.c
@@ -0,0 +1,103 @@
+#include "store.h"
+#include "plugin/common.h"
+
+#include "plugin/api/interfaces/check.h"
+#include "plugin/api/interfaces/store.h"
+#include "plugin/data/interfaces/interface.h"
+#include "srpc/ly_tree.h"
+
+#include
+#include
+#include
+
+static int interfaces_running_store_interface(void* priv, const struct lyd_node* parent_container);
+
+int interfaces_running_store(interfaces_ctx_t* ctx, sr_session_ctx_t* session)
+{
+ int error = 0;
+ sr_data_t* subtree = NULL;
+
+ SRPC_SAFE_CALL_ERR(error, sr_get_subtree(session, INTERFACES_INTERFACES_CONTAINER_YANG_PATH, 0, &subtree), error_out);
+
+ srpc_startup_store_t store_values[] = {
+ {
+ "/ietf-interfaces:interfaces/interface[name='%s']",
+ interfaces_running_store_interface,
+ },
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(store_values); i++) {
+ const srpc_startup_store_t* store = &store_values[i];
+
+ error = store->cb(ctx, subtree->tree);
+ if (error != 0) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Startup store callback failed for value %s", store->name);
+ goto error_out;
+ }
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ if (subtree) {
+ sr_release_data(subtree);
+ }
+
+ return error;
+}
+
+static int interfaces_running_store_interface(void* priv, const struct lyd_node* parent_container)
+{
+ int error = 0;
+ interfaces_ctx_t* ctx = (interfaces_ctx_t*)priv;
+ srpc_check_status_t check_status = srpc_check_status_none;
+ interfaces_interface_hash_element_t* if_hash = NULL;
+ struct lyd_node* interface_node = NULL;
+
+ interface_node = srpc_ly_tree_get_child_list(parent_container, "interface");
+ if (interface_node == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "srpc_ly_tree_get_child_leaf returned NULL for 'interfaces'");
+ goto error_out;
+ }
+
+ // map libyang data to the interface hash
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_from_ly(&if_hash, interface_node), error_out);
+
+ // check startup data
+ SRPLG_LOG_INF(PLUGIN_NAME, "Checking interface list data");
+ check_status = interfaces_check_interface(ctx, if_hash);
+
+ switch (check_status) {
+ case srpc_check_status_none:
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Error loading current interface list");
+ goto error_out;
+ case srpc_check_status_error:
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Error loading current interface list");
+ goto error_out;
+ case srpc_check_status_non_existant:
+ SRPLG_LOG_INF(PLUGIN_NAME, "Storing interface list");
+ SRPC_SAFE_CALL_ERR(error, interfaces_store_interface(ctx, if_hash), error_out);
+ break;
+ case srpc_check_status_equal:
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Startup interface list is already applied on the system");
+ break;
+ case srpc_check_status_partial:
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Error loading current interface list");
+ goto error_out;
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ if (if_hash) {
+ interfaces_interface_hash_free(&if_hash);
+ }
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/running/store.h b/src/interfaces/src/plugin/running/store.h
new file mode 100644
index 00000000..a2a9cece
--- /dev/null
+++ b/src/interfaces/src/plugin/running/store.h
@@ -0,0 +1,8 @@
+#ifndef INTERFACES_PLUGIN_RUNNING_STORE_H
+#define INTERFACES_PLUGIN_RUNNING_STORE_H
+
+#include "plugin/context.h"
+
+int interfaces_running_store(interfaces_ctx_t* ctx, sr_session_ctx_t* session);
+
+#endif // INTERFACES_PLUGIN_RUNNING_STORE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/startup/load.c b/src/interfaces/src/plugin/startup/load.c
new file mode 100644
index 00000000..ede59c6c
--- /dev/null
+++ b/src/interfaces/src/plugin/startup/load.c
@@ -0,0 +1,104 @@
+#include "load.h"
+#include "libyang/printer_data.h"
+#include "plugin/common.h"
+#include "plugin/data/interfaces/interface.h"
+#include "plugin/ly_tree.h"
+
+#include "plugin/api/interfaces/load.h"
+#include "plugin/types.h"
+#include "src/uthash.h"
+#include "src/utlist.h"
+#include "srpc/common.h"
+
+#include
+#include
+#include
+
+static int interfaces_startup_load_interface(void* priv, sr_session_ctx_t* session, const struct ly_ctx* ly_ctx, struct lyd_node* parent_node);
+
+int interfaces_startup_load(interfaces_ctx_t* ctx, sr_session_ctx_t* session)
+{
+ int error = 0;
+
+ const struct ly_ctx* ly_ctx = NULL;
+ struct lyd_node* root_node = NULL;
+ sr_conn_ctx_t* conn_ctx = NULL;
+
+ srpc_startup_load_t load_values[] = {
+ {
+ "/ietf-interfaces:interfaces/interface",
+ interfaces_startup_load_interface,
+ },
+ };
+
+ SRPC_SAFE_CALL_PTR(conn_ctx, sr_session_get_connection(session), error_out);
+ SRPC_SAFE_CALL_PTR(ly_ctx, sr_acquire_context(conn_ctx), error_out);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces(ly_ctx, &root_node), error_out);
+
+ for (size_t i = 0; i < ARRAY_SIZE(load_values); i++) {
+ const srpc_startup_load_t* load = &load_values[i];
+
+ error = load->cb((void*)ctx, session, ly_ctx, root_node);
+ if (error) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Node creation callback failed for value %s", load->name);
+ goto error_out;
+ }
+ }
+
+/* enable or disable storing into startup, use for testing */
+#define INTERFACES_PLUGIN_LOAD_STARTUP
+/* disable for now */
+// #undef INTERFACES_PLUGIN_LOAD_STARTUP
+#ifdef INTERFACES_PLUGIN_LOAD_STARTUP
+ error = sr_edit_batch(session, root_node, "merge");
+ if (error != SR_ERR_OK) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_edit_batch() error (%d): %s", error, sr_strerror(error));
+ goto error_out;
+ }
+
+ error = sr_apply_changes(session, 0);
+ if (error != 0) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_apply_changes() error (%d): %s", error, sr_strerror(error));
+ goto error_out;
+ }
+#endif
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ if (root_node) {
+ lyd_free_tree(root_node);
+ }
+ sr_release_context(conn_ctx);
+
+ return error;
+}
+
+static int interfaces_startup_load_interface(void* priv, sr_session_ctx_t* session, const struct ly_ctx* ly_ctx, struct lyd_node* parent_node)
+{
+ int error = 0;
+ interfaces_ctx_t* ctx = (interfaces_ctx_t*)priv;
+ interfaces_interface_hash_element_t* interface_hash = NULL;
+
+ // load interfaces data
+ SRPC_SAFE_CALL_ERR(error, interfaces_load_interface(ctx, &interface_hash), error_out);
+
+ // convert to libyang
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_to_ly(ly_ctx, interface_hash, &parent_node), error_out);
+
+ // print created tree
+ // lyd_print_file(stdout, parent_node, LYD_XML, 0);
+
+ goto out;
+
+error_out:
+ error = -1;
+out:
+ interfaces_interface_hash_free(&interface_hash);
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/startup/load.h b/src/interfaces/src/plugin/startup/load.h
new file mode 100644
index 00000000..89f3a89c
--- /dev/null
+++ b/src/interfaces/src/plugin/startup/load.h
@@ -0,0 +1,8 @@
+#ifndef INTERFACES_PLUGIN_STARTUP_LOAD_H
+#define INTERFACES_PLUGIN_STARTUP_LOAD_H
+
+#include "plugin/context.h"
+
+int interfaces_startup_load(interfaces_ctx_t* ctx, sr_session_ctx_t* session);
+
+#endif // INTERFACES_PLUGIN_STARTUP_LOAD_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/startup/store.c b/src/interfaces/src/plugin/startup/store.c
new file mode 100644
index 00000000..4dc91851
--- /dev/null
+++ b/src/interfaces/src/plugin/startup/store.c
@@ -0,0 +1,105 @@
+#include "store.h"
+#include "plugin/common.h"
+
+#include "plugin/api/interfaces/check.h"
+#include "plugin/api/interfaces/store.h"
+#include "plugin/data/interfaces/interface.h"
+#include "srpc/ly_tree.h"
+
+#include
+#include
+#include
+
+static int interfaces_startup_store_interface(void* priv, const struct lyd_node* parent_container);
+
+int interfaces_startup_store(interfaces_ctx_t* ctx, sr_session_ctx_t* session)
+{
+ int error = 0;
+ sr_data_t* subtree = NULL;
+
+ SRPC_SAFE_CALL_ERR(error, sr_get_subtree(session, INTERFACES_INTERFACES_CONTAINER_YANG_PATH, 0, &subtree), error_out);
+
+ srpc_startup_store_t store_values[] = {
+ {
+ "/ietf-interfaces:interfaces/interface[name='%s']",
+ interfaces_startup_store_interface,
+ },
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(store_values); i++) {
+ const srpc_startup_store_t* store = &store_values[i];
+
+ error = store->cb(ctx, subtree->tree);
+ if (error != 0) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Startup store callback failed for value %s", store->name);
+ goto error_out;
+ }
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ if (subtree) {
+ sr_release_data(subtree);
+ }
+
+ return error;
+}
+
+static int interfaces_startup_store_interface(void* priv, const struct lyd_node* parent_container)
+{
+ int error = 0;
+ interfaces_ctx_t* ctx = (interfaces_ctx_t*)priv;
+ srpc_check_status_t check_status = srpc_check_status_none;
+ interfaces_interface_hash_element_t* if_hash = NULL;
+ struct lyd_node* interface_node = NULL;
+
+ interface_node = srpc_ly_tree_get_child_list(parent_container, "interface");
+ if (interface_node == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "srpc_ly_tree_get_child_leaf returned NULL for 'interfaces'");
+ goto error_out;
+ }
+
+ // map libyang data to the interface hash
+ SRPC_SAFE_CALL_ERR(error, interfaces_interface_hash_from_ly(&if_hash, interface_node), error_out);
+
+ interfaces_interface_hash_print_debug(if_hash);
+
+ // check startup data
+ SRPLG_LOG_INF(PLUGIN_NAME, "Checking interface list data");
+ check_status = interfaces_check_interface(ctx, if_hash);
+
+ switch (check_status) {
+ case srpc_check_status_none:
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Error loading current interface list");
+ goto error_out;
+ case srpc_check_status_error:
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Error loading current interface list");
+ goto error_out;
+ case srpc_check_status_non_existant:
+ SRPLG_LOG_INF(PLUGIN_NAME, "Storing interface list");
+ SRPC_SAFE_CALL_ERR(error, interfaces_store_interface(ctx, if_hash), error_out);
+ break;
+ case srpc_check_status_equal:
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Startup interface list is already applied on the system");
+ break;
+ case srpc_check_status_partial:
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Error loading current interface list");
+ goto error_out;
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ if (if_hash) {
+ interfaces_interface_hash_free(&if_hash);
+ }
+
+ return error;
+}
diff --git a/src/interfaces/src/plugin/startup/store.h b/src/interfaces/src/plugin/startup/store.h
new file mode 100644
index 00000000..7e5a9a65
--- /dev/null
+++ b/src/interfaces/src/plugin/startup/store.h
@@ -0,0 +1,8 @@
+#ifndef INTERFACES_PLUGIN_STARTUP_STORE_H
+#define INTERFACES_PLUGIN_STARTUP_STORE_H
+
+#include "plugin/context.h"
+
+int interfaces_startup_store(interfaces_ctx_t* ctx, sr_session_ctx_t* session);
+
+#endif // INTERFACES_PLUGIN_STARTUP_STORE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/subscription/change.c b/src/interfaces/src/plugin/subscription/change.c
new file mode 100644
index 00000000..f3312256
--- /dev/null
+++ b/src/interfaces/src/plugin/subscription/change.c
@@ -0,0 +1,102 @@
+#include "change.h"
+#include "plugin/common.h"
+#include "plugin/context.h"
+
+#include
+#include
+#include
+#include
+
+// change API
+#include "plugin/api/interfaces/change.h"
+#include "plugin/api/interfaces/interface/change.h"
+#include "plugin/api/interfaces/interface/ipv4/address/change.h"
+#include "plugin/api/interfaces/interface/ipv4/change.h"
+#include "plugin/api/interfaces/interface/ipv4/neighbor/change.h"
+#include "plugin/api/interfaces/interface/ipv6/address/change.h"
+#include "plugin/api/interfaces/interface/ipv6/neighbor/change.h"
+
+int interfaces_subscription_change_interfaces_interface(sr_session_ctx_t* session, uint32_t subscription_id, const char* module_name, const char* xpath, sr_event_t event, uint32_t request_id, void* private_data)
+{
+ int error = SR_ERR_OK;
+ interfaces_ctx_t* ctx = (interfaces_ctx_t*)private_data;
+ char change_xpath_buffer[PATH_MAX] = { 0 };
+ int rc = 0;
+
+ if (event == SR_EV_ABORT) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "Aborting changes for %s", xpath);
+ goto error_out;
+ } else if (event == SR_EV_CHANGE) {
+ // name
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/name", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_change_name, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // description
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/description", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_change_description, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // type
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/type", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_change_type, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // enabled
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/enabled", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_change_enabled, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // link-up-down-trap-enable
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/link-up-down-trap-enable", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_change_link_up_down_trap_enable, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // ipv4/mtu
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv4/mtu", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv4_change_mtu, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // ipv4/enabled
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv4/enabled", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv4_change_enabled, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // ipv4/address/ip
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv4/address/ip", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv4_address_change_ip, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // ipv4/address/prefix-length
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv4/address/prefix-length", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv4_address_change_prefix_length, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // ipv4/address/netmask
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv4/address/netmask", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv4_address_change_netmask, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // ipv4/neighbor/ip
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv4/neighbor/ip", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv4_neighbor_change_ip, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // ipv4/neighbor/link-layer-address
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv4/neighbor/link-layer-address", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv4_neighbor_change_link_layer_address, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // ipv6/address/ip
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv6/address/ip", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv6_address_change_ip, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // ipv6/address/prefix-length
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv6/address/prefix-length", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv6_address_change_prefix_length, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // ipv6/neighbor/ip
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv6/neighbor/ip", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv6_neighbor_change_ip, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+
+ // ipv6/neighbor/link-layer-address
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(change_xpath_buffer, sizeof(change_xpath_buffer), "%s/ipv6/neighbor/link-layer-address", xpath), error_out);
+ SRPC_SAFE_CALL_ERR(rc, srpc_iterate_changes(ctx, session, change_xpath_buffer, interfaces_interface_ipv6_neighbor_change_link_layer_address, interfaces_change_interface_init, interfaces_change_interface_free), error_out);
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
diff --git a/src/interfaces/src/plugin/subscription/change.h b/src/interfaces/src/plugin/subscription/change.h
new file mode 100644
index 00000000..bfefa610
--- /dev/null
+++ b/src/interfaces/src/plugin/subscription/change.h
@@ -0,0 +1,8 @@
+#ifndef INTERFACES_PLUGIN_SUBSCRIPTION_CHANGE_H
+#define INTERFACES_PLUGIN_SUBSCRIPTION_CHANGE_H
+
+#include
+
+int interfaces_subscription_change_interfaces_interface(sr_session_ctx_t* session, uint32_t subscription_id, const char* module_name, const char* xpath, sr_event_t event, uint32_t request_id, void* private_data);
+
+#endif // INTERFACES_PLUGIN_SUBSCRIPTION_CHANGE_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/subscription/operational.c b/src/interfaces/src/plugin/subscription/operational.c
new file mode 100644
index 00000000..843b1f04
--- /dev/null
+++ b/src/interfaces/src/plugin/subscription/operational.c
@@ -0,0 +1,2310 @@
+#include "operational.h"
+#include "libyang/context.h"
+#include "libyang/tree_data.h"
+#include "netlink/addr.h"
+#include "netlink/cache.h"
+#include "netlink/object.h"
+#include "netlink/route/neighbour.h"
+#include "netlink/route/tc.h"
+#include "netlink/socket.h"
+#include "plugin/common.h"
+#include "plugin/context.h"
+#include "plugin/data/interfaces/interface_state.h"
+#include "plugin/ly_tree.h"
+#include "plugin/types.h"
+#include "srpc/common.h"
+#include "sysrepo_types.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+static struct rtnl_link* interfaces_get_current_link(interfaces_ctx_t* ctx, sr_session_ctx_t* session, const char* xpath);
+static int interfaces_extract_interface_name(sr_session_ctx_t* session, const char* xpath, char* buffer, size_t buffer_size);
+static int interfaces_extract_interface_address_ip(sr_session_ctx_t* session, const char* xpath, char* buffer, size_t buffer_size);
+static int interfaces_extract_interface_neighbor_ip(sr_session_ctx_t* session, const char* xpath, char* buffer, size_t buffer_size);
+static int interfaces_get_system_boot_time(char* buffer, size_t buffer_size);
+
+int interfaces_subscription_operational_interfaces_interface_admin_status(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ enum {
+ admin_status_none = 0,
+ admin_status_up,
+ admin_status_down,
+ admin_status_testing,
+ } admin_status
+ = admin_status_none;
+
+ const char* adminstate_map[] = {
+ [admin_status_none] = "none",
+ [admin_status_up] = "up",
+ [admin_status_down] = "down",
+ [admin_status_testing] = "testing",
+ };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "interface") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // get admin status
+ const unsigned int flags = rtnl_link_get_flags(link);
+
+ if ((flags & IFF_UP) || (flags & IFF_RUNNING)) {
+ admin_status = admin_status_up;
+ } else {
+ admin_status = admin_status_down;
+ }
+
+ if (admin_status != admin_status_none) {
+ const char* admin_status_str = adminstate_map[admin_status];
+ SRPLG_LOG_INF(PLUGIN_NAME, "oper-status(%s) = %s", rtnl_link_get_name(link), admin_status_str);
+
+ // add admin-status node
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_admin_status(ly_ctx, *parent, admin_status_str), error_out);
+ } else {
+ SRPLG_LOG_INF(PLUGIN_NAME, "Unable to determine admin-status for link %s", rtnl_link_get_name(link));
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_oper_status(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ const char* operstate_map[] = {
+ [IF_OPER_UNKNOWN] = "unknown",
+ [IF_OPER_NOTPRESENT] = "not-present",
+ [IF_OPER_DOWN] = "down",
+ [IF_OPER_LOWERLAYERDOWN] = "lower-layer-down",
+ [IF_OPER_TESTING] = "testing",
+ [IF_OPER_DORMANT] = "dormant",
+ [IF_OPER_UP] = "up",
+ };
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "interface") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // get oper status
+ const uint8_t oper_status = rtnl_link_get_operstate(link);
+ const char* oper_status_str = operstate_map[oper_status];
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "oper-status(%s) = %s", rtnl_link_get_name(link), oper_status_str);
+
+ // add oper-status node
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_oper_status(ly_ctx, *parent, oper_status_str), error_out);
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_last_change(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_state_changes_ctx_t* state_ctx = &ctx->oper_ctx.state_changes_ctx;
+ interfaces_interface_state_hash_element_t* state_element = NULL;
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // buffers
+ char last_change_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "interface") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // synchronization
+ pthread_mutex_lock(&state_ctx->state_hash_mutex);
+
+ // get last change
+ SRPC_SAFE_CALL_PTR(state_element, interfaces_interface_state_hash_get(state_ctx->state_hash, rtnl_link_get_name(link)), error_out);
+
+ const time_t last_change = state_element->state.last_change;
+ struct tm* last_change_tm = localtime(&last_change);
+
+ size_t written = strftime(last_change_buffer, sizeof(last_change_buffer), "%FT%TZ", last_change_tm);
+ if (written == 0) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "strftime() failed");
+ goto error_out;
+ }
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "last-change(%s) = %s", rtnl_link_get_name(link), last_change_buffer);
+
+ // add oper-status node
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_last_change(ly_ctx, *parent, last_change_buffer), error_out);
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ pthread_mutex_unlock(&state_ctx->state_hash_mutex);
+
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_if_index(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char ifindex_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "interface") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // get if-index
+ const int ifindex = rtnl_link_get_ifindex(link);
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(ifindex_buffer, sizeof(ifindex_buffer), "%d", ifindex), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "if-index(%s) = %s", rtnl_link_get_name(link), ifindex_buffer);
+
+ // add ifindex node
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_if_index(ly_ctx, *parent, ifindex_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_phys_address(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char phys_address_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+ struct nl_addr* addr = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "interface") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // get phys-address
+ SRPC_SAFE_CALL_PTR(addr, rtnl_link_get_addr(link), error_out);
+ SRPC_SAFE_CALL_PTR(error_ptr, nl_addr2str(addr, phys_address_buffer, sizeof(phys_address_buffer)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "phys-address(%s) = %s", rtnl_link_get_name(link), phys_address_buffer);
+
+ // add phys-address node
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_phys_address(ly_ctx, *parent, phys_address_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_higher_layer_if(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_nl_ctx_t* nl_ctx = &ctx->oper_ctx.nl_ctx;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+ struct rtnl_link* master_link = NULL;
+
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "interface") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ int master_if_index = rtnl_link_get_master(link);
+ while (master_if_index) {
+ master_link = rtnl_link_get(nl_ctx->link_cache, master_if_index);
+ const char* master_name = rtnl_link_get_name(master_link);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "higher-layer-if(%s) = %s", rtnl_link_get_name(link), master_name);
+
+ // add higher-layer-if
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_higher_layer_if(ly_ctx, *parent, master_name), error_out);
+
+ // go one layer higher
+ master_if_index = rtnl_link_get_master(master_link);
+ }
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_lower_layer_if(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_nl_ctx_t* nl_ctx = &ctx->oper_ctx.nl_ctx;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+ struct rtnl_link* master_link = NULL;
+
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "interface") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // iterate over all links and check for ones which have a master equal to the current link
+ struct nl_cache* link_cache = nl_ctx->link_cache;
+ struct rtnl_link* link_iter = (struct rtnl_link*)nl_cache_get_first(link_cache);
+
+ while (link_iter) {
+ int master_if_index = rtnl_link_get_master(link);
+ while (master_if_index) {
+ master_link = rtnl_link_get(nl_ctx->link_cache, master_if_index);
+ const char* master_name = rtnl_link_get_name(master_link);
+
+ if (!strcmp(master_name, rtnl_link_get_name(link))) {
+ // current link is the higher layer interface of the iterated link
+ SRPLG_LOG_INF(PLUGIN_NAME, "lower-layer-if(%s) = %s", rtnl_link_get_name(link), rtnl_link_get_name(link_iter));
+
+ // add lower-layer-if
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_lower_layer_if(ly_ctx, *parent, rtnl_link_get_name(link_iter)), error_out);
+
+ // found in the master list - continue checking other interfaces
+ break;
+ }
+
+ // go one layer higher
+ master_if_index = rtnl_link_get_master(master_link);
+ }
+
+ link_iter = (struct rtnl_link*)nl_cache_get_next((struct nl_object*)link_iter);
+ }
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_speed(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char speed_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+ struct rtnl_qdisc* qdisc = NULL;
+ struct rtnl_tc* tc = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "interface") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ qdisc = rtnl_qdisc_alloc();
+
+ // setup traffic control
+ tc = TC_CAST(qdisc);
+ rtnl_tc_set_link(tc, link);
+
+ // get speed
+ const uint64_t speed = rtnl_tc_get_stat(tc, RTNL_TC_RATE_BPS);
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(speed_buffer, sizeof(speed_buffer), "%lu", speed), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "speed(%s) = %s", rtnl_link_get_name(link), speed_buffer);
+
+ // add phys-address node
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_speed(ly_ctx, *parent, speed_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ rtnl_qdisc_put(qdisc);
+
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_discontinuity_time(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char discontinuity_time_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // get boot time as discontinuity time
+ SRPC_SAFE_CALL_ERR(error, interfaces_get_system_boot_time(discontinuity_time_buffer, sizeof(discontinuity_time_buffer)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "discontinuity-time(%s) = %s", rtnl_link_get_name(link), discontinuity_time_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_discontinuity_time(ly_ctx, *parent, discontinuity_time_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_in_octets(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char in_octets_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ const uint64_t in_octets = rtnl_link_get_stat(link, RTNL_LINK_RX_BYTES);
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(in_octets_buffer, sizeof(in_octets_buffer), "%lu", in_octets), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "in-octets(%s) = %s", rtnl_link_get_name(link), in_octets_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_in_octets(ly_ctx, *parent, in_octets_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_in_unicast_pkts(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char in_unicast_pkts_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ const uint64_t in_pkts = rtnl_link_get_stat(link, RTNL_LINK_RX_PACKETS);
+ const uint64_t in_broadcast_pkts = rtnl_link_get_stat(link, RTNL_LINK_IP6_INBCASTPKTS);
+ const uint64_t in_multicast_pkts = rtnl_link_get_stat(link, RTNL_LINK_IP6_INMCASTPKTS);
+ const uint64_t in_unicast_pkts = in_pkts - in_broadcast_pkts - in_multicast_pkts;
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(in_unicast_pkts_buffer, sizeof(in_unicast_pkts_buffer), "%lu", in_unicast_pkts), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "in-unicast-pkts(%s) = %s", rtnl_link_get_name(link), in_unicast_pkts_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_in_unicast_pkts(ly_ctx, *parent, in_unicast_pkts_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_in_broadcast_pkts(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char in_broadcast_pkts_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ const uint64_t in_broadcast_pkts = rtnl_link_get_stat(link, RTNL_LINK_IP6_INBCASTPKTS);
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(in_broadcast_pkts_buffer, sizeof(in_broadcast_pkts_buffer), "%lu", in_broadcast_pkts), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "in-broadcast-pkts(%s) = %s", rtnl_link_get_name(link), in_broadcast_pkts_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_in_broadcast_pkts(ly_ctx, *parent, in_broadcast_pkts_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_in_multicast_pkts(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char in_multicast_pkts_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ const uint64_t in_multicast_pkts = rtnl_link_get_stat(link, RTNL_LINK_IP6_INMCASTPKTS);
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(in_multicast_pkts_buffer, sizeof(in_multicast_pkts_buffer), "%lu", in_multicast_pkts), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "in-multicast-pkts(%s) = %s", rtnl_link_get_name(link), in_multicast_pkts_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_in_multicast_pkts(ly_ctx, *parent, in_multicast_pkts_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_in_discards(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char in_discards_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ const uint32_t in_discards = (uint32_t)rtnl_link_get_stat(link, RTNL_LINK_RX_DROPPED);
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(in_discards_buffer, sizeof(in_discards_buffer), "%u", in_discards), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "in-discards(%s) = %s", rtnl_link_get_name(link), in_discards_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_in_discards(ly_ctx, *parent, in_discards_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_in_errors(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char in_errors_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ const uint32_t in_errors = (uint32_t)rtnl_link_get_stat(link, RTNL_LINK_RX_ERRORS);
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(in_errors_buffer, sizeof(in_errors_buffer), "%u", in_errors), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "in-errors(%s) = %s", rtnl_link_get_name(link), in_errors_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_in_errors(ly_ctx, *parent, in_errors_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_in_unknown_protos(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char in_unknown_protos_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ const uint32_t in_unknown_protos = (uint32_t)rtnl_link_get_stat(link, RTNL_LINK_IP6_INUNKNOWNPROTOS);
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(in_unknown_protos_buffer, sizeof(in_unknown_protos_buffer), "%u", in_unknown_protos), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "in-unknown-protos(%s) = %s", rtnl_link_get_name(link), in_unknown_protos_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_in_unknown_protos(ly_ctx, *parent, in_unknown_protos_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_out_octets(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char out_octets_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ const uint64_t out_octets = rtnl_link_get_stat(link, RTNL_LINK_TX_BYTES);
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(out_octets_buffer, sizeof(out_octets_buffer), "%lu", out_octets), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "out-octets(%s) = %s", rtnl_link_get_name(link), out_octets_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_out_octets(ly_ctx, *parent, out_octets_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_out_unicast_pkts(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char out_unicast_pkts_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ const uint64_t out_pkts = rtnl_link_get_stat(link, RTNL_LINK_TX_PACKETS);
+ const uint64_t out_broadcast_pkts = rtnl_link_get_stat(link, RTNL_LINK_IP6_OUTBCASTPKTS);
+ const uint64_t out_multicast_pkts = rtnl_link_get_stat(link, RTNL_LINK_IP6_OUTMCASTPKTS);
+ const uint64_t out_unicast_pkts = out_pkts - out_broadcast_pkts - out_multicast_pkts;
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(out_unicast_pkts_buffer, sizeof(out_unicast_pkts_buffer), "%lu", out_unicast_pkts), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "out-unicast-pkts(%s) = %s", rtnl_link_get_name(link), out_unicast_pkts_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_out_unicast_pkts(ly_ctx, *parent, out_unicast_pkts_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_out_broadcast_pkts(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char out_broadcast_pkts_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ const uint64_t out_broadcast_pkts = rtnl_link_get_stat(link, RTNL_LINK_IP6_OUTBCASTPKTS);
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(out_broadcast_pkts_buffer, sizeof(out_broadcast_pkts_buffer), "%lu", out_broadcast_pkts), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "out-broadcast-pkts(%s) = %s", rtnl_link_get_name(link), out_broadcast_pkts_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_out_broadcast_pkts(ly_ctx, *parent, out_broadcast_pkts_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_out_multicast_pkts(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char out_multicast_pkts_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ const uint64_t out_multicast_pkts = rtnl_link_get_stat(link, RTNL_LINK_IP6_OUTMCASTPKTS);
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(out_multicast_pkts_buffer, sizeof(out_multicast_pkts_buffer), "%lu", out_multicast_pkts), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "out-multicast-pkts(%s) = %s", rtnl_link_get_name(link), out_multicast_pkts_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_out_multicast_pkts(ly_ctx, *parent, out_multicast_pkts_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_out_discards(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char out_discards_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ const uint64_t out_discards = rtnl_link_get_stat(link, RTNL_LINK_TX_DROPPED);
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(out_discards_buffer, sizeof(out_discards_buffer), "%lu", out_discards), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "out-discards(%s) = %s", rtnl_link_get_name(link), out_discards_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_out_discards(ly_ctx, *parent, out_discards_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_out_errors(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ // context
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+
+ // buffers
+ char out_errors_buffer[100] = { 0 };
+ char xpath_buffer[PATH_MAX] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "statistics") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ const uint64_t out_errors = rtnl_link_get_stat(link, RTNL_LINK_TX_DROPPED);
+
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(out_errors_buffer, sizeof(out_errors_buffer), "%lu", out_errors), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "out-errors(%s) = %s", rtnl_link_get_name(link), out_errors_buffer);
+
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics_out_errors(ly_ctx, *parent, out_errors_buffer), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_statistics_in_discard_unknown_encaps(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ // void* error_ptr = NULL;
+ const struct ly_ctx* ly_ctx = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_carrier_delay_carrier_transitions(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ // void* error_ptr = NULL;
+ const struct ly_ctx* ly_ctx = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_carrier_delay_timer_running(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ // void* error_ptr = NULL;
+ const struct ly_ctx* ly_ctx = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_dampening_penalty(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ // void* error_ptr = NULL;
+ const struct ly_ctx* ly_ctx = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_dampening_suppressed(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ // void* error_ptr = NULL;
+ const struct ly_ctx* ly_ctx = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_dampening_time_remaining(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ // void* error_ptr = NULL;
+ const struct ly_ctx* ly_ctx = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_forwarding_mode(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ // void* error_ptr = NULL;
+ const struct ly_ctx* ly_ctx = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_ipv4_address_origin(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ int rc = 0;
+ void* error_ptr = NULL;
+
+ sr_session_ctx_t* running_session = NULL;
+ sr_conn_ctx_t* connection = NULL;
+
+ sr_val_t* prefix_length_val = NULL;
+
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_oper_ctx_t* oper_ctx = &ctx->oper_ctx;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+ char ip_buffer[20] = { 0 };
+ char address_buffer[100] = { 0 };
+
+ char prefix_path_buffer[PATH_MAX] = { 0 };
+
+ const struct ly_ctx* ly_ctx = NULL;
+
+ struct rtnl_link* link = NULL;
+ struct nl_addr* local = NULL;
+ struct rtnl_addr* addr = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ // connect to the operational DS
+ SRPC_SAFE_CALL_PTR(connection, sr_session_get_connection(session), error_out);
+ SRPC_SAFE_CALL_ERR(error, sr_session_start(connection, SR_DS_RUNNING, &running_session), error_out);
+
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "address") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // get IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_extract_interface_address_ip(session, xpath_buffer, ip_buffer, sizeof(ip_buffer)), error_out);
+
+ // get prefix length from the operational DS
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(prefix_path_buffer, sizeof(prefix_path_buffer), "/ietf-interfaces:interfaces/interface[name=\"%s\"]/ietf-ip:ipv4/address[ip=\"%s\"]/prefix-length", rtnl_link_get_name(link), ip_buffer), error_out);
+ SRPC_SAFE_CALL_ERR(error, sr_get_item(running_session, prefix_path_buffer, 0, &prefix_length_val), error_out);
+
+ // create an address
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(address_buffer, sizeof(address_buffer), "%s/%d", ip_buffer, prefix_length_val->data.uint8_val), error_out);
+
+ // parse address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(address_buffer, AF_INET, &local), error_out);
+
+ // get rtnl address
+ SRPC_SAFE_CALL_PTR(addr, rtnl_addr_get(oper_ctx->nl_ctx.addr_cache, rtnl_link_get_ifindex(link), local), error_out);
+
+ // get address origin - static or dynamic
+ const char* origin = (rtnl_addr_get_flags(addr) & IFA_F_PERMANENT) > 0 ? "static" : "dhcp";
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "origin(interface[%s]:address[%s]) = %s", rtnl_link_get_name(link), ip_buffer, origin);
+
+ // add origin
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_address_origin(ly_ctx, *parent, origin), error_out);
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ if (running_session) {
+ sr_session_stop(running_session);
+ }
+
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_ipv4_address(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ int rc = 0;
+ void* error_ptr = NULL;
+
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_oper_ctx_t* oper_ctx = &ctx->oper_ctx;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+ char ip_buffer[20] = { 0 };
+ char prefix_buffer[20] = { 0 };
+
+ const struct ly_ctx* ly_ctx = NULL;
+ struct lyd_node* address_node = NULL;
+
+ struct rtnl_link* link = NULL;
+ struct rtnl_addr* addr_iter = NULL;
+ struct nl_addr* local = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "ipv4") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ addr_iter = (struct rtnl_addr*)nl_cache_get_first(oper_ctx->nl_ctx.addr_cache);
+
+ while (addr_iter) {
+ if (rtnl_addr_get_ifindex(addr_iter) == rtnl_link_get_ifindex(link) && rtnl_addr_get_family(addr_iter) == AF_INET) {
+ SRPLG_LOG_INF(PLUGIN_NAME, "Found IPv4 address for %s", rtnl_link_get_name(link));
+
+ // IP
+ SRPC_SAFE_CALL_PTR(local, rtnl_addr_get_local(addr_iter), error_out);
+ SRPC_SAFE_CALL_PTR(error_ptr, nl_addr2str(local, ip_buffer, sizeof(ip_buffer)), error_out);
+
+ // remove prefix from IP
+ char* prefix = strchr(ip_buffer, '/');
+ if (prefix) {
+ *prefix = 0;
+ }
+
+ // prefix
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(prefix_buffer, sizeof(prefix_buffer), "%d", rtnl_addr_get_prefixlen(addr_iter)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "ipv4:address(%s) = %s/%s", rtnl_link_get_name(link), ip_buffer, prefix_buffer);
+
+ // address from the current link - add to the list
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_address(ly_ctx, *parent, &address_node, ip_buffer), error_out);
+
+ // prefix
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_address_prefix_length(ly_ctx, address_node, prefix_buffer), error_out);
+ }
+
+ addr_iter = (struct rtnl_addr*)nl_cache_get_next((struct nl_object*)addr_iter);
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_ipv4_neighbor_origin(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_oper_ctx_t* oper_ctx = &ctx->oper_ctx;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+ char ip_buffer[20] = { 0 };
+
+ const struct ly_ctx* ly_ctx = NULL;
+
+ struct rtnl_link* link = NULL;
+ struct rtnl_neigh* neigh = NULL;
+ struct nl_addr* neigh_addr = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "neighbor") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // get IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_extract_interface_neighbor_ip(session, xpath_buffer, ip_buffer, sizeof(ip_buffer)), error_out);
+
+ // parse address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(ip_buffer, AF_INET, &neigh_addr), error_out);
+
+ // get neighbor
+ SRPC_SAFE_CALL_PTR(neigh, rtnl_neigh_get(oper_ctx->nl_ctx.neigh_cache, rtnl_link_get_ifindex(link), neigh_addr), error_out);
+
+ // get address origin - static or dynamic
+ const char* origin = (rtnl_neigh_get_flags(neigh) & NTF_ROUTER) > 0 ? "dynamic" : "static";
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "origin(interface[%s]:neighbor[%s]) = %s", rtnl_link_get_name(link), ip_buffer, origin);
+
+ // add origin
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_neighbor_origin(ly_ctx, *parent, origin), error_out);
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_ipv4_neighbor(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_oper_ctx_t* oper_ctx = &ctx->oper_ctx;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+ char dst_buffer[20] = { 0 };
+ char ll_buffer[100] = { 0 };
+
+ const struct ly_ctx* ly_ctx = NULL;
+ struct lyd_node* address_node = NULL;
+
+ struct rtnl_link* link = NULL;
+ struct rtnl_neigh* neigh_iter = NULL;
+ struct nl_addr *dst_addr = NULL, *ll_addr = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "ipv4") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ neigh_iter = (struct rtnl_neigh*)nl_cache_get_first(oper_ctx->nl_ctx.neigh_cache);
+
+ while (neigh_iter) {
+ // check for interface IPv4 neighbor
+ if (rtnl_neigh_get_ifindex(neigh_iter) == rtnl_link_get_ifindex(link) && rtnl_neigh_get_family(neigh_iter) == AF_INET) {
+ SRPLG_LOG_INF(PLUGIN_NAME, "Found IPv4 neighbor for %s", rtnl_link_get_name(link));
+
+ // IP
+ SRPC_SAFE_CALL_PTR(dst_addr, rtnl_neigh_get_dst(neigh_iter), error_out);
+ SRPC_SAFE_CALL_PTR(error_ptr, nl_addr2str(dst_addr, dst_buffer, sizeof(dst_buffer)), error_out);
+
+ // link-layer-address
+ SRPC_SAFE_CALL_PTR(ll_addr, rtnl_neigh_get_lladdr(neigh_iter), error_out);
+ SRPC_SAFE_CALL_PTR(error_ptr, nl_addr2str(ll_addr, ll_buffer, sizeof(ll_buffer)), error_out);
+
+ // remove prefix from IP
+ char* prefix = strchr(dst_buffer, '/');
+ if (prefix) {
+ *prefix = 0;
+ }
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "ipv4:neighbor(%s) = %s | %s", rtnl_link_get_name(link), dst_buffer, ll_buffer);
+
+ // neighbor IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_neighbor(ly_ctx, *parent, &address_node, dst_buffer), error_out);
+
+ // link-layer-address
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4_neighbor_link_layer_address(ly_ctx, address_node, ll_buffer), error_out);
+ }
+
+ neigh_iter = (struct rtnl_neigh*)nl_cache_get_next((struct nl_object*)neigh_iter);
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_ipv6_address_origin(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ int rc = 0;
+ void* error_ptr = NULL;
+
+ sr_session_ctx_t* running_session = NULL;
+ sr_conn_ctx_t* connection = NULL;
+
+ sr_val_t* prefix_length_val = NULL;
+
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_oper_ctx_t* oper_ctx = &ctx->oper_ctx;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+ char ip_buffer[100] = { 0 };
+ char address_buffer[100] = { 0 };
+
+ char prefix_path_buffer[PATH_MAX] = { 0 };
+
+ const struct ly_ctx* ly_ctx = NULL;
+
+ struct rtnl_link* link = NULL;
+ struct nl_addr* local = NULL;
+ struct rtnl_addr* addr = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ // connect to the operational DS
+ SRPC_SAFE_CALL_PTR(connection, sr_session_get_connection(session), error_out);
+ SRPC_SAFE_CALL_ERR(error, sr_session_start(connection, SR_DS_RUNNING, &running_session), error_out);
+
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "address") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // get IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_extract_interface_address_ip(session, xpath_buffer, ip_buffer, sizeof(ip_buffer)), error_out);
+
+ // get prefix length from the operational DS
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(prefix_path_buffer, sizeof(prefix_path_buffer), "/ietf-interfaces:interfaces/interface[name=\"%s\"]/ietf-ip:ipv6/address[ip=\"%s\"]/prefix-length", rtnl_link_get_name(link), ip_buffer), error_out);
+ SRPC_SAFE_CALL_ERR(error, sr_get_item(running_session, prefix_path_buffer, 0, &prefix_length_val), error_out);
+
+ // create an address
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(address_buffer, sizeof(address_buffer), "%s/%d", ip_buffer, prefix_length_val->data.uint8_val), error_out);
+
+ // parse address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(address_buffer, AF_INET6, &local), error_out);
+
+ // get rtnl address
+ SRPC_SAFE_CALL_PTR(addr, rtnl_addr_get(oper_ctx->nl_ctx.addr_cache, rtnl_link_get_ifindex(link), local), error_out);
+
+ // get address origin - static or dynamic
+ const char* origin = (rtnl_addr_get_flags(addr) & IFA_F_PERMANENT) > 0 ? "static" : "dhcp";
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "origin(interface[%s]:address[%s]) = %s", rtnl_link_get_name(link), ip_buffer, origin);
+
+ // add origin
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_address_origin(ly_ctx, *parent, origin), error_out);
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ if (running_session) {
+ sr_session_stop(running_session);
+ }
+
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_ipv6_address_status(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ int rc = 0;
+ void* error_ptr = NULL;
+
+ sr_session_ctx_t* running_session = NULL;
+ sr_conn_ctx_t* connection = NULL;
+
+ sr_val_t* prefix_length_val = NULL;
+
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_oper_ctx_t* oper_ctx = &ctx->oper_ctx;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+ char ip_buffer[100] = { 0 };
+ char address_buffer[100] = { 0 };
+
+ char prefix_path_buffer[PATH_MAX] = { 0 };
+
+ const struct ly_ctx* ly_ctx = NULL;
+
+ struct rtnl_link* link = NULL;
+ struct nl_addr* local = NULL;
+ struct rtnl_addr* addr = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ // connect to the operational DS
+ SRPC_SAFE_CALL_PTR(connection, sr_session_get_connection(session), error_out);
+ SRPC_SAFE_CALL_ERR(error, sr_session_start(connection, SR_DS_RUNNING, &running_session), error_out);
+
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "address") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // get IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_extract_interface_address_ip(session, xpath_buffer, ip_buffer, sizeof(ip_buffer)), error_out);
+
+ // get prefix length from the operational DS
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(prefix_path_buffer, sizeof(prefix_path_buffer), "/ietf-interfaces:interfaces/interface[name=\"%s\"]/ietf-ip:ipv6/address[ip=\"%s\"]/prefix-length", rtnl_link_get_name(link), ip_buffer), error_out);
+ SRPC_SAFE_CALL_ERR(error, sr_get_item(running_session, prefix_path_buffer, 0, &prefix_length_val), error_out);
+
+ // create an address
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(address_buffer, sizeof(address_buffer), "%s/%d", ip_buffer, prefix_length_val->data.uint8_val), error_out);
+
+ // parse address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(address_buffer, AF_INET6, &local), error_out);
+
+ // get rtnl address
+ SRPC_SAFE_CALL_PTR(addr, rtnl_addr_get(oper_ctx->nl_ctx.addr_cache, rtnl_link_get_ifindex(link), local), error_out);
+
+ // get address status
+ const char* status = "preferred";
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "status(interface[%s]:address[%s]) = %s", rtnl_link_get_name(link), ip_buffer, status);
+
+ // add origin
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_address_status(ly_ctx, *parent, status), error_out);
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ if (running_session) {
+ sr_session_stop(running_session);
+ }
+
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_ipv6_address(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ int rc = 0;
+ void* error_ptr = NULL;
+
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_oper_ctx_t* oper_ctx = &ctx->oper_ctx;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+ char ip_buffer[100] = { 0 };
+ char prefix_buffer[20] = { 0 };
+
+ const struct ly_ctx* ly_ctx = NULL;
+ struct lyd_node* address_node = NULL;
+
+ struct rtnl_link* link = NULL;
+ struct rtnl_addr* addr_iter = NULL;
+ struct nl_addr* local = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "ipv6") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ addr_iter = (struct rtnl_addr*)nl_cache_get_first(oper_ctx->nl_ctx.addr_cache);
+
+ while (addr_iter) {
+ if (rtnl_addr_get_ifindex(addr_iter) == rtnl_link_get_ifindex(link) && rtnl_addr_get_family(addr_iter) == AF_INET6) {
+ SRPLG_LOG_INF(PLUGIN_NAME, "Found IPv6 address for %s", rtnl_link_get_name(link));
+
+ SRPC_SAFE_CALL_PTR(local, rtnl_addr_get_local(addr_iter), error_out);
+ SRPC_SAFE_CALL_PTR(error_ptr, nl_addr2str(local, ip_buffer, sizeof(ip_buffer)), error_out);
+
+ // remove prefix from IP
+ char* prefix = strchr(ip_buffer, '/');
+ if (prefix) {
+ *prefix = 0;
+ }
+
+ // prefix
+ SRPC_SAFE_CALL_ERR_COND(rc, rc < 0, snprintf(prefix_buffer, sizeof(prefix_buffer), "%d", rtnl_addr_get_prefixlen(addr_iter)), error_out);
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "ipv6:address(%s) = %s/%s", rtnl_link_get_name(link), ip_buffer, prefix_buffer);
+
+ // address from the current link - add to the list
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_address(ly_ctx, *parent, &address_node, ip_buffer), error_out);
+
+ // prefix
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_address_prefix_length(ly_ctx, address_node, prefix_buffer), error_out);
+ }
+
+ addr_iter = (struct rtnl_addr*)nl_cache_get_next((struct nl_object*)addr_iter);
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_ipv6_neighbor_origin(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_oper_ctx_t* oper_ctx = &ctx->oper_ctx;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+ char ip_buffer[100] = { 0 };
+
+ const struct ly_ctx* ly_ctx = NULL;
+
+ struct rtnl_link* link = NULL;
+ struct rtnl_neigh* neigh = NULL;
+ struct nl_addr* neigh_addr = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "neighbor") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // get IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_extract_interface_neighbor_ip(session, xpath_buffer, ip_buffer, sizeof(ip_buffer)), error_out);
+
+ // parse address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(ip_buffer, AF_INET6, &neigh_addr), error_out);
+
+ // get neighbor
+ SRPC_SAFE_CALL_PTR(neigh, rtnl_neigh_get(oper_ctx->nl_ctx.neigh_cache, rtnl_link_get_ifindex(link), neigh_addr), error_out);
+
+ // get address origin - static or dynamic
+ const char* origin = (rtnl_neigh_get_flags(neigh) & NTF_ROUTER) > 0 ? "dynamic" : "static";
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "origin(interface[%s]:neighbor[%s]) = %s", rtnl_link_get_name(link), ip_buffer, origin);
+
+ // add origin
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_origin(ly_ctx, *parent, origin), error_out);
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_ipv6_neighbor_is_router(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_oper_ctx_t* oper_ctx = &ctx->oper_ctx;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+ char ip_buffer[100] = { 0 };
+
+ const struct ly_ctx* ly_ctx = NULL;
+
+ struct rtnl_link* link = NULL;
+ struct rtnl_neigh* neigh = NULL;
+ struct nl_addr* neigh_addr = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "neighbor") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // get IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_extract_interface_neighbor_ip(session, xpath_buffer, ip_buffer, sizeof(ip_buffer)), error_out);
+
+ // parse address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(ip_buffer, AF_INET6, &neigh_addr), error_out);
+
+ // get neighbor
+ SRPC_SAFE_CALL_PTR(neigh, rtnl_neigh_get(oper_ctx->nl_ctx.neigh_cache, rtnl_link_get_ifindex(link), neigh_addr), error_out);
+
+ // check if is-router
+ const bool is_router = (rtnl_neigh_get_flags(neigh) & NTF_ROUTER) > 0;
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "is-router(interface[%s]:neighbor[%s]) = %s", rtnl_link_get_name(link), ip_buffer, is_router ? "true" : "false");
+
+ // add is-router
+ if (is_router) {
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_is_router(ly_ctx, *parent, ""), error_out);
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_ipv6_neighbor_state(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_oper_ctx_t* oper_ctx = &ctx->oper_ctx;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+ char ip_buffer[100] = { 0 };
+
+ const struct ly_ctx* ly_ctx = NULL;
+
+ struct rtnl_link* link = NULL;
+ struct rtnl_neigh* neigh = NULL;
+ struct nl_addr* neigh_addr = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ const char* state_map[] = {
+ [NUD_INCOMPLETE] = "incomplete",
+ [NUD_REACHABLE] = "reachable",
+ [NUD_STALE] = "stale",
+ [NUD_DELAY] = "delay",
+ [NUD_PROBE] = "probe",
+ [NUD_FAILED] = "failed",
+ [NUD_NOARP] = "noarp",
+ [NUD_PERMANENT] = "permanent",
+ };
+
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "neighbor") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ // get IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_extract_interface_neighbor_ip(session, xpath_buffer, ip_buffer, sizeof(ip_buffer)), error_out);
+
+ // parse address
+ SRPC_SAFE_CALL_ERR(error, nl_addr_parse(ip_buffer, AF_INET6, &neigh_addr), error_out);
+
+ // get neighbor
+ SRPC_SAFE_CALL_PTR(neigh, rtnl_neigh_get(oper_ctx->nl_ctx.neigh_cache, rtnl_link_get_ifindex(link), neigh_addr), error_out);
+
+ // get neighbor state - static or dynamic
+ const char* state = state_map[rtnl_neigh_get_state(neigh)];
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "state(interface[%s]:neighbor[%s]) = %s", rtnl_link_get_name(link), ip_buffer, state);
+
+ if (rtnl_neigh_get_state(neigh) != NUD_FAILED && rtnl_neigh_get_state(neigh) != NUD_NOARP && rtnl_neigh_get_state(neigh) != NUD_PERMANENT) {
+ // add state
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_state(ly_ctx, *parent, state), error_out);
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface_ipv6_neighbor(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ void* error_ptr = NULL;
+
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_oper_ctx_t* oper_ctx = &ctx->oper_ctx;
+
+ char xpath_buffer[PATH_MAX] = { 0 };
+ char dst_buffer[100] = { 0 };
+ char ll_buffer[100] = { 0 };
+
+ const struct ly_ctx* ly_ctx = NULL;
+ struct lyd_node* address_node = NULL;
+
+ struct rtnl_link* link = NULL;
+ struct rtnl_neigh* neigh_iter = NULL;
+ struct nl_addr *dst_addr = NULL, *ll_addr = NULL;
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+ }
+
+ // there needs to be an allocated link cache in memory
+ assert(*parent != NULL);
+ assert(strcmp(LYD_NAME(*parent), "ipv6") == 0);
+
+ // get node xpath
+ SRPC_SAFE_CALL_PTR(error_ptr, lyd_path(*parent, LYD_PATH_STD, xpath_buffer, sizeof(xpath_buffer)), error_out);
+
+ // get link
+ SRPC_SAFE_CALL_PTR(link, interfaces_get_current_link(ctx, session, xpath_buffer), error_out);
+
+ neigh_iter = (struct rtnl_neigh*)nl_cache_get_first(oper_ctx->nl_ctx.neigh_cache);
+
+ while (neigh_iter) {
+ // check for interface IPv6 neighbor
+ if (rtnl_neigh_get_ifindex(neigh_iter) == rtnl_link_get_ifindex(link) && rtnl_neigh_get_family(neigh_iter) == AF_INET6) {
+ SRPLG_LOG_INF(PLUGIN_NAME, "Found IPv6 neighbor for %s", rtnl_link_get_name(link));
+
+ // IP
+ SRPC_SAFE_CALL_PTR(dst_addr, rtnl_neigh_get_dst(neigh_iter), error_out);
+ SRPC_SAFE_CALL_PTR(error_ptr, nl_addr2str(dst_addr, dst_buffer, sizeof(dst_buffer)), error_out);
+
+ // link-layer-address
+ SRPC_SAFE_CALL_PTR(ll_addr, rtnl_neigh_get_lladdr(neigh_iter), error_out);
+ SRPC_SAFE_CALL_PTR(error_ptr, nl_addr2str(ll_addr, ll_buffer, sizeof(ll_buffer)), error_out);
+
+ // remove prefix from IP
+ char* prefix = strchr(dst_buffer, '/');
+ if (prefix) {
+ *prefix = 0;
+ }
+
+ SRPLG_LOG_INF(PLUGIN_NAME, "ipv6:neighbor(%s) = %s | %s", rtnl_link_get_name(link), dst_buffer, ll_buffer);
+
+ // neighbor IP
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor(ly_ctx, *parent, &address_node, dst_buffer), error_out);
+
+ // link-layer-address
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6_neighbor_link_layer_address(ly_ctx, address_node, ll_buffer), error_out);
+ }
+
+ neigh_iter = (struct rtnl_neigh*)nl_cache_get_next((struct nl_object*)neigh_iter);
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+ return error;
+}
+
+int interfaces_subscription_operational_interfaces_interface(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data)
+{
+ int error = SR_ERR_OK;
+ // void* error_ptr = NULL;
+ const struct ly_ctx* ly_ctx = NULL;
+ interfaces_ctx_t* ctx = private_data;
+ interfaces_nl_ctx_t* nl_ctx = &ctx->oper_ctx.nl_ctx;
+ struct rtnl_link* link_iter = NULL;
+
+ // libyang
+ struct lyd_node* interface_list_node = NULL;
+
+ // setup nl socket
+ if (!nl_ctx->socket) {
+ // netlink
+ SRPC_SAFE_CALL_PTR(nl_ctx->socket, nl_socket_alloc(), error_out);
+
+ // connect
+ SRPC_SAFE_CALL_ERR(error, nl_connect(nl_ctx->socket, NETLINK_ROUTE), error_out);
+ }
+
+ // cache was already allocated - free existing cache
+ if (nl_ctx->link_cache) {
+ nl_cache_refill(nl_ctx->socket, nl_ctx->link_cache);
+ nl_cache_refill(nl_ctx->socket, nl_ctx->addr_cache);
+ nl_cache_refill(nl_ctx->socket, nl_ctx->neigh_cache);
+ } else {
+ // allocate new link cache
+ SRPC_SAFE_CALL_ERR(error, rtnl_link_alloc_cache(nl_ctx->socket, AF_UNSPEC, &nl_ctx->link_cache), error_out);
+
+ // allocate new address cache
+ SRPC_SAFE_CALL_ERR(error, rtnl_addr_alloc_cache(nl_ctx->socket, &nl_ctx->addr_cache), error_out);
+
+ // allocate new neighbor cache
+ SRPC_SAFE_CALL_ERR(error, rtnl_neigh_alloc_cache(nl_ctx->socket, &nl_ctx->neigh_cache), error_out);
+ }
+
+ if (*parent == NULL) {
+ ly_ctx = sr_acquire_context(sr_session_get_connection(session));
+ if (ly_ctx == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "sr_acquire_context() failed");
+ goto error_out;
+ }
+
+ // set parent to the interfaces container
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces(ly_ctx, parent), error_out);
+ }
+
+ // iterate links and add them to the operational DS
+ link_iter = (struct rtnl_link*)nl_cache_get_first(nl_ctx->link_cache);
+ while (link_iter) {
+ // add interface
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface(ly_ctx, *parent, &interface_list_node, rtnl_link_get_name(link_iter)), error_out);
+
+ // create needed containers for the interface
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_statistics(ly_ctx, interface_list_node, NULL), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv4(ly_ctx, interface_list_node, NULL), error_out);
+ SRPC_SAFE_CALL_ERR(error, interfaces_ly_tree_create_interfaces_interface_ipv6(ly_ctx, interface_list_node, NULL), error_out);
+
+ link_iter = (struct rtnl_link*)nl_cache_get_next((struct nl_object*)link_iter);
+ }
+
+ goto out;
+
+error_out:
+ error = SR_ERR_CALLBACK_FAILED;
+
+out:
+
+ return error;
+}
+
+static struct rtnl_link* interfaces_get_current_link(interfaces_ctx_t* ctx, sr_session_ctx_t* session, const char* xpath)
+{
+ int error = 0;
+
+ const interfaces_nl_ctx_t* nl_ctx = &ctx->oper_ctx.nl_ctx;
+
+ // buffers
+ char interface_name_buffer[100] = { 0 };
+
+ // libnl
+ struct rtnl_link* link = NULL;
+
+ // there needs to be an allocated link cache in memory
+ assert(nl_ctx->link_cache != NULL);
+
+ // extract interface name
+ SRPC_SAFE_CALL_ERR(error, interfaces_extract_interface_name(session, xpath, interface_name_buffer, sizeof(interface_name_buffer)), error_out);
+
+ // get link by name
+ SRPC_SAFE_CALL_PTR(link, rtnl_link_get_by_name(nl_ctx->link_cache, interface_name_buffer), error_out);
+
+ return link;
+
+error_out:
+ return NULL;
+}
+
+static int interfaces_extract_interface_name(sr_session_ctx_t* session, const char* xpath, char* buffer, size_t buffer_size)
+{
+ int error = 0;
+
+ const char* name = NULL;
+ char* xpath_copy = NULL;
+
+ sr_xpath_ctx_t xpath_ctx = { 0 };
+
+ // copy xpath due to changing it when using xpath_ctx from sysrepo
+ SRPC_SAFE_CALL_PTR(xpath_copy, strdup(xpath), error_out);
+
+ // extract key
+ SRPC_SAFE_CALL_PTR(name, sr_xpath_key_value(xpath_copy, "interface", "name", &xpath_ctx), error_out);
+
+ // store to buffer
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(buffer, buffer_size, "%s", name), error_out);
+
+ error = 0;
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ if (xpath_copy) {
+ free(xpath_copy);
+ }
+
+ return error;
+}
+
+static int interfaces_extract_interface_address_ip(sr_session_ctx_t* session, const char* xpath, char* buffer, size_t buffer_size)
+{
+ int error = 0;
+
+ const char* ip = NULL;
+ char* xpath_copy = NULL;
+
+ sr_xpath_ctx_t xpath_ctx = { 0 };
+
+ // copy xpath due to changing it when using xpath_ctx from sysrepo
+ SRPC_SAFE_CALL_PTR(xpath_copy, strdup(xpath), error_out);
+
+ // extract key
+ SRPC_SAFE_CALL_PTR(ip, sr_xpath_key_value(xpath_copy, "address", "ip", &xpath_ctx), error_out);
+
+ // store to buffer
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(buffer, buffer_size, "%s", ip), error_out);
+
+ error = 0;
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ if (xpath_copy) {
+ free(xpath_copy);
+ }
+
+ return error;
+}
+
+static int interfaces_extract_interface_neighbor_ip(sr_session_ctx_t* session, const char* xpath, char* buffer, size_t buffer_size)
+{
+ int error = 0;
+
+ const char* ip = NULL;
+ char* xpath_copy = NULL;
+
+ sr_xpath_ctx_t xpath_ctx = { 0 };
+
+ // copy xpath due to changing it when using xpath_ctx from sysrepo
+ SRPC_SAFE_CALL_PTR(xpath_copy, strdup(xpath), error_out);
+
+ // extract key
+ SRPC_SAFE_CALL_PTR(ip, sr_xpath_key_value(xpath_copy, "neighbor", "ip", &xpath_ctx), error_out);
+
+ // store to buffer
+ SRPC_SAFE_CALL_ERR_COND(error, error < 0, snprintf(buffer, buffer_size, "%s", ip), error_out);
+
+ error = 0;
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ if (xpath_copy) {
+ free(xpath_copy);
+ }
+
+ return error;
+}
+
+static int interfaces_get_system_boot_time(char* buffer, size_t buffer_size)
+{
+ time_t now = 0;
+ struct tm* ts = { 0 };
+ struct sysinfo s_info = { 0 };
+ time_t uptime_seconds = 0;
+
+ now = time(NULL);
+
+ ts = localtime(&now);
+ if (ts == NULL)
+ return -1;
+
+ if (sysinfo(&s_info) != 0)
+ return -1;
+
+ uptime_seconds = s_info.uptime;
+
+ time_t diff = now - uptime_seconds;
+
+ ts = localtime(&diff);
+ if (ts == NULL)
+ return -1;
+
+ strftime(buffer, buffer_size, "%FT%TZ", ts);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/subscription/operational.h b/src/interfaces/src/plugin/subscription/operational.h
new file mode 100644
index 00000000..6b395434
--- /dev/null
+++ b/src/interfaces/src/plugin/subscription/operational.h
@@ -0,0 +1,48 @@
+#ifndef INTERFACES_PLUGIN_SUBSCRIPTION_OPERATIONAL_H
+#define INTERFACES_PLUGIN_SUBSCRIPTION_OPERATIONAL_H
+
+#include
+
+int interfaces_subscription_operational_interfaces_interface_admin_status(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_oper_status(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_last_change(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_if_index(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_phys_address(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_higher_layer_if(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_lower_layer_if(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_speed(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_discontinuity_time(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_in_octets(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_in_unicast_pkts(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_in_broadcast_pkts(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_in_multicast_pkts(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_in_discards(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_in_errors(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_in_unknown_protos(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_out_octets(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_out_unicast_pkts(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_out_broadcast_pkts(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_out_multicast_pkts(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_out_discards(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_out_errors(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_statistics_in_discard_unknown_encaps(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_carrier_delay_carrier_transitions(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_carrier_delay_timer_running(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_dampening_penalty(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_dampening_suppressed(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_dampening_time_remaining(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_forwarding_mode(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_ipv4_address_origin(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_ipv4_address(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_ipv4_neighbor_origin(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_ipv4_neighbor(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_ipv6_address_origin(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_ipv6_address_status(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_ipv6_address(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_ipv6_neighbor_origin(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_ipv6_neighbor_is_router(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_ipv6_neighbor_state(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface_ipv6_neighbor(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+int interfaces_subscription_operational_interfaces_interface(sr_session_ctx_t* session, uint32_t sub_id, const char* module_name, const char* path, const char* request_xpath, uint32_t request_id, struct lyd_node** parent, void* private_data);
+
+#endif // INTERFACES_PLUGIN_SUBSCRIPTION_OPERATIONAL_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/subscription/rpc.c b/src/interfaces/src/plugin/subscription/rpc.c
new file mode 100644
index 00000000..03645e83
--- /dev/null
+++ b/src/interfaces/src/plugin/subscription/rpc.c
@@ -0,0 +1,7 @@
+#include "rpc.h"
+#include "plugin/common.h"
+#include "plugin/context.h"
+
+#include
+#include
+#include
diff --git a/src/interfaces/src/plugin/subscription/rpc.h b/src/interfaces/src/plugin/subscription/rpc.h
new file mode 100644
index 00000000..6afecbaa
--- /dev/null
+++ b/src/interfaces/src/plugin/subscription/rpc.h
@@ -0,0 +1,6 @@
+#ifndef INTERFACES_PLUGIN_SUBSCRIPTION_RPC_H
+#define INTERFACES_PLUGIN_SUBSCRIPTION_RPC_H
+
+#include
+
+#endif // INTERFACES_PLUGIN_SUBSCRIPTION_RPC_H
\ No newline at end of file
diff --git a/src/interfaces/src/plugin/types.h b/src/interfaces/src/plugin/types.h
new file mode 100644
index 00000000..a9c9ef75
--- /dev/null
+++ b/src/interfaces/src/plugin/types.h
@@ -0,0 +1,197 @@
+#ifndef INTERFACES_PLUGIN_TYPES_H
+#define INTERFACES_PLUGIN_TYPES_H
+
+#include
+#include
+#include
+#include
+
+#include
+
+// typedefs
+typedef struct interfaces_interface_carrier_delay interfaces_interface_carrier_delay_t;
+typedef struct interfaces_interface_dampening interfaces_interface_dampening_t;
+typedef struct interfaces_interface_encapsulation_dot1q_vlan_outer_tag interfaces_interface_encapsulation_dot1q_vlan_outer_tag_t;
+typedef struct interfaces_interface_encapsulation_dot1q_vlan_second_tag interfaces_interface_encapsulation_dot1q_vlan_second_tag_t;
+typedef struct interfaces_interface_encapsulation_dot1q_vlan interfaces_interface_encapsulation_dot1q_vlan_t;
+typedef struct interfaces_interface_encapsulation interfaces_interface_encapsulation_t;
+typedef struct interfaces_interface_ipv4_address interfaces_interface_ipv4_address_t;
+typedef struct interfaces_interface_ipv4_address_element interfaces_interface_ipv4_address_element_t;
+typedef struct interfaces_interface_ipv4_neighbor interfaces_interface_ipv4_neighbor_t;
+typedef struct interfaces_interface_ipv4_neighbor_element interfaces_interface_ipv4_neighbor_element_t;
+typedef struct interfaces_interface_ipv4 interfaces_interface_ipv4_t;
+typedef struct interfaces_interface_ipv6_address interfaces_interface_ipv6_address_t;
+typedef struct interfaces_interface_ipv6_address_element interfaces_interface_ipv6_address_element_t;
+typedef struct interfaces_interface_ipv6_neighbor interfaces_interface_ipv6_neighbor_t;
+typedef struct interfaces_interface_ipv6_neighbor_element interfaces_interface_ipv6_neighbor_element_t;
+typedef struct interfaces_interface_ipv6_autoconf interfaces_interface_ipv6_autoconf_t;
+typedef struct interfaces_interface_ipv6 interfaces_interface_ipv6_t;
+typedef struct interfaces_interface interfaces_interface_t;
+typedef struct interfaces_interface_element interfaces_interface_element_t;
+typedef struct interfaces interfaces_t;
+typedef struct interfaces_interface_state interfaces_interface_state_t;
+typedef struct interfaces_interface_state_hash_element interfaces_interface_state_hash_element_t;
+typedef struct interfaces_interface_hash_element interfaces_interface_hash_element_t;
+
+enum interfaces_interface_enable {
+ interfaces_interface_enable_disabled = 0,
+ interfaces_interface_enable_enabled = 1,
+};
+
+enum interfaces_interface_link_up_down_trap_enable {
+ interfaces_interface_link_up_down_trap_enable_disabled,
+ interfaces_interface_link_up_down_trap_enable_enabled,
+};
+
+typedef enum interfaces_interface_link_up_down_trap_enable interfaces_interface_link_up_down_trap_enable_t;
+
+struct interfaces_interface_carrier_delay {
+ uint32_t down;
+ uint32_t up;
+};
+
+struct interfaces_interface_dampening {
+ uint32_t half_life;
+ uint32_t reuse;
+ uint32_t suppress;
+ uint32_t max_suppress_time;
+};
+
+struct interfaces_interface_encapsulation_dot1q_vlan_outer_tag {
+ char* tag_type;
+ uint16_t vlan_id;
+};
+
+struct interfaces_interface_encapsulation_dot1q_vlan_second_tag {
+ char* tag_type;
+ uint16_t vlan_id;
+};
+
+struct interfaces_interface_encapsulation_dot1q_vlan {
+ interfaces_interface_encapsulation_dot1q_vlan_outer_tag_t outer_tag;
+ interfaces_interface_encapsulation_dot1q_vlan_second_tag_t second_tag;
+};
+
+struct interfaces_interface_encapsulation {
+ interfaces_interface_encapsulation_dot1q_vlan_t dot1q_vlan;
+};
+
+enum interfaces_interface_ipv4_address_subnet {
+ interfaces_interface_ipv4_address_subnet_none = 0,
+ interfaces_interface_ipv4_address_subnet_prefix_length,
+ interfaces_interface_ipv4_address_subnet_netmask,
+};
+
+typedef enum interfaces_interface_ipv4_address_subnet interfaces_interface_ipv4_address_subnet_t;
+
+struct interfaces_interface_ipv4_address {
+ char* ip;
+ union {
+ uint8_t prefix_length;
+ char* netmask;
+ } subnet;
+ interfaces_interface_ipv4_address_subnet_t subnet_type;
+};
+
+struct interfaces_interface_ipv4_address_element {
+ interfaces_interface_ipv4_address_element_t* next;
+ interfaces_interface_ipv4_address_t address;
+};
+
+struct interfaces_interface_ipv4_neighbor {
+ char* ip;
+ char* link_layer_address;
+};
+
+struct interfaces_interface_ipv4_neighbor_element {
+ interfaces_interface_ipv4_neighbor_element_t* next;
+ interfaces_interface_ipv4_neighbor_t neighbor;
+};
+
+struct interfaces_interface_ipv4 {
+ uint8_t enabled;
+ uint8_t forwarding;
+ uint16_t mtu;
+ interfaces_interface_ipv4_address_element_t* address;
+ interfaces_interface_ipv4_neighbor_element_t* neighbor;
+};
+
+struct interfaces_interface_ipv6_address {
+ char* ip;
+ uint8_t prefix_length;
+};
+
+struct interfaces_interface_ipv6_address_element {
+ interfaces_interface_ipv6_address_element_t* next;
+ interfaces_interface_ipv6_address_t address;
+};
+
+struct interfaces_interface_ipv6_neighbor {
+ char* ip;
+ char* link_layer_address;
+};
+
+struct interfaces_interface_ipv6_neighbor_element {
+ interfaces_interface_ipv6_neighbor_element_t* next;
+ interfaces_interface_ipv6_neighbor_t neighbor;
+};
+
+struct interfaces_interface_ipv6_autoconf {
+ uint8_t create_global_addresses;
+ uint8_t create_temporary_addresses;
+ uint32_t temporary_valid_lifetime;
+ uint32_t temporary_preferred_lifetime;
+};
+
+struct interfaces_interface_ipv6 {
+ uint8_t enabled;
+ uint8_t forwarding;
+ uint32_t mtu;
+ interfaces_interface_ipv6_address_element_t* address;
+ interfaces_interface_ipv6_neighbor_element_t* neighbor;
+ uint32_t dup_addr_detect_transmits;
+ interfaces_interface_ipv6_autoconf_t autoconf;
+};
+
+struct interfaces_interface {
+ char* name;
+ char* description;
+ char* type;
+ uint8_t enabled;
+ interfaces_interface_link_up_down_trap_enable_t link_up_down_trap_enable;
+ interfaces_interface_carrier_delay_t carrier_delay;
+ interfaces_interface_dampening_t dampening;
+ interfaces_interface_encapsulation_t encapsulation;
+ char* loopback;
+ uint32_t max_frame_size;
+ char* parent_interface;
+ interfaces_interface_ipv4_t ipv4;
+ interfaces_interface_ipv6_t ipv6;
+};
+
+struct interfaces_interface_element {
+ interfaces_interface_element_t* next;
+ interfaces_interface_t interface;
+};
+
+struct interfaces {
+ interfaces_interface_element_t* interface;
+};
+
+struct interfaces_interface_state {
+ char* name; // key
+ uint8_t state;
+ time_t last_change;
+};
+
+struct interfaces_interface_state_hash_element {
+ interfaces_interface_state_t state;
+ UT_hash_handle hh;
+};
+
+struct interfaces_interface_hash_element {
+ interfaces_interface_t interface;
+ UT_hash_handle hh;
+};
+
+#endif // INTERFACES_PLUGIN_TYPES_H
diff --git a/src/interfaces/tests/CMakeLists.txt b/src/interfaces/tests/CMakeLists.txt
new file mode 100644
index 00000000..1fbb933b
--- /dev/null
+++ b/src/interfaces/tests/CMakeLists.txt
@@ -0,0 +1,33 @@
+set(INTERFACES_UTEST_NAME "interfaces_utest")
+set(UTILS_MEMORY_LIBRARY_NAME "utils_memory")
+# for wrapping cmocka mock functions
+set(
+ INTERFACES_UTEST_LINKER_OPTIONS
+ #"-Wl,--wrap=function_foo"
+)
+
+add_executable(
+ ${INTERFACES_UTEST_NAME}
+
+ ${CMAKE_SOURCE_DIR}/src/interfaces/tests/interfaces_utest.c
+)
+target_include_directories(
+ ${INTERFACES_UTEST_NAME}
+ PUBLIC ${CMAKE_SOURCE_DIR}/src/interfaces/src
+)
+target_link_options(
+ ${INTERFACES_UTEST_NAME}
+ PRIVATE ${INTERFACES_UTEST_LINKER_OPTIONS}
+)
+target_link_libraries(
+ ${INTERFACES_UTEST_NAME}
+
+ ${CMOCKA_LIBRARIES}
+ ${PLUGIN_LIBRARY_NAME}
+ ${SYSREPO_LIBRARIES}
+ ${LIBYANG_LIBRARIES}
+)
+add_test(
+ NAME ${INTERFACES_UTEST_NAME}
+ COMMAND interfaces_utest
+)
diff --git a/src/interfaces/tests/interfaces_utest.c b/src/interfaces/tests/interfaces_utest.c
new file mode 100644
index 00000000..a884d612
--- /dev/null
+++ b/src/interfaces/tests/interfaces_utest.c
@@ -0,0 +1,489 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* load api */
+#include "plugin/api/interfaces/load.h"
+
+/* interfaces hash table state */
+#include "plugin/data/interfaces/interface_state.h"
+
+/* interfaces interface linked list */
+#include "plugin/data/interfaces/interface/linked_list.h"
+
+/* init functionality */
+static int setup(void **state);
+static int teardown(void **state);
+
+/* tests */
+
+/** interface hash table state **/
+static void test_state_hash_new_correct(void **state);
+static void test_state_hash_element_new_correct(void **state);
+static void test_state_hash_add_correct(void **state);
+static void test_state_hash_add_incorrect(void **state);
+static void test_state_hash_get_correct(void **state);
+static void test_state_hash_get_incorrect(void **state);
+static void test_state_hash_set_name_correct(void **state);
+static void test_state_hash_set_name_incorrect(void **state);
+
+
+/** interface list state **/
+static void test_interface_list_new_correct(void **state);
+static void test_interface_list_add_element_correct(void **state);
+/*** ipv4 ***/
+static void test_interface_list_new_ipv4_address_correct(void **state);
+static void test_interface_list_new_ipv4_neighbor_correct(void **state);
+static void test_interface_list_element_new_ipv4_address_correct(void **state);
+static void test_interface_list_element_new_ipv4_neighbor_correct(void **state);
+static void test_interface_ipv4_address_netmask2prefix_correct(void **state);
+static void test_interface_ipv4_address_netmask2prefix_incorrect(void **state);
+
+/*** ipv6 ***/
+static void test_interface_list_new_ipv6_address_correct(void **state);
+static void test_interface_list_new_ipv6_neighbor_correct(void **state);
+static void test_interface_list_element_new_ipv6_address_correct(void **state);
+static void test_interface_list_element_new_ipv6_neighbor_correct(void **state);
+
+/** load **/
+static void test_correct_load_interface(void **state);
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ /** interface hash table state **/
+ cmocka_unit_test(test_correct_load_interface),
+ cmocka_unit_test(test_state_hash_new_correct),
+ cmocka_unit_test(test_state_hash_element_new_correct),
+ cmocka_unit_test(test_state_hash_add_correct),
+ cmocka_unit_test(test_state_hash_add_incorrect),
+ cmocka_unit_test(test_state_hash_get_correct),
+ cmocka_unit_test(test_state_hash_get_incorrect),
+ cmocka_unit_test(test_state_hash_set_name_correct),
+ cmocka_unit_test(test_state_hash_set_name_incorrect),
+ /** interface list state **/
+ cmocka_unit_test(test_interface_list_new_correct),
+ cmocka_unit_test(test_interface_list_add_element_correct),
+ /*** ipv4 ***/
+ cmocka_unit_test(test_interface_list_new_ipv4_address_correct),
+ cmocka_unit_test(test_interface_list_new_ipv4_neighbor_correct),
+ cmocka_unit_test(test_interface_list_element_new_ipv4_address_correct),
+ cmocka_unit_test(test_interface_list_element_new_ipv4_neighbor_correct),
+ cmocka_unit_test(test_interface_ipv4_address_netmask2prefix_correct),
+ cmocka_unit_test(test_interface_ipv4_address_netmask2prefix_incorrect),
+ /*** ipv6 ***/
+ cmocka_unit_test(test_interface_list_new_ipv6_address_correct),
+ cmocka_unit_test(test_interface_list_new_ipv6_neighbor_correct),
+ cmocka_unit_test(test_interface_list_element_new_ipv6_address_correct),
+ cmocka_unit_test(test_interface_list_element_new_ipv6_neighbor_correct),
+ };
+
+ return cmocka_run_group_tests(tests, setup, teardown);
+}
+
+static int setup(void **state)
+{
+ interfaces_ctx_t *ctx = malloc(sizeof(interfaces_ctx_t));
+ if (!ctx) {
+ return -1;
+ }
+
+ *ctx = (interfaces_ctx_t){0};
+ *state = ctx;
+
+ return 0;
+}
+
+static int teardown(void **state)
+{
+ if (*state) {
+ free(*state);
+ }
+
+ return 0;
+}
+
+static void test_correct_load_interface(void **state)
+{
+ (void) state;
+
+ int rc = 0;
+
+ assert_int_equal(rc, 0);
+}
+
+static void test_state_hash_new_correct(void **state)
+{
+ (void) state;
+
+ interfaces_interface_state_hash_element_t *state_hash;
+
+ state_hash = interfaces_interface_state_hash_new();
+ assert_null(state_hash);
+}
+
+static void test_state_hash_element_new_correct(void **state)
+{
+ (void) state;
+
+ interfaces_interface_state_hash_element_t *new_elem;
+
+ new_elem = interfaces_interface_state_hash_element_new();
+ assert_non_null(new_elem);
+
+ interfaces_interface_state_hash_element_free(&new_elem);
+ assert_null(new_elem);
+}
+
+static void test_state_hash_add_correct(void **state)
+{
+ (void) state;
+
+ int rc = 0;
+
+ interfaces_interface_state_hash_element_t *state_hash, *new_elem;
+
+ state_hash = interfaces_interface_state_hash_new();
+ assert_null(state_hash);
+
+ new_elem = interfaces_interface_state_hash_element_new();
+
+ interfaces_interface_state_hash_element_set_name(&new_elem, "FOO");
+ assert_int_equal(rc, 0);
+
+ rc = interfaces_interface_state_hash_add(&state_hash, new_elem);
+ assert_int_equal(rc, 0);
+
+ interfaces_interface_state_hash_free(&state_hash);
+}
+
+static void test_state_hash_add_incorrect(void **state)
+{
+ (void) state;
+
+ int rc = 0;
+
+ interfaces_interface_state_hash_element_t *state_hash, *new_elem;
+
+ state_hash = interfaces_interface_state_hash_new();
+ assert_null(state_hash);
+
+ new_elem = interfaces_interface_state_hash_element_new();
+
+ interfaces_interface_state_hash_element_set_name(&new_elem, "FOO");
+ assert_int_equal(rc, 0);
+
+ rc = interfaces_interface_state_hash_add(&state_hash, new_elem);
+ assert_int_equal(rc, 0);
+
+ /* try adding the same element */
+ rc = interfaces_interface_state_hash_add(&state_hash, new_elem);
+ assert_int_not_equal(rc, 0);
+
+ interfaces_interface_state_hash_free(&state_hash);
+}
+
+static void test_state_hash_get_correct(void **state)
+{
+ (void) state;
+
+ int rc = 0;
+ const char *name = "FOO";
+
+ interfaces_interface_state_hash_element_t *state_hash, *new_elem, *found;
+
+ state_hash = interfaces_interface_state_hash_new();
+ assert_null(state_hash);
+
+ new_elem = interfaces_interface_state_hash_element_new();
+
+ interfaces_interface_state_hash_element_set_name(&new_elem, name);
+ assert_int_equal(rc, 0);
+
+ rc = interfaces_interface_state_hash_add(&state_hash, new_elem);
+ assert_int_equal(rc, 0);
+
+ found = interfaces_interface_state_hash_get(state_hash, name);
+ assert_non_null(found);
+
+ interfaces_interface_state_hash_free(&state_hash);
+}
+
+static void test_state_hash_get_incorrect(void **state)
+{
+ (void) state;
+
+ int rc = 0;
+ const char *names[] = { "FOO", "BAR" };
+
+ interfaces_interface_state_hash_element_t *state_hash, *new_elem, *found;
+
+ state_hash = interfaces_interface_state_hash_new();
+ assert_null(state_hash);
+
+ new_elem = interfaces_interface_state_hash_element_new();
+
+ interfaces_interface_state_hash_element_set_name(&new_elem, names[0]);
+ assert_int_equal(rc, 0);
+
+ rc = interfaces_interface_state_hash_add(&state_hash, new_elem);
+ assert_int_equal(rc, 0);
+
+ found = interfaces_interface_state_hash_get(state_hash, names[1]);
+ assert_null(found);
+
+ interfaces_interface_state_hash_free(&state_hash);
+}
+
+static void test_state_hash_set_name_correct(void **state)
+{
+ (void) state;
+
+ int rc = 0;
+
+ interfaces_interface_state_hash_element_t *new_elem;
+
+ new_elem = interfaces_interface_state_hash_element_new();
+
+ rc = interfaces_interface_state_hash_element_set_name(&new_elem, "FOO");
+ assert_int_equal(rc, 0);
+ assert_non_null(new_elem->state.name);
+
+ interfaces_interface_state_hash_element_free(&new_elem);
+}
+
+static void test_state_hash_set_name_incorrect(void **state)
+{
+ (void) state;
+
+ int rc = 0;
+
+ interfaces_interface_state_hash_element_t *new_elem;
+
+ new_elem = interfaces_interface_state_hash_element_new();
+
+ rc = interfaces_interface_state_hash_element_set_name(&new_elem, NULL);
+ assert_int_equal(rc, 0);
+ assert_null(new_elem->state.name);
+
+ interfaces_interface_state_hash_element_free(&new_elem);
+}
+
+static void test_interface_list_new_correct(void **state)
+{
+ (void) state;
+ interfaces_interface_ipv4_address_element_t *address_ipv4;
+ interfaces_interface_ipv4_neighbor_element_t *neighbor_ipv4;
+
+ interfaces_interface_ipv6_address_element_t *address_ipv6;
+ interfaces_interface_ipv6_neighbor_element_t *neighbor_ipv6;
+
+ INTERFACES_INTERFACE_LIST_NEW(address_ipv4);
+ assert_null(address_ipv4);
+
+ INTERFACES_INTERFACE_LIST_NEW(neighbor_ipv4);
+ assert_null(neighbor_ipv4);
+
+ INTERFACES_INTERFACE_LIST_NEW(address_ipv6);
+ assert_null(address_ipv6);
+
+ INTERFACES_INTERFACE_LIST_NEW(neighbor_ipv6);
+ assert_null(neighbor_ipv6);
+}
+
+static void test_interface_list_add_element_correct(void **state)
+{
+ (void) state;
+ interfaces_interface_ipv4_address_element_t *address_ipv4;
+ interfaces_interface_ipv4_address_element_t *address_elem_ipv4;
+
+ interfaces_interface_ipv4_neighbor_element_t *neighbor_ipv4;
+ interfaces_interface_ipv4_neighbor_element_t *neighbor_elem_ipv4;
+
+ interfaces_interface_ipv6_address_element_t *address_ipv6;
+ interfaces_interface_ipv6_address_element_t *address_elem_ipv6;
+
+ interfaces_interface_ipv6_neighbor_element_t *neighbor_ipv6;
+ interfaces_interface_ipv6_neighbor_element_t *neighbor_elem_ipv6;
+
+ INTERFACES_INTERFACE_LIST_NEW(address_ipv4);
+ assert_null(address_ipv4);
+
+ INTERFACES_INTERFACE_LIST_NEW(neighbor_ipv4);
+ assert_null(neighbor_ipv4);
+
+ INTERFACES_INTERFACE_LIST_NEW(address_ipv6);
+ assert_null(address_ipv6);
+
+ INTERFACES_INTERFACE_LIST_NEW(neighbor_ipv6);
+ assert_null(neighbor_ipv6);
+
+ address_elem_ipv4 = interfaces_interface_ipv4_address_new();
+ assert_null(address_elem_ipv4);
+ address_elem_ipv4 = interfaces_interface_ipv4_address_element_new();
+ assert_non_null(address_elem_ipv4);
+ INTERFACES_INTERFACE_LIST_ADD_ELEMENT(address_ipv4, address_elem_ipv4);
+ assert_non_null(address_ipv4);
+ INTERFACES_INTERFACE_LIST_FREE(address_ipv4);
+ assert_null(address_ipv4);
+
+ neighbor_elem_ipv4 = interfaces_interface_ipv4_neighbor_new();
+ assert_null(neighbor_elem_ipv4);
+ neighbor_elem_ipv4 = interfaces_interface_ipv4_neighbor_element_new();
+ assert_non_null(neighbor_elem_ipv4);
+ INTERFACES_INTERFACE_LIST_ADD_ELEMENT(neighbor_ipv4, neighbor_elem_ipv4);
+ assert_non_null(neighbor_ipv4);
+ INTERFACES_INTERFACE_LIST_FREE(neighbor_ipv4);
+ assert_null(neighbor_ipv4);
+
+ address_elem_ipv6 = interfaces_interface_ipv6_address_new();
+ assert_null(address_elem_ipv6);
+ address_elem_ipv6 = interfaces_interface_ipv6_address_element_new();
+ assert_non_null(address_elem_ipv6);
+ INTERFACES_INTERFACE_LIST_ADD_ELEMENT(address_ipv6, address_elem_ipv6);
+ assert_non_null(address_ipv6);
+ INTERFACES_INTERFACE_LIST_FREE(address_ipv6);
+ assert_null(address_ipv6);
+
+ neighbor_elem_ipv6 = interfaces_interface_ipv6_neighbor_new();
+ assert_null(neighbor_elem_ipv6);
+ neighbor_elem_ipv6 = interfaces_interface_ipv6_neighbor_element_new();
+ assert_non_null(neighbor_elem_ipv6);
+ INTERFACES_INTERFACE_LIST_ADD_ELEMENT(neighbor_ipv6, neighbor_elem_ipv6);
+ assert_non_null(neighbor_ipv6);
+ INTERFACES_INTERFACE_LIST_FREE(neighbor_ipv6);
+ assert_null(neighbor_ipv6);
+}
+
+static void test_interface_list_new_ipv4_address_correct(void **state)
+{
+ (void) state;
+
+ interfaces_interface_ipv4_address_element_t *address;
+
+ address = interfaces_interface_ipv4_address_new();
+ assert_null(address);
+}
+
+static void test_interface_list_new_ipv4_neighbor_correct(void **state)
+{
+ (void) state;
+
+ interfaces_interface_ipv4_neighbor_element_t *neighbor;
+
+ neighbor = interfaces_interface_ipv4_neighbor_new();
+ assert_null(neighbor);
+}
+
+static void test_interface_list_element_new_ipv4_address_correct(void **state)
+{
+ (void) state;
+
+ interfaces_interface_ipv4_address_element_t *address;
+
+ address = interfaces_interface_ipv4_address_new();
+ assert_null(address);
+
+ address = interfaces_interface_ipv4_address_element_new();
+ assert_non_null(address);
+
+ interfaces_interface_ipv4_address_element_free(&address);
+ assert_null(address);
+}
+
+static void test_interface_list_element_new_ipv4_neighbor_correct(void **state)
+{
+ (void) state;
+
+ interfaces_interface_ipv4_neighbor_element_t *neighbor;
+
+ neighbor = interfaces_interface_ipv4_neighbor_new();
+ assert_null(neighbor);
+
+ neighbor = interfaces_interface_ipv4_neighbor_element_new();
+ assert_non_null(neighbor);
+
+ interfaces_interface_ipv4_neighbor_element_free(&neighbor);
+ assert_null(neighbor);
+}
+
+static void test_interface_ipv4_address_netmask2prefix_correct(void **state)
+{
+ (void) state;
+
+ int rc = -1;
+ uint8_t prefix_length = 0;
+ const char *netmask = "255.255.255.0";
+
+ rc = interfaces_interface_ipv4_address_netmask2prefix(netmask, &prefix_length);
+ assert_int_equal(rc, 0);
+ assert_int_equal(prefix_length, 24);
+}
+
+static void test_interface_ipv4_address_netmask2prefix_incorrect(void **state)
+{
+ (void) state;
+
+ int rc = 0;
+ uint8_t prefix_length = 0;
+ const char *netmask = "FOOBAR";
+
+ rc = interfaces_interface_ipv4_address_netmask2prefix(netmask, &prefix_length);
+ assert_int_not_equal(rc, 0);
+}
+
+static void test_interface_list_new_ipv6_address_correct(void **state)
+{
+ (void) state;
+
+ interfaces_interface_ipv6_address_element_t *address;
+
+ address = interfaces_interface_ipv6_address_new();
+ assert_null(address);
+}
+
+static void test_interface_list_new_ipv6_neighbor_correct(void **state)
+{
+ (void) state;
+
+ interfaces_interface_ipv6_neighbor_element_t *neighbor;
+
+ neighbor = interfaces_interface_ipv6_neighbor_new();
+ assert_null(neighbor);
+}
+
+static void test_interface_list_element_new_ipv6_address_correct(void **state)
+{
+ (void) state;
+
+ interfaces_interface_ipv6_address_element_t *address;
+
+ address = interfaces_interface_ipv6_address_new();
+ assert_null(address);
+
+ address = interfaces_interface_ipv6_address_element_new();
+ assert_non_null(address);
+
+ interfaces_interface_ipv6_address_element_free(&address);
+ assert_null(address);
+}
+
+static void test_interface_list_element_new_ipv6_neighbor_correct(void **state)
+{
+ (void) state;
+
+ interfaces_interface_ipv6_neighbor_element_t *neighbor;
+
+ neighbor = interfaces_interface_ipv6_neighbor_new();
+ assert_null(neighbor);
+
+ neighbor = interfaces_interface_ipv6_neighbor_element_new();
+ assert_non_null(neighbor);
+
+ interfaces_interface_ipv6_neighbor_element_free(&neighbor);
+ assert_null(neighbor);
+}
diff --git a/tests/interfaces/manual_tests.md b/src/interfaces/tests/manual_tests.md
similarity index 99%
rename from tests/interfaces/manual_tests.md
rename to src/interfaces/tests/manual_tests.md
index 754441e3..c18a340d 100644
--- a/tests/interfaces/manual_tests.md
+++ b/src/interfaces/tests/manual_tests.md
@@ -510,7 +510,8 @@ $ sysrepocfg -X -x '/ietf-interfaces:interfaces/interface[name="lo"]/ietf-ip:ipv
The same can be done for IPv6, just replace ipv4 with ipv6 in the examples above and for the ip node add an ipv6 address.
-We can also use a netmask instead of prefix length but the following feature has to be enabled first:
+We can also use a netmask instead of prefix length, although only for IPv4.
+The following feature has to be enabled first:
```
sysrepoctl --change ietf-ip --enable-feature ipv4-non-contiguous-netmasks
```
diff --git a/src/routing/CMakeLists.txt b/src/routing/CMakeLists.txt
index eab72309..c1d0783d 100644
--- a/src/routing/CMakeLists.txt
+++ b/src/routing/CMakeLists.txt
@@ -16,7 +16,14 @@ set(
route/next_hop.c
route.c
control_plane_protocol.c
- control_plane_protocol/list.c
+ common.c
+ startup.c
+ persist.c
+
+ # subscriptions
+ subscription/change.c
+ subscription/rpc.c
+ subscription/operational.c
)
if (NOT PLUGIN)
diff --git a/src/routing/common.c b/src/routing/common.c
new file mode 100644
index 00000000..87a7240a
--- /dev/null
+++ b/src/routing/common.c
@@ -0,0 +1,437 @@
+/*
+ * telekom / sysrepo-plugin-interfaces
+ *
+ * This program is made available under the terms of the
+ * BSD 3-Clause license which is available at
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: 2022 Deutsche Telekom AG
+ * SPDX-FileContributor: Sartura Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "common.h"
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include
+
+void foreach_nexthop(struct rtnl_nexthop *nh, void *arg)
+{
+ struct route_next_hop *nexthop = arg;
+ struct nl_cache *link_cache = NULL;
+ struct nl_sock *socket = NULL;
+ struct rtnl_link *iface = NULL;
+ int ifindex = 0;
+ char *if_name = NULL;
+ int nl_err = 0;
+
+ socket = nl_socket_alloc();
+ if (socket == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "unable to init nl_sock struct...");
+ return;
+ }
+
+ nl_err = nl_connect(socket, NETLINK_ROUTE);
+ if (nl_err != 0) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "nl_connect failed (%d): %s", nl_err, nl_geterror(nl_err));
+ return;
+ }
+
+ nl_err = rtnl_link_alloc_cache(socket, AF_UNSPEC, &link_cache);
+ if (nl_err != 0) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_route_alloc_cache failed (%d): %s", nl_err, nl_geterror(nl_err));
+ return;
+ }
+
+ ifindex = rtnl_route_nh_get_ifindex(nh);
+ iface = rtnl_link_get(link_cache, ifindex);
+ if_name = xstrdup(rtnl_link_get_name(iface));
+
+ route_next_hop_add_list(nexthop, ifindex, if_name, rtnl_route_nh_get_gateway(nh));
+
+ rtnl_link_put(iface);
+ nl_cache_free(link_cache);
+ nl_socket_free(socket);
+}
+
+int routing_collect_ribs(struct nl_cache *routes_cache, struct rib_list_element **ribs_head)
+{
+ int error = 0;
+ struct rtnl_route *route = NULL;
+ char table_buffer[32] = {0};
+
+ route = (struct rtnl_route *) nl_cache_get_first(routes_cache);
+
+ while (route != NULL) {
+ // fetch table name
+ const int table_id = (int) rtnl_route_get_table(route);
+ const uint8_t af = rtnl_route_get_family(route);
+ rtnl_route_table2str(table_id, table_buffer, sizeof(table_buffer));
+
+ // add the table to the list and set properties
+ rib_list_add(ribs_head, table_buffer, af);
+
+ // default table is main (same as iproute2) - for both ipv4 and ipv6 addresses
+ if (strncmp(table_buffer, "main", sizeof("main") - 1) == 0) {
+ rib_list_set_default(ribs_head, table_buffer, af, 1);
+ }
+
+ route = (struct rtnl_route *) nl_cache_get_next((struct nl_object *) route);
+ }
+
+ // after the list is finished -> build descriptions for all ribs
+ error = routing_build_rib_descriptions(ribs_head);
+ if (error != 0) {
+ SRPLG_LOG_DBG(PLUGIN_NAME, "routing_build_rib_descriptions() failed");
+ goto error_out;
+ }
+
+ goto out;
+
+error_out:
+ SRPLG_LOG_ERR(PLUGIN_NAME, "error collecting RIBs: %d", error);
+
+out:
+ return error;
+}
+
+int routing_collect_routes(struct nl_cache *routes_cache, struct nl_cache *link_cache, struct rib_list_element **ribs_head)
+{
+ int error = 0;
+ struct rtnl_route *route = NULL;
+ struct route tmp_route = {0};
+ char table_buffer[32] = {0};
+ struct rib *tmp_rib = NULL;
+ struct rtnl_link *iface = NULL;
+ int ifindex = 0;
+ char *if_name = NULL;
+ struct rib_list_element *ribs_iter = NULL;
+ struct route_list_hash_element *routes_hash_iter = NULL;
+ struct route_list_element *routes_iter = NULL;
+
+ error = routing_collect_ribs(routes_cache, ribs_head);
+ if (error != 0) {
+ goto error_out;
+ }
+
+ // gather all routes for each table
+ route = (struct rtnl_route *) nl_cache_get_first(routes_cache);
+ while (route != NULL) {
+ // fetch table name
+ const int table_id = (int) rtnl_route_get_table(route);
+ const int route_type = (int) rtnl_route_get_type(route);
+ const uint8_t af = rtnl_route_get_family(route);
+ rtnl_route_table2str(table_id, table_buffer, sizeof(table_buffer));
+
+ // get the current RIB of the route
+ tmp_rib = rib_list_get(ribs_head, table_buffer, af);
+ if (tmp_rib == NULL) {
+ error = -1;
+ goto error_out;
+ }
+
+ // fill the route with info and add to the hash of the current RIB
+ route_init(&tmp_route);
+ route_set_preference(&tmp_route, rtnl_route_get_priority(route));
+
+ // next-hop container
+ switch (route_type) {
+ case RTN_BLACKHOLE:
+ route_next_hop_set_special(&tmp_route.next_hop, "blackhole");
+ break;
+ case RTN_UNREACHABLE:
+ route_next_hop_set_special(&tmp_route.next_hop, "unreachable");
+ break;
+ case RTN_PROHIBIT:
+ route_next_hop_set_special(&tmp_route.next_hop, "prohibit");
+ break;
+ case RTN_LOCAL:
+ route_next_hop_set_special(&tmp_route.next_hop, "local");
+ break;
+ default: {
+ const int NEXTHOP_COUNT = rtnl_route_get_nnexthops(route);
+ if (NEXTHOP_COUNT == 1) {
+ struct rtnl_nexthop *nh = rtnl_route_nexthop_n(route, 0);
+ ifindex = rtnl_route_nh_get_ifindex(nh);
+ iface = rtnl_link_get(link_cache, ifindex);
+ if_name = rtnl_link_get_name(iface);
+ route_next_hop_set_simple(&tmp_route.next_hop, ifindex, if_name, rtnl_route_nh_get_gateway(nh));
+
+ // free recieved link
+ rtnl_link_put(iface);
+ } else {
+ rtnl_route_foreach_nexthop(route, foreach_nexthop, &tmp_route.next_hop);
+ }
+ }
+ }
+
+ // route-metadata/source-protocol
+ if (rtnl_route_get_protocol(route) == RTPROT_STATIC) {
+ route_set_source_protocol(&tmp_route, "ietf-routing:static");
+ } else {
+ route_set_source_protocol(&tmp_route, "ietf-routing:direct");
+ }
+
+ // add the created route to the hash by a destination address
+ route_list_hash_add(&tmp_rib->routes_head, rtnl_route_get_dst(route), &tmp_route);
+
+ // last-updated -> TODO: implement later
+ route_free(&tmp_route);
+ route = (struct rtnl_route *) nl_cache_get_next((struct nl_object *) route);
+ }
+
+ LL_FOREACH(*ribs_head, ribs_iter)
+ {
+ struct route_list_hash_element **routes_hash_head = &ribs_iter->rib.routes_head;
+
+ LL_FOREACH(*routes_hash_head, routes_hash_iter)
+ {
+ struct route_list_element **routes_list_head = &routes_hash_iter->routes_head;
+ if (*routes_list_head != NULL) {
+ struct route *pref = &(*routes_list_head)->route;
+
+ LL_FOREACH(*routes_list_head, routes_iter)
+ {
+ // struct route *ptr = &ls->list[k];
+ if (routes_iter->route.preference < pref->preference) {
+ pref = &routes_iter->route;
+ }
+ }
+
+ pref->metadata.active = 1;
+ }
+ }
+ }
+
+error_out:
+ return error;
+}
+
+int routing_build_rib_descriptions(struct rib_list_element **ribs_head)
+{
+ int error = 0;
+
+ char name_buffer[32 + 5] = {0};
+ struct rib_list_element *ribs_iter = NULL;
+
+ struct rib_description_pair default_descriptions[] = {
+ {"ipv4-main", "main routing table - normal routing table containing all non-policy routes (ipv4 only)"},
+ {"ipv6-main", "main routing table - normal routing table containing all non-policy routes (ipv6 only)"},
+ {"ipv4-default", "default routing table - empty and reserved for post-processing if previous default rules did not select the packet (ipv4 only)"},
+ {"ipv6-default", "default routing table - empty and reserved for post-processing if previous default rules did not select the packet (ipv6 only)"},
+ {"ipv4-local", "local routing table - maintained by the kernel, containing high priority control routes for local and broadcast addresses (ipv4 only)"},
+ {"ipv6-local", "local routing table - maintained by the kernel, containing high priority control routes for local and broadcast addresses (ipv6 only)"},
+ };
+
+ // now iterate over each rib and set its description
+ LL_FOREACH(*ribs_head, ribs_iter)
+ {
+ const struct rib *RIB = &ribs_iter->rib;
+ const int TABLE_ID = rtnl_route_str2table(RIB->name);
+
+ // for default, local and main add prefixes, for other use given names
+ if (routing_is_rib_known(TABLE_ID)) {
+ snprintf(name_buffer, sizeof(name_buffer), "%s-%s", RIB->address_family == AF_INET ? "ipv4" : "ipv6", RIB->name);
+ } else {
+ snprintf(name_buffer, sizeof(name_buffer), "%s", RIB->name);
+ }
+
+ const size_t NAME_LEN = strlen(name_buffer);
+
+ // set the description for each AF
+ for (size_t j = 0; j < sizeof(default_descriptions) / sizeof(struct rib_description_pair); j++) {
+ const struct rib_description_pair *PAIR = &default_descriptions[j];
+ if (strncmp(name_buffer, PAIR->name, NAME_LEN) == 0) {
+ memcpy(((struct rib *) RIB)->description, PAIR->description, sizeof(PAIR->description));
+ break;
+ }
+ }
+ }
+
+ return error;
+}
+
+int routing_is_rib_known(int table)
+{
+ return table == RT_TABLE_DEFAULT || table == RT_TABLE_LOCAL || table == RT_TABLE_MAIN;
+}
+
+int routing_apply_new_routes(struct nl_sock *socket, struct route_list_hash_element *routes_hash)
+{
+ int error = 0;
+ int nl_err = 0;
+
+ // libnl
+ struct rtnl_route *route = NULL;
+ struct rtnl_nexthop *next_hop = NULL;
+ struct nl_addr *dst_addr = NULL;
+
+ // plugin
+ struct route_list_hash_element *routes_iter = NULL;
+ struct route_list_element *route_iter = NULL;
+
+ LL_FOREACH(routes_hash, routes_iter)
+ {
+ LL_FOREACH(routes_iter->routes_head, route_iter)
+ {
+ route = rtnl_route_alloc();
+ if (route == NULL) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "unable to alloc rtnl_route struct");
+ goto error_out;
+ }
+
+ dst_addr = nl_addr_clone(routes_iter->prefix);
+
+ rtnl_route_set_table(route, RT_TABLE_MAIN);
+ rtnl_route_set_protocol(route, RTPROT_STATIC);
+ rtnl_route_set_dst(route, dst_addr);
+ rtnl_route_set_priority(route, route_iter->route.preference);
+
+ if (route_iter->route.next_hop.kind == route_next_hop_kind_simple) {
+ next_hop = rtnl_route_nh_alloc();
+ if (next_hop == NULL) {
+ error = -1;
+ SRPLG_LOG_ERR(PLUGIN_NAME, "unable to alloc rtnl_nexthop struct");
+ goto error_out;
+ }
+
+ if (route_iter->route.next_hop.value.simple.if_name == NULL && route_iter->route.next_hop.value.simple.addr == NULL) {
+ error = -1;
+ SRPLG_LOG_ERR(PLUGIN_NAME, "outgoing-interface and next-hop-address can't both be NULL");
+ goto error_out;
+ }
+
+ if (route_iter->route.next_hop.value.simple.if_name != NULL) {
+ rtnl_route_nh_set_ifindex(next_hop, route_iter->route.next_hop.value.simple.ifindex);
+ }
+
+ if (route_iter->route.next_hop.value.simple.addr != NULL) {
+ rtnl_route_nh_set_gateway(next_hop, route_iter->route.next_hop.value.simple.addr);
+ }
+ rtnl_route_add_nexthop(route, next_hop);
+ } else if (route_iter->route.next_hop.kind == route_next_hop_kind_list) {
+ struct route_next_hop_list_element *nexthop_iter = NULL;
+
+ LL_FOREACH(route_iter->route.next_hop.value.list_head, nexthop_iter)
+ {
+ next_hop = rtnl_route_nh_alloc();
+ if (next_hop == NULL) {
+ error = -1;
+ SRPLG_LOG_ERR(PLUGIN_NAME, "unable to alloc rtnl_nexthop struct");
+ goto error_out;
+ }
+
+ rtnl_route_nh_set_ifindex(next_hop, nexthop_iter->simple.ifindex);
+ rtnl_route_nh_set_gateway(next_hop, nexthop_iter->simple.addr);
+ rtnl_route_add_nexthop(route, next_hop);
+ }
+ }
+
+ rtnl_route_set_scope(route, (uint8_t) rtnl_route_guess_scope(route));
+
+ // create new route
+ nl_err = rtnl_route_add(socket, route, NLM_F_CREATE);
+ if (nl_err != 0) {
+ error = -1;
+ SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_route_add() failed (%d): %s", nl_err, nl_geterror(nl_err));
+ goto error_out;
+ }
+
+ nl_addr_put(dst_addr);
+ rtnl_route_put(route);
+ route = NULL;
+ dst_addr = NULL;
+ }
+ }
+
+ goto out;
+
+error_out:
+ error = -1;
+
+out:
+ if (dst_addr) {
+ nl_addr_put(dst_addr);
+ }
+
+ if (route) {
+ rtnl_route_put(route);
+ }
+
+ return error;
+}
+
+int routing_apply_modify_routes(struct nl_sock *socket, struct route_list_hash_element *routes_hash)
+{
+ int error = 0;
+
+ return error;
+}
+
+int routing_apply_delete_routes(struct nl_sock *socket, struct route_list_hash_element *routes_hash)
+{
+ int error = 0;
+ int nl_err = 0;
+
+ // libnl
+ struct rtnl_route *route = NULL;
+ struct nl_addr *dst_addr = NULL;
+
+ // plugin
+ struct route_list_hash_element *routes_iter = NULL;
+ struct route_list_element *route_iter = NULL;
+
+ LL_FOREACH(routes_hash, routes_iter)
+ {
+ LL_FOREACH(routes_iter->routes_head, route_iter)
+ {
+ route = rtnl_route_alloc();
+ if (route == NULL) {
+ error = -1;
+ SRPLG_LOG_ERR(PLUGIN_NAME, "unable to alloc rtnl_route struct");
+ goto error_out;
+ }
+
+ // alloc destination prefix for route
+ dst_addr = nl_addr_clone(routes_iter->prefix);
+
+ // setup route for deletion
+ rtnl_route_set_table(route, RT_TABLE_MAIN);
+ rtnl_route_set_protocol(route, RTPROT_STATIC);
+ rtnl_route_set_dst(route, dst_addr);
+ rtnl_route_set_priority(route, route_iter->route.preference);
+ rtnl_route_set_scope(route, RT_SCOPE_NOWHERE);
+
+ // delete route
+ nl_err = rtnl_route_delete(socket, route, 0);
+ if (nl_err != 0) {
+ SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_route_delete() failed (%d): %s", nl_err, nl_geterror(nl_err));
+ error = -1;
+ goto error_out;
+ }
+
+ nl_addr_put(dst_addr);
+ rtnl_route_put(route);
+ route = NULL;
+ dst_addr = NULL;
+ }
+ }
+
+error_out:
+ if (dst_addr) {
+ nl_addr_put(dst_addr);
+ }
+
+ if (route) {
+ rtnl_route_put(route);
+ }
+
+ return error;
+}
\ No newline at end of file
diff --git a/src/routing/common.h b/src/routing/common.h
new file mode 100644
index 00000000..ea3c0be9
--- /dev/null
+++ b/src/routing/common.h
@@ -0,0 +1,61 @@
+/*
+ * telekom / sysrepo-plugin-interfaces
+ *
+ * This program is made available under the terms of the
+ * BSD 3-Clause license which is available at
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: 2022 Deutsche Telekom AG
+ * SPDX-FileContributor: Sartura Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef ROUTING_PLUGIN_COMMON_H
+#define ROUTING_PLUGIN_COMMON_H
+
+#include "route/list_hash.h"
+#define PLUGIN_NAME "routing plugin"
+
+#define ROUTING_RIBS_COUNT 256
+#define ROUTING_PROTOS_COUNT 256
+
+#define BASE_YANG_MODEL "ietf-routing"
+
+// base of all - routing container
+#define ROUTING_CONTAINER_YANG_PATH "/" BASE_YANG_MODEL ":routing"
+
+// router ID
+#define ROUTING_ROUTER_ID_YANG_PATH ROUTING_CONTAINER_YANG_PATH "/router-id"
+
+// interfaces container
+#define ROUTING_INTERFACES_CONTAINER_YANG_PATH ROUTING_CONTAINER_YANG_PATH "/interfaces"
+#define ROUTING_INTERFACE_LEAF_LIST_YANG_PATH ROUTING_INTERFACES_CONTAINER_YANG_PATH "/interface"
+
+// control plane protocols container
+#define ROUTING_CONTROL_PLANE_PROTOCOLS_CONTAINER_YANG_PATH ROUTING_CONTAINER_YANG_PATH "/control-plane-protocols"
+#define ROUTING_CONTROL_PLANE_PROTOCOL_LIST_YANG_PATH ROUTING_CONTROL_PLANE_PROTOCOLS_CONTAINER_YANG_PATH "/control-plane-protocol"
+
+// static routes
+#define ROUTING_STATIC_ROUTES_CONTAINER_YANG_PATH ROUTING_CONTROL_PLANE_PROTOCOL_LIST_YANG_PATH "[type=\"ietf-routing:static\"][name=\"static\"]/static-routes"
+
+// ribs
+#define ROUTING_RIBS_CONTAINER_YANG_PATH ROUTING_CONTAINER_YANG_PATH "/ribs"
+#define ROUTING_RIB_LIST_YANG_PATH ROUTING_RIBS_CONTAINER_YANG_PATH "/rib"
+#define ROUTING_RIB_LIST_ACTIVE_ROUTE_RPC_PATH ROUTING_RIB_LIST_YANG_PATH "/active-route"
+
+#include
+#include
+
+void foreach_nexthop(struct rtnl_nexthop *nh, void *arg);
+
+int routing_collect_ribs(struct nl_cache *routes_cache, struct rib_list_element **ribs_head);
+int routing_collect_routes(struct nl_cache *routes_cache, struct nl_cache *link_cache, struct rib_list_element **ribs_head);
+int routing_build_rib_descriptions(struct rib_list_element **ribs_head);
+int routing_is_rib_known(int table);
+
+int routing_apply_new_routes(struct nl_sock *socket, struct route_list_hash_element *routes_hash);
+int routing_apply_modify_routes(struct nl_sock *socket, struct route_list_hash_element *routes_hash);
+int routing_apply_delete_routes(struct nl_sock *socket, struct route_list_hash_element *routes_hash);
+
+#endif // ROUTING_PLUGIN_COMMON_H
\ No newline at end of file
diff --git a/src/routing/context.h b/src/routing/context.h
new file mode 100644
index 00000000..fe724e3d
--- /dev/null
+++ b/src/routing/context.h
@@ -0,0 +1,25 @@
+/*
+ * telekom / sysrepo-plugin-interfaces
+ *
+ * This program is made available under the terms of the
+ * BSD 3-Clause license which is available at
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: 2022 Deutsche Telekom AG
+ * SPDX-FileContributor: Sartura Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef ROUTING_PLUGIN_CONTEXT_H
+#define ROUTING_PLUGIN_CONTEXT_H
+
+#include
+
+#include "route/list_hash.h"
+
+struct routing_ctx {
+ sr_session_ctx_t *startup_session;
+};
+
+#endif // ROUTING_PLUGIN_CONTEXT_H
\ No newline at end of file
diff --git a/src/routing/control_plane_protocol.c b/src/routing/control_plane_protocol.c
index ac382950..65fe1001 100644
--- a/src/routing/control_plane_protocol.c
+++ b/src/routing/control_plane_protocol.c
@@ -1,7 +1,21 @@
-#include "route/list_hash.h"
+/*
+ * telekom / sysrepo-plugin-interfaces
+ *
+ * This program is made available under the terms of the
+ * BSD 3-Clause license which is available at
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: 2022 Deutsche Telekom AG
+ * SPDX-FileContributor: Sartura Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
#include "control_plane_protocol.h"
#include "utils/memory.h"
+#include
+
void control_plane_protocol_init(struct control_plane_protocol *cpp)
{
cpp->type = NULL;
diff --git a/src/routing/control_plane_protocol.h b/src/routing/control_plane_protocol.h
index cbb169ff..4d2111bc 100644
--- a/src/routing/control_plane_protocol.h
+++ b/src/routing/control_plane_protocol.h
@@ -1,16 +1,21 @@
+/*
+ * telekom / sysrepo-plugin-interfaces
+ *
+ * This program is made available under the terms of the
+ * BSD 3-Clause license which is available at
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: 2022 Deutsche Telekom AG
+ * SPDX-FileContributor: Sartura Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
#ifndef ROUTING_CONTROL_PLANE_PROTOCOL_H
#define ROUTING_CONTROL_PLANE_PROTOCOL_H
#include "route/list_hash.h"
-struct static_routes_ipv4 {
- struct route_list_hash routes;
-};
-
-struct static_routes_ipv6 {
- struct route_list_hash routes;
-};
-
struct control_plane_protocol {
char *type;
char *description;
diff --git a/src/routing/control_plane_protocol/list.c b/src/routing/control_plane_protocol/list.c
deleted file mode 100644
index 8223e923..00000000
--- a/src/routing/control_plane_protocol/list.c
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "control_plane_protocol/list.h"
-
-void control_plane_protocol_list_init(struct control_plane_protocol_list *ls)
-{
- ls->list = NULL;
- ls->size = 0;
-}
-
-void control_plane_protocol_list_free(struct control_plane_protocol_list *ls)
-{
- for (int i = 0; i < ls->size; i++) {
- }
-}
diff --git a/src/routing/control_plane_protocol/list.h b/src/routing/control_plane_protocol/list.h
deleted file mode 100644
index 21b7084d..00000000
--- a/src/routing/control_plane_protocol/list.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef ROUTING_CONTROL_PLANE_PROTOCOL_LIST_H
-#define ROUTING_CONTROL_PLANE_PROTOCOL_LIST_H
-
-#include "control_plane_protocol.h"
-
-struct control_plane_protocol_list {
- struct control_plane_protocol *list;
- size_t size;
-};
-
-void control_plane_protocol_list_init(struct control_plane_protocol_list *ls);
-void control_plane_protocol_list_free(struct control_plane_protocol_list *ls);
-
-#endif // ROUTING_CONTROL_PLANE_PROTOCOL_LIST_H
diff --git a/src/routing/main.c b/src/routing/main.c
index ae28c246..8cf264bf 100644
--- a/src/routing/main.c
+++ b/src/routing/main.c
@@ -1,7 +1,21 @@
+/*
+ * telekom / sysrepo-plugin-interfaces
+ *
+ * This program is made available under the terms of the
+ * BSD 3-Clause license which is available at
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: 2022 Deutsche Telekom AG
+ * SPDX-FileContributor: Sartura Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
#include
#include