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 #include #include +#include volatile int exit_application = 0; @@ -19,19 +33,19 @@ int main(void) /* connect to sysrepo */ error = sr_connect(SR_CONN_DEFAULT, &connection); if (error) { - SRP_LOG_ERR("sr_connect error (%d): %s", error, sr_strerror(error)); + SRPLG_LOG_ERR(PLUGIN_NAME, "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)); + SRPLG_LOG_ERR(PLUGIN_NAME, "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"); + SRPLG_LOG_ERR(PLUGIN_NAME, "sr_plugin_init_cb error"); goto out; } @@ -51,6 +65,6 @@ int main(void) static void sigint_handler(__attribute__((unused)) int signum) { - SRP_LOG_INF("Sigint called, exiting..."); + SRPLG_LOG_INF(PLUGIN_NAME, "Sigint called, exiting..."); exit_application = 1; } diff --git a/src/routing/persist.c b/src/routing/persist.c new file mode 100644 index 00000000..0c950876 --- /dev/null +++ b/src/routing/persist.c @@ -0,0 +1,178 @@ +/* + * 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 "persist.h" +#include "libyang/log.h" +#include "libyang/out.h" +#include "libyang/printer_data.h" +#include "libyang/tree_data.h" +#include "netlink/socket.h" +#include "route/list_hash.h" +#include "route/next_hop.h" + +#include +#include +#include + +#include +#include + +#include + +// gather routes from the running datastore which will be applied to the system +static int gather_static_routes(struct route_list_hash_element **routes_head, sr_session_ctx_t *session, const char *augment_path); + +int routing_persist_static_routes(sr_session_ctx_t *session) +{ + int error = 0; + struct route_list_hash_element *routes_head = NULL; + struct nl_sock *socket = NULL; + + // netlink + socket = nl_socket_alloc(); + if (socket == NULL) { + error = -1; + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to init nl_sock struct..."); + goto error_out; + } + + error = nl_connect(socket, NETLINK_ROUTE); + if (error != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "nl_connect() failed (%d): %s", error, nl_geterror(error)); + goto error_out; + } + + error = gather_static_routes(&routes_head, session, ROUTING_STATIC_ROUTES_CONTAINER_YANG_PATH "/ietf-ipv4-unicast-routing:ipv4"); + if (error != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "Unable to gather static IPv4 routes from the running datastore"); + goto error_out; + } + + error = gather_static_routes(&routes_head, session, ROUTING_STATIC_ROUTES_CONTAINER_YANG_PATH "/ietf-ipv6-unicast-routing:ipv6"); + if (error != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "Unable to gather static IPv6 routes from the running datastore"); + goto error_out; + } + + error = routing_apply_new_routes(socket, routes_head); + if (error != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "routing_apply_new_routes() failed (%d)", error); + goto error_out; + } + + goto out; + +error_out: + error = -1; + +out: + nl_socket_free(socket); + route_list_hash_free(&routes_head); + return error; +} + +static int gather_static_routes(struct route_list_hash_element **routes_head, sr_session_ctx_t *session, const char *augment_path) +{ + int error = 0; + LY_ERR ly_error = 0; + + // sysrepo + sr_data_t *static_routes_container = NULL; + + // libnl + struct nl_addr *prefix = NULL, *gateway = NULL; + + // libyang + const struct ly_ctx *ly_context = sr_session_acquire_context(session); + struct lyd_node *route_node_iterator = NULL; + + error = sr_get_subtree(session, augment_path, 0, &static_routes_container); + if (error != SR_ERR_OK) { + SRPLG_LOG_INF(PLUGIN_NAME, "sr_get_subtree() failed (%d): %s", error, sr_strerror(error)); + SRPLG_LOG_INF(PLUGIN_NAME, "Static routes path (%s) doesn't exist in the running datastore", augment_path); + goto out; + } + + route_node_iterator = lyd_child(static_routes_container->tree); + + while (route_node_iterator) { + struct lyd_node *prefix_node = NULL, *next_hop_address = NULL, *outgoing_interface = NULL; + + ly_error = lyd_find_path(route_node_iterator, "destination-prefix", 0, &prefix_node); + if (ly_error != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "lyd_find_path() failed (%d): %s", ly_error, ly_errmsg(ly_context)); + goto error_out; + } + + ly_error = lyd_find_path(route_node_iterator, "next-hop/next-hop-address", 0, &next_hop_address); + if (ly_error != LY_SUCCESS) { + next_hop_address = NULL; + SRPLG_LOG_INF(PLUGIN_NAME, "lyd_find_path() failed (%d): %s", ly_error, ly_errmsg(ly_context)); + SRPLG_LOG_INF(PLUGIN_NAME, "next-hop-address node not found, trying outgoing-interface"); + } + + ly_error = lyd_find_path(route_node_iterator, "next-hop/outgoing-interface", 0, &outgoing_interface); + if (ly_error != LY_SUCCESS) { + outgoing_interface = NULL; + SRPLG_LOG_ERR(PLUGIN_NAME, "lyd_find_path() failed (%d): %s", ly_error, ly_errmsg(ly_context)); + if (!next_hop_address) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to get next-hop-address or outgoing-interface node"); + goto error_out; + } + } + + SRPLG_LOG_DBG(PLUGIN_NAME, "destination-prefix: %s", lyd_get_value(prefix_node)); + + error = nl_addr_parse(lyd_get_value(prefix_node), AF_UNSPEC, &prefix); + if (error != 0) { + SRPLG_LOG_INF(PLUGIN_NAME, "nl_addr_parse() failed (%d): %s", ly_error, ly_errmsg(ly_context)); + } + + // create new route + route_list_hash_add(routes_head, prefix, &(struct route){0}); + struct route_list_element **route_list_head = route_list_hash_get(routes_head, prefix); + + nl_addr_put(prefix); + + if (next_hop_address != NULL) { + SRPLG_LOG_DBG(PLUGIN_NAME, "next-hop: %s", lyd_get_value(next_hop_address)); + error = nl_addr_parse(lyd_get_value(next_hop_address), AF_UNSPEC, &gateway); + if (error != 0) { + SRPLG_LOG_INF(PLUGIN_NAME, "nl_addr_parse() failed (%d): %s", ly_error, ly_errmsg(ly_context)); + } + route_next_hop_set_simple_gateway(&(*route_list_head)->route.next_hop, gateway); + + // cleanup + nl_addr_put(gateway); + } + if (outgoing_interface != NULL) { + SRPLG_LOG_DBG(PLUGIN_NAME, "outgoing-interface: %s", lyd_get_value(outgoing_interface)); + const int ifindex = (int) if_nametoindex(lyd_get_value(outgoing_interface)); + route_next_hop_set_simple_interface(&(*route_list_head)->route.next_hop, ifindex, lyd_get_value(outgoing_interface)); + } + + route_node_iterator = route_node_iterator->next; + } + + goto out; + +error_out: + error = -1; + +out: + + lyd_free_tree(static_routes_container->tree); + FREE_SAFE(static_routes_container); + + return error; +} \ No newline at end of file diff --git a/src/interfaces/ipv4_data.h b/src/routing/persist.h similarity index 54% rename from src/interfaces/ipv4_data.h rename to src/routing/persist.h index 37964bed..545c85d7 100644 --- a/src/interfaces/ipv4_data.h +++ b/src/routing/persist.h @@ -5,17 +5,17 @@ * BSD 3-Clause license which is available at * https://opensource.org/licenses/BSD-3-Clause * - * SPDX-FileCopyrightText: 2021 Deutsche Telekom AG + * SPDX-FileCopyrightText: 2022 Deutsche Telekom AG * SPDX-FileContributor: Sartura Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef IPv4_DATA_H_ONCE -#define IPv4_DATA_H_ONCE +#ifndef ROUTING_PLUGIN_PERSIST_H +#define ROUTING_PLUGIN_PERSIST_H -#include "ip_data.h" +#include -typedef ip_data_t ipv4_data_t; +int routing_persist_static_routes(sr_session_ctx_t *session); -#endif // IPv4_DATA_H_ONCE +#endif // ROUTING_PLUGIN_PERSIST_H \ No newline at end of file diff --git a/src/routing/rib.c b/src/routing/rib.c index 02c4acc7..f2ef5a7e 100644 --- a/src/routing/rib.c +++ b/src/routing/rib.c @@ -1,3 +1,16 @@ +/* + * 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 #include @@ -16,7 +29,7 @@ void rib_init(struct rib *rib) memset(rib->name, 0, sizeof(rib->name)); memset(rib->description, 0, sizeof(rib->description)); - route_list_hash_init(&rib->routes); + route_list_hash_init(&rib->routes_head); } void rib_set_address_family(struct rib *rib, int af) @@ -41,5 +54,5 @@ void rib_set_name(struct rib *rib, char *buff) void rib_free(struct rib *rib) { - route_list_hash_free(&rib->routes); + route_list_hash_free(&rib->routes_head); } diff --git a/src/routing/rib.h b/src/routing/rib.h index 11fb4da5..a07d0404 100644 --- a/src/routing/rib.h +++ b/src/routing/rib.h @@ -1,3 +1,16 @@ +/* + * 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_RIB_H #define ROUTING_RIB_H @@ -9,7 +22,7 @@ struct rib { char description[ROUTING_RIB_DESCRIPTION_SIZE]; int address_family; int default_rib; - struct route_list_hash routes; + struct route_list_hash_element *routes_head; }; void rib_init(struct rib *rib); diff --git a/src/routing/rib/description_pair.h b/src/routing/rib/description_pair.h index f475844a..d483ed3f 100644 --- a/src/routing/rib/description_pair.h +++ b/src/routing/rib/description_pair.h @@ -1,3 +1,16 @@ +/* + * 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_RIB_DESCRIPTION_PAIR_HPP #define ROUTING_RIB_DESCRIPTION_PAIR_HPP diff --git a/src/routing/rib/list.c b/src/routing/rib/list.c index df2a696e..81ceef8e 100644 --- a/src/routing/rib/list.c +++ b/src/routing/rib/list.c @@ -1,3 +1,16 @@ +/* + * 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 #include @@ -9,67 +22,95 @@ #include "rib.h" #include "rib/list.h" -void rib_list_init(struct rib_list *ls) +#include + +static int rib_list_element_cmp(struct rib_list_element *e1, struct rib_list_element *e2); + +void rib_list_init(struct rib_list_element **head) { - ls->list = NULL; - ls->size = 0; + *head = NULL; } -void rib_list_add(struct rib_list *ls, char *name, int af) +void rib_list_add(struct rib_list_element **head, char *name, int af) { - struct rib *exists = rib_list_get(ls, name, af); - if (exists == NULL) { - struct rib *ptr = NULL; - ls->list = realloc(ls->list, sizeof(struct rib) * (unsigned) (ls->size + 1)); - ptr = &ls->list[ls->size]; + struct rib *found = NULL; + struct rib_list_element *new_rib = NULL; - rib_init(ptr); - rib_set_name(ptr, name); - rib_set_address_family(ptr, af); + found = rib_list_get(head, name, af); + if (!found) { + new_rib = xmalloc(sizeof(*new_rib)); + new_rib->next = NULL; - ls->size += 1; + rib_init(&new_rib->rib); + rib_set_name(&new_rib->rib, name); + rib_set_address_family(&new_rib->rib, af); + + LL_APPEND(*head, new_rib); } } -int rib_list_set_description(struct rib_list *ls, char *name, int af, const char *desc) +int rib_list_set_description(struct rib_list_element **head, char *name, int af, const char *desc) { - struct rib *rib = rib_list_get(ls, name, af); - if (rib == NULL) { - return -1; + int error = 0; + struct rib *rib = NULL; + + rib = rib_list_get(head, name, af); + if (rib) { + rib_set_description(rib, desc); + } else { + error = -1; } - rib_set_description(rib, desc); - return 0; + return error; } -int rib_list_set_default(struct rib_list *ls, char *name, int af, int def) +int rib_list_set_default(struct rib_list_element **head, char *name, int af, int def) { - struct rib *rib = rib_list_get(ls, name, af); - if (rib == NULL) { - return -1; + int error = 0; + struct rib *rib = NULL; + + rib = rib_list_get(head, name, af); + if (rib) { + rib_set_default(rib, def); + } else { + error = -1; } - rib_set_default(rib, def); - return 0; + return error; } -struct rib *rib_list_get(struct rib_list *ls, char *name, int af) +struct rib *rib_list_get(struct rib_list_element **head, char *name, int af) { - for (size_t i = 0; i < ls->size; i++) { - struct rib *ptr = &ls->list[i]; - if (strcmp(ptr->name, name) == 0 && ptr->address_family == af) { - return &ls->list[i]; - } + struct rib_list_element *found = NULL; + struct rib_list_element find_element = {0}; + struct rib *rib_ptr = NULL; + + // setup find element + rib_set_name(&find_element.rib, name); + rib_set_address_family(&find_element.rib, af); + + LL_SEARCH(*head, found, &find_element, rib_list_element_cmp); + if (found) { + rib_ptr = &found->rib; } - return NULL; + + return rib_ptr; } -void rib_list_free(struct rib_list *ls) +void rib_list_free(struct rib_list_element **head) { - if (ls->list) { - for (size_t i = 0; i < ls->size; i++) { - rib_free(&ls->list[i]); - } - FREE_SAFE(ls->list); + struct rib_list_element *iter = NULL, *tmp = NULL; + + LL_FOREACH_SAFE(*head, iter, tmp) + { + LL_DELETE(*head, iter); + rib_free(&iter->rib); + free(iter); } } + +static int rib_list_element_cmp(struct rib_list_element *e1, struct rib_list_element *e2) +{ + // return only eq/neq + return !(strcmp(e1->rib.name, e2->rib.name) == 0 && e1->rib.address_family == e2->rib.address_family); +} \ No newline at end of file diff --git a/src/routing/rib/list.h b/src/routing/rib/list.h index 78a32521..cd5e00a8 100644 --- a/src/routing/rib/list.h +++ b/src/routing/rib/list.h @@ -1,18 +1,31 @@ +/* + * 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_RIB_LIST_H #define ROUTING_RIB_LIST_H #include "rib.h" -struct rib_list { - struct rib *list; - size_t size; +struct rib_list_element { + struct rib rib; + struct rib_list_element *next; }; -void rib_list_init(struct rib_list *ls); -void rib_list_add(struct rib_list *ls, char *name, int af); -int rib_list_set_description(struct rib_list *ls, char *name, int af, const char *desc); -int rib_list_set_default(struct rib_list *ls, char *name, int af, int def); -struct rib *rib_list_get(struct rib_list *ls, char *name, int af); -void rib_list_free(struct rib_list *ls); +void rib_list_init(struct rib_list_element **head); +void rib_list_add(struct rib_list_element **head, char *name, int af); +int rib_list_set_description(struct rib_list_element **head, char *name, int af, const char *desc); +int rib_list_set_default(struct rib_list_element **head, char *name, int af, int def); +struct rib *rib_list_get(struct rib_list_element **head, char *name, int af); +void rib_list_free(struct rib_list_element **head); #endif // ROUTING_RIB_LIST_H diff --git a/src/routing/route.c b/src/routing/route.c index 03648085..9fc71199 100644 --- a/src/routing/route.c +++ b/src/routing/route.c @@ -1,3 +1,16 @@ +/* + * 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 "route.h" #include "utils/memory.h" @@ -35,6 +48,13 @@ void route_set_last_updated(struct route *route, char *last_up) } } +void route_set_description(struct route *route, const char *description) +{ + if (description) { + route->metadata.description = xstrdup(description); + } +} + struct route route_clone(struct route *route) { struct route out; @@ -45,6 +65,7 @@ struct route route_clone(struct route *route) route_set_active(&out, route->metadata.active); route_set_source_protocol(&out, route->metadata.source_protocol); route_set_last_updated(&out, route->metadata.last_updated); + route_set_description(&out, route->metadata.description); out.next_hop = route_next_hop_clone(&route->next_hop); return out; @@ -55,6 +76,7 @@ void route_free(struct route *route) if (route->metadata.source_protocol) { FREE_SAFE(route->metadata.source_protocol); } + if (route->metadata.last_updated) { FREE_SAFE(route->metadata.last_updated); } diff --git a/src/routing/route.h b/src/routing/route.h index b7844234..403b877a 100644 --- a/src/routing/route.h +++ b/src/routing/route.h @@ -1,3 +1,16 @@ +/* + * 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_ROUTE_H #define ROUTING_ROUTE_H @@ -25,6 +38,7 @@ void route_set_preference(struct route *route, uint32_t pref); void route_set_active(struct route *route, bool active); void route_set_source_protocol(struct route *route, char *proto); void route_set_last_updated(struct route *route, char *last_up); +void route_set_description(struct route *route, const char *description); struct route route_clone(struct route *route); void route_free(struct route *route); diff --git a/src/routing/route/list.c b/src/routing/route/list.c index 5fb7a002..2de5e3b8 100644 --- a/src/routing/route/list.c +++ b/src/routing/route/list.c @@ -1,40 +1,54 @@ +/* + * 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 "route/list.h" #include "route.h" #include "utils/memory.h" -void route_list_init(struct route_list *ls) -{ - ls->list = NULL; - ls->size = 0; - ls->delete = false; -} +#include -bool route_list_is_empty(struct route_list *ls) +void route_list_init(struct route_list_element **head) { - return ls->list == NULL && ls->size == 0; + *head = NULL; } -void route_list_add(struct route_list *ls, struct route *route) +bool route_list_is_empty(struct route_list_element **head) { - ls->list = xrealloc(ls->list, sizeof(struct route) * (unsigned long) (ls->size + 1)); - ls->list[ls->size] = route_clone(route); - ls->size += 1; + return *head == NULL; } -struct route *route_list_get_last(struct route_list *ls) +void route_list_add(struct route_list_element **head, struct route *route) { - return &ls->list[ls->size - 1]; + struct route_list_element *new_route = NULL; + + new_route = xmalloc(sizeof(*new_route)); + new_route->next = NULL; + new_route->route = route_clone(route); + + // use prepend - used when adding static routes - head is always the newest element and can be modified easily + LL_PREPEND(*head, new_route); } -void route_list_free(struct route_list *ls) +void route_list_free(struct route_list_element **head) { - if (ls->list) { - for (size_t i = 0; i < ls->size; i++) { - route_free(&ls->list[i]); - } - FREE_SAFE(ls->list); + struct route_list_element *iter = NULL, *tmp = NULL; + + LL_FOREACH_SAFE(*head, iter, tmp) + { + LL_DELETE(*head, iter); + route_free(&iter->route); + free(iter); } - route_list_init(ls); -} +} \ No newline at end of file diff --git a/src/routing/route/list.h b/src/routing/route/list.h index 641b3f42..70ca2b7d 100644 --- a/src/routing/route/list.h +++ b/src/routing/route/list.h @@ -1,18 +1,29 @@ +/* + * 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_ROUTE_LIST_H #define ROUTING_ROUTE_LIST_H #include "route.h" -struct route_list { - struct route *list; - bool delete; - size_t size; +struct route_list_element { + struct route route; + struct route_list_element *next; }; -void route_list_init(struct route_list *ls); -bool route_list_is_empty(struct route_list *ls); -void route_list_add(struct route_list *ls, struct route *route); -struct route *route_list_get_last(struct route_list *ls); -void route_list_free(struct route_list *ls); +void route_list_init(struct route_list_element **head); +bool route_list_is_empty(struct route_list_element **head); +void route_list_add(struct route_list_element **head, struct route *route); +void route_list_free(struct route_list_element **head); #endif // ROUTING_ROUTE_LIST_H diff --git a/src/routing/route/list_hash.c b/src/routing/route/list_hash.c index bf26e122..adb3771c 100644 --- a/src/routing/route/list_hash.c +++ b/src/routing/route/list_hash.c @@ -1,70 +1,81 @@ +/* + * 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 "netlink/addr.h" +#include "route/list.h" #include "route/list_hash.h" #include "utils/memory.h" -void route_list_hash_init(struct route_list_hash *hash) +#include + +static int route_list_hash_element_cmp(struct route_list_hash_element *e1, struct route_list_hash_element *e2); + +void route_list_hash_init(struct route_list_hash_element **head) { - hash->list_addr = NULL; - hash->list_route = NULL; - hash->size = 0; + *head = NULL; } -void route_list_hash_add(struct route_list_hash *hash, struct nl_addr *addr, struct route *route) +void route_list_hash_add(struct route_list_hash_element **head, struct nl_addr *addr, struct route *route) { - struct route_list *exists = NULL; + struct route_list_element **routes_head = NULL; + struct route_list_hash_element *new_hash = NULL; - exists = route_list_hash_get_by_addr(hash, addr); - if (exists != NULL) { - route_list_add(exists, route); + routes_head = route_list_hash_get(head, addr); + if (routes_head) { + route_list_add(routes_head, route); } else { - for (size_t i = 0; i < hash->size; i++) { - if (route_list_is_empty(&hash->list_route[i]) && hash->list_addr[i] == NULL) { - hash->list_addr[i] = nl_addr_clone(addr); - route_list_add(&hash->list_route[i], route); - return; - } - } + // create new hash + new_hash = xmalloc(sizeof(*new_hash)); + new_hash->prefix = nl_addr_clone(addr); + new_hash->next = NULL; + new_hash->routes_head = NULL; - hash->list_addr = xrealloc(hash->list_addr, sizeof(struct nl_addr *) * (unsigned long) (hash->size + 1)); - hash->list_route = xrealloc(hash->list_route, sizeof(struct route_list) * (unsigned long) (hash->size + 1)); - hash->list_addr[hash->size] = nl_addr_clone(addr); - route_list_init(&hash->list_route[hash->size]); - route_list_add(&hash->list_route[hash->size], route); - hash->size += 1; + route_list_add(&new_hash->routes_head, route); + LL_APPEND(*head, new_hash); } } -void route_list_hash_free(struct route_list_hash *hash) +struct route_list_element **route_list_hash_get(struct route_list_hash_element **head, struct nl_addr *addr) { - if (hash->size) { - for (size_t i = 0; i < hash->size; i++) { - nl_addr_put(hash->list_addr[i]); - route_list_free(&hash->list_route[i]); - } - FREE_SAFE(hash->list_addr); - FREE_SAFE(hash->list_route); - } - route_list_hash_init(hash); -} + struct route_list_hash_element *found = NULL; + struct route_list_hash_element find_element = { + .prefix = addr, + }; -struct route_list *route_list_hash_get_by_addr(struct route_list_hash *hash, struct nl_addr *addr) -{ - for (size_t i = 0; i < hash->size; i++) { - if (hash->list_addr[i] != NULL && nl_addr_cmp(addr, hash->list_addr[i]) == 0) { - return &hash->list_route[i]; - } + LL_SEARCH(*head, found, &find_element, route_list_hash_element_cmp); + if (found) { + return &found->routes_head; } + return NULL; } -void route_list_hash_prune(struct route_list_hash *hash) +void route_list_hash_free(struct route_list_hash_element **head) { - for (size_t i = 0; i < hash->size; i++) { - if (hash->list_route[i].delete) { - route_list_free(&hash->list_route[i]); - nl_addr_put(hash->list_addr[i]); - hash->list_addr[i] = NULL; - } + struct route_list_hash_element *iter = NULL, *tmp = NULL; + + LL_FOREACH_SAFE(*head, iter, tmp) + { + LL_DELETE(*head, iter); + nl_addr_put(iter->prefix); + route_list_free(&iter->routes_head); + free(iter); } } + +static int route_list_hash_element_cmp(struct route_list_hash_element *e1, struct route_list_hash_element *e2) +{ + return nl_addr_cmp(e1->prefix, e2->prefix); +} \ No newline at end of file diff --git a/src/routing/route/list_hash.h b/src/routing/route/list_hash.h index d6127f5d..40095bc2 100644 --- a/src/routing/route/list_hash.h +++ b/src/routing/route/list_hash.h @@ -1,3 +1,16 @@ +/* + * 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_ROUTE_LIST_HASH_H #define ROUTING_ROUTE_LIST_HASH_H @@ -7,17 +20,17 @@ #include "route/list.h" // "hash" like struct - simpler implementation rather than adding one more linked list struct for real hash implementation -// struct maps lists of routes by the destionation prefix -struct route_list_hash { - struct nl_addr **list_addr; - struct route_list *list_route; - size_t size; +// use this implementation for now instead of using uthash - see about key and how to implement hash because of prefix pointer being the key +struct route_list_hash_element { + struct nl_addr *prefix; + struct route_list_element *routes_head; + struct route_list_hash_element *next; }; -void route_list_hash_init(struct route_list_hash *hash); -void route_list_hash_add(struct route_list_hash *hash, struct nl_addr *addr, struct route *route); -void route_list_hash_free(struct route_list_hash *hash); -void route_list_hash_prune(struct route_list_hash *hash); -struct route_list *route_list_hash_get_by_addr(struct route_list_hash *hash, struct nl_addr *addr); +void route_list_hash_init(struct route_list_hash_element **head); +void route_list_hash_add(struct route_list_hash_element **head, struct nl_addr *addr, struct route *route); +void route_list_hash_add_empty(struct route_list_hash_element **head, struct nl_addr *addr); +struct route_list_element **route_list_hash_get(struct route_list_hash_element **head, struct nl_addr *addr); +void route_list_hash_free(struct route_list_hash_element **head); #endif // ROUTING_ROUTE_LIST_HASH_H diff --git a/src/routing/route/next_hop.c b/src/routing/route/next_hop.c index 6e700e17..a0ff30fd 100644 --- a/src/routing/route/next_hop.c +++ b/src/routing/route/next_hop.c @@ -1,19 +1,33 @@ +/* + * 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 +#include #include "route/next_hop.h" #include "utils/memory.h" +#include + void route_next_hop_init(struct route_next_hop *nh) { nh->kind = route_next_hop_kind_none; } -void route_next_hop_set_simple(struct route_next_hop *nh, int ifindex, const char *if_name, struct nl_addr *gw) +void route_next_hop_set_simple_gateway(struct route_next_hop *nh, struct nl_addr *gw) { nh->kind = route_next_hop_kind_simple; - nh->value.simple.ifindex = ifindex; - nh->value.simple.if_name = xstrdup(if_name); if (gw) { nh->value.simple.addr = nl_addr_clone(gw); } else { @@ -21,6 +35,18 @@ void route_next_hop_set_simple(struct route_next_hop *nh, int ifindex, const cha } } +void route_next_hop_set_simple_interface(struct route_next_hop *nh, int ifindex, const char *if_name) +{ + nh->value.simple.ifindex = ifindex; + nh->value.simple.if_name = xstrdup(if_name); +} + +void route_next_hop_set_simple(struct route_next_hop *nh, int ifindex, const char *if_name, struct nl_addr *gw) +{ + route_next_hop_set_simple_gateway(nh, gw); + route_next_hop_set_simple_interface(nh, ifindex, if_name); +} + void route_next_hop_set_special(struct route_next_hop *nh, char *value) { nh->kind = route_next_hop_kind_special; @@ -31,30 +57,30 @@ void route_next_hop_set_special(struct route_next_hop *nh, char *value) void route_next_hop_add_list(struct route_next_hop *nh, int ifindex, const char *if_name, struct nl_addr *gw) { - int idx = 0; + struct route_next_hop_list_element *new_element = NULL; if (nh->kind == route_next_hop_kind_none) { - // initialize the list + nh->value.list_head = NULL; nh->kind = route_next_hop_kind_list; - nh->value.list.list = xmalloc(sizeof(struct route_next_hop_simple)); - idx = 0; - } else { - nh->value.list.list = xrealloc(nh->value.list.list, sizeof(struct route_next_hop_simple) * (unsigned long) (nh->value.list.size + 1)); - idx = nh->value.list.size; } - nh->value.list.list[idx].ifindex = ifindex; - nh->value.list.list[idx].if_name = xstrdup(if_name); + + new_element = xmalloc(sizeof(*new_element)); + new_element->next = NULL; + new_element->simple.ifindex = ifindex; + new_element->simple.if_name = xstrdup(if_name); if (gw) { - nh->value.list.list[idx].addr = nl_addr_clone(gw); + new_element->simple.addr = nl_addr_clone(gw); } else { - nh->value.list.list[idx].addr = NULL; + new_element->simple.addr = NULL; } - ++nh->value.list.size; + + LL_APPEND(nh->value.list_head, new_element); } struct route_next_hop route_next_hop_clone(struct route_next_hop *nh) { struct route_next_hop out = {0}; + struct route_next_hop_list_element *list_iter = NULL; switch (nh->kind) { case route_next_hop_kind_none: @@ -66,8 +92,9 @@ struct route_next_hop route_next_hop_clone(struct route_next_hop *nh) route_next_hop_set_special(&out, nh->value.special.value); break; case route_next_hop_kind_list: - for (int i = 0; i < nh->value.list.size; i++) { - route_next_hop_add_list(&out, nh->value.list.list[i].ifindex, nh->value.list.list[i].if_name, nh->value.list.list[i].addr); + LL_FOREACH(nh->value.list_head, list_iter) + { + route_next_hop_add_list(&out, list_iter->simple.ifindex, list_iter->simple.if_name, list_iter->simple.addr); } break; } @@ -77,6 +104,8 @@ struct route_next_hop route_next_hop_clone(struct route_next_hop *nh) void route_next_hop_free(struct route_next_hop *nh) { + struct route_next_hop_list_element *list_iter = NULL, *tmp = NULL; + switch (nh->kind) { case route_next_hop_kind_none: break; @@ -88,7 +117,6 @@ void route_next_hop_free(struct route_next_hop *nh) if (nh->value.simple.if_name) { FREE_SAFE(nh->value.simple.if_name); } - break; case route_next_hop_kind_special: if (nh->value.special.value != NULL) { @@ -96,17 +124,19 @@ void route_next_hop_free(struct route_next_hop *nh) } break; case route_next_hop_kind_list: - if (nh->value.list.size > 0) { - for (int i = 0; i < nh->value.list.size; i++) { - if (nh->value.list.list[i].addr) { - nl_addr_put(nh->value.list.list[i].addr); - } - - if (nh->value.simple.if_name) { - FREE_SAFE(nh->value.simple.if_name); - } + LL_FOREACH_SAFE(nh->value.list_head, list_iter, tmp) + { + LL_DELETE(nh->value.list_head, list_iter); + + if (list_iter->simple.addr) { + nl_addr_put(list_iter->simple.addr); } - FREE_SAFE(nh->value.list.list); + + if (list_iter->simple.if_name) { + FREE_SAFE(list_iter->simple.if_name); + } + + free(list_iter); } break; } diff --git a/src/routing/route/next_hop.h b/src/routing/route/next_hop.h index a4e89b2c..8f75f5a6 100644 --- a/src/routing/route/next_hop.h +++ b/src/routing/route/next_hop.h @@ -1,6 +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_ROUTE_NEXT_HOP_H #define ROUTING_ROUTE_NEXT_HOP_H +#include + enum route_next_hop_kind { route_next_hop_kind_none = 0, route_next_hop_kind_simple, @@ -20,16 +35,15 @@ struct route_next_hop_special { char *value; }; -// list of interface indexes -struct route_next_hop_list { - struct route_next_hop_simple *list; - size_t size; +struct route_next_hop_list_element { + struct route_next_hop_simple simple; + struct route_next_hop_list_element *next; }; union route_next_hop_value { struct route_next_hop_simple simple; struct route_next_hop_special special; - struct route_next_hop_list list; + struct route_next_hop_list_element *list_head; }; struct route_next_hop { @@ -38,6 +52,8 @@ struct route_next_hop { }; void route_next_hop_init(struct route_next_hop *nh); +void route_next_hop_set_simple_gateway(struct route_next_hop *nh, struct nl_addr *gw); +void route_next_hop_set_simple_interface(struct route_next_hop *nh, int ifindex, const char *if_name); void route_next_hop_set_simple(struct route_next_hop *nh, int ifindex, const char *if_name, struct nl_addr *gw); void route_next_hop_set_special(struct route_next_hop *nh, char *value); void route_next_hop_add_list(struct route_next_hop *nh, int ifindex, const char *if_name, struct nl_addr *gw); diff --git a/src/routing/routing.c b/src/routing/routing.c index 226355f4..c9b093a2 100644 --- a/src/routing/routing.c +++ b/src/routing/routing.c @@ -1,3 +1,16 @@ +/* + * 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 #include @@ -19,98 +32,31 @@ #include #include +#include + +#include "route/next_hop.h" #include "routing.h" +#include "context.h" +#include "common.h" #include "rib.h" #include "rib/list.h" #include "rib/description_pair.h" #include "route/list.h" #include "route/list_hash.h" #include "control_plane_protocol.h" -#include "control_plane_protocol/list.h" +#include "sysrepo_types.h" #include "utils/memory.h" -// dir for storing data used by the plugin - usually build directory of the plugin -#define ROUTING_PLUGIN_DATA_DIR "ROUTING_PLUGIN_DATA_DIR" - -// other #define's used in the plugin -#define ROUTING_PROTOS_MAP_FNAME "protos_map" -#define ROUTING_RIBS_DESCRIPTIONS_MAP_FNAME "ribs_map" -#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" - -// 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" - -// sysrepocfg -#define SYSREPOCFG_EMPTY_CHECK_COMMAND "sysrepocfg -X -d running -m " BASE_YANG_MODEL - -// module change -static int routing_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); - -// module change helpers - changing/deleting values -static int set_control_plane_protocol_value(char *xpath, char *value); -static int set_static_route_value(char *xpath, char *node_name, char *node_value, struct route_list_hash *route_hash, int family); -static void set_static_route_description(struct route_list *route_list, char *node_value); -static int set_static_route_simple_next_hop(struct route_list *route_list, char *node_value, int family); -static int set_static_route_simple_outgoing_if(struct route_list *route_list, char *node_value); -static int delete_control_plane_protocol_value(char *xpath); -static int delete_static_route_value(char *xpath, char *node_name, int family); -static int set_rib_value(char *node_xpath, char *node_value); -static int delete_rib_value(char *node_xpath); - -// getting xpath from the node -static char *routing_xpath_get(const struct lyd_node *node); - -// control-plane-protocol list module changes -static int routing_control_plane_protocol_set_description(const char *type, const char *name, const char *description); - -// rib list module changes -static int routing_rib_set_address_family(const char *name, const char *address_family); -static int routing_rib_set_description(const char *name, const char *description); - -// operational callbacks -static int routing_oper_get_rib_routes_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); -static int routing_oper_get_interfaces_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); +// subs +#include "subscription/change.h" +#include "subscription/rpc.h" +#include "subscription/operational.h" -// rpc callbacks -static int routing_rpc_active_route_cb(sr_session_ctx_t *session, uint32_t subscription_id, const char *xpath, const sr_val_t *input, const size_t input_cnt, sr_event_t event, uint32_t request_id, sr_val_t **output, size_t *output_cnt, void *private_data); +// startup datastore -> initial load +#include "startup.h" +#include "persist.h" -// initial loading into the datastore -static int routing_load_data(sr_session_ctx_t *session); -static int routing_load_ribs(sr_session_ctx_t *session, struct lyd_node *routing_container_node); -static int routing_collect_ribs(struct nl_cache *routes_cache, struct rib_list *ribs); -static int routing_collect_routes(struct nl_cache *routes_cache, struct nl_cache *link_cache, struct rib_list *ribs); -static int routing_load_control_plane_protocols(sr_session_ctx_t *session, struct lyd_node *routing_container_node); -static int routing_build_rib_descriptions(struct rib_list *ribs); -static inline int routing_is_rib_known(int table); -static int routing_build_protos_map(struct control_plane_protocol map[ROUTING_PROTOS_COUNT]); -static inline int routing_is_proto_type_known(int type); -static bool routing_running_datastore_is_empty(void); - -static struct route_list_hash *ipv4_static_routes = NULL; -static struct route_list_hash *ipv6_static_routes = NULL; - -static int static_routes_init(struct route_list_hash **ipv4_routes, struct route_list_hash **ipv6_routes); -static void foreach_nexthop(struct rtnl_nexthop *nh, void *arg); -static int update_static_routes(struct route_list_hash *routes, uint8_t family); +static bool routing_running_datastore_is_empty(sr_session_ctx_t *session); int sr_plugin_init_cb(sr_session_ctx_t *session, void **private_data) { @@ -120,2252 +66,128 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **private_data) sr_session_ctx_t *startup_session = NULL; sr_conn_ctx_t *connection = NULL; sr_subscription_ctx_t *subscription = NULL; + struct routing_ctx *ctx = NULL; *private_data = NULL; - 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)); + // allocate routing plugin context + ctx = xmalloc(sizeof(*ctx)); + if (!ctx) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to allocate memory for routing context struct"); + goto error_out; } - *private_data = startup_session; + // memset context to 0 + *ctx = (struct routing_ctx){0}; - error = static_routes_init(&ipv4_static_routes, &ipv6_static_routes); + // set to private data + *private_data = ctx; + + connection = sr_session_get_connection(session); + error = sr_session_start(connection, SR_DS_STARTUP, &startup_session); if (error) { - SRP_LOG_ERR("static_routes_init error"); - goto error_out; + SRPLG_LOG_ERR(PLUGIN_NAME, "sr_session_start error: %d -> %s", error, sr_strerror(error)); } - if (routing_running_datastore_is_empty()) { - SRP_LOG_INF("running datasore is empty -> loading data"); - error = routing_load_data(session); + ctx->startup_session = startup_session; + + if (routing_running_datastore_is_empty(session)) { + SRPLG_LOG_INF(PLUGIN_NAME, "running datasore is empty -> loading data"); + error = routing_startup_load_data(ctx, session); if (error) { - SRP_LOG_ERR("error loading initial data into the datastore... exiting"); + SRPLG_LOG_ERR(PLUGIN_NAME, "error loading initial data into the datastore... exiting"); 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)); + SRPLG_LOG_ERR(PLUGIN_NAME, "sr_copy_config error (%d): %s", error, sr_strerror(error)); goto error_out; } + } else { + // load needed persistent data which needs to be applied from the running datastore + // TODO: figure out a way to run this only once when the system starts + // error = routing_persist_static_routes(session); + // if (error) { + // SRPLG_LOG_ERR(PLUGIN_NAME, "routing_persist_static_routes() failed (%d)", error); + // goto error_out; + // } } - SRP_LOG_INF("subscribing to module change"); + SRPLG_LOG_INF(PLUGIN_NAME, "subscribing to module change"); // control-plane-protocol list module changes - error = sr_module_change_subscribe(session, BASE_YANG_MODEL, "/" BASE_YANG_MODEL ":*//.", routing_module_change_cb, *private_data, 0, SR_SUBSCR_DEFAULT, &subscription); + error = sr_module_change_subscribe(session, BASE_YANG_MODEL, ROUTING_CONTROL_PLANE_PROTOCOL_LIST_YANG_PATH, routing_control_plane_protocol_list_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)); + SRPLG_LOG_ERR(PLUGIN_NAME, "sr_module_change_subscribe error (%d): %s", error, sr_strerror(error)); goto error_out; } - SRP_LOG_INF("subscribing to interfaces operational data"); + SRPLG_LOG_INF(PLUGIN_NAME, "subscribing to interfaces operational data"); // interface leaf-list oper data - error = sr_oper_get_items_subscribe(session, BASE_YANG_MODEL, ROUTING_INTERFACE_LEAF_LIST_YANG_PATH, routing_oper_get_interfaces_cb, NULL, SR_SUBSCR_CTX_REUSE, &subscription); + error = sr_oper_get_subscribe(session, BASE_YANG_MODEL, ROUTING_INTERFACES_CONTAINER_YANG_PATH, routing_oper_get_interfaces_cb, NULL, 0, &subscription); if (error) { - SRP_LOG_ERR("sr_oper_get_items_subscribe error (%d): %s", error, sr_strerror(error)); + SRPLG_LOG_ERR(PLUGIN_NAME, "sr_oper_get_items_subscribe error (%d): %s", error, sr_strerror(error)); goto error_out; } // RIB oper data - error = sr_oper_get_items_subscribe(session, BASE_YANG_MODEL, ROUTING_RIB_LIST_YANG_PATH, routing_oper_get_rib_routes_cb, NULL, SR_SUBSCR_CTX_REUSE, &subscription); + error = sr_oper_get_subscribe(session, BASE_YANG_MODEL, ROUTING_RIB_LIST_YANG_PATH, routing_oper_get_rib_routes_cb, NULL, 1, &subscription); if (error) { - SRP_LOG_ERR("sr_oper_get_items_subscribe error (%d): %s", error, sr_strerror(error)); + SRPLG_LOG_ERR(PLUGIN_NAME, "sr_oper_get_items_subscribe error (%d): %s", error, sr_strerror(error)); goto error_out; } - SRP_LOG_INF("subscribing to plugin RPCs/actions"); + SRPLG_LOG_INF(PLUGIN_NAME, "subscribing to plugin RPCs/actions"); // active-route RPC/action - error = sr_rpc_subscribe(session, ROUTING_RIB_LIST_ACTIVE_ROUTE_RPC_PATH, routing_rpc_active_route_cb, NULL, 1, SR_SUBSCR_CTX_REUSE, &subscription); + error = sr_rpc_subscribe(session, ROUTING_RIB_LIST_ACTIVE_ROUTE_RPC_PATH, routing_rpc_active_route_cb, NULL, 1, 1, &subscription); if (error) { - SRP_LOG_ERR("sr_rpc_subscribe error (%d): %s", error, sr_strerror(error)); - goto error_out; - } - - goto out; -error_out: - SRP_LOG_ERR("error occured while initializing the plugin -> %d", error); -out: - return error; -} - -static int static_routes_init(struct route_list_hash **ipv4_routes, struct route_list_hash **ipv6_routes) -{ - int nl_err = 0; - int error = 0; - - struct nl_sock *socket = NULL; - struct rtnl_route *route = NULL; - struct nl_cache *cache = NULL; - struct nl_cache *link_cache = NULL; - struct route tmp_route = {0}; - struct rtnl_link *iface = NULL; - char *if_name = NULL; - int ifindex = 0; - - *ipv4_routes = xmalloc(sizeof(struct route_list_hash)); - *ipv6_routes = xmalloc(sizeof(struct route_list_hash)); - - route_list_hash_init(*ipv4_routes); - route_list_hash_init(*ipv6_routes); - - socket = nl_socket_alloc(); - if (socket == NULL) { - SRP_LOG_ERR("unable to init nl_sock struct..."); - goto error_out; - } - - nl_err = nl_connect(socket, NETLINK_ROUTE); - if (nl_err != 0) { - SRP_LOG_ERR("nl_connect failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - nl_err = rtnl_route_alloc_cache(socket, AF_UNSPEC, 0, &cache); - if (nl_err != 0) { - SRP_LOG_ERR("rtnl_route_alloc_cache failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - nl_err = rtnl_link_alloc_cache(socket, AF_UNSPEC, &link_cache); - if (nl_err != 0) { - SRP_LOG_ERR("rtnl_route_alloc_cache failed (%d): %s", nl_err, nl_geterror(nl_err)); + SRPLG_LOG_ERR(PLUGIN_NAME, "sr_rpc_subscribe error (%d): %s", error, sr_strerror(error)); goto error_out; } - route = (struct rtnl_route *) nl_cache_get_first(cache); - while (route != NULL) { - const int PROTO = rtnl_route_get_protocol(route); - - if (PROTO == RTPROT_STATIC) { - const int AF = rtnl_route_get_family(route); - // 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 -> TODO: see what about special type - 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 = xstrdup(rtnl_link_get_name(iface)); - route_next_hop_set_simple(&tmp_route.next_hop, ifindex, if_name, rtnl_route_nh_get_gateway(nh)); - rtnl_link_put(iface); - } else { - rtnl_route_foreach_nexthop(route, foreach_nexthop, &tmp_route.next_hop); - } - - // route-metadata/source-protocol - route_set_source_protocol(&tmp_route, "ietf-routing:static"); - - // add the route to the protocol's container - if (AF == AF_INET) { - // v4 - route_list_hash_add(*ipv4_routes, rtnl_route_get_dst(route), &tmp_route); - } else if (AF == AF_INET6) { - // v6 - route_list_hash_add(*ipv6_routes, rtnl_route_get_dst(route), &tmp_route); - } - - route_free(&tmp_route); - } - route = (struct rtnl_route *) nl_cache_get_next((struct nl_object *) route); - } - goto out; error_out: - SRP_LOG_ERR("error initializing static routes"); - error = -1; + SRPLG_LOG_ERR(PLUGIN_NAME, "error occured while initializing the plugin -> %d", error); out: - nl_cache_free(cache); - nl_cache_free(link_cache); - nl_socket_free(socket); return error; } void sr_plugin_cleanup_cb(sr_session_ctx_t *session, void *private_data) { - route_list_hash_free(ipv4_static_routes); - route_list_hash_free(ipv6_static_routes); - FREE_SAFE(ipv4_static_routes); - FREE_SAFE(ipv6_static_routes); -} - -static int routing_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; - - // sysrepo - sr_session_ctx_t *startup_session = (sr_session_ctx_t *) private_data; - sr_change_iter_t *routing_change_iter = NULL; - sr_change_oper_t operation = SR_OP_CREATED; - - // libyang - const struct lyd_node *node = NULL; - - // temp helper vars - const char *prev_value = NULL; - const char *prev_list = NULL; - int prev_default = false; - char *node_xpath = NULL; - char *node_value = NULL; - - bool ipv4_update = false; - bool ipv6_update = false; - - 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; - } else if (event == SR_EV_DONE) { - 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; - } - } else if (event == SR_EV_CHANGE) { - error = sr_get_changes_iter(session, xpath, &routing_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, routing_change_iter, &operation, &node, &prev_value, &prev_list, &prev_default) == SR_ERR_OK) { - node_xpath = routing_xpath_get(node); - - 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 (strstr(node_xpath, "ietf-ipv4-unicast-routing:ipv4")) { - ipv4_update = true; - } else if (strstr(node_xpath, "ietf-ipv6-unicast-routing:ipv6")) { - ipv6_update = true; - } - - if (operation == SR_OP_CREATED || operation == SR_OP_MODIFIED) { - if (strstr(node_xpath, "/ietf-routing:routing/ribs")) { - error = set_rib_value(node_xpath, node_value); - if (error) { - SRP_LOG_ERR("set_control_plane_protocol_value error (%d)", error); - goto error_out; - } - } else if (strstr(node_xpath, "ietf-routing:routing/control-plane-protocols")) { - error = set_control_plane_protocol_value(node_xpath, node_value); - if (error) { - SRP_LOG_ERR("set_control_plane_protocol_value error (%d)", error); - goto error_out; - } - } - } else if (operation == SR_OP_DELETED) { - if (strstr(node_xpath, "/ietf-routing:routing/ribs")) { - error = delete_rib_value(node_xpath); - if (error) { - SRP_LOG_ERR("set_control_plane_protocol_value error (%d)", error); - goto error_out; - } - } else if (strstr(node_xpath, "ietf-routing:routing/control-plane-protocols")) { - error = delete_control_plane_protocol_value(node_xpath); - if (error) { - SRP_LOG_ERR("set_control_plane_protocol_value error (%d)", error); - goto error_out; - } - } - } - } - FREE_SAFE(node_xpath); - FREE_SAFE(node_value); - } - - if (ipv4_update) { - error = update_static_routes(ipv4_static_routes, AF_INET); - if (error) { - SRP_LOG_ERR("failed to update IPv4 static routes on system"); - goto error_out; - } - route_list_hash_prune(ipv4_static_routes); - } - - if (ipv6_update) { - error = update_static_routes(ipv6_static_routes, AF_INET6); - if (error) { - SRP_LOG_ERR("failed to update IPv6 static routes on system"); - goto error_out; - } - route_list_hash_prune(ipv6_static_routes); - } - } - goto out; - -error_out: - SRP_LOG_ERR("error applying control plane protocols module changes"); - error = -1; - FREE_SAFE(node_xpath); - FREE_SAFE(node_value); - -out: - sr_free_change_iter(routing_change_iter); - - return error != 0 ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK; -} - -static int update_static_routes(struct route_list_hash *routes, uint8_t family) -{ - struct nl_sock *socket = NULL; - struct rtnl_route *route = NULL; - struct rtnl_nexthop *next_hop = NULL; - struct nl_addr *dst_addr = NULL; - int error = 0; - int nl_err = 0; - - socket = nl_socket_alloc(); - if (socket == NULL) { - error = -1; - SRP_LOG_ERR("unable to init nl_sock struct..."); - goto error_out; - } - - nl_err = nl_connect(socket, NETLINK_ROUTE); - if (nl_err != 0) { - error = -1; - SRP_LOG_ERR("nl_connect failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - for (size_t i = 0; i < routes->size; i++) { - if (route_list_is_empty(&routes->list_route[i])) { - continue; - } - - route = rtnl_route_alloc(); - if (route == NULL) { - error = -1; - SRP_LOG_ERR("unable to alloc rtnl_route struct"); - goto error_out; - } - - rtnl_route_set_table(route, RT_TABLE_MAIN); - rtnl_route_set_protocol(route, RTPROT_STATIC); - rtnl_route_set_family(route, family); - dst_addr = nl_addr_clone(routes->list_addr[i]); - rtnl_route_set_dst(route, dst_addr); - rtnl_route_set_priority(route, routes->list_route[i].list[0].preference); - - if (routes->list_route[i].delete) { - rtnl_route_set_scope(route, RT_SCOPE_NOWHERE); - nl_err = rtnl_route_delete(socket, route, 0); - if (nl_err != 0) { - error = -1; - SRP_LOG_ERR("nl_connect failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - rtnl_route_put(route); - route = NULL; - continue; - } - - if (routes->list_route[i].list[0].next_hop.kind == route_next_hop_kind_simple) { - next_hop = rtnl_route_nh_alloc(); - if (next_hop == NULL) { - error = -1; - SRP_LOG_ERR("unable to alloc rtnl_nexthop struct"); - goto error_out; - } - - if (routes->list_route[i].list[0].next_hop.value.simple.if_name == NULL && routes->list_route[i].list[0].next_hop.value.simple.addr == NULL) { - error = -1; - SRP_LOG_ERR("outgoing-interface and next-hop-address can't both be NULL"); - goto error_out; - } - - if (routes->list_route[i].list[0].next_hop.value.simple.if_name != NULL) { - rtnl_route_nh_set_ifindex(next_hop, routes->list_route[i].list[0].next_hop.value.simple.ifindex); - } - - if (routes->list_route[i].list[0].next_hop.value.simple.addr != NULL) { - rtnl_route_nh_set_gateway(next_hop, routes->list_route[i].list[0].next_hop.value.simple.addr); - } - rtnl_route_add_nexthop(route, next_hop); - } else if (routes->list_route[i].list[0].next_hop.kind == route_next_hop_kind_list) { - for (size_t j = 0; j < routes->list_route[i].list[0].next_hop.value.list.size; j++) { - next_hop = rtnl_route_nh_alloc(); - if (next_hop == NULL) { - error = -1; - SRP_LOG_ERR("unable to alloc rtnl_nexthop struct"); - goto error_out; - } - - rtnl_route_nh_set_ifindex(next_hop, routes->list_route[i].list[0].next_hop.value.list.list[j].ifindex); - rtnl_route_nh_set_gateway(next_hop, routes->list_route[i].list[0].next_hop.value.list.list[j].addr); - rtnl_route_add_nexthop(route, next_hop); - } - } - - rtnl_route_set_scope(route, rtnl_route_guess_scope(route)); - - nl_err = rtnl_route_add(socket, route, NLM_F_REPLACE); - if (nl_err != 0) { - error = -1; - SRP_LOG_ERR("rtnl_route_add failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - nl_addr_put(dst_addr); - dst_addr = NULL; - rtnl_route_put(route); - route = NULL; - } -error_out: - if (dst_addr) { - nl_addr_put(dst_addr); - } - - if (route) { - rtnl_route_put(route); - } - - if (socket) { - nl_socket_free(socket); - } - - return error; -} - -static int set_control_plane_protocol_value(char *node_xpath, char *node_value) -{ - sr_xpath_ctx_t xpath_ctx = {0}; - - char *node_name = NULL; - char *name_key = NULL; - char *type_key = NULL; - char *orig_xpath = NULL; - - int error = SR_ERR_OK; - - orig_xpath = xstrdup(node_xpath); - - node_name = sr_xpath_node_name(node_xpath); - name_key = sr_xpath_key_value(node_xpath, "control-plane-protocol", "name", &xpath_ctx); - type_key = sr_xpath_key_value(node_xpath, "control-plane-protocol", "type", &xpath_ctx); - - if (!strcmp(node_name, "description") && !strstr(orig_xpath, "ietf-ipv4-unicast-routing:ipv4") && !strstr(orig_xpath, "ietf-ipv6-unicast-routing:ipv6")) { - error = routing_control_plane_protocol_set_description(type_key, name_key, node_value); - if (error != 0) { - SRP_LOG_ERR("routing_control_plane_protocol_set_description failed"); - goto out; - } - } else if (strstr(orig_xpath, "ietf-ipv4-unicast-routing:ipv4")) { - error = set_static_route_value(orig_xpath, node_name, node_value, ipv4_static_routes, AF_INET); - if (error != 0) { - SRP_LOG_ERR("error setting IPv4 static route value"); - goto out; - } - } else if (strstr(orig_xpath, "ietf-ipv6-unicast-routing:ipv6")) { - error = set_static_route_value(orig_xpath, node_name, node_value, ipv6_static_routes, AF_INET6); - if (error != 0) { - SRP_LOG_ERR("error setting IPv6 static route value"); - goto out; - } - } - -out: - if (orig_xpath) { - FREE_SAFE(orig_xpath); - } - - return error ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK; -} - -static int set_static_route_value(char *xpath, char *node_name, char *node_value, struct route_list_hash *routes_hash, int family) -{ - sr_xpath_ctx_t xpath_ctx = {0}; - - struct nl_addr *destination_prefix_addr = NULL; - - struct route_list *route_list = NULL; - char *next_hop_list = NULL; - char *destination_prefix = NULL; - int error = 0; - - next_hop_list = sr_xpath_key_value(xpath, "next-hop", "index", &xpath_ctx); - destination_prefix = sr_xpath_key_value(xpath, "route", "destination-prefix", &xpath_ctx); - - if (destination_prefix == NULL) { - error = -1; - SRP_LOG_ERR("destination-prefix couldn't be retrieved"); - goto out; - } - - error = nl_addr_parse(destination_prefix, family, &destination_prefix_addr); - if (error != 0) { - error = -1; - SRP_LOG_ERR("failed to parse destination-prefix into nl_addr"); - goto out; - } - - if (strcmp(node_name, "destination-prefix")) { - route_list = route_list_hash_get_by_addr(routes_hash, destination_prefix_addr); - if (route_list == NULL) { - error = -1; - SRP_LOG_ERR("matching route_list destination-prefix %s not found", destination_prefix); - goto out; - } - } - - if (next_hop_list != NULL) { - route_list->list[0].next_hop.kind = route_next_hop_kind_list; - - if (!strcmp(node_name, "next-hop-address")) { - } else if (!strcmp(node_name, "outgoing-interface")) { - } - } else if (!strcmp(node_name, "destination-prefix")) { - route_list_hash_add(routes_hash, destination_prefix_addr, &(struct route){0}); - } else if (!strcmp(node_name, "description")) { - set_static_route_description(route_list, node_value); - } else if (!strcmp(node_name, "next-hop-address")) { - error = set_static_route_simple_next_hop(route_list, node_value, family); - if (error) { - error = -1; - SRP_LOG_ERR("failed to set static route next-hop-address"); - goto out; - } - } else if (!strcmp(node_name, "outgoing-interface")) { - error = set_static_route_simple_outgoing_if(route_list, node_value); - if (error) { - error = -1; - SRP_LOG_ERR("failed to set static route next-hop-address"); - goto out; - } - } - -out: - if (destination_prefix_addr) { - nl_addr_put(destination_prefix_addr); - } - - return error ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK; -} - -static void set_static_route_description(struct route_list *route_list, char *node_value) -{ - route_list->list[0].metadata.description = xstrdup(node_value); -} - -static int set_static_route_simple_next_hop(struct route_list *route_list, char *node_value, int family) -{ - int error = 0; - - route_list->list[0].next_hop.kind = route_next_hop_kind_simple; - - error = nl_addr_parse(node_value, family, &route_list->list[0].next_hop.value.simple.addr); - if (error != 0) { - SRP_LOG_ERR("failed to parse next-hop-address into nl_addr"); - return -1; - } - - return 0; -} + struct routing_ctx *ctx = (struct routing_ctx *) private_data; -static int set_static_route_simple_outgoing_if(struct route_list *route_list, char *node_value) -{ - int ifindex = 0; + sr_session_ctx_t *startup_session = ctx->startup_session; - route_list->list[0].next_hop.kind = route_next_hop_kind_simple; - route_list->list[0].next_hop.value.simple.if_name = xstrdup(node_value); - ifindex = if_nametoindex(node_value); - if (ifindex == 0) { - SRP_LOG_ERR("failed to get ifindex for %s", node_value); - return -1; + if (startup_session) { + sr_session_stop(startup_session); } - route_list->list[0].next_hop.value.simple.ifindex = ifindex; - return 0; + // release context memory + FREE_SAFE(ctx); } -static int delete_control_plane_protocol_value(char *node_xpath) +static bool routing_running_datastore_is_empty(sr_session_ctx_t *session) { - sr_xpath_ctx_t xpath_ctx = {0}; - - char *node_name = NULL; - char *name_key = NULL; - char *type_key = NULL; - char *orig_xpath = NULL; - int error = SR_ERR_OK; + bool is_empty = true; + sr_val_t *values = NULL; + size_t value_cnt = 0; - orig_xpath = xstrdup(node_xpath); - - node_name = sr_xpath_node_name(node_xpath); - name_key = sr_xpath_key_value(node_xpath, "control-plane-protocol", "name", &xpath_ctx); - type_key = sr_xpath_key_value(node_xpath, "control-plane-protocol", "type", &xpath_ctx); - - if (!strcmp(node_name, "description") && !strstr(orig_xpath, "ietf-ipv4-unicast-routing:ipv4") && !strstr(orig_xpath, "ietf-ipv6-unicast-routing:ipv6")) { - error = routing_control_plane_protocol_set_description(type_key, name_key, ""); - if (error != 0) { - SRP_LOG_ERR("routing_control_plane_protocol_set_description failed"); - goto out; - } - } else if (strstr(orig_xpath, "ietf-ipv4-unicast-routing:ipv4")) { - error = delete_static_route_value(orig_xpath, node_name, AF_INET); - if (error != 0) { - SRP_LOG_ERR("error setting IPv4 static route value"); - goto out; - } - } else if (strstr(orig_xpath, "ietf-ipv6-unicast-routing:ipv6")) { - error = delete_static_route_value(orig_xpath, node_name, AF_INET6); - if (error != 0) { - SRP_LOG_ERR("error setting IPv6 static route value"); - goto out; - } - } - -out: - if (orig_xpath) { - FREE_SAFE(orig_xpath); - } - - return error ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK; -} - -static int delete_static_route_value(char *xpath, char *node_name, int family) -{ - sr_xpath_ctx_t xpath_ctx = {0}; - - struct nl_addr *destination_prefix_addr = NULL; - - struct route_list *route_list = NULL; - char *next_hop_list = NULL; - char *destination_prefix = NULL; - int error = 0; - - next_hop_list = sr_xpath_key_value(xpath, "next-hop", "index", &xpath_ctx); - destination_prefix = sr_xpath_key_value(xpath, "route", "destination-prefix", &xpath_ctx); - - if (destination_prefix == NULL) { - error = -1; - SRP_LOG_ERR("destination-prefix couldn't be retrieved"); - goto out; - } - - error = nl_addr_parse(destination_prefix, family, &destination_prefix_addr); - if (error != 0) { - error = -1; - SRP_LOG_ERR("failed to parse destination-prefix into nl_addr"); - goto out; - } - - if (family == AF_INET) { - route_list = route_list_hash_get_by_addr(ipv4_static_routes, destination_prefix_addr); - } else if (family == AF_INET6) { - route_list = route_list_hash_get_by_addr(ipv6_static_routes, destination_prefix_addr); - } - - if (route_list == NULL) { - error = -1; - SRP_LOG_ERR("matching route_list destination-prefix %s not found", destination_prefix); + error = sr_get_items(session, ROUTING_CONTROL_PLANE_PROTOCOL_LIST_YANG_PATH, 0, SR_OPER_DEFAULT, &values, &value_cnt); + if (error) { + SRPLG_LOG_ERR(PLUGIN_NAME, "sr_get_items error (%d): %s", error, sr_strerror(error)); goto out; } - if (next_hop_list != NULL) { - route_list->list[0].next_hop.kind = route_next_hop_kind_list; - - if (!strcmp(node_name, "next-hop-address")) { - } else if (!strcmp(node_name, "outgoing-interface")) { - } - } else if (!strcmp(node_name, "destination-prefix")) { - route_list->delete = true; - } else if (!strcmp(node_name, "description")) { - set_static_route_description(route_list, NULL); - } else if (!strcmp(node_name, "next-hop-address")) { - route_list->list[0].next_hop.value.simple.addr = NULL; - } else if (!strcmp(node_name, "outgoing-interface")) { - route_list->list[0].next_hop.value.simple.if_name = NULL; - } - -out: - if (destination_prefix_addr) { - nl_addr_put(destination_prefix_addr); - } - - return error ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK; -} - -static int set_rib_value(char *node_xpath, char *node_value) -{ - sr_xpath_ctx_t xpath_ctx = {0}; - - char *name_key = NULL; - char *node_name = NULL; - - int error = SR_ERR_OK; - - node_name = sr_xpath_node_name(node_xpath); - name_key = sr_xpath_key_value(node_xpath, "rib", "name", &xpath_ctx); - - if (!strcmp(node_name, "description")) { - error = routing_rib_set_description(name_key, node_value); - if (error != 0) { - SRP_LOG_ERR("routing_rib_set_description failed"); - goto out; - } - } - -out: - return error ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK; -} - -static int delete_rib_value(char *node_xpath) -{ - sr_xpath_ctx_t xpath_ctx = {0}; - - char *name_key = NULL; - char *node_name = NULL; - - int error = SR_ERR_OK; - - node_name = sr_xpath_node_name(node_xpath); - name_key = sr_xpath_key_value(node_xpath, "rib", "name", &xpath_ctx); - - if (!strcmp(node_name, "description")) { - error = routing_rib_set_description(name_key, ""); - if (error != 0) { - SRP_LOG_ERR("routing_rib_set_description failed"); - goto out; - } + // check if cpp list is empty + if (value_cnt > 0) { + sr_free_values(values, value_cnt); + is_empty = false; } out: - return error ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK; -} - -static int routing_control_plane_protocol_set_description(const char *type, const char *name, const char *description) -{ - int error = 0; - - struct control_plane_protocol protos[ROUTING_PROTOS_COUNT] = {0}; - - // temp variables and buffers - char *data_dir = NULL; - char path_buffer[PATH_MAX] = {0}; - char line_buffer[PATH_MAX] = {0}; - int tmp_type = 0; - char type_buffer[100] = {0}; - char desc_buffer[256] = {0}; - FILE *fptr = NULL; - - data_dir = getenv(ROUTING_PLUGIN_DATA_DIR); - if (data_dir == NULL) { - SRP_LOG_ERR("unable to use environment variable for the plugin data dir: %s", ROUTING_PLUGIN_DATA_DIR); - goto error_out; - } - - for (size_t i = 0; i < ROUTING_PROTOS_COUNT; i++) { - control_plane_protocol_init(&protos[i]); - } - - // check if file exists first - snprintf(path_buffer, sizeof(path_buffer), "%s%s%s", data_dir, (data_dir[strlen(data_dir) - 1] == '/' ? "" : "/"), ROUTING_PROTOS_MAP_FNAME); - - if (access(path_buffer, F_OK) != 0) { - SRP_LOG_ERR("protocols map file doesnt exist - error"); - goto error_out; - } else { - fptr = fopen(path_buffer, "r"); - if (fptr == NULL) { - SRP_LOG_ERR("unable to open file %s", path_buffer); - goto error_out; - } - - SRP_LOG_DBG("protocols map file exists - reading map values and changing description of '%s' to %s", name, description); - - while (fgets(line_buffer, sizeof(line_buffer), fptr) != NULL) { - const int read_n = sscanf(line_buffer, "%d %s \"%256[^\"]\"", &tmp_type, type_buffer, desc_buffer); - if (read_n == 3) { - if (tmp_type >= 0 && tmp_type <= ROUTING_PROTOS_COUNT) { - rtnl_route_proto2str(tmp_type, protos[tmp_type].name, sizeof(protos[tmp_type].name)); - protos[tmp_type].type = xstrdup(type_buffer); - - if (strcmp(protos[tmp_type].name, name) == 0) { - // don't read this description - change it - protos[tmp_type].description = xstrdup(description); - } else { - protos[tmp_type].description = xstrdup(desc_buffer); - } - protos[tmp_type].initialized = 1; - } else { - SRP_LOG_ERR("invalid protocol type found in the protocol types map file: %d", tmp_type); - goto error_out; - } - } else { - SRP_LOG_ERR("unable to properly read the protocol types map file format; read %d values", read_n); - goto error_out; - } - } - - // return to the beginning and write protocols again - reopen because clearing the content of a file is needed - fclose(fptr); - fptr = fopen(path_buffer, "w"); - - for (size_t i = 0; i < ROUTING_PROTOS_COUNT; i++) { - if (protos[i].initialized == 1) { - fprintf(fptr, "%lu\t%s\t\"%s\"\n", i, protos[i].type, protos[i].description); - } - } - } - goto out; - -error_out: - error = -1; -out: - if (fptr) { - fclose(fptr); - } - - for (size_t i = 0; i < ROUTING_PROTOS_COUNT; i++) { - control_plane_protocol_free(&protos[i]); - } - return error; -} - -static int routing_rib_set_address_family(const char *name, const char *address_family) -{ - int error = 0; - return error; -} - -static int routing_rib_set_description(const char *name, const char *description) -{ - int error = 0; - - // calcualte sizes for faster string comparisons later - const size_t DESC_LEN = strlen(description); - const size_t NAME_LEN = strlen(name); - - // file stream - FILE *fptr = NULL; - - // buffers and temporary values - char path_buffer[PATH_MAX] = {0}; - char line_buffer[PATH_MAX] = {0}; - const char *data_dir = NULL; - struct rib_description_pair tmp_description = {0}; - - // list of descriptions - struct { - struct rib_description_pair *list; - size_t size; - } rib_descriptions = {0}; - - // first read all descriptions from the map file and change the wanted description - // after that -> write those descriptions back to the file - - // get the data_dir variable using getenv - data_dir = getenv(ROUTING_PLUGIN_DATA_DIR); - if (data_dir == NULL) { - SRP_LOG_ERR("unable to use environment variable for the plugin data dir: %s", ROUTING_PLUGIN_DATA_DIR); - goto error_out; - } - - // create file path and see if it exists - snprintf(path_buffer, sizeof(path_buffer), "%s%s%s", data_dir, (data_dir[strlen(data_dir) - 1] == '/') ? "" : "/", ROUTING_RIBS_DESCRIPTIONS_MAP_FNAME); - SRP_LOG_DBG("RIBs description map file path: %s", path_buffer); - - if (access(path_buffer, F_OK) == 0) { - // file exists - fptr = fopen(path_buffer, "r"); - if (fptr == NULL) { - SRP_LOG_ERR("unable to open file %s", path_buffer); - goto error_out; - } - - while (fgets(line_buffer, sizeof(line_buffer), fptr) != NULL) { - const int read_n = sscanf(line_buffer, "%s \"%256[^\"]\"", tmp_description.name, tmp_description.description); - if (read_n == 2) { - // add the description to the list - rib_descriptions.list = xrealloc(rib_descriptions.list, sizeof(struct rib_description_pair) * (unsigned) (rib_descriptions.size + 1)); - memcpy(&rib_descriptions.list[rib_descriptions.size], &tmp_description, sizeof(struct rib_description_pair)); - rib_descriptions.size += 1; - } else { - SRP_LOG_ERR("unable to correctly read 2 values from the RIBs map file"); - goto error_out; - } - } - } else { - // file doesn't exist -> error - SRP_LOG_ERR("%s file doesn't exist", path_buffer); - goto error_out; - } - - bool description_changed = false; - - SRP_LOG_DBG("setting description of %s to \"%s\"", name, description); - - for (size_t i = 0; i < rib_descriptions.size; i++) { - struct rib_description_pair *PAIR = &rib_descriptions.list[i]; - if (strncmp(PAIR->name, name, NAME_LEN) == 0) { - // found description -> change it - if (DESC_LEN <= ROUTING_RIB_DESCRIPTION_SIZE) { - memcpy(PAIR->description, description, DESC_LEN); - PAIR->description[DESC_LEN] = 0; - SRP_LOG_DBG("description = %s", PAIR->description); - description_changed = true; - } else { - SRP_LOG_ERR("unable to set description: %d characters max...", ROUTING_RIB_DESCRIPTION_SIZE); - goto error_out; - } - break; - } - } - - if (description_changed == false) { - goto error_out; - } - - // write changes back to the file - fclose(fptr); - fptr = fopen(path_buffer, "w"); - - for (size_t i = 0; i < rib_descriptions.size; i++) { - SRP_LOG_DBG("%s = %s", rib_descriptions.list[i].name, rib_descriptions.list[i].description); - fprintf(fptr, "%s\t\"%s\"\n", rib_descriptions.list[i].name, rib_descriptions.list[i].description); - } - - goto out; - -error_out: - SRP_LOG_ERR("unable to set description for %s", name); - error = -1; - -out: - FREE_SAFE(rib_descriptions.list); - if (fptr != NULL) { - fclose(fptr); - } - return error; -} - -static char *routing_xpath_get(const struct lyd_node *node) -{ - char *xpath_node = NULL; - char *xpath_leaflist_open_bracket = NULL; - size_t xpath_trimed_size = 0; - char *xpath_trimed = NULL; - - if (node->schema->nodetype == LYS_LEAFLIST) { - xpath_node = lyd_path(node, LYD_PATH_STD, NULL, 0); - xpath_leaflist_open_bracket = strrchr(xpath_node, '['); - if (xpath_leaflist_open_bracket == NULL) { - return xpath_node; - } - - xpath_trimed_size = (size_t) xpath_leaflist_open_bracket - (size_t) xpath_node + 1; - xpath_trimed = xcalloc(1, xpath_trimed_size); - strncpy(xpath_trimed, xpath_node, xpath_trimed_size - 1); - xpath_trimed[xpath_trimed_size - 1] = '\0'; - - FREE_SAFE(xpath_node); - - return xpath_trimed; - } else { - return lyd_path(node, LYD_PATH_STD, NULL, 0); - } -} - -static int routing_oper_get_interfaces_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) -{ - // error handling - int error = SR_ERR_OK; - LY_ERR ly_err = LY_SUCCESS; - - // libyang - const struct ly_ctx *ly_ctx = NULL; - - // libnl - struct nl_sock *socket = NULL; - struct nl_cache *cache = NULL; - struct rtnl_link *link = NULL; - - if (*parent == NULL) { - ly_ctx = sr_get_context(sr_session_get_connection(session)); - if (ly_ctx == NULL) { - goto error_out; - } - ly_err = lyd_new_path(*parent, ly_ctx, request_xpath, NULL, 0, NULL); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new node"); - goto error_out; - } - } - - socket = nl_socket_alloc(); - if (socket == NULL) { - SRP_LOG_ERR("unable to init nl_sock struct..."); - goto error_out; - } - - error = nl_connect(socket, NETLINK_ROUTE); - if (error != 0) { - SRP_LOG_ERR("nl_connect failed (%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 failed (%d): %s", error, nl_geterror(error)); - goto error_out; - } - - SRP_LOG_DBG("adding interfaces to the list"); - - link = (struct rtnl_link *) nl_cache_get_first(cache); - while (link) { - const char *name = rtnl_link_get_name(link); - SRP_LOG_DBG("adding interface '%s' ", name); - ly_err = lyd_new_path(*parent, ly_ctx, ROUTING_INTERFACE_LEAF_LIST_YANG_PATH, (void *) name, 0, NULL); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new interface node"); - goto error_out; - } - - link = (struct rtnl_link *) nl_cache_get_next((struct nl_object *) link); - } - - goto out; - -error_out: - error = SR_ERR_CALLBACK_FAILED; -out: - // free allocated objects - nl_cache_free(cache); - nl_socket_free(socket); - - return error; -} - -static 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) { - SRP_LOG_ERR("unable to init nl_sock struct..."); - return; - } - - nl_err = nl_connect(socket, NETLINK_ROUTE); - if (nl_err != 0) { - SRP_LOG_ERR("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) { - SRP_LOG_ERR("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); -} - -static int routing_oper_get_rib_routes_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; - int nl_err = 0; - LY_ERR ly_err = LY_SUCCESS; - - // libyang - const struct ly_ctx *ly_ctx = NULL; - const struct lys_module *ly_uv4mod = NULL, *ly_uv6mod = NULL; - struct lyd_node *ly_node = NULL, *routes_node = NULL, *nh_node = NULL, *nh_list_node = NULL; - - // libnl - struct nl_sock *socket = NULL; - struct nl_cache *cache = NULL; - struct nl_cache *link_cache = NULL; - struct rib_list ribs = {0}; - - // temp buffers - char routes_buffer[PATH_MAX]; - char value_buffer[PATH_MAX]; - char ip_buffer[INET6_ADDRSTRLEN]; - char prefix_buffer[INET6_ADDRSTRLEN + 1 + 3]; - char xpath_buffer[256] = {0}; - - ly_ctx = sr_get_context(sr_session_get_connection(session)); - - ly_uv4mod = ly_ctx_get_module(ly_ctx, "ietf-ipv4-unicast-routing", "2018-03-13"); - ly_uv6mod = ly_ctx_get_module(ly_ctx, "ietf-ipv6-unicast-routing", "2018-03-13"); - - socket = nl_socket_alloc(); - if (socket == NULL) { - SRP_LOG_ERR("unable to init nl_sock struct..."); - goto error_out; - } - - nl_err = nl_connect(socket, NETLINK_ROUTE); - if (nl_err != 0) { - SRP_LOG_ERR("nl_connect failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - nl_err = rtnl_route_alloc_cache(socket, AF_UNSPEC, 0, &cache); - if (nl_err != 0) { - SRP_LOG_ERR("rtnl_route_alloc_cache failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - nl_err = rtnl_link_alloc_cache(socket, AF_UNSPEC, &link_cache); - if (nl_err != 0) { - SRP_LOG_ERR("rtnl_link_alloc_cache failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - nl_err = rtnl_route_read_table_names("/etc/iproute2/rt_tables"); - if (nl_err != 0) { - SRP_LOG_ERR("rtnl_route_read_table_names failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - nl_err = rtnl_route_read_protocol_names("/etc/iproute2/rt_protos"); - if (nl_err != 0) { - SRP_LOG_ERR("rtnl_route_read_table_names failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - error = routing_collect_routes(cache, link_cache, &ribs); - if (error != 0) { - goto error_out; - } - - for (size_t hash_iter = 0; hash_iter < ribs.size; hash_iter++) { - // create new routes container for every table - const struct route_list_hash *ROUTES_HASH = &ribs.list[hash_iter].routes; - const int ADDR_FAMILY = ribs.list[hash_iter].address_family; - const char *TABLE_NAME = ribs.list[hash_iter].name; - snprintf(routes_buffer, sizeof(routes_buffer), "%s[name='%s-%s']/routes", ROUTING_RIB_LIST_YANG_PATH, ADDR_FAMILY == AF_INET ? "ipv4" : "ipv6", TABLE_NAME); - ly_err = lyd_new_path(*parent, ly_ctx, routes_buffer, NULL, LYD_NEW_PATH_UPDATE, &routes_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new routes node"); - goto error_out; - } - - for (size_t i = 0; i < ROUTES_HASH->size; i++) { - struct route_list *list_ptr = &ROUTES_HASH->list_route[i]; - struct nl_addr *dst_prefix = ROUTES_HASH->list_addr[i]; - nl_addr2str(dst_prefix, ip_buffer, sizeof(ip_buffer)); - - // check for prefix - libnl doesn't write prefix into the buffer if its 8*4/8*6 i.e. only that address/no subnet - if (strchr(ip_buffer, '/') == NULL) { - snprintf(prefix_buffer, sizeof(prefix_buffer), "%s/%d", ip_buffer, nl_addr_get_prefixlen(dst_prefix)); - } else { - snprintf(prefix_buffer, sizeof(prefix_buffer), "%s", ip_buffer); - } - - for (size_t j = 0; j < list_ptr->size; j++) { - const struct route *ROUTE = &list_ptr->list[j]; - // create a new list and after that add properties to it - ly_err = lyd_new_path(routes_node, ly_ctx, "routes/route", NULL, 0, &ly_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new route node"); - goto error_out; - } - - // route-preference - snprintf(value_buffer, sizeof(value_buffer), "%u", ROUTE->preference); - SRP_LOG_DBG("route-preference = %s", value_buffer); - ly_err = lyd_new_path(ly_node, ly_ctx, "route-preference", (void *) value_buffer, 0, NULL); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new route-preference node"); - goto error_out; - } - - // next-hop container - const union route_next_hop_value *NEXTHOP = &ROUTE->next_hop.value; - ly_err = lyd_new_path(ly_node, ly_ctx, "next-hop", NULL, 0, &nh_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new next-hop node"); - goto error_out; - } - - switch (ROUTE->next_hop.kind) { - case route_next_hop_kind_none: - break; - case route_next_hop_kind_simple: { - struct rtnl_link *iface = rtnl_link_get(link_cache, ROUTE->next_hop.value.simple.ifindex); - const char *if_name = rtnl_link_get_name(iface); - - // outgoing-interface - SRP_LOG_DBG("outgoing-interface = %s", if_name); - ly_err = lyd_new_path(nh_node, ly_ctx, "outgoing-interface", (void *) if_name, 0, NULL); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new outgoing-interface node"); - goto error_out; - } - - // next-hop-address - if (NEXTHOP->simple.addr) { - nl_addr2str(NEXTHOP->simple.addr, ip_buffer, sizeof(ip_buffer)); - if (ADDR_FAMILY == AF_INET && ly_uv4mod != NULL) { - SRP_LOG_DBG("IPv4 next-hop-address = %s", ip_buffer); - ly_err = lyd_new_term(nh_node, ly_uv4mod, "next-hop-address", ip_buffer, false, NULL); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new IPv4 next-hop-address node"); - goto error_out; - } - } else if (ADDR_FAMILY == AF_INET6 && ly_uv6mod != NULL) { - SRP_LOG_DBG("IPv6 next-hop-address = %s", ip_buffer); - ly_err = lyd_new_term(nh_node, ly_uv6mod, "next-hop-address", ip_buffer, false, NULL); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new IPv6 next-hop-address node"); - goto error_out; - } - } - } - rtnl_link_put(iface); - break; - } - case route_next_hop_kind_special: - break; - case route_next_hop_kind_list: { - const struct route_next_hop_list *NEXTHOP_LIST = &ROUTE->next_hop.value.list; - - for (size_t k = 0; k < NEXTHOP_LIST->size; k++) { - struct rtnl_link *iface = rtnl_link_get(link_cache, NEXTHOP_LIST->list[k].ifindex); - const char *if_name = rtnl_link_get_name(iface); - - error = snprintf(xpath_buffer, sizeof(xpath_buffer), "next-hop/next-hop-list/next-hop[index=%d]", NEXTHOP_LIST->list[k].ifindex); - if (error < 0) { - SRP_LOG_ERR("unable to create new next-hop-list/next-hop node, couldn't retrieve interface index"); - goto error_out; - } - ly_err = lyd_new_path(nh_node, ly_ctx, xpath_buffer, NULL, 0, &nh_list_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new next-hop-list/next-hop node"); - goto error_out; - } - - // outgoing-interface - SRP_LOG_DBG("outgoing-interface = %s", if_name); - ly_err = lyd_new_path(nh_list_node, ly_ctx, "outgoing-interface", (void *) if_name, 0, NULL); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new outgoing-interface node"); - goto error_out; - } - - // next-hop-address - if (NEXTHOP_LIST->list[k].addr) { - nl_addr2str(NEXTHOP_LIST->list[k].addr, ip_buffer, sizeof(ip_buffer)); - if (ADDR_FAMILY == AF_INET && ly_uv4mod != NULL) { - SRP_LOG_DBG("IPv4 next-hop/next-hop-list/next-hop/next-hop-address = %s", ip_buffer); - ly_err = lyd_new_term(nh_list_node, ly_uv4mod, "next-hop-address", ip_buffer, false, NULL); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new IPv4 next-hop-address node"); - goto error_out; - } - } else if (ADDR_FAMILY == AF_INET6 && ly_uv6mod != NULL) { - SRP_LOG_DBG("IPv6 next-hop/next-hop-list/next-hop/next-hop-address = %s", ip_buffer); - ly_err = lyd_new_term(nh_list_node, ly_uv6mod, "next-hop-address", ip_buffer, false, NULL); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new IPv6 next-hop-address node"); - goto error_out; - } - } - } - rtnl_link_put(iface); - } - break; - } - } - - // destination-prefix - if (ADDR_FAMILY == AF_INET && ly_uv4mod != NULL) { - if (strncmp("none/0", prefix_buffer, strnlen(prefix_buffer, sizeof(prefix_buffer))) == 0) { - strcpy(prefix_buffer, "0.0.0.0/0"); - } - - SRP_LOG_DBG("destination-prefix = %s", prefix_buffer); - ly_err = lyd_new_term(ly_node, ly_uv4mod, "destination-prefix", prefix_buffer, false, NULL); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new IPv4 destination-prefix node"); - goto error_out; - } - } else if (ADDR_FAMILY == AF_INET6 && ly_uv6mod != NULL) { - if (strncmp("none/0", prefix_buffer, strnlen(prefix_buffer, sizeof(prefix_buffer))) == 0) { - strcpy(prefix_buffer, "::/0"); - } - - SRP_LOG_DBG("destination-prefix = %s", prefix_buffer); - ly_err = lyd_new_term(ly_node, ly_uv6mod, "destination-prefix", prefix_buffer, false, NULL); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new IPv6 destination-prefix node"); - goto error_out; - } - } - - // route-metadata/source-protocol - SRP_LOG_DBG("source-protocol = %s", ROUTE->metadata.source_protocol); - ly_err = lyd_new_path(ly_node, ly_ctx, "source-protocol", ROUTE->metadata.source_protocol, 0, NULL); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new source-protocol node"); - goto error_out; - } - - // route-metadata/active - if (ROUTE->metadata.active == 1) { - SRP_LOG_DBG("active = %d", ROUTE->metadata.active); - ly_err = lyd_new_path(ly_node, ly_ctx, "active", NULL, 0, NULL); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new active node"); - goto error_out; - } - } - } - } - } - - if (error != 0) { - goto error_out; - } - - goto out; - -error_out: - SRP_LOG_ERR("unable to return routes for routing tables"); - error = SR_ERR_CALLBACK_FAILED; - -out: - rib_list_free(&ribs); - nl_socket_free(socket); - nl_cache_free(cache); - nl_cache_free(link_cache); - return error; -} - -static int routing_rpc_active_route_cb(sr_session_ctx_t *session, uint32_t subscription_id, const char *xpath, const sr_val_t *input, const size_t input_cnt, sr_event_t event, uint32_t request_id, sr_val_t **output, size_t *output_cnt, void *private_data) -{ - int error = SR_ERR_OK; - SRP_LOG_DBG("xpath for RPC: %s", xpath); - return error; -} - -static int routing_load_data(sr_session_ctx_t *session) -{ - int error = 0; - LY_ERR ly_err = LY_SUCCESS; - const struct ly_ctx *ly_ctx = NULL; - struct lyd_node *routing_container_node = NULL; - - ly_ctx = sr_get_context(sr_session_get_connection(session)); - if (ly_ctx == NULL) { - SRP_LOG_ERR("unable to get ly_ctx variable... exiting immediately"); - goto error_out; - } - - // create the routing container node and pass it to other functions which will add to the given node - ly_err = lyd_new_path(NULL, ly_ctx, ROUTING_CONTAINER_YANG_PATH, NULL, 0, &routing_container_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create the routing container ly node"); - goto error_out; - } - - error = routing_load_ribs(session, routing_container_node); - if (error) { - SRP_LOG_ERR("routing_load_ribs failed : %d", error); - goto error_out; - } - - error = routing_load_control_plane_protocols(session, routing_container_node); - if (error) { - SRP_LOG_ERR("routing_load_control_plane_protocols failed : %d", error); - goto error_out; - } - - // after all nodes have been added with no error -> edit the values and apply changes - error = sr_edit_batch(session, routing_container_node, "merge"); - if (error != SR_ERR_OK) { - SRP_LOG_ERR("sr_edit_batch error (%d): %s", error, sr_strerror(error)); - goto error_out; - } - error = sr_apply_changes(session, 0); - if (error != 0) { - SRP_LOG_ERR("sr_apply_changes error (%d): %s", error, sr_strerror(error)); - goto error_out; - } - - goto out; -error_out: - SRP_LOG_ERR("unable to load initial data"); -out: - if (routing_container_node) { - lyd_free_tree(routing_container_node); - } - return error; -} - -static int routing_load_ribs(sr_session_ctx_t *session, struct lyd_node *routing_container_node) -{ - // error handling - int error = 0; - int nl_err = 0; - LY_ERR ly_err = LY_SUCCESS; - - // libyang - struct lyd_node *ribs_node = NULL, *rib_node = NULL, *added_node = NULL; - const struct ly_ctx *ly_ctx = NULL; - - // libnl - struct nl_sock *socket = NULL; - struct nl_cache *cache = NULL; - struct rib_list ribs = {0}; - - // temp buffers - char list_buffer[PATH_MAX] = {0}; - - ly_ctx = sr_get_context(sr_session_get_connection(session)); - if (!ly_ctx) { - SRP_LOG_ERR("unable to load external modules... exiting"); - error = -1; - goto error_out; - } - - rib_list_init(&ribs); - - socket = nl_socket_alloc(); - if (socket == NULL) { - SRP_LOG_ERR("unable to init nl_sock struct..."); - goto error_out; - } - - nl_err = nl_connect(socket, NETLINK_ROUTE); - if (nl_err != 0) { - SRP_LOG_ERR("nl_connect failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - nl_err = rtnl_route_alloc_cache(socket, AF_UNSPEC, 0, &cache); - if (nl_err != 0) { - SRP_LOG_ERR("rtnl_route_alloc_cache failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - nl_err = rtnl_route_read_table_names("/etc/iproute2/rt_tables"); - if (nl_err != 0) { - SRP_LOG_ERR("rtnl_route_read_table_names failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - nl_err = rtnl_route_read_protocol_names("/etc/iproute2/rt_protos"); - if (nl_err != 0) { - SRP_LOG_ERR("rtnl_route_read_table_names failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - error = routing_collect_ribs(cache, &ribs); - if (error != 0) { - goto error_out; - } - - ly_err = lyd_new_path(routing_container_node, ly_ctx, ROUTING_RIBS_CONTAINER_YANG_PATH, NULL, 0, &ribs_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create rib list container..."); - goto error_out; - } - - // all RIBs loaded - add them to the initial config - struct rib *iter = NULL; - for (size_t i = 0; i < ribs.size; i++) { - iter = &ribs.list[i]; - SRP_LOG_DBG("adding table %s to the list", iter->name); - - // write the current adding table into the buffer - snprintf(list_buffer, sizeof(list_buffer), "%s[name='%s-%s']", ROUTING_RIB_LIST_YANG_PATH, iter->address_family == AF_INET ? "ipv4" : "ipv6", iter->name); - ly_err = lyd_new_path(ribs_node, ly_ctx, list_buffer, NULL, 0, &rib_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to add new list..."); - goto error_out; - } - - // address family - ly_err = lyd_new_path(rib_node, ly_ctx, "address-family", iter->address_family == AF_INET ? "ipv4" : "ipv6", 0, &added_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to add 'address-family' node to the tree"); - goto error_out; - } - - // description - if (iter->description[0] != 0) { - ly_err = lyd_new_path(rib_node, ly_ctx, "description", iter->description, 0, &added_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to add 'description' node to the rib"); - goto error_out; - } - } - } - - goto out; -error_out: - SRP_LOG_ERR("error loading initial data"); - error = -1; - -out: - rib_list_free(&ribs); - nl_socket_free(socket); - nl_cache_free(cache); - - return error; -} - -static int routing_collect_ribs(struct nl_cache *routes_cache, struct rib_list *ribs) -{ - int error = 0; - struct rtnl_route *route = NULL; - char table_buffer[32] = {0}; - char name_buffer[32 + 5] = {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)); - snprintf(name_buffer, sizeof(name_buffer), "%s-%s", af == AF_INET ? "ipv4" : "ipv6", table_buffer); - - // add the table to the list and set properties - rib_list_add(ribs, 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, 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); - if (error != 0) { - goto error_out; - } - - goto out; - -error_out: - SRP_LOG_ERR("error collecting RIBs: %d", error); - -out: - return error; -} - -static int routing_collect_routes(struct nl_cache *routes_cache, struct nl_cache *link_cache, struct rib_list *ribs) -{ - 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; - - error = routing_collect_ribs(routes_cache, ribs); - 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 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, 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 -> TODO: see what about special type - 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, 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); - } - - for (size_t i = 0; i < ribs->size; i++) { - const struct route_list_hash *routes_hash = &ribs->list[i].routes; - if (routes_hash->size) { - // iterate every "hash" and set the active value for the preferred route - for (size_t j = 0; j < routes_hash->size; j++) { - struct route_list *ls = &routes_hash->list_route[j]; - if (ls->size > 0) { - struct route *pref = &ls->list[0]; - for (size_t k = 1; k < ls->size; k++) { - struct route *ptr = &ls->list[k]; - if (ptr->preference < pref->preference) { - pref = ptr; - } - } - pref->metadata.active = 1; - } - } - } - } - -error_out: - return error; -} - -static int routing_load_control_plane_protocols(sr_session_ctx_t *session, struct lyd_node *routing_container_node) -{ - // error handling - int error = 0; - int nl_err = 0; - LY_ERR ly_err = LY_SUCCESS; - - // libyang - const struct lys_module *ly_uv4mod = NULL, *ly_uv6mod = NULL; - const struct ly_ctx *ly_ctx = NULL; - struct lyd_node *cpp_container_node = NULL; - struct lyd_node *cpp_list_node = NULL; - struct lyd_node *static_routes_container_node = NULL; - struct lyd_node *tmp_node = NULL; - struct lyd_node *ipv4_container_node = NULL, *ipv6_container_node = NULL; - struct lyd_node *route_node = NULL; - struct lyd_node *nh_node = NULL, *nh_list_node = NULL; - - // temp buffers - char list_buffer[PATH_MAX] = {0}; - char ip_buffer[INET6_ADDRSTRLEN] = {0}; - char prefix_buffer[INET6_ADDRSTRLEN + 1 + 3] = {0}; - char route_path_buffer[PATH_MAX] = {0}; - char xpath_buffer[256] = {0}; - - // control-plane-protocol structs - struct control_plane_protocol cpp_map[ROUTING_PROTOS_COUNT] = {0}; - - ly_ctx = sr_get_context(sr_session_get_connection(session)); - if (ly_ctx) { - ly_uv4mod = ly_ctx_get_module(ly_ctx, "ietf-ipv4-unicast-routing", "2018-03-13"); - ly_uv6mod = ly_ctx_get_module(ly_ctx, "ietf-ipv6-unicast-routing", "2018-03-13"); - } else { - SRP_LOG_ERR("unable to load external modules... exiting"); - error = -1; - goto error_out; - } - - nl_err = rtnl_route_read_table_names("/etc/iproute2/rt_tables"); - if (nl_err != 0) { - SRP_LOG_ERR("rtnl_route_read_table_names failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - nl_err = rtnl_route_read_protocol_names("/etc/iproute2/rt_protos"); - if (nl_err != 0) { - SRP_LOG_ERR("rtnl_route_read_table_names failed (%d): %s", nl_err, nl_geterror(nl_err)); - goto error_out; - } - - // build protocols map array - error = routing_build_protos_map(cpp_map); - if (error) { - SRP_LOG_ERR("unable to build protocols mapping array: %d", error); - goto error_out; - } - - ly_err = lyd_new_path(routing_container_node, ly_ctx, ROUTING_CONTROL_PLANE_PROTOCOLS_CONTAINER_YANG_PATH, NULL, 0, &cpp_container_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create control plane protocols container..."); - goto error_out; - } - - // control plane protocols container created - add all protocols to the inner list - - // add the data to the datastore - for (size_t i = 0; i < ROUTING_PROTOS_COUNT; i++) { - const struct control_plane_protocol *proto = &cpp_map[i]; - if (proto->initialized) { - // write proto path - type + name are added automatically when creating the list node - snprintf(list_buffer, sizeof(list_buffer), "%s[type=\"%s\"][name=\"%s\"]", ROUTING_CONTROL_PLANE_PROTOCOL_LIST_YANG_PATH, proto->type, proto->name); - ly_err = lyd_new_path(cpp_container_node, ly_ctx, list_buffer, NULL, 0, &cpp_list_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to add new control plane protocol %s", proto->name); - goto error_out; - } - - // description - ly_err = lyd_new_path(cpp_list_node, ly_ctx, "description", proto->description, 0, &tmp_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to add 'description' node for the control plane protocol %s...", proto->name); - goto error_out; - } - - // static protocol -> static-routes - if (strncmp(proto->name, "static", sizeof("static") - 1) == 0) { - ly_err = lyd_new_path(cpp_list_node, ly_ctx, "static-routes", NULL, 0, &static_routes_container_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to add static-routes container node... exiting"); - goto error_out; - } - - // for the static protocol add ipv4 and ipv6 static routes - each destination prefix => one route => one nexthop + description - ly_err = lyd_new_inner(static_routes_container_node, ly_uv4mod, "ipv4", false, &ipv4_container_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to add ipv4 container node... exiting"); - goto error_out; - } - ly_err = lyd_new_inner(static_routes_container_node, ly_uv6mod, "ipv6", false, &ipv6_container_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to add ipv6 container node... exiting"); - goto error_out; - } - - // ipv4 - for (size_t j = 0; j < ipv4_static_routes->size; j++) { - const struct nl_addr *DST_PREFIX = ipv4_static_routes->list_addr[j]; - const struct route *ROUTE = &ipv4_static_routes->list_route[j].list[0]; - const union route_next_hop_value *NEXTHOP = &ROUTE->next_hop.value; - - // configure prefix - nl_addr2str(DST_PREFIX, ip_buffer, sizeof(ip_buffer)); - if (strchr(ip_buffer, '/') == NULL) { - snprintf(prefix_buffer, sizeof(prefix_buffer), "%s/%d", ip_buffer, nl_addr_get_prefixlen(DST_PREFIX)); - } else { - snprintf(prefix_buffer, sizeof(prefix_buffer), "%s", ip_buffer); - } - - // create new list node - snprintf(route_path_buffer, sizeof(route_path_buffer), "route[destination-prefix=\"%s\"]", prefix_buffer); - ly_err = lyd_new_path(ipv4_container_node, ly_ctx, route_path_buffer, NULL, 0, &route_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create a new route node for the list %s", route_path_buffer); - goto error_out; - } - - // description - if (ROUTE->metadata.description != NULL) { - ly_err = lyd_new_term(route_node, ly_uv4mod, "description", ROUTE->metadata.description, false, &tmp_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to add description leaf to the %s node", route_path_buffer); - goto error_out; - } - } - - // next-hop container - ly_err = lyd_new_inner(route_node, ly_uv4mod, "next-hop", false, &nh_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new next-hop container for the node %s", route_path_buffer); - goto error_out; - } - switch (ROUTE->next_hop.kind) { - case route_next_hop_kind_none: - break; - case route_next_hop_kind_simple: { - // next-hop-address - if (NEXTHOP->simple.addr) { - nl_addr2str(NEXTHOP->simple.addr, ip_buffer, sizeof(ip_buffer)); - ly_err = lyd_new_term(nh_node, ly_uv4mod, "next-hop-address", ip_buffer, false, &tmp_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create next-hop-address leaf for the node %s", route_path_buffer); - goto error_out; - } - - ly_err = lyd_new_term(nh_node, ly_uv4mod, "outgoing-interface", NEXTHOP->simple.if_name, false, &tmp_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create outgoing-interface leaf for the node %s", route_path_buffer); - goto error_out; - } - } - break; - } - case route_next_hop_kind_special: - break; - case route_next_hop_kind_list: { - const struct route_next_hop_list *NEXTHOP_LIST = &ROUTE->next_hop.value.list; - for (size_t k = 0; k < NEXTHOP_LIST->size; k++) { - - error = snprintf(xpath_buffer, sizeof(xpath_buffer), "next-hop/next-hop-list]/next-hop[index=%d]", NEXTHOP_LIST->list[k].ifindex); - if (error < 0) { - SRP_LOG_ERR("unable to create new next-hop-list/next-hop node, couldn't retrieve interface index"); - goto error_out; - } - - ly_err = lyd_new_any(nh_node, ly_uv4mod, xpath_buffer, NULL, true, LYD_ANYDATA_STRING, false, &nh_list_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new next-hop-list/next-hop list for the node %s", route_path_buffer); - SRP_LOG_ERR("ly err = %d", ly_err); - goto error_out; - } - // next-hop-address - if (NEXTHOP_LIST->list[k].addr) { - nl_addr2str(NEXTHOP_LIST->list[k].addr, ip_buffer, sizeof(ip_buffer)); - ly_err = lyd_new_term(nh_list_node, ly_uv4mod, "next-hop-address", ip_buffer, false, &tmp_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create next-hop-address leaf in the list for route %s", route_path_buffer); - goto error_out; - } - - ly_err = lyd_new_term(nh_list_node, ly_uv4mod, "outgoing-interface", NEXTHOP_LIST->list[k].if_name, false, &tmp_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create outgoing-interface leaf in the list for route %s", route_path_buffer); - goto error_out; - } - } - } - break; - } - } - } - - // ipv6 - for (size_t j = 0; j < ipv6_static_routes->size; j++) { - const struct nl_addr *DST_PREFIX = ipv6_static_routes->list_addr[j]; - const struct route *ROUTE = &ipv6_static_routes->list_route[j].list[0]; - const union route_next_hop_value *NEXTHOP = &ROUTE->next_hop.value; - - // configure prefix - nl_addr2str(DST_PREFIX, ip_buffer, sizeof(ip_buffer)); - if (strchr(ip_buffer, '/') == NULL) { - snprintf(prefix_buffer, sizeof(prefix_buffer), "%s/%d", ip_buffer, nl_addr_get_prefixlen(DST_PREFIX)); - } else { - snprintf(prefix_buffer, sizeof(prefix_buffer), "%s", ip_buffer); - } - - // create new list node - snprintf(route_path_buffer, sizeof(route_path_buffer), "route[destination-prefix=\"%s\"]", prefix_buffer); - ly_err = lyd_new_path(ipv6_container_node, ly_ctx, route_path_buffer, NULL, 0, &route_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create a new route node for the list %s", route_path_buffer); - goto error_out; - } - - // description - if (ROUTE->metadata.description != NULL) { - ly_err = lyd_new_term(route_node, ly_uv6mod, "description", ROUTE->metadata.description, false, &tmp_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to add description leaf to the %s node", route_path_buffer); - goto error_out; - } - } - - // next-hop container - ly_err = lyd_new_inner(route_node, ly_uv6mod, "next-hop", false, &nh_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new next-hop container for the node %s", route_path_buffer); - goto error_out; - } - switch (ROUTE->next_hop.kind) { - case route_next_hop_kind_none: - break; - case route_next_hop_kind_simple: { - // next-hop-address - if (NEXTHOP->simple.addr) { - nl_addr2str(NEXTHOP->simple.addr, ip_buffer, sizeof(ip_buffer)); - ly_err = lyd_new_term(nh_node, ly_uv6mod, "next-hop-address", ip_buffer, false, &tmp_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create next-hop-address leaf for the node %s", route_path_buffer); - goto error_out; - } - - ly_err = lyd_new_term(nh_node, ly_uv6mod, "outgoing-interface", NEXTHOP->simple.if_name, false, &tmp_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create outgoing-interface leaf for the node %s", route_path_buffer); - goto error_out; - } - } - break; - } - case route_next_hop_kind_special: - break; - case route_next_hop_kind_list: { - const struct route_next_hop_list *NEXTHOP_LIST = &ROUTE->next_hop.value.list; - for (size_t k = 0; k < NEXTHOP_LIST->size; k++) { - error = snprintf(xpath_buffer, sizeof(xpath_buffer), "next-hop/next-hop-list/next-hop[index=%d]", NEXTHOP_LIST->list[k].ifindex); - if (error < 0) { - SRP_LOG_ERR("unable to create new next-hop-list/next-hop node, couldn't retrieve interface index"); - goto error_out; - } - - ly_err = lyd_new_any(nh_node, ly_uv6mod, xpath_buffer, NULL, true, LYD_ANYDATA_STRING, false, &nh_list_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create new next-hop-list/next-hop list for the node %s", route_path_buffer); - goto error_out; - } - // next-hop-address - if (NEXTHOP_LIST->list[k].addr) { - nl_addr2str(NEXTHOP_LIST->list[k].addr, ip_buffer, sizeof(ip_buffer)); - ly_err = lyd_new_term(nh_list_node, ly_uv6mod, "next-hop-address", ip_buffer, false, &tmp_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create next-hop-address leaf in the list for route %s", route_path_buffer); - goto error_out; - } - - ly_err = lyd_new_term(nh_list_node, ly_uv6mod, "outgoing-interface", NEXTHOP_LIST->list[k].if_name, false, &tmp_node); - if (ly_err != LY_SUCCESS) { - SRP_LOG_ERR("unable to create outgoing-interface leaf in the list for route %s", route_path_buffer); - goto error_out; - } - } - } - break; - } - } - } - } - } - } - - goto out; - -error_out: - SRP_LOG_ERR("error loading initial data for control-plane-procotols container"); - error = 1; -out: - // free all control plane protocol structs - for (size_t i = 0; i < ROUTING_PROTOS_COUNT; i++) { - control_plane_protocol_free(&cpp_map[i]); - } - - return error; -} - -static int routing_build_rib_descriptions(struct rib_list *ribs) -{ - int error = 0; - - const char *data_dir = 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)"}, - }; - struct { - struct rib_description_pair *list; - size_t size; - } rib_descriptions = {0}; - - // file stream - FILE *fptr = NULL; - - // buffers and temporary values - char path_buffer[PATH_MAX] = {0}; - char line_buffer[PATH_MAX] = {0}; - char name_buffer[32 + 5] = {0}; - struct rib_description_pair tmp_description = {0}; - - // get the data_dir variable using getenv - data_dir = getenv(ROUTING_PLUGIN_DATA_DIR); - if (data_dir == NULL) { - SRP_LOG_ERR("unable to use environment variable for the plugin data dir: %s", ROUTING_PLUGIN_DATA_DIR); - goto error_out; - } - - // create file path and see if it exists - snprintf(path_buffer, sizeof(path_buffer), "%s%s%s", data_dir, (data_dir[strlen(data_dir) - 1] == '/') ? "" : "/", ROUTING_RIBS_DESCRIPTIONS_MAP_FNAME); - SRP_LOG_DBG("RIBs description map file path: %s", path_buffer); - - if (access(path_buffer, F_OK) != 0) { - // file doesn't exist - use default values and create the file for future use - fptr = fopen(path_buffer, "w"); - if (fptr == NULL) { - SRP_LOG_ERR("unable to open %s file for writing", path_buffer); - goto error_out; - } - - for (unsigned i = 0; i < sizeof(default_descriptions) / sizeof(default_descriptions[0]); i++) { - rib_descriptions.list = xrealloc(rib_descriptions.list, sizeof(struct rib_description_pair) * (unsigned) (rib_descriptions.size + 1)); - memcpy(&rib_descriptions.list[rib_descriptions.size], &default_descriptions[i], sizeof(struct rib_description_pair)); - rib_descriptions.size += 1; - - fprintf(fptr, "%s\t\"%s\"\n", default_descriptions[i].name, default_descriptions[i].description); - } - } else { - // file exists - use those values - fptr = fopen(path_buffer, "r"); - if (fptr == NULL) { - SRP_LOG_ERR("unable to open file %s", path_buffer); - goto error_out; - } - - while (fgets(line_buffer, sizeof(line_buffer), fptr) != NULL) { - const int read_n = sscanf(line_buffer, "%s \"%256[^\"]\"", tmp_description.name, tmp_description.description); - if (read_n == 2) { - // add the description to the list - rib_descriptions.list = xrealloc(rib_descriptions.list, sizeof(struct rib_description_pair) * (unsigned) (rib_descriptions.size + 1)); - memcpy(&rib_descriptions.list[rib_descriptions.size], &tmp_description, sizeof(struct rib_description_pair)); - rib_descriptions.size += 1; - } else { - SRP_LOG_ERR("unable to correctly read 2 values from the RIBs map file"); - goto error_out; - } - } - } - - // now iterate over each rib and set its description - for (size_t i = 0; i < ribs->size; i++) { - const struct rib *RIB = &ribs->list[i]; - 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 < rib_descriptions.size; j++) { - const struct rib_description_pair *PAIR = &rib_descriptions.list[j]; - if (strncmp(name_buffer, PAIR->name, NAME_LEN) == 0) { - memcpy(((struct rib *) RIB)->description, PAIR->description, sizeof(PAIR->description)); - break; - } - } - } - - goto out; - -error_out: - SRP_LOG_ERR("unable to build RIB descriptions"); - error = -1; - -out: - if (fptr != NULL) { - fclose(fptr); - } - FREE_SAFE(rib_descriptions.list); - - return error; -} - -static inline int routing_is_rib_known(int table) -{ - return table == RT_TABLE_DEFAULT || table == RT_TABLE_LOCAL || table == RT_TABLE_MAIN; -} - -static int routing_build_protos_map(struct control_plane_protocol map[ROUTING_PROTOS_COUNT]) -{ - int error = 0; - - // temp variables and buffers - char *data_dir = NULL; - char path_buffer[PATH_MAX] = {0}; - char line_buffer[PATH_MAX] = {0}; - int tmp_type = 0; - char type_buffer[100] = {0}; - char desc_buffer[256] = {0}; - FILE *fptr = NULL; - - data_dir = getenv(ROUTING_PLUGIN_DATA_DIR); - if (data_dir == NULL) { - SRP_LOG_ERR("unable to use environment variable for the plugin data dir: %s", ROUTING_PLUGIN_DATA_DIR); - goto error_out; - } - snprintf(path_buffer, sizeof(path_buffer), "%s%s%s", data_dir, (data_dir[strlen(data_dir) - 1] == '/' ? "" : "/"), ROUTING_PROTOS_MAP_FNAME); - fptr = fopen(path_buffer, "r"); - if (fptr == NULL) { - SRP_LOG_DBG("protocols map file doesnt exist - using only already known proto types and descriptions"); - // file doesn't exist -> build the initial map and write it to the file - const char *known_types_map[ROUTING_PROTOS_COUNT] = { - [RTPROT_UNSPEC] = "ietf-routing:direct", - [RTPROT_REDIRECT] = "ietf-routing:direct", - [RTPROT_KERNEL] = "ietf-routing:direct", - [RTPROT_BOOT] = "ietf-routing:direct", - [RTPROT_STATIC] = "ietf-routing:static", - [RTPROT_GATED] = "ietf-routing:direct", - [RTPROT_RA] = "ietf-routing:direct", - [RTPROT_MRT] = "ietf-routing:direct", - [RTPROT_ZEBRA] = "ietf-routing:direct", - [RTPROT_BIRD] = "ietf-routing:direct", - [RTPROT_DNROUTED] = "ietf-routing:direct", - [RTPROT_XORP] = "ietf-routing:direct", - [RTPROT_NTK] = "ietf-routing:direct", - [RTPROT_DHCP] = "ietf-routing:direct", - [RTPROT_MROUTED] = "ietf-routing:direct", - [RTPROT_BABEL] = "ietf-routing:direct", - [RTPROT_BGP] = "ietf-routing:direct", - [RTPROT_ISIS] = "ietf-routing:direct", - [RTPROT_OSPF] = "ietf-routing:direct", - [RTPROT_RIP] = "ietf-routing:direct", - [RTPROT_EIGRP] = "ietf-routing:direct", - }; - - const char *known_descr_map[ROUTING_PROTOS_COUNT] = { - [RTPROT_UNSPEC] = "unspecified protocol", - [RTPROT_REDIRECT] = "redirect protocol", - [RTPROT_KERNEL] = "kernel protocol", - [RTPROT_BOOT] = "boot protocol", - [RTPROT_STATIC] = "static protocol", - [RTPROT_GATED] = "gated protocol", - [RTPROT_RA] = "ra protocol", - [RTPROT_MRT] = "mrt protocol", - [RTPROT_ZEBRA] = "zebra protocol", - [RTPROT_BIRD] = "bird protocol", - [RTPROT_DNROUTED] = "dnrouted protocol", - [RTPROT_XORP] = "xorp protocol", - [RTPROT_NTK] = "ntk protocol", - [RTPROT_DHCP] = "dhcp protocol", - [RTPROT_MROUTED] = "mrouted protocol", - [RTPROT_BABEL] = "babel protocol", - [RTPROT_BGP] = "bgp protocol", - [RTPROT_ISIS] = "isis protocol", - [RTPROT_OSPF] = "ospf protocol", - [RTPROT_RIP] = "rip protocol", - [RTPROT_EIGRP] = "eigrp protocol", - }; - - fptr = fopen(path_buffer, "w"); - if (fptr == NULL) { - SRP_LOG_ERR("unable to open %s file for writing", ROUTING_PROTOS_MAP_FNAME); - goto error_out; - } - - // file opened - write protocol types into the file - for (int i = 0; i < ROUTING_PROTOS_COUNT; i++) { - if (routing_is_proto_type_known(i)) { - rtnl_route_proto2str(i, map[i].name, sizeof(map[i].name)); - map[i].type = xstrdup(known_types_map[i]); - map[i].description = xstrdup(known_descr_map[i]); - map[i].initialized = 1; - fprintf(fptr, "%d\t%s\t\"%s\"\n", i, map[i].type, map[i].description); - } - } - } else { - SRP_LOG_DBG("protocols map file exists - using map values"); - // file exists -> use map file values - while (fgets(line_buffer, sizeof(line_buffer), fptr) != NULL) { - const int read_n = sscanf(line_buffer, "%d %s \"%256[^\"]\"", &tmp_type, type_buffer, desc_buffer); - if (read_n == 3) { - if (tmp_type >= 0 && tmp_type <= ROUTING_PROTOS_COUNT) { - rtnl_route_proto2str(tmp_type, map[tmp_type].name, sizeof(map[tmp_type].name)); - map[tmp_type].type = xstrdup(type_buffer); - map[tmp_type].description = xstrdup(desc_buffer); - map[tmp_type].initialized = 1; - } else { - SRP_LOG_ERR("invalid protocol type found in the protocol types map file: %d", tmp_type); - goto error_out; - } - } else { - SRP_LOG_ERR("unable to properly read the protocol types map file format; read %d values", read_n); - goto error_out; - } - } - } - goto out; - -error_out: - error = -1; -out: - if (fptr) { - fclose(fptr); - } - return error; -} - -static inline int routing_is_proto_type_known(int type) -{ - return type == RTPROT_UNSPEC || - type == RTPROT_REDIRECT || - type == RTPROT_KERNEL || - type == RTPROT_BOOT || - type == RTPROT_STATIC || - type == RTPROT_GATED || - type == RTPROT_RA || - type == RTPROT_MRT || - type == RTPROT_ZEBRA || - type == RTPROT_BIRD || - type == RTPROT_DNROUTED || - type == RTPROT_XORP || - type == RTPROT_NTK || - type == RTPROT_DHCP || - type == RTPROT_MROUTED || - type == RTPROT_BABEL || - type == RTPROT_BGP || - type == RTPROT_ISIS || - type == RTPROT_OSPF || - type == RTPROT_RIP || - type == RTPROT_EIGRP; -} - -static bool routing_running_datastore_is_empty(void) -{ - FILE *sysrepocfg_DS_empty_check = NULL; - bool is_empty = false; - - sysrepocfg_DS_empty_check = popen(SYSREPOCFG_EMPTY_CHECK_COMMAND, "r"); - if (sysrepocfg_DS_empty_check == NULL) { - SRP_LOG_WRN("could not execute %s", SYSREPOCFG_EMPTY_CHECK_COMMAND); - is_empty = true; - goto out; - } - - if (fgetc(sysrepocfg_DS_empty_check) == EOF) { - is_empty = true; - } - -out: - if (sysrepocfg_DS_empty_check) { - pclose(sysrepocfg_DS_empty_check); - } - return is_empty; } diff --git a/src/routing/routing.h b/src/routing/routing.h index 273b643e..d2ef4a95 100644 --- a/src/routing/routing.h +++ b/src/routing/routing.h @@ -1,3 +1,16 @@ +/* + * 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_H #define ROUTING_PLUGIN_H diff --git a/src/routing/startup.c b/src/routing/startup.c new file mode 100644 index 00000000..75aa42ce --- /dev/null +++ b/src/routing/startup.c @@ -0,0 +1,725 @@ +/* + * 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 "startup.h" +#include "route/list_hash.h" +#include +#include +#include + +#include + +#include +#include +#include + +#include + +static int routing_startup_load_ribs(sr_session_ctx_t *session, struct lyd_node *routing_container_node); +static int routing_startup_load_control_plane_protocols(sr_session_ctx_t *session, struct lyd_node *routing_container_node); +static int routing_startup_collect_static_routes(struct route_list_hash_element **ipv4_head, struct route_list_hash_element **ipv6_head); +static int routing_build_protos_map(struct control_plane_protocol map[ROUTING_PROTOS_COUNT]); +static int routing_is_proto_type_known(int type); + +int routing_startup_load_data(struct routing_ctx *ctx, sr_session_ctx_t *session) +{ + int error = 0; + LY_ERR ly_err = LY_SUCCESS; + const struct ly_ctx *ly_ctx = NULL; + struct lyd_node *routing_container_node = NULL; + + ly_ctx = sr_acquire_context(sr_session_get_connection(session)); + if (ly_ctx == NULL) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to get ly_ctx variable... exiting immediately"); + goto error_out; + } + + // create the routing container node and pass it to other functions which will add to the given node + ly_err = lyd_new_path(NULL, ly_ctx, ROUTING_CONTAINER_YANG_PATH, NULL, 0, &routing_container_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create the routing container ly node"); + goto error_out; + } + + error = routing_startup_load_ribs(session, routing_container_node); + if (error) { + SRPLG_LOG_ERR(PLUGIN_NAME, "routing_load_ribs failed : %d", error); + goto error_out; + } + + error = routing_startup_load_control_plane_protocols(session, routing_container_node); + if (error) { + SRPLG_LOG_ERR(PLUGIN_NAME, "routing_load_control_plane_protocols failed : %d", error); + goto error_out; + } + + // after all nodes have been added with no error -> edit the values and apply changes + error = sr_edit_batch(session, routing_container_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; + } + + goto out; +error_out: + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to load initial data"); +out: + if (routing_container_node) { + lyd_free_tree(routing_container_node); + } + return error; +} + +static int routing_startup_load_ribs(sr_session_ctx_t *session, struct lyd_node *routing_container_node) +{ + // error handling + int error = 0; + int nl_err = 0; + LY_ERR ly_err = LY_SUCCESS; + + // libyang + struct lyd_node *ribs_node = NULL, *rib_node = NULL, *added_node = NULL; + const struct ly_ctx *ly_ctx = NULL; + + // libnl + struct nl_sock *socket = NULL; + struct nl_cache *cache = NULL; + struct rib_list_element *ribs_head = NULL, *ribs_iter = NULL; + + // temp buffers + char list_buffer[PATH_MAX] = {0}; + + ly_ctx = sr_acquire_context(sr_session_get_connection(session)); + if (!ly_ctx) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to load external modules... exiting"); + error = -1; + goto error_out; + } + + // rib_list_init(&ribs_head); + + socket = nl_socket_alloc(); + if (socket == NULL) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to init nl_sock struct..."); + goto error_out; + } + + 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)); + goto error_out; + } + + nl_err = rtnl_route_alloc_cache(socket, AF_UNSPEC, 0, &cache); + if (nl_err != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_route_alloc_cache failed (%d): %s", nl_err, nl_geterror(nl_err)); + goto error_out; + } + + nl_err = rtnl_route_read_table_names("/etc/iproute2/rt_tables"); + if (nl_err != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_route_read_table_names failed (%d): %s", nl_err, nl_geterror(nl_err)); + goto error_out; + } + + nl_err = rtnl_route_read_protocol_names("/etc/iproute2/rt_protos"); + if (nl_err != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_route_read_table_names failed (%d): %s", nl_err, nl_geterror(nl_err)); + goto error_out; + } + + error = routing_collect_ribs(cache, &ribs_head); + if (error != 0) { + goto error_out; + } + + ly_err = lyd_new_path(routing_container_node, ly_ctx, ROUTING_RIBS_CONTAINER_YANG_PATH, NULL, 0, &ribs_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create rib list container..."); + goto error_out; + } + + // all RIBs loaded - add them to the initial config + struct rib *iter = NULL; + LL_FOREACH(ribs_head, ribs_iter) + { + iter = &ribs_iter->rib; + SRPLG_LOG_DBG(PLUGIN_NAME, "adding table %s to the list", iter->name); + + // write the current adding table into the buffer + snprintf(list_buffer, sizeof(list_buffer), "%s[name='%s-%s']", ROUTING_RIB_LIST_YANG_PATH, iter->address_family == AF_INET ? "ipv4" : "ipv6", iter->name); + ly_err = lyd_new_path(ribs_node, ly_ctx, list_buffer, NULL, 0, &rib_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to add new list..."); + goto error_out; + } + + // address family + ly_err = lyd_new_path(rib_node, ly_ctx, "address-family", iter->address_family == AF_INET ? "ipv4" : "ipv6", 0, &added_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to add 'address-family' node to the tree"); + goto error_out; + } + + // description + if (iter->description[0] != 0) { + ly_err = lyd_new_path(rib_node, ly_ctx, "description", iter->description, 0, &added_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to add 'description' node to the rib"); + goto error_out; + } + } + } + + goto out; +error_out: + SRPLG_LOG_ERR(PLUGIN_NAME, "error loading initial data"); + error = -1; + +out: + rib_list_free(&ribs_head); + nl_socket_free(socket); + nl_cache_free(cache); + + return error; +} + +static int routing_startup_load_control_plane_protocols(sr_session_ctx_t *session, struct lyd_node *routing_container_node) +{ + // error handling + int error = 0; + int nl_err = 0; + LY_ERR ly_err = LY_SUCCESS; + + // libyang + const struct lys_module *ly_uv4mod = NULL, *ly_uv6mod = NULL; + const struct ly_ctx *ly_ctx = NULL; + struct lyd_node *cpp_container_node = NULL; + struct lyd_node *cpp_list_node = NULL; + struct lyd_node *static_routes_container_node = NULL; + struct lyd_node *tmp_node = NULL; + struct lyd_node *ipv4_container_node = NULL, *ipv6_container_node = NULL; + struct lyd_node *route_node = NULL; + struct lyd_node *nh_node = NULL, *nh_list_node = NULL; + + // temp buffers + char list_buffer[PATH_MAX] = {0}; + char ip_buffer[INET6_ADDRSTRLEN] = {0}; + char prefix_buffer[INET6_ADDRSTRLEN + 1 + 3] = {0}; + char route_path_buffer[PATH_MAX] = {0}; + char xpath_buffer[256] = {0}; + + // static routes for static protocol + struct route_list_hash_element *ipv4_static_routes_head = NULL, *ipv6_static_routes_head = NULL; + + // iterators + struct route_list_hash_element *routes_hash_iter = NULL; + + // control-plane-protocol structs + struct control_plane_protocol cpp_map[ROUTING_PROTOS_COUNT] = {0}; + + ly_ctx = sr_acquire_context(sr_session_get_connection(session)); + if (ly_ctx) { + ly_uv4mod = ly_ctx_get_module(ly_ctx, "ietf-ipv4-unicast-routing", "2018-03-13"); + ly_uv6mod = ly_ctx_get_module(ly_ctx, "ietf-ipv6-unicast-routing", "2018-03-13"); + } else { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to load external modules... exiting"); + error = -1; + goto error_out; + } + + nl_err = rtnl_route_read_table_names("/etc/iproute2/rt_tables"); + if (nl_err != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_route_read_table_names failed (%d): %s", nl_err, nl_geterror(nl_err)); + goto error_out; + } + + nl_err = rtnl_route_read_protocol_names("/etc/iproute2/rt_protos"); + if (nl_err != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_route_read_table_names failed (%d): %s", nl_err, nl_geterror(nl_err)); + goto error_out; + } + + // build protocols map array + error = routing_build_protos_map(cpp_map); + if (error) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to build protocols mapping array: %d", error); + goto error_out; + } + + // collect current system static routes + error = routing_startup_collect_static_routes(&ipv4_static_routes_head, &ipv6_static_routes_head); + if (error) { + SRPLG_LOG_ERR(PLUGIN_NAME, "routing_startup_collect_static_routes() failed: %d", error); + goto error_out; + } + + ly_err = lyd_new_path(routing_container_node, ly_ctx, ROUTING_CONTROL_PLANE_PROTOCOLS_CONTAINER_YANG_PATH, NULL, 0, &cpp_container_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create control plane protocols container..."); + goto error_out; + } + + // control plane protocols container created - add all protocols to the inner list + + // add the data to the datastore + for (size_t i = 0; i < ROUTING_PROTOS_COUNT; i++) { + const struct control_plane_protocol *proto = &cpp_map[i]; + if (proto->initialized) { + // write proto path - type + name are added automatically when creating the list node + snprintf(list_buffer, sizeof(list_buffer), "%s[type=\"%s\"][name=\"%s\"]", ROUTING_CONTROL_PLANE_PROTOCOL_LIST_YANG_PATH, proto->type, proto->name); + ly_err = lyd_new_path(cpp_container_node, ly_ctx, list_buffer, NULL, 0, &cpp_list_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to add new control plane protocol %s", proto->name); + goto error_out; + } + + // description + ly_err = lyd_new_path(cpp_list_node, ly_ctx, "description", proto->description, 0, &tmp_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to add 'description' node for the control plane protocol %s...", proto->name); + goto error_out; + } + + // static protocol -> static-routes + if (strncmp(proto->name, "static", sizeof("static") - 1) == 0) { + ly_err = lyd_new_path(cpp_list_node, ly_ctx, "static-routes", NULL, 0, &static_routes_container_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to add static-routes container node... exiting"); + goto error_out; + } + + // for the static protocol add ipv4 and ipv6 static routes - each destination prefix => one route => one nexthop + description + ly_err = lyd_new_inner(static_routes_container_node, ly_uv4mod, "ipv4", false, &ipv4_container_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to add ipv4 container node... exiting"); + goto error_out; + } + + ly_err = lyd_new_inner(static_routes_container_node, ly_uv6mod, "ipv6", false, &ipv6_container_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to add ipv6 container node... exiting"); + goto error_out; + } + + LL_FOREACH(ipv4_static_routes_head, routes_hash_iter) + { + const struct nl_addr *DST_PREFIX = routes_hash_iter->prefix; + const struct route *ROUTE = &routes_hash_iter->routes_head->route; + const union route_next_hop_value *NEXTHOP = &ROUTE->next_hop.value; + + // configure prefix + nl_addr2str(DST_PREFIX, ip_buffer, sizeof(ip_buffer)); + if (strchr(ip_buffer, '/') == NULL) { + snprintf(prefix_buffer, sizeof(prefix_buffer), "%s/%d", ip_buffer, nl_addr_get_prefixlen(DST_PREFIX)); + } else { + snprintf(prefix_buffer, sizeof(prefix_buffer), "%s", ip_buffer); + } + + // create new list node + snprintf(route_path_buffer, sizeof(route_path_buffer), "route[destination-prefix=\"%s\"]", prefix_buffer); + ly_err = lyd_new_path(ipv4_container_node, ly_ctx, route_path_buffer, NULL, 0, &route_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create a new route node for the list %s", route_path_buffer); + goto error_out; + } + + // description + if (ROUTE->metadata.description != NULL) { + ly_err = lyd_new_term(route_node, ly_uv4mod, "description", ROUTE->metadata.description, false, &tmp_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to add description leaf to the %s node", route_path_buffer); + goto error_out; + } + } + + // next-hop container + ly_err = lyd_new_inner(route_node, ly_uv4mod, "next-hop", false, &nh_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new next-hop container for the node %s", route_path_buffer); + goto error_out; + } + switch (ROUTE->next_hop.kind) { + case route_next_hop_kind_none: + break; + case route_next_hop_kind_simple: { + // next-hop-address + if (NEXTHOP->simple.addr) { + nl_addr2str(NEXTHOP->simple.addr, ip_buffer, sizeof(ip_buffer)); + ly_err = lyd_new_term(nh_node, ly_uv4mod, "next-hop-address", ip_buffer, false, &tmp_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create next-hop-address leaf for the node %s", route_path_buffer); + goto error_out; + } + } + + // outgoing-interface + if (NEXTHOP->simple.if_name) { + SRPLG_LOG_DBG(PLUGIN_NAME, "outgoing-interface = %s", NEXTHOP->simple.if_name); + + ly_err = lyd_new_term(nh_node, ly_uv4mod, "outgoing-interface", NEXTHOP->simple.if_name, false, &tmp_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create outgoing-interface leaf for the node %s", route_path_buffer); + goto error_out; + } + } + break; + } + case route_next_hop_kind_special: + break; + case route_next_hop_kind_list: { + struct route_next_hop_list_element *nexthop_iter = NULL; + + LL_FOREACH(ROUTE->next_hop.value.list_head, nexthop_iter) + { + error = snprintf(xpath_buffer, sizeof(xpath_buffer), "next-hop/next-hop-list]/next-hop[index=%d]", nexthop_iter->simple.ifindex); + if (error < 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new next-hop-list/next-hop node, couldn't retrieve interface index"); + goto error_out; + } + + ly_err = lyd_new_any(nh_node, ly_uv4mod, xpath_buffer, NULL, true, LYD_ANYDATA_STRING, false, &nh_list_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new next-hop-list/next-hop list for the node %s", route_path_buffer); + SRPLG_LOG_ERR(PLUGIN_NAME, "ly err = %d", ly_err); + goto error_out; + } + // next-hop-address + if (nexthop_iter->simple.addr) { + nl_addr2str(nexthop_iter->simple.addr, ip_buffer, sizeof(ip_buffer)); + ly_err = lyd_new_term(nh_list_node, ly_uv4mod, "next-hop-address", ip_buffer, false, &tmp_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create next-hop-address leaf in the list for route %s", route_path_buffer); + goto error_out; + } + } + + // outgoing-interface + if (nexthop_iter->simple.if_name) { + ly_err = lyd_new_term(nh_list_node, ly_uv4mod, "outgoing-interface", nexthop_iter->simple.if_name, false, &tmp_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create outgoing-interface leaf in the list for route %s", route_path_buffer); + goto error_out; + } + } + } + break; + } + } + } + + LL_FOREACH(ipv6_static_routes_head, routes_hash_iter) + { + const struct nl_addr *DST_PREFIX = routes_hash_iter->prefix; + const struct route *ROUTE = &routes_hash_iter->routes_head->route; + const union route_next_hop_value *NEXTHOP = &ROUTE->next_hop.value; + + // configure prefix + nl_addr2str(DST_PREFIX, ip_buffer, sizeof(ip_buffer)); + if (strchr(ip_buffer, '/') == NULL) { + snprintf(prefix_buffer, sizeof(prefix_buffer), "%s/%d", ip_buffer, nl_addr_get_prefixlen(DST_PREFIX)); + } else { + snprintf(prefix_buffer, sizeof(prefix_buffer), "%s", ip_buffer); + } + + // create new list node + snprintf(route_path_buffer, sizeof(route_path_buffer), "route[destination-prefix=\"%s\"]", prefix_buffer); + ly_err = lyd_new_path(ipv6_container_node, ly_ctx, route_path_buffer, NULL, 0, &route_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create a new route node for the list %s", route_path_buffer); + goto error_out; + } + + // description + if (ROUTE->metadata.description != NULL) { + ly_err = lyd_new_term(route_node, ly_uv6mod, "description", ROUTE->metadata.description, false, &tmp_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to add description leaf to the %s node", route_path_buffer); + goto error_out; + } + } + + // next-hop container + ly_err = lyd_new_inner(route_node, ly_uv6mod, "next-hop", false, &nh_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new next-hop container for the node %s", route_path_buffer); + goto error_out; + } + switch (ROUTE->next_hop.kind) { + case route_next_hop_kind_none: + break; + case route_next_hop_kind_simple: { + // next-hop-address + if (NEXTHOP->simple.addr) { + nl_addr2str(NEXTHOP->simple.addr, ip_buffer, sizeof(ip_buffer)); + ly_err = lyd_new_term(nh_node, ly_uv6mod, "next-hop-address", ip_buffer, false, &tmp_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create next-hop-address leaf for the node %s", route_path_buffer); + goto error_out; + } + } + + // outgoing-interface + if (NEXTHOP->simple.if_name) { + ly_err = lyd_new_term(nh_node, ly_uv6mod, "outgoing-interface", NEXTHOP->simple.if_name, false, &tmp_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create outgoing-interface leaf for the node %s", route_path_buffer); + goto error_out; + } + } + break; + } + case route_next_hop_kind_special: + break; + case route_next_hop_kind_list: { + struct route_next_hop_list_element *nexthop_iter = NULL; + LL_FOREACH(ROUTE->next_hop.value.list_head, nexthop_iter) + { + error = snprintf(xpath_buffer, sizeof(xpath_buffer), "next-hop/next-hop-list/next-hop[index=%d]", nexthop_iter->simple.ifindex); + if (error < 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new next-hop-list/next-hop node, couldn't retrieve interface index"); + goto error_out; + } + + ly_err = lyd_new_any(nh_node, ly_uv6mod, xpath_buffer, NULL, true, LYD_ANYDATA_STRING, false, &nh_list_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new next-hop-list/next-hop list for the node %s", route_path_buffer); + goto error_out; + } + // next-hop-address + if (nexthop_iter->simple.addr) { + nl_addr2str(nexthop_iter->simple.addr, ip_buffer, sizeof(ip_buffer)); + ly_err = lyd_new_term(nh_list_node, ly_uv6mod, "next-hop-address", ip_buffer, false, &tmp_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create next-hop-address leaf in the list for route %s", route_path_buffer); + goto error_out; + } + } + + // outgoing-interface + if (nexthop_iter->simple.if_name) { + ly_err = lyd_new_term(nh_list_node, ly_uv6mod, "outgoing-interface", nexthop_iter->simple.if_name, false, &tmp_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create outgoing-interface leaf in the list for route %s", route_path_buffer); + goto error_out; + } + } + } + break; + } + } + } + } + } + } + + goto out; + +error_out: + SRPLG_LOG_ERR(PLUGIN_NAME, "error loading initial data for control-plane-procotols container"); + error = 1; +out: + // free all control plane protocol structs + for (size_t i = 0; i < ROUTING_PROTOS_COUNT; i++) { + control_plane_protocol_free(&cpp_map[i]); + } + + route_list_hash_free(&ipv4_static_routes_head); + route_list_hash_free(&ipv6_static_routes_head); + + return error; +} + +static int routing_startup_collect_static_routes(struct route_list_hash_element **ipv4_head, struct route_list_hash_element **ipv6_head) +{ + int nl_err = 0; + int error = 0; + + struct nl_sock *socket = NULL; + struct rtnl_route *route = NULL; + struct nl_cache *cache = NULL; + struct nl_cache *link_cache = NULL; + struct route tmp_route = {0}; + struct rtnl_link *iface = NULL; + char *if_name = NULL; + int ifindex = 0; + + socket = nl_socket_alloc(); + if (socket == NULL) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to init nl_sock struct..."); + goto error_out; + } + + 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)); + goto error_out; + } + + nl_err = rtnl_route_alloc_cache(socket, AF_UNSPEC, 0, &cache); + if (nl_err != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_route_alloc_cache failed (%d): %s", nl_err, nl_geterror(nl_err)); + goto error_out; + } + + 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)); + goto error_out; + } + + route = (struct rtnl_route *) nl_cache_get_first(cache); + while (route != NULL) { + const int PROTO = rtnl_route_get_protocol(route); + + if (PROTO == RTPROT_STATIC) { + const int AF = rtnl_route_get_family(route); + // 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 -> TODO: see what about special type + 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)); + rtnl_link_put(iface); + } else { + rtnl_route_foreach_nexthop(route, foreach_nexthop, &tmp_route.next_hop); + } + + // route-metadata/source-protocol + route_set_source_protocol(&tmp_route, "ietf-routing:static"); + + // add the route to the protocol's container + if (AF == AF_INET) { + // v4 + route_list_hash_add(ipv4_head, rtnl_route_get_dst(route), &tmp_route); + } else if (AF == AF_INET6) { + // v6 + route_list_hash_add(ipv6_head, rtnl_route_get_dst(route), &tmp_route); + } + + route_free(&tmp_route); + } + route = (struct rtnl_route *) nl_cache_get_next((struct nl_object *) route); + } + + goto out; + +error_out: + SRPLG_LOG_ERR(PLUGIN_NAME, "error initializing static routes"); + error = -1; +out: + nl_cache_free(cache); + nl_cache_free(link_cache); + nl_socket_free(socket); + return error; +} + +static int routing_build_protos_map(struct control_plane_protocol map[ROUTING_PROTOS_COUNT]) +{ + int error = 0; + + const char *known_types_map[ROUTING_PROTOS_COUNT] = { + [RTPROT_UNSPEC] = "ietf-routing:direct", + [RTPROT_REDIRECT] = "ietf-routing:direct", + [RTPROT_KERNEL] = "ietf-routing:direct", + [RTPROT_BOOT] = "ietf-routing:direct", + [RTPROT_STATIC] = "ietf-routing:static", + [RTPROT_GATED] = "ietf-routing:direct", + [RTPROT_RA] = "ietf-routing:direct", + [RTPROT_MRT] = "ietf-routing:direct", + [RTPROT_ZEBRA] = "ietf-routing:direct", + [RTPROT_BIRD] = "ietf-routing:direct", + [RTPROT_DNROUTED] = "ietf-routing:direct", + [RTPROT_XORP] = "ietf-routing:direct", + [RTPROT_NTK] = "ietf-routing:direct", + [RTPROT_DHCP] = "ietf-routing:direct", + [RTPROT_MROUTED] = "ietf-routing:direct", + [RTPROT_BABEL] = "ietf-routing:direct", + [RTPROT_BGP] = "ietf-routing:direct", + [RTPROT_ISIS] = "ietf-routing:direct", + [RTPROT_OSPF] = "ietf-routing:direct", + [RTPROT_RIP] = "ietf-routing:direct", + [RTPROT_EIGRP] = "ietf-routing:direct", + }; + + const char *known_descr_map[ROUTING_PROTOS_COUNT] = { + [RTPROT_UNSPEC] = "unspecified protocol", + [RTPROT_REDIRECT] = "redirect protocol", + [RTPROT_KERNEL] = "kernel protocol", + [RTPROT_BOOT] = "boot protocol", + [RTPROT_STATIC] = "static protocol", + [RTPROT_GATED] = "gated protocol", + [RTPROT_RA] = "ra protocol", + [RTPROT_MRT] = "mrt protocol", + [RTPROT_ZEBRA] = "zebra protocol", + [RTPROT_BIRD] = "bird protocol", + [RTPROT_DNROUTED] = "dnrouted protocol", + [RTPROT_XORP] = "xorp protocol", + [RTPROT_NTK] = "ntk protocol", + [RTPROT_DHCP] = "dhcp protocol", + [RTPROT_MROUTED] = "mrouted protocol", + [RTPROT_BABEL] = "babel protocol", + [RTPROT_BGP] = "bgp protocol", + [RTPROT_ISIS] = "isis protocol", + [RTPROT_OSPF] = "ospf protocol", + [RTPROT_RIP] = "rip protocol", + [RTPROT_EIGRP] = "eigrp protocol", + }; + + for (int i = 0; i < ROUTING_PROTOS_COUNT; i++) { + if (routing_is_proto_type_known(i)) { + rtnl_route_proto2str(i, map[i].name, sizeof(map[i].name)); + map[i].type = xstrdup(known_types_map[i]); + map[i].description = xstrdup(known_descr_map[i]); + map[i].initialized = 1; + } + } + + return error; +} + +static inline int routing_is_proto_type_known(int type) +{ + return type == RTPROT_UNSPEC || + type == RTPROT_REDIRECT || + type == RTPROT_KERNEL || + type == RTPROT_BOOT || + type == RTPROT_STATIC || + type == RTPROT_GATED || + type == RTPROT_RA || + type == RTPROT_MRT || + type == RTPROT_ZEBRA || + type == RTPROT_BIRD || + type == RTPROT_DNROUTED || + type == RTPROT_XORP || + type == RTPROT_NTK || + type == RTPROT_DHCP || + type == RTPROT_MROUTED || + type == RTPROT_BABEL || + type == RTPROT_BGP || + type == RTPROT_ISIS || + type == RTPROT_OSPF || + type == RTPROT_RIP || + type == RTPROT_EIGRP; +} \ No newline at end of file diff --git a/src/routing/startup.h b/src/routing/startup.h new file mode 100644 index 00000000..7ec32b70 --- /dev/null +++ b/src/routing/startup.h @@ -0,0 +1,22 @@ +/* + * 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_STARTUP_H +#define ROUTING_PLUGIN_STARTUP_H + +#include + +// loading data when the running DS is empty and storing it in startup DS +int routing_startup_load_data(struct routing_ctx *ctx, sr_session_ctx_t *session); + +#endif // ROUTING_PLUGIN_STARTUP_H \ No newline at end of file diff --git a/src/routing/subscription/change.c b/src/routing/subscription/change.c new file mode 100644 index 00000000..fa5e23f6 --- /dev/null +++ b/src/routing/subscription/change.c @@ -0,0 +1,302 @@ +/* + * 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 "change.h" +#include "libyang/tree_data.h" +#include "netlink/errno.h" +#include "netlink/socket.h" +#include "route.h" +#include "route/list_hash.h" +#include "route/next_hop.h" +#include "sysrepo_types.h" +#include +#include +#include +#include + +// stdlib +#include + +// sysrepo +#include +#include + +// libnl +#include + +// uthash +#include + +// helpers +static int apply_static_routes_changes(struct routing_ctx *ctx, sr_session_ctx_t *session, const char *base_xpath); + +int routing_control_plane_protocol_list_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; + + // context + struct routing_ctx *ctx = (struct routing_ctx *) private_data; + + // xpath buffer + char xpath_buffer[PATH_MAX] = {0}; + + SRPLG_LOG_INF(PLUGIN_NAME, "Module Name: %s; XPath: %s; Event: %d, Request ID: %u", module_name, xpath, event, request_id); + + if (event == SR_EV_ABORT) { + SRPLG_LOG_ERR(PLUGIN_NAME, "aborting changes for: %s", xpath); + error = -1; + goto error_out; + } else if (event == SR_EV_DONE) { + error = sr_copy_config(ctx->startup_session, BASE_YANG_MODEL, SR_DS_RUNNING, 0); + if (error) { + SRPLG_LOG_ERR(PLUGIN_NAME, "sr_copy_config error (%d): %s", error, sr_strerror(error)); + goto error_out; + } + } else if (event == SR_EV_CHANGE) { + // ipv4 static routes changes + error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/static-routes/ietf-ipv4-unicast-routing:ipv4//.", xpath); + if (error < 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "snprintf() failed (%d)", error); + goto error_out; + } + error = apply_static_routes_changes(ctx, session, xpath_buffer); + if (error) { + SRPLG_LOG_ERR(PLUGIN_NAME, "apply_static_routes_changes() failed (%d) - ipv4", error); + goto error_out; + } + + // ipv6 static routes changes + error = snprintf(xpath_buffer, sizeof(xpath_buffer), "%s/static-routes/ietf-ipv6-unicast-routing:ipv6//.", xpath); + if (error < 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "snprintf() failed (%d)", error); + goto error_out; + } + error = apply_static_routes_changes(ctx, session, xpath_buffer); + if (error) { + SRPLG_LOG_ERR(PLUGIN_NAME, "apply_static_routes_changes() failed (%d) - ipv6", error); + goto error_out; + } + } + goto out; + +error_out: + SRPLG_LOG_ERR(PLUGIN_NAME, "error applying control plane protocols module changes"); + error = -1; + +out: + return error != 0 ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK; +} + +static int apply_static_routes_changes(struct routing_ctx *ctx, sr_session_ctx_t *session, const char *xpath) +{ + int error = 0; + + // buffers + char change_path[PATH_MAX] = {0}; + + // sysrepo + sr_change_iter_t *changes_iterator = NULL; + sr_change_oper_t operation = SR_OP_CREATED; + const char *prev_value = NULL; + const char *prev_list = NULL; + int prev_default = 0; + const char *node_value = NULL; + const char *node_name = NULL; + + // libnl + struct nl_addr *prefix = NULL, *gateway = NULL; + struct nl_sock *socket = NULL; + + // libyang + const struct lyd_node *node = NULL; + + // new routes to add + struct route_list_hash_element *new_routes = NULL; + + // routes to modify + struct route_list_hash_element *modify_routes = NULL; + + // existing routes to delete + struct route_list_hash_element *delete_routes = NULL; + + // helper value + struct route_list_element **routes_head = NULL; + + error = sr_get_changes_iter(session, xpath, &changes_iterator); + if (error != SR_ERR_OK) { + SRPLG_LOG_ERR(PLUGIN_NAME, "sr_get_changes_iter() failed (%d): %s", error, sr_strerror(error)); + goto error_out; + } + + while (sr_get_change_tree_next(session, changes_iterator, &operation, &node, &prev_value, &prev_list, &prev_default) == SR_ERR_OK) { + error = (lyd_path(node, LYD_PATH_STD, change_path, sizeof(change_path)) == NULL); + if (error) { + SRPLG_LOG_ERR(PLUGIN_NAME, "lyd_path() error"); + goto error_out; + } + + node_name = sr_xpath_node_name(change_path); + node_value = lyd_get_value(node); + + SRPLG_LOG_DBG(PLUGIN_NAME, "Node Path: %s", change_path); + SRPLG_LOG_DBG(PLUGIN_NAME, "Node Name: %s", node_name); + SRPLG_LOG_DBG(PLUGIN_NAME, "Previous Value: %s; Value: %s; Operation: %d", prev_value, node_value, operation); + + switch (operation) { + case SR_OP_CREATED: + // data needed: destination-prefix and next-hop address + if (!strncmp(node_name, "destination-prefix", sizeof("destination-prefix") - 1)) { + // create new route + error = nl_addr_parse(node_value, AF_UNSPEC, &prefix); + if (error != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "nl_addr_parse() error (%d): %s", error, nl_geterror(error)); + goto error_out; + } + + // add empty route to the start of the list for 'prefix' + route_list_hash_add(&new_routes, prefix, &(struct route){0}); + routes_head = route_list_hash_get(&new_routes, prefix); + + // free created prefix + nl_addr_put(prefix); + } else if (!strncmp(node_name, "description", sizeof("description") - 1)) { + if (!routes_head) { + // error + SRPLG_LOG_ERR(PLUGIN_NAME, "invalid routes_head value"); + goto error_out; + } else { + route_set_description(&(*routes_head)->route, node_value); + } + } else if (!strncmp(node_name, "next-hop-address", sizeof("next-hop-address") - 1)) { + // simple next hop + if (!routes_head) { + SRPLG_LOG_ERR(PLUGIN_NAME, "invalid routes_head value"); + goto error_out; + } else { + error = nl_addr_parse(node_value, AF_UNSPEC, &gateway); + if (error != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "nl_addr_parse() error (%d): %s", error, nl_geterror(error)); + goto error_out; + } + route_next_hop_set_simple_gateway(&(*routes_head)->route.next_hop, gateway); + + // free created gateway + nl_addr_put(gateway); + } + } + break; + case SR_OP_MODIFIED: + // data needed: next-hop container value + break; + case SR_OP_DELETED: + // data needed: destination-prefix (optional next-hop value) + if (!strncmp(node_name, "destination-prefix", sizeof("destination-prefix") - 1)) { + // create new route + error = nl_addr_parse(node_value, AF_UNSPEC, &prefix); + if (error != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "nl_addr_parse() error (%d): %s", error, nl_geterror(error)); + goto error_out; + } + + // add empty route to the start of the list for 'prefix' + route_list_hash_add(&delete_routes, prefix, &(struct route){0}); + routes_head = route_list_hash_get(&delete_routes, prefix); + + // free created prefix + nl_addr_put(prefix); + } else if (!strncmp(node_name, "description", sizeof("description") - 1)) { + if (!routes_head) { + // error + SRPLG_LOG_ERR(PLUGIN_NAME, "invalid routes_head value"); + goto error_out; + } else { + route_set_description(&(*routes_head)->route, node_value); + } + } else if (!strncmp(node_name, "next-hop-address", sizeof("next-hop-address") - 1)) { + // simple next hop + if (!routes_head) { + SRPLG_LOG_ERR(PLUGIN_NAME, "invalid routes_head value"); + goto error_out; + } else { + error = nl_addr_parse(node_value, AF_UNSPEC, &gateway); + if (error != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "nl_addr_parse() error (%d): %s", error, nl_geterror(error)); + goto error_out; + } + route_next_hop_set_simple_gateway(&(*routes_head)->route.next_hop, gateway); + + // free created gateway + nl_addr_put(gateway); + } + } + break; + default: + break; + } + } + + // allocate libnl socket for all modifications (new, modify and delete) + socket = nl_socket_alloc(); + if (socket == NULL) { + error = -1; + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to init nl_sock struct..."); + goto error_out; + } + + error = nl_connect(socket, NETLINK_ROUTE); + if (error != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "nl_connect() failed (%d): %s", error, nl_geterror(error)); + goto error_out; + } + + SRPLG_LOG_INF(PLUGIN_NAME, "applying recieved changes for static routes"); + + error = routing_apply_new_routes(socket, new_routes); + if (error) { + SRPLG_LOG_ERR(PLUGIN_NAME, "routing_apply_new_routes() error (%d)", error); + goto error_out; + } + + error = routing_apply_modify_routes(socket, modify_routes); + if (error) { + SRPLG_LOG_ERR(PLUGIN_NAME, "routing_apply_modify_routes() error (%d)", error); + goto error_out; + } + + error = routing_apply_delete_routes(socket, delete_routes); + if (error) { + SRPLG_LOG_ERR(PLUGIN_NAME, "routing_apply_delete_routes() error (%d)", error); + goto error_out; + } + + goto out; + +error_out: + error = -1; + +out: + // libnl + if (socket) { + nl_socket_free(socket); + } + + // list hashes + route_list_hash_free(&new_routes); + route_list_hash_free(&modify_routes); + route_list_hash_free(&delete_routes); + + // iterator + sr_free_change_iter(changes_iterator); + + return error; +} \ No newline at end of file diff --git a/src/routing/subscription/change.h b/src/routing/subscription/change.h new file mode 100644 index 00000000..cfec658c --- /dev/null +++ b/src/routing/subscription/change.h @@ -0,0 +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_PLUGIN_SUBSCRIPTION_CHANGE_H +#define ROUTING_PLUGIN_SUBSCRIPTION_CHANGE_H + +#include + +int routing_control_plane_protocol_list_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); + +#endif // ROUTING_PLUGIN_SUBSCRIPTION_CHANGE_H \ No newline at end of file diff --git a/src/routing/subscription/operational.c b/src/routing/subscription/operational.c new file mode 100644 index 00000000..82dfbca1 --- /dev/null +++ b/src/routing/subscription/operational.c @@ -0,0 +1,389 @@ +/* + * 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 "operational.h" +#include +#include +#include +#include +#include +#include + +// sysrepo +#include + +// libnl +#include +#include + +// uthash +#include + +int routing_oper_get_rib_routes_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; + int nl_err = 0; + LY_ERR ly_err = LY_SUCCESS; + + // libyang + const struct ly_ctx *ly_ctx = NULL; + const struct lys_module *ly_uv4mod = NULL, *ly_uv6mod = NULL; + struct lyd_node *ly_node = NULL, *routes_node = NULL, *nh_node = NULL, *nh_list_node = NULL; + + // libnl + struct nl_sock *socket = NULL; + struct nl_cache *cache = NULL; + struct nl_cache *link_cache = NULL; + struct rib_list_element *ribs_head = NULL, *ribs_iter = NULL; + struct route_list_hash_element *routes_hash_iter = NULL; + struct route_list_element *routes_iter = NULL; + + // temp buffers + char routes_buffer[PATH_MAX]; + char value_buffer[PATH_MAX]; + char ip_buffer[INET6_ADDRSTRLEN]; + char prefix_buffer[INET6_ADDRSTRLEN + 1 + 3]; + char xpath_buffer[256] = {0}; + + ly_ctx = sr_acquire_context(sr_session_get_connection(session)); + + ly_uv4mod = ly_ctx_get_module(ly_ctx, "ietf-ipv4-unicast-routing", "2018-03-13"); + ly_uv6mod = ly_ctx_get_module(ly_ctx, "ietf-ipv6-unicast-routing", "2018-03-13"); + + socket = nl_socket_alloc(); + if (socket == NULL) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to init nl_sock struct..."); + goto error_out; + } + + 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)); + goto error_out; + } + + nl_err = rtnl_route_alloc_cache(socket, AF_UNSPEC, 0, &cache); + if (nl_err != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_route_alloc_cache failed (%d): %s", nl_err, nl_geterror(nl_err)); + goto error_out; + } + + nl_err = rtnl_link_alloc_cache(socket, AF_UNSPEC, &link_cache); + if (nl_err != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_link_alloc_cache failed (%d): %s", nl_err, nl_geterror(nl_err)); + goto error_out; + } + + nl_err = rtnl_route_read_table_names("/etc/iproute2/rt_tables"); + if (nl_err != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_route_read_table_names failed (%d): %s", nl_err, nl_geterror(nl_err)); + goto error_out; + } + + nl_err = rtnl_route_read_protocol_names("/etc/iproute2/rt_protos"); + if (nl_err != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_route_read_table_names failed (%d): %s", nl_err, nl_geterror(nl_err)); + goto error_out; + } + + error = routing_collect_routes(cache, link_cache, &ribs_head); + if (error != 0) { + goto error_out; + } + + LL_FOREACH(ribs_head, ribs_iter) + { + // create new routes container for every table + struct route_list_hash_element **routes_hash_head = &ribs_iter->rib.routes_head; + const int ADDR_FAMILY = ribs_iter->rib.address_family; + const char *TABLE_NAME = ribs_iter->rib.name; + snprintf(routes_buffer, sizeof(routes_buffer), "%s[name='%s-%s']/routes", ROUTING_RIB_LIST_YANG_PATH, ADDR_FAMILY == AF_INET ? "ipv4" : "ipv6", TABLE_NAME); + ly_err = lyd_new_path(*parent, ly_ctx, routes_buffer, NULL, LYD_NEW_PATH_UPDATE, &routes_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new routes node"); + goto error_out; + } + + LL_FOREACH(*routes_hash_head, routes_hash_iter) + { + struct route_list_element **routes_list_head = &routes_hash_iter->routes_head; + struct nl_addr *dst_prefix = routes_hash_iter->prefix; + nl_addr2str(dst_prefix, ip_buffer, sizeof(ip_buffer)); + + // check for prefix - libnl doesn't write prefix into the buffer if its 8*4/8*6 i.e. only that address/no subnet + if (strchr(ip_buffer, '/') == NULL) { + snprintf(prefix_buffer, sizeof(prefix_buffer), "%s/%d", ip_buffer, nl_addr_get_prefixlen(dst_prefix)); + } else { + snprintf(prefix_buffer, sizeof(prefix_buffer), "%s", ip_buffer); + } + + LL_FOREACH(*routes_list_head, routes_iter) + { + const struct route *ROUTE = &routes_iter->route; + // create a new list and after that add properties to it + ly_err = lyd_new_path(routes_node, ly_ctx, "routes/route", NULL, 0, &ly_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new route node"); + goto error_out; + } + + // route-preference + snprintf(value_buffer, sizeof(value_buffer), "%u", ROUTE->preference); + SRPLG_LOG_DBG(PLUGIN_NAME, "route-preference = %s", value_buffer); + ly_err = lyd_new_path(ly_node, ly_ctx, "route-preference", (void *) value_buffer, 0, NULL); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new route-preference node"); + goto error_out; + } + + // next-hop container + const union route_next_hop_value *NEXTHOP = &ROUTE->next_hop.value; + ly_err = lyd_new_path(ly_node, ly_ctx, "next-hop", NULL, 0, &nh_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new next-hop node"); + goto error_out; + } + + switch (ROUTE->next_hop.kind) { + case route_next_hop_kind_none: + break; + case route_next_hop_kind_simple: { + struct rtnl_link *iface = rtnl_link_get(link_cache, ROUTE->next_hop.value.simple.ifindex); + const char *if_name = rtnl_link_get_name(iface); + + // outgoing-interface + SRPLG_LOG_DBG(PLUGIN_NAME, "outgoing-interface = %s", if_name); + ly_err = lyd_new_path(nh_node, ly_ctx, "outgoing-interface", (void *) if_name, 0, NULL); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new outgoing-interface node"); + goto error_out; + } + + // next-hop-address + if (NEXTHOP->simple.addr) { + nl_addr2str(NEXTHOP->simple.addr, ip_buffer, sizeof(ip_buffer)); + if (ADDR_FAMILY == AF_INET && ly_uv4mod != NULL) { + SRPLG_LOG_DBG(PLUGIN_NAME, "IPv4 next-hop-address = %s", ip_buffer); + ly_err = lyd_new_term(nh_node, ly_uv4mod, "next-hop-address", ip_buffer, false, NULL); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new IPv4 next-hop-address node"); + goto error_out; + } + } else if (ADDR_FAMILY == AF_INET6 && ly_uv6mod != NULL) { + SRPLG_LOG_DBG(PLUGIN_NAME, "IPv6 next-hop-address = %s", ip_buffer); + ly_err = lyd_new_term(nh_node, ly_uv6mod, "next-hop-address", ip_buffer, false, NULL); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new IPv6 next-hop-address node"); + goto error_out; + } + } + } + rtnl_link_put(iface); + break; + } + case route_next_hop_kind_special: + // SRPLG_LOG_DBG(PLUGIN_NAME, "special-next-hop = %s", NEXTHOP->special.value); + // ly_err = lyd_new_path(nh_node, ly_ctx, "special-next-hop", NEXTHOP->special.value, false, NULL); + // if (ly_err != LY_SUCCESS) { + // SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new special-next-hop node"); + // goto error_out; + // } + break; + case route_next_hop_kind_list: { + struct route_next_hop_list_element *nexthop_iter = NULL; + + LL_FOREACH(ROUTE->next_hop.value.list_head, nexthop_iter) + { + struct rtnl_link *iface = rtnl_link_get(link_cache, nexthop_iter->simple.ifindex); + const char *if_name = rtnl_link_get_name(iface); + + error = snprintf(xpath_buffer, sizeof(xpath_buffer), "next-hop/next-hop-list/next-hop[index=%d]", nexthop_iter->simple.ifindex); + if (error < 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new next-hop-list/next-hop node, couldn't retrieve interface index"); + goto error_out; + } + ly_err = lyd_new_path(nh_node, ly_ctx, xpath_buffer, NULL, 0, &nh_list_node); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new next-hop-list/next-hop node"); + goto error_out; + } + + // outgoing-interface + SRPLG_LOG_DBG(PLUGIN_NAME, "outgoing-interface = %s", if_name); + ly_err = lyd_new_path(nh_list_node, ly_ctx, "outgoing-interface", (void *) if_name, 0, NULL); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new outgoing-interface node"); + goto error_out; + } + + // next-hop-address + if (nexthop_iter->simple.addr) { + nl_addr2str(nexthop_iter->simple.addr, ip_buffer, sizeof(ip_buffer)); + if (ADDR_FAMILY == AF_INET && ly_uv4mod != NULL) { + SRPLG_LOG_DBG(PLUGIN_NAME, "IPv4 next-hop/next-hop-list/next-hop/next-hop-address = %s", ip_buffer); + ly_err = lyd_new_term(nh_list_node, ly_uv4mod, "next-hop-address", ip_buffer, false, NULL); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new IPv4 next-hop-address node"); + goto error_out; + } + } else if (ADDR_FAMILY == AF_INET6 && ly_uv6mod != NULL) { + SRPLG_LOG_DBG(PLUGIN_NAME, "IPv6 next-hop/next-hop-list/next-hop/next-hop-address = %s", ip_buffer); + ly_err = lyd_new_term(nh_list_node, ly_uv6mod, "next-hop-address", ip_buffer, false, NULL); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new IPv6 next-hop-address node"); + goto error_out; + } + } + } + rtnl_link_put(iface); + } + break; + } + } + + // destination-prefix + if (ADDR_FAMILY == AF_INET && ly_uv4mod != NULL) { + if (strncmp("none/0", prefix_buffer, strnlen(prefix_buffer, sizeof(prefix_buffer))) == 0) { + strcpy(prefix_buffer, "0.0.0.0/0"); + } + + SRPLG_LOG_DBG(PLUGIN_NAME, "destination-prefix = %s", prefix_buffer); + ly_err = lyd_new_term(ly_node, ly_uv4mod, "destination-prefix", prefix_buffer, false, NULL); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new IPv4 destination-prefix node"); + goto error_out; + } + } else if (ADDR_FAMILY == AF_INET6 && ly_uv6mod != NULL) { + if (strncmp("none/0", prefix_buffer, strnlen(prefix_buffer, sizeof(prefix_buffer))) == 0) { + strcpy(prefix_buffer, "::/0"); + } + + SRPLG_LOG_DBG(PLUGIN_NAME, "destination-prefix = %s", prefix_buffer); + ly_err = lyd_new_term(ly_node, ly_uv6mod, "destination-prefix", prefix_buffer, false, NULL); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new IPv6 destination-prefix node"); + goto error_out; + } + } + + // route-metadata/source-protocol + SRPLG_LOG_DBG(PLUGIN_NAME, "source-protocol = %s", ROUTE->metadata.source_protocol); + ly_err = lyd_new_path(ly_node, ly_ctx, "source-protocol", ROUTE->metadata.source_protocol, 0, NULL); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new source-protocol node"); + goto error_out; + } + + // route-metadata/active + if (ROUTE->metadata.active == 1) { + SRPLG_LOG_DBG(PLUGIN_NAME, "active = %d", ROUTE->metadata.active); + ly_err = lyd_new_path(ly_node, ly_ctx, "active", NULL, 0, NULL); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new active node"); + goto error_out; + } + } + } + } + } + + if (error != 0) { + goto error_out; + } + + goto out; + +error_out: + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to return routes for routing tables"); + error = SR_ERR_CALLBACK_FAILED; + +out: + rib_list_free(&ribs_head); + nl_socket_free(socket); + nl_cache_free(cache); + nl_cache_free(link_cache); + return error; +} + +int routing_oper_get_interfaces_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) +{ + // error handling + int error = SR_ERR_OK; + LY_ERR ly_err = LY_SUCCESS; + + // libyang + const struct ly_ctx *ly_ctx = NULL; + + // libnl + struct nl_sock *socket = NULL; + struct nl_cache *cache = NULL; + struct rtnl_link *link = NULL; + + if (*parent == NULL) { + ly_ctx = sr_acquire_context(sr_session_get_connection(session)); + if (ly_ctx == NULL) { + goto error_out; + } + + ly_err = lyd_new_path(*parent, ly_ctx, request_xpath, NULL, 0, NULL); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new node"); + goto error_out; + } + } + + socket = nl_socket_alloc(); + if (socket == NULL) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to init nl_sock struct..."); + goto error_out; + } + + error = nl_connect(socket, NETLINK_ROUTE); + if (error != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "nl_connect failed (%d): %s", error, nl_geterror(error)); + goto error_out; + } + + error = rtnl_link_alloc_cache(socket, AF_UNSPEC, &cache); + if (error != 0) { + SRPLG_LOG_ERR(PLUGIN_NAME, "rtnl_link_alloc_cache failed (%d): %s", error, nl_geterror(error)); + goto error_out; + } + + SRPLG_LOG_DBG(PLUGIN_NAME, "adding interfaces to the list"); + + link = (struct rtnl_link *) nl_cache_get_first(cache); + while (link) { + const char *name = rtnl_link_get_name(link); + SRPLG_LOG_DBG(PLUGIN_NAME, "adding interface '%s' ", name); + + ly_err = lyd_new_path(*parent, ly_ctx, ROUTING_INTERFACE_LEAF_LIST_YANG_PATH, (void *) name, 0, NULL); + if (ly_err != LY_SUCCESS) { + SRPLG_LOG_ERR(PLUGIN_NAME, "unable to create new interface node"); + goto error_out; + } + + link = (struct rtnl_link *) nl_cache_get_next((struct nl_object *) link); + } + + goto out; + +error_out: + error = SR_ERR_CALLBACK_FAILED; +out: + // free allocated objects + nl_cache_free(cache); + nl_socket_free(socket); + + return error; +} \ No newline at end of file diff --git a/src/routing/subscription/operational.h b/src/routing/subscription/operational.h new file mode 100644 index 00000000..f1eb1102 --- /dev/null +++ b/src/routing/subscription/operational.h @@ -0,0 +1,22 @@ +/* + * 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_SUBSCRIPTION_OPERATIONAL_H +#define ROUTING_PLUGIN_SUBSCRIPTION_OPERATIONAL_H + +#include + +int routing_oper_get_rib_routes_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 routing_oper_get_interfaces_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); + +#endif // ROUTING_PLUGIN_SUBSCRIPTION_OPERATIONAL_H diff --git a/src/routing/subscription/rpc.c b/src/routing/subscription/rpc.c new file mode 100644 index 00000000..eb11f5d4 --- /dev/null +++ b/src/routing/subscription/rpc.c @@ -0,0 +1,24 @@ +/* + * 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 "rpc.h" +#include + +#include + +int routing_rpc_active_route_cb(sr_session_ctx_t *session, uint32_t subscription_id, const char *op_path, const sr_val_t *input, const size_t input_cnt, sr_event_t event, uint32_t request_id, sr_val_t **output, size_t *output_cnt, void *private_data) +{ + int error = SR_ERR_OK; + SRPLG_LOG_DBG(PLUGIN_NAME, "xpath for RPC: %s", op_path); + return error; +} \ No newline at end of file diff --git a/src/routing/subscription/rpc.h b/src/routing/subscription/rpc.h new file mode 100644 index 00000000..a2e0c0a8 --- /dev/null +++ b/src/routing/subscription/rpc.h @@ -0,0 +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_PLUGIN_SUBSCRIPTION_RPC_H +#define ROUTING_PLUGIN_SUBSCRIPTION_RPC_H + +#include + +int routing_rpc_active_route_cb(sr_session_ctx_t *session, uint32_t subscription_id, const char *op_path, const sr_val_t *input, const size_t input_cnt, sr_event_t event, uint32_t request_id, sr_val_t **output, size_t *output_cnt, void *private_data); + +#endif // ROUTING_PLUGIN_SUBSCRIPTION_RPC_H diff --git a/src/utils/memory.c b/src/utils/memory.c index 491955b6..1c669d36 100644 --- a/src/utils/memory.c +++ b/src/utils/memory.c @@ -17,6 +17,10 @@ void *xmalloc(size_t size) { void *res; + if (size == 0) { + abort(); + } + res = malloc(size); if (res == NULL) { @@ -30,6 +34,10 @@ void *xrealloc(void *ptr, size_t size) { void *res; + if (size == 0) { + abort(); + } + res = realloc(ptr, size); if (res == NULL) { @@ -43,6 +51,10 @@ void *xcalloc(size_t nmemb, size_t size) { void *res; + if (size == 0) { + abort(); + } + res = calloc(nmemb, size); if (res == NULL) { diff --git a/tests/Tests.cmake b/tests/Tests.cmake new file mode 100644 index 00000000..14082338 --- /dev/null +++ b/tests/Tests.cmake @@ -0,0 +1 @@ +add_subdirectory(${CMAKE_SOURCE_DIR}/tests/routing) diff --git a/tests/integration/README.md b/tests/integration/README.md index 713b28ef..4962fd0a 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -2,10 +2,11 @@ This directory contains the integration tests and required data for the ietf-interfaces plugin. -The tests are located in `ietf-interfaces.py` while the test data is in XML files in `data/`. +The tests are located in `ietf-interfaces.py` or in the directories starting with `robot-*` in case of Robot framework tests, while the test data is in XML files in `data`. # Dependencies +## Python 3 As the integration tests are writen in python, several dependencies are required in order to run the tests. @@ -20,8 +21,29 @@ source test-venv pip3 install -r requirements.txt ``` +## Robot framework + +Make sure to have Sysrepo and the accompanying plugins installed under `devel`. + +Create a virtual environment, activate it and make sure `pip`, `setuptools` and `wheel` are up to date. +Finally install the packages. + +``` +$ python3 -m venv sysrepolibrary-venv +$ source sysrepolibrary-venv/bin/activate +$ python3 -m pip install --upgrade pip setuptools wheel +$ python3 -m pip install rpaframework SysrepoLibrary robotframework-tidy +``` + +To autoformat the robot code: +``` +$ robotidy robot-ietf-interfaces/ +``` + + # Running the tests +## Python 3 After setting up the dependencies, to run the tests either execute the `.py` file or run it in a python interpreter. @@ -39,3 +61,15 @@ Ran 3 tests in 6.182s OK ``` + +## Robot framework +Note the root privileges when invoking the command (datastore permission issues otherwise, item not found): + +To test the interfaces plugin (change env var path): +``` +# SYSREPO_INTERFACES_PLUGIN_PATH=/path/to/interfaces/plugin/executable robot robot-ietf-interfaces +``` +To test the routing plugin: +``` +# SYSREPO_ROUTING_PLUGIN_PATH=/path/to/routing/plugin/executable robot robot-ietf-routing` +``` diff --git a/tests/integration/data/dummy.xml b/tests/integration/data/dummy.xml new file mode 100644 index 00000000..14114e69 --- /dev/null +++ b/tests/integration/data/dummy.xml @@ -0,0 +1,7 @@ + + + dummy + ianaift:other + true + + diff --git a/tests/integration/data/loopback_addr_netmask_ipv4.xml b/tests/integration/data/loopback_addr_netmask_ipv4.xml new file mode 100644 index 00000000..1436650d --- /dev/null +++ b/tests/integration/data/loopback_addr_netmask_ipv4.xml @@ -0,0 +1,12 @@ + + + lo + ianaift:softwareLoopback + +
+ 127.0.0.0 + 255.0.0.0 +
+
+
+
diff --git a/tests/integration/data/loopback_addr_prefix_ipv4.xml b/tests/integration/data/loopback_addr_prefix_ipv4.xml new file mode 100644 index 00000000..439a8ac8 --- /dev/null +++ b/tests/integration/data/loopback_addr_prefix_ipv4.xml @@ -0,0 +1,12 @@ + + + lo + ianaift:softwareLoopback + +
+ 127.0.0.0 + 8 +
+
+
+
diff --git a/tests/integration/data/loopback_addr_prefix_ipv6.xml b/tests/integration/data/loopback_addr_prefix_ipv6.xml new file mode 100644 index 00000000..57249451 --- /dev/null +++ b/tests/integration/data/loopback_addr_prefix_ipv6.xml @@ -0,0 +1,12 @@ + + + lo + ianaift:softwareLoopback + +
+ ::1 + 128 +
+
+
+
diff --git a/tests/integration/data/loopback_change_type.xml b/tests/integration/data/loopback_change_type.xml new file mode 100644 index 00000000..bbe756ae --- /dev/null +++ b/tests/integration/data/loopback_change_type.xml @@ -0,0 +1,6 @@ + + + lo + ianaift:ethernetCsmacd + + diff --git a/tests/integration/data/loopback_description.xml b/tests/integration/data/loopback_description.xml new file mode 100644 index 00000000..0d2bc149 --- /dev/null +++ b/tests/integration/data/loopback_description.xml @@ -0,0 +1,7 @@ + + + lo + test + ianaift:softwareLoopback + + diff --git a/tests/integration/data/loopback_enabled.xml b/tests/integration/data/loopback_enabled.xml new file mode 100644 index 00000000..560165ff --- /dev/null +++ b/tests/integration/data/loopback_enabled.xml @@ -0,0 +1,7 @@ + + + lo + ianaift:softwareLoopback + true + + diff --git a/tests/integration/data/loopback_ipv4_forwarding.xml b/tests/integration/data/loopback_ipv4_forwarding.xml new file mode 100644 index 00000000..7bf94ec9 --- /dev/null +++ b/tests/integration/data/loopback_ipv4_forwarding.xml @@ -0,0 +1,9 @@ + + + lo + ianaift:softwareLoopback + + true + + + diff --git a/tests/integration/data/loopback_ipv6_forwarding.xml b/tests/integration/data/loopback_ipv6_forwarding.xml new file mode 100644 index 00000000..0cceb7ad --- /dev/null +++ b/tests/integration/data/loopback_ipv6_forwarding.xml @@ -0,0 +1,9 @@ + + + lo + ianaift:softwareLoopback + + true + + + diff --git a/tests/integration/data/loopback_ipv6_mtu.xml b/tests/integration/data/loopback_ipv6_mtu.xml new file mode 100644 index 00000000..bd699953 --- /dev/null +++ b/tests/integration/data/loopback_ipv6_mtu.xml @@ -0,0 +1,9 @@ + + + lo + ianaift:softwareLoopback + + 1300 + + + diff --git a/tests/integration/data/loopback_mtu.xml b/tests/integration/data/loopback_mtu.xml new file mode 100644 index 00000000..97fdc79d --- /dev/null +++ b/tests/integration/data/loopback_mtu.xml @@ -0,0 +1,9 @@ + + + lo + ianaift:softwareLoopback + + 12345 + + + diff --git a/tests/integration/data/loopback_rename.xml b/tests/integration/data/loopback_rename.xml new file mode 100644 index 00000000..be533daf --- /dev/null +++ b/tests/integration/data/loopback_rename.xml @@ -0,0 +1,6 @@ + + + lo_renamed + ianaift:softwareLoopback + + diff --git a/tests/integration/data/routing/static-route.xml b/tests/integration/data/routing/static-route.xml new file mode 100644 index 00000000..ddaed802 --- /dev/null +++ b/tests/integration/data/routing/static-route.xml @@ -0,0 +1,19 @@ + + + + static + static + + + + 192.168.100.0/24 + Test Route + + 10.0.2.1 + + + + + + + \ No newline at end of file diff --git a/tests/integration/data/subinterface.xml b/tests/integration/data/subinterface.xml new file mode 100644 index 00000000..76e74167 --- /dev/null +++ b/tests/integration/data/subinterface.xml @@ -0,0 +1,31 @@ + + + {PARENT_INTERFACE}.sub1 + ianaift:l2vlan + {PARENT_INTERFACE} + + + + dot1q-types:s-vlan + 10 + + + dot1q-types:c-vlan + 20 + + + + +
+ 2001:db8:10::1 + 48 +
+ 0 +
+
+
diff --git a/tests/integration/robot-ietf-interfaces/.gitignore b/tests/integration/robot-ietf-interfaces/.gitignore new file mode 100644 index 00000000..28d48547 --- /dev/null +++ b/tests/integration/robot-ietf-interfaces/.gitignore @@ -0,0 +1,2 @@ +*.html +*.xml diff --git a/tests/integration/robot-ietf-interfaces/InterfaceInit.resource b/tests/integration/robot-ietf-interfaces/InterfaceInit.resource new file mode 100644 index 00000000..fc29c5e6 --- /dev/null +++ b/tests/integration/robot-ietf-interfaces/InterfaceInit.resource @@ -0,0 +1,21 @@ +*** Settings *** +Library SysrepoLibrary +Library RPA.JSON +Library Process + +*** Variables *** +${Xpath Interfaces} /ietf-interfaces:interfaces + +*** Keywords *** +Restore Initial Running Datastore + Edit Datastore Config ${Connection Default} ${Session Running} ${If Init Str} json + Check If Datastore Is Restored + +Check If Datastore Is Restored + [Documentation] Make sure the initial datastore is restored + ${Restored}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... ${Xpath Interfaces} + ... json + Should Be Equal As Strings ${Restored} ${If Init Str} msg="failed to restore inital running datastore" diff --git a/tests/integration/robot-ietf-interfaces/InterfaceKeywords.resource b/tests/integration/robot-ietf-interfaces/InterfaceKeywords.resource new file mode 100644 index 00000000..7de0f5f4 --- /dev/null +++ b/tests/integration/robot-ietf-interfaces/InterfaceKeywords.resource @@ -0,0 +1,28 @@ +*** Settings *** +Library BuiltIn +Library OperatingSystem + + +*** Keywords *** +List Elements Should Be Empty + [Arguments] ${List} + FOR ${Item} IN @{List} + Should Be Equal As Strings ${Item} ${EMPTY} + END + +Interface Operstate Should Be Up + [Arguments] ${If Name} + ${Sys Enabled}= Get File /sys/class/net/${If Name}/operstate + Should Be Equal As Strings "${Sys Enabled.strip()}" "up" + +Sysrepo Get Json Fields + [Arguments] ${Path} ${Field} + ${Path Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... ${Path} + ... json + &{Path JSON}= Convert String To JSON ${Path Str} + ${Fields}= Get Values From Json ${Path Json} $..${Field} + [Return] ${Fields} + diff --git a/tests/integration/robot-ietf-interfaces/InterfaceTests.robot b/tests/integration/robot-ietf-interfaces/InterfaceTests.robot new file mode 100644 index 00000000..f033191c --- /dev/null +++ b/tests/integration/robot-ietf-interfaces/InterfaceTests.robot @@ -0,0 +1,70 @@ +*** Settings *** +Library Collections +Library OperatingSystem +Library String +Library Process +Library BuiltIn +Library SysrepoLibrary +Library RPA.JSON +Library Utils.py +Variables InterfaceVariables.py +Resource InterfaceKeywords.resource +Resource InterfaceInit.resource + + +*** Test Cases *** +Test interfaces name get + [Documentation] Check if datastore names match system network interface names + @{If Names}= Get Values From JSON ${If Init JSON} $..name + @{Sys Names}= List Directory /sys/class/net + Lists Should Be Equal ${If Names} ${Sys names} ignore_order=True + +Test interfaces description + [Documentation] Check if datastore descriptions are empty + @{If Descriptions}= Get Values From JSON ${If Init JSON} $..description + List Elements Should Be Empty ${If Descriptions} + +Test interfaces types + [Documentation] Check if datastore contains only supported types + @{If Types}= Get Values From JSON ${If Init JSON} $..type + List Should Contain Sub List ${Supported If Types} ${If Types} + +Test interfaces network status + [Documentation] Check if datastore interface state matches system state + @{If Names}= Get Values From JSON ${If Init JSON} $..name + @{If Enabled}= Get Values From JSON ${If Init JSON} $..enabled + &{States}= Create Dict From Lists ${If Names} ${If Enabled} + FOR ${Name} ${Enabled} IN &{States} + ${Sys Oper Path}= Set Variable /sys/class/net/${Name}/operstate + File Should Exist ${Sys Oper Path} + ${Sys Enabled}= Get File ${Sys Oper Path} + ${Enabled Norm}= Set Variable If "${Sys Enabled.strip()}" == "up" + ... True + ... False + Should Be Equal As Strings ${Enabled} ${Enabled Norm} + END + +Test Interface Lo Enable + [Documentation] Attempt to enable loopback interface + Edit Datastore Config By File ${Connection Default} ${Session Running} data/loopback_enabled.xml xml + ${Lo Enabled}= Sysrepo Get Json Fields /ietf-interfaces:interfaces/interface[name="lo"]/enabled enabled + Should Be True ${Lo Enabled} + Interface Operstate Should Be Up lo + +Test Interface Lo Description + [Documentation] Attempt to change loopback interface description + Edit Datastore Config By File ${Connection Default} ${Session Running} data/loopback_description.xml xml + ${Lo Description}= Sysrepo Get Json Fields /ietf-interfaces:interfaces/interface[name="lo"]/description description + Should Be Equal As Strings "${Lo Description}[0]" "test" + +Test Interface Dummy + [Documentation] Attempt to add a dummy interface + Edit Datastore Config By File ${Connection Default} ${Session Running} data/dummy.xml xml + ${Dummy Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... /ietf-interfaces:interfaces/interface[name="dummy"] + ... xml + Should Be Equal As Strings ${Dummy Str} ${Expected Dummy} + File Should Exist /sys/class/net/dummy + Interface Operstate Should Be Up dummy diff --git a/tests/integration/robot-ietf-interfaces/InterfaceVariables.py b/tests/integration/robot-ietf-interfaces/InterfaceVariables.py new file mode 100644 index 00000000..525f03f6 --- /dev/null +++ b/tests/integration/robot-ietf-interfaces/InterfaceVariables.py @@ -0,0 +1,41 @@ + +def getVariables(): + # Variables get imported into robot from the dict as Key = Value pairs + variables = { + 'Supported If Types': [ + 'iana-if-type:softwareLoopback', + 'iana-if-type:ethernetCsmacd', + 'iana-if-type:l2vlan', + 'iana-if-type:other', + 'iana-if-type:bridge', + ], + 'Expected Dummy': '' \ + 'dummy' \ + 'ianaift:othertrue', + + 'Expected Ip Prefix Address': '' \ + 'lo' \ + '
127.0.0.08
', + + 'Expected Ip Netmask Address': '' \ + 'lo' \ + '
127.0.0.0255.0.0.0
', + + 'Expected Ip Sub Address': '' \ + 'lo' \ + '
::1128
', + 'Expected IPv4 Mtu': '' \ + 'lo' \ + '12345', + 'Expected IPv6 Mtu': '' \ + 'lo' \ + '1300', + 'Expected IPv4 Forwarding': '' \ + 'lo' \ + 'true', + 'Expected IPv6 Forwarding': '' \ + 'lo' \ + 'true', + } + + return variables diff --git a/tests/integration/robot-ietf-interfaces/IpTests.robot b/tests/integration/robot-ietf-interfaces/IpTests.robot new file mode 100644 index 00000000..dd728de6 --- /dev/null +++ b/tests/integration/robot-ietf-interfaces/IpTests.robot @@ -0,0 +1,212 @@ +*** Settings *** +Library Collections +Library OperatingSystem +Library String +Library Process +Library BuiltIn +Library SysrepoLibrary +Library RPA.JSON +Library Utils.py +Variables InterfaceVariables.py +Resource InterfaceKeywords.resource +Resource InterfaceInit.resource + + +*** Test Cases *** +Test Ip Addr Prefix IPv4 + [Documentation] Attempt to set loopback interface IPv4 address and subnet via prefix + Edit Datastore Config By File ${Connection Default} ${Session Running} data/loopback_addr_prefix_ipv4.xml xml + ${Ip Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... /ietf-interfaces:interfaces/interface[name="lo"]/ietf-ip:ipv4/address[ip="127.0.0.0"] + ... xml + Should Be Equal As Strings ${Ip Str} ${Expected Ip Prefix Address} + ${Proc Res}= Run Process ip addr show lo + Should Contain ${Proc Res.stdout} inet 127.0.0.1/8 + +Test Ip Addr Netmask IPv4 + [Documentation] Attempt to set loopback interface IPv4 address and subnet via netmask + Edit Datastore Config By File ${Connection Default} ${Session Running} data/loopback_addr_netmask_ipv4.xml xml + ${Ip Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... /ietf-interfaces:interfaces/interface[name="lo"]/ietf-ip:ipv4/address[ip="127.0.0.0"] + ... xml + Should Be Equal As Strings ${Ip Str} ${Expected Ip Netmask Address} + ${Proc Res}= Run Process ip addr show lo + Should Contain ${Proc Res.stdout} inet 127.0.0.1/8 + +Test Ip Addr Sub IPv6 + [Documentation] Attempt to set loopback interface IPv6 address and subnet via prefix + Edit Datastore Config By File ${Connection Default} ${Session Running} data/loopback_addr_prefix_ipv6.xml xml + ${Ip Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... /ietf-interfaces:interfaces/interface[name="lo"]/ietf-ip:ipv6/address + ... xml + Should Be Equal As Strings ${Ip Str} ${Expected Ip Sub Address} + ${Proc Res}= Run Process ip addr show lo + Should Contain ${Proc Res.stdout} inet6 ::1/128 + +Test Interface IPv4 Mtu + [Documentation] Attempt to change loopback interface mtu + Edit Datastore Config By File ${Connection Default} ${Session Running} data/loopback_mtu.xml xml + ${Mtu Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... /ietf-interfaces:interfaces/interface[name="lo"]/ietf-ip:ipv4/mtu + ... xml + Should Be Equal As Strings ${Mtu Str} ${Expected IPv4 Mtu} + ${Sys Mtu Path}= Set Variable /sys/class/net/lo/mtu + File Should Exist ${Sys Mtu Path} + ${Sys Mtu Str}= Get File ${Sys Mtu Path} + Should Be Equal As Strings ${Sys Mtu Str.strip()} 12345 + +Test Interface IPv6 Mtu + [Documentation] Attempt to change loopback interface IPv6 mtu + Edit Datastore Config By File ${Connection Default} ${Session Running} data/loopback_ipv6_mtu.xml xml + ${Mtu Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... /ietf-interfaces:interfaces/interface[name="lo"]/ietf-ip:ipv6/mtu + ... xml + Should Be Equal As Strings ${Mtu Str} ${Expected IPv6 Mtu} + ${Sys Mtu Path}= Set Variable /proc/sys/net/ipv6/conf/lo/mtu + File Should Exist ${Sys Mtu Path} + ${Sys Mtu Str}= Get File ${Sys Mtu Path} + Should Be Equal As Strings ${Sys Mtu Str.strip()} 1300 + +Test Interface IPv4 Forwarding + [Documentation] Attempt to enable loopback ipv4 address forwarding + Edit Datastore Config By File ${Connection Default} ${Session Running} data/loopback_ipv4_forwarding.xml xml + ${Forwarding Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... /ietf-interfaces:interfaces/interface[name="lo"]/ietf-ip:ipv4/forwarding + ... xml + Should Be Equal As Strings ${Forwarding Str} ${Expected IPv4 Forwarding} + ${Sys Forwarding Path}= Set Variable /proc/sys/net/ipv4/conf/lo/forwarding + File Should Exist ${Sys Forwarding Path} + ${Sys Forwarding Str}= Get File ${Sys Forwarding Path} + Should Be Equal As Strings ${Sys Forwarding Str.strip()} 1 + +Test Interface IPv6 Forwarding + [Documentation] Attempt to enable loopback ipv6 address forwarding + Edit Datastore Config By File ${Connection Default} ${Session Running} data/loopback_ipv6_forwarding.xml xml + ${Forwarding Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... /ietf-interfaces:interfaces/interface[name="lo"]/ietf-ip:ipv6/forwarding + ... xml + Should Be Equal As Strings ${Forwarding Str} ${Expected IPv6 Forwarding} + ${Sys Forwarding Path}= Set Variable /proc/sys/net/ipv6/conf/lo/forwarding + File Should Exist ${Sys Forwarding Path} + ${Sys Forwarding Str}= Get File ${Sys Forwarding Path} + Should Be Equal As Strings ${Sys Forwarding Str.strip()} 1 + +Test Interface IPv4 Neighbor + [Documentation] Attempt to add neighbor IPv4 address + @{Sys Names}= List Directory /sys/class/net + Remove Values From List ${Sys Names} lo docker0 + ${Chosen If}= Set Variable ${Sys Names}[0] + ${Neighbor Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... /ietf-interfaces:interfaces/interface[name="${Chosen If}"]/type + ... json + &{Neighbor JSON}= Convert String To JSON ${Neighbor Str} + @{If Type}= Get Values From JSON ${Neighbor JSON} $..type + ${If Type}= Fetch From Right ${If Type}[0] : + ${Test XML}= Catenate SEPARATOR= + ... + ... ${Chosen If} + ... ianaift:${If Type} + ... 192.0.2.200:00:5e:00:53:ab + ... + Edit Datastore Config ${Connection Default} ${Session Running} ${Test XML} xml + ${Neighbor Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... /ietf-interfaces:interfaces/interface[name="${Chosen If}"]/ietf-ip:ipv4/neighbor + ... xml + Should Contain ${Neighbor Str} 192.0.2.200:00:5e:00:53:ab + ${Proc Res}= Run Process ip neigh show dev ${Chosen If} + Should Contain ${Proc Res.stdout} 192.0.2.2 lladdr 00:00:5e:00:53:ab + +Test Interface IPv6 Neighbor + [Documentation] Attempt to add neighbor IPv6 address + @{Sys Names}= List Directory /sys/class/net + Remove Values From List ${Sys Names} lo docker0 + ${Chosen If}= Set Variable ${Sys Names}[0] + ${Neighbor Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... /ietf-interfaces:interfaces/interface[name="${Chosen If}"]/type + ... json + &{Neighbor JSON}= Convert String To JSON ${Neighbor Str} + @{If Type}= Get Values From JSON ${Neighbor JSON} $..type + ${If Type}= Fetch From Right ${If Type}[0] : + ${Test XML}= Catenate SEPARATOR= + ... + ... ${Chosen If} + ... ianaift:${If Type} + ... ::ffff:c000:20300:00:5e:00:53:ab + ... + Edit Datastore Config ${Connection Default} ${Session Running} ${Test XML} xml + ${Neighbor Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... /ietf-interfaces:interfaces/interface[name="${Chosen If}"]/ietf-ip:ipv6/neighbor + ... xml + Should Contain ${Neighbor Str} ::ffff:192.0.2.300:00:5e:00:53:ab + ${Proc Res}= Run Process ip neigh show dev ${Chosen If} + Should Contain ${Proc Res.stdout} ::ffff:192.0.2.3 lladdr 00:00:5e:00:53:ab + +Test Interface Subinterface + [Documentation] Attempt to create a vlan qinq subinterface + @{Sys Names}= List Directory /sys/class/net + Remove Values From List ${Sys Names} lo + # Select a parent interface + FOR ${Interface} IN @{Sys Names} + ${Proc Res}= Run Process ip link show ${Interface} + IF "link/ether" in """${Proc Res.stdout}""" + ${Parent Interface}= Set Variable ${Interface} + BREAK + END + END + # Modify datastore + ${Test Unformatted XML}= Get File data/subinterface.xml + ${Test XML}= Utils.Format String ${Test Unformatted XML} PARENT_INTERFACE=${Parent Interface} + Edit Datastore Config ${Connection Default} ${Session Running} ${Test XML} xml + ${Expected XML}= Catenate SEPARATOR= + ... + ... ${Parent Interface}.sub1 + ... ianaift:l2vlan + ... + ... + ... dot1q-types:s-vlan10 + ... + ... dot1q-types:c-vlan20 + ...
2001:db8:10::1 + ... 48
+ ... 0
+ ... + ... ${Parent Interface}
+ ${Path}= Set Variable /ietf-interfaces:interfaces/interface[name="${Parent Interface}.sub1"] + ${Subinterface Data}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... ${Path} + ... xml + Should Be Equal As Strings ${Expected XML} ${Subinterface Data} + @{Sys Names}= List Directory /sys/class/net + ${Subinterface Name}= Set Variable ${Parent Interface}.sub1 + Should Contain ${Sys Names} ${Subinterface Name} + Should Contain ${Sys Names} ${Subinterface Name}.20 + ${Proc Res}= Run Process ip addr show dev ${Subinterface Name} + Should Contain ${Proc Res.stdout} inet6 2001:db8:10::1/48 + ${Proc Res}= Run Process ip -d link show ${Subinterface Name} + Should Contain ${Proc Res.stdout} vlan protocol 802.1ad id 10 + ${Proc Res}= Run Process ip -d link show ${Subinterface Name}.20 + Should Contain ${Proc Res.stdout} vlan protocol 802.1Q id 20 diff --git a/tests/integration/robot-ietf-interfaces/README.md b/tests/integration/robot-ietf-interfaces/README.md new file mode 100644 index 00000000..08f54481 --- /dev/null +++ b/tests/integration/robot-ietf-interfaces/README.md @@ -0,0 +1,19 @@ +# Robot IETF Interfaces +Robot framework tests via the Sysrepo robot library. + +## `__init__.robot` +Initializes a default Sysrepo connection as well as a running datastore session at the start of each test. +At the end of a test, the initial datastore data is restored as well as all connections and sessions being closed. + +## `InterfaceTests.robot`, `IpTests.robot` +These files contain integration test cases + +## `InterfaceKeywords.resource` +Contains robot specific composite keywords. + +## `InterfaceVariables.py` +Creates robot variables through the `getVariable` function. +Key, value pairs of the returned dictionary are mapped to robot Variable names and values respectively. + +## `Utils.py` +Extended robot custom keywords written in Python 3. diff --git a/tests/integration/robot-ietf-interfaces/Utils.py b/tests/integration/robot-ietf-interfaces/Utils.py new file mode 100644 index 00000000..02ae3199 --- /dev/null +++ b/tests/integration/robot-ietf-interfaces/Utils.py @@ -0,0 +1,14 @@ +from robot.api.deco import keyword + + +ROBOT_AUTO_KEYWORDS = False + + +@keyword('Create Dict From Lists') +def create_dict_from_lists(keys, values): + return dict(zip(keys, values)) + +@keyword('Format String') +def format_str(string, *args, **kwargs): + return string.format(*args, **kwargs) + diff --git a/tests/integration/robot-ietf-interfaces/__init__.robot b/tests/integration/robot-ietf-interfaces/__init__.robot new file mode 100644 index 00000000..c53c5611 --- /dev/null +++ b/tests/integration/robot-ietf-interfaces/__init__.robot @@ -0,0 +1,45 @@ +*** Settings *** +Library SysrepoLibrary +Library RPA.JSON +Library Process +Resource InterfaceInit.resource + +Suite Setup Setup IETF Interfaces +Suite Teardown Cleanup IETF Interfaces + +Test Teardown Restore Initial Running Datastore + +*** Variables *** +${Xpath Interfaces} /ietf-interfaces:interfaces +${Running Datastore} running + + +*** Keywords *** +Setup IETF Interfaces + [Documentation] Create a default connection and running session + Start Plugin + ${Connection Default}= Open Sysrepo Connection + Set Global Variable ${Connection Default} + Init Running Session + +Start Plugin + ${Plugin}= Start Process %{SYSREPO_INTERFACES_PLUGIN_PATH} + Set Suite Variable ${Plugin} + Wait For Process ${Plugin} timeout=2s on_timeout=continue + +Init Running Session + ${Session Running}= Open Datastore Session ${Connection Default} ${Running Datastore} + Set Global Variable ${Session Running} + ${If Init Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... ${Xpath Interfaces} + ... json + Set Global Variable ${If Init Str} + &{If Init JSON}= Convert String To JSON ${If Init Str} + Set Global Variable ${If Init JSON} + +Cleanup IETF Interfaces + # Restore initial data + Terminate Process ${Plugin} + Close All Sysrepo Connections And Sessions diff --git a/tests/integration/robot-ietf-routing/InterfaceTests.robot b/tests/integration/robot-ietf-routing/InterfaceTests.robot new file mode 100644 index 00000000..b74734e8 --- /dev/null +++ b/tests/integration/robot-ietf-routing/InterfaceTests.robot @@ -0,0 +1,22 @@ +*** Settings *** +Library Collections +Library OperatingSystem +Library BuiltIn +Library SysrepoLibrary +Library RPA.JSON +Variables RoutingVariables.py +Resource RoutingKeywords.resource +Resource RoutingInit.resource + +*** Test Cases *** +Test Routing Name Get + [Documentation] Check if datastore names match system network interface names + ${If Names Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Operational} + ... /ietf-routing:routing/interfaces + ... json + &{If Names JSON}= Convert String To JSON ${If Names Str} + @{If Names}= Get Values From JSON ${If Names JSON} $..interface + @{Sys Names}= List Directory /sys/class/net + Lists Should Be Equal ${If Names}[0] ${Sys names} ignore_order=True diff --git a/tests/integration/robot-ietf-routing/README.md b/tests/integration/robot-ietf-routing/README.md new file mode 100644 index 00000000..ddf46f34 --- /dev/null +++ b/tests/integration/robot-ietf-routing/README.md @@ -0,0 +1,16 @@ +# Robot IETF Routing +Robot framework tests via the Sysrepo robot library. + +## `__init__.robot` +Initializes a default Sysrepo connection as well as running and operational datastore sessions at the start of each test. +At the end of a test, the initial datastore data is restored as well as all connections and sessions being closed. + +## `InterfaceTests.robot`, `RoutesTests.robot`, `StaticRoutesTests.robot` +These files contain integration test cases + +## `RoutingKeywords.resource` +Contains robot specific composite keywords. + +## `RoutingVariables.py` +Creates robot variables through the `getVariable` function. +Key, value pairs of the returned dictionary are mapped to robot Variable names and values respectively. diff --git a/tests/integration/robot-ietf-routing/RoutesTests.robot b/tests/integration/robot-ietf-routing/RoutesTests.robot new file mode 100644 index 00000000..c8585e69 --- /dev/null +++ b/tests/integration/robot-ietf-routing/RoutesTests.robot @@ -0,0 +1,15 @@ +*** Settings *** +Library Collections +Library OperatingSystem +Library BuiltIn +Library SysrepoLibrary +Library RPA.JSON +Variables RoutingVariables.py +Resource RoutingKeywords.resource +Resource RoutingInit.resource + +*** Test Cases *** +Test Routing Routes + [Documentation] Check datastore routes count nonzero + @{Routes}= Get Values From JSON ${Routing Init Operational JSON} $..route + Should Not Be Empty ${Routes} diff --git a/tests/integration/robot-ietf-routing/RoutingInit.resource b/tests/integration/robot-ietf-routing/RoutingInit.resource new file mode 100644 index 00000000..50c154d8 --- /dev/null +++ b/tests/integration/robot-ietf-routing/RoutingInit.resource @@ -0,0 +1,21 @@ +*** Settings *** +Library SysrepoLibrary +Library RPA.JSON +Library Process + +*** Variables *** +${Xpath Routing} /ietf-routing:routing + +*** Keywords *** +Restore Initial Running Datastore + Edit Datastore Config ${Connection Default} ${Session Running} ${Routing Init Running Str} json + Check If Datastore Is Restored + +Check If Datastore Is Restored + [Documentation] Make sure the initial datastore is restored + ${Restored}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... ${Xpath Routing} + ... json + Should Be Equal As Strings ${Restored} ${Routing Init Running Str} msg="failed to restore inital running datastore" diff --git a/tests/integration/robot-ietf-routing/RoutingKeywords.resource b/tests/integration/robot-ietf-routing/RoutingKeywords.resource new file mode 100644 index 00000000..cf987f20 --- /dev/null +++ b/tests/integration/robot-ietf-routing/RoutingKeywords.resource @@ -0,0 +1,26 @@ +*** Settings *** +Library BuiltIn + + +*** Keywords *** +Running Get Json Fields + [Arguments] ${Path} ${Field} + ${Path Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Running} + ... ${Path} + ... json + &{Path JSON}= Convert String To JSON ${Path Str} + ${Fields}= Get Values From Json ${Path Json} $..${Field} + [Return] ${Fields} + +Operational Get Json Fields + [Arguments] ${Path} ${Field} + ${Path Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Operational} + ... ${Path} + ... json + &{Path JSON}= Convert String To JSON ${Path Str} + ${Fields}= Get Values From Json ${Path Json} $..${Field} + [Return] ${Fields} diff --git a/tests/integration/robot-ietf-routing/RoutingVariables.py b/tests/integration/robot-ietf-routing/RoutingVariables.py new file mode 100644 index 00000000..cef414e7 --- /dev/null +++ b/tests/integration/robot-ietf-routing/RoutingVariables.py @@ -0,0 +1,7 @@ + +def getVariables(): + # Variables get imported into robot from the dict as Key = Value pairs + variables = { + } + + return variables diff --git a/tests/integration/robot-ietf-routing/StaticRoutesTests.robot b/tests/integration/robot-ietf-routing/StaticRoutesTests.robot new file mode 100644 index 00000000..cb642f26 --- /dev/null +++ b/tests/integration/robot-ietf-routing/StaticRoutesTests.robot @@ -0,0 +1,33 @@ +*** Settings *** +Library Collections +Library OperatingSystem +Library BuiltIn +Library SysrepoLibrary +Library RPA.JSON +Variables RoutingVariables.py +Resource RoutingKeywords.resource +Resource RoutingInit.resource + + +*** Test Cases *** +Test Routing Static Routes + [Documentation] Check datastore static routes + Edit Datastore Config By File ${Connection Default} ${Session Running} data/routing/static-route.xml xml + @{Routes}= Running Get Json Fields + ... /ietf-routing:routing/control-plane-protocols/control-plane-protocol[type="static"][name="static"]/static-routes + ... route + Should Be Equal As Strings Test Route ${Routes}[0][0][description] + Should Be Equal As Strings 192.168.100.0/24 ${Routes}[0][0][destination-prefix] + Should Be Equal As Strings 10.0.2.1 ${Routes}[0][0][next-hop][next-hop-address] + @{Operational Routes}= Operational Get Json Fields /ietf-routing:routing/ribs/rib[name="ipv4-main"] route + ${In Operational}= Set Variable False + FOR ${Route} IN @{Operational Routes}[0] + IF 'ietf-ipv4-unicast-routing:destination-prefix' in ${Route} and 'ietf-ipv4-unicast-routing:next-hop-address' in ${Route}[next-hop] + IF "${Route}[ietf-ipv4-unicast-routing:destination-prefix]" == "192.168.100.0/24" + Should Be Equal As Strings ${Route}[next-hop][ietf-ipv4-unicast-routing:next-hop-address] + ... 10.0.2.1 + ${In Operational}= Set Variable True + END + END + END + Should Be True ${In Operational} diff --git a/tests/integration/robot-ietf-routing/__init__.robot b/tests/integration/robot-ietf-routing/__init__.robot new file mode 100644 index 00000000..7ecccded --- /dev/null +++ b/tests/integration/robot-ietf-routing/__init__.robot @@ -0,0 +1,60 @@ +*** Settings *** +Library SysrepoLibrary +Library RPA.JSON +Library Process +Resource RoutingInit.resource + +Suite Setup Setup IETF Routing +Suite Teardown Cleanup IETF Routing + +Test Teardown Restore Initial Running Datastore + +*** Variables *** +${Xpath Routing} /ietf-routing:routing +${Operational Datastore} operational +${Running Datastore} running + + +*** Keywords *** +Setup IETF Routing + [Documentation] Create a default connection and running and operational sessions + Start Plugin + ${Connection Default}= Open Sysrepo Connection + Set Global Variable ${Connection Default} + Init Running Session + Init Operational Session + +Start Plugin + ${Plugin}= Start Process %{SYSREPO_ROUTING_PLUGIN_PATH} + Set Suite Variable ${Plugin} + Wait For Process ${Plugin} timeout=2s on_timeout=continue + +Init Running Session + ${Session Running}= Open Datastore Session ${Connection Default} ${Running Datastore} + Set Global Variable ${Session Running} + ${Routing Init Running Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session RUnning} + ... ${Xpath Routing} + ... json + Set Global Variable ${Routing Init Running Str} + &{Routing Init Running JSON}= Convert String To JSON ${Routing Init Running Str} + Set Global Variable ${Routing Init Running JSON} + +Init Operational Session + ${Session Operational}= Open Datastore Session ${Connection Default} ${Operational Datastore} + Set Global Variable ${Session Operational} + ${Routing Init Operational Str}= Get Datastore Data + ... ${Connection Default} + ... ${Session Operational} + ... ${Xpath Routing} + ... json + Set Global Variable ${Routing Init Operational Str} + &{Routing Init Operational JSON}= Convert String To JSON ${Routing Init Operational Str} + Set Global Variable ${Routing Init Operational JSON} + +Cleanup IETF Routing + [Documentation] Restore initial running datastore + Terminate Process ${Plugin} + Close All Sysrepo Connections And Sessions + diff --git a/tests/integration/robotidy.toml b/tests/integration/robotidy.toml new file mode 100644 index 00000000..8b1bbc45 --- /dev/null +++ b/tests/integration/robotidy.toml @@ -0,0 +1,12 @@ +[tool.robotidy] +overwrite = false +diff = true +startline = 10 +endline = 20 +transform = [ + "DiscardEmptySections:allow_only_comments=True", + "SplitTooLongLine" +] +configure = [ + "SplitTooLongLine:split_on_every_arg=True" +] diff --git a/tests/routing/CMakeLists.txt b/tests/routing/CMakeLists.txt new file mode 100644 index 00000000..0a9303f0 --- /dev/null +++ b/tests/routing/CMakeLists.txt @@ -0,0 +1,22 @@ +set(ROUTING_UTEST_NAME "routing_utest") + +add_executable( + ${ROUTING_UTEST_NAME} + + "routing_utest.c" +) +target_include_directories( + ${ROUTING_UTEST_NAME} + PUBLIC ${CMAKE_SOURCE_DIR}/src/routing/src +) +target_link_libraries( + ${ROUTING_UTEST_NAME} + + ${CMOCKA_LIBRARIES} + ${SYSREPO_LIBRARIES} + ${LIBYANG_LIBRARIES} +) +add_test( + NAME ${ROUTING_UTEST_NAME} + COMMAND routing_utest +) diff --git a/tests/routing/routing_utest.c b/tests/routing/routing_utest.c new file mode 100644 index 00000000..209db173 --- /dev/null +++ b/tests/routing/routing_utest.c @@ -0,0 +1,22 @@ +#include +#include +#include +#include +#include +#include +#include + +static void test_correct_routing(void **state); + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_correct_routing), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} + +static void test_correct_routing(void **state) +{ +} \ No newline at end of file