mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-04-28 17:01:51 +00:00
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:
parent
40c1b0e6b8
commit
749714731e
1
.gitignore
vendored
1
.gitignore
vendored
@ -29,6 +29,7 @@
|
||||
/libtool
|
||||
/libtool.orig
|
||||
/changelog-auto
|
||||
/test-driver
|
||||
|
||||
/Makefile
|
||||
/Makefile.in
|
||||
|
@ -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
|
||||
|
22
configure.ac
22
configure.ac
@ -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
1
debian/frr.install
vendored
@ -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
|
||||
|
BIN
doc/developer/images/PCEPlib_design.jpg
Normal file
BIN
doc/developer/images/PCEPlib_design.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
BIN
doc/developer/images/PCEPlib_internal_deps.jpg
Normal file
BIN
doc/developer/images/PCEPlib_internal_deps.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
BIN
doc/developer/images/PCEPlib_socket_comm.jpg
Normal file
BIN
doc/developer/images/PCEPlib_socket_comm.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
BIN
doc/developer/images/PCEPlib_threading_model.jpg
Normal file
BIN
doc/developer/images/PCEPlib_threading_model.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
BIN
doc/developer/images/PCEPlib_threading_model_frr_infra.jpg
Normal file
BIN
doc/developer/images/PCEPlib_threading_model_frr_infra.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
BIN
doc/developer/images/PCEPlib_timers.jpg
Normal file
BIN
doc/developer/images/PCEPlib_timers.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
@ -19,4 +19,5 @@ FRRouting Developer's Guide
|
||||
zebra
|
||||
vtysh
|
||||
path
|
||||
pceplib
|
||||
link-state
|
||||
|
781
doc/developer/pceplib.rst
Normal file
781
doc/developer/pceplib.rst
Normal 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);``
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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(¤t_time, <);
|
||||
gmtime_r(&session->time_connected, <);
|
||||
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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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_
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
14
pceplib/.gitignore
vendored
Normal 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
48
pceplib/pcep.h
Normal 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
140
pceplib/pcep_msg_encoding.h
Normal 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
308
pceplib/pcep_msg_messages.c
Normal 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
132
pceplib/pcep_msg_messages.h
Normal 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
|
351
pceplib/pcep_msg_messages_encoding.c
Normal file
351
pceplib/pcep_msg_messages_encoding.c
Normal 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);
|
||||
}
|
389
pceplib/pcep_msg_object_error_types.c
Normal file
389
pceplib/pcep_msg_object_error_types.c
Normal 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];
|
||||
}
|
284
pceplib/pcep_msg_object_error_types.h
Normal file
284
pceplib/pcep_msg_object_error_types.h
Normal 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
854
pceplib/pcep_msg_objects.c
Normal 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
741
pceplib/pcep_msg_objects.h
Normal 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
|
1720
pceplib/pcep_msg_objects_encoding.c
Normal file
1720
pceplib/pcep_msg_objects_encoding.c
Normal file
File diff suppressed because it is too large
Load Diff
464
pceplib/pcep_msg_tlvs.c
Normal file
464
pceplib/pcep_msg_tlvs.c
Normal 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
380
pceplib/pcep_msg_tlvs.h
Normal 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_ */
|
1282
pceplib/pcep_msg_tlvs_encoding.c
Normal file
1282
pceplib/pcep_msg_tlvs_encoding.c
Normal file
File diff suppressed because it is too large
Load Diff
465
pceplib/pcep_msg_tools.c
Normal file
465
pceplib/pcep_msg_tools.c
Normal 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
71
pceplib/pcep_msg_tools.h
Normal 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
517
pceplib/pcep_pcc.c
Normal 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
392
pceplib/pcep_pcc_api.c
Normal 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
103
pceplib/pcep_pcc_api.h
Normal 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_ */
|
683
pceplib/pcep_session_logic.c
Normal file
683
pceplib/pcep_session_logic.c
Normal 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;
|
||||
}
|
290
pceplib/pcep_session_logic.h
Normal file
290
pceplib/pcep_session_logic.h
Normal 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_ */
|
450
pceplib/pcep_session_logic_counters.c
Normal file
450
pceplib/pcep_session_logic_counters.c
Normal 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);
|
||||
}
|
109
pceplib/pcep_session_logic_internals.h
Normal file
109
pceplib/pcep_session_logic_internals.h
Normal 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_ */
|
360
pceplib/pcep_session_logic_loop.c
Normal file
360
pceplib/pcep_session_logic_loop.c
Normal 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;
|
||||
}
|
1133
pceplib/pcep_session_logic_states.c
Normal file
1133
pceplib/pcep_session_logic_states.c
Normal file
File diff suppressed because it is too large
Load Diff
781
pceplib/pcep_socket_comm.c
Normal file
781
pceplib/pcep_socket_comm.c
Normal 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
198
pceplib/pcep_socket_comm.h
Normal 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_ */
|
69
pceplib/pcep_socket_comm_internals.h
Normal file
69
pceplib/pcep_socket_comm_internals.h
Normal 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_ */
|
486
pceplib/pcep_socket_comm_loop.c
Normal file
486
pceplib/pcep_socket_comm_loop.c
Normal 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;
|
||||
}
|
32
pceplib/pcep_socket_comm_loop.h
Normal file
32
pceplib/pcep_socket_comm_loop.h
Normal 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_ */
|
363
pceplib/pcep_socket_comm_mock.c
Normal file
363
pceplib/pcep_socket_comm_mock.c
Normal 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;
|
||||
}
|
67
pceplib/pcep_socket_comm_mock.h
Normal file
67
pceplib/pcep_socket_comm_mock.h
Normal 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_ */
|
76
pceplib/pcep_timer_internals.h
Normal file
76
pceplib/pcep_timer_internals.h
Normal 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
482
pceplib/pcep_timers.c
Normal 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
92
pceplib/pcep_timers.h
Normal 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_ */
|
106
pceplib/pcep_timers_event_loop.c
Normal file
106
pceplib/pcep_timers_event_loop.c
Normal 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;
|
||||
}
|
34
pceplib/pcep_timers_event_loop.h
Normal file
34
pceplib/pcep_timers_event_loop.h
Normal 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
|
475
pceplib/pcep_utils_counters.c
Normal file
475
pceplib/pcep_utils_counters.c
Normal 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;
|
||||
}
|
232
pceplib/pcep_utils_counters.h
Normal file
232
pceplib/pcep_utils_counters.h
Normal 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_ */
|
262
pceplib/pcep_utils_double_linked_list.c
Normal file
262
pceplib/pcep_utils_double_linked_list.c
Normal 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;
|
||||
}
|
72
pceplib/pcep_utils_double_linked_list.h
Normal file
72
pceplib/pcep_utils_double_linked_list.h
Normal 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_ */
|
82
pceplib/pcep_utils_logging.c
Normal file
82
pceplib/pcep_utils_logging.c
Normal 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;
|
||||
}
|
66
pceplib/pcep_utils_logging.h
Normal file
66
pceplib/pcep_utils_logging.h
Normal 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
220
pceplib/pcep_utils_memory.c
Normal 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);
|
||||
}
|
||||
}
|
89
pceplib/pcep_utils_memory.h
Normal file
89
pceplib/pcep_utils_memory.h
Normal 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_ */
|
322
pceplib/pcep_utils_ordered_list.c
Normal file
322
pceplib/pcep_utils_ordered_list.c
Normal 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;
|
||||
}
|
109
pceplib/pcep_utils_ordered_list.h
Normal file
109
pceplib/pcep_utils_ordered_list.h
Normal 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
150
pceplib/pcep_utils_queue.c
Normal 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;
|
||||
}
|
49
pceplib/pcep_utils_queue.h
Normal file
49
pceplib/pcep_utils_queue.h
Normal 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
62
pceplib/subdir.am
Normal 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
|
498
pceplib/test/pcep_msg_messages_test.c
Normal file
498
pceplib/test/pcep_msg_messages_test.c
Normal 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);
|
||||
}
|
48
pceplib/test/pcep_msg_messages_test.h
Normal file
48
pceplib/test/pcep_msg_messages_test.h
Normal 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_ */
|
256
pceplib/test/pcep_msg_messages_tests.c
Normal file
256
pceplib/test/pcep_msg_messages_tests.c
Normal 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;
|
||||
}
|
84
pceplib/test/pcep_msg_object_error_types_test.c
Normal file
84
pceplib/test/pcep_msg_object_error_types_test.c
Normal 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));
|
||||
}
|
37
pceplib/test/pcep_msg_object_error_types_test.h
Normal file
37
pceplib/test/pcep_msg_object_error_types_test.h
Normal 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_ */
|
1289
pceplib/test/pcep_msg_objects_test.c
Normal file
1289
pceplib/test/pcep_msg_objects_test.c
Normal file
File diff suppressed because it is too large
Load Diff
64
pceplib/test/pcep_msg_objects_test.h
Normal file
64
pceplib/test/pcep_msg_objects_test.h
Normal 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
|
2
pceplib/test/pcep_msg_tests_valgrind.sh
Executable file
2
pceplib/test/pcep_msg_tests_valgrind.sh
Executable file
@ -0,0 +1,2 @@
|
||||
source pceplib/test/pcep_tests_valgrind.sh
|
||||
valgrind_test pceplib/test/pcep_msg_tests
|
671
pceplib/test/pcep_msg_tlvs_test.c
Normal file
671
pceplib/test/pcep_msg_tlvs_test.c
Normal 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);
|
||||
}
|
51
pceplib/test/pcep_msg_tlvs_test.h
Normal file
51
pceplib/test/pcep_msg_tlvs_test.h
Normal 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
|
1258
pceplib/test/pcep_msg_tools_test.c
Normal file
1258
pceplib/test/pcep_msg_tools_test.c
Normal file
File diff suppressed because it is too large
Load Diff
48
pceplib/test/pcep_msg_tools_test.h
Normal file
48
pceplib/test/pcep_msg_tools_test.h
Normal 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_ */
|
285
pceplib/test/pcep_pcc_api_test.c
Normal file
285
pceplib/test/pcep_pcc_api_test.c
Normal 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);
|
||||
}
|
43
pceplib/test/pcep_pcc_api_test.h
Normal file
43
pceplib/test/pcep_pcc_api_test.h
Normal 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_ */
|
88
pceplib/test/pcep_pcc_api_tests.c
Normal file
88
pceplib/test/pcep_pcc_api_tests.c
Normal 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;
|
||||
}
|
2
pceplib/test/pcep_pcc_api_tests_valgrind.sh
Executable file
2
pceplib/test/pcep_pcc_api_tests_valgrind.sh
Executable file
@ -0,0 +1,2 @@
|
||||
source pceplib/test/pcep_tests_valgrind.sh
|
||||
valgrind_test pceplib/test/pcep_pcc_api_tests
|
219
pceplib/test/pcep_session_logic_loop_test.c
Normal file
219
pceplib/test/pcep_session_logic_loop_test.c
Normal 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);
|
||||
}
|
40
pceplib/test/pcep_session_logic_loop_test.h
Normal file
40
pceplib/test/pcep_session_logic_loop_test.h
Normal 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_ */
|
919
pceplib/test/pcep_session_logic_states_test.c
Normal file
919
pceplib/test/pcep_session_logic_states_test.c
Normal 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);
|
||||
}
|
52
pceplib/test/pcep_session_logic_states_test.h
Normal file
52
pceplib/test/pcep_session_logic_states_test.h
Normal 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_ */
|
360
pceplib/test/pcep_session_logic_test.c
Normal file
360
pceplib/test/pcep_session_logic_test.c
Normal 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);
|
||||
}
|
43
pceplib/test/pcep_session_logic_test.h
Normal file
43
pceplib/test/pcep_session_logic_test.h
Normal 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_ */
|
201
pceplib/test/pcep_session_logic_tests.c
Normal file
201
pceplib/test/pcep_session_logic_tests.c
Normal 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;
|
||||
}
|
2
pceplib/test/pcep_session_logic_tests_valgrind.sh
Executable file
2
pceplib/test/pcep_session_logic_tests_valgrind.sh
Executable 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
Loading…
Reference in New Issue
Block a user