pceplib: Integrate pcelib into frr

Signed-off-by: Brady Johnson <brady@voltanet.io>
Co-authored-by: Javier Garcia <javier.garcia@voltanet.io>
Signed-off-by: Javier Garcia <javier.garcia@voltanet.io>
This commit is contained in:
Javier Garcia 2021-01-22 10:38:12 +01:00
parent 40c1b0e6b8
commit 749714731e
127 changed files with 27103 additions and 212 deletions

1
.gitignore vendored
View File

@ -29,6 +29,7 @@
/libtool
/libtool.orig
/changelog-auto
/test-driver
/Makefile
/Makefile.in

View File

@ -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

View File

@ -1709,12 +1709,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
@ -2536,15 +2534,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"])
@ -2569,6 +2566,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"

1
debian/frr.install vendored
View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -19,4 +19,5 @@ FRRouting Developer's Guide
zebra
vtysh
path
pceplib
link-state

781
doc/developer/pceplib.rst Normal file
View File

@ -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);``

View File

@ -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;

View File

@ -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"

View File

@ -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);
}

View File

@ -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,

View File

@ -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);

View File

@ -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,6 +75,14 @@ struct pcep_main_event_data {
void *payload;
};
struct pcep_refine_path_event_data {
struct ctrl_state *ctrl_state;
int pcc_id;
pcep_refine_callback_t continue_lsp_update_handler;
struct path *path;
void *payload;
};
/* Synchronous call arguments */
struct get_counters_args {
@ -81,12 +91,6 @@ struct get_counters_args {
struct counters_group *counters;
};
struct send_report_args {
struct ctrl_state *ctrl_state;
int pcc_id;
struct path *path;
};
struct get_pcep_session_args {
struct ctrl_state *ctrl_state;
int pcc_id;
@ -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);

View File

@ -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_

View File

@ -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";
}

View File

@ -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"

View File

@ -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;
}

View File

@ -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"

View File

@ -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_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,
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 continue_pcep_lsp_update(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
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;
}
}
}

View File

@ -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,

View File

@ -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@

14
pceplib/.gitignore vendored Normal file
View File

@ -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

48
pceplib/pcep.h Normal file
View File

@ -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

140
pceplib/pcep_msg_encoding.h Normal file
View File

@ -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

308
pceplib/pcep_msg_messages.c Normal file
View File

@ -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;
}

132
pceplib/pcep_msg_messages.h Normal file
View File

@ -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

View File

@ -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);
}

View File

@ -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];
}

View File

@ -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

854
pceplib/pcep_msg_objects.c Normal file
View File

@ -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;
}

741
pceplib/pcep_msg_objects.h Normal file
View File

@ -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

File diff suppressed because it is too large Load Diff

464
pceplib/pcep_msg_tlvs.c Normal file
View File

@ -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;
}

380
pceplib/pcep_msg_tlvs.h Normal file
View File

@ -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_ */

File diff suppressed because it is too large Load Diff

465
pceplib/pcep_msg_tools.c Normal file
View File

@ -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));
}

71
pceplib/pcep_msg_tools.h Normal file
View File

@ -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

517
pceplib/pcep_pcc.c Normal file
View File

@ -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;
}

392
pceplib/pcep_pcc_api.c Normal file
View File

@ -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);
}

103
pceplib/pcep_pcc_api.h Normal file
View File

@ -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_ */

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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_ */

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

781
pceplib/pcep_socket_comm.c Normal file
View File

@ -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));
}

198
pceplib/pcep_socket_comm.h Normal file
View File

@ -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_ */

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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_ */

482
pceplib/pcep_timers.c Normal file
View File

@ -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);
}

92
pceplib/pcep_timers.h Normal file
View File

@ -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_ */

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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_ */

220
pceplib/pcep_utils_memory.c Normal file
View File

@ -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);
}
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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_ */

150
pceplib/pcep_utils_queue.c Normal file
View File

@ -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;
}

View File

@ -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_ */

62
pceplib/subdir.am Normal file
View File

@ -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

View File

@ -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);
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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_ */

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -0,0 +1,2 @@
source pceplib/test/pcep_tests_valgrind.sh
valgrind_test pceplib/test/pcep_msg_tests

View File

@ -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);
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -0,0 +1,2 @@
source pceplib/test/pcep_tests_valgrind.sh
valgrind_test pceplib/test/pcep_pcc_api_tests

View File

@ -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);
}

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -0,0 +1,2 @@
source pceplib/test/pcep_tests_valgrind.sh
valgrind_test pceplib/test/pcep_session_logic_tests

Some files were not shown because too many files have changed in this diff Show More