summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am2
-rwxr-xr-xconfigure.ac22
-rw-r--r--debian/frr.install1
-rw-r--r--doc/developer/images/PCEPlib_design.jpgbin0 -> 42003 bytes
-rw-r--r--doc/developer/images/PCEPlib_internal_deps.jpgbin0 -> 31742 bytes
-rw-r--r--doc/developer/images/PCEPlib_socket_comm.jpgbin0 -> 36823 bytes
-rw-r--r--doc/developer/images/PCEPlib_threading_model.jpgbin0 -> 69181 bytes
-rw-r--r--doc/developer/images/PCEPlib_threading_model_frr_infra.jpgbin0 -> 83409 bytes
-rw-r--r--doc/developer/images/PCEPlib_timers.jpgbin0 -> 37363 bytes
-rw-r--r--doc/developer/index.rst1
-rw-r--r--doc/developer/pceplib.rst781
-rw-r--r--pathd/path_pcep.c25
-rw-r--r--pathd/path_pcep.h4
-rw-r--r--pathd/path_pcep_cli.c22
-rw-r--r--pathd/path_pcep_config.c25
-rw-r--r--pathd/path_pcep_config.h9
-rw-r--r--pathd/path_pcep_controller.c227
-rw-r--r--pathd/path_pcep_controller.h22
-rw-r--r--pathd/path_pcep_debug.c4
-rw-r--r--pathd/path_pcep_debug.h4
-rw-r--r--pathd/path_pcep_lib.c14
-rw-r--r--pathd/path_pcep_lib.h2
-rw-r--r--pathd/path_pcep_pcc.c73
-rw-r--r--pathd/path_pcep_pcc.h9
-rw-r--r--pathd/subdir.am13
-rw-r--r--pceplib/.gitignore14
-rw-r--r--pceplib/pcep.h48
-rw-r--r--pceplib/pcep_msg_encoding.h140
-rw-r--r--pceplib/pcep_msg_messages.c308
-rw-r--r--pceplib/pcep_msg_messages.h132
-rw-r--r--pceplib/pcep_msg_messages_encoding.c351
-rw-r--r--pceplib/pcep_msg_object_error_types.c389
-rw-r--r--pceplib/pcep_msg_object_error_types.h284
-rw-r--r--pceplib/pcep_msg_objects.c854
-rw-r--r--pceplib/pcep_msg_objects.h741
-rw-r--r--pceplib/pcep_msg_objects_encoding.c1720
-rw-r--r--pceplib/pcep_msg_tlvs.c464
-rw-r--r--pceplib/pcep_msg_tlvs.h380
-rw-r--r--pceplib/pcep_msg_tlvs_encoding.c1282
-rw-r--r--pceplib/pcep_msg_tools.c465
-rw-r--r--pceplib/pcep_msg_tools.h71
-rw-r--r--pceplib/pcep_pcc.c517
-rw-r--r--pceplib/pcep_pcc_api.c392
-rw-r--r--pceplib/pcep_pcc_api.h103
-rw-r--r--pceplib/pcep_session_logic.c683
-rw-r--r--pceplib/pcep_session_logic.h290
-rw-r--r--pceplib/pcep_session_logic_counters.c450
-rw-r--r--pceplib/pcep_session_logic_internals.h109
-rw-r--r--pceplib/pcep_session_logic_loop.c360
-rw-r--r--pceplib/pcep_session_logic_states.c1133
-rw-r--r--pceplib/pcep_socket_comm.c781
-rw-r--r--pceplib/pcep_socket_comm.h198
-rw-r--r--pceplib/pcep_socket_comm_internals.h69
-rw-r--r--pceplib/pcep_socket_comm_loop.c486
-rw-r--r--pceplib/pcep_socket_comm_loop.h32
-rw-r--r--pceplib/pcep_socket_comm_mock.c363
-rw-r--r--pceplib/pcep_socket_comm_mock.h67
-rw-r--r--pceplib/pcep_timer_internals.h76
-rw-r--r--pceplib/pcep_timers.c482
-rw-r--r--pceplib/pcep_timers.h92
-rw-r--r--pceplib/pcep_timers_event_loop.c106
-rw-r--r--pceplib/pcep_timers_event_loop.h34
-rw-r--r--pceplib/pcep_utils_counters.c475
-rw-r--r--pceplib/pcep_utils_counters.h232
-rw-r--r--pceplib/pcep_utils_double_linked_list.c262
-rw-r--r--pceplib/pcep_utils_double_linked_list.h72
-rw-r--r--pceplib/pcep_utils_logging.c82
-rw-r--r--pceplib/pcep_utils_logging.h66
-rw-r--r--pceplib/pcep_utils_memory.c220
-rw-r--r--pceplib/pcep_utils_memory.h89
-rw-r--r--pceplib/pcep_utils_ordered_list.c322
-rw-r--r--pceplib/pcep_utils_ordered_list.h109
-rw-r--r--pceplib/pcep_utils_queue.c150
-rw-r--r--pceplib/pcep_utils_queue.h49
-rw-r--r--pceplib/subdir.am62
-rw-r--r--pceplib/test/pcep_msg_messages_test.c498
-rw-r--r--pceplib/test/pcep_msg_messages_test.h48
-rw-r--r--pceplib/test/pcep_msg_messages_tests.c256
-rw-r--r--pceplib/test/pcep_msg_object_error_types_test.c84
-rw-r--r--pceplib/test/pcep_msg_object_error_types_test.h37
-rw-r--r--pceplib/test/pcep_msg_objects_test.c1289
-rw-r--r--pceplib/test/pcep_msg_objects_test.h64
-rwxr-xr-xpceplib/test/pcep_msg_tests_valgrind.sh2
-rw-r--r--pceplib/test/pcep_msg_tlvs_test.c671
-rw-r--r--pceplib/test/pcep_msg_tlvs_test.h51
-rw-r--r--pceplib/test/pcep_msg_tools_test.c1258
-rw-r--r--pceplib/test/pcep_msg_tools_test.h48
-rw-r--r--pceplib/test/pcep_pcc_api_test.c285
-rw-r--r--pceplib/test/pcep_pcc_api_test.h43
-rw-r--r--pceplib/test/pcep_pcc_api_tests.c88
-rwxr-xr-xpceplib/test/pcep_pcc_api_tests_valgrind.sh2
-rw-r--r--pceplib/test/pcep_session_logic_loop_test.c219
-rw-r--r--pceplib/test/pcep_session_logic_loop_test.h40
-rw-r--r--pceplib/test/pcep_session_logic_states_test.c919
-rw-r--r--pceplib/test/pcep_session_logic_states_test.h52
-rw-r--r--pceplib/test/pcep_session_logic_test.c360
-rw-r--r--pceplib/test/pcep_session_logic_test.h43
-rw-r--r--pceplib/test/pcep_session_logic_tests.c201
-rwxr-xr-xpceplib/test/pcep_session_logic_tests_valgrind.sh2
-rw-r--r--pceplib/test/pcep_socket_comm_loop_test.c194
-rw-r--r--pceplib/test/pcep_socket_comm_loop_test.h38
-rw-r--r--pceplib/test/pcep_socket_comm_test.c308
-rw-r--r--pceplib/test/pcep_socket_comm_test.h42
-rw-r--r--pceplib/test/pcep_socket_comm_tests.c128
-rwxr-xr-xpceplib/test/pcep_socket_comm_tests_valgrind.sh2
-rwxr-xr-xpceplib/test/pcep_tests_valgrind.sh15
-rw-r--r--pceplib/test/pcep_timers_event_loop_test.c160
-rw-r--r--pceplib/test/pcep_timers_event_loop_test.h38
-rw-r--r--pceplib/test/pcep_timers_test.c109
-rw-r--r--pceplib/test/pcep_timers_test.h40
-rw-r--r--pceplib/test/pcep_timers_tests.c113
-rwxr-xr-xpceplib/test/pcep_timers_tests_valgrind.sh2
-rw-r--r--pceplib/test/pcep_utils_counters_test.c254
-rw-r--r--pceplib/test/pcep_utils_counters_test.h43
-rw-r--r--pceplib/test/pcep_utils_double_linked_list_test.c297
-rw-r--r--pceplib/test/pcep_utils_double_linked_list_test.h38
-rw-r--r--pceplib/test/pcep_utils_memory_test.c241
-rw-r--r--pceplib/test/pcep_utils_memory_test.h33
-rw-r--r--pceplib/test/pcep_utils_ordered_list_test.c248
-rw-r--r--pceplib/test/pcep_utils_ordered_list_test.h39
-rw-r--r--pceplib/test/pcep_utils_queue_test.c157
-rw-r--r--pceplib/test/pcep_utils_queue_test.h36
-rw-r--r--pceplib/test/pcep_utils_tests.c136
-rwxr-xr-xpceplib/test/pcep_utils_tests_valgrind.sh2
-rw-r--r--pceplib/test/subdir.am122
-rw-r--r--redhat/frr.spec.in1
127 files changed, 27102 insertions, 211 deletions
diff --git a/.gitignore b/.gitignore
index 97349769a..05ca0b114 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,7 @@
/libtool
/libtool.orig
/changelog-auto
+/test-driver
/Makefile
/Makefile.in
diff --git a/Makefile.am b/Makefile.am
index bb8e97a11..0dff83e50 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -158,6 +158,8 @@ include bfdd/subdir.am
include yang/subdir.am
include yang/libyang_plugins/subdir.am
include vrrpd/subdir.am
+include pceplib/subdir.am
+include pceplib/test/subdir.am
include pathd/subdir.am
include vtysh/subdir.am
diff --git a/configure.ac b/configure.ac
index 139fca7c4..b034fab50 100755
--- a/configure.ac
+++ b/configure.ac
@@ -1723,12 +1723,10 @@ fi
AS_IF([test "$enable_pathd" != "no"], [
AC_DEFINE([HAVE_PATHD], [1], [pathd])
-])
-
-AS_IF([test "$enable_pcep" != "no"], [
AC_DEFINE([HAVE_PATHD_PCEP], [1], [pathd-pcep])
])
+
if test "$ac_cv_lib_json_c_json_object_get" = "no" -a "$BFDD" = "bfdd"; then
AC_MSG_ERROR(["you must use json-c library to use bfdd"])
fi
@@ -2550,15 +2548,14 @@ AM_CONDITIONAL([HAVE_PROTOBUF], [test "$enable_protobuf" = "yes"])
AM_CONDITIONAL([HAVE_PROTOBUF3], [$PROTO3])
dnl PCEP plugin
-AM_CONDITIONAL([HAVE_PATHD_PCEP], [test "$enable_pcep" = "yes"])
-AS_IF([test "$enable_pcep" = "yes"], [
- AC_CHECK_LIB([pcep_pcc], [initialize_pcc], [
- PATHD_PCEP_LIBS="-lpcep_pcc"
- ],[
- AC_MSG_ERROR([PCEP library libpcep_pcc not found])
- ])
- AC_SUBST([PATHD_PCEP_LIBS])
-])
+AS_IF([test "$enable_pathd" != "no"], [
+ AC_SUBST([PATHD_PCEP_LIBS], ["pceplib/libpcep_pcc.la"])
+ AC_SUBST([PATHD_PCEP_INCL], ["-I./pceplib "])
+ ])
+AC_CHECK_LIB([cunit], [CU_initialize_registry], [pcep_cunit=yes],[pcep_cunit=no])
+AM_CONDITIONAL([PATHD_PCEP_TEST], [test "x${pcep_cunit}" = xyes])
+AC_CHECK_PROG(VALGRIND_CHECK, valgrind, yes)
+AM_CONDITIONAL([HAVE_VALGRIND_PCEP], [test "$VALGRIND_CHECK" = "yes"])
dnl daemons
AM_CONDITIONAL([VTYSH], [test "$VTYSH" = "vtysh"])
@@ -2583,6 +2580,7 @@ AM_CONDITIONAL([STATICD], [test "$enable_staticd" != "no"])
AM_CONDITIONAL([FABRICD], [test "$enable_fabricd" != "no"])
AM_CONDITIONAL([VRRPD], [test "$enable_vrrpd" != "no"])
AM_CONDITIONAL([PATHD], [test "$enable_pathd" != "no"])
+AM_CONDITIONAL([PATHD_PCEP], [test "$enable_pathd" != "no"])
AC_CONFIG_FILES([Makefile],[
test "$enable_dev_build" = "yes" && makefile_devbuild="--dev-build"
diff --git a/debian/frr.install b/debian/frr.install
index cefc3135b..9972b579f 100644
--- a/debian/frr.install
+++ b/debian/frr.install
@@ -11,6 +11,7 @@ usr/lib/*/frr/modules/dplane_fpm_nl.so
usr/lib/*/frr/modules/zebra_cumulus_mlag.so
usr/lib/*/frr/modules/zebra_fpm.so
usr/lib/*/frr/modules/zebra_irdp.so
+usr/lib/*/frr/modules/pathd_pcep.so
usr/lib/frr/*.sh
usr/lib/frr/*d
usr/lib/frr/watchfrr
diff --git a/doc/developer/images/PCEPlib_design.jpg b/doc/developer/images/PCEPlib_design.jpg
new file mode 100644
index 000000000..41aada377
--- /dev/null
+++ b/doc/developer/images/PCEPlib_design.jpg
Binary files differ
diff --git a/doc/developer/images/PCEPlib_internal_deps.jpg b/doc/developer/images/PCEPlib_internal_deps.jpg
new file mode 100644
index 000000000..8380021b3
--- /dev/null
+++ b/doc/developer/images/PCEPlib_internal_deps.jpg
Binary files differ
diff --git a/doc/developer/images/PCEPlib_socket_comm.jpg b/doc/developer/images/PCEPlib_socket_comm.jpg
new file mode 100644
index 000000000..3d62a462f
--- /dev/null
+++ b/doc/developer/images/PCEPlib_socket_comm.jpg
Binary files differ
diff --git a/doc/developer/images/PCEPlib_threading_model.jpg b/doc/developer/images/PCEPlib_threading_model.jpg
new file mode 100644
index 000000000..afe91c240
--- /dev/null
+++ b/doc/developer/images/PCEPlib_threading_model.jpg
Binary files differ
diff --git a/doc/developer/images/PCEPlib_threading_model_frr_infra.jpg b/doc/developer/images/PCEPlib_threading_model_frr_infra.jpg
new file mode 100644
index 000000000..5648a9d78
--- /dev/null
+++ b/doc/developer/images/PCEPlib_threading_model_frr_infra.jpg
Binary files differ
diff --git a/doc/developer/images/PCEPlib_timers.jpg b/doc/developer/images/PCEPlib_timers.jpg
new file mode 100644
index 000000000..a178ee9c6
--- /dev/null
+++ b/doc/developer/images/PCEPlib_timers.jpg
Binary files differ
diff --git a/doc/developer/index.rst b/doc/developer/index.rst
index 8e7913419..46fd8f612 100644
--- a/doc/developer/index.rst
+++ b/doc/developer/index.rst
@@ -19,4 +19,5 @@ FRRouting Developer's Guide
zebra
vtysh
path
+ pceplib
link-state
diff --git a/doc/developer/pceplib.rst b/doc/developer/pceplib.rst
new file mode 100644
index 000000000..774617dfe
--- /dev/null
+++ b/doc/developer/pceplib.rst
@@ -0,0 +1,781 @@
+.. _pceplib:
+
+*******
+PCEPlib
+*******
+
+Overview
+========
+
+The PCEPlib is a PCEP implementation library that can be used by either a PCE
+or PCC.
+
+Currently, only the FRR pathd has been implemented as a PCC with the PCEPlib.
+The PCEPlib is able to simultaneously connect to multiple PCEP peers and can
+maintain persistent PCEP connections.
+
+
+PCEPlib compliance
+==================
+
+The PCEPlib implements version 1 of the PCEP protocol, according to `RFC 5440 <https://tools.ietf.org/html/rfc5440>`_.
+
+Additionally, the PCEPlib implements the following PCEP extensions:
+
+- `RFC 8281 <https://tools.ietf.org/html/rfc8281>`_ PCE initiated for PCE-Initiated LSP Setup
+- `RFC 8231 <https://tools.ietf.org/html/rfc8231>`_ Extensions for Stateful PCE
+- `RFC 8232 <https://tools.ietf.org/html/rfc8232>`_ Optimizations of Label Switched Path State Synchronization Procedures for a Stateful PCE
+- `RFC 8282 <https://tools.ietf.org/html/rfc8282>`_ Extensions to PCEP for Inter-Layer MPLS and GMPLS Traffic Engineering
+- `RFC 8408 <https://tools.ietf.org/html/rfc8408>`_ Conveying Path Setup Type in PCE Communication Protocol (PCEP) Messages
+- `draft-ietf-pce-segment-routing-07 <https://tools.ietf.org/html/draft-ietf-pce-segment-routing-07>`_,
+ `draft-ietf-pce-segment-routing-16 <https://tools.ietf.org/html/draft-ietf-pce-segment-routing-16>`_,
+ `RFC 8664 <https://tools.ietf.org/html/rfc8664>`_ Segment routing protocol extensions
+- `RFC 7470 <https://tools.ietf.org/html/rfc7470>`_ Conveying Vendor-Specific Constraints
+- `Draft-ietf-pce-association-group-10 <https://tools.ietf.org/html/draft-ietf-pce-association-group-10>`_
+ Establishing Relationships Between Sets of Label Switched Paths
+- `Draft-barth-pce-segment-routing-policy-cp-04 <https://tools.ietf.org/html/draft-barth-pce-segment-routing-policy-cp-04>`_
+ Segment Routing Policy Candidate Paths
+
+
+PCEPlib Architecture
+====================
+
+The PCEPlib is comprised of the following modules, each of which will be
+detailed in the following sections.
+
+- **pcep_messages**
+ - PCEP messages, objects, and TLVs implementations
+
+- **pcep_pcc**
+ - PCEPlib public PCC API with a sample PCC binary
+
+- **pcep_session_logic**
+ - PCEP Session handling
+
+- **pcep_socket_comm**
+ - Socket communications
+
+- **pcep_timers**
+ - PCEP timers
+
+- **pcep_utils**
+ - Internal utilities used by the PCEPlib modules.
+
+The interaction of these modules can be seen in the following diagram.
+
+PCEPlib Architecture:
+
+.. image:: images/PCEPlib_design.jpg
+
+
+PCEP Session Logic library
+--------------------------
+
+The PCEP Session Logic library orchestrates calls to the rest of the PCC libraries.
+
+PCEP Session Logic library responsibilities:
+
+- Handle messages received from "PCEP Socket Comm"
+- Create and manage "PCEP Session" objects
+- Set timers and react to timer expirations
+- Manage counters
+
+The PCEP Session Logic library will have 2 main triggers controlled by a
+pthread condition variable:
+
+- Timer expirations - ``on_timer_expire()`` callback
+- Messages received from PCEP SocketComm - ``message_received()`` callback
+
+The counters are created and managed using the ``pcep_utils/pcep_utils_counters.h``
+counters library. The following are the different counter groups managed:
+
+- **COUNTER_SUBGROUP_ID_RX_MSG**
+- **COUNTER_SUBGROUP_ID_TX_MSG**
+- **COUNTER_SUBGROUP_ID_RX_OBJ**
+- **COUNTER_SUBGROUP_ID_TX_OBJ**
+- **COUNTER_SUBGROUP_ID_RX_SUBOBJ**
+- **COUNTER_SUBGROUP_ID_TX_SUBOBJ**
+- **COUNTER_SUBGROUP_ID_RX_RO_SR_SUBOBJ**
+- **COUNTER_SUBGROUP_ID_TX_RO_SR_SUBOBJ**
+- **COUNTER_SUBGROUP_ID_RX_TLV**
+- **COUNTER_SUBGROUP_ID_TX_TLV**
+- **COUNTER_SUBGROUP_ID_EVENT**
+
+The counters can be obtained and reset as explained later in the PCEPlib PCC API.
+
+PCEP Socket Comm library
+------------------------
+
+PCEP communication can be configured to be handled internally in this simple
+library. When this library is instantiated by the PCEP Session Logic, callbacks
+are provided to handle received messages and error conditions.
+
+The following diagram illustrates how the library works.
+
+PCEPlib Socket Comm:
+
+.. image:: images/PCEPlib_socket_comm.jpg
+
+
+PCEP Timers library
+-------------------
+
+Timers can be configured to be handled internally by this library. When this
+library is instantiated by the PCEP Session Logic, callbacks are provided to
+ha:0
+ndle timer expirations. The following timers are implemented and handled,
+according to `RFC 5440 <https://tools.ietf.org/html/rfc5440>`_.
+
+- Open KeepWait (fixed at 60 seconds)
+ - Set once the PCC sends an Open, and if it expires before receiving a KeepAlive or PCErr, then the PCC should send a PCErr and close the TCP connection
+
+- Keepalive timer
+ - How often the PCC should send Keepalive messages to the PCE (and vice-versa)
+ - The timer will be reset after any message is sent: any message serves as a Keepalive
+
+- DeadTimer
+ - If no messages are received before expiration, the session is declared as down
+ - Reset everytime any message is received
+
+- PCReq request timer
+ - How long the PCC waits for the PCE to reply to PCReq messages.
+
+PCEPlib Timers:
+
+.. image:: images/PCEPlib_timers.jpg
+
+
+PCEP Messages library
+---------------------
+
+The PCEP Messages library has all of the implemented PCEP messages, objects,
+TLVs, and related functionality.
+
+The following header files can be used for creating and handling received PCEP
+entities.
+
+- pcep-messages.h
+- pcep-objects.h
+- pcep-tlvs.h
+
+
+PCEP Messages
++++++++++++++
+
+The following PCEP messages can be created and received:
+
+- ``struct pcep_message* pcep_msg_create_open(...);``
+- ``struct pcep_message* pcep_msg_create_open_with_tlvs(...);``
+- ``struct pcep_message* pcep_msg_create_request(...);``
+- ``struct pcep_message* pcep_msg_create_request_ipv6(...);``
+- ``struct pcep_message* pcep_msg_create_reply(...);``
+- ``struct pcep_message* pcep_msg_create_close(...);``
+- ``struct pcep_message* pcep_msg_create_error(...);``
+- ``struct pcep_message* pcep_msg_create_error_with_objects(...);``
+- ``struct pcep_message* pcep_msg_create_keepalive(...);``
+- ``struct pcep_message* pcep_msg_create_report(...);``
+- ``struct pcep_message* pcep_msg_create_update(...);``
+- ``struct pcep_message* pcep_msg_create_initiate(...);``
+
+Refer to ``pcep_messages/include/pcep-messages.h`` and the API section
+below for more details.
+
+
+PCEP Objects
+++++++++++++
+
+The following PCEP objects can be created and received:
+
+- ``struct pcep_object_open* pcep_obj_create_open(...);``
+- ``struct pcep_object_rp* pcep_obj_create_rp(...);``
+- ``struct pcep_object_notify* pcep_obj_create_notify(...);``
+- ``struct pcep_object_nopath* pcep_obj_create_nopath(...);``
+- ``struct pcep_object_association_ipv4* pcep_obj_create_association_ipv4(...);``
+- ``struct pcep_object_association_ipv6* pcep_obj_create_association_ipv6(...);``
+- ``struct pcep_object_endpoints_ipv4* pcep_obj_create_endpoint_ipv4(...);``
+- ``struct pcep_object_endpoints_ipv6* pcep_obj_create_endpoint_ipv6(...);``
+- ``struct pcep_object_bandwidth* pcep_obj_create_bandwidth(...);``
+- ``struct pcep_object_metric* pcep_obj_create_metric(...);``
+- ``struct pcep_object_lspa* pcep_obj_create_lspa(...);``
+- ``struct pcep_object_svec* pcep_obj_create_svec(...);``
+- ``struct pcep_object_error* pcep_obj_create_error(...);``
+- ``struct pcep_object_close* pcep_obj_create_close(...);``
+- ``struct pcep_object_srp* pcep_obj_create_srp(...);``
+- ``struct pcep_object_lsp* pcep_obj_create_lsp(...);``
+- ``struct pcep_object_vendor_info* pcep_obj_create_vendor_info(...);``
+- ``struct pcep_object_ro* pcep_obj_create_ero(...);``
+- ``struct pcep_object_ro* pcep_obj_create_rro(...);``
+- ``struct pcep_object_ro* pcep_obj_create_iro(...);``
+- ``struct pcep_ro_subobj_ipv4* pcep_obj_create_ro_subobj_ipv4(...);``
+- ``struct pcep_ro_subobj_ipv6* pcep_obj_create_ro_subobj_ipv6(...);``
+- ``struct pcep_ro_subobj_unnum* pcep_obj_create_ro_subobj_unnum(...);``
+- ``struct pcep_ro_subobj_32label* pcep_obj_create_ro_subobj_32label(...);``
+- ``struct pcep_ro_subobj_asn* pcep_obj_create_ro_subobj_asn(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_nonai(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_ipv4_node(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_ipv6_node(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_ipv4_adj(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_ipv6_adj(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(...);``
+
+Refer to ``pcep_messages/include/pcep-objects.h`` and the API section
+below for more details.
+
+
+PCEP TLVs
++++++++++
+
+The following PCEP TLVs (Tag, Length, Value) can be created and received:
+
+- Open Object TLVs
+ - ``struct pcep_object_tlv_stateful_pce_capability* pcep_tlv_create_stateful_pce_capability(...);``
+ - ``struct pcep_object_tlv_lsp_db_version* pcep_tlv_create_lsp_db_version(...);``
+ - ``struct pcep_object_tlv_speaker_entity_identifier* pcep_tlv_create_speaker_entity_id(...);``
+ - ``struct pcep_object_tlv_path_setup_type* pcep_tlv_create_path_setup_type(...);``
+ - ``struct pcep_object_tlv_path_setup_type_capability* pcep_tlv_create_path_setup_type_capability(...);``
+ - ``struct pcep_object_tlv_sr_pce_capability* pcep_tlv_create_sr_pce_capability(...);``
+
+- LSP Object TLVs
+ - ``struct pcep_object_tlv_ipv4_lsp_identifier* pcep_tlv_create_ipv4_lsp_identifiers(...);``
+ - ``struct pcep_object_tlv_ipv6_lsp_identifier* pcep_tlv_create_ipv6_lsp_identifiers(...);``
+ - ``struct pcep_object_tlv_symbolic_path_name* pcep_tlv_create_symbolic_path_name(...);``
+ - ``struct pcep_object_tlv_lsp_error_code* pcep_tlv_create_lsp_error_code(...);``
+ - ``struct pcep_object_tlv_rsvp_error_spec* pcep_tlv_create_rsvp_ipv4_error_spec(...);``
+ - ``struct pcep_object_tlv_rsvp_error_spec* pcep_tlv_create_rsvp_ipv6_error_spec(...);``
+ - ``struct pcep_object_tlv_nopath_vector* pcep_tlv_create_nopath_vector(...);``
+ - ``struct pcep_object_tlv_vendor_info* pcep_tlv_create_vendor_info(...);``
+ - ``struct pcep_object_tlv_arbitrary* pcep_tlv_create_tlv_arbitrary(...);``
+
+- SRPAG (SR Association Group) TLVs
+ - ``struct pcep_object_tlv_srpag_pol_id *pcep_tlv_create_srpag_pol_id_ipv4(...);``
+ - ``struct pcep_object_tlv_srpag_pol_id *pcep_tlv_create_srpag_pol_id_ipv6(...);``
+ - ``struct pcep_object_tlv_srpag_pol_name *pcep_tlv_create_srpag_pol_name(...);``
+ - ``struct pcep_object_tlv_srpag_cp_id *pcep_tlv_create_srpag_cp_id(...);``
+ - ``struct pcep_object_tlv_srpag_cp_pref *pcep_tlv_create_srpag_cp_pref(...);``
+
+Refer to ``pcep_messages/include/pcep-tlvs.h`` and the API section
+below for more details.
+
+
+PCEP PCC
+--------
+
+This module has a Public PCC API library (explained in detail later) and a
+sample PCC binary. The APIs in this library encapsulate other PCEPlib libraries
+for simplicity. With this API, the PCEPlib PCC can be started and stopped, and
+the PCEPlib event queue can be accessed. The PCEP Messages library is not
+encapsulated, and should be used directly.
+
+
+Internal Dependencies
+---------------------
+
+The following diagram illustrates the internal PCEPlib library dependencies.
+
+PCEPlib internal dependencies:
+
+.. image:: images/PCEPlib_internal_deps.jpg
+
+
+External Dependencies
+---------------------
+
+Originally the PCEPlib was based on the open source `libpcep project <https://www.acreo.se/open-software-libpcep>`_,
+but that dependency has been reduced to just one source file (pcep-tools.[ch]).
+
+
+PCEPlib Threading model
+-----------------------
+
+The PCEPlib can be run in stand-alone mode whereby a thread is launched for
+timers and socket comm, as is illustrated in the following diagram.
+
+PCEPlib Threading model:
+
+.. image:: images/PCEPlib_threading_model.jpg
+
+The PCEPlib can also be configured to use an external timers and socket
+infrastructure like the FRR threads and tasks. In this case, no internal
+threads are launched for timers and socket comm, as is illustrated in the
+following diagram.
+
+PCEPlib Threading model with external infra:
+
+.. image:: images/PCEPlib_threading_model_frr_infra.jpg
+
+
+Building
+--------
+
+The autotools build system is used and integrated with the frr build system.
+
+Testing
+-------
+
+The Unit Tests for an individual library are executed with the ``make check``
+command. The Unit Test binary will be written to the project ``build`` directory.
+All Unit Tests are executed with Valgrind, and any memory issues reported by
+Valgrind will cause the Unit Test to fail.
+
+
+PCEPlib PCC API
+===============
+
+The following sections describe the PCEPlib PCC API.
+
+
+PCEPlib PCC Initialization and Destruction
+------------------------------------------
+
+The PCEPlib can be initialized to handle memory, timers, and socket comm
+internally in what is called stand-alone mode, or with an external
+infrastructure, like FRR.
+
+PCEPlib PCC Initialization and Destruction in stand-alone mode
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+PCEPlib PCC initialization and destruction functions:
+
+- ``bool initialize_pcc();``
+- ``bool initialize_pcc_wait_for_completion();``
+- ``bool destroy_pcc();``
+
+The PCC can be initialized with either ``initialize_pcc()`` or
+``initialize_pcc_wait_for_completion()``.
+
+- ``initialize_pcc_wait_for_completion()`` blocks until ``destroy_pcc()``
+ is called from a separate pthread.
+- ``initialize_pcc()`` is non-blocking and will be stopped when
+ ``destroy_pcc()`` is called.
+
+Both initialize functions will launch 3 pthreads:
+
+- 1 Timer pthread
+- 1 SocketComm pthread
+- 1 SessionLogic pthread
+
+When ``destroy_pcc()`` is called, all pthreads will be stopped and all
+resources will be released.
+
+All 3 functions return true upon success, and false otherwise.
+
+PCEPlib PCC Initialization and Destruction with FRR infrastructure
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+PCEPlib PCC initialization and destruction functions:
+
+- ``bool initialize_pcc_infra(struct pceplib_infra_config *infra_config);``
+- ``bool destroy_pcc();``
+
+The ``pceplib_infra_config`` struct has the following fields:
+
+- **void *pceplib_infra_mt**
+ - FRR Memory type pointer for infra related memory management
+
+- **void *pceplib_messages_mt**
+ - FRR Memory type pointer for PCEP messages related memory management
+
+- **pceplib_malloc_func mfunc**
+ - FRR malloc function pointer
+
+- **pceplib_calloc_func cfunc**
+ - FRR calloc function pointer
+
+- **pceplib_realloc_func rfunc**
+ - FRR realloc function pointer
+
+- **pceplib_strdup_func sfunc**
+ - FRR strdup function pointer
+
+- **pceplib_free_func ffunc**
+ - FRR free function pointer
+
+- **void *external_infra_data**
+ - FRR data used by FRR timers and sockets infrastructure
+
+- **ext_timer_create timer_create_func**
+ - FRR timer create function pointer
+
+- **ext_timer_cancel timer_cancel_func**
+ - FRR timer cancel function pointer
+
+- **ext_socket_write socket_write_func**
+ - FRR socket write function pointer, indicating fd is ready to be written to
+
+- **ext_socket_read socket_read_func**
+ - FRR socket write function pointer, indicating fd is ready to be read from
+
+
+PCEPlib PCC configuration
+-------------------------
+
+PCEPlib PCC configuratoin functions:
+
+- ``pcep_configuration *create_default_pcep_configuration();``
+- ``void destroy_pcep_configuration(pcep_configuration *config);``
+
+A ``pcep_configuration`` object with default values is created with
+``create_default_pcep_configuration()``. These values can be tailored to
+specific use cases.
+
+Created ``pcep_configuration`` objects are destroyed with
+``destroy_pcep_configuration()``.
+
+
+PCEPlib PCC configuration paramaters
+++++++++++++++++++++++++++++++++++++
+
+The ``pcep_configuration`` object is defined in ``pcep_session_logic/include/pcep_session_logic.h``
+The attributes in the ``pcep_configuration`` object are detailed as follows.
+
+PCEP Connection parameters:
+
+- **dst_pcep_port**
+ - Defaults to 0, in which case the default PCEP TCP destination port
+ 4189 will be used.
+ - Set to use a specific PCEP TCP destination port.
+
+- **src_pcep_port**
+ - Defaults to 0, in which case the default PCEP TCP source port
+ 4189 will be used.
+ - Set to use a specific PCEP TCP source port.
+
+- **Source IP**
+ - Defaults to IPv4 INADDR_ANY
+ - Set **src_ip.src_ipv4** and **is_src_ipv6=false** to set the source IPv4.
+ - Set **src_ip.src_ipv6** and **is_src_ipv6=true** to set the source IPv6.
+
+- **socket_connect_timeout_millis**
+ - Maximum amount of time to wait to connect to the PCE TCP socket
+ before failing, in milliseconds.
+
+PCEP Versioning:
+
+- **pcep_msg_versioning->draft_ietf_pce_segment_routing_07**
+ - Defaults to false, in which case draft 16 versioning will be used.
+ - Set to true to use draft 07 versioning.
+
+PCEP Open Message Parameters:
+
+- **keep_alive_seconds**
+ - Sent to PCE in PCEP Open Msg
+ - Recommended value = 30, Minimum value = 1
+ - Disabled by setting value = 0
+
+- **dead_timer_seconds**
+ - Sent to PCE in PCEP Open Msg
+ - Recommended value = 4 * keepalive timer value
+
+- Supported value ranges for PCEP Open Message received from the PCE
+ - **min_keep_alive_seconds**, **max_keep_alive_seconds**
+ - **min_dead_timer_seconds**, **max_dead_timer_seconds**
+
+- **request_time_seconds**
+ - When a PCC sends a PcReq to a PCE, the amount of time a PCC will
+ wait for a PcRep reply from the PCE.
+
+- **max_unknown_requests**
+ - If a PCC/PCE receives PCRep/PCReq messages with unknown requests
+ at a rate equal or greater than MAX-UNKNOWN-REQUESTS per minute,
+ the PCC/PCE MUST send a PCEP CLOSE message.
+ - Recommended value = 5
+
+- **max_unknown_messages**
+ - If a PCC/PCE receives unrecognized messages at a rate equal or
+ greater than MAX-UNKNOWN-MESSAGES per minute, the PCC/PCE MUST
+ send a PCEP CLOSE message
+ - Recommended value = 5
+
+Stateful PCE Capability TLV configuration parameters (RFC 8231, 8232, 8281, and
+draft-ietf-pce-segment-routing-16):
+
+- **support_stateful_pce_lsp_update**
+ - If this flag is true, then a Stateful PCE Capability TLV will
+ be added to the PCEP Open object, with the LSP Update Capability
+ U-flag set true.
+ - The rest of these parameters are used to configure the Stateful
+ PCE Capability TLV
+
+- **support_pce_lsp_instantiation**
+ - Sets the I-flag true, indicating the PCC allows instantiation
+ of an LSP by a PCE.
+
+- **support_include_db_version**
+ - Sets the S-bit true, indicating the PCC will include the
+ LSP-DB-VERSION TLV in each LSP object. See lsp_db_version below.
+
+- **support_lsp_triggered_resync**
+ - Sets the T-bit true, indicating the PCE can trigger resynchronization
+ of LSPs at any point in the life of the session.
+
+- **support_lsp_delta_sync**
+ - Sets the D-bit true, indicating the PCEP speaker allows incremental
+ (delta) State Synchronization.
+
+- **support_pce_triggered_initial_sync**
+ - Sets the F-bit true, indicating the PCE SHOULD trigger initial (first)
+ State Synchronization
+
+LSP DB Version TLV configuration parameters:
+
+- **lsp_db_version**
+ - If this parameter has a value other than 0, and the above
+ support_include_db_version flag is true, then an LSP DB
+ Version TLV will be added to the PCEP Open object.
+ - This parameter should only be set if LSP-DB survived a restart
+ and is available.
+ - This value will be copied over to the pcep_session upon initialization.
+
+SR PCE Capability sub-TLV configuration parameters (draft-ietf-pce-segment-routing-16):
+
+- **support_sr_te_pst**
+ - If this flag is true, then an SR PCE Capability sub-TLV will be
+ added to a Path Setup type Capability TLV, which will be added
+ to the PCEP Open object.
+ - The PST used in the Path Setup type Capability will be 1,
+ indicating the Path is setup using Segment Routing Traffic Engineering.
+
+Only set the following fields if the **support_sr_te_pst** flag is true.
+
+- **pcc_can_resolve_nai_to_sid**
+ - Sets the N-flag true, indicating that the PCC is capable of resolving
+ a Node or Adjacency Identifier to a SID
+
+- **max_sid_depth**
+ - If set other than 0, then the PCC imposes a limit on the Maximum
+ SID depth.
+ - If this parameter is other than 0, then the X bit will be true,
+ and the parameter value will be set in the MSD field.
+
+
+PCEPlib PCC connections
+-----------------------
+
+PCEPlib PCC connect and disconnect functions:
+
+- ``pcep_session *connect_pce(pcep_configuration *config, struct in_addr *pce_ip);``
+- ``pcep_session *connect_pce_ipv6(pcep_configuration *config, struct in6_addr *pce_ip);``
+- ``void disconnect_pce(pcep_session *session);``
+
+When connecting to a PCE, a ``pcep_session`` will be returned on success, NULL
+otherwise.
+
+Refer to the above PCC configuration parameters section for setting the source
+and destination PCEP TCP ports, and the source IP address and version.
+
+
+PCEP Messages, Objects, and TLVs
+--------------------------------
+
+The PCEP messages, objects, and TLVs created in the PCEPlib are high-level API
+structures, meaning they need to be encoded before being sent on-the-wire, and
+the raw data received needs to be decoded into these structures. This makes
+using these objects much easier for the library consumer, since they do not
+need to know the detailed raw format of the PCEP entities.
+
+
+PCEP Messages
++++++++++++++
+
+Received messages (in the ``pcep_event`` explained below) are of type
+``pcep_message``, which have the following fields:
+
+- ``struct pcep_message_header *msg_header;``
+ - Defines the PCEP version and message type
+
+- ``double_linked_list *obj_list;``
+ - A double linked list of the message objects
+ - Each entry is a pointer to a ``struct pcep_object_header``, and
+ using the ``object_class`` and ``object_type`` fields, the pointer
+ can be cast to the appropriate object structure to access the
+ rest of the object fields
+
+- ``uint8_t *encoded_message;``
+ - This field is only populated for received messages or once the
+ ``pcep_encode_message()`` function has been called on the message.
+ - This field is a pointer to the raw PCEP data for the entire
+ message, including all objects and TLVs.
+
+- ``uint16_t encoded_message_length;``
+ - This field is only populated for received messages or once the
+ ``pcep_encode_message()`` function has been called on the message.
+ - This field is the length of the entire raw message, including
+ all objects and TLVs.
+ - This field is in host byte order.
+
+
+PCEP Objects
+++++++++++++
+
+A PCEP message has a double linked list of pointers to ``struct pcep_object_header``
+structures, which have the following fields:
+
+- ``enum pcep_object_classes object_class;``
+- ``enum pcep_object_types object_type;``
+- ``bool flag_p;``
+ - PCC Processing rule bit: When set, the object MUST be taken into
+ account, when cleared the object is optional
+
+- ``bool flag_i;``
+ - PCE Ignore bit: indicates to a PCC whether or not an optional
+ object was processed
+
+- ``double_linked_list *tlv_list;``
+ - A double linked list of the object TLVs
+ - Each entry is a pointer to a ``struct pcep_object_tlv_header``, and
+ using the TLV type field, the pointer can be cast to the
+ appropriate TLV structure to access the rest of the TLV fields
+
+- ``uint8_t *encoded_object;``
+ - This field is only populated for received objects or once the
+ ``pcep_encode_object()`` (called by ``pcep_encode_message()``)
+ function has been called on the object.
+ - Pointer into the encoded_message field (from the pcep_message)
+ where the raw object PCEP data starts.
+
+- ``uint16_t encoded_object_length;``
+ - This field is only populated for received objects or once the
+ ``pcep_encode_object()`` (called by ``pcep_encode_message()``)
+ function has been called on the object.
+ - This field is the length of the entire raw TLV
+ - This field is in host byte order.
+
+The object class and type can be used to cast the ``struct pcep_object_header``
+pointer to the appropriate object structure so the specific object fields can
+be accessed.
+
+
+PCEP TLVs
++++++++++
+
+A PCEP object has a double linked list of pointers to ``struct pcep_object_tlv_header``
+structures, which have the following fields:
+
+- ``enum pcep_object_tlv_types type;``
+- ``uint8_t *encoded_tlv;``
+ - This field is only populated for received TLVs or once the
+ ``pcep_encode_tlv()`` (called by ``pcep_encode_message()``)
+ function has been called on the TLV.
+ - Pointer into the encoded_message field (from the pcep_message)
+ where the raw TLV PCEP data starts.
+
+- ``uint16_t encoded_tlv_length;``
+ - This field is only populated for received TLVs or once the
+ ``pcep_encode_tlv()`` (called by ``pcep_encode_message()``)
+ function has been called on the TLV.
+ - This field is the length of the entire raw TLV
+ - This field is in host byte order.
+
+
+Memory management
++++++++++++++++++
+
+Any of the PCEPlib Message Library functions that receive a pointer to a
+``double_linked_list``, ``pcep_object_header``, or ``pcep_object_tlv_header``,
+transfer the ownership of the entity to the PCEPlib. The memory will be freed
+internally when the encapsulating structure is freed. If the memory for any of
+these is freed by the caller, then there will be a double memory free error
+when the memory is freed internally in the PCEPlib.
+
+Any of the PCEPlib Message Library functions that receive either a pointer to a
+``struct in_addr`` or ``struct in6_addr`` will allocate memory for the IP
+address internally and copy the IP address. It is the responsibility of the
+caller to manage the memory for the IP address passed into the PCEPlib Message
+Library functions.
+
+For messages received via the event queue (explained below), the message will
+be freed when the event is freed by calling ``destroy_pcep_event()``.
+
+When sending messages, the message will be freed internally in the PCEPlib when
+the ``send_message()`` ``pcep_pcc`` API function when the ``free_after_send`` flag
+is set true.
+
+To manually delete a message, call the ``pcep_msg_free_message()`` function.
+Internally, this will call ``pcep_obj_free_object()`` and ``pcep_obj_free_tlv()``
+appropriately.
+
+
+Sending a PCEP Report message
+-----------------------------
+
+This section shows how to send a PCEP Report messages from the PCC to the PCE,
+and serves as an example of how to send other messages. Refer to the sample
+PCC binary located in ``pcep_pcc/src/pcep_pcc.c`` for code examples os sending
+a PCEP Report message.
+
+The Report message must have at least an SRP, LSP, and ERO object.
+
+The PCEP Report message objects are created with the following APIs:
+
+- ``struct pcep_object_srp *pcep_obj_create_srp(...);``
+- ``struct pcep_object_lsp *pcep_obj_create_lsp(...);``
+- ``struct pcep_object_ro *pcep_obj_create_ero(...);``
+ - Create ero subobjects with the ``pcep_obj_create_ro_subobj_*(...);`` functions
+
+PCEP Report message is created with the following API:
+
+- ``struct pcep_header *pcep_msg_create_report(double_linked_list *report_object_list);``
+
+A PCEP report messages is sent with the following API:
+
+- ``void send_message(pcep_session *session, pcep_message *message, bool free_after_send);``
+
+
+PCEPlib Received event queue
+----------------------------
+
+PCEP events and messages of interest to the PCEPlib consumer will be stored
+internally in a message queue for retrieval.
+
+The following are the event types:
+
+- **MESSAGE_RECEIVED**
+- **PCE_CLOSED_SOCKET**
+- **PCE_SENT_PCEP_CLOSE**
+- **PCE_DEAD_TIMER_EXPIRED**
+- **PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED**
+- **PCC_CONNECTED_TO_PCE**
+- **PCC_CONNECTION_FAILURE**
+- **PCC_PCEP_SESSION_CLOSED**
+- **PCC_RCVD_INVALID_OPEN**
+- **PCC_SENT_INVALID_OPEN**
+- **PCC_RCVD_MAX_INVALID_MSGS**
+- **PCC_RCVD_MAX_UNKOWN_MSGS**
+
+The following PCEP messages will not be posted on the message queue, as they
+are handled internally in the library:
+
+- **Open**
+- **Keep Alive**
+- **Close**
+
+Received event queue API:
+
+- ``bool event_queue_is_empty();``
+ - Returns true if the queue is empty, false otherwise
+
+- ``uint32_t event_queue_num_events_available();``
+ - Return the number of events on the queue, 0 if empty
+
+- ``struct pcep_event *event_queue_get_event();``
+ - Return the next event on the queue, NULL if empty
+ - The ``message`` pointer will only be non-NULL if ``event_type``
+ is ``MESSAGE_RECEIVED``
+
+- ``void destroy_pcep_event(struct pcep_event *event);``
+ - Free the PCEP Event resources, including the PCEP message if present
+
+
+PCEPlib Counters
+----------------
+
+The PCEPlib counters are managed in the ``pcep_session_logic`` library, and can
+be accessed in the ``pcep_session_counters`` field of the ``pcep_session`` structure.
+There are 2 API functions to manage the counters:
+
+- ``void dump_pcep_session_counters(pcep_session *session);``
+ - Dump all of the counters to the logs
+
+- ``void reset_pcep_session_counters(pcep_session *session);``
+
diff --git a/pathd/path_pcep.c b/pathd/path_pcep.c
index 2f9ff4f0f..8b5ca8aff 100644
--- a/pathd/path_pcep.c
+++ b/pathd/path_pcep.c
@@ -17,7 +17,7 @@
*/
#include <zebra.h>
-#include <pcep_utils_counters.h>
+#include "pceplib/pcep_utils_counters.h"
#include "log.h"
#include "command.h"
@@ -215,29 +215,10 @@ int pcep_main_event_update_candidate(struct path *path)
ret = path_pcep_config_update_path(path);
if (ret != PATH_NB_ERR && path->srp_id != 0) {
- /* ODL and Cisco requires the first reported
- * LSP to have a DOWN status, the later status changes
- * will be comunicated through hook calls.
- */
- enum pcep_lsp_operational_status real_status;
if ((resp = path_pcep_config_get_path(&path->nbkey))) {
resp->srp_id = path->srp_id;
- real_status = resp->status;
- resp->status = PCEP_LSP_OPERATIONAL_DOWN;
- pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp);
- /* If the update did not have any effect and the real
- * status is not DOWN, we need to send a second report
- * so the PCE is aware of the real status. This is due
- * to the fact that NO notification will be received
- * if the update did not apply any changes */
- if ((ret == PATH_NB_NO_CHANGE)
- && (real_status != PCEP_LSP_OPERATIONAL_DOWN)) {
- resp->status = real_status;
- resp->srp_id = 0;
- pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id,
- resp);
- }
- pcep_free_path(resp);
+ pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp,
+ ret == PATH_NB_NO_CHANGE);
}
}
return ret;
diff --git a/pathd/path_pcep.h b/pathd/path_pcep.h
index 1896c265c..b131b3144 100644
--- a/pathd/path_pcep.h
+++ b/pathd/path_pcep.h
@@ -22,8 +22,8 @@
#include <stdbool.h>
#include <debug.h>
#include <netinet/tcp.h>
-#include <pcep_utils_logging.h>
-#include <pcep_pcc_api.h>
+#include "pceplib/pcep_utils_logging.h"
+#include "pceplib/pcep_pcc_api.h"
#include "mpls.h"
#include "pathd/pathd.h"
#include "pathd/path_pcep_memory.h"
diff --git a/pathd/path_pcep_cli.c b/pathd/path_pcep_cli.c
index add3391f2..7bbed9464 100644
--- a/pathd/path_pcep_cli.c
+++ b/pathd/path_pcep_cli.c
@@ -18,8 +18,8 @@
*/
#include <zebra.h>
-#include <pcep_utils_counters.h>
-#include <pcep_session_logic.h>
+#include "pceplib/pcep_utils_counters.h"
+#include "pceplib/pcep_session_logic.h"
#include "log.h"
#include "command.h"
@@ -321,8 +321,9 @@ pcep_cli_merge_pcep_pce_config_options(struct pce_opts_cli *pce_opts_cli)
default_pcep_config_group_opts_g.tcp_md5_auth;
}
}
- strncpy(pce_opts_cli->pce_opts.config_opts.tcp_md5_auth,
- tcp_md5_auth_str, TCP_MD5SIG_MAXKEYLEN);
+ strlcpy(pce_opts_cli->pce_opts.config_opts.tcp_md5_auth,
+ tcp_md5_auth_str,
+ sizeof(pce_opts_cli->pce_opts.config_opts.tcp_md5_auth));
struct ipaddr *source_ip =
&pce_opts_cli->pce_config_group_opts.source_ip;
@@ -525,8 +526,9 @@ static int path_pcep_cli_show_srte_pcep_counters(struct vty *vty)
tm_info = localtime(&group->start_time);
strftime(tm_buffer, sizeof(tm_buffer), "%Y-%m-%d %H:%M:%S", tm_info);
- vty_out(vty, "PCEP counters since %s (%luh %lum %lus):\n", tm_buffer,
- diff_time / 3600, (diff_time / 60) % 60, diff_time % 60);
+ vty_out(vty, "PCEP counters since %s (%uh %um %us):\n", tm_buffer,
+ (uint32_t)(diff_time / 3600), (uint32_t)((diff_time / 60) % 60),
+ (uint32_t)(diff_time % 60));
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
@@ -815,7 +817,8 @@ static int path_pcep_cli_peer_tcp_md5_auth(struct vty *vty,
return CMD_ERR_NO_MATCH;
}
- strncpy(pce_config->tcp_md5_auth, tcp_md5_auth, TCP_MD5SIG_MAXKEYLEN);
+ strlcpy(pce_config->tcp_md5_auth, tcp_md5_auth,
+ sizeof(pce_config->tcp_md5_auth));
return CMD_SUCCESS;
}
@@ -1220,8 +1223,9 @@ static void print_pcep_session(struct vty *vty, struct pce_opts *pce_opts,
localtime_r(&current_time, &lt);
gmtime_r(&session->time_connected, &lt);
vty_out(vty,
- " Connected for %ld seconds, since %d-%02d-%02d %02d:%02d:%02d UTC\n",
- (current_time - session->time_connected),
+ " Connected for %u seconds, since %d-%02d-%02d %02d:%02d:%02d UTC\n",
+ (uint32_t)(current_time
+ - session->time_connected),
lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday,
lt.tm_hour, lt.tm_min, lt.tm_sec);
}
diff --git a/pathd/path_pcep_config.c b/pathd/path_pcep_config.c
index 989223ebc..107475bec 100644
--- a/pathd/path_pcep_config.c
+++ b/pathd/path_pcep_config.c
@@ -19,7 +19,7 @@
#include <northbound.h>
#include <yang.h>
#include <printfrr.h>
-#include <pcep-objects.h>
+#include "pceplib/pcep_msg_objects.h"
#include "pathd/pathd.h"
#include "pathd/path_pcep.h"
#include "pathd/path_pcep_config.h"
@@ -45,14 +45,13 @@ status_int_to_ext(enum srte_policy_status status);
static enum pcep_sr_subobj_nai pcep_nai_type(enum srte_segment_nai_type type);
static enum srte_segment_nai_type srte_nai_type(enum pcep_sr_subobj_nai type);
-static int path_pcep_config_lookup_cb(struct thread *t)
+void path_pcep_refine_path(struct path *path)
{
- struct path *path = THREAD_ARG(t);
struct srte_candidate *candidate = lookup_candidate(&path->nbkey);
struct srte_lsp *lsp;
if (candidate == NULL)
- return 0;
+ return;
lsp = candidate->lsp;
@@ -65,16 +64,6 @@ static int path_pcep_config_lookup_cb(struct thread *t)
if ((path->update_origin == SRTE_ORIGIN_UNDEFINED)
&& (lsp->segment_list != NULL))
path->update_origin = lsp->segment_list->protocol_origin;
-
- return 0;
-}
-
-void path_pcep_config_lookup(struct path *path)
-{
- /*
- * Configuration access is strictly done via the main thread
- */
- thread_execute(master, path_pcep_config_lookup_cb, path, 0);
}
struct path *path_pcep_config_get_path(struct lsp_nb_key *key)
@@ -346,9 +335,11 @@ int path_pcep_config_update_path(struct path *path)
SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
for (metric = path->first_metric; metric != NULL; metric = metric->next)
- srte_lsp_set_metric(candidate->lsp, metric->type, metric->value,
- metric->enforce, metric->is_bound,
- metric->is_computed);
+ srte_lsp_set_metric(
+ candidate->lsp,
+ (enum srte_candidate_metric_type)metric->type,
+ metric->value, metric->enforce, metric->is_bound,
+ metric->is_computed);
if (path->has_bandwidth)
srte_lsp_set_bandwidth(candidate->lsp, path->bandwidth,
diff --git a/pathd/path_pcep_config.h b/pathd/path_pcep_config.h
index de29ab29c..223dd10c8 100644
--- a/pathd/path_pcep_config.h
+++ b/pathd/path_pcep_config.h
@@ -31,10 +31,11 @@
typedef int (*path_list_cb_t)(struct path *path, void *arg);
/* Lookup the candidate path and fill up the missing path attributes like name
- and type. Used for path generated from PCEP message received from the PCE
- so they contains more information about the candidate path. If no matching
- policy or candidate path is found, nothing is changed */
-void path_pcep_config_lookup(struct path *path);
+ * and type. Used for path generated from PCEP message received from the PCE
+ * so they contains more information about the candidate path. If no matching
+ * policy or candidate path is found, nothing is changed.
+ * MUST BE CALLED FROM THE MAIN THREAD */
+void path_pcep_refine_path(struct path *path);
struct path *path_pcep_config_get_path(struct lsp_nb_key *key);
void path_pcep_config_list_path(path_list_cb_t cb, void *arg);
int path_pcep_config_update_path(struct path *path);
diff --git a/pathd/path_pcep_controller.c b/pathd/path_pcep_controller.c
index f4871a4d8..db7d2b55a 100644
--- a/pathd/path_pcep_controller.c
+++ b/pathd/path_pcep_controller.c
@@ -55,7 +55,9 @@ enum pcep_ctrl_event_type {
EV_SYNC_PATH,
EV_SYNC_DONE,
EV_PCEPLIB_EVENT,
- EV_RESET_PCC_SESSION
+ EV_RESET_PCC_SESSION,
+ EV_SEND_REPORT,
+ EV_PATH_REFINED
};
struct pcep_ctrl_event_data {
@@ -73,18 +75,20 @@ struct pcep_main_event_data {
void *payload;
};
-/* Synchronous call arguments */
-
-struct get_counters_args {
+struct pcep_refine_path_event_data {
struct ctrl_state *ctrl_state;
int pcc_id;
- struct counters_group *counters;
+ pcep_refine_callback_t continue_lsp_update_handler;
+ struct path *path;
+ void *payload;
};
-struct send_report_args {
+/* Synchronous call arguments */
+
+struct get_counters_args {
struct ctrl_state *ctrl_state;
int pcc_id;
- struct path *path;
+ struct counters_group *counters;
};
struct get_pcep_session_args {
@@ -95,13 +99,10 @@ struct get_pcep_session_args {
/* Internal Functions Called From Main Thread */
static int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res);
+static int pcep_refine_path_event_cb(struct thread *thread);
/* Internal Functions Called From Controller Thread */
static int pcep_thread_finish_event_handler(struct thread *thread);
-static int pcep_thread_get_counters_callback(struct thread *t);
-static int pcep_thread_send_report_callback(struct thread *t);
-static int pcep_thread_get_pcep_session_callback(struct thread *t);
-static int pcep_thread_get_pcc_info_callback(struct thread *t);
/* Controller Thread Timer Handler */
static int schedule_thread_timer(struct ctrl_state *ctrl_state, int pcc_id,
@@ -148,6 +149,9 @@ static int pcep_thread_event_sync_done(struct ctrl_state *ctrl_state,
static int pcep_thread_event_pathd_event(struct ctrl_state *ctrl_state,
enum pcep_pathd_event_type type,
struct path *path);
+static void
+pcep_thread_path_refined_event(struct ctrl_state *ctrl_state,
+ struct pcep_refine_path_event_data *data);
/* Main Thread Event Handler */
static int send_to_main(struct ctrl_state *ctrl_state, int pcc_id,
@@ -280,48 +284,50 @@ struct counters_group *pcep_ctrl_get_counters(struct frr_pthread *fpt,
int pcc_id)
{
struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
- struct get_counters_args args = {
- .ctrl_state = ctrl_state, .pcc_id = pcc_id, .counters = NULL};
- thread_execute(ctrl_state->self, pcep_thread_get_counters_callback,
- &args, 0);
- return args.counters;
+ struct counters_group *counters = NULL;
+ struct pcc_state *pcc_state;
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ if (pcc_state) {
+ counters = pcep_lib_copy_counters(pcc_state->sess);
+ }
+ return counters;
}
pcep_session *pcep_ctrl_get_pcep_session(struct frr_pthread *fpt, int pcc_id)
{
struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
- struct get_pcep_session_args args = {.ctrl_state = ctrl_state,
- .pcc_id = pcc_id,
- .pcep_session = NULL};
- thread_execute(ctrl_state->self, pcep_thread_get_pcep_session_callback,
- &args, 0);
- return args.pcep_session;
+ struct pcc_state *pcc_state;
+ pcep_session *session = NULL;
+
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ if (pcc_state) {
+ session = pcep_lib_copy_pcep_session(pcc_state->sess);
+ }
+ return session;
}
struct pcep_pcc_info *pcep_ctrl_get_pcc_info(struct frr_pthread *fpt,
const char *pce_name)
{
struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
- struct pcep_pcc_info *args = XCALLOC(MTYPE_PCEP, sizeof(*args));
- args->ctrl_state = ctrl_state;
- strncpy(args->pce_name, pce_name, sizeof(args->pce_name));
- thread_execute(ctrl_state->self, pcep_thread_get_pcc_info_callback,
- args, 0);
+ struct pcep_pcc_info *pcc_info = XCALLOC(MTYPE_PCEP, sizeof(*pcc_info));
+ if( pcc_info && ctrl_state){
+ strlcpy(pcc_info->pce_name, pce_name, sizeof(pcc_info->pce_name));
+ pcep_pcc_copy_pcc_info(ctrl_state->pcc, pcc_info);
+ }
- return args;
+ return pcc_info;
}
-void pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
- struct path *path)
+int pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
+ struct path *path, bool is_stable)
{
- /* Sends a report stynchronously */
struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
- struct send_report_args args = {
- .ctrl_state = ctrl_state, .pcc_id = pcc_id, .path = path};
- thread_execute(ctrl_state->self, pcep_thread_send_report_callback,
- &args, 0);
+ return send_to_thread(ctrl_state, pcc_id, EV_SEND_REPORT, is_stable,
+ path);
}
+
/* ------------ Internal Functions Called from Main Thread ------------ */
int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res)
@@ -333,6 +339,20 @@ int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res)
return 0;
}
+int pcep_refine_path_event_cb(struct thread *thread)
+{
+ struct pcep_refine_path_event_data *data = THREAD_ARG(thread);
+ assert(data != NULL);
+ struct ctrl_state *ctrl_state = data->ctrl_state;
+ struct path *path = data->path;
+ assert(path != NULL);
+ int pcc_id = data->pcc_id;
+
+
+ path_pcep_refine_path(path);
+ return send_to_thread(ctrl_state, pcc_id, EV_PATH_REFINED, 0, data);
+}
+
/* ------------ API Functions Called From Controller Thread ------------ */
@@ -442,6 +462,41 @@ int pcep_thread_pcc_count(struct ctrl_state *ctrl_state)
return ctrl_state->pcc_count;
}
+int pcep_thread_refine_path(struct ctrl_state *ctrl_state, int pcc_id,
+ pcep_refine_callback_t cb, struct path *path,
+ void *payload)
+{
+ struct pcep_refine_path_event_data *data;
+
+ data = XCALLOC(MTYPE_PCEP, sizeof(*data));
+ data->ctrl_state = ctrl_state;
+ data->path = path;
+ data->pcc_id = pcc_id;
+ data->continue_lsp_update_handler = cb;
+ data->payload = payload;
+
+ thread_add_event(ctrl_state->main, pcep_refine_path_event_cb,
+ (void *)data, 0, NULL);
+ return 0;
+}
+
+void pcep_thread_path_refined_event(struct ctrl_state *ctrl_state,
+ struct pcep_refine_path_event_data *data)
+{
+ assert(data != NULL);
+ int pcc_id = data->pcc_id;
+ pcep_refine_callback_t continue_lsp_update_handler = data->continue_lsp_update_handler;
+ assert(continue_lsp_update_handler != NULL);
+ struct path *path = data->path;
+ void *payload = data->payload;
+ struct pcc_state *pcc_state = NULL;
+ XFREE(MTYPE_PCEP, data);
+
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ continue_lsp_update_handler(ctrl_state, pcc_state, path, payload);
+}
+
+
/* ------------ Internal Functions Called From Controller Thread ------------ */
int pcep_thread_finish_event_handler(struct thread *thread)
@@ -467,78 +522,6 @@ int pcep_thread_finish_event_handler(struct thread *thread)
return 0;
}
-int pcep_thread_get_counters_callback(struct thread *t)
-{
- struct get_counters_args *args = THREAD_ARG(t);
- assert(args != NULL);
- struct ctrl_state *ctrl_state = args->ctrl_state;
- assert(ctrl_state != NULL);
- struct pcc_state *pcc_state;
-
- pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, args->pcc_id);
- if (pcc_state) {
- args->counters = pcep_lib_copy_counters(pcc_state->sess);
- } else {
- args->counters = NULL;
- }
-
- return 0;
-}
-
-int pcep_thread_send_report_callback(struct thread *t)
-{
- struct send_report_args *args = THREAD_ARG(t);
- assert(args != NULL);
- struct ctrl_state *ctrl_state = args->ctrl_state;
- assert(ctrl_state != NULL);
- struct pcc_state *pcc_state;
-
- if (args->pcc_id == 0) {
- for (int i = 0; i < MAX_PCC; i++) {
- if (ctrl_state->pcc[i]) {
- pcep_pcc_send_report(ctrl_state,
- ctrl_state->pcc[i],
- args->path);
- }
- }
- } else {
- pcc_state =
- pcep_pcc_get_pcc_by_id(ctrl_state->pcc, args->pcc_id);
- pcep_pcc_send_report(ctrl_state, pcc_state, args->path);
- }
-
- return 0;
-}
-
-int pcep_thread_get_pcep_session_callback(struct thread *t)
-{
- struct get_pcep_session_args *args = THREAD_ARG(t);
- assert(args != NULL);
- struct ctrl_state *ctrl_state = args->ctrl_state;
- assert(ctrl_state != NULL);
- struct pcc_state *pcc_state;
-
- pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, args->pcc_id);
- if (pcc_state) {
- args->pcep_session =
- pcep_lib_copy_pcep_session(pcc_state->sess);
- }
-
- return 0;
-}
-
-int pcep_thread_get_pcc_info_callback(struct thread *t)
-{
- struct pcep_pcc_info *args = THREAD_ARG(t);
- assert(args != NULL);
- struct ctrl_state *ctrl_state = args->ctrl_state;
- assert(ctrl_state != NULL);
-
- pcep_pcc_copy_pcc_info(ctrl_state->pcc, args);
-
- return 0;
-}
-
/* ------------ Controller Thread Timer Handler ------------ */
int schedule_thread_timer_with_cb(struct ctrl_state *ctrl_state, int pcc_id,
@@ -752,11 +735,14 @@ int pcep_thread_event_handler(struct thread *thread)
/* Possible sub-type values */
enum pcep_pathd_event_type path_event_type = PCEP_PATH_UNDEFINED;
- /* Possible payload values */
+ /* Possible payload values, maybe an union would be better... */
struct path *path = NULL;
struct pcc_opts *pcc_opts = NULL;
struct pce_opts *pce_opts = NULL;
struct pcc_state *pcc_state = NULL;
+ struct pcep_refine_path_event_data *refine_data = NULL;
+
+ struct path *path_copy = NULL;
switch (type) {
case EV_UPDATE_PCC_OPTS:
@@ -808,6 +794,30 @@ int pcep_thread_event_handler(struct thread *thread)
(const char *)payload);
}
break;
+ case EV_SEND_REPORT:
+ assert(payload != NULL);
+ path = (struct path *)payload;
+ if (pcc_id == 0) {
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (ctrl_state->pcc[i]) {
+ path_copy = pcep_copy_path(path);
+ pcep_pcc_send_report(
+ ctrl_state, ctrl_state->pcc[i],
+ path_copy, (bool)sub_type);
+ }
+ }
+ } else {
+ pcc_state =
+ pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ pcep_pcc_send_report(ctrl_state, pcc_state, path,
+ (bool)sub_type);
+ }
+ break;
+ case EV_PATH_REFINED:
+ assert(payload != NULL);
+ refine_data = (struct pcep_refine_path_event_data *)payload;
+ pcep_thread_path_refined_event(ctrl_state, refine_data);
+ break;
default:
flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
"Unexpected event received in controller thread: %u",
@@ -984,6 +994,7 @@ int pcep_main_event_handler(struct thread *thread)
/* ------------ Helper functions ------------ */
+
void set_ctrl_state(struct frr_pthread *fpt, struct ctrl_state *ctrl_state)
{
assert(fpt != NULL);
diff --git a/pathd/path_pcep_controller.h b/pathd/path_pcep_controller.h
index f6eaa0ca2..1b7c3a4c7 100644
--- a/pathd/path_pcep_controller.h
+++ b/pathd/path_pcep_controller.h
@@ -21,16 +21,21 @@
#include "pathd/path_pcep.h"
+struct ctrl_state;
+struct pcc_state;
enum pcep_main_event_type {
PCEP_MAIN_EVENT_UNDEFINED = 0,
PCEP_MAIN_EVENT_START_SYNC,
PCEP_MAIN_EVENT_UPDATE_CANDIDATE,
- PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP
+ PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP,
};
typedef int (*pcep_main_event_handler_t)(enum pcep_main_event_type type,
int pcc_id, void *payload);
+typedef void (*pcep_refine_callback_t)(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct path *path, void *payload);
enum pcep_pathd_event_type {
PCEP_PATH_UNDEFINED = 0,
@@ -124,10 +129,13 @@ pcep_session *pcep_ctrl_get_pcep_session(struct frr_pthread *fpt, int pcc_id);
struct pcep_pcc_info *pcep_ctrl_get_pcc_info(struct frr_pthread *fpt,
const char *pce_name);
-/* Synchronously send a report, the caller is responsible to free the path,
- * If `pcc_id` is `0` the report is sent by all PCCs */
-void pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
- struct path *path);
+/* Asynchronously send a report. The caller is giving away the path structure,
+ * it shouldn't be allocated on the stack. If `pcc_id` is `0` the report is
+ * sent by all PCCs. The parameter is_stable is used to hint wether the status
+ * will soon change, this is used to ensure all report updates are sent even
+ * when missing status update events */
+int pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
+ struct path *path, bool is_stable);
/* Functions called from the controller thread */
void pcep_thread_start_sync(struct ctrl_state *ctrl_state, int pcc_id);
@@ -162,5 +170,9 @@ int pcep_thread_send_ctrl_event(void *fpt, void *payload,
pcep_ctrl_thread_callback cb);
int pcep_thread_pcep_event(struct thread *thread);
int pcep_thread_pcc_count(struct ctrl_state *ctrl_state);
+/* Called by the PCC to refine a path in the main thread */
+int pcep_thread_refine_path(struct ctrl_state *ctrl_state, int pcc_id,
+ pcep_refine_callback_t cb, struct path *path,
+ void *payload);
#endif // _PATH_PCEP_CONTROLLER_H_
diff --git a/pathd/path_pcep_debug.c b/pathd/path_pcep_debug.c
index bcaadfe4d..2dfe2130b 100644
--- a/pathd/path_pcep_debug.c
+++ b/pathd/path_pcep_debug.c
@@ -636,8 +636,8 @@ const char *pcep_message_type_name(enum pcep_message_types pcep_message_type)
return "UPDATE";
case PCEP_TYPE_INITIATE:
return "INITIATE";
- case PCEP_TYPE_UNKOWN_MSG:
- return "UNKOWN_MSG";
+ case PCEP_TYPE_START_TLS:
+ return "START_TLS";
default:
return "UNKNOWN";
}
diff --git a/pathd/path_pcep_debug.h b/pathd/path_pcep_debug.h
index 68b29ab65..5a504e4e1 100644
--- a/pathd/path_pcep_debug.h
+++ b/pathd/path_pcep_debug.h
@@ -20,8 +20,8 @@
#define _PATH_PCEP_DEBUG_H_
#include "pathd/path_debug.h"
-#include <pcep_pcc_api.h>
-#include <pcep-objects.h>
+#include "pceplib/pcep_pcc_api.h"
+#include "pceplib/pcep_msg_objects.h"
#include "pathd/path_pcep.h"
#include "pathd/path_pcep_controller.h"
#include "pathd/path_pcep_pcc.h"
diff --git a/pathd/path_pcep_lib.c b/pathd/path_pcep_lib.c
index bb6bfb133..1d2f25889 100644
--- a/pathd/path_pcep_lib.c
+++ b/pathd/path_pcep_lib.c
@@ -16,9 +16,11 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <zebra.h>
+
#include <debug.h>
-#include <pcep_utils_counters.h>
-#include <pcep_timers.h>
+#include "pceplib/pcep_utils_counters.h"
+#include "pceplib/pcep_timers.h"
#include "pathd/path_errors.h"
#include "pathd/path_memory.h"
#include "pathd/path_pcep.h"
@@ -176,11 +178,11 @@ pcep_lib_connect(struct ipaddr *src_addr, int src_port, struct ipaddr *dst_addr,
/* TODO when available in the pceplib, set it here
pcep_options->state_timeout_inteval_seconds;*/
- if (pcep_options->tcp_md5_auth != NULL
- && pcep_options->tcp_md5_auth[0] != '\0') {
+ if (pcep_options->tcp_md5_auth[0] != '\0') {
config->is_tcp_auth_md5 = true;
- strncpy(config->tcp_authentication_str,
- pcep_options->tcp_md5_auth, TCP_MD5SIG_MAXKEYLEN);
+ strlcpy(config->tcp_authentication_str,
+ pcep_options->tcp_md5_auth,
+ sizeof(config->tcp_authentication_str));
} else {
config->is_tcp_auth_md5 = false;
}
diff --git a/pathd/path_pcep_lib.h b/pathd/path_pcep_lib.h
index 3bea28432..3f34edcb3 100644
--- a/pathd/path_pcep_lib.h
+++ b/pathd/path_pcep_lib.h
@@ -20,7 +20,7 @@
#define _PATH_PCEP_LIB_H_
#include <stdbool.h>
-#include <pcep_pcc_api.h>
+#include "pceplib/pcep_pcc_api.h"
#include "frr_pthread.h"
#include "pathd/path_pcep.h"
diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c
index c1f60edd2..9de3ba781 100644
--- a/pathd/path_pcep_pcc.c
+++ b/pathd/path_pcep_pcc.c
@@ -55,6 +55,7 @@
#define MAX_ERROR_MSG_SIZE 256
#define MAX_COMPREQ_TRIES 3
+pthread_mutex_t g_pcc_info_mtx = PTHREAD_MUTEX_INITIALIZER;
/* PCEP Event Handler */
static void handle_pcep_open(struct ctrl_state *ctrl_state,
@@ -63,12 +64,15 @@ static void handle_pcep_open(struct ctrl_state *ctrl_state,
static void handle_pcep_message(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
struct pcep_message *msg);
+static void handle_pcep_lsp_initiate(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct pcep_message *msg);
static void handle_pcep_lsp_update(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
struct pcep_message *msg);
-static void handle_pcep_lsp_initiate(struct ctrl_state *ctrl_state,
+static void continue_pcep_lsp_update(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
- struct pcep_message *msg);
+ struct path *path, void *payload);
static void handle_pcep_comp_reply(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
struct pcep_message *msg);
@@ -543,23 +547,43 @@ void pcep_pcc_sync_done(struct ctrl_state *ctrl_state,
}
void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
- struct pcc_state *pcc_state, struct path *path)
+ struct pcc_state *pcc_state, struct path *path,
+ bool is_stable)
{
- if (pcc_state->status != PCEP_PCC_OPERATING)
+ if ((pcc_state->status != PCEP_PCC_OPERATING)
+ || (!pcc_state->caps.is_stateful)) {
+ pcep_free_path(path);
return;
+ }
- if (pcc_state->caps.is_stateful) {
- PCEP_DEBUG("%s Send report for candidate path %s",
- pcc_state->tag, path->name);
+ PCEP_DEBUG("%s Send report for candidate path %s", pcc_state->tag,
+ path->name);
+
+ /* ODL and Cisco requires the first reported
+ * LSP to have a DOWN status, the later status changes
+ * will be comunicated through hook calls.
+ */
+ enum pcep_lsp_operational_status real_status = path->status;
+ path->status = PCEP_LSP_OPERATIONAL_DOWN;
+ send_report(pcc_state, path);
+
+ /* If no update is expected and the real status wasn't down, we need to
+ * send a second report with the real status */
+ if (is_stable && (real_status != PCEP_LSP_OPERATIONAL_DOWN)) {
+ path->srp_id = 0;
+ path->status = real_status;
send_report(pcc_state, path);
}
+
+ pcep_free_path(path);
}
+
/* ------------ Timeout handler ------------ */
void pcep_pcc_timeout_handler(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
- enum pcep_ctrl_timer_type type, void *param)
+ enum pcep_ctrl_timeout_type type, void *param)
{
struct req_entry *req;
@@ -926,6 +950,7 @@ int pcep_pcc_calculate_best_pce(struct pcc_state **pcc)
// Changed of state so ...
if (step_0_best != best_pce) {
+ pthread_mutex_lock(&g_pcc_info_mtx);
// Calculate previous
previous_best_pce = step_0_best;
// Clean state
@@ -970,6 +995,7 @@ int pcep_pcc_calculate_best_pce(struct pcc_state **pcc)
}
}
}
+ pthread_mutex_unlock(&g_pcc_info_mtx);
}
return ((best_pce == -1) ? 0 : pcc[best_pce]->id);
@@ -1094,18 +1120,24 @@ void pcep_pcc_copy_pcc_info(struct pcc_state **pcc,
}
pcc_info->ctrl_state = NULL;
- pcc_info->msd = pcc_state->pcc_opts->msd;
- pcc_info->pcc_port = pcc_state->pcc_opts->port;
+ if(pcc_state->pcc_opts){
+ pcc_info->msd = pcc_state->pcc_opts->msd;
+ pcc_info->pcc_port = pcc_state->pcc_opts->port;
+ }
pcc_info->next_plspid = pcc_state->next_plspid;
pcc_info->next_reqid = pcc_state->next_reqid;
pcc_info->status = pcc_state->status;
pcc_info->pcc_id = pcc_state->id;
+ pthread_mutex_lock(&g_pcc_info_mtx);
pcc_info->is_best_multi_pce = pcc_state->is_best;
pcc_info->previous_best = pcc_state->previous_best;
+ pthread_mutex_unlock(&g_pcc_info_mtx);
pcc_info->precedence =
pcc_state->pce_opts ? pcc_state->pce_opts->precedence : 0;
- memcpy(&pcc_info->pcc_addr, &pcc_state->pcc_addr_tr,
- sizeof(struct ipaddr));
+ if(pcc_state->pcc_addr_tr.ipa_type != IPADDR_NONE){
+ memcpy(&pcc_info->pcc_addr, &pcc_state->pcc_addr_tr,
+ sizeof(struct ipaddr));
+ }
}
@@ -1154,12 +1186,19 @@ void handle_pcep_lsp_update(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
struct pcep_message *msg)
{
- char err[MAX_ERROR_MSG_SIZE] = "";
struct path *path;
path = pcep_lib_parse_path(msg);
lookup_nbkey(pcc_state, path);
- /* TODO: Investigate if this is safe to do in the controller thread */
- path_pcep_config_lookup(path);
+ pcep_thread_refine_path(ctrl_state, pcc_state->id,
+ &continue_pcep_lsp_update, path, NULL);
+}
+
+void continue_pcep_lsp_update(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct path *path,
+ void *payload)
+{
+ char err[MAX_ERROR_MSG_SIZE] = {0};
+
specialize_incoming_path(pcc_state, path);
PCEP_DEBUG("%s Received LSP update", pcc_state->tag);
PCEP_DEBUG_PATH("%s", format_path(path));
@@ -1309,13 +1348,13 @@ void select_transport_address(struct pcc_state *pcc_state)
* address */
if (IS_IPADDR_V4(&pcc_state->pce_opts->addr)) {
if (CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4)) {
- taddr->ipa_type = IPADDR_V4;
taddr->ipaddr_v4 = pcc_state->pcc_addr_v4;
+ taddr->ipa_type = IPADDR_V4;
}
} else {
if (CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6)) {
- taddr->ipa_type = IPADDR_V6;
taddr->ipaddr_v6 = pcc_state->pcc_addr_v6;
+ taddr->ipa_type = IPADDR_V6;
}
}
}
diff --git a/pathd/path_pcep_pcc.h b/pathd/path_pcep_pcc.h
index a466d92d5..c07a6ae54 100644
--- a/pathd/path_pcep_pcc.h
+++ b/pathd/path_pcep_pcc.h
@@ -113,13 +113,18 @@ void pcep_pcc_pathd_event_handler(struct ctrl_state *ctrl_state,
struct path *path);
void pcep_pcc_timeout_handler(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
- enum pcep_ctrl_timer_type type, void *param);
+ enum pcep_ctrl_timeout_type type, void *param);
void pcep_pcc_sync_path(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state, struct path *path);
void pcep_pcc_sync_done(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state);
+/* Send a report explicitly. When doing so the PCC may send multiple reports
+ * due to expectations from vendors for the first report to be with a DOWN
+ * status. The parameter is_stable is used for that purpose as a hint wheter
+ * to expect an update for the report */
void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
- struct pcc_state *pcc_state, struct path *path);
+ struct pcc_state *pcc_state, struct path *path,
+ bool is_stable);
int pcep_pcc_multi_pce_sync_path(struct ctrl_state *ctrl_state, int pcc_id,
struct pcc_state **pcc_state_list);
int pcep_pcc_multi_pce_remove_pcc(struct ctrl_state *ctrl_state,
diff --git a/pathd/subdir.am b/pathd/subdir.am
index 520a8c696..452d82466 100644
--- a/pathd/subdir.am
+++ b/pathd/subdir.am
@@ -11,7 +11,7 @@ vtysh_daemons += pathd
# TODO add man page
#man8 += $(MANBUILD)/pathd.8
-if HAVE_PATHD_PCEP
+if PATHD_PCEP
vtysh_scan += $(top_srcdir)/pathd/path_pcep_cli.c
module_LTLIBRARIES += pathd/pathd_pcep.la
endif
@@ -69,6 +69,15 @@ pathd_pathd_pcep_la_SOURCES = \
pathd/path_pcep_config.c \
pathd/path_pcep_pcc.c \
# end
+
+if PATHD_PCEP
+pathd_pathd_pcep_la_CPPFLAGS = -I./pceplib $(AM_CPPFLAGS)
+pathd_pathd_pcep_la_LIBADD = pceplib/libpcep_pcc.la
+else
+pathd_pathd_pcep_la_CPPFLAGS = $(AM_CPPFLAGS)
+pathd_pathd_pcep_la_LIBADD =
+endif
+
+
pathd_pathd_pcep_la_CFLAGS = $(WERROR)
pathd_pathd_pcep_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
-pathd_pathd_pcep_la_LIBADD = @PATHD_PCEP_LIBS@
diff --git a/pceplib/.gitignore b/pceplib/.gitignore
new file mode 100644
index 000000000..5861f25a4
--- /dev/null
+++ b/pceplib/.gitignore
@@ -0,0 +1,14 @@
+pcep_pcc
+test/pcep_msg_tests
+test/pcep_pcc_api_tests
+test/pcep_session_logic_tests
+test/pcep_socket_comm_tests
+test/pcep_timers_tests
+test/pcep_utils_tests
+test/valgrind.pcep_msg_tests.log
+test/valgrind.pcep_pcc_api_tests.log
+test/valgrind.pcep_session_logic_tests.log
+test/valgrind.pcep_socket_comm_tests.log
+test/valgrind.pcep_timers_tests.log
+test/valgrind.pcep_utils_tests.log
+../test-driver
diff --git a/pceplib/pcep.h b/pceplib/pcep.h
new file mode 100644
index 000000000..278ab9d5d
--- /dev/null
+++ b/pceplib/pcep.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+
+#ifndef PCEP_H_
+#define PCEP_H_
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined(linux) || defined(GNU_LINUX)
+//#include <netinet/in.h>
+#define ipv6_u __in6_u
+#else
+// bsd family
+#define TCP_MD5SIG_MAXKEYLEN 80
+//#include <netinet/in.h>
+#define ipv6_u __u6_addr
+#ifdef __FreeBSD__
+#include <sys/endian.h>
+#else
+#include <endian.h>
+#endif /* __FreeBSD__ */
+#endif
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#endif
diff --git a/pceplib/pcep_msg_encoding.h b/pceplib/pcep_msg_encoding.h
new file mode 100644
index 000000000..d835b87f9
--- /dev/null
+++ b/pceplib/pcep_msg_encoding.h
@@ -0,0 +1,140 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Definitions for encoding and decoding PCEP messages, objects, and TLVs.
+ */
+
+#ifndef PCEP_ENCODING_H
+#define PCEP_ENCODING_H
+
+#include <stdbool.h>
+
+#include "pcep_msg_messages.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tlvs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct pcep_versioning {
+ bool draft_ietf_pce_segment_routing_07; /* If false, use draft16 */
+ /* As more draft versions are incorporated, add appropriate attributes
+ */
+};
+
+#define MESSAGE_HEADER_LENGTH 4
+#define PCEP_MESSAGE_LENGTH 65535
+#define OBJECT_HEADER_LENGTH 4
+#define OBJECT_RO_SUBOBJ_HEADER_LENGTH 2
+#define TLV_HEADER_LENGTH 4
+#define LENGTH_1WORD sizeof(uint32_t)
+#define LENGTH_2WORDS sizeof(uint32_t) * 2
+#define LENGTH_3WORDS sizeof(uint32_t) * 3
+#define LENGTH_4WORDS sizeof(uint32_t) * 4
+#define LENGTH_5WORDS sizeof(uint32_t) * 5
+#define LENGTH_6WORDS sizeof(uint32_t) * 6
+#define LENGTH_7WORDS sizeof(uint32_t) * 7
+#define LENGTH_8WORDS sizeof(uint32_t) * 8
+#define LENGTH_9WORDS sizeof(uint32_t) * 9
+#define LENGTH_10WORDS sizeof(uint32_t) * 10
+#define LENGTH_11WORDS sizeof(uint32_t) * 11
+#define LENGTH_12WORDS sizeof(uint32_t) * 12
+#define LENGTH_13WORDS sizeof(uint32_t) * 13
+
+/* When iterating sub-objects or TLVs, limit to 10 in case corrupt data is
+ * received */
+#define MAX_ITERATIONS 10
+
+struct pcep_versioning *create_default_pcep_versioning(void);
+void destroy_pcep_versioning(struct pcep_versioning *versioning);
+
+/*
+ * Message encoding / decoding functions
+ */
+
+/* Called before sending messages to encode the message to a byte buffer in
+ * Network byte order. This function will also encode all the objects and their
+ * TLVs in the message. The result will be stored in the encoded_message field
+ * in the pcep_message. Implemented in pcep-messages-encoding.c */
+void pcep_encode_message(struct pcep_message *message,
+ struct pcep_versioning *versioning);
+
+/* Decode the message header and return the message length.
+ * Returns < 0 for invalid message headers. */
+int32_t pcep_decode_validate_msg_header(const uint8_t *msg_buf);
+
+/* Decode the entire message */
+struct pcep_message *pcep_decode_message(const uint8_t *message_buffer);
+
+
+/*
+ * Object encoding / decoding functions
+ */
+
+/* Implemented in pcep-objects-encoding.c
+ * Encode the object in struct pcep_object_header* into the uint8_t *buf,
+ * and return the encoded object_length. */
+uint16_t pcep_encode_object(struct pcep_object_header *object_hdr,
+ struct pcep_versioning *versioning, uint8_t *buf);
+
+/* Implemented in pcep-objects-encoding.c
+ * Decode the object, including the TLVs (if any) and return the object.
+ * Returns object on success, NULL otherwise. */
+struct pcep_object_header *pcep_decode_object(const uint8_t *msg_buf);
+
+/* Internal util functions implemented in pcep-objects-encoding.c */
+void encode_ipv6(struct in6_addr *src_ipv6, uint32_t *dst);
+void decode_ipv6(const uint32_t *src, struct in6_addr *dst_ipv6);
+uint16_t normalize_pcep_tlv_length(uint16_t length);
+bool pcep_object_has_tlvs(struct pcep_object_header *object_hdr);
+uint16_t pcep_object_get_length_by_hdr(struct pcep_object_header *object_hdr);
+uint16_t pcep_object_get_length(enum pcep_object_classes object_class,
+ enum pcep_object_types object_type);
+
+
+/*
+ * TLV encoding / decoding functions
+ */
+
+/* Implemented in pcep-tlv-encoding.c
+ * Encode the tlv in struct pcep_tlv_header* into the uint8_t *buf,
+ * and return the encoded tlv_length. */
+uint16_t pcep_encode_tlv(struct pcep_object_tlv_header *tlv_hdr,
+ struct pcep_versioning *versioning, uint8_t *buf);
+
+/* Decode the TLV in tlv_buf and return a pointer to the object */
+struct pcep_object_tlv_header *pcep_decode_tlv(const uint8_t *tlv_buf);
+
+
+/*
+ * utils mainly for testing purposes
+ */
+bool validate_message_objects(struct pcep_message *msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pceplib/pcep_msg_messages.c b/pceplib/pcep_msg_messages.c
new file mode 100644
index 000000000..ec2a237f3
--- /dev/null
+++ b/pceplib/pcep_msg_messages.c
@@ -0,0 +1,308 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * This is the implementation of a High Level PCEP message API.
+ */
+
+#include <string.h>
+#include <arpa/inet.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_messages.h"
+#include "pcep_msg_objects.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+static struct pcep_message *
+pcep_msg_create_common_with_obj_list(enum pcep_message_types msg_type,
+ double_linked_list *obj_list)
+{
+ struct pcep_message *message =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct pcep_message));
+ memset(message, 0, sizeof(struct pcep_message));
+ message->msg_header = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(struct pcep_message_header));
+ memset(message->msg_header, 0, sizeof(struct pcep_message_header));
+ message->msg_header->type = msg_type;
+ message->msg_header->pcep_version = PCEP_MESSAGE_HEADER_VERSION;
+ message->obj_list = ((obj_list == NULL) ? dll_initialize() : obj_list);
+
+ return message;
+}
+
+static struct pcep_message *
+pcep_msg_create_common(enum pcep_message_types msg_type)
+{
+ return pcep_msg_create_common_with_obj_list(msg_type, NULL);
+}
+
+struct pcep_message *pcep_msg_create_open(uint8_t keepalive, uint8_t deadtimer,
+ uint8_t sid)
+{
+ struct pcep_message *message = pcep_msg_create_common(PCEP_TYPE_OPEN);
+ dll_append(message->obj_list,
+ pcep_obj_create_open(keepalive, deadtimer, sid, NULL));
+
+ return message;
+}
+
+struct pcep_message *
+pcep_msg_create_open_with_tlvs(uint8_t keepalive, uint8_t deadtimer,
+ uint8_t sid, double_linked_list *tlv_list)
+{
+ struct pcep_message *message = pcep_msg_create_common(PCEP_TYPE_OPEN);
+ dll_append(message->obj_list,
+ pcep_obj_create_open(keepalive, deadtimer, sid, tlv_list));
+
+ return message;
+}
+
+
+struct pcep_message *
+pcep_msg_create_request(struct pcep_object_rp *rp,
+ struct pcep_object_endpoints_ipv4 *endpoints,
+ double_linked_list *object_list)
+{
+ if ((rp == NULL) || (endpoints == NULL)) {
+ return NULL;
+ }
+
+ struct pcep_message *message = pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_PCREQ, object_list);
+ dll_prepend(message->obj_list, endpoints);
+ dll_prepend(message->obj_list, rp);
+
+ return message;
+}
+
+struct pcep_message *
+pcep_msg_create_request_ipv6(struct pcep_object_rp *rp,
+ struct pcep_object_endpoints_ipv6 *endpoints,
+ double_linked_list *object_list)
+{
+ if ((rp == NULL) || (endpoints == NULL)) {
+ return NULL;
+ }
+
+ struct pcep_message *message = pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_PCREQ, object_list);
+ dll_prepend(message->obj_list, endpoints);
+ dll_prepend(message->obj_list, rp);
+
+ return message;
+}
+
+struct pcep_message *pcep_msg_create_reply(struct pcep_object_rp *rp,
+ double_linked_list *object_list)
+{
+ struct pcep_message *message = pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_PCREP, object_list);
+
+ if (rp != NULL) {
+ dll_prepend(message->obj_list, rp);
+ }
+
+ return message;
+}
+
+struct pcep_message *pcep_msg_create_close(uint8_t reason)
+{
+ struct pcep_message *message = pcep_msg_create_common(PCEP_TYPE_CLOSE);
+ dll_append(message->obj_list, pcep_obj_create_close(reason));
+
+ return message;
+}
+
+struct pcep_message *pcep_msg_create_error(uint8_t error_type,
+ uint8_t error_value)
+{
+ struct pcep_message *message = pcep_msg_create_common(PCEP_TYPE_ERROR);
+ dll_append(message->obj_list,
+ pcep_obj_create_error(error_type, error_value));
+
+ return message;
+}
+
+struct pcep_message *
+pcep_msg_create_error_with_objects(uint8_t error_type, uint8_t error_value,
+ double_linked_list *object_list)
+{
+ struct pcep_message *message = pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_ERROR, object_list);
+ dll_prepend(message->obj_list,
+ pcep_obj_create_error(error_type, error_value));
+
+ return message;
+}
+
+struct pcep_message *pcep_msg_create_keepalive()
+{
+ return (pcep_msg_create_common(PCEP_TYPE_KEEPALIVE));
+}
+
+struct pcep_message *
+pcep_msg_create_report(double_linked_list *state_report_object_list)
+{
+ return (state_report_object_list == NULL
+ ? NULL
+ : pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_REPORT, state_report_object_list));
+}
+
+struct pcep_message *
+pcep_msg_create_update(double_linked_list *update_request_object_list)
+{
+ if (update_request_object_list == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_update NULL update_request_object_list",
+ __func__);
+ return NULL;
+ }
+
+ /* There must be at least 3 objects:
+ * These 3 are mandatory: SRP, LSP, and ERO. The ERO may be empty */
+ if (update_request_object_list->num_entries < 3) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_update there must be at least 3 update objects",
+ __func__);
+ return NULL;
+ }
+
+ double_linked_list_node *node = update_request_object_list->head;
+ struct pcep_object_header *obj_hdr =
+ (struct pcep_object_header *)node->data;
+
+ /* Check for the mandatory first SRP object */
+ if (obj_hdr->object_class != PCEP_OBJ_CLASS_SRP) {
+ /* If the SRP object is missing, the receiving PCC MUST send a
+ * PCErr message with Error-type=6 (Mandatory Object missing)
+ * and Error-value=10 (SRP object missing). */
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_update missing mandatory first SRP object",
+ __func__);
+ return NULL;
+ }
+
+ /* Check for the mandatory 2nd LSP object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ if (obj_hdr->object_class != PCEP_OBJ_CLASS_LSP) {
+ /* If the LSP object is missing, the receiving PCC MUST send a
+ * PCErr message with Error-type=6 (Mandatory Object missing)
+ * and Error-value=8 (LSP object missing). */
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_update missing mandatory second LSP object",
+ __func__);
+ return NULL;
+ }
+
+ /* Check for the mandatory 3rd ERO object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ if (obj_hdr->object_class != PCEP_OBJ_CLASS_ERO) {
+ /* If the ERO object is missing, the receiving PCC MUST send a
+ * PCErr message with Error-type=6 (Mandatory Object missing)
+ * and Error-value=9 (ERO object missing). */
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_update missing mandatory third ERO object",
+ __func__);
+ return NULL;
+ }
+
+ return (pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_UPDATE, update_request_object_list));
+}
+
+struct pcep_message *
+pcep_msg_create_initiate(double_linked_list *lsp_object_list)
+{
+ if (lsp_object_list == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_initiate NULL update_request_object_list",
+ __func__);
+ return NULL;
+ }
+
+ /* There must be at least 2 objects: SRP and LSP. */
+ if (lsp_object_list->num_entries < 2) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_initiate there must be at least 2 objects",
+ __func__);
+ return NULL;
+ }
+
+ double_linked_list_node *node = lsp_object_list->head;
+ struct pcep_object_header *obj_hdr =
+ (struct pcep_object_header *)node->data;
+
+ /* Check for the mandatory first SRP object */
+ if (obj_hdr->object_class != PCEP_OBJ_CLASS_SRP) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_initiate missing mandatory first SRP object",
+ __func__);
+ return NULL;
+ }
+
+ /* Check for the mandatory 2nd LSP object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ if (obj_hdr->object_class != PCEP_OBJ_CLASS_LSP) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_initiate missing mandatory second LSP object",
+ __func__);
+ return NULL;
+ }
+
+ return (pcep_msg_create_common_with_obj_list(PCEP_TYPE_INITIATE,
+ lsp_object_list));
+}
+
+struct pcep_message *pcep_msg_create_notify(struct pcep_object_notify *notify,
+ double_linked_list *object_list)
+{
+ if (notify == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: pcep_msg_create_notify NULL notify object",
+ __func__);
+ return NULL;
+ }
+
+ struct pcep_message *message = pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_PCNOTF, object_list);
+ dll_prepend(message->obj_list, notify);
+
+ return message;
+}
diff --git a/pceplib/pcep_msg_messages.h b/pceplib/pcep_msg_messages.h
new file mode 100644
index 000000000..8542ea10e
--- /dev/null
+++ b/pceplib/pcep_msg_messages.h
@@ -0,0 +1,132 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ */
+
+
+/*
+ * This is a High Level PCEP message API.
+ */
+
+#ifndef PCEP_MESSAGES_H
+#define PCEP_MESSAGES_H
+
+#include <stdint.h>
+#include <netinet/in.h> /* struct in_addr */
+
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_msg_objects.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum pcep_message_types {
+ PCEP_TYPE_OPEN = 1,
+ PCEP_TYPE_KEEPALIVE = 2,
+ PCEP_TYPE_PCREQ = 3,
+ PCEP_TYPE_PCREP = 4,
+ PCEP_TYPE_PCNOTF = 5,
+ PCEP_TYPE_ERROR = 6,
+ PCEP_TYPE_CLOSE = 7,
+ PCEP_TYPE_REPORT = 10,
+ PCEP_TYPE_UPDATE = 11,
+ PCEP_TYPE_INITIATE = 12,
+ PCEP_TYPE_START_TLS = 13,
+ PCEP_TYPE_MAX,
+};
+
+#define PCEP_MESSAGE_HEADER_VERSION 1
+
+struct pcep_message_header {
+ uint8_t pcep_version; /* Current version is 1. */
+ enum pcep_message_types
+ type; /* Defines message type:
+ OPEN/KEEPALIVE/PCREQ/PCREP/PCNOTF/ERROR/CLOSE */
+};
+
+/* The obj_list is a double_linked_list of struct pcep_object_header pointers.
+ */
+struct pcep_message {
+ struct pcep_message_header *msg_header;
+ double_linked_list *obj_list;
+ uint8_t *encoded_message;
+ uint16_t encoded_message_length;
+};
+
+
+/*
+ * Regarding memory usage:
+ * When creating messages, any objects and tlvs passed into these APIs will be
+ * free'd when the pcep_message is free'd. That includes the
+ * double_linked_list's. So, just create the objects and TLVs, put them in their
+ * double_linked_list's, and everything will be managed internally. The message
+ * will be deleted by pcep_msg_free_message() or pcep_msg_free_message_list()
+ * which, in turn will call one of: pcep_obj_free_object() and
+ * pcep_obj_free_tlv(). For received messages, call pcep_msg_free_message() to
+ * free them.
+ */
+
+struct pcep_message *pcep_msg_create_open(uint8_t keepalive, uint8_t deadtimer,
+ uint8_t sid);
+struct pcep_message *
+pcep_msg_create_open_with_tlvs(uint8_t keepalive, uint8_t deadtimer,
+ uint8_t sid, double_linked_list *tlv_list);
+struct pcep_message *
+pcep_msg_create_request(struct pcep_object_rp *rp,
+ struct pcep_object_endpoints_ipv4 *endpoints,
+ double_linked_list *object_list);
+struct pcep_message *
+pcep_msg_create_request_ipv6(struct pcep_object_rp *rp,
+ struct pcep_object_endpoints_ipv6 *endpoints,
+ double_linked_list *object_list);
+struct pcep_message *pcep_msg_create_reply(struct pcep_object_rp *rp,
+ double_linked_list *object_list);
+struct pcep_message *pcep_msg_create_close(uint8_t reason);
+struct pcep_message *pcep_msg_create_error(uint8_t error_type,
+ uint8_t error_value);
+struct pcep_message *pcep_msg_create_error_with_objects(
+ uint8_t error_type, uint8_t error_value,
+ double_linked_list *object_list); /* include the offending objects */
+struct pcep_message *pcep_msg_create_keepalive(void);
+struct pcep_message *pcep_msg_create_notify(struct pcep_object_notify *notify,
+ double_linked_list *object_list);
+
+/* Message defined in RFC 8231 section 6.1. Expecting double_linked_list of
+ * struct pcep_object_header* objects of type SRP, LSP, or path (ERO, Bandwidth,
+ * metrics, and RRO objects). */
+struct pcep_message *
+pcep_msg_create_report(double_linked_list *state_report_object_list);
+/* Message defined in RFC 8231. Expecting double_linked_list of at least 3
+ * struct pcep_object_header* objects of type SRP, LSP, and path (ERO and
+ * intended-attribute-list). The ERO must be present, but may be empty if
+ * the PCE cannot find a valid path for a delegated LSP. */
+struct pcep_message *
+pcep_msg_create_update(double_linked_list *update_request_object_list);
+/* Message defined in RFC 8281. Expecting double_linked_list of at least 2
+ * struct pcep_object_header* objects of type SRP and LSP for LSP deletion, and
+ * may also contain Endpoints, ERO and an attribute list for LSP creation. */
+struct pcep_message *
+pcep_msg_create_initiate(double_linked_list *lsp_object_list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pceplib/pcep_msg_messages_encoding.c b/pceplib/pcep_msg_messages_encoding.c
new file mode 100644
index 000000000..23ccef480
--- /dev/null
+++ b/pceplib/pcep_msg_messages_encoding.c
@@ -0,0 +1,351 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Encoding and decoding for PCEP messages.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_messages.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tools.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+#define ANY_OBJECT 0
+#define NO_OBJECT -1
+#define NUM_CHECKED_OBJECTS 4
+/* It wont compile with this definition:
+ static const int
+ MANDATORY_MESSAGE_OBJECT_CLASSES[PCEP_TYPE_INITIATE+1][NUM_CHECKED_OBJECTS]
+ */
+static const enum pcep_object_classes MANDATORY_MESSAGE_OBJECT_CLASSES[13][4] =
+ {
+ {NO_OBJECT, NO_OBJECT, NO_OBJECT,
+ NO_OBJECT}, /* unsupported message ID = 0 */
+ {PCEP_OBJ_CLASS_OPEN, NO_OBJECT, NO_OBJECT,
+ NO_OBJECT}, /* PCEP_TYPE_OPEN = 1 */
+ {NO_OBJECT, NO_OBJECT, NO_OBJECT,
+ NO_OBJECT}, /* PCEP_TYPE_KEEPALIVE = 2 */
+ {PCEP_OBJ_CLASS_RP, PCEP_OBJ_CLASS_ENDPOINTS, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_PCREQ = 3 */
+ {PCEP_OBJ_CLASS_RP, ANY_OBJECT, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_PCREP = 4 */
+ {PCEP_OBJ_CLASS_NOTF, ANY_OBJECT, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_PCNOTF = 5 */
+ {PCEP_OBJ_CLASS_ERROR, ANY_OBJECT, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_ERROR = 6 */
+ {PCEP_OBJ_CLASS_CLOSE, NO_OBJECT, NO_OBJECT,
+ NO_OBJECT}, /* PCEP_TYPE_CLOSE = 7 */
+ {NO_OBJECT, NO_OBJECT, NO_OBJECT,
+ NO_OBJECT}, /* unsupported message ID = 8 */
+ {NO_OBJECT, NO_OBJECT, NO_OBJECT,
+ NO_OBJECT}, /* unsupported message ID = 9 */
+ {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_REPORT = 10 */
+ {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_UPDATE = 11 */
+ {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_INITIATE = 12 */
+};
+
+/* PCEP Message Common Header, According to RFC 5440
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Ver | Flags | Message-Type | Message-Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Ver (Version - 3 bits): PCEP version number. Current version is version 1.
+ *
+ * Flags (5 bits): No flags are currently defined. Unassigned bits are
+ * considered as reserved. They MUST be set to zero on transmission
+ * and MUST be ignored on receipt.
+ */
+void pcep_encode_message(struct pcep_message *message,
+ struct pcep_versioning *versioning)
+{
+ if (message == NULL) {
+ return;
+ }
+
+ if (message->msg_header == NULL) {
+ return;
+ }
+
+ /* Internal buffer used for the entire message. Later, once the entire
+ * length is known, memory will be allocated and this buffer will be
+ * copied. */
+ uint8_t message_buffer[PCEP_MESSAGE_LENGTH] = {0};
+
+ /* Write the message header. The message header length will be
+ * written when the entire length is known. */
+ uint32_t message_length = MESSAGE_HEADER_LENGTH;
+ uint16_t net_order_length = 0;
+ message_buffer[0] = (message->msg_header->pcep_version << 5) & 0xf0;
+ message_buffer[1] = message->msg_header->type;
+
+ if (message->obj_list == NULL) {
+ net_order_length = htons(message_length);
+ memcpy(message_buffer + 2, &net_order_length,
+ sizeof(net_order_length));
+ message->encoded_message =
+ pceplib_malloc(PCEPLIB_MESSAGES, message_length);
+ memcpy(message->encoded_message, message_buffer,
+ message_length);
+ message->encoded_message_length = message_length;
+
+ return;
+ }
+
+ /* Encode each of the objects */
+ double_linked_list_node *node = message->obj_list->head;
+ for (; node != NULL; node = node->next_node) {
+ message_length +=
+ pcep_encode_object(node->data, versioning,
+ message_buffer + message_length);
+ if (message_length > PCEP_MESSAGE_LENGTH) {
+ message->encoded_message = NULL;
+ message->encoded_message_length = 0;
+ return;
+ }
+ }
+
+ net_order_length = htons(message_length);
+ memcpy(message_buffer + 2, &net_order_length, sizeof(net_order_length));
+ message->encoded_message =
+ pceplib_malloc(PCEPLIB_MESSAGES, message_length);
+ memcpy(message->encoded_message, message_buffer, message_length);
+ message->encoded_message_length = message_length;
+}
+
+/*
+ * Decoding functions
+ */
+
+/* Expecting Host byte ordered header */
+static bool validate_msg_header(uint8_t msg_version, uint8_t msg_flags,
+ uint8_t msg_type, uint16_t msg_length)
+{
+ /* Invalid message if the length is less than the header
+ * size or if its not a multiple of 4 */
+ if (msg_length < MESSAGE_HEADER_LENGTH || (msg_length % 4) != 0) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PCEP message header length [%d]",
+ __func__, msg_length);
+ return false;
+ }
+
+ if (msg_version != PCEP_MESSAGE_HEADER_VERSION) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PCEP message header version [0x%x] expected version [0x%x]",
+ __func__, msg_version, PCEP_MESSAGE_HEADER_VERSION);
+ return false;
+ }
+
+ if (msg_flags != 0) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PCEP message header flags [0x%x]",
+ __func__, msg_flags);
+ return false;
+ }
+
+ switch (msg_type) {
+ /* Supported message types */
+ case PCEP_TYPE_OPEN:
+ case PCEP_TYPE_KEEPALIVE:
+ case PCEP_TYPE_PCREQ:
+ case PCEP_TYPE_PCREP:
+ case PCEP_TYPE_PCNOTF:
+ case PCEP_TYPE_ERROR:
+ case PCEP_TYPE_CLOSE:
+ case PCEP_TYPE_REPORT:
+ case PCEP_TYPE_UPDATE:
+ case PCEP_TYPE_INITIATE:
+ break;
+ default:
+ pcep_log(LOG_INFO, "%s: Invalid PCEP message header type [%d]",
+ __func__, msg_type);
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+/* Internal util function */
+static uint16_t pcep_decode_msg_header(const uint8_t *msg_buf,
+ uint8_t *msg_version, uint8_t *msg_flags,
+ uint8_t *msg_type)
+{
+ // Check RFC 5440 for version and flags position.
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //| Ver | Flags | Message-Type | Message-Length |
+ //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *msg_version = (msg_buf[0] >> 5) & 0x07;
+ *msg_flags = (msg_buf[0] & 0x1f);
+ *msg_type = msg_buf[1];
+ uint16_t host_order_length;
+ memcpy(&host_order_length, msg_buf + 2, sizeof(host_order_length));
+ return ntohs(host_order_length);
+}
+
+/* Decode the message header and return the message length */
+int32_t pcep_decode_validate_msg_header(const uint8_t *msg_buf)
+{
+ uint8_t msg_version;
+ uint8_t msg_flags;
+ uint8_t msg_type;
+ uint32_t msg_length;
+
+ msg_length = pcep_decode_msg_header(msg_buf, &msg_version, &msg_flags,
+ &msg_type);
+
+ return ((validate_msg_header(msg_version, msg_flags, msg_type,
+ msg_length)
+ == false)
+ ? -1
+ : (int32_t)msg_length);
+}
+
+bool validate_message_objects(struct pcep_message *msg)
+{
+ if (msg->msg_header->type >= PCEP_TYPE_START_TLS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting received message: Unknown message type [%d]",
+ __func__, msg->msg_header->type);
+ return false;
+ }
+
+ const enum pcep_object_classes *object_classes =
+ MANDATORY_MESSAGE_OBJECT_CLASSES[msg->msg_header->type];
+ double_linked_list_node *node;
+ int index;
+ for (node = (msg->obj_list == NULL ? NULL : msg->obj_list->head),
+ index = 0;
+ index < NUM_CHECKED_OBJECTS;
+ index++, (node = (node == NULL ? NULL : node->next_node))) {
+ struct pcep_object_header *obj =
+ ((node == NULL)
+ ? NULL
+ : (struct pcep_object_header *)node->data);
+
+ if ((int)object_classes[index] == NO_OBJECT) {
+ if (node != NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting received message: Unexpected object [%d] present",
+ __func__, obj->object_class);
+ return false;
+ }
+ } else if (object_classes[index] != ANY_OBJECT) {
+ if (node == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting received message: Expecting object in position [%d], but none received",
+ __func__, index);
+ return false;
+ } else if (object_classes[index] != obj->object_class) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting received message: Unexpected Object Class received [%d]",
+ __func__, object_classes[index]);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+struct pcep_message *pcep_decode_message(const uint8_t *msg_buf)
+{
+ uint8_t msg_version;
+ uint8_t msg_flags;
+ uint8_t msg_type;
+ uint16_t msg_length;
+
+ msg_length = pcep_decode_msg_header(msg_buf, &msg_version, &msg_flags,
+ &msg_type);
+
+ struct pcep_message *msg =
+ pceplib_calloc(PCEPLIB_MESSAGES, sizeof(struct pcep_message));
+
+ msg->msg_header = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct pcep_message_header));
+ msg->msg_header->pcep_version = msg_version;
+ msg->msg_header->type = msg_type;
+
+ msg->obj_list = dll_initialize();
+ msg->encoded_message = pceplib_malloc(PCEPLIB_MESSAGES, msg_length);
+ memcpy(msg->encoded_message, msg_buf, msg_length);
+ msg->encoded_message_length = msg_length;
+
+ uint16_t bytes_read = MESSAGE_HEADER_LENGTH;
+ while ((msg_length - bytes_read) >= OBJECT_HEADER_LENGTH) {
+ struct pcep_object_header *obj_hdr =
+ pcep_decode_object(msg_buf + bytes_read);
+
+ if (obj_hdr == NULL) {
+ pcep_log(LOG_INFO, "%s: Discarding invalid message",
+ __func__);
+ pcep_msg_free_message(msg);
+
+ return NULL;
+ }
+
+ dll_append(msg->obj_list, obj_hdr);
+ bytes_read += obj_hdr->encoded_object_length;
+ }
+
+ if (validate_message_objects(msg) == false) {
+ pcep_log(LOG_INFO, "%s: Discarding invalid message", __func__);
+ pcep_msg_free_message(msg);
+
+ return NULL;
+ }
+
+ return msg;
+}
+
+struct pcep_versioning *create_default_pcep_versioning()
+{
+ struct pcep_versioning *versioning =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(struct pcep_versioning));
+ memset(versioning, 0, sizeof(struct pcep_versioning));
+
+ return versioning;
+}
+
+void destroy_pcep_versioning(struct pcep_versioning *versioning)
+{
+ pceplib_free(PCEPLIB_INFRA, versioning);
+}
diff --git a/pceplib/pcep_msg_object_error_types.c b/pceplib/pcep_msg_object_error_types.c
new file mode 100644
index 000000000..a4fd8151c
--- /dev/null
+++ b/pceplib/pcep_msg_object_error_types.c
@@ -0,0 +1,389 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ */
+
+#include <stdlib.h>
+
+#include "pcep_msg_object_error_types.h"
+#include "pcep_utils_logging.h"
+
+/* All of these values were copied from:
+ * https://www.iana.org/assignments/pcep/pcep.xhtml#pcep-error-object
+ * Which was last updated 2020-06-02 */
+
+static const char *error_type_strings[] = {
+ "Reserved",
+ "PCEP session establishment failure",
+ "Capability not supported",
+ "Unknown Object",
+ "Not supported object",
+ "Policy violation",
+ "Mandatory Object missing",
+ "Synchronized path computation request missing",
+ "Unknown request reference",
+ "Attempt to establish a second PCEP session",
+
+ "Reception of an invalid object", /* 10 */
+ "Unrecognized EXRS subobject",
+ "Diffserv-aware TE error",
+ "BRPC procedure completion failure",
+ "Unassigned 14",
+ "Global Concurrent Optimization Error",
+ "P2MP Capability Error",
+ "P2MP END-POINTS Error",
+ "P2MP Fragmentation Error",
+ "Invalid Operation",
+
+ "LSP State Synchronization Error", /* 20 */
+ "Invalid traffic engineering path setup type",
+ "Unassigned 22",
+ "Bad parameter value",
+ "LSP instantiation error",
+ "PCEP StartTLS failure",
+ "Association Error",
+ "WSON RWA Error",
+ "H-PCE Error",
+ "Path computation failure",
+ "Unassigned 30"};
+
+static const char *error_value_strings[MAX_ERROR_TYPE][MAX_ERROR_VALUE] = {
+
+ /* 0 Reserved */
+ {"Unassigned"},
+
+ /* 1 PCEP session establishment failure */
+ {
+ "Unassigned",
+ "reception of an invalid Open message or a non Open message.",
+ "no Open message received before the expiration of the OpenWait timer",
+ "unacceptable and non negotiable session characteristics",
+ "unacceptable but negotiable session characteristics",
+ "reception of a second Open message with still unacceptable session characteristics",
+ "reception of a PCErr message proposing unacceptable session characteristics",
+ "No Keepalive or PCErr message received before the expiration of the KeepWait timer",
+ "PCEP version not supported",
+ },
+
+ /* 2 Capability not supported */
+ {"Unassigned"},
+
+ /* 3 Unknown Object */
+ {
+ "Unassigned",
+ "Unrecognized object class",
+ "Unrecognized object Type",
+ },
+
+ /* 4 Not supported object */
+ {
+ "Unassigned",
+ "Not supported object class",
+ "Not supported object Type",
+ "Unassigned",
+ "Unsupported parameter",
+ "Unsupported network performance constraint",
+ "Bandwidth Object type 3 or 4 not supported",
+ "Unsupported endpoint type in END-POINTS Generalized Endpoint object type",
+ "Unsupported TLV present in END-POINTS Generalized Endpoint object type",
+ "Unsupported granularity in the RP object flags",
+ },
+
+ /* 5 Policy violation */
+ {
+ "Unassigned",
+ "C bit of the METRIC object set (request rejected)",
+ "O bit of the RP object cleared (request rejected)",
+ "objective function not allowed (request rejected)",
+ "OF bit of the RP object set (request rejected)",
+ "Global concurrent optimization not allowed",
+ "Monitoring message supported but rejected due to policy violation",
+ "P2MP Path computation is not allowed",
+ "Not allowed network performance constraint",
+ },
+
+ /* 6 Mandatory Object missing */
+ {
+ "Unassigned",
+ "RP object missing",
+ "RRO missing for a reoptimization request (R bit of the RP object set)",
+ "END-POINTS object missing",
+ "MONITORING object missing",
+ "Unassigned",
+ "Unassigned",
+ "Unassigned",
+ "LSP object missing",
+ "ERO object missing",
+ "SRP object missing",
+ "LSP-IDENTIFIERS TLV missing",
+ "LSP-DB-VERSION TLV missing",
+ "S2LS object missing",
+ "P2MP-LSP-IDENTIFIERS TLV missing",
+ "DISJOINTNESS-CONFIGURATION TLV missing",
+ },
+
+ /* 7 Synchronized path computation request missing */
+ {"Unassigned"},
+
+ /* 8 Unknown request reference */
+ {"Unassigned"},
+
+ /* 9 Attempt to establish a second PCEP session */
+ {"Unassigned"},
+
+ /* 10 Reception of an invalid object */
+ {
+ "Unassigned",
+ "reception of an object with P flag not set although the P-flag must be set according to this specification.",
+ "Bad label value",
+ "Unsupported number of SR-ERO subobjects",
+ "Bad label format",
+ "ERO mixes SR-ERO subobjects with other subobject types",
+ "Both SID and NAI are absent in the SR-ERO subobject",
+ "Both SID and NAI are absent in the SR-RRO subobject",
+ "SYMBOLIC-PATH-NAME TLV missing",
+ "MSD exceeds the default for the PCEP session",
+ "RRO mixes SR-RRO subobjects with other subobject types",
+ "Malformed object",
+ "Missing PCE-SR-CAPABILITY sub-TLV",
+ "Unsupported NAI Type in the SR-ERO/SR-RRO subobject",
+ "Unknown SID",
+ "NAI cannot be resolved to a SID",
+ "Could not find SRGB",
+ "SID index exceeds SRGB size",
+ "Could not find SRLB",
+ "SID index exceeds SRLB size",
+ "Inconsistent SIDs in SR-ERO / SR-RRO subobjects",
+ "MSD must be nonzero",
+ "Mismatch of O field in S2LS and LSP object",
+ "Incompatible OF codes in H-PCE",
+ "Bad Bandwidth Object type 3 (Generalized bandwidth) or 4 (Generalized bandwidth of existing TE-LSP for which a reoptimization is requested)",
+ "Unsupported LSP Protection Flags in PROTECTION-ATTRIBUTE TLV",
+ "Unsupported Secondary LSP Protection Flags in PROTECTION-ATTRIBUTE TLV",
+ "Unsupported Link Protection Type in PROTECTION-ATTRIBUTE TLV",
+ "LABEL-SET TLV present with 0 bit set but without R bit set in RP",
+ "Wrong LABEL-SET TLV present with 0 and L bit set",
+ "Wrong LABEL-SET with O bit set and wrong format",
+ "Missing GMPLS-CAPABILITY TLV",
+ "Incompatible OF code",
+ },
+
+ /* 11 Unrecognized EXRS subobject */
+ {"Unassigned"},
+
+ /* 12 Diffserv-aware TE error */
+ {
+ "Unassigned",
+ "Unsupported class-type",
+ "Invalid class-type",
+ "Class-Type and setup priority do not form a configured TE-class",
+ },
+
+ /* 13 BRPC procedure completion failure */
+ {
+ "Unassigned",
+ "BRPC procedure not supported by one or more PCEs along the domain path",
+ },
+
+ /* 14 Unassigned */
+ {"Unassigned"},
+
+ /* 15 Global Concurrent Optimization Error */
+ {
+ "Unassigned",
+ "Insufficient memory",
+ "Global concurrent optimization not supported",
+ },
+
+ /* 16 P2MP Capability Error */
+ {
+ "Unassigned",
+ "The PCE cannot satisfy the request due to insufficient memory",
+ "The PCE is not capable of P2MP computation",
+ },
+
+ /* 17 P2MP END-POINTS Error */
+ {
+ "Unassigned",
+ "The PCE cannot satisfy the request due to no END-POINTS with leaf type 2",
+ "The PCE cannot satisfy the request due to no END-POINTS with leaf type 3",
+ "The PCE cannot satisfy the request due to no END-POINTS with leaf type 4",
+ "The PCE cannot satisfy the request due to inconsistent END-POINTS",
+ },
+
+ /* 18 P2MP Fragmentation Error */
+ {
+ "Unassigned",
+ "Fragmented request failure",
+ "Fragmented Report failure",
+ "Fragmented Update failure",
+ "Fragmented Instantiation failure",
+ },
+
+ /* 19 Invalid Operation */
+ {
+ "Unassigned",
+ "Attempted LSP Update Request for a non-delegated LSP. The PCEP-ERROR object is followed by the LSP object that identifies the LSP.",
+ "Attempted LSP Update Request if the stateful PCE capability was not advertised.",
+ "Attempted LSP Update Request for an LSP identified by an unknown PLSP-ID.",
+ "Unassigned",
+ "Attempted LSP State Report if active stateful PCE capability was not advertised.",
+ "PCE-initiated LSP limit reached",
+ "Delegation for PCE-initiated LSP cannot be revoked",
+ "Non-zero PLSP-ID in LSP Initiate Request",
+ "LSP is not PCE initiated",
+ "PCE-initiated operation-frequency limit reached",
+ "Attempted LSP State Report for P2MP if stateful PCE capability for P2MP was not advertised",
+ "Attempted LSP Update Request for P2MP if active stateful PCE capability for P2MP was not advertised",
+ "Attempted LSP Instantiation Request for P2MP if stateful PCE instantiation capability for P2MP was not advertised",
+ "Auto-Bandwidth capability was not advertised",
+ },
+
+ /* 20 LSP State Synchronization Error */
+ {
+ "Unassigned",
+ "A PCE indicates to a PCC that it cannot process (an otherwise valid) LSP State Report. The PCEP- ERROR object is followed by the LSP object that identifies the LSP.",
+ "LSP-DB version mismatch.",
+ "Attempt to trigger synchronization before PCE trigger.",
+ "Attempt to trigger a synchronization when the PCE triggered synchronization capability has not been advertised.",
+ "A PCC indicates to a PCE that it cannot complete the State Synchronization.",
+ "Received an invalid LSP-DB Version Number.",
+ "Received an invalid Speaker Entity Identifier.",
+ },
+
+ /* 21 Invalid traffic engineering path setup type */
+ {
+ "Unassigned",
+ "Unsupported path setup type",
+ "Mismatched path setup type",
+ },
+
+ /* 22 Unassigned */
+ {"Unassigned"},
+
+ /* 23 Bad parameter value */
+ {
+ "Unassigned",
+ "SYMBOLIC-PATH-NAME in use",
+ "Speaker identity included for an LSP that is not PCE initiated",
+ },
+
+ /* 24 LSP instantiation error */
+ {
+ "Unassigned",
+ "Unacceptable instantiation parameters",
+ "Internal error",
+ "Signaling error",
+ },
+
+ /* 25 PCEP StartTLS failure */
+ {
+ "Unassigned",
+ "Reception of StartTLS after any PCEP exchange",
+ "Reception of any other message apart from StartTLS, Open, or PCErr",
+ "Failure, connection without TLS is not possible",
+ "Failure, connection without TLS is possible",
+ "No StartTLS message (nor PCErr/Open) before StartTLSWait timer expiry",
+ },
+
+ /* 26 Association Error */
+ {
+ "Unassigned",
+ "Association Type is not supported",
+ "Too many LSPs in the association group",
+ "Too many association groups",
+ "Association unknown",
+ "Operator-configured association information mismatch",
+ "Association information mismatch",
+ "Cannot join the association group",
+ "Association ID not in range",
+ "Tunnel ID or End points mismatch for Path Protection Association",
+ "Attempt to add another working/protection LSP for Path Protection Association",
+ "Protection type is not supported",
+ },
+
+ /* 27 WSON RWA Error */
+ {
+ "Unassigned",
+ "Insufficient Memory",
+ "RWA computation Not supported",
+ "Syntactical Encoding error",
+ },
+
+ /* 28 H-PCE Error */
+ {
+ "Unassigned",
+ "H-PCE Capability not advertised",
+ "Parent PCE Capability cannot be provided",
+ },
+
+ /* 29 Path computation failure */
+ {
+ "Unassigned",
+ "Unacceptable request message",
+ "Generalized bandwidth value not supported",
+ "Label Set constraint could not be met",
+ "Label constraint could not be met",
+ }
+
+ /* 30-255 Unassigned */
+};
+
+
+const char *get_error_type_str(enum pcep_error_type error_type)
+{
+ if (error_type < 0 || error_type >= MAX_ERROR_TYPE) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: get_error_type_str: error_type [%d] out of range [0..%d]",
+ __func__, error_type, MAX_ERROR_TYPE);
+
+ return NULL;
+ }
+
+ return error_type_strings[error_type];
+}
+
+const char *get_error_value_str(enum pcep_error_type error_type,
+ enum pcep_error_value error_value)
+{
+ if (error_type < 0 || error_type >= MAX_ERROR_TYPE) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: get_error_value_str: error_type [%d] out of range [0..%d]",
+ __func__, error_type, MAX_ERROR_TYPE);
+
+ return NULL;
+ }
+
+ if (error_value < 0 || error_value >= MAX_ERROR_VALUE) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: get_error_value_str: error_value [%d] out of range [0..%d]",
+ __func__, error_value, MAX_ERROR_VALUE);
+
+ return NULL;
+ }
+
+ if (error_value_strings[error_type][error_value] == NULL) {
+ return "Unassigned";
+ }
+
+ return error_value_strings[error_type][error_value];
+}
diff --git a/pceplib/pcep_msg_object_error_types.h b/pceplib/pcep_msg_object_error_types.h
new file mode 100644
index 000000000..d62cc7e27
--- /dev/null
+++ b/pceplib/pcep_msg_object_error_types.h
@@ -0,0 +1,284 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ */
+
+
+/*
+ * Error Object Type and Value definitions
+ */
+
+#ifndef PCEP_OBJECT_ERROR_TYPES_H
+#define PCEP_OBJECT_ERROR_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_ERROR_TYPE 30
+#define MAX_ERROR_VALUE 255
+
+enum pcep_error_type {
+ PCEP_ERRT_SESSION_FAILURE = 1,
+ PCEP_ERRT_CAPABILITY_NOT_SUPPORTED = 2,
+ PCEP_ERRT_UNKNOW_OBJECT = 3,
+ PCEP_ERRT_NOT_SUPPORTED_OBJECT = 4,
+ PCEP_ERRT_POLICY_VIOLATION = 5,
+ PCEP_ERRT_MANDATORY_OBJECT_MISSING = 6,
+ PCEP_ERRT_SYNC_PC_REQ_MISSING = 7,
+ PCEP_ERRT_UNKNOWN_REQ_REF = 8,
+ PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION = 9,
+ PCEP_ERRT_RECEPTION_OF_INV_OBJECT = 10,
+
+ PCEP_ERRT_UNRECOGNIZED_EXRS_SUBOBJ = 11,
+ PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR = 12,
+ PCEP_ERRT_BRPC_PROC_COMPLETION_ERROR = 13,
+ PCEP_ERRT_UNASSIGNED14 = 14,
+ PCEP_ERRT_GLOBAL_CONCURRENT_ERROR = 15,
+ PCEP_ERRT_P2PMP_CAP_ERROR = 16,
+ PCEP_ERRT_P2P_ENDPOINTS_ERROR = 17,
+ PCEP_ERRT_P2P_FRAGMENTATION_ERROR = 18,
+ PCEP_ERRT_INVALID_OPERATION = 19,
+ PCEP_ERRT_LSP_STATE_SYNC_ERROR = 20,
+
+ PCEP_ERRT_INVALID_TE_PATH_SETUP_TYPE = 21,
+ PCEP_ERRT_UNASSIGNED22 = 22,
+ PCEP_ERRT_BAD_PARAMETER_VALUE = 23,
+ PCEP_ERRT_LSP_INSTANTIATE_ERROR = 24,
+ PCEP_ERRT_START_TLS_FAILURE = 25,
+ PCEP_ERRT_ASSOCIATION_ERROR = 26,
+ PCEP_ERRT_WSON_RWA_ERROR = 27,
+ PCEP_ERRT_H_PCE_ERROR = 28,
+ PCEP_ERRT_PATH_COMP_FAILURE = 29,
+ PCEP_ERRT_UNASSIGNED30 = 30 /* 30 - 255 Unassigned */
+};
+
+enum pcep_error_value {
+ /* Error Value for Error Types that do not use an Error Value:
+ * PCEP_ERRT_CAPABILITY_NOT_SUPPORTED=2
+ * PCEP_ERRT_SYNC_PC_REQ_MISSING=7
+ * PCEP_ERRT_UNKNOWN_REQ_REF=8
+ * PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION=9
+ * PCEP_ERRT_UNRECOGNIZED_EXRS_SUBOBJ=11 */
+ PCEP_ERRV_UNASSIGNED = 0,
+
+ /* Error Values for PCEP_ERRT_SESSION_FAILURE=1 */
+ PCEP_ERRV_RECVD_INVALID_OPEN_MSG = 1,
+ PCEP_ERRV_OPENWAIT_TIMED_OUT = 2,
+ PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NO_NEG = 3,
+ PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG = 4,
+ PCEP_ERRV_RECVD_SECOND_OPEN_MSG_UNACCEPTABLE = 5,
+ PCEP_ERRV_RECVD_PCERR = 6,
+ PCEP_ERRV_KEEPALIVEWAIT_TIMED_OUT = 7,
+ PCEP_ERRV_PCEP_VERSION_NOT_SUPPORTED = 8,
+
+ /* Error Values for PCEP_ERRT_UNKNOW_OBJECT=3 */
+ PCEP_ERRV_UNREC_OBJECT_CLASS = 1,
+ PCEP_ERRV_UNREC_OBJECT_TYPE = 2,
+
+ /* Error Values for PCEP_ERRT_NOT_SUPPORTED_OBJECT=4 */
+ PCEP_ERRV_NOT_SUPPORTED_OBJECT_CLASS = 1,
+ PCEP_ERRV_NOT_SUPPORTED_OBJECT_TYPE = 2,
+ /* 3: Unassigned */
+ PCEP_ERRV_UNSUPPORTED_PARAM = 4,
+ PCEP_ERRV_UNSUPPORTED_NW_PERF_CONSTRAINT = 5,
+ PCEP_ERRV_NOT_SUPPORTED_BW_OBJECT_3_4 = 6,
+ PCEP_ERRV_UNSUPPORTED_ENDPOINT_TYPE = 7,
+ PCEP_ERRV_UNSUPPORTED_ENDPOINT_TLV = 8,
+ PCEP_ERRV_UNSUPPORTED_RP_FLAG_GRANULARITY = 9,
+
+ /* Error Values for PCEP_ERRT_POLICY_VIOLATION=5 */
+ PCEP_ERRV_C_BIT_SET_IN_METRIC_OBJECT = 1,
+ PCEP_ERRV_O_BIT_CLEARD_IN_RP_OBJECT = 2,
+ PCEP_ERRV_OBJECTIVE_FUNC_NOT_ALLOWED = 3,
+ PCEP_ERRV_RP_OF_BIT_SET = 4,
+ PCEP_ERRV_GLOBAL_CONCURRENCY_NOT_ALLOWED = 5,
+ PCEP_ERRV_MONITORING_MSG_REJECTED = 6,
+ PCEP_ERRV_P2MP_PATH_COMP_NOT_ALLOWED = 7,
+ PCEP_ERRV_UNALLOWED_NW_PERF_CONSTRAINT = 8,
+
+ /* Error Values for PCEP_ERRT_MANDATORY_OBJECT_MISSING=6 */
+ PCEP_ERRV_RP_OBJECT_MISSING = 1,
+ PCEP_ERRV_RRO_OBJECT_MISSING_FOR_REOP = 2,
+ PCEP_ERRV_EP_OBJECT_MISSING = 3,
+ PCEP_ERRV_MONITOR_OBJECT_MISSING = 4,
+ /* 5 - 7 Unassigned */
+ PCEP_ERRV_LSP_OBJECT_MISSING = 8,
+ PCEP_ERRV_ERO_OBJECT_MISSING = 9,
+ PCEP_ERRV_SRP_OBJECT_MISSING = 10,
+ PCEP_ERRV_LSP_ID_TLV_MISSING = 11,
+ PCEP_ERRV_LSP_DB_TLV_MISSING = 12,
+ PCEP_ERRV_S2LS_OBJECT_MISSING = 13,
+ PCEP_ERRV_P2MP_LSP_ID_TLV_MISSING = 14,
+ PCEP_ERRV_DISJOINTED_CONF_TLV_MISSING = 15,
+
+ /* Error Values for PCEP_ERRT_RECEPTION_OF_INV_OBJECT=10 */
+ PCEP_ERRV_P_FLAG_NOT_CORRECT_IN_OBJECT = 1,
+ PCEP_ERRV_BAD_LABEL_VALUE = 2,
+ PCEP_ERRV_UNSUPPORTED_NUM_SR_ERO_SUBOBJECTS = 3,
+ PCEP_ERRV_BAD_LABEL_FORMAT = 4,
+ PCEP_ERRV_ERO_SR_ERO_MIX = 5,
+ PCEP_ERRV_SR_ERO_SID_NAI_ABSENT = 6,
+ PCEP_ERRV_SR_RRO_SID_NAI_ABSENT = 7,
+ PCEP_ERRV_SYMBOLIC_PATH_NAME_TLV_MISSING = 8,
+ PCEP_ERRV_MSD_EXCEEDS_PCEP_SESSION_MAX = 9,
+
+ PCEP_ERRV_RRO_SR_RRO_MIX = 10,
+ PCEP_ERRV_MALFORMED_OBJECT = 11,
+ PCEP_ERRV_MISSING_PCE_SR_CAP_TLV = 12,
+ PCEP_ERRV_UNSUPPORTED_NAI = 13,
+ PCEP_ERRV_UNKNOWN_SID = 14,
+ PCEP_ERRV_CANNOT_RESOLVE_NAI_TO_SID = 15,
+ PCEP_ERRV_COULD_NOT_FIND_SRGB = 16,
+ PCEP_ERRV_SID_EXCEEDS_SRGB = 17,
+ PCEP_ERRV_COULD_NOT_FIND_SRLB = 18,
+ PCEP_ERRV_SID_EXCEEDS_SRLB = 19,
+
+ PCEP_ERRV_INCONSISTENT_SID = 20,
+ PCEP_ERRV_MSD_MUST_BE_NONZERO = 21,
+ PCEP_ERRV_MISMATCH_O_S2LS_LSP = 22,
+ PCEP_ERRV_INCOMPATIBLE_H_PCE_OF = 23,
+ PCEP_ERRV_BAD_BANDWIDTH_TYPE_3_4 = 24,
+ PCEP_ERRV_UNSUPPORTED_LSP_PROT_FLAGS = 25,
+ PCEP_ERRV_UNSUPPORTED_2ND_LSP_PROT_FLAGS = 26,
+ PCEP_ERRV_UNSUPPORTED_LINK_PROT_TYPE = 27,
+ PCEP_ERRV_LABEL_SET_TLV_NO_RP_R = 28,
+ PCEP_ERRV_WRONG_LABEL_SET_TLV_O_L_SET = 29,
+
+ PCEP_ERRV_WRONG_LABEL_SET_O_SET = 30,
+ PCEP_ERRV_MISSING_GMPLS_CAP_TLV = 31,
+ PCEP_ERRV_INCOMPATIBLE_OF_CODE = 32,
+
+ /* PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR = 12 */
+ PCEP_ERRV_UNSUPPORTED_CLASS_TYPE = 1,
+ PCEP_ERRV_INVALID_CLASS_TYPE = 2,
+ PCEP_ERRV_CLASS_SETUP_TYPE_NOT_TE_CLASS = 3,
+
+ /* PCEP_ERRT_BRPC_PROC_COMPLETION_ERROR = 13 */
+ PCEP_ERRV_BRPC_PROC_NOT_SUPPORTED = 1,
+
+ /* PCEP_ERRT_UNASSIGNED14 = 14 */
+
+ /* PCEP_ERRT_GLOBAL_CONCURRENT_ERROR = 15 */
+ PCEP_ERRV_INSUFFICIENT_MEMORY = 1,
+ PCEP_ERRV_GLOBAL_CONCURRENT_OPT_NOT_SUPPORTED = 2,
+
+ /* PCEP_ERRT_P2PMP_CAP_ERROR = 16 */
+ PCEP_ERRV_PCE_INSUFFICIENT_MEMORY = 1,
+ PCEP_ERRV_PCE_NOT_CAPABLE_P2MP_COMP = 2,
+
+ /* PCEP_ERRT_P2P_ENDPOINTS_ERROR = 17 */
+ PCEP_ERRV_NO_EP_WITH_LEAF_TYPE2 = 1,
+ PCEP_ERRV_NO_EP_WITH_LEAF_TYPE3 = 2,
+ PCEP_ERRV_NO_EP_WITH_LEAF_TYPE4 = 3,
+ PCEP_ERRV_INCONSITENT_EP = 4,
+
+ /* PCEP_ERRT_P2P_FRAGMENTATION_ERROR = 18 */
+ PCEP_ERRV_FRAG_REQUEST_FAILURE = 1,
+ PCEP_ERRV_FRAG_REPORT_FAILURE = 2,
+ PCEP_ERRV_FRAG_UPDATE_FAILURE = 3,
+ PCEP_ERRV_FRAG_INSTANTIATION_FAILURE = 4,
+
+ /* Error Values for PCEP_ERRT_INVALID_OPERATION=19 */
+ PCEP_ERRV_LSP_UPDATE_FOR_NON_DELEGATED_LSP = 1,
+ PCEP_ERRV_LSP_UPDATE_NON_ADVERTISED_PCE = 2,
+ PCEP_ERRV_LSP_UPDATE_UNKNOWN_PLSP_ID = 3,
+ /* 4: unassigned */
+ PCEP_ERRV_LSP_REPORT_NON_ADVERTISED_PCE = 5,
+ PCEP_ERRV_PCE_INIT_LSP_LIMIT_REACHED = 6,
+ PCEP_ERRV_PCE_INIT_LSP_DELEGATION_CANT_REVOKE = 7,
+ PCEP_ERRV_LSP_INIT_NON_ZERO_PLSP_ID = 8,
+ PCEP_ERRV_LSP_NOT_PCE_INITIATED = 9,
+ PCEP_ERRV_PCE_INIT_OP_FREQ_LIMIT_REACHED = 10,
+ PCEP_ERRV_LSP_REPORT_P2MP_NOT_ADVERTISED = 11,
+ PCEP_ERRV_LSP_UPDATE_P2MP_NOT_ADVERTISED = 12,
+ PCEP_ERRV_LSP_INSTANTIATION_P2MP_NOT_ADVERTISED = 13,
+ PCEP_ERRV_AUTO_BW_CAP_NOT_ADVERTISED = 14,
+
+ /* Error Values for PCEP_ERRT_LSP_STATE_SYNC_ERROR=20 */
+ PCEP_ERRV_PCE_CANT_PROCESS_LSP_REPORT = 1,
+ PCEP_ERRV_LSP_DB_VERSION_MISMATCH = 2,
+ PCEP_ERRV_TRIGGER_ATTEMPT_BEFORE_PCE_TRIGGER = 3,
+ PCEP_ERRV_TRIGGER_ATTEMPT_NO_PCE_TRIGGER_CAP = 4,
+ PCEP_ERRV_PCC_CANT_COMPLETE_STATE_SYNC = 5,
+ PCEP_ERRV_INVALID_LSP_DB_VERSION_NUMBER = 6,
+ PCEP_ERRV_INVALID_SPEAKER_ENTITY_ID = 7,
+
+ /* PCEP_ERRT_INVALID_TE_PATH_SETUP_TYPE = 21 */
+ PCEP_ERRV_UNSUPPORTED_PATH_SETUP_TYPE = 1,
+ PCEP_ERRV_MISMATCHED_PATH_SETUP_TYPE = 2,
+
+ /* PCEP_ERRT_UNASSIGNED22 = 22 */
+
+ /* Error Values for PCEP_ERRT_BAD_PARAMETER_VALUE=23 */
+ PCEP_ERRV_SYMBOLIC_PATH_NAME_IN_USE = 1,
+ PCEP_ERRV_LSP_SPEAKER_ID_NOT_PCE_INITIATED = 2,
+
+ /* Error Values for PCEP_ERRT_LSP_INSTANTIATE_ERROR=24 */
+ PCEP_ERRV_UNACCEPTABLE_INSTANTIATE_ERROR = 1,
+ PCEP_ERRV_INTERNAL_ERROR = 2,
+ PCEP_ERRV_SIGNALLING_ERROR = 3,
+
+ /* PCEP_ERRT_START_TLS_FAILURE = 25 */
+ PCEP_ERRV_START_TLS_AFTER_PCEP_EXCHANGE = 1,
+ PCEP_ERRV_MSG_NOT_START_TLS_OPEN_ERROR = 2,
+ PCEP_ERRV_CONNECTION_WO_TLS_NOT_POSSIBLE = 3,
+ PCEP_ERRV_CONNECTION_WO_TLS_IS_POSSIBLE = 4,
+ PCEP_ERRV_NO_START_TLS_BEFORE_START_TLS_WAIT_TIMER = 5,
+
+ /* PCEP_ERRT_ASSOCIATION_ERROR = 26 */
+ PCEP_ERRV_ASSOC_TYPE_NOT_SUPPORTED = 1,
+ PCEP_ERRV_TOO_MANY_LSPS_IN_ASSOC_GRP = 2,
+ PCEP_ERRV_TOO_MANY_ASSOC_GROUPS = 3,
+ PCEP_ERRV_ASSOCIATION_UNKNOWN = 4,
+ PCEP_ERRV_OP_CONF_ASSOC_INFO_MISMATCH = 5,
+ PCEP_ERRV_ASSOC_INFO_MISMATCH = 6,
+ PCEP_ERRV_CANNOT_JOIN_ASSOC_GROUP = 7,
+ PCEP_ERRV_ASSOC_ID_NOT_IN_RANGE = 8,
+ PCEP_ERRV_TUNNEL_EP_MISMATCH_PATH_PROT_ASSOC = 9,
+ PCEP_ERRV_ATTEMPTED_ADD_LSP_PATH_PROT_ASSOC = 10,
+ PCEP_ERRV_PROTECTION_TYPE_NOT_SUPPORTED = 11,
+
+ /* PCEP_ERRT_WSON_RWA_ERROR = 27 */
+ PCEP_ERRV_RWA_INSUFFICIENT_MEMORY = 1,
+ PCEP_ERRV_RWA_COMP_NOT_SUPPORTED = 2,
+ PCEP_ERRV_SYNTAX_ENC_ERROR = 3,
+
+ /* PCEP_ERRT_H_PCE_ERROR = 28 */
+ PCEP_ERRV_H_PCE_CAP_NOT_ADVERTISED = 1,
+ PCEP_ERRV_PARENT_PCE_CAP_CANT_BE_PROVIDED = 2,
+
+ /* PCEP_ERRT_PATH_COMP_FAILURE = 29 */
+ PCEP_ERRV_UNACCEPTABLE_REQUEST_MSG = 1,
+ PCEP_ERRV_GENERALIZED_BW_VAL_NOT_SUPPORTED = 2,
+ PCEP_ERRV_LABEL_SET_CONSTRAINT_COULD_NOT_BE_MET = 3,
+ PCEP_ERRV_LABEL_CONSTRAINT_COULD_NOT_BE_MET = 4,
+
+};
+
+const char *get_error_type_str(enum pcep_error_type error_type);
+const char *get_error_value_str(enum pcep_error_type error_type,
+ enum pcep_error_value error_value);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pceplib/pcep_msg_objects.c b/pceplib/pcep_msg_objects.c
new file mode 100644
index 000000000..6c943ddc2
--- /dev/null
+++ b/pceplib/pcep_msg_objects.c
@@ -0,0 +1,854 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * This is the implementation of a High Level PCEP message object API.
+ */
+
+#include <string.h>
+#include <arpa/inet.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tlvs.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+/* Internal common function used to create a pcep_object and populate the header
+ */
+static struct pcep_object_header *pcep_obj_create_common_with_tlvs(
+ uint8_t obj_length, enum pcep_object_classes object_class,
+ enum pcep_object_types object_type, double_linked_list *tlv_list)
+{
+ uint8_t *buffer = pceplib_malloc(PCEPLIB_MESSAGES, obj_length);
+ memset(buffer, 0, obj_length);
+
+ /* The flag_p and flag_i flags will be set externally */
+ struct pcep_object_header *hdr = (struct pcep_object_header *)buffer;
+ hdr->object_class = object_class;
+ hdr->object_type = object_type;
+ hdr->tlv_list = tlv_list;
+
+ return hdr;
+}
+
+static struct pcep_object_header *
+pcep_obj_create_common(uint8_t obj_length,
+ enum pcep_object_classes object_class,
+ enum pcep_object_types object_type)
+{
+ return pcep_obj_create_common_with_tlvs(obj_length, object_class,
+ object_type, NULL);
+}
+
+struct pcep_object_open *pcep_obj_create_open(uint8_t keepalive,
+ uint8_t deadtimer, uint8_t sid,
+ double_linked_list *tlv_list)
+{
+ struct pcep_object_open *open =
+ (struct pcep_object_open *)pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_open), PCEP_OBJ_CLASS_OPEN,
+ PCEP_OBJ_TYPE_OPEN, tlv_list);
+
+ open->open_version =
+ PCEP_OBJECT_OPEN_VERSION; /* PCEP version. Current version is 1
+ /No flags are currently defined. */
+ open->open_keepalive =
+ keepalive; /* Maximum period of time between two consecutive
+ PCEP messages sent by the sender. */
+ open->open_deadtimer = deadtimer; /* Specifies the amount of time before
+ closing the session down. */
+ open->open_sid = sid; /* PCEP session number that identifies the current
+ session. */
+
+ return open;
+}
+
+struct pcep_object_rp *pcep_obj_create_rp(uint8_t priority, bool flag_r,
+ bool flag_b, bool flag_s,
+ bool flag_of, uint32_t reqid,
+ double_linked_list *tlv_list)
+{
+ if (priority > OBJECT_RP_MAX_PRIORITY) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Error creating RP object, invalid priority [%d], max priority [%d].",
+ __func__, priority, OBJECT_RP_MAX_PRIORITY);
+ return NULL;
+ }
+
+ struct pcep_object_rp *obj =
+ (struct pcep_object_rp *)pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_rp), PCEP_OBJ_CLASS_RP,
+ PCEP_OBJ_TYPE_RP, tlv_list);
+
+ obj->priority = priority;
+ obj->flag_reoptimization = flag_r;
+ obj->flag_bidirectional = flag_b;
+ obj->flag_strict = flag_s;
+ obj->flag_of = flag_of;
+ obj->request_id = reqid;
+
+ return obj;
+}
+
+struct pcep_object_notify *
+pcep_obj_create_notify(enum pcep_notification_types notification_type,
+ enum pcep_notification_values notification_value)
+{
+ struct pcep_object_notify *obj =
+ (struct pcep_object_notify *)pcep_obj_create_common(
+ sizeof(struct pcep_object_notify), PCEP_OBJ_CLASS_NOTF,
+ PCEP_OBJ_TYPE_NOTF);
+
+ obj->notification_type = notification_type;
+ obj->notification_value = notification_value;
+
+ return obj;
+}
+
+struct pcep_object_nopath *
+pcep_obj_create_nopath(uint8_t ni, bool flag_c,
+ enum pcep_nopath_tlv_err_codes error_code)
+{
+ struct pcep_object_tlv_nopath_vector *tlv =
+ pcep_tlv_create_nopath_vector(error_code);
+ double_linked_list *tlv_list = dll_initialize();
+ dll_append(tlv_list, tlv);
+
+ struct pcep_object_nopath *obj =
+ (struct pcep_object_nopath *)pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_nopath),
+ PCEP_OBJ_CLASS_NOPATH, PCEP_OBJ_TYPE_NOPATH, tlv_list);
+
+ obj->ni = ni;
+ obj->flag_c = flag_c;
+ obj->err_code = error_code;
+
+ return obj;
+}
+
+struct pcep_object_association_ipv4 *
+pcep_obj_create_association_ipv4(bool r_flag, uint16_t association_type,
+ uint16_t association_id, struct in_addr src)
+{
+ struct pcep_object_association_ipv4 *obj =
+ (struct pcep_object_association_ipv4 *)pcep_obj_create_common(
+ sizeof(struct pcep_object_association_ipv4),
+ PCEP_OBJ_CLASS_ASSOCIATION,
+ PCEP_OBJ_TYPE_ASSOCIATION_IPV4);
+
+ obj->R_flag = r_flag;
+ obj->association_type = association_type;
+ obj->association_id = association_id;
+ obj->src = src;
+
+ return obj;
+}
+struct pcep_object_association_ipv6 *
+pcep_obj_create_association_ipv6(bool r_flag, uint16_t association_type,
+ uint16_t association_id, struct in6_addr src)
+{
+ struct pcep_object_association_ipv6 *obj =
+ (struct pcep_object_association_ipv6 *)pcep_obj_create_common(
+ sizeof(struct pcep_object_association_ipv6),
+ PCEP_OBJ_CLASS_ASSOCIATION,
+ PCEP_OBJ_TYPE_ASSOCIATION_IPV6);
+
+ obj->R_flag = r_flag;
+ obj->association_type = association_type;
+ obj->association_id = association_id;
+ obj->src = src;
+
+ return obj;
+}
+struct pcep_object_endpoints_ipv4 *
+pcep_obj_create_endpoint_ipv4(const struct in_addr *src_ipv4,
+ const struct in_addr *dst_ipv4)
+{
+ if (src_ipv4 == NULL || dst_ipv4 == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_endpoints_ipv4 *obj =
+ (struct pcep_object_endpoints_ipv4 *)pcep_obj_create_common(
+ sizeof(struct pcep_object_endpoints_ipv4),
+ PCEP_OBJ_CLASS_ENDPOINTS, PCEP_OBJ_TYPE_ENDPOINT_IPV4);
+
+ obj->src_ipv4.s_addr = src_ipv4->s_addr;
+ obj->dst_ipv4.s_addr = dst_ipv4->s_addr;
+
+ return obj;
+}
+
+struct pcep_object_endpoints_ipv6 *
+pcep_obj_create_endpoint_ipv6(const struct in6_addr *src_ipv6,
+ const struct in6_addr *dst_ipv6)
+{
+ if (src_ipv6 == NULL || dst_ipv6 == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_endpoints_ipv6 *obj =
+ (struct pcep_object_endpoints_ipv6 *)pcep_obj_create_common(
+ sizeof(struct pcep_object_endpoints_ipv6),
+ PCEP_OBJ_CLASS_ENDPOINTS, PCEP_OBJ_TYPE_ENDPOINT_IPV6);
+
+ memcpy(&obj->src_ipv6, src_ipv6, sizeof(struct in6_addr));
+ memcpy(&obj->dst_ipv6, dst_ipv6, sizeof(struct in6_addr));
+
+ return obj;
+}
+
+struct pcep_object_bandwidth *pcep_obj_create_bandwidth(float bandwidth)
+{
+ struct pcep_object_bandwidth *obj =
+ (struct pcep_object_bandwidth *)pcep_obj_create_common(
+ sizeof(struct pcep_object_bandwidth),
+ PCEP_OBJ_CLASS_BANDWIDTH, PCEP_OBJ_TYPE_BANDWIDTH_REQ);
+
+ obj->bandwidth = bandwidth;
+
+ return obj;
+}
+
+struct pcep_object_metric *pcep_obj_create_metric(enum pcep_metric_types type,
+ bool flag_b, bool flag_c,
+ float value)
+{
+ struct pcep_object_metric *obj =
+ (struct pcep_object_metric *)pcep_obj_create_common(
+ sizeof(struct pcep_object_metric),
+ PCEP_OBJ_CLASS_METRIC, PCEP_OBJ_TYPE_METRIC);
+
+ obj->flag_b = flag_b;
+ obj->flag_c = flag_c;
+ obj->type = type;
+ obj->value = value;
+
+ return obj;
+}
+
+struct pcep_object_lspa *
+pcep_obj_create_lspa(uint32_t exclude_any, uint32_t include_any,
+ uint32_t include_all, uint8_t setup_priority,
+ uint8_t holding_priority, bool flag_local_protection)
+{
+ struct pcep_object_lspa *obj =
+ (struct pcep_object_lspa *)pcep_obj_create_common(
+ sizeof(struct pcep_object_lspa), PCEP_OBJ_CLASS_LSPA,
+ PCEP_OBJ_TYPE_LSPA);
+
+ obj->lspa_exclude_any = exclude_any;
+ obj->lspa_include_any = include_any;
+ obj->lspa_include_all = include_all;
+ obj->setup_priority = setup_priority;
+ obj->holding_priority = holding_priority;
+ obj->flag_local_protection = flag_local_protection;
+
+ return obj;
+}
+
+struct pcep_object_svec *
+pcep_obj_create_svec(bool srlg, bool node, bool link,
+ double_linked_list *request_id_list)
+{
+ if (request_id_list == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_svec *obj =
+ (struct pcep_object_svec *)pcep_obj_create_common(
+ sizeof(struct pcep_object_svec), PCEP_OBJ_CLASS_SVEC,
+ PCEP_OBJ_TYPE_SVEC);
+
+ obj->flag_srlg_diverse = srlg;
+ obj->flag_node_diverse = node;
+ obj->flag_link_diverse = link;
+ obj->request_id_list = request_id_list;
+
+ return obj;
+}
+
+struct pcep_object_error *
+pcep_obj_create_error(enum pcep_error_type error_type,
+ enum pcep_error_value error_value)
+{
+ struct pcep_object_error *obj =
+ (struct pcep_object_error *)pcep_obj_create_common(
+ sizeof(struct pcep_object_error), PCEP_OBJ_CLASS_ERROR,
+ PCEP_OBJ_TYPE_ERROR);
+
+ obj->error_type = error_type;
+ obj->error_value = error_value;
+
+ return obj;
+}
+
+struct pcep_object_close *pcep_obj_create_close(enum pcep_close_reason reason)
+{
+ struct pcep_object_close *obj =
+ (struct pcep_object_close *)pcep_obj_create_common(
+ sizeof(struct pcep_object_close), PCEP_OBJ_CLASS_CLOSE,
+ PCEP_OBJ_TYPE_CLOSE);
+
+ obj->reason = reason;
+
+ return obj;
+}
+
+struct pcep_object_srp *pcep_obj_create_srp(bool lsp_remove,
+ uint32_t srp_id_number,
+ double_linked_list *tlv_list)
+{
+ struct pcep_object_srp *obj =
+ (struct pcep_object_srp *)pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_srp), PCEP_OBJ_CLASS_SRP,
+ PCEP_OBJ_TYPE_SRP, tlv_list);
+
+ obj->flag_lsp_remove = lsp_remove;
+ obj->srp_id_number = srp_id_number;
+
+ return obj;
+}
+
+struct pcep_object_lsp *
+pcep_obj_create_lsp(uint32_t plsp_id, enum pcep_lsp_operational_status status,
+ bool c_flag, bool a_flag, bool r_flag, bool s_flag,
+ bool d_flag, double_linked_list *tlv_list)
+{
+ /* The plsp_id is only 20 bits */
+ if (plsp_id > MAX_PLSP_ID) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_obj_create_lsp invalid plsp_id [%d] max value [%d]",
+ __func__, plsp_id, MAX_PLSP_ID);
+ return NULL;
+ }
+
+ /* The status is only 3 bits */
+ if (status > MAX_LSP_STATUS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_obj_create_lsp invalid status [%d] max value [%d]",
+ __func__, plsp_id, MAX_PLSP_ID);
+ return NULL;
+ }
+
+ struct pcep_object_lsp *obj =
+ (struct pcep_object_lsp *)pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_lsp), PCEP_OBJ_CLASS_LSP,
+ PCEP_OBJ_TYPE_LSP, tlv_list);
+
+ obj->plsp_id = plsp_id;
+ obj->operational_status = status;
+ obj->flag_c = c_flag;
+ obj->flag_a = a_flag;
+ obj->flag_r = r_flag;
+ obj->flag_s = s_flag;
+ obj->flag_d = d_flag;
+
+ return obj;
+}
+
+struct pcep_object_vendor_info *
+pcep_obj_create_vendor_info(uint32_t enterprise_number,
+ uint32_t enterprise_spec_info)
+{
+ struct pcep_object_vendor_info *obj =
+ (struct pcep_object_vendor_info *)pcep_obj_create_common(
+ sizeof(struct pcep_object_vendor_info),
+ PCEP_OBJ_CLASS_VENDOR_INFO, PCEP_OBJ_TYPE_VENDOR_INFO);
+
+ obj->enterprise_number = enterprise_number;
+ obj->enterprise_specific_info = enterprise_spec_info;
+
+ return obj;
+}
+
+struct pcep_object_inter_layer *
+pcep_obj_create_inter_layer(bool flag_i, bool flag_m, bool flag_t)
+{
+ struct pcep_object_inter_layer *obj =
+ (struct pcep_object_inter_layer *)pcep_obj_create_common(
+ sizeof(struct pcep_object_inter_layer),
+ PCEP_OBJ_CLASS_INTER_LAYER, PCEP_OBJ_TYPE_INTER_LAYER);
+
+ obj->flag_i = flag_i;
+ obj->flag_m = flag_m;
+ obj->flag_t = flag_t;
+
+ return obj;
+}
+
+struct pcep_object_switch_layer *
+pcep_obj_create_switch_layer(double_linked_list *switch_layer_rows)
+{
+ struct pcep_object_switch_layer *obj =
+ (struct pcep_object_switch_layer *)pcep_obj_create_common(
+ sizeof(struct pcep_object_switch_layer),
+ PCEP_OBJ_CLASS_SWITCH_LAYER,
+ PCEP_OBJ_TYPE_SWITCH_LAYER);
+
+ obj->switch_layer_rows = switch_layer_rows;
+
+ return obj;
+}
+
+struct pcep_object_req_adap_cap *
+pcep_obj_create_req_adap_cap(enum pcep_switching_capability sw_cap,
+ enum pcep_lsp_encoding_type encoding)
+{
+ struct pcep_object_req_adap_cap *obj =
+ (struct pcep_object_req_adap_cap *)pcep_obj_create_common(
+ sizeof(struct pcep_object_req_adap_cap),
+ PCEP_OBJ_CLASS_REQ_ADAP_CAP,
+ PCEP_OBJ_TYPE_REQ_ADAP_CAP);
+
+ obj->switching_capability = sw_cap;
+ obj->encoding = encoding;
+
+ return obj;
+}
+
+struct pcep_object_server_indication *
+pcep_obj_create_server_indication(enum pcep_switching_capability sw_cap,
+ enum pcep_lsp_encoding_type encoding,
+ double_linked_list *tlv_list)
+{
+ struct pcep_object_server_indication *obj =
+ (struct pcep_object_server_indication *)
+ pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_server_indication),
+ PCEP_OBJ_CLASS_SERVER_IND,
+ PCEP_OBJ_TYPE_SERVER_IND, tlv_list);
+
+ obj->switching_capability = sw_cap;
+ obj->encoding = encoding;
+
+ return obj;
+}
+
+struct pcep_object_objective_function *
+pcep_obj_create_objective_function(uint16_t of_code,
+ double_linked_list *tlv_list)
+{
+ struct pcep_object_objective_function *obj =
+ (struct pcep_object_objective_function *)
+ pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_objective_function),
+ PCEP_OBJ_CLASS_OF, PCEP_OBJ_TYPE_OF, tlv_list);
+
+ obj->of_code = of_code;
+
+ return obj;
+}
+
+/* Wrap a list of ro subobjects in a structure with an object header */
+struct pcep_object_ro *pcep_obj_create_ero(double_linked_list *ero_list)
+{
+ struct pcep_object_ro *ero =
+ (struct pcep_object_ro *)pcep_obj_create_common(
+ sizeof(struct pcep_object_ro), PCEP_OBJ_CLASS_ERO,
+ PCEP_OBJ_TYPE_ERO);
+ ero->sub_objects = ero_list;
+
+ return ero;
+}
+
+/* Wrap a list of ro subobjects in a structure with an object header */
+struct pcep_object_ro *pcep_obj_create_iro(double_linked_list *iro_list)
+{
+ struct pcep_object_ro *iro =
+ (struct pcep_object_ro *)pcep_obj_create_common(
+ sizeof(struct pcep_object_ro), PCEP_OBJ_CLASS_IRO,
+ PCEP_OBJ_TYPE_IRO);
+ iro->sub_objects = iro_list;
+
+ return iro;
+}
+
+/* Wrap a list of ro subobjects in a structure with an object header */
+struct pcep_object_ro *pcep_obj_create_rro(double_linked_list *rro_list)
+{
+ struct pcep_object_ro *rro =
+ (struct pcep_object_ro *)pcep_obj_create_common(
+ sizeof(struct pcep_object_ro), PCEP_OBJ_CLASS_RRO,
+ PCEP_OBJ_TYPE_RRO);
+ rro->sub_objects = rro_list;
+
+ return rro;
+}
+
+/*
+ * Route Object Sub-object creation functions
+ */
+
+static struct pcep_object_ro_subobj *
+pcep_obj_create_ro_subobj_common(uint8_t subobj_size,
+ enum pcep_ro_subobj_types ro_subobj_type,
+ bool flag_subobj_loose_hop)
+{
+ struct pcep_object_ro_subobj *ro_subobj =
+ pceplib_malloc(PCEPLIB_MESSAGES, subobj_size);
+ memset(ro_subobj, 0, subobj_size);
+ ro_subobj->flag_subobj_loose_hop = flag_subobj_loose_hop;
+ ro_subobj->ro_subobj_type = ro_subobj_type;
+
+ return ro_subobj;
+}
+
+struct pcep_ro_subobj_ipv4 *
+pcep_obj_create_ro_subobj_ipv4(bool loose_hop, const struct in_addr *rro_ipv4,
+ uint8_t prefix_length, bool flag_local_prot)
+{
+ if (rro_ipv4 == NULL) {
+ return NULL;
+ }
+
+ struct pcep_ro_subobj_ipv4 *obj =
+ (struct pcep_ro_subobj_ipv4 *)pcep_obj_create_ro_subobj_common(
+ sizeof(struct pcep_ro_subobj_ipv4), RO_SUBOBJ_TYPE_IPV4,
+ loose_hop);
+ obj->ip_addr.s_addr = rro_ipv4->s_addr;
+ obj->prefix_length = prefix_length;
+ obj->flag_local_protection = flag_local_prot;
+
+ return obj;
+}
+
+struct pcep_ro_subobj_ipv6 *
+pcep_obj_create_ro_subobj_ipv6(bool loose_hop, const struct in6_addr *rro_ipv6,
+ uint8_t prefix_length, bool flag_local_prot)
+{
+ if (rro_ipv6 == NULL) {
+ return NULL;
+ }
+
+ struct pcep_ro_subobj_ipv6 *obj =
+ (struct pcep_ro_subobj_ipv6 *)pcep_obj_create_ro_subobj_common(
+ sizeof(struct pcep_ro_subobj_ipv6), RO_SUBOBJ_TYPE_IPV6,
+ loose_hop);
+ obj->prefix_length = prefix_length;
+ obj->flag_local_protection = flag_local_prot;
+ memcpy(&obj->ip_addr, rro_ipv6, sizeof(struct in6_addr));
+
+ return obj;
+}
+
+struct pcep_ro_subobj_unnum *
+pcep_obj_create_ro_subobj_unnum(struct in_addr *router_id, uint32_t if_id)
+{
+ if (router_id == NULL) {
+ return NULL;
+ }
+
+ struct pcep_ro_subobj_unnum *obj =
+ (struct pcep_ro_subobj_unnum *)pcep_obj_create_ro_subobj_common(
+ sizeof(struct pcep_ro_subobj_unnum),
+ RO_SUBOBJ_TYPE_UNNUM, false);
+ obj->interface_id = if_id;
+ obj->router_id.s_addr = router_id->s_addr;
+
+ return obj;
+}
+
+struct pcep_ro_subobj_32label *
+pcep_obj_create_ro_subobj_32label(bool flag_global_label, uint8_t class_type,
+ uint32_t label)
+{
+ struct pcep_ro_subobj_32label *obj = (struct pcep_ro_subobj_32label *)
+ pcep_obj_create_ro_subobj_common(
+ sizeof(struct pcep_ro_subobj_32label),
+ RO_SUBOBJ_TYPE_LABEL, false);
+ obj->class_type = class_type;
+ obj->flag_global_label = flag_global_label;
+ obj->label = label;
+
+ return obj;
+}
+
+struct pcep_ro_subobj_asn *pcep_obj_create_ro_subobj_asn(uint16_t asn)
+{
+ struct pcep_ro_subobj_asn *obj =
+ (struct pcep_ro_subobj_asn *)pcep_obj_create_ro_subobj_common(
+ sizeof(struct pcep_ro_subobj_asn), RO_SUBOBJ_TYPE_ASN,
+ false);
+ obj->asn = asn;
+
+ return obj;
+}
+
+/* Internal util function to create pcep_ro_subobj_sr sub-objects */
+static struct pcep_ro_subobj_sr *
+pcep_obj_create_ro_subobj_sr_common(enum pcep_sr_subobj_nai nai_type,
+ bool loose_hop, bool f_flag, bool s_flag,
+ bool c_flag_in, bool m_flag_in)
+{
+ struct pcep_ro_subobj_sr *obj =
+ (struct pcep_ro_subobj_sr *)pcep_obj_create_ro_subobj_common(
+ sizeof(struct pcep_ro_subobj_sr), RO_SUBOBJ_TYPE_SR,
+ loose_hop);
+
+ /* Flag logic according to draft-ietf-pce-segment-routing-16 */
+ bool c_flag = c_flag_in;
+ bool m_flag = m_flag_in;
+ if (s_flag) {
+ c_flag = false;
+ m_flag = false;
+ }
+
+ if (m_flag == false) {
+ c_flag = false;
+ }
+
+ obj->nai_type = nai_type;
+ obj->flag_f = f_flag;
+ obj->flag_s = s_flag;
+ obj->flag_c = c_flag;
+ obj->flag_m = m_flag;
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_nonai(bool loose_hop,
+ uint32_t sid,
+ bool c_flag,
+ bool m_flag)
+{
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=0, the F bit MUST be 1, the S bit MUST be zero and the
+ * Length MUST be 8. */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_ABSENT, loose_hop, true, false, c_flag,
+ m_flag);
+ obj->sid = sid;
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *
+pcep_obj_create_ro_subobj_sr_ipv4_node(bool loose_hop, bool sid_absent,
+ bool c_flag, bool m_flag, uint32_t sid,
+ struct in_addr *ipv4_node_id)
+{
+ if (ipv4_node_id == NULL) {
+ return NULL;
+ }
+
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=1, the F bit MUST be zero. If the S bit is 1, the Length
+ * MUST be 8, otherwise the Length MUST be 12 */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE, loose_hop, false, sid_absent,
+ c_flag, m_flag);
+
+ if (!sid_absent) {
+ obj->sid = sid;
+ }
+ obj->nai_list = dll_initialize();
+ /* Since the IP has to be stored in the list, copy it so the caller
+ * doesnt have any restrictions about the type of memory used externally
+ * for the IP. This memory will be freed with the object is freed. */
+ struct in_addr *ipv4_node_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in_addr));
+ ipv4_node_id_copy->s_addr = ipv4_node_id->s_addr;
+ dll_append(obj->nai_list, ipv4_node_id_copy);
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *
+pcep_obj_create_ro_subobj_sr_ipv6_node(bool loose_hop, bool sid_absent,
+ bool c_flag, bool m_flag, uint32_t sid,
+ struct in6_addr *ipv6_node_id)
+{
+ if (ipv6_node_id == NULL) {
+ return NULL;
+ }
+
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=2, the F bit MUST be zero. If the S bit is 1, the Length
+ * MUST be 20, otherwise the Length MUST be 24. */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_IPV6_NODE, loose_hop, false, sid_absent,
+ c_flag, m_flag);
+
+ if (!sid_absent) {
+ obj->sid = sid;
+ }
+ obj->nai_list = dll_initialize();
+ struct in6_addr *ipv6_node_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr));
+ memcpy(ipv6_node_id_copy, ipv6_node_id, sizeof(struct in6_addr));
+ dll_append(obj->nai_list, ipv6_node_id_copy);
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_ipv4_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ struct in_addr *local_ipv4, struct in_addr *remote_ipv4)
+{
+ if (local_ipv4 == NULL || remote_ipv4 == NULL) {
+ return NULL;
+ }
+
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=3, the F bit MUST be zero. If the S bit is 1, the Length
+ * MUST be 12, otherwise the Length MUST be 16 */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY, loose_hop, false, sid_absent,
+ c_flag, m_flag);
+
+ if (!sid_absent) {
+ obj->sid = sid;
+ }
+ obj->nai_list = dll_initialize();
+ struct in_addr *local_ipv4_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in_addr));
+ struct in_addr *remote_ipv4_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in_addr));
+ local_ipv4_copy->s_addr = local_ipv4->s_addr;
+ remote_ipv4_copy->s_addr = remote_ipv4->s_addr;
+ dll_append(obj->nai_list, local_ipv4_copy);
+ dll_append(obj->nai_list, remote_ipv4_copy);
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_ipv6_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ struct in6_addr *local_ipv6, struct in6_addr *remote_ipv6)
+{
+ if (local_ipv6 == NULL || remote_ipv6 == NULL) {
+ return NULL;
+ }
+
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=4, the F bit MUST be zero. If the S bit is 1, the Length
+ * MUST be 36, otherwise the Length MUST be 40 */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY, loose_hop, false, sid_absent,
+ c_flag, m_flag);
+
+ if (!sid_absent) {
+ obj->sid = sid;
+ }
+ obj->nai_list = dll_initialize();
+ struct in6_addr *local_ipv6_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr));
+ struct in6_addr *remote_ipv6_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr));
+ memcpy(local_ipv6_copy, local_ipv6, sizeof(struct in6_addr));
+ memcpy(remote_ipv6_copy, remote_ipv6, sizeof(struct in6_addr));
+ dll_append(obj->nai_list, local_ipv6_copy);
+ dll_append(obj->nai_list, remote_ipv6_copy);
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ uint32_t local_node_id, uint32_t local_if_id, uint32_t remote_node_id,
+ uint32_t remote_if_id)
+{
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=5, the F bit MUST be zero. If the S bit is 1, the Length
+ * MUST be 20, otherwise the Length MUST be 24. */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY, loose_hop, false,
+ sid_absent, c_flag, m_flag);
+
+ if (!sid_absent) {
+ obj->sid = sid;
+ }
+
+ obj->nai_list = dll_initialize();
+ uint32_t *local_node_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *local_node_id_copy = local_node_id;
+ dll_append(obj->nai_list, local_node_id_copy);
+
+ uint32_t *local_if_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *local_if_id_copy = local_if_id;
+ dll_append(obj->nai_list, local_if_id_copy);
+
+ uint32_t *remote_node_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *remote_node_id_copy = remote_node_id;
+ dll_append(obj->nai_list, remote_node_id_copy);
+
+ uint32_t *remote_if_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *remote_if_id_copy = remote_if_id;
+ dll_append(obj->nai_list, remote_if_id_copy);
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ struct in6_addr *local_ipv6, uint32_t local_if_id,
+ struct in6_addr *remote_ipv6, uint32_t remote_if_id)
+{
+ if (local_ipv6 == NULL || remote_ipv6 == NULL) {
+ return NULL;
+ }
+
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=6, the F bit MUST be zero. If the S bit is 1, the Length
+ * MUST be 44, otherwise the Length MUST be 48 */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY, loose_hop, false,
+ sid_absent, c_flag, m_flag);
+
+ if (!sid_absent) {
+ obj->sid = sid;
+ }
+ obj->nai_list = dll_initialize();
+ struct in6_addr *local_ipv6_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr));
+ memcpy(local_ipv6_copy, local_ipv6, sizeof(struct in6_addr));
+ dll_append(obj->nai_list, local_ipv6_copy);
+
+ uint32_t *local_if_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *local_if_id_copy = local_if_id;
+ dll_append(obj->nai_list, local_if_id_copy);
+
+ struct in6_addr *remote_ipv6_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr));
+ memcpy(remote_ipv6_copy, remote_ipv6, sizeof(struct in6_addr));
+ dll_append(obj->nai_list, remote_ipv6_copy);
+
+ uint32_t *remote_if_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *remote_if_id_copy = remote_if_id;
+ dll_append(obj->nai_list, remote_if_id_copy);
+
+ return obj;
+}
diff --git a/pceplib/pcep_msg_objects.h b/pceplib/pcep_msg_objects.h
new file mode 100644
index 000000000..959a6f8cf
--- /dev/null
+++ b/pceplib/pcep_msg_objects.h
@@ -0,0 +1,741 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ */
+
+
+/*
+ * This is a High Level PCEP message object API.
+ */
+
+#ifndef PCEP_OBJECTS_H
+#define PCEP_OBJECTS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "pcep.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_msg_object_error_types.h"
+#include "pcep_msg_tlvs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Regarding memory usage:
+ * When creating objects, any objects passed into these APIs will be free'd when
+ * the enclosing pcep_message is free'd. That includes the double_linked_list's.
+ * So, just create the objects and TLVs, put them in their double_linked_list's,
+ * and everything will be managed internally. The enclosing message will be
+ * deleted by pcep_msg_free_message() or pcep_msg_free_message_list() which,
+ * in turn will call one of: pcep_obj_free_object() and pcep_obj_free_tlv().
+ * For received messages with objects, call pcep_msg_free_message() to free
+ * them.
+ */
+
+enum pcep_object_classes {
+ PCEP_OBJ_CLASS_OPEN = 1,
+ PCEP_OBJ_CLASS_RP = 2,
+ PCEP_OBJ_CLASS_NOPATH = 3,
+ PCEP_OBJ_CLASS_ENDPOINTS = 4,
+ PCEP_OBJ_CLASS_BANDWIDTH = 5,
+ PCEP_OBJ_CLASS_METRIC = 6,
+ PCEP_OBJ_CLASS_ERO = 7,
+ PCEP_OBJ_CLASS_RRO = 8,
+ PCEP_OBJ_CLASS_LSPA = 9,
+ PCEP_OBJ_CLASS_IRO = 10,
+ PCEP_OBJ_CLASS_SVEC = 11,
+ PCEP_OBJ_CLASS_NOTF = 12,
+ PCEP_OBJ_CLASS_ERROR = 13,
+ PCEP_OBJ_CLASS_CLOSE = 15,
+ PCEP_OBJ_CLASS_OF = 21,
+ PCEP_OBJ_CLASS_LSP = 32,
+ PCEP_OBJ_CLASS_SRP = 33,
+ PCEP_OBJ_CLASS_VENDOR_INFO = 34,
+ PCEP_OBJ_CLASS_INTER_LAYER = 36, /* RFC 8282 */
+ PCEP_OBJ_CLASS_SWITCH_LAYER = 37, /* RFC 8282 */
+ PCEP_OBJ_CLASS_REQ_ADAP_CAP = 38, /* RFC 8282 */
+ PCEP_OBJ_CLASS_SERVER_IND = 39, /* RFC 8282 */
+ PCEP_OBJ_CLASS_ASSOCIATION = 40, /*draft-ietf-pce-association-group-10*/
+ PCEP_OBJ_CLASS_MAX,
+};
+
+enum pcep_object_types {
+ PCEP_OBJ_TYPE_OPEN = 1,
+ PCEP_OBJ_TYPE_RP = 1,
+ PCEP_OBJ_TYPE_NOPATH = 1,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV4 = 1,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV6 = 2,
+ PCEP_OBJ_TYPE_BANDWIDTH_REQ = 1,
+ PCEP_OBJ_TYPE_BANDWIDTH_TELSP = 2,
+ PCEP_OBJ_TYPE_BANDWIDTH_CISCO =
+ 5, /* IANA unassigned, but rcvd from Cisco PCE */
+ PCEP_OBJ_TYPE_SRP = 1,
+ PCEP_OBJ_TYPE_VENDOR_INFO = 1,
+ PCEP_OBJ_TYPE_LSP = 1,
+ PCEP_OBJ_TYPE_METRIC = 1,
+ PCEP_OBJ_TYPE_ERO = 1,
+ PCEP_OBJ_TYPE_RRO = 1,
+ PCEP_OBJ_TYPE_LSPA = 1,
+ PCEP_OBJ_TYPE_IRO = 1,
+ PCEP_OBJ_TYPE_SVEC = 1,
+ PCEP_OBJ_TYPE_NOTF = 1,
+ PCEP_OBJ_TYPE_ERROR = 1,
+ PCEP_OBJ_TYPE_CLOSE = 1,
+ PCEP_OBJ_TYPE_INTER_LAYER = 1,
+ PCEP_OBJ_TYPE_SWITCH_LAYER = 1,
+ PCEP_OBJ_TYPE_REQ_ADAP_CAP = 1,
+ PCEP_OBJ_TYPE_SERVER_IND = 1,
+ PCEP_OBJ_TYPE_ASSOCIATION_IPV4 =
+ 1, /*draft-ietf-pce-association-group-10*/
+ PCEP_OBJ_TYPE_ASSOCIATION_IPV6 =
+ 2, /*draft-ietf-pce-association-group-10*/
+ PCEP_OBJ_TYPE_OF = 1,
+ PCEP_OBJ_TYPE_MAX = 2,
+};
+
+#define OBJECT_HEADER_FLAG_I 0x01
+#define OBJECT_HEADER_FLAG_P 0x02
+
+/* The flag_p and flag_i arent set via the APIs, if they need to be set, just
+ * set them on the returned object once it has been created. */
+struct pcep_object_header {
+ enum pcep_object_classes object_class;
+ enum pcep_object_types object_type;
+ bool flag_p; /* PCC Processing rule bit: When set, the object MUST be
+ taken into account, when cleared the object is optional.
+ */
+ bool flag_i; /* PCE Ignore bit: indicates to a PCC whether or not an
+ optional object was processed */
+ double_linked_list *tlv_list;
+ /* Pointer into encoded_message field from the pcep_message */
+ const uint8_t *encoded_object;
+ uint16_t encoded_object_length;
+};
+
+#define PCEP_OBJECT_OPEN_VERSION 1
+
+struct pcep_object_open {
+ struct pcep_object_header header;
+ uint8_t open_version; /* PCEP version. Current version is 1 */
+ uint8_t open_keepalive; /* Maximum period of time between two
+ consecutive PCEP messages sent by the sender.
+ */
+ uint8_t open_deadtimer; /* Specifies the amount of time before closing
+ the session down. */
+ uint8_t open_sid; /* PCEP session number that identifies the current
+ session. */
+};
+
+#define OBJECT_RP_FLAG_R 0x08
+#define OBJECT_RP_FLAG_B 0x10
+#define OBJECT_RP_FLAG_O 0x20
+#define OBJECT_RP_FLAG_OF 0x80
+#define OBJECT_RP_MAX_PRIORITY 0x07
+
+struct pcep_object_rp {
+ struct pcep_object_header header;
+ uint8_t priority; /* 3 bit priority, max priority is 7 */
+ bool flag_reoptimization;
+ bool flag_bidirectional;
+ bool flag_strict; /* when set, a loose path is acceptable */
+ bool flag_of; /* Supply Objective Function on Response */
+ uint32_t request_id; /* The Request-id-number value combined with the
+ source for PCC & PCE creates a uniquely number.
+ */
+};
+
+enum pcep_notification_types {
+ PCEP_NOTIFY_TYPE_PENDING_REQUEST_CANCELLED = 1,
+ PCEP_NOTIFY_TYPE_PCE_OVERLOADED = 2
+};
+
+enum pcep_notification_values {
+ PCEP_NOTIFY_VALUE_PCC_CANCELLED_REQUEST = 1,
+ PCEP_NOTIFY_VALUE_PCE_CANCELLED_REQUEST = 2,
+ PCEP_NOTIFY_VALUE_PCE_CURRENTLY_OVERLOADED = 1,
+ PCEP_NOTIFY_VALUE_PCE_NO_LONGER_OVERLOADED = 2
+};
+
+struct pcep_object_notify {
+ struct pcep_object_header header;
+ enum pcep_notification_types notification_type;
+ enum pcep_notification_values notification_value;
+};
+
+enum pcep_association_type {
+ PCEP_ASSOCIATION_TYPE_PATH_PROTECTION_ASSOCIATION =
+ 1, // iana unique value define as 2020-01-08!
+ PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE =
+ 65535 // TBD1 draft-barth-pce-segment-routing-policy-cp-04
+};
+#define OBJECT_ASSOCIATION_FLAG_R 0x01
+struct pcep_object_association_ipv4 { // draft-ietf-pce-association-group-10
+ struct pcep_object_header header;
+ bool R_flag;
+ uint16_t association_type;
+ uint16_t association_id;
+ struct in_addr src;
+};
+
+struct pcep_object_association_ipv6 { // draft-ietf-pce-association-group-10
+ struct pcep_object_header header;
+ bool R_flag;
+ uint16_t association_type;
+ uint16_t association_id;
+ struct in6_addr src;
+};
+
+
+enum pcep_nopath_nature_of_issue {
+ PCEP_NOPATH_NI_NO_PATH_FOUND = 0,
+ PCEP_NOPATH_NI_PCE_CHAIN_BROKEN = 1,
+};
+
+enum pcep_nopath_tlv_err_codes {
+ PCEP_NOPATH_TLV_ERR_NO_TLV = 0,
+ PCEP_NOPATH_TLV_ERR_PCE_UNAVAILABLE = 1,
+ PCEP_NOPATH_TLV_ERR_UNKNOWN_DST = 2,
+ PCEP_NOPATH_TLV_ERR_UNKNOWN_SRC = 3
+};
+
+#define OBJECT_NOPATH_FLAG_C 0x80
+
+struct pcep_object_nopath {
+ struct pcep_object_header header;
+ uint8_t ni; /* Nature of Issue, reports the nature of the issue that led
+ to a negative reply */
+ bool flag_c; /* when set, indicates the unsatisfied constraints by
+ including relevant PCEP objects. */
+ enum pcep_nopath_tlv_err_codes
+ err_code; /* When set other than 0, an appropriate TLV will be
+ included */
+};
+
+struct pcep_object_endpoints_ipv4 {
+ struct pcep_object_header header;
+ struct in_addr src_ipv4;
+ struct in_addr dst_ipv4;
+};
+
+struct pcep_object_endpoints_ipv6 {
+ struct pcep_object_header header;
+ struct in6_addr src_ipv6;
+ struct in6_addr dst_ipv6;
+};
+
+/* PCEP floats are encoded according to:
+ * https://en.wikipedia.org/wiki/IEEE_754-1985
+ * Luckily, this is the same encoding used by C */
+struct pcep_object_bandwidth {
+ struct pcep_object_header header;
+ float bandwidth;
+};
+
+enum pcep_metric_types {
+ /* RFC 5440 */
+ PCEP_METRIC_IGP = 1,
+ PCEP_METRIC_TE = 2,
+ PCEP_METRIC_HOP_COUNT = 3,
+ /* RFC 5541 */
+ PCEP_METRIC_AGGREGATE_BW = 4,
+ PCEP_METRIC_MOST_LOADED_LINK = 5,
+ PCEP_METRIC_CUMULATIVE_IGP = 6,
+ PCEP_METRIC_CUMULATIVE_TE = 7,
+ /* RFC 8306 */
+ PCEP_METRIC_P2MP_IGP = 8,
+ PCEP_METRIC_P2MP_TE = 9,
+ PCEP_METRIC_P2MP_HOP_COUNT = 10,
+ /* RFC 8864 */
+ PCEP_METRIC_SEGMENT_ID_DEPTH = 11,
+ /* RFC 8233 */
+ PCEP_METRIC_PATH_DELAY = 12,
+ PCEP_METRIC_PATH_DELAY_VARIATION = 13,
+ PCEP_METRIC_PATH_LOSS = 14,
+ PCEP_METRIC_P2MP_PATH_DELAY = 15,
+ PCEP_METRIC_P2MP_PATH_DELAY_VARIATION = 16,
+ PCEP_METRIC_P2MP_PATH_LOSS = 17,
+ /* RFC 8282 */
+ PCEP_METRIC_NUM_PATH_ADAPTATIONS = 18,
+ PCEP_METRIC_NUM_PATH_LAYERS = 19,
+ /* RFC 8685 */
+ PCEP_METRIC_DOMAIN_COUNT = 20,
+ PCEP_METRIC_BORDER_NODE_COUNT = 21,
+};
+
+#define OBJECT_METRIC_FLAC_B 0x01
+#define OBJECT_METRIC_FLAC_C 0x02
+
+/* PCEP floats are encoded according to:
+ * https://en.wikipedia.org/wiki/IEEE_754-1985
+ * Luckily, this is the same encoding used by C */
+struct pcep_object_metric {
+ struct pcep_object_header header;
+ enum pcep_metric_types type;
+ bool flag_b; /* Bound flag */
+ bool flag_c; /* Computed metric */
+ float value; /* Metric value in 32 bits */
+};
+
+#define OBJECT_LSPA_FLAG_L 0x01
+
+struct pcep_object_lspa {
+ struct pcep_object_header header;
+ uint32_t lspa_exclude_any;
+ uint32_t lspa_include_any;
+ uint32_t lspa_include_all;
+ uint8_t setup_priority;
+ uint8_t holding_priority;
+ bool flag_local_protection; /* Local protection desired bit */
+};
+
+/* The SVEC object with some custom extensions. */
+#define OBJECT_SVEC_FLAG_L 0x01
+#define OBJECT_SVEC_FLAG_N 0x02
+#define OBJECT_SVEC_FLAG_S 0x04
+
+struct pcep_object_svec {
+ struct pcep_object_header header;
+ bool flag_link_diverse;
+ bool flag_node_diverse;
+ bool flag_srlg_diverse;
+ double_linked_list
+ *request_id_list; /* list of 32-bit request ID pointers */
+};
+
+struct pcep_object_error {
+ struct pcep_object_header header;
+ enum pcep_error_type error_type;
+ enum pcep_error_value error_value;
+};
+
+struct pcep_object_load_balancing {
+ struct pcep_object_header header;
+ uint8_t load_maxlsp; /* Maximum number of TE LSPs in the set */
+ uint32_t load_minband; /* Specifies the minimum bandwidth of each
+ element */
+};
+
+enum pcep_close_reason {
+ PCEP_CLOSE_REASON_NO = 1,
+ PCEP_CLOSE_REASON_DEADTIMER = 2,
+ PCEP_CLOSE_REASON_FORMAT = 3,
+ PCEP_CLOSE_REASON_UNKNOWN_REQ = 4,
+ PCEP_CLOSE_REASON_UNREC_MSG = 5
+};
+
+struct pcep_object_close {
+ struct pcep_object_header header;
+ enum pcep_close_reason reason;
+};
+
+/* Stateful PCE Request Parameters RFC 8231, 8281 */
+
+#define OBJECT_SRP_FLAG_R 0x01
+
+struct pcep_object_srp {
+ struct pcep_object_header header;
+ bool flag_lsp_remove; /* RFC 8281 */
+ uint32_t srp_id_number;
+};
+
+/* Label Switched Path Object RFC 8231 */
+enum pcep_lsp_operational_status {
+ PCEP_LSP_OPERATIONAL_DOWN = 0,
+ PCEP_LSP_OPERATIONAL_UP = 1,
+ PCEP_LSP_OPERATIONAL_ACTIVE = 2,
+ PCEP_LSP_OPERATIONAL_GOING_DOWN = 3,
+ PCEP_LSP_OPERATIONAL_GOING_UP = 4,
+};
+
+#define MAX_PLSP_ID 0x000fffff /* The plsp_id is only 20 bits */
+#define MAX_LSP_STATUS 0x0007 /* The status is only 3 bits */
+#define OBJECT_LSP_FLAG_D 0x01
+#define OBJECT_LSP_FLAG_S 0x02
+#define OBJECT_LSP_FLAG_R 0x04
+#define OBJECT_LSP_FLAG_A 0x08
+#define OBJECT_LSP_FLAG_C 0x80
+
+struct pcep_object_lsp {
+ struct pcep_object_header header;
+ uint32_t plsp_id; /* plsp_id is 20 bits, must be <= MAX_PLSP_ID*/
+ enum pcep_lsp_operational_status operational_status; /* max 3 bits */
+ bool flag_d;
+ bool flag_s;
+ bool flag_r;
+ bool flag_a;
+ bool flag_c;
+};
+
+/* RFC 7470 */
+struct pcep_object_vendor_info {
+ struct pcep_object_header header;
+ uint32_t enterprise_number;
+ uint32_t enterprise_specific_info;
+};
+
+/* RFC 8282 */
+#define OBJECT_INTER_LAYER_FLAG_I 0x01
+#define OBJECT_INTER_LAYER_FLAG_M 0x02
+#define OBJECT_INTER_LAYER_FLAG_T 0x04
+
+struct pcep_object_inter_layer {
+ struct pcep_object_header header;
+ bool flag_i;
+ bool flag_m;
+ bool flag_t;
+};
+
+/* RFC 8282 */
+#define OBJECT_SWITCH_LAYER_FLAG_I 0x01
+enum pcep_lsp_encoding_type {
+ /* Values taken from RFC 3471 as suggested by RFC 8282 */
+ PCEP_LSP_ENC_PACKET = 1,
+ PCEP_LSP_ENC_ETHERNET = 2,
+ PCEP_LSP_ENC_PDH = 3,
+ PCEP_LSP_ENC_RESERVED4 = 4,
+ PCEP_LSP_ENC_SDH_SONET = 5,
+ PCEP_LSP_ENC_RESERVED6 = 6,
+ PCEP_LSP_ENC_DIG_WRAPPER = 7,
+ PCEP_LSP_ENC_LAMBDA = 8,
+ PCEP_LSP_ENC_FIBER = 9,
+ PCEP_LSP_ENC_RESERVED10 = 10,
+ PCEP_LSP_ENC_FIBER_CHAN = 11
+};
+
+enum pcep_switching_capability {
+ /* Switching capability values taken from RFC 4203/3471 as suggested by
+ RFC 8282 */
+ PCEP_SW_CAP_PSC1 = 1, /* Packet-Switch Capable-1 (PSC-1) */
+ PCEP_SW_CAP_PSC2 = 2,
+ PCEP_SW_CAP_PSC3 = 3,
+ PCEP_SW_CAP_PSC4 = 4,
+ PCEP_SW_CAP_L2SC = 51, /* Layer-2 Switch Capable */
+ PCEP_SW_CAP_TDM = 100, /* Time-Division-Multiplex Capable */
+ PCEP_SW_CAP_LSC = 150, /* Lambda-Switch Capable */
+ PCEP_SW_CAP_FSC = 200 /* Fiber-Switch Capable */
+};
+
+struct pcep_object_switch_layer_row {
+ enum pcep_lsp_encoding_type lsp_encoding_type;
+ enum pcep_switching_capability switching_type;
+ bool flag_i;
+};
+
+struct pcep_object_switch_layer {
+ struct pcep_object_header header;
+ double_linked_list
+ *switch_layer_rows; /* list of struct
+ pcep_object_switch_layer_row */
+};
+
+/* RFC 8282
+ * Requested Adaptation capability */
+
+struct pcep_object_req_adap_cap {
+ struct pcep_object_header header;
+ enum pcep_switching_capability switching_capability;
+ enum pcep_lsp_encoding_type encoding;
+};
+
+/* RFC 8282 */
+
+struct pcep_object_server_indication {
+ struct pcep_object_header header;
+ enum pcep_switching_capability switching_capability;
+ enum pcep_lsp_encoding_type encoding;
+ /* This object is identical to req_adap_cap, except it allows TLVs */
+};
+
+/* Objective Function Object: RFC 5541 */
+
+struct pcep_object_objective_function {
+ struct pcep_object_header header;
+ uint16_t of_code;
+};
+
+/*
+ * Common Route Object sub-object definitions
+ * used by ERO, IRO, and RRO
+ */
+
+/* Common Route Object sub-object types
+ * used by ERO, IRO, and RRO */
+enum pcep_ro_subobj_types {
+ RO_SUBOBJ_TYPE_IPV4 = 1, /* RFC 3209 */
+ RO_SUBOBJ_TYPE_IPV6 = 2, /* RFC 3209 */
+ RO_SUBOBJ_TYPE_LABEL = 3, /* RFC 3209 */
+ RO_SUBOBJ_TYPE_UNNUM = 4, /* RFC 3477 */
+ RO_SUBOBJ_TYPE_ASN = 32, /* RFC 3209, Section 4.3.3.4 */
+ RO_SUBOBJ_TYPE_SR = 36, /* RFC 8408, draft-ietf-pce-segment-routing-16.
+ Type 5 for draft07 has been assigned to
+ something else. */
+ RO_SUBOBJ_UNKNOWN
+};
+
+struct pcep_object_ro {
+ struct pcep_object_header header;
+ double_linked_list
+ *sub_objects; /* list of struct pcep_object_ro_subobj */
+};
+
+struct pcep_object_ro_subobj {
+ bool flag_subobj_loose_hop; /* L subobj flag */
+ enum pcep_ro_subobj_types ro_subobj_type;
+};
+
+#define OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT 0x01
+
+struct pcep_ro_subobj_ipv4 {
+ struct pcep_object_ro_subobj ro_subobj;
+ struct in_addr ip_addr;
+ uint8_t prefix_length;
+ bool flag_local_protection;
+};
+
+struct pcep_ro_subobj_ipv6 {
+ struct pcep_object_ro_subobj ro_subobj;
+ struct in6_addr ip_addr;
+ uint8_t prefix_length;
+ bool flag_local_protection;
+};
+
+struct pcep_ro_subobj_unnum {
+ struct pcep_object_ro_subobj ro_subobj;
+ struct in_addr router_id;
+ uint32_t interface_id;
+};
+
+#define OBJECT_SUBOBJ_LABEL_FLAG_GLOGAL 0x01
+struct pcep_ro_subobj_32label {
+ struct pcep_object_ro_subobj ro_subobj;
+ bool flag_global_label;
+ uint8_t class_type; /* label class-type (generalized label = 2) */
+ uint32_t label; /* label supported */
+};
+
+struct pcep_ro_subobj_asn {
+ struct pcep_object_ro_subobj ro_subobj;
+ uint16_t asn; /* Autonomous system number */
+};
+
+/* The SR ERO and SR RRO subojbects are the same, except
+ * the SR-RRO does not have the L flag in the Type field.
+ * Defined in draft-ietf-pce-segment-routing-16 */
+enum pcep_sr_subobj_nai {
+ PCEP_SR_SUBOBJ_NAI_ABSENT = 0,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE = 1,
+ PCEP_SR_SUBOBJ_NAI_IPV6_NODE = 2,
+ PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY = 3,
+ PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY = 4,
+ PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY = 5,
+ PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY = 6,
+ PCEP_SR_SUBOBJ_NAI_UNKNOWN
+};
+
+#define OBJECT_SUBOBJ_SR_FLAG_M 0x01
+#define OBJECT_SUBOBJ_SR_FLAG_C 0x02
+#define OBJECT_SUBOBJ_SR_FLAG_S 0x04
+#define OBJECT_SUBOBJ_SR_FLAG_F 0x08
+
+struct pcep_ro_subobj_sr {
+ struct pcep_object_ro_subobj ro_subobj;
+ enum pcep_sr_subobj_nai nai_type;
+ bool flag_f;
+ bool flag_s;
+ bool flag_c;
+ bool flag_m;
+
+ /* The SID and NAI are optional depending on the flags,
+ * and the NAI can be variable length */
+ uint32_t sid;
+ double_linked_list
+ *nai_list; /* double linked list of in_addr or in6_addr */
+};
+
+/* Macros to make a SID Label
+ *
+ * 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Label
+ | Label | TC |S| TTL | Stack
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Entry
+ */
+#define ENCODE_SR_ERO_SID(label_20bits, tc_3bits, stack_bottom_bit, ttl_8bits) \
+ ((((label_20bits) << 12) & 0xfffff000) \
+ | (((tc_3bits) << 9) & 0x00000e00) \
+ | (((stack_bottom_bit) << 8) & 0x00000100) | ((ttl_8bits)&0xff))
+#define GET_SR_ERO_SID_LABEL(SID) ((SID & 0xfffff000) >> 12)
+#define GET_SR_ERO_SID_TC(SID) ((SID & 0x00000e00) >> 9)
+#define GET_SR_ERO_SID_S(SID) ((SID & 0x00000100) >> 8)
+#define GET_SR_ERO_SID_TTL(SID) ((SID & 0x000000ff))
+
+/*
+ * All created objects will be in Host byte order, except for IPs.
+ * All IP addresses are expected to be passed-in in Network byte order,
+ * and any objects received will have their IPs in Network byte order.
+ * The message containing the objects should be converted to Network byte order
+ * with pcep_encode_msg_header() before sending, which will also convert the
+ * Objects, TLVs, and sub-objects.
+ */
+
+struct pcep_object_open *pcep_obj_create_open(uint8_t keepalive,
+ uint8_t deadtimer, uint8_t sid,
+ double_linked_list *tlv_list);
+struct pcep_object_rp *pcep_obj_create_rp(uint8_t priority, bool flag_r,
+ bool flag_b, bool flag_s,
+ bool flag_of, uint32_t reqid,
+ double_linked_list *tlv_list);
+struct pcep_object_notify *
+pcep_obj_create_notify(enum pcep_notification_types notification_type,
+ enum pcep_notification_values notification_value);
+struct pcep_object_nopath *
+pcep_obj_create_nopath(uint8_t ni, bool flag_c,
+ enum pcep_nopath_tlv_err_codes error_code);
+struct pcep_object_association_ipv4 *
+pcep_obj_create_association_ipv4(bool r_flag, uint16_t association_type,
+ uint16_t association_id, struct in_addr src);
+struct pcep_object_association_ipv6 *
+pcep_obj_create_association_ipv6(bool r_flag, uint16_t association_type,
+ uint16_t association_id, struct in6_addr src);
+struct pcep_object_endpoints_ipv4 *
+pcep_obj_create_endpoint_ipv4(const struct in_addr *src_ipv4,
+ const struct in_addr *dst_ipv4);
+struct pcep_object_endpoints_ipv6 *
+pcep_obj_create_endpoint_ipv6(const struct in6_addr *src_ipv6,
+ const struct in6_addr *dst_ipv6);
+struct pcep_object_bandwidth *pcep_obj_create_bandwidth(float bandwidth);
+struct pcep_object_metric *pcep_obj_create_metric(enum pcep_metric_types type,
+ bool flag_b, bool flag_c,
+ float value);
+struct pcep_object_lspa *
+pcep_obj_create_lspa(uint32_t exclude_any, uint32_t include_any,
+ uint32_t include_all, uint8_t setup_priority,
+ uint8_t holding_priority, bool flag_local_protection);
+struct pcep_object_svec *
+pcep_obj_create_svec(bool srlg, bool node, bool link,
+ double_linked_list *request_id_list);
+struct pcep_object_error *
+pcep_obj_create_error(enum pcep_error_type error_type,
+ enum pcep_error_value error_value);
+struct pcep_object_close *pcep_obj_create_close(enum pcep_close_reason reason);
+struct pcep_object_srp *pcep_obj_create_srp(bool lsp_remove,
+ uint32_t srp_id_number,
+ double_linked_list *tlv_list);
+struct pcep_object_lsp *
+pcep_obj_create_lsp(uint32_t plsp_id, enum pcep_lsp_operational_status status,
+ bool c_flag, bool a_flag, bool r_flag, bool s_flag,
+ bool d_flag, double_linked_list *tlv_list);
+struct pcep_object_vendor_info *
+pcep_obj_create_vendor_info(uint32_t enterprise_number,
+ uint32_t enterprise_spec_info);
+struct pcep_object_inter_layer *
+pcep_obj_create_inter_layer(bool flag_i, bool flag_m, bool flag_t);
+struct pcep_object_switch_layer *
+pcep_obj_create_switch_layer(double_linked_list *switch_layer_rows);
+struct pcep_object_req_adap_cap *
+pcep_obj_create_req_adap_cap(enum pcep_switching_capability sw_cap,
+ enum pcep_lsp_encoding_type encoding);
+struct pcep_object_server_indication *
+pcep_obj_create_server_indication(enum pcep_switching_capability sw_cap,
+ enum pcep_lsp_encoding_type encoding,
+ double_linked_list *tlv_list);
+struct pcep_object_objective_function *
+pcep_obj_create_objective_function(uint16_t of_code,
+ double_linked_list *tlv_list);
+
+/* Route Object (Explicit ero, Reported rro, and Include iro) functions
+ * First, the sub-objects should be created and appended to a
+ * double_linked_list, then call one of these Route Object creation functions
+ * with the subobj list */
+struct pcep_object_ro *pcep_obj_create_ero(double_linked_list *ero_list);
+struct pcep_object_ro *pcep_obj_create_rro(double_linked_list *rro_list);
+struct pcep_object_ro *pcep_obj_create_iro(double_linked_list *iro_list);
+/* Route Object sub-object creation functions */
+struct pcep_ro_subobj_ipv4 *
+pcep_obj_create_ro_subobj_ipv4(bool loose_hop, const struct in_addr *ro_ipv4,
+ uint8_t prefix_len, bool flag_local_prot);
+struct pcep_ro_subobj_ipv6 *
+pcep_obj_create_ro_subobj_ipv6(bool loose_hop, const struct in6_addr *ro_ipv6,
+ uint8_t prefix_len, bool flag_local_prot);
+struct pcep_ro_subobj_unnum *
+pcep_obj_create_ro_subobj_unnum(struct in_addr *router_id, uint32_t if_id);
+struct pcep_ro_subobj_32label *
+pcep_obj_create_ro_subobj_32label(bool flag_global_label, uint8_t class_type,
+ uint32_t label);
+struct pcep_ro_subobj_asn *pcep_obj_create_ro_subobj_asn(uint16_t asn);
+
+/* SR ERO and SR RRO creation functions for different NAI (Node/Adj ID) types.
+ * - The loose_hop is only used for sr ero and must always be false for sr rro.
+ * - The NAI value will be set internally, depending on which function is used.
+ * m_flag:
+ * - If this flag is true, the SID value represents an MPLS label stack
+ * entry as specified in [RFC3032]. Otherwise, the SID value is an
+ * administratively configured value which represents an index into
+ * an MPLS label space (either SRGB or SRLB) per [RFC8402].
+ * c_flag:
+ * - If the M flag and the C flag are both true, then the TC, S, and TTL
+ * fields in the MPLS label stack entry are specified by the PCE. However,
+ * a PCC MAY choose to override these values according to its local policy
+ * and MPLS forwarding rules.
+ * - If the M flag is true but the C flag is false, then the TC, S, and TTL
+ * fields MUST be ignored by the PCC.
+ * - The PCC MUST set these fields according to its local policy and MPLS
+ * forwarding rules.
+ * - If the M flag is false then the C bit MUST be false. */
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_nonai(bool loose_hop,
+ uint32_t sid,
+ bool c_flag,
+ bool m_flag);
+
+/* The ipv4_node_id will be copied internally */
+struct pcep_ro_subobj_sr *
+pcep_obj_create_ro_subobj_sr_ipv4_node(bool loose_hop, bool sid_absent,
+ bool c_flag, bool m_flag, uint32_t sid,
+ struct in_addr *ipv4_node_id);
+/* The ipv6_node_id will be copied internally */
+struct pcep_ro_subobj_sr *
+pcep_obj_create_ro_subobj_sr_ipv6_node(bool loose_hop, bool sid_absent,
+ bool c_flag, bool m_flag, uint32_t sid,
+ struct in6_addr *ipv6_node_id);
+/* The local_ipv4 and remote_ipv4 will be copied internally */
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_ipv4_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ struct in_addr *local_ipv4, struct in_addr *remote_ipv4);
+/* The local_ipv6 and remote_ipv6 will be copied internally */
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_ipv6_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ struct in6_addr *local_ipv6, struct in6_addr *remote_ipv6);
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ uint32_t local_node_id, uint32_t local_if_id, uint32_t remote_node_id,
+ uint32_t remote_if_id);
+/* The local_ipv6 and remote_ipv6 will be copied internally */
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ struct in6_addr *local_ipv6, uint32_t local_if_id,
+ struct in6_addr *remote_ipv6, uint32_t remote_if_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pceplib/pcep_msg_objects_encoding.c b/pceplib/pcep_msg_objects_encoding.c
new file mode 100644
index 000000000..d40b84086
--- /dev/null
+++ b/pceplib/pcep_msg_objects_encoding.c
@@ -0,0 +1,1720 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Encoding and decoding for PCEP Objects.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "pcep_msg_objects.h"
+#include "pcep_msg_encoding.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+void write_object_header(struct pcep_object_header *object_hdr,
+ uint16_t object_length, uint8_t *buf);
+void pcep_decode_object_hdr(const uint8_t *obj_buf,
+ struct pcep_object_header *obj_hdr);
+void set_ro_subobj_fields(struct pcep_object_ro_subobj *subobj, bool flag_l,
+ uint8_t subobj_type);
+
+/*
+ * forward declarations for initialize_object_encoders()
+ */
+uint16_t pcep_encode_obj_open(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_rp(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_nopath(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_endpoints(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_association(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_bandwidth(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_metric(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_ro(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_lspa(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_svec(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_notify(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_error(struct pcep_object_header *error,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_close(struct pcep_object_header *close,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_srp(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_lsp(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_vendor_info(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_inter_layer(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_switch_layer(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_req_adap_cap(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_server_ind(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_objective_function(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+typedef uint16_t (*object_encoder_funcptr)(struct pcep_object_header *,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+
+#define MAX_OBJECT_ENCODER_INDEX 64
+
+#define PCEP_ENCODERS_ARGS \
+ struct pcep_object_header *, struct pcep_versioning *versioning, \
+ uint8_t *buf
+uint16_t (*const object_encoders[MAX_OBJECT_ENCODER_INDEX])(
+ PCEP_ENCODERS_ARGS) = {
+ [PCEP_OBJ_CLASS_OPEN] = pcep_encode_obj_open,
+ [PCEP_OBJ_CLASS_RP] = pcep_encode_obj_rp,
+ [PCEP_OBJ_CLASS_NOPATH] = pcep_encode_obj_nopath,
+ [PCEP_OBJ_CLASS_ENDPOINTS] = pcep_encode_obj_endpoints,
+ [PCEP_OBJ_CLASS_BANDWIDTH] = pcep_encode_obj_bandwidth,
+ [PCEP_OBJ_CLASS_METRIC] = pcep_encode_obj_metric,
+ [PCEP_OBJ_CLASS_ERO] = pcep_encode_obj_ro,
+ [PCEP_OBJ_CLASS_RRO] = pcep_encode_obj_ro,
+ [PCEP_OBJ_CLASS_LSPA] = pcep_encode_obj_lspa,
+ [PCEP_OBJ_CLASS_IRO] = pcep_encode_obj_ro,
+ [PCEP_OBJ_CLASS_SVEC] = pcep_encode_obj_svec,
+ [PCEP_OBJ_CLASS_NOTF] = pcep_encode_obj_notify,
+ [PCEP_OBJ_CLASS_ERROR] = pcep_encode_obj_error,
+ [PCEP_OBJ_CLASS_CLOSE] = pcep_encode_obj_close,
+ [PCEP_OBJ_CLASS_LSP] = pcep_encode_obj_lsp,
+ [PCEP_OBJ_CLASS_SRP] = pcep_encode_obj_srp,
+ [PCEP_OBJ_CLASS_ASSOCIATION] = pcep_encode_obj_association,
+ [PCEP_OBJ_CLASS_INTER_LAYER] = pcep_encode_obj_inter_layer,
+ [PCEP_OBJ_CLASS_SWITCH_LAYER] = pcep_encode_obj_switch_layer,
+ [PCEP_OBJ_CLASS_REQ_ADAP_CAP] = pcep_encode_obj_req_adap_cap,
+ [PCEP_OBJ_CLASS_SERVER_IND] = pcep_encode_obj_server_ind,
+ [PCEP_OBJ_CLASS_VENDOR_INFO] = pcep_encode_obj_vendor_info,
+ [PCEP_OBJ_CLASS_OF] = pcep_encode_obj_objective_function,
+};
+/*
+ * forward declarations for initialize_object_decoders()
+ */
+struct pcep_object_header *pcep_decode_obj_open(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_rp(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_nopath(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_endpoints(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_association(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_bandwidth(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_metric(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_ro(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_lspa(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_svec(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_notify(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_error(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_close(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_srp(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_lsp(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_vendor_info(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_inter_layer(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_switch_layer(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_req_adap_cap(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_server_ind(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_objective_function(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+typedef struct pcep_object_header *(*object_decoder_funcptr)(
+ struct pcep_object_header *, const uint8_t *buf);
+
+#define PCEP_DECODERS_ARGS struct pcep_object_header *, const uint8_t *buf
+
+struct pcep_object_header *(*const object_decoders[MAX_OBJECT_ENCODER_INDEX])(
+ PCEP_DECODERS_ARGS) = {
+ [PCEP_OBJ_CLASS_OPEN] = pcep_decode_obj_open,
+ [PCEP_OBJ_CLASS_RP] = pcep_decode_obj_rp,
+ [PCEP_OBJ_CLASS_NOPATH] = pcep_decode_obj_nopath,
+ [PCEP_OBJ_CLASS_ENDPOINTS] = pcep_decode_obj_endpoints,
+ [PCEP_OBJ_CLASS_BANDWIDTH] = pcep_decode_obj_bandwidth,
+ [PCEP_OBJ_CLASS_METRIC] = pcep_decode_obj_metric,
+ [PCEP_OBJ_CLASS_ERO] = pcep_decode_obj_ro,
+ [PCEP_OBJ_CLASS_RRO] = pcep_decode_obj_ro,
+ [PCEP_OBJ_CLASS_LSPA] = pcep_decode_obj_lspa,
+ [PCEP_OBJ_CLASS_IRO] = pcep_decode_obj_ro,
+ [PCEP_OBJ_CLASS_SVEC] = pcep_decode_obj_svec,
+ [PCEP_OBJ_CLASS_NOTF] = pcep_decode_obj_notify,
+ [PCEP_OBJ_CLASS_ERROR] = pcep_decode_obj_error,
+ [PCEP_OBJ_CLASS_CLOSE] = pcep_decode_obj_close,
+ [PCEP_OBJ_CLASS_LSP] = pcep_decode_obj_lsp,
+ [PCEP_OBJ_CLASS_SRP] = pcep_decode_obj_srp,
+ [PCEP_OBJ_CLASS_ASSOCIATION] = pcep_decode_obj_association,
+ [PCEP_OBJ_CLASS_INTER_LAYER] = pcep_decode_obj_inter_layer,
+ [PCEP_OBJ_CLASS_SWITCH_LAYER] = pcep_decode_obj_switch_layer,
+ [PCEP_OBJ_CLASS_REQ_ADAP_CAP] = pcep_decode_obj_req_adap_cap,
+ [PCEP_OBJ_CLASS_SERVER_IND] = pcep_decode_obj_server_ind,
+ [PCEP_OBJ_CLASS_VENDOR_INFO] = pcep_decode_obj_vendor_info,
+ [PCEP_OBJ_CLASS_OF] = pcep_decode_obj_objective_function,
+};
+
+/* Object lengths, including the Object Header.
+ * Used by pcep_object_get_length() and pcep_object_has_tlvs() */
+static uint8_t pcep_object_class_lengths[] = {
+ 0, /* Object class 0 unused */
+ 8, /* PCEP_OBJ_CLASS_OPEN = 1 */
+ 12, /* PCEP_OBJ_CLASS_RP = 2 */
+ 16, /* PCEP_OBJ_CLASS_NOPATH = 3, includes 8 for mandatory TLV */
+ 0, /* PCEP_OBJ_CLASS_ENDPOINTS = 4, could be ipv4 or ipv6, setting to 0
+ */
+ 8, /* PCEP_OBJ_CLASS_BANDWIDTH = 5 */
+ 12, /* PCEP_OBJ_CLASS_METRIC = 6 */
+ 0, /* PCEP_OBJ_CLASS_ERO = 7, setting 0, ROs cannot have TLVs */
+ 0, /* PCEP_OBJ_CLASS_RRO = 8, setting 0, ROs cannot have TLVs */
+ 20, /* PCEP_OBJ_CLASS_LSPA = 9 */
+ 0, /* PCEP_OBJ_CLASS_IRO = 10, setting 0, ROs cannot have TLVs */
+ 0, /* PCEP_OBJ_CLASS_SVEC = 11, SVECs cannot have TLVs */
+ 8, /* PCEP_OBJ_CLASS_NOTF = 12 */
+ 8, /* PCEP_OBJ_CLASS_ERROR = 13 */
+ 0, /* Object class 14 unused */
+ 8, /* PCEP_OBJ_CLASS_CLOSE = 15 */
+ 0, 0, 0, 0, 0, /* Object classes 16 - 20 are not used */
+ 8, /* PCEP_OBJ_CLASS_OF = 21 */
+ 0, 0, 0, 0, 0, /* Object classes 22 - 26 are not used */
+ 0, 0, 0, 0, 0, /* Object classes 27 - 31 are not used */
+ 8, /* PCEP_OBJ_CLASS_LSP = 32 */
+ 12, /* PCEP_OBJ_CLASS_SRP = 33 */
+ 12, /* PCEP_OBJ_CLASS_VENDOR_INFO = 34 */
+ 0, /* Object class 35 unused */
+ 0, /* PCEP_OBJ_CLASS_INTER_LAYER = 36, cannot have TLVs */
+ 0, /* PCEP_OBJ_CLASS_SWITCH_LAYER = 37, cannot have TLVs */
+ 0, /* PCEP_OBJ_CLASS_REQ_ADAP_CAP = 38, cannot have TLVs*/
+ 8, /* PCEP_OBJ_CLASS_SERVER_IND = 39 */
+ 0, /* PCEP_OBJ_CLASS_ASSOCIATION = 40, cannot have TLVs */
+};
+
+/*
+ * The TLVs can have strange length values, since they do not include padding in
+ * the TLV header length, but that extra padding must be taken into account by
+ * the enclosing object by rounding up to the next 4 byte boundary.
+ * Example returned lengths:
+ * normalize_length(4) = 4, normalize_length(5) = 8, normalize_length(6)
+ * = 8, normalize_length(7) = 8, normalize_length(8) = 8
+ * normalize_length(9) = 12, normalize_length(10) = 12, normalize_length(11) =
+ * 12, normalize_length(12) = 12, normalize_length(13) = 13...
+ */
+uint16_t normalize_pcep_tlv_length(uint16_t length)
+{
+ return (length % 4 == 0) ? length : (length + (4 - (length % 4)));
+}
+
+/*
+ * Encoding functions
+ */
+uint16_t pcep_encode_object(struct pcep_object_header *object_hdr,
+ struct pcep_versioning *versioning, uint8_t *buf)
+{
+
+ if (object_hdr->object_class >= MAX_OBJECT_ENCODER_INDEX) {
+ pcep_log(LOG_INFO,
+ "%s: Cannot encode unknown Object class [%d]",
+ __func__, object_hdr->object_class);
+ return 0;
+ }
+
+ object_encoder_funcptr obj_encoder =
+ object_encoders[object_hdr->object_class];
+ if (obj_encoder == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: No object encoder found for Object class [%d]",
+ __func__, object_hdr->object_class);
+ return 0;
+ }
+
+ uint16_t object_length = OBJECT_HEADER_LENGTH
+ + obj_encoder(object_hdr, versioning,
+ buf + OBJECT_HEADER_LENGTH);
+ double_linked_list_node *node =
+ (object_hdr->tlv_list == NULL ? NULL
+ : object_hdr->tlv_list->head);
+ for (; node != NULL; node = node->next_node) {
+ /* Returns the length of the TLV, including the TLV header */
+ object_length += pcep_encode_tlv(
+ (struct pcep_object_tlv_header *)node->data, versioning,
+ buf + object_length);
+ }
+ object_length = normalize_pcep_tlv_length(object_length);
+ write_object_header(object_hdr, object_length, buf);
+ object_hdr->encoded_object = buf;
+ object_hdr->encoded_object_length = object_length;
+
+ return object_length;
+}
+
+
+/* Object Header
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Object-Class | OT |Res|P|I| Object Length (bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * // (Object body) //
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+void write_object_header(struct pcep_object_header *object_hdr,
+ uint16_t object_length, uint8_t *buf)
+{
+ buf[0] = object_hdr->object_class;
+ buf[1] = ((object_hdr->object_type << 4)
+ | (object_hdr->flag_p ? OBJECT_HEADER_FLAG_P : 0x00)
+ | (object_hdr->flag_i ? OBJECT_HEADER_FLAG_I : 0x00));
+ uint16_t net_order_length = htons(object_length);
+ memcpy(buf + 2, &net_order_length, sizeof(net_order_length));
+}
+
+
+/*
+ * Functions to encode objects
+ * - they will be passed a pointer to a buffer to write the object body,
+ * which is past the object header.
+ * - they should return the object body length, not including the object header
+ * length.
+ */
+
+uint16_t pcep_encode_obj_open(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_open *open = (struct pcep_object_open *)hdr;
+ obj_body_buf[0] = (open->open_version << 5) & 0xe0;
+ obj_body_buf[1] = open->open_keepalive;
+ obj_body_buf[2] = open->open_deadtimer;
+ obj_body_buf[3] = open->open_sid;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_rp(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_rp *rp = (struct pcep_object_rp *)hdr;
+ obj_body_buf[3] = ((rp->flag_strict ? OBJECT_RP_FLAG_O : 0x00)
+ | (rp->flag_bidirectional ? OBJECT_RP_FLAG_B : 0x00)
+ | (rp->flag_reoptimization ? OBJECT_RP_FLAG_R : 0x00)
+ | (rp->flag_of ? OBJECT_RP_FLAG_OF : 0x00)
+ | (rp->priority & 0x07));
+ uint32_t *uint32_ptr = (uint32_t *)(obj_body_buf + 4);
+ *uint32_ptr = htonl(rp->request_id);
+
+ return LENGTH_2WORDS;
+}
+
+uint16_t pcep_encode_obj_notify(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_notify *notify = (struct pcep_object_notify *)hdr;
+ obj_body_buf[2] = notify->notification_type;
+ obj_body_buf[3] = notify->notification_value;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_nopath(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_nopath *nopath = (struct pcep_object_nopath *)hdr;
+ obj_body_buf[0] = nopath->ni;
+ obj_body_buf[1] = ((nopath->flag_c) ? OBJECT_NOPATH_FLAG_C : 0x00);
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_association(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ uint16_t *uint16_ptr = (uint16_t *)obj_body_buf;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ if (hdr->object_type == PCEP_OBJ_TYPE_ASSOCIATION_IPV4) {
+ struct pcep_object_association_ipv4 *ipv4 =
+ (struct pcep_object_association_ipv4 *)hdr;
+ obj_body_buf[3] =
+ (ipv4->R_flag ? OBJECT_ASSOCIATION_FLAG_R : 0x00);
+ uint16_ptr[2] = htons(ipv4->association_type);
+ uint16_ptr[3] = htons(ipv4->association_id);
+ uint32_ptr[2] = ipv4->src.s_addr;
+
+ return LENGTH_3WORDS;
+ } else {
+ struct pcep_object_association_ipv6 *ipv6 =
+ (struct pcep_object_association_ipv6 *)hdr;
+ obj_body_buf[3] =
+ (ipv6->R_flag ? OBJECT_ASSOCIATION_FLAG_R : 0x00);
+ uint16_ptr[2] = htons(ipv6->association_type);
+ uint16_ptr[3] = htons(ipv6->association_id);
+ memcpy(uint32_ptr, &ipv6->src, sizeof(struct in6_addr));
+
+ return LENGTH_6WORDS;
+ }
+}
+
+uint16_t pcep_encode_obj_endpoints(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ if (hdr->object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV4) {
+ struct pcep_object_endpoints_ipv4 *ipv4 =
+ (struct pcep_object_endpoints_ipv4 *)hdr;
+ uint32_ptr[0] = ipv4->src_ipv4.s_addr;
+ uint32_ptr[1] = ipv4->dst_ipv4.s_addr;
+
+ return LENGTH_2WORDS;
+ } else {
+ struct pcep_object_endpoints_ipv6 *ipv6 =
+ (struct pcep_object_endpoints_ipv6 *)hdr;
+ memcpy(uint32_ptr, &ipv6->src_ipv6, sizeof(struct in6_addr));
+ memcpy(&uint32_ptr[4], &ipv6->dst_ipv6,
+ sizeof(struct in6_addr));
+
+ return LENGTH_8WORDS;
+ }
+}
+
+uint16_t pcep_encode_obj_bandwidth(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_bandwidth *bandwidth =
+ (struct pcep_object_bandwidth *)hdr;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ /* Seems like the compiler doesnt correctly copy the float, so memcpy()
+ * it */
+ memcpy(uint32_ptr, &(bandwidth->bandwidth), sizeof(uint32_t));
+ *uint32_ptr = htonl(*uint32_ptr);
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_metric(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_metric *metric = (struct pcep_object_metric *)hdr;
+ obj_body_buf[2] = ((metric->flag_c ? OBJECT_METRIC_FLAC_C : 0x00)
+ | (metric->flag_b ? OBJECT_METRIC_FLAC_B : 0x00));
+ obj_body_buf[3] = metric->type;
+ uint32_t *uint32_ptr = (uint32_t *)(obj_body_buf + 4);
+ /* Seems like the compiler doesnt correctly copy the float, so memcpy()
+ * it */
+ memcpy(uint32_ptr, &(metric->value), sizeof(uint32_t));
+ *uint32_ptr = htonl(*uint32_ptr);
+
+ return LENGTH_2WORDS;
+}
+
+uint16_t pcep_encode_obj_lspa(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_lspa *lspa = (struct pcep_object_lspa *)hdr;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ uint32_ptr[0] = htonl(lspa->lspa_exclude_any);
+ uint32_ptr[1] = htonl(lspa->lspa_include_any);
+ uint32_ptr[2] = htonl(lspa->lspa_include_all);
+ obj_body_buf[12] = lspa->setup_priority;
+ obj_body_buf[13] = lspa->holding_priority;
+ obj_body_buf[14] =
+ (lspa->flag_local_protection ? OBJECT_LSPA_FLAG_L : 0x00);
+
+ return LENGTH_4WORDS;
+}
+
+uint16_t pcep_encode_obj_svec(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_svec *svec = (struct pcep_object_svec *)hdr;
+ obj_body_buf[3] =
+ ((svec->flag_srlg_diverse ? OBJECT_SVEC_FLAG_S : 0x00)
+ | (svec->flag_node_diverse ? OBJECT_SVEC_FLAG_N : 0x00)
+ | (svec->flag_link_diverse ? OBJECT_SVEC_FLAG_L : 0x00));
+
+ if (svec->request_id_list == NULL) {
+ return LENGTH_1WORD;
+ }
+
+ int index = 1;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ double_linked_list_node *node = svec->request_id_list->head;
+ for (; node != NULL; node = node->next_node) {
+ uint32_ptr[index++] = htonl(*((uint32_t *)(node->data)));
+ }
+
+ return LENGTH_1WORD
+ + (svec->request_id_list->num_entries * sizeof(uint32_t));
+}
+
+uint16_t pcep_encode_obj_error(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_error *error = (struct pcep_object_error *)hdr;
+ obj_body_buf[2] = error->error_type;
+ obj_body_buf[3] = error->error_value;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_close(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_close *close = (struct pcep_object_close *)hdr;
+ obj_body_buf[3] = close->reason;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_srp(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_srp *srp = (struct pcep_object_srp *)hdr;
+ obj_body_buf[3] = (srp->flag_lsp_remove ? OBJECT_SRP_FLAG_R : 0x00);
+ uint32_t *uint32_ptr = (uint32_t *)(obj_body_buf + 4);
+ *uint32_ptr = htonl(srp->srp_id_number);
+
+ return LENGTH_2WORDS;
+}
+
+uint16_t pcep_encode_obj_lsp(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_lsp *lsp = (struct pcep_object_lsp *)hdr;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ uint32_ptr[0] = htonl((lsp->plsp_id << 12) & 0xfffff000);
+ obj_body_buf[3] = ((lsp->flag_c ? OBJECT_LSP_FLAG_C : 0x00)
+ | ((lsp->operational_status << 4) & 0x70)
+ | (lsp->flag_a ? OBJECT_LSP_FLAG_A : 0x00)
+ | (lsp->flag_r ? OBJECT_LSP_FLAG_R : 0x00)
+ | (lsp->flag_s ? OBJECT_LSP_FLAG_S : 0x00)
+ | (lsp->flag_d ? OBJECT_LSP_FLAG_D : 0x00));
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_vendor_info(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_vendor_info *obj =
+ (struct pcep_object_vendor_info *)hdr;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ uint32_ptr[0] = htonl(obj->enterprise_number);
+ uint32_ptr[1] = htonl(obj->enterprise_specific_info);
+
+ return LENGTH_2WORDS;
+}
+
+uint16_t pcep_encode_obj_inter_layer(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_inter_layer *obj =
+ (struct pcep_object_inter_layer *)hdr;
+ obj_body_buf[3] = ((obj->flag_i ? OBJECT_INTER_LAYER_FLAG_I : 0x00)
+ | (obj->flag_m ? OBJECT_INTER_LAYER_FLAG_M : 0x00)
+ | (obj->flag_t ? OBJECT_INTER_LAYER_FLAG_T : 0x00));
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_switch_layer(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_switch_layer *obj =
+ (struct pcep_object_switch_layer *)hdr;
+ uint8_t buf_index = 0;
+
+ double_linked_list_node *node = obj->switch_layer_rows->head;
+ while (node != NULL) {
+ struct pcep_object_switch_layer_row *row = node->data;
+ if (row == NULL) {
+ break;
+ }
+
+ obj_body_buf[buf_index] = row->lsp_encoding_type;
+ obj_body_buf[buf_index + 1] = row->switching_type;
+ obj_body_buf[buf_index + 3] =
+ (row->flag_i ? OBJECT_SWITCH_LAYER_FLAG_I : 0x00);
+
+ buf_index += LENGTH_1WORD;
+ }
+
+ return buf_index;
+}
+
+uint16_t pcep_encode_obj_req_adap_cap(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_req_adap_cap *obj =
+ (struct pcep_object_req_adap_cap *)hdr;
+
+ obj_body_buf[0] = obj->switching_capability;
+ obj_body_buf[1] = obj->encoding;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_server_ind(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_server_indication *obj =
+ (struct pcep_object_server_indication *)hdr;
+
+ obj_body_buf[0] = obj->switching_capability;
+ obj_body_buf[1] = obj->encoding;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_objective_function(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_objective_function *obj =
+ (struct pcep_object_objective_function *)hdr;
+
+ uint16_t *uint16_ptr = (uint16_t *)obj_body_buf;
+ *uint16_ptr = htons(obj->of_code);
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_ro(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_ro *ro = (struct pcep_object_ro *)hdr;
+ if (ro == NULL || ro->sub_objects == NULL) {
+ return 0;
+ }
+
+ /* RO Subobject format
+ *
+ * 0 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------//----------------+
+ * |L| Type | Length | (Subobject contents) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------//----------------+
+ */
+
+ uint16_t index = 0;
+ double_linked_list_node *node = ro->sub_objects->head;
+ for (; node != NULL; node = node->next_node) {
+ struct pcep_object_ro_subobj *ro_subobj = node->data;
+ obj_body_buf[index++] =
+ ((ro_subobj->flag_subobj_loose_hop ? 0x80 : 0x00)
+ | (ro_subobj->ro_subobj_type));
+ /* The length will be written below, depending on the subobj
+ * type */
+ uint8_t *length_ptr = &(obj_body_buf[index++]);
+ uint32_t *uint32_ptr = (uint32_t *)(obj_body_buf + index);
+
+ /* - The index has already been incremented past the header,
+ * and now points to the ro_subobj body. Below it just needs
+ * to be incremented past the body.
+ *
+ * - Each section below needs to write the total length,
+ * including the 2 byte subobj header. */
+
+ switch (ro_subobj->ro_subobj_type) {
+ case RO_SUBOBJ_TYPE_IPV4: {
+ struct pcep_ro_subobj_ipv4 *ipv4 =
+ (struct pcep_ro_subobj_ipv4 *)ro_subobj;
+ uint32_ptr[0] = ipv4->ip_addr.s_addr;
+ index += LENGTH_1WORD;
+ obj_body_buf[index++] = ipv4->prefix_length;
+ obj_body_buf[index++] =
+ (ipv4->flag_local_protection
+ ? OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT
+ : 0x00);
+ *length_ptr = LENGTH_2WORDS;
+ } break;
+
+ case RO_SUBOBJ_TYPE_IPV6: {
+ struct pcep_ro_subobj_ipv6 *ipv6 =
+ (struct pcep_ro_subobj_ipv6 *)ro_subobj;
+ encode_ipv6(&ipv6->ip_addr, uint32_ptr);
+ index += LENGTH_4WORDS;
+ obj_body_buf[index++] = ipv6->prefix_length;
+ obj_body_buf[index++] =
+ (ipv6->flag_local_protection
+ ? OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT
+ : 0x00);
+ *length_ptr = LENGTH_5WORDS;
+ } break;
+
+ case RO_SUBOBJ_TYPE_LABEL: {
+ struct pcep_ro_subobj_32label *label =
+ (struct pcep_ro_subobj_32label *)ro_subobj;
+ obj_body_buf[index++] =
+ (label->flag_global_label
+ ? OBJECT_SUBOBJ_LABEL_FLAG_GLOGAL
+ : 0x00);
+ obj_body_buf[index++] = label->class_type;
+ uint32_ptr = (uint32_t *)(obj_body_buf + index);
+ *uint32_ptr = htonl(label->label);
+ *length_ptr = LENGTH_2WORDS;
+ index += LENGTH_1WORD;
+ } break;
+
+ case RO_SUBOBJ_TYPE_UNNUM: {
+ struct pcep_ro_subobj_unnum *unum =
+ (struct pcep_ro_subobj_unnum *)ro_subobj;
+ index += 2; /* increment past 2 reserved bytes */
+ uint32_ptr = (uint32_t *)(obj_body_buf + index);
+ uint32_ptr[0] = unum->router_id.s_addr;
+ uint32_ptr[1] = htonl(unum->interface_id);
+ *length_ptr = LENGTH_3WORDS;
+ index += LENGTH_2WORDS;
+ } break;
+
+ case RO_SUBOBJ_TYPE_ASN: {
+ struct pcep_ro_subobj_asn *asn =
+ (struct pcep_ro_subobj_asn *)ro_subobj;
+ uint16_t *uint16_ptr =
+ (uint16_t *)(obj_body_buf + index);
+ *uint16_ptr = htons(asn->asn);
+ *length_ptr = LENGTH_1WORD;
+ index += 2;
+ } break;
+
+ case RO_SUBOBJ_TYPE_SR: {
+ /* SR-ERO subobject format
+ *
+ * 0 1 2 3 0 1 2 3 4
+ * 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |L| Type=36 | Length | NT | Flags
+ * |F|S|C|M|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SID (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * // NAI (variable, optional) //
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ struct pcep_ro_subobj_sr *sr_subobj =
+ (struct pcep_ro_subobj_sr *)ro_subobj;
+ obj_body_buf[index++] =
+ ((sr_subobj->nai_type << 4) & 0xf0);
+ obj_body_buf[index++] =
+ ((sr_subobj->flag_f ? OBJECT_SUBOBJ_SR_FLAG_F
+ : 0x00)
+ | (sr_subobj->flag_s ? OBJECT_SUBOBJ_SR_FLAG_S
+ : 0x00)
+ | (sr_subobj->flag_c ? OBJECT_SUBOBJ_SR_FLAG_C
+ : 0x00)
+ | (sr_subobj->flag_m ? OBJECT_SUBOBJ_SR_FLAG_M
+ : 0x00));
+ uint32_ptr = (uint32_t *)(obj_body_buf + index);
+ /* Start with LENGTH_1WORD for the SubObj HDR + NT +
+ * Flags */
+ uint8_t sr_base_length = LENGTH_1WORD;
+ /* If the sid_absent flag is true, then dont convert the
+ * sid */
+ if (sr_subobj->flag_s == false) {
+ uint32_ptr[0] = htonl(sr_subobj->sid);
+ index += LENGTH_1WORD;
+ uint32_ptr = (uint32_t *)(obj_body_buf + index);
+ sr_base_length += LENGTH_1WORD;
+ }
+
+ /* The lengths below need to include:
+ * - sr_base_length: set above to include SR SubObj Hdr
+ * and the SID if present
+ * - Number of bytes written to the NAI
+ * The index will only be incremented below by the
+ * number of bytes written to the NAI, since the RO SR
+ * subobj header and the SID have already been written.
+ */
+
+ double_linked_list_node *nai_node =
+ (sr_subobj->nai_list == NULL
+ ? NULL
+ : sr_subobj->nai_list->head);
+ if (nai_node == NULL) {
+ if (sr_subobj->nai_type
+ == PCEP_SR_SUBOBJ_NAI_ABSENT) {
+ *length_ptr = sr_base_length;
+ continue;
+ } else {
+ return 0;
+ }
+ }
+ switch (sr_subobj->nai_type) {
+ case PCEP_SR_SUBOBJ_NAI_IPV4_NODE:
+ uint32_ptr[0] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ *length_ptr = sr_base_length + LENGTH_1WORD;
+ index += LENGTH_1WORD;
+ break;
+
+ case PCEP_SR_SUBOBJ_NAI_IPV6_NODE:
+ encode_ipv6((struct in6_addr *)nai_node->data,
+ uint32_ptr);
+ *length_ptr = sr_base_length + LENGTH_4WORDS;
+ index += LENGTH_4WORDS;
+ break;
+
+ case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY:
+ uint32_ptr[0] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ nai_node = nai_node->next_node;
+ uint32_ptr[1] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ nai_node = nai_node->next_node;
+ uint32_ptr[2] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ nai_node = nai_node->next_node;
+ uint32_ptr[3] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ *length_ptr = sr_base_length + LENGTH_4WORDS;
+ index += LENGTH_4WORDS;
+ break;
+
+ case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY:
+ uint32_ptr[0] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ nai_node = nai_node->next_node;
+ uint32_ptr[1] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ *length_ptr = sr_base_length + LENGTH_2WORDS;
+ index += LENGTH_2WORDS;
+ break;
+
+ case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY:
+ encode_ipv6((struct in6_addr *)nai_node->data,
+ uint32_ptr);
+ nai_node = nai_node->next_node;
+ encode_ipv6((struct in6_addr *)nai_node->data,
+ uint32_ptr + 4);
+ *length_ptr = sr_base_length + LENGTH_8WORDS;
+ index += LENGTH_8WORDS;
+ break;
+
+ case PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY:
+ encode_ipv6((struct in6_addr *)nai_node->data,
+ uint32_ptr);
+ nai_node = nai_node->next_node;
+ uint32_ptr[4] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ nai_node = nai_node->next_node;
+ encode_ipv6((struct in6_addr *)nai_node->data,
+ uint32_ptr + 5);
+ nai_node = nai_node->next_node;
+ uint32_ptr[9] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ *length_ptr = sr_base_length + LENGTH_10WORDS;
+ index += LENGTH_10WORDS;
+ break;
+
+ default:
+ break;
+ }
+ } break;
+
+ default:
+ break;
+ }
+ }
+
+ return index;
+}
+
+void encode_ipv6(struct in6_addr *src_ipv6, uint32_t *dst)
+{
+ memcpy(dst, src_ipv6, sizeof(struct in6_addr));
+}
+
+/*
+ * Decoding functions.
+ */
+
+void pcep_decode_object_hdr(const uint8_t *obj_buf,
+ struct pcep_object_header *obj_hdr)
+{
+ memset(obj_hdr, 0, sizeof(struct pcep_object_header));
+
+ obj_hdr->object_class = obj_buf[0];
+ obj_hdr->object_type = (obj_buf[1] >> 4) & 0x0f;
+ obj_hdr->flag_p = (obj_buf[1] & OBJECT_HEADER_FLAG_P);
+ obj_hdr->flag_i = (obj_buf[1] & OBJECT_HEADER_FLAG_I);
+ uint16_t net_order_length;
+ memcpy(&net_order_length, obj_buf + 2, sizeof(net_order_length));
+ obj_hdr->encoded_object_length = ntohs(net_order_length);
+ obj_hdr->encoded_object = obj_buf;
+}
+
+uint16_t pcep_object_get_length(enum pcep_object_classes object_class,
+ enum pcep_object_types object_type)
+{
+ uint8_t object_length = pcep_object_class_lengths[object_class];
+ if (object_length == 0) {
+ if (object_class == PCEP_OBJ_CLASS_ENDPOINTS) {
+ if (object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV4) {
+ return 12;
+ } else if (object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV6) {
+ return 36;
+ }
+ }
+
+ return 0;
+ }
+
+ return object_length;
+}
+
+uint16_t pcep_object_get_length_by_hdr(struct pcep_object_header *object_hdr)
+{
+ return (pcep_object_get_length(object_hdr->object_class,
+ object_hdr->object_type));
+}
+
+bool pcep_object_has_tlvs(struct pcep_object_header *object_hdr)
+{
+ uint8_t object_length = pcep_object_get_length_by_hdr(object_hdr);
+ if (object_length == 0) {
+ return false;
+ }
+
+ return (object_hdr->encoded_object_length - object_length) > 0;
+}
+
+struct pcep_object_header *pcep_decode_object(const uint8_t *obj_buf)
+{
+
+ struct pcep_object_header object_hdr;
+ /* Only initializes and decodes the Object Header: class, type, flags,
+ * and length */
+ pcep_decode_object_hdr(obj_buf, &object_hdr);
+
+ if (object_hdr.object_class >= MAX_OBJECT_ENCODER_INDEX) {
+ pcep_log(LOG_INFO,
+ "%s: Cannot decode unknown Object class [%d]",
+ __func__, object_hdr.object_class);
+ return NULL;
+ }
+
+ object_decoder_funcptr obj_decoder =
+ object_decoders[object_hdr.object_class];
+ if (obj_decoder == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: No object decoder found for Object class [%d]",
+ __func__, object_hdr.object_class);
+ return NULL;
+ }
+
+ /* The object decoders will start decoding the object body, if
+ * anything from the header is needed, they have the object_hdr */
+ struct pcep_object_header *object =
+ obj_decoder(&object_hdr, obj_buf + OBJECT_HEADER_LENGTH);
+ if (object == NULL) {
+ pcep_log(LOG_INFO, "%s: Unable to decode Object class [%d].",
+ __func__, object_hdr.object_class);
+ return NULL;
+ }
+
+ if (pcep_object_has_tlvs(&object_hdr)) {
+ object->tlv_list = dll_initialize();
+ int num_iterations = 0;
+ uint16_t tlv_index = pcep_object_get_length_by_hdr(&object_hdr);
+ while ((object->encoded_object_length - tlv_index) > 0
+ && num_iterations++ < MAX_ITERATIONS) {
+ struct pcep_object_tlv_header *tlv =
+ pcep_decode_tlv(obj_buf + tlv_index);
+ if (tlv == NULL) {
+ /* TODO should we do anything else here ? */
+ return object;
+ }
+
+ /* The TLV length does not include the TLV header */
+ tlv_index += normalize_pcep_tlv_length(
+ tlv->encoded_tlv_length + TLV_HEADER_LENGTH);
+ dll_append(object->tlv_list, tlv);
+ }
+ }
+
+ return object;
+}
+
+static struct pcep_object_header *
+common_object_create(struct pcep_object_header *hdr, uint16_t new_obj_length)
+{
+ struct pcep_object_header *new_object =
+ pceplib_malloc(PCEPLIB_MESSAGES, new_obj_length);
+ memset(new_object, 0, new_obj_length);
+ memcpy(new_object, hdr, sizeof(struct pcep_object_header));
+
+ return new_object;
+}
+
+/*
+ * Decoders
+ */
+
+struct pcep_object_header *pcep_decode_obj_open(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_open *obj =
+ (struct pcep_object_open *)common_object_create(
+ hdr, sizeof(struct pcep_object_open));
+
+ obj->open_version = (obj_buf[0] >> 5) & 0x07;
+ obj->open_keepalive = obj_buf[1];
+ obj->open_deadtimer = obj_buf[2];
+ obj->open_sid = obj_buf[3];
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_rp(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_rp *obj =
+ (struct pcep_object_rp *)common_object_create(
+ hdr, sizeof(struct pcep_object_rp));
+
+ obj->flag_reoptimization = (obj_buf[3] & OBJECT_RP_FLAG_R);
+ obj->flag_bidirectional = (obj_buf[3] & OBJECT_RP_FLAG_B);
+ obj->flag_strict = (obj_buf[3] & OBJECT_RP_FLAG_O);
+ obj->flag_of = (obj_buf[3] & OBJECT_RP_FLAG_OF);
+ obj->priority = (obj_buf[3] & 0x07);
+ obj->request_id = ntohl(*((uint32_t *)(obj_buf + 4)));
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_notify(struct pcep_object_header *hdr, const uint8_t *obj_buf)
+{
+ struct pcep_object_notify *obj =
+ (struct pcep_object_notify *)common_object_create(
+ hdr, sizeof(struct pcep_object_notify));
+
+ obj->notification_type = obj_buf[2];
+ obj->notification_value = obj_buf[3];
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_nopath(struct pcep_object_header *hdr, const uint8_t *obj_buf)
+{
+ struct pcep_object_nopath *obj =
+ (struct pcep_object_nopath *)common_object_create(
+ hdr, sizeof(struct pcep_object_nopath));
+
+ obj->ni = (obj_buf[0] >> 1);
+ obj->flag_c = (obj_buf[0] & OBJECT_NOPATH_FLAG_C);
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_association(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ uint16_t *uint16_ptr = (uint16_t *)obj_buf;
+ uint32_t *uint32_ptr = (uint32_t *)obj_buf;
+
+ if (hdr->object_type == PCEP_OBJ_TYPE_ASSOCIATION_IPV4) {
+ struct pcep_object_association_ipv4 *obj =
+ (struct pcep_object_association_ipv4 *)
+ common_object_create(
+ hdr,
+ sizeof(struct
+ pcep_object_association_ipv4));
+ obj->R_flag = (obj_buf[3] & OBJECT_ASSOCIATION_FLAG_R);
+ obj->association_type = ntohs(uint16_ptr[2]);
+ obj->association_id = ntohs(uint16_ptr[3]);
+ obj->src.s_addr = uint32_ptr[2];
+
+ return (struct pcep_object_header *)obj;
+ } else if (hdr->object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV6) {
+ struct pcep_object_association_ipv6 *obj =
+ (struct pcep_object_association_ipv6 *)
+ common_object_create(
+ hdr,
+ sizeof(struct
+ pcep_object_association_ipv6));
+
+ obj->R_flag = (obj_buf[3] & OBJECT_ASSOCIATION_FLAG_R);
+ obj->association_type = ntohs(uint16_ptr[2]);
+ obj->association_id = ntohs(uint16_ptr[3]);
+ memcpy(&obj->src, &uint32_ptr[2], sizeof(struct in6_addr));
+
+ return (struct pcep_object_header *)obj;
+ }
+
+ return NULL;
+}
+struct pcep_object_header *
+pcep_decode_obj_endpoints(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ uint32_t *uint32_ptr = (uint32_t *)obj_buf;
+
+ if (hdr->object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV4) {
+ struct pcep_object_endpoints_ipv4 *obj =
+ (struct pcep_object_endpoints_ipv4 *)
+ common_object_create(
+ hdr,
+ sizeof(struct
+ pcep_object_endpoints_ipv4));
+ obj->src_ipv4.s_addr = uint32_ptr[0];
+ obj->dst_ipv4.s_addr = uint32_ptr[1];
+
+ return (struct pcep_object_header *)obj;
+ } else if (hdr->object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV6) {
+ struct pcep_object_endpoints_ipv6 *obj =
+ (struct pcep_object_endpoints_ipv6 *)
+ common_object_create(
+ hdr,
+ sizeof(struct
+ pcep_object_endpoints_ipv6));
+
+ memcpy(&obj->src_ipv6, &uint32_ptr[0], sizeof(struct in6_addr));
+ memcpy(&obj->dst_ipv6, &uint32_ptr[4], sizeof(struct in6_addr));
+
+ return (struct pcep_object_header *)obj;
+ }
+
+ return NULL;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_bandwidth(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_bandwidth *obj =
+ (struct pcep_object_bandwidth *)common_object_create(
+ hdr, sizeof(struct pcep_object_bandwidth));
+
+ uint32_t value = ntohl(*((uint32_t *)obj_buf));
+ /* Seems like the compiler doesnt correctly copy to the float, so
+ * memcpy() it */
+ memcpy(&obj->bandwidth, &value, sizeof(uint32_t));
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_metric(struct pcep_object_header *hdr, const uint8_t *obj_buf)
+{
+ struct pcep_object_metric *obj =
+ (struct pcep_object_metric *)common_object_create(
+ hdr, sizeof(struct pcep_object_metric));
+ obj->flag_b = (obj_buf[2] & OBJECT_METRIC_FLAC_B);
+ obj->flag_c = (obj_buf[2] & OBJECT_METRIC_FLAC_C);
+ obj->type = obj_buf[3];
+ uint32_t value = ntohl(*((uint32_t *)(obj_buf + 4)));
+ /* Seems like the compiler doesnt correctly copy to the float, so
+ * memcpy() it */
+ memcpy(&obj->value, &value, sizeof(uint32_t));
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_lspa(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_lspa *obj =
+ (struct pcep_object_lspa *)common_object_create(
+ hdr, sizeof(struct pcep_object_lspa));
+ uint32_t *uint32_ptr = (uint32_t *)obj_buf;
+
+ obj->lspa_exclude_any = ntohl(uint32_ptr[0]);
+ obj->lspa_include_any = ntohl(uint32_ptr[1]);
+ obj->lspa_include_all = ntohl(uint32_ptr[2]);
+ obj->setup_priority = obj_buf[12];
+ obj->holding_priority = obj_buf[13];
+ obj->flag_local_protection = (obj_buf[14] & OBJECT_LSPA_FLAG_L);
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_svec(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_svec *obj =
+ (struct pcep_object_svec *)common_object_create(
+ hdr, sizeof(struct pcep_object_svec));
+
+ obj->flag_link_diverse = (obj_buf[3] & OBJECT_SVEC_FLAG_L);
+ obj->flag_node_diverse = (obj_buf[3] & OBJECT_SVEC_FLAG_N);
+ obj->flag_srlg_diverse = (obj_buf[3] & OBJECT_SVEC_FLAG_S);
+
+ if (hdr->encoded_object_length > LENGTH_2WORDS) {
+ obj->request_id_list = dll_initialize();
+ uint16_t index = 1;
+ uint32_t *uint32_ptr = (uint32_t *)obj_buf;
+ for (;
+ index < ((hdr->encoded_object_length - LENGTH_2WORDS) / 4);
+ index++) {
+ uint32_t *req_id_ptr = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(uint32_t));
+ *req_id_ptr = uint32_ptr[index];
+ dll_append(obj->request_id_list, req_id_ptr);
+ }
+ }
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_error(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_error *obj =
+ (struct pcep_object_error *)common_object_create(
+ hdr, sizeof(struct pcep_object_error));
+
+ obj->error_type = obj_buf[2];
+ obj->error_value = obj_buf[3];
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_close(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_close *obj =
+ (struct pcep_object_close *)common_object_create(
+ hdr, sizeof(struct pcep_object_close));
+
+ obj->reason = obj_buf[3];
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_srp(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_srp *obj =
+ (struct pcep_object_srp *)common_object_create(
+ hdr, sizeof(struct pcep_object_srp));
+
+ obj->flag_lsp_remove = (obj_buf[3] & OBJECT_SRP_FLAG_R);
+ obj->srp_id_number = ntohl(*((uint32_t *)(obj_buf + 4)));
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_lsp(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_lsp *obj =
+ (struct pcep_object_lsp *)common_object_create(
+ hdr, sizeof(struct pcep_object_lsp));
+
+ obj->flag_d = (obj_buf[3] & OBJECT_LSP_FLAG_D);
+ obj->flag_s = (obj_buf[3] & OBJECT_LSP_FLAG_S);
+ obj->flag_r = (obj_buf[3] & OBJECT_LSP_FLAG_R);
+ obj->flag_a = (obj_buf[3] & OBJECT_LSP_FLAG_A);
+ obj->flag_c = (obj_buf[3] & OBJECT_LSP_FLAG_C);
+ obj->operational_status = ((obj_buf[3] >> 4) & 0x07);
+ obj->plsp_id = ((ntohl(*((uint32_t *)obj_buf)) >> 12) & 0x000fffff);
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_vendor_info(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_vendor_info *obj =
+ (struct pcep_object_vendor_info *)common_object_create(
+ hdr, sizeof(struct pcep_object_vendor_info));
+ obj->enterprise_number = ntohl(*((uint32_t *)(obj_buf)));
+ obj->enterprise_specific_info = ntohl(*((uint32_t *)(obj_buf + 4)));
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_inter_layer(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_inter_layer *obj =
+ (struct pcep_object_inter_layer *)common_object_create(
+ hdr, sizeof(struct pcep_object_inter_layer));
+ obj->flag_t = (obj_buf[3] & OBJECT_INTER_LAYER_FLAG_T);
+ obj->flag_m = (obj_buf[3] & OBJECT_INTER_LAYER_FLAG_M);
+ obj->flag_i = (obj_buf[3] & OBJECT_INTER_LAYER_FLAG_I);
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_switch_layer(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_switch_layer *obj =
+ (struct pcep_object_switch_layer *)common_object_create(
+ hdr, sizeof(struct pcep_object_switch_layer));
+ obj->switch_layer_rows = dll_initialize();
+ int num_rows = ((hdr->encoded_object_length - 4) / 4);
+ uint8_t buf_index = 0;
+
+ int i = 0;
+ for (; i < num_rows; i++) {
+ struct pcep_object_switch_layer_row *row = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_object_switch_layer_row));
+ row->lsp_encoding_type = obj_buf[buf_index];
+ row->switching_type = obj_buf[buf_index + 1];
+ row->flag_i =
+ (obj_buf[buf_index + 3] & OBJECT_SWITCH_LAYER_FLAG_I);
+ dll_append(obj->switch_layer_rows, row);
+
+ buf_index += LENGTH_1WORD;
+ }
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_req_adap_cap(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_req_adap_cap *obj =
+ (struct pcep_object_req_adap_cap *)common_object_create(
+ hdr, sizeof(struct pcep_object_req_adap_cap));
+
+ obj->switching_capability = obj_buf[0];
+ obj->encoding = obj_buf[1];
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_server_ind(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_server_indication *obj =
+ (struct pcep_object_server_indication *)common_object_create(
+ hdr, sizeof(struct pcep_object_server_indication));
+
+ obj->switching_capability = obj_buf[0];
+ obj->encoding = obj_buf[1];
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_objective_function(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_objective_function *obj =
+ (struct pcep_object_objective_function *)common_object_create(
+ hdr, sizeof(struct pcep_object_objective_function));
+
+ uint16_t *uint16_ptr = (uint16_t *)obj_buf;
+ obj->of_code = ntohs(*uint16_ptr);
+
+ return (struct pcep_object_header *)obj;
+}
+
+void set_ro_subobj_fields(struct pcep_object_ro_subobj *subobj, bool flag_l,
+ uint8_t subobj_type)
+{
+ subobj->flag_subobj_loose_hop = flag_l;
+ subobj->ro_subobj_type = subobj_type;
+}
+
+void decode_ipv6(const uint32_t *src, struct in6_addr *dst_ipv6)
+{
+ memcpy(dst_ipv6, src, sizeof(struct in6_addr));
+}
+struct pcep_object_header *pcep_decode_obj_ro(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_ro *obj =
+ (struct pcep_object_ro *)common_object_create(
+ hdr, sizeof(struct pcep_object_ro));
+ obj->sub_objects = dll_initialize();
+
+ /* RO Subobject format
+ *
+ * 0 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------//----------------+
+ * |L| Type | Length | (Subobject contents) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------//----------------+
+ */
+
+ uint16_t read_count = 0;
+ int num_sub_objects = 1;
+ uint32_t *uint32_ptr;
+ uint16_t obj_body_length =
+ hdr->encoded_object_length - OBJECT_HEADER_LENGTH;
+
+ while ((obj_body_length - read_count) > OBJECT_RO_SUBOBJ_HEADER_LENGTH
+ && num_sub_objects < MAX_ITERATIONS) {
+ num_sub_objects++;
+ /* Read the Sub-Object Header */
+ bool flag_l = (obj_buf[read_count] & 0x80);
+ uint8_t subobj_type = (obj_buf[read_count++] & 0x7f);
+ uint8_t subobj_length = obj_buf[read_count++];
+
+ if (subobj_length <= OBJECT_RO_SUBOBJ_HEADER_LENGTH) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid ro subobj type [%d] length [%d]",
+ __func__, subobj_type, subobj_length);
+ pceplib_free(PCEPLIB_MESSAGES, obj);
+ return NULL;
+ }
+
+ switch (subobj_type) {
+ case RO_SUBOBJ_TYPE_IPV4: {
+ struct pcep_ro_subobj_ipv4 *ipv4 = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_ro_subobj_ipv4));
+ ipv4->ro_subobj.flag_subobj_loose_hop = flag_l;
+ ipv4->ro_subobj.ro_subobj_type = subobj_type;
+ uint32_ptr = (uint32_t *)(obj_buf + read_count);
+ ipv4->ip_addr.s_addr = *uint32_ptr;
+ read_count += LENGTH_1WORD;
+ ipv4->prefix_length = obj_buf[read_count++];
+ ipv4->flag_local_protection =
+ (obj_buf[read_count++]
+ & OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT);
+
+ dll_append(obj->sub_objects, ipv4);
+ } break;
+
+ case RO_SUBOBJ_TYPE_IPV6: {
+ struct pcep_ro_subobj_ipv6 *ipv6 = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_ro_subobj_ipv6));
+ ipv6->ro_subobj.flag_subobj_loose_hop = flag_l;
+ ipv6->ro_subobj.ro_subobj_type = subobj_type;
+ decode_ipv6((uint32_t *)obj_buf, &ipv6->ip_addr);
+ read_count += LENGTH_4WORDS;
+ ipv6->prefix_length = obj_buf[read_count++];
+ ipv6->flag_local_protection =
+ (obj_buf[read_count++]
+ & OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT);
+
+ dll_append(obj->sub_objects, ipv6);
+ } break;
+
+ case RO_SUBOBJ_TYPE_LABEL: {
+ struct pcep_ro_subobj_32label *label = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_ro_subobj_32label));
+ label->ro_subobj.flag_subobj_loose_hop = flag_l;
+ label->ro_subobj.ro_subobj_type = subobj_type;
+ label->flag_global_label =
+ (obj_buf[read_count++]
+ & OBJECT_SUBOBJ_LABEL_FLAG_GLOGAL);
+ label->class_type = obj_buf[read_count++];
+ label->label = ntohl(obj_buf[read_count]);
+ read_count += LENGTH_1WORD;
+
+ dll_append(obj->sub_objects, label);
+ } break;
+
+ case RO_SUBOBJ_TYPE_UNNUM: {
+ struct pcep_ro_subobj_unnum *unum = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_ro_subobj_unnum));
+ unum->ro_subobj.flag_subobj_loose_hop = flag_l;
+ unum->ro_subobj.ro_subobj_type = subobj_type;
+ set_ro_subobj_fields(
+ (struct pcep_object_ro_subobj *)unum, flag_l,
+ subobj_type);
+ uint32_ptr = (uint32_t *)(obj_buf + read_count);
+ unum->interface_id = ntohl(uint32_ptr[0]);
+ unum->router_id.s_addr = uint32_ptr[1];
+ read_count += 2;
+
+ dll_append(obj->sub_objects, unum);
+ } break;
+
+ case RO_SUBOBJ_TYPE_ASN: {
+ struct pcep_ro_subobj_asn *asn = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_ro_subobj_asn));
+ asn->ro_subobj.flag_subobj_loose_hop = flag_l;
+ asn->ro_subobj.ro_subobj_type = subobj_type;
+ uint16_t *uint16_ptr =
+ (uint16_t *)(obj_buf + read_count);
+ asn->asn = ntohs(*uint16_ptr);
+ read_count += 2;
+
+ dll_append(obj->sub_objects, asn);
+ } break;
+
+ case RO_SUBOBJ_TYPE_SR: {
+ /* SR-ERO subobject format
+ *
+ * 0 1 2 3 0 1 2 3 4
+ * 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |L| Type=36 | Length | NT | Flags
+ * |F|S|C|M|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SID (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * // NAI (variable, optional) //
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ struct pcep_ro_subobj_sr *sr_subobj = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_ro_subobj_sr));
+ sr_subobj->ro_subobj.flag_subobj_loose_hop = flag_l;
+ sr_subobj->ro_subobj.ro_subobj_type = subobj_type;
+ dll_append(obj->sub_objects, sr_subobj);
+
+ sr_subobj->nai_list = dll_initialize();
+ sr_subobj->nai_type =
+ ((obj_buf[read_count++] >> 4) & 0x0f);
+ sr_subobj->flag_f =
+ (obj_buf[read_count] & OBJECT_SUBOBJ_SR_FLAG_F);
+ sr_subobj->flag_s =
+ (obj_buf[read_count] & OBJECT_SUBOBJ_SR_FLAG_S);
+ sr_subobj->flag_c =
+ (obj_buf[read_count] & OBJECT_SUBOBJ_SR_FLAG_C);
+ sr_subobj->flag_m =
+ (obj_buf[read_count] & OBJECT_SUBOBJ_SR_FLAG_M);
+ read_count++;
+
+ /* If the sid_absent flag is true, then dont decode the
+ * sid */
+ uint32_ptr = (uint32_t *)(obj_buf + read_count);
+ if (sr_subobj->flag_s == false) {
+ sr_subobj->sid = ntohl(*uint32_ptr);
+ read_count += LENGTH_1WORD;
+ uint32_ptr += 1;
+ }
+
+ switch (sr_subobj->nai_type) {
+ case PCEP_SR_SUBOBJ_NAI_IPV4_NODE: {
+ struct in_addr *ipv4 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = *uint32_ptr;
+ dll_append(sr_subobj->nai_list, ipv4);
+ read_count += LENGTH_1WORD;
+ } break;
+
+ case PCEP_SR_SUBOBJ_NAI_IPV6_NODE: {
+ struct in6_addr *ipv6 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in6_addr));
+ decode_ipv6(uint32_ptr, ipv6);
+ dll_append(sr_subobj->nai_list, ipv6);
+ read_count += LENGTH_4WORDS;
+ } break;
+
+ case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY: {
+ struct in_addr *ipv4 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[0];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ ipv4 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[1];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ ipv4 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[2];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ ipv4 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[3];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ read_count += LENGTH_4WORDS;
+ } break;
+
+ case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY: {
+ struct in_addr *ipv4 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[0];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ ipv4 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[1];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ read_count += LENGTH_2WORDS;
+ } break;
+
+ case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY: {
+ struct in6_addr *ipv6 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in6_addr));
+ decode_ipv6(uint32_ptr, ipv6);
+ dll_append(sr_subobj->nai_list, ipv6);
+
+ ipv6 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in6_addr));
+ decode_ipv6(uint32_ptr + LENGTH_4WORDS, ipv6);
+ dll_append(sr_subobj->nai_list, ipv6);
+
+ read_count += LENGTH_8WORDS;
+ } break;
+
+ case PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY: {
+ struct in6_addr *ipv6 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in6_addr));
+ decode_ipv6(uint32_ptr, ipv6);
+ dll_append(sr_subobj->nai_list, ipv6);
+
+ struct in_addr *ipv4 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[LENGTH_4WORDS];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ ipv6 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in6_addr));
+ decode_ipv6(uint32_ptr + LENGTH_5WORDS, ipv6);
+ dll_append(sr_subobj->nai_list, ipv6);
+
+ ipv4 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[LENGTH_9WORDS];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ read_count += LENGTH_10WORDS;
+ } break;
+
+ case PCEP_SR_SUBOBJ_NAI_ABSENT:
+ default:
+ break;
+ }
+ } break;
+
+ default:
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_decode_obj_ro skipping unrecognized sub-object type [%d]",
+ __func__, subobj_type);
+ read_count += subobj_length;
+ break;
+ }
+ }
+
+ return (struct pcep_object_header *)obj;
+}
diff --git a/pceplib/pcep_msg_tlvs.c b/pceplib/pcep_msg_tlvs.c
new file mode 100644
index 000000000..890da9517
--- /dev/null
+++ b/pceplib/pcep_msg_tlvs.c
@@ -0,0 +1,464 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * This is the implementation of a High Level PCEP message object TLV API.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pcep_msg_tlvs.h"
+#include "pcep_msg_encoding.h"
+#include "pcep_utils_memory.h"
+
+static struct pcep_object_tlv_header *
+pcep_tlv_common_create(enum pcep_object_tlv_types type, uint16_t size)
+{
+ struct pcep_object_tlv_header *tlv =
+ pceplib_malloc(PCEPLIB_MESSAGES, size);
+ memset(tlv, 0, size);
+ tlv->type = type;
+
+ return tlv;
+}
+
+/*
+ * Open Object TLVs
+ */
+
+struct pcep_object_tlv_stateful_pce_capability *
+pcep_tlv_create_stateful_pce_capability(
+ bool flag_u_lsp_update_capability, bool flag_s_include_db_version,
+ bool flag_i_lsp_instantiation_capability, bool flag_t_triggered_resync,
+ bool flag_d_delta_lsp_sync, bool flag_f_triggered_initial_sync)
+{
+ struct pcep_object_tlv_stateful_pce_capability *tlv =
+ (struct pcep_object_tlv_stateful_pce_capability *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY,
+ sizeof(struct
+ pcep_object_tlv_stateful_pce_capability));
+ tlv->flag_u_lsp_update_capability = flag_u_lsp_update_capability;
+ tlv->flag_s_include_db_version = flag_s_include_db_version;
+ tlv->flag_i_lsp_instantiation_capability =
+ flag_i_lsp_instantiation_capability;
+ tlv->flag_t_triggered_resync = flag_t_triggered_resync;
+ tlv->flag_d_delta_lsp_sync = flag_d_delta_lsp_sync;
+ tlv->flag_f_triggered_initial_sync = flag_f_triggered_initial_sync;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_lsp_db_version *
+pcep_tlv_create_lsp_db_version(uint64_t lsp_db_version)
+{
+ struct pcep_object_tlv_lsp_db_version *tlv =
+ (struct pcep_object_tlv_lsp_db_version *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION,
+ sizeof(struct pcep_object_tlv_lsp_db_version));
+ tlv->lsp_db_version = lsp_db_version;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_speaker_entity_identifier *
+pcep_tlv_create_speaker_entity_id(double_linked_list *speaker_entity_id_list)
+{
+ if (speaker_entity_id_list == NULL) {
+ return NULL;
+ }
+
+ if (speaker_entity_id_list->num_entries == 0) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_speaker_entity_identifier *tlv =
+ (struct pcep_object_tlv_speaker_entity_identifier *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID,
+ sizeof(struct
+ pcep_object_tlv_speaker_entity_identifier));
+ tlv->speaker_entity_id_list = speaker_entity_id_list;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_path_setup_type *
+pcep_tlv_create_path_setup_type(uint8_t pst)
+{
+ struct pcep_object_tlv_path_setup_type *tlv =
+ (struct pcep_object_tlv_path_setup_type *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE,
+ sizeof(struct pcep_object_tlv_path_setup_type));
+ tlv->path_setup_type = pst;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_path_setup_type_capability *
+pcep_tlv_create_path_setup_type_capability(double_linked_list *pst_list,
+ double_linked_list *sub_tlv_list)
+{
+ if (pst_list == NULL) {
+ return NULL;
+ }
+
+ if (pst_list->num_entries == 0) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_path_setup_type_capability *tlv =
+ (struct pcep_object_tlv_path_setup_type_capability *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY,
+ sizeof(struct
+ pcep_object_tlv_path_setup_type_capability));
+
+ tlv->pst_list = pst_list;
+ tlv->sub_tlv_list = sub_tlv_list;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_sr_pce_capability *
+pcep_tlv_create_sr_pce_capability(bool flag_n, bool flag_x,
+ uint8_t max_sid_depth)
+{
+ struct pcep_object_tlv_sr_pce_capability *tlv =
+ (struct pcep_object_tlv_sr_pce_capability *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY,
+ sizeof(struct
+ pcep_object_tlv_sr_pce_capability));
+ tlv->flag_n = flag_n;
+ tlv->flag_x = flag_x;
+ tlv->max_sid_depth = max_sid_depth;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_of_list *
+pcep_tlv_create_of_list(double_linked_list *of_list)
+{
+ if (of_list == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_of_list *tlv =
+ (struct pcep_object_tlv_of_list *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST,
+ sizeof(struct pcep_object_tlv_of_list));
+
+ tlv->of_list = of_list;
+
+ return tlv;
+}
+
+/*
+ * LSP Object TLVs
+ */
+
+struct pcep_object_tlv_ipv4_lsp_identifier *
+pcep_tlv_create_ipv4_lsp_identifiers(struct in_addr *ipv4_tunnel_sender,
+ struct in_addr *ipv4_tunnel_endpoint,
+ uint16_t lsp_id, uint16_t tunnel_id,
+ struct in_addr *extended_tunnel_id)
+{
+ if (ipv4_tunnel_sender == NULL || ipv4_tunnel_endpoint == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_ipv4_lsp_identifier *tlv =
+ (struct pcep_object_tlv_ipv4_lsp_identifier *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS,
+ sizeof(struct
+ pcep_object_tlv_ipv4_lsp_identifier));
+ tlv->ipv4_tunnel_sender.s_addr = ipv4_tunnel_sender->s_addr;
+ tlv->ipv4_tunnel_endpoint.s_addr = ipv4_tunnel_endpoint->s_addr;
+ tlv->lsp_id = lsp_id;
+ tlv->tunnel_id = tunnel_id;
+ tlv->extended_tunnel_id.s_addr =
+ (extended_tunnel_id == NULL ? INADDR_ANY
+ : extended_tunnel_id->s_addr);
+
+ return tlv;
+}
+
+struct pcep_object_tlv_ipv6_lsp_identifier *
+pcep_tlv_create_ipv6_lsp_identifiers(struct in6_addr *ipv6_tunnel_sender,
+ struct in6_addr *ipv6_tunnel_endpoint,
+ uint16_t lsp_id, uint16_t tunnel_id,
+ struct in6_addr *extended_tunnel_id)
+{
+ if (ipv6_tunnel_sender == NULL || ipv6_tunnel_endpoint == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_ipv6_lsp_identifier *tlv =
+ (struct pcep_object_tlv_ipv6_lsp_identifier *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS,
+ sizeof(struct
+ pcep_object_tlv_ipv6_lsp_identifier));
+
+ memcpy(&tlv->ipv6_tunnel_sender, ipv6_tunnel_sender,
+ sizeof(struct in6_addr));
+
+ tlv->tunnel_id = tunnel_id;
+ tlv->lsp_id = lsp_id;
+
+ memcpy(&tlv->extended_tunnel_id, extended_tunnel_id,
+ sizeof(struct in6_addr));
+
+ memcpy(&tlv->ipv6_tunnel_endpoint, ipv6_tunnel_endpoint,
+ sizeof(struct in6_addr));
+
+ return tlv;
+}
+
+struct pcep_object_tlv_symbolic_path_name *
+pcep_tlv_create_symbolic_path_name(const char *symbolic_path_name,
+ uint16_t symbolic_path_name_length)
+{
+ /* symbolic_path_name_length should NOT include the null terminator and
+ * cannot be zero */
+ if (symbolic_path_name == NULL || symbolic_path_name_length == 0) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_symbolic_path_name *tlv =
+ (struct pcep_object_tlv_symbolic_path_name *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME,
+ sizeof(struct
+ pcep_object_tlv_symbolic_path_name));
+
+ uint16_t length = (symbolic_path_name_length > MAX_SYMBOLIC_PATH_NAME)
+ ? MAX_SYMBOLIC_PATH_NAME
+ : symbolic_path_name_length;
+ memcpy(tlv->symbolic_path_name, symbolic_path_name, length);
+ tlv->symbolic_path_name_length = length;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_lsp_error_code *
+pcep_tlv_create_lsp_error_code(enum pcep_tlv_lsp_error_codes lsp_error_code)
+{
+ struct pcep_object_tlv_lsp_error_code *tlv =
+ (struct pcep_object_tlv_lsp_error_code *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE,
+ sizeof(struct pcep_object_tlv_lsp_error_code));
+ tlv->lsp_error_code = lsp_error_code;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_rsvp_error_spec *
+pcep_tlv_create_rsvp_ipv4_error_spec(struct in_addr *error_node_ip,
+ uint8_t error_code, uint16_t error_value)
+{
+ if (error_node_ip == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_rsvp_error_spec *tlv =
+ (struct pcep_object_tlv_rsvp_error_spec *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC,
+ sizeof(struct pcep_object_tlv_rsvp_error_spec));
+
+ tlv->c_type = RSVP_ERROR_SPEC_IPV4_CTYPE;
+ tlv->class_num = RSVP_ERROR_SPEC_CLASS_NUM;
+ tlv->error_code = error_code;
+ tlv->error_value = error_value;
+ tlv->error_spec_ip.ipv4_error_node_address.s_addr =
+ error_node_ip->s_addr;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_rsvp_error_spec *
+pcep_tlv_create_rsvp_ipv6_error_spec(struct in6_addr *error_node_ip,
+ uint8_t error_code, uint16_t error_value)
+{
+ if (error_node_ip == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_rsvp_error_spec *tlv =
+ (struct pcep_object_tlv_rsvp_error_spec *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC,
+ sizeof(struct pcep_object_tlv_rsvp_error_spec));
+
+ tlv->c_type = RSVP_ERROR_SPEC_IPV6_CTYPE;
+ tlv->class_num = RSVP_ERROR_SPEC_CLASS_NUM;
+ tlv->error_code = error_code;
+ tlv->error_value = error_value;
+ memcpy(&tlv->error_spec_ip, error_node_ip, sizeof(struct in6_addr));
+
+ return tlv;
+}
+
+struct pcep_object_tlv_nopath_vector *
+pcep_tlv_create_nopath_vector(uint32_t error_code)
+{
+ struct pcep_object_tlv_nopath_vector *tlv =
+ (struct pcep_object_tlv_nopath_vector *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR,
+ sizeof(struct pcep_object_tlv_nopath_vector));
+
+ tlv->error_code = error_code;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_vendor_info *
+pcep_tlv_create_vendor_info(uint32_t enterprise_number,
+ uint32_t enterprise_specific_info)
+{
+ struct pcep_object_tlv_vendor_info *tlv =
+ (struct pcep_object_tlv_vendor_info *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_VENDOR_INFO,
+ sizeof(struct pcep_object_tlv_vendor_info));
+
+ tlv->enterprise_number = enterprise_number;
+ tlv->enterprise_specific_info = enterprise_specific_info;
+
+ return tlv;
+}
+
+/*
+ * SRPAG (SR Association Group) TLVs
+ */
+
+struct pcep_object_tlv_srpag_pol_id *
+pcep_tlv_create_srpag_pol_id_ipv4(uint32_t color, struct in_addr *ipv4)
+{
+ struct pcep_object_tlv_srpag_pol_id *tlv =
+ (struct pcep_object_tlv_srpag_pol_id *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID,
+ sizeof(struct pcep_object_tlv_srpag_pol_id));
+ tlv->color = color;
+ tlv->is_ipv4 = true;
+ memcpy(&tlv->end_point.ipv4.s_addr, ipv4, sizeof(struct in_addr));
+
+ return tlv;
+}
+
+struct pcep_object_tlv_srpag_pol_id *
+pcep_tlv_create_srpag_pol_id_ipv6(uint32_t color, struct in6_addr *ipv6)
+{
+ struct pcep_object_tlv_srpag_pol_id *tlv =
+ (struct pcep_object_tlv_srpag_pol_id *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID,
+ sizeof(struct pcep_object_tlv_srpag_pol_id));
+ tlv->color = color;
+ tlv->is_ipv4 = false;
+ memcpy(&tlv->end_point.ipv6, ipv6, sizeof(struct in6_addr));
+
+ return tlv;
+}
+
+
+struct pcep_object_tlv_srpag_pol_name *
+pcep_tlv_create_srpag_pol_name(const char *pol_name, uint16_t pol_name_length)
+{
+ if (pol_name == NULL) {
+ return NULL;
+ }
+ struct pcep_object_tlv_srpag_pol_name *tlv =
+ (struct pcep_object_tlv_srpag_pol_name *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME,
+ sizeof(struct pcep_object_tlv_srpag_pol_name));
+ uint16_t length =
+ (normalize_pcep_tlv_length(pol_name_length) > MAX_POLICY_NAME)
+ ? MAX_POLICY_NAME
+ : pol_name_length;
+ memcpy(tlv->name, pol_name, pol_name_length);
+ tlv->name_length = length;
+
+ return tlv;
+}
+struct pcep_object_tlv_srpag_cp_id *
+pcep_tlv_create_srpag_cp_id(uint8_t proto_origin, uint32_t asn,
+ struct in6_addr *in6_addr_with_mapped_ipv4,
+ uint32_t discriminator)
+{
+ if (!in6_addr_with_mapped_ipv4) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_srpag_cp_id *tlv =
+ (struct pcep_object_tlv_srpag_cp_id *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID,
+ sizeof(struct pcep_object_tlv_srpag_cp_id));
+ tlv->proto = proto_origin;
+ tlv->orig_asn = asn;
+ memcpy(&(tlv->orig_addres), in6_addr_with_mapped_ipv4,
+ sizeof(*in6_addr_with_mapped_ipv4));
+ tlv->discriminator = discriminator;
+
+ return tlv;
+}
+struct pcep_object_tlv_srpag_cp_pref *
+pcep_tlv_create_srpag_cp_pref(uint32_t pref)
+{
+
+ struct pcep_object_tlv_srpag_cp_pref *tlv =
+ (struct pcep_object_tlv_srpag_cp_pref *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE,
+ sizeof(struct pcep_object_tlv_srpag_cp_pref));
+ tlv->preference = pref;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_arbitrary *
+pcep_tlv_create_tlv_arbitrary(const char *data, uint16_t data_length,
+ int tlv_id)
+{
+ if (data == NULL || data_length == 0) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_arbitrary *tlv =
+ (struct pcep_object_tlv_arbitrary *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_ARBITRARY,
+ sizeof(struct pcep_object_tlv_arbitrary));
+
+ uint16_t length = (data_length > MAX_ARBITRARY_SIZE)
+ ? MAX_ARBITRARY_SIZE
+ : data_length;
+ memcpy(tlv->data, data, data_length);
+ tlv->data_length = length;
+ tlv->arbitraty_type = tlv_id;
+
+ return tlv;
+}
diff --git a/pceplib/pcep_msg_tlvs.h b/pceplib/pcep_msg_tlvs.h
new file mode 100644
index 000000000..5197201e4
--- /dev/null
+++ b/pceplib/pcep_msg_tlvs.h
@@ -0,0 +1,380 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ */
+
+
+/*
+ * This is a High Level PCEP message object TLV API.
+ */
+
+#ifndef PCEP_TLVS_H_
+#define PCEP_TLVS_H_
+
+#include <arpa/inet.h>
+#include <stdint.h>
+
+#include "pcep.h"
+#include "pcep_utils_double_linked_list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Regarding memory usage:
+ * When creating TLVs, any TLVs passed into messages or objects with these APIs
+ * will be free'd when the the enclosing pcep_message is free'd. That includes
+ * the double_linked_list's. So, just create the objects and TLVs, put them in
+ * their double_linked_list's, and everything will be managed internally. The
+ * enclosing message will be deleted by pcep_msg_free_message() or
+ * pcep_msg_free_message_list() which, * in turn will call one of:
+ * pcep_obj_free_object() and pcep_obj_free_tlv().
+ * For received messages, call pcep_msg_free_message() to free them.
+ */
+
+/* These numbers can be found here:
+ * https://www.iana.org/assignments/pcep/pcep.xhtml */
+enum pcep_object_tlv_types {
+ PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR = 1,
+ PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST = 4, /* RFC 5541 */
+ PCEP_OBJ_TLV_TYPE_VENDOR_INFO = 7, /* RFC 7470 */
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY = 16, /* RFC 8231 */
+ PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME = 17, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS = 18, /* RFC 8231 */
+ PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS = 19, /* RFC 8231 */
+ PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE = 20, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC = 21, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION = 23, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID = 24, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY =
+ 26, /* draft-ietf-pce-segment-routing-16 */
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE = 28, /* RFC 8408 */
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY =
+ 34, /* RFC 8408, draft-ietf-pce-segment-routing-16 */
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID =
+ 60, /*TDB2 draft-barth-pce-segment-routing-policy-cp-04 */
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME =
+ 61, /*TDB3 draft-barth-pce-segment-routing-policy-cp-04 */
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID =
+ 62, /*TDB4 draft-barth-pce-segment-routing-policy-cp-04 */
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE =
+ 63, /*TDB5 draft-barth-pce-segment-routing-policy-cp-04 */
+ PCEP_OBJ_TLV_TYPE_UNKNOWN = 128,
+ PCEP_OBJ_TLV_TYPE_ARBITRARY =
+ 65533 /* Max IANA To write arbitrary data */
+};
+
+struct pcep_object_tlv_header {
+ enum pcep_object_tlv_types type;
+ /* Pointer into encoded_message field from the pcep_message */
+ const uint8_t *encoded_tlv;
+ uint16_t encoded_tlv_length;
+};
+
+/* STATEFUL-PCE-CAPABILITY TLV, Used in Open Object. RFCs: 8231, 8232, 8281 */
+#define TLV_STATEFUL_PCE_CAP_FLAG_U 0x01
+#define TLV_STATEFUL_PCE_CAP_FLAG_S 0x02
+#define TLV_STATEFUL_PCE_CAP_FLAG_I 0x04
+#define TLV_STATEFUL_PCE_CAP_FLAG_T 0x08
+#define TLV_STATEFUL_PCE_CAP_FLAG_D 0x10
+#define TLV_STATEFUL_PCE_CAP_FLAG_F 0x20
+
+struct pcep_object_tlv_stateful_pce_capability {
+ struct pcep_object_tlv_header header;
+ bool flag_u_lsp_update_capability; /* RFC 8231 */
+ bool flag_s_include_db_version; /* RFC 8232 */
+ bool flag_i_lsp_instantiation_capability; /* RFC 8281 */
+ bool flag_t_triggered_resync; /* RFC 8232 */
+ bool flag_d_delta_lsp_sync; /* RFC 8232 */
+ bool flag_f_triggered_initial_sync; /* RFC 8232 */
+};
+
+/* NOPATH-VECTOR TLV, Used in the Reply NoPath Object. */
+struct pcep_object_tlv_nopath_vector {
+ struct pcep_object_tlv_header header;
+ uint32_t error_code;
+};
+
+/* STATEFUL-PCE-CAPABILITY TLV, Used in Open Object. RFCs: 8232 */
+struct pcep_object_tlv_lsp_db_version {
+ struct pcep_object_tlv_header header;
+ uint64_t lsp_db_version;
+};
+
+/* Speaker Entity Identifier TLV, Used in Open Object. RFCs: 8232 */
+struct pcep_object_tlv_speaker_entity_identifier {
+ struct pcep_object_tlv_header header;
+ double_linked_list *speaker_entity_id_list; /* list of uint32_t speaker
+ entity ids */
+};
+
+/* Ipv4 LSP Identifier TLV, Used in LSP Object. RFCs: 8231 */
+struct pcep_object_tlv_ipv4_lsp_identifier {
+ struct pcep_object_tlv_header header;
+ struct in_addr ipv4_tunnel_sender;
+ uint16_t lsp_id;
+ uint16_t tunnel_id;
+ struct in_addr extended_tunnel_id;
+ struct in_addr ipv4_tunnel_endpoint;
+};
+
+/* Ipv6 LSP Identifier TLV, Used in LSP Object. RFCs: 8231 */
+struct pcep_object_tlv_ipv6_lsp_identifier {
+ struct pcep_object_tlv_header header;
+ struct in6_addr ipv6_tunnel_sender;
+ uint16_t lsp_id;
+ uint16_t tunnel_id;
+ struct in6_addr extended_tunnel_id;
+ struct in6_addr ipv6_tunnel_endpoint;
+};
+
+/* Symbolic Path Name TLV, Used in LSP Object. RFCs: 8231 */
+#define MAX_SYMBOLIC_PATH_NAME 256
+
+struct pcep_object_tlv_symbolic_path_name {
+ struct pcep_object_tlv_header header;
+ uint16_t symbolic_path_name_length;
+ char symbolic_path_name[MAX_SYMBOLIC_PATH_NAME];
+};
+
+/* LSP Error Code TLV, Used in LSP Object. RFCs: 8231 */
+enum pcep_tlv_lsp_error_codes {
+ PCEP_TLV_LSP_ERROR_CODE_UNKNOWN = 1,
+ PCEP_TLV_LSP_ERROR_CODE_LSP_LIMIT_REACHED = 2,
+ PCEP_TLV_LSP_ERROR_CODE_TOO_MANY_PENDING_LSP_UPDATES = 3,
+ PCEP_TLV_LSP_ERROR_CODE_UNACCEPTABLE_PARAMS = 4,
+ PCEP_TLV_LSP_ERROR_CODE_INTERNAL_ERROR = 5,
+ PCEP_TLV_LSP_ERROR_CODE_LSP_BROUGHT_DOWN = 6,
+ PCEP_TLV_LSP_ERROR_CODE_LSP_PREEMPTED = 7,
+ PCEP_TLV_LSP_ERROR_CODE_RSVP_SIGNALING_ERROR = 8,
+};
+
+struct pcep_object_tlv_lsp_error_code {
+ struct pcep_object_tlv_header header;
+ enum pcep_tlv_lsp_error_codes lsp_error_code;
+};
+
+/* Path Setup Type TLV, Used in RP and SRP Object. RFCs: 8408,
+ * draft-ietf-pce-segment-routing-16 */
+#define SR_TE_PST 1
+
+struct pcep_object_tlv_path_setup_type {
+ struct pcep_object_tlv_header header;
+ uint8_t path_setup_type;
+};
+
+/* Path Setup Type Capability TLV, Used in Open Object. RFCs: 8408,
+ * draft-ietf-pce-segment-routing-16 */
+struct pcep_object_tlv_path_setup_type_capability {
+ struct pcep_object_tlv_header header;
+ double_linked_list *pst_list; /* list of uint8_t PSTs */
+ double_linked_list *sub_tlv_list; /* list of sub_tlvs */
+};
+
+/* SR PCE Capability sub-TLV, Used in Open Object. RFCs:
+ * draft-ietf-pce-segment-routing-16 */
+#define TLV_SR_PCE_CAP_FLAG_X 0x01
+#define TLV_SR_PCE_CAP_FLAG_N 0x02
+
+struct pcep_object_tlv_sr_pce_capability {
+ struct pcep_object_tlv_header header;
+ bool flag_n;
+ bool flag_x;
+ uint8_t max_sid_depth;
+};
+
+
+/* RSVP Error Spec TLV, Used in LSP Object. RFCs: 8231, 2205 */
+#define RSVP_ERROR_SPEC_IPV4_CTYPE 1
+#define RSVP_ERROR_SPEC_IPV6_CTYPE 2
+#define RSVP_ERROR_SPEC_CLASS_NUM 6
+
+struct pcep_object_tlv_rsvp_error_spec {
+ struct pcep_object_tlv_header header;
+ uint8_t class_num;
+ uint8_t c_type;
+ uint8_t error_code;
+ uint16_t error_value;
+ /* Use the c_type to determine which union entry to use */
+ union error_spec_ip {
+ struct in_addr ipv4_error_node_address;
+ struct in6_addr ipv6_error_node_address;
+ } error_spec_ip;
+};
+
+/* SR Policy Identifier TLV Used in Association Object.
+ * draft-barth-pce-segment-routing-policy-cp-04*/
+struct pcep_object_tlv_srpag_pol_id {
+ struct pcep_object_tlv_header header;
+ uint32_t color;
+ bool is_ipv4;
+ union end_point_ {
+ struct in_addr ipv4;
+ struct in6_addr ipv6;
+ } end_point;
+};
+
+/*draft-ietf-spring-segment-routing-policy-06*/
+#define MAX_POLICY_NAME 256
+
+/* SR Policy Name TLV Used in Association Object.
+ * draft-barth-pce-segment-routing-policy-cp-04*/
+struct pcep_object_tlv_srpag_pol_name {
+ struct pcep_object_tlv_header header;
+ uint16_t name_length;
+ char name[MAX_POLICY_NAME];
+};
+
+/* SR Candidate Path Id TLV Used in Association Object.
+ * draft-barth-pce-segment-routing-policy-cp-04*/
+struct pcep_object_tlv_srpag_cp_id {
+ struct pcep_object_tlv_header header;
+ uint8_t proto;
+ uint32_t orig_asn;
+ struct in6_addr orig_addres; /*With ipv4 embedded*/
+ uint32_t discriminator;
+};
+
+/* SR Candidate Preference TLV Used in Association Object.
+ * draft-barth-pce-segment-routing-policy-cp-04*/
+struct pcep_object_tlv_srpag_cp_pref {
+ struct pcep_object_tlv_header header;
+ uint32_t preference;
+};
+
+struct pcep_object_tlv_vendor_info {
+ struct pcep_object_tlv_header header;
+ uint32_t enterprise_number;
+ uint32_t enterprise_specific_info;
+};
+
+/* arbitrary TLV 65535 */
+#define MAX_ARBITRARY_SIZE 256
+struct pcep_object_tlv_arbitrary {
+ struct pcep_object_tlv_header header;
+ enum pcep_object_tlv_types arbitraty_type;
+ uint16_t data_length;
+ char data[MAX_ARBITRARY_SIZE];
+};
+
+/* Objective Functions List RFC 5541
+ * At least the following 6 OF codes must be supported */
+enum objective_function_codes {
+ PCEP_OF_CODE_MINIMUM_COST_PATH = 1, /* MCP */
+ PCEP_OF_CODE_MINIMUM_LOAD_PATH = 2, /* MLP */
+ PCEP_OF_CODE_MAXIMUM_BW_PATH = 3, /* MBP */
+ PCEP_OF_CODE_MINIMIZE_AGGR_BW_CONSUMPTION = 4, /* MBC */
+ PCEP_OF_CODE_MINIMIZE_MOST_LOADED_LINK = 5, /* MLL */
+ PCEP_OF_CODE_MINIMIZE_CUMULATIVE_COST_PATHS = 6, /* MCC */
+};
+
+struct pcep_object_tlv_of_list {
+ struct pcep_object_tlv_header header;
+ double_linked_list *of_list; /* list of uint16_t OF code points */
+};
+
+/*
+ * TLV creation functions
+ */
+
+/*
+ * Open Object TLVs
+ */
+
+struct pcep_object_tlv_stateful_pce_capability *
+pcep_tlv_create_stateful_pce_capability(
+ bool flag_u_lsp_update_capability, bool flag_s_include_db_version,
+ bool flag_i_lsp_instantiation_capability, bool flag_t_triggered_resync,
+ bool flag_d_delta_lsp_sync, bool flag_f_triggered_initial_sync);
+struct pcep_object_tlv_lsp_db_version *
+pcep_tlv_create_lsp_db_version(uint64_t lsp_db_version);
+struct pcep_object_tlv_speaker_entity_identifier *
+pcep_tlv_create_speaker_entity_id(double_linked_list *speaker_entity_id_list);
+struct pcep_object_tlv_path_setup_type *
+pcep_tlv_create_path_setup_type(uint8_t pst);
+struct pcep_object_tlv_path_setup_type_capability *
+pcep_tlv_create_path_setup_type_capability(double_linked_list *pst_list,
+ double_linked_list *sub_tlv_list);
+struct pcep_object_tlv_sr_pce_capability *
+pcep_tlv_create_sr_pce_capability(bool flag_n, bool flag_x,
+ uint8_t max_sid_depth);
+struct pcep_object_tlv_of_list *
+pcep_tlv_create_of_list(double_linked_list *of_list);
+
+/*
+ * LSP Object TLVs
+ */
+
+struct pcep_object_tlv_ipv4_lsp_identifier *
+pcep_tlv_create_ipv4_lsp_identifiers(struct in_addr *ipv4_tunnel_sender,
+ struct in_addr *ipv4_tunnel_endpoint,
+ uint16_t lsp_id, uint16_t tunnel_id,
+ struct in_addr *extended_tunnel_id);
+struct pcep_object_tlv_ipv6_lsp_identifier *
+pcep_tlv_create_ipv6_lsp_identifiers(struct in6_addr *ipv6_tunnel_sender,
+ struct in6_addr *extended_tunnel_id,
+ uint16_t lsp_id, uint16_t tunnel_id,
+ struct in6_addr *ipv6_tunnel_endpoint);
+/* symbolic_path_name_length should NOT include the null terminator and cannot
+ * be zero */
+struct pcep_object_tlv_symbolic_path_name *
+pcep_tlv_create_symbolic_path_name(const char *symbolic_path_name,
+ uint16_t symbolic_path_name_length);
+struct pcep_object_tlv_lsp_error_code *
+pcep_tlv_create_lsp_error_code(enum pcep_tlv_lsp_error_codes lsp_error_code);
+struct pcep_object_tlv_rsvp_error_spec *
+pcep_tlv_create_rsvp_ipv4_error_spec(struct in_addr *error_node_ip,
+ uint8_t error_code, uint16_t error_value);
+struct pcep_object_tlv_rsvp_error_spec *
+pcep_tlv_create_rsvp_ipv6_error_spec(struct in6_addr *error_node_ip,
+ uint8_t error_code, uint16_t error_value);
+
+struct pcep_object_tlv_nopath_vector *
+pcep_tlv_create_nopath_vector(uint32_t error_code);
+struct pcep_object_tlv_vendor_info *
+pcep_tlv_create_vendor_info(uint32_t enterprise_number,
+ uint32_t enterprise_specific_info);
+
+struct pcep_object_tlv_arbitrary *
+pcep_tlv_create_tlv_arbitrary(const char *data, uint16_t data_length,
+ int tlv_id);
+/*
+ * SRPAG (SR Association Group) TLVs
+ */
+
+struct pcep_object_tlv_srpag_pol_id *
+pcep_tlv_create_srpag_pol_id_ipv4(uint32_t color, struct in_addr *ipv4);
+struct pcep_object_tlv_srpag_pol_id *
+pcep_tlv_create_srpag_pol_id_ipv6(uint32_t color, struct in6_addr *ipv6);
+struct pcep_object_tlv_srpag_pol_name *
+pcep_tlv_create_srpag_pol_name(const char *pol_name, uint16_t pol_name_length);
+struct pcep_object_tlv_srpag_cp_id *
+pcep_tlv_create_srpag_cp_id(uint8_t proto_origin, uint32_t asn,
+ struct in6_addr *in6_addr_with_mapped_ipv4,
+ uint32_t discriminator);
+struct pcep_object_tlv_srpag_cp_pref *
+pcep_tlv_create_srpag_cp_pref(uint32_t pref);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PCEP_TLVS_H_ */
diff --git a/pceplib/pcep_msg_tlvs_encoding.c b/pceplib/pcep_msg_tlvs_encoding.c
new file mode 100644
index 000000000..3322663dc
--- /dev/null
+++ b/pceplib/pcep_msg_tlvs_encoding.c
@@ -0,0 +1,1282 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Encoding and decoding for PCEP Object TLVs.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "pcep.h"
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_tlvs.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+void write_tlv_header(struct pcep_object_tlv_header *tlv_hdr,
+ uint16_t tlv_length, struct pcep_versioning *versioning,
+ uint8_t *buf);
+void pcep_decode_tlv_hdr(const uint8_t *tlv_buf,
+ struct pcep_object_tlv_header *tlv_hdr);
+
+/*
+ * forward declarations for initialize_tlv_encoders()
+ */
+uint16_t pcep_encode_tlv_no_path_vector(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t
+pcep_encode_tlv_stateful_pce_capability(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_symbolic_path_name(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t
+pcep_encode_tlv_ipv4_lsp_identifiers(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t
+pcep_encode_tlv_ipv6_lsp_identifiers(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_lsp_error_code(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_rsvp_error_spec(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_lsp_db_version(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_speaker_entity_id(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_sr_pce_capability(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_path_setup_type(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t
+pcep_encode_tlv_path_setup_type_capability(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_pol_id(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_pol_name(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_cpath_id(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_cpath_preference(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_vendor_info(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_arbitrary(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_of_list(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+typedef uint16_t (*tlv_encoder_funcptr)(struct pcep_object_tlv_header *,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+
+#define MAX_TLV_ENCODER_INDEX 65533 + 1 // 65
+
+#define PCEP_TLV_ENCODERS_ARGS \
+ struct pcep_object_tlv_header *, struct pcep_versioning *versioning, \
+ uint8_t *tlv_body_buf
+uint16_t (*const tlv_encoders[MAX_TLV_ENCODER_INDEX])(
+ PCEP_TLV_ENCODERS_ARGS) = {
+ [PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR] = pcep_encode_tlv_no_path_vector,
+ [PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY] =
+ pcep_encode_tlv_stateful_pce_capability,
+ [PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME] =
+ pcep_encode_tlv_symbolic_path_name,
+ [PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS] =
+ pcep_encode_tlv_ipv4_lsp_identifiers,
+ [PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS] =
+ pcep_encode_tlv_ipv6_lsp_identifiers,
+ [PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE] = pcep_encode_tlv_lsp_error_code,
+ [PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC] = pcep_encode_tlv_rsvp_error_spec,
+ [PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION] = pcep_encode_tlv_lsp_db_version,
+ [PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID] =
+ pcep_encode_tlv_speaker_entity_id,
+ [PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY] =
+ pcep_encode_tlv_sr_pce_capability,
+ [PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE] = pcep_encode_tlv_path_setup_type,
+ [PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY] =
+ pcep_encode_tlv_path_setup_type_capability,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID] = pcep_encode_tlv_pol_id,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME] = pcep_encode_tlv_pol_name,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID] = pcep_encode_tlv_cpath_id,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE] =
+ pcep_encode_tlv_cpath_preference,
+ [PCEP_OBJ_TLV_TYPE_VENDOR_INFO] = pcep_encode_tlv_vendor_info,
+ [PCEP_OBJ_TLV_TYPE_ARBITRARY] = pcep_encode_tlv_arbitrary,
+ [PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST] = pcep_encode_tlv_of_list,
+};
+/*
+ * forward declarations for initialize_tlv_decoders()
+ */
+struct pcep_object_tlv_header *
+pcep_decode_tlv_no_path_vector(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_stateful_pce_capability(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_symbolic_path_name(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_ipv4_lsp_identifiers(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_ipv6_lsp_identifiers(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_lsp_error_code(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_rsvp_error_spec(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_lsp_db_version(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_speaker_entity_id(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_sr_pce_capability(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_path_setup_type(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *pcep_decode_tlv_path_setup_type_capability(
+ struct pcep_object_tlv_header *tlv_hdr, const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_pol_id(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_pol_name(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_cpath_id(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_cpath_preference(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_vendor_info(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_arbitrary(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_of_list(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+typedef struct pcep_object_tlv_header *(*tlv_decoder_funcptr)(
+ struct pcep_object_tlv_header *tlv_hdr, const uint8_t *tlv_body_buf);
+
+// tlv_decoder_funcptr tlv_decoders[MAX_TLV_ENCODER_INDEX];
+
+#define PCEP_TLV_DECODERS_ARGS \
+ struct pcep_object_tlv_header *tlv_hdr, const uint8_t *tlv_body_buf
+
+struct pcep_object_tlv_header *(*const tlv_decoders[MAX_TLV_ENCODER_INDEX])(
+ PCEP_TLV_DECODERS_ARGS) = {
+ [PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR] = pcep_decode_tlv_no_path_vector,
+ [PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY] =
+ pcep_decode_tlv_stateful_pce_capability,
+ [PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME] =
+ pcep_decode_tlv_symbolic_path_name,
+ [PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS] =
+ pcep_decode_tlv_ipv4_lsp_identifiers,
+ [PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS] =
+ pcep_decode_tlv_ipv6_lsp_identifiers,
+ [PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE] = pcep_decode_tlv_lsp_error_code,
+ [PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC] = pcep_decode_tlv_rsvp_error_spec,
+ [PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION] = pcep_decode_tlv_lsp_db_version,
+ [PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID] =
+ pcep_decode_tlv_speaker_entity_id,
+ [PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY] =
+ pcep_decode_tlv_sr_pce_capability,
+ [PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE] = pcep_decode_tlv_path_setup_type,
+ [PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY] =
+ pcep_decode_tlv_path_setup_type_capability,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID] = pcep_decode_tlv_pol_id,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME] = pcep_decode_tlv_pol_name,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID] = pcep_decode_tlv_cpath_id,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE] =
+ pcep_decode_tlv_cpath_preference,
+ [PCEP_OBJ_TLV_TYPE_VENDOR_INFO] = pcep_decode_tlv_vendor_info,
+ [PCEP_OBJ_TLV_TYPE_ARBITRARY] = pcep_decode_tlv_arbitrary,
+ [PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST] = pcep_decode_tlv_of_list,
+};
+
+static void initialize_tlv_coders()
+{
+ static bool initialized = false;
+
+ if (initialized == true) {
+ return;
+ }
+
+ initialized = true;
+
+ /* Encoders */
+ /*
+ memset(tlv_encoders, 0, sizeof(tlv_encoder_funcptr) *
+ MAX_TLV_ENCODER_INDEX); tlv_encoders[PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR] =
+ pcep_encode_tlv_no_path_vector;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY] =
+ pcep_encode_tlv_stateful_pce_capability;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME] =
+ pcep_encode_tlv_symbolic_path_name;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS] =
+ pcep_encode_tlv_ipv4_lsp_identifiers;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS] =
+ pcep_encode_tlv_ipv6_lsp_identifiers;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE] =
+ pcep_encode_tlv_lsp_error_code;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC] =
+ pcep_encode_tlv_rsvp_error_spec;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION] =
+ pcep_encode_tlv_lsp_db_version;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID] =
+ pcep_encode_tlv_speaker_entity_id;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY] =
+ pcep_encode_tlv_sr_pce_capability;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE] =
+ pcep_encode_tlv_path_setup_type;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY] =
+ pcep_encode_tlv_path_setup_type_capability;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID] =
+ pcep_encode_tlv_pol_id;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME] =
+ pcep_encode_tlv_pol_name;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID] =
+ pcep_encode_tlv_cpath_id;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE] =
+ pcep_encode_tlv_cpath_preference;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_VENDOR_INFO] =
+ pcep_encode_tlv_vendor_info; tlv_encoders[PCEP_OBJ_TLV_TYPE_ARBITRARY] =
+ pcep_encode_tlv_arbitrary;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST] =
+ pcep_encode_tlv_of_list;
+ */
+
+ /* Decoders */
+ /*
+ memset(tlv_decoders, 0, sizeof(tlv_decoder_funcptr) *
+ MAX_TLV_ENCODER_INDEX); tlv_decoders[PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR] =
+ pcep_decode_tlv_no_path_vector;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY] =
+ pcep_decode_tlv_stateful_pce_capability;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME] =
+ pcep_decode_tlv_symbolic_path_name;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS] =
+ pcep_decode_tlv_ipv4_lsp_identifiers;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS] =
+ pcep_decode_tlv_ipv6_lsp_identifiers;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE] =
+ pcep_decode_tlv_lsp_error_code;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC] =
+ pcep_decode_tlv_rsvp_error_spec;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION] =
+ pcep_decode_tlv_lsp_db_version;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID] =
+ pcep_decode_tlv_speaker_entity_id;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY] =
+ pcep_decode_tlv_sr_pce_capability;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE] =
+ pcep_decode_tlv_path_setup_type;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY] =
+ pcep_decode_tlv_path_setup_type_capability;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID] =
+ pcep_decode_tlv_pol_id;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME] =
+ pcep_decode_tlv_pol_name;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID] =
+ pcep_decode_tlv_cpath_id;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE] =
+ pcep_decode_tlv_cpath_preference;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_VENDOR_INFO] =
+ pcep_decode_tlv_vendor_info; tlv_decoders[PCEP_OBJ_TLV_TYPE_ARBITRARY] =
+ pcep_decode_tlv_arbitrary;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST] =
+ pcep_decode_tlv_of_list;
+ */
+}
+
+uint16_t pcep_encode_tlv(struct pcep_object_tlv_header *tlv_hdr,
+ struct pcep_versioning *versioning, uint8_t *buf)
+{
+ initialize_tlv_coders();
+
+ if (tlv_hdr->type >= MAX_TLV_ENCODER_INDEX) {
+ pcep_log(LOG_INFO,
+ "%s: Cannot encode unknown Object class [%d]",
+ __func__, tlv_hdr->type);
+ return 0;
+ }
+
+ tlv_encoder_funcptr tlv_encoder = tlv_encoders[tlv_hdr->type];
+ if (tlv_encoder == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: No object encoder found for Object class [%d]",
+ __func__, tlv_hdr->type);
+ return 0;
+ }
+
+ /* Notice: The length in the TLV header does not include the TLV header,
+ * so the length returned from the tlv_encoder() is only the TLV body.
+ */
+ uint16_t tlv_length =
+ tlv_encoder(tlv_hdr, versioning, buf + TLV_HEADER_LENGTH);
+ write_tlv_header(tlv_hdr, tlv_length, versioning, buf);
+ tlv_hdr->encoded_tlv = buf;
+ tlv_hdr->encoded_tlv_length = tlv_length;
+
+ return normalize_pcep_tlv_length(tlv_length + TLV_HEADER_LENGTH);
+}
+
+/* TLV Header format
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type (2 bytes) | Length (2 bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Value (Variable) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+void write_tlv_header(struct pcep_object_tlv_header *tlv_hdr,
+ uint16_t tlv_length, struct pcep_versioning *versioning,
+ uint8_t *buf)
+{
+ (void)versioning;
+ uint16_t *uint16_ptr = (uint16_t *)buf;
+ uint16_ptr[0] = htons(tlv_hdr->type);
+ uint16_ptr[1] = htons(tlv_length);
+}
+
+/*
+ * Functions to encode TLVs
+ */
+
+uint16_t pcep_encode_tlv_no_path_vector(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_nopath_vector *nopath_tlv =
+ (struct pcep_object_tlv_nopath_vector *)tlv;
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ *uint32_ptr = htonl(nopath_tlv->error_code);
+
+ return LENGTH_1WORD;
+}
+
+uint16_t
+pcep_encode_tlv_stateful_pce_capability(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_stateful_pce_capability *spc_tlv =
+ (struct pcep_object_tlv_stateful_pce_capability *)tlv;
+ tlv_body_buf[3] =
+ ((spc_tlv->flag_f_triggered_initial_sync == true
+ ? TLV_STATEFUL_PCE_CAP_FLAG_F
+ : 0x00)
+ | (spc_tlv->flag_d_delta_lsp_sync == true
+ ? TLV_STATEFUL_PCE_CAP_FLAG_D
+ : 0x00)
+ | (spc_tlv->flag_t_triggered_resync == true
+ ? TLV_STATEFUL_PCE_CAP_FLAG_T
+ : 0x00)
+ | (spc_tlv->flag_i_lsp_instantiation_capability == true
+ ? TLV_STATEFUL_PCE_CAP_FLAG_I
+ : 0x00)
+ | (spc_tlv->flag_s_include_db_version == true
+ ? TLV_STATEFUL_PCE_CAP_FLAG_S
+ : 0x00)
+ | (spc_tlv->flag_u_lsp_update_capability == true
+ ? TLV_STATEFUL_PCE_CAP_FLAG_U
+ : 0x00));
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_tlv_symbolic_path_name(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_symbolic_path_name *spn_tlv =
+ (struct pcep_object_tlv_symbolic_path_name *)tlv;
+ memcpy(tlv_body_buf, spn_tlv->symbolic_path_name,
+ spn_tlv->symbolic_path_name_length);
+
+ return spn_tlv->symbolic_path_name_length;
+}
+
+uint16_t
+pcep_encode_tlv_ipv4_lsp_identifiers(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_ipv4_lsp_identifier *ipv4_lsp =
+ (struct pcep_object_tlv_ipv4_lsp_identifier *)tlv;
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ uint32_ptr[0] = ipv4_lsp->ipv4_tunnel_sender.s_addr;
+ /* uint32_t[1] is lsp_id and tunnel_id, below */
+ uint32_ptr[2] = ipv4_lsp->extended_tunnel_id.s_addr;
+ uint32_ptr[3] = ipv4_lsp->ipv4_tunnel_endpoint.s_addr;
+
+ uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + LENGTH_1WORD);
+ uint16_ptr[0] = htons(ipv4_lsp->lsp_id);
+ uint16_ptr[1] = htons(ipv4_lsp->tunnel_id);
+
+ return LENGTH_4WORDS;
+}
+
+uint16_t
+pcep_encode_tlv_ipv6_lsp_identifiers(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_ipv6_lsp_identifier *ipv6_lsp =
+ (struct pcep_object_tlv_ipv6_lsp_identifier *)tlv;
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ encode_ipv6(&ipv6_lsp->ipv6_tunnel_sender, uint32_ptr);
+ encode_ipv6(&ipv6_lsp->extended_tunnel_id, uint32_ptr + 5);
+ encode_ipv6(&ipv6_lsp->ipv6_tunnel_endpoint, uint32_ptr + 9);
+
+ uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + LENGTH_4WORDS);
+ uint16_ptr[0] = htons(ipv6_lsp->lsp_id);
+ uint16_ptr[1] = htons(ipv6_lsp->tunnel_id);
+
+ return LENGTH_13WORDS;
+}
+
+uint16_t pcep_encode_tlv_lsp_error_code(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_lsp_error_code *lsp_error_tlv =
+ (struct pcep_object_tlv_lsp_error_code *)tlv;
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ *uint32_ptr = htonl(lsp_error_tlv->lsp_error_code);
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_tlv_rsvp_error_spec(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ /* Same decode tlv function for both types:
+ pcep_create_tlv_rsvp_ipv4_error_spec(tlv);
+ pcep_create_tlv_rsvp_ipv6_error_spec(tlv); */
+
+ /* RSVP Object Header
+ *
+ * 0 1 2 3
+ * +-------------+-------------+-------------+-------------+
+ * | Length (bytes) | Class-Num | C-Type |
+ * +-------------+-------------+-------------+-------------+
+ * | |
+ * // (Object contents) //
+ * | |
+ * +-------------+-------------+-------------+-------------+
+ *
+ * IPv4 ERROR_SPEC object: Class = 6, C-Type = 1
+ * +-------------+-------------+-------------+-------------+
+ * | IPv4 Error Node Address (4 bytes) |
+ * +-------------+-------------+-------------+-------------+
+ * | Flags | Error Code | Error Value |
+ * +-------------+-------------+-------------+-------------+
+ *
+ * IPv6 ERROR_SPEC object: Class = 6, C-Type = 2
+ * +-------------+-------------+-------------+-------------+
+ * | IPv6 Error Node Address (16 bytes) |
+ * +-------------+-------------+-------------+-------------+
+ * | Flags | Error Code | Error Value |
+ * +-------------+-------------+-------------+-------------+
+ */
+
+ (void)versioning;
+ struct pcep_object_tlv_rsvp_error_spec *rsvp_hdr =
+ (struct pcep_object_tlv_rsvp_error_spec *)tlv;
+ tlv_body_buf[2] = rsvp_hdr->class_num;
+ tlv_body_buf[3] = rsvp_hdr->c_type;
+
+ uint16_t *length_ptr = (uint16_t *)tlv_body_buf;
+ uint32_t *uint32_ptr = (uint32_t *)(tlv_body_buf + LENGTH_1WORD);
+ if (rsvp_hdr->c_type == RSVP_ERROR_SPEC_IPV4_CTYPE) {
+ *length_ptr = htons(LENGTH_3WORDS);
+ *uint32_ptr =
+ rsvp_hdr->error_spec_ip.ipv4_error_node_address.s_addr;
+ tlv_body_buf[LENGTH_2WORDS + 1] = rsvp_hdr->error_code;
+ uint16_t *uint16_ptr =
+ (uint16_t *)(tlv_body_buf + LENGTH_2WORDS + 2);
+ *uint16_ptr = htons(rsvp_hdr->error_value);
+
+ return LENGTH_3WORDS;
+ } else if (rsvp_hdr->c_type == RSVP_ERROR_SPEC_IPV6_CTYPE) {
+ *length_ptr = htons(LENGTH_6WORDS);
+ encode_ipv6(&rsvp_hdr->error_spec_ip.ipv6_error_node_address,
+ uint32_ptr);
+ tlv_body_buf[LENGTH_5WORDS + 1] = rsvp_hdr->error_code;
+ uint16_t *uint16_ptr =
+ (uint16_t *)(tlv_body_buf + LENGTH_5WORDS + 2);
+ *uint16_ptr = htons(rsvp_hdr->error_value);
+
+ return LENGTH_6WORDS;
+ }
+
+ return 0;
+}
+
+uint16_t pcep_encode_tlv_lsp_db_version(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_lsp_db_version *lsp_db_ver =
+ (struct pcep_object_tlv_lsp_db_version *)tlv;
+ *((uint64_t *)tlv_body_buf) = htobe64(lsp_db_ver->lsp_db_version);
+
+ return LENGTH_2WORDS;
+}
+
+uint16_t pcep_encode_tlv_speaker_entity_id(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_speaker_entity_identifier *speaker_id =
+ (struct pcep_object_tlv_speaker_entity_identifier *)tlv;
+ if (speaker_id->speaker_entity_id_list == NULL) {
+ return 0;
+ }
+
+ int index = 0;
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ double_linked_list_node *node =
+ speaker_id->speaker_entity_id_list->head;
+ for (; node != NULL; node = node->next_node) {
+ uint32_ptr[index++] = htonl(*((uint32_t *)node->data));
+ }
+
+ return speaker_id->speaker_entity_id_list->num_entries * LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_tlv_sr_pce_capability(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_sr_pce_capability *sr_pce_cap =
+ (struct pcep_object_tlv_sr_pce_capability *)tlv;
+ tlv_body_buf[2] =
+ ((sr_pce_cap->flag_n == true ? TLV_SR_PCE_CAP_FLAG_N : 0x00)
+ | (sr_pce_cap->flag_x == true ? TLV_SR_PCE_CAP_FLAG_X : 0x00));
+ tlv_body_buf[3] = sr_pce_cap->max_sid_depth;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_tlv_path_setup_type(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_path_setup_type *pst =
+ (struct pcep_object_tlv_path_setup_type *)tlv;
+ tlv_body_buf[3] = pst->path_setup_type;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t
+pcep_encode_tlv_path_setup_type_capability(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_path_setup_type_capability *pst_cap =
+ (struct pcep_object_tlv_path_setup_type_capability *)tlv;
+ if (pst_cap->pst_list == NULL) {
+ return 0;
+ }
+
+ tlv_body_buf[3] = pst_cap->pst_list->num_entries;
+
+ /* Index past the reserved and NumPSTs fields */
+ int index = 4;
+ double_linked_list_node *node = pst_cap->pst_list->head;
+ for (; node != NULL; node = node->next_node) {
+ tlv_body_buf[index++] = *((uint8_t *)node->data);
+ }
+
+ uint16_t pst_length = normalize_pcep_tlv_length(
+ LENGTH_1WORD + pst_cap->pst_list->num_entries);
+ if (pst_cap->sub_tlv_list == NULL) {
+ return pst_length;
+ }
+
+ /* Any padding used for the PSTs should not be included in the tlv
+ * header length */
+ index = normalize_pcep_tlv_length(index);
+ uint16_t sub_tlvs_length = 0;
+ node = pst_cap->sub_tlv_list->head;
+ for (; node != NULL; node = node->next_node) {
+ struct pcep_object_tlv_header *sub_tlv =
+ (struct pcep_object_tlv_header *)node->data;
+ uint16_t sub_tlv_length = pcep_encode_tlv(sub_tlv, versioning,
+ tlv_body_buf + index);
+ index += sub_tlv_length;
+ sub_tlvs_length += sub_tlv_length;
+ }
+
+ return sub_tlvs_length + pst_length;
+}
+uint16_t pcep_encode_tlv_pol_id(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ struct pcep_object_tlv_srpag_pol_id *ipv4 =
+ (struct pcep_object_tlv_srpag_pol_id *)tlv;
+ if (ipv4->is_ipv4) {
+ uint32_ptr[0] = htonl(ipv4->color);
+ uint32_ptr[1] = ipv4->end_point.ipv4.s_addr;
+ return LENGTH_2WORDS;
+ } else {
+ struct pcep_object_tlv_srpag_pol_id *ipv6 =
+ (struct pcep_object_tlv_srpag_pol_id *)tlv;
+ uint32_ptr[0] = htonl(ipv6->color);
+ encode_ipv6(&ipv6->end_point.ipv6, &uint32_ptr[1]);
+ return LENGTH_5WORDS;
+ }
+}
+
+uint16_t pcep_encode_tlv_pol_name(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_srpag_pol_name *pol_name_tlv =
+ (struct pcep_object_tlv_srpag_pol_name *)tlv;
+ memcpy(tlv_body_buf, pol_name_tlv->name, pol_name_tlv->name_length);
+
+ return normalize_pcep_tlv_length(pol_name_tlv->name_length);
+}
+
+uint16_t pcep_encode_tlv_cpath_id(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_srpag_cp_id *cpath_id_tlv =
+ (struct pcep_object_tlv_srpag_cp_id *)tlv;
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ tlv_body_buf[0] = cpath_id_tlv->proto;
+ uint32_ptr[1] = htonl(cpath_id_tlv->orig_asn);
+ encode_ipv6(&cpath_id_tlv->orig_addres, &uint32_ptr[2]);
+ uint32_ptr[6] = htonl(cpath_id_tlv->discriminator);
+
+ return sizeof(cpath_id_tlv->proto) + sizeof(cpath_id_tlv->orig_asn)
+ + sizeof(cpath_id_tlv->orig_addres)
+ + sizeof(cpath_id_tlv->discriminator);
+}
+
+uint16_t pcep_encode_tlv_cpath_preference(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_srpag_cp_pref *cpath_pref_tlv =
+ (struct pcep_object_tlv_srpag_cp_pref *)tlv;
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ uint32_ptr[0] = htonl(cpath_pref_tlv->preference);
+
+ return sizeof(cpath_pref_tlv->preference);
+}
+
+uint16_t pcep_encode_tlv_vendor_info(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_vendor_info *vendor_info =
+ (struct pcep_object_tlv_vendor_info *)tlv;
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ uint32_ptr[0] = htonl(vendor_info->enterprise_number);
+ uint32_ptr[1] = htonl(vendor_info->enterprise_specific_info);
+
+ return LENGTH_2WORDS;
+}
+
+uint16_t pcep_encode_tlv_arbitrary(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_arbitrary *tlv_arbitrary =
+ (struct pcep_object_tlv_arbitrary *)tlv;
+ memcpy(tlv_body_buf, tlv_arbitrary->data, tlv_arbitrary->data_length);
+ tlv->type = tlv_arbitrary->arbitraty_type;
+
+ return tlv_arbitrary->data_length;
+}
+
+uint16_t pcep_encode_tlv_of_list(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_of_list *of_list =
+ (struct pcep_object_tlv_of_list *)tlv;
+
+ if (of_list->of_list == NULL) {
+ return 0;
+ }
+
+ int index = 0;
+ double_linked_list_node *node = of_list->of_list->head;
+ while (node != NULL) {
+ uint16_t *of_code = (uint16_t *)node->data;
+ if (of_code == NULL) {
+ return 0;
+ }
+
+ uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + index);
+ *uint16_ptr = *of_code;
+ index += 2;
+
+ node = node->next_node;
+ }
+
+ return of_list->of_list->num_entries * 2;
+}
+
+/*
+ * Decoding functions
+ */
+
+void pcep_decode_tlv_hdr(const uint8_t *tlv_buf,
+ struct pcep_object_tlv_header *tlv_hdr)
+{
+ memset(tlv_hdr, 0, sizeof(struct pcep_object_tlv_header));
+
+ uint16_t *uint16_ptr = (uint16_t *)tlv_buf;
+ tlv_hdr->type = ntohs(uint16_ptr[0]);
+ tlv_hdr->encoded_tlv_length = ntohs(uint16_ptr[1]);
+ tlv_hdr->encoded_tlv = tlv_buf;
+}
+
+struct pcep_object_tlv_header *pcep_decode_tlv(const uint8_t *tlv_buf)
+{
+ initialize_tlv_coders();
+
+ struct pcep_object_tlv_header tlv_hdr;
+ /* Only initializes and decodes the Object Header: class, type, flags,
+ * and length */
+ pcep_decode_tlv_hdr(tlv_buf, &tlv_hdr);
+
+ if (tlv_hdr.type >= MAX_TLV_ENCODER_INDEX) {
+ pcep_log(LOG_INFO, "%s: Cannot decode unknown TLV type [%d]",
+ __func__, tlv_hdr.type);
+ return NULL;
+ }
+
+ tlv_decoder_funcptr tlv_decoder = tlv_decoders[tlv_hdr.type];
+ if (tlv_decoder == NULL) {
+ pcep_log(LOG_INFO, "%s: No TLV decoder found for TLV type [%d]",
+ __func__, tlv_hdr.type);
+ return NULL;
+ }
+
+ return tlv_decoder(&tlv_hdr, tlv_buf + LENGTH_1WORD);
+}
+
+static struct pcep_object_tlv_header *
+common_tlv_create(struct pcep_object_tlv_header *hdr, uint16_t new_tlv_length)
+{
+ struct pcep_object_tlv_header *new_tlv =
+ pceplib_malloc(PCEPLIB_MESSAGES, new_tlv_length);
+ memset(new_tlv, 0, new_tlv_length);
+ memcpy(new_tlv, hdr, sizeof(struct pcep_object_tlv_header));
+
+ return new_tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_no_path_vector(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_nopath_vector *tlv =
+ (struct pcep_object_tlv_nopath_vector *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_nopath_vector));
+
+ tlv->error_code = ntohl(*((uint32_t *)tlv_body_buf));
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_stateful_pce_capability(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_stateful_pce_capability *tlv =
+ (struct pcep_object_tlv_stateful_pce_capability *)
+ common_tlv_create(
+ tlv_hdr,
+ sizeof(struct
+ pcep_object_tlv_stateful_pce_capability));
+
+ tlv->flag_f_triggered_initial_sync =
+ (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_F);
+ tlv->flag_d_delta_lsp_sync =
+ (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_D);
+ tlv->flag_t_triggered_resync =
+ (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_T);
+ tlv->flag_i_lsp_instantiation_capability =
+ (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_I);
+ tlv->flag_s_include_db_version =
+ (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_S);
+ tlv->flag_u_lsp_update_capability =
+ (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_U);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_symbolic_path_name(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_symbolic_path_name *tlv =
+ (struct pcep_object_tlv_symbolic_path_name *)common_tlv_create(
+ tlv_hdr,
+ sizeof(struct pcep_object_tlv_symbolic_path_name));
+
+ uint16_t length = tlv_hdr->encoded_tlv_length;
+ if (length > MAX_SYMBOLIC_PATH_NAME) {
+ /* TODO should we also reset the tlv_hdr->encoded_tlv_length ?
+ */
+ length = MAX_SYMBOLIC_PATH_NAME;
+ pcep_log(
+ LOG_INFO,
+ "%s: Decoding Symbolic Path Name TLV, truncate path name from [%d] to [%d].\",",
+ __func__, tlv_hdr->encoded_tlv_length,
+ MAX_SYMBOLIC_PATH_NAME);
+ }
+
+ tlv->symbolic_path_name_length = length;
+ memcpy(tlv->symbolic_path_name, tlv_body_buf, length);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_ipv4_lsp_identifiers(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_ipv4_lsp_identifier *tlv =
+ (struct pcep_object_tlv_ipv4_lsp_identifier *)common_tlv_create(
+ tlv_hdr,
+ sizeof(struct pcep_object_tlv_ipv4_lsp_identifier));
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ tlv->ipv4_tunnel_sender.s_addr = uint32_ptr[0];
+ /* uint32_t[1] is lsp_id and tunnel_id, below */
+ tlv->extended_tunnel_id.s_addr = uint32_ptr[2];
+ tlv->ipv4_tunnel_endpoint.s_addr = uint32_ptr[3];
+
+ uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + LENGTH_1WORD);
+ tlv->lsp_id = ntohs(uint16_ptr[0]);
+ tlv->tunnel_id = ntohs(uint16_ptr[1]);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_ipv6_lsp_identifiers(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_ipv6_lsp_identifier *tlv =
+ (struct pcep_object_tlv_ipv6_lsp_identifier *)common_tlv_create(
+ tlv_hdr,
+ sizeof(struct pcep_object_tlv_ipv6_lsp_identifier));
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ decode_ipv6(uint32_ptr, &tlv->ipv6_tunnel_sender);
+ decode_ipv6(uint32_ptr + 5, &tlv->extended_tunnel_id);
+ decode_ipv6(uint32_ptr + 9, &tlv->ipv6_tunnel_endpoint);
+
+ uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + LENGTH_4WORDS);
+ tlv->lsp_id = htons(uint16_ptr[0]);
+ tlv->tunnel_id = htons(uint16_ptr[1]);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_lsp_error_code(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_lsp_error_code *tlv =
+ (struct pcep_object_tlv_lsp_error_code *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_lsp_error_code));
+
+ tlv->lsp_error_code = ntohl(*((uint32_t *)tlv_body_buf));
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_rsvp_error_spec(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ uint8_t class_num = tlv_body_buf[2];
+ uint8_t ctype = tlv_body_buf[3];
+
+ if (class_num != RSVP_ERROR_SPEC_CLASS_NUM) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Decoding RSVP Error Spec TLV, unknown class num [%d]",
+ __func__, class_num);
+ return NULL;
+ }
+
+ if (ctype != RSVP_ERROR_SPEC_IPV4_CTYPE
+ && ctype != RSVP_ERROR_SPEC_IPV6_CTYPE) {
+ pcep_log(LOG_INFO,
+ "%s: Decoding RSVP Error Spec TLV, unknown ctype [%d]",
+ __func__, ctype);
+ return NULL;
+ }
+
+ struct pcep_object_tlv_rsvp_error_spec *tlv =
+ (struct pcep_object_tlv_rsvp_error_spec *)common_tlv_create(
+ tlv_hdr,
+ sizeof(struct pcep_object_tlv_rsvp_error_spec));
+
+ tlv->class_num = class_num;
+ tlv->c_type = ctype;
+
+ uint32_t *uint32_ptr = (uint32_t *)(tlv_body_buf + LENGTH_1WORD);
+ if (ctype == RSVP_ERROR_SPEC_IPV4_CTYPE) {
+ tlv->error_spec_ip.ipv4_error_node_address.s_addr = *uint32_ptr;
+ tlv->error_code = tlv_body_buf[LENGTH_2WORDS + 1];
+ tlv->error_value = ntohs(
+ *((uint16_t *)(tlv_body_buf + LENGTH_2WORDS + 2)));
+ } else /* RSVP_ERROR_SPEC_IPV6_CTYPE */
+ {
+ decode_ipv6(uint32_ptr,
+ &tlv->error_spec_ip.ipv6_error_node_address);
+ tlv->error_code = tlv_body_buf[LENGTH_5WORDS + 1];
+ tlv->error_value = ntohs(
+ *((uint16_t *)(tlv_body_buf + LENGTH_5WORDS + 2)));
+ }
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_lsp_db_version(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_lsp_db_version *tlv =
+ (struct pcep_object_tlv_lsp_db_version *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_lsp_db_version));
+
+ tlv->lsp_db_version = be64toh(*((uint64_t *)tlv_body_buf));
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_speaker_entity_id(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_speaker_entity_identifier *tlv =
+ (struct pcep_object_tlv_speaker_entity_identifier *)
+ common_tlv_create(
+ tlv_hdr,
+ sizeof(struct
+ pcep_object_tlv_speaker_entity_identifier));
+
+ uint8_t num_entity_ids = tlv_hdr->encoded_tlv_length / LENGTH_1WORD;
+ if (num_entity_ids > MAX_ITERATIONS) {
+ num_entity_ids = MAX_ITERATIONS;
+ pcep_log(
+ LOG_INFO,
+ "%s: Decode Speaker Entity ID, truncating num entities from [%d] to [%d].",
+ __func__, num_entity_ids, MAX_ITERATIONS);
+ }
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ tlv->speaker_entity_id_list = dll_initialize();
+ int i;
+ for (i = 0; i < num_entity_ids; i++) {
+ uint32_t *entity_id =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *entity_id = ntohl(uint32_ptr[i]);
+ dll_append(tlv->speaker_entity_id_list, entity_id);
+ }
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_sr_pce_capability(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_sr_pce_capability *tlv =
+ (struct pcep_object_tlv_sr_pce_capability *)common_tlv_create(
+ tlv_hdr,
+ sizeof(struct pcep_object_tlv_sr_pce_capability));
+
+ tlv->flag_n = (tlv_body_buf[2] & TLV_SR_PCE_CAP_FLAG_N);
+ tlv->flag_x = (tlv_body_buf[2] & TLV_SR_PCE_CAP_FLAG_X);
+ tlv->max_sid_depth = tlv_body_buf[3];
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_path_setup_type(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_path_setup_type *tlv =
+ (struct pcep_object_tlv_path_setup_type *)common_tlv_create(
+ tlv_hdr,
+ sizeof(struct pcep_object_tlv_path_setup_type));
+
+ tlv->path_setup_type = tlv_body_buf[3];
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *pcep_decode_tlv_path_setup_type_capability(
+ struct pcep_object_tlv_header *tlv_hdr, const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_path_setup_type_capability *tlv =
+ (struct pcep_object_tlv_path_setup_type_capability *)
+ common_tlv_create(
+ tlv_hdr,
+ sizeof(struct
+ pcep_object_tlv_path_setup_type_capability));
+
+ uint8_t num_psts = tlv_body_buf[3];
+ if (num_psts > MAX_ITERATIONS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Decode Path Setup Type Capability num PSTs [%d] exceeds MAX [%d] continuing anyways",
+ __func__, num_psts, MAX_ITERATIONS);
+ }
+
+ int i;
+ tlv->pst_list = dll_initialize();
+ for (i = 0; i < num_psts; i++) {
+ uint8_t *pst =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint8_t));
+ *pst = tlv_body_buf[i + LENGTH_1WORD];
+ dll_append(tlv->pst_list, pst);
+ }
+
+ if (tlv->header.encoded_tlv_length
+ == (TLV_HEADER_LENGTH + LENGTH_1WORD + num_psts)) {
+ return (struct pcep_object_tlv_header *)tlv;
+ }
+
+ uint8_t num_iterations = 0;
+ tlv->sub_tlv_list = dll_initialize();
+ uint16_t buf_index = normalize_pcep_tlv_length(
+ TLV_HEADER_LENGTH + LENGTH_1WORD + num_psts);
+ while ((tlv->header.encoded_tlv_length - buf_index) > TLV_HEADER_LENGTH
+ && num_iterations++ > MAX_ITERATIONS) {
+ struct pcep_object_tlv_header *sub_tlv =
+ pcep_decode_tlv(tlv_body_buf + buf_index);
+ if (sub_tlv == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Decode PathSetupType Capability sub-TLV decode returned NULL",
+ __func__);
+ return (struct pcep_object_tlv_header *)tlv;
+ }
+
+ buf_index +=
+ normalize_pcep_tlv_length(sub_tlv->encoded_tlv_length);
+ dll_append(tlv->sub_tlv_list, sub_tlv);
+ }
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+struct pcep_object_tlv_header *
+pcep_decode_tlv_pol_id(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ struct pcep_object_tlv_srpag_pol_id *ipv4 =
+ (struct pcep_object_tlv_srpag_pol_id *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_srpag_pol_id));
+ if (tlv_hdr->encoded_tlv_length == 8) {
+ ipv4->is_ipv4 = true;
+ ipv4->color = ntohl(uint32_ptr[0]);
+ ipv4->end_point.ipv4.s_addr = uint32_ptr[1];
+ return (struct pcep_object_tlv_header *)ipv4;
+ } else {
+ ipv4->is_ipv4 = false;
+ struct pcep_object_tlv_srpag_pol_id *ipv6 =
+ (struct pcep_object_tlv_srpag_pol_id *)ipv4;
+ ipv6->color = ntohl(uint32_ptr[0]);
+ decode_ipv6(&uint32_ptr[1], &ipv6->end_point.ipv6);
+ return (struct pcep_object_tlv_header *)ipv6;
+ }
+}
+struct pcep_object_tlv_header *
+pcep_decode_tlv_pol_name(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_srpag_pol_name *tlv =
+ (struct pcep_object_tlv_srpag_pol_name *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_srpag_pol_name));
+
+ memcpy(tlv->name, tlv_body_buf, tlv->header.encoded_tlv_length);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+struct pcep_object_tlv_header *
+pcep_decode_tlv_cpath_id(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ struct pcep_object_tlv_srpag_cp_id *tlv =
+ (struct pcep_object_tlv_srpag_cp_id *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_srpag_cp_id));
+
+ tlv->proto = tlv_body_buf[0];
+ tlv->orig_asn = ntohl(uint32_ptr[1]);
+ decode_ipv6(&uint32_ptr[2], &tlv->orig_addres);
+ tlv->discriminator = ntohl(uint32_ptr[6]);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+struct pcep_object_tlv_header *
+pcep_decode_tlv_cpath_preference(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ struct pcep_object_tlv_srpag_cp_pref *tlv =
+ (struct pcep_object_tlv_srpag_cp_pref *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_srpag_cp_pref));
+
+ tlv->preference = ntohl(uint32_ptr[0]);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_vendor_info(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_vendor_info *tlv =
+ (struct pcep_object_tlv_vendor_info *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_vendor_info));
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ tlv->enterprise_number = ntohl(uint32_ptr[0]);
+ tlv->enterprise_specific_info = ntohl(uint32_ptr[1]);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_arbitrary(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_arbitrary *tlv_arbitrary =
+ (struct pcep_object_tlv_arbitrary *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_arbitrary));
+
+ uint16_t length = tlv_hdr->encoded_tlv_length;
+ if (length > MAX_ARBITRARY_SIZE) {
+ /* TODO should we also reset the tlv_hdr->encoded_tlv_length ?
+ */
+ length = MAX_ARBITRARY_SIZE;
+ pcep_log(
+ LOG_INFO,
+ "%s: Decoding Arbitrary TLV , truncate path name from [%d] to [%d].\",",
+ __func__, tlv_hdr->encoded_tlv_length,
+ MAX_ARBITRARY_SIZE);
+ }
+
+ tlv_arbitrary->data_length = length;
+ tlv_arbitrary->arbitraty_type = tlv_hdr->type;
+ tlv_hdr->type = PCEP_OBJ_TLV_TYPE_ARBITRARY;
+ memcpy(tlv_arbitrary->data, tlv_body_buf, length);
+
+ return (struct pcep_object_tlv_header *)tlv_arbitrary;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_of_list(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_of_list *of_tlv =
+ (struct pcep_object_tlv_of_list *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_of_list));
+
+ of_tlv->of_list = dll_initialize();
+ uint16_t *uint16_ptr = (uint16_t *)tlv_body_buf;
+ int i = 0;
+ for (; i < tlv_hdr->encoded_tlv_length && i < MAX_ITERATIONS; i++) {
+ uint16_t *of_code_ptr =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint16_t));
+ *of_code_ptr = ntohs(uint16_ptr[i]);
+ dll_append(of_tlv->of_list, of_code_ptr);
+ }
+
+ return (struct pcep_object_tlv_header *)of_tlv;
+}
diff --git a/pceplib/pcep_msg_tools.c b/pceplib/pcep_msg_tools.c
new file mode 100644
index 000000000..1d157ec3f
--- /dev/null
+++ b/pceplib/pcep_msg_tools.c
@@ -0,0 +1,465 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pcep_msg_tools.h"
+#include "pcep_msg_encoding.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+static const char *message_type_strs[] = {"NOT_IMPLEMENTED0",
+ "OPEN",
+ "KEEPALIVE",
+ "PCREQ",
+ "PCREP",
+ "PCNOTF",
+ "ERROR",
+ "CLOSE",
+ "NOT_IMPLEMENTED8",
+ "NOT_IMPLEMENTED9",
+ "REPORT",
+ "UPDATE",
+ "INITIATE",
+ "UNKOWN_MESSAGE_TYPE"};
+
+static const char *object_class_strs[] = {"NOT_IMPLEMENTED0",
+ "OPEN",
+ "RP",
+ "NOPATH",
+ "ENDPOINTS",
+ "BANDWIDTH",
+ "METRIC",
+ "ERO",
+ "RRO",
+ "LSPA",
+ "IRO",
+ "SVEC",
+ "NOTF",
+ "ERROR",
+ "NOT_IMPLEMENTED14",
+ "CLOSE",
+ "NOT_IMPLEMENTED16",
+ "NOT_IMPLEMENTED17",
+ "NOT_IMPLEMENTED18",
+ "NOT_IMPLEMENTED19",
+ "NOT_IMPLEMENTED20",
+ "OBJECTIVE_FUNCTION",
+ "NOT_IMPLEMENTED22",
+ "NOT_IMPLEMENTED23",
+ "NOT_IMPLEMENTED24",
+ "NOT_IMPLEMENTED25",
+ "NOT_IMPLEMENTED26",
+ "NOT_IMPLEMENTED27",
+ "NOT_IMPLEMENTED28",
+ "NOT_IMPLEMENTED29",
+ "NOT_IMPLEMENTED30",
+ "NOT_IMPLEMENTED31",
+ "LSP",
+ "SRP",
+ "VENDOR_INFO",
+ "NOT_IMPLEMENTED35",
+ "INTER_LAYER",
+ "SWITCH_LAYER",
+ "REQ_ADAP_CAP",
+ "SERVER_IND",
+ "ASSOCIATION", /* 40 */
+ "UNKNOWN_MESSAGE_TYPE"};
+
+
+double_linked_list *pcep_msg_read(int sock_fd)
+{
+ int ret;
+ uint8_t buffer[PCEP_MAX_SIZE] = {0};
+ uint16_t buffer_read = 0;
+
+
+ ret = read(sock_fd, &buffer, PCEP_MAX_SIZE);
+
+ if (ret < 0) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_read: Failed to read from socket fd [%d] errno [%d %s]",
+ __func__, sock_fd, errno, strerror(errno));
+ return NULL;
+ } else if (ret == 0) {
+ pcep_log(LOG_INFO, "%s: pcep_msg_read: Remote shutdown fd [%d]",
+ __func__, sock_fd);
+ return NULL;
+ }
+
+ double_linked_list *msg_list = dll_initialize();
+ struct pcep_message *msg = NULL;
+
+ while ((ret - buffer_read) >= MESSAGE_HEADER_LENGTH) {
+
+ /* Get the Message header, validate it, and return the msg
+ * length */
+ int32_t msg_hdr_length =
+ pcep_decode_validate_msg_header(buffer + buffer_read);
+ if (msg_hdr_length < 0) {
+ /* If the message header is invalid, we cant keep
+ * reading since the length may be invalid */
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_read: Received an invalid message fd [%d]",
+ __func__, sock_fd);
+ return msg_list;
+ }
+
+ /* Check if the msg_hdr_length is longer than what was read,
+ * in which case, we need to read the rest of the message. */
+ if ((ret - buffer_read) < msg_hdr_length) {
+ int read_len = (msg_hdr_length - (ret - buffer_read));
+ int read_ret = 0;
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_read: Message not fully read! Trying to read %d bytes more, fd [%d]",
+ __func__, read_len, sock_fd);
+
+ read_ret = read(sock_fd, &buffer[ret], read_len);
+
+ if (read_ret != read_len) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_read: Did not manage to read enough data (%d != %d) fd [%d]",
+ __func__, read_ret, read_len, sock_fd);
+ return msg_list;
+ }
+ }
+
+ msg = pcep_decode_message(buffer + buffer_read);
+ buffer_read += msg_hdr_length;
+
+ if (msg == NULL) {
+ return msg_list;
+ } else {
+ dll_append(msg_list, msg);
+ }
+ }
+
+ return msg_list;
+}
+
+struct pcep_message *pcep_msg_get(double_linked_list *msg_list, uint8_t type)
+{
+ if (msg_list == NULL) {
+ return NULL;
+ }
+
+ double_linked_list_node *node;
+ for (node = msg_list->head; node != NULL; node = node->next_node) {
+ if (((struct pcep_message *)node->data)->msg_header->type
+ == type) {
+ return (struct pcep_message *)node->data;
+ }
+ }
+
+ return NULL;
+}
+
+struct pcep_message *pcep_msg_get_next(double_linked_list *list,
+ struct pcep_message *current,
+ uint8_t type)
+{
+ if (list == NULL || current == NULL) {
+ return NULL;
+ }
+
+ if (list->head == NULL) {
+ return NULL;
+ }
+
+ double_linked_list_node *node;
+ for (node = list->head; node != NULL; node = node->next_node) {
+ if (node->data == current) {
+ continue;
+ }
+
+ if (((struct pcep_message *)node->data)->msg_header->type
+ == type) {
+ return (struct pcep_message *)node->data;
+ }
+ }
+
+ return NULL;
+}
+
+struct pcep_object_header *pcep_obj_get(double_linked_list *list,
+ uint8_t object_class)
+{
+ if (list == NULL) {
+ return NULL;
+ }
+
+ if (list->head == NULL) {
+ return NULL;
+ }
+
+ double_linked_list_node *obj_item;
+ for (obj_item = list->head; obj_item != NULL;
+ obj_item = obj_item->next_node) {
+ if (((struct pcep_object_header *)obj_item->data)->object_class
+ == object_class) {
+ return (struct pcep_object_header *)obj_item->data;
+ }
+ }
+
+ return NULL;
+}
+
+struct pcep_object_header *pcep_obj_get_next(double_linked_list *list,
+ struct pcep_object_header *current,
+ uint8_t object_class)
+{
+ if (list == NULL || current == NULL) {
+ return NULL;
+ }
+
+ if (list->head == NULL) {
+ return NULL;
+ }
+
+ double_linked_list_node *node;
+ for (node = list->head; node != NULL; node = node->next_node) {
+ if (node->data == current) {
+ continue;
+ }
+
+ if (((struct pcep_object_header *)node->data)->object_class
+ == object_class) {
+ return (struct pcep_object_header *)node->data;
+ }
+ }
+
+ return NULL;
+}
+
+void pcep_obj_free_tlv(struct pcep_object_tlv_header *tlv)
+{
+ /* Specific TLV freeing */
+ switch (tlv->type) {
+ case PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID:
+ if (((struct pcep_object_tlv_speaker_entity_identifier *)tlv)
+ ->speaker_entity_id_list
+ != NULL) {
+ dll_destroy_with_data_memtype(
+ ((struct
+ pcep_object_tlv_speaker_entity_identifier *)
+ tlv)
+ ->speaker_entity_id_list,
+ PCEPLIB_MESSAGES);
+ }
+ break;
+
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY:
+ if (((struct pcep_object_tlv_path_setup_type_capability *)tlv)
+ ->pst_list
+ != NULL) {
+ dll_destroy_with_data_memtype(
+ ((struct
+ pcep_object_tlv_path_setup_type_capability *)
+ tlv)
+ ->pst_list,
+ PCEPLIB_MESSAGES);
+ }
+
+ if (((struct pcep_object_tlv_path_setup_type_capability *)tlv)
+ ->sub_tlv_list
+ != NULL) {
+ dll_destroy_with_data_memtype(
+ ((struct
+ pcep_object_tlv_path_setup_type_capability *)
+ tlv)
+ ->sub_tlv_list,
+ PCEPLIB_MESSAGES);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ pceplib_free(PCEPLIB_MESSAGES, tlv);
+}
+
+void pcep_obj_free_object(struct pcep_object_header *obj)
+{
+ /* Iterate the TLVs and free each one */
+ if (obj->tlv_list != NULL) {
+ struct pcep_object_tlv_header *tlv;
+ while ((tlv = (struct pcep_object_tlv_header *)
+ dll_delete_first_node(obj->tlv_list))
+ != NULL) {
+ pcep_obj_free_tlv(tlv);
+ }
+
+ dll_destroy(obj->tlv_list);
+ }
+
+ /* Specific object freeing */
+ switch (obj->object_class) {
+ case PCEP_OBJ_CLASS_ERO:
+ case PCEP_OBJ_CLASS_IRO:
+ case PCEP_OBJ_CLASS_RRO: {
+ if (((struct pcep_object_ro *)obj)->sub_objects != NULL) {
+ double_linked_list_node *node =
+ ((struct pcep_object_ro *)obj)
+ ->sub_objects->head;
+ for (; node != NULL; node = node->next_node) {
+ struct pcep_object_ro_subobj *ro_subobj =
+ (struct pcep_object_ro_subobj *)
+ node->data;
+ if (ro_subobj->ro_subobj_type
+ == RO_SUBOBJ_TYPE_SR) {
+ if (((struct pcep_ro_subobj_sr *)
+ ro_subobj)
+ ->nai_list
+ != NULL) {
+ dll_destroy_with_data_memtype(
+ ((struct
+ pcep_ro_subobj_sr *)
+ ro_subobj)
+ ->nai_list,
+ PCEPLIB_MESSAGES);
+ }
+ }
+ }
+ dll_destroy_with_data_memtype(
+ ((struct pcep_object_ro *)obj)->sub_objects,
+ PCEPLIB_MESSAGES);
+ }
+ } break;
+
+ case PCEP_OBJ_CLASS_SVEC:
+ if (((struct pcep_object_svec *)obj)->request_id_list != NULL) {
+ dll_destroy_with_data_memtype(
+ ((struct pcep_object_svec *)obj)
+ ->request_id_list,
+ PCEPLIB_MESSAGES);
+ }
+ break;
+
+ case PCEP_OBJ_CLASS_SWITCH_LAYER:
+ if (((struct pcep_object_switch_layer *)obj)->switch_layer_rows
+ != NULL) {
+ dll_destroy_with_data_memtype(
+ ((struct pcep_object_switch_layer *)obj)
+ ->switch_layer_rows,
+ PCEPLIB_MESSAGES);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ pceplib_free(PCEPLIB_MESSAGES, obj);
+}
+
+void pcep_msg_free_message(struct pcep_message *message)
+{
+ /* Iterate the objects and free each one */
+ if (message->obj_list != NULL) {
+ struct pcep_object_header *obj;
+ while ((obj = (struct pcep_object_header *)
+ dll_delete_first_node(message->obj_list))
+ != NULL) {
+ pcep_obj_free_object(obj);
+ }
+
+ dll_destroy(message->obj_list);
+ }
+
+ if (message->msg_header != NULL) {
+ pceplib_free(PCEPLIB_MESSAGES, message->msg_header);
+ }
+
+ if (message->encoded_message != NULL) {
+ pceplib_free(PCEPLIB_MESSAGES, message->encoded_message);
+ }
+
+ pceplib_free(PCEPLIB_MESSAGES, message);
+}
+
+void pcep_msg_free_message_list(double_linked_list *list)
+{
+ /* Iterate the messages and free each one */
+ struct pcep_message *msg;
+ while ((msg = (struct pcep_message *)dll_delete_first_node(list))
+ != NULL) {
+ pcep_msg_free_message(msg);
+ }
+
+ dll_destroy(list);
+}
+
+const char *get_message_type_str(uint8_t type)
+{
+ uint8_t msg_type =
+ (type > PCEP_TYPE_INITIATE) ? PCEP_TYPE_INITIATE + 1 : type;
+
+ return message_type_strs[msg_type];
+}
+
+const char *get_object_class_str(uint8_t class)
+{
+ uint8_t object_class =
+ (class > PCEP_OBJ_CLASS_SRP) ? PCEP_OBJ_CLASS_SRP + 1 : class;
+
+ return object_class_strs[object_class];
+}
+
+/* Expecting a list of struct pcep_message pointers */
+void pcep_msg_print(double_linked_list *msg_list)
+{
+ double_linked_list_node *node;
+ for (node = msg_list->head; node != NULL; node = node->next_node) {
+ struct pcep_message *msg = (struct pcep_message *)node->data;
+ pcep_log(LOG_INFO, "%s: PCEP_MSG %s", __func__,
+ get_message_type_str(msg->msg_header->type));
+
+ double_linked_list_node *obj_node =
+ (msg->obj_list == NULL ? NULL : msg->obj_list->head);
+ for (; obj_node != NULL; obj_node = obj_node->next_node) {
+ struct pcep_object_header *obj_header =
+ ((struct pcep_object_header *)obj_node->data);
+ pcep_log(
+ LOG_INFO, "%s: PCEP_OBJ %s", __func__,
+ get_object_class_str(obj_header->object_class));
+ }
+ }
+}
+
+int pcep_msg_send(int sock_fd, struct pcep_message *msg)
+{
+ if (msg == NULL) {
+ return 0;
+ }
+
+ return write(sock_fd, msg->encoded_message,
+ ntohs(msg->encoded_message_length));
+}
diff --git a/pceplib/pcep_msg_tools.h b/pceplib/pcep_msg_tools.h
new file mode 100644
index 000000000..b62bdde1c
--- /dev/null
+++ b/pceplib/pcep_msg_tools.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ */
+
+#ifndef PCEP_TOOLS_H
+#define PCEP_TOOLS_H
+
+#include <stdint.h>
+#include <netinet/in.h> // struct in_addr
+
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_msg_messages.h"
+#include "pcep_msg_objects.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PCEP_MAX_SIZE 6000
+
+/* Returns a double linked list of PCEP messages */
+double_linked_list *pcep_msg_read(int sock_fd);
+/* Given a double linked list of PCEP messages, return the first node that has
+ * the same message type */
+struct pcep_message *pcep_msg_get(double_linked_list *msg_list, uint8_t type);
+/* Given a double linked list of PCEP messages, return the next node after
+ * current node that has the same message type */
+struct pcep_message *pcep_msg_get_next(double_linked_list *msg_list,
+ struct pcep_message *current,
+ uint8_t type);
+struct pcep_object_header *pcep_obj_get(double_linked_list *list,
+ uint8_t object_class);
+struct pcep_object_header *pcep_obj_get_next(double_linked_list *list,
+ struct pcep_object_header *current,
+ uint8_t object_class);
+struct pcep_object_tlv_header *pcep_tlv_get(double_linked_list *list,
+ uint16_t type);
+struct pcep_object_tlv_header *
+pcep_tlv_get_next(double_linked_list *list,
+ struct pcep_object_tlv_header *current, uint16_t type);
+void pcep_obj_free_tlv(struct pcep_object_tlv_header *tlv);
+void pcep_obj_free_object(struct pcep_object_header *obj);
+void pcep_msg_free_message(struct pcep_message *message);
+void pcep_msg_free_message_list(double_linked_list *list);
+void pcep_msg_print(double_linked_list *list);
+const char *get_message_type_str(uint8_t type);
+const char *get_object_class_str(uint8_t class);
+int pcep_msg_send(int sock_fd, struct pcep_message *hdr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pceplib/pcep_pcc.c b/pceplib/pcep_pcc.c
new file mode 100644
index 000000000..2171f883c
--- /dev/null
+++ b/pceplib/pcep_pcc.c
@@ -0,0 +1,517 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Sample PCC implementation
+ */
+
+#include <zebra.h>
+
+#include <netdb.h> // gethostbyname
+#include <netinet/tcp.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "pcep_pcc_api.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+/*
+ * PCEP PCC design spec:
+ * https://docs.google.com/presentation/d/1DYc3ZhYA1c_qg9A552HjhneJXQKdh_yrKW6v3NRYPtnbw/edit?usp=sharing
+ */
+#define MAX_SRC_IP_STR 40
+#define MAX_DST_IP_STR 40
+struct cmd_line_args {
+ char src_ip_str[MAX_SRC_IP_STR];
+ char dest_ip_str[MAX_DST_IP_STR];
+ short src_tcp_port;
+ short dest_tcp_port;
+ char tcp_md5_str[TCP_MD5SIG_MAXKEYLEN]; /* RFC 2385 */
+ bool is_ipv6;
+ bool eventpoll; /* poll for pcep_event's, or use callback (default) */
+};
+
+bool pcc_active_ = true;
+pcep_session *session = NULL;
+struct cmd_line_args *cmd_line_args = NULL;
+/* pcep_event callback variables */
+bool pcep_event_condition = false;
+struct pcep_event *event = NULL;
+pthread_mutex_t pcep_event_mutex;
+pthread_cond_t pcep_event_cond_var;
+
+static const char DEFAULT_DEST_HOSTNAME[] = "localhost";
+static const char DEFAULT_DEST_HOSTNAME_IPV6[] = "ip6-localhost";
+static const short DEFAULT_SRC_TCP_PORT = 4999;
+
+// Private fn's
+struct cmd_line_args *get_cmdline_args(int argc, char *argv[]);
+void handle_signal_action(int sig_number);
+int setup_signals(void);
+void send_pce_path_request_message(pcep_session *session);
+void send_pce_report_message(pcep_session *session);
+void print_queue_event(struct pcep_event *event);
+void pcep_event_callback(void *cb_data, pcep_event *e);
+
+struct cmd_line_args *get_cmdline_args(int argc, char *argv[])
+{
+ /* Allocate and set default values */
+ struct cmd_line_args *cmd_line_args =
+ malloc(sizeof(struct cmd_line_args));
+ memset(cmd_line_args, 0, sizeof(struct cmd_line_args));
+ strlcpy(cmd_line_args->dest_ip_str, DEFAULT_DEST_HOSTNAME,
+ MAX_DST_IP_STR);
+ cmd_line_args->src_tcp_port = DEFAULT_SRC_TCP_PORT;
+ cmd_line_args->is_ipv6 = false;
+
+ /* Parse the cmd_line args:
+ * -ipv6
+ * -srcip localhost
+ * -destip 192.168.0.2
+ * -srcport 4999
+ * -dstport 4189
+ * -tcpmd5 hello
+ * -event_poll */
+ int i = 1;
+ for (; i < argc; ++i) {
+ if (strcmp(argv[i], "-help") == 0
+ || strcmp(argv[i], "--help") == 0
+ || strcmp(argv[i], "-h") == 0) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_pcc [-ipv6] [-srcip localhost] [-destip 192.168.0.1] [-srcport 4999] [-dstport 4189] [-tcpmd5 authstr] [-eventpoll]",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ } else if (strcmp(argv[i], "-ipv6") == 0) {
+ cmd_line_args->is_ipv6 = true;
+ if (argc == 2) {
+ strlcpy(cmd_line_args->dest_ip_str,
+ DEFAULT_DEST_HOSTNAME_IPV6,
+ MAX_DST_IP_STR);
+ }
+ } else if (strcmp(argv[i], "-eventpoll") == 0) {
+ cmd_line_args->eventpoll = true;
+ } else if (strcmp(argv[i], "-srcip") == 0) {
+ if (argc >= i + 2) {
+ strlcpy(cmd_line_args->src_ip_str, argv[++i],
+ MAX_SRC_IP_STR);
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-srcip\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else if (strcmp(argv[i], "-destip") == 0) {
+ if (argc >= i + 2) {
+ strlcpy(cmd_line_args->dest_ip_str, argv[++i],
+ MAX_DST_IP_STR);
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-destip\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else if (strcmp(argv[i], "-srcport") == 0) {
+ if (argc >= i + 2) {
+ cmd_line_args->src_tcp_port = atoi(argv[++i]);
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-srcport\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else if (strcmp(argv[i], "-destport") == 0) {
+ if (argc >= i + 2) {
+ cmd_line_args->dest_tcp_port = atoi(argv[++i]);
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-destport\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else if (strcmp(argv[i], "-tcpmd5") == 0) {
+ if (argc >= i + 2) {
+ strlcpy(cmd_line_args->tcp_md5_str, argv[++i],
+ sizeof(cmd_line_args->tcp_md5_str));
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-tcpmd5\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else {
+ pcep_log(LOG_ERR, "%s: Invalid cmd_line_arg[%d] = %s",
+ __func__, i, argv[i]);
+ free(cmd_line_args);
+ return NULL;
+ }
+ }
+
+ return cmd_line_args;
+}
+
+void handle_signal_action(int sig_number)
+{
+ if (sig_number == SIGINT) {
+ pcep_log(LOG_INFO, "%s: SIGINT was caught!", __func__);
+ pcc_active_ = false;
+ if (cmd_line_args->eventpoll == false) {
+ pthread_mutex_lock(&pcep_event_mutex);
+ pcep_event_condition = true;
+ pthread_cond_signal(&pcep_event_cond_var);
+ pthread_mutex_unlock(&pcep_event_mutex);
+ }
+ } else if (sig_number == SIGUSR1) {
+ pcep_log(LOG_INFO, "%s: SIGUSR1 was caught, dumping counters",
+ __func__);
+ dump_pcep_session_counters(session);
+ pceplib_memory_dump();
+ } else if (sig_number == SIGUSR2) {
+ pcep_log(LOG_INFO, "%s: SIGUSR2 was caught, reseting counters",
+ __func__);
+ reset_pcep_session_counters(session);
+ }
+}
+
+
+int setup_signals()
+{
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = handle_signal_action;
+ if (sigaction(SIGINT, &sa, 0) != 0) {
+ perror("sigaction()");
+ return -1;
+ }
+
+ if (sigaction(SIGUSR1, &sa, 0) != 0) {
+ perror("sigaction()");
+ return -1;
+ }
+
+ if (sigaction(SIGUSR2, &sa, 0) != 0) {
+ perror("sigaction()");
+ return -1;
+ }
+
+ return 0;
+}
+
+void send_pce_path_request_message(pcep_session *session)
+{
+ struct in_addr src_ipv4;
+ struct in_addr dst_ipv4;
+ inet_pton(AF_INET, "1.2.3.4", &src_ipv4);
+ inet_pton(AF_INET, "10.20.30.40", &dst_ipv4);
+
+ struct pcep_object_rp *rp_object =
+ pcep_obj_create_rp(1, false, false, false, false, 42, NULL);
+ struct pcep_object_endpoints_ipv4 *ep_object =
+ pcep_obj_create_endpoint_ipv4(&src_ipv4, &dst_ipv4);
+
+ struct pcep_message *path_request =
+ pcep_msg_create_request(rp_object, ep_object, NULL);
+ send_message(session, path_request, true);
+}
+
+void send_pce_report_message(pcep_session *session)
+{
+ double_linked_list *report_list = dll_initialize();
+
+ /* SRP Path Setup Type TLV */
+ struct pcep_object_tlv_path_setup_type *pst_tlv =
+ pcep_tlv_create_path_setup_type(SR_TE_PST);
+ double_linked_list *srp_tlv_list = dll_initialize();
+ dll_append(srp_tlv_list, pst_tlv);
+
+ /*
+ * Create the SRP object
+ */
+ uint32_t srp_id_number = 0x10203040;
+ struct pcep_object_header *obj =
+ (struct pcep_object_header *)pcep_obj_create_srp(
+ false, srp_id_number, srp_tlv_list);
+ if (obj == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: send_pce_report_message SRP object was NULL",
+ __func__);
+ return;
+ }
+ dll_append(report_list, obj);
+
+ /* LSP Symbolic path name TLV */
+ char symbolic_path_name[] = "second-default";
+ struct pcep_object_tlv_symbolic_path_name *spn_tlv =
+ pcep_tlv_create_symbolic_path_name(symbolic_path_name, 14);
+ double_linked_list *lsp_tlv_list = dll_initialize();
+ dll_append(lsp_tlv_list, spn_tlv);
+
+ /* LSP IPv4 LSP ID TLV */
+ struct in_addr ipv4_tunnel_sender;
+ struct in_addr ipv4_tunnel_endpoint;
+ inet_pton(AF_INET, "9.9.1.1", &ipv4_tunnel_sender);
+ inet_pton(AF_INET, "9.9.2.1", &ipv4_tunnel_endpoint);
+ struct pcep_object_tlv_ipv4_lsp_identifier *ipv4_lsp_id_tlv =
+ pcep_tlv_create_ipv4_lsp_identifiers(&ipv4_tunnel_sender,
+ &ipv4_tunnel_endpoint, 42,
+ 1, NULL);
+ dll_append(lsp_tlv_list, ipv4_lsp_id_tlv);
+
+ /*
+ * Create the LSP object
+ */
+ uint32_t plsp_id = 42;
+ enum pcep_lsp_operational_status lsp_status =
+ PCEP_LSP_OPERATIONAL_ACTIVE;
+ bool c_flag = false; /* Lsp was created by PcInitiate msg */
+ bool a_flag = false; /* Admin state, active / inactive */
+ bool r_flag = false; /* true if LSP has been removed */
+ bool s_flag = true; /* Synchronization */
+ bool d_flag = false; /* Delegate LSP to PCE */
+ obj = (struct pcep_object_header *)pcep_obj_create_lsp(
+ plsp_id, lsp_status, c_flag, a_flag, r_flag, s_flag, d_flag,
+ lsp_tlv_list);
+ if (obj == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: send_pce_report_message LSP object was NULL",
+ __func__);
+ return;
+ }
+ dll_append(report_list, obj);
+
+ /* Create 2 ERO NONAI sub-objects */
+ double_linked_list *ero_subobj_list = dll_initialize();
+ struct pcep_ro_subobj_sr *sr_subobj_nonai1 =
+ pcep_obj_create_ro_subobj_sr_nonai(false, 503808, true, true);
+ dll_append(ero_subobj_list, sr_subobj_nonai1);
+
+ struct pcep_ro_subobj_sr *sr_subobj_nonai2 =
+ pcep_obj_create_ro_subobj_sr_nonai(false, 1867776, true, true);
+ dll_append(ero_subobj_list, sr_subobj_nonai2);
+
+ /* Create ERO IPv4 node sub-object */
+ struct in_addr sr_subobj_ipv4;
+ inet_pton(AF_INET, "9.9.9.1", &sr_subobj_ipv4);
+ struct pcep_ro_subobj_sr *sr_subobj_ipv4node =
+ pcep_obj_create_ro_subobj_sr_ipv4_node(
+ false, false, false, true, 16060, &sr_subobj_ipv4);
+ if (sr_subobj_ipv4node == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: send_pce_report_message ERO sub-object was NULL",
+ __func__);
+ return;
+ }
+ dll_append(ero_subobj_list, sr_subobj_ipv4node);
+
+ /*
+ * Create the ERO object
+ */
+ obj = (struct pcep_object_header *)pcep_obj_create_ero(ero_subobj_list);
+ if (obj == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: send_pce_report_message ERO object was NULL",
+ __func__);
+ return;
+ }
+ dll_append(report_list, obj);
+
+ /*
+ * Create the Metric object
+ */
+ obj = (struct pcep_object_header *)pcep_obj_create_metric(
+ PCEP_METRIC_TE, false, true, 16.0);
+ dll_append(report_list, obj);
+
+ /* Create and send the report message */
+ struct pcep_message *report_msg = pcep_msg_create_report(report_list);
+ send_message(session, report_msg, true);
+}
+
+void print_queue_event(struct pcep_event *event)
+{
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] Received Event: type [%s] on session [%d] occurred at [%ld]",
+ __func__, time(NULL), pthread_self(),
+ get_event_type_str(event->event_type),
+ event->session->session_id, event->event_time);
+
+ if (event->event_type == MESSAGE_RECEIVED) {
+ pcep_log(
+ LOG_INFO, "%s: \t Event message type [%s]", __func__,
+ get_message_type_str(event->message->msg_header->type));
+ }
+}
+
+/* Called by pcep_session_logic when pcep_event's are ready */
+void pcep_event_callback(void *cb_data, pcep_event *e)
+{
+ (void)cb_data;
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] pcep_event_callback", __func__,
+ time(NULL), pthread_self());
+ pthread_mutex_lock(&pcep_event_mutex);
+ event = e;
+ pcep_event_condition = true;
+ pthread_cond_signal(&pcep_event_cond_var);
+ pthread_mutex_unlock(&pcep_event_mutex);
+}
+
+int main(int argc, char **argv)
+{
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] starting pcc_pcep example client",
+ __func__, time(NULL), pthread_self());
+
+ cmd_line_args = get_cmdline_args(argc, argv);
+ if (cmd_line_args == NULL) {
+ return -1;
+ }
+
+ setup_signals();
+
+ if (cmd_line_args->eventpoll == false) {
+ struct pceplib_infra_config infra_config;
+ memset(&infra_config, 0, sizeof(infra_config));
+ infra_config.pcep_event_func = pcep_event_callback;
+ if (!initialize_pcc_infra(&infra_config)) {
+ pcep_log(LOG_ERR,
+ "%s: Error initializing PCC with infra.",
+ __func__);
+ return -1;
+ }
+ } else {
+ if (!initialize_pcc()) {
+ pcep_log(LOG_ERR, "%s: Error initializing PCC.",
+ __func__);
+ return -1;
+ }
+ }
+
+ pcep_configuration *config = create_default_pcep_configuration();
+ config->pcep_msg_versioning->draft_ietf_pce_segment_routing_07 = true;
+ config->src_pcep_port = cmd_line_args->src_tcp_port;
+ config->is_tcp_auth_md5 = true;
+
+ strlcpy(config->tcp_authentication_str, cmd_line_args->tcp_md5_str,
+ sizeof(config->tcp_authentication_str));
+
+ int af = (cmd_line_args->is_ipv6 ? AF_INET6 : AF_INET);
+ struct hostent *host_info =
+ gethostbyname2(cmd_line_args->dest_ip_str, af);
+ if (host_info == NULL) {
+ pcep_log(LOG_ERR, "%s: Error getting IP address.", __func__);
+ return -1;
+ }
+
+ if (cmd_line_args->is_ipv6) {
+ struct in6_addr host_address;
+ memcpy(&host_address, host_info->h_addr, host_info->h_length);
+ session = connect_pce_ipv6(config, &host_address);
+ } else {
+ struct in_addr host_address;
+ memcpy(&host_address, host_info->h_addr, host_info->h_length);
+ session = connect_pce(config, &host_address);
+ }
+
+ if (session == NULL) {
+ pcep_log(LOG_WARNING, "%s: Error in connect_pce.", __func__);
+ destroy_pcep_configuration(config);
+ return -1;
+ }
+
+ sleep(2);
+
+ send_pce_report_message(session);
+ /*send_pce_path_request_message(session);*/
+
+ /* Wait for pcep_event's either by polling the event queue or by
+ * callback */
+ if (cmd_line_args->eventpoll == true) {
+ /* Poll the pcep_event queue*/
+ while (pcc_active_) {
+ if (event_queue_is_empty() == false) {
+ struct pcep_event *event =
+ event_queue_get_event();
+ print_queue_event(event);
+ destroy_pcep_event(event);
+ }
+
+ sleep(5);
+ }
+ } else {
+ /* Get events via callback and conditional variable */
+ pthread_mutex_init(&pcep_event_mutex, NULL);
+ pthread_cond_init(&pcep_event_cond_var, NULL);
+ while (pcc_active_) {
+ pthread_mutex_lock(&pcep_event_mutex);
+
+ /* this internal loop helps avoid spurious interrupts */
+ while (!pcep_event_condition) {
+ pthread_cond_wait(&pcep_event_cond_var,
+ &pcep_event_mutex);
+ }
+
+ /* Check if we have been interrupted by SIGINT */
+ if (pcc_active_) {
+ print_queue_event(event);
+ destroy_pcep_event(event);
+ }
+
+ pcep_event_condition = false;
+ pthread_mutex_unlock(&pcep_event_mutex);
+ }
+
+ pthread_mutex_destroy(&pcep_event_mutex);
+ pthread_cond_destroy(&pcep_event_cond_var);
+ }
+
+ pcep_log(LOG_NOTICE, "%s: Disconnecting from PCE", __func__);
+ disconnect_pce(session);
+ destroy_pcep_configuration(config);
+ free(cmd_line_args);
+
+ if (!destroy_pcc()) {
+ pcep_log(LOG_NOTICE, "%s: Error stopping PCC.", __func__);
+ }
+
+ pceplib_memory_dump();
+
+ return 0;
+}
diff --git a/pceplib/pcep_pcc_api.c b/pceplib/pcep_pcc_api.c
new file mode 100644
index 000000000..b7813c5a0
--- /dev/null
+++ b/pceplib/pcep_pcc_api.c
@@ -0,0 +1,392 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Public PCEPlib PCC API implementation
+ */
+
+#include <zebra.h>
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pcep_msg_messages.h"
+#include "pcep_pcc_api.h"
+#include "pcep_utils_counters.h"
+#include "pcep_utils_logging.h"
+
+/* Not using an array here since the enum pcep_event_type indeces go into the
+ * 100's */
+const char MESSAGE_RECEIVED_STR[] = "MESSAGE_RECEIVED";
+const char PCE_CLOSED_SOCKET_STR[] = "PCE_CLOSED_SOCKET";
+const char PCE_SENT_PCEP_CLOSE_STR[] = "PCE_SENT_PCEP_CLOSE";
+const char PCE_DEAD_TIMER_EXPIRED_STR[] = "PCE_DEAD_TIMER_EXPIRED";
+const char PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED_STR[] =
+ "PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED";
+const char PCC_CONNECTED_TO_PCE_STR[] = "PCC_CONNECTED_TO_PCE";
+const char PCC_PCEP_SESSION_CLOSED_STR[] = "PCC_PCEP_SESSION_CLOSED";
+const char PCC_RCVD_INVALID_OPEN_STR[] = "PCC_RCVD_INVALID_OPEN";
+const char PCC_RCVD_MAX_INVALID_MSGS_STR[] = "PCC_RCVD_MAX_INVALID_MSGS";
+const char PCC_RCVD_MAX_UNKOWN_MSGS_STR[] = "PCC_RCVD_MAX_UNKOWN_MSGS";
+const char UNKNOWN_EVENT_STR[] = "UNKNOWN Event Type";
+
+/* Session Logic Handle managed in pcep_session_logic.c */
+extern pcep_event_queue *session_logic_event_queue_;
+
+bool initialize_pcc()
+{
+ if (!run_session_logic()) {
+ pcep_log(LOG_ERR, "%s: Error initializing PCC session logic.",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+
+bool initialize_pcc_infra(struct pceplib_infra_config *infra_config)
+{
+ if (infra_config == NULL) {
+ return initialize_pcc();
+ }
+
+ if (!run_session_logic_with_infra(infra_config)) {
+ pcep_log(LOG_ERR,
+ "%s: Error initializing PCC session logic with infra.",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+
+/* this function is blocking */
+bool initialize_pcc_wait_for_completion()
+{
+ return run_session_logic_wait_for_completion();
+}
+
+
+bool destroy_pcc()
+{
+ if (!stop_session_logic()) {
+ pcep_log(LOG_WARNING, "%s: Error stopping PCC session logic.",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+
+pcep_configuration *create_default_pcep_configuration()
+{
+ pcep_configuration *config =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_configuration));
+ memset(config, 0, sizeof(pcep_configuration));
+
+ config->keep_alive_seconds = DEFAULT_CONFIG_KEEP_ALIVE;
+ /* This value will possibly be overwritten later with PCE config data */
+ config->keep_alive_pce_negotiated_timer_seconds =
+ DEFAULT_CONFIG_KEEP_ALIVE;
+ config->min_keep_alive_seconds = DEFAULT_MIN_CONFIG_KEEP_ALIVE;
+ config->max_keep_alive_seconds = DEFAULT_MAX_CONFIG_KEEP_ALIVE;
+
+ config->dead_timer_seconds = DEFAULT_CONFIG_DEAD_TIMER;
+ /* This value will be overwritten later with PCE config data */
+ config->dead_timer_pce_negotiated_seconds = DEFAULT_CONFIG_DEAD_TIMER;
+ config->min_dead_timer_seconds = DEFAULT_MIN_CONFIG_DEAD_TIMER;
+ config->max_dead_timer_seconds = DEFAULT_MAX_CONFIG_DEAD_TIMER;
+
+ config->request_time_seconds = DEFAULT_CONFIG_REQUEST_TIME;
+ config->max_unknown_messages = DEFAULT_CONFIG_MAX_UNKNOWN_MESSAGES;
+ config->max_unknown_requests = DEFAULT_CONFIG_MAX_UNKNOWN_REQUESTS;
+
+ config->socket_connect_timeout_millis =
+ DEFAULT_TCP_CONNECT_TIMEOUT_MILLIS;
+ config->support_stateful_pce_lsp_update = true;
+ config->support_pce_lsp_instantiation = true;
+ config->support_include_db_version = true;
+ config->lsp_db_version = 0;
+ config->support_lsp_triggered_resync = true;
+ config->support_lsp_delta_sync = true;
+ config->support_pce_triggered_initial_sync = true;
+ config->support_sr_te_pst = true;
+ config->pcc_can_resolve_nai_to_sid = true;
+ config->max_sid_depth = 0;
+ config->dst_pcep_port = 0;
+ config->src_pcep_port = 0;
+ config->src_ip.src_ipv4.s_addr = INADDR_ANY;
+ config->is_src_ipv6 = false;
+ config->pcep_msg_versioning = create_default_pcep_versioning();
+ config->tcp_authentication_str[0] = '\0';
+ config->is_tcp_auth_md5 = true;
+
+ return config;
+}
+
+void destroy_pcep_configuration(pcep_configuration *config)
+{
+ destroy_pcep_versioning(config->pcep_msg_versioning);
+ pceplib_free(PCEPLIB_INFRA, config);
+}
+
+pcep_session *connect_pce(pcep_configuration *config, struct in_addr *pce_ip)
+{
+ return create_pcep_session(config, pce_ip);
+}
+
+pcep_session *connect_pce_ipv6(pcep_configuration *config,
+ struct in6_addr *pce_ip)
+{
+ return create_pcep_session_ipv6(config, pce_ip);
+}
+
+void disconnect_pce(pcep_session *session)
+{
+ if (session_exists(session) == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: disconnect_pce session [%p] has already been deleted",
+ __func__, session);
+ return;
+ }
+
+ if (session->socket_comm_session == NULL
+ || session->socket_comm_session->socket_fd < 0) {
+ /* If the socket has already been closed, just destroy the
+ * session */
+ destroy_pcep_session(session);
+ } else {
+ /* This will cause the session to be destroyed AFTER the close
+ * message is sent */
+ session->destroy_session_after_write = true;
+
+ /* Send a PCEP close message */
+ close_pcep_session(session);
+ }
+}
+
+void send_message(pcep_session *session, struct pcep_message *msg,
+ bool free_after_send)
+{
+ if (session == NULL || msg == NULL) {
+ pcep_log(LOG_DEBUG,
+ "%s: send_message NULL params session [%p] msg [%p]",
+ __func__, session, msg);
+
+ return;
+ }
+
+ if (session_exists(session) == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: send_message session [%p] has already been deleted",
+ __func__, session);
+ return;
+ }
+
+ pcep_encode_message(msg, session->pcc_config.pcep_msg_versioning);
+ socket_comm_session_send_message(
+ session->socket_comm_session, (char *)msg->encoded_message,
+ msg->encoded_message_length, free_after_send);
+
+ increment_message_tx_counters(session, msg);
+
+ if (free_after_send == true) {
+ /* The encoded_message will be deleted once sent, so everything
+ * else in the message will be freed */
+ msg->encoded_message = NULL;
+ pcep_msg_free_message(msg);
+ }
+}
+
+/* Returns true if the queue is empty, false otherwise */
+bool event_queue_is_empty()
+{
+ if (session_logic_event_queue_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: event_queue_is_empty Session Logic is not initialized yet",
+ __func__);
+ return false;
+ }
+
+ pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex);
+ bool is_empty =
+ (session_logic_event_queue_->event_queue->num_entries == 0);
+ pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex);
+
+ return is_empty;
+}
+
+
+/* Return the number of events on the queue, 0 if empty */
+uint32_t event_queue_num_events_available()
+{
+ if (session_logic_event_queue_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: event_queue_num_events_available Session Logic is not initialized yet",
+ __func__);
+ return 0;
+ }
+
+ pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex);
+ uint32_t num_events =
+ session_logic_event_queue_->event_queue->num_entries;
+ pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex);
+
+ return num_events;
+}
+
+
+/* Return the next event on the queue, NULL if empty */
+struct pcep_event *event_queue_get_event()
+{
+ if (session_logic_event_queue_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: event_queue_get_event Session Logic is not initialized yet",
+ __func__);
+ return NULL;
+ }
+
+ pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex);
+ struct pcep_event *event = (struct pcep_event *)queue_dequeue(
+ session_logic_event_queue_->event_queue);
+ pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex);
+
+ return event;
+}
+
+
+/* Free the PCEP Event resources, including the PCEP message */
+void destroy_pcep_event(struct pcep_event *event)
+{
+ if (event == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: destroy_pcep_event cannot destroy NULL event",
+ __func__);
+ return;
+ }
+
+ if (event->event_type == MESSAGE_RECEIVED && event->message != NULL) {
+ pcep_msg_free_message(event->message);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, event);
+}
+
+const char *get_event_type_str(int event_type)
+{
+ switch (event_type) {
+ case MESSAGE_RECEIVED:
+ return MESSAGE_RECEIVED_STR;
+ break;
+ case PCE_CLOSED_SOCKET:
+ return PCE_CLOSED_SOCKET_STR;
+ break;
+ case PCE_SENT_PCEP_CLOSE:
+ return PCE_SENT_PCEP_CLOSE_STR;
+ break;
+ case PCE_DEAD_TIMER_EXPIRED:
+ return PCE_DEAD_TIMER_EXPIRED_STR;
+ break;
+ case PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED:
+ return PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED_STR;
+ break;
+ case PCC_CONNECTED_TO_PCE:
+ return PCC_CONNECTED_TO_PCE_STR;
+ break;
+ case PCC_PCEP_SESSION_CLOSED:
+ return PCC_PCEP_SESSION_CLOSED_STR;
+ break;
+ case PCC_RCVD_INVALID_OPEN:
+ return PCC_RCVD_INVALID_OPEN_STR;
+ break;
+ case PCC_RCVD_MAX_INVALID_MSGS:
+ return PCC_RCVD_MAX_INVALID_MSGS_STR;
+ break;
+ case PCC_RCVD_MAX_UNKOWN_MSGS:
+ return PCC_RCVD_MAX_UNKOWN_MSGS_STR;
+ break;
+ default:
+ return UNKNOWN_EVENT_STR;
+ break;
+ }
+}
+
+void dump_pcep_session_counters(pcep_session *session)
+{
+ if (session_exists(session) == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: dump_pcep_session_counters session [%p] has already been deleted",
+ __func__, session);
+ return;
+ }
+
+ /* Update the counters group name so that the PCE session connected time
+ * is accurate */
+ time_t now = time(NULL);
+ char counters_name[MAX_COUNTER_STR_LENGTH] = {0};
+ char ip_str[40] = {0};
+ if (session->socket_comm_session->is_ipv6) {
+ inet_ntop(AF_INET6,
+ &session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6.sin6_addr,
+ ip_str, 40);
+ } else {
+ inet_ntop(AF_INET,
+ &session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4.sin_addr,
+ ip_str, 40);
+ }
+ snprintf(counters_name, MAX_COUNTER_STR_LENGTH,
+ "PCEP Session [%d], connected to [%s] for [%u seconds]",
+ session->session_id, ip_str,
+ (uint32_t)(now - session->time_connected));
+ strlcpy(session->pcep_session_counters->counters_group_name,
+ counters_name,
+ sizeof(session->pcep_session_counters->counters_group_name));
+
+ dump_counters_group_to_log(session->pcep_session_counters);
+}
+
+void reset_pcep_session_counters(pcep_session *session)
+{
+ if (session_exists(session) == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: reset_pcep_session_counters session [%p] has already been deleted",
+ session);
+ return;
+ }
+
+ reset_group_counters(session->pcep_session_counters);
+}
diff --git a/pceplib/pcep_pcc_api.h b/pceplib/pcep_pcc_api.h
new file mode 100644
index 000000000..5756e23ef
--- /dev/null
+++ b/pceplib/pcep_pcc_api.h
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Public PCEPlib PCC API
+ */
+
+#ifndef PCEPPCC_INCLUDE_PCEPPCCAPI_H_
+#define PCEPPCC_INCLUDE_PCEPPCCAPI_H_
+
+#include <stdbool.h>
+
+#include "pcep_session_logic.h"
+#include "pcep_timers.h"
+
+#define DEFAULT_PCEP_TCP_PORT 4189
+#define DEFAULT_CONFIG_KEEP_ALIVE 30
+#define DEFAULT_CONFIG_DEAD_TIMER DEFAULT_CONFIG_KEEP_ALIVE * 4
+#define DEFAULT_CONFIG_REQUEST_TIME 30
+#define DEFAULT_CONFIG_MAX_UNKNOWN_REQUESTS 5
+#define DEFAULT_CONFIG_MAX_UNKNOWN_MESSAGES 5
+#define DEFAULT_TCP_CONNECT_TIMEOUT_MILLIS 250
+
+/* Acceptable MIN and MAX values used in deciding if the PCEP
+ * Open received from a PCE should be accepted or rejected. */
+#define DEFAULT_MIN_CONFIG_KEEP_ALIVE 5
+#define DEFAULT_MAX_CONFIG_KEEP_ALIVE 120
+#define DEFAULT_MIN_CONFIG_DEAD_TIMER DEFAULT_MIN_CONFIG_KEEP_ALIVE * 4
+#define DEFAULT_MAX_CONFIG_DEAD_TIMER DEFAULT_MAX_CONFIG_KEEP_ALIVE * 4
+
+/*
+ * PCEP PCC library initialization/teardown functions
+ */
+
+/* Later when this is integrated with FRR pathd, it will be changed
+ * to just initialize_pcc(struct pceplib_infra_config *infra_config) */
+bool initialize_pcc(void);
+bool initialize_pcc_infra(struct pceplib_infra_config *infra_config);
+/* this function is blocking */
+bool initialize_pcc_wait_for_completion(void);
+bool destroy_pcc(void);
+
+
+/*
+ * PCEP session functions
+ */
+
+pcep_configuration *create_default_pcep_configuration(void);
+void destroy_pcep_configuration(pcep_configuration *config);
+
+/* Uses the standard PCEP TCP src and dest port = 4189.
+ * To use a specific dest or src port, set them other than 0 in the
+ * pcep_configuration. If src_ip is not set, INADDR_ANY will be used. */
+pcep_session *connect_pce(pcep_configuration *config, struct in_addr *pce_ip);
+pcep_session *connect_pce_ipv6(pcep_configuration *config,
+ struct in6_addr *pce_ip);
+void disconnect_pce(pcep_session *session);
+void send_message(pcep_session *session, struct pcep_message *msg,
+ bool free_after_send);
+
+void dump_pcep_session_counters(pcep_session *session);
+void reset_pcep_session_counters(pcep_session *session);
+
+/*
+ * Event Queue functions
+ */
+
+/* Returns true if the queue is empty, false otherwise */
+bool event_queue_is_empty(void);
+
+/* Return the number of events on the queue, 0 if empty */
+uint32_t event_queue_num_events_available(void);
+
+/* Return the next event on the queue, NULL if empty */
+struct pcep_event *event_queue_get_event(void);
+
+/* Free the PCEP Event resources, including the PCEP message */
+void destroy_pcep_event(struct pcep_event *event);
+
+const char *get_event_type_str(int event_type);
+
+
+#endif /* PCEPPCC_INCLUDE_PCEPPCCAPI_H_ */
diff --git a/pceplib/pcep_session_logic.c b/pceplib/pcep_session_logic.c
new file mode 100644
index 000000000..5e4dae490
--- /dev/null
+++ b/pceplib/pcep_session_logic.c
@@ -0,0 +1,683 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_internals.h"
+#include "pcep_timers.h"
+#include "pcep_utils_counters.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+/*
+ * public API function implementations for the session_logic
+ */
+
+pcep_session_logic_handle *session_logic_handle_ = NULL;
+pcep_event_queue *session_logic_event_queue_ = NULL;
+int session_id_ = 0;
+
+void send_pcep_open(pcep_session *session); /* forward decl */
+
+static bool run_session_logic_common()
+{
+ if (session_logic_handle_ != NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Session Logic is already initialized.", __func__);
+ return false;
+ }
+
+ session_logic_handle_ = pceplib_malloc(
+ PCEPLIB_INFRA, sizeof(pcep_session_logic_handle));
+ memset(session_logic_handle_, 0, sizeof(pcep_session_logic_handle));
+
+ session_logic_handle_->active = true;
+ session_logic_handle_->session_logic_condition = false;
+ session_logic_handle_->session_list =
+ ordered_list_initialize(pointer_compare_function);
+ session_logic_handle_->session_event_queue = queue_initialize();
+
+ /* Initialize the event queue */
+ session_logic_event_queue_ =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event_queue));
+ session_logic_event_queue_->event_queue = queue_initialize();
+ if (pthread_mutex_init(&(session_logic_event_queue_->event_queue_mutex),
+ NULL)
+ != 0) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize session_logic event queue mutex.",
+ __func__);
+ return false;
+ }
+
+ pthread_cond_init(&(session_logic_handle_->session_logic_cond_var),
+ NULL);
+
+ if (pthread_mutex_init(&(session_logic_handle_->session_logic_mutex),
+ NULL)
+ != 0) {
+ pcep_log(LOG_ERR, "%s: Cannot initialize session_logic mutex.",
+ __func__);
+ return false;
+ }
+
+ if (pthread_mutex_init(&(session_logic_handle_->session_list_mutex),
+ NULL)
+ != 0) {
+ pcep_log(LOG_ERR, "%s: Cannot initialize session_list mutex.",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+
+bool run_session_logic()
+{
+ if (!run_session_logic_common()) {
+ return false;
+ }
+
+ if (pthread_create(&(session_logic_handle_->session_logic_thread), NULL,
+ session_logic_loop, session_logic_handle_)) {
+ pcep_log(LOG_ERR, "%s: Cannot initialize session_logic thread.",
+ __func__);
+ return false;
+ }
+
+ if (!initialize_timers(session_logic_timer_expire_handler)) {
+ pcep_log(LOG_ERR, "%s: Cannot initialize session_logic timers.",
+ __func__);
+ return false;
+ }
+
+ /* No need to call initialize_socket_comm_loop() since it will be
+ * called internally when the first socket_comm_session is created. */
+
+ return true;
+}
+
+
+bool run_session_logic_with_infra(pceplib_infra_config *infra_config)
+{
+ if (infra_config == NULL) {
+ return run_session_logic();
+ }
+
+ /* Initialize the memory infrastructure before anything gets allocated
+ */
+ if (infra_config->pceplib_infra_mt != NULL
+ && infra_config->pceplib_messages_mt != NULL) {
+ pceplib_memory_initialize(
+ infra_config->pceplib_infra_mt,
+ infra_config->pceplib_messages_mt,
+ infra_config->malloc_func, infra_config->calloc_func,
+ infra_config->realloc_func, infra_config->strdup_func,
+ infra_config->free_func);
+ }
+
+ if (!run_session_logic_common()) {
+ return false;
+ }
+
+ /* Create the pcep_session_logic pthread so it can be managed externally
+ */
+ if (infra_config->pthread_create_func != NULL) {
+ if (infra_config->pthread_create_func(
+ &(session_logic_handle_->session_logic_thread),
+ NULL, session_logic_loop, session_logic_handle_,
+ "pcep_session_logic")) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize external session_logic thread.",
+ __func__);
+ return false;
+ }
+ } else {
+ if (pthread_create(
+ &(session_logic_handle_->session_logic_thread),
+ NULL, session_logic_loop, session_logic_handle_)) {
+ pcep_log(LOG_ERR,
+ "%s: Cannot initialize session_logic thread.",
+ __func__);
+ return false;
+ }
+ }
+
+ session_logic_event_queue_->event_callback =
+ infra_config->pcep_event_func;
+ session_logic_event_queue_->event_callback_data =
+ infra_config->external_infra_data;
+
+ if (!initialize_timers_external_infra(
+ session_logic_timer_expire_handler,
+ infra_config->external_infra_data,
+ infra_config->timer_create_func,
+ infra_config->timer_cancel_func,
+ infra_config->pthread_create_func)) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize session_logic timers with infra.",
+ __func__);
+ return false;
+ }
+
+ /* We found a problem with the FRR sockets, where not all the KeepAlive
+ * messages were received, so if the pthread_create_func is set, the
+ * internal PCEPlib socket infrastructure will be used. */
+
+ /* For the SocketComm, the socket_read/write_func and the
+ * pthread_create_func are mutually exclusive. */
+ if (infra_config->pthread_create_func != NULL) {
+ if (!initialize_socket_comm_external_infra(
+ infra_config->external_infra_data, NULL, NULL,
+ infra_config->pthread_create_func)) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize session_logic socket comm with infra.",
+ __func__);
+ return false;
+ }
+ } else if (infra_config->socket_read_func != NULL
+ && infra_config->socket_write_func != NULL) {
+ if (!initialize_socket_comm_external_infra(
+ infra_config->external_infra_data,
+ infra_config->socket_read_func,
+ infra_config->socket_write_func, NULL)) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize session_logic socket comm with infra.",
+ __func__);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool run_session_logic_wait_for_completion()
+{
+ if (!run_session_logic()) {
+ return false;
+ }
+
+ /* Blocking call, waits for session logic thread to complete */
+ pthread_join(session_logic_handle_->session_logic_thread, NULL);
+
+ return true;
+}
+
+
+bool stop_session_logic()
+{
+ if (session_logic_handle_ == NULL) {
+ pcep_log(LOG_WARNING, "%s: Session logic already stopped",
+ __func__);
+ return false;
+ }
+
+ session_logic_handle_->active = false;
+ teardown_timers();
+
+ pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
+ session_logic_handle_->session_logic_condition = true;
+ pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
+ pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
+ pthread_join(session_logic_handle_->session_logic_thread, NULL);
+
+ pthread_mutex_destroy(&(session_logic_handle_->session_logic_mutex));
+ pthread_mutex_destroy(&(session_logic_handle_->session_list_mutex));
+ ordered_list_destroy(session_logic_handle_->session_list);
+ queue_destroy(session_logic_handle_->session_event_queue);
+
+ /* destroy the event_queue */
+ pthread_mutex_destroy(&(session_logic_event_queue_->event_queue_mutex));
+ queue_destroy(session_logic_event_queue_->event_queue);
+ pceplib_free(PCEPLIB_INFRA, session_logic_event_queue_);
+
+ /* Explicitly stop the socket comm loop started by the pcep_sessions */
+ destroy_socket_comm_loop();
+
+ pceplib_free(PCEPLIB_INFRA, session_logic_handle_);
+ session_logic_handle_ = NULL;
+
+ return true;
+}
+
+
+void close_pcep_session(pcep_session *session)
+{
+ close_pcep_session_with_reason(session, PCEP_CLOSE_REASON_NO);
+}
+
+void close_pcep_session_with_reason(pcep_session *session,
+ enum pcep_close_reason reason)
+{
+ struct pcep_message *close_msg = pcep_msg_create_close(reason);
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic send pcep_close message for session [%d]",
+ __func__, time(NULL), pthread_self(), session->session_id);
+
+ session_send_message(session, close_msg);
+ socket_comm_session_close_tcp_after_write(session->socket_comm_session);
+ session->session_state = SESSION_STATE_INITIALIZED;
+}
+
+
+void destroy_pcep_session(pcep_session *session)
+{
+ if (session == NULL) {
+ pcep_log(LOG_WARNING, "%s: Cannot destroy NULL session",
+ __func__);
+ return;
+ }
+
+ /* Remove the session from the session_list and synchronize session
+ * destroy with the session_logic_loop, so that no in-flight events
+ * will be handled now that the session is destroyed. */
+ pthread_mutex_lock(&(session_logic_handle_->session_list_mutex));
+ ordered_list_remove_first_node_equals(
+ session_logic_handle_->session_list, session);
+ pcep_log(LOG_DEBUG,
+ "%s: destroy_pcep_session delete session_list sessionPtr %p",
+ __func__, session);
+
+ pcep_session_cancel_timers(session);
+ delete_counters_group(session->pcep_session_counters);
+ queue_destroy_with_data(session->num_unknown_messages_time_queue);
+ socket_comm_session_teardown(session->socket_comm_session);
+
+ if (session->pcc_config.pcep_msg_versioning != NULL) {
+ pceplib_free(PCEPLIB_INFRA,
+ session->pcc_config.pcep_msg_versioning);
+ }
+
+ if (session->pce_config.pcep_msg_versioning != NULL) {
+ pceplib_free(PCEPLIB_INFRA,
+ session->pce_config.pcep_msg_versioning);
+ }
+
+ int session_id = session->session_id;
+ pceplib_free(PCEPLIB_INFRA, session);
+ pcep_log(LOG_INFO, "%s: [%ld-%ld] session [%d] destroyed", __func__,
+ time(NULL), pthread_self(), session_id);
+ pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex));
+}
+
+void pcep_session_cancel_timers(pcep_session *session)
+{
+ if (session == NULL) {
+ return;
+ }
+
+ if (session->timer_id_dead_timer != TIMER_ID_NOT_SET) {
+ cancel_timer(session->timer_id_dead_timer);
+ }
+
+ if (session->timer_id_keep_alive != TIMER_ID_NOT_SET) {
+ cancel_timer(session->timer_id_keep_alive);
+ }
+
+ if (session->timer_id_open_keep_wait != TIMER_ID_NOT_SET) {
+ cancel_timer(session->timer_id_open_keep_wait);
+ }
+
+ if (session->timer_id_open_keep_alive != TIMER_ID_NOT_SET) {
+ cancel_timer(session->timer_id_open_keep_alive);
+ }
+}
+
+/* Internal util function */
+static int get_next_session_id()
+{
+ if (session_id_ == INT_MAX) {
+ session_id_ = 0;
+ }
+
+ return session_id_++;
+}
+
+/* Internal util function */
+static pcep_session *create_pcep_session_pre_setup(pcep_configuration *config)
+{
+ if (config == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot create pcep session with NULL config",
+ __func__);
+ return NULL;
+ }
+
+ pcep_session *session =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_session));
+ memset(session, 0, sizeof(pcep_session));
+ session->session_id = get_next_session_id();
+ session->session_state = SESSION_STATE_INITIALIZED;
+ session->timer_id_open_keep_wait = TIMER_ID_NOT_SET;
+ session->timer_id_open_keep_alive = TIMER_ID_NOT_SET;
+ session->timer_id_dead_timer = TIMER_ID_NOT_SET;
+ session->timer_id_keep_alive = TIMER_ID_NOT_SET;
+ session->stateful_pce = false;
+ session->num_unknown_messages_time_queue = queue_initialize();
+ session->pce_open_received = false;
+ session->pce_open_rejected = false;
+ session->pce_open_keep_alive_sent = false;
+ session->pcc_open_rejected = false;
+ session->pce_open_accepted = false;
+ session->pcc_open_accepted = false;
+ session->destroy_session_after_write = false;
+ session->lsp_db_version = config->lsp_db_version;
+ memcpy(&(session->pcc_config), config, sizeof(pcep_configuration));
+ /* copy the pcc_config to the pce_config until we receive the open
+ * keep_alive response */
+ memcpy(&(session->pce_config), config, sizeof(pcep_configuration));
+ if (config->pcep_msg_versioning != NULL) {
+ session->pcc_config.pcep_msg_versioning = pceplib_malloc(
+ PCEPLIB_INFRA, sizeof(struct pcep_versioning));
+ memcpy(session->pcc_config.pcep_msg_versioning,
+ config->pcep_msg_versioning,
+ sizeof(struct pcep_versioning));
+ session->pce_config.pcep_msg_versioning = pceplib_malloc(
+ PCEPLIB_INFRA, sizeof(struct pcep_versioning));
+ memcpy(session->pce_config.pcep_msg_versioning,
+ config->pcep_msg_versioning,
+ sizeof(struct pcep_versioning));
+ }
+
+ pthread_mutex_lock(&(session_logic_handle_->session_list_mutex));
+ ordered_list_add_node(session_logic_handle_->session_list, session);
+ pcep_log(
+ LOG_DEBUG,
+ "%s: create_pcep_session_pre_setup add session_list sessionPtr %p",
+ __func__, session);
+ pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex));
+
+ return session;
+}
+
+/* Internal util function */
+static bool create_pcep_session_post_setup(pcep_session *session)
+{
+ if (!socket_comm_session_connect_tcp(session->socket_comm_session)) {
+ pcep_log(LOG_WARNING, "%s: Cannot establish TCP socket.",
+ __func__);
+ destroy_pcep_session(session);
+
+ return false;
+ }
+
+ session->time_connected = time(NULL);
+ create_session_counters(session);
+
+ send_pcep_open(session);
+
+ session->session_state = SESSION_STATE_PCEP_CONNECTING;
+ session->timer_id_open_keep_wait =
+ create_timer(session->pcc_config.keep_alive_seconds, session);
+ // session->session_state = SESSION_STATE_OPENED;
+
+ return true;
+}
+
+pcep_session *create_pcep_session(pcep_configuration *config,
+ struct in_addr *pce_ip)
+{
+ if (pce_ip == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot create pcep session with NULL pce_ip",
+ __func__);
+ return NULL;
+ }
+
+ pcep_session *session = create_pcep_session_pre_setup(config);
+ if (session == NULL) {
+ return NULL;
+ }
+
+ session->socket_comm_session = socket_comm_session_initialize_with_src(
+ NULL, session_logic_msg_ready_handler,
+ session_logic_message_sent_handler,
+ session_logic_conn_except_notifier, &(config->src_ip.src_ipv4),
+ ((config->src_pcep_port == 0) ? PCEP_TCP_PORT
+ : config->src_pcep_port),
+ pce_ip,
+ ((config->dst_pcep_port == 0) ? PCEP_TCP_PORT
+ : config->dst_pcep_port),
+ config->socket_connect_timeout_millis,
+ config->tcp_authentication_str, config->is_tcp_auth_md5,
+ session);
+ if (session->socket_comm_session == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot establish socket_comm_session.", __func__);
+ destroy_pcep_session(session);
+
+ return NULL;
+ }
+
+ if (create_pcep_session_post_setup(session) == false) {
+ return NULL;
+ }
+
+ return session;
+}
+
+pcep_session *create_pcep_session_ipv6(pcep_configuration *config,
+ struct in6_addr *pce_ip)
+{
+ if (pce_ip == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot create pcep session with NULL pce_ip",
+ __func__);
+ return NULL;
+ }
+
+ pcep_session *session = create_pcep_session_pre_setup(config);
+ if (session == NULL) {
+ return NULL;
+ }
+
+ session->socket_comm_session =
+ socket_comm_session_initialize_with_src_ipv6(
+ NULL, session_logic_msg_ready_handler,
+ session_logic_message_sent_handler,
+ session_logic_conn_except_notifier,
+ &(config->src_ip.src_ipv6),
+ ((config->src_pcep_port == 0) ? PCEP_TCP_PORT
+ : config->src_pcep_port),
+ pce_ip,
+ ((config->dst_pcep_port == 0) ? PCEP_TCP_PORT
+ : config->dst_pcep_port),
+ config->socket_connect_timeout_millis,
+ config->tcp_authentication_str, config->is_tcp_auth_md5,
+ session);
+ if (session->socket_comm_session == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot establish ipv6 socket_comm_session.",
+ __func__);
+ destroy_pcep_session(session);
+
+ return NULL;
+ }
+
+ if (create_pcep_session_post_setup(session) == false) {
+ return NULL;
+ }
+
+ return session;
+}
+
+
+void session_send_message(pcep_session *session, struct pcep_message *message)
+{
+ pcep_encode_message(message, session->pcc_config.pcep_msg_versioning);
+ socket_comm_session_send_message(session->socket_comm_session,
+ (char *)message->encoded_message,
+ message->encoded_message_length, true);
+
+ increment_message_tx_counters(session, message);
+
+ /* The message->encoded_message will be freed in
+ * socket_comm_session_send_message() once sent.
+ * Setting to NULL here so pcep_msg_free_message() does not free it */
+ message->encoded_message = NULL;
+ pcep_msg_free_message(message);
+}
+
+
+/* This function is also used in pcep_session_logic_states.c */
+struct pcep_message *create_pcep_open(pcep_session *session)
+{
+ /* create and send PCEP open
+ * with PCEP, the PCC sends the config the PCE should use in the open
+ * message,
+ * and the PCE will send an open with the config the PCC should use. */
+ double_linked_list *tlv_list = dll_initialize();
+ if (session->pcc_config.support_stateful_pce_lsp_update
+ || session->pcc_config.support_pce_lsp_instantiation
+ || session->pcc_config.support_include_db_version
+ || session->pcc_config.support_lsp_triggered_resync
+ || session->pcc_config.support_lsp_delta_sync
+ || session->pcc_config.support_pce_triggered_initial_sync) {
+ /* Prepend this TLV as the first in the list */
+ dll_append(
+ tlv_list,
+ pcep_tlv_create_stateful_pce_capability(
+ session->pcc_config
+ .support_stateful_pce_lsp_update, /* U
+ flag
+ */
+ session->pcc_config
+ .support_include_db_version, /* S flag
+ */
+ session->pcc_config
+ .support_lsp_triggered_resync, /* T flag
+ */
+ session->pcc_config
+ .support_lsp_delta_sync, /* D flag */
+ session->pcc_config
+ .support_pce_triggered_initial_sync, /* F flag */
+ session->pcc_config
+ .support_pce_lsp_instantiation)); /* I
+ flag
+ */
+ }
+
+ if (session->pcc_config.support_include_db_version) {
+ if (session->pcc_config.lsp_db_version != 0) {
+ dll_append(tlv_list,
+ pcep_tlv_create_lsp_db_version(
+ session->pcc_config.lsp_db_version));
+ }
+ }
+
+ if (session->pcc_config.support_sr_te_pst) {
+ bool flag_n = false;
+ bool flag_x = false;
+ if (session->pcc_config.pcep_msg_versioning
+ ->draft_ietf_pce_segment_routing_07
+ == false) {
+ flag_n = session->pcc_config.pcc_can_resolve_nai_to_sid;
+ flag_x = (session->pcc_config.max_sid_depth == 0);
+ }
+
+ struct pcep_object_tlv_sr_pce_capability *sr_pce_cap_tlv =
+ pcep_tlv_create_sr_pce_capability(
+ flag_n, flag_x,
+ session->pcc_config.max_sid_depth);
+
+ double_linked_list *sub_tlv_list = NULL;
+ if (session->pcc_config.pcep_msg_versioning
+ ->draft_ietf_pce_segment_routing_07
+ == true) {
+ /* With draft07, send the sr_pce_cap_tlv as a normal TLV
+ */
+ dll_append(tlv_list, sr_pce_cap_tlv);
+ } else {
+ /* With draft16, send the sr_pce_cap_tlv as a sub-TLV in
+ * the path_setup_type_capability TLV */
+ sub_tlv_list = dll_initialize();
+ dll_append(sub_tlv_list, sr_pce_cap_tlv);
+ }
+
+ uint8_t *pst =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint8_t));
+ *pst = SR_TE_PST;
+ double_linked_list *pst_list = dll_initialize();
+ dll_append(pst_list, pst);
+ dll_append(tlv_list, pcep_tlv_create_path_setup_type_capability(
+ pst_list, sub_tlv_list));
+ }
+
+ struct pcep_message *open_msg = pcep_msg_create_open_with_tlvs(
+ session->pcc_config.keep_alive_seconds,
+ session->pcc_config.dead_timer_seconds, session->session_id,
+ tlv_list);
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic create open message: TLVs [%d] for session [%d]",
+ __func__, time(NULL), pthread_self(), tlv_list->num_entries,
+ session->session_id);
+
+ return (open_msg);
+}
+
+
+void send_pcep_open(pcep_session *session)
+{
+ session_send_message(session, create_pcep_open(session));
+}
+
+/* This is a blocking call, since it is synchronized with destroy_pcep_session()
+ * and session_logic_loop(). It may be possible that the session has been
+ * deleted but API users havent been informed yet.
+ */
+bool session_exists(pcep_session *session)
+{
+ if (session_logic_handle_ == NULL) {
+ pcep_log(LOG_DEBUG,
+ "%s: session_exists session_logic_handle_ is NULL",
+ __func__);
+ return false;
+ }
+
+ pthread_mutex_lock(&(session_logic_handle_->session_list_mutex));
+ bool retval =
+ (ordered_list_find(session_logic_handle_->session_list, session)
+ != NULL);
+ pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex));
+
+ return retval;
+}
diff --git a/pceplib/pcep_session_logic.h b/pceplib/pcep_session_logic.h
new file mode 100644
index 000000000..a082ec66d
--- /dev/null
+++ b/pceplib/pcep_session_logic.h
@@ -0,0 +1,290 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#ifndef INCLUDE_PCEPSESSIONLOGIC_H_
+#define INCLUDE_PCEPSESSIONLOGIC_H_
+
+#include <stdbool.h>
+#include <netinet/tcp.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_socket_comm.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tools.h"
+#include "pcep_timers.h"
+#include "pcep_utils_queue.h"
+#include "pcep_utils_memory.h"
+
+#define PCEP_TCP_PORT 4189
+
+typedef struct pcep_configuration_ {
+ /* These are the configuration values that will
+ * be sent to the PCE in the PCEP Open message */
+ int keep_alive_seconds;
+ int dead_timer_seconds;
+ int dead_timer_pce_negotiated_seconds; /* Config data negotiated with
+ PCE */
+ int keep_alive_pce_negotiated_timer_seconds; /* Config data negotiated
+ with PCE */
+ int request_time_seconds;
+
+ /* These are the acceptable ranges of values received by
+ * the PCE in the initial PCEP Open Message. If a value is
+ * received outside of these ranges, then the Open message
+ * will be rejected. */
+ int min_keep_alive_seconds;
+ int max_keep_alive_seconds;
+ int min_dead_timer_seconds;
+ int max_dead_timer_seconds;
+
+ /* If more than this many unknown messages/requests are received
+ * per minute, then the session will be closed. */
+ int max_unknown_messages;
+ int max_unknown_requests;
+
+ /* Maximum amount of time to wait to connect to the
+ * PCE TCP socket before failing, in milliseconds. */
+ uint32_t socket_connect_timeout_millis;
+
+ /* Set if the PCE/PCC will support stateful PCE LSP Updates
+ * according to RCF8231, section 7.1.1, defaults to true.
+ * Will cause an additional TLV to be sent from the PCC in
+ * the PCEP Open */
+ bool support_stateful_pce_lsp_update;
+
+ /* RFC 8281: I-bit, the PCC allows instantiation of an LSP by a PCE */
+ bool support_pce_lsp_instantiation;
+
+ /* RFC 8232: S-bit, the PCC will include the LSP-DB-VERSION
+ * TLV in each LSP object */
+ bool support_include_db_version;
+
+ /* Only set if support_include_db_version is true and if the LSP-DB
+ * survived a restart and is available. If this has a value other than
+ * 0, then a LSP-DB-VERSION TLV will be sent in the OPEN object. This
+ * value will be copied over to the pcep_session upon init. */
+ uint64_t lsp_db_version;
+
+ /* RFC 8232: T-bit, the PCE can trigger resynchronization of
+ * LSPs at any point in the life of the session */
+ bool support_lsp_triggered_resync;
+
+ /* RFC 8232: D-bit, the PCEP speaker allows incremental (delta)
+ * State Synchronization */
+ bool support_lsp_delta_sync;
+
+ /* RFC 8232: F-bit, the PCE SHOULD trigger initial (first)
+ * State Synchronization */
+ bool support_pce_triggered_initial_sync;
+
+ /* draft-ietf-pce-segment-routing-16: Send a SR PCE Capability
+ * sub-TLV in a Path Setup Type Capability TLV with a PST = 1,
+ * Path is setup using SR TE. */
+ bool support_sr_te_pst;
+ /* Used in the SR PCE Capability sub-TLV */
+ bool pcc_can_resolve_nai_to_sid;
+ /* Used in the SR TE Capability sub-TLV, 0 means there are no max sid
+ * limits */
+ uint8_t max_sid_depth;
+
+ /* If set to 0, then the default 4189 PCEP port will be used */
+ uint16_t dst_pcep_port;
+
+ /* If set to 0, then the default 4189 PCEP port will be used.
+ * This is according to the RFC5440, Section 5 */
+ uint16_t src_pcep_port;
+
+ union src_ip {
+ struct in_addr src_ipv4;
+ struct in6_addr src_ipv6;
+ } src_ip;
+ bool is_src_ipv6;
+
+ struct pcep_versioning *pcep_msg_versioning;
+
+ char tcp_authentication_str[TCP_MD5SIG_MAXKEYLEN];
+ bool is_tcp_auth_md5; /* true: RFC 2385, false: RFC 5925 */
+
+} pcep_configuration;
+
+
+typedef enum pcep_session_state_ {
+ SESSION_STATE_UNKNOWN = 0,
+ SESSION_STATE_INITIALIZED = 1,
+ SESSION_STATE_PCEP_CONNECTING = 2,
+ SESSION_STATE_PCEP_CONNECTED = 3
+
+} pcep_session_state;
+
+
+typedef struct pcep_session_ {
+ int session_id;
+ pcep_session_state session_state;
+ int timer_id_open_keep_wait;
+ int timer_id_open_keep_alive;
+ int timer_id_dead_timer;
+ int timer_id_keep_alive;
+ bool pce_open_received;
+ bool pce_open_rejected;
+ bool pce_open_accepted;
+ bool pce_open_keep_alive_sent;
+ bool pcc_open_rejected;
+ bool pcc_open_accepted;
+ bool stateful_pce;
+ time_t time_connected;
+ uint64_t lsp_db_version;
+ queue_handle *num_unknown_messages_time_queue;
+ /* set this flag when finalizing the session */
+ bool destroy_session_after_write;
+ pcep_socket_comm_session *socket_comm_session;
+ /* Configuration sent from the PCC to the PCE */
+ pcep_configuration pcc_config;
+ /* Configuration received from the PCE, to be used in the PCC */
+ pcep_configuration pce_config;
+ struct counters_group *pcep_session_counters;
+
+} pcep_session;
+
+
+typedef enum pcep_event_type {
+ MESSAGE_RECEIVED = 0,
+ PCE_CLOSED_SOCKET = 1,
+ PCE_SENT_PCEP_CLOSE = 2,
+ PCE_DEAD_TIMER_EXPIRED = 3,
+ PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED = 4,
+ PCC_CONNECTED_TO_PCE = 100,
+ PCC_CONNECTION_FAILURE = 101,
+ PCC_PCEP_SESSION_CLOSED = 102,
+ PCC_RCVD_INVALID_OPEN = 103,
+ PCC_SENT_INVALID_OPEN = 104,
+ PCC_RCVD_MAX_INVALID_MSGS = 105,
+ PCC_RCVD_MAX_UNKOWN_MSGS = 106
+
+} pcep_event_type;
+
+
+typedef struct pcep_event {
+ enum pcep_event_type event_type;
+ time_t event_time;
+ struct pcep_message *message;
+ pcep_session *session;
+
+} pcep_event;
+
+typedef void (*pceplib_pcep_event_callback)(void *cb_data, pcep_event *);
+typedef int (*pthread_create_callback)(pthread_t *pthread_id,
+ const pthread_attr_t *attr,
+ void *(*start_routine)(void *),
+ void *data, const char *thread_name);
+
+
+typedef struct pcep_event_queue {
+ /* The event_queue and event_callback are mutually exclusive.
+ * If the event_callback is configured, then the event_queue
+ * will not be used. */
+ queue_handle *event_queue;
+ pthread_mutex_t event_queue_mutex;
+ pceplib_pcep_event_callback event_callback;
+ void *event_callback_data;
+
+} pcep_event_queue;
+
+
+typedef struct pceplib_infra_config {
+ /* Memory infrastructure */
+ void *pceplib_infra_mt;
+ void *pceplib_messages_mt;
+ pceplib_malloc_func malloc_func;
+ pceplib_calloc_func calloc_func;
+ pceplib_realloc_func realloc_func;
+ pceplib_strdup_func strdup_func;
+ pceplib_free_func free_func;
+
+ /* External Timer and Socket infrastructure */
+ void *external_infra_data;
+ ext_timer_create timer_create_func;
+ ext_timer_cancel timer_cancel_func;
+ ext_socket_write socket_write_func;
+ ext_socket_read socket_read_func;
+
+ /* External pcep_event infrastructure */
+ pceplib_pcep_event_callback pcep_event_func;
+
+ /* Callback to create pthreads */
+ pthread_create_callback pthread_create_func;
+
+} pceplib_infra_config;
+
+/*
+ * Counters Sub-groups definitions
+ */
+typedef enum pcep_session_counters_subgroup_ids {
+ COUNTER_SUBGROUP_ID_RX_MSG = 0,
+ COUNTER_SUBGROUP_ID_TX_MSG = 1,
+ COUNTER_SUBGROUP_ID_RX_OBJ = 2,
+ COUNTER_SUBGROUP_ID_TX_OBJ = 3,
+ COUNTER_SUBGROUP_ID_RX_SUBOBJ = 4,
+ COUNTER_SUBGROUP_ID_TX_SUBOBJ = 5,
+ COUNTER_SUBGROUP_ID_RX_RO_SR_SUBOBJ = 6,
+ COUNTER_SUBGROUP_ID_TX_RO_SR_SUBOBJ = 7,
+ COUNTER_SUBGROUP_ID_RX_TLV = 8,
+ COUNTER_SUBGROUP_ID_TX_TLV = 9,
+ COUNTER_SUBGROUP_ID_EVENT = 10
+
+} pcep_session_counters_subgroup_ids;
+
+bool run_session_logic(void);
+bool run_session_logic_with_infra(pceplib_infra_config *infra_config);
+
+bool run_session_logic_wait_for_completion(void);
+
+bool stop_session_logic(void);
+
+/* Uses the standard PCEP TCP dest port = 4189 and an ephemeral src port.
+ * To use a specific dest or src port, set them other than 0 in the
+ * pcep_configuration. */
+pcep_session *create_pcep_session(pcep_configuration *config,
+ struct in_addr *pce_ip);
+pcep_session *create_pcep_session_ipv6(pcep_configuration *config,
+ struct in6_addr *pce_ip);
+
+/* Send a PCEP close for this pcep_session */
+void close_pcep_session(pcep_session *session);
+void close_pcep_session_with_reason(pcep_session *session,
+ enum pcep_close_reason);
+
+/* Destroy the PCEP session, a PCEP close should have
+ * already been sent with close_pcep_session() */
+void destroy_pcep_session(pcep_session *session);
+
+void pcep_session_cancel_timers(pcep_session *session);
+
+/* Increments transmitted message counters, additionally counters for the
+ * objects, sub-objects, and TLVs in the message will be incremented. Received
+ * counters are incremented internally. */
+void increment_message_tx_counters(pcep_session *session,
+ struct pcep_message *message);
+
+bool session_exists(pcep_session *session);
+
+#endif /* INCLUDE_PCEPSESSIONLOGIC_H_ */
diff --git a/pceplib/pcep_session_logic_counters.c b/pceplib/pcep_session_logic_counters.c
new file mode 100644
index 000000000..a6bd41b4f
--- /dev/null
+++ b/pceplib/pcep_session_logic_counters.c
@@ -0,0 +1,450 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * PCEP session logic counters configuration.
+ */
+
+#include <stdio.h>
+#include <time.h>
+
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_internals.h"
+#include "pcep_utils_counters.h"
+#include "pcep_utils_logging.h"
+
+void increment_message_counters(pcep_session *session,
+ struct pcep_message *message, bool is_rx);
+
+void create_session_counters(pcep_session *session)
+{
+ /*
+ * Message RX and TX counters
+ */
+ struct counters_subgroup *rx_msg_subgroup = create_counters_subgroup(
+ "RX Message counters", COUNTER_SUBGROUP_ID_RX_MSG,
+ PCEP_TYPE_MAX + 1);
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_OPEN,
+ "Message Open");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_KEEPALIVE,
+ "Message KeepAlive");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_PCREQ,
+ "Message PcReq");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_PCREP,
+ "Message PcRep");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_PCNOTF,
+ "Message Notify");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_ERROR,
+ "Message Error");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_CLOSE,
+ "Message Close");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_REPORT,
+ "Message Report");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_UPDATE,
+ "Message Update");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_INITIATE,
+ "Message Initiate");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_START_TLS,
+ "Message StartTls");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_MAX,
+ "Message Erroneous");
+
+ struct counters_subgroup *tx_msg_subgroup =
+ clone_counters_subgroup(rx_msg_subgroup, "TX Message counters",
+ COUNTER_SUBGROUP_ID_TX_MSG);
+
+ /*
+ * Object RX and TX counters
+ */
+
+ /* For the Endpoints, the ID will be either 64 or 65, so setting
+ * num_counters to 100 */
+ struct counters_subgroup *rx_obj_subgroup = create_counters_subgroup(
+ "RX Object counters", COUNTER_SUBGROUP_ID_RX_OBJ, 100);
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_OPEN,
+ "Object Open");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_RP,
+ "Object RP");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_NOPATH,
+ "Object Nopath");
+ create_subgroup_counter(
+ rx_obj_subgroup,
+ ((PCEP_OBJ_CLASS_ENDPOINTS << 4) | PCEP_OBJ_TYPE_ENDPOINT_IPV4),
+ "Object Endpoint IPv4");
+ create_subgroup_counter(
+ rx_obj_subgroup,
+ ((PCEP_OBJ_CLASS_ENDPOINTS << 4) | PCEP_OBJ_TYPE_ENDPOINT_IPV6),
+ "Object Endpoint IPv6");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_BANDWIDTH,
+ "Object Bandwidth");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_METRIC,
+ "Object Metric");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_ERO,
+ "Object ERO");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_RRO,
+ "Object RRO");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_LSPA,
+ "Object LSPA");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_IRO,
+ "Object IRO");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SVEC,
+ "Object SVEC");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_NOTF,
+ "Object Notify");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_ERROR,
+ "Object Error");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_CLOSE,
+ "Object Close");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_LSP,
+ "Object LSP");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SRP,
+ "Object SRP");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_VENDOR_INFO,
+ "Object Vendor Info");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_INTER_LAYER,
+ "Object Inter-Layer");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SWITCH_LAYER,
+ "Object Switch-Layer");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_REQ_ADAP_CAP,
+ "Object Requested Adap-Cap");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SERVER_IND,
+ "Object Server-Indication");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_ASSOCIATION,
+ "Object Association");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_MAX,
+ "Object Unknown");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_MAX + 1,
+ "Object Erroneous");
+
+ struct counters_subgroup *tx_obj_subgroup =
+ clone_counters_subgroup(rx_obj_subgroup, "TX Object counters",
+ COUNTER_SUBGROUP_ID_TX_OBJ);
+
+ /*
+ * Sub-Object RX and TX counters
+ */
+ struct counters_subgroup *rx_subobj_subgroup = create_counters_subgroup(
+ "RX RO Sub-Object counters", COUNTER_SUBGROUP_ID_RX_SUBOBJ,
+ RO_SUBOBJ_UNKNOWN + 2);
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_IPV4,
+ "RO Sub-Object IPv4");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_IPV6,
+ "RO Sub-Object IPv6");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_LABEL,
+ "RO Sub-Object Label");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_UNNUM,
+ "RO Sub-Object Unnum");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_ASN,
+ "RO Sub-Object ASN");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_SR,
+ "RO Sub-Object SR");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_UNKNOWN,
+ "RO Sub-Object Unknown");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_UNKNOWN + 1,
+ "RO Sub-Object Erroneous");
+
+ struct counters_subgroup *tx_subobj_subgroup = clone_counters_subgroup(
+ rx_subobj_subgroup, "TX RO Sub-Object counters",
+ COUNTER_SUBGROUP_ID_TX_SUBOBJ);
+
+ /*
+ * RO SR Sub-Object RX and TX counters
+ */
+ struct counters_subgroup *rx_subobj_sr_nai_subgroup =
+ create_counters_subgroup("RX RO SR NAI Sub-Object counters",
+ COUNTER_SUBGROUP_ID_RX_RO_SR_SUBOBJ,
+ PCEP_SR_SUBOBJ_NAI_UNKNOWN + 1);
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_ABSENT,
+ "RO Sub-Object SR NAI absent");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE,
+ "RO Sub-Object SR NAI IPv4 Node");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_IPV6_NODE,
+ "RO Sub-Object SR NAI IPv6 Node");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY,
+ "RO Sub-Object SR NAI IPv4 Adj");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY,
+ "RO Sub-Object SR NAI IPv6 Adj");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY,
+ "RO Sub-Object SR NAI Unnumbered IPv4 Adj");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY,
+ "RO Sub-Object SR NAI Link Local IPv6 Adj");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_UNKNOWN,
+ "RO Sub-Object SR NAI Unknown");
+
+ struct counters_subgroup *tx_subobj_sr_nai_subgroup =
+ clone_counters_subgroup(rx_subobj_sr_nai_subgroup,
+ "TX RO SR NAI Sub-Object counters",
+ COUNTER_SUBGROUP_ID_TX_RO_SR_SUBOBJ);
+
+ /*
+ * TLV RX and TX counters
+ */
+ struct counters_subgroup *rx_tlv_subgroup = create_counters_subgroup(
+ "RX TLV counters", COUNTER_SUBGROUP_ID_RX_TLV,
+ PCEP_OBJ_TLV_TYPE_UNKNOWN + 1);
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR,
+ "TLV No Path Vector");
+ create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_VENDOR_INFO,
+ "TLV Vendor Info");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY,
+ "TLV Stateful PCE Capability");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME,
+ "TLV Symbolic Path Name");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS,
+ "TLV IPv4 LSP Identifier");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS,
+ "TLV IPv6 LSP Identifier");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE,
+ "TLV LSP Error Code");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC,
+ "TLV RSVP Error Spec");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION,
+ "TLV LSP DB Version");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID,
+ "TLV Speaker Entity ID");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY,
+ "TLV SR PCE Capability");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE,
+ "TLV Path Setup Type");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY,
+ "TLV Path Setup Type Capability");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID,
+ "TLV SR Policy PolId");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME,
+ "TLV SR Policy PolName");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID,
+ "TLV SR Policy CpathId");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE,
+ "TLV SR Policy CpathRef");
+ create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_UNKNOWN,
+ "TLV Unknown");
+
+ struct counters_subgroup *tx_tlv_subgroup = clone_counters_subgroup(
+ rx_tlv_subgroup, "TX TLV counters", COUNTER_SUBGROUP_ID_TX_TLV);
+
+ /*
+ * PCEP Event counters
+ */
+ struct counters_subgroup *events_subgroup = create_counters_subgroup(
+ "Events counters", COUNTER_SUBGROUP_ID_EVENT, MAX_COUNTERS);
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_PCC_CONNECT,
+ "PCC connect");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_PCE_CONNECT,
+ "PCE connect");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_PCC_DISCONNECT,
+ "PCC disconnect");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_PCE_DISCONNECT,
+ "PCE disconnect");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_TIMER_KEEPALIVE,
+ "Timer KeepAlive expired");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_TIMER_DEADTIMER,
+ "Timer DeadTimer expired");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPWAIT,
+ "Timer OpenKeepWait expired");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPALIVE,
+ "Timer OpenKeepAlive expired");
+
+ /*
+ * Create the parent counters group
+ */
+ time_t now = time(NULL);
+ char counters_name[MAX_COUNTER_STR_LENGTH] = {0};
+ char ip_str[40] = {0};
+ if (session->socket_comm_session->is_ipv6) {
+ inet_ntop(AF_INET6,
+ &session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6.sin6_addr,
+ ip_str, 40);
+ } else {
+ inet_ntop(AF_INET,
+ &session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4.sin_addr,
+ ip_str, 40);
+ }
+ snprintf(counters_name, MAX_COUNTER_STR_LENGTH,
+ "PCEP Session [%d], connected to [%s] for [%u seconds]",
+ session->session_id, ip_str,
+ (uint32_t)(now - session->time_connected));
+ /* The (time(NULL) - session->time_connected) will probably be 0,
+ * so the group name will be updated when the counters are dumped */
+ session->pcep_session_counters =
+ create_counters_group(counters_name, MAX_COUNTER_GROUPS);
+
+ /*
+ * Add all the subgroups to the parent counters group
+ */
+ add_counters_subgroup(session->pcep_session_counters, rx_msg_subgroup);
+ add_counters_subgroup(session->pcep_session_counters, tx_msg_subgroup);
+ add_counters_subgroup(session->pcep_session_counters, rx_obj_subgroup);
+ add_counters_subgroup(session->pcep_session_counters, tx_obj_subgroup);
+ add_counters_subgroup(session->pcep_session_counters,
+ rx_subobj_subgroup);
+ add_counters_subgroup(session->pcep_session_counters,
+ tx_subobj_subgroup);
+ add_counters_subgroup(session->pcep_session_counters,
+ rx_subobj_sr_nai_subgroup);
+ add_counters_subgroup(session->pcep_session_counters,
+ tx_subobj_sr_nai_subgroup);
+ add_counters_subgroup(session->pcep_session_counters, rx_tlv_subgroup);
+ add_counters_subgroup(session->pcep_session_counters, tx_tlv_subgroup);
+ add_counters_subgroup(session->pcep_session_counters, events_subgroup);
+}
+
+/* Internal util function used by increment_message_rx_counters or
+ * increment_message_tx_counters */
+void increment_message_counters(pcep_session *session,
+ struct pcep_message *message, bool is_rx)
+{
+ uint16_t counter_subgroup_id_msg = (is_rx ? COUNTER_SUBGROUP_ID_RX_MSG
+ : COUNTER_SUBGROUP_ID_TX_MSG);
+ uint16_t counter_subgroup_id_obj = (is_rx ? COUNTER_SUBGROUP_ID_RX_OBJ
+ : COUNTER_SUBGROUP_ID_TX_OBJ);
+ uint16_t counter_subgroup_id_subobj =
+ (is_rx ? COUNTER_SUBGROUP_ID_RX_SUBOBJ
+ : COUNTER_SUBGROUP_ID_TX_SUBOBJ);
+ uint16_t counter_subgroup_id_ro_sr_subobj =
+ (is_rx ? COUNTER_SUBGROUP_ID_RX_RO_SR_SUBOBJ
+ : COUNTER_SUBGROUP_ID_TX_RO_SR_SUBOBJ);
+ uint16_t counter_subgroup_id_tlv = (is_rx ? COUNTER_SUBGROUP_ID_RX_TLV
+ : COUNTER_SUBGROUP_ID_TX_TLV);
+
+ increment_counter(session->pcep_session_counters,
+ counter_subgroup_id_msg, message->msg_header->type);
+
+ /* Iterate the objects */
+ double_linked_list_node *obj_node =
+ (message->obj_list == NULL ? NULL : message->obj_list->head);
+ for (; obj_node != NULL; obj_node = obj_node->next_node) {
+ struct pcep_object_header *obj =
+ (struct pcep_object_header *)obj_node->data;
+
+ /* Handle class: PCEP_OBJ_CLASS_ENDPOINTS,
+ * type: PCEP_OBJ_TYPE_ENDPOINT_IPV4 or
+ * PCEP_OBJ_TYPE_ENDPOINT_IPV6 */
+ uint16_t obj_counter_id =
+ (obj->object_class == PCEP_OBJ_CLASS_ENDPOINTS
+ ? (obj->object_class << 4) | obj->object_type
+ : obj->object_class);
+
+ increment_counter(session->pcep_session_counters,
+ counter_subgroup_id_obj, obj_counter_id);
+
+ /* Iterate the RO Sub-objects */
+ if (obj->object_class == PCEP_OBJ_CLASS_ERO
+ || obj->object_class == PCEP_OBJ_CLASS_IRO
+ || obj->object_class == PCEP_OBJ_CLASS_RRO) {
+ struct pcep_object_ro *ro_obj =
+ (struct pcep_object_ro *)obj;
+
+ double_linked_list_node *ro_subobj_node =
+ (ro_obj->sub_objects == NULL
+ ? NULL
+ : ro_obj->sub_objects->head);
+ for (; ro_subobj_node != NULL;
+ ro_subobj_node = ro_subobj_node->next_node) {
+ struct pcep_object_ro_subobj *ro_subobj =
+ (struct pcep_object_ro_subobj *)
+ ro_subobj_node->data;
+ increment_counter(
+ session->pcep_session_counters,
+ counter_subgroup_id_subobj,
+ ro_subobj->ro_subobj_type);
+
+ /* Handle the ro subobj type RO_SUBOBJ_TYPE_SR
+ * different NAI types */
+ if (ro_subobj->ro_subobj_type
+ == RO_SUBOBJ_TYPE_SR) {
+ struct pcep_ro_subobj_sr *ro_sr_subobj =
+ (struct pcep_ro_subobj_sr *)
+ ro_subobj;
+ increment_counter(
+ session->pcep_session_counters,
+ counter_subgroup_id_ro_sr_subobj,
+ ro_sr_subobj->nai_type);
+ }
+ }
+ }
+
+ /* Iterate the TLVs */
+ double_linked_list_node *tlv_node =
+ (obj->tlv_list == NULL ? NULL : obj->tlv_list->head);
+ for (; tlv_node != NULL; tlv_node = tlv_node->next_node) {
+ struct pcep_object_tlv_header *tlv =
+ (struct pcep_object_tlv_header *)tlv_node->data;
+ increment_counter(session->pcep_session_counters,
+ counter_subgroup_id_tlv, tlv->type);
+ }
+ }
+}
+
+void increment_message_rx_counters(pcep_session *session,
+ struct pcep_message *message)
+{
+ increment_message_counters(session, message, true);
+}
+
+void increment_message_tx_counters(pcep_session *session,
+ struct pcep_message *message)
+{
+ increment_message_counters(session, message, false);
+}
+
+void increment_event_counters(
+ pcep_session *session,
+ pcep_session_counters_event_counter_ids counter_id)
+{
+ increment_counter(session->pcep_session_counters,
+ COUNTER_SUBGROUP_ID_EVENT, counter_id);
+}
diff --git a/pceplib/pcep_session_logic_internals.h b/pceplib/pcep_session_logic_internals.h
new file mode 100644
index 000000000..004459b91
--- /dev/null
+++ b/pceplib/pcep_session_logic_internals.h
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Internal Session Logic declarations, not intended to be in the public API.
+ */
+
+#ifndef SRC_PCEPSESSIONLOGICINTERNALS_H_
+#define SRC_PCEPSESSIONLOGICINTERNALS_H_
+
+
+#include <pthread.h>
+#include <stdbool.h>
+
+#include "pcep_msg_tools.h"
+
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_queue.h"
+
+
+typedef struct pcep_session_logic_handle_ {
+ pthread_t session_logic_thread;
+ pthread_mutex_t session_logic_mutex;
+ pthread_cond_t session_logic_cond_var;
+ bool session_logic_condition;
+ bool active;
+
+ ordered_list_handle *session_list;
+ pthread_mutex_t session_list_mutex;
+ /* Internal timers and socket events */
+ queue_handle *session_event_queue;
+
+} pcep_session_logic_handle;
+
+
+/* Used internally for Session events: message received, timer expired,
+ * or socket closed */
+typedef struct pcep_session_event_ {
+ pcep_session *session;
+ int expired_timer_id;
+ double_linked_list *received_msg_list;
+ bool socket_closed;
+
+} pcep_session_event;
+
+/* Event Counters counter-id definitions */
+typedef enum pcep_session_counters_event_counter_ids {
+ PCEP_EVENT_COUNTER_ID_PCC_CONNECT = 0,
+ PCEP_EVENT_COUNTER_ID_PCE_CONNECT = 1,
+ PCEP_EVENT_COUNTER_ID_PCC_DISCONNECT = 2,
+ PCEP_EVENT_COUNTER_ID_PCE_DISCONNECT = 3,
+ PCEP_EVENT_COUNTER_ID_TIMER_KEEPALIVE = 4,
+ PCEP_EVENT_COUNTER_ID_TIMER_DEADTIMER = 5,
+ PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPWAIT = 6,
+ PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPALIVE = 7
+
+} pcep_session_counters_event_counter_ids;
+
+/* functions implemented in pcep_session_logic_loop.c */
+void *session_logic_loop(void *data);
+int session_logic_msg_ready_handler(void *data, int socket_fd);
+void session_logic_message_sent_handler(void *data, int socket_fd);
+void session_logic_conn_except_notifier(void *data, int socket_fd);
+void session_logic_timer_expire_handler(void *data, int timer_id);
+
+void handle_timer_event(pcep_session_event *event);
+void handle_socket_comm_event(pcep_session_event *event);
+void session_send_message(pcep_session *session, struct pcep_message *message);
+
+/* defined in pcep_session_logic_states.c */
+void send_pcep_error(pcep_session *session, enum pcep_error_type error_type,
+ enum pcep_error_value error_value);
+void enqueue_event(pcep_session *session, pcep_event_type event_type,
+ struct pcep_message *message);
+void increment_unknown_message(pcep_session *session);
+
+/* defined in pcep_session_logic_counters.c */
+void create_session_counters(pcep_session *session);
+void increment_event_counters(
+ pcep_session *session,
+ pcep_session_counters_event_counter_ids counter_id);
+void increment_message_rx_counters(pcep_session *session,
+ struct pcep_message *message);
+
+/* defined in pcep_session_logic.c, also used in pcep_session_logic_states.c */
+struct pcep_message *create_pcep_open(pcep_session *session);
+
+#endif /* SRC_PCEPSESSIONLOGICINTERNALS_H_ */
diff --git a/pceplib/pcep_session_logic_loop.c b/pceplib/pcep_session_logic_loop.c
new file mode 100644
index 000000000..5705ff200
--- /dev/null
+++ b/pceplib/pcep_session_logic_loop.c
@@ -0,0 +1,360 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_internals.h"
+#include "pcep_timers.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+/* global var needed for callback handlers */
+extern pcep_session_logic_handle *session_logic_handle_;
+
+/* internal util function to create session_event's */
+static pcep_session_event *create_session_event(pcep_session *session)
+{
+ pcep_session_event *event =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_session_event));
+ event->session = session;
+ event->expired_timer_id = TIMER_ID_NOT_SET;
+ event->received_msg_list = NULL;
+ event->socket_closed = false;
+
+ return event;
+}
+
+
+/* A function pointer to this function is passed to pcep_socket_comm
+ * for each pcep_session creation, so it will be called whenever
+ * messages are ready to be read. This function will be called
+ * by the socket_comm thread.
+ * This function will decode the read PCEP message and give it
+ * to the session_logic_loop so it can be handled by the session_logic
+ * state machine. */
+int session_logic_msg_ready_handler(void *data, int socket_fd)
+{
+ if (data == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot handle msg_ready with NULL data",
+ __func__);
+ return -1;
+ }
+
+ if (session_logic_handle_->active == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Received a message ready notification while the session logic is not active",
+ __func__);
+ return -1;
+ }
+
+ pcep_session *session = (pcep_session *)data;
+
+ pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
+ session_logic_handle_->session_logic_condition = true;
+
+ /* This event will ultimately be handled by handle_socket_comm_event()
+ * in pcep_session_logic_states.c */
+ pcep_session_event *rcvd_msg_event = create_session_event(session);
+
+ int msg_length = 0;
+ double_linked_list *msg_list = pcep_msg_read(socket_fd);
+
+ if (msg_list == NULL) {
+ /* The socket was closed, or there was a socket read error */
+ pcep_log(LOG_INFO,
+ "%s: PCEP connection closed for session [%d]",
+ __func__, session->session_id);
+ dll_destroy(msg_list);
+ rcvd_msg_event->socket_closed = true;
+ socket_comm_session_teardown(session->socket_comm_session);
+ pcep_session_cancel_timers(session);
+ session->socket_comm_session = NULL;
+ session->session_state = SESSION_STATE_INITIALIZED;
+ enqueue_event(session, PCE_CLOSED_SOCKET, NULL);
+ } else if (msg_list->num_entries == 0) {
+ /* Invalid message received */
+ increment_unknown_message(session);
+ } else {
+ /* Just logging the first of potentially several messages
+ * received */
+ struct pcep_message *msg =
+ ((struct pcep_message *)msg_list->head->data);
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] session_logic_msg_ready_handler received message of type [%d] len [%d] on session [%d]",
+ __func__, time(NULL), pthread_self(),
+ msg->msg_header->type, msg->encoded_message_length,
+ session->session_id);
+
+ rcvd_msg_event->received_msg_list = msg_list;
+ msg_length = msg->encoded_message_length;
+ }
+
+ queue_enqueue(session_logic_handle_->session_event_queue,
+ rcvd_msg_event);
+ pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
+ pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
+
+ return msg_length;
+}
+
+
+/* A function pointer to this function was passed to pcep_socket_comm,
+ * so it will be called when a message is sent. This is useful since
+ * message sending is asynchronous, and there are times that actions
+ * need to be performed only after a message has been sent. */
+void session_logic_message_sent_handler(void *data, int socket_fd)
+{
+ (void)socket_fd;
+
+ if (data == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot handle msg_sent with NULL data", __func__);
+ return;
+ }
+
+ pcep_session *session = (pcep_session *)data;
+ if (session->destroy_session_after_write == true) {
+ /* Do not call destroy until all of the queued messages are
+ * written */
+ if (session->socket_comm_session != NULL
+ && session->socket_comm_session->message_queue->num_entries
+ == 0) {
+ destroy_pcep_session(session);
+ }
+ } else {
+ /* Reset the keep alive timer for every message sent on
+ * the session, only if the session is not destroyed */
+ if (session->timer_id_keep_alive == TIMER_ID_NOT_SET) {
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic set keep alive timer [%d secs] for session [%d]",
+ __func__, time(NULL), pthread_self(),
+ session->pcc_config
+ .keep_alive_pce_negotiated_timer_seconds,
+ session->session_id);
+ session->timer_id_keep_alive = create_timer(
+ session->pcc_config
+ .keep_alive_pce_negotiated_timer_seconds,
+ session);
+ } else {
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic reset keep alive timer [%d secs] for session [%d]",
+ __func__, time(NULL), pthread_self(),
+ session->pcc_config
+ .keep_alive_pce_negotiated_timer_seconds,
+ session->session_id);
+ reset_timer(session->timer_id_keep_alive);
+ }
+ }
+}
+
+
+/* A function pointer to this function was passed to pcep_socket_comm,
+ * so it will be called whenever the socket is closed. this function
+ * will be called by the socket_comm thread. */
+void session_logic_conn_except_notifier(void *data, int socket_fd)
+{
+ if (data == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot handle conn_except with NULL data",
+ __func__);
+ return;
+ }
+
+ if (session_logic_handle_->active == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Received a connection exception notification while the session logic is not active",
+ __func__);
+ return;
+ }
+
+ pcep_session *session = (pcep_session *)data;
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic session_logic_conn_except_notifier socket closed [%d], session [%d]",
+ __func__, time(NULL), pthread_self(), socket_fd,
+ session->session_id);
+
+ pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
+ pcep_session_event *socket_event = create_session_event(session);
+ socket_event->socket_closed = true;
+ queue_enqueue(session_logic_handle_->session_event_queue, socket_event);
+ session_logic_handle_->session_logic_condition = true;
+
+ pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
+ pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
+}
+
+
+/*
+ * this method is the timer expire handler, and will only
+ * pass the event to the session_logic loop and notify it
+ * that there is a timer available. this function will be
+ * called by the timers thread.
+ */
+void session_logic_timer_expire_handler(void *data, int timer_id)
+{
+ if (data == NULL) {
+ pcep_log(LOG_WARNING, "%s: Cannot handle timer with NULL data",
+ __func__);
+ return;
+ }
+
+ if (session_logic_handle_->active == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Received a timer expiration while the session logic is not active",
+ __func__);
+ return;
+ }
+
+ pcep_log(LOG_INFO, "%s: [%ld-%ld] timer expired handler timer_id [%d]",
+ __func__, time(NULL), pthread_self(), timer_id);
+ pcep_session_event *expired_timer_event =
+ create_session_event((pcep_session *)data);
+ expired_timer_event->expired_timer_id = timer_id;
+
+ pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
+ session_logic_handle_->session_logic_condition = true;
+ queue_enqueue(session_logic_handle_->session_event_queue,
+ expired_timer_event);
+
+ pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
+ pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
+}
+
+
+/*
+ * session_logic event loop
+ * this function is called upon thread creation from pcep_session_logic.c
+ */
+void *session_logic_loop(void *data)
+{
+ if (data == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot start session_logic_loop with NULL data",
+ __func__);
+
+ return NULL;
+ }
+
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting session_logic_loop thread",
+ __func__, time(NULL), pthread_self());
+
+ pcep_session_logic_handle *session_logic_handle =
+ (pcep_session_logic_handle *)data;
+
+ while (session_logic_handle->active) {
+ /* Mutex locking for session_logic_loop condition variable */
+ pthread_mutex_lock(
+ &(session_logic_handle->session_logic_mutex));
+
+ /* this internal loop helps avoid spurious interrupts */
+ while (!session_logic_handle->session_logic_condition) {
+ pthread_cond_wait(
+ &(session_logic_handle->session_logic_cond_var),
+ &(session_logic_handle->session_logic_mutex));
+ }
+
+ pcep_session_event *event = queue_dequeue(
+ session_logic_handle->session_event_queue);
+ while (event != NULL) {
+ if (event->session == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] Invalid session_logic_loop event [%s] with NULL session",
+ __func__, time(NULL), pthread_self(),
+ (event->expired_timer_id
+ != TIMER_ID_NOT_SET)
+ ? "timer"
+ : "message");
+ pceplib_free(PCEPLIB_INFRA, event);
+ event = queue_dequeue(
+ session_logic_handle
+ ->session_event_queue);
+ continue;
+ }
+
+ /* Check if the session still exists, and synchronize
+ * possible session destroy */
+ pcep_log(
+ LOG_DEBUG,
+ "%s: session_logic_loop checking session_list sessionPtr %p",
+ __func__, event->session);
+ pthread_mutex_lock(
+ &(session_logic_handle->session_list_mutex));
+ if (ordered_list_find(
+ session_logic_handle->session_list,
+ event->session)
+ == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] In-flight event [%s] for destroyed session being discarded",
+ __func__, time(NULL), pthread_self(),
+ (event->expired_timer_id
+ != TIMER_ID_NOT_SET)
+ ? "timer"
+ : "message");
+ pceplib_free(PCEPLIB_INFRA, event);
+ event = queue_dequeue(
+ session_logic_handle
+ ->session_event_queue);
+ pthread_mutex_unlock(
+ &(session_logic_handle_
+ ->session_list_mutex));
+ continue;
+ }
+
+ if (event->expired_timer_id != TIMER_ID_NOT_SET) {
+ handle_timer_event(event);
+ }
+
+ if (event->received_msg_list != NULL) {
+ handle_socket_comm_event(event);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, event);
+ event = queue_dequeue(
+ session_logic_handle->session_event_queue);
+
+ pthread_mutex_unlock(
+ &(session_logic_handle_->session_list_mutex));
+ }
+
+ session_logic_handle->session_logic_condition = false;
+ pthread_mutex_unlock(
+ &(session_logic_handle->session_logic_mutex));
+ }
+
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Finished session_logic_loop thread",
+ __func__, time(NULL), pthread_self());
+
+ return NULL;
+}
diff --git a/pceplib/pcep_session_logic_states.c b/pceplib/pcep_session_logic_states.c
new file mode 100644
index 000000000..5fac66765
--- /dev/null
+++ b/pceplib/pcep_session_logic_states.c
@@ -0,0 +1,1133 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_internals.h"
+#include "pcep_timers.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+#define TIMER_OPEN_KEEP_ALIVE_SECONDS 1
+
+/* Session Logic Handle managed in pcep_session_logic.c */
+extern pcep_event_queue *session_logic_event_queue_;
+void send_keep_alive(pcep_session *session);
+void send_pcep_error_with_object(pcep_session *session,
+ enum pcep_error_type error_type,
+ enum pcep_error_value error_value,
+ struct pcep_object_header *object);
+void reset_dead_timer(pcep_session *session);
+bool verify_pcep_open_object(pcep_session *session,
+ struct pcep_object_open *open_object);
+void send_reconciled_pcep_open(pcep_session *session,
+ struct pcep_message *error_msg);
+bool handle_pcep_update(pcep_session *session, struct pcep_message *upd_msg);
+bool handle_pcep_initiate(pcep_session *session, struct pcep_message *init_msg);
+bool check_and_send_open_keep_alive(pcep_session *session);
+void log_pcc_pce_connection(pcep_session *session);
+bool handle_pcep_open(pcep_session *session, struct pcep_message *open_msg);
+
+/*
+ * util functions called by the state handling below
+ */
+
+void send_keep_alive(pcep_session *session)
+{
+ struct pcep_message *keep_alive_msg = pcep_msg_create_keepalive();
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic send keep_alive message for session [%d]",
+ __func__, time(NULL), pthread_self(), session->session_id);
+
+ session_send_message(session, keep_alive_msg);
+
+ /* The keep alive timer will be (re)set once the message
+ * is sent in session_logic_message_sent_handler() */
+}
+
+
+/* Send an error message with the corrected or offending object */
+void send_pcep_error_with_object(pcep_session *session,
+ enum pcep_error_type error_type,
+ enum pcep_error_value error_value,
+ struct pcep_object_header *object)
+{
+ double_linked_list *obj_list = dll_initialize();
+ dll_append(obj_list, object);
+ struct pcep_message *error_msg = pcep_msg_create_error_with_objects(
+ error_type, error_value, obj_list);
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic send error message with object [%d][%d] for session [%d]",
+ __func__, time(NULL), pthread_self(), error_type, error_value,
+ session->session_id);
+
+ session_send_message(session, error_msg);
+}
+
+
+void send_pcep_error(pcep_session *session, enum pcep_error_type error_type,
+ enum pcep_error_value error_value)
+{
+ struct pcep_message *error_msg =
+ pcep_msg_create_error(error_type, error_value);
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic send error message [%d][%d] for session [%d]",
+ __func__, time(NULL), pthread_self(), error_type, error_value,
+ session->session_id);
+
+ session_send_message(session, error_msg);
+}
+
+
+void reset_dead_timer(pcep_session *session)
+{
+ /* Default to configured dead_timer if its not set yet or set to 0 by
+ * the PCE */
+ int dead_timer_seconds =
+ (session->pcc_config.dead_timer_pce_negotiated_seconds == 0)
+ ? session->pcc_config.dead_timer_seconds
+ : session->pcc_config.dead_timer_pce_negotiated_seconds;
+
+ if (session->timer_id_dead_timer == TIMER_ID_NOT_SET) {
+ session->timer_id_dead_timer =
+ create_timer(dead_timer_seconds, session);
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic set dead timer [%d secs] id [%d] for session [%d]",
+ __func__, time(NULL), pthread_self(),
+ dead_timer_seconds, session->timer_id_dead_timer,
+ session->session_id);
+ } else {
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic reset dead timer [%d secs] id [%d] for session [%d]",
+ __func__, time(NULL), pthread_self(),
+ dead_timer_seconds, session->timer_id_dead_timer,
+ session->session_id);
+ reset_timer(session->timer_id_dead_timer);
+ }
+}
+
+
+void enqueue_event(pcep_session *session, pcep_event_type event_type,
+ struct pcep_message *message)
+{
+ if (event_type == MESSAGE_RECEIVED && message == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: enqueue_event cannot enqueue a NULL message session [%d]",
+ __func__, session->session_id);
+ return;
+ }
+
+ pcep_event *event = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event));
+ memset(event, 0, sizeof(pcep_event));
+
+ event->session = session;
+ event->event_type = event_type;
+ event->event_time = time(NULL);
+ event->message = message;
+
+ pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex);
+ if (session_logic_event_queue_->event_callback != NULL) {
+ session_logic_event_queue_->event_callback(
+ session_logic_event_queue_->event_callback_data, event);
+ } else {
+ queue_enqueue(session_logic_event_queue_->event_queue, event);
+ }
+ pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex);
+}
+
+/* Verify the received PCEP Open object parameters are acceptable. If not,
+ * update the unacceptable value(s) with an acceptable value so it can be sent
+ * back to the sender. */
+bool verify_pcep_open_object(pcep_session *session,
+ struct pcep_object_open *open_object)
+{
+ int retval = true;
+
+ if (open_object->open_keepalive
+ < session->pcc_config.min_keep_alive_seconds) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting unsupported Open Keep Alive value [%d] min [%d]",
+ __func__, open_object->open_keepalive,
+ session->pcc_config.min_keep_alive_seconds);
+ open_object->open_keepalive =
+ session->pcc_config.min_keep_alive_seconds;
+ retval = false;
+ } else if (open_object->open_keepalive
+ > session->pcc_config.max_keep_alive_seconds) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting unsupported Open Keep Alive value [%d] max [%d]",
+ __func__, open_object->open_keepalive,
+ session->pcc_config.max_keep_alive_seconds);
+ open_object->open_keepalive =
+ session->pcc_config.max_keep_alive_seconds;
+ retval = false;
+ }
+
+ if (open_object->open_deadtimer
+ < session->pcc_config.min_dead_timer_seconds) {
+ pcep_log(LOG_INFO,
+ "%s: Rejecting unsupported Open Dead Timer value [%d]",
+ __func__, open_object->open_deadtimer);
+ open_object->open_deadtimer =
+ session->pcc_config.min_dead_timer_seconds;
+ retval = false;
+ } else if (open_object->open_deadtimer
+ > session->pcc_config.max_dead_timer_seconds) {
+ pcep_log(LOG_INFO,
+ "%s: Rejecting unsupported Open Dead Timer value [%d]",
+ __func__, open_object->open_deadtimer);
+ open_object->open_deadtimer =
+ session->pcc_config.max_dead_timer_seconds;
+ retval = false;
+ }
+
+ /* Check for Open Object TLVs */
+ if (pcep_object_has_tlvs((struct pcep_object_header *)open_object)
+ == false) {
+ /* There are no TLVs, all done */
+ return retval;
+ }
+
+ double_linked_list_node *tlv_node = open_object->header.tlv_list->head;
+ while (tlv_node != NULL) {
+ struct pcep_object_tlv_header *tlv = tlv_node->data;
+ tlv_node = tlv_node->next_node;
+
+ /* Supported Open Object TLVs */
+ switch (tlv->type) {
+ case PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION:
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID:
+ case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY:
+ break;
+
+ default:
+ /* TODO how to handle unrecognized TLV ?? */
+ pcep_log(
+ LOG_INFO,
+ "%s: Unhandled OPEN Object TLV type: %d, length %d",
+ __func__, tlv->type, tlv->encoded_tlv_length);
+ break;
+ }
+
+ /* Verify the STATEFUL-PCE-CAPABILITY TLV */
+ if (tlv->type == PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY) {
+ struct pcep_object_tlv_stateful_pce_capability
+ *pce_cap_tlv =
+ (struct
+ pcep_object_tlv_stateful_pce_capability
+ *)tlv;
+
+ /* If the U flag is set, then the PCE is
+ * capable of updating LSP parameters */
+ if (pce_cap_tlv->flag_u_lsp_update_capability) {
+ if (session->pce_config
+ .support_stateful_pce_lsp_update
+ == false) {
+ /* Turn off the U bit, as it is not
+ * supported */
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting unsupported Open STATEFUL-PCE-CAPABILITY TLV U flag",
+ __func__);
+ pce_cap_tlv
+ ->flag_u_lsp_update_capability =
+ false;
+ retval = false;
+ } else {
+ session->stateful_pce = true;
+ pcep_log(
+ LOG_INFO,
+ "%s: Setting PCEP session [%d] STATEFUL to support LSP updates",
+ __func__, session->session_id);
+ }
+ }
+ /* TODO the rest of the flags are not implemented yet */
+ else if (pce_cap_tlv->flag_s_include_db_version) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV S Include DB Version flag",
+ __func__);
+ } else if (
+ pce_cap_tlv
+ ->flag_i_lsp_instantiation_capability) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV I LSP Instantiation Capability flag",
+ __func__);
+ } else if (pce_cap_tlv->flag_t_triggered_resync) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV T Triggered Resync flag",
+ __func__);
+ } else if (pce_cap_tlv->flag_d_delta_lsp_sync) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV D Delta LSP Sync flag",
+ __func__);
+ } else if (pce_cap_tlv->flag_f_triggered_initial_sync) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV F Triggered Initial Sync flag",
+ __func__);
+ }
+ } else if (tlv->type == PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION) {
+ if (session->pce_config.support_include_db_version
+ == false) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting unsupported Open LSP DB VERSION TLV",
+ __func__);
+ /* Remove this TLV from the list */
+ dll_delete_node(open_object->header.tlv_list,
+ tlv_node);
+ retval = false;
+ }
+ }
+ }
+
+ return retval;
+}
+
+
+bool handle_pcep_open(pcep_session *session, struct pcep_message *open_msg)
+{
+ /* Open Message validation and errors according to:
+ * https://tools.ietf.org/html/rfc5440#section-7.15 */
+
+ if (session->session_state != SESSION_STATE_PCEP_CONNECTING
+ && session->session_state != SESSION_STATE_INITIALIZED) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Received unexpected OPEN, current session state [%d, replying with error]",
+ __func__, session->session_state);
+ send_pcep_error(session,
+ PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION,
+ PCEP_ERRV_RECVD_INVALID_OPEN_MSG);
+ return false;
+ }
+
+ if (session->pce_open_received == true
+ && session->pce_open_rejected == false) {
+ pcep_log(LOG_INFO,
+ "%s: Received duplicate OPEN, replying with error",
+ __func__);
+ send_pcep_error(session,
+ PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION,
+ PCEP_ERRV_RECVD_INVALID_OPEN_MSG);
+ return false;
+ }
+
+ struct pcep_object_open *open_object =
+ (struct pcep_object_open *)pcep_obj_get(open_msg->obj_list,
+ PCEP_OBJ_CLASS_OPEN);
+ if (open_object == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Received OPEN message with no OPEN object, replying with error",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_SESSION_FAILURE,
+ PCEP_ERRV_RECVD_INVALID_OPEN_MSG);
+ return false;
+ }
+
+ /* Check for additional Open Msg objects */
+ if (open_msg->obj_list->num_entries > 1) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Found additional unsupported objects in the Open message, replying with error",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_SESSION_FAILURE,
+ PCEP_ERRV_RECVD_INVALID_OPEN_MSG);
+ return false;
+ }
+
+ session->pce_open_received = true;
+
+ /* Verify the open object parameters and TLVs */
+ if (verify_pcep_open_object(session, open_object) == false) {
+ enqueue_event(session, PCC_RCVD_INVALID_OPEN, NULL);
+ if (session->pce_open_rejected) {
+ /* The Open message was already rejected once, so
+ * according to the spec, send an error message and
+ * close the TCP connection. */
+ pcep_log(
+ LOG_INFO,
+ "%s: Received 2 consecutive unsupported Open messages, closing the connection.",
+ __func__);
+ send_pcep_error(
+ session, PCEP_ERRT_SESSION_FAILURE,
+ PCEP_ERRV_RECVD_SECOND_OPEN_MSG_UNACCEPTABLE);
+ socket_comm_session_close_tcp_after_write(
+ session->socket_comm_session);
+ session->session_state = SESSION_STATE_INITIALIZED;
+ enqueue_event(session, PCC_CONNECTION_FAILURE, NULL);
+ } else {
+ session->pce_open_rejected = true;
+ /* Clone the object here, since the encapsulating
+ * message will be deleted in handle_socket_comm_event()
+ * most likely before this error message is sent */
+ struct pcep_object_open *cloned_open_object =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct pcep_object_open));
+ memcpy(cloned_open_object, open_object,
+ sizeof(struct pcep_object_open));
+ open_object->header.tlv_list = NULL;
+ cloned_open_object->header.encoded_object = NULL;
+ cloned_open_object->header.encoded_object_length = 0;
+ send_pcep_error_with_object(
+ session, PCEP_ERRT_SESSION_FAILURE,
+ PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG,
+ &cloned_open_object->header);
+ }
+
+ return false;
+ }
+
+ /*
+ * Open Message accepted
+ * Sending the keep-alive response will be managed the function caller
+ */
+
+ session->timer_id_open_keep_alive =
+ create_timer(TIMER_OPEN_KEEP_ALIVE_SECONDS, session);
+ session->pcc_config.dead_timer_pce_negotiated_seconds =
+ (int)open_object->open_deadtimer;
+ /* Cancel the timer so we can change the dead_timer value */
+ cancel_timer(session->timer_id_dead_timer);
+ session->timer_id_dead_timer = TIMER_ID_NOT_SET;
+ reset_dead_timer(session);
+
+ return true;
+}
+
+
+/* The original PCEP Open message sent to the PCE was rejected,
+ * try to reconcile the differences and re-send a new Open. */
+void send_reconciled_pcep_open(pcep_session *session,
+ struct pcep_message *error_msg)
+{
+ struct pcep_message *open_msg = create_pcep_open(session);
+
+ struct pcep_object_open *error_open_obj =
+ (struct pcep_object_open *)pcep_obj_get(error_msg->obj_list,
+ PCEP_OBJ_CLASS_OPEN);
+ if (error_open_obj == NULL) {
+ /* Nothing to reconcile, send the same Open message again */
+ pcep_log(
+ LOG_INFO,
+ "%s: No Open object received in Error, sending the same Open message",
+ __func__);
+ session_send_message(session, open_msg);
+ return;
+ }
+
+ struct pcep_object_open *open_obj =
+ (struct pcep_object_open *)pcep_obj_get(open_msg->obj_list,
+ PCEP_OBJ_CLASS_OPEN);
+
+ if (error_open_obj->open_deadtimer
+ != session->pce_config.dead_timer_seconds) {
+ if (error_open_obj->open_deadtimer
+ >= session->pce_config.min_dead_timer_seconds
+ && error_open_obj->open_deadtimer
+ <= session->pce_config.max_dead_timer_seconds) {
+ open_obj->open_deadtimer =
+ error_open_obj->open_deadtimer;
+ session->pcc_config.dead_timer_pce_negotiated_seconds =
+ error_open_obj->open_deadtimer;
+ pcep_log(
+ LOG_INFO,
+ "%s: Open deadtimer value [%d] rejected, using PCE value [%d]",
+ __func__,
+ session->pcc_config.dead_timer_seconds,
+ session->pcc_config
+ .dead_timer_pce_negotiated_seconds);
+ /* Reset the timer with the new value */
+ cancel_timer(session->timer_id_dead_timer);
+ session->timer_id_dead_timer = TIMER_ID_NOT_SET;
+ reset_dead_timer(session);
+ } else {
+ pcep_log(
+ LOG_INFO,
+ "%s: Can not reconcile Open with suggested deadtimer [%d]",
+ __func__, error_open_obj->open_deadtimer);
+ }
+ }
+
+ if (error_open_obj->open_keepalive
+ != session->pce_config.keep_alive_seconds) {
+ if (error_open_obj->open_keepalive
+ >= session->pce_config.min_keep_alive_seconds
+ && error_open_obj->open_keepalive
+ <= session->pce_config.max_keep_alive_seconds) {
+ open_obj->open_keepalive =
+ error_open_obj->open_keepalive;
+ session->pcc_config
+ .keep_alive_pce_negotiated_timer_seconds =
+ error_open_obj->open_keepalive;
+ pcep_log(
+ LOG_INFO,
+ "%s: Open keep alive value [%d] rejected, using PCE value [%d]",
+ __func__,
+ session->pcc_config.keep_alive_seconds,
+ session->pcc_config
+ .keep_alive_pce_negotiated_timer_seconds);
+ /* Cancel the timer, the timer will be set again with
+ * the new value when this open message is sent */
+ cancel_timer(session->timer_id_keep_alive);
+ session->timer_id_keep_alive = TIMER_ID_NOT_SET;
+ } else {
+ pcep_log(
+ LOG_INFO,
+ "%s: Can not reconcile Open with suggested keepalive [%d]",
+ __func__, error_open_obj->open_keepalive);
+ }
+ }
+
+ /* TODO reconcile the TLVs */
+
+ session_send_message(session, open_msg);
+ reset_timer(session->timer_id_open_keep_alive);
+}
+
+
+bool handle_pcep_update(pcep_session *session, struct pcep_message *upd_msg)
+{
+ /* Update Message validation and errors according to:
+ * https://tools.ietf.org/html/rfc8231#section-6.2 */
+
+ if (upd_msg->obj_list == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PcUpd message: Message has no objects",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_SRP_OBJECT_MISSING);
+ return false;
+ }
+
+ /* Verify the mandatory objects are present */
+ struct pcep_object_header *obj =
+ pcep_obj_get(upd_msg->obj_list, PCEP_OBJ_CLASS_SRP);
+ if (obj == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PcUpd message: Missing SRP object",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_SRP_OBJECT_MISSING);
+ return false;
+ }
+
+ obj = pcep_obj_get(upd_msg->obj_list, PCEP_OBJ_CLASS_LSP);
+ if (obj == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PcUpd message: Missing LSP object",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_LSP_OBJECT_MISSING);
+ return false;
+ }
+
+ obj = pcep_obj_get(upd_msg->obj_list, PCEP_OBJ_CLASS_ERO);
+ if (obj == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PcUpd message: Missing ERO object",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_ERO_OBJECT_MISSING);
+ return false;
+ }
+
+ /* Verify the objects are are in the correct order */
+ double_linked_list_node *node = upd_msg->obj_list->head;
+ struct pcep_object_srp *srp_object =
+ (struct pcep_object_srp *)node->data;
+ if (srp_object->header.object_class != PCEP_OBJ_CLASS_SRP) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PcUpd message: First object must be an SRP, found [%d]",
+ __func__, srp_object->header.object_class);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_SRP_OBJECT_MISSING);
+ return false;
+ }
+
+ node = node->next_node;
+ struct pcep_object_lsp *lsp_object =
+ (struct pcep_object_lsp *)node->data;
+ if (lsp_object->header.object_class != PCEP_OBJ_CLASS_LSP) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PcUpd message: Second object must be an LSP, found [%d]",
+ __func__, lsp_object->header.object_class);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_LSP_OBJECT_MISSING);
+ return false;
+ }
+
+ node = node->next_node;
+ struct pcep_object_ro *ero_object = node->data;
+ if (ero_object->header.object_class != PCEP_OBJ_CLASS_ERO) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PcUpd message: Third object must be an ERO, found [%d]",
+ __func__, ero_object->header.object_class);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_ERO_OBJECT_MISSING);
+ return false;
+ }
+
+ return true;
+}
+
+bool handle_pcep_initiate(pcep_session *session, struct pcep_message *init_msg)
+{
+ /* Instantiate Message validation and errors according to:
+ * https://tools.ietf.org/html/rfc8281#section-5 */
+
+ if (init_msg->obj_list == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PcInitiate message: Message has no objects",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_SRP_OBJECT_MISSING);
+ return false;
+ }
+
+ /* Verify the mandatory objects are present */
+ struct pcep_object_header *obj =
+ pcep_obj_get(init_msg->obj_list, PCEP_OBJ_CLASS_SRP);
+ if (obj == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PcInitiate message: Missing SRP object",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_SRP_OBJECT_MISSING);
+ return false;
+ }
+
+ obj = pcep_obj_get(init_msg->obj_list, PCEP_OBJ_CLASS_LSP);
+ if (obj == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PcInitiate message: Missing LSP object",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_LSP_OBJECT_MISSING);
+ return false;
+ }
+
+ /* Verify the objects are are in the correct order */
+ double_linked_list_node *node = init_msg->obj_list->head;
+ struct pcep_object_srp *srp_object =
+ (struct pcep_object_srp *)node->data;
+ if (srp_object->header.object_class != PCEP_OBJ_CLASS_SRP) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PcInitiate message: First object must be an SRP, found [%d]",
+ __func__, srp_object->header.object_class);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_SRP_OBJECT_MISSING);
+ return false;
+ }
+
+ node = node->next_node;
+ struct pcep_object_lsp *lsp_object =
+ (struct pcep_object_lsp *)node->data;
+ if (lsp_object->header.object_class != PCEP_OBJ_CLASS_LSP) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PcInitiate message: Second object must be an LSP, found [%d]",
+ __func__, lsp_object->header.object_class);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_LSP_OBJECT_MISSING);
+ return false;
+ }
+
+ /* There may be more optional objects */
+ return true;
+}
+
+void increment_unknown_message(pcep_session *session)
+{
+ /* https://tools.ietf.org/html/rfc5440#section-6.9
+ * If a PCC/PCE receives unrecognized messages at a rate equal or
+ * greater than MAX-UNKNOWN-MESSAGES unknown message requests per
+ * minute, the PCC/PCE MUST send a PCEP CLOSE message */
+
+ time_t *unknown_message_time =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(time_t));
+ *unknown_message_time = time(NULL);
+ time_t expire_time = *unknown_message_time + 60;
+ queue_enqueue(session->num_unknown_messages_time_queue,
+ unknown_message_time);
+
+ /* Purge any entries older than 1 minute. The oldest entries are at the
+ * queue head */
+ queue_node *time_node = session->num_unknown_messages_time_queue->head;
+ while (time_node != NULL) {
+ if (*((time_t *)time_node->data) > expire_time) {
+ pceplib_free(
+ PCEPLIB_INFRA,
+ queue_dequeue(
+ session->num_unknown_messages_time_queue));
+ time_node =
+ session->num_unknown_messages_time_queue->head;
+ } else {
+ time_node = NULL;
+ }
+ }
+
+ if ((int)session->num_unknown_messages_time_queue->num_entries
+ >= session->pcc_config.max_unknown_messages) {
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] Max unknown messages reached [%d] closing session [%d]",
+ __func__, time(NULL), pthread_self(),
+ session->pcc_config.max_unknown_messages,
+ session->session_id);
+
+ close_pcep_session_with_reason(session,
+ PCEP_CLOSE_REASON_UNREC_MSG);
+ enqueue_event(session, PCC_RCVD_MAX_UNKOWN_MSGS, NULL);
+ }
+}
+
+bool check_and_send_open_keep_alive(pcep_session *session)
+{
+ if (session->pce_open_received == true
+ && session->pce_open_rejected == false
+ && session->pce_open_keep_alive_sent == false) {
+ /* Send the PCE Open keep-alive response if it hasnt been sent
+ * yet */
+ cancel_timer(session->timer_id_open_keep_alive);
+ session->timer_id_open_keep_alive = TIMER_ID_NOT_SET;
+ send_keep_alive(session);
+ session->pce_open_keep_alive_sent = true;
+
+ return true;
+ }
+
+ return false;
+}
+
+void log_pcc_pce_connection(pcep_session *session)
+{
+ if (session->socket_comm_session == NULL) {
+ /* This only happens in UT */
+ return;
+ }
+
+ char src_ip_buf[40] = {0}, dst_ip_buf[40] = {0};
+ uint16_t src_port, dst_port;
+
+ if (session->socket_comm_session->is_ipv6) {
+ inet_ntop(AF_INET6,
+ &session->socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv6.sin6_addr,
+ src_ip_buf, sizeof(src_ip_buf));
+ inet_ntop(AF_INET6,
+ &session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6.sin6_addr,
+ dst_ip_buf, sizeof(dst_ip_buf));
+ src_port = htons(session->socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv6.sin6_port);
+ dst_port = htons(session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6.sin6_port);
+ } else {
+ inet_ntop(AF_INET,
+ &session->socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv4.sin_addr,
+ src_ip_buf, sizeof(src_ip_buf));
+ inet_ntop(AF_INET,
+ &session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4.sin_addr,
+ dst_ip_buf, sizeof(dst_ip_buf));
+ src_port = htons(session->socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv4.sin_port);
+ dst_port = htons(session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4.sin_port);
+ }
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] Successful PCC [%s:%d] connection to PCE [%s:%d] session [%d] fd [%d]",
+ __func__, time(NULL), pthread_self(), src_ip_buf, src_port,
+ dst_ip_buf, dst_port, session->session_id,
+ session->socket_comm_session->socket_fd);
+}
+
+/*
+ * these functions are called by session_logic_loop() from
+ * pcep_session_logic_loop.c these functions are executed in the
+ * session_logic_loop thread, and the mutex is locked before calling these
+ * functions, so they are thread safe.
+ */
+
+/* state machine handling for expired timers */
+void handle_timer_event(pcep_session_event *event)
+{
+ if (event == NULL) {
+ pcep_log(LOG_INFO, "%s: handle_timer_event NULL event",
+ __func__);
+ return;
+ }
+
+ pcep_session *session = event->session;
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic handle_timer_event: session [%d] event timer_id [%d] "
+ "session timers [OKW, OKA, DT, KA] [%d, %d, %d, %d]",
+ __func__, time(NULL), pthread_self(), session->session_id,
+ event->expired_timer_id, session->timer_id_open_keep_wait,
+ session->timer_id_open_keep_alive, session->timer_id_dead_timer,
+ session->timer_id_keep_alive);
+
+ /*
+ * these timer expirations are independent of the session state
+ */
+ if (event->expired_timer_id == session->timer_id_dead_timer) {
+ session->timer_id_dead_timer = TIMER_ID_NOT_SET;
+ increment_event_counters(session,
+ PCEP_EVENT_COUNTER_ID_TIMER_DEADTIMER);
+ close_pcep_session_with_reason(session,
+ PCEP_CLOSE_REASON_DEADTIMER);
+ enqueue_event(session, PCE_DEAD_TIMER_EXPIRED, NULL);
+ return;
+ } else if (event->expired_timer_id == session->timer_id_keep_alive) {
+ session->timer_id_keep_alive = TIMER_ID_NOT_SET;
+ increment_event_counters(session,
+ PCEP_EVENT_COUNTER_ID_TIMER_KEEPALIVE);
+ send_keep_alive(session);
+ return;
+ }
+
+ /*
+ * handle timers that depend on the session state
+ */
+ switch (session->session_state) {
+ case SESSION_STATE_PCEP_CONNECTING:
+ if (event->expired_timer_id
+ == session->timer_id_open_keep_wait) {
+ /* close the TCP session */
+ pcep_log(
+ LOG_INFO,
+ "%s: handle_timer_event open_keep_wait timer expired for session [%d]",
+ __func__, session->session_id);
+ increment_event_counters(
+ session,
+ PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPWAIT);
+ socket_comm_session_close_tcp_after_write(
+ session->socket_comm_session);
+ session->session_state = SESSION_STATE_INITIALIZED;
+ session->timer_id_open_keep_wait = TIMER_ID_NOT_SET;
+ enqueue_event(session, PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED,
+ NULL);
+ }
+
+ if (event->expired_timer_id
+ == session->timer_id_open_keep_alive) {
+ increment_event_counters(
+ session,
+ PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPALIVE);
+ session->timer_id_open_keep_alive = TIMER_ID_NOT_SET;
+ if (check_and_send_open_keep_alive(session) == true) {
+ if (session->pcc_open_accepted == true
+ && session->session_state
+ != SESSION_STATE_PCEP_CONNECTED) {
+ log_pcc_pce_connection(session);
+ session->session_state =
+ SESSION_STATE_PCEP_CONNECTED;
+ increment_event_counters(
+ session,
+ PCEP_EVENT_COUNTER_ID_PCE_CONNECT);
+ enqueue_event(session,
+ PCC_CONNECTED_TO_PCE,
+ NULL);
+ }
+ }
+ return;
+ }
+ break;
+
+ case SESSION_STATE_INITIALIZED:
+ case SESSION_STATE_PCEP_CONNECTED:
+ default:
+ pcep_log(
+ LOG_INFO,
+ "%s: handle_timer_event unrecognized state transition, timer_id [%d] state [%d] session [%d]",
+ __func__, event->expired_timer_id,
+ session->session_state, session->session_id);
+ break;
+ }
+}
+
+/* State machine handling for received messages.
+ * This event was created in session_logic_msg_ready_handler() in
+ * pcep_session_logic_loop.c */
+void handle_socket_comm_event(pcep_session_event *event)
+{
+ if (event == NULL) {
+ pcep_log(LOG_INFO, "%s: handle_socket_comm_event NULL event",
+ __func__);
+ return;
+ }
+
+ pcep_session *session = event->session;
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic handle_socket_comm_event: session [%d] num messages [%d] socket_closed [%d]",
+ __func__, time(NULL), pthread_self(), session->session_id,
+ (event->received_msg_list == NULL
+ ? -1
+ : (int)event->received_msg_list->num_entries),
+ event->socket_closed);
+
+ /*
+ * independent of the session state
+ */
+ if (event->socket_closed) {
+ pcep_log(
+ LOG_INFO,
+ "%s: handle_socket_comm_event socket closed for session [%d]",
+ __func__, session->session_id);
+ socket_comm_session_close_tcp(session->socket_comm_session);
+ enqueue_event(session, PCE_CLOSED_SOCKET, NULL);
+ if (session->session_state == SESSION_STATE_PCEP_CONNECTING) {
+ enqueue_event(session, PCC_CONNECTION_FAILURE, NULL);
+ }
+ session->session_state = SESSION_STATE_INITIALIZED;
+ increment_event_counters(session,
+ PCEP_EVENT_COUNTER_ID_PCE_DISCONNECT);
+ return;
+ }
+
+ reset_dead_timer(session);
+
+ if (event->received_msg_list == NULL) {
+ return;
+ }
+
+ /* Message received on socket */
+ double_linked_list_node *msg_node;
+ for (msg_node = event->received_msg_list->head; msg_node != NULL;
+ msg_node = msg_node->next_node) {
+ bool message_enqueued = false;
+ struct pcep_message *msg =
+ (struct pcep_message *)msg_node->data;
+ pcep_log(LOG_INFO, "%s: \t %s message", __func__,
+ get_message_type_str(msg->msg_header->type));
+
+ increment_message_rx_counters(session, msg);
+
+ switch (msg->msg_header->type) {
+ case PCEP_TYPE_OPEN:
+ /* handle_pcep_open() checks session state, and for
+ * duplicate erroneous open messages, and replies with
+ * error messages as needed. It also sets
+ * pce_open_received. */
+ if (handle_pcep_open(session, msg) == true) {
+ /* PCE Open Message Accepted */
+ enqueue_event(session, MESSAGE_RECEIVED, msg);
+ message_enqueued = true;
+ session->pce_open_accepted = true;
+ session->pce_open_rejected = false;
+ if (session->pcc_open_accepted) {
+ /* If both the PCC and PCE Opens are
+ * accepted, then the session is
+ * connected */
+
+ check_and_send_open_keep_alive(session);
+ log_pcc_pce_connection(session);
+ session->session_state =
+ SESSION_STATE_PCEP_CONNECTED;
+ increment_event_counters(
+ session,
+ PCEP_EVENT_COUNTER_ID_PCE_CONNECT);
+ enqueue_event(session,
+ PCC_CONNECTED_TO_PCE,
+ NULL);
+ }
+ }
+ break;
+
+ case PCEP_TYPE_KEEPALIVE:
+ if (session->session_state
+ == SESSION_STATE_PCEP_CONNECTING) {
+ /* PCC Open Message Accepted */
+ cancel_timer(session->timer_id_open_keep_wait);
+ session->timer_id_open_keep_wait =
+ TIMER_ID_NOT_SET;
+ session->pcc_open_accepted = true;
+ session->pcc_open_rejected = false;
+ check_and_send_open_keep_alive(session);
+
+ if (session->pce_open_accepted) {
+ /* If both the PCC and PCE Opens are
+ * accepted, then the session is
+ * connected */
+ log_pcc_pce_connection(session);
+ session->session_state =
+ SESSION_STATE_PCEP_CONNECTED;
+ increment_event_counters(
+ session,
+ PCEP_EVENT_COUNTER_ID_PCC_CONNECT);
+ enqueue_event(session,
+ PCC_CONNECTED_TO_PCE,
+ NULL);
+ }
+ }
+ /* The dead_timer was already reset above, so nothing
+ * extra to do here */
+ break;
+
+ case PCEP_TYPE_PCREP:
+ enqueue_event(session, MESSAGE_RECEIVED, msg);
+ message_enqueued = true;
+ break;
+
+ case PCEP_TYPE_CLOSE:
+ session->session_state = SESSION_STATE_INITIALIZED;
+ socket_comm_session_close_tcp(
+ session->socket_comm_session);
+ /* TODO should we also enqueue the message, so they can
+ * see the reasons?? */
+ enqueue_event(session, PCE_SENT_PCEP_CLOSE, NULL);
+ /* TODO could this duplicate the disconnect counter with
+ * socket close ?? */
+ increment_event_counters(
+ session, PCEP_EVENT_COUNTER_ID_PCE_DISCONNECT);
+ break;
+
+ case PCEP_TYPE_PCREQ:
+ /* The PCC does not support receiving PcReq messages */
+ send_pcep_error(session,
+ PCEP_ERRT_CAPABILITY_NOT_SUPPORTED,
+ PCEP_ERRV_UNASSIGNED);
+ break;
+
+ case PCEP_TYPE_REPORT:
+ /* The PCC does not support receiving Report messages */
+ send_pcep_error(session,
+ PCEP_ERRT_CAPABILITY_NOT_SUPPORTED,
+ PCEP_ERRV_UNASSIGNED);
+ break;
+
+ case PCEP_TYPE_UPDATE:
+ /* Should reply with a PcRpt */
+ if (handle_pcep_update(session, msg) == true) {
+ enqueue_event(session, MESSAGE_RECEIVED, msg);
+ message_enqueued = true;
+ }
+ break;
+
+ case PCEP_TYPE_INITIATE:
+ /* Should reply with a PcRpt */
+ if (handle_pcep_initiate(session, msg) == true) {
+ enqueue_event(session, MESSAGE_RECEIVED, msg);
+ message_enqueued = true;
+ }
+ break;
+
+ case PCEP_TYPE_PCNOTF:
+ enqueue_event(session, MESSAGE_RECEIVED, msg);
+ message_enqueued = true;
+ break;
+
+ case PCEP_TYPE_ERROR:
+ if (msg->obj_list != NULL
+ && msg->obj_list->num_entries > 0) {
+ struct pcep_object_header *obj_hdr =
+ pcep_obj_get(msg->obj_list,
+ PCEP_OBJ_CLASS_ERROR);
+ if (obj_hdr != NULL) {
+ struct pcep_object_error *error_obj =
+ (struct pcep_object_error *)
+ obj_hdr;
+ pcep_log(
+ LOG_DEBUG,
+ "%s: Error object [type, value] = [%s, %s]",
+ __func__,
+ get_error_type_str(
+ error_obj->error_type),
+ get_error_value_str(
+ error_obj->error_type,
+ error_obj
+ ->error_value));
+ }
+ }
+
+ if (session->session_state
+ == SESSION_STATE_PCEP_CONNECTING) {
+ /* A PCC_CONNECTION_FAILURE event will be sent
+ * when the socket is closed, if the state is
+ * SESSION_STATE_PCEP_CONNECTING, in case the
+ * PCE allows more than 2 failed open messages.
+ */
+ pcep_log(LOG_INFO,
+ "%s: PCC Open message rejected by PCE",
+ __func__);
+ session->pcc_open_rejected = true;
+ send_reconciled_pcep_open(session, msg);
+ enqueue_event(session, PCC_SENT_INVALID_OPEN,
+ NULL);
+ }
+ enqueue_event(session, MESSAGE_RECEIVED, msg);
+ message_enqueued = true;
+ break;
+
+ default:
+ pcep_log(LOG_INFO, "%s: \t UnSupported message",
+ __func__);
+ send_pcep_error(session,
+ PCEP_ERRT_CAPABILITY_NOT_SUPPORTED,
+ PCEP_ERRV_UNASSIGNED);
+ increment_unknown_message(session);
+ break;
+ }
+
+ /* if the message was enqueued, dont free it yet */
+ if (message_enqueued == false) {
+ pcep_msg_free_message(msg);
+ }
+ }
+ dll_destroy(event->received_msg_list);
+}
diff --git a/pceplib/pcep_socket_comm.c b/pceplib/pcep_socket_comm.c
new file mode 100644
index 000000000..e22eb6e67
--- /dev/null
+++ b/pceplib/pcep_socket_comm.c
@@ -0,0 +1,781 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Implementation of public API functions.
+ */
+
+#include <zebra.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h> // gethostbyname
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h> // close
+
+#include <arpa/inet.h> // sockets etc.
+#include <sys/types.h> // sockets etc.
+#include <sys/socket.h> // sockets etc.
+
+#include "pcep.h"
+#include "pcep_socket_comm.h"
+#include "pcep_socket_comm_internals.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_queue.h"
+
+bool initialize_socket_comm_pre(void);
+bool socket_comm_session_initialize_post(
+ pcep_socket_comm_session *socket_comm_session);
+
+pcep_socket_comm_handle *socket_comm_handle_ = NULL;
+
+
+/* simple compare method callback used by pcep_utils_ordered_list
+ * for ordered list insertion. */
+int socket_fd_node_compare(void *list_entry, void *new_entry)
+{
+ return ((pcep_socket_comm_session *)new_entry)->socket_fd
+ - ((pcep_socket_comm_session *)list_entry)->socket_fd;
+}
+
+
+bool initialize_socket_comm_pre()
+{
+ socket_comm_handle_ =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_handle));
+ memset(socket_comm_handle_, 0, sizeof(pcep_socket_comm_handle));
+
+ socket_comm_handle_->active = true;
+ socket_comm_handle_->num_active_sessions = 0;
+ socket_comm_handle_->read_list =
+ ordered_list_initialize(socket_fd_node_compare);
+ socket_comm_handle_->write_list =
+ ordered_list_initialize(socket_fd_node_compare);
+ socket_comm_handle_->session_list =
+ ordered_list_initialize(pointer_compare_function);
+ FD_ZERO(&socket_comm_handle_->except_master_set);
+ FD_ZERO(&socket_comm_handle_->read_master_set);
+ FD_ZERO(&socket_comm_handle_->write_master_set);
+
+ if (pthread_mutex_init(&(socket_comm_handle_->socket_comm_mutex), NULL)
+ != 0) {
+ pcep_log(LOG_ERR, "%s: Cannot initialize socket_comm mutex.",
+ __func__);
+ pceplib_free(PCEPLIB_INFRA, socket_comm_handle_);
+ socket_comm_handle_ = NULL;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool initialize_socket_comm_external_infra(
+ void *external_infra_data, ext_socket_read socket_read_cb,
+ ext_socket_write socket_write_cb,
+ ext_socket_pthread_create_callback thread_create_func)
+{
+ if (socket_comm_handle_ != NULL) {
+ /* already initialized */
+ return true;
+ }
+
+ if (initialize_socket_comm_pre() == false) {
+ return false;
+ }
+
+ /* Notice: If the thread_create_func is set, then both the
+ * socket_read_cb and the socket_write_cb SHOULD be NULL. */
+ if (thread_create_func != NULL) {
+ if (thread_create_func(
+ &(socket_comm_handle_->socket_comm_thread), NULL,
+ socket_comm_loop, socket_comm_handle_,
+ "pceplib_timers")) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize external socket_comm thread.",
+ __func__);
+ return false;
+ }
+ }
+
+ socket_comm_handle_->external_infra_data = external_infra_data;
+ socket_comm_handle_->socket_write_func = socket_write_cb;
+ socket_comm_handle_->socket_read_func = socket_read_cb;
+
+ return true;
+}
+
+bool initialize_socket_comm_loop()
+{
+ if (socket_comm_handle_ != NULL) {
+ /* already initialized */
+ return true;
+ }
+
+ if (initialize_socket_comm_pre() == false) {
+ return false;
+ }
+
+ /* Launch socket comm loop pthread */
+ if (pthread_create(&(socket_comm_handle_->socket_comm_thread), NULL,
+ socket_comm_loop, socket_comm_handle_)) {
+ pcep_log(LOG_ERR, "%s: Cannot initialize socket_comm thread.",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+
+bool destroy_socket_comm_loop()
+{
+ socket_comm_handle_->active = false;
+
+ pthread_join(socket_comm_handle_->socket_comm_thread, NULL);
+ ordered_list_destroy(socket_comm_handle_->read_list);
+ ordered_list_destroy(socket_comm_handle_->write_list);
+ ordered_list_destroy(socket_comm_handle_->session_list);
+ pthread_mutex_destroy(&(socket_comm_handle_->socket_comm_mutex));
+
+ pceplib_free(PCEPLIB_INFRA, socket_comm_handle_);
+ socket_comm_handle_ = NULL;
+
+ return true;
+}
+
+/* Internal common init function */
+static pcep_socket_comm_session *socket_comm_session_initialize_pre(
+ message_received_handler message_handler,
+ message_ready_to_read_handler message_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str, bool is_tcp_auth_md5,
+ void *session_data)
+{
+ /* check that not both message handlers were set */
+ if (message_handler != NULL && message_ready_handler != NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Only one of <message_received_handler | message_ready_to_read_handler> can be set.",
+ __func__);
+ return NULL;
+ }
+
+ /* check that at least one message handler was set */
+ if (message_handler == NULL && message_ready_handler == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: At least one of <message_received_handler | message_ready_to_read_handler> must be set.",
+ __func__);
+ return NULL;
+ }
+
+ if (!initialize_socket_comm_loop()) {
+ pcep_log(LOG_WARNING,
+ "%s: ERROR: cannot initialize socket_comm_loop.",
+ __func__);
+
+ return NULL;
+ }
+
+ /* initialize everything for a pcep_session socket_comm */
+
+ pcep_socket_comm_session *socket_comm_session =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_session));
+ memset(socket_comm_session, 0, sizeof(pcep_socket_comm_session));
+
+ socket_comm_handle_->num_active_sessions++;
+ socket_comm_session->close_after_write = false;
+ socket_comm_session->session_data = session_data;
+ socket_comm_session->message_handler = message_handler;
+ socket_comm_session->message_ready_to_read_handler =
+ message_ready_handler;
+ socket_comm_session->message_sent_handler = msg_sent_notifier;
+ socket_comm_session->conn_except_notifier = notifier;
+ socket_comm_session->message_queue = queue_initialize();
+ socket_comm_session->connect_timeout_millis = connect_timeout_millis;
+ socket_comm_session->external_socket_data = NULL;
+ if (tcp_authentication_str != NULL) {
+ socket_comm_session->is_tcp_auth_md5 = is_tcp_auth_md5;
+ strlcpy(socket_comm_session->tcp_authentication_str,
+ tcp_authentication_str,
+ sizeof(socket_comm_session->tcp_authentication_str));
+ }
+
+ return socket_comm_session;
+}
+
+/* Internal common init function */
+bool socket_comm_session_initialize_post(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ /* If we dont use SO_REUSEADDR, the socket will take 2 TIME_WAIT
+ * periods before being closed in the kernel if bind() was called */
+ int reuse_addr = 1;
+ if (setsockopt(socket_comm_session->socket_fd, SOL_SOCKET, SO_REUSEADDR,
+ &reuse_addr, sizeof(int))
+ < 0) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Error in setsockopt() SO_REUSEADDR errno [%d %s].",
+ __func__, errno, strerror(errno));
+ socket_comm_session_teardown(socket_comm_session);
+
+ return false;
+ }
+
+ struct sockaddr *src_sock_addr =
+ (socket_comm_session->is_ipv6
+ ? (struct sockaddr *)&(
+ socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv6)
+ : (struct sockaddr *)&(
+ socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv4));
+ int addr_len = (socket_comm_session->is_ipv6
+ ? sizeof(socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv6)
+ : sizeof(socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv4));
+ if (bind(socket_comm_session->socket_fd, src_sock_addr, addr_len)
+ == -1) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot bind address to socket errno [%d %s].",
+ __func__, errno, strerror(errno));
+ socket_comm_session_teardown(socket_comm_session);
+
+ return false;
+ }
+
+ /* Register the session as active with the Socket Comm Loop */
+ pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
+ ordered_list_add_node(socket_comm_handle_->session_list,
+ socket_comm_session);
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+
+ /* dont connect to the destination yet, since the PCE will have a timer
+ * for max time between TCP connect and PCEP open. we'll connect later
+ * when we send the PCEP open. */
+
+ return true;
+}
+
+
+pcep_socket_comm_session *socket_comm_session_initialize(
+ message_received_handler message_handler,
+ message_ready_to_read_handler message_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in_addr *dest_ip,
+ short dest_port, uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str, bool is_tcp_auth_md5,
+ void *session_data)
+{
+ return socket_comm_session_initialize_with_src(
+ message_handler, message_ready_handler, msg_sent_notifier,
+ notifier, NULL, 0, dest_ip, dest_port, connect_timeout_millis,
+ tcp_authentication_str, is_tcp_auth_md5, session_data);
+}
+
+pcep_socket_comm_session *socket_comm_session_initialize_ipv6(
+ message_received_handler message_handler,
+ message_ready_to_read_handler message_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in6_addr *dest_ip,
+ short dest_port, uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str, bool is_tcp_auth_md5,
+ void *session_data)
+{
+ return socket_comm_session_initialize_with_src_ipv6(
+ message_handler, message_ready_handler, msg_sent_notifier,
+ notifier, NULL, 0, dest_ip, dest_port, connect_timeout_millis,
+ tcp_authentication_str, is_tcp_auth_md5, session_data);
+}
+
+
+pcep_socket_comm_session *socket_comm_session_initialize_with_src(
+ message_received_handler message_handler,
+ message_ready_to_read_handler message_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in_addr *src_ip,
+ short src_port, struct in_addr *dest_ip, short dest_port,
+ uint32_t connect_timeout_millis, const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data)
+{
+ if (dest_ip == NULL) {
+ pcep_log(LOG_WARNING, "%s: dest_ipv4 is NULL", __func__);
+ return NULL;
+ }
+
+ pcep_socket_comm_session *socket_comm_session =
+ socket_comm_session_initialize_pre(
+ message_handler, message_ready_handler,
+ msg_sent_notifier, notifier, connect_timeout_millis,
+ tcp_authentication_str, is_tcp_auth_md5, session_data);
+ if (socket_comm_session == NULL) {
+ return NULL;
+ }
+
+ socket_comm_session->socket_fd =
+ socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (socket_comm_session->socket_fd == -1) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot create ipv4 socket errno [%d %s].",
+ __func__, errno, strerror(errno));
+ socket_comm_session_teardown(
+ socket_comm_session); // socket_comm_session freed
+ // inside fn so NOLINT next.
+
+ return NULL; // NOLINT(clang-analyzer-unix.Malloc)
+ }
+
+ socket_comm_session->is_ipv6 = false;
+ socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_family =
+ AF_INET;
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_family =
+ AF_INET;
+ socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_port =
+ htons(dest_port);
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_port =
+ htons(src_port);
+ socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_addr
+ .s_addr = dest_ip->s_addr;
+ if (src_ip != NULL) {
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_addr
+ .s_addr = src_ip->s_addr;
+ } else {
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_addr
+ .s_addr = INADDR_ANY;
+ }
+
+ if (socket_comm_session_initialize_post(socket_comm_session) == false) {
+ return NULL;
+ }
+
+ return socket_comm_session;
+}
+
+pcep_socket_comm_session *socket_comm_session_initialize_with_src_ipv6(
+ message_received_handler message_handler,
+ message_ready_to_read_handler message_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in6_addr *src_ip,
+ short src_port, struct in6_addr *dest_ip, short dest_port,
+ uint32_t connect_timeout_millis, const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data)
+{
+ if (dest_ip == NULL) {
+ pcep_log(LOG_WARNING, "%s: dest_ipv6 is NULL", __func__);
+ return NULL;
+ }
+
+ pcep_socket_comm_session *socket_comm_session =
+ socket_comm_session_initialize_pre(
+ message_handler, message_ready_handler,
+ msg_sent_notifier, notifier, connect_timeout_millis,
+ tcp_authentication_str, is_tcp_auth_md5, session_data);
+ if (socket_comm_session == NULL) {
+ return NULL;
+ }
+
+ socket_comm_session->socket_fd =
+ socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
+ if (socket_comm_session->socket_fd == -1) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot create ipv6 socket errno [%d %s].",
+ __func__, errno, strerror(errno));
+ socket_comm_session_teardown(
+ socket_comm_session); // socket_comm_session freed
+ // inside fn so NOLINT next.
+
+ return NULL; // NOLINT(clang-analyzer-unix.Malloc)
+ }
+
+ socket_comm_session->is_ipv6 = true;
+ socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_family =
+ AF_INET6;
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_family =
+ AF_INET6;
+ socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_port =
+ htons(dest_port);
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_port =
+ htons(src_port);
+ memcpy(&socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6
+ .sin6_addr,
+ dest_ip, sizeof(struct in6_addr));
+ if (src_ip != NULL) {
+ memcpy(&socket_comm_session->src_sock_addr.src_sock_addr_ipv6
+ .sin6_addr,
+ src_ip, sizeof(struct in6_addr));
+ } else {
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv6
+ .sin6_addr = in6addr_any;
+ }
+
+ if (socket_comm_session_initialize_post(socket_comm_session) == false) {
+ return NULL;
+ }
+
+ return socket_comm_session;
+}
+
+
+bool socket_comm_session_connect_tcp(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ if (socket_comm_session == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: socket_comm_session_connect_tcp NULL socket_comm_session.",
+ __func__);
+ return NULL;
+ }
+
+ /* Set the socket to non-blocking, so connect() does not block */
+ int fcntl_arg;
+ if ((fcntl_arg = fcntl(socket_comm_session->socket_fd, F_GETFL, NULL))
+ < 0) {
+ pcep_log(LOG_WARNING, "%s: Error fcntl(..., F_GETFL) [%d %s]",
+ __func__, errno, strerror(errno));
+ return false;
+ }
+
+ fcntl_arg |= O_NONBLOCK;
+ if (fcntl(socket_comm_session->socket_fd, F_SETFL, fcntl_arg) < 0) {
+ pcep_log(LOG_WARNING, "%s: Error fcntl(..., F_SETFL) [%d %s]",
+ __func__, errno, strerror(errno));
+ return false;
+ }
+
+#if HAVE_DECL_TCP_MD5SIG
+ /* TCP authentication, currently only TCP MD5 RFC2385 is supported */
+ if (socket_comm_session->tcp_authentication_str[0] != '\0') {
+#if defined(linux) || defined(GNU_LINUX)
+ struct tcp_md5sig sig;
+ memset(&sig, 0, sizeof(sig));
+ if (socket_comm_session->is_ipv6) {
+ memcpy(&sig.tcpm_addr,
+ &socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6,
+ sizeof(struct sockaddr_in6));
+ } else {
+ memcpy(&sig.tcpm_addr,
+ &socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4,
+ sizeof(struct sockaddr_in));
+ }
+ sig.tcpm_keylen =
+ strlen(socket_comm_session->tcp_authentication_str);
+ memcpy(sig.tcpm_key,
+ socket_comm_session->tcp_authentication_str,
+ sig.tcpm_keylen);
+#else
+ int sig = 1;
+#endif
+ if (setsockopt(socket_comm_session->socket_fd, IPPROTO_TCP,
+ TCP_MD5SIG, &sig, sizeof(sig))
+ == -1) {
+ pcep_log(LOG_ERR, "%s: Failed to setsockopt(): [%d %s]",
+ __func__, errno, strerror(errno));
+ return false;
+ }
+ }
+#endif
+
+ int connect_result = 0;
+ if (socket_comm_session->is_ipv6) {
+ connect_result = connect(
+ socket_comm_session->socket_fd,
+ (struct sockaddr *)&(socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6),
+ sizeof(socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6));
+ } else {
+ connect_result = connect(
+ socket_comm_session->socket_fd,
+ (struct sockaddr *)&(socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4),
+ sizeof(socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4));
+ }
+
+ if (connect_result < 0) {
+ if (errno == EINPROGRESS) {
+ /* Calculate the configured timeout in seconds and
+ * microseconds */
+ struct timeval tv;
+ if (socket_comm_session->connect_timeout_millis
+ > 1000) {
+ tv.tv_sec = socket_comm_session
+ ->connect_timeout_millis
+ / 1000;
+ tv.tv_usec = (socket_comm_session
+ ->connect_timeout_millis
+ - (tv.tv_sec * 1000))
+ * 1000;
+ } else {
+ tv.tv_sec = 0;
+ tv.tv_usec = socket_comm_session
+ ->connect_timeout_millis
+ * 1000;
+ }
+
+ /* Use select to wait a max timeout for connect
+ * https://stackoverflow.com/questions/2597608/c-socket-connection-timeout
+ */
+ fd_set fdset;
+ FD_ZERO(&fdset);
+ FD_SET(socket_comm_session->socket_fd, &fdset);
+ if (select(socket_comm_session->socket_fd + 1, NULL,
+ &fdset, NULL, &tv)
+ > 0) {
+ int so_error;
+ socklen_t len = sizeof(so_error);
+ getsockopt(socket_comm_session->socket_fd,
+ SOL_SOCKET, SO_ERROR, &so_error,
+ &len);
+ if (so_error) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: TCP connect failed on socket_fd [%d].",
+ __func__,
+ socket_comm_session->socket_fd);
+ return false;
+ }
+ } else {
+ pcep_log(
+ LOG_WARNING,
+ "%s: TCP connect timed-out on socket_fd [%d].",
+ __func__,
+ socket_comm_session->socket_fd);
+ return false;
+ }
+ } else {
+ pcep_log(
+ LOG_WARNING,
+ "%s: TCP connect, error connecting on socket_fd [%d] errno [%d %s]",
+ __func__, socket_comm_session->socket_fd, errno,
+ strerror(errno));
+ return false;
+ }
+ }
+
+ pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
+ /* once the TCP connection is open, we should be ready to read at any
+ * time */
+ ordered_list_add_node(socket_comm_handle_->read_list,
+ socket_comm_session);
+
+ if (socket_comm_handle_->socket_read_func != NULL) {
+ socket_comm_handle_->socket_read_func(
+ socket_comm_handle_->external_infra_data,
+ &socket_comm_session->external_socket_data,
+ socket_comm_session->socket_fd, socket_comm_handle_);
+ }
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+
+ return true;
+}
+
+
+bool socket_comm_session_close_tcp(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ if (socket_comm_session == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: socket_comm_session_close_tcp NULL socket_comm_session.",
+ __func__);
+ return false;
+ }
+
+ pcep_log(LOG_DEBUG,
+ "%s: socket_comm_session_close_tcp close() socket fd [%d]",
+ __func__, socket_comm_session->socket_fd);
+
+ pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
+ ordered_list_remove_first_node_equals(socket_comm_handle_->read_list,
+ socket_comm_session);
+ ordered_list_remove_first_node_equals(socket_comm_handle_->write_list,
+ socket_comm_session);
+ // TODO should it be close() or shutdown()??
+ close(socket_comm_session->socket_fd);
+ socket_comm_session->socket_fd = -1;
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+
+ return true;
+}
+
+
+bool socket_comm_session_close_tcp_after_write(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ if (socket_comm_session == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: socket_comm_session_close_tcp_after_write NULL socket_comm_session.",
+ __func__);
+ return false;
+ }
+
+ pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
+ socket_comm_session->close_after_write = true;
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+
+ return true;
+}
+
+
+bool socket_comm_session_teardown(pcep_socket_comm_session *socket_comm_session)
+{
+ if (socket_comm_handle_ == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot teardown NULL socket_comm_handle",
+ __func__);
+ return false;
+ }
+
+ if (socket_comm_session == NULL) {
+ pcep_log(LOG_WARNING, "%s: Cannot teardown NULL session",
+ __func__);
+ return false;
+ }
+
+ if (comm_session_exists_locking(socket_comm_handle_,
+ socket_comm_session)
+ == false) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot teardown session that does not exist",
+ __func__);
+ return false;
+ }
+
+ if (socket_comm_session->socket_fd >= 0) {
+ shutdown(socket_comm_session->socket_fd, SHUT_RDWR);
+ close(socket_comm_session->socket_fd);
+ }
+
+ pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
+ queue_destroy(socket_comm_session->message_queue);
+ ordered_list_remove_first_node_equals(socket_comm_handle_->session_list,
+ socket_comm_session);
+ ordered_list_remove_first_node_equals(socket_comm_handle_->read_list,
+ socket_comm_session);
+ ordered_list_remove_first_node_equals(socket_comm_handle_->write_list,
+ socket_comm_session);
+ socket_comm_handle_->num_active_sessions--;
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] socket_comm_session fd [%d] destroyed, [%d] sessions remaining",
+ __func__, time(NULL), pthread_self(),
+ socket_comm_session->socket_fd,
+ socket_comm_handle_->num_active_sessions);
+
+ pceplib_free(PCEPLIB_INFRA, socket_comm_session);
+
+ /* It would be nice to call destroy_socket_comm_loop() here if
+ * socket_comm_handle_->num_active_sessions == 0, but this function
+ * will usually be called from the message_sent_notifier callback,
+ * which gets called in the middle of the socket_comm_loop, and that
+ * is dangerous, so destroy_socket_comm_loop() must be called upon
+ * application exit. */
+
+ return true;
+}
+
+
+void socket_comm_session_send_message(
+ pcep_socket_comm_session *socket_comm_session,
+ const char *encoded_message, unsigned int msg_length,
+ bool free_after_send)
+{
+ if (socket_comm_session == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: socket_comm_session_send_message NULL socket_comm_session.",
+ __func__);
+ return;
+ }
+
+ pcep_socket_comm_queued_message *queued_message = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(pcep_socket_comm_queued_message));
+ queued_message->encoded_message = encoded_message;
+ queued_message->msg_length = msg_length;
+ queued_message->free_after_send = free_after_send;
+
+ pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
+
+ /* Do not proceed if the socket_comm_session has been deleted */
+ if (ordered_list_find(socket_comm_handle_->session_list,
+ socket_comm_session)
+ == NULL) {
+ /* Should never get here, only if the session was deleted and
+ * someone still tries to write on it */
+ pcep_log(
+ LOG_WARNING,
+ "%s: Cannot write a message on a deleted socket comm session, discarding message",
+ __func__);
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+ pceplib_free(PCEPLIB_MESSAGES, queued_message);
+
+ return;
+ }
+
+ /* Do not proceed if the socket has been closed */
+ if (socket_comm_session->socket_fd < 0) {
+ /* Should never get here, only if the session was deleted and
+ * someone still tries to write on it */
+ pcep_log(
+ LOG_WARNING,
+ "%s: Cannot write a message on a closed socket, discarding message",
+ __func__);
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+ pceplib_free(PCEPLIB_MESSAGES, queued_message);
+
+ return;
+ }
+
+ queue_enqueue(socket_comm_session->message_queue, queued_message);
+
+ /* Add it to the write list only if its not already there */
+ if (ordered_list_find(socket_comm_handle_->write_list,
+ socket_comm_session)
+ == NULL) {
+ ordered_list_add_node(socket_comm_handle_->write_list,
+ socket_comm_session);
+ }
+
+ if (socket_comm_handle_->socket_write_func != NULL) {
+ socket_comm_handle_->socket_write_func(
+ socket_comm_handle_->external_infra_data,
+ &socket_comm_session->external_socket_data,
+ socket_comm_session->socket_fd, socket_comm_handle_);
+ }
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+}
diff --git a/pceplib/pcep_socket_comm.h b/pceplib/pcep_socket_comm.h
new file mode 100644
index 000000000..797ffda86
--- /dev/null
+++ b/pceplib/pcep_socket_comm.h
@@ -0,0 +1,198 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Declaration of public API functions.
+ */
+
+#ifndef INCLUDE_PCEPSOCKETCOMM_H_
+#define INCLUDE_PCEPSOCKETCOMM_H_
+
+#include "pcep.h"
+#include <arpa/inet.h> // sockaddr_in
+#include <netinet/tcp.h>
+#include <stdbool.h>
+
+#include "pcep_utils_queue.h"
+
+#define MAX_RECVD_MSG_SIZE 2048
+
+/*
+ * A socket_comm_session can be initialized with 1 of 2 types of mutually
+ * exclusive message callbacks:
+ * - message_received_handler : the socket_comm library reads the message and
+ * calls the callback with the message_data and message_length. this callback
+ * should be used for smaller/simpler messages.
+ * - message_ready_to_read_handler : the socket_comm library will call this
+ * callback when a message is ready to be read on a socket_fd. this callback
+ * should be used if the
+ */
+
+/* message received handler that receives the message data and message length */
+typedef void (*message_received_handler)(void *session_data,
+ const char *message_data,
+ unsigned int message_length);
+/* message ready received handler that should read the message on socket_fd
+ * and return the number of bytes read */
+typedef int (*message_ready_to_read_handler)(void *session_data, int socket_fd);
+/* callback handler called when a messages is sent */
+typedef void (*message_sent_notifier)(void *session_data, int socket_fd);
+/* callback handler called when the socket is closed */
+typedef void (*connection_except_notifier)(void *session_data, int socket_fd);
+
+/* Function pointers when an external socket infrastructure is used */
+typedef int (*ext_socket_write)(void *infra_data, void **infra_socket_data,
+ int fd, void *data);
+typedef int (*ext_socket_read)(void *infra_data, void **infra_socket_data,
+ int fd, void *data);
+typedef int (*ext_socket_pthread_create_callback)(
+ pthread_t *pthread_id, const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *data, const char *thread_name);
+
+typedef struct pcep_socket_comm_session_ {
+ message_received_handler message_handler;
+ message_ready_to_read_handler message_ready_to_read_handler;
+ message_sent_notifier message_sent_handler;
+ connection_except_notifier conn_except_notifier;
+ union src_sock_addr {
+ struct sockaddr_in src_sock_addr_ipv4;
+ struct sockaddr_in6 src_sock_addr_ipv6;
+ } src_sock_addr;
+ union dest_sock_addr {
+ struct sockaddr_in dest_sock_addr_ipv4;
+ struct sockaddr_in6 dest_sock_addr_ipv6;
+ } dest_sock_addr;
+ bool is_ipv6;
+ uint32_t connect_timeout_millis;
+ int socket_fd;
+ void *session_data;
+ queue_handle *message_queue;
+ char received_message[MAX_RECVD_MSG_SIZE];
+ int received_bytes;
+ bool close_after_write;
+ void *external_socket_data; /* used for external socket infra */
+ char tcp_authentication_str[TCP_MD5SIG_MAXKEYLEN
+ + 1]; /* should be used with is_tcp_auth_md5
+ flag */
+ bool is_tcp_auth_md5; /* flag to distinguish between rfc 2385 (md5) and
+ rfc 5925 (tcp-ao) */
+
+} pcep_socket_comm_session;
+
+
+/* Need to document that when the msg_rcv_handler is called, the data needs
+ * to be handled in the same function call, else it may be overwritten by
+ * the next read from this socket */
+
+
+/* Initialize the Socket Comm infrastructure, with either an internal pthread
+ * or with an external infrastructure.
+ * If an internal pthread infrastructure is to be used, then it is not necessary
+ * to explicitly call initialize_socket_comm_loop() as it will be called
+ * internally when a socket comm session is initialized. */
+
+/* Initialize the Socket Comm infrastructure with an internal pthread */
+bool initialize_socket_comm_loop(void);
+/* Initialize the Socket Comm infrastructure with an external infrastructure.
+ * Notice: If the thread_create_func is set, then both the socket_read_cb
+ * and the socket_write_cb SHOULD be NULL. */
+bool initialize_socket_comm_external_infra(
+ void *external_infra_data, ext_socket_read socket_read_cb,
+ ext_socket_write socket_write_cb,
+ ext_socket_pthread_create_callback thread_create_func);
+
+/* The msg_rcv_handler and msg_ready_handler are mutually exclusive, and only
+ * one can be set (as explained above), else NULL will be returned. */
+pcep_socket_comm_session *
+socket_comm_session_initialize(message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier,
+ struct in_addr *dst_ip, short dst_port,
+ uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data);
+
+pcep_socket_comm_session *socket_comm_session_initialize_ipv6(
+ message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in6_addr *dst_ip,
+ short dst_port, uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str, bool is_tcp_auth_md5,
+ void *session_data);
+
+pcep_socket_comm_session *socket_comm_session_initialize_with_src(
+ message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in_addr *src_ip,
+ short src_port, struct in_addr *dst_ip, short dst_port,
+ uint32_t connect_timeout_millis, const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data);
+
+pcep_socket_comm_session *socket_comm_session_initialize_with_src_ipv6(
+ message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in6_addr *src_ip,
+ short src_port, struct in6_addr *dst_ip, short dst_port,
+ uint32_t connect_timeout_millis, const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data);
+
+bool socket_comm_session_teardown(
+ pcep_socket_comm_session *socket_comm_session);
+
+bool socket_comm_session_connect_tcp(
+ pcep_socket_comm_session *socket_comm_session);
+
+/* Immediately close the TCP connection, irregardless if there are pending
+ * messages to be sent. */
+bool socket_comm_session_close_tcp(
+ pcep_socket_comm_session *socket_comm_session);
+
+/* Sets a flag to close the TCP connection either after all the pending messages
+ * are written, or if there are no pending messages, the next time the socket is
+ * checked to be writeable. */
+bool socket_comm_session_close_tcp_after_write(
+ pcep_socket_comm_session *socket_comm_session);
+
+void socket_comm_session_send_message(
+ pcep_socket_comm_session *socket_comm_session,
+ const char *encoded_message, unsigned int msg_length,
+ bool free_after_send);
+
+/* If an external Socket infra like FRR is used, then these functions will
+ * be called when a socket is ready to read/write in the external infra.
+ * Implemented in pcep_socket_comm_loop.c */
+int pceplib_external_socket_read(int fd, void *payload);
+int pceplib_external_socket_write(int fd, void *payload);
+
+/* the socket comm loop is started internally by
+ * socket_comm_session_initialize()
+ * but needs to be explicitly stopped with this call. */
+bool destroy_socket_comm_loop(void);
+
+int socket_fd_node_compare(void *list_entry, void *new_entry);
+
+#endif /* INCLUDE_PCEPSOCKETCOMM_H_ */
diff --git a/pceplib/pcep_socket_comm_internals.h b/pceplib/pcep_socket_comm_internals.h
new file mode 100644
index 000000000..4445a14fa
--- /dev/null
+++ b/pceplib/pcep_socket_comm_internals.h
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#ifndef SRC_PCEPSOCKETCOMMINTERNALS_H_
+#define SRC_PCEPSOCKETCOMMINTERNALS_H_
+
+#include <pthread.h>
+#include <stdbool.h>
+
+#include "pcep_utils_ordered_list.h"
+#include "pcep_socket_comm.h"
+
+
+typedef struct pcep_socket_comm_handle_ {
+ bool active;
+ pthread_t socket_comm_thread;
+ pthread_mutex_t socket_comm_mutex;
+ fd_set read_master_set;
+ fd_set write_master_set;
+ fd_set except_master_set;
+ /* ordered_list of socket_descriptors to read from */
+ ordered_list_handle *read_list;
+ /* ordered_list of socket_descriptors to write to */
+ ordered_list_handle *write_list;
+ ordered_list_handle *session_list;
+ int num_active_sessions;
+ void *external_infra_data;
+ ext_socket_write socket_write_func;
+ ext_socket_read socket_read_func;
+
+} pcep_socket_comm_handle;
+
+
+typedef struct pcep_socket_comm_queued_message_ {
+ const char *encoded_message;
+ int msg_length;
+ bool free_after_send;
+
+} pcep_socket_comm_queued_message;
+
+
+/* Functions implemented in pcep_socket_comm_loop.c */
+void *socket_comm_loop(void *data);
+bool comm_session_exists(pcep_socket_comm_handle *socket_comm_handle,
+ pcep_socket_comm_session *socket_comm_session);
+bool comm_session_exists_locking(pcep_socket_comm_handle *socket_comm_handle,
+ pcep_socket_comm_session *socket_comm_session);
+
+#endif /* SRC_PCEPSOCKETCOMMINTERNALS_H_ */
diff --git a/pceplib/pcep_socket_comm_loop.c b/pceplib/pcep_socket_comm_loop.c
new file mode 100644
index 000000000..8346c9302
--- /dev/null
+++ b/pceplib/pcep_socket_comm_loop.c
@@ -0,0 +1,486 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pcep_socket_comm_internals.h"
+#include "pcep_socket_comm_loop.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+void write_message(int socket_fd, const char *message, unsigned int msg_length);
+unsigned int read_message(int socket_fd, char *received_message,
+ unsigned int max_message_size);
+int build_fd_sets(pcep_socket_comm_handle *socket_comm_handle);
+void handle_writes(pcep_socket_comm_handle *socket_comm_handle);
+void handle_excepts(pcep_socket_comm_handle *socket_comm_handle);
+
+bool comm_session_exists(pcep_socket_comm_handle *socket_comm_handle,
+ pcep_socket_comm_session *socket_comm_session)
+{
+ if (socket_comm_handle == NULL) {
+ return false;
+ }
+
+ return (ordered_list_find(socket_comm_handle->session_list,
+ socket_comm_session)
+ != NULL);
+}
+
+
+bool comm_session_exists_locking(pcep_socket_comm_handle *socket_comm_handle,
+ pcep_socket_comm_session *socket_comm_session)
+{
+ if (socket_comm_handle == NULL) {
+ return false;
+ }
+
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+ bool exists =
+ comm_session_exists(socket_comm_handle, socket_comm_session);
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ return exists;
+}
+
+
+void write_message(int socket_fd, const char *message, unsigned int msg_length)
+{
+ ssize_t bytes_sent = 0;
+ unsigned int total_bytes_sent = 0;
+
+ while ((uint32_t)bytes_sent < msg_length) {
+ bytes_sent = write(socket_fd, message + total_bytes_sent,
+ msg_length);
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] socket_comm writing on socket fd [%d] msg_lenth [%u] bytes sent [%d]",
+ __func__, time(NULL), pthread_self(), socket_fd,
+ msg_length, bytes_sent);
+
+ if (bytes_sent < 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ pcep_log(LOG_WARNING, "%s: send() failure",
+ __func__);
+
+ return;
+ }
+ } else {
+ total_bytes_sent += bytes_sent;
+ }
+ }
+}
+
+
+unsigned int read_message(int socket_fd, char *received_message,
+ unsigned int max_message_size)
+{
+ /* TODO what if bytes_read == max_message_size? there could be more to
+ * read */
+ unsigned int bytes_read =
+ read(socket_fd, received_message, max_message_size);
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] socket_comm read message bytes_read [%u] on socket fd [%d]",
+ __func__, time(NULL), pthread_self(), bytes_read, socket_fd);
+
+ return bytes_read;
+}
+
+
+int build_fd_sets(pcep_socket_comm_handle *socket_comm_handle)
+{
+ int max_fd = 0;
+
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+
+ FD_ZERO(&socket_comm_handle->except_master_set);
+ FD_ZERO(&socket_comm_handle->read_master_set);
+ ordered_list_node *node = socket_comm_handle->read_list->head;
+ pcep_socket_comm_session *comm_session;
+ while (node != NULL) {
+ comm_session = (pcep_socket_comm_session *)node->data;
+ if (comm_session->socket_fd > max_fd) {
+ max_fd = comm_session->socket_fd;
+ }
+
+ /*pcep_log(LOG_DEBUG, ld] socket_comm::build_fdSets set
+ ready_toRead
+ [%d]", __func__, time(NULL), comm_session->socket_fd);*/
+ FD_SET(comm_session->socket_fd,
+ &socket_comm_handle->read_master_set);
+ FD_SET(comm_session->socket_fd,
+ &socket_comm_handle->except_master_set);
+ node = node->next_node;
+ }
+
+ FD_ZERO(&socket_comm_handle->write_master_set);
+ node = socket_comm_handle->write_list->head;
+ while (node != NULL) {
+ comm_session = (pcep_socket_comm_session *)node->data;
+ if (comm_session->socket_fd > max_fd) {
+ max_fd = comm_session->socket_fd;
+ }
+
+ /*pcep_log(LOG_DEBUG, "%s: [%ld] socket_comm::build_fdSets set
+ ready_toWrite [%d]", __func__, time(NULL),
+ comm_session->socket_fd);*/
+ FD_SET(comm_session->socket_fd,
+ &socket_comm_handle->write_master_set);
+ FD_SET(comm_session->socket_fd,
+ &socket_comm_handle->except_master_set);
+ node = node->next_node;
+ }
+
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ return max_fd + 1;
+}
+
+
+void handle_reads(pcep_socket_comm_handle *socket_comm_handle)
+{
+
+ /*
+ * iterate all the socket_fd's in the read_list. it may be that not
+ * all of them have something to read. dont remove the socket_fd
+ * from the read_list since messages could come at any time.
+ */
+
+ /* Notice: Only locking the mutex when accessing the read_list,
+ * since the read callbacks may end up calling back into the socket
+ * comm module to write messages which could be a deadlock. */
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+ ordered_list_node *node = socket_comm_handle->read_list->head;
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ while (node != NULL) {
+ pcep_socket_comm_session *comm_session =
+ (pcep_socket_comm_session *)node->data;
+
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+ node = node->next_node;
+ if (!comm_session_exists(socket_comm_handle, comm_session)) {
+ /* This comm_session has been deleted, move on to the
+ * next one */
+ pthread_mutex_unlock(
+ &(socket_comm_handle->socket_comm_mutex));
+ continue;
+ }
+
+ int is_set = FD_ISSET(comm_session->socket_fd,
+ &(socket_comm_handle->read_master_set));
+ /* Upon read failure, the comm_session might be free'd, so we
+ * cant store the received_bytes in the comm_session, until we
+ * know the read was successful. */
+ int received_bytes = 0;
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ if (is_set) {
+ FD_CLR(comm_session->socket_fd,
+ &(socket_comm_handle->read_master_set));
+
+ /* either read the message locally, or call the
+ * message_ready_handler to read it */
+ if (comm_session->message_handler != NULL) {
+ received_bytes = read_message(
+ comm_session->socket_fd,
+ comm_session->received_message,
+ MAX_RECVD_MSG_SIZE);
+ if (received_bytes > 0) {
+ /* Send the received message to the
+ * handler */
+ comm_session->received_bytes =
+ received_bytes;
+ comm_session->message_handler(
+ comm_session->session_data,
+ comm_session->received_message,
+ comm_session->received_bytes);
+ }
+ } else {
+ /* Tell the handler a message is ready to be
+ * read. The comm_session may be destroyed in
+ * this call, if
+ * there is an error reading or if the socket is
+ * closed. */
+ received_bytes =
+ comm_session
+ ->message_ready_to_read_handler(
+ comm_session
+ ->session_data,
+ comm_session
+ ->socket_fd);
+ }
+
+ /* handle the read results */
+ if (received_bytes == 0) {
+ if (comm_session_exists_locking(
+ socket_comm_handle, comm_session)) {
+ comm_session->received_bytes = 0;
+ /* the socket was closed */
+ /* TODO should we define a socket except
+ * enum? or will the only time we call
+ * this is when the socket is closed??
+ */
+ if (comm_session->conn_except_notifier
+ != NULL) {
+ comm_session->conn_except_notifier(
+ comm_session
+ ->session_data,
+ comm_session
+ ->socket_fd);
+ }
+
+ /* stop reading from the socket if its
+ * closed */
+ pthread_mutex_lock(
+ &(socket_comm_handle
+ ->socket_comm_mutex));
+ ordered_list_remove_first_node_equals(
+ socket_comm_handle->read_list,
+ comm_session);
+ pthread_mutex_unlock(
+ &(socket_comm_handle
+ ->socket_comm_mutex));
+ }
+ } else if (received_bytes < 0) {
+ /* TODO should we call conn_except_notifier()
+ * here ? */
+ pcep_log(
+ LOG_WARNING,
+ "%s: Error on socket fd [%d] : errno [%d][%s]",
+ __func__, comm_session->socket_fd,
+ errno, strerror(errno));
+ } else {
+ comm_session->received_bytes = received_bytes;
+ }
+ }
+ }
+}
+
+
+void handle_writes(pcep_socket_comm_handle *socket_comm_handle)
+{
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+
+ /*
+ * iterate all the socket_fd's in the write_list. it may be that not
+ * all of them are ready to be written to. only remove the socket_fd
+ * from the list if it is ready to be written to.
+ */
+
+ ordered_list_node *node = socket_comm_handle->write_list->head;
+ pcep_socket_comm_session *comm_session;
+ bool msg_written;
+ while (node != NULL) {
+ comm_session = (pcep_socket_comm_session *)node->data;
+ node = node->next_node;
+ msg_written = false;
+
+ if (!comm_session_exists(socket_comm_handle, comm_session)) {
+ /* This comm_session has been deleted, move on to the
+ * next one */
+ continue;
+ }
+
+ if (FD_ISSET(comm_session->socket_fd,
+ &(socket_comm_handle->write_master_set))) {
+ /* only remove the entry from the list, if it is written
+ * to */
+ ordered_list_remove_first_node_equals(
+ socket_comm_handle->write_list, comm_session);
+ FD_CLR(comm_session->socket_fd,
+ &(socket_comm_handle->write_master_set));
+
+ /* dequeue all the comm_session messages and send them
+ */
+ pcep_socket_comm_queued_message *queued_message =
+ queue_dequeue(comm_session->message_queue);
+ while (queued_message != NULL) {
+ msg_written = true;
+ write_message(comm_session->socket_fd,
+ queued_message->encoded_message,
+ queued_message->msg_length);
+ if (queued_message->free_after_send) {
+ pceplib_free(PCEPLIB_MESSAGES,
+ (void *)queued_message
+ ->encoded_message);
+ }
+ pceplib_free(PCEPLIB_MESSAGES, queued_message);
+ queued_message = queue_dequeue(
+ comm_session->message_queue);
+ }
+ }
+
+ /* check if the socket should be closed after writing */
+ if (comm_session->close_after_write == true) {
+ if (comm_session->message_queue->num_entries == 0) {
+ /* TODO check to make sure modifying the
+ * write_list while iterating it doesnt cause
+ * problems. */
+ pcep_log(
+ LOG_DEBUG,
+ "%s: handle_writes close() socket fd [%d]",
+ __func__, comm_session->socket_fd);
+ ordered_list_remove_first_node_equals(
+ socket_comm_handle->read_list,
+ comm_session);
+ ordered_list_remove_first_node_equals(
+ socket_comm_handle->write_list,
+ comm_session);
+ close(comm_session->socket_fd);
+ comm_session->socket_fd = -1;
+ }
+ }
+
+ if (comm_session->message_sent_handler != NULL
+ && msg_written == true) {
+ /* Unlocking to allow the message_sent_handler to
+ * make calls like destroy_socket_comm_session */
+ pthread_mutex_unlock(
+ &(socket_comm_handle->socket_comm_mutex));
+ comm_session->message_sent_handler(
+ comm_session->session_data,
+ comm_session->socket_fd);
+ pthread_mutex_lock(
+ &(socket_comm_handle->socket_comm_mutex));
+ }
+ }
+
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+}
+
+
+void handle_excepts(pcep_socket_comm_handle *socket_comm_handle)
+{
+ /* TODO finish this */
+ (void)socket_comm_handle;
+}
+
+
+/* pcep_socket_comm::initialize_socket_comm_loop() will create a thread and
+ * invoke this method */
+void *socket_comm_loop(void *data)
+{
+ if (data == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Cannot start socket_comm_loop with NULL pcep_socketcomm_handle",
+ __func__);
+ return NULL;
+ }
+
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting socket_comm_loop thread",
+ __func__, time(NULL), pthread_self());
+
+ pcep_socket_comm_handle *socket_comm_handle =
+ (pcep_socket_comm_handle *)data;
+ struct timeval timer;
+ int max_fd;
+
+ while (socket_comm_handle->active) {
+ /* check the FD's every 1/4 sec, 250 milliseconds */
+ timer.tv_sec = 0;
+ timer.tv_usec = 250000;
+ max_fd = build_fd_sets(socket_comm_handle);
+
+ if (select(max_fd, &(socket_comm_handle->read_master_set),
+ &(socket_comm_handle->write_master_set),
+ &(socket_comm_handle->except_master_set), &timer)
+ < 0) {
+ /* TODO handle the error */
+ pcep_log(
+ LOG_WARNING,
+ "%s: ERROR socket_comm_loop on select : errno [%d][%s]",
+ __func__, errno, strerror(errno));
+ }
+
+ handle_reads(socket_comm_handle);
+ handle_writes(socket_comm_handle);
+ handle_excepts(socket_comm_handle);
+ }
+
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Finished socket_comm_loop thread",
+ __func__, time(NULL), pthread_self());
+
+ return NULL;
+}
+
+int pceplib_external_socket_read(int fd, void *payload)
+{
+ pcep_socket_comm_handle *socket_comm_handle =
+ (pcep_socket_comm_handle *)payload;
+ if (socket_comm_handle == NULL) {
+ return -1;
+ }
+
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+ FD_SET(fd, &(socket_comm_handle->read_master_set));
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ handle_reads(socket_comm_handle);
+
+ /* Get the socket_comm_session */
+ pcep_socket_comm_session find_session = {.socket_fd = fd};
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+ ordered_list_node *node =
+ ordered_list_find(socket_comm_handle->read_list, &find_session);
+
+ /* read again */
+ if (node != NULL) {
+ socket_comm_handle->socket_read_func(
+ socket_comm_handle->external_infra_data,
+ &((pcep_socket_comm_session *)node)
+ ->external_socket_data,
+ fd, socket_comm_handle);
+ }
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ return 0;
+}
+
+int pceplib_external_socket_write(int fd, void *payload)
+{
+ pcep_socket_comm_handle *socket_comm_handle =
+ (pcep_socket_comm_handle *)payload;
+ if (socket_comm_handle == NULL) {
+ return -1;
+ }
+
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+ FD_SET(fd, &(socket_comm_handle->write_master_set));
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ handle_writes(socket_comm_handle);
+
+ /* TODO do we need to cancel this FD from writing?? */
+
+ return 0;
+}
diff --git a/pceplib/pcep_socket_comm_loop.h b/pceplib/pcep_socket_comm_loop.h
new file mode 100644
index 000000000..3ca2c037f
--- /dev/null
+++ b/pceplib/pcep_socket_comm_loop.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEPSOCKETCOMMLOOP_H_
+#define PCEPSOCKETCOMMLOOP_H_
+
+void handle_reads(pcep_socket_comm_handle *socket_comm_handle);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/pcep_socket_comm_mock.c b/pceplib/pcep_socket_comm_mock.c
new file mode 100644
index 000000000..069d0cf99
--- /dev/null
+++ b/pceplib/pcep_socket_comm_mock.c
@@ -0,0 +1,363 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * This module is built into a separate library, and is used by several
+ * other modules for unit testing, so that real sockets dont have to be
+ * created.
+ */
+
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_socket_comm.h"
+#include "pcep_socket_comm_mock.h"
+#include "pcep_utils_queue.h"
+
+/* reset_mock_socket_comm_info() should be used before each test */
+mock_socket_comm_info mock_socket_metadata;
+
+void setup_mock_socket_comm_info(void)
+{
+ mock_socket_metadata.socket_comm_session_initialize_times_called = 0;
+ mock_socket_metadata.socket_comm_session_initialize_src_times_called =
+ 0;
+ mock_socket_metadata.socket_comm_session_teardown_times_called = 0;
+ mock_socket_metadata.socket_comm_session_connect_tcp_times_called = 0;
+ mock_socket_metadata.socket_comm_session_send_message_times_called = 0;
+ mock_socket_metadata
+ .socket_comm_session_close_tcp_after_write_times_called = 0;
+ mock_socket_metadata.socket_comm_session_close_tcp_times_called = 0;
+ mock_socket_metadata.destroy_socket_comm_loop_times_called = 0;
+ mock_socket_metadata.send_message_save_message = false;
+ mock_socket_metadata.sent_message_list = dll_initialize();
+}
+
+void teardown_mock_socket_comm_info(void)
+{
+ dll_destroy(mock_socket_metadata.sent_message_list);
+}
+
+void reset_mock_socket_comm_info(void)
+{
+ teardown_mock_socket_comm_info();
+ setup_mock_socket_comm_info();
+}
+
+mock_socket_comm_info *get_mock_socket_comm_info(void)
+{
+ return &mock_socket_metadata;
+}
+
+void verify_socket_comm_times_called(int initialized, int teardown, int connect,
+ int send_message,
+ int close_tcp_after_write, int close_tcp,
+ int destroy)
+{
+ CU_ASSERT_EQUAL(initialized,
+ mock_socket_metadata
+ .socket_comm_session_initialize_times_called);
+ CU_ASSERT_EQUAL(
+ teardown,
+ mock_socket_metadata.socket_comm_session_teardown_times_called);
+ CU_ASSERT_EQUAL(connect,
+ mock_socket_metadata
+ .socket_comm_session_connect_tcp_times_called);
+ CU_ASSERT_EQUAL(send_message,
+ mock_socket_metadata
+ .socket_comm_session_send_message_times_called);
+ CU_ASSERT_EQUAL(
+ close_tcp_after_write,
+ mock_socket_metadata
+ .socket_comm_session_close_tcp_after_write_times_called);
+ CU_ASSERT_EQUAL(close_tcp,
+ mock_socket_metadata
+ .socket_comm_session_close_tcp_times_called);
+ CU_ASSERT_EQUAL(
+ destroy,
+ mock_socket_metadata.destroy_socket_comm_loop_times_called);
+}
+
+
+/*
+ * Mock the socket_comm functions used by session_logic for Unit Testing
+ */
+
+bool initialize_socket_comm_external_infra(
+ void *external_infra_data, ext_socket_read socket_read_cb,
+ ext_socket_write socket_write_cb,
+ ext_socket_pthread_create_callback thread_create_func)
+{
+ (void)external_infra_data;
+ (void)socket_read_cb;
+ (void)socket_write_cb;
+ (void)thread_create_func;
+
+ mock_socket_metadata
+ .socket_comm_initialize_external_infra_times_called++;
+
+ return true;
+}
+
+bool destroy_socket_comm_loop()
+{
+ mock_socket_metadata.destroy_socket_comm_loop_times_called++;
+
+ return false;
+}
+
+pcep_socket_comm_session *
+socket_comm_session_initialize(message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier,
+ struct in_addr *dst_ip, short dst_port,
+ uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data)
+{
+ (void)msg_sent_notifier;
+ (void)tcp_authentication_str;
+ (void)is_tcp_auth_md5;
+
+ mock_socket_metadata.socket_comm_session_initialize_times_called++;
+
+ pcep_socket_comm_session *comm_session =
+ malloc(sizeof(pcep_socket_comm_session));
+ memset(comm_session, 0, sizeof(pcep_socket_comm_session));
+
+ comm_session->message_handler = msg_rcv_handler;
+ comm_session->message_ready_to_read_handler = msg_ready_handler;
+ comm_session->conn_except_notifier = notifier;
+ comm_session->message_queue = queue_initialize();
+ comm_session->session_data = session_data;
+ comm_session->close_after_write = false;
+ comm_session->connect_timeout_millis = connect_timeout_millis;
+ comm_session->is_ipv6 = false;
+ comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_family = AF_INET;
+ comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_port =
+ htons(dst_port);
+ comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_addr.s_addr =
+ dst_ip->s_addr;
+
+ return comm_session;
+}
+
+pcep_socket_comm_session *socket_comm_session_initialize_ipv6(
+ message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in6_addr *dst_ip,
+ short dst_port, uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str, bool is_tcp_auth_md5,
+ void *session_data)
+{
+ (void)msg_sent_notifier;
+ (void)tcp_authentication_str;
+ (void)is_tcp_auth_md5;
+
+ mock_socket_metadata.socket_comm_session_initialize_times_called++;
+
+ pcep_socket_comm_session *comm_session =
+ malloc(sizeof(pcep_socket_comm_session));
+ memset(comm_session, 0, sizeof(pcep_socket_comm_session));
+
+ comm_session->message_handler = msg_rcv_handler;
+ comm_session->message_ready_to_read_handler = msg_ready_handler;
+ comm_session->conn_except_notifier = notifier;
+ comm_session->message_queue = queue_initialize();
+ comm_session->session_data = session_data;
+ comm_session->close_after_write = false;
+ comm_session->connect_timeout_millis = connect_timeout_millis;
+ comm_session->is_ipv6 = true;
+ comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_family = AF_INET6;
+ comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_port =
+ htons(dst_port);
+ memcpy(&comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_addr,
+ dst_ip, sizeof(struct in6_addr));
+
+ return comm_session;
+}
+
+pcep_socket_comm_session *socket_comm_session_initialize_with_src(
+ message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in_addr *src_ip,
+ short src_port, struct in_addr *dst_ip, short dst_port,
+ uint32_t connect_timeout_millis, const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data)
+{
+ (void)msg_sent_notifier;
+ (void)tcp_authentication_str;
+ (void)is_tcp_auth_md5;
+
+ mock_socket_metadata.socket_comm_session_initialize_src_times_called++;
+
+ pcep_socket_comm_session *comm_session =
+ malloc(sizeof(pcep_socket_comm_session));
+ memset(comm_session, 0, sizeof(pcep_socket_comm_session));
+
+ comm_session->message_handler = msg_rcv_handler;
+ comm_session->message_ready_to_read_handler = msg_ready_handler;
+ comm_session->conn_except_notifier = notifier;
+ comm_session->message_queue = queue_initialize();
+ comm_session->session_data = session_data;
+ comm_session->close_after_write = false;
+ comm_session->connect_timeout_millis = connect_timeout_millis;
+ comm_session->is_ipv6 = false;
+ comm_session->src_sock_addr.src_sock_addr_ipv4.sin_family = AF_INET;
+ comm_session->src_sock_addr.src_sock_addr_ipv4.sin_port =
+ htons(src_port);
+ comm_session->src_sock_addr.src_sock_addr_ipv4.sin_addr.s_addr =
+ ((src_ip == NULL) ? INADDR_ANY : src_ip->s_addr);
+ comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_family = AF_INET;
+ comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_port =
+ htons(dst_port);
+ comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_addr.s_addr =
+ dst_ip->s_addr;
+
+ return comm_session;
+}
+
+pcep_socket_comm_session *socket_comm_session_initialize_with_src_ipv6(
+ message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in6_addr *src_ip,
+ short src_port, struct in6_addr *dst_ip, short dst_port,
+ uint32_t connect_timeout_millis, const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data)
+{
+ (void)msg_sent_notifier;
+ (void)tcp_authentication_str;
+ (void)is_tcp_auth_md5;
+
+ mock_socket_metadata.socket_comm_session_initialize_src_times_called++;
+
+ pcep_socket_comm_session *comm_session =
+ malloc(sizeof(pcep_socket_comm_session));
+ memset(comm_session, 0, sizeof(pcep_socket_comm_session));
+
+ comm_session->message_handler = msg_rcv_handler;
+ comm_session->message_ready_to_read_handler = msg_ready_handler;
+ comm_session->conn_except_notifier = notifier;
+ comm_session->message_queue = queue_initialize();
+ comm_session->session_data = session_data;
+ comm_session->close_after_write = false;
+ comm_session->connect_timeout_millis = connect_timeout_millis;
+ comm_session->is_ipv6 = true;
+ comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_family = AF_INET6;
+ comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_port =
+ htons(src_port);
+ if (src_ip == NULL) {
+ comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_addr =
+ in6addr_any;
+ } else {
+ memcpy(&comm_session->src_sock_addr.src_sock_addr_ipv6
+ .sin6_addr,
+ src_ip, sizeof(struct in6_addr));
+ }
+ comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_family = AF_INET6;
+ comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_port =
+ htons(dst_port);
+ memcpy(&comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_addr,
+ dst_ip, sizeof(struct in6_addr));
+
+ return comm_session;
+}
+
+bool socket_comm_session_teardown(pcep_socket_comm_session *socket_comm_session)
+{
+ mock_socket_metadata.socket_comm_session_teardown_times_called++;
+
+ if (socket_comm_session != NULL) {
+ queue_destroy(socket_comm_session->message_queue);
+ free(socket_comm_session);
+ }
+
+ return true;
+}
+
+
+bool socket_comm_session_connect_tcp(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ (void)socket_comm_session;
+
+ mock_socket_metadata.socket_comm_session_connect_tcp_times_called++;
+
+ return true;
+}
+
+
+void socket_comm_session_send_message(
+ pcep_socket_comm_session *socket_comm_session,
+ const char *encoded_message, unsigned int msg_length,
+ bool delete_after_send)
+{
+ (void)socket_comm_session;
+ (void)msg_length;
+
+ mock_socket_metadata.socket_comm_session_send_message_times_called++;
+
+ if (mock_socket_metadata.send_message_save_message == true) {
+ /* the caller/test case is responsible for freeing the message
+ */
+ dll_append(mock_socket_metadata.sent_message_list,
+ (char *)encoded_message);
+ } else {
+ if (delete_after_send == true) {
+ free((void *)encoded_message);
+ }
+ }
+
+ return;
+}
+
+
+bool socket_comm_session_close_tcp_after_write(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ (void)socket_comm_session;
+
+ mock_socket_metadata
+ .socket_comm_session_close_tcp_after_write_times_called++;
+
+ return true;
+}
+
+
+bool socket_comm_session_close_tcp(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ (void)socket_comm_session;
+
+ mock_socket_metadata.socket_comm_session_close_tcp_times_called++;
+
+ return true;
+}
diff --git a/pceplib/pcep_socket_comm_mock.h b/pceplib/pcep_socket_comm_mock.h
new file mode 100644
index 000000000..ca0e38b80
--- /dev/null
+++ b/pceplib/pcep_socket_comm_mock.h
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+/*
+ * This module is built into a separate library, and is used by several
+ * other modules for unit testing, so that real sockets dont have to be
+ * created.
+ */
+
+#ifndef PCEP_SOCKET_COMM_MOCK_SOCKET_COMM_H_
+#define PCEP_SOCKET_COMM_MOCK_SOCKET_COMM_H_
+
+#include <stdbool.h>
+
+#include "pcep_utils_double_linked_list.h"
+
+typedef struct mock_socket_comm_info_ {
+ int socket_comm_initialize_external_infra_times_called;
+ int socket_comm_session_initialize_times_called;
+ int socket_comm_session_initialize_src_times_called;
+ int socket_comm_session_teardown_times_called;
+ int socket_comm_session_connect_tcp_times_called;
+ int socket_comm_session_send_message_times_called;
+ int socket_comm_session_close_tcp_after_write_times_called;
+ int socket_comm_session_close_tcp_times_called;
+ int destroy_socket_comm_loop_times_called;
+
+ /* TODO later if necessary, we can add return values for
+ * those functions that return something */
+
+ /* Used to access messages sent with socket_comm_session_send_message()
+ */
+ bool send_message_save_message;
+ double_linked_list *sent_message_list;
+
+} mock_socket_comm_info;
+
+void setup_mock_socket_comm_info(void);
+void teardown_mock_socket_comm_info(void);
+void reset_mock_socket_comm_info(void);
+bool destroy_socket_comm_loop(void);
+
+mock_socket_comm_info *get_mock_socket_comm_info(void);
+void verify_socket_comm_times_called(int initialized, int teardown, int connect,
+ int send_message, int close_after_write,
+ int close, int destroy);
+
+#endif /* PCEP_SOCKET_COMM_MOCK_SOCKET_COMM_H_ */
diff --git a/pceplib/pcep_timer_internals.h b/pceplib/pcep_timer_internals.h
new file mode 100644
index 000000000..8221c78ba
--- /dev/null
+++ b/pceplib/pcep_timer_internals.h
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEPTIMERINTERNALS_H_
+#define PCEPTIMERINTERNALS_H_
+
+#include <stdint.h>
+#include <pthread.h>
+
+#include "pcep_utils_ordered_list.h"
+
+/* Function pointer to be called when timers expire.
+ * Parameters:
+ * void *data - passed into create_timer
+ * int timer_id - the timer_id returned by create_timer
+ */
+typedef void (*timer_expire_handler)(void *, int);
+
+/* Function pointer when an external timer infrastructure is used */
+typedef void (*ext_timer_create)(void *infra_data, void **timer, int seconds,
+ void *data);
+typedef void (*ext_timer_cancel)(void **timer);
+typedef int (*ext_pthread_create_callback)(pthread_t *pthread_id,
+ const pthread_attr_t *attr,
+ void *(*start_routine)(void *),
+ void *data, const char *thread_name);
+
+typedef struct pcep_timer_ {
+ time_t expire_time;
+ uint16_t sleep_seconds;
+ int timer_id;
+ void *data;
+ void *external_timer;
+
+} pcep_timer;
+
+typedef struct pcep_timers_context_ {
+ ordered_list_handle *timer_list;
+ bool active;
+ timer_expire_handler expire_handler;
+ pthread_t event_loop_thread;
+ pthread_mutex_t timer_list_lock;
+ void *external_timer_infra_data;
+ ext_timer_create timer_create_func;
+ ext_timer_cancel timer_cancel_func;
+
+} pcep_timers_context;
+
+/* functions implemented in pcep_timers_loop.c */
+void *event_loop(void *context);
+
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/pcep_timers.c b/pceplib/pcep_timers.c
new file mode 100644
index 000000000..e9d9d4b21
--- /dev/null
+++ b/pceplib/pcep_timers.c
@@ -0,0 +1,482 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Implementation of public API timer functions.
+ */
+
+#include <limits.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "pcep_timers.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+#include "pcep_utils_ordered_list.h"
+
+static pcep_timers_context *timers_context_ = NULL;
+static int timer_id_ = 0;
+
+
+/* simple compare method callback used by pcep_utils_ordered_list
+ * for ordered list insertion. */
+int timer_list_node_compare(void *list_entry, void *new_entry)
+{
+ /* return:
+ * < 0 if new_entry < list_entry
+ * == 0 if new_entry == list_entry (new_entry will be inserted after
+ * list_entry) > 0 if new_entry > list_entry */
+ return ((pcep_timer *)new_entry)->expire_time
+ - ((pcep_timer *)list_entry)->expire_time;
+}
+
+
+/* simple compare method callback used by pcep_utils_ordered_list
+ * ordered_list_remove_first_node_equals2 to remove a timer based on
+ * its timer_id. */
+int timer_list_node_timer_id_compare(void *list_entry, void *new_entry)
+{
+ return ((pcep_timer *)new_entry)->timer_id
+ - ((pcep_timer *)list_entry)->timer_id;
+}
+
+/* simple compare method callback used by pcep_utils_ordered_list
+ * ordered_list_remove_first_node_equals2 to remove a timer based on
+ * its address. */
+int timer_list_node_timer_ptr_compare(void *list_entry, void *new_entry)
+{
+ return ((char *)new_entry - (char *)list_entry);
+}
+
+/* internal util method */
+static pcep_timers_context *create_timers_context_()
+{
+ if (timers_context_ == NULL) {
+ timers_context_ = pceplib_malloc(PCEPLIB_INFRA,
+ sizeof(pcep_timers_context));
+ memset(timers_context_, 0, sizeof(pcep_timers_context));
+ timers_context_->active = false;
+ }
+
+ return timers_context_;
+}
+
+
+/* Internal util function */
+static bool initialize_timers_common(timer_expire_handler expire_handler)
+{
+ if (expire_handler == NULL) {
+ /* Cannot have a NULL handler function */
+ return false;
+ }
+
+ timers_context_ = create_timers_context_();
+
+ if (timers_context_->active == true) {
+ /* already initialized */
+ return false;
+ }
+
+ timers_context_->active = true;
+ timers_context_->timer_list =
+ ordered_list_initialize(timer_list_node_compare);
+ timers_context_->expire_handler = expire_handler;
+
+ if (pthread_mutex_init(&(timers_context_->timer_list_lock), NULL)
+ != 0) {
+ pcep_log(
+ LOG_ERR,
+ "%s: ERROR initializing timers, cannot initialize the mutex",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+bool initialize_timers(timer_expire_handler expire_handler)
+{
+ if (initialize_timers_common(expire_handler) == false) {
+ return false;
+ }
+
+ if (pthread_create(&(timers_context_->event_loop_thread), NULL,
+ event_loop, timers_context_)) {
+ pcep_log(
+ LOG_ERR,
+ "%s: ERROR initializing timers, cannot initialize the thread",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+bool initialize_timers_external_infra(
+ timer_expire_handler expire_handler, void *external_timer_infra_data,
+ ext_timer_create timer_create_func, ext_timer_cancel timer_cancel_func,
+ ext_pthread_create_callback thread_create_func)
+{
+ if (initialize_timers_common(expire_handler) == false) {
+ return false;
+ }
+
+ if (thread_create_func != NULL) {
+ if (thread_create_func(&(timers_context_->event_loop_thread),
+ NULL, event_loop, timers_context_,
+ "pceplib_timers")) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize external timers thread.",
+ __func__);
+ return false;
+ }
+ } else {
+ if (pthread_create(&(timers_context_->event_loop_thread), NULL,
+ event_loop, timers_context_)) {
+ pcep_log(
+ LOG_ERR,
+ "%s: ERROR initializing timers, cannot initialize the thread",
+ __func__);
+ return false;
+ }
+ }
+
+ timers_context_->external_timer_infra_data = external_timer_infra_data;
+ timers_context_->timer_create_func = timer_create_func;
+ timers_context_->timer_cancel_func = timer_cancel_func;
+
+ return true;
+}
+
+/*
+ * This function is only used to tear_down the timer data.
+ * Only the timer data is deleted, not the list itself,
+ * which is deleted by ordered_list_destroy().
+ */
+void free_all_timers(pcep_timers_context *timers_context)
+{
+ pthread_mutex_lock(&timers_context->timer_list_lock);
+
+ ordered_list_node *timer_node = timers_context->timer_list->head;
+
+ while (timer_node != NULL) {
+ if (timer_node->data != NULL) {
+ pceplib_free(PCEPLIB_INFRA, timer_node->data);
+ }
+ timer_node = timer_node->next_node;
+ }
+
+ pthread_mutex_unlock(&timers_context->timer_list_lock);
+}
+
+
+bool teardown_timers()
+{
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to teardown the timers, but they are not initialized",
+ __func__);
+ return false;
+ }
+
+ if (timers_context_->active == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to teardown the timers, but they are not active",
+ __func__);
+ return false;
+ }
+
+ timers_context_->active = false;
+ if (timers_context_->event_loop_thread != 0) {
+ /* TODO this does not build
+ * Instead of calling pthread_join() which could block if the
+ thread
+ * is blocked, try joining for at most 1 second.
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += 1;
+ int retval =
+ pthread_timedjoin_np(timers_context_->event_loop_thread, NULL,
+ &ts); if (retval != 0)
+ {
+ pcep_log(LOG_WARNING, "%s: thread did not stop after 1
+ second waiting on it.", __func__);
+ }
+ */
+ pthread_join(timers_context_->event_loop_thread, NULL);
+ }
+
+ free_all_timers(timers_context_);
+ ordered_list_destroy(timers_context_->timer_list);
+
+ if (pthread_mutex_destroy(&(timers_context_->timer_list_lock)) != 0) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to teardown the timers, cannot destroy the mutex",
+ __func__);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, timers_context_);
+ timers_context_ = NULL;
+
+ return true;
+}
+
+
+int get_next_timer_id()
+{
+ if (timer_id_ == INT_MAX) {
+ timer_id_ = 0;
+ }
+
+ return timer_id_++;
+}
+
+int create_timer(uint16_t sleep_seconds, void *data)
+{
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to create a timer: the timers have not been initialized",
+ __func__);
+ return -1;
+ }
+
+ pcep_timer *timer = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_timer));
+ memset(timer, 0, sizeof(pcep_timer));
+ timer->data = data;
+ timer->sleep_seconds = sleep_seconds;
+ timer->expire_time = time(NULL) + sleep_seconds;
+
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+ timer->timer_id = get_next_timer_id();
+
+ /* implemented in pcep_utils_ordered_list.c */
+ if (ordered_list_add_node(timers_context_->timer_list, timer) == NULL) {
+ pceplib_free(PCEPLIB_INFRA, timer);
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to create a timer, cannot add the timer to the timer list",
+ __func__);
+
+ return -1;
+ }
+
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ if (timers_context_->timer_create_func) {
+ timers_context_->timer_create_func(
+ timers_context_->external_timer_infra_data,
+ &timer->external_timer, sleep_seconds, timer);
+ }
+
+ return timer->timer_id;
+}
+
+
+bool cancel_timer(int timer_id)
+{
+ static pcep_timer compare_timer;
+
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to cancel a timer: the timers have not been initialized",
+ __func__);
+ return false;
+ }
+
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+
+ compare_timer.timer_id = timer_id;
+ pcep_timer *timer_toRemove = ordered_list_remove_first_node_equals2(
+ timers_context_->timer_list, &compare_timer,
+ timer_list_node_timer_id_compare);
+ if (timer_toRemove == NULL) {
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to cancel a timer [%d] that does not exist",
+ __func__, timer_id);
+ return false;
+ }
+
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ if (timers_context_->timer_cancel_func) {
+ timers_context_->timer_cancel_func(
+ &timer_toRemove->external_timer);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, timer_toRemove);
+
+ return true;
+}
+
+
+bool reset_timer(int timer_id)
+{
+ static pcep_timer compare_timer;
+
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to reset a timer: the timers have not been initialized",
+ __func__);
+
+ return false;
+ }
+
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+
+ compare_timer.timer_id = timer_id;
+ ordered_list_node *timer_to_reset_node =
+ ordered_list_find2(timers_context_->timer_list, &compare_timer,
+ timer_list_node_timer_id_compare);
+ if (timer_to_reset_node == NULL) {
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(LOG_WARNING,
+ "%s: Trying to reset a timer node that does not exist",
+ __func__);
+
+ return false;
+ }
+
+ pcep_timer *timer_to_reset = timer_to_reset_node->data;
+ if (timer_to_reset == NULL) {
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(LOG_WARNING,
+ "%s: Trying to reset a timer that does not exist",
+ __func__);
+
+ return false;
+ }
+
+ /* First check if the timer to reset already has the same expire time,
+ * which means multiple reset_timer() calls were made on the same timer
+ * in the same second */
+ time_t expire_time = time(NULL) + timer_to_reset->sleep_seconds;
+ if (timer_to_reset->expire_time == expire_time) {
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ return true;
+ }
+
+ ordered_list_remove_node2(timers_context_->timer_list,
+ timer_to_reset_node);
+
+ timer_to_reset->expire_time = expire_time;
+ if (ordered_list_add_node(timers_context_->timer_list, timer_to_reset)
+ == NULL) {
+ pceplib_free(PCEPLIB_INFRA, timer_to_reset);
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to reset a timer, cannot add the timer to the timer list",
+ __func__);
+
+ return false;
+ }
+
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ if (timers_context_->timer_cancel_func) {
+ /* Keeping this log for now, since in older versions of FRR the
+ * timer cancellation was blocking. This allows us to see how
+ * long the it takes.*/
+ pcep_log(LOG_DEBUG, "%s: Reseting timer [%d] with callback",
+ __func__, timer_to_reset->timer_id);
+ timers_context_->timer_cancel_func(
+ &timer_to_reset->external_timer);
+ timer_to_reset->external_timer = NULL;
+ }
+
+ if (timers_context_->timer_create_func) {
+ timers_context_->timer_create_func(
+ timers_context_->external_timer_infra_data,
+ &timer_to_reset->external_timer,
+ timer_to_reset->sleep_seconds, timer_to_reset);
+ /* Keeping this log for now, since in older versions of FRR the
+ * timer cancellation was blocking. This allows us to see how
+ * long the it takes.*/
+ pcep_log(LOG_DEBUG, "%s: Reset timer [%d] with callback",
+ __func__, timer_to_reset->timer_id);
+ }
+
+ return true;
+}
+
+
+void pceplib_external_timer_expire_handler(void *data)
+{
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: External timer expired but timers_context is not initialized",
+ __func__);
+ return;
+ }
+
+ if (timers_context_->expire_handler == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: External timer expired but expire_handler is not initialized",
+ __func__);
+ return;
+ }
+
+ if (data == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: External timer expired with NULL data", __func__);
+ return;
+ }
+
+ pcep_timer *timer = (pcep_timer *)data;
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+ ordered_list_node *timer_node =
+ ordered_list_find2(timers_context_->timer_list, timer,
+ timer_list_node_timer_ptr_compare);
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ /* Cannot continue if the timer does not exist */
+ if (timer_node == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: pceplib_external_timer_expire_handler timer [%p] id [%d] does not exist",
+ __func__, timer, timer->timer_id);
+ return;
+ }
+
+ timers_context_->expire_handler(timer->data, timer->timer_id);
+
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+ ordered_list_remove_node2(timers_context_->timer_list, timer_node);
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ pceplib_free(PCEPLIB_INFRA, timer);
+}
diff --git a/pceplib/pcep_timers.h b/pceplib/pcep_timers.h
new file mode 100644
index 000000000..b2cc6ec54
--- /dev/null
+++ b/pceplib/pcep_timers.h
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Public API for pcep_timers
+ */
+
+#ifndef PCEPTIMERS_H_
+#define PCEPTIMERS_H_
+
+#include <pthread.h>
+#include <stdbool.h>
+
+#include "pcep_timer_internals.h"
+
+#define TIMER_ID_NOT_SET -1
+
+/*
+ * Initialize the timers module.
+ * The timer_expire_handler function pointer will be called each time a timer
+ * expires. Return true for successful initialization, false otherwise.
+ */
+bool initialize_timers(timer_expire_handler expire_handler);
+
+/*
+ * Initialize the timers module with an external back-end infrastructure, like
+ * FRR.
+ */
+bool initialize_timers_external_infra(
+ timer_expire_handler expire_handler, void *external_timer_infra_data,
+ ext_timer_create timer_create_func, ext_timer_cancel timer_cancel_func,
+ ext_pthread_create_callback thread_create_func);
+
+/*
+ * Teardown the timers module.
+ */
+bool teardown_timers(void);
+
+/*
+ * Create a new timer for "sleep_seconds" seconds.
+ * If the timer expires before being cancelled, the timer_expire_handler
+ * passed to initialize_timers() will be called with the pointer to "data".
+ * Returns a timer_id <= 0 that can be used to cancel_timer.
+ * Returns < 0 on error.
+ */
+int create_timer(uint16_t sleep_seconds, void *data);
+
+/*
+ * Cancel a timer created with create_timer().
+ * Returns true if the timer was found and cancelled, false otherwise.
+ */
+bool cancel_timer(int timer_id);
+
+/*
+ * Reset an previously created timer, maintaining the same timer_id.
+ * Returns true if the timer was found and reset, false otherwise.
+ */
+bool reset_timer(int timer_id);
+
+/*
+ * If an external timer infra like FRR is used, then this function
+ * will be called when the timers expire in the external infra.
+ */
+void pceplib_external_timer_expire_handler(void *data);
+
+int timer_list_node_compare(void *list_entry, void *new_entry);
+int timer_list_node_timer_id_compare(void *list_entry, void *new_entry);
+int timer_list_node_timer_ptr_compare(void *list_entry, void *new_entry);
+void free_all_timers(pcep_timers_context *timers_context);
+int get_next_timer_id(void);
+
+#endif /* PCEPTIMERS_H_ */
diff --git a/pceplib/pcep_timers_event_loop.c b/pceplib/pcep_timers_event_loop.c
new file mode 100644
index 000000000..932a53eb2
--- /dev/null
+++ b/pceplib/pcep_timers_event_loop.c
@@ -0,0 +1,106 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/select.h>
+
+#include "pcep_timers_event_loop.h"
+#include "pcep_timer_internals.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+/* For each expired timer: remove the timer from the list, call the
+ * expire_handler, and free the timer. */
+void walk_and_process_timers(pcep_timers_context *timers_context)
+{
+ pthread_mutex_lock(&timers_context->timer_list_lock);
+
+ bool keep_walking = true;
+ ordered_list_node *timer_node = timers_context->timer_list->head;
+ time_t now = time(NULL);
+ pcep_timer *timer_data;
+
+ /* the timers are sorted by expire_time, so we will only
+ * remove the top node each time through the loop */
+ while (timer_node != NULL && keep_walking) {
+ timer_data = (pcep_timer *)timer_node->data;
+ if (timer_data->expire_time <= now) {
+ timer_node = timer_node->next_node;
+ ordered_list_remove_first_node(
+ timers_context->timer_list);
+ /* call the timer expired handler */
+ timers_context->expire_handler(timer_data->data,
+ timer_data->timer_id);
+ pceplib_free(PCEPLIB_INFRA, timer_data);
+ } else {
+ keep_walking = false;
+ }
+ }
+
+ pthread_mutex_unlock(&timers_context->timer_list_lock);
+}
+
+
+/* pcep_timers::initialize() will create a thread and invoke this method */
+void *event_loop(void *context)
+{
+ if (context == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: pcep_timers_event_loop cannot start event_loop with NULL data",
+ __func__);
+ return NULL;
+ }
+
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting timers_event_loop thread",
+ __func__, time(NULL), pthread_self());
+
+ pcep_timers_context *timers_context = (pcep_timers_context *)context;
+ struct timeval timer;
+ int retval;
+
+ while (timers_context->active) {
+ /* check the timers every half second */
+ timer.tv_sec = 0;
+ timer.tv_usec = 500000;
+
+ do {
+ /* if the select() call gets interrupted, select() will
+ * set the remaining time in timer, so we need to call
+ * it again.
+ */
+ retval = select(0, NULL, NULL, NULL, &timer);
+ } while (retval != 0 && errno == EINTR);
+
+ walk_and_process_timers(timers_context);
+ }
+
+ pcep_log(LOG_WARNING, "%s: [%ld-%ld] Finished timers_event_loop thread",
+ __func__, time(NULL), pthread_self());
+
+ return NULL;
+}
diff --git a/pceplib/pcep_timers_event_loop.h b/pceplib/pcep_timers_event_loop.h
new file mode 100644
index 000000000..c4a7264da
--- /dev/null
+++ b/pceplib/pcep_timers_event_loop.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_TIMERS_EVENT_LOOP_H_
+#define PCEP_TIMERS_EVENT_LOOP_H_
+
+#include "pcep_timer_internals.h"
+
+void walk_and_process_timers(pcep_timers_context *timers_context);
+
+#endif
diff --git a/pceplib/pcep_utils_counters.c b/pceplib/pcep_utils_counters.c
new file mode 100644
index 000000000..d8078f683
--- /dev/null
+++ b/pceplib/pcep_utils_counters.c
@@ -0,0 +1,475 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Implementation of PCEP Counters.
+ */
+
+#include <zebra.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "pcep_utils_counters.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+struct counters_group *create_counters_group(const char *group_name,
+ uint16_t max_subgroups)
+{
+ if (group_name == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create counters group: group_name is NULL.",
+ __func__);
+ return NULL;
+ }
+
+ if (max_subgroups > MAX_COUNTER_GROUPS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create counters group: max_subgroups [%d] is larger than max the [%d].",
+ __func__, max_subgroups, MAX_COUNTER_GROUPS);
+ return NULL;
+ }
+
+ struct counters_group *group =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(struct counters_group));
+ memset(group, 0, sizeof(struct counters_group));
+ group->subgroups =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(struct counters_subgroup *)
+ * (max_subgroups + 1));
+ memset(group->subgroups, 0,
+ sizeof(struct counters_subgroup *) * (max_subgroups + 1));
+
+ strlcpy(group->counters_group_name, group_name,
+ sizeof(group->counters_group_name));
+ group->max_subgroups = max_subgroups;
+ group->start_time = time(NULL);
+
+ return group;
+}
+
+struct counters_subgroup *create_counters_subgroup(const char *subgroup_name,
+ uint16_t subgroup_id,
+ uint16_t max_counters)
+{
+ if (subgroup_name == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create counters subgroup: subgroup_name is NULL.",
+ __func__);
+ return NULL;
+ }
+
+ if (max_counters > MAX_COUNTERS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create counters subgroup: max_counters [%d] is larger than the max [%d].",
+ __func__, max_counters, MAX_COUNTERS);
+ return NULL;
+ }
+
+ if (subgroup_id > MAX_COUNTER_GROUPS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create counters subgroup: subgroup_id [%d] is larger than max the [%d].",
+ __func__, subgroup_id, MAX_COUNTER_GROUPS);
+ return NULL;
+ }
+
+ struct counters_subgroup *subgroup =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(struct counters_subgroup));
+ memset(subgroup, 0, sizeof(struct counters_subgroup));
+ subgroup->counters = pceplib_malloc(
+ PCEPLIB_INFRA, sizeof(struct counter *) * (max_counters + 1));
+ memset(subgroup->counters, 0,
+ sizeof(struct counter *) * (max_counters + 1));
+
+ strlcpy(subgroup->counters_subgroup_name, subgroup_name,
+ sizeof(subgroup->counters_subgroup_name));
+ subgroup->subgroup_id = subgroup_id;
+ subgroup->max_counters = max_counters;
+
+ return subgroup;
+}
+
+struct counters_subgroup *
+clone_counters_subgroup(struct counters_subgroup *subgroup,
+ const char *subgroup_name, uint16_t subgroup_id)
+{
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot clone counters subgroup: input counters_subgroup is NULL.",
+ __func__);
+ return NULL;
+ }
+
+ if (subgroup_name == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot clone counters subgroup: subgroup_name is NULL.",
+ __func__);
+ return NULL;
+ }
+
+ if (subgroup_id > MAX_COUNTER_GROUPS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot clone counters subgroup: subgroup_id [%d] is larger than max the [%d].",
+ __func__, subgroup_id, MAX_COUNTER_GROUPS);
+ return NULL;
+ }
+
+ struct counters_subgroup *cloned_subgroup = create_counters_subgroup(
+ subgroup_name, subgroup_id, subgroup->max_counters);
+ int i = 0;
+ for (; i <= subgroup->max_counters; i++) {
+ struct counter *counter = subgroup->counters[i];
+ if (counter != NULL) {
+ create_subgroup_counter(cloned_subgroup,
+ counter->counter_id,
+ counter->counter_name);
+ }
+ }
+
+ return cloned_subgroup;
+}
+
+bool add_counters_subgroup(struct counters_group *group,
+ struct counters_subgroup *subgroup)
+{
+ if (group == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot add counters subgroup: counters_group is NULL.",
+ __func__);
+ return false;
+ }
+
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot add counters subgroup: counters_subgroup is NULL.",
+ __func__);
+ return false;
+ }
+
+ if (subgroup->subgroup_id >= group->max_subgroups) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot add counters subgroup: counters_subgroup id [%d] is larger than the group max_subgroups [%d].",
+ __func__, subgroup->subgroup_id, group->max_subgroups);
+ return false;
+ }
+
+ group->num_subgroups++;
+ group->subgroups[subgroup->subgroup_id] = subgroup;
+
+ return true;
+}
+
+bool create_subgroup_counter(struct counters_subgroup *subgroup,
+ uint32_t counter_id, const char *counter_name)
+{
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create subgroup counter: counters_subgroup is NULL.",
+ __func__);
+ return false;
+ }
+
+ if (counter_id >= subgroup->max_counters) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create subgroup counter: counter_id [%d] is larger than the subgroup max_counters [%d].",
+ __func__, counter_id, subgroup->max_counters);
+ return false;
+ }
+
+ if (counter_name == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create subgroup counter: counter_name is NULL.",
+ __func__);
+ return NULL;
+ }
+
+ struct counter *counter =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(struct counter));
+ memset(counter, 0, sizeof(struct counter));
+ counter->counter_id = counter_id;
+ strlcpy(counter->counter_name, counter_name,
+ sizeof(counter->counter_name));
+
+ subgroup->num_counters++;
+ subgroup->counters[counter->counter_id] = counter;
+
+ return true;
+}
+
+bool delete_counters_group(struct counters_group *group)
+{
+ if (group == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot delete group counters: counters_group is NULL.",
+ __func__);
+ return false;
+ }
+
+ int i = 0;
+ for (; i <= group->max_subgroups; i++) {
+ struct counters_subgroup *subgroup = group->subgroups[i];
+ if (subgroup != NULL) {
+ delete_counters_subgroup(subgroup);
+ }
+ }
+
+ pceplib_free(PCEPLIB_INFRA, group->subgroups);
+ pceplib_free(PCEPLIB_INFRA, group);
+
+ return true;
+}
+
+bool delete_counters_subgroup(struct counters_subgroup *subgroup)
+{
+ if (subgroup == NULL || subgroup->counters == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot delete subgroup counters: counters_subgroup is NULL.",
+ __func__);
+ return false;
+ }
+
+ int i = 0;
+ for (; i <= subgroup->max_counters; i++) {
+ struct counter *counter = subgroup->counters[i];
+ if (counter != NULL) {
+ pceplib_free(PCEPLIB_INFRA, counter);
+ }
+ }
+
+ pceplib_free(PCEPLIB_INFRA, subgroup->counters);
+ pceplib_free(PCEPLIB_INFRA, subgroup);
+
+ return true;
+}
+
+bool reset_group_counters(struct counters_group *group)
+{
+ if (group == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot reset group counters: counters_group is NULL.",
+ __func__);
+ return false;
+ }
+
+ int i = 0;
+ for (; i <= group->max_subgroups; i++) {
+ struct counters_subgroup *subgroup = group->subgroups[i];
+ if (subgroup != NULL) {
+ reset_subgroup_counters(subgroup);
+ }
+ }
+
+ group->start_time = time(NULL);
+
+ return true;
+}
+
+bool reset_subgroup_counters(struct counters_subgroup *subgroup)
+{
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot reset subgroup counters: counters_subgroup is NULL.",
+ __func__);
+ return false;
+ }
+
+ int i = 0;
+ for (; i <= subgroup->max_counters; i++) {
+ struct counter *counter = subgroup->counters[i];
+ if (counter != NULL) {
+ counter->counter_value = 0;
+ }
+ }
+
+ return true;
+}
+
+bool increment_counter(struct counters_group *group, uint16_t subgroup_id,
+ uint16_t counter_id)
+{
+ if (group == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot increment counter: counters_group is NULL.",
+ __func__);
+ return false;
+ }
+
+ if (subgroup_id >= group->max_subgroups) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: Cannot increment counter: subgroup_id [%d] is larger than the group max_subgroups [%d].",
+ __func__, subgroup_id, group->max_subgroups);
+ return false;
+ }
+
+ struct counters_subgroup *subgroup = group->subgroups[subgroup_id];
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot increment counter: counters_subgroup in counters_group is NULL.",
+ __func__);
+ return false;
+ }
+
+ return increment_subgroup_counter(subgroup, counter_id);
+}
+
+bool increment_subgroup_counter(struct counters_subgroup *subgroup,
+ uint16_t counter_id)
+{
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot increment counter: counters_subgroup is NULL.",
+ __func__);
+ return false;
+ }
+
+ if (counter_id >= subgroup->max_counters) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: Cannot increment counter: counter_id [%d] is larger than the subgroup max_counters [%d].",
+ __func__, counter_id, subgroup->max_counters);
+ return false;
+ }
+
+ if (subgroup->counters[counter_id] == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot increment counter: No counter exists for counter_id [%d].",
+ __func__, counter_id);
+ return false;
+ }
+
+ subgroup->counters[counter_id]->counter_value++;
+
+ return true;
+}
+
+bool dump_counters_group_to_log(struct counters_group *group)
+{
+ if (group == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot dump group counters to log: counters_group is NULL.",
+ __func__);
+ return false;
+ }
+
+ time_t now = time(NULL);
+ pcep_log(
+ LOG_INFO,
+ "%s: PCEP Counters group:\n %s \n Sub-Groups [%d] \n Active for [%d seconds]",
+ __func__, group->counters_group_name, group->num_subgroups,
+ (now - group->start_time));
+
+ int i = 0;
+ for (; i <= group->max_subgroups; i++) {
+ struct counters_subgroup *subgroup = group->subgroups[i];
+ if (subgroup != NULL) {
+ dump_counters_subgroup_to_log(subgroup);
+ }
+ }
+
+ return true;
+}
+
+bool dump_counters_subgroup_to_log(struct counters_subgroup *subgroup)
+{
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot dump subgroup counters to log: counters_subgroup is NULL.",
+ __func__);
+ return false;
+ }
+
+ pcep_log(LOG_INFO,
+ "%s: \tPCEP Counters sub-group [%s] with [%d] counters",
+ __func__, subgroup->counters_subgroup_name,
+ subgroup->num_counters);
+
+ int i = 0;
+ for (; i <= subgroup->max_counters; i++) {
+ struct counter *counter = subgroup->counters[i];
+ if (counter != NULL) {
+ pcep_log(LOG_INFO, "%s: \t\t%s %d", __func__,
+ counter->counter_name, counter->counter_value);
+ }
+ }
+
+ return true;
+}
+
+struct counters_subgroup *find_subgroup(const struct counters_group *group,
+ uint16_t subgroup_id)
+{
+ int i = 0;
+ for (; i <= group->max_subgroups; i++) {
+ struct counters_subgroup *subgroup = group->subgroups[i];
+ if (subgroup != NULL) {
+ if (subgroup->subgroup_id == subgroup_id) {
+ return subgroup;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+uint32_t subgroup_counters_total(struct counters_subgroup *subgroup)
+{
+ if (subgroup == NULL) {
+ return 0;
+ }
+ uint32_t counter_total = 0;
+ int i = 0;
+ for (; i <= subgroup->max_counters; i++) {
+ struct counter *counter = subgroup->counters[i];
+ if (counter != NULL) {
+ counter_total += counter->counter_value;
+ }
+ }
+
+ return counter_total;
+}
diff --git a/pceplib/pcep_utils_counters.h b/pceplib/pcep_utils_counters.h
new file mode 100644
index 000000000..240e9758b
--- /dev/null
+++ b/pceplib/pcep_utils_counters.h
@@ -0,0 +1,232 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Definitions of PCEP Counters.
+ */
+
+#ifndef PCEP_UTILS_INCLUDE_PCEP_UTILS_COUNTERS_H_
+#define PCEP_UTILS_INCLUDE_PCEP_UTILS_COUNTERS_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Example Counter group with sub-groups and counters
+ *
+ * pcep_counters {
+ * counters_group_rx {
+ * message_open;
+ * message_keepalive;
+ * message_pcreq;
+ * }
+ * counters_group_tx {
+ * message_open;
+ * message_keepalive;
+ * message_pcreq;
+ * }
+ * counters_group_events {
+ * pcc_connect;
+ * pce_connect;
+ * pcc_disconnect;
+ * pce_disconnect;
+ * }
+ * }
+ *
+ * To create the above structure of groups, sub-groups, and counters, do the
+ * following:
+ *
+ * struct counters_subgroup *rx_subgroup = create_counters_subgroup("rx
+ * counters", 3); struct counters_subgroup *tx_subgroup =
+ * create_counters_subgroup("tx counters", 3); struct counters_subgroup
+ * *events_subgroup = create_counters_subgroup("events counters", 4);
+ *
+ * Use message_id: PCEP_TYPE_OPEN=1
+ * create_subgroup_counter(rx_subgroup, 1, "Message Open");
+ * create_subgroup_counter(rx_subgroup, 2, "Message KeepAlive");
+ * create_subgroup_counter(rx_subgroup, 3, "Message PcReq");
+ *
+ * create_subgroup_counter(tx_subgroup, 1, "Message Open");
+ * create_subgroup_counter(tx_subgroup, 2, "Message KeepAlive");
+ * create_subgroup_counter(tx_subgroup, 3, "Message PcReq");
+ *
+ * create_subgroup_counter(events_subgroup, 1, "PCC Connect");
+ * create_subgroup_counter(events_subgroup, 2, "PCE Connect");
+ * create_subgroup_counter(events_subgroup, 3, "PCC Disconnect");
+ * create_subgroup_counter(events_subgroup, 4, "PCE Disconnect");
+ *
+ * struct counters_group *cntrs_group = create_counters_group("PCEP Counters",
+ * 3); add_counters_subgroup(cntrs_group, rx_subgroup);
+ * add_counters_subgroup(cntrs_group, tx_subgroup);
+ * add_counters_subgroup(cntrs_group, events_subgroup);
+ */
+
+#define MAX_COUNTER_STR_LENGTH 128
+#define MAX_COUNTER_GROUPS 500
+#define MAX_COUNTERS 500
+
+struct counter {
+ uint16_t counter_id;
+ char counter_name[MAX_COUNTER_STR_LENGTH];
+ uint32_t counter_value;
+};
+
+struct counters_subgroup {
+ char counters_subgroup_name[MAX_COUNTER_STR_LENGTH];
+ uint16_t subgroup_id;
+ uint16_t num_counters;
+ uint16_t max_counters;
+ /* Array of (struct counter *) allocated when the subgroup is created.
+ * The array is indexed by the struct counter->counter_id */
+ struct counter **counters;
+};
+
+struct counters_group {
+ char counters_group_name[MAX_COUNTER_STR_LENGTH];
+ uint16_t num_subgroups;
+ uint16_t max_subgroups;
+ time_t start_time;
+ /* Array of (struct counters_subgroup *) allocated when the group is
+ * created. The subgroup is indexed by the (struct counters_subgroup
+ * *)->subgroup_id */
+ struct counters_subgroup **subgroups;
+};
+
+/*
+ * Create a counters group with the given group_name and number of subgroups.
+ * Subgroup_ids are 0-based, so take that into account when setting
+ * max_subgroups. Return true on success or false if group_name is NULL or
+ * max_subgroups >= MAX_COUNTER_GROUPS.
+ */
+struct counters_group *create_counters_group(const char *group_name,
+ uint16_t max_subgroups);
+
+/*
+ * Create a counters subgroup with the given subgroup_name, subgroup_id and
+ * number of counters. The subgroup_id is 0-based. counter_ids are 0-based, so
+ * take that into account when setting max_counters. Return true on success or
+ * false if subgroup_name is NULL, subgroup_id >= MAX_COUNTER_GROUPS, or
+ * max_counters >= MAX_COUNTERS.
+ */
+struct counters_subgroup *create_counters_subgroup(const char *subgroup_name,
+ uint16_t subgroup_id,
+ uint16_t max_counters);
+
+/*
+ * Add a counter_subgroup to a counter_group.
+ * Return true on success or false if group is NULL or if subgroup is NULL.
+ */
+bool add_counters_subgroup(struct counters_group *group,
+ struct counters_subgroup *subgroup);
+
+/*
+ * Clone a subgroup and set a new name and subgroup_id for the new subgroup.
+ * This is useful for RX and TX counters: just create the RX counters and clone
+ * it for the TX counters.
+ */
+struct counters_subgroup *
+clone_counters_subgroup(struct counters_subgroup *subgroup,
+ const char *subgroup_name, uint16_t subgroup_id);
+
+/*
+ * Create a counter in a subgroup with the given counter_id and counter_name.
+ * The counter_id is 0-based.
+ * Return true on success or false if subgroup is NULL, counter_id >=
+ * MAX_COUNTERS, or if counter_name is NULL.
+ */
+bool create_subgroup_counter(struct counters_subgroup *subgroup,
+ uint32_t counter_id, const char *counter_name);
+
+/*
+ * Delete the counters_group and recursively delete all subgroups and their
+ * counters. Return true on success or false if group is NULL.
+ */
+bool delete_counters_group(struct counters_group *group);
+
+/*
+ * Delete the counters_subgroup and all its counters counters.
+ * Return true on success or false if subgroup is NULL.
+ */
+bool delete_counters_subgroup(struct counters_subgroup *subgroup);
+
+/*
+ * Reset all the counters in all sub-groups contained in this group.
+ * Return true on success or false if group is NULL.
+ */
+bool reset_group_counters(struct counters_group *group);
+
+/*
+ * Reset all the counters in this subgroup.
+ * Return true on success or false if subgroup is NULL.
+ */
+bool reset_subgroup_counters(struct counters_subgroup *subgroup);
+
+/*
+ * Increment a counter given a counter_group, subgroup_id, and counter_id.
+ * Return true on success or false if group is NULL, subgroup_id >=
+ * MAX_COUNTER_GROUPS, or counter_id >= MAX_COUNTERS.
+ */
+bool increment_counter(struct counters_group *group, uint16_t subgroup_id,
+ uint16_t counter_id);
+
+/*
+ * Increment a counter given the counter_subgroup and counter_id.
+ * Return true on success or false if subgroup is NULL or counter_id >=
+ * MAX_COUNTERS.
+ */
+bool increment_subgroup_counter(struct counters_subgroup *subgroup,
+ uint16_t counter_id);
+
+/*
+ * Dump the counter_group info and all its counter_subgroups.
+ * Return true on success or false if group is NULL.
+ */
+bool dump_counters_group_to_log(struct counters_group *group);
+
+/*
+ * Dump all the counters in a counter_subgroup.
+ * Return true on success or false if subgroup is NULL.
+ */
+bool dump_counters_subgroup_to_log(struct counters_subgroup *subgroup);
+
+/*
+ * Search for a counters_subgroup by subgroup_id in a counters_group
+ * and return it, if found, else return NULL.
+ */
+struct counters_subgroup *find_subgroup(const struct counters_group *group,
+ uint16_t subgroup_id);
+
+/*
+ * Given a counters_subgroup, return the sum of all the counters.
+ */
+uint32_t subgroup_counters_total(struct counters_subgroup *subgroup);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PCEP_UTILS_INCLUDE_PCEP_UTILS_COUNTERS_H_ */
diff --git a/pceplib/pcep_utils_double_linked_list.c b/pceplib/pcep_utils_double_linked_list.c
new file mode 100644
index 000000000..acdcee059
--- /dev/null
+++ b/pceplib/pcep_utils_double_linked_list.c
@@ -0,0 +1,262 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+double_linked_list *dll_initialize()
+{
+ double_linked_list *handle =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(double_linked_list));
+ if (handle != NULL) {
+ memset(handle, 0, sizeof(double_linked_list));
+ handle->num_entries = 0;
+ handle->head = NULL;
+ handle->tail = NULL;
+ } else {
+ pcep_log(LOG_WARNING,
+ "%s: dll_initialize cannot allocate memory for handle",
+ __func__);
+ return NULL;
+ }
+
+ return handle;
+}
+
+
+void dll_destroy(double_linked_list *handle)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: dll_destroy cannot destroy NULL handle",
+ __func__);
+ return;
+ }
+
+ double_linked_list_node *node = handle->head;
+ while (node != NULL) {
+ double_linked_list_node *node_to_delete = node;
+ node = node->next_node;
+ pceplib_free(PCEPLIB_INFRA, node_to_delete);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, handle);
+}
+
+
+void dll_destroy_with_data_memtype(double_linked_list *handle,
+ void *data_memory_type)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: dll_destroy_with_data cannot destroy NULL handle",
+ __func__);
+ return;
+ }
+
+ double_linked_list_node *node = handle->head;
+ while (node != NULL) {
+ double_linked_list_node *node_to_delete = node;
+ pceplib_free(data_memory_type, node->data);
+ node = node->next_node;
+ pceplib_free(PCEPLIB_INFRA, node_to_delete);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, handle);
+}
+
+
+void dll_destroy_with_data(double_linked_list *handle)
+{
+ /* Default to destroying the data with the INFRA mem type */
+ dll_destroy_with_data_memtype(handle, PCEPLIB_INFRA);
+}
+
+
+/* Creates a node and adds it as the first item in the list */
+double_linked_list_node *dll_prepend(double_linked_list *handle, void *data)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING, "%s: dll_prepend_data NULL handle",
+ __func__);
+ return NULL;
+ }
+
+ /* Create the new node */
+ double_linked_list_node *new_node =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(double_linked_list_node));
+ memset(new_node, 0, sizeof(double_linked_list_node));
+ new_node->data = data;
+
+ if (handle->head == NULL) {
+ handle->head = new_node;
+ handle->tail = new_node;
+ } else {
+ new_node->next_node = handle->head;
+ handle->head->prev_node = new_node;
+ handle->head = new_node;
+ }
+
+ (handle->num_entries)++;
+
+ return new_node;
+}
+
+
+/* Creates a node and adds it as the last item in the list */
+double_linked_list_node *dll_append(double_linked_list *handle, void *data)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING, "%s: dll_append_data NULL handle",
+ __func__);
+ return NULL;
+ }
+
+ /* Create the new node */
+ double_linked_list_node *new_node =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(double_linked_list_node));
+ memset(new_node, 0, sizeof(double_linked_list_node));
+ new_node->data = data;
+
+ if (handle->head == NULL) {
+ handle->head = new_node;
+ handle->tail = new_node;
+ } else {
+ new_node->prev_node = handle->tail;
+ handle->tail->next_node = new_node;
+ handle->tail = new_node;
+ }
+
+ (handle->num_entries)++;
+
+ return new_node;
+}
+
+
+/* Delete the first node in the list, and return the data */
+void *dll_delete_first_node(double_linked_list *handle)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING, "%s: dll_delete_first_node NULL handle",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ double_linked_list_node *delete_node = handle->head;
+ void *data = delete_node->data;
+
+ if (delete_node->next_node == NULL) {
+ /* Its the last node in the list */
+ handle->head = NULL;
+ handle->tail = NULL;
+ } else {
+ handle->head = delete_node->next_node;
+ handle->head->prev_node = NULL;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, delete_node);
+ (handle->num_entries)--;
+
+ return data;
+}
+
+
+/* Delete the last node in the list, and return the data */
+void *dll_delete_last_node(double_linked_list *handle)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING, "%s: dll_delete_last_node NULL handle",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ double_linked_list_node *delete_node = handle->tail;
+ void *data = delete_node->data;
+
+ if (delete_node->prev_node == NULL) {
+ /* Its the last node in the list */
+ handle->head = NULL;
+ handle->tail = NULL;
+ } else {
+ handle->tail = delete_node->prev_node;
+ handle->tail->next_node = NULL;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, delete_node);
+ (handle->num_entries)--;
+
+ return data;
+}
+
+
+/* Delete the designated node in the list, and return the data */
+void *dll_delete_node(double_linked_list *handle, double_linked_list_node *node)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING, "%s: dll_delete_node NULL handle",
+ __func__);
+ return NULL;
+ }
+
+ if (node == NULL) {
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ void *data = node->data;
+
+ if (handle->head == handle->tail) {
+ /* Its the last node in the list */
+ handle->head = NULL;
+ handle->tail = NULL;
+ } else if (handle->head == node) {
+ handle->head = node->next_node;
+ handle->head->prev_node = NULL;
+ } else if (handle->tail == node) {
+ handle->tail = node->prev_node;
+ handle->tail->next_node = NULL;
+ } else {
+ /* Its somewhere in the middle of the list */
+ node->next_node->prev_node = node->prev_node;
+ node->prev_node->next_node = node->next_node;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, node);
+ (handle->num_entries)--;
+
+ return data;
+}
diff --git a/pceplib/pcep_utils_double_linked_list.h b/pceplib/pcep_utils_double_linked_list.h
new file mode 100644
index 000000000..4fe01726a
--- /dev/null
+++ b/pceplib/pcep_utils_double_linked_list.h
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#ifndef PCEP_UTILS_INCLUDE_PCEP_UTILS_DOUBLE_LINKED_LIST_H_
+#define PCEP_UTILS_INCLUDE_PCEP_UTILS_DOUBLE_LINKED_LIST_H_
+
+typedef struct double_linked_list_node_ {
+ struct double_linked_list_node_ *prev_node;
+ struct double_linked_list_node_ *next_node;
+ void *data;
+
+} double_linked_list_node;
+
+
+typedef struct double_linked_list_ {
+ double_linked_list_node *head;
+ double_linked_list_node *tail;
+ unsigned int num_entries;
+
+} double_linked_list;
+
+
+/* Initialize a double linked list */
+double_linked_list *dll_initialize(void);
+
+/* Destroy a double linked list, by freeing the handle and nodes,
+ * user data will not be freed, and may be leaked if not handled
+ * externally. */
+void dll_destroy(double_linked_list *handle);
+/* Destroy a double linked list, by freeing the handle and nodes,
+ * and the user data. */
+void dll_destroy_with_data(double_linked_list *handle);
+void dll_destroy_with_data_memtype(double_linked_list *handle,
+ void *data_memory_type);
+
+/* Creates a node and adds it as the first item in the list */
+double_linked_list_node *dll_prepend(double_linked_list *handle, void *data);
+
+/* Creates a node and adds it as the last item in the list */
+double_linked_list_node *dll_append(double_linked_list *handle, void *data);
+
+/* Delete the first node in the list, and return the data */
+void *dll_delete_first_node(double_linked_list *handle);
+
+/* Delete the last node in the list, and return the data */
+void *dll_delete_last_node(double_linked_list *handle);
+
+/* Delete the designated node in the list, and return the data */
+void *dll_delete_node(double_linked_list *handle,
+ double_linked_list_node *node);
+
+#endif /* PCEP_UTILS_INCLUDE_PCEP_UTILS_DOUBLE_LINKED_LIST_H_ */
diff --git a/pceplib/pcep_utils_logging.c b/pceplib/pcep_utils_logging.c
new file mode 100644
index 000000000..65e1abbc0
--- /dev/null
+++ b/pceplib/pcep_utils_logging.c
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "pcep_utils_logging.h"
+
+/* Forward declaration */
+int pcep_stdout_logger(int priority, const char *format, va_list args);
+
+static pcep_logger_func logger_func = pcep_stdout_logger;
+static int logging_level_ = LOG_INFO;
+
+void register_logger(pcep_logger_func logger)
+{
+ logger_func = logger;
+}
+
+void set_logging_level(int level)
+{
+ logging_level_ = level;
+}
+
+int get_logging_level()
+{
+ return logging_level_;
+}
+
+void pcep_log(int priority, const char *format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ logger_func(priority, format, va);
+ va_end(va);
+}
+
+void pcep_log_hexbytes(int priority, const char *message, const uint8_t *bytes,
+ uint8_t bytes_len)
+{
+ char byte_str[2048] = {0};
+ int i = 0;
+
+ snprintf(byte_str, 2048, "%s ", message);
+ for (; i < bytes_len; i++) {
+ snprintf(byte_str, 2048, "%02x ", bytes[i]);
+ }
+ snprintf(byte_str, 2048, "\n");
+
+ pcep_log(priority, "%s", byte_str);
+}
+
+/* Defined with a return type to match the FRR logging signature.
+ * Assuming glibc printf() is thread-safe. */
+int pcep_stdout_logger(int priority, const char *format, va_list args)
+{
+ if (priority <= logging_level_) {
+ vprintf(format, args);
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/pceplib/pcep_utils_logging.h b/pceplib/pcep_utils_logging.h
new file mode 100644
index 000000000..24ea495bd
--- /dev/null
+++ b/pceplib/pcep_utils_logging.h
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#ifndef PCEP_UTILS_INCLUDE_PCEP_UTILS_LOGGING_H_
+#define PCEP_UTILS_INCLUDE_PCEP_UTILS_LOGGING_H_
+
+#include <syslog.h> /* Logging levels */
+#include <stdarg.h> /* va_list */
+#include <stdint.h> /* uint8_t */
+
+/*
+ * The logging defined here i intended to provide the infrastructure to
+ * be able to plug-in an external logger, primarily the FRR logger. There
+ * will be a default internal logger implemented that will write to stdout,
+ * but any other advanced logging features should be implemented externally.
+ */
+
+/* Only the following logging levels from syslog.h should be used:
+ *
+ * LOG_DEBUG - For all messages that are enabled by optional debugging
+ * features, typically preceded by "if (IS...DEBUG...)"
+ * LOG_INFO - Information that may be of interest, but
+ * everything seems to be working properly.
+ * LOG_NOTICE - Only for message pertaining to daemon startup or shutdown.
+ * LOG_WARNING - Warning conditions: unexpected events, but the daemon
+ * believes it can continue to operate correctly.
+ * LOG_ERR - Error situations indicating malfunctions.
+ * Probably requires attention.
+ */
+
+
+/* The signature of this logger function is the same as the FRR logger */
+typedef int (*pcep_logger_func)(int, const char *, va_list);
+void register_logger(pcep_logger_func logger);
+
+/* These functions only take affect when using the internal stdout logger */
+void set_logging_level(int level);
+int get_logging_level(void);
+
+/* Log messages either to a previously registered
+ * logger or to the internal default stdout logger. */
+void pcep_log(int priority, const char *format, ...);
+void pcep_log_hexbytes(int priority, const char *message, const uint8_t *bytes,
+ uint8_t bytes_len);
+
+#endif /* PCEP_UTILS_INCLUDE_PCEP_UTILS_LOGGING_H_ */
diff --git a/pceplib/pcep_utils_memory.c b/pceplib/pcep_utils_memory.c
new file mode 100644
index 000000000..7362e3433
--- /dev/null
+++ b/pceplib/pcep_utils_memory.c
@@ -0,0 +1,220 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+/* Set default values for memory function pointers */
+static pceplib_malloc_func mfunc = NULL;
+static pceplib_calloc_func cfunc = NULL;
+static pceplib_realloc_func rfunc = NULL;
+static pceplib_strdup_func sfunc = NULL;
+static pceplib_free_func ffunc = NULL;
+
+/* Internal memory types */
+struct pceplib_memory_type pceplib_infra_mt = {
+ .memory_type_name = "PCEPlib Infrastructure memory",
+ .total_bytes_allocated = 0,
+ .num_allocates = 0,
+ .total_bytes_freed = 0,
+ .num_frees = 0};
+struct pceplib_memory_type pceplib_messages_mt = {
+ .memory_type_name = "PCEPlib Messages memory",
+ .total_bytes_allocated = 0,
+ .num_allocates = 0,
+ .total_bytes_freed = 0,
+ .num_frees = 0};
+
+/* The memory type pointers default to the internal memory types */
+void *PCEPLIB_INFRA = &pceplib_infra_mt;
+void *PCEPLIB_MESSAGES = &pceplib_messages_mt;
+
+/* Initialize memory function pointers and memory type pointers */
+bool pceplib_memory_initialize(void *pceplib_infra_mt,
+ void *pceplib_messages_mt,
+ pceplib_malloc_func mf, pceplib_calloc_func cf,
+ pceplib_realloc_func rf, pceplib_strdup_func sf,
+ pceplib_free_func ff)
+{
+ PCEPLIB_INFRA = (pceplib_infra_mt ? pceplib_infra_mt : PCEPLIB_INFRA);
+ PCEPLIB_MESSAGES =
+ (pceplib_messages_mt ? pceplib_messages_mt : PCEPLIB_MESSAGES);
+
+ mfunc = (mf ? mf : mfunc);
+ cfunc = (cf ? cf : cfunc);
+ rfunc = (rf ? rf : rfunc);
+ sfunc = (sf ? sf : sfunc);
+ ffunc = (ff ? ff : ffunc);
+
+ return true;
+}
+
+void pceplib_memory_reset()
+{
+ pceplib_infra_mt.total_bytes_allocated = 0;
+ pceplib_infra_mt.num_allocates = 0;
+ pceplib_infra_mt.total_bytes_freed = 0;
+ pceplib_infra_mt.num_frees = 0;
+
+ pceplib_messages_mt.total_bytes_allocated = 0;
+ pceplib_messages_mt.num_allocates = 0;
+ pceplib_messages_mt.total_bytes_freed = 0;
+ pceplib_messages_mt.num_frees = 0;
+}
+
+void pceplib_memory_dump()
+{
+ if (PCEPLIB_INFRA) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Memory Type [%s] Total [allocs, alloc bytes, frees] [%d, %d, %d]",
+ __func__,
+ ((struct pceplib_memory_type *)PCEPLIB_INFRA)
+ ->memory_type_name,
+ ((struct pceplib_memory_type *)PCEPLIB_INFRA)
+ ->num_allocates,
+ ((struct pceplib_memory_type *)PCEPLIB_INFRA)
+ ->total_bytes_allocated,
+ ((struct pceplib_memory_type *)PCEPLIB_INFRA)
+ ->num_frees);
+ }
+
+ if (PCEPLIB_MESSAGES) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Memory Type [%s] Total [allocs, alloc bytes, frees] [%d, %d, %d]",
+ __func__,
+ ((struct pceplib_memory_type *)PCEPLIB_MESSAGES)
+ ->memory_type_name,
+ ((struct pceplib_memory_type *)PCEPLIB_MESSAGES)
+ ->num_allocates,
+ ((struct pceplib_memory_type *)PCEPLIB_MESSAGES)
+ ->total_bytes_allocated,
+ ((struct pceplib_memory_type *)PCEPLIB_MESSAGES)
+ ->num_frees);
+ }
+}
+
+/* PCEPlib memory functions:
+ * They either call the supplied function pointers, or use the internal
+ * implementations, which just increment simple counters and call the
+ * C stdlib memory implementations. */
+
+void *pceplib_malloc(void *mem_type, size_t size)
+{
+ if (mfunc == NULL) {
+ if (mem_type != NULL) {
+ ((struct pceplib_memory_type *)mem_type)
+ ->total_bytes_allocated += size;
+ ((struct pceplib_memory_type *)mem_type)
+ ->num_allocates++;
+ }
+
+ return malloc(size);
+ } else {
+ return mfunc(mem_type, size);
+ }
+}
+
+void *pceplib_calloc(void *mem_type, size_t size)
+{
+ if (cfunc == NULL) {
+ if (mem_type != NULL) {
+ ((struct pceplib_memory_type *)mem_type)
+ ->total_bytes_allocated += size;
+ ((struct pceplib_memory_type *)mem_type)
+ ->num_allocates++;
+ }
+
+ return calloc(1, size);
+ } else {
+ return cfunc(mem_type, size);
+ }
+}
+
+void *pceplib_realloc(void *mem_type, void *ptr, size_t size)
+{
+ if (rfunc == NULL) {
+ if (mem_type != NULL) {
+ /* TODO should add previous allocated bytes to
+ * total_bytes_freed */
+ ((struct pceplib_memory_type *)mem_type)
+ ->total_bytes_allocated += size;
+ ((struct pceplib_memory_type *)mem_type)
+ ->num_allocates++;
+ }
+
+ return realloc(ptr, size);
+ } else {
+ return rfunc(mem_type, ptr, size);
+ }
+}
+
+void *pceplib_strdup(void *mem_type, const char *str)
+{
+ if (sfunc == NULL) {
+ if (mem_type != NULL) {
+ ((struct pceplib_memory_type *)mem_type)
+ ->total_bytes_allocated += strlen(str);
+ ((struct pceplib_memory_type *)mem_type)
+ ->num_allocates++;
+ }
+
+ return strdup(str);
+ } else {
+ return sfunc(mem_type, str);
+ }
+}
+
+void pceplib_free(void *mem_type, void *ptr)
+{
+ if (ffunc == NULL) {
+ if (mem_type != NULL) {
+ /* TODO in order to increment total_bytes_freed, we need
+ * to keep track of the bytes allocated per pointer.
+ * Currently not implemented. */
+ ((struct pceplib_memory_type *)mem_type)->num_frees++;
+ if (((struct pceplib_memory_type *)mem_type)
+ ->num_allocates
+ < ((struct pceplib_memory_type *)mem_type)
+ ->num_frees) {
+ pcep_log(
+ LOG_ERR,
+ "%s: pceplib_free MT N_Alloc < N_Free: MemType [%s] NumAllocates [%d] NumFrees [%d]",
+ __func__,
+ ((struct pceplib_memory_type *)mem_type)
+ ->memory_type_name,
+ ((struct pceplib_memory_type *)mem_type)
+ ->num_allocates,
+ ((struct pceplib_memory_type *)mem_type)
+ ->num_frees);
+ }
+ }
+
+ return free(ptr);
+ } else {
+ return ffunc(mem_type, ptr);
+ }
+}
diff --git a/pceplib/pcep_utils_memory.h b/pceplib/pcep_utils_memory.h
new file mode 100644
index 000000000..4624a91a1
--- /dev/null
+++ b/pceplib/pcep_utils_memory.h
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+#ifndef PCEP_UTILS_INCLUDE_PCEP_UTILS_MEMORY_H_
+#define PCEP_UTILS_INCLUDE_PCEP_UTILS_MEMORY_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/* This module is intended to be used primarily with FRR's memory module,
+ * which has memory groups and memory types, although any memory infrastructure
+ * can be used that has memory types or the memory types in this module can be
+ * set to NULL. The PCEPlib can be used stand-alone, in which case the simple
+ * internal memory type system will be used.
+ */
+
+/* These memory function pointers are modeled after the memory functions
+ * in frr/lib/memory.h */
+typedef void *(*pceplib_malloc_func)(void *mem_type, size_t size);
+typedef void *(*pceplib_calloc_func)(void *mem_type, size_t size);
+typedef void *(*pceplib_realloc_func)(void *mem_type, void *ptr, size_t size);
+typedef void *(*pceplib_strdup_func)(void *mem_type, const char *str);
+typedef void (*pceplib_free_func)(void *mem_type, void *ptr);
+
+/* Either an internal pceplib_memory_type pointer
+ * or could be an FRR memory type pointer */
+extern void *PCEPLIB_INFRA;
+extern void *PCEPLIB_MESSAGES;
+
+/* Internal PCEPlib memory type */
+struct pceplib_memory_type {
+ char memory_type_name[64];
+ uint32_t total_bytes_allocated;
+ uint32_t num_allocates;
+ uint32_t total_bytes_freed; /* currently not used */
+ uint32_t num_frees;
+};
+
+/* Initialize this module by passing in the 2 memory types used in the PCEPlib
+ * and by passing in the different memory allocation/free function pointers.
+ * Any of these parameters can be NULL, in which case an internal implementation
+ * will be used.
+ */
+bool pceplib_memory_initialize(void *pceplib_infra_mt,
+ void *pceplib_messages_mt,
+ pceplib_malloc_func mfunc,
+ pceplib_calloc_func cfunc,
+ pceplib_realloc_func rfunc,
+ pceplib_strdup_func sfunc,
+ pceplib_free_func ffunc);
+
+/* Reset the internal allocation/free counters. Used mainly for internal
+ * testing. */
+void pceplib_memory_reset(void);
+void pceplib_memory_dump(void);
+
+/* Memory functions to be used throughout the PCEPlib. Internally, these
+ * functions will either used the function pointers passed in via
+ * pceplib_memory_initialize() or a simple internal implementation. The
+ * internal implementations just increment the internal memory type
+ * counters and call the C stdlib memory functions.
+ */
+void *pceplib_malloc(void *mem_type, size_t size);
+void *pceplib_calloc(void *mem_type, size_t size);
+void *pceplib_realloc(void *mem_type, void *ptr, size_t size);
+void *pceplib_strdup(void *mem_type, const char *str);
+void pceplib_free(void *mem_type, void *ptr);
+
+#endif /* PCEP_UTILS_INCLUDE_PCEP_UTILS_MEMORY_H_ */
diff --git a/pceplib/pcep_utils_ordered_list.c b/pceplib/pcep_utils_ordered_list.c
new file mode 100644
index 000000000..f5c7f7024
--- /dev/null
+++ b/pceplib/pcep_utils_ordered_list.c
@@ -0,0 +1,322 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+#include "pcep_utils_ordered_list.h"
+
+/* Compare function that simply compares pointers.
+ * return:
+ * < 0 if new_entry < list_entry
+ * == 0 if new_entry == list_entry (new_entry will be inserted after
+ * list_entry) > 0 if new_entry > list_entry
+ */
+int pointer_compare_function(void *list_entry, void *new_entry)
+{
+ return (char *)new_entry - (char *)list_entry;
+}
+
+ordered_list_handle *ordered_list_initialize(ordered_compare_function func_ptr)
+{
+ ordered_list_handle *handle =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(ordered_list_handle));
+ memset(handle, 0, sizeof(ordered_list_handle));
+ handle->head = NULL;
+ handle->num_entries = 0;
+ handle->compare_function = func_ptr;
+
+ return handle;
+}
+
+
+/* free all the ordered_list_node resources and the ordered_list_handle.
+ * it is assumed that the user is responsible fore freeing the data
+ * pointed to by the nodes.
+ */
+void ordered_list_destroy(ordered_list_handle *handle)
+{
+ if (handle == NULL) {
+ return;
+ }
+
+ ordered_list_node *node = handle->head;
+ ordered_list_node *next;
+
+ while (node != NULL) {
+ next = node->next_node;
+ pceplib_free(PCEPLIB_INFRA, node);
+ node = next;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, handle);
+}
+
+
+ordered_list_node *ordered_list_add_node(ordered_list_handle *handle,
+ void *data)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_add_node, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+ handle->num_entries++;
+
+ ordered_list_node *new_node =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(ordered_list_node));
+ memset(new_node, 0, sizeof(ordered_list_node));
+ new_node->data = data;
+ new_node->next_node = NULL;
+
+ /* check if its an empty list */
+ if (handle->head == NULL) {
+ handle->head = new_node;
+
+ return new_node;
+ }
+
+ ordered_list_node *prev_node = handle->head;
+ ordered_list_node *node = prev_node;
+ int compare_result;
+
+ while (node != NULL) {
+ compare_result = handle->compare_function(node->data, data);
+ if (compare_result < 0) {
+ /* insert the node */
+ new_node->next_node = node;
+ if (handle->head == node) {
+ /* add it at the beginning of the list */
+ handle->head = new_node;
+ } else {
+ prev_node->next_node = new_node;
+ }
+
+ return new_node;
+ }
+
+ /* keep searching with the next node in the list */
+ prev_node = node;
+ node = node->next_node;
+ }
+
+ /* at the end of the list, add it here */
+ prev_node->next_node = new_node;
+
+ return new_node;
+}
+
+
+ordered_list_node *ordered_list_find2(ordered_list_handle *handle, void *data,
+ ordered_compare_function compare_func)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_find2, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ ordered_list_node *node = handle->head;
+ int compare_result;
+
+ while (node != NULL) {
+ compare_result = compare_func(node->data, data);
+ if (compare_result == 0) {
+ return node;
+ } else {
+ node = node->next_node;
+ }
+ }
+
+ return NULL;
+}
+
+
+ordered_list_node *ordered_list_find(ordered_list_handle *handle, void *data)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_find, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ return ordered_list_find2(handle, data, handle->compare_function);
+}
+
+
+void *ordered_list_remove_first_node(ordered_list_handle *handle)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_remove_first_node, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+ handle->num_entries--;
+
+ void *data = handle->head->data;
+ ordered_list_node *next_node = handle->head->next_node;
+ pceplib_free(PCEPLIB_INFRA, handle->head);
+ handle->head = next_node;
+
+ return data;
+}
+
+
+void *
+ordered_list_remove_first_node_equals2(ordered_list_handle *handle, void *data,
+ ordered_compare_function compare_func)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_remove_first_node_equals2, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ ordered_list_node *prev_node = handle->head;
+ ordered_list_node *node = prev_node;
+ bool keep_walking = true;
+ void *return_data = NULL;
+ int compare_result;
+
+ while (node != NULL && keep_walking) {
+ compare_result = compare_func(node->data, data);
+ if (compare_result == 0) {
+ return_data = node->data;
+ keep_walking = false;
+ handle->num_entries--;
+
+ /* adjust the corresponding pointers accordingly */
+ if (handle->head == node) {
+ /* its the first node in the list */
+ handle->head = node->next_node;
+ } else {
+ prev_node->next_node = node->next_node;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, node);
+ } else {
+ prev_node = node;
+ node = node->next_node;
+ }
+ }
+
+ return return_data;
+}
+
+
+void *ordered_list_remove_first_node_equals(ordered_list_handle *handle,
+ void *data)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_remove_first_node_equals, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ return ordered_list_remove_first_node_equals2(handle, data,
+ handle->compare_function);
+}
+
+
+void *ordered_list_remove_node(ordered_list_handle *handle,
+ ordered_list_node *prev_node,
+ ordered_list_node *node_toRemove)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_remove_node, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ void *return_data = node_toRemove->data;
+ handle->num_entries--;
+
+ if (node_toRemove == handle->head) {
+ handle->head = node_toRemove->next_node;
+ } else {
+ prev_node->next_node = node_toRemove->next_node;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, node_toRemove);
+
+ return return_data;
+}
+
+void *ordered_list_remove_node2(ordered_list_handle *handle,
+ ordered_list_node *node_to_remove)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_remove_node2, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ ordered_list_node *node = handle->head;
+ ordered_list_node *prev_node = handle->head;
+
+ while (node != NULL) {
+ if (node == node_to_remove) {
+ return (ordered_list_remove_node(handle, prev_node,
+ node));
+ } else {
+ prev_node = node;
+ node = node->next_node;
+ }
+ }
+
+ return NULL;
+}
diff --git a/pceplib/pcep_utils_ordered_list.h b/pceplib/pcep_utils_ordered_list.h
new file mode 100644
index 000000000..ec132dc16
--- /dev/null
+++ b/pceplib/pcep_utils_ordered_list.h
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#ifndef INCLUDE_PCEPUTILSORDEREDLIST_H_
+#define INCLUDE_PCEPUTILSORDEREDLIST_H_
+
+#include <stdbool.h>
+
+typedef struct ordered_list_node_ {
+ struct ordered_list_node_ *next_node;
+ void *data;
+
+} ordered_list_node;
+
+/* The implementation of this function will receive a pointer to the
+ * new data to be inserted and a pointer to the list_entry, and should
+ * return:
+ * < 0 if new_entry < list_entry
+ * == 0 if new_entry == list_entry (new_entry will be inserted after
+ * list_entry) > 0 if new_entry > list_entry
+ */
+typedef int (*ordered_compare_function)(void *list_entry, void *new_entry);
+
+/* Compare function that compares pointers */
+int pointer_compare_function(void *list_entry, void *new_entry);
+
+typedef struct ordered_list_handle_ {
+ ordered_list_node *head;
+ unsigned int num_entries;
+ ordered_compare_function compare_function;
+
+} ordered_list_handle;
+
+ordered_list_handle *ordered_list_initialize(ordered_compare_function func_ptr);
+void ordered_list_destroy(ordered_list_handle *handle);
+
+/* Add a new ordered_list_node to the list, using the ordered_compare_function
+ * to determine where in the list to add it. The newly created ordered_list_node
+ * will be returned.
+ */
+ordered_list_node *ordered_list_add_node(ordered_list_handle *handle,
+ void *data);
+
+/* Find an entry in the ordered_list using the ordered_compare_function to
+ * compare the data passed in.
+ * Return the node if found, NULL otherwise.
+ */
+ordered_list_node *ordered_list_find(ordered_list_handle *handle, void *data);
+
+/* The same as the previous function, but with a specific orderedComparefunction
+ */
+ordered_list_node *ordered_list_find2(ordered_list_handle *handle, void *data,
+ ordered_compare_function compare_func);
+
+/* Remove the first entry in the list and return the data it points to.
+ * Will return NULL if the handle is NULL or if the list is empty.
+ */
+void *ordered_list_remove_first_node(ordered_list_handle *handle);
+
+/* Remove the first entry in the list that has the same data, using the
+ * ordered_compare_function, and return the data it points to.
+ * Will return NULL if the handle is NULL or if the list is empty or
+ * if no entry is found that equals data.
+ */
+void *ordered_list_remove_first_node_equals(ordered_list_handle *handle,
+ void *data);
+
+/* The same as the previous function, but with a specific orderedComparefunction
+ */
+void *ordered_list_remove_first_node_equals2(ordered_list_handle *handle,
+ void *data,
+ ordered_compare_function func_ptr);
+
+/* Remove the node "node_to_remove" and adjust the "prev_node" pointers
+ * accordingly, returning the data pointed to by "node_to_remove". Will return
+ * NULL if the handle is NULL or if the list is empty.
+ */
+void *ordered_list_remove_node(ordered_list_handle *handle,
+ ordered_list_node *prev_node,
+ ordered_list_node *node_to_remove);
+
+/* Remove the node "node_to_remove" by searching for it in the entire list,
+ * returning the data pointed to by "node_to_remove".
+ * Will return NULL if the handle is NULL or if the list is empty.
+ */
+void *ordered_list_remove_node2(ordered_list_handle *handle,
+ ordered_list_node *node_to_remove);
+
+#endif /* INCLUDE_PCEPUTILSORDEREDLIST_H_ */
diff --git a/pceplib/pcep_utils_queue.c b/pceplib/pcep_utils_queue.c
new file mode 100644
index 000000000..e8c3f2be0
--- /dev/null
+++ b/pceplib/pcep_utils_queue.c
@@ -0,0 +1,150 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+#include "pcep_utils_queue.h"
+
+queue_handle *queue_initialize()
+{
+ /* Set the max_entries to 0 to disable it */
+ return queue_initialize_with_size(0);
+}
+
+
+queue_handle *queue_initialize_with_size(unsigned int max_entries)
+{
+ queue_handle *handle =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(queue_handle));
+ memset(handle, 0, sizeof(queue_handle));
+ handle->max_entries = max_entries;
+
+ return handle;
+}
+
+
+void queue_destroy(queue_handle *handle)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: queue_destroy, the queue has not been initialized",
+ __func__);
+ return;
+ }
+
+ while (queue_dequeue(handle) != NULL) {
+ }
+ pceplib_free(PCEPLIB_INFRA, handle);
+}
+
+
+void queue_destroy_with_data(queue_handle *handle)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: queue_destroy_with_data, the queue has not been initialized",
+ __func__);
+ return;
+ }
+
+ void *data = queue_dequeue(handle);
+ while (data != NULL) {
+ pceplib_free(PCEPLIB_INFRA, data);
+ data = queue_dequeue(handle);
+ }
+ pceplib_free(PCEPLIB_INFRA, handle);
+}
+
+
+queue_node *queue_enqueue(queue_handle *handle, void *data)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: queue_enqueue, the queue has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->max_entries > 0
+ && handle->num_entries >= handle->max_entries) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: queue_enqueue, cannot enqueue: max entries hit [%u]",
+ handle->num_entries);
+ return NULL;
+ }
+
+ queue_node *new_node =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(queue_node));
+ memset(new_node, 0, sizeof(queue_node));
+ new_node->data = data;
+ new_node->next_node = NULL;
+
+ (handle->num_entries)++;
+ if (handle->head == NULL) {
+ /* its the first entry in the queue */
+ handle->head = handle->tail = new_node;
+ } else {
+ handle->tail->next_node = new_node;
+ handle->tail = new_node;
+ }
+
+ return new_node;
+}
+
+
+void *queue_dequeue(queue_handle *handle)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: queue_dequeue, the queue has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ void *node_data = handle->head->data;
+ queue_node *node = handle->head;
+ (handle->num_entries)--;
+ if (handle->head == handle->tail) {
+ /* its the last entry in the queue */
+ handle->head = handle->tail = NULL;
+ } else {
+ handle->head = node->next_node;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, node);
+
+ return node_data;
+}
diff --git a/pceplib/pcep_utils_queue.h b/pceplib/pcep_utils_queue.h
new file mode 100644
index 000000000..838067640
--- /dev/null
+++ b/pceplib/pcep_utils_queue.h
@@ -0,0 +1,49 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#ifndef INCLUDE_PCEPUTILSQUEUE_H_
+#define INCLUDE_PCEPUTILSQUEUE_H_
+
+typedef struct queue_node_ {
+ struct queue_node_ *next_node;
+ void *data;
+
+} queue_node;
+
+typedef struct queue_handle_ {
+ queue_node *head;
+ queue_node *tail;
+ unsigned int num_entries;
+ /* Set to 0 to disable */
+ unsigned int max_entries;
+
+} queue_handle;
+
+queue_handle *queue_initialize(void);
+queue_handle *queue_initialize_with_size(unsigned int max_entries);
+void queue_destroy(queue_handle *handle);
+void queue_destroy_with_data(queue_handle *handle);
+queue_node *queue_enqueue(queue_handle *handle, void *data);
+void *queue_dequeue(queue_handle *handle);
+
+#endif /* INCLUDE_PCEPUTILSQUEUE_H_ */
diff --git a/pceplib/subdir.am b/pceplib/subdir.am
new file mode 100644
index 000000000..eee2ec28c
--- /dev/null
+++ b/pceplib/subdir.am
@@ -0,0 +1,62 @@
+if PATHD_PCEP
+
+noinst_LTLIBRARIES = pceplib/libpcep_pcc.la pceplib/libsocket_comm_mock.la
+pceplib_libpcep_pcc_la_CFLAGS = -fPIC
+pceplib_libpcep_pcc_la_SOURCES = pceplib/pcep_msg_messages.c \
+ pceplib/pcep_msg_objects.c \
+ pceplib/pcep_msg_tlvs.c \
+ pceplib/pcep_msg_tools.c \
+ pceplib/pcep_msg_messages_encoding.c \
+ pceplib/pcep_msg_objects_encoding.c \
+ pceplib/pcep_msg_tlvs_encoding.c \
+ pceplib/pcep_msg_object_error_types.c \
+ pceplib/pcep_pcc_api.c \
+ pceplib/pcep_session_logic.c \
+ pceplib/pcep_session_logic_loop.c \
+ pceplib/pcep_session_logic_states.c \
+ pceplib/pcep_session_logic_counters.c \
+ pceplib/pcep_socket_comm_loop.c \
+ pceplib/pcep_socket_comm.c \
+ pceplib/pcep_timers_event_loop.c \
+ pceplib/pcep_timers.c \
+ pceplib/pcep_utils_counters.c \
+ pceplib/pcep_utils_double_linked_list.c \
+ pceplib/pcep_utils_logging.c \
+ pceplib/pcep_utils_memory.c \
+ pceplib/pcep_utils_ordered_list.c \
+ pceplib/pcep_utils_queue.c
+
+if PATHD_PCEP_TEST
+# SocketComm Mock library used for Unit Testing
+pceplib_libsocket_comm_mock_la_SOURCES = pceplib/pcep_socket_comm_mock.c
+endif
+
+noinst_HEADERS += pceplib/pcep.h \
+ pceplib/pcep_msg_encoding.h \
+ pceplib/pcep_msg_messages.h \
+ pceplib/pcep_msg_object_error_types.h \
+ pceplib/pcep_msg_objects.h \
+ pceplib/pcep_msg_tlvs.h \
+ pceplib/pcep_msg_tools.h \
+ pceplib/pcep_pcc_api.h \
+ pceplib/pcep_session_logic.h \
+ pceplib/pcep_session_logic_internals.h \
+ pceplib/pcep_socket_comm.h \
+ pceplib/pcep_socket_comm_internals.h \
+ pceplib/pcep_socket_comm_loop.h \
+ pceplib/pcep_socket_comm_mock.h \
+ pceplib/pcep_timer_internals.h \
+ pceplib/pcep_timers.h \
+ pceplib/pcep_timers_event_loop.h \
+ pceplib/pcep_utils_counters.h \
+ pceplib/pcep_utils_double_linked_list.h \
+ pceplib/pcep_utils_logging.h \
+ pceplib/pcep_utils_memory.h \
+ pceplib/pcep_utils_ordered_list.h \
+ pceplib/pcep_utils_queue.h
+
+noinst_PROGRAMS += pceplib/pcep_pcc
+pceplib_pcep_pcc_SOURCES = pceplib/pcep_pcc.c
+pceplib_pcep_pcc_LDADD = pceplib/libpcep_pcc.la lib/libfrr.la -lpthread
+
+endif
diff --git a/pceplib/test/pcep_msg_messages_test.c b/pceplib/test/pcep_msg_messages_test.c
new file mode 100644
index 000000000..10b678bce
--- /dev/null
+++ b/pceplib/test/pcep_msg_messages_test.c
@@ -0,0 +1,498 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_messages.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tools.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_memory.h"
+#include "pcep_msg_messages_test.h"
+
+/*
+ * Notice:
+ * All of these message Unit Tests encode the created messages by explicitly
+ * calling pcep_encode_message() thus testing the message creation and the
+ * message encoding.
+ */
+
+static struct pcep_versioning *versioning = NULL;
+
+int pcep_messages_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_messages_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+void pcep_messages_test_setup()
+{
+ versioning = create_default_pcep_versioning();
+}
+
+void pcep_messages_test_teardown()
+{
+ destroy_pcep_versioning(versioning);
+}
+
+void test_pcep_msg_create_open()
+{
+ uint8_t keepalive = 30;
+ uint8_t deadtimer = 60;
+ uint8_t sid = 255;
+
+ struct pcep_message *message =
+ pcep_msg_create_open(keepalive, deadtimer, sid);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length(PCEP_OBJ_CLASS_OPEN,
+ PCEP_OBJ_TYPE_OPEN));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_OPEN);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ /* Just check the class and type, the rest of the hdr fields
+ * are verified in pcep-objects-test.c */
+ struct pcep_object_open *open_obj =
+ (struct pcep_object_open *)message->obj_list->head->data;
+ CU_ASSERT_EQUAL(open_obj->header.object_class, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_EQUAL(open_obj->header.object_type, PCEP_OBJ_TYPE_OPEN);
+
+ CU_ASSERT_EQUAL(open_obj->open_deadtimer, deadtimer);
+ CU_ASSERT_EQUAL(open_obj->open_keepalive, keepalive);
+ CU_ASSERT_EQUAL(open_obj->open_sid, sid);
+ CU_ASSERT_EQUAL(open_obj->open_version, PCEP_OBJECT_OPEN_VERSION);
+ pcep_msg_free_message(message);
+}
+
+
+void test_pcep_msg_create_request()
+{
+ /* First test with NULL objects */
+ struct pcep_message *message =
+ pcep_msg_create_request(NULL, NULL, NULL);
+ CU_ASSERT_PTR_NULL(message);
+
+ /* Test IPv4 */
+ struct pcep_object_rp *rp_obj =
+ pcep_obj_create_rp(0, false, false, false, false, 10, NULL);
+ struct in_addr src_addr, dst_addr;
+ struct pcep_object_endpoints_ipv4 *ipv4_obj =
+ pcep_obj_create_endpoint_ipv4(&src_addr, &dst_addr);
+ message = pcep_msg_create_request(rp_obj, ipv4_obj, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 2);
+ CU_ASSERT_EQUAL(
+ message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length_by_hdr(&rp_obj->header)
+ + pcep_object_get_length_by_hdr(&ipv4_obj->header));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREQ);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+
+ /* Test IPv6 */
+ rp_obj = pcep_obj_create_rp(0, false, false, false, false, 10, NULL);
+ struct in6_addr src_addr_ipv6, dst_addr_ipv6;
+ struct pcep_object_endpoints_ipv6 *ipv6_obj =
+ pcep_obj_create_endpoint_ipv6(&src_addr_ipv6, &dst_addr_ipv6);
+ message = pcep_msg_create_request_ipv6(rp_obj, ipv6_obj, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 2);
+ CU_ASSERT_EQUAL(
+ message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length_by_hdr(&rp_obj->header)
+ + pcep_object_get_length_by_hdr(&ipv6_obj->header));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREQ);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+
+ /* The objects get deleted with the message, so they need to be created
+ * again */
+ rp_obj = pcep_obj_create_rp(0, false, false, false, false, 10, NULL);
+ ipv4_obj = pcep_obj_create_endpoint_ipv4(&src_addr, &dst_addr);
+ struct pcep_object_bandwidth *bandwidth_obj =
+ pcep_obj_create_bandwidth(4.2);
+ double_linked_list *obj_list = dll_initialize();
+ dll_append(obj_list, bandwidth_obj);
+ message = pcep_msg_create_request(rp_obj, ipv4_obj, obj_list);
+
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 3);
+ CU_ASSERT_EQUAL(
+ message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length_by_hdr(&rp_obj->header)
+ + pcep_object_get_length_by_hdr(&ipv4_obj->header)
+ + pcep_object_get_length_by_hdr(
+ &bandwidth_obj->header));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREQ);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+}
+
+
+void test_pcep_msg_create_request_svec()
+{
+}
+
+
+void test_pcep_msg_create_reply_nopath()
+{
+ struct pcep_object_rp *rp_obj =
+ pcep_obj_create_rp(0, false, false, false, false, 10, NULL);
+ struct pcep_object_nopath *nopath_obj = pcep_obj_create_nopath(
+ false, false, PCEP_NOPATH_TLV_ERR_NO_TLV);
+ double_linked_list *obj_list = dll_initialize();
+ dll_append(obj_list, nopath_obj);
+
+ struct pcep_message *message = pcep_msg_create_reply(rp_obj, obj_list);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 2);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ (MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length_by_hdr(&rp_obj->header)
+ + pcep_object_get_length_by_hdr(&nopath_obj->header)));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREP);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+}
+
+
+void test_pcep_msg_create_reply()
+{
+ /* First test with NULL ero and rp objects */
+ struct pcep_message *message = pcep_msg_create_reply(NULL, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 0);
+ CU_ASSERT_EQUAL(message->encoded_message_length, MESSAGE_HEADER_LENGTH);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREP);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+
+ double_linked_list *ero_subobj_list = dll_initialize();
+ struct pcep_object_ro_subobj *ero_subobj =
+ (struct pcep_object_ro_subobj *)
+ pcep_obj_create_ro_subobj_32label(true, 1, 10);
+ dll_append(ero_subobj_list, ero_subobj);
+ struct pcep_object_ro *ero = pcep_obj_create_ero(ero_subobj_list);
+
+ double_linked_list *object_list = dll_initialize();
+ dll_append(object_list, ero);
+ struct pcep_object_rp *rp_obj =
+ pcep_obj_create_rp(0, false, false, false, false, 10, NULL);
+ message = pcep_msg_create_reply(rp_obj, object_list);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 2);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length_by_hdr(&rp_obj->header)
+ + OBJECT_HEADER_LENGTH
+ + OBJECT_RO_SUBOBJ_HEADER_LENGTH
+ + 6 /* size of the 32label */);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREP);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+}
+
+
+void test_pcep_msg_create_close()
+{
+ uint8_t reason = PCEP_CLOSE_REASON_UNREC_MSG;
+
+ struct pcep_message *message = pcep_msg_create_close(reason);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length(PCEP_OBJ_CLASS_CLOSE,
+ PCEP_OBJ_TYPE_CLOSE));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_CLOSE);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ /* Just check the class and type, the rest of the hdr fields
+ * are verified in pcep-objects-test.c */
+ struct pcep_object_close *close_obj =
+ (struct pcep_object_close *)message->obj_list->head->data;
+ CU_ASSERT_EQUAL(close_obj->header.object_class, PCEP_OBJ_CLASS_CLOSE);
+ CU_ASSERT_EQUAL(close_obj->header.object_type, PCEP_OBJ_TYPE_CLOSE);
+ CU_ASSERT_EQUAL(close_obj->reason, reason);
+ pcep_msg_free_message(message);
+}
+
+
+void test_pcep_msg_create_error()
+{
+ uint8_t error_type = PCEP_ERRT_RECEPTION_OF_INV_OBJECT;
+ uint8_t error_value = PCEP_ERRV_KEEPALIVEWAIT_TIMED_OUT;
+
+ struct pcep_message *message =
+ pcep_msg_create_error(error_type, error_value);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length(PCEP_OBJ_CLASS_ERROR,
+ PCEP_OBJ_TYPE_ERROR));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_ERROR);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ /* Just check the class and type, the rest of the hdr fields
+ * are verified in pcep-objects-test.c */
+ struct pcep_object_error *error_obj =
+ (struct pcep_object_error *)message->obj_list->head->data;
+ CU_ASSERT_EQUAL(error_obj->header.object_class, PCEP_OBJ_CLASS_ERROR);
+ CU_ASSERT_EQUAL(error_obj->header.object_type, PCEP_OBJ_TYPE_ERROR);
+
+ CU_ASSERT_EQUAL(error_obj->error_type, error_type);
+ CU_ASSERT_EQUAL(error_obj->error_value, error_value);
+ pcep_msg_free_message(message);
+}
+
+
+void test_pcep_msg_create_keepalive()
+{
+ struct pcep_message *message = pcep_msg_create_keepalive();
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 0);
+ CU_ASSERT_EQUAL(message->encoded_message_length, MESSAGE_HEADER_LENGTH);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_KEEPALIVE);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+}
+
+void test_pcep_msg_create_report()
+{
+ double_linked_list *obj_list = dll_initialize();
+
+ /* Should return NULL if obj_list is empty */
+ struct pcep_message *message = pcep_msg_create_report(NULL);
+ CU_ASSERT_PTR_NULL(message);
+
+ struct pcep_object_lsp *lsp =
+ pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true,
+ true, true, true, NULL);
+ dll_append(obj_list, lsp);
+ message = pcep_msg_create_report(obj_list);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + lsp->header.encoded_object_length);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_REPORT);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ pcep_msg_free_message(message);
+}
+
+void test_pcep_msg_create_update()
+{
+ double_linked_list *obj_list = dll_initialize();
+ double_linked_list *ero_subobj_list = dll_initialize();
+
+ struct pcep_message *message = pcep_msg_create_update(NULL);
+ CU_ASSERT_PTR_NULL(message);
+
+ /* Should return NULL if obj_list is empty */
+ message = pcep_msg_create_update(obj_list);
+ CU_ASSERT_PTR_NULL(message);
+
+ struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL);
+ struct pcep_object_lsp *lsp =
+ pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true,
+ true, true, true, NULL);
+ dll_append(ero_subobj_list, pcep_obj_create_ro_subobj_asn(0x0102));
+ struct pcep_object_ro *ero = pcep_obj_create_ero(ero_subobj_list);
+
+ /* Should return NULL if obj_list does not have 3 entries */
+ dll_append(obj_list, srp);
+ dll_append(obj_list, lsp);
+ message = pcep_msg_create_update(obj_list);
+ CU_ASSERT_PTR_NULL(message);
+
+ dll_append(obj_list, ero);
+ message = pcep_msg_create_update(obj_list);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 3);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + srp->header.encoded_object_length
+ + lsp->header.encoded_object_length
+ + ero->header.encoded_object_length);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_UPDATE);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ pcep_msg_free_message(message);
+}
+
+void test_pcep_msg_create_initiate()
+{
+ double_linked_list *obj_list = dll_initialize();
+ double_linked_list *ero_subobj_list = dll_initialize();
+
+ /* Should return NULL if obj_list is empty */
+ struct pcep_message *message = pcep_msg_create_initiate(NULL);
+ CU_ASSERT_PTR_NULL(message);
+
+ struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL);
+ struct pcep_object_lsp *lsp =
+ pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true,
+ true, true, true, NULL);
+ dll_append(ero_subobj_list, pcep_obj_create_ro_subobj_asn(0x0102));
+ struct pcep_object_ro *ero = pcep_obj_create_ero(ero_subobj_list);
+
+ /* Should return NULL if obj_list does not have 2 entries */
+ dll_append(obj_list, srp);
+ message = pcep_msg_create_initiate(obj_list);
+ CU_ASSERT_PTR_NULL(message);
+
+ dll_append(obj_list, lsp);
+ dll_append(obj_list, ero);
+ message = pcep_msg_create_initiate(obj_list);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 3);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + srp->header.encoded_object_length
+ + lsp->header.encoded_object_length
+ + ero->header.encoded_object_length);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_INITIATE);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ pcep_msg_free_message(message);
+}
+
+void test_pcep_msg_create_notify(void)
+{
+ struct pcep_object_notify *notify_obj = pcep_obj_create_notify(
+ PCEP_NOTIFY_TYPE_PENDING_REQUEST_CANCELLED,
+ PCEP_NOTIFY_VALUE_PCC_CANCELLED_REQUEST);
+
+ /* Should return NULL if the notify obj is empty */
+ struct pcep_message *message = pcep_msg_create_notify(NULL, NULL);
+ CU_ASSERT_PTR_NULL(message);
+
+ message = pcep_msg_create_notify(notify_obj, NULL);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + notify_obj->header.encoded_object_length);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCNOTF);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ pcep_msg_free_message(message);
+
+ struct pcep_object_rp *rp_obj =
+ pcep_obj_create_rp(0, false, false, false, false, 10, NULL);
+ double_linked_list *obj_list = dll_initialize();
+ dll_append(obj_list, rp_obj);
+ notify_obj = pcep_obj_create_notify(
+ PCEP_NOTIFY_TYPE_PENDING_REQUEST_CANCELLED,
+ PCEP_NOTIFY_VALUE_PCC_CANCELLED_REQUEST);
+
+ message = pcep_msg_create_notify(notify_obj, obj_list);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 2);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + notify_obj->header.encoded_object_length
+ + rp_obj->header.encoded_object_length);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCNOTF);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ pcep_msg_free_message(message);
+}
diff --git a/pceplib/test/pcep_msg_messages_test.h b/pceplib/test/pcep_msg_messages_test.h
new file mode 100644
index 000000000..a3295c74e
--- /dev/null
+++ b/pceplib/test/pcep_msg_messages_test.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_MSG_MSG_TEST_H_
+#define PCEP_MSG_MSG_TEST_H_
+
+/* functions to be tested from pcep-messages.c */
+int pcep_messages_test_suite_setup(void);
+int pcep_messages_test_suite_teardown(void);
+void pcep_messages_test_setup(void);
+void pcep_messages_test_teardown(void);
+void test_pcep_msg_create_open(void);
+void test_pcep_msg_create_request(void);
+void test_pcep_msg_create_request_svec(void);
+void test_pcep_msg_create_reply_nopath(void);
+void test_pcep_msg_create_reply(void);
+void test_pcep_msg_create_close(void);
+void test_pcep_msg_create_error(void);
+void test_pcep_msg_create_keepalive(void);
+void test_pcep_msg_create_report(void);
+void test_pcep_msg_create_update(void);
+void test_pcep_msg_create_initiate(void);
+void test_pcep_msg_create_notify(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_msg_messages_tests.c b/pceplib/test/pcep_msg_messages_tests.c
new file mode 100644
index 000000000..bd85a1653
--- /dev/null
+++ b/pceplib/test/pcep_msg_messages_tests.c
@@ -0,0 +1,256 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/Basic.h>
+#include <CUnit/CUnit.h>
+#include <CUnit/TestDB.h>
+
+#include "pcep_msg_messages_test.h"
+#include "pcep_msg_tools_test.h"
+#include "pcep_msg_object_error_types.h"
+#include "pcep_msg_object_error_types_test.h"
+#include "pcep_msg_tlvs_test.h"
+#include "pcep_msg_objects_test.h"
+
+
+int main(int argc, char **argv)
+{
+ /* Unused parameters cause compilation warnings */
+ (void)argc;
+ (void)argv;
+
+ CU_initialize_registry();
+
+ CU_pSuite messages_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP Messages Test Suite", pcep_messages_test_suite_setup,
+ pcep_messages_test_suite_teardown, /* suite setup and cleanup
+ function pointers */
+ pcep_messages_test_setup, pcep_messages_test_teardown);
+ CU_add_test(messages_suite, "test_pcep_msg_create_open",
+ test_pcep_msg_create_open);
+ CU_add_test(messages_suite, "test_pcep_msg_create_request",
+ test_pcep_msg_create_request);
+ CU_add_test(messages_suite, "test_pcep_msg_create_request_svec",
+ test_pcep_msg_create_request_svec);
+ CU_add_test(messages_suite, "test_pcep_msg_create_reply_nopath",
+ test_pcep_msg_create_reply_nopath);
+ CU_add_test(messages_suite, "test_pcep_msg_create_reply",
+ test_pcep_msg_create_reply);
+ CU_add_test(messages_suite, "test_pcep_msg_create_close",
+ test_pcep_msg_create_close);
+ CU_add_test(messages_suite, "test_pcep_msg_create_error",
+ test_pcep_msg_create_error);
+ CU_add_test(messages_suite, "test_pcep_msg_create_keepalive",
+ test_pcep_msg_create_keepalive);
+ CU_add_test(messages_suite, "test_pcep_msg_create_report",
+ test_pcep_msg_create_report);
+ CU_add_test(messages_suite, "test_pcep_msg_create_update",
+ test_pcep_msg_create_update);
+ CU_add_test(messages_suite, "test_pcep_msg_create_initiate",
+ test_pcep_msg_create_initiate);
+ CU_add_test(messages_suite, "test_pcep_msg_create_notify",
+ test_pcep_msg_create_notify);
+
+ CU_pSuite tlvs_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP TLVs Test Suite", pcep_tlvs_test_suite_setup,
+ pcep_tlvs_test_suite_teardown, /* suite setup and cleanup
+ function pointers */
+ pcep_tlvs_test_setup, pcep_tlvs_test_teardown);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_stateful_pce_capability",
+ test_pcep_tlv_create_stateful_pce_capability);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_speaker_entity_id",
+ test_pcep_tlv_create_speaker_entity_id);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_lsp_db_version",
+ test_pcep_tlv_create_lsp_db_version);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_path_setup_type",
+ test_pcep_tlv_create_path_setup_type);
+ CU_add_test(tlvs_suite,
+ "test_pcep_tlv_create_path_setup_type_capability",
+ test_pcep_tlv_create_path_setup_type_capability);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_sr_pce_capability",
+ test_pcep_tlv_create_sr_pce_capability);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_symbolic_path_name",
+ test_pcep_tlv_create_symbolic_path_name);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_ipv4_lsp_identifiers",
+ test_pcep_tlv_create_ipv4_lsp_identifiers);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_ipv6_lsp_identifiers",
+ test_pcep_tlv_create_ipv6_lsp_identifiers);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_pol_id_ipv4",
+ test_pcep_tlv_create_srpag_pol_id_ipv4);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_pol_id_ipv6",
+ test_pcep_tlv_create_srpag_pol_id_ipv6);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_pol_name",
+ test_pcep_tlv_create_srpag_pol_name);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_cp_id",
+ test_pcep_tlv_create_srpag_cp_id);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_cp_pref",
+ test_pcep_tlv_create_srpag_cp_pref);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_lsp_error_code",
+ test_pcep_tlv_create_lsp_error_code);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_rsvp_ipv4_error_spec",
+ test_pcep_tlv_create_rsvp_ipv4_error_spec);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_rsvp_ipv6_error_spec",
+ test_pcep_tlv_create_rsvp_ipv6_error_spec);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_nopath_vector",
+ test_pcep_tlv_create_nopath_vector);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_arbitrary",
+ test_pcep_tlv_create_arbitrary);
+
+ CU_pSuite objects_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP Objects Test Suite", pcep_objects_test_suite_setup,
+ pcep_objects_test_suite_teardown, /* suite setup and cleanup
+ function pointers */
+ pcep_objects_test_setup, pcep_objects_test_teardown);
+ CU_add_test(objects_suite, "test_pcep_obj_create_open",
+ test_pcep_obj_create_open);
+ CU_add_test(objects_suite, "test_pcep_obj_create_open",
+ test_pcep_obj_create_open_with_tlvs);
+ CU_add_test(objects_suite, "test_pcep_obj_create_rp",
+ test_pcep_obj_create_rp);
+ CU_add_test(objects_suite, "test_pcep_obj_create_nopath",
+ test_pcep_obj_create_nopath);
+ CU_add_test(objects_suite, "test_pcep_obj_create_enpoint_ipv4",
+ test_pcep_obj_create_endpoint_ipv4);
+ CU_add_test(objects_suite, "test_pcep_obj_create_enpoint_ipv6",
+ test_pcep_obj_create_endpoint_ipv6);
+ CU_add_test(objects_suite, "test_pcep_obj_create_association_ipv4",
+ test_pcep_obj_create_association_ipv4);
+ CU_add_test(objects_suite, "test_pcep_obj_create_association_ipv6",
+ test_pcep_obj_create_association_ipv6);
+ CU_add_test(objects_suite, "test_pcep_obj_create_bandwidth",
+ test_pcep_obj_create_bandwidth);
+ CU_add_test(objects_suite, "test_pcep_obj_create_metric",
+ test_pcep_obj_create_metric);
+ CU_add_test(objects_suite, "test_pcep_obj_create_lspa",
+ test_pcep_obj_create_lspa);
+ CU_add_test(objects_suite, "test_pcep_obj_create_svec",
+ test_pcep_obj_create_svec);
+ CU_add_test(objects_suite, "test_pcep_obj_create_error",
+ test_pcep_obj_create_error);
+ CU_add_test(objects_suite, "test_pcep_obj_create_close",
+ test_pcep_obj_create_close);
+ CU_add_test(objects_suite, "test_pcep_obj_create_srp",
+ test_pcep_obj_create_srp);
+ CU_add_test(objects_suite, "test_pcep_obj_create_lsp",
+ test_pcep_obj_create_lsp);
+ CU_add_test(objects_suite, "test_pcep_obj_create_vendor_info",
+ test_pcep_obj_create_vendor_info);
+
+ CU_add_test(objects_suite, "test_pcep_obj_create_ero",
+ test_pcep_obj_create_ero);
+ CU_add_test(objects_suite, "test_pcep_obj_create_rro",
+ test_pcep_obj_create_rro);
+ CU_add_test(objects_suite, "test_pcep_obj_create_iro",
+ test_pcep_obj_create_iro);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_ipv4",
+ test_pcep_obj_create_ro_subobj_ipv4);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_ipv6",
+ test_pcep_obj_create_ro_subobj_ipv6);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_unnum",
+ test_pcep_obj_create_ro_subobj_unnum);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_32label",
+ test_pcep_obj_create_ro_subobj_32label);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_asn",
+ test_pcep_obj_create_ro_subobj_asn);
+
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_sr_nonai",
+ test_pcep_obj_create_ro_subobj_sr_nonai);
+ CU_add_test(objects_suite,
+ "test_pcep_obj_create_ro_subobj_sr_ipv4_node",
+ test_pcep_obj_create_ro_subobj_sr_ipv4_node);
+ CU_add_test(objects_suite,
+ "test_pcep_obj_create_ro_subobj_sr_ipv6_node",
+ test_pcep_obj_create_ro_subobj_sr_ipv6_node);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_sr_ipv4_adj",
+ test_pcep_obj_create_ro_subobj_sr_ipv4_adj);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_sr_ipv6_adj",
+ test_pcep_obj_create_ro_subobj_sr_ipv6_adj);
+ CU_add_test(objects_suite,
+ "test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj",
+ test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj);
+ CU_add_test(objects_suite,
+ "test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj",
+ test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj);
+
+ CU_pSuite tools_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP Tools Test Suite", pcep_tools_test_suite_setup,
+ pcep_tools_test_suite_teardown, pcep_tools_test_setup,
+ pcep_tools_test_teardown);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_initiate",
+ test_pcep_msg_read_pcep_initiate);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_initiate2",
+ test_pcep_msg_read_pcep_initiate2);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_update",
+ test_pcep_msg_read_pcep_update);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_open",
+ test_pcep_msg_read_pcep_open);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_open_initiate",
+ test_pcep_msg_read_pcep_open_initiate);
+ CU_add_test(tools_suite, "test_validate_message_header",
+ test_validate_message_header);
+ CU_add_test(tools_suite, "test_validate_message_objects",
+ test_validate_message_objects);
+ CU_add_test(tools_suite, "test_validate_message_objects_invalid",
+ test_validate_message_objects_invalid);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_open_cisco_pce",
+ test_pcep_msg_read_pcep_open_cisco_pce);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_update_cisco_pce",
+ test_pcep_msg_read_pcep_update_cisco_pce);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_report_cisco_pcc",
+ test_pcep_msg_read_pcep_report_cisco_pcc);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_initiate_cisco_pcc",
+ test_pcep_msg_read_pcep_initiate_cisco_pcc);
+
+ CU_pSuite obj_errors_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP Object Error Types Test Suite",
+ pcep_object_error_types_test_suite_setup,
+ pcep_object_error_types_test_suite_teardown,
+ pcep_object_error_types_test_setup,
+ pcep_object_error_types_test_teardown);
+ CU_add_test(obj_errors_suite, "test_get_error_type_str",
+ test_get_error_type_str);
+ CU_add_test(obj_errors_suite, "test_get_error_value_str",
+ test_get_error_value_str);
+
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_FailureRecord *failure_record = CU_get_failure_list();
+ if (failure_record != NULL) {
+ printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n");
+ do {
+ printf("\t [%s] [%s] [%s:%d]\n",
+ failure_record->pSuite->pName,
+ failure_record->pTest->pName,
+ failure_record->strFileName,
+ failure_record->uiLineNumber);
+ failure_record = failure_record->pNext;
+
+ } while (failure_record != NULL);
+ }
+
+ CU_pRunSummary run_summary = CU_get_run_summary();
+ int result = run_summary->nTestsFailed;
+ CU_cleanup_registry();
+
+ return result;
+}
diff --git a/pceplib/test/pcep_msg_object_error_types_test.c b/pceplib/test/pcep_msg_object_error_types_test.c
new file mode 100644
index 000000000..7275eaf09
--- /dev/null
+++ b/pceplib/test/pcep_msg_object_error_types_test.c
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdio.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_msg_object_error_types.h"
+#include "pcep_msg_object_error_types_test.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+int pcep_object_error_types_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ set_logging_level(LOG_DEBUG);
+ return 0;
+}
+
+int pcep_object_error_types_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+void pcep_object_error_types_test_setup(void)
+{
+}
+
+void pcep_object_error_types_test_teardown(void)
+{
+}
+
+void test_get_error_type_str()
+{
+ const char *error_type_str;
+ int i = 0;
+ for (; i < MAX_ERROR_TYPE; i++) {
+ error_type_str = get_error_type_str(i);
+ CU_ASSERT_PTR_NOT_NULL(error_type_str);
+ }
+
+ CU_ASSERT_PTR_NULL(get_error_type_str(-1));
+ CU_ASSERT_PTR_NULL(get_error_type_str(MAX_ERROR_TYPE));
+}
+
+void test_get_error_value_str()
+{
+ const char *error_value_str;
+ int i = 0, j = 0;
+
+ for (; i < MAX_ERROR_TYPE; i++) {
+ for (; j < MAX_ERROR_VALUE; j++) {
+ error_value_str = get_error_value_str(i, j);
+ CU_ASSERT_PTR_NOT_NULL(error_value_str);
+ }
+ }
+
+ CU_ASSERT_PTR_NULL(get_error_value_str(-1, 0));
+ CU_ASSERT_PTR_NULL(get_error_value_str(MAX_ERROR_TYPE, 0));
+ CU_ASSERT_PTR_NULL(get_error_value_str(1, -1));
+ CU_ASSERT_PTR_NULL(get_error_value_str(1, MAX_ERROR_VALUE));
+}
diff --git a/pceplib/test/pcep_msg_object_error_types_test.h b/pceplib/test/pcep_msg_object_error_types_test.h
new file mode 100644
index 000000000..863517d1e
--- /dev/null
+++ b/pceplib/test/pcep_msg_object_error_types_test.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_MSG_OBJECT_ERROR_TYPES_TEST_
+#define PCEP_MSG_OBJECT_ERROR_TYPES_TEST_
+
+int pcep_object_error_types_test_suite_setup(void);
+int pcep_object_error_types_test_suite_teardown(void);
+void pcep_object_error_types_test_setup(void);
+void pcep_object_error_types_test_teardown(void);
+void test_get_error_type_str(void);
+void test_get_error_value_str(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_msg_objects_test.c b/pceplib/test/pcep_msg_objects_test.c
new file mode 100644
index 000000000..a4c069945
--- /dev/null
+++ b/pceplib/test/pcep_msg_objects_test.c
@@ -0,0 +1,1289 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tools.h"
+#include "pcep_utils_memory.h"
+#include "pcep_msg_objects_test.h"
+
+/*
+ * Notice:
+ * All of these object Unit Tests encode the created objects by explicitly
+ * calling pcep_encode_object() thus testing the object creation and the object
+ * encoding. All APIs expect IPs to be in network byte order.
+ */
+
+static struct pcep_versioning *versioning = NULL;
+static uint8_t object_buf[2000];
+
+void reset_objects_buffer(void);
+
+int pcep_objects_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_objects_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+void reset_objects_buffer()
+{
+ memset(object_buf, 0, 2000);
+}
+
+void pcep_objects_test_setup()
+{
+ versioning = create_default_pcep_versioning();
+ reset_objects_buffer();
+}
+
+void pcep_objects_test_teardown()
+{
+ destroy_pcep_versioning(versioning);
+}
+
+/* Internal util verification function */
+static void verify_pcep_obj_header2(uint8_t obj_class, uint8_t obj_type,
+ uint16_t obj_length, const uint8_t *obj_buf)
+{
+ /* Object Header
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Object-Class | OT |Res|P|I| Object Length (bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ /* Not using CU_ASSERT_EQUAL here, so that in case of failure,
+ * we can provide more info in the error message. */
+ if (obj_buf[0] != obj_class) {
+ fprintf(stderr,
+ "Test failure obj_class expected [%d] found [%d]\n",
+ obj_class, obj_buf[0]);
+ CU_FAIL("Object Header Class");
+ }
+
+ uint8_t found8 = (obj_buf[1] >> 4) & 0x0f;
+ if (obj_type != found8) {
+ fprintf(stderr,
+ "Test failure obj_class [%d] obj_type expected [%d] found [%d]\n",
+ obj_class, obj_type, found8);
+ CU_FAIL("Object Header Type");
+ }
+
+ uint8_t exp8 = 0;
+ found8 = obj_buf[1] & 0x0f;
+ if (exp8 != found8) {
+ fprintf(stderr,
+ "Test failure obj_class [%d] flags expected [%d] found [%d]\n",
+ obj_class, exp8, found8);
+ CU_FAIL("Object Header Flags");
+ }
+
+ uint16_t found16 = ntohs(*((uint16_t *)(obj_buf + 2)));
+ if (obj_length != found16) {
+ fprintf(stderr,
+ "Test failure obj_class [%d] obj_length expected [%d] found [%d]\n",
+ obj_class, obj_length, found16);
+ CU_FAIL("Object Header Length");
+ }
+}
+
+/* Internal util verification function */
+static void verify_pcep_obj_header(uint8_t obj_class, uint8_t obj_type,
+ struct pcep_object_header *obj_hdr)
+{
+ verify_pcep_obj_header2(obj_class, obj_type,
+ pcep_object_get_length_by_hdr(obj_hdr),
+ obj_hdr->encoded_object);
+}
+
+void test_pcep_obj_create_open()
+{
+ uint8_t deadtimer = 60;
+ uint8_t keepalive = 30;
+ uint8_t sid = 1;
+
+ struct pcep_object_open *open =
+ pcep_obj_create_open(keepalive, deadtimer, sid, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL(open);
+ pcep_encode_object(&open->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_OPEN, PCEP_OBJ_TYPE_OPEN,
+ &open->header);
+
+ CU_ASSERT_EQUAL(open->header.encoded_object[4],
+ (PCEP_OBJECT_OPEN_VERSION << 5) & 0xe0);
+ CU_ASSERT_EQUAL(open->header.encoded_object[4] & 0x1f, 0);
+ CU_ASSERT_EQUAL(open->header.encoded_object[5], keepalive);
+ CU_ASSERT_EQUAL(open->header.encoded_object[6], deadtimer);
+ CU_ASSERT_EQUAL(open->header.encoded_object[7], sid);
+
+ pcep_obj_free_object((struct pcep_object_header *)open);
+}
+
+void test_pcep_obj_create_open_with_tlvs()
+{
+ uint8_t deadtimer = 60;
+ uint8_t keepalive = 30;
+ uint8_t sid = 1;
+ double_linked_list *tlv_list = dll_initialize();
+
+ struct pcep_object_tlv_stateful_pce_capability *tlv =
+ pcep_tlv_create_stateful_pce_capability(true, true, true, true,
+ true, true);
+ dll_append(tlv_list, tlv);
+ struct pcep_object_open *open =
+ pcep_obj_create_open(keepalive, deadtimer, sid, tlv_list);
+
+ CU_ASSERT_PTR_NOT_NULL(open);
+ pcep_encode_object(&open->header, versioning, object_buf);
+ verify_pcep_obj_header2(PCEP_OBJ_CLASS_OPEN, PCEP_OBJ_TYPE_OPEN,
+ pcep_object_get_length_by_hdr(&open->header)
+ + sizeof(uint32_t) * 2,
+ open->header.encoded_object);
+ CU_ASSERT_PTR_NOT_NULL(open->header.tlv_list);
+ CU_ASSERT_EQUAL(open->header.tlv_list->num_entries, 1);
+
+ CU_ASSERT_EQUAL(open->header.encoded_object[4],
+ (PCEP_OBJECT_OPEN_VERSION << 5) & 0xe0);
+ CU_ASSERT_EQUAL(open->header.encoded_object[4] & 0x1f, 0);
+ CU_ASSERT_EQUAL(open->header.encoded_object[5], keepalive);
+ CU_ASSERT_EQUAL(open->header.encoded_object[6], deadtimer);
+ CU_ASSERT_EQUAL(open->header.encoded_object[7], sid);
+
+ pcep_obj_free_object((struct pcep_object_header *)open);
+}
+
+void test_pcep_obj_create_rp()
+{
+ uint32_t reqid = 15;
+ uint8_t invalid_priority = 100;
+ uint8_t priority = 7;
+
+ struct pcep_object_rp *rp = pcep_obj_create_rp(
+ invalid_priority, true, false, false, true, reqid, NULL);
+ CU_ASSERT_PTR_NULL(rp);
+
+ rp = pcep_obj_create_rp(priority, true, false, false, true, reqid,
+ NULL);
+ CU_ASSERT_PTR_NOT_NULL(rp);
+ pcep_encode_object(&rp->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_RP, PCEP_OBJ_TYPE_RP,
+ &rp->header);
+
+ CU_ASSERT_EQUAL(rp->header.encoded_object[4], 0);
+ CU_ASSERT_EQUAL(rp->header.encoded_object[5], 0);
+ CU_ASSERT_EQUAL(rp->header.encoded_object[6], 0);
+ CU_ASSERT_EQUAL((rp->header.encoded_object[7] & 0x07), priority);
+ CU_ASSERT_TRUE(rp->header.encoded_object[7] & OBJECT_RP_FLAG_R);
+ CU_ASSERT_TRUE(rp->header.encoded_object[7] & OBJECT_RP_FLAG_OF);
+ CU_ASSERT_TRUE(rp->header.encoded_object[7] & ~OBJECT_RP_FLAG_B);
+ CU_ASSERT_TRUE(rp->header.encoded_object[7] & ~OBJECT_RP_FLAG_O);
+ CU_ASSERT_EQUAL(*((uint32_t *)(rp->header.encoded_object + 8)),
+ htonl(reqid));
+
+ pcep_obj_free_object((struct pcep_object_header *)rp);
+}
+
+void test_pcep_obj_create_nopath()
+{
+ uint8_t ni = 8;
+ uint32_t errorcode = 42;
+
+ struct pcep_object_nopath *nopath =
+ pcep_obj_create_nopath(ni, true, errorcode);
+
+ CU_ASSERT_PTR_NOT_NULL(nopath);
+ pcep_encode_object(&nopath->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_NOPATH, PCEP_OBJ_TYPE_NOPATH,
+ &nopath->header);
+
+ CU_ASSERT_EQUAL(nopath->header.encoded_object[4], ni);
+ CU_ASSERT_TRUE(nopath->header.encoded_object[5] & OBJECT_NOPATH_FLAG_C);
+ CU_ASSERT_EQUAL(nopath->header.encoded_object[6], 0);
+ CU_ASSERT_EQUAL(nopath->header.encoded_object[7], 0);
+
+ /* Verify the TLV */
+ CU_ASSERT_PTR_NOT_NULL(nopath->header.tlv_list);
+ struct pcep_object_tlv_nopath_vector *tlv =
+ (struct pcep_object_tlv_nopath_vector *)
+ nopath->header.tlv_list->head->data;
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 4);
+ CU_ASSERT_EQUAL(tlv->header.type, 1);
+ CU_ASSERT_EQUAL(tlv->error_code, errorcode);
+
+ CU_ASSERT_EQUAL(*((uint16_t *)(nopath->header.encoded_object + 8)),
+ htons(PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR));
+ CU_ASSERT_EQUAL(*((uint16_t *)(nopath->header.encoded_object + 10)),
+ htons(4));
+ CU_ASSERT_EQUAL(*((uint32_t *)(nopath->header.encoded_object + 12)),
+ htonl(errorcode));
+
+ pcep_obj_free_object((struct pcep_object_header *)nopath);
+}
+void test_pcep_obj_create_association_ipv4()
+{
+
+ uint16_t all_assoc_groups = 0xffff;
+ struct in_addr src;
+ inet_pton(AF_INET, "192.168.1.2", &src);
+
+ struct pcep_object_association_ipv4 *assoc =
+ pcep_obj_create_association_ipv4(
+ false, PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE,
+ all_assoc_groups, src);
+ CU_ASSERT_PTR_NOT_NULL(assoc);
+ CU_ASSERT_EQUAL(assoc->association_type,
+ PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE);
+ CU_ASSERT_EQUAL(assoc->association_id, all_assoc_groups);
+ CU_ASSERT_EQUAL(assoc->header.object_class, PCEP_OBJ_CLASS_ASSOCIATION);
+ CU_ASSERT_EQUAL(assoc->header.object_type,
+ PCEP_OBJ_TYPE_ASSOCIATION_IPV4);
+ CU_ASSERT_EQUAL(assoc->src.s_addr, src.s_addr);
+
+ pcep_obj_free_object((struct pcep_object_header *)assoc);
+}
+
+void test_pcep_obj_create_association_ipv6()
+{
+ uint32_t all_assoc_groups = 0xffff;
+ struct in6_addr src;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &src);
+
+ struct pcep_object_association_ipv6 *assoc =
+ pcep_obj_create_association_ipv6(
+ false, PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE,
+ all_assoc_groups, src);
+ CU_ASSERT_PTR_NOT_NULL(assoc);
+ CU_ASSERT_EQUAL(assoc->association_type,
+ PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE);
+ CU_ASSERT_EQUAL(assoc->association_id, all_assoc_groups);
+ CU_ASSERT_EQUAL(assoc->header.object_class, PCEP_OBJ_CLASS_ASSOCIATION);
+ CU_ASSERT_EQUAL(assoc->header.object_type,
+ PCEP_OBJ_TYPE_ASSOCIATION_IPV6);
+ CU_ASSERT_EQUAL(assoc->src.__in6_u.__u6_addr32[0],
+ (src.__in6_u.__u6_addr32[0]));
+ CU_ASSERT_EQUAL(assoc->src.__in6_u.__u6_addr32[1],
+ (src.__in6_u.__u6_addr32[1]));
+ CU_ASSERT_EQUAL(assoc->src.__in6_u.__u6_addr32[2],
+ (src.__in6_u.__u6_addr32[2]));
+ CU_ASSERT_EQUAL(assoc->src.__in6_u.__u6_addr32[3],
+ (src.__in6_u.__u6_addr32[3]));
+
+ pcep_obj_free_object((struct pcep_object_header *)assoc);
+}
+
+void test_pcep_obj_create_endpoint_ipv4()
+{
+ struct in_addr src_ipv4, dst_ipv4;
+ inet_pton(AF_INET, "192.168.1.2", &src_ipv4);
+ inet_pton(AF_INET, "172.168.1.2", &dst_ipv4);
+
+ struct pcep_object_endpoints_ipv4 *ipv4 =
+ pcep_obj_create_endpoint_ipv4(NULL, NULL);
+ CU_ASSERT_PTR_NULL(ipv4);
+
+ ipv4 = pcep_obj_create_endpoint_ipv4(&src_ipv4, NULL);
+ CU_ASSERT_PTR_NULL(ipv4);
+
+ ipv4 = pcep_obj_create_endpoint_ipv4(NULL, &dst_ipv4);
+ CU_ASSERT_PTR_NULL(ipv4);
+
+ ipv4 = pcep_obj_create_endpoint_ipv4(&src_ipv4, &dst_ipv4);
+ CU_ASSERT_PTR_NOT_NULL(ipv4);
+ pcep_encode_object(&ipv4->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_ENDPOINTS,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV4, &ipv4->header);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ipv4->header.encoded_object + 4)),
+ src_ipv4.s_addr);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ipv4->header.encoded_object + 8)),
+ dst_ipv4.s_addr);
+
+ pcep_obj_free_object((struct pcep_object_header *)ipv4);
+}
+
+void test_pcep_obj_create_endpoint_ipv6()
+{
+ struct in6_addr src_ipv6, dst_ipv6;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &src_ipv6);
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:8446", &dst_ipv6);
+
+ struct pcep_object_endpoints_ipv6 *ipv6 =
+ pcep_obj_create_endpoint_ipv6(NULL, NULL);
+ CU_ASSERT_PTR_NULL(ipv6);
+
+ ipv6 = pcep_obj_create_endpoint_ipv6(&src_ipv6, NULL);
+ CU_ASSERT_PTR_NULL(ipv6);
+
+ ipv6 = pcep_obj_create_endpoint_ipv6(NULL, &dst_ipv6);
+ CU_ASSERT_PTR_NULL(ipv6);
+
+ ipv6 = pcep_obj_create_endpoint_ipv6(&src_ipv6, &dst_ipv6);
+ CU_ASSERT_PTR_NOT_NULL(ipv6);
+ pcep_encode_object(&ipv6->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_ENDPOINTS,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV6, &ipv6->header);
+ uint32_t *uint32_ptr = (uint32_t *)(ipv6->header.encoded_object + 4);
+ CU_ASSERT_EQUAL(uint32_ptr[0], src_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[1], src_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], src_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], src_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(uint32_ptr[4], dst_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[5], dst_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[6], dst_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[7], dst_ipv6.__in6_u.__u6_addr32[3]);
+
+ pcep_obj_free_object((struct pcep_object_header *)ipv6);
+}
+
+void test_pcep_obj_create_bandwidth()
+{
+ /* 1.8 => binary 1.11001101
+ * exponent = 127 => 0111 1111
+ * fraction = 1100 1101 0000 0000 0000 000 */
+ float bandwidth = 1.8;
+
+ struct pcep_object_bandwidth *bw = pcep_obj_create_bandwidth(bandwidth);
+
+ CU_ASSERT_PTR_NOT_NULL(bw);
+ pcep_encode_object(&bw->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_BANDWIDTH,
+ PCEP_OBJ_TYPE_BANDWIDTH_REQ, &bw->header);
+ CU_ASSERT_EQUAL(bw->header.encoded_object[4], 0x3f);
+ CU_ASSERT_EQUAL(bw->header.encoded_object[5], 0xe6);
+ CU_ASSERT_EQUAL(bw->header.encoded_object[6], 0x66);
+ CU_ASSERT_EQUAL(bw->header.encoded_object[7], 0x66);
+
+ pcep_obj_free_object((struct pcep_object_header *)bw);
+}
+
+void test_pcep_obj_create_metric()
+{
+ uint8_t type = PCEP_METRIC_BORDER_NODE_COUNT;
+ /* https://en.wikipedia.org/wiki/IEEE_754-1985
+ * 0.15625 = 1/8 + 1/32 = binary 0.00101 = 1.01 x 10^-3
+ * Exponent bias = 127, so exponent = (127-3) = 124 = 0111 1100
+ * Sign Exponent Fraction
+ * (8 bits) (23 bits)
+ * 0.15625 => 0 0111 1100 010 0000 ... 0000 */
+ float value = 0.15625;
+
+ struct pcep_object_metric *metric =
+ pcep_obj_create_metric(type, true, true, value);
+
+ CU_ASSERT_PTR_NOT_NULL(metric);
+ pcep_encode_object(&metric->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_METRIC, PCEP_OBJ_TYPE_METRIC,
+ &metric->header);
+ CU_ASSERT_EQUAL(metric->header.encoded_object[4], 0);
+ CU_ASSERT_EQUAL(metric->header.encoded_object[5], 0);
+ CU_ASSERT_TRUE(metric->header.encoded_object[6] & OBJECT_METRIC_FLAC_B);
+ CU_ASSERT_TRUE(metric->header.encoded_object[6] & OBJECT_METRIC_FLAC_C);
+ CU_ASSERT_EQUAL(metric->header.encoded_object[7], type);
+ /* See comments above for explanation of these values */
+ CU_ASSERT_EQUAL(metric->header.encoded_object[8], 0x3e);
+ CU_ASSERT_EQUAL(metric->header.encoded_object[9], 0x20);
+ CU_ASSERT_EQUAL(metric->header.encoded_object[10], 0x00);
+ CU_ASSERT_EQUAL(metric->header.encoded_object[11], 0x00);
+
+ pcep_obj_free_object((struct pcep_object_header *)metric);
+}
+
+void test_pcep_obj_create_lspa()
+{
+ uint32_t exclude_any = 10;
+ uint32_t include_any = 20;
+ uint32_t include_all = 30;
+ uint8_t prio = 0;
+ uint8_t hold_prio = 10;
+
+ struct pcep_object_lspa *lspa = pcep_obj_create_lspa(
+ exclude_any, include_any, include_all, prio, hold_prio, true);
+
+ CU_ASSERT_PTR_NOT_NULL(lspa);
+ pcep_encode_object(&lspa->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_LSPA, PCEP_OBJ_TYPE_LSPA,
+ &lspa->header);
+ uint32_t *uint32_ptr = (uint32_t *)(lspa->header.encoded_object + 4);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(exclude_any));
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(include_any));
+ CU_ASSERT_EQUAL(uint32_ptr[2], htonl(include_all));
+ CU_ASSERT_EQUAL(lspa->header.encoded_object[16], prio);
+ CU_ASSERT_EQUAL(lspa->header.encoded_object[17], hold_prio);
+ CU_ASSERT_TRUE(lspa->header.encoded_object[18] & OBJECT_LSPA_FLAG_L);
+ CU_ASSERT_EQUAL(lspa->header.encoded_object[19], 0);
+
+ pcep_obj_free_object((struct pcep_object_header *)lspa);
+}
+
+void test_pcep_obj_create_svec()
+{
+ struct pcep_object_svec *svec =
+ pcep_obj_create_svec(true, true, true, NULL);
+ CU_ASSERT_PTR_NULL(svec);
+
+ double_linked_list *id_list = dll_initialize();
+ uint32_t *uint32_ptr =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *uint32_ptr = 10;
+ dll_append(id_list, uint32_ptr);
+
+ svec = pcep_obj_create_svec(true, true, true, id_list);
+ CU_ASSERT_PTR_NOT_NULL(svec);
+ pcep_encode_object(&svec->header, versioning, object_buf);
+ verify_pcep_obj_header2(PCEP_OBJ_CLASS_SVEC, PCEP_OBJ_TYPE_SVEC,
+ (OBJECT_HEADER_LENGTH + sizeof(uint32_t) * 2),
+ svec->header.encoded_object);
+ CU_ASSERT_EQUAL(svec->header.encoded_object[4], 0);
+ CU_ASSERT_EQUAL(svec->header.encoded_object[5], 0);
+ CU_ASSERT_EQUAL(svec->header.encoded_object[6], 0);
+ CU_ASSERT_TRUE(svec->header.encoded_object[7] & OBJECT_SVEC_FLAG_S);
+ CU_ASSERT_TRUE(svec->header.encoded_object[7] & OBJECT_SVEC_FLAG_N);
+ CU_ASSERT_TRUE(svec->header.encoded_object[7] & OBJECT_SVEC_FLAG_L);
+ CU_ASSERT_EQUAL(*((uint32_t *)(svec->header.encoded_object + 8)),
+ htonl(*uint32_ptr));
+
+ pcep_obj_free_object((struct pcep_object_header *)svec);
+}
+
+void test_pcep_obj_create_error()
+{
+ uint8_t error_type = PCEP_ERRT_SESSION_FAILURE;
+ uint8_t error_value = PCEP_ERRV_RECVD_INVALID_OPEN_MSG;
+
+ struct pcep_object_error *error =
+ pcep_obj_create_error(error_type, error_value);
+
+ CU_ASSERT_PTR_NOT_NULL(error);
+ pcep_encode_object(&error->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_ERROR, PCEP_OBJ_TYPE_ERROR,
+ &error->header);
+ CU_ASSERT_EQUAL(error->header.encoded_object[4], 0);
+ CU_ASSERT_EQUAL(error->header.encoded_object[5], 0);
+ CU_ASSERT_EQUAL(error->header.encoded_object[6], error_type);
+ CU_ASSERT_EQUAL(error->header.encoded_object[7], error_value);
+
+ pcep_obj_free_object((struct pcep_object_header *)error);
+}
+
+void test_pcep_obj_create_close()
+{
+ uint8_t reason = PCEP_CLOSE_REASON_DEADTIMER;
+
+ struct pcep_object_close *close = pcep_obj_create_close(reason);
+
+ CU_ASSERT_PTR_NOT_NULL(close);
+ pcep_encode_object(&close->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_CLOSE, PCEP_OBJ_TYPE_CLOSE,
+ &close->header);
+ CU_ASSERT_EQUAL(close->header.encoded_object[4], 0);
+ CU_ASSERT_EQUAL(close->header.encoded_object[5], 0);
+ CU_ASSERT_EQUAL(close->header.encoded_object[6], 0);
+ CU_ASSERT_EQUAL(close->header.encoded_object[7], reason);
+
+ pcep_obj_free_object((struct pcep_object_header *)close);
+}
+
+void test_pcep_obj_create_srp()
+{
+ bool lsp_remove = true;
+ uint32_t srp_id_number = 0x89674523;
+ struct pcep_object_srp *srp =
+ pcep_obj_create_srp(lsp_remove, srp_id_number, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL(srp);
+ pcep_encode_object(&srp->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_SRP, PCEP_OBJ_TYPE_SRP,
+ &srp->header);
+ CU_ASSERT_EQUAL(srp->header.encoded_object[4], 0);
+ CU_ASSERT_EQUAL(srp->header.encoded_object[5], 0);
+ CU_ASSERT_EQUAL(srp->header.encoded_object[6], 0);
+ CU_ASSERT_TRUE(srp->header.encoded_object[7] & OBJECT_SRP_FLAG_R);
+ CU_ASSERT_EQUAL(*((uint32_t *)(srp->header.encoded_object + 8)),
+ htonl(srp_id_number));
+
+ pcep_obj_free_object((struct pcep_object_header *)srp);
+}
+
+void test_pcep_obj_create_lsp()
+{
+ uint32_t plsp_id = 0x000fffff;
+ enum pcep_lsp_operational_status status = PCEP_LSP_OPERATIONAL_ACTIVE;
+ bool c_flag = true;
+ bool a_flag = true;
+ bool r_flag = true;
+ bool s_flag = true;
+ bool d_flag = true;
+
+ /* Should return for invalid plsp_id */
+ struct pcep_object_lsp *lsp =
+ pcep_obj_create_lsp(0x001fffff, status, c_flag, a_flag, r_flag,
+ s_flag, d_flag, NULL);
+ CU_ASSERT_PTR_NULL(lsp);
+
+ /* Should return for invalid status */
+ lsp = pcep_obj_create_lsp(plsp_id, 8, c_flag, a_flag, r_flag, s_flag,
+ d_flag, NULL);
+ CU_ASSERT_PTR_NULL(lsp);
+
+ lsp = pcep_obj_create_lsp(plsp_id, status, c_flag, a_flag, r_flag,
+ s_flag, d_flag, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL(lsp);
+ pcep_encode_object(&lsp->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_LSP, PCEP_OBJ_TYPE_LSP,
+ &lsp->header);
+ CU_ASSERT_EQUAL((ntohl(*((uint32_t *)(lsp->header.encoded_object + 4)))
+ >> 12) & 0x000fffff,
+ plsp_id);
+ CU_ASSERT_EQUAL((lsp->header.encoded_object[7] >> 4) & 0x07, status);
+ CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_A);
+ CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_C);
+ CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_D);
+ CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_R);
+ CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_S);
+
+ pcep_obj_free_object((struct pcep_object_header *)lsp);
+}
+
+void test_pcep_obj_create_vendor_info()
+{
+ uint32_t enterprise_number = 0x01020304;
+ uint32_t enterprise_specific_info = 0x05060708;
+
+ struct pcep_object_vendor_info *obj = pcep_obj_create_vendor_info(
+ enterprise_number, enterprise_specific_info);
+
+ CU_ASSERT_PTR_NOT_NULL(obj);
+ pcep_encode_object(&obj->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_VENDOR_INFO,
+ PCEP_OBJ_TYPE_VENDOR_INFO, &obj->header);
+ uint32_t *uint32_ptr = (uint32_t *)(obj->header.encoded_object + 4);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(enterprise_number));
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(enterprise_specific_info));
+
+ pcep_obj_free_object((struct pcep_object_header *)obj);
+}
+
+/* Internal test function. The only difference between pcep_obj_create_ero(),
+ * pcep_obj_create_iro(), and pcep_obj_create_rro() is the object_class
+ * and the object_type.
+ */
+typedef struct pcep_object_ro *(*ro_func)(double_linked_list *);
+static void test_pcep_obj_create_object_common(ro_func func_to_test,
+ uint8_t object_class,
+ uint8_t object_type)
+{
+ double_linked_list *ero_list = dll_initialize();
+
+ struct pcep_object_ro *ero = func_to_test(NULL);
+ CU_ASSERT_PTR_NOT_NULL(ero);
+ pcep_encode_object(&ero->header, versioning, object_buf);
+ verify_pcep_obj_header2(object_class, object_type, OBJECT_HEADER_LENGTH,
+ ero->header.encoded_object);
+ pcep_obj_free_object((struct pcep_object_header *)ero);
+
+ reset_objects_buffer();
+ ero = func_to_test(ero_list);
+ CU_ASSERT_PTR_NOT_NULL(ero);
+ pcep_encode_object(&ero->header, versioning, object_buf);
+ verify_pcep_obj_header2(object_class, object_type, OBJECT_HEADER_LENGTH,
+ ero->header.encoded_object);
+ pcep_obj_free_object((struct pcep_object_header *)ero);
+
+ reset_objects_buffer();
+ struct pcep_ro_subobj_32label *ro_subobj =
+ pcep_obj_create_ro_subobj_32label(false, 0, 101);
+ ero_list = dll_initialize();
+ dll_append(ero_list, ro_subobj);
+ ero = func_to_test(ero_list);
+ CU_ASSERT_PTR_NOT_NULL(ero);
+ pcep_encode_object(&ero->header, versioning, object_buf);
+ /* 4 bytes for obj header +
+ * 2 bytes for ro_subobj header +
+ * 2 bytes for lable c-type and flags +
+ * 4 bytes for label */
+ verify_pcep_obj_header2(object_class, object_type,
+ OBJECT_HEADER_LENGTH + sizeof(uint32_t) * 2,
+ ero->header.encoded_object);
+ pcep_obj_free_object((struct pcep_object_header *)ero);
+}
+
+void test_pcep_obj_create_ero()
+{
+ test_pcep_obj_create_object_common(
+ pcep_obj_create_ero, PCEP_OBJ_CLASS_ERO, PCEP_OBJ_TYPE_ERO);
+}
+
+void test_pcep_obj_create_rro()
+{
+ test_pcep_obj_create_object_common(
+ pcep_obj_create_rro, PCEP_OBJ_CLASS_RRO, PCEP_OBJ_TYPE_RRO);
+}
+
+void test_pcep_obj_create_iro()
+{
+ test_pcep_obj_create_object_common(
+ pcep_obj_create_iro, PCEP_OBJ_CLASS_IRO, PCEP_OBJ_TYPE_IRO);
+}
+
+/* Internal util function to wrap an RO Subobj in a RO and encode it */
+static struct pcep_object_ro *encode_ro_subobj(struct pcep_object_ro_subobj *sr)
+{
+ double_linked_list *sr_subobj_list = dll_initialize();
+ dll_append(sr_subobj_list, sr);
+ struct pcep_object_ro *ro = pcep_obj_create_ero(sr_subobj_list);
+ pcep_encode_object(&ro->header, versioning, object_buf);
+
+ return ro;
+}
+
+static void verify_pcep_obj_ro_header(struct pcep_object_ro *ro,
+ struct pcep_object_ro_subobj *ro_subobj,
+ uint8_t ro_subobj_type, bool loose_hop,
+ uint16_t length)
+{
+ (void)ro_subobj;
+
+ verify_pcep_obj_header2(PCEP_OBJ_CLASS_ERO, PCEP_OBJ_TYPE_ERO, length,
+ ro->header.encoded_object);
+
+ /* TODO consider printing the stack trace:
+ * https://stackoverflow.com/questions/105659/how-can-one-grab-a-stack-trace-in-c
+ */
+
+ /* Not using CU_ASSERT_EQUAL here, so that in case of failure,
+ * we can provide more info in the error message. */
+ uint8_t found_type = (ro->header.encoded_object[4]
+ & 0x7f); /* remove the Loose hop bit */
+ if (found_type != ro_subobj_type) {
+ fprintf(stderr,
+ "Test failure ro_sub_obj_type expected [%d] found [%d]\n",
+ ro_subobj_type, found_type);
+ CU_FAIL("Sub Object Header Type");
+ }
+
+ bool loose_hop_found = (ro->header.encoded_object[4] & 0x80);
+ if (loose_hop != loose_hop_found) {
+ fprintf(stderr,
+ "Test failure ro_sub_obj Loose Hop bit expected [%d] found [%d]\n",
+ loose_hop, loose_hop_found);
+ CU_FAIL("Sub Object Header Loose Hop bit");
+ }
+
+ if (length - 4 != ro->header.encoded_object[5]) {
+ fprintf(stderr,
+ "Test failure ro_sub_obj length expected [%d] found [%d]\n",
+ length - 4, ro->header.encoded_object[5]);
+ CU_FAIL("Sub Object Length");
+ }
+}
+
+static void
+verify_pcep_obj_ro_sr_header(struct pcep_object_ro *ro,
+ struct pcep_object_ro_subobj *ro_subobj,
+ uint8_t nai_type, bool loose_hop, uint16_t length)
+{
+ verify_pcep_obj_ro_header(ro, ro_subobj, RO_SUBOBJ_TYPE_SR, loose_hop,
+ length);
+ uint8_t found_nai_type = ((ro->header.encoded_object[6] >> 4) & 0x0f);
+ if (nai_type != found_nai_type) {
+ fprintf(stderr,
+ "Test failure ro_sr_sub_obj nai_type expected [%d] found [%d]\n",
+ nai_type, found_nai_type);
+ CU_FAIL("Sub Object SR NAI Type");
+ }
+}
+
+void test_pcep_obj_create_ro_subobj_ipv4()
+{
+ struct in_addr ro_ipv4;
+ inet_pton(AF_INET, "192.168.1.2", &ro_ipv4);
+ uint8_t prefix_len = 8;
+
+ struct pcep_ro_subobj_ipv4 *ipv4 =
+ pcep_obj_create_ro_subobj_ipv4(true, NULL, prefix_len, false);
+ CU_ASSERT_PTR_NULL(ipv4);
+
+ ipv4 = pcep_obj_create_ro_subobj_ipv4(false, &ro_ipv4, prefix_len,
+ true);
+ CU_ASSERT_PTR_NOT_NULL(ipv4);
+ struct pcep_object_ro *ro = encode_ro_subobj(&ipv4->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &ipv4->ro_subobj, RO_SUBOBJ_TYPE_IPV4,
+ false, sizeof(uint32_t) * 3);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 6)),
+ ro_ipv4.s_addr);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[10], prefix_len);
+ CU_ASSERT_TRUE(ro->header.encoded_object[11]
+ & OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ reset_objects_buffer();
+ ipv4 = pcep_obj_create_ro_subobj_ipv4(true, &ro_ipv4, prefix_len,
+ false);
+ CU_ASSERT_PTR_NOT_NULL(ipv4);
+ ro = encode_ro_subobj(&ipv4->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &ipv4->ro_subobj, RO_SUBOBJ_TYPE_IPV4,
+ true, sizeof(uint32_t) * 3);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 6)),
+ ro_ipv4.s_addr);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[10], prefix_len);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[11], 0);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_ipv6()
+{
+ struct in6_addr ro_ipv6;
+ uint8_t prefix_len = 16;
+
+ struct pcep_ro_subobj_ipv6 *ipv6 =
+ pcep_obj_create_ro_subobj_ipv6(true, NULL, prefix_len, true);
+ CU_ASSERT_PTR_NULL(ipv6);
+
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &ro_ipv6);
+ ipv6 = pcep_obj_create_ro_subobj_ipv6(false, &ro_ipv6, prefix_len,
+ true);
+ CU_ASSERT_PTR_NOT_NULL(ipv6);
+ struct pcep_object_ro *ro = encode_ro_subobj(&ipv6->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &ipv6->ro_subobj, RO_SUBOBJ_TYPE_IPV6,
+ false, sizeof(uint32_t) * 6);
+ uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 6);
+ CU_ASSERT_EQUAL(uint32_ptr[0], ro_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[1], ro_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], ro_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], ro_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[22], prefix_len);
+ CU_ASSERT_TRUE(ro->header.encoded_object[23]
+ & OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ reset_objects_buffer();
+ ipv6 = pcep_obj_create_ro_subobj_ipv6(true, &ro_ipv6, prefix_len,
+ false);
+ CU_ASSERT_PTR_NOT_NULL(ipv6);
+ ro = encode_ro_subobj(&ipv6->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &ipv6->ro_subobj, RO_SUBOBJ_TYPE_IPV6,
+ true, sizeof(uint32_t) * 6);
+ uint32_ptr = (uint32_t *)(ro->header.encoded_object + 6);
+ CU_ASSERT_EQUAL(uint32_ptr[0], ro_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[1], ro_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], ro_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], ro_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[22], prefix_len);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[23], 0);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_unnum()
+{
+ struct in_addr router_id;
+ uint32_t if_id = 123;
+
+ struct pcep_ro_subobj_unnum *unnum =
+ pcep_obj_create_ro_subobj_unnum(NULL, if_id);
+ CU_ASSERT_PTR_NULL(unnum);
+
+ inet_pton(AF_INET, "192.168.1.2", &router_id);
+ unnum = pcep_obj_create_ro_subobj_unnum(&router_id, if_id);
+ CU_ASSERT_PTR_NOT_NULL(unnum);
+ struct pcep_object_ro *ro = encode_ro_subobj(&unnum->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &unnum->ro_subobj, RO_SUBOBJ_TYPE_UNNUM,
+ false, sizeof(uint32_t) * 4);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[6], 0);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[7], 0);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 8)),
+ router_id.s_addr);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 12)),
+ htonl(if_id));
+
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_32label()
+{
+ uint8_t class_type = 1;
+ uint32_t label = 0xeeffaabb;
+
+ struct pcep_ro_subobj_32label *label32 =
+ pcep_obj_create_ro_subobj_32label(true, class_type, label);
+ CU_ASSERT_PTR_NOT_NULL(label32);
+ struct pcep_object_ro *ro = encode_ro_subobj(&label32->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &label32->ro_subobj, RO_SUBOBJ_TYPE_LABEL,
+ false, sizeof(uint32_t) * 3);
+ CU_ASSERT_TRUE(ro->header.encoded_object[6]
+ & OBJECT_SUBOBJ_LABEL_FLAG_GLOGAL);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[7], class_type);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 8)),
+ htonl(label));
+
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_asn()
+{
+ uint16_t asn = 0x0102;
+
+ struct pcep_ro_subobj_asn *asn_obj = pcep_obj_create_ro_subobj_asn(asn);
+ CU_ASSERT_PTR_NOT_NULL(asn_obj);
+ struct pcep_object_ro *ro = encode_ro_subobj(&asn_obj->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &asn_obj->ro_subobj, RO_SUBOBJ_TYPE_ASN,
+ false, sizeof(uint32_t) * 2);
+ CU_ASSERT_EQUAL(*((uint16_t *)(ro->header.encoded_object + 6)),
+ htons(asn));
+
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_sr_nonai()
+{
+ uint32_t sid = 0x01020304;
+
+ struct pcep_ro_subobj_sr *sr =
+ pcep_obj_create_ro_subobj_sr_nonai(false, sid, false, false);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_ABSENT, false,
+ sizeof(uint32_t) * 3);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ reset_objects_buffer();
+ sr = pcep_obj_create_ro_subobj_sr_nonai(true, sid, true, true);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_ABSENT, true,
+ sizeof(uint32_t) * 3);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_sr_ipv4_node()
+{
+ uint32_t sid = 0x01020304;
+ struct in_addr ipv4_node_id;
+ inet_pton(AF_INET, "192.168.1.2", &ipv4_node_id);
+
+ /* (loose_hop, sid_absent, c_flag, m_flag, sid, ipv4_node_id) */
+ struct pcep_ro_subobj_sr *sr = pcep_obj_create_ro_subobj_sr_ipv4_node(
+ true, false, true, true, sid, NULL);
+ CU_ASSERT_PTR_NULL(sr);
+
+ /* Test the sid is absent */
+ sr = pcep_obj_create_ro_subobj_sr_ipv4_node(true, true, false, false,
+ sid, &ipv4_node_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE, true,
+ sizeof(uint32_t) * 3);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_EQUAL(sr->sid, 0);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 8)),
+ ipv4_node_id.s_addr);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* Test the sid is present */
+ reset_objects_buffer();
+ inet_pton(AF_INET, "192.168.1.2", &ipv4_node_id);
+ sr = pcep_obj_create_ro_subobj_sr_ipv4_node(false, false, true, true,
+ sid, &ipv4_node_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE, false,
+ sizeof(uint32_t) * 4);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 8)),
+ htonl(sid));
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 12)),
+ ipv4_node_id.s_addr);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_sr_ipv6_node()
+{
+ uint32_t sid = 0x01020304;
+ struct in6_addr ipv6_node_id;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &ipv6_node_id);
+
+ /* (loose_hop, sid_absent, c_flag, m_flag, sid, ipv6_node_id) */
+ struct pcep_ro_subobj_sr *sr = pcep_obj_create_ro_subobj_sr_ipv6_node(
+ false, true, true, true, sid, NULL);
+ CU_ASSERT_PTR_NULL(sr);
+
+ /* Test the sid is absent */
+ sr = pcep_obj_create_ro_subobj_sr_ipv6_node(true, true, true, true, sid,
+ &ipv6_node_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV6_NODE, true,
+ sizeof(uint32_t) * 6);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], ipv6_node_id.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[1], ipv6_node_id.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], ipv6_node_id.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], ipv6_node_id.__in6_u.__u6_addr32[3]);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* Test the sid is present */
+ reset_objects_buffer();
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &ipv6_node_id);
+ sr = pcep_obj_create_ro_subobj_sr_ipv6_node(false, false, true, true,
+ sid, &ipv6_node_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV6_NODE, false,
+ sizeof(uint32_t) * 7);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid));
+ CU_ASSERT_EQUAL(uint32_ptr[1], ipv6_node_id.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], ipv6_node_id.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], ipv6_node_id.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[4], ipv6_node_id.__in6_u.__u6_addr32[3]);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_sr_ipv4_adj()
+{
+ struct in_addr local_ipv4;
+ struct in_addr remote_ipv4;
+ inet_pton(AF_INET, "192.168.1.2", &local_ipv4);
+ inet_pton(AF_INET, "172.168.1.2", &remote_ipv4);
+
+ uint32_t sid = ENCODE_SR_ERO_SID(3, 7, 0, 188);
+ CU_ASSERT_EQUAL(sid, 16060);
+
+ /* (loose_hop, sid_absent, c_flag, m_flag, sid, local_ipv4, remote_ipv4)
+ */
+ struct pcep_ro_subobj_sr *sr = pcep_obj_create_ro_subobj_sr_ipv4_adj(
+ false, true, true, true, sid, NULL, NULL);
+ CU_ASSERT_PTR_NULL(sr);
+
+ sr = pcep_obj_create_ro_subobj_sr_ipv4_adj(false, true, true, true, sid,
+ &local_ipv4, NULL);
+ CU_ASSERT_PTR_NULL(sr);
+
+ sr = pcep_obj_create_ro_subobj_sr_ipv4_adj(false, true, true, true, sid,
+ NULL, &remote_ipv4);
+ CU_ASSERT_PTR_NULL(sr);
+
+ /* Test the sid is absent */
+ sr = pcep_obj_create_ro_subobj_sr_ipv4_adj(true, true, true, true, sid,
+ &local_ipv4, &remote_ipv4);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY, true,
+ sizeof(uint32_t) * 4);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_EQUAL(sr->sid, 0);
+ uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], local_ipv4.s_addr);
+ CU_ASSERT_EQUAL(uint32_ptr[1], remote_ipv4.s_addr);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* Test the sid is present */
+ inet_pton(AF_INET, "192.168.1.2", &local_ipv4);
+ inet_pton(AF_INET, "172.168.1.2", &remote_ipv4);
+ reset_objects_buffer();
+ sr = pcep_obj_create_ro_subobj_sr_ipv4_adj(
+ false, false, true, true, sid, &local_ipv4, &remote_ipv4);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY, false,
+ sizeof(uint32_t) * 5);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid));
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv4.s_addr);
+ CU_ASSERT_EQUAL(uint32_ptr[2], remote_ipv4.s_addr);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_sr_ipv6_adj()
+{
+ uint32_t sid = 0x01020304;
+ struct in6_addr local_ipv6;
+ struct in6_addr remote_ipv6;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:8221", &local_ipv6);
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &remote_ipv6);
+
+ /* (loose_hop, sid_absent, c_flag, m_flag, sid, local_ipv6, remote_ipv6)
+ */
+ struct pcep_ro_subobj_sr *sr = pcep_obj_create_ro_subobj_sr_ipv6_adj(
+ false, true, true, true, sid, NULL, NULL);
+ CU_ASSERT_PTR_NULL(sr);
+
+ sr = pcep_obj_create_ro_subobj_sr_ipv6_adj(false, true, true, true, sid,
+ &local_ipv6, NULL);
+ CU_ASSERT_PTR_NULL(sr);
+
+ sr = pcep_obj_create_ro_subobj_sr_ipv6_adj(false, true, true, true, sid,
+ NULL, &remote_ipv6);
+ CU_ASSERT_PTR_NULL(sr);
+
+ /* Test the sid is absent */
+ sr = pcep_obj_create_ro_subobj_sr_ipv6_adj(true, true, true, true, sid,
+ &local_ipv6, &remote_ipv6);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY, true,
+ sizeof(uint32_t) * 10);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_EQUAL(sr->sid, 0);
+ uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], local_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], local_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], local_ipv6.__in6_u.__u6_addr32[3]);
+
+ CU_ASSERT_EQUAL(uint32_ptr[4], remote_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[5], remote_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[6], remote_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[7], remote_ipv6.__in6_u.__u6_addr32[3]);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* Test the sid is present */
+ reset_objects_buffer();
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:8221", &local_ipv6);
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &remote_ipv6);
+ sr = pcep_obj_create_ro_subobj_sr_ipv6_adj(
+ false, false, true, false, sid, &local_ipv6, &remote_ipv6);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY, false,
+ sizeof(uint32_t) * 11);
+ /* All flags are false */
+ CU_ASSERT_EQUAL(ro->header.encoded_object[7], 0);
+ uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid));
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], local_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], local_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[4], local_ipv6.__in6_u.__u6_addr32[3]);
+
+ CU_ASSERT_EQUAL(uint32_ptr[5], remote_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[6], remote_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[7], remote_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[8], remote_ipv6.__in6_u.__u6_addr32[3]);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj()
+{
+ uint32_t sid = 0x01020304;
+ uint32_t local_node_id = 0x11223344;
+ uint32_t local_if_id = 0x55667788;
+ uint32_t remote_node_id = 0x99aabbcc;
+ uint32_t remote_if_id = 0xddeeff11;
+
+ /* (loose_hop, sid_absent, c_flag, m_flag,
+ sid, local_node_id, local_if_id, remote_node_id, remote_if_id) */
+
+ /* Test the sid is absent */
+ struct pcep_ro_subobj_sr *sr =
+ pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(
+ true, true, true, true, sid, local_node_id, local_if_id,
+ remote_node_id, remote_if_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(
+ ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY, true,
+ sizeof(uint32_t) * 6);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_EQUAL(sr->sid, 0);
+ uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], local_node_id);
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_if_id);
+ CU_ASSERT_EQUAL(uint32_ptr[2], remote_node_id);
+ CU_ASSERT_EQUAL(uint32_ptr[3], remote_if_id);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* Test the sid is present */
+ reset_objects_buffer();
+ sr = pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(
+ false, false, true, true, sid, local_node_id, local_if_id,
+ remote_node_id, remote_if_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(
+ ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY, false,
+ sizeof(uint32_t) * 7);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid));
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_node_id);
+ CU_ASSERT_EQUAL(uint32_ptr[2], local_if_id);
+ CU_ASSERT_EQUAL(uint32_ptr[3], remote_node_id);
+ CU_ASSERT_EQUAL(uint32_ptr[4], remote_if_id);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* TODO Test draft07 types */
+}
+
+void test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj()
+{
+ uint32_t sid = 0x01020304;
+ uint32_t local_if_id = 0x11002200;
+ uint32_t remote_if_id = 0x00110022;
+ struct in6_addr local_ipv6;
+ struct in6_addr remote_ipv6;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:8221", &local_ipv6);
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &remote_ipv6);
+
+ /* (loose_hop, sid_absent, c_flag, m_flag, sid, local_ipv6, local_if_id,
+ * remote_ipv6, remote_if_id */
+ struct pcep_ro_subobj_sr *sr =
+ pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ false, true, true, true, sid, NULL, local_if_id, NULL,
+ remote_if_id);
+ CU_ASSERT_PTR_NULL(sr);
+
+ sr = pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ false, true, true, true, sid, &local_ipv6, local_if_id, NULL,
+ remote_if_id);
+ CU_ASSERT_PTR_NULL(sr);
+
+ sr = pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ false, true, true, true, sid, NULL, local_if_id, &remote_ipv6,
+ remote_if_id);
+ CU_ASSERT_PTR_NULL(sr);
+
+ /* Test the sid is absent */
+ sr = pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ true, true, true, true, sid, &local_ipv6, local_if_id,
+ &remote_ipv6, remote_if_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(
+ ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY, true,
+ sizeof(uint32_t) * 12);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_EQUAL(sr->sid, 0);
+ uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], local_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], local_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], local_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(uint32_ptr[4], local_if_id);
+
+ CU_ASSERT_EQUAL(uint32_ptr[5], remote_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[6], remote_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[7], remote_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[8], remote_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(uint32_ptr[9], remote_if_id);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* Test the sid is present */
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:8221", &local_ipv6);
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &remote_ipv6);
+ reset_objects_buffer();
+ sr = pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ false, false, true, true, sid, &local_ipv6, local_if_id,
+ &remote_ipv6, remote_if_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(
+ ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY, false,
+ sizeof(uint32_t) * 13);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid));
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], local_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], local_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[4], local_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(uint32_ptr[5], local_if_id);
+
+ CU_ASSERT_EQUAL(uint32_ptr[6], remote_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[7], remote_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[8], remote_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[9], remote_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(uint32_ptr[10], remote_if_id);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
diff --git a/pceplib/test/pcep_msg_objects_test.h b/pceplib/test/pcep_msg_objects_test.h
new file mode 100644
index 000000000..0f08193a5
--- /dev/null
+++ b/pceplib/test/pcep_msg_objects_test.h
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+
+#ifndef PCEP_MSG_OBJECTS_TEST_H_
+#define PCEP_MSG_OBJECTS_TEST_H_
+
+int pcep_objects_test_suite_setup(void);
+int pcep_objects_test_suite_teardown(void);
+void pcep_objects_test_setup(void);
+void pcep_objects_test_teardown(void);
+void test_pcep_obj_create_open(void);
+void test_pcep_obj_create_open_with_tlvs(void);
+void test_pcep_obj_create_rp(void);
+void test_pcep_obj_create_nopath(void);
+void test_pcep_obj_create_endpoint_ipv4(void);
+void test_pcep_obj_create_endpoint_ipv6(void);
+void test_pcep_obj_create_association_ipv4(void);
+void test_pcep_obj_create_association_ipv6(void);
+void test_pcep_obj_create_bandwidth(void);
+void test_pcep_obj_create_metric(void);
+void test_pcep_obj_create_lspa(void);
+void test_pcep_obj_create_svec(void);
+void test_pcep_obj_create_error(void);
+void test_pcep_obj_create_close(void);
+void test_pcep_obj_create_srp(void);
+void test_pcep_obj_create_lsp(void);
+void test_pcep_obj_create_vendor_info(void);
+void test_pcep_obj_create_ero(void);
+void test_pcep_obj_create_rro(void);
+void test_pcep_obj_create_iro(void);
+void test_pcep_obj_create_ro_subobj_ipv4(void);
+void test_pcep_obj_create_ro_subobj_ipv6(void);
+void test_pcep_obj_create_ro_subobj_unnum(void);
+void test_pcep_obj_create_ro_subobj_32label(void);
+void test_pcep_obj_create_ro_subobj_asn(void);
+void test_pcep_obj_create_ro_subobj_sr_nonai(void);
+void test_pcep_obj_create_ro_subobj_sr_ipv4_node(void);
+void test_pcep_obj_create_ro_subobj_sr_ipv6_node(void);
+void test_pcep_obj_create_ro_subobj_sr_ipv4_adj(void);
+void test_pcep_obj_create_ro_subobj_sr_ipv6_adj(void);
+void test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(void);
+void test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(void);
+
+#endif
diff --git a/pceplib/test/pcep_msg_tests_valgrind.sh b/pceplib/test/pcep_msg_tests_valgrind.sh
new file mode 100755
index 000000000..4a9a99939
--- /dev/null
+++ b/pceplib/test/pcep_msg_tests_valgrind.sh
@@ -0,0 +1,2 @@
+source pceplib/test/pcep_tests_valgrind.sh
+valgrind_test pceplib/test/pcep_msg_tests
diff --git a/pceplib/test/pcep_msg_tlvs_test.c b/pceplib/test/pcep_msg_tlvs_test.c
new file mode 100644
index 000000000..878e4d62a
--- /dev/null
+++ b/pceplib/test/pcep_msg_tlvs_test.c
@@ -0,0 +1,671 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tlvs.h"
+#include "pcep_msg_tools.h"
+#include "pcep_utils_memory.h"
+#include "pcep_msg_tlvs_test.h"
+
+/*
+ * Notice:
+ * All of these TLV Unit Tests encode the created TLVs by explicitly calling
+ * pcep_encode_tlv() thus testing the TLV creation and the TLV encoding.
+ * All APIs expect IPs to be in network byte order.
+ */
+
+static struct pcep_versioning *versioning = NULL;
+static uint8_t tlv_buf[2000];
+
+void reset_tlv_buffer(void);
+
+int pcep_tlvs_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_tlvs_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+void reset_tlv_buffer()
+{
+ memset(tlv_buf, 0, 2000);
+}
+
+void pcep_tlvs_test_setup()
+{
+ versioning = create_default_pcep_versioning();
+ reset_tlv_buffer();
+}
+
+void pcep_tlvs_test_teardown()
+{
+ destroy_pcep_versioning(versioning);
+}
+
+void test_pcep_tlv_create_stateful_pce_capability()
+{
+ struct pcep_object_tlv_stateful_pce_capability *tlv =
+ pcep_tlv_create_stateful_pce_capability(true, true, true, true,
+ true, true);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t));
+ CU_ASSERT_TRUE(tlv->flag_u_lsp_update_capability);
+ CU_ASSERT_TRUE(tlv->flag_s_include_db_version);
+ CU_ASSERT_TRUE(tlv->flag_i_lsp_instantiation_capability);
+ CU_ASSERT_TRUE(tlv->flag_t_triggered_resync);
+ CU_ASSERT_TRUE(tlv->flag_d_delta_lsp_sync);
+ CU_ASSERT_TRUE(tlv->flag_f_triggered_initial_sync);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[7], 0x3f);
+ /* TODO add a new function: verify_tlv_header(tlv->header.encoded_tlv)
+ * to all tests */
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_speaker_entity_id()
+{
+ struct pcep_object_tlv_speaker_entity_identifier *tlv =
+ pcep_tlv_create_speaker_entity_id(NULL);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ double_linked_list *list = dll_initialize();
+ tlv = pcep_tlv_create_speaker_entity_id(list);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ uint32_t *speaker_entity =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *speaker_entity = 42;
+ dll_append(list, speaker_entity);
+ tlv = pcep_tlv_create_speaker_entity_id(list);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t));
+ CU_ASSERT_PTR_NOT_NULL(tlv->speaker_entity_id_list);
+ CU_ASSERT_EQUAL(tlv->speaker_entity_id_list->num_entries, 1);
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(*speaker_entity));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_lsp_db_version()
+{
+ uint64_t lsp_db_version = 0xf005ba11ba5eba11;
+ struct pcep_object_tlv_lsp_db_version *tlv =
+ pcep_tlv_create_lsp_db_version(lsp_db_version);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint64_t));
+ CU_ASSERT_EQUAL(tlv->lsp_db_version, lsp_db_version);
+ CU_ASSERT_EQUAL(*((uint64_t *)(tlv->header.encoded_tlv + 4)),
+ be64toh(lsp_db_version));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_path_setup_type()
+{
+ uint8_t pst = 0x89;
+
+ struct pcep_object_tlv_path_setup_type *tlv =
+ pcep_tlv_create_path_setup_type(pst);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t));
+ CU_ASSERT_EQUAL(tlv->path_setup_type, pst);
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(0x000000FF & pst));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_path_setup_type_capability()
+{
+ /* The sub_tlv list is optional */
+
+ /* Should return NULL if pst_list is NULL */
+ struct pcep_object_tlv_path_setup_type_capability *tlv =
+ pcep_tlv_create_path_setup_type_capability(NULL, NULL);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ /* Should return NULL if pst_list is empty */
+ double_linked_list *pst_list = dll_initialize();
+ tlv = pcep_tlv_create_path_setup_type_capability(pst_list, NULL);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ /* Should still return NULL if pst_list is NULL */
+ double_linked_list *sub_tlv_list = dll_initialize();
+ tlv = pcep_tlv_create_path_setup_type_capability(NULL, sub_tlv_list);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ /* Should still return NULL if pst_list is empty */
+ tlv = pcep_tlv_create_path_setup_type_capability(pst_list,
+ sub_tlv_list);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ /* Test only populating the pst list */
+ uint8_t *pst1 = pceplib_malloc(PCEPLIB_MESSAGES, 1);
+ uint8_t *pst2 = pceplib_malloc(PCEPLIB_MESSAGES, 1);
+ uint8_t *pst3 = pceplib_malloc(PCEPLIB_MESSAGES, 1);
+ *pst1 = 1;
+ *pst2 = 2;
+ *pst3 = 3;
+ dll_append(pst_list, pst1);
+ dll_append(pst_list, pst2);
+ dll_append(pst_list, pst3);
+ tlv = pcep_tlv_create_path_setup_type_capability(pst_list,
+ sub_tlv_list);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t) * 2);
+ CU_ASSERT_PTR_NOT_NULL(tlv->pst_list);
+ CU_ASSERT_EQUAL(tlv->pst_list->num_entries, 3);
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(0x00000003));
+ CU_ASSERT_EQUAL(uint32_ptr[2], htonl(0x01020300));
+ pcep_obj_free_tlv(&tlv->header);
+
+ /* Now test populating both the pst_list and the sub_tlv_list */
+ reset_tlv_buffer();
+ struct pcep_object_tlv_header *sub_tlv =
+ (struct pcep_object_tlv_header *)
+ pcep_tlv_create_sr_pce_capability(true, true, 0);
+ pst_list = dll_initialize();
+ sub_tlv_list = dll_initialize();
+ pst1 = pceplib_malloc(PCEPLIB_MESSAGES, 1);
+ *pst1 = 1;
+ dll_append(pst_list, pst1);
+ dll_append(sub_tlv_list, sub_tlv);
+ tlv = pcep_tlv_create_path_setup_type_capability(pst_list,
+ sub_tlv_list);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length,
+ sizeof(uint32_t) * 2 + TLV_HEADER_LENGTH
+ + sub_tlv->encoded_tlv_length);
+ CU_ASSERT_PTR_NOT_NULL(tlv->pst_list);
+ CU_ASSERT_PTR_NOT_NULL(tlv->sub_tlv_list);
+ uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ uint16_t *uint16_ptr = (uint16_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint16_ptr[0],
+ htons(PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY));
+ CU_ASSERT_EQUAL(uint16_ptr[1], htons(tlv->header.encoded_tlv_length));
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(0x00000001));
+ CU_ASSERT_EQUAL(uint32_ptr[2], htonl(0x01000000));
+ /* Verify the Sub-TLV */
+ uint16_ptr = (uint16_t *)(tlv->header.encoded_tlv + 12);
+ CU_ASSERT_EQUAL(uint16_ptr[0],
+ htons(PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY));
+ CU_ASSERT_EQUAL(uint16_ptr[1], htons(4));
+ CU_ASSERT_EQUAL(uint16_ptr[2], 0);
+ CU_ASSERT_EQUAL(uint16_ptr[3], htons(0x0300));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_sr_pce_capability()
+{
+ struct pcep_object_tlv_sr_pce_capability *tlv =
+ pcep_tlv_create_sr_pce_capability(true, true, 8);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t));
+ uint16_t *uint16_ptr = (uint16_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint16_ptr[0],
+ htons(PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY));
+ CU_ASSERT_EQUAL(uint16_ptr[1], htons(tlv->header.encoded_tlv_length));
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(0x00000308));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_symbolic_path_name()
+{
+ /* char *symbolic_path_name, uint16_t symbolic_path_name_length); */
+ char path_name[16] = "Some Path Name";
+ uint16_t path_name_length = 14;
+ struct pcep_object_tlv_symbolic_path_name *tlv =
+ pcep_tlv_create_symbolic_path_name(path_name, path_name_length);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, path_name_length);
+ /* Test the padding is correct */
+ CU_ASSERT_EQUAL(0, strncmp((char *)&(tlv->header.encoded_tlv[4]),
+ &path_name[0], 4));
+ CU_ASSERT_EQUAL(0, strncmp((char *)&(tlv->header.encoded_tlv[8]),
+ &path_name[4], 4));
+ CU_ASSERT_EQUAL(0, strncmp((char *)&(tlv->header.encoded_tlv[12]),
+ &path_name[8], 4));
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[16], 'm');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[17], 'e');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[18], 0);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[19], 0);
+ pcep_obj_free_tlv(&tlv->header);
+
+ reset_tlv_buffer();
+ tlv = pcep_tlv_create_symbolic_path_name(path_name, 3);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 3);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[4], 'S');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[5], 'o');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[6], 'm');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[7], 0);
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_ipv4_lsp_identifiers()
+{
+ struct in_addr sender_ip, endpoint_ip;
+ uint16_t lsp_id = 7;
+ uint16_t tunnel_id = 16;
+ struct in_addr extended_tunnel_id;
+ extended_tunnel_id.s_addr = 256;
+ inet_pton(AF_INET, "192.168.1.1", &sender_ip);
+ inet_pton(AF_INET, "192.168.1.2", &endpoint_ip);
+
+ struct pcep_object_tlv_ipv4_lsp_identifier *tlv =
+ pcep_tlv_create_ipv4_lsp_identifiers(NULL, &endpoint_ip, lsp_id,
+ tunnel_id,
+ &extended_tunnel_id);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_ipv4_lsp_identifiers(
+ &sender_ip, NULL, lsp_id, tunnel_id, &extended_tunnel_id);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_ipv4_lsp_identifiers(
+ NULL, NULL, lsp_id, tunnel_id, &extended_tunnel_id);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_ipv4_lsp_identifiers(&sender_ip, &endpoint_ip,
+ lsp_id, tunnel_id,
+ &extended_tunnel_id);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t) * 4);
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], sender_ip.s_addr);
+ CU_ASSERT_EQUAL(uint32_ptr[2],
+ (uint32_t)(htons(tunnel_id) << 16) | htons(lsp_id));
+ CU_ASSERT_EQUAL(uint32_ptr[3], extended_tunnel_id.s_addr);
+ CU_ASSERT_EQUAL(uint32_ptr[4], endpoint_ip.s_addr);
+ pcep_obj_free_tlv(&tlv->header);
+
+ reset_tlv_buffer();
+ tlv = pcep_tlv_create_ipv4_lsp_identifiers(&sender_ip, &endpoint_ip,
+ lsp_id, tunnel_id, NULL);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t) * 4);
+ uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], sender_ip.s_addr);
+ CU_ASSERT_EQUAL(uint32_ptr[2],
+ (uint32_t)(htons(tunnel_id) << 16) | htons(lsp_id));
+ CU_ASSERT_EQUAL(uint32_ptr[3], INADDR_ANY);
+ CU_ASSERT_EQUAL(uint32_ptr[4], endpoint_ip.s_addr);
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_ipv6_lsp_identifiers()
+{
+ struct in6_addr sender_ip, endpoint_ip;
+ uint16_t lsp_id = 3;
+ uint16_t tunnel_id = 16;
+ uint32_t extended_tunnel_id[4];
+
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &sender_ip);
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:8446", &endpoint_ip);
+ extended_tunnel_id[0] = 1;
+ extended_tunnel_id[1] = 2;
+ extended_tunnel_id[2] = 3;
+ extended_tunnel_id[3] = 4;
+
+ struct pcep_object_tlv_ipv6_lsp_identifier *tlv =
+ pcep_tlv_create_ipv6_lsp_identifiers(
+ NULL, &endpoint_ip, lsp_id, tunnel_id,
+ (struct in6_addr *)&extended_tunnel_id);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_ipv6_lsp_identifiers(
+ &sender_ip, NULL, lsp_id, tunnel_id,
+ (struct in6_addr *)&extended_tunnel_id);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_ipv6_lsp_identifiers(
+ NULL, NULL, lsp_id, tunnel_id,
+ (struct in6_addr *)&extended_tunnel_id);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_ipv6_lsp_identifiers(
+ &sender_ip, &endpoint_ip, lsp_id, tunnel_id,
+ (struct in6_addr *)&extended_tunnel_id);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 52);
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[5],
+ (uint32_t)(htons(tunnel_id) << 16) | htons(lsp_id));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+void test_pcep_tlv_create_srpag_pol_id_ipv4()
+{
+ uint32_t color = 1;
+ struct in_addr src;
+ inet_pton(AF_INET, "192.168.1.2", &src);
+
+ struct pcep_object_tlv_srpag_pol_id *tlv =
+ pcep_tlv_create_srpag_pol_id_ipv4(color, (void *)&src);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, (PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID));
+ CU_ASSERT_EQUAL(
+ tlv->header.encoded_tlv_length,
+ (8 /*draft-barth-pce-segment-routing-policy-cp-04#5.1*/));
+ CU_ASSERT_EQUAL(tlv->color, (color));
+ uint32_t aux_color = htonl(color); // Is color right encoded
+ CU_ASSERT_EQUAL(0, memcmp(&tlv_buf[0] + TLV_HEADER_LENGTH, &aux_color,
+ sizeof(color)));
+ CU_ASSERT_EQUAL(tlv->end_point.ipv4.s_addr, (src.s_addr));
+ // Are simetrical?
+ struct pcep_object_tlv_header *dec_hdr = pcep_decode_tlv(tlv_buf);
+ struct pcep_object_tlv_srpag_pol_id *dec_tlv =
+ (struct pcep_object_tlv_srpag_pol_id *)dec_hdr;
+ CU_ASSERT_EQUAL(tlv->color, dec_tlv->color);
+
+ pceplib_free(PCEPLIB_MESSAGES, dec_hdr);
+ pcep_obj_free_tlv(&tlv->header);
+}
+void test_pcep_tlv_create_srpag_pol_id_ipv6()
+{
+
+ uint32_t color = 1;
+ struct in6_addr src;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &src);
+
+ struct pcep_object_tlv_srpag_pol_id *tlv =
+ pcep_tlv_create_srpag_pol_id_ipv6(color, &src);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, (PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID));
+ CU_ASSERT_EQUAL(
+ tlv->header.encoded_tlv_length,
+ (20 /*draft-barth-pce-segment-routing-policy-cp-04#5.1*/));
+ CU_ASSERT_EQUAL(tlv->color, (color));
+ CU_ASSERT_EQUAL(0, memcmp(&tlv->end_point.ipv6, &src, sizeof(src)));
+
+ uint32_t aux_color = htonl(color);
+ CU_ASSERT_EQUAL(0, memcmp(&aux_color, tlv_buf + TLV_HEADER_LENGTH,
+ sizeof(tlv->color)));
+ // Are simetrical?
+ struct pcep_object_tlv_header *dec_hdr = pcep_decode_tlv(tlv_buf);
+ struct pcep_object_tlv_srpag_pol_id *dec_tlv =
+ (struct pcep_object_tlv_srpag_pol_id *)dec_hdr;
+ CU_ASSERT_EQUAL(tlv->color, dec_tlv->color);
+
+ pceplib_free(PCEPLIB_MESSAGES, dec_hdr);
+ pcep_obj_free_tlv(&tlv->header);
+}
+void test_pcep_tlv_create_srpag_pol_name()
+{
+ const char *pol_name = "Some Pol Name";
+
+ struct pcep_object_tlv_srpag_pol_name *tlv =
+ pcep_tlv_create_srpag_pol_name(pol_name, strlen(pol_name));
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ (PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME));
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length,
+ (normalize_pcep_tlv_length(strlen(pol_name))));
+ CU_ASSERT_EQUAL(0, strcmp(pol_name, (char *)tlv->name));
+
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_srpag_cp_id()
+{
+ // draft-ietf-spring-segment-routing-policy-06.pdf#2.3
+ // 10 PCEP, 20 BGP SR Policy, 30 Via Configuration
+ uint8_t proto_origin = 10;
+ uint32_t ASN = 0;
+ struct in6_addr with_mapped_ipv4;
+ inet_pton(AF_INET6, "::ffff:192.0.2.128", &with_mapped_ipv4);
+ uint32_t discriminator = 0;
+
+ struct pcep_object_tlv_srpag_cp_id *tlv = pcep_tlv_create_srpag_cp_id(
+ proto_origin, ASN, &with_mapped_ipv4, discriminator);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ (PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID));
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length,
+ (sizeof(proto_origin) + sizeof(ASN)
+ + sizeof(with_mapped_ipv4) + sizeof(discriminator)));
+ CU_ASSERT_EQUAL(tlv->proto, (proto_origin));
+ CU_ASSERT_EQUAL(tlv->orig_asn, (ASN));
+ CU_ASSERT_EQUAL(0, memcmp(&tlv->orig_addres, &with_mapped_ipv4,
+ sizeof(with_mapped_ipv4)));
+ CU_ASSERT_EQUAL(tlv->discriminator, (discriminator));
+ // Are simetrical?
+ struct pcep_object_tlv_header *dec_hdr = pcep_decode_tlv(tlv_buf);
+ struct pcep_object_tlv_srpag_cp_id *dec_tlv =
+ (struct pcep_object_tlv_srpag_cp_id *)dec_hdr;
+ CU_ASSERT_EQUAL(tlv->proto, dec_tlv->proto);
+
+ pceplib_free(PCEPLIB_MESSAGES, dec_hdr);
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_srpag_cp_pref()
+{
+ uint32_t preference_default = 100;
+
+ struct pcep_object_tlv_srpag_cp_pref *tlv =
+ pcep_tlv_create_srpag_cp_pref(preference_default);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ (PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE));
+ printf(" encoded length vs sizeof pref (%d) vs (%ld)\n",
+ tlv->header.encoded_tlv_length, sizeof(preference_default));
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length,
+ sizeof(preference_default));
+ CU_ASSERT_EQUAL(tlv->preference, (preference_default));
+ uint32_t aux_pref = htonl(preference_default); // Is pref right encoded
+ CU_ASSERT_EQUAL(0, memcmp(tlv_buf + TLV_HEADER_LENGTH, &aux_pref,
+ sizeof(preference_default)));
+ // Are simetrical?
+ struct pcep_object_tlv_header *dec_hdr = pcep_decode_tlv(tlv_buf);
+ struct pcep_object_tlv_srpag_cp_pref *dec_tlv =
+ (struct pcep_object_tlv_srpag_cp_pref *)dec_hdr;
+ CU_ASSERT_EQUAL(tlv->preference, dec_tlv->preference);
+
+ pceplib_free(PCEPLIB_MESSAGES, dec_hdr);
+ pcep_obj_free_tlv(&tlv->header);
+}
+void test_pcep_tlv_create_lsp_error_code()
+{
+ struct pcep_object_tlv_lsp_error_code *tlv =
+ pcep_tlv_create_lsp_error_code(
+ PCEP_TLV_LSP_ERROR_CODE_RSVP_SIGNALING_ERROR);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t));
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1],
+ htonl(PCEP_TLV_LSP_ERROR_CODE_RSVP_SIGNALING_ERROR));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_rsvp_ipv4_error_spec()
+{
+ struct in_addr error_node_ip;
+ inet_pton(AF_INET, "192.168.1.1", &error_node_ip);
+ uint8_t error_code = 8;
+ uint16_t error_value = 0xaabb;
+
+ struct pcep_object_tlv_rsvp_error_spec *tlv =
+ pcep_tlv_create_rsvp_ipv4_error_spec(NULL, error_code,
+ error_value);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_rsvp_ipv4_error_spec(&error_node_ip, error_code,
+ error_value);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 12);
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_rsvp_ipv6_error_spec()
+{
+ struct in6_addr error_node_ip;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &error_node_ip);
+ uint8_t error_code = 8;
+ uint16_t error_value = 0xaabb;
+
+ struct pcep_object_tlv_rsvp_error_spec *tlv =
+ pcep_tlv_create_rsvp_ipv6_error_spec(NULL, error_code,
+ error_value);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_rsvp_ipv6_error_spec(&error_node_ip, error_code,
+ error_value);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 24);
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_nopath_vector()
+{
+ uint32_t enterprise_number = 0x01020304;
+ uint32_t enterprise_specific_info = 0x05060708;
+
+ struct pcep_object_tlv_vendor_info *tlv = pcep_tlv_create_vendor_info(
+ enterprise_number, enterprise_specific_info);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_VENDOR_INFO);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 8);
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(enterprise_number));
+ CU_ASSERT_EQUAL(uint32_ptr[2], htonl(enterprise_specific_info));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_arbitrary()
+{
+ char data[16] = "Some Data";
+ uint16_t data_length = 9;
+ uint16_t tlv_id_unknown = 1; // 65505; // Whatever id to be created
+ struct pcep_object_tlv_arbitrary *tlv = pcep_tlv_create_tlv_arbitrary(
+ data, data_length, tlv_id_unknown);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, tlv_id_unknown);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, data_length);
+ /* Test the padding is correct */
+ CU_ASSERT_EQUAL(
+ 0, strncmp((char *)&(tlv->header.encoded_tlv[4]), &data[0], 4));
+ CU_ASSERT_EQUAL(
+ 0, strncmp((char *)&(tlv->header.encoded_tlv[8]), &data[4], 4));
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[11], 't');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[12], 'a');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[13], 0);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[14], 0);
+ pcep_obj_free_tlv(&tlv->header);
+
+ reset_tlv_buffer();
+ tlv = pcep_tlv_create_tlv_arbitrary(data, 3, tlv_id_unknown);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, tlv_id_unknown);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 3);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[4], 'S');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[5], 'o');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[6], 'm');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[7], 0);
+
+ pcep_obj_free_tlv(&tlv->header);
+}
diff --git a/pceplib/test/pcep_msg_tlvs_test.h b/pceplib/test/pcep_msg_tlvs_test.h
new file mode 100644
index 000000000..a961d7e47
--- /dev/null
+++ b/pceplib/test/pcep_msg_tlvs_test.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+#ifndef PCEP_MSG_TLVS_TEST_H_
+#define PCEP_MSG_TLVS_TEST_H_
+
+int pcep_tlvs_test_suite_setup(void);
+int pcep_tlvs_test_suite_teardown(void);
+void pcep_tlvs_test_setup(void);
+void pcep_tlvs_test_teardown(void);
+void test_pcep_tlv_create_stateful_pce_capability(void);
+void test_pcep_tlv_create_speaker_entity_id(void);
+void test_pcep_tlv_create_lsp_db_version(void);
+void test_pcep_tlv_create_path_setup_type(void);
+void test_pcep_tlv_create_path_setup_type_capability(void);
+void test_pcep_tlv_create_sr_pce_capability(void);
+void test_pcep_tlv_create_symbolic_path_name(void);
+void test_pcep_tlv_create_ipv4_lsp_identifiers(void);
+void test_pcep_tlv_create_ipv6_lsp_identifiers(void);
+void test_pcep_tlv_create_lsp_error_code(void);
+void test_pcep_tlv_create_rsvp_ipv4_error_spec(void);
+void test_pcep_tlv_create_rsvp_ipv6_error_spec(void);
+void test_pcep_tlv_create_srpag_pol_id_ipv4(void);
+void test_pcep_tlv_create_srpag_pol_id_ipv6(void);
+void test_pcep_tlv_create_srpag_pol_name(void);
+void test_pcep_tlv_create_srpag_cp_id(void);
+void test_pcep_tlv_create_srpag_cp_pref(void);
+void test_pcep_tlv_create_nopath_vector(void);
+void test_pcep_tlv_create_arbitrary(void);
+
+
+#endif
diff --git a/pceplib/test/pcep_msg_tools_test.c b/pceplib/test/pcep_msg_tools_test.c
new file mode 100644
index 000000000..a1260b118
--- /dev/null
+++ b/pceplib/test/pcep_msg_tools_test.c
@@ -0,0 +1,1258 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_messages.h"
+#include "pcep_msg_tools.h"
+#include "pcep_msg_tools_test.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+const uint8_t any_obj_class = 255;
+
+uint16_t pcep_open_hexbyte_strs_length = 28;
+const char *pcep_open_odl_hexbyte_strs[] = {
+ "20", "01", "00", "1c", "01", "10", "00", "18", "20", "1e",
+ "78", "55", "00", "10", "00", "04", "00", "00", "00", "3f",
+ "00", "1a", "00", "04", "00", "00", "00", "00"};
+
+/* PCEP INITIATE str received from ODL with 4 objects: [SRP, LSP, Endpoints,
+ * ERO] The LSP has a SYMBOLIC_PATH_NAME TLV. The ERO has 2 IPV4 Endpoints. */
+uint16_t pcep_initiate_hexbyte_strs_length = 68;
+const char *pcep_initiate_hexbyte_strs[] = {
+ "20", "0c", "00", "44", "21", "12", "00", "0c", "00", "00", "00", "00",
+ "00", "00", "00", "01", "20", "10", "00", "14", "00", "00", "00", "09",
+ "00", "11", "00", "08", "66", "61", "39", "33", "33", "39", "32", "39",
+ "04", "10", "00", "0c", "7f", "00", "00", "01", "28", "28", "28", "28",
+ "07", "10", "00", "14", "01", "08", "0a", "00", "01", "01", "18", "00",
+ "01", "08", "0a", "00", "07", "04", "18", "00"};
+
+uint16_t pcep_initiate2_hexbyte_strs_length = 72;
+const char *pcep_initiate2_hexbyte_strs[] = {
+ "20", "0c", "00", "48", "21", "12", "00", "14", "00", "00", "00", "00",
+ "00", "00", "00", "01", "00", "1c", "00", "04", "00", "00", "00", "01",
+ "20", "10", "00", "14", "00", "00", "00", "09", "00", "11", "00", "08",
+ "36", "65", "31", "31", "38", "39", "32", "31", "04", "10", "00", "0c",
+ "c0", "a8", "14", "05", "01", "01", "01", "01", "07", "10", "00", "10",
+ "05", "0c", "10", "01", "03", "e8", "a0", "00", "01", "01", "01", "01"};
+
+uint16_t pcep_update_hexbyte_strs_length = 48;
+const char *pcep_update_hexbyte_strs[] = {
+ "20", "0b", "00", "30", "21", "12", "00", "14", "00", "00", "00", "00",
+ "00", "00", "00", "01", "00", "1c", "00", "04", "00", "00", "00", "01",
+ "20", "10", "00", "08", "00", "02", "a0", "09", "07", "10", "00", "10",
+ "05", "0c", "10", "01", "03", "e8", "a0", "00", "01", "01", "01", "01"};
+
+/* Test that pcep_msg_read() can read multiple messages in 1 call */
+uint16_t pcep_open_initiate_hexbyte_strs_length = 100;
+const char *pcep_open_initiate_odl_hexbyte_strs[] = {
+ "20", "01", "00", "1c", "01", "10", "00", "18", "20", "1e", "78", "55",
+ "00", "10", "00", "04", "00", "00", "00", "3f", "00", "1a", "00", "04",
+ "00", "00", "00", "00", "20", "0c", "00", "48", "21", "12", "00", "14",
+ "00", "00", "00", "00", "00", "00", "00", "01", "00", "1c", "00", "04",
+ "00", "00", "00", "01", "20", "10", "00", "14", "00", "00", "00", "09",
+ "00", "11", "00", "08", "36", "65", "31", "31", "38", "39", "32", "31",
+ "04", "10", "00", "0c", "c0", "a8", "14", "05", "01", "01", "01", "01",
+ "07", "10", "00", "10", "05", "0c", "10", "01", "03", "e8", "a0", "00",
+ "01", "01", "01", "01"};
+
+uint16_t pcep_open_cisco_pce_hexbyte_strs_length = 28;
+const char *pcep_open_cisco_pce_hexbyte_strs[] = {
+ "20", "01", "00", "1c", "01", "10", "00", "18", "20", "3c",
+ "78", "00", "00", "10", "00", "04", "00", "00", "00", "05",
+ "00", "1a", "00", "04", "00", "00", "00", "0a"};
+
+uint16_t pcep_update_cisco_pce_hexbyte_strs_length = 100;
+const char *pcep_update_cisco_pce_hexbyte_strs[] = {
+ "20", "0b", "00", "64", "21", "10", "00", "14", "00", "00", "00", "00",
+ "00", "00", "00", "01", "00", "1c", "00", "04", "00", "00", "00", "01",
+ "20", "10", "00", "18", "80", "00", "f0", "89", "00", "07", "00", "0c",
+ "00", "00", "00", "09", "00", "03", "00", "04", "00", "00", "00", "01",
+ "07", "10", "00", "28", "24", "0c", "10", "01", "04", "65", "50", "00",
+ "0a", "0a", "0a", "05", "24", "0c", "10", "01", "04", "65", "20", "00",
+ "0a", "0a", "0a", "02", "24", "0c", "10", "01", "04", "65", "10", "00",
+ "0a", "0a", "0a", "01", "06", "10", "00", "0c", "00", "00", "00", "02",
+ "41", "f0", "00", "00"};
+
+uint16_t pcep_report_cisco_pcc_hexbyte_strs_length = 148;
+const char *pcep_report_cisco_pcc_hexbyte_strs[] = {
+ "20", "0a", "00", "94", "21", "10", "00", "14", "00", "00", "00", "00",
+ "00", "00", "00", "00", "00", "1c", "00", "04", "00", "00", "00", "01",
+ "20", "10", "00", "3c", "80", "00", "f0", "09", "00", "12", "00", "10",
+ "0a", "0a", "0a", "06", "00", "02", "00", "0f", "0a", "0a", "0a", "06",
+ "0a", "0a", "0a", "01", "00", "11", "00", "0d", "63", "66", "67", "5f",
+ "52", "36", "2d", "74", "6f", "2d", "52", "31", "00", "00", "00", "00",
+ "ff", "e1", "00", "06", "00", "00", "05", "dd", "70", "00", "00", "00",
+ "07", "10", "00", "04", "09", "10", "00", "14", "00", "00", "00", "00",
+ "00", "00", "00", "00", "00", "00", "00", "00", "07", "07", "01", "00",
+ "05", "12", "00", "08", "00", "00", "00", "00", "05", "52", "00", "08",
+ "00", "00", "00", "00", "06", "10", "00", "0c", "00", "00", "00", "02",
+ "00", "00", "00", "00", "06", "10", "00", "0c", "00", "00", "01", "04",
+ "41", "80", "00", "00"};
+
+/* Cisco PcInitiate with the following objects:
+ * SRP, LSP, Endpoint, Inter-layer, Switch-layer, ERO
+ */
+uint16_t pcep_initiate_cisco_pcc_hexbyte_strs_length = 104;
+const char *pcep_initiate_cisco_pcc_hexbyte_strs[] = {
+ "20", "0c", "00", "68", "21", "10", "00", "14", "00", "00", "00", "00",
+ "00", "00", "00", "01", "00", "1c", "00", "04", "00", "00", "00", "01",
+ "20", "10", "00", "30", "00", "00", "00", "89", "00", "11", "00", "13",
+ "50", "4f", "4c", "31", "5f", "50", "43", "49", "4e", "49", "54", "41",
+ "54", "45", "5f", "54", "45", "53", "54", "00", "00", "07", "00", "0c",
+ "00", "00", "00", "09", "00", "03", "00", "04", "00", "00", "00", "01",
+ "04", "10", "00", "0c", "0a", "0a", "0a", "0a", "0a", "0a", "0a", "04",
+ "24", "10", "00", "08", "00", "00", "01", "4d", "25", "10", "00", "08",
+ "00", "00", "00", "64", "07", "10", "00", "04"};
+
+struct pcep_message *create_message(uint8_t msg_type, uint8_t obj1_class,
+ uint8_t obj2_class, uint8_t obj3_class,
+ uint8_t obj4_class);
+int convert_hexstrs_to_binary(const char *hexbyte_strs[],
+ uint16_t hexbyte_strs_length);
+
+int pcep_tools_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_tools_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+void pcep_tools_test_setup(void)
+{
+}
+
+void pcep_tools_test_teardown(void)
+{
+}
+
+/* Reads an array of hexbyte strs, and writes them to a temporary file.
+ * The caller should close the returned file. */
+int convert_hexstrs_to_binary(const char *hexbyte_strs[],
+ uint16_t hexbyte_strs_length)
+{
+ int fd = fileno(tmpfile());
+
+ int i = 0;
+ for (; i < hexbyte_strs_length; i++) {
+ uint8_t byte = (uint8_t)strtol(hexbyte_strs[i], 0, 16);
+ if (write(fd, (char *)&byte, 1) < 0) {
+ return -1;
+ }
+ }
+
+ /* Go back to the beginning of the file */
+ lseek(fd, 0, SEEK_SET);
+ return fd;
+}
+
+static bool pcep_obj_has_tlv(struct pcep_object_header *obj_hdr)
+{
+ if (obj_hdr->tlv_list == NULL) {
+ return false;
+ }
+
+ return (obj_hdr->tlv_list->num_entries > 0);
+}
+
+void test_pcep_msg_read_pcep_initiate()
+{
+ int fd = convert_hexstrs_to_binary(pcep_initiate_hexbyte_strs,
+ pcep_initiate_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 4);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_INITIATE);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_initiate_hexbyte_strs_length);
+
+ /* Verify each of the object types */
+
+ /* SRP object */
+ double_linked_list_node *node = msg->obj_list->head;
+ struct pcep_object_header *obj_hdr =
+ (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_SRP);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_SRP);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length,
+ pcep_object_get_length_by_hdr(obj_hdr));
+ CU_ASSERT_FALSE(pcep_obj_has_tlv(obj_hdr));
+
+ /* LSP object and its TLV*/
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_LSP);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_LSP);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20);
+ CU_ASSERT_EQUAL(((struct pcep_object_lsp *)obj_hdr)->plsp_id, 0);
+ CU_ASSERT_TRUE(((struct pcep_object_lsp *)obj_hdr)->flag_d);
+ CU_ASSERT_TRUE(((struct pcep_object_lsp *)obj_hdr)->flag_a);
+ CU_ASSERT_FALSE(((struct pcep_object_lsp *)obj_hdr)->flag_s);
+ CU_ASSERT_FALSE(((struct pcep_object_lsp *)obj_hdr)->flag_r);
+ CU_ASSERT_FALSE(((struct pcep_object_lsp *)obj_hdr)->flag_c);
+
+ /* LSP TLV */
+ CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr));
+ CU_ASSERT_EQUAL(obj_hdr->tlv_list->num_entries, 1);
+ struct pcep_object_tlv_header *tlv =
+ (struct pcep_object_tlv_header *)obj_hdr->tlv_list->head->data;
+ CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME);
+ CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 8);
+
+ /* Endpoints object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ENDPOINTS);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ENDPOINT_IPV4);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length,
+ pcep_object_get_length_by_hdr(obj_hdr));
+ CU_ASSERT_FALSE(pcep_obj_has_tlv(obj_hdr));
+
+ /* ERO object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ERO);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ERO);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20);
+
+ /* ERO Subobjects */
+ double_linked_list *ero_subobj_list =
+ ((struct pcep_object_ro *)obj_hdr)->sub_objects;
+ CU_ASSERT_PTR_NOT_NULL(ero_subobj_list);
+ CU_ASSERT_EQUAL(ero_subobj_list->num_entries, 2);
+ double_linked_list_node *subobj_node = ero_subobj_list->head;
+ struct pcep_object_ro_subobj *subobj_hdr =
+ (struct pcep_object_ro_subobj *)subobj_node->data;
+ CU_ASSERT_EQUAL(subobj_hdr->ro_subobj_type, RO_SUBOBJ_TYPE_IPV4);
+ struct in_addr ero_subobj_ip;
+ inet_pton(AF_INET, "10.0.1.1", &ero_subobj_ip);
+ CU_ASSERT_EQUAL(
+ ((struct pcep_ro_subobj_ipv4 *)subobj_hdr)->ip_addr.s_addr,
+ ero_subobj_ip.s_addr);
+ CU_ASSERT_EQUAL(
+ ((struct pcep_ro_subobj_ipv4 *)subobj_hdr)->prefix_length, 24);
+
+ subobj_hdr =
+ (struct pcep_object_ro_subobj *)subobj_node->next_node->data;
+ CU_ASSERT_EQUAL(subobj_hdr->ro_subobj_type, RO_SUBOBJ_TYPE_IPV4);
+ inet_pton(AF_INET, "10.0.7.4", &ero_subobj_ip);
+ CU_ASSERT_EQUAL(
+ ((struct pcep_ro_subobj_ipv4 *)subobj_hdr)->ip_addr.s_addr,
+ ero_subobj_ip.s_addr);
+ CU_ASSERT_EQUAL(
+ ((struct pcep_ro_subobj_ipv4 *)subobj_hdr)->prefix_length, 24);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+
+void test_pcep_msg_read_pcep_initiate2()
+{
+ int fd = convert_hexstrs_to_binary(pcep_initiate2_hexbyte_strs,
+ pcep_initiate2_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 4);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_INITIATE);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_initiate2_hexbyte_strs_length);
+
+ /* Verify each of the object types */
+
+ /* SRP object */
+ double_linked_list_node *node = msg->obj_list->head;
+ struct pcep_object_header *obj_hdr =
+ (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_SRP);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_SRP);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20);
+ CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr));
+ /* TODO test the TLVs */
+
+ /* LSP object and its TLV*/
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_LSP);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_LSP);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20);
+
+ /* LSP TLV */
+ CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr));
+ CU_ASSERT_EQUAL(obj_hdr->tlv_list->num_entries, 1);
+ struct pcep_object_tlv_header *tlv =
+ (struct pcep_object_tlv_header *)obj_hdr->tlv_list->head->data;
+ CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME);
+ CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 8);
+
+ /* Endpoints object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ENDPOINTS);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ENDPOINT_IPV4);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length,
+ pcep_object_get_length_by_hdr(obj_hdr));
+ CU_ASSERT_FALSE(pcep_obj_has_tlv(obj_hdr));
+
+ /* ERO object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ERO);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ERO);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 16);
+
+ /* ERO Subobjects */
+ double_linked_list *ero_subobj_list =
+ ((struct pcep_object_ro *)obj_hdr)->sub_objects;
+ CU_ASSERT_PTR_NOT_NULL(ero_subobj_list);
+ CU_ASSERT_EQUAL(ero_subobj_list->num_entries, 0);
+ double_linked_list_node *subobj_node = ero_subobj_list->head;
+ CU_ASSERT_PTR_NULL(subobj_node);
+ /* We no longer support draft07 SR sub-object type=5, and only support
+ type=36 struct pcep_object_ro_subobj *subobj_hdr = (struct
+ pcep_object_ro_subobj *) subobj_node->data;
+ CU_ASSERT_EQUAL(subobj_hdr->ro_subobj_type, RO_SUBOBJ_TYPE_SR);
+ struct pcep_ro_subobj_sr *subobj_sr = (struct pcep_ro_subobj_sr *)
+ subobj_hdr; CU_ASSERT_EQUAL(subobj_sr->nai_type,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE); CU_ASSERT_TRUE(subobj_sr->flag_m);
+ CU_ASSERT_FALSE(subobj_sr->flag_c);
+ CU_ASSERT_FALSE(subobj_sr->flag_s);
+ CU_ASSERT_FALSE(subobj_sr->flag_f);
+ CU_ASSERT_EQUAL(subobj_sr->sid, 65576960);
+ CU_ASSERT_EQUAL(*((uint32_t *) subobj_sr->nai_list->head->data),
+ 0x01010101);
+ */
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_open()
+{
+ int fd = convert_hexstrs_to_binary(pcep_open_odl_hexbyte_strs,
+ pcep_open_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_OPEN);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_open_hexbyte_strs_length);
+
+ /* Verify the Open message */
+ struct pcep_object_header *obj_hdr =
+ (struct pcep_object_header *)msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_OPEN);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 24);
+ CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr));
+
+ /* Open TLV: Stateful PCE Capability */
+ CU_ASSERT_EQUAL(obj_hdr->tlv_list->num_entries, 2);
+ double_linked_list_node *tlv_node = obj_hdr->tlv_list->head;
+ struct pcep_object_tlv_header *tlv =
+ (struct pcep_object_tlv_header *)tlv_node->data;
+ CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 4);
+
+ /* Open TLV: SR PCE Capability */
+ tlv_node = tlv_node->next_node;
+ tlv = (struct pcep_object_tlv_header *)tlv_node->data;
+ CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 4);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_update()
+{
+ int fd = convert_hexstrs_to_binary(pcep_update_hexbyte_strs,
+ pcep_update_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 3);
+
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_UPDATE);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_update_hexbyte_strs_length);
+
+ /* Verify each of the object types */
+
+ double_linked_list_node *node = msg->obj_list->head;
+
+ /* SRP object */
+ struct pcep_object_header *obj_hdr =
+ (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_SRP);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_SRP);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20);
+ CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr));
+
+ /* SRP TLV */
+ CU_ASSERT_EQUAL(obj_hdr->tlv_list->num_entries, 1);
+ struct pcep_object_tlv_header *tlv =
+ (struct pcep_object_tlv_header *)obj_hdr->tlv_list->head->data;
+ CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE);
+ CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 4);
+ /* TODO verify the path setup type */
+
+ /* LSP object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_LSP);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_LSP);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length,
+ pcep_object_get_length_by_hdr(obj_hdr));
+ CU_ASSERT_FALSE(pcep_obj_has_tlv(obj_hdr));
+
+ /* ERO object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ERO);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ERO);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 16);
+
+ /* ERO Subobjects */
+ double_linked_list *ero_subobj_list =
+ ((struct pcep_object_ro *)obj_hdr)->sub_objects;
+ CU_ASSERT_PTR_NOT_NULL(ero_subobj_list);
+ CU_ASSERT_EQUAL(ero_subobj_list->num_entries, 0);
+ double_linked_list_node *subobj_node = ero_subobj_list->head;
+ CU_ASSERT_PTR_NULL(subobj_node);
+ /* We no longer support draft07 SR sub-object type=5, and only support
+ type=36 struct pcep_object_ro_subobj *subobj_hdr = (struct
+ pcep_object_ro_subobj *) subobj_node->data;
+ CU_ASSERT_EQUAL(subobj_hdr->ro_subobj_type, RO_SUBOBJ_TYPE_SR);
+ struct pcep_ro_subobj_sr *subobj_sr = (struct pcep_ro_subobj_sr *)
+ subobj_hdr; CU_ASSERT_EQUAL(subobj_sr->nai_type,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE); CU_ASSERT_TRUE(subobj_sr->flag_m);
+ CU_ASSERT_FALSE(subobj_sr->flag_c);
+ CU_ASSERT_FALSE(subobj_sr->flag_s);
+ CU_ASSERT_FALSE(subobj_sr->flag_f);
+ CU_ASSERT_EQUAL(subobj_sr->sid, 65576960);
+ CU_ASSERT_EQUAL(*((uint32_t *) subobj_sr->nai_list->head->data),
+ 0x01010101);
+ */
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_open_initiate()
+{
+ int fd = convert_hexstrs_to_binary(
+ pcep_open_initiate_odl_hexbyte_strs,
+ pcep_open_initiate_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 2);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_OPEN);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_open_hexbyte_strs_length);
+
+ msg = (struct pcep_message *)msg_list->head->next_node->data;
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 4);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_INITIATE);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_initiate2_hexbyte_strs_length);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_open_cisco_pce()
+{
+ int fd = convert_hexstrs_to_binary(
+ pcep_open_cisco_pce_hexbyte_strs,
+ pcep_open_cisco_pce_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_OPEN);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_open_hexbyte_strs_length);
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 1);
+
+ /* Open object */
+ struct pcep_object_open *open =
+ (struct pcep_object_open *)msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(open->header.object_class, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_EQUAL(open->header.object_type, PCEP_OBJ_TYPE_OPEN);
+ CU_ASSERT_EQUAL(open->header.encoded_object_length, 24);
+ CU_ASSERT_EQUAL(open->open_deadtimer, 120);
+ CU_ASSERT_EQUAL(open->open_keepalive, 60);
+ CU_ASSERT_EQUAL(open->open_sid, 0);
+ CU_ASSERT_EQUAL(open->open_version, 1);
+ CU_ASSERT_PTR_NOT_NULL(open->header.tlv_list);
+ CU_ASSERT_EQUAL(open->header.tlv_list->num_entries, 2);
+
+ /* Stateful PCE Capability TLV */
+ double_linked_list_node *tlv_node = open->header.tlv_list->head;
+ struct pcep_object_tlv_stateful_pce_capability *pce_cap_tlv =
+ (struct pcep_object_tlv_stateful_pce_capability *)
+ tlv_node->data;
+ CU_ASSERT_EQUAL(pce_cap_tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(pce_cap_tlv->header.encoded_tlv_length, 4);
+ CU_ASSERT_TRUE(pce_cap_tlv->flag_u_lsp_update_capability);
+ CU_ASSERT_TRUE(pce_cap_tlv->flag_i_lsp_instantiation_capability);
+ CU_ASSERT_FALSE(pce_cap_tlv->flag_s_include_db_version);
+ CU_ASSERT_FALSE(pce_cap_tlv->flag_t_triggered_resync);
+ CU_ASSERT_FALSE(pce_cap_tlv->flag_d_delta_lsp_sync);
+ CU_ASSERT_FALSE(pce_cap_tlv->flag_f_triggered_initial_sync);
+
+ /* SR PCE Capability TLV */
+ tlv_node = tlv_node->next_node;
+ struct pcep_object_tlv_sr_pce_capability *sr_pce_cap_tlv =
+ (struct pcep_object_tlv_sr_pce_capability *)tlv_node->data;
+ CU_ASSERT_EQUAL(sr_pce_cap_tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(sr_pce_cap_tlv->header.encoded_tlv_length, 4);
+ CU_ASSERT_FALSE(sr_pce_cap_tlv->flag_n);
+ CU_ASSERT_FALSE(sr_pce_cap_tlv->flag_x);
+ CU_ASSERT_EQUAL(sr_pce_cap_tlv->max_sid_depth, 10);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_update_cisco_pce()
+{
+ int fd = convert_hexstrs_to_binary(
+ pcep_update_cisco_pce_hexbyte_strs,
+ pcep_update_cisco_pce_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_UPDATE);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_update_cisco_pce_hexbyte_strs_length);
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 4);
+
+ /* SRP object */
+ double_linked_list_node *obj_node = msg->obj_list->head;
+ struct pcep_object_srp *srp = (struct pcep_object_srp *)obj_node->data;
+ CU_ASSERT_EQUAL(srp->header.object_class, PCEP_OBJ_CLASS_SRP);
+ CU_ASSERT_EQUAL(srp->header.object_type, PCEP_OBJ_TYPE_SRP);
+ CU_ASSERT_EQUAL(srp->header.encoded_object_length, 20);
+ CU_ASSERT_PTR_NOT_NULL(srp->header.tlv_list);
+ CU_ASSERT_EQUAL(srp->header.tlv_list->num_entries, 1);
+ CU_ASSERT_EQUAL(srp->srp_id_number, 1);
+ CU_ASSERT_FALSE(srp->flag_lsp_remove);
+
+ /* SRP Path Setup Type TLV */
+ double_linked_list_node *tlv_node = srp->header.tlv_list->head;
+ struct pcep_object_tlv_path_setup_type *pst_tlv =
+ (struct pcep_object_tlv_path_setup_type *)tlv_node->data;
+ CU_ASSERT_EQUAL(pst_tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE);
+ CU_ASSERT_EQUAL(pst_tlv->header.encoded_tlv_length, 4);
+ CU_ASSERT_EQUAL(pst_tlv->path_setup_type, 1);
+
+ /* LSP object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_lsp *lsp = (struct pcep_object_lsp *)obj_node->data;
+ CU_ASSERT_EQUAL(lsp->header.object_class, PCEP_OBJ_CLASS_LSP);
+ CU_ASSERT_EQUAL(lsp->header.object_type, PCEP_OBJ_TYPE_LSP);
+ CU_ASSERT_EQUAL(lsp->header.encoded_object_length, 24);
+ CU_ASSERT_PTR_NOT_NULL(lsp->header.tlv_list);
+ CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 1);
+ CU_ASSERT_EQUAL(lsp->plsp_id, 524303);
+ CU_ASSERT_EQUAL(lsp->operational_status, PCEP_LSP_OPERATIONAL_DOWN);
+ CU_ASSERT_TRUE(lsp->flag_a);
+ CU_ASSERT_TRUE(lsp->flag_c);
+ CU_ASSERT_TRUE(lsp->flag_d);
+ CU_ASSERT_FALSE(lsp->flag_r);
+ CU_ASSERT_FALSE(lsp->flag_s);
+
+ /* LSP Vendor Info TLV */
+ tlv_node = lsp->header.tlv_list->head;
+ struct pcep_object_tlv_vendor_info *vendor_tlv =
+ (struct pcep_object_tlv_vendor_info *)tlv_node->data;
+ CU_ASSERT_EQUAL(vendor_tlv->header.type, PCEP_OBJ_TLV_TYPE_VENDOR_INFO);
+ CU_ASSERT_EQUAL(vendor_tlv->header.encoded_tlv_length, 12);
+ CU_ASSERT_EQUAL(vendor_tlv->enterprise_number, 9);
+ CU_ASSERT_EQUAL(vendor_tlv->enterprise_specific_info, 0x00030004);
+
+ /* ERO object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_ro *ero = (struct pcep_object_ro *)obj_node->data;
+ CU_ASSERT_EQUAL(ero->header.object_class, PCEP_OBJ_CLASS_ERO);
+ CU_ASSERT_EQUAL(ero->header.object_type, PCEP_OBJ_TYPE_ERO);
+ CU_ASSERT_EQUAL(ero->header.encoded_object_length, 40);
+ CU_ASSERT_PTR_NULL(ero->header.tlv_list);
+ CU_ASSERT_PTR_NOT_NULL(ero->sub_objects);
+ CU_ASSERT_EQUAL(ero->sub_objects->num_entries, 3);
+
+ /* ERO Subobjects */
+ double_linked_list_node *ero_subobj_node = ero->sub_objects->head;
+ struct pcep_ro_subobj_sr *sr_subobj_ipv4_node =
+ (struct pcep_ro_subobj_sr *)ero_subobj_node->data;
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->ro_subobj.ro_subobj_type,
+ RO_SUBOBJ_TYPE_SR);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->ro_subobj.flag_subobj_loose_hop);
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->nai_type,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE);
+ CU_ASSERT_TRUE(sr_subobj_ipv4_node->flag_m);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_c);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_f);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_s);
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->sid, 73748480);
+ CU_ASSERT_EQUAL(
+ *((uint32_t *)sr_subobj_ipv4_node->nai_list->head->data),
+ htonl(0x0a0a0a05));
+
+ ero_subobj_node = ero_subobj_node->next_node;
+ sr_subobj_ipv4_node = (struct pcep_ro_subobj_sr *)ero_subobj_node->data;
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->ro_subobj.ro_subobj_type,
+ RO_SUBOBJ_TYPE_SR);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->ro_subobj.flag_subobj_loose_hop);
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->nai_type,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE);
+ CU_ASSERT_TRUE(sr_subobj_ipv4_node->flag_m);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_c);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_f);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_s);
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->sid, 73736192);
+ CU_ASSERT_EQUAL(
+ *((uint32_t *)sr_subobj_ipv4_node->nai_list->head->data),
+ htonl(0x0a0a0a02));
+
+ ero_subobj_node = ero_subobj_node->next_node;
+ sr_subobj_ipv4_node = (struct pcep_ro_subobj_sr *)ero_subobj_node->data;
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->ro_subobj.ro_subobj_type,
+ RO_SUBOBJ_TYPE_SR);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->ro_subobj.flag_subobj_loose_hop);
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->nai_type,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE);
+ CU_ASSERT_TRUE(sr_subobj_ipv4_node->flag_m);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_c);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_f);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_s);
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->sid, 73732096);
+ CU_ASSERT_EQUAL(
+ *((uint32_t *)sr_subobj_ipv4_node->nai_list->head->data),
+ htonl(0x0a0a0a01));
+
+ /* Metric object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_metric *metric =
+ (struct pcep_object_metric *)obj_node->data;
+ CU_ASSERT_EQUAL(metric->header.object_class, PCEP_OBJ_CLASS_METRIC);
+ CU_ASSERT_EQUAL(metric->header.object_type, PCEP_OBJ_TYPE_METRIC);
+ CU_ASSERT_EQUAL(metric->header.encoded_object_length, 12);
+ CU_ASSERT_PTR_NULL(metric->header.tlv_list);
+ CU_ASSERT_FALSE(metric->flag_b);
+ CU_ASSERT_FALSE(metric->flag_c);
+ CU_ASSERT_EQUAL(metric->type, PCEP_METRIC_TE);
+ CU_ASSERT_EQUAL(metric->value, 30.0);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_report_cisco_pcc()
+{
+ int fd = convert_hexstrs_to_binary(
+ pcep_report_cisco_pcc_hexbyte_strs,
+ pcep_report_cisco_pcc_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_REPORT);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_report_cisco_pcc_hexbyte_strs_length);
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 8);
+
+ /* SRP object */
+ double_linked_list_node *obj_node = msg->obj_list->head;
+ struct pcep_object_srp *srp = (struct pcep_object_srp *)obj_node->data;
+ CU_ASSERT_EQUAL(srp->header.object_class, PCEP_OBJ_CLASS_SRP);
+ CU_ASSERT_EQUAL(srp->header.object_type, PCEP_OBJ_TYPE_SRP);
+ CU_ASSERT_EQUAL(srp->header.encoded_object_length, 20);
+ CU_ASSERT_PTR_NOT_NULL(srp->header.tlv_list);
+ CU_ASSERT_EQUAL(srp->header.tlv_list->num_entries, 1);
+ CU_ASSERT_EQUAL(srp->srp_id_number, 0);
+ CU_ASSERT_FALSE(srp->flag_lsp_remove);
+
+ /* SRP Path Setup Type TLV */
+ double_linked_list_node *tlv_node = srp->header.tlv_list->head;
+ struct pcep_object_tlv_path_setup_type *pst_tlv =
+ (struct pcep_object_tlv_path_setup_type *)tlv_node->data;
+ CU_ASSERT_EQUAL(pst_tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE);
+ CU_ASSERT_EQUAL(pst_tlv->header.encoded_tlv_length, 4);
+ CU_ASSERT_EQUAL(pst_tlv->path_setup_type, 1);
+
+ /* LSP object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_lsp *lsp = (struct pcep_object_lsp *)obj_node->data;
+ CU_ASSERT_EQUAL(lsp->header.object_class, PCEP_OBJ_CLASS_LSP);
+ CU_ASSERT_EQUAL(lsp->header.object_type, PCEP_OBJ_TYPE_LSP);
+ CU_ASSERT_EQUAL(lsp->header.encoded_object_length, 60);
+ CU_ASSERT_PTR_NOT_NULL(lsp->header.tlv_list);
+ /* The TLV with ID 65505 is not recognized, and its not in the list */
+ CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 2);
+ CU_ASSERT_EQUAL(lsp->plsp_id, 524303);
+ CU_ASSERT_EQUAL(lsp->operational_status, PCEP_LSP_OPERATIONAL_DOWN);
+ CU_ASSERT_TRUE(lsp->flag_a);
+ CU_ASSERT_TRUE(lsp->flag_d);
+ CU_ASSERT_FALSE(lsp->flag_c);
+ CU_ASSERT_FALSE(lsp->flag_r);
+ CU_ASSERT_FALSE(lsp->flag_s);
+
+ /* LSP IPv4 LSP Identifier TLV */
+ tlv_node = lsp->header.tlv_list->head;
+ struct pcep_object_tlv_ipv4_lsp_identifier *ipv4_lsp_id =
+ (struct pcep_object_tlv_ipv4_lsp_identifier *)tlv_node->data;
+ CU_ASSERT_EQUAL(ipv4_lsp_id->header.type,
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS);
+ CU_ASSERT_EQUAL(ipv4_lsp_id->header.encoded_tlv_length, 16);
+ CU_ASSERT_EQUAL(ipv4_lsp_id->ipv4_tunnel_sender.s_addr,
+ htonl(0x0a0a0a06));
+ CU_ASSERT_EQUAL(ipv4_lsp_id->ipv4_tunnel_endpoint.s_addr,
+ htonl(0x0a0a0a01));
+ CU_ASSERT_EQUAL(ipv4_lsp_id->extended_tunnel_id.s_addr,
+ htonl(0x0a0a0a06));
+ CU_ASSERT_EQUAL(ipv4_lsp_id->tunnel_id, 15);
+ CU_ASSERT_EQUAL(ipv4_lsp_id->lsp_id, 2);
+
+ /* LSP Symbolic Path Name TLV */
+ tlv_node = tlv_node->next_node;
+ struct pcep_object_tlv_symbolic_path_name *sym_path_name =
+ (struct pcep_object_tlv_symbolic_path_name *)tlv_node->data;
+ CU_ASSERT_EQUAL(sym_path_name->header.type,
+ PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME);
+ CU_ASSERT_EQUAL(sym_path_name->header.encoded_tlv_length, 13);
+ CU_ASSERT_EQUAL(sym_path_name->symbolic_path_name_length, 13);
+ CU_ASSERT_EQUAL(
+ strncmp(sym_path_name->symbolic_path_name, "cfg_R6-to-R1", 13),
+ 0);
+
+ /* ERO object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_ro *ero = (struct pcep_object_ro *)obj_node->data;
+ CU_ASSERT_EQUAL(ero->header.object_class, PCEP_OBJ_CLASS_ERO);
+ CU_ASSERT_EQUAL(ero->header.object_type, PCEP_OBJ_TYPE_ERO);
+ CU_ASSERT_EQUAL(ero->header.encoded_object_length, 4);
+ CU_ASSERT_PTR_NULL(ero->header.tlv_list);
+ CU_ASSERT_PTR_NOT_NULL(ero->sub_objects);
+ CU_ASSERT_EQUAL(ero->sub_objects->num_entries, 0);
+
+ /* LSPA object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_lspa *lspa =
+ (struct pcep_object_lspa *)obj_node->data;
+ CU_ASSERT_EQUAL(lspa->header.object_class, PCEP_OBJ_CLASS_LSPA);
+ CU_ASSERT_EQUAL(lspa->header.object_type, PCEP_OBJ_TYPE_LSPA);
+ CU_ASSERT_EQUAL(lspa->header.encoded_object_length, 20);
+ CU_ASSERT_PTR_NULL(lspa->header.tlv_list);
+ CU_ASSERT_TRUE(lspa->flag_local_protection);
+ CU_ASSERT_EQUAL(lspa->holding_priority, 7);
+ CU_ASSERT_EQUAL(lspa->setup_priority, 7);
+ CU_ASSERT_EQUAL(lspa->lspa_include_all, 0);
+ CU_ASSERT_EQUAL(lspa->lspa_include_any, 0);
+ CU_ASSERT_EQUAL(lspa->lspa_exclude_any, 0);
+
+ /* Bandwidth object 1 */
+ obj_node = obj_node->next_node;
+ struct pcep_object_bandwidth *bandwidth =
+ (struct pcep_object_bandwidth *)obj_node->data;
+ CU_ASSERT_EQUAL(bandwidth->header.object_class,
+ PCEP_OBJ_CLASS_BANDWIDTH);
+ CU_ASSERT_EQUAL(bandwidth->header.object_type,
+ PCEP_OBJ_TYPE_BANDWIDTH_REQ);
+ CU_ASSERT_EQUAL(bandwidth->header.encoded_object_length, 8);
+ CU_ASSERT_EQUAL(bandwidth->bandwidth, 0);
+
+ /* Bandwidth object 2 */
+ obj_node = obj_node->next_node;
+ bandwidth = (struct pcep_object_bandwidth *)obj_node->data;
+ CU_ASSERT_EQUAL(bandwidth->header.object_class,
+ PCEP_OBJ_CLASS_BANDWIDTH);
+ CU_ASSERT_EQUAL(bandwidth->header.object_type,
+ PCEP_OBJ_TYPE_BANDWIDTH_CISCO);
+ CU_ASSERT_EQUAL(bandwidth->header.encoded_object_length, 8);
+ CU_ASSERT_EQUAL(bandwidth->bandwidth, 0);
+
+ /* Metric object 1 */
+ obj_node = obj_node->next_node;
+ struct pcep_object_metric *metric =
+ (struct pcep_object_metric *)obj_node->data;
+ CU_ASSERT_EQUAL(metric->header.object_class, PCEP_OBJ_CLASS_METRIC);
+ CU_ASSERT_EQUAL(metric->header.object_type, PCEP_OBJ_TYPE_METRIC);
+ CU_ASSERT_EQUAL(metric->header.encoded_object_length, 12);
+ CU_ASSERT_PTR_NULL(metric->header.tlv_list);
+ CU_ASSERT_FALSE(metric->flag_b);
+ CU_ASSERT_FALSE(metric->flag_c);
+ CU_ASSERT_EQUAL(metric->type, PCEP_METRIC_TE);
+ CU_ASSERT_EQUAL(metric->value, 0);
+
+ /* Metric object 2 */
+ obj_node = obj_node->next_node;
+ metric = (struct pcep_object_metric *)obj_node->data;
+ CU_ASSERT_EQUAL(metric->header.object_class, PCEP_OBJ_CLASS_METRIC);
+ CU_ASSERT_EQUAL(metric->header.object_type, PCEP_OBJ_TYPE_METRIC);
+ CU_ASSERT_EQUAL(metric->header.encoded_object_length, 12);
+ CU_ASSERT_PTR_NULL(metric->header.tlv_list);
+ CU_ASSERT_TRUE(metric->flag_b);
+ CU_ASSERT_FALSE(metric->flag_c);
+ CU_ASSERT_EQUAL(metric->type, PCEP_METRIC_AGGREGATE_BW);
+ CU_ASSERT_EQUAL(metric->value, 16.0);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_initiate_cisco_pcc()
+{
+ int fd = convert_hexstrs_to_binary(
+ pcep_initiate_cisco_pcc_hexbyte_strs,
+ pcep_initiate_cisco_pcc_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_INITIATE);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_initiate_cisco_pcc_hexbyte_strs_length);
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 6);
+
+ /* SRP object */
+ double_linked_list_node *obj_node = msg->obj_list->head;
+ struct pcep_object_srp *srp = (struct pcep_object_srp *)obj_node->data;
+ CU_ASSERT_EQUAL(srp->header.object_class, PCEP_OBJ_CLASS_SRP);
+ CU_ASSERT_EQUAL(srp->header.object_type, PCEP_OBJ_TYPE_SRP);
+ CU_ASSERT_EQUAL(srp->header.encoded_object_length, 20);
+ CU_ASSERT_PTR_NOT_NULL(srp->header.tlv_list);
+ CU_ASSERT_EQUAL(srp->header.tlv_list->num_entries, 1);
+ CU_ASSERT_EQUAL(srp->srp_id_number, 1);
+ CU_ASSERT_FALSE(srp->flag_lsp_remove);
+
+ /* LSP object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_lsp *lsp = (struct pcep_object_lsp *)obj_node->data;
+ CU_ASSERT_EQUAL(lsp->header.object_class, PCEP_OBJ_CLASS_LSP);
+ CU_ASSERT_EQUAL(lsp->header.object_type, PCEP_OBJ_TYPE_LSP);
+ CU_ASSERT_EQUAL(lsp->header.encoded_object_length, 48);
+ CU_ASSERT_PTR_NOT_NULL(lsp->header.tlv_list);
+ CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 2);
+ CU_ASSERT_EQUAL(lsp->plsp_id, 0);
+ CU_ASSERT_EQUAL(lsp->operational_status, PCEP_LSP_OPERATIONAL_DOWN);
+ CU_ASSERT_TRUE(lsp->flag_a);
+ CU_ASSERT_TRUE(lsp->flag_d);
+ CU_ASSERT_TRUE(lsp->flag_c);
+ CU_ASSERT_FALSE(lsp->flag_r);
+ CU_ASSERT_FALSE(lsp->flag_s);
+
+ /* Endpoint object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_endpoints_ipv4 *endpoint =
+ (struct pcep_object_endpoints_ipv4 *)obj_node->data;
+ CU_ASSERT_EQUAL(endpoint->header.object_class,
+ PCEP_OBJ_CLASS_ENDPOINTS);
+ CU_ASSERT_EQUAL(endpoint->header.object_type,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV4);
+ CU_ASSERT_EQUAL(endpoint->header.encoded_object_length, 12);
+ CU_ASSERT_PTR_NULL(endpoint->header.tlv_list);
+ CU_ASSERT_EQUAL(endpoint->src_ipv4.s_addr, htonl(0x0a0a0a0a));
+ CU_ASSERT_EQUAL(endpoint->dst_ipv4.s_addr, htonl(0x0a0a0a04));
+
+ /* Inter-Layer object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_inter_layer *inter_layer =
+ (struct pcep_object_inter_layer *)obj_node->data;
+ CU_ASSERT_EQUAL(inter_layer->header.object_class,
+ PCEP_OBJ_CLASS_INTER_LAYER);
+ CU_ASSERT_EQUAL(inter_layer->header.object_type,
+ PCEP_OBJ_TYPE_INTER_LAYER);
+ CU_ASSERT_EQUAL(inter_layer->header.encoded_object_length, 8);
+ CU_ASSERT_PTR_NULL(inter_layer->header.tlv_list);
+ CU_ASSERT_TRUE(inter_layer->flag_i);
+ CU_ASSERT_FALSE(inter_layer->flag_m);
+ CU_ASSERT_TRUE(inter_layer->flag_t);
+
+ /* Switch-Layer object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_switch_layer *switch_layer =
+ (struct pcep_object_switch_layer *)obj_node->data;
+ CU_ASSERT_EQUAL(switch_layer->header.object_class,
+ PCEP_OBJ_CLASS_SWITCH_LAYER);
+ CU_ASSERT_EQUAL(switch_layer->header.object_type,
+ PCEP_OBJ_TYPE_SWITCH_LAYER);
+ CU_ASSERT_EQUAL(switch_layer->header.encoded_object_length, 8);
+ CU_ASSERT_PTR_NULL(switch_layer->header.tlv_list);
+ CU_ASSERT_PTR_NOT_NULL(switch_layer->switch_layer_rows);
+ CU_ASSERT_EQUAL(switch_layer->switch_layer_rows->num_entries, 1);
+ struct pcep_object_switch_layer_row *switch_layer_row =
+ (struct pcep_object_switch_layer_row *)
+ switch_layer->switch_layer_rows->head->data;
+ CU_ASSERT_EQUAL(switch_layer_row->lsp_encoding_type, 0);
+ CU_ASSERT_EQUAL(switch_layer_row->switching_type, 0);
+ CU_ASSERT_FALSE(switch_layer_row->flag_i);
+
+ /* ERO object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_ro *ero = (struct pcep_object_ro *)obj_node->data;
+ CU_ASSERT_EQUAL(ero->header.object_class, PCEP_OBJ_CLASS_ERO);
+ CU_ASSERT_EQUAL(ero->header.object_type, PCEP_OBJ_TYPE_ERO);
+ CU_ASSERT_EQUAL(ero->header.encoded_object_length, 4);
+ CU_ASSERT_PTR_NULL(ero->header.tlv_list);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_validate_message_header()
+{
+ uint8_t pcep_message_invalid_version[] = {0x40, 0x01, 0x04, 0x00};
+ uint8_t pcep_message_invalid_flags[] = {0x22, 0x01, 0x04, 0x00};
+ uint8_t pcep_message_invalid_length[] = {0x20, 0x01, 0x00, 0x00};
+ uint8_t pcep_message_invalid_type[] = {0x20, 0xff, 0x04, 0x00};
+ uint8_t pcep_message_valid[] = {0x20, 0x01, 0x00, 0x04};
+
+ /* Verify invalid message header version */
+ CU_ASSERT_TRUE(
+ pcep_decode_validate_msg_header(pcep_message_invalid_version)
+ < 0);
+
+ /* Verify invalid message header flags */
+ CU_ASSERT_TRUE(
+ pcep_decode_validate_msg_header(pcep_message_invalid_flags)
+ < 0);
+
+ /* Verify invalid message header lengths */
+ CU_ASSERT_TRUE(
+ pcep_decode_validate_msg_header(pcep_message_invalid_length)
+ < 0);
+ pcep_message_invalid_length[3] = 0x05;
+ CU_ASSERT_TRUE(
+ pcep_decode_validate_msg_header(pcep_message_invalid_length)
+ < 0);
+
+ /* Verify invalid message header types */
+ CU_ASSERT_TRUE(
+ pcep_decode_validate_msg_header(pcep_message_invalid_type) < 0);
+ pcep_message_invalid_type[1] = 0x00;
+ CU_ASSERT_TRUE(
+ pcep_decode_validate_msg_header(pcep_message_invalid_type) < 0);
+
+ /* Verify a valid message header */
+ CU_ASSERT_EQUAL(pcep_decode_validate_msg_header(pcep_message_valid), 4);
+}
+
+/* Internal util function */
+struct pcep_message *create_message(uint8_t msg_type, uint8_t obj1_class,
+ uint8_t obj2_class, uint8_t obj3_class,
+ uint8_t obj4_class)
+{
+ struct pcep_message *msg =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct pcep_message));
+ msg->obj_list = dll_initialize();
+ msg->msg_header = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct pcep_message_header));
+ msg->msg_header->type = msg_type;
+ msg->encoded_message = NULL;
+
+ if (obj1_class > 0) {
+ struct pcep_object_header *obj_hdr = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(struct pcep_object_header));
+ obj_hdr->object_class = obj1_class;
+ obj_hdr->tlv_list = NULL;
+ dll_append(msg->obj_list, obj_hdr);
+ }
+
+ if (obj2_class > 0) {
+ struct pcep_object_header *obj_hdr = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(struct pcep_object_header));
+ obj_hdr->object_class = obj2_class;
+ obj_hdr->tlv_list = NULL;
+ dll_append(msg->obj_list, obj_hdr);
+ }
+
+ if (obj3_class > 0) {
+ struct pcep_object_header *obj_hdr = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(struct pcep_object_header));
+ obj_hdr->object_class = obj3_class;
+ obj_hdr->tlv_list = NULL;
+ dll_append(msg->obj_list, obj_hdr);
+ }
+
+ if (obj4_class > 0) {
+ struct pcep_object_header *obj_hdr = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(struct pcep_object_header));
+ obj_hdr->object_class = obj4_class;
+ obj_hdr->tlv_list = NULL;
+ dll_append(msg->obj_list, obj_hdr);
+ }
+
+ return msg;
+}
+
+void test_validate_message_objects()
+{
+ /* Valid Open message */
+ struct pcep_message *msg =
+ create_message(PCEP_TYPE_OPEN, PCEP_OBJ_CLASS_OPEN, 0, 0, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid KeepAlive message */
+ msg = create_message(PCEP_TYPE_KEEPALIVE, 0, 0, 0, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid PcReq message */
+ /* Using object_class=255 to verify it can take any object */
+ msg = create_message(PCEP_TYPE_PCREQ, PCEP_OBJ_CLASS_RP,
+ PCEP_OBJ_CLASS_ENDPOINTS, any_obj_class, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid PcRep message */
+ msg = create_message(PCEP_TYPE_PCREP, PCEP_OBJ_CLASS_RP, any_obj_class,
+ 0, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid Notify message */
+ msg = create_message(PCEP_TYPE_PCNOTF, PCEP_OBJ_CLASS_NOTF,
+ any_obj_class, 0, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid Error message */
+ msg = create_message(PCEP_TYPE_ERROR, PCEP_OBJ_CLASS_ERROR,
+ any_obj_class, 0, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid Close message */
+ msg = create_message(PCEP_TYPE_CLOSE, PCEP_OBJ_CLASS_CLOSE, 0, 0, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid Report message */
+ msg = create_message(PCEP_TYPE_REPORT, PCEP_OBJ_CLASS_SRP,
+ PCEP_OBJ_CLASS_LSP, any_obj_class, any_obj_class);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid Update message */
+ msg = create_message(PCEP_TYPE_UPDATE, PCEP_OBJ_CLASS_SRP,
+ PCEP_OBJ_CLASS_LSP, any_obj_class, any_obj_class);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid Initiate message */
+ msg = create_message(PCEP_TYPE_INITIATE, PCEP_OBJ_CLASS_SRP,
+ PCEP_OBJ_CLASS_LSP, any_obj_class, any_obj_class);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+}
+
+void test_validate_message_objects_invalid()
+{
+ /* unsupported message ID = 0
+ * {NO_OBJECT, NO_OBJECT, NO_OBJECT, NO_OBJECT} */
+ struct pcep_message *msg = create_message(0, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Open message
+ * {PCEP_OBJ_CLASS_OPEN, NO_OBJECT, NO_OBJECT, NO_OBJECT} */
+ msg = create_message(PCEP_TYPE_OPEN, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_OPEN, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_OPEN, PCEP_OBJ_CLASS_OPEN, any_obj_class,
+ 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* KeepAlive message
+ * {NO_OBJECT, NO_OBJECT, NO_OBJECT, NO_OBJECT} */
+ msg = create_message(PCEP_TYPE_KEEPALIVE, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* PcReq message
+ * {PCEP_OBJ_CLASS_RP, PCEP_OBJ_CLASS_ENDPOINTS, ANY_OBJECT, ANY_OBJECT}
+ */
+ msg = create_message(PCEP_TYPE_PCREQ, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_PCREQ, PCEP_OBJ_CLASS_RP, any_obj_class,
+ 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* PcRep message
+ * {PCEP_OBJ_CLASS_RP, ANY_OBJECT, ANY_OBJECT, ANY_OBJECT} */
+ msg = create_message(PCEP_TYPE_PCREP, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_PCREP, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Notify message
+ * {PCEP_OBJ_CLASS_NOTF, ANY_OBJECT, ANY_OBJECT, ANY_OBJECT} */
+ msg = create_message(PCEP_TYPE_PCNOTF, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_PCNOTF, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Error message
+ * {PCEP_OBJ_CLASS_ERROR, ANY_OBJECT, ANY_OBJECT, ANY_OBJECT} */
+ msg = create_message(PCEP_TYPE_ERROR, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_ERROR, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Close message
+ * {PCEP_OBJ_CLASS_CLOSE, NO_OBJECT, NO_OBJECT, NO_OBJECT} */
+ msg = create_message(PCEP_TYPE_CLOSE, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_CLOSE, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* unsupported message ID = 8
+ * {NO_OBJECT, NO_OBJECT, NO_OBJECT, NO_OBJECT} */
+ msg = create_message(8, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* unsupported message ID = 9
+ * {NO_OBJECT, NO_OBJECT, NO_OBJECT, NO_OBJECT} */
+ msg = create_message(9, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Report message
+ * {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, ANY_OBJECT} */
+ msg = create_message(PCEP_TYPE_REPORT, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_REPORT, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_REPORT, PCEP_OBJ_CLASS_SRP, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_REPORT, PCEP_OBJ_CLASS_SRP,
+ any_obj_class, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Update message
+ * {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, ANY_OBJECT} */
+ msg = create_message(PCEP_TYPE_UPDATE, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_UPDATE, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_UPDATE, PCEP_OBJ_CLASS_SRP, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_UPDATE, PCEP_OBJ_CLASS_SRP,
+ any_obj_class, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Initiate message
+ * {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, ANY_OBJECT} */
+ msg = create_message(PCEP_TYPE_INITIATE, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_INITIATE, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_INITIATE, PCEP_OBJ_CLASS_SRP, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_INITIATE, PCEP_OBJ_CLASS_SRP,
+ any_obj_class, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+}
diff --git a/pceplib/test/pcep_msg_tools_test.h b/pceplib/test/pcep_msg_tools_test.h
new file mode 100644
index 000000000..dc6639080
--- /dev/null
+++ b/pceplib/test/pcep_msg_tools_test.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_MSG_TOOLS_TEST_H_
+#define PCEP_MSG_TOOLS_TEST_H_
+
+
+int pcep_tools_test_suite_setup(void);
+int pcep_tools_test_suite_teardown(void);
+void pcep_tools_test_setup(void);
+void pcep_tools_test_teardown(void);
+void test_pcep_msg_read_pcep_initiate(void);
+void test_pcep_msg_read_pcep_initiate2(void);
+void test_pcep_msg_read_pcep_update(void);
+void test_pcep_msg_read_pcep_open(void);
+void test_pcep_msg_read_pcep_open_initiate(void);
+void test_validate_message_header(void);
+void test_validate_message_objects(void);
+void test_validate_message_objects_invalid(void);
+void test_pcep_msg_read_pcep_open_cisco_pce(void);
+void test_pcep_msg_read_pcep_update_cisco_pce(void);
+void test_pcep_msg_read_pcep_report_cisco_pcc(void);
+void test_pcep_msg_read_pcep_initiate_cisco_pcc(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_pcc_api_test.c b/pceplib/test/pcep_pcc_api_test.c
new file mode 100644
index 000000000..c227dc1a3
--- /dev/null
+++ b/pceplib/test/pcep_pcc_api_test.c
@@ -0,0 +1,285 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <netdb.h> // gethostbyname
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_pcc_api.h"
+#include "pcep_pcc_api_test.h"
+#include "pcep_socket_comm_mock.h"
+#include "pcep_utils_memory.h"
+
+extern pcep_event_queue *session_logic_event_queue_;
+extern const char MESSAGE_RECEIVED_STR[];
+extern const char UNKNOWN_EVENT_STR[];
+
+/*
+ * Test suite setup and teardown called before AND after the test suite.
+ */
+
+int pcep_pcc_api_test_suite_setup()
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_pcc_api_test_suite_teardown()
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+/*
+ * Test case setup and teardown called before AND after each test.
+ */
+
+void pcep_pcc_api_test_setup()
+{
+ setup_mock_socket_comm_info();
+}
+
+
+void pcep_pcc_api_test_teardown()
+{
+ teardown_mock_socket_comm_info();
+}
+
+/*
+ * Unit test cases
+ */
+
+void test_initialize_pcc()
+{
+ CU_ASSERT_TRUE(initialize_pcc());
+ /* Give the PCC time to initialize */
+ sleep(1);
+ CU_ASSERT_TRUE(destroy_pcc());
+}
+
+void test_connect_pce()
+{
+ pcep_configuration *config = create_default_pcep_configuration();
+ struct hostent *host_info = gethostbyname("localhost");
+ struct in_addr dest_address;
+ memcpy(&dest_address, host_info->h_addr, host_info->h_length);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ initialize_pcc();
+
+ pcep_session *session = connect_pce(config, &dest_address);
+
+ CU_ASSERT_PTR_NOT_NULL(session);
+ CU_ASSERT_EQUAL(mock_info->sent_message_list->num_entries, 1);
+ /* What gets saved in the mock is the msg byte buffer. The msg struct
+ * was deleted when it was sent. Instead of inspecting the msg byte
+ * buffer, lets just decode it. */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN);
+
+ pcep_msg_free_message(open_msg);
+ destroy_pcep_session(session);
+ destroy_pcep_configuration(config);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ destroy_pcc();
+}
+
+void test_connect_pce_ipv6()
+{
+ pcep_configuration *config = create_default_pcep_configuration();
+ struct in6_addr dest_address;
+ dest_address.__in6_u.__u6_addr32[0] = 0;
+ dest_address.__in6_u.__u6_addr32[1] = 0;
+ dest_address.__in6_u.__u6_addr32[2] = 0;
+ dest_address.__in6_u.__u6_addr32[3] = htonl(1);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ initialize_pcc();
+
+ pcep_session *session = connect_pce_ipv6(config, &dest_address);
+
+ CU_ASSERT_PTR_NOT_NULL(session);
+ CU_ASSERT_TRUE(session->socket_comm_session->is_ipv6);
+ CU_ASSERT_EQUAL(mock_info->sent_message_list->num_entries, 1);
+ /* What gets saved in the mock is the msg byte buffer. The msg struct
+ * was deleted when it was sent. Instead of inspecting the msg byte
+ * buffer, lets just decode it. */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN);
+
+ pcep_msg_free_message(open_msg);
+ destroy_pcep_session(session);
+ destroy_pcep_configuration(config);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ destroy_pcc();
+}
+
+void test_connect_pce_with_src_ip()
+{
+ pcep_configuration *config = create_default_pcep_configuration();
+ struct hostent *host_info = gethostbyname("localhost");
+ struct in_addr dest_address;
+ memcpy(&dest_address, host_info->h_addr, host_info->h_length);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ config->src_ip.src_ipv4.s_addr = 0x0a0a0102;
+
+ initialize_pcc();
+
+ pcep_session *session = connect_pce(config, &dest_address);
+
+ CU_ASSERT_PTR_NOT_NULL(session);
+ CU_ASSERT_EQUAL(mock_info->sent_message_list->num_entries, 1);
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN);
+
+ pcep_msg_free_message(open_msg);
+ destroy_pcep_session(session);
+ destroy_pcep_configuration(config);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ destroy_pcc();
+}
+
+void test_disconnect_pce()
+{
+ pcep_configuration *config = create_default_pcep_configuration();
+ struct hostent *host_info = gethostbyname("localhost");
+ struct in_addr dest_address;
+ memcpy(&dest_address, host_info->h_addr, host_info->h_length);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ initialize_pcc();
+
+ pcep_session *session = connect_pce(config, &dest_address);
+ disconnect_pce(session);
+
+ CU_ASSERT_EQUAL(mock_info->sent_message_list->num_entries, 2);
+
+ /* First there should be an open message from connect_pce() */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(msg);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_OPEN);
+ pcep_msg_free_message(msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ /* Then there should be a close message from disconnect_pce() */
+ encoded_msg = dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(msg);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_CLOSE);
+
+ pcep_msg_free_message(msg);
+ destroy_pcep_session(session);
+ destroy_pcep_configuration(config);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ destroy_pcc();
+}
+
+
+void test_send_message()
+{
+ pcep_configuration *config = create_default_pcep_configuration();
+ struct hostent *host_info = gethostbyname("localhost");
+ struct in_addr dest_address;
+
+ initialize_pcc();
+
+ memcpy(&dest_address, host_info->h_addr, host_info->h_length);
+ pcep_session *session = connect_pce(config, &dest_address);
+ verify_socket_comm_times_called(0, 0, 1, 1, 0, 0, 0);
+
+ struct pcep_message *msg = pcep_msg_create_keepalive();
+ send_message(session, msg, false);
+
+ verify_socket_comm_times_called(0, 0, 1, 2, 0, 0, 0);
+
+ pcep_msg_free_message(msg);
+ destroy_pcep_session(session);
+ destroy_pcep_configuration(config);
+
+ destroy_pcc();
+}
+
+void test_event_queue()
+{
+ /* This initializes the event_queue */
+ CU_ASSERT_TRUE(initialize_pcc());
+
+ /* Verify correct behavior when the queue is empty */
+ CU_ASSERT_TRUE(event_queue_is_empty());
+ CU_ASSERT_EQUAL(event_queue_num_events_available(), 0);
+ CU_ASSERT_PTR_NULL(event_queue_get_event());
+ destroy_pcep_event(NULL);
+
+ /* Create an empty event and put it on the queue */
+ pcep_event *event = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event));
+ memset(event, 0, sizeof(pcep_event));
+ pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex);
+ queue_enqueue(session_logic_event_queue_->event_queue, event);
+ pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex);
+
+ /* Verify correct behavior when there is an entry in the queue */
+ CU_ASSERT_FALSE(event_queue_is_empty());
+ CU_ASSERT_EQUAL(event_queue_num_events_available(), 1);
+ pcep_event *queued_event = event_queue_get_event();
+ CU_ASSERT_PTR_NOT_NULL(queued_event);
+ CU_ASSERT_PTR_EQUAL(event, queued_event);
+ destroy_pcep_event(queued_event);
+
+ CU_ASSERT_TRUE(destroy_pcc());
+}
+
+void test_get_event_type_str()
+{
+ CU_ASSERT_EQUAL(strcmp(get_event_type_str(MESSAGE_RECEIVED),
+ MESSAGE_RECEIVED_STR),
+ 0);
+ CU_ASSERT_EQUAL(strcmp(get_event_type_str(1000), UNKNOWN_EVENT_STR), 0);
+}
diff --git a/pceplib/test/pcep_pcc_api_test.h b/pceplib/test/pcep_pcc_api_test.h
new file mode 100644
index 000000000..d3db96e0c
--- /dev/null
+++ b/pceplib/test/pcep_pcc_api_test.h
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_PCC_API_TEST_
+#define PCEP_PCC_API_TEST_
+
+int pcep_pcc_api_test_suite_setup(void);
+int pcep_pcc_api_test_suite_teardown(void);
+void pcep_pcc_api_test_setup(void);
+void pcep_pcc_api_test_teardown(void);
+void test_initialize_pcc(void);
+void test_connect_pce(void);
+void test_connect_pce_ipv6(void);
+void test_connect_pce_with_src_ip(void);
+void test_disconnect_pce(void);
+void test_send_message(void);
+void test_event_queue(void);
+void test_get_event_type_str(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_pcc_api_tests.c b/pceplib/test/pcep_pcc_api_tests.c
new file mode 100644
index 000000000..04895d634
--- /dev/null
+++ b/pceplib/test/pcep_pcc_api_tests.c
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/Basic.h>
+#include <CUnit/CUnit.h>
+#include <CUnit/TestDB.h>
+
+#include "pcep_pcc_api_test.h"
+
+int main(int argc, char **argv)
+{
+ /* Unused parameters cause compilation warnings */
+ (void)argc;
+ (void)argv;
+
+ CU_initialize_registry();
+
+ /*
+ * Tests defined in pcep_socket_comm_test.c
+ */
+ CU_pSuite test_pcc_api_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP PCC API Test Suite",
+ pcep_pcc_api_test_suite_setup, // suite setup and cleanup
+ // function pointers
+ pcep_pcc_api_test_suite_teardown,
+ pcep_pcc_api_test_setup, // test case setup function pointer
+ pcep_pcc_api_test_teardown); // test case teardown function
+ // pointer
+
+ CU_add_test(test_pcc_api_suite, "test_initialize_pcc",
+ test_initialize_pcc);
+ CU_add_test(test_pcc_api_suite, "test_connect_pce", test_connect_pce);
+ CU_add_test(test_pcc_api_suite, "test_connect_pce_ipv6",
+ test_connect_pce_ipv6);
+ CU_add_test(test_pcc_api_suite, "test_connect_pce_with_src_ip",
+ test_connect_pce_with_src_ip);
+ CU_add_test(test_pcc_api_suite, "test_disconnect_pce",
+ test_disconnect_pce);
+ CU_add_test(test_pcc_api_suite, "test_send_message", test_send_message);
+ CU_add_test(test_pcc_api_suite, "test_event_queue", test_event_queue);
+ CU_add_test(test_pcc_api_suite, "test_get_event_type_str",
+ test_get_event_type_str);
+
+ /*
+ * Run the tests and cleanup.
+ */
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_FailureRecord *failure_record = CU_get_failure_list();
+ if (failure_record != NULL) {
+ printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n");
+ do {
+ printf("\t [%s] [%s] [%s:%d]\n",
+ failure_record->pSuite->pName,
+ failure_record->pTest->pName,
+ failure_record->strFileName,
+ failure_record->uiLineNumber);
+ failure_record = failure_record->pNext;
+
+ } while (failure_record != NULL);
+ }
+
+ CU_pRunSummary run_summary = CU_get_run_summary();
+ int result = run_summary->nTestsFailed;
+ CU_cleanup_registry();
+
+ return result;
+}
diff --git a/pceplib/test/pcep_pcc_api_tests_valgrind.sh b/pceplib/test/pcep_pcc_api_tests_valgrind.sh
new file mode 100755
index 000000000..74494b752
--- /dev/null
+++ b/pceplib/test/pcep_pcc_api_tests_valgrind.sh
@@ -0,0 +1,2 @@
+source pceplib/test/pcep_tests_valgrind.sh
+valgrind_test pceplib/test/pcep_pcc_api_tests
diff --git a/pceplib/test/pcep_session_logic_loop_test.c b/pceplib/test/pcep_session_logic_loop_test.c
new file mode 100644
index 000000000..38fabd4cc
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_loop_test.c
@@ -0,0 +1,219 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_internals.h"
+#include "pcep_timers.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_memory.h"
+#include "pcep_session_logic_loop_test.h"
+
+
+extern pcep_session_logic_handle *session_logic_handle_;
+extern pcep_event_queue *session_logic_event_queue_;
+
+/*
+ * Test suite setup and teardown called before AND after the test suite.
+ */
+
+int pcep_session_logic_loop_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_session_logic_loop_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+
+/*
+ * Test case setup and teardown called before AND after each test.
+ */
+
+void pcep_session_logic_loop_test_setup()
+{
+ /* We need to setup the session_logic_handle_ without starting the
+ * thread */
+ session_logic_handle_ = pceplib_malloc(
+ PCEPLIB_INFRA, sizeof(pcep_session_logic_handle));
+ memset(session_logic_handle_, 0, sizeof(pcep_session_logic_handle));
+ session_logic_handle_->active = true;
+ session_logic_handle_->session_logic_condition = false;
+ session_logic_handle_->session_list =
+ ordered_list_initialize(pointer_compare_function);
+ session_logic_handle_->session_event_queue = queue_initialize();
+ pthread_cond_init(&(session_logic_handle_->session_logic_cond_var),
+ NULL);
+ pthread_mutex_init(&(session_logic_handle_->session_logic_mutex), NULL);
+ pthread_mutex_init(&(session_logic_handle_->session_list_mutex), NULL);
+
+ session_logic_event_queue_ =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event_queue));
+ memset(session_logic_event_queue_, 0, sizeof(pcep_event_queue));
+ session_logic_event_queue_->event_queue = queue_initialize();
+}
+
+
+void pcep_session_logic_loop_test_teardown()
+{
+ ordered_list_destroy(session_logic_handle_->session_list);
+ queue_destroy(session_logic_handle_->session_event_queue);
+ pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
+ pthread_mutex_destroy(&(session_logic_handle_->session_logic_mutex));
+ pthread_mutex_destroy(&(session_logic_handle_->session_list_mutex));
+ pceplib_free(PCEPLIB_INFRA, session_logic_handle_);
+ session_logic_handle_ = NULL;
+
+ queue_destroy(session_logic_event_queue_->event_queue);
+ pceplib_free(PCEPLIB_INFRA, session_logic_event_queue_);
+ session_logic_event_queue_ = NULL;
+}
+
+
+/*
+ * Test cases
+ */
+
+void test_session_logic_loop_null_data()
+{
+ /* Just testing that it does not core dump */
+ session_logic_loop(NULL);
+}
+
+
+void test_session_logic_loop_inactive()
+{
+ session_logic_handle_->active = false;
+
+ session_logic_loop(session_logic_handle_);
+}
+
+
+void test_session_logic_msg_ready_handler()
+{
+ /* Just testing that it does not core dump */
+ CU_ASSERT_EQUAL(session_logic_msg_ready_handler(NULL, 0), -1);
+
+ /* Read from an empty file should return 0, thus
+ * session_logic_msg_ready_handler returns -1 */
+ int fd = fileno(tmpfile());
+ pcep_session session;
+ memset(&session, 0, sizeof(pcep_session));
+ session.session_id = 100;
+ CU_ASSERT_EQUAL(session_logic_msg_ready_handler(&session, fd), 0);
+ CU_ASSERT_EQUAL(session_logic_handle_->session_event_queue->num_entries,
+ 1);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCE_CLOSED_SOCKET, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ pcep_session_event *socket_event = (pcep_session_event *)queue_dequeue(
+ session_logic_handle_->session_event_queue);
+ CU_ASSERT_PTR_NOT_NULL(socket_event);
+ CU_ASSERT_TRUE(socket_event->socket_closed);
+ pceplib_free(PCEPLIB_INFRA, socket_event);
+
+ /* A pcep_session_event should be created */
+ struct pcep_versioning *versioning = create_default_pcep_versioning();
+ struct pcep_message *keep_alive_msg = pcep_msg_create_keepalive();
+ pcep_encode_message(keep_alive_msg, versioning);
+ int retval = write(fd, (char *)keep_alive_msg->encoded_message,
+ keep_alive_msg->encoded_message_length);
+ CU_ASSERT_TRUE(retval > 0);
+ lseek(fd, 0, SEEK_SET);
+ CU_ASSERT_EQUAL(session_logic_msg_ready_handler(&session, fd),
+ keep_alive_msg->encoded_message_length);
+ CU_ASSERT_EQUAL(session_logic_handle_->session_event_queue->num_entries,
+ 1);
+ socket_event = (pcep_session_event *)queue_dequeue(
+ session_logic_handle_->session_event_queue);
+ CU_ASSERT_PTR_NOT_NULL(socket_event);
+ CU_ASSERT_FALSE(socket_event->socket_closed);
+ CU_ASSERT_PTR_EQUAL(socket_event->session, &session);
+ CU_ASSERT_EQUAL(socket_event->expired_timer_id, TIMER_ID_NOT_SET);
+ CU_ASSERT_PTR_NOT_NULL(socket_event->received_msg_list);
+ pcep_msg_free_message_list(socket_event->received_msg_list);
+ pcep_msg_free_message(keep_alive_msg);
+ destroy_pcep_versioning(versioning);
+ pceplib_free(PCEPLIB_INFRA, socket_event);
+ close(fd);
+}
+
+
+void test_session_logic_conn_except_notifier()
+{
+ /* Just testing that it does not core dump */
+ session_logic_conn_except_notifier(NULL, 1);
+
+ /* A pcep_session_event should be created */
+ pcep_session session;
+ memset(&session, 0, sizeof(pcep_session));
+ session.session_id = 100;
+ session_logic_conn_except_notifier(&session, 10);
+ CU_ASSERT_EQUAL(session_logic_handle_->session_event_queue->num_entries,
+ 1);
+ pcep_session_event *socket_event = (pcep_session_event *)queue_dequeue(
+ session_logic_handle_->session_event_queue);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(socket_event);
+ CU_ASSERT_TRUE(socket_event->socket_closed);
+ CU_ASSERT_PTR_EQUAL(socket_event->session, &session);
+ CU_ASSERT_EQUAL(socket_event->expired_timer_id, TIMER_ID_NOT_SET);
+ CU_ASSERT_PTR_NULL(socket_event->received_msg_list);
+
+ pceplib_free(PCEPLIB_INFRA, socket_event);
+}
+
+
+void test_session_logic_timer_expire_handler()
+{
+ /* Just testing that it does not core dump */
+ session_logic_timer_expire_handler(NULL, 42);
+
+ /* A pcep_session_event should be created */
+ pcep_session session;
+ memset(&session, 0, sizeof(pcep_session));
+ session.session_id = 100;
+ session_logic_timer_expire_handler(&session, 42);
+ CU_ASSERT_EQUAL(session_logic_handle_->session_event_queue->num_entries,
+ 1);
+ pcep_session_event *socket_event = (pcep_session_event *)queue_dequeue(
+ session_logic_handle_->session_event_queue);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(socket_event);
+ CU_ASSERT_FALSE(socket_event->socket_closed);
+ CU_ASSERT_PTR_EQUAL(socket_event->session, &session);
+ CU_ASSERT_EQUAL(socket_event->expired_timer_id, 42);
+ CU_ASSERT_PTR_NULL(socket_event->received_msg_list);
+
+ pceplib_free(PCEPLIB_INFRA, socket_event);
+}
diff --git a/pceplib/test/pcep_session_logic_loop_test.h b/pceplib/test/pcep_session_logic_loop_test.h
new file mode 100644
index 000000000..ae3c3e375
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_loop_test.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_SESSION_LOGIC_LOOP_TEST_H_
+#define PCEP_SESSION_LOGIC_LOOP_TEST_H_
+
+int pcep_session_logic_loop_test_suite_setup(void);
+int pcep_session_logic_loop_test_suite_teardown(void);
+void pcep_session_logic_loop_test_setup(void);
+void pcep_session_logic_loop_test_teardown(void);
+void test_session_logic_loop_null_data(void);
+void test_session_logic_loop_inactive(void);
+void test_session_logic_msg_ready_handler(void);
+void test_session_logic_conn_except_notifier(void);
+void test_session_logic_timer_expire_handler(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_session_logic_states_test.c b/pceplib/test/pcep_session_logic_states_test.c
new file mode 100644
index 000000000..f75c16e39
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_states_test.c
@@ -0,0 +1,919 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_socket_comm_mock.h"
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_internals.h"
+#include "pcep_timers.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_memory.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tools.h"
+#include "pcep_session_logic_states_test.h"
+
+/* Functions being tested */
+extern pcep_session_logic_handle *session_logic_handle_;
+extern pcep_event_queue *session_logic_event_queue_;
+
+static pcep_session_event event;
+static pcep_session session;
+/* A message list is a dll of struct pcep_messages_list_node items */
+static double_linked_list *msg_list;
+struct pcep_message *message;
+static bool free_msg_list;
+static bool msg_enqueued;
+/* Forward declaration */
+void destroy_message_for_test(void);
+void create_message_for_test(uint8_t msg_type, bool free_msg_list_at_teardown,
+ bool was_msg_enqueued);
+void test_handle_timer_event_open_keep_alive(void);
+
+/*
+ * Test suite setup and teardown called before AND after the test suite.
+ */
+
+int pcep_session_logic_states_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_session_logic_states_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+/*
+ * Test case setup and teardown called before AND after each test.
+ */
+
+void pcep_session_logic_states_test_setup()
+{
+ session_logic_handle_ = pceplib_malloc(
+ PCEPLIB_INFRA, sizeof(pcep_session_logic_handle));
+ memset(session_logic_handle_, 0, sizeof(pcep_session_logic_handle));
+
+ session_logic_event_queue_ =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event_queue));
+ memset(session_logic_event_queue_, 0, sizeof(pcep_event_queue));
+ session_logic_event_queue_->event_queue = queue_initialize();
+
+ memset(&session, 0, sizeof(pcep_session));
+ session.pcc_config.keep_alive_seconds = 5;
+ session.pcc_config.keep_alive_pce_negotiated_timer_seconds = 5;
+ session.pcc_config.min_keep_alive_seconds = 1;
+ session.pcc_config.max_keep_alive_seconds = 10;
+ session.pcc_config.dead_timer_seconds = 5;
+ session.pcc_config.dead_timer_pce_negotiated_seconds = 5;
+ session.pcc_config.min_dead_timer_seconds = 1;
+ session.pcc_config.max_dead_timer_seconds = 10;
+ session.pcc_config.max_unknown_messages = 2;
+ memcpy(&session.pce_config, &session.pcc_config,
+ sizeof(pcep_configuration));
+ session.num_unknown_messages_time_queue = queue_initialize();
+
+ memset(&event, 0, sizeof(pcep_session_event));
+ event.socket_closed = false;
+ event.session = &session;
+
+ setup_mock_socket_comm_info();
+ free_msg_list = false;
+ msg_enqueued = false;
+}
+
+
+void pcep_session_logic_states_test_teardown()
+{
+ destroy_message_for_test();
+ pceplib_free(PCEPLIB_INFRA, session_logic_handle_);
+ queue_destroy(session_logic_event_queue_->event_queue);
+ pceplib_free(PCEPLIB_INFRA, session_logic_event_queue_);
+ session_logic_handle_ = NULL;
+ session_logic_event_queue_ = NULL;
+ queue_destroy_with_data(session.num_unknown_messages_time_queue);
+ teardown_mock_socket_comm_info();
+}
+
+void create_message_for_test(uint8_t msg_type, bool free_msg_list_at_teardown,
+ bool was_msg_enqueued)
+{
+ /* See the comments in destroy_message_for_test() about these 2
+ * variables */
+ free_msg_list = free_msg_list_at_teardown;
+ msg_enqueued = was_msg_enqueued;
+
+ message = pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct pcep_message));
+ memset(message, 0, sizeof(struct pcep_message));
+
+ message->msg_header = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(struct pcep_message_header));
+ memset(message->msg_header, 0, sizeof(struct pcep_message_header));
+ message->obj_list = dll_initialize();
+ message->msg_header->type = msg_type;
+
+ msg_list = dll_initialize();
+ dll_append(msg_list, message);
+ event.received_msg_list = msg_list;
+}
+
+void destroy_message_for_test()
+{
+ /* Some test cases internally free the message list, so we dont
+ * want to double free it */
+ if (free_msg_list == true) {
+ /* This will destroy both the msg_list and the obj_list */
+ pcep_msg_free_message_list(msg_list);
+ }
+
+ /* Some tests cause the message to be enqueued and dont delete it,
+ * so we have to delete it here */
+ if (msg_enqueued == true) {
+ pcep_msg_free_message(message);
+ }
+}
+
+/*
+ * Test cases
+ */
+
+void test_handle_timer_event_dead_timer()
+{
+ /* Dead Timer expired */
+ event.expired_timer_id = session.timer_id_dead_timer = 100;
+
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_dead_timer, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCE_DEAD_TIMER_EXPIRED, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+
+ /* verify_socket_comm_times_called(
+ * initialized, teardown, connect, send_message, close_after_write,
+ * close, destroy); */
+ verify_socket_comm_times_called(0, 0, 0, 1, 1, 0, 0);
+}
+
+
+void test_handle_timer_event_keep_alive()
+{
+ /* Keep Alive timer expired */
+ event.expired_timer_id = session.timer_id_keep_alive = 200;
+
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_keep_alive, TIMER_ID_NOT_SET);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+}
+
+
+void test_handle_timer_event_open_keep_wait()
+{
+ /* Open Keep Wait timer expired */
+ event.expired_timer_id = session.timer_id_open_keep_wait = 300;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 1, 0, 0);
+
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+
+ /* If the state is not SESSION_STATE_PCEP_CONNECTED, then nothing should
+ * happen */
+ reset_mock_socket_comm_info();
+ session.session_state = SESSION_STATE_UNKNOWN;
+ event.expired_timer_id = session.timer_id_open_keep_wait = 300;
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, 300);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_UNKNOWN);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+}
+
+
+void test_handle_timer_event_open_keep_alive()
+{
+ /* Open Keep Alive timer expired, but the Keep Alive should not be sent
+ * since the PCE Open has not been received yet */
+ event.expired_timer_id = session.timer_id_open_keep_alive = 300;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ session.pce_open_keep_alive_sent = false;
+ session.pce_open_received = false;
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_alive, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ CU_ASSERT_FALSE(session.pce_open_keep_alive_sent);
+
+ /* Open Keep Alive timer expired, the Keep Alive should be sent,
+ * but the session should not be connected, since the PCC Open
+ * has not been accepted yet */
+ event.expired_timer_id = session.timer_id_open_keep_alive = 300;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ session.pce_open_keep_alive_sent = false;
+ session.pce_open_received = true;
+ session.pce_open_rejected = false;
+ session.pcc_open_accepted = false;
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_alive, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ CU_ASSERT_TRUE(session.pce_open_keep_alive_sent);
+
+ /* Open Keep Alive timer expired, the Keep Alive should be sent,
+ * and the session is connected */
+ event.expired_timer_id = session.timer_id_open_keep_alive = 300;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ session.pce_open_keep_alive_sent = false;
+ session.pce_open_received = true;
+ session.pce_open_rejected = false;
+ session.pcc_open_accepted = true;
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_alive, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTED);
+ CU_ASSERT_FALSE(session.pce_open_keep_alive_sent);
+}
+
+
+void test_handle_socket_comm_event_null_params()
+{
+ /* Verify it doesnt core dump */
+ handle_socket_comm_event(NULL);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ reset_mock_socket_comm_info();
+
+ event.received_msg_list = NULL;
+ handle_socket_comm_event(&event);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+}
+
+
+void test_handle_socket_comm_event_close()
+{
+ event.socket_closed = true;
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 1, 0);
+
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCE_CLOSED_SOCKET, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_open()
+{
+ /*
+ * Test when a PCE Open is received, but the PCC Open has not been
+ * accepted yet
+ */
+ create_message_for_test(PCEP_TYPE_OPEN, false, true);
+ struct pcep_object_open *open_object =
+ pcep_obj_create_open(1, 1, 1, NULL);
+ dll_append(message->obj_list, open_object);
+ session.pcc_open_accepted = false;
+ session.pce_open_received = false;
+ session.pce_open_accepted = false;
+ session.timer_id_open_keep_alive = 100;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pce_open_received);
+ CU_ASSERT_TRUE(session.pce_open_accepted);
+ CU_ASSERT_FALSE(session.pce_open_rejected);
+ CU_ASSERT_FALSE(session.pce_open_keep_alive_sent);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ CU_ASSERT_NOT_EQUAL(session.timer_id_open_keep_alive, 100);
+ /* A keep alive response should NOT be sent yet */
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_OPEN, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ destroy_message_for_test();
+
+ /*
+ * Test when a PCE Open is received, and the PCC Open has been accepted
+ */
+ create_message_for_test(PCEP_TYPE_OPEN, false, true);
+ reset_mock_socket_comm_info();
+ open_object = pcep_obj_create_open(1, 1, 1, NULL);
+ dll_append(message->obj_list, open_object);
+ session.pcc_open_accepted = true;
+ session.pce_open_received = false;
+ session.pce_open_accepted = false;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pce_open_received);
+ CU_ASSERT_TRUE(session.pce_open_accepted);
+ CU_ASSERT_FALSE(session.pce_open_rejected);
+ CU_ASSERT_TRUE(session.pce_open_keep_alive_sent);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTED);
+ /* A keep alive response should be sent, accepting the Open */
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 2);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_OPEN, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_CONNECTED_TO_PCE, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ destroy_message_for_test();
+
+ /*
+ * Send a 2nd Open, an error should be sent
+ */
+ create_message_for_test(PCEP_TYPE_OPEN, false, false);
+ reset_mock_socket_comm_info();
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 0);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ /* What gets saved in the mock is the msg byte buffer. The msg struct
+ * was deleted when it was sent. Instead of inspecting the msg byte
+ * buffer, lets just decode it. */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(msg);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, msg->msg_header->type);
+ /* Verify the error object */
+ CU_ASSERT_EQUAL(1, msg->obj_list->num_entries);
+ struct pcep_object_error *error_obj = msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, error_obj->header.object_class);
+ CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, error_obj->header.object_type);
+ CU_ASSERT_EQUAL(PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION,
+ error_obj->error_type);
+ CU_ASSERT_EQUAL(PCEP_ERRV_RECVD_INVALID_OPEN_MSG,
+ error_obj->error_value);
+ pcep_msg_free_message(msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+}
+
+
+void test_handle_socket_comm_event_open_error()
+{
+ /* Test when the PCE rejects the PCC Open with an Error
+ * that a "corrected" Open message is sent. */
+
+ create_message_for_test(PCEP_TYPE_ERROR, false, true);
+ struct pcep_object_error *error_object = pcep_obj_create_error(
+ PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG);
+ struct pcep_object_open *error_open_object =
+ pcep_obj_create_open(1, 1, 1, NULL);
+ /* The configured [Keep-alive, Dead-timer] values are [5, 5],
+ * this error open object will request they be changed to [10, 10] */
+ error_open_object->open_keepalive = 10;
+ error_open_object->open_deadtimer = 10;
+ dll_append(message->obj_list, error_object);
+ dll_append(message->obj_list, error_open_object);
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pcc_open_rejected);
+ CU_ASSERT_FALSE(session.pce_open_keep_alive_sent);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ /* Another Open should be sent */
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 2);
+
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_SENT_INVALID_OPEN, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+
+ /* Check the Corrected Open Message */
+
+ /* What gets saved in the mock is the msg byte buffer. The msg struct
+ * was deleted when it was sent. Instead of inspecting the msg byte
+ * buffer, lets just decode it. */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *open_msg_corrected =
+ pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg_corrected);
+ struct pcep_object_open *open_object_corrected =
+ (struct pcep_object_open *)pcep_obj_get(
+ open_msg_corrected->obj_list, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_PTR_NOT_NULL(open_object_corrected);
+ /* Verify the Keep-alive and Dead timers have been negotiated */
+ CU_ASSERT_EQUAL(error_open_object->open_keepalive,
+ open_object_corrected->open_keepalive);
+ CU_ASSERT_EQUAL(error_open_object->open_deadtimer,
+ open_object_corrected->open_deadtimer);
+ CU_ASSERT_EQUAL(session.pcc_config.dead_timer_pce_negotiated_seconds,
+ open_object_corrected->open_deadtimer);
+ CU_ASSERT_EQUAL(
+ session.pcc_config.keep_alive_pce_negotiated_timer_seconds,
+ open_object_corrected->open_keepalive);
+ CU_ASSERT_NOT_EQUAL(
+ session.pcc_config.dead_timer_pce_negotiated_seconds,
+ session.pcc_config.dead_timer_seconds);
+ CU_ASSERT_NOT_EQUAL(
+ session.pcc_config.keep_alive_pce_negotiated_timer_seconds,
+ session.pcc_config.keep_alive_seconds);
+
+ pcep_msg_free_message(open_msg_corrected);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+}
+
+
+void test_handle_socket_comm_event_keep_alive()
+{
+ /* Test when a Keep Alive is received, but the PCE Open has not been
+ * received yet */
+ create_message_for_test(PCEP_TYPE_KEEPALIVE, false, false);
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ session.timer_id_dead_timer = 100;
+ session.timer_id_open_keep_wait = 200;
+ session.pce_open_accepted = false;
+ session.pce_open_received = false;
+ session.pcc_open_accepted = false;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pcc_open_accepted);
+ CU_ASSERT_FALSE(session.pce_open_keep_alive_sent);
+ CU_ASSERT_FALSE(session.pcc_open_rejected);
+ CU_ASSERT_FALSE(session.pce_open_accepted);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.timer_id_dead_timer, 100);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 0);
+
+ /* Test when a Keep Alive is received, and the PCE Open has been
+ * received and accepted */
+ create_message_for_test(PCEP_TYPE_KEEPALIVE, false, false);
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ session.timer_id_dead_timer = 100;
+ session.timer_id_open_keep_wait = 200;
+ session.pce_open_received = true;
+ session.pce_open_accepted = true;
+ session.pcc_open_accepted = false;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pcc_open_accepted);
+ CU_ASSERT_TRUE(session.pce_open_keep_alive_sent);
+ CU_ASSERT_FALSE(session.pcc_open_rejected);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTED);
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.timer_id_dead_timer, 100);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+
+ /* Test when a Keep Alive is received, and the PCE Open has been
+ * received and rejected */
+ create_message_for_test(PCEP_TYPE_KEEPALIVE, false, false);
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ session.timer_id_dead_timer = 100;
+ session.timer_id_open_keep_wait = 200;
+ session.pce_open_received = true;
+ session.pce_open_accepted = false;
+ session.pce_open_rejected = true;
+ session.pce_open_keep_alive_sent = false;
+ session.pcc_open_accepted = true;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pcc_open_accepted);
+ CU_ASSERT_FALSE(session.pce_open_keep_alive_sent);
+ CU_ASSERT_FALSE(session.pcc_open_rejected);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.timer_id_dead_timer, 100);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+
+ /* The session is considered connected, when both the
+ * PCE and PCC Open messages have been accepted */
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_CONNECTED_TO_PCE, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_pcrep()
+{
+ create_message_for_test(PCEP_TYPE_PCREP, false, true);
+ struct pcep_object_rp *rp =
+ pcep_obj_create_rp(1, true, true, true, true, 1, NULL);
+ dll_append(message->obj_list, rp);
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_pcreq()
+{
+ create_message_for_test(PCEP_TYPE_PCREQ, false, false);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ /* The PCC does not support receiving PcReq messages, so an error should
+ * be sent */
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 0);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *error_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(error_msg);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, error_msg->msg_header->type);
+ /* Verify the error object */
+ CU_ASSERT_EQUAL(1, error_msg->obj_list->num_entries);
+ struct pcep_object_error *obj = error_msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, obj->header.object_class);
+ CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, obj->header.object_type);
+ CU_ASSERT_EQUAL(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED, obj->error_type);
+ CU_ASSERT_EQUAL(PCEP_ERRV_UNASSIGNED, obj->error_value);
+ pcep_msg_free_message(error_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+}
+
+
+void test_handle_socket_comm_event_report()
+{
+ create_message_for_test(PCEP_TYPE_REPORT, false, false);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ /* The PCC does not support receiving Report messages, so an error
+ * should be sent */
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 0);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *error_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(error_msg);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, error_msg->msg_header->type);
+ /* Verify the error object */
+ CU_ASSERT_EQUAL(1, error_msg->obj_list->num_entries);
+ struct pcep_object_error *obj = error_msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, obj->header.object_class);
+ CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, obj->header.object_type);
+ CU_ASSERT_EQUAL(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED, obj->error_type);
+ CU_ASSERT_EQUAL(PCEP_ERRV_UNASSIGNED, obj->error_value);
+ pcep_msg_free_message(error_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+}
+
+
+void test_handle_socket_comm_event_update()
+{
+ create_message_for_test(PCEP_TYPE_UPDATE, false, true);
+ struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL);
+ struct pcep_object_lsp *lsp =
+ pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true,
+ true, true, true, NULL);
+ double_linked_list *ero_subobj_list = dll_initialize();
+ dll_append(ero_subobj_list, pcep_obj_create_ro_subobj_asn(0x0102));
+ struct pcep_object_ro *ero = pcep_obj_create_ero(ero_subobj_list);
+ struct pcep_object_metric *metric =
+ pcep_obj_create_metric(PCEP_METRIC_TE, false, true, 16.0);
+ dll_append(message->obj_list, srp);
+ dll_append(message->obj_list, lsp);
+ dll_append(message->obj_list, ero);
+ dll_append(message->obj_list, metric);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_UPDATE, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_initiate()
+{
+ create_message_for_test(PCEP_TYPE_INITIATE, false, true);
+ struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL);
+ struct pcep_object_lsp *lsp =
+ pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true,
+ true, true, true, NULL);
+ dll_append(message->obj_list, srp);
+ dll_append(message->obj_list, lsp);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_INITIATE, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_notify()
+{
+ create_message_for_test(PCEP_TYPE_PCNOTF, false, true);
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_PCNOTF, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_error()
+{
+ create_message_for_test(PCEP_TYPE_ERROR, false, true);
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_unknown_msg()
+{
+ create_message_for_test(13, false, false);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ /* Sending an unsupported message type, so an error should be sent,
+ * but the connection should remain open, since max_unknown_messages = 2
+ */
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 0);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(msg);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, msg->msg_header->type);
+ /* Verify the error object */
+ CU_ASSERT_EQUAL(1, msg->obj_list->num_entries);
+ struct pcep_object_error *error_obj = msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, error_obj->header.object_class);
+ CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, error_obj->header.object_type);
+ CU_ASSERT_EQUAL(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED,
+ error_obj->error_type);
+ CU_ASSERT_EQUAL(PCEP_ERRV_UNASSIGNED, error_obj->error_value);
+ pcep_msg_free_message(msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+ destroy_message_for_test();
+
+ /* Send another unsupported message type, an error should be sent and
+ * the connection should be closed, since max_unknown_messages = 2 */
+ create_message_for_test(13, false, false);
+ reset_mock_socket_comm_info();
+ mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ verify_socket_comm_times_called(0, 0, 0, 2, 1, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_RCVD_MAX_UNKOWN_MSGS, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+
+ /* Verify the error message */
+ encoded_msg = dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(msg);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, msg->msg_header->type);
+ /* Verify the error object */
+ CU_ASSERT_EQUAL(1, msg->obj_list->num_entries);
+ error_obj = msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, error_obj->header.object_class);
+ CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, error_obj->header.object_type);
+ CU_ASSERT_EQUAL(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED,
+ error_obj->error_type);
+ CU_ASSERT_EQUAL(PCEP_ERRV_UNASSIGNED, error_obj->error_value);
+ pcep_msg_free_message(msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ /* Verify the Close message */
+ encoded_msg = dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(msg);
+ CU_ASSERT_EQUAL(PCEP_TYPE_CLOSE, msg->msg_header->type);
+ /* Verify the error object */
+ CU_ASSERT_EQUAL(1, msg->obj_list->num_entries);
+ struct pcep_object_close *close_obj = msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_CLOSE, close_obj->header.object_class);
+ CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_CLOSE, close_obj->header.object_type);
+ CU_ASSERT_EQUAL(PCEP_CLOSE_REASON_UNREC_MSG, close_obj->reason);
+ pcep_msg_free_message(msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+}
+
+
+void test_connection_failure(void)
+{
+ /*
+ * Test when 2 invalid Open messages are received that a
+ * PCC_CONNECTION_FAILURE event is generated.
+ */
+ create_message_for_test(PCEP_TYPE_OPEN, false, false);
+ reset_mock_socket_comm_info();
+ struct pcep_object_open *open_object =
+ pcep_obj_create_open(1, 1, 1, NULL);
+ /* Make the Open message invalid */
+ open_object->open_deadtimer =
+ session.pcc_config.max_dead_timer_seconds + 1;
+ dll_append(message->obj_list, open_object);
+ session.pce_open_received = false;
+ session.pce_open_accepted = false;
+ session.pce_open_rejected = false;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pce_open_received);
+ CU_ASSERT_TRUE(session.pce_open_rejected);
+ CU_ASSERT_FALSE(session.pce_open_accepted);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ /* An error response should be sent, rejecting the Open */
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_RCVD_INVALID_OPEN, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ destroy_message_for_test();
+
+ /* Send the same erroneous Open again */
+ create_message_for_test(PCEP_TYPE_OPEN, false, false);
+ reset_mock_socket_comm_info();
+ open_object = pcep_obj_create_open(1, 1, 1, NULL);
+ /* Make the Open message invalid */
+ open_object->open_deadtimer =
+ session.pcc_config.max_dead_timer_seconds + 1;
+ dll_append(message->obj_list, open_object);
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pce_open_received);
+ CU_ASSERT_TRUE(session.pce_open_rejected);
+ CU_ASSERT_FALSE(session.pce_open_accepted);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED);
+ /* An error response should be sent, rejecting the Open */
+ verify_socket_comm_times_called(0, 0, 0, 1, 1, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 2);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_RCVD_INVALID_OPEN, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_CONNECTION_FAILURE, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+
+ destroy_message_for_test();
+
+ /*
+ * Test when 2 invalid Open messages are sent that a
+ * PCC_CONNECTION_FAILURE event is generated.
+ */
+ create_message_for_test(PCEP_TYPE_ERROR, false, false);
+ reset_mock_socket_comm_info();
+ struct pcep_object_error *error_object = pcep_obj_create_error(
+ PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG);
+ dll_append(message->obj_list, error_object);
+ session.pcc_open_accepted = false;
+ session.pcc_open_rejected = false;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pcc_open_rejected);
+ CU_ASSERT_FALSE(session.pcc_open_accepted);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ /* Another Open should be sent */
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 2);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_SENT_INVALID_OPEN, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ destroy_message_for_test();
+
+ /* Send a socket close while connecting, which should
+ * generate a PCC_CONNECTION_FAILURE event */
+ reset_mock_socket_comm_info();
+ event.socket_closed = true;
+ event.received_msg_list = NULL;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pcc_open_rejected);
+ CU_ASSERT_FALSE(session.pcc_open_accepted);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 1, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 2);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCE_CLOSED_SOCKET, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_CONNECTION_FAILURE, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
diff --git a/pceplib/test/pcep_session_logic_states_test.h b/pceplib/test/pcep_session_logic_states_test.h
new file mode 100644
index 000000000..e42b501ed
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_states_test.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_SESSION_LOGIC_STATES_TEST_H
+#define PCEP_SESSION_LOGIC_STATES_TEST_H
+
+int pcep_session_logic_states_test_suite_setup(void);
+int pcep_session_logic_states_test_suite_teardown(void);
+void pcep_session_logic_states_test_setup(void);
+void pcep_session_logic_states_test_teardown(void);
+void test_handle_timer_event_dead_timer(void);
+void test_handle_timer_event_keep_alive(void);
+void test_handle_timer_event_open_keep_wait(void);
+void test_handle_socket_comm_event_null_params(void);
+void test_handle_socket_comm_event_close(void);
+void test_handle_socket_comm_event_open(void);
+void test_handle_socket_comm_event_open_error(void);
+void test_handle_socket_comm_event_keep_alive(void);
+void test_handle_socket_comm_event_pcrep(void);
+void test_handle_socket_comm_event_pcreq(void);
+void test_handle_socket_comm_event_report(void);
+void test_handle_socket_comm_event_update(void);
+void test_handle_socket_comm_event_initiate(void);
+void test_handle_socket_comm_event_notify(void);
+void test_handle_socket_comm_event_error(void);
+void test_handle_socket_comm_event_unknown_msg(void);
+void test_connection_failure(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_session_logic_test.c b/pceplib/test/pcep_session_logic_test.c
new file mode 100644
index 000000000..66db4fbae
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_test.c
@@ -0,0 +1,360 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_socket_comm_mock.h"
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_test.h"
+
+/*
+ * Test suite setup and teardown called before AND after the test suite.
+ */
+
+int pcep_session_logic_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_session_logic_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+/*
+ * Test case setup and teardown called before AND after each test.
+ */
+
+void pcep_session_logic_test_setup()
+{
+ setup_mock_socket_comm_info();
+}
+
+
+void pcep_session_logic_test_teardown()
+{
+ stop_session_logic();
+ teardown_mock_socket_comm_info();
+}
+
+
+/*
+ * Test cases
+ */
+
+void test_run_stop_session_logic()
+{
+ CU_ASSERT_TRUE(run_session_logic());
+ CU_ASSERT_TRUE(stop_session_logic());
+}
+
+
+void test_run_session_logic_twice()
+{
+ CU_ASSERT_TRUE(run_session_logic());
+ CU_ASSERT_FALSE(run_session_logic());
+}
+
+
+void test_session_logic_without_run()
+{
+ /* Verify the functions that depend on run_session_logic() being called
+ */
+ CU_ASSERT_FALSE(stop_session_logic());
+}
+
+
+void test_create_pcep_session_null_params()
+{
+ pcep_configuration config;
+ struct in_addr pce_ip;
+
+ CU_ASSERT_PTR_NULL(create_pcep_session(NULL, NULL));
+ CU_ASSERT_PTR_NULL(create_pcep_session(NULL, &pce_ip));
+ CU_ASSERT_PTR_NULL(create_pcep_session(&config, NULL));
+}
+
+
+void test_create_destroy_pcep_session()
+{
+ pcep_session *session;
+ pcep_configuration config;
+ struct in_addr pce_ip;
+
+ run_session_logic();
+
+ memset(&config, 0, sizeof(pcep_configuration));
+ config.keep_alive_seconds = 5;
+ config.dead_timer_seconds = 5;
+ config.request_time_seconds = 5;
+ config.max_unknown_messages = 5;
+ config.max_unknown_requests = 5;
+ inet_pton(AF_INET, "127.0.0.1", &(pce_ip));
+
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ session = create_pcep_session(&config, &pce_ip);
+ CU_ASSERT_PTR_NOT_NULL(session);
+ /* What gets saved in the mock is the msg byte buffer. The msg struct
+ * was deleted when it was sent. Instead of inspecting the msg byte
+ * buffer, lets just decode it. */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ /* Should be an Open, with no TLVs: length = 12 */
+ CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN);
+ CU_ASSERT_EQUAL(open_msg->encoded_message_length, 12);
+ destroy_pcep_session(session);
+ pcep_msg_free_message(open_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ stop_session_logic();
+}
+
+
+void test_create_destroy_pcep_session_ipv6()
+{
+ pcep_session *session;
+ pcep_configuration config;
+ struct in6_addr pce_ip;
+
+ run_session_logic();
+
+ memset(&config, 0, sizeof(pcep_configuration));
+ config.keep_alive_seconds = 5;
+ config.dead_timer_seconds = 5;
+ config.request_time_seconds = 5;
+ config.max_unknown_messages = 5;
+ config.max_unknown_requests = 5;
+ config.is_src_ipv6 = true;
+ inet_pton(AF_INET6, "::1", &pce_ip);
+
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ session = create_pcep_session_ipv6(&config, &pce_ip);
+ CU_ASSERT_PTR_NOT_NULL(session);
+ CU_ASSERT_TRUE(session->socket_comm_session->is_ipv6);
+ /* What gets saved in the mock is the msg byte buffer. The msg struct
+ * was deleted when it was sent. Instead of inspecting the msg byte
+ * buffer, lets just decode it. */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ /* Should be an Open, with no TLVs: length = 12 */
+ CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN);
+ CU_ASSERT_EQUAL(open_msg->encoded_message_length, 12);
+ destroy_pcep_session(session);
+ pcep_msg_free_message(open_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ stop_session_logic();
+}
+
+
+void test_create_pcep_session_open_tlvs()
+{
+ pcep_session *session;
+ struct in_addr pce_ip;
+ struct pcep_message *open_msg;
+ struct pcep_object_header *open_obj;
+ pcep_configuration config;
+ memset(&config, 0, sizeof(pcep_configuration));
+ config.pcep_msg_versioning = create_default_pcep_versioning();
+ inet_pton(AF_INET, "127.0.0.1", &(pce_ip));
+
+ run_session_logic();
+
+ /* Verify the created Open message only has 1 TLV:
+ * pcep_tlv_create_stateful_pce_capability() */
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ config.support_stateful_pce_lsp_update = true;
+ config.pcep_msg_versioning->draft_ietf_pce_segment_routing_07 = false;
+ config.support_sr_te_pst = false;
+
+ session = create_pcep_session(&config, &pce_ip);
+ CU_ASSERT_PTR_NOT_NULL(session);
+ /* Get and verify the Open Message */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ /* Get and verify the Open Message objects */
+ CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list);
+ CU_ASSERT_TRUE(open_msg->obj_list->num_entries > 0);
+ /* Get and verify the Open object */
+ open_obj = pcep_obj_get(open_msg->obj_list, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_PTR_NOT_NULL(open_obj);
+ /* Get and verify the Open object TLVs */
+ CU_ASSERT_PTR_NOT_NULL(open_obj->tlv_list);
+ CU_ASSERT_EQUAL(open_obj->tlv_list->num_entries, 1);
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)
+ open_obj->tlv_list->head->data)
+ ->type,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+
+ destroy_pcep_session(session);
+ pcep_msg_free_message(open_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ /* Verify the created Open message only has 2 TLVs:
+ * pcep_tlv_create_stateful_pce_capability()
+ * pcep_tlv_create_lsp_db_version() */
+ reset_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ config.support_include_db_version = true;
+ config.lsp_db_version = 100;
+
+ session = create_pcep_session(&config, &pce_ip);
+ CU_ASSERT_PTR_NOT_NULL(session);
+ /* Get and verify the Open Message */
+ encoded_msg = dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ /* Get and verify the Open Message objects */
+ CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list);
+ CU_ASSERT_TRUE(open_msg->obj_list->num_entries > 0);
+ /* Get and verify the Open object */
+ open_obj = pcep_obj_get(open_msg->obj_list, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_PTR_NOT_NULL(open_obj);
+ /* Get and verify the Open object TLVs */
+ CU_ASSERT_PTR_NOT_NULL(open_obj->tlv_list);
+ CU_ASSERT_EQUAL(open_obj->tlv_list->num_entries, 2);
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)
+ open_obj->tlv_list->head->data)
+ ->type,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)
+ open_obj->tlv_list->head->next_node->data)
+ ->type,
+ PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION);
+
+ destroy_pcep_session(session);
+ pcep_msg_free_message(open_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+
+ /* Verify the created Open message only has 4 TLVs:
+ * pcep_tlv_create_stateful_pce_capability()
+ * pcep_tlv_create_lsp_db_version()
+ * pcep_tlv_create_sr_pce_capability()
+ * pcep_tlv_create_path_setup_type_capability() */
+ reset_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ config.support_sr_te_pst = true;
+
+ session = create_pcep_session(&config, &pce_ip);
+ CU_ASSERT_PTR_NOT_NULL(session);
+ /* Get and verify the Open Message */
+ encoded_msg = dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ /* Get and verify the Open Message objects */
+ CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list);
+ CU_ASSERT_TRUE(open_msg->obj_list->num_entries > 0);
+ /* Get and verify the Open object */
+ open_obj = pcep_obj_get(open_msg->obj_list, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_PTR_NOT_NULL(open_obj);
+ /* Get and verify the Open object TLVs */
+ CU_ASSERT_PTR_NOT_NULL(open_obj->tlv_list);
+ CU_ASSERT_EQUAL(open_obj->tlv_list->num_entries, 3);
+ double_linked_list_node *tlv_node = open_obj->tlv_list->head;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+ tlv_node = tlv_node->next_node;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION);
+ tlv_node = tlv_node->next_node;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY);
+
+ destroy_pcep_session(session);
+ pcep_msg_free_message(open_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ /* Verify the created Open message only has 4 TLVs:
+ * pcep_tlv_create_stateful_pce_capability()
+ * pcep_tlv_create_lsp_db_version()
+ * pcep_tlv_create_sr_pce_capability()
+ * pcep_tlv_create_path_setup_type_capability() */
+ reset_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ config.pcep_msg_versioning->draft_ietf_pce_segment_routing_07 = true;
+
+ session = create_pcep_session(&config, &pce_ip);
+ CU_ASSERT_PTR_NOT_NULL(session);
+ /* Get and verify the Open Message */
+ encoded_msg = dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ /* Get and verify the Open Message objects */
+ CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list);
+ CU_ASSERT_TRUE(open_msg->obj_list->num_entries > 0);
+ /* Get and verify the Open object */
+ open_obj = pcep_obj_get(open_msg->obj_list, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_PTR_NOT_NULL(open_obj);
+ /* Get and verify the Open object TLVs */
+ CU_ASSERT_PTR_NOT_NULL(open_obj->tlv_list);
+ CU_ASSERT_EQUAL(open_obj->tlv_list->num_entries, 4);
+ tlv_node = open_obj->tlv_list->head;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+ tlv_node = tlv_node->next_node;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION);
+ tlv_node = tlv_node->next_node;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY);
+ tlv_node = tlv_node->next_node;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY);
+
+ destroy_pcep_versioning(config.pcep_msg_versioning);
+ destroy_pcep_session(session);
+ pcep_msg_free_message(open_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ stop_session_logic();
+}
+
+
+void test_destroy_pcep_session_null_session()
+{
+ /* Just testing that it does not core dump */
+ destroy_pcep_session(NULL);
+}
diff --git a/pceplib/test/pcep_session_logic_test.h b/pceplib/test/pcep_session_logic_test.h
new file mode 100644
index 000000000..6cc196325
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_test.h
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_SESSION_LOGIC_TEST_H_
+#define PCEP_SESSION_LOGIC_TEST_H_
+
+int pcep_session_logic_test_suite_setup(void);
+int pcep_session_logic_test_suite_teardown(void);
+void pcep_session_logic_test_setup(void);
+void pcep_session_logic_test_teardown(void);
+void test_run_stop_session_logic(void);
+void test_run_session_logic_twice(void);
+void test_session_logic_without_run(void);
+void test_create_pcep_session_null_params(void);
+void test_create_destroy_pcep_session(void);
+void test_create_destroy_pcep_session_ipv6(void);
+void test_create_pcep_session_open_tlvs(void);
+void test_destroy_pcep_session_null_session(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_session_logic_tests.c b/pceplib/test/pcep_session_logic_tests.c
new file mode 100644
index 000000000..67bf6e22e
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_tests.c
@@ -0,0 +1,201 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/Basic.h>
+#include <CUnit/CUnit.h>
+#include <CUnit/TestDB.h>
+
+#include "pcep_session_logic_loop_test.h"
+#include "pcep_session_logic_states_test.h"
+#include "pcep_session_logic_test.h"
+
+
+int main(int argc, char **argv)
+{
+ /* Unused parameters cause compilation warnings */
+ (void)argc;
+ (void)argv;
+
+ CU_initialize_registry();
+
+ /*
+ * Tests defined in pcep_socket_comm_test.c
+ */
+ CU_pSuite test_session_logic_suite =
+ CU_add_suite_with_setup_and_teardown(
+ "PCEP Session Logic Test Suite",
+ pcep_session_logic_test_suite_setup, // suite setup and
+ // cleanup function
+ // pointers
+ pcep_session_logic_test_suite_teardown,
+ pcep_session_logic_test_setup, // test case setup
+ // function pointer
+ pcep_session_logic_test_teardown); // test case teardown
+ // function pointer
+
+ CU_add_test(test_session_logic_suite, "test_run_stop_session_logic",
+ test_run_stop_session_logic);
+ CU_add_test(test_session_logic_suite, "test_run_session_logic_twice",
+ test_run_session_logic_twice);
+ CU_add_test(test_session_logic_suite, "test_session_logic_without_run",
+ test_session_logic_without_run);
+ CU_add_test(test_session_logic_suite,
+ "test_create_pcep_session_null_params",
+ test_create_pcep_session_null_params);
+ CU_add_test(test_session_logic_suite,
+ "test_create_destroy_pcep_session",
+ test_create_destroy_pcep_session);
+ CU_add_test(test_session_logic_suite,
+ "test_create_destroy_pcep_session_ipv6",
+ test_create_destroy_pcep_session_ipv6);
+ CU_add_test(test_session_logic_suite,
+ "test_create_pcep_session_open_tlvs",
+ test_create_pcep_session_open_tlvs);
+ CU_add_test(test_session_logic_suite,
+ "test_destroy_pcep_session_null_session",
+ test_destroy_pcep_session_null_session);
+
+ CU_pSuite test_session_logic_loop_suite =
+ CU_add_suite_with_setup_and_teardown(
+ "PCEP Session Logic Loop Test Suite",
+ pcep_session_logic_loop_test_suite_setup, // suite setup
+ // and cleanup
+ // function
+ // pointers
+ pcep_session_logic_loop_test_suite_teardown,
+ pcep_session_logic_loop_test_setup, // test case setup
+ // function pointer
+ pcep_session_logic_loop_test_teardown); // test case
+ // teardown
+ // function
+ // pointer
+
+ CU_add_test(test_session_logic_loop_suite,
+ "test_session_logic_loop_null_data",
+ test_session_logic_loop_null_data);
+ CU_add_test(test_session_logic_loop_suite,
+ "test_session_logic_loop_inactive",
+ test_session_logic_loop_inactive);
+ CU_add_test(test_session_logic_loop_suite,
+ "test_session_logic_msg_ready_handler",
+ test_session_logic_msg_ready_handler);
+ CU_add_test(test_session_logic_loop_suite,
+ "test_session_logic_conn_except_notifier",
+ test_session_logic_conn_except_notifier);
+ CU_add_test(test_session_logic_loop_suite,
+ "test_session_logic_timer_expire_handler",
+ test_session_logic_timer_expire_handler);
+
+ CU_pSuite test_session_logic_states_suite =
+ CU_add_suite_with_setup_and_teardown(
+ "PCEP Session Logic States Test Suite",
+ pcep_session_logic_states_test_suite_setup, // suite
+ // setup and
+ // cleanup
+ // function
+ // pointers
+ pcep_session_logic_states_test_suite_teardown,
+ pcep_session_logic_states_test_setup, // test case setup
+ // function
+ // pointer
+ pcep_session_logic_states_test_teardown); // test case
+ // teardown
+ // function
+ // pointer
+
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_timer_event_dead_timer",
+ test_handle_timer_event_dead_timer);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_timer_event_keep_alive",
+ test_handle_timer_event_keep_alive);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_timer_event_open_keep_wait",
+ test_handle_timer_event_open_keep_wait);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_null_params",
+ test_handle_socket_comm_event_null_params);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_close",
+ test_handle_socket_comm_event_close);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_open",
+ test_handle_socket_comm_event_open);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_open_error",
+ test_handle_socket_comm_event_open_error);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_keep_alive",
+ test_handle_socket_comm_event_keep_alive);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_pcrep",
+ test_handle_socket_comm_event_pcrep);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_pcreq",
+ test_handle_socket_comm_event_pcreq);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_report",
+ test_handle_socket_comm_event_report);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_update",
+ test_handle_socket_comm_event_update);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_initiate",
+ test_handle_socket_comm_event_initiate);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_notify",
+ test_handle_socket_comm_event_notify);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_error",
+ test_handle_socket_comm_event_error);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_unknown_msg",
+ test_handle_socket_comm_event_unknown_msg);
+ CU_add_test(test_session_logic_states_suite, "test_connection_failure",
+ test_connection_failure);
+
+ /*
+ * Run the tests and cleanup.
+ */
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_FailureRecord *failure_record = CU_get_failure_list();
+ if (failure_record != NULL) {
+ printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n");
+ do {
+ printf("\t [%s] [%s] [%s:%d]\n",
+ failure_record->pSuite->pName,
+ failure_record->pTest->pName,
+ failure_record->strFileName,
+ failure_record->uiLineNumber);
+ failure_record = failure_record->pNext;
+
+ } while (failure_record != NULL);
+ }
+
+ CU_pRunSummary run_summary = CU_get_run_summary();
+ int result = run_summary->nTestsFailed;
+ CU_cleanup_registry();
+
+ return result;
+}
diff --git a/pceplib/test/pcep_session_logic_tests_valgrind.sh b/pceplib/test/pcep_session_logic_tests_valgrind.sh
new file mode 100755
index 000000000..435bb3d5c
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_tests_valgrind.sh
@@ -0,0 +1,2 @@
+source pceplib/test/pcep_tests_valgrind.sh
+valgrind_test pceplib/test/pcep_session_logic_tests
diff --git a/pceplib/test/pcep_socket_comm_loop_test.c b/pceplib/test/pcep_socket_comm_loop_test.c
new file mode 100644
index 000000000..94f0983ca
--- /dev/null
+++ b/pceplib/test/pcep_socket_comm_loop_test.c
@@ -0,0 +1,194 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_socket_comm_internals.h"
+#include "pcep_socket_comm_loop.h"
+#include "pcep_socket_comm_loop_test.h"
+#include "pcep_socket_comm.h"
+#include "pcep_utils_memory.h"
+
+void test_loop_conn_except_notifier(void *session_data, int socket_fd);
+
+/*
+ * Functions to be tested, implemented in pcep_socket_comm_loop.c
+ */
+
+typedef struct ready_to_read_handler_info_ {
+ bool handler_called;
+ bool except_handler_called;
+ void *data;
+ int socket_fd;
+ int bytes_read;
+
+} ready_to_read_handler_info;
+
+static ready_to_read_handler_info read_handler_info;
+static pcep_socket_comm_session *test_comm_session;
+static pcep_socket_comm_handle *test_socket_comm_handle = NULL;
+
+static int test_loop_message_ready_to_read_handler(void *session_data,
+ int socket_fd)
+{
+ read_handler_info.handler_called = true;
+ read_handler_info.data = session_data;
+ read_handler_info.socket_fd = socket_fd;
+
+ return read_handler_info.bytes_read;
+}
+
+
+void test_loop_conn_except_notifier(void *session_data, int socket_fd)
+{
+ (void)session_data;
+ (void)socket_fd;
+ read_handler_info.except_handler_called = true;
+}
+
+
+/*
+ * Test case setup and teardown called before AND after each test.
+ */
+void pcep_socket_comm_loop_test_setup()
+{
+ test_socket_comm_handle =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_handle));
+ memset(test_socket_comm_handle, 0, sizeof(pcep_socket_comm_handle));
+ test_socket_comm_handle->active = false;
+ test_socket_comm_handle->read_list =
+ ordered_list_initialize(socket_fd_node_compare);
+ test_socket_comm_handle->write_list =
+ ordered_list_initialize(socket_fd_node_compare);
+ test_socket_comm_handle->session_list =
+ ordered_list_initialize(pointer_compare_function);
+ pthread_mutex_init(&test_socket_comm_handle->socket_comm_mutex, NULL);
+ test_socket_comm_handle->num_active_sessions = 0;
+
+ test_comm_session =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_session));
+ memset(test_comm_session, 0, sizeof(pcep_socket_comm_session));
+ test_comm_session->message_ready_to_read_handler =
+ test_loop_message_ready_to_read_handler;
+ ordered_list_add_node(test_socket_comm_handle->session_list,
+ test_comm_session);
+
+ read_handler_info.handler_called = false;
+ read_handler_info.except_handler_called = false;
+ read_handler_info.data = NULL;
+ read_handler_info.socket_fd = -1;
+ read_handler_info.bytes_read = 0;
+}
+
+
+void pcep_socket_comm_loop_test_teardown()
+{
+ pthread_mutex_destroy(&test_socket_comm_handle->socket_comm_mutex);
+ ordered_list_destroy(test_socket_comm_handle->read_list);
+ ordered_list_destroy(test_socket_comm_handle->write_list);
+ ordered_list_destroy(test_socket_comm_handle->session_list);
+ pceplib_free(PCEPLIB_INFRA, test_socket_comm_handle);
+ test_socket_comm_handle = NULL;
+
+ if (test_comm_session != NULL) {
+ pceplib_free(PCEPLIB_INFRA, test_comm_session);
+ test_comm_session = NULL;
+ }
+}
+
+
+/*
+ * Test cases
+ */
+
+void test_socket_comm_loop_null_handle()
+{
+ /* Verify that socket_comm_loop() correctly handles a NULL
+ * timers_context */
+ socket_comm_loop(NULL);
+}
+
+
+void test_socket_comm_loop_not_active()
+{
+ /* Verify that event_loop() correctly handles an inactive flag */
+ pcep_socket_comm_handle handle;
+ handle.active = false;
+ socket_comm_loop(&handle);
+}
+
+
+void test_handle_reads_no_read()
+{
+ CU_ASSERT_PTR_NULL(test_socket_comm_handle->read_list->head);
+
+ handle_reads(test_socket_comm_handle);
+
+ CU_ASSERT_FALSE(read_handler_info.handler_called);
+ CU_ASSERT_FALSE(read_handler_info.except_handler_called);
+ CU_ASSERT_PTR_NULL(test_socket_comm_handle->read_list->head);
+}
+
+
+void test_handle_reads_read_message()
+{
+ /* Setup the comm session so that it can read.
+ * It should read 100 bytes, which simulates a successful read */
+ test_comm_session->socket_fd = 10;
+ read_handler_info.bytes_read = 100;
+ FD_SET(test_comm_session->socket_fd,
+ &test_socket_comm_handle->read_master_set);
+ ordered_list_add_node(test_socket_comm_handle->read_list,
+ test_comm_session);
+
+ handle_reads(test_socket_comm_handle);
+
+ CU_ASSERT_TRUE(read_handler_info.handler_called);
+ CU_ASSERT_FALSE(read_handler_info.except_handler_called);
+ CU_ASSERT_EQUAL(test_comm_session->received_bytes,
+ read_handler_info.bytes_read);
+}
+
+
+void test_handle_reads_read_message_close()
+{
+ /* Setup the comm session so that it can read.
+ * It should read 0 bytes, which simulates that the socket closed */
+ test_comm_session->socket_fd = 11;
+ read_handler_info.bytes_read = 0;
+ FD_SET(test_comm_session->socket_fd,
+ &test_socket_comm_handle->read_master_set);
+ ordered_list_add_node(test_socket_comm_handle->read_list,
+ test_comm_session);
+
+ handle_reads(test_socket_comm_handle);
+
+ CU_ASSERT_TRUE(read_handler_info.handler_called);
+ CU_ASSERT_FALSE(read_handler_info.except_handler_called);
+ CU_ASSERT_EQUAL(test_comm_session->received_bytes,
+ read_handler_info.bytes_read);
+ CU_ASSERT_PTR_NULL(test_socket_comm_handle->read_list->head);
+}
diff --git a/pceplib/test/pcep_socket_comm_loop_test.h b/pceplib/test/pcep_socket_comm_loop_test.h
new file mode 100644
index 000000000..d2e3f21ee
--- /dev/null
+++ b/pceplib/test/pcep_socket_comm_loop_test.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_SOCKET_COMM_LOOP_TEST_H_
+#define PCEP_SOCKET_COMM_LOOP_TEST_H_
+
+void pcep_socket_comm_loop_test_setup(void);
+void pcep_socket_comm_loop_test_teardown(void);
+void test_socket_comm_loop_null_handle(void);
+void test_socket_comm_loop_not_active(void);
+void test_handle_reads_no_read(void);
+void test_handle_reads_read_message(void);
+void test_handle_reads_read_message_close(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_socket_comm_test.c b/pceplib/test/pcep_socket_comm_test.c
new file mode 100644
index 000000000..35afbcbb1
--- /dev/null
+++ b/pceplib/test/pcep_socket_comm_test.c
@@ -0,0 +1,308 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <netinet/in.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_socket_comm.h"
+#include "pcep_socket_comm_internals.h"
+#include "pcep_socket_comm_test.h"
+
+extern pcep_socket_comm_handle *socket_comm_handle_;
+
+static pcep_socket_comm_session *test_session = NULL;
+static struct in_addr test_host_ip;
+static struct in_addr test_src_ip;
+static struct in6_addr test_host_ipv6;
+static struct in6_addr test_src_ipv6;
+static short test_port = 4789;
+static short test_src_port = 4999;
+static uint32_t connect_timeout_millis = 500;
+
+/*
+ * Unit Test Basic pcep_socket_comm API usage.
+ * Testing sending messages, etc via sockets should be done
+ * with integration tests, not unit tests.
+ */
+
+/*
+ * Different socket_comm handler test implementations
+ */
+static void test_message_received_handler(void *session_data,
+ const char *message_data,
+ unsigned int message_length)
+{
+ (void)session_data;
+ (void)message_data;
+ (void)message_length;
+}
+
+static int test_message_ready_to_read_handler(void *session_data, int socket_fd)
+{
+ (void)session_data;
+ (void)socket_fd;
+ return 1;
+}
+
+static void test_message_sent_handler(void *session_data, int socket_fd)
+{
+ (void)session_data;
+ (void)socket_fd;
+ return;
+}
+
+static void test_connection_except_notifier(void *session_data, int socket_fd)
+{
+ (void)session_data;
+ (void)socket_fd;
+}
+
+
+/*
+ * Test case setup and teardown called before AND after each test.
+ */
+void pcep_socket_comm_test_setup()
+{
+ inet_pton(AF_INET, "127.0.0.1", &(test_host_ip));
+ inet_pton(AF_INET, "127.0.0.1", &(test_src_ip));
+ inet_pton(AF_INET6, "::1", &(test_host_ipv6));
+ inet_pton(AF_INET6, "::1", &(test_src_ipv6));
+}
+
+void pcep_socket_comm_test_teardown()
+{
+ socket_comm_session_teardown(test_session);
+ test_session = NULL;
+}
+
+
+/*
+ * Test cases
+ */
+
+void test_pcep_socket_comm_initialize()
+{
+ test_session = socket_comm_session_initialize(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_host_ip, test_port,
+ connect_timeout_millis, NULL, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_FALSE(test_session->is_ipv6);
+}
+
+
+void test_pcep_socket_comm_initialize_ipv6()
+{
+ test_session = socket_comm_session_initialize_ipv6(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_host_ipv6, test_port,
+ connect_timeout_millis, NULL, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_TRUE(test_session->is_ipv6);
+}
+
+
+void test_pcep_socket_comm_initialize_with_src()
+{
+ /* Test that INADDR_ANY will be used when src_ip is NULL */
+ test_session = socket_comm_session_initialize_with_src(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, NULL, 0, &test_host_ip,
+ test_port, connect_timeout_millis, NULL, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(
+ test_session->src_sock_addr.src_sock_addr_ipv4.sin_addr.s_addr,
+ INADDR_ANY);
+ CU_ASSERT_FALSE(test_session->is_ipv6);
+
+ socket_comm_session_teardown(test_session);
+ test_session = socket_comm_session_initialize_with_src(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_src_ip, test_src_port,
+ &test_host_ip, test_port, connect_timeout_millis, NULL, false,
+ NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(
+ test_session->src_sock_addr.src_sock_addr_ipv4.sin_addr.s_addr,
+ test_src_ip.s_addr);
+ CU_ASSERT_EQUAL(test_session->src_sock_addr.src_sock_addr_ipv4.sin_port,
+ ntohs(test_src_port));
+ CU_ASSERT_FALSE(test_session->is_ipv6);
+}
+
+
+void test_pcep_socket_comm_initialize_with_src_ipv6()
+{
+ /* Test that INADDR6_ANY will be used when src_ip is NULL */
+ test_session = socket_comm_session_initialize_with_src_ipv6(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, NULL, 0, &test_host_ipv6,
+ test_port, connect_timeout_millis, NULL, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(memcmp(&test_session->src_sock_addr.src_sock_addr_ipv6
+ .sin6_addr,
+ &in6addr_any, sizeof(struct in6_addr)),
+ 0);
+ CU_ASSERT_TRUE(test_session->is_ipv6);
+
+ socket_comm_session_teardown(test_session);
+ test_session = socket_comm_session_initialize_with_src_ipv6(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_src_ipv6, test_src_port,
+ &test_host_ipv6, test_port, connect_timeout_millis, NULL, false,
+ NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(memcmp(&test_session->src_sock_addr.src_sock_addr_ipv6
+ .sin6_addr,
+ &test_src_ipv6, sizeof(struct in6_addr)),
+ 0);
+ CU_ASSERT_EQUAL(
+ test_session->src_sock_addr.src_sock_addr_ipv6.sin6_port,
+ ntohs(test_src_port));
+ CU_ASSERT_TRUE(test_session->is_ipv6);
+}
+
+
+void test_pcep_socket_comm_initialize_tcpmd5()
+{
+ char tcp_md5_str[] = "hello";
+ int tcp_md5_strlen = strlen(tcp_md5_str);
+
+ test_session = socket_comm_session_initialize(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_host_ip, test_port, 1,
+ tcp_md5_str, true, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(0, strncmp(tcp_md5_str,
+ test_session->tcp_authentication_str,
+ tcp_md5_strlen));
+ CU_ASSERT_TRUE(test_session->is_tcp_auth_md5);
+ CU_ASSERT_FALSE(socket_comm_session_connect_tcp(test_session));
+ /* This call does not work, it returns errno=92, Protocol not available
+ getsockopt(test_session->socket_fd, SOL_SOCKET, TCP_MD5SIG, &sig,
+ &siglen);*/
+
+ socket_comm_session_teardown(test_session);
+ test_session = socket_comm_session_initialize(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_host_ip, test_port, 1,
+ tcp_md5_str, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(0, strncmp(tcp_md5_str,
+ test_session->tcp_authentication_str,
+ tcp_md5_strlen));
+ CU_ASSERT_FALSE(test_session->is_tcp_auth_md5);
+ CU_ASSERT_FALSE(socket_comm_session_connect_tcp(test_session));
+}
+
+
+void test_pcep_socket_comm_initialize_ipv6_tcpmd5()
+{
+ char tcp_md5_str[] = "hello";
+ int tcp_md5_strlen = strlen(tcp_md5_str);
+
+ test_session = socket_comm_session_initialize_ipv6(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_host_ipv6, test_port, 1,
+ tcp_md5_str, true, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(0, strncmp(tcp_md5_str,
+ test_session->tcp_authentication_str,
+ tcp_md5_strlen));
+ CU_ASSERT_TRUE(test_session->is_tcp_auth_md5);
+ CU_ASSERT_FALSE(socket_comm_session_connect_tcp(test_session));
+ /* This call does not work, it returns errno=92, Protocol not available
+ getsockopt(test_session->socket_fd, SOL_SOCKET, TCP_MD5SIG, &sig,
+ &siglen);*/
+
+ socket_comm_session_teardown(test_session);
+ test_session = socket_comm_session_initialize_ipv6(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_host_ipv6, test_port, 1,
+ tcp_md5_str, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(0, strncmp(tcp_md5_str,
+ test_session->tcp_authentication_str,
+ tcp_md5_strlen));
+ CU_ASSERT_FALSE(test_session->is_tcp_auth_md5);
+ CU_ASSERT_FALSE(socket_comm_session_connect_tcp(test_session));
+}
+
+
+void test_pcep_socket_comm_initialize_handlers()
+{
+ /* Verify incorrect handler usage is correctly handled */
+
+ /* Both receive handlers cannot be NULL */
+ test_session = socket_comm_session_initialize(
+ NULL, NULL, NULL, test_connection_except_notifier,
+ &test_host_ip, test_port, connect_timeout_millis, NULL, false,
+ NULL);
+ CU_ASSERT_PTR_NULL(test_session);
+
+ /* Both receive handlers cannot be set */
+ test_session = socket_comm_session_initialize(
+ test_message_received_handler,
+ test_message_ready_to_read_handler, test_message_sent_handler,
+ test_connection_except_notifier, &test_host_ip, test_port,
+ connect_timeout_millis, NULL, false, NULL);
+ CU_ASSERT_PTR_NULL(test_session);
+
+ /* Only one receive handler can be set */
+ test_session = socket_comm_session_initialize(
+ NULL, test_message_ready_to_read_handler,
+ test_message_sent_handler, test_connection_except_notifier,
+ &test_host_ip, test_port, connect_timeout_millis, NULL, false,
+ NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+}
+
+
+void test_pcep_socket_comm_session_not_initialized()
+{
+ CU_ASSERT_FALSE(socket_comm_session_connect_tcp(NULL));
+ CU_ASSERT_FALSE(socket_comm_session_close_tcp(NULL));
+ CU_ASSERT_FALSE(socket_comm_session_close_tcp_after_write(NULL));
+ socket_comm_session_send_message(NULL, NULL, 0, true);
+ CU_ASSERT_FALSE(socket_comm_session_teardown(NULL));
+}
+
+
+void test_pcep_socket_comm_session_destroy()
+{
+ test_session = socket_comm_session_initialize(
+ test_message_received_handler, NULL, test_message_sent_handler,
+ test_connection_except_notifier, &test_host_ip, test_port,
+ connect_timeout_millis, NULL, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_PTR_NOT_NULL(socket_comm_handle_);
+ CU_ASSERT_EQUAL(socket_comm_handle_->num_active_sessions, 1);
+
+ CU_ASSERT_TRUE(socket_comm_session_teardown(test_session));
+ test_session = NULL;
+ CU_ASSERT_PTR_NOT_NULL(socket_comm_handle_);
+
+ CU_ASSERT_TRUE(destroy_socket_comm_loop());
+ CU_ASSERT_PTR_NULL(socket_comm_handle_);
+}
diff --git a/pceplib/test/pcep_socket_comm_test.h b/pceplib/test/pcep_socket_comm_test.h
new file mode 100644
index 000000000..f857af087
--- /dev/null
+++ b/pceplib/test/pcep_socket_comm_test.h
@@ -0,0 +1,42 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_SOCKET_COMM_TEST_H_
+#define PCEP_SOCKET_COMM_TEST_H_
+
+void pcep_socket_comm_test_teardown(void);
+void pcep_socket_comm_test_setup(void);
+void test_pcep_socket_comm_initialize(void);
+void test_pcep_socket_comm_initialize_ipv6(void);
+void test_pcep_socket_comm_initialize_with_src(void);
+void test_pcep_socket_comm_initialize_with_src_ipv6(void);
+void test_pcep_socket_comm_initialize_tcpmd5(void);
+void test_pcep_socket_comm_initialize_ipv6_tcpmd5(void);
+void test_pcep_socket_comm_initialize_handlers(void);
+void test_pcep_socket_comm_session_not_initialized(void);
+void test_pcep_socket_comm_session_destroy(void);
+
+#endif
diff --git a/pceplib/test/pcep_socket_comm_tests.c b/pceplib/test/pcep_socket_comm_tests.c
new file mode 100644
index 000000000..293678f1a
--- /dev/null
+++ b/pceplib/test/pcep_socket_comm_tests.c
@@ -0,0 +1,128 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/Basic.h>
+#include <CUnit/CUnit.h>
+#include <CUnit/TestDB.h>
+
+#include "pcep_socket_comm_loop_test.h"
+#include "pcep_socket_comm_test.h"
+
+
+int main(int argc, char **argv)
+{
+ /* Unused parameters cause compilation warnings */
+ (void)argc;
+ (void)argv;
+
+ CU_initialize_registry();
+
+ /*
+ * Tests defined in pcep_socket_comm_test.c
+ */
+ CU_pSuite test_socket_comm_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP Socket Comm Test Suite", NULL,
+ NULL, // suite setup and cleanup function pointers
+ pcep_socket_comm_test_setup, // test case setup function pointer
+ pcep_socket_comm_test_teardown); // test case teardown function
+ // pointer
+
+ CU_add_test(test_socket_comm_suite, "test_pcep_socket_comm_initialize",
+ test_pcep_socket_comm_initialize);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_initialize_ipv6",
+ test_pcep_socket_comm_initialize_ipv6);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_initialize_with_src",
+ test_pcep_socket_comm_initialize_with_src);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_initialize_with_src_ipv6",
+ test_pcep_socket_comm_initialize_with_src_ipv6);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_initialize_tcpmd5",
+ test_pcep_socket_comm_initialize_tcpmd5);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_initialize_ipv6_tcpmd5",
+ test_pcep_socket_comm_initialize_ipv6_tcpmd5);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_initialize_handlers",
+ test_pcep_socket_comm_initialize_handlers);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_session_not_initialized",
+ test_pcep_socket_comm_session_not_initialized);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_session_destroy",
+ test_pcep_socket_comm_session_destroy);
+
+ /*
+ * Tests defined in pcep_socket_comm_loop_test.c
+ */
+ CU_pSuite test_socket_comm_loop_suite =
+ CU_add_suite_with_setup_and_teardown(
+ "PCEP Socket Comm Loop Test Suite", NULL, NULL,
+ pcep_socket_comm_loop_test_setup, // suite setup
+ // function pointer
+ pcep_socket_comm_loop_test_teardown); // suite cleanup
+ // function
+ // pointer
+
+ CU_add_test(test_socket_comm_loop_suite,
+ "test_socket_comm_loop_null_handle",
+ test_socket_comm_loop_null_handle);
+ CU_add_test(test_socket_comm_loop_suite,
+ "test_socket_comm_loop_not_active",
+ test_socket_comm_loop_not_active);
+ CU_add_test(test_socket_comm_loop_suite, "test_handle_reads_no_read",
+ test_handle_reads_no_read);
+ CU_add_test(test_socket_comm_loop_suite,
+ "test_handle_reads_read_message",
+ test_handle_reads_read_message);
+ CU_add_test(test_socket_comm_loop_suite,
+ "test_handle_reads_read_message_close",
+ test_handle_reads_read_message_close);
+
+ /*
+ * Run the tests and cleanup.
+ */
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_FailureRecord *failure_record = CU_get_failure_list();
+ if (failure_record != NULL) {
+ printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n");
+ do {
+ printf("\t [%s] [%s] [%s:%d]\n",
+ failure_record->pSuite->pName,
+ failure_record->pTest->pName,
+ failure_record->strFileName,
+ failure_record->uiLineNumber);
+ failure_record = failure_record->pNext;
+
+ } while (failure_record != NULL);
+ }
+
+ CU_pRunSummary run_summary = CU_get_run_summary();
+ int result = run_summary->nTestsFailed;
+ CU_cleanup_registry();
+
+ return result;
+}
diff --git a/pceplib/test/pcep_socket_comm_tests_valgrind.sh b/pceplib/test/pcep_socket_comm_tests_valgrind.sh
new file mode 100755
index 000000000..d9e95e4c1
--- /dev/null
+++ b/pceplib/test/pcep_socket_comm_tests_valgrind.sh
@@ -0,0 +1,2 @@
+source pceplib/test/pcep_tests_valgrind.sh
+valgrind_test pceplib/test/pcep_socket_comm_tests
diff --git a/pceplib/test/pcep_tests_valgrind.sh b/pceplib/test/pcep_tests_valgrind.sh
new file mode 100755
index 000000000..ca4772cb6
--- /dev/null
+++ b/pceplib/test/pcep_tests_valgrind.sh
@@ -0,0 +1,15 @@
+#
+# Common function definition for PCEPlib valgrind tests
+#
+
+function valgrind_test()
+{
+ local test_suite=$1
+ [[ -z ${test_suite} ]] && { echo "${FUNCNAME}(): test_suite not specified."; exit 1; }
+ [[ ! -x "${test_suite}" ]] && { echo "${test_suite} is not an executable file."; exit 1; }
+
+ G_SLICE=always-malloc
+ G_DEBUG=gc-friendly
+ VALGRIND="valgrind -v --tool=memcheck --leak-check=full --num-callers=40 --error-exitcode=1"
+ ${VALGRIND} --log-file=${test_suite}.val.log ./${test_suite} || ({ echo "Valgrind memory check error"; exit 1; })
+}
diff --git a/pceplib/test/pcep_timers_event_loop_test.c b/pceplib/test/pcep_timers_event_loop_test.c
new file mode 100644
index 000000000..9fcacaf0f
--- /dev/null
+++ b/pceplib/test/pcep_timers_event_loop_test.c
@@ -0,0 +1,160 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_timers.h"
+#include "pcep_utils_memory.h"
+#include "pcep_timers_event_loop.h"
+#include "pcep_timers_event_loop_test.h"
+
+
+typedef struct timer_expire_handler_info_ {
+ bool handler_called;
+ void *data;
+ int timerId;
+
+} timer_expire_handler_info;
+
+static pcep_timers_context *test_timers_context = NULL;
+static timer_expire_handler_info expire_handler_info;
+#define TEST_EVENT_LOOP_TIMER_ID 500
+
+
+/* Called when a timer expires */
+static void test_timer_expire_handler(void *data, int timerId)
+{
+ expire_handler_info.handler_called = true;
+ expire_handler_info.data = data;
+ expire_handler_info.timerId = timerId;
+}
+
+
+/* Test case setup called before each test.
+ * Declared in pcep_timers_tests.c */
+void pcep_timers_event_loop_test_setup()
+{
+ test_timers_context =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_timers_context));
+ memset(test_timers_context, 0, sizeof(pcep_timers_context));
+ if (pthread_mutex_init(&(test_timers_context->timer_list_lock), NULL)
+ != 0) {
+ fprintf(stderr,
+ "ERROR initializing timers, cannot initialize the mutex\n");
+ }
+ test_timers_context->active = false;
+ test_timers_context->expire_handler = test_timer_expire_handler;
+ test_timers_context->timer_list =
+ ordered_list_initialize(timer_list_node_timer_id_compare);
+
+ expire_handler_info.handler_called = false;
+ expire_handler_info.data = NULL;
+ expire_handler_info.timerId = -1;
+}
+
+
+/* Test case teardown called after each test.
+ * Declared in pcep_timers_tests.c */
+void pcep_timers_event_loop_test_teardown()
+{
+ pthread_mutex_unlock(&test_timers_context->timer_list_lock);
+ pthread_mutex_destroy(&(test_timers_context->timer_list_lock));
+ ordered_list_destroy(test_timers_context->timer_list);
+ pceplib_free(PCEPLIB_INFRA, test_timers_context);
+ test_timers_context = NULL;
+}
+
+
+/*
+ * Test functions
+ */
+
+void test_walk_and_process_timers_no_timers()
+{
+ CU_ASSERT_EQUAL(test_timers_context->timer_list->num_entries, 0);
+ CU_ASSERT_PTR_NULL(test_timers_context->timer_list->head);
+
+ walk_and_process_timers(test_timers_context);
+
+ CU_ASSERT_FALSE(expire_handler_info.handler_called);
+ CU_ASSERT_EQUAL(test_timers_context->timer_list->num_entries, 0);
+ CU_ASSERT_PTR_NULL(test_timers_context->timer_list->head);
+}
+
+
+void test_walk_and_process_timers_timer_not_expired()
+{
+ pcep_timer timer;
+ timer.data = &timer;
+ // Set the timer to expire 100 seconds from now
+ timer.expire_time = time(NULL) + 100;
+ timer.timer_id = TEST_EVENT_LOOP_TIMER_ID;
+ ordered_list_add_node(test_timers_context->timer_list, &timer);
+
+ walk_and_process_timers(test_timers_context);
+
+ /* The timer should still be in the list, since it hasnt expired yet */
+ CU_ASSERT_FALSE(expire_handler_info.handler_called);
+ CU_ASSERT_EQUAL(test_timers_context->timer_list->num_entries, 1);
+ CU_ASSERT_PTR_NOT_NULL(test_timers_context->timer_list->head);
+}
+
+
+void test_walk_and_process_timers_timer_expired()
+{
+ /* We need to alloc it, since it will be free'd in
+ * walk_and_process_timers */
+ pcep_timer *timer = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_timer));
+ timer->data = timer;
+ // Set the timer to expire 10 seconds ago
+ timer->expire_time = time(NULL) - 10;
+ timer->timer_id = TEST_EVENT_LOOP_TIMER_ID;
+ ordered_list_add_node(test_timers_context->timer_list, timer);
+
+ walk_and_process_timers(test_timers_context);
+
+ /* Since the timer expired, the expire_handler should have been called
+ * and the timer should have been removed from the timer list */
+ CU_ASSERT_TRUE(expire_handler_info.handler_called);
+ CU_ASSERT_PTR_EQUAL(expire_handler_info.data, timer);
+ CU_ASSERT_EQUAL(expire_handler_info.timerId, TEST_EVENT_LOOP_TIMER_ID);
+ CU_ASSERT_EQUAL(test_timers_context->timer_list->num_entries, 0);
+ CU_ASSERT_PTR_NULL(test_timers_context->timer_list->head);
+}
+
+void test_event_loop_null_handle()
+{
+ /* Verify that event_loop() correctly handles a NULL timers_context */
+ event_loop(NULL);
+}
+
+
+void test_event_loop_not_active()
+{
+ /* Verify that event_loop() correctly handles an inactive timers_context
+ * flag */
+ test_timers_context->active = false;
+ event_loop(test_timers_context);
+}
diff --git a/pceplib/test/pcep_timers_event_loop_test.h b/pceplib/test/pcep_timers_event_loop_test.h
new file mode 100644
index 000000000..19fd26485
--- /dev/null
+++ b/pceplib/test/pcep_timers_event_loop_test.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_TIMERS_EVENT_LOOP_TEST_H_
+#define PCEP_TIMERS_EVENT_LOOP_TEST_H_
+
+void pcep_timers_event_loop_test_setup(void);
+void pcep_timers_event_loop_test_teardown(void);
+void test_walk_and_process_timers_no_timers(void);
+void test_walk_and_process_timers_timer_not_expired(void);
+void test_walk_and_process_timers_timer_expired(void);
+void test_event_loop_null_handle(void);
+void test_event_loop_not_active(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_timers_test.c b/pceplib/test/pcep_timers_test.c
new file mode 100644
index 000000000..9d9e0f6c1
--- /dev/null
+++ b/pceplib/test/pcep_timers_test.c
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdbool.h>
+#include <CUnit/CUnit.h>
+
+#include "pcep_timers.h"
+#include "pcep_timers_test.h"
+
+/* Test case teardown called after each test.
+ * Declared in pcep_timers_tests.c */
+void pcep_timers_test_teardown()
+{
+ teardown_timers();
+}
+
+static void test_timer_expire_handler(void *data, int timerId)
+{
+ (void)data;
+ (void)timerId;
+}
+
+
+void test_double_initialization(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true);
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), false);
+}
+
+
+void test_initialization_null_callback(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(NULL), false);
+}
+
+
+void test_not_initialized(void)
+{
+ /* All of these should fail if initialize_timers() hasnt been called */
+ CU_ASSERT_EQUAL(create_timer(5, NULL), -1);
+ CU_ASSERT_EQUAL(cancel_timer(7), false);
+ CU_ASSERT_EQUAL(reset_timer(7), false);
+ CU_ASSERT_EQUAL(teardown_timers(), false);
+}
+
+
+void test_create_timer(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true);
+
+ int timer_id = create_timer(0, NULL);
+ CU_ASSERT_TRUE(timer_id > -1);
+}
+
+
+void test_cancel_timer(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true);
+
+ int timer_id = create_timer(10, NULL);
+ CU_ASSERT_TRUE(timer_id > -1);
+
+ CU_ASSERT_EQUAL(cancel_timer(timer_id), true);
+}
+
+
+void test_cancel_timer_invalid(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true);
+ CU_ASSERT_EQUAL(cancel_timer(1), false);
+}
+
+
+void test_reset_timer(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true);
+
+ int timer_id = create_timer(10, NULL);
+ CU_ASSERT_TRUE(timer_id > -1);
+
+ CU_ASSERT_EQUAL(reset_timer(timer_id), true);
+}
+
+
+void test_reset_timer_invalid(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true);
+ CU_ASSERT_EQUAL(reset_timer(1), false);
+}
diff --git a/pceplib/test/pcep_timers_test.h b/pceplib/test/pcep_timers_test.h
new file mode 100644
index 000000000..6ac9a90e4
--- /dev/null
+++ b/pceplib/test/pcep_timers_test.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_TIMERS_TEST_H_
+#define PCEP_TIMERS_TEST_H_
+
+void pcep_timers_test_teardown(void);
+void test_double_initialization(void);
+void test_initialization_null_callback(void);
+void test_not_initialized(void);
+void test_create_timer(void);
+void test_cancel_timer(void);
+void test_cancel_timer_invalid(void);
+void test_reset_timer(void);
+void test_reset_timer_invalid(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_timers_tests.c b/pceplib/test/pcep_timers_tests.c
new file mode 100644
index 000000000..adfea17e2
--- /dev/null
+++ b/pceplib/test/pcep_timers_tests.c
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/Basic.h>
+#include <CUnit/CUnit.h>
+#include <CUnit/TestDB.h>
+
+#include "pcep_timers_test.h"
+#include "pcep_timers_event_loop_test.h"
+
+
+int main(int argc, char **argv)
+{
+ /* Unused parameters cause compilation warnings */
+ (void)argc;
+ (void)argv;
+
+ CU_initialize_registry();
+
+ /*
+ * Tests defined in pcep_timers_test.c
+ */
+ CU_pSuite test_timers_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP Timers Test Suite", NULL,
+ NULL, // suite setup and cleanup function pointers
+ NULL, pcep_timers_test_teardown); // test case setup and
+ // teardown function pointers
+ CU_add_test(test_timers_suite, "test_double_initialization",
+ test_double_initialization);
+ CU_add_test(test_timers_suite, "test_initialization_null_callback",
+ test_initialization_null_callback);
+ CU_add_test(test_timers_suite, "test_not_initialized",
+ test_not_initialized);
+ CU_add_test(test_timers_suite, "test_create_timer", test_create_timer);
+ CU_add_test(test_timers_suite, "test_cancel_timer", test_cancel_timer);
+ CU_add_test(test_timers_suite, "test_cancel_timer_invalid",
+ test_cancel_timer_invalid);
+ CU_add_test(test_timers_suite, "test_reset_timer", test_reset_timer);
+ CU_add_test(test_timers_suite, "test_reset_timer_invalid",
+ test_reset_timer_invalid);
+
+ /*
+ * Tests defined in pcep_timers_event_loop_test.c
+ */
+ CU_pSuite test_timers_event_loop_suite =
+ CU_add_suite_with_setup_and_teardown(
+ "PCEP Timers Event Loop Test Suite", NULL,
+ NULL, // suite setup and cleanup function pointers
+ pcep_timers_event_loop_test_setup, // test case setup
+ // function pointer
+ pcep_timers_event_loop_test_teardown); // test case
+ // teardown
+ // function
+ // pointer
+ CU_add_test(test_timers_event_loop_suite,
+ "test_walk_and_process_timers_no_timers",
+ test_walk_and_process_timers_no_timers);
+ CU_add_test(test_timers_event_loop_suite,
+ "test_walk_and_process_timers_timer_not_expired",
+ test_walk_and_process_timers_timer_not_expired);
+ CU_add_test(test_timers_event_loop_suite,
+ "test_walk_and_process_timers_timer_expired",
+ test_walk_and_process_timers_timer_expired);
+ CU_add_test(test_timers_event_loop_suite, "test_event_loop_null_handle",
+ test_event_loop_null_handle);
+ CU_add_test(test_timers_event_loop_suite, "test_event_loop_not_active",
+ test_event_loop_not_active);
+
+ /*
+ * Run the tests and cleanup.
+ */
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_FailureRecord *failure_record = CU_get_failure_list();
+ if (failure_record != NULL) {
+ printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n");
+ do {
+ printf("\t [%s] [%s] [%s:%d]\n",
+ failure_record->pSuite->pName,
+ failure_record->pTest->pName,
+ failure_record->strFileName,
+ failure_record->uiLineNumber);
+ failure_record = failure_record->pNext;
+
+ } while (failure_record != NULL);
+ }
+
+ CU_pRunSummary run_summary = CU_get_run_summary();
+ int result = run_summary->nTestsFailed;
+ CU_cleanup_registry();
+
+ return result;
+}
diff --git a/pceplib/test/pcep_timers_tests_valgrind.sh b/pceplib/test/pcep_timers_tests_valgrind.sh
new file mode 100755
index 000000000..f9bff3b2a
--- /dev/null
+++ b/pceplib/test/pcep_timers_tests_valgrind.sh
@@ -0,0 +1,2 @@
+source pceplib/test/pcep_tests_valgrind.sh
+valgrind_test pceplib/test/pcep_timers_tests
diff --git a/pceplib/test/pcep_utils_counters_test.c b/pceplib/test/pcep_utils_counters_test.c
new file mode 100644
index 000000000..6f53e4d40
--- /dev/null
+++ b/pceplib/test/pcep_utils_counters_test.c
@@ -0,0 +1,254 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_utils_counters.h"
+#include "pcep_utils_counters_test.h"
+
+
+void test_create_counters_group()
+{
+ const char group_name[] = "group";
+ uint16_t num_subgroups = 10;
+
+ struct counters_group *group =
+ create_counters_group(NULL, num_subgroups);
+ CU_ASSERT_PTR_NULL(group);
+
+ group = create_counters_group(group_name, MAX_COUNTER_GROUPS + 1);
+ CU_ASSERT_PTR_NULL(group);
+
+ group = create_counters_group(group_name, num_subgroups);
+ CU_ASSERT_PTR_NOT_NULL(group);
+
+ CU_ASSERT_EQUAL(group->num_subgroups, 0);
+ CU_ASSERT_EQUAL(group->max_subgroups, num_subgroups);
+ CU_ASSERT_EQUAL(strcmp(group->counters_group_name, group_name), 0);
+
+ delete_counters_group(group);
+}
+
+void test_create_counters_subgroup()
+{
+ const char subgroup_name[] = "subgroup";
+ uint16_t subgroup_id = 10;
+ uint16_t num_counters = 20;
+
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup(NULL, subgroup_id, num_counters);
+ CU_ASSERT_PTR_NULL(subgroup);
+
+ subgroup = create_counters_subgroup(
+ subgroup_name, MAX_COUNTER_GROUPS + 1, num_counters);
+ CU_ASSERT_PTR_NULL(subgroup);
+
+ subgroup = create_counters_subgroup(subgroup_name, subgroup_id,
+ MAX_COUNTERS + 1);
+ CU_ASSERT_PTR_NULL(subgroup);
+
+ subgroup = create_counters_subgroup(subgroup_name, subgroup_id,
+ num_counters);
+ CU_ASSERT_PTR_NOT_NULL(subgroup);
+
+ CU_ASSERT_EQUAL(subgroup->subgroup_id, subgroup_id);
+ CU_ASSERT_EQUAL(subgroup->num_counters, 0);
+ CU_ASSERT_EQUAL(subgroup->max_counters, num_counters);
+ CU_ASSERT_EQUAL(strcmp(subgroup->counters_subgroup_name, subgroup_name),
+ 0);
+
+ delete_counters_subgroup(subgroup);
+}
+
+void test_add_counters_subgroup()
+{
+ struct counters_group *group = create_counters_group("group", 1);
+ struct counters_subgroup *subgroup1 =
+ create_counters_subgroup("subgroup", 0, 5);
+ struct counters_subgroup *subgroup2 =
+ create_counters_subgroup("subgroup", 1, 5);
+
+ CU_ASSERT_FALSE(add_counters_subgroup(NULL, NULL));
+ CU_ASSERT_FALSE(add_counters_subgroup(NULL, subgroup1));
+ CU_ASSERT_FALSE(add_counters_subgroup(group, NULL));
+
+ CU_ASSERT_EQUAL(group->num_subgroups, 0);
+ CU_ASSERT_TRUE(add_counters_subgroup(group, subgroup1));
+ CU_ASSERT_EQUAL(group->num_subgroups, 1);
+ /* Cant add more than num_subgroups to the group */
+ CU_ASSERT_FALSE(add_counters_subgroup(group, subgroup2));
+
+ CU_ASSERT_PTR_NOT_NULL(find_subgroup(group, 0));
+ CU_ASSERT_PTR_NULL(find_subgroup(group, 1));
+
+ delete_counters_group(group);
+ delete_counters_subgroup(subgroup2);
+}
+
+void test_create_subgroup_counter()
+{
+ uint16_t counter_id = 1;
+ char counter_name[] = "my counter";
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", 1, 2);
+
+ CU_ASSERT_FALSE(
+ create_subgroup_counter(NULL, counter_id, counter_name));
+ CU_ASSERT_FALSE(create_subgroup_counter(subgroup, counter_id + 1,
+ counter_name));
+ CU_ASSERT_FALSE(create_subgroup_counter(subgroup, counter_id, NULL));
+ CU_ASSERT_EQUAL(subgroup->num_counters, 0);
+ CU_ASSERT_TRUE(
+ create_subgroup_counter(subgroup, counter_id, counter_name));
+ CU_ASSERT_EQUAL(subgroup->num_counters, 1);
+
+ delete_counters_subgroup(subgroup);
+}
+
+void test_delete_counters_group()
+{
+ struct counters_group *group = create_counters_group("group", 1);
+
+ CU_ASSERT_FALSE(delete_counters_group(NULL));
+ CU_ASSERT_TRUE(delete_counters_group(group));
+}
+
+void test_delete_counters_subgroup()
+{
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", 1, 1);
+
+ CU_ASSERT_FALSE(delete_counters_subgroup(NULL));
+ CU_ASSERT_TRUE(delete_counters_subgroup(subgroup));
+}
+
+void test_reset_group_counters()
+{
+ uint16_t subgroup_id = 1;
+ uint16_t counter_id = 1;
+ struct counters_group *group = create_counters_group("group", 10);
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", subgroup_id, 10);
+ create_subgroup_counter(subgroup, counter_id, "counter");
+ add_counters_subgroup(group, subgroup);
+
+ struct counter *counter = subgroup->counters[counter_id];
+ counter->counter_value = 100;
+
+ CU_ASSERT_FALSE(reset_group_counters(NULL));
+ CU_ASSERT_TRUE(reset_group_counters(group));
+ CU_ASSERT_EQUAL(counter->counter_value, 0);
+
+ delete_counters_group(group);
+}
+
+void test_reset_subgroup_counters()
+{
+ uint16_t counter_id = 1;
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", 1, 10);
+ create_subgroup_counter(subgroup, counter_id, "counter");
+
+ struct counter *counter = subgroup->counters[counter_id];
+ counter->counter_value = 100;
+
+ CU_ASSERT_FALSE(reset_subgroup_counters(NULL));
+ CU_ASSERT_TRUE(reset_subgroup_counters(subgroup));
+ CU_ASSERT_EQUAL(counter->counter_value, 0);
+
+ delete_counters_subgroup(subgroup);
+}
+
+void test_increment_counter()
+{
+ uint16_t subgroup_id = 1;
+ uint16_t counter_id = 1;
+ struct counters_group *group = create_counters_group("group", 10);
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", subgroup_id, 10);
+ create_subgroup_counter(subgroup, counter_id, "counter");
+ add_counters_subgroup(group, subgroup);
+
+ struct counter *counter = subgroup->counters[counter_id];
+ counter->counter_value = 100;
+
+ CU_ASSERT_FALSE(increment_counter(NULL, subgroup_id, counter_id));
+ CU_ASSERT_FALSE(increment_counter(group, 100, counter_id));
+ CU_ASSERT_FALSE(increment_counter(group, subgroup_id, 123));
+ CU_ASSERT_TRUE(increment_counter(group, subgroup_id, counter_id));
+ CU_ASSERT_EQUAL(counter->counter_value, 101);
+ CU_ASSERT_EQUAL(subgroup_counters_total(subgroup), 101);
+
+ delete_counters_group(group);
+}
+
+void test_increment_subgroup_counter()
+{
+ int counter_id = 1;
+ uint32_t counter_value = 100;
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", 1, 10);
+ create_subgroup_counter(subgroup, counter_id, "counter");
+
+ struct counter *counter = subgroup->counters[counter_id];
+ counter->counter_value = counter_value;
+
+ CU_ASSERT_FALSE(increment_subgroup_counter(NULL, counter_id));
+ CU_ASSERT_FALSE(increment_subgroup_counter(subgroup, counter_id + 1));
+ CU_ASSERT_TRUE(increment_subgroup_counter(subgroup, counter_id));
+ CU_ASSERT_EQUAL(counter->counter_value, counter_value + 1);
+
+ delete_counters_subgroup(subgroup);
+}
+
+void test_dump_counters_group_to_log()
+{
+ uint16_t subgroup_id = 1;
+ uint16_t counter_id = 1;
+ struct counters_group *group = create_counters_group("group", 10);
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", subgroup_id, 10);
+ create_subgroup_counter(subgroup, counter_id, "counter");
+ add_counters_subgroup(group, subgroup);
+
+ CU_ASSERT_FALSE(dump_counters_group_to_log(NULL));
+ CU_ASSERT_TRUE(dump_counters_group_to_log(group));
+
+ delete_counters_group(group);
+}
+
+void test_dump_counters_subgroup_to_log()
+{
+ uint16_t subgroup_id = 1;
+ uint16_t counter_id = 1;
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", subgroup_id, 10);
+ create_subgroup_counter(subgroup, counter_id, "counter");
+
+ CU_ASSERT_FALSE(dump_counters_subgroup_to_log(NULL));
+ CU_ASSERT_TRUE(dump_counters_subgroup_to_log(subgroup));
+
+ delete_counters_subgroup(subgroup);
+}
diff --git a/pceplib/test/pcep_utils_counters_test.h b/pceplib/test/pcep_utils_counters_test.h
new file mode 100644
index 000000000..07236dcb5
--- /dev/null
+++ b/pceplib/test/pcep_utils_counters_test.h
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_UTILS_COUNTERS_TEST_H_
+#define PCEP_UTILS_COUNTERS_TEST_H_
+
+void test_create_counters_group(void);
+void test_create_counters_subgroup(void);
+void test_add_counters_subgroup(void);
+void test_create_subgroup_counter(void);
+void test_delete_counters_group(void);
+void test_delete_counters_subgroup(void);
+void test_reset_group_counters(void);
+void test_reset_subgroup_counters(void);
+void test_increment_counter(void);
+void test_increment_subgroup_counter(void);
+void test_dump_counters_group_to_log(void);
+void test_dump_counters_subgroup_to_log(void);
+
+#endif
diff --git a/pceplib/test/pcep_utils_double_linked_list_test.c b/pceplib/test/pcep_utils_double_linked_list_test.c
new file mode 100644
index 000000000..d2600e66c
--- /dev/null
+++ b/pceplib/test/pcep_utils_double_linked_list_test.c
@@ -0,0 +1,297 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_double_linked_list_test.h"
+
+typedef struct dll_node_data_ {
+ int int_data;
+
+} dll_node_data;
+
+void test_empty_dl_list()
+{
+ double_linked_list *handle = dll_initialize();
+
+ CU_ASSERT_PTR_NULL(dll_delete_first_node(handle));
+ CU_ASSERT_PTR_NULL(dll_delete_last_node(handle));
+ CU_ASSERT_PTR_NULL(dll_delete_node(handle, NULL));
+
+ dll_destroy(handle);
+}
+
+void test_null_dl_list_handle()
+{
+ dll_destroy(NULL);
+ CU_ASSERT_PTR_NULL(dll_prepend(NULL, NULL));
+ CU_ASSERT_PTR_NULL(dll_append(NULL, NULL));
+ CU_ASSERT_PTR_NULL(dll_delete_first_node(NULL));
+ CU_ASSERT_PTR_NULL(dll_delete_last_node(NULL));
+ CU_ASSERT_PTR_NULL(dll_delete_node(NULL, NULL));
+}
+
+void test_dll_prepend_data()
+{
+ dll_node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ double_linked_list *handle = dll_initialize();
+
+ CU_ASSERT_PTR_NOT_NULL(dll_prepend(handle, &data3));
+ CU_ASSERT_PTR_NOT_NULL(dll_prepend(handle, &data2));
+ CU_ASSERT_PTR_NOT_NULL(dll_prepend(handle, &data1));
+
+ CU_ASSERT_EQUAL(handle->num_entries, 3);
+
+ double_linked_list_node *node = handle->head;
+ CU_ASSERT_PTR_EQUAL(node->data, &data1);
+ CU_ASSERT_PTR_NULL(node->prev_node);
+ CU_ASSERT_PTR_NOT_NULL(node->next_node);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node->prev_node);
+ CU_ASSERT_PTR_NOT_NULL(node->next_node);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data3);
+ CU_ASSERT_PTR_NOT_NULL(node->prev_node);
+ CU_ASSERT_PTR_NULL(node->next_node);
+ CU_ASSERT_PTR_EQUAL(handle->tail, node);
+
+ dll_destroy(handle);
+}
+
+
+void test_dll_append_data()
+{
+ dll_node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ double_linked_list *handle = dll_initialize();
+
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1));
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data2));
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data3));
+
+ CU_ASSERT_EQUAL(handle->num_entries, 3);
+
+ double_linked_list_node *node = handle->head;
+ CU_ASSERT_PTR_EQUAL(node->data, &data1);
+ CU_ASSERT_PTR_NULL(node->prev_node);
+ CU_ASSERT_PTR_NOT_NULL(node->next_node);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node->prev_node);
+ CU_ASSERT_PTR_NOT_NULL(node->next_node);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data3);
+ CU_ASSERT_PTR_NOT_NULL(node->prev_node);
+ CU_ASSERT_PTR_NULL(node->next_node);
+ CU_ASSERT_PTR_EQUAL(handle->tail, node);
+
+ dll_destroy(handle);
+}
+
+
+void test_dll_delete_first_node()
+{
+ dll_node_data data1, data2;
+ data1.int_data = 1;
+ data2.int_data = 2;
+
+ double_linked_list *handle = dll_initialize();
+
+ /* Test deleting with just 1 node in the list */
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1));
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ void *deleted_data = dll_delete_first_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data1, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+ CU_ASSERT_PTR_NULL(handle->head);
+ CU_ASSERT_PTR_NULL(handle->tail);
+
+ /* Test deleting with 2 nodes in the list */
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1));
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data2));
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ deleted_data = dll_delete_first_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data1, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+ CU_ASSERT_PTR_EQUAL(handle->head->data, &data2);
+ CU_ASSERT_PTR_EQUAL(handle->head, handle->tail);
+ CU_ASSERT_PTR_NULL(handle->head->prev_node);
+ CU_ASSERT_PTR_NULL(handle->head->next_node);
+
+ dll_destroy(handle);
+}
+
+
+void test_dll_delete_last_node()
+{
+ dll_node_data data1, data2;
+ data1.int_data = 1;
+ data2.int_data = 2;
+
+ double_linked_list *handle = dll_initialize();
+
+ /* Test deleting with just 1 node in the list */
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1));
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ void *deleted_data = dll_delete_last_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data1, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+ CU_ASSERT_PTR_NULL(handle->head);
+ CU_ASSERT_PTR_NULL(handle->tail);
+
+ /* Test deleting with 2 nodes in the list */
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1));
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data2));
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ deleted_data = dll_delete_last_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data2, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+ CU_ASSERT_PTR_EQUAL(handle->head->data, &data1);
+ CU_ASSERT_PTR_EQUAL(handle->head, handle->tail);
+ CU_ASSERT_PTR_NULL(handle->head->prev_node);
+ CU_ASSERT_PTR_NULL(handle->head->next_node);
+
+ dll_destroy(handle);
+}
+
+
+void test_dll_delete_node()
+{
+ dll_node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+ double_linked_list_node *node1, *node2, *node3;
+ double_linked_list *handle;
+
+ /* Test deleting with just 1 node in the list */
+ handle = dll_initialize();
+ node1 = dll_append(handle, &data1);
+ CU_ASSERT_PTR_NOT_NULL(node1);
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ void *deleted_data = dll_delete_node(handle, node1);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data1, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+ CU_ASSERT_PTR_NULL(handle->head);
+ CU_ASSERT_PTR_NULL(handle->tail);
+
+ /*
+ * Test deleting the head with 2 nodes in the list
+ */
+ node1 = dll_append(handle, &data1);
+ node2 = dll_append(handle, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node1);
+ CU_ASSERT_PTR_NOT_NULL(node2);
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ /* Delete the head entry */
+ deleted_data = dll_delete_node(handle, node1);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data1, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+ CU_ASSERT_PTR_EQUAL(handle->head->data, &data2);
+ CU_ASSERT_PTR_EQUAL(handle->head, handle->tail);
+ CU_ASSERT_PTR_NULL(handle->head->prev_node);
+ CU_ASSERT_PTR_NULL(handle->head->next_node);
+ dll_destroy(handle);
+
+ /*
+ * Test deleting the tail with 2 nodes in the list
+ */
+ handle = dll_initialize();
+ node1 = dll_append(handle, &data1);
+ node2 = dll_append(handle, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node1);
+ CU_ASSERT_PTR_NOT_NULL(node2);
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ /* Delete the tail entry */
+ deleted_data = dll_delete_node(handle, node2);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data2, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+ CU_ASSERT_PTR_EQUAL(handle->head->data, &data1);
+ CU_ASSERT_PTR_EQUAL(handle->head, handle->tail);
+ CU_ASSERT_PTR_NULL(handle->head->prev_node);
+ CU_ASSERT_PTR_NULL(handle->head->next_node);
+ dll_destroy(handle);
+
+ /*
+ * Test deleting in the middle with 3 nodes in the list
+ */
+ handle = dll_initialize();
+ node1 = dll_append(handle, &data1);
+ node2 = dll_append(handle, &data2);
+ node3 = dll_append(handle, &data3);
+ CU_ASSERT_PTR_NOT_NULL(node1);
+ CU_ASSERT_PTR_NOT_NULL(node2);
+ CU_ASSERT_PTR_NOT_NULL(node3);
+ CU_ASSERT_EQUAL(handle->num_entries, 3);
+
+ /* Delete the middle entry */
+ deleted_data = dll_delete_node(handle, node2);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data2, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+ CU_ASSERT_PTR_EQUAL(handle->head, node1);
+ CU_ASSERT_PTR_EQUAL(handle->tail, node3);
+ CU_ASSERT_PTR_EQUAL(node1->data, &data1);
+ CU_ASSERT_PTR_EQUAL(node3->data, &data3);
+ CU_ASSERT_PTR_EQUAL(node1->next_node, node3);
+ CU_ASSERT_PTR_EQUAL(node3->prev_node, node1);
+ CU_ASSERT_PTR_NULL(node1->prev_node);
+ CU_ASSERT_PTR_NULL(node3->next_node);
+
+ dll_destroy(handle);
+}
diff --git a/pceplib/test/pcep_utils_double_linked_list_test.h b/pceplib/test/pcep_utils_double_linked_list_test.h
new file mode 100644
index 000000000..ddb6467cb
--- /dev/null
+++ b/pceplib/test/pcep_utils_double_linked_list_test.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_UTILS_DOUBLE_LINKED_LIST_TEST_H_
+#define PCEP_UTILS_DOUBLE_LINKED_LIST_TEST_H_
+
+void test_empty_dl_list(void);
+void test_null_dl_list_handle(void);
+void test_dll_prepend_data(void);
+void test_dll_append_data(void);
+void test_dll_delete_first_node(void);
+void test_dll_delete_last_node(void);
+void test_dll_delete_node(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_utils_memory_test.c b/pceplib/test/pcep_utils_memory_test.c
new file mode 100644
index 000000000..b0b528f08
--- /dev/null
+++ b/pceplib/test/pcep_utils_memory_test.c
@@ -0,0 +1,241 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_utils_memory.h"
+#include "pcep_utils_memory_test.h"
+
+void *test_pceplib_malloc(void *mem_type, size_t size);
+void *test_pceplib_calloc(void *mem_type, size_t size);
+void *test_pceplib_realloc(void *mem_type, void *ptr, size_t size);
+void *test_pceplib_strdup(void *mem_type, const char *str);
+void test_pceplib_free(void *mem_type, void *ptr);
+void verify_memory_type(struct pceplib_memory_type *mt, uint32_t num_alloc,
+ uint32_t alloc_bytes, uint32_t num_free,
+ uint32_t free_bytes);
+void verify_ext_memory_type(void *mt, int num_malloc_calls,
+ int num_calloc_calls, int num_realloc_calls,
+ int num_strdup_calls, int num_free_calls);
+
+struct test_memory_type {
+ int num_malloc_calls;
+ int num_calloc_calls;
+ int num_realloc_calls;
+ int num_strdup_calls;
+ int num_free_calls;
+};
+
+void *test_pceplib_malloc(void *mem_type, size_t size)
+{
+ ((struct test_memory_type *)mem_type)->num_malloc_calls++;
+ return malloc(size);
+}
+
+void *test_pceplib_calloc(void *mem_type, size_t size)
+{
+ ((struct test_memory_type *)mem_type)->num_calloc_calls++;
+ return calloc(1, size);
+}
+
+void *test_pceplib_realloc(void *mem_type, void *ptr, size_t size)
+{
+ ((struct test_memory_type *)mem_type)->num_realloc_calls++;
+ return realloc(ptr, size);
+}
+
+void *test_pceplib_strdup(void *mem_type, const char *str)
+{
+ ((struct test_memory_type *)mem_type)->num_strdup_calls++;
+ return strdup(str);
+}
+
+void test_pceplib_free(void *mem_type, void *ptr)
+{
+ ((struct test_memory_type *)mem_type)->num_free_calls++;
+ free(ptr);
+}
+
+void verify_memory_type(struct pceplib_memory_type *mt, uint32_t num_alloc,
+ uint32_t alloc_bytes, uint32_t num_free,
+ uint32_t free_bytes)
+{
+ CU_ASSERT_EQUAL(num_alloc, mt->num_allocates);
+ CU_ASSERT_EQUAL(alloc_bytes, mt->total_bytes_allocated);
+ CU_ASSERT_EQUAL(num_free, mt->num_frees);
+ CU_ASSERT_EQUAL(free_bytes, mt->total_bytes_freed);
+}
+
+void verify_ext_memory_type(void *mt, int num_malloc_calls,
+ int num_calloc_calls, int num_realloc_calls,
+ int num_strdup_calls, int num_free_calls)
+{
+ struct test_memory_type *mt_ptr = (struct test_memory_type *)mt;
+ CU_ASSERT_EQUAL(num_malloc_calls, mt_ptr->num_malloc_calls);
+ CU_ASSERT_EQUAL(num_calloc_calls, mt_ptr->num_calloc_calls);
+ CU_ASSERT_EQUAL(num_realloc_calls, mt_ptr->num_realloc_calls);
+ CU_ASSERT_EQUAL(num_strdup_calls, mt_ptr->num_strdup_calls);
+ CU_ASSERT_EQUAL(num_free_calls, mt_ptr->num_free_calls);
+}
+
+void test_memory_internal_impl()
+{
+ int alloc_size = 100;
+ struct pceplib_memory_type *pceplib_infra_ptr =
+ (struct pceplib_memory_type *)PCEPLIB_INFRA;
+ struct pceplib_memory_type *pceplib_messages_ptr =
+ (struct pceplib_memory_type *)PCEPLIB_MESSAGES;
+ int alloc_counter = 1;
+ int free_counter = 1;
+
+ /* reset the memory type counters for easier testing */
+ pceplib_infra_ptr->num_allocates =
+ pceplib_infra_ptr->total_bytes_allocated =
+ pceplib_infra_ptr->num_frees =
+ pceplib_infra_ptr->total_bytes_freed = 0;
+ pceplib_messages_ptr->num_allocates =
+ pceplib_messages_ptr->total_bytes_allocated =
+ pceplib_messages_ptr->num_frees =
+ pceplib_messages_ptr->total_bytes_freed = 0;
+
+ /* Make sure nothing crashes when all these are set NULL, since the
+ * internal default values should still be used. */
+ pceplib_memory_initialize(NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ /* Test malloc() */
+ void *ptr = pceplib_malloc(PCEPLIB_INFRA, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_INFRA, ptr);
+ verify_memory_type(pceplib_infra_ptr, alloc_counter, alloc_size,
+ free_counter++, 0);
+
+ /* Test calloc() */
+ ptr = pceplib_calloc(PCEPLIB_INFRA, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_INFRA, ptr);
+ alloc_counter++;
+ verify_memory_type(pceplib_infra_ptr, alloc_counter,
+ alloc_size * alloc_counter, free_counter++, 0);
+
+ /* Test realloc() */
+ ptr = pceplib_malloc(PCEPLIB_INFRA, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ ptr = pceplib_realloc(PCEPLIB_INFRA, ptr, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_INFRA, ptr);
+ alloc_counter += 2;
+ verify_memory_type(pceplib_infra_ptr, alloc_counter,
+ alloc_size * alloc_counter, free_counter++, 0);
+
+ /* Test strdup() */
+ ptr = pceplib_malloc(PCEPLIB_INFRA, alloc_size);
+ /* Make strdup duplicate (alloc_size - 1) bytes */
+ memset(ptr, 'a', alloc_size);
+ ((char *)ptr)[alloc_size - 1] = '\0';
+ char *str = pceplib_strdup(PCEPLIB_INFRA, (char *)ptr);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_INFRA, ptr);
+ pceplib_free(PCEPLIB_INFRA, str);
+ alloc_counter += 2;
+ free_counter++;
+ verify_memory_type(pceplib_infra_ptr, alloc_counter,
+ (alloc_size * alloc_counter) - 1, free_counter, 0);
+
+ /* Make sure only the pceplib_infra_ptr memory counters are incremented
+ */
+ verify_memory_type(pceplib_messages_ptr, 0, 0, 0, 0);
+}
+
+void test_memory_external_impl()
+{
+ int alloc_size = 100;
+ struct pceplib_memory_type *pceplib_infra_ptr =
+ (struct pceplib_memory_type *)PCEPLIB_INFRA;
+ struct pceplib_memory_type *pceplib_messages_ptr =
+ (struct pceplib_memory_type *)PCEPLIB_MESSAGES;
+
+ /* reset the internal memory type counters to later verify they are NOT
+ * incremented since an external impl was provided */
+ pceplib_infra_ptr->num_allocates =
+ pceplib_infra_ptr->total_bytes_allocated =
+ pceplib_infra_ptr->num_frees =
+ pceplib_infra_ptr->total_bytes_freed = 0;
+ pceplib_messages_ptr->num_allocates =
+ pceplib_messages_ptr->total_bytes_allocated =
+ pceplib_messages_ptr->num_frees =
+ pceplib_messages_ptr->total_bytes_freed = 0;
+
+ /* Setup the external memory type */
+ struct test_memory_type infra_mt, messages_mt;
+ void *infra_ptr = &infra_mt;
+ void *messages_ptr = &messages_mt;
+ memset(infra_ptr, 0, sizeof(struct test_memory_type));
+ memset(messages_ptr, 0, sizeof(struct test_memory_type));
+ int free_counter = 1;
+
+ /* Initialize the PCEPlib memory system with an external implementation
+ */
+ pceplib_memory_initialize(infra_ptr, messages_ptr, test_pceplib_malloc,
+ test_pceplib_calloc, test_pceplib_realloc,
+ test_pceplib_strdup, test_pceplib_free);
+
+ /* Test malloc() */
+ void *ptr = pceplib_malloc(PCEPLIB_MESSAGES, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_MESSAGES, ptr);
+ verify_ext_memory_type(messages_ptr, 1, 0, 0, 0, free_counter++);
+
+ /* Test calloc() */
+ ptr = pceplib_calloc(PCEPLIB_MESSAGES, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_MESSAGES, ptr);
+ verify_ext_memory_type(messages_ptr, 1, 1, 0, 0, free_counter++);
+
+ /* Test realloc() */
+ ptr = pceplib_malloc(PCEPLIB_MESSAGES, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ ptr = pceplib_realloc(PCEPLIB_MESSAGES, ptr, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_MESSAGES, ptr);
+ verify_ext_memory_type(messages_ptr, 2, 1, 1, 0, free_counter++);
+
+ /* Test strdup() */
+ ptr = pceplib_malloc(PCEPLIB_MESSAGES, alloc_size);
+ /* Make strdup duplicate (alloc_size - 1) bytes */
+ memset(ptr, 'a', alloc_size);
+ ((char *)ptr)[alloc_size - 1] = '\0';
+ char *str = pceplib_strdup(PCEPLIB_MESSAGES, (char *)ptr);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_MESSAGES, ptr);
+ pceplib_free(PCEPLIB_MESSAGES, str);
+ verify_ext_memory_type(messages_ptr, 3, 1, 1, 1, free_counter + 1);
+
+ /* Make sure the internal memory counters are NOT incremented */
+ verify_memory_type(pceplib_infra_ptr, 0, 0, 0, 0);
+ verify_memory_type(pceplib_messages_ptr, 0, 0, 0, 0);
+
+ verify_ext_memory_type(infra_ptr, 0, 0, 0, 0, 0);
+}
diff --git a/pceplib/test/pcep_utils_memory_test.h b/pceplib/test/pcep_utils_memory_test.h
new file mode 100644
index 000000000..4e0c3fadf
--- /dev/null
+++ b/pceplib/test/pcep_utils_memory_test.h
@@ -0,0 +1,33 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_MEMORY_TEST_H_
+#define PCEP_MEMORY_TEST_H_
+
+void test_memory_internal_impl(void);
+void test_memory_external_impl(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_utils_ordered_list_test.c b/pceplib/test/pcep_utils_ordered_list_test.c
new file mode 100644
index 000000000..fe9ee5882
--- /dev/null
+++ b/pceplib/test/pcep_utils_ordered_list_test.c
@@ -0,0 +1,248 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_ordered_list_test.h"
+
+typedef struct node_data_ {
+ int int_data;
+
+} node_data;
+
+
+int node_data_compare(void *list_entry, void *new_entry)
+{
+ /*
+ * < 0 if new_entry < list_entry
+ * == 0 if new_entry == list_entry (new_entry will be inserted after
+ * list_entry) > 0 if new_entry > list_entry
+ */
+
+ return ((node_data *)new_entry)->int_data
+ - ((node_data *)list_entry)->int_data;
+}
+
+
+void test_empty_list()
+{
+ ordered_list_handle *handle =
+ ordered_list_initialize(node_data_compare);
+
+ CU_ASSERT_PTR_NOT_NULL(handle);
+ CU_ASSERT_PTR_NULL(handle->head);
+ CU_ASSERT_PTR_NOT_NULL(handle->compare_function);
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+
+ ordered_list_destroy(handle);
+}
+
+
+void test_null_list_handle()
+{
+ node_data data;
+ ordered_list_node node_data;
+
+ void *ptr = ordered_list_add_node(NULL, &data);
+ CU_ASSERT_PTR_NULL(ptr);
+
+ ptr = ordered_list_find(NULL, &data);
+ CU_ASSERT_PTR_NULL(ptr);
+
+ ptr = ordered_list_remove_first_node(NULL);
+ CU_ASSERT_PTR_NULL(ptr);
+
+ ptr = ordered_list_remove_first_node_equals(NULL, &data);
+ CU_ASSERT_PTR_NULL(ptr);
+
+ ptr = ordered_list_remove_node(NULL, &node_data, &node_data);
+ CU_ASSERT_PTR_NULL(ptr);
+}
+
+
+void test_add_to_list()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ ordered_list_handle *handle =
+ ordered_list_initialize(node_data_compare);
+
+ ordered_list_add_node(handle, &data3);
+ ordered_list_add_node(handle, &data1);
+ ordered_list_add_node(handle, &data2);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 3);
+
+ ordered_list_node *node = handle->head;
+ CU_ASSERT_PTR_EQUAL(node->data, &data1);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data2);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data3);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node, NULL);
+
+ ordered_list_destroy(handle);
+}
+
+
+void test_find()
+{
+ node_data data1, data2, data3, data_not_inList;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+ data_not_inList.int_data = 5;
+
+ ordered_list_handle *handle =
+ ordered_list_initialize(node_data_compare);
+
+ ordered_list_add_node(handle, &data3);
+ ordered_list_add_node(handle, &data2);
+ ordered_list_add_node(handle, &data1);
+
+ ordered_list_node *node = ordered_list_find(handle, &data1);
+ CU_ASSERT_PTR_NOT_NULL(node);
+ CU_ASSERT_PTR_EQUAL(node->data, &data1);
+
+ node = ordered_list_find(handle, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node);
+ CU_ASSERT_PTR_EQUAL(node->data, &data2);
+
+ node = ordered_list_find(handle, &data3);
+ CU_ASSERT_PTR_NOT_NULL(node);
+ CU_ASSERT_PTR_EQUAL(node->data, &data3);
+
+ node = ordered_list_find(handle, &data_not_inList);
+ CU_ASSERT_PTR_NULL(node);
+
+ ordered_list_destroy(handle);
+}
+
+
+void test_remove_first_node()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ ordered_list_handle *handle =
+ ordered_list_initialize(node_data_compare);
+
+ ordered_list_add_node(handle, &data1);
+ ordered_list_add_node(handle, &data2);
+ ordered_list_add_node(handle, &data3);
+
+ void *node_data = ordered_list_remove_first_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data1);
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ node_data = ordered_list_remove_first_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data2);
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ node_data = ordered_list_remove_first_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data3);
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+ CU_ASSERT_PTR_NULL(handle->head);
+
+ node_data = ordered_list_remove_first_node(handle);
+ CU_ASSERT_PTR_NULL(node_data);
+
+ ordered_list_destroy(handle);
+}
+
+
+void test_remove_first_node_equals()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ ordered_list_handle *handle =
+ ordered_list_initialize(node_data_compare);
+
+ ordered_list_add_node(handle, &data1);
+ ordered_list_add_node(handle, &data2);
+ ordered_list_add_node(handle, &data3);
+
+ void *node_data = ordered_list_remove_first_node_equals(handle, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data2);
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ node_data = ordered_list_remove_first_node_equals(handle, &data3);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data3);
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ node_data = ordered_list_remove_first_node_equals(handle, &data1);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data1);
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+
+ node_data = ordered_list_remove_first_node_equals(handle, &data1);
+ CU_ASSERT_PTR_NULL(node_data);
+
+ ordered_list_destroy(handle);
+}
+
+
+void test_remove_node()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ ordered_list_handle *handle =
+ ordered_list_initialize(node_data_compare);
+
+ ordered_list_node *node1 = ordered_list_add_node(handle, &data1);
+ ordered_list_node *node2 = ordered_list_add_node(handle, &data2);
+ ordered_list_node *node3 = ordered_list_add_node(handle, &data3);
+
+ void *node_data = ordered_list_remove_node(handle, node2, node3);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data3);
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ node_data = ordered_list_remove_node(handle, node1, node2);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data2);
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ ordered_list_destroy(handle);
+}
diff --git a/pceplib/test/pcep_utils_ordered_list_test.h b/pceplib/test/pcep_utils_ordered_list_test.h
new file mode 100644
index 000000000..3686848b6
--- /dev/null
+++ b/pceplib/test/pcep_utils_ordered_list_test.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_UTILS_ORDERED_LIST_TEST_H_
+#define PCEP_UTILS_ORDERED_LIST_TEST_H_
+
+void test_empty_list(void);
+void test_null_list_handle(void);
+void test_add_to_list(void);
+void test_find(void);
+void test_remove_first_node(void);
+void test_remove_first_node_equals(void);
+void test_remove_node(void);
+int node_data_compare(void *list_entry, void *new_entry);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_utils_queue_test.c b/pceplib/test/pcep_utils_queue_test.c
new file mode 100644
index 000000000..173145778
--- /dev/null
+++ b/pceplib/test/pcep_utils_queue_test.c
@@ -0,0 +1,157 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_utils_queue.h"
+#include "pcep_utils_queue_test.h"
+
+typedef struct node_data_ {
+ int int_data;
+
+} node_data;
+
+
+void test_empty_queue()
+{
+ queue_handle *handle = queue_initialize();
+
+ CU_ASSERT_PTR_NOT_NULL(handle);
+ CU_ASSERT_PTR_NULL(handle->head);
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+
+ queue_destroy(handle);
+}
+
+
+void test_null_queue_handle()
+{
+ /* test each method handles a NULL handle without crashing */
+ node_data data;
+ queue_destroy(NULL);
+ void *ptr = queue_enqueue(NULL, &data);
+ CU_ASSERT_PTR_NULL(ptr);
+
+ ptr = queue_dequeue(NULL);
+ CU_ASSERT_PTR_NULL(ptr);
+}
+
+
+void test_enqueue()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ queue_handle *handle = queue_initialize();
+
+ queue_enqueue(handle, &data1);
+ queue_enqueue(handle, &data2);
+ queue_enqueue(handle, &data3);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 3);
+
+ queue_node *node = handle->head;
+ CU_ASSERT_PTR_EQUAL(node->data, &data1);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data2);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data3);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_NULL(node);
+
+ queue_destroy(handle);
+}
+
+
+void test_enqueue_with_limit()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ queue_handle *handle = queue_initialize_with_size(2);
+
+ queue_node *node = queue_enqueue(handle, &data1);
+ CU_ASSERT_PTR_NOT_NULL(node);
+
+ node = queue_enqueue(handle, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node);
+
+ node = queue_enqueue(handle, &data3);
+ CU_ASSERT_PTR_NULL(node);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ node = handle->head;
+ CU_ASSERT_PTR_EQUAL(node->data, &data1);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data2);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_NULL(node);
+
+ queue_destroy(handle);
+}
+
+
+void test_dequeue()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ queue_handle *handle = queue_initialize();
+
+ /* first test dequeue handles an empty queue */
+ void *node_data = queue_dequeue(handle);
+ CU_ASSERT_PTR_NULL(node_data);
+
+ queue_enqueue(handle, &data1);
+ queue_enqueue(handle, &data2);
+ queue_enqueue(handle, &data3);
+
+ node_data = queue_dequeue(handle);
+ CU_ASSERT_PTR_EQUAL(node_data, &data1);
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ node_data = queue_dequeue(handle);
+ CU_ASSERT_PTR_EQUAL(node_data, &data2);
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ node_data = queue_dequeue(handle);
+ CU_ASSERT_PTR_EQUAL(node_data, &data3);
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+
+ node_data = queue_dequeue(handle);
+ CU_ASSERT_PTR_NULL(node_data);
+
+ queue_destroy(handle);
+}
diff --git a/pceplib/test/pcep_utils_queue_test.h b/pceplib/test/pcep_utils_queue_test.h
new file mode 100644
index 000000000..16236d0d9
--- /dev/null
+++ b/pceplib/test/pcep_utils_queue_test.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_UTILS_QUEUE_TEST_H_
+#define PCEP_UTILS_QUEUE_TEST_H_
+
+void test_empty_queue(void);
+void test_null_queue_handle(void);
+void test_enqueue(void);
+void test_enqueue_with_limit(void);
+void test_dequeue(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_utils_tests.c b/pceplib/test/pcep_utils_tests.c
new file mode 100644
index 000000000..452b9fa09
--- /dev/null
+++ b/pceplib/test/pcep_utils_tests.c
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/Basic.h>
+#include <CUnit/CUnit.h>
+#include <CUnit/TestDB.h>
+#include "pcep_utils_ordered_list_test.h"
+#include "pcep_utils_queue_test.h"
+#include "pcep_utils_double_linked_list_test.h"
+#include "pcep_utils_counters_test.h"
+#include "pcep_utils_memory_test.h"
+
+
+int main(int argc, char **argv)
+{
+ /* Unused parameters cause compilation warnings */
+ (void)argc;
+ (void)argv;
+
+ CU_initialize_registry();
+
+ CU_pSuite test_queue_suite =
+ CU_add_suite("PCEP Utils Queue Test Suite", NULL, NULL);
+ CU_add_test(test_queue_suite, "test_empty_queue", test_empty_queue);
+ CU_add_test(test_queue_suite, "test_null_queue_handle",
+ test_null_queue_handle);
+ CU_add_test(test_queue_suite, "test_enqueue", test_enqueue);
+ CU_add_test(test_queue_suite, "test_enqueue_with_limit",
+ test_enqueue_with_limit);
+ CU_add_test(test_queue_suite, "test_dequeue", test_dequeue);
+
+ CU_pSuite test_list_suite =
+ CU_add_suite("PCEP Utils Ordered List Test Suite", NULL, NULL);
+ CU_add_test(test_list_suite, "test_empty_list", test_empty_list);
+ CU_add_test(test_list_suite, "test_null_handle", test_null_list_handle);
+ CU_add_test(test_list_suite, "test_add_toList", test_add_to_list);
+ CU_add_test(test_list_suite, "test_find", test_find);
+ CU_add_test(test_list_suite, "test_remove_first_node",
+ test_remove_first_node);
+ CU_add_test(test_list_suite, "test_remove_first_node_equals",
+ test_remove_first_node_equals);
+ CU_add_test(test_list_suite, "test_remove_node", test_remove_node);
+
+ CU_pSuite test_dl_list_suite = CU_add_suite(
+ "PCEP Utils Double Linked List Test Suite", NULL, NULL);
+ CU_add_test(test_dl_list_suite, "test_empty_dl_list",
+ test_empty_dl_list);
+ CU_add_test(test_dl_list_suite, "test_null_dl_handle",
+ test_null_dl_list_handle);
+ CU_add_test(test_dl_list_suite, "test_dll_prepend_data",
+ test_dll_prepend_data);
+ CU_add_test(test_dl_list_suite, "test_dll_append_data",
+ test_dll_append_data);
+ CU_add_test(test_dl_list_suite, "test_dll_delete_first_node",
+ test_dll_delete_first_node);
+ CU_add_test(test_dl_list_suite, "test_dll_delete_last_node",
+ test_dll_delete_last_node);
+ CU_add_test(test_dl_list_suite, "test_dll_delete_node",
+ test_dll_delete_node);
+
+ CU_pSuite test_counters_suite =
+ CU_add_suite("PCEP Utils Counters Test Suite", NULL, NULL);
+ CU_add_test(test_counters_suite, "test_create_counters_group",
+ test_create_counters_group);
+ CU_add_test(test_counters_suite, "test_create_counters_subgroup",
+ test_create_counters_subgroup);
+ CU_add_test(test_counters_suite, "test_add_counters_subgroup",
+ test_add_counters_subgroup);
+ CU_add_test(test_counters_suite, "test_create_subgroup_counter",
+ test_create_subgroup_counter);
+ CU_add_test(test_counters_suite, "test_delete_counters_group",
+ test_delete_counters_group);
+ CU_add_test(test_counters_suite, "test_delete_counters_subgroup",
+ test_delete_counters_subgroup);
+ CU_add_test(test_counters_suite, "test_reset_group_counters",
+ test_reset_group_counters);
+ CU_add_test(test_counters_suite, "test_reset_subgroup_counters",
+ test_reset_subgroup_counters);
+ CU_add_test(test_counters_suite, "test_increment_counter",
+ test_increment_counter);
+ CU_add_test(test_counters_suite, "test_increment_subgroup_counter",
+ test_increment_subgroup_counter);
+ CU_add_test(test_counters_suite, "test_dump_counters_group_to_log",
+ test_dump_counters_group_to_log);
+ CU_add_test(test_counters_suite, "test_dump_counters_subgroup_to_log",
+ test_dump_counters_subgroup_to_log);
+
+ CU_pSuite test_memory_suite =
+ CU_add_suite("PCEP Utils Memory Test Suite", NULL, NULL);
+ CU_add_test(test_memory_suite, "test_memory_internal_impl",
+ test_memory_internal_impl);
+ CU_add_test(test_memory_suite, "test_memory_external_impl",
+ test_memory_external_impl);
+
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_FailureRecord *failure_record = CU_get_failure_list();
+ if (failure_record != NULL) {
+ printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n");
+ do {
+ printf("\t [%s] [%s] [%s:%d]\n",
+ failure_record->pSuite->pName,
+ failure_record->pTest->pName,
+ failure_record->strFileName,
+ failure_record->uiLineNumber);
+ failure_record = failure_record->pNext;
+
+ } while (failure_record != NULL);
+ }
+
+ CU_pRunSummary run_summary = CU_get_run_summary();
+ int result = run_summary->nTestsFailed;
+ CU_cleanup_registry();
+
+ return result;
+}
diff --git a/pceplib/test/pcep_utils_tests_valgrind.sh b/pceplib/test/pcep_utils_tests_valgrind.sh
new file mode 100755
index 000000000..6348d8270
--- /dev/null
+++ b/pceplib/test/pcep_utils_tests_valgrind.sh
@@ -0,0 +1,2 @@
+source pceplib/test/pcep_tests_valgrind.sh
+valgrind_test pceplib/test/pcep_utils_tests
diff --git a/pceplib/test/subdir.am b/pceplib/test/subdir.am
new file mode 100644
index 000000000..0ae61d1bc
--- /dev/null
+++ b/pceplib/test/subdir.am
@@ -0,0 +1,122 @@
+if PATHD_PCEP
+if PATHD_PCEP_TEST
+
+# The default Automake target is check, add a test target to call check.
+# Also make sure the binaries are current before running the tests.
+test: pceplib/test/pcep_msg_tests pceplib/test/pcep_pcc_api_tests pceplib/test/pcep_session_logic_tests pceplib/test/pcep_socket_comm_tests pceplib/test/pcep_timers_tests pceplib/test/pcep_utils_tests
+
+check_SCRIPTS = pceplib/test/pcep_msg_tests pceplib/test/pcep_pcc_api_tests pceplib/test/pcep_session_logic_tests pceplib/test/pcep_socket_comm_tests pceplib/test/pcep_timers_tests pceplib/test/pcep_utils_tests
+TESTS = $(check_SCRIPTS)
+
+
+# Definitions to build the Unit Test binaries with CUnit
+noinst_PROGRAMS += pceplib/test/pcep_msg_tests \
+ pceplib/test/pcep_pcc_api_tests \
+ pceplib/test/pcep_session_logic_tests \
+ pceplib/test/pcep_socket_comm_tests \
+ pceplib/test/pcep_timers_tests \
+ pceplib/test/pcep_utils_tests
+
+noinst_HEADERS += pceplib/test/pcep_msg_messages_test.h \
+ pceplib/test/pcep_msg_object_error_types_test.h \
+ pceplib/test/pcep_msg_objects_test.h \
+ pceplib/test/pcep_msg_tlvs_test.h \
+ pceplib/test/pcep_msg_tools_test.h \
+ pceplib/test/pcep_pcc_api_test.h \
+ pceplib/test/pcep_session_logic_loop_test.h \
+ pceplib/test/pcep_session_logic_states_test.h \
+ pceplib/test/pcep_session_logic_test.h \
+ pceplib/test/pcep_socket_comm_loop_test.h \
+ pceplib/test/pcep_socket_comm_test.h \
+ pceplib/test/pcep_timers_event_loop_test.h \
+ pceplib/test/pcep_timers_test.h \
+ pceplib/test/pcep_utils_counters_test.h \
+ pceplib/test/pcep_utils_double_linked_list_test.h \
+ pceplib/test/pcep_utils_memory_test.h \
+ pceplib/test/pcep_utils_ordered_list_test.h \
+ pceplib/test/pcep_utils_queue_test.h
+
+pceplib_test_pcep_msg_tests_CFLAGS = -I$(top_srcdir)/pceplib
+pceplib_test_pcep_msg_tests_LDADD = $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread
+pceplib_test_pcep_msg_tests_SOURCES = pceplib/test/pcep_msg_messages_test.c \
+ pceplib/test/pcep_msg_messages_tests.c \
+ pceplib/test/pcep_msg_object_error_types_test.c \
+ pceplib/test/pcep_msg_objects_test.c \
+ pceplib/test/pcep_msg_tlvs_test.c \
+ pceplib/test/pcep_msg_tools_test.c
+
+# The pcc_api_tests and pcep_session_logic_tests use the
+# socket_comm_mock, so the LDADD variable needs to be modified
+pceplib_test_pcep_pcc_api_tests_CFLAGS = -I$(top_srcdir)/pceplib
+pceplib_test_pcep_pcc_api_tests_LDADD = $(top_builddir)/pceplib/libsocket_comm_mock.la $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread
+pceplib_test_pcep_pcc_api_tests_SOURCES = pceplib/test/pcep_pcc_api_test.c pceplib/test/pcep_pcc_api_tests.c
+
+pceplib_test_pcep_session_logic_tests_CFLAGS = -I$(top_srcdir)/pceplib
+pceplib_test_pcep_session_logic_tests_LDADD = $(top_builddir)/pceplib/libsocket_comm_mock.la $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread
+pceplib_test_pcep_session_logic_tests_SOURCES = pceplib/test/pcep_session_logic_loop_test.c \
+ pceplib/test/pcep_session_logic_states_test.c \
+ pceplib/test/pcep_session_logic_test.c \
+ pceplib/test/pcep_session_logic_tests.c
+
+pceplib_test_pcep_socket_comm_tests_CFLAGS = -I$(top_srcdir)/pceplib
+pceplib_test_pcep_socket_comm_tests_LDADD = $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread
+pceplib_test_pcep_socket_comm_tests_SOURCES = pceplib/test/pcep_socket_comm_loop_test.c \
+ pceplib/test/pcep_socket_comm_test.c \
+ pceplib/test/pcep_socket_comm_tests.c
+
+pceplib_test_pcep_timers_tests_CFLAGS = -I$(top_srcdir)/pceplib
+pceplib_test_pcep_timers_tests_LDADD = $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread
+pceplib_test_pcep_timers_tests_SOURCES = pceplib/test/pcep_timers_event_loop_test.c \
+ pceplib/test/pcep_timers_test.c \
+ pceplib/test/pcep_timers_tests.c
+
+pceplib_test_pcep_utils_tests_CFLAGS = -I$(top_srcdir)/pceplib
+pceplib_test_pcep_utils_tests_LDADD = $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread
+pceplib_test_pcep_utils_tests_SOURCES = pceplib/test/pcep_utils_counters_test.c \
+ pceplib/test/pcep_utils_double_linked_list_test.c \
+ pceplib/test/pcep_utils_memory_test.c \
+ pceplib/test/pcep_utils_ordered_list_test.c \
+ pceplib/test/pcep_utils_queue_test.c \
+ pceplib/test/pcep_utils_tests.c
+
+# These test scripts will call the test binaries
+# defined above in noinst_PROGRAMS with Valgrind
+if HAVE_VALGRIND_PCEP
+
+dist_noinst_SCRIPTS = pceplib/test/pcep_pcc_api_tests_valgrind.sh \
+ pceplib/test/pcep_session_logic_tests_valgrind.sh \
+ pceplib/test/pcep_socket_comm_tests_valgrind.sh \
+ pceplib/test/pcep_timers_tests_valgrind.sh \
+ pceplib/test/pcep_utils_tests_valgrind.sh \
+ pceplib/test/pcep_msg_tests_valgrind.sh \
+ pceplib/test/pcep_tests_valgrind.sh
+
+check_SCRIPTS += pceplib/test/pcep_msg_tests_valgrind.sh \
+ pceplib/test/pcep_pcc_api_tests_valgrind.sh \
+ pceplib/test/pcep_session_logic_tests_valgrind.sh \
+ pceplib/test/pcep_socket_comm_tests_valgrind.sh \
+ pceplib/test/pcep_timers_tests_valgrind.sh \
+ pceplib/test/pcep_utils_tests_valgrind.sh
+
+TESTS += $(check_SCRIPTS)
+
+
+
+pceplib/test/pcep_msg_tests_valgrind.sh:
+ chmod +x pceplib/test/pcep_msg_tests_valgrind.sh
+pceplib/test/pcep_pcc_api_tests_valgrind.sh:
+ chmod +x pceplib/test/pcep_pcc_api_tests_valgrind.sh
+pceplib/test/pcep_session_logic_tests_valgrind.sh:
+ chmod +x pceplib/test/pcep_session_logic_tests_valgrind.sh
+pceplib/test/pcep_socket_comm_tests_valgrind.sh:
+ chmod +x pceplib/test/pcep_socket_comm_tests_valgrind.sh
+pceplib/test/pcep_timers_tests_valgrind.sh:
+ chmod +x pceplib/test/pcep_timers_tests_valgrind.sh
+pceplib/test/pcep_utils_tests_valgrind.sh:
+ chmod +x pceplib/test/pcep_utils_tests_valgrind.sh
+
+
+endif
+
+endif
+endif
diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in
index 02c272f47..b6d7ab241 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -698,6 +698,7 @@ fi
%endif
%if %{with_pathd}
%{_sbindir}/pathd
+ %{_libdir}/frr/modules/pathd_pcep.so
%endif
%{_libdir}/libfrr.so*
%{_libdir}/libfrrcares*